1- import { InformationCircleIcon } from "@heroicons/react/24/outline" ;
1+ import {
2+ ChevronRightIcon ,
3+ InformationCircleIcon ,
4+ } from "@heroicons/react/24/outline" ;
25import { Tool } from "core" ;
3- import { useEffect } from "react" ;
6+ import { useEffect , useMemo , useState } from "react" ;
47import { useDispatch } from "react-redux" ;
58import { useAppSelector } from "../../../../../redux/hooks" ;
69import {
710 addTool ,
811 toggleToolSetting ,
912} from "../../../../../redux/slices/uiSlice" ;
10- import { fontSize } from "../../../../../util" ;
1113import { ToolTip } from "../../../../gui/Tooltip" ;
14+ import { useFontSize } from "../../../../ui/font" ;
1215
1316interface ToolDropdownItemProps {
1417 tool : Tool ;
@@ -21,81 +24,141 @@ function ToolPolicyItem(props: ToolDropdownItemProps) {
2124 const policy = useAppSelector (
2225 ( state ) => state . ui . toolSettings [ props . tool . function . name ] ,
2326 ) ;
27+ const [ isExpanded , setIsExpanded ] = useState ( false ) ;
2428
2529 useEffect ( ( ) => {
2630 if ( ! policy ) {
2731 dispatch ( addTool ( props . tool ) ) ;
2832 }
2933 } , [ props . tool . function . name , policy ] ) ;
3034
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+
3147 if ( ! policy ) {
3248 return null ;
3349 }
3450
3551 return (
3652 < 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 "
3854 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,
4656 } }
4757 >
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"
55102 />
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+ ) ) }
71158 </ >
72159 ) : 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 >
83161 </ 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- ) }
99162 </ div >
100163 ) ;
101164}
0 commit comments