1
- import { InformationCircleIcon } from "@heroicons/react/24/outline" ;
1
+ import {
2
+ ChevronRightIcon ,
3
+ InformationCircleIcon ,
4
+ } from "@heroicons/react/24/outline" ;
2
5
import { Tool } from "core" ;
3
- import { useEffect } from "react" ;
6
+ import { useEffect , useMemo , useState } from "react" ;
4
7
import { useDispatch } from "react-redux" ;
5
8
import { useAppSelector } from "../../../../../redux/hooks" ;
6
9
import {
7
10
addTool ,
8
11
toggleToolSetting ,
9
12
} from "../../../../../redux/slices/uiSlice" ;
10
- import { fontSize } from "../../../../../util" ;
11
13
import { ToolTip } from "../../../../gui/Tooltip" ;
14
+ import { useFontSize } from "../../../../ui/font" ;
12
15
13
16
interface ToolDropdownItemProps {
14
17
tool : Tool ;
@@ -21,81 +24,141 @@ function ToolPolicyItem(props: ToolDropdownItemProps) {
21
24
const policy = useAppSelector (
22
25
( state ) => state . ui . toolSettings [ props . tool . function . name ] ,
23
26
) ;
27
+ const [ isExpanded , setIsExpanded ] = useState ( false ) ;
24
28
25
29
useEffect ( ( ) => {
26
30
if ( ! policy ) {
27
31
dispatch ( addTool ( props . tool ) ) ;
28
32
}
29
33
} , [ props . tool . function . name , policy ] ) ;
30
34
35
+ const parameters = useMemo ( ( ) => {
36
+ if ( props . tool . function . parameters ?. properties ) {
37
+ return Object . entries ( props . tool . function . parameters . properties ) . map (
38
+ ( [ name , schema ] ) =>
39
+ [ name , schema ] as [ string , { description : string ; type : string } ] ,
40
+ ) ;
41
+ }
42
+ return undefined ;
43
+ } , [ props . tool . function . parameters ] ) ;
44
+
45
+ const fontSize = useFontSize ( - 2 ) ;
46
+
31
47
if ( ! policy ) {
32
48
return null ;
33
49
}
34
50
35
51
return (
36
52
< div
37
- className = "hover:bg-list-active hover:text-list-active-foreground -mx-2 flex cursor-pointer items-center justify-between gap-2 rounded-md px-2 py-0.5 "
53
+ className = "flex flex-col "
38
54
style = { {
39
- fontSize : fontSize ( - 3 ) ,
40
- } }
41
- data-testid = { `tool-policy-item-${ props . tool . function . name } ` }
42
- onClick = { ( e ) => {
43
- dispatch ( toggleToolSetting ( props . tool . function . name ) ) ;
44
- e . stopPropagation ( ) ;
45
- e . preventDefault ( ) ;
55
+ fontSize,
46
56
} }
47
57
>
48
- < div className = "flex flex-1 flex-row items-center gap-1" >
49
- { props . duplicatesDetected ? (
50
- < >
51
- < div >
52
- < InformationCircleIcon
53
- data-tooltip-id = { props . tool . displayTitle + "-duplicate-warning" }
54
- className = "h-3 w-3 cursor-help text-yellow-500"
58
+ < div className = "flex flex-row items-center" >
59
+ < div
60
+ className = { `hover:bg-list-active hover:text-list-active-foreground xs:gap-1.5 flex flex-1 cursor-pointer flex-row items-center gap-1 py-0.5 pl-1 pr-2` }
61
+ onClick = { ( ) => setIsExpanded ( ( val ) => ! val ) }
62
+ >
63
+ < ChevronRightIcon
64
+ className = { `xs:flex hidden h-3 w-3 flex-shrink-0 transition-all duration-200 ${ isExpanded ? "rotate-90" : "" } ` }
65
+ />
66
+
67
+ < div
68
+ className = { `flex items-center gap-1 rounded-md` }
69
+ style = { {
70
+ fontSize,
71
+ } }
72
+ >
73
+ { props . duplicatesDetected ? (
74
+ < >
75
+ < InformationCircleIcon
76
+ data-tooltip-id = {
77
+ props . tool . displayTitle + "-duplicate-warning"
78
+ }
79
+ className = "h-3 w-3 flex-shrink-0 cursor-help text-yellow-500"
80
+ />
81
+ < ToolTip
82
+ id = { props . tool . displayTitle + "-duplicate-warning" }
83
+ place = "bottom"
84
+ className = "flex flex-wrap items-center"
85
+ >
86
+ < p className = "m-0 p-0" >
87
+ < span > Duplicate tool name</ span > { " " }
88
+ < code > { props . tool . function . name } </ code > { " " }
89
+ < span >
90
+ detected. Permissions will conflict and usage may be
91
+ unpredictable
92
+ </ span >
93
+ </ p >
94
+ </ ToolTip >
95
+ </ >
96
+ ) : null }
97
+ { props . tool . faviconUrl && (
98
+ < img
99
+ src = { props . tool . faviconUrl }
100
+ alt = { props . tool . displayTitle }
101
+ className = "h-3 w-3 flex-shrink-0"
55
102
/>
56
- </ div >
57
- < ToolTip
58
- id = { props . tool . displayTitle + "-duplicate-warning" }
59
- place = "bottom"
60
- className = "flex flex-wrap items-center"
61
- >
62
- < p className = "m-0 p-0" >
63
- < span > Duplicate tool name</ span > { " " }
64
- < code > { props . tool . function . name } </ code > { " " }
65
- < span >
66
- detected. Permissions will conflict and usage may be
67
- unpredictable
68
- </ span >
69
- </ p >
70
- </ ToolTip >
103
+ ) }
104
+ < span className = "line-clamp-1 break-all" >
105
+ { props . tool . function . name }
106
+ </ span >
107
+ </ div >
108
+ </ div >
109
+ < div
110
+ className = { `flex w-8 flex-row items-center justify-end gap-2 px-2 py-0.5 sm:w-16 ${ props . excluded ? "cursor-not-allowed" : "hover:bg-list-active hover:text-list-active-foreground cursor-pointer" } ` }
111
+ data-testid = { `tool-policy-item-${ props . tool . function . name } ` }
112
+ onClick = { ( e ) => {
113
+ dispatch ( toggleToolSetting ( props . tool . function . name ) ) ;
114
+ e . stopPropagation ( ) ;
115
+ e . preventDefault ( ) ;
116
+ } }
117
+ >
118
+ { props . excluded || policy === "disabled" ? (
119
+ < >
120
+ < span className = "text-lightgray sm:hidden" > Off</ span >
121
+ < span className = "text-lightgray hidden sm:inline-block" >
122
+ Excluded
123
+ </ span >
124
+ </ >
125
+ ) : policy === "allowedWithoutPermission" ? (
126
+ < >
127
+ < span className = "text-green-500 sm:hidden" > Auto</ span >
128
+ < span className = "hidden text-green-500 sm:inline-block" >
129
+ Automatic
130
+ </ span >
131
+ </ >
132
+ ) : (
133
+ // allowedWithPermission
134
+ < >
135
+ < span className = "text-yellow-500 sm:hidden" > Ask</ span >
136
+ < span className = "hidden text-yellow-500 sm:inline-block" >
137
+ Ask First
138
+ </ span >
139
+ </ >
140
+ ) }
141
+ </ div >
142
+ </ div >
143
+ < div
144
+ className = { `flex flex-col overflow-hidden ${ isExpanded ? "h-min" : "h-0 opacity-0" } gap-x-1 gap-y-2 pl-2 transition-all` }
145
+ >
146
+ < span className = "mt-1.5 text-xs font-bold" > Description:</ span >
147
+ < span className = "italic" > { props . tool . function . description } </ span >
148
+ { parameters ? (
149
+ < >
150
+ < span className = "text-xs font-bold" > Arguments:</ span >
151
+ { parameters . map ( ( param ) => (
152
+ < div className = "block" >
153
+ < code className = "" > { param [ 0 ] } </ code >
154
+ < span className = "ml-1" > { `(${ param [ 1 ] . type } ):` } </ span >
155
+ < span className = "ml-1 italic" > { param [ 1 ] . description } </ span >
156
+ </ div >
157
+ ) ) }
71
158
</ >
72
159
) : null }
73
- < span className = "line-clamp-1 flex items-center gap-1" >
74
- { props . tool . faviconUrl && (
75
- < img
76
- src = { props . tool . faviconUrl }
77
- alt = { props . tool . displayTitle }
78
- className = "h-4 w-4"
79
- />
80
- ) }
81
- < pre className = "my-0.5 text-[11px]" > { props . tool . function . name } </ pre >
82
- </ span >
160
+ < div className = "h-1" > </ div >
83
161
</ div >
84
- { props . excluded ? (
85
- < span className = "text-lightgray" > Excluded</ span >
86
- ) : (
87
- < div className = "flex cursor-pointer gap-2" >
88
- { ( policy === "allowedWithPermission" || policy === undefined ) && (
89
- < span className = "text-yellow-500" > Ask First</ span >
90
- ) }
91
- { policy === "allowedWithoutPermission" && (
92
- < span className = "text-green-500" > Automatic</ span >
93
- ) }
94
- { policy === "disabled" && (
95
- < span className = "text-lightgray" > Excluded</ span >
96
- ) }
97
- </ div >
98
- ) }
99
162
</ div >
100
163
) ;
101
164
}
0 commit comments