11import type { ObjectReference } from "@dafthunk/types" ;
22import type { Node as ReactFlowNode } from "@xyflow/react" ;
3+ import ChevronDownIcon from "lucide-react/icons/chevron-down" ;
34import EyeIcon from "lucide-react/icons/eye" ;
45import EyeOffIcon from "lucide-react/icons/eye-off" ;
56import XCircleIcon from "lucide-react/icons/x-circle" ;
67import { useEffect , useState } from "react" ;
78
8- import { Card , CardContent , CardHeader , CardTitle } from "@/components/ui/card" ;
99import { Input } from "@/components/ui/input" ;
1010import { Label } from "@/components/ui/label" ;
1111import { Textarea } from "@/components/ui/textarea" ;
@@ -49,6 +49,10 @@ export function WorkflowNodeInspector({
4949 readonly WorkflowParameter [ ]
5050 > ( node ?. data . outputs || [ ] ) ;
5151
52+ // Collapsible section state
53+ const [ inputsExpanded , setInputsExpanded ] = useState ( true ) ;
54+ const [ outputsExpanded , setOutputsExpanded ] = useState ( true ) ;
55+
5256 // Update local state when node changes
5357 useEffect ( ( ) => {
5458 if ( ! node ) return ;
@@ -131,48 +135,75 @@ export function WorkflowNodeInspector({
131135 } ;
132136
133137 return (
134- < Card className = "border-none shadow-none rounded-none h-full" >
135- < CardHeader className = "pb-2" >
136- < CardTitle className = "text-lg" >
138+ < div className = "flex flex-col h-full bg-card" >
139+ { /* Header */ }
140+ < div className = "px-4 py-3 border-b border-border" >
141+ < h1 className = "text-sm font-semibold text-foreground" >
137142 { readonly ? "Node Properties (Read-only)" : "Node Properties" }
138- </ CardTitle >
139- </ CardHeader >
140- < CardContent >
141- < div className = "space-y-4" >
142- < div className = "space-y-2" >
143- < Label > Type</ Label >
144- < div className = "text-sm" > { node . data . nodeType || node . type } </ div >
143+ </ h1 >
144+ </ div >
145+
146+ { /* Content */ }
147+ < div className = "flex-1 overflow-y-auto" >
148+ { /* Type Section */ }
149+ < div className = "px-4 py-3 border-b border-border" >
150+ < Label className = "text-xs font-medium text-muted-foreground" >
151+ Type
152+ </ Label >
153+ < div className = "text-sm text-foreground mt-1" >
154+ { node . data . nodeType || node . type }
145155 </ div >
156+ </ div >
146157
147- < div className = "space-y-2" >
148- < Label htmlFor = "node-name" > Name</ Label >
149- < Input
150- id = "node-name"
151- value = { localName }
152- onChange = { handleNameChange }
153- disabled = { readonly }
154- className = { readonly ? "opacity-70 cursor-not-allowed" : "" }
155- />
156- </ div >
158+ { /* Name Section */ }
159+ < div className = "px-4 py-3 border-b border-border" >
160+ < Label
161+ htmlFor = "node-name"
162+ className = "text-xs font-medium text-muted-foreground"
163+ >
164+ Name
165+ </ Label >
166+ < Input
167+ id = "node-name"
168+ value = { localName }
169+ onChange = { handleNameChange }
170+ disabled = { readonly }
171+ className = { `mt-2 text-sm h-8 ${ readonly ? "opacity-70 cursor-not-allowed" : "" } ` }
172+ />
173+ </ div >
157174
158- < div className = "space-y-2" >
159- < h2 className = "font-medium border-b border-border pb-2" > Inputs</ h2 >
160- < div className = "space-y-2" >
175+ { /* Inputs Section */ }
176+ < div className = "border-b border-border" >
177+ < button
178+ onClick = { ( ) => setInputsExpanded ( ! inputsExpanded ) }
179+ className = "w-full px-4 py-3 bg-neutral-100 dark:bg-neutral-900 hover:bg-neutral-200 dark:hover:bg-neutral-800 transition-colors flex items-center justify-between"
180+ >
181+ < h2 className = "text-xs font-semibold text-foreground" > Inputs</ h2 >
182+ < ChevronDownIcon
183+ className = { `h-4 w-4 text-muted-foreground transition-transform ${
184+ inputsExpanded ? "rotate-0" : "-rotate-90"
185+ } `}
186+ />
187+ </ button >
188+ { inputsExpanded && (
189+ < div className = "px-4 py-3 space-y-3" >
161190 { localInputs . length > 0 ? (
162191 localInputs . map ( ( input ) => (
163192 < div key = { input . id } className = "text-sm space-y-1" >
164193 < div className = "flex items-center justify-between" >
165- < div className = "flex items-center gap-2" >
166- < span > { input . name } </ span >
167- < span className = "text-xs text-neutral-500" >
194+ < div className = "flex items-center gap-2 min-w-0" >
195+ < span className = "text-foreground font-medium truncate" >
196+ { input . name }
197+ </ span >
198+ < span className = "text-xs text-muted-foreground shrink-0" >
168199 { input . type }
169200 </ span >
170201 </ div >
171- < div className = "flex items-center gap-2 " >
202+ < div className = "flex items-center gap-1 shrink-0 " >
172203 { input . value !== undefined && ! readonly && (
173204 < button
174205 onClick = { ( ) => handleClearValue ( input . id ) }
175- className = "text-neutral-400 hover: text-neutral-600 dark: hover:text-neutral-300 transition-colors"
206+ className = "p-1 text-muted-foreground hover:text-foreground transition-colors"
176207 aria-label = { `Clear ${ input . name } value` }
177208 >
178209 < XCircleIcon className = "h-4 w-4" />
@@ -185,7 +216,7 @@ export function WorkflowNodeInspector({
185216 handleToggleVisibility ( input . id )
186217 }
187218 aria-label = { `Toggle visibility for ${ input . name } ` }
188- className = { `bg-transparent data-[state=on]:bg-transparent hover:bg-transparent data-[state=on]:text-neutral-400 hover:text-neutral-600 dark:hover:text-neutral-300 transition-colors ${
219+ className = { `px-1 h-8 w-8 bg-transparent data-[state=on]:bg-transparent hover:bg-muted data-[state=on]:text-muted-foreground hover:text-foreground transition-colors ${
189220 readonly ? "opacity-70 cursor-not-allowed" : ""
190221 } `}
191222 disabled = { readonly }
@@ -233,33 +264,48 @@ export function WorkflowNodeInspector({
233264 </ div >
234265 ) )
235266 ) : (
236- < div className = "text-sm text-neutral-500 " > No inputs</ div >
267+ < div className = "text-sm text-muted-foreground " > No inputs</ div >
237268 ) }
238269 </ div >
239- </ div >
270+ ) }
271+ </ div >
240272
241- < div className = "space-y-2" >
242- < h2 className = "font-medium border-b border-border pb-2" > Outputs</ h2 >
243- < div className = "space-y-2" >
273+ { /* Outputs Section */ }
274+ < div className = "border-b border-border" >
275+ < button
276+ onClick = { ( ) => setOutputsExpanded ( ! outputsExpanded ) }
277+ className = "w-full px-4 py-3 bg-neutral-100 dark:bg-neutral-900 hover:bg-neutral-200 dark:hover:bg-neutral-800 transition-colors flex items-center justify-between"
278+ >
279+ < h2 className = "text-xs font-semibold text-foreground" > Outputs</ h2 >
280+ < ChevronDownIcon
281+ className = { `h-4 w-4 text-muted-foreground transition-transform ${
282+ outputsExpanded ? "rotate-0" : "-rotate-90"
283+ } `}
284+ />
285+ </ button >
286+ { outputsExpanded && (
287+ < div className = "px-4 py-3 space-y-3" >
244288 { localOutputs . length > 0 ? (
245289 localOutputs . map ( ( output ) => (
246290 < div key = { output . id } className = "text-sm space-y-1" >
247291 < div className = "flex items-center justify-between" >
248- < div className = "flex items-center gap-2" >
249- < span > { output . name } </ span >
250- < span className = "text-xs text-neutral-500" >
292+ < div className = "flex items-center gap-2 min-w-0" >
293+ < span className = "text-foreground font-medium truncate" >
294+ { output . name }
295+ </ span >
296+ < span className = "text-xs text-muted-foreground shrink-0" >
251297 { output . type }
252298 </ span >
253299 </ div >
254- < div className = "flex items-center gap-2 " >
300+ < div className = "flex items-center gap-1 shrink-0 " >
255301 < Toggle
256302 size = "sm"
257303 pressed = { output . hidden }
258304 onPressedChange = { ( ) =>
259305 handleToggleOutputVisibility ( output . id )
260306 }
261307 aria-label = { `Toggle visibility for ${ output . name } ` }
262- className = { `bg-transparent data-[state=on]:bg-transparent hover:bg-transparent data-[state=on]:text-neutral-400 hover:text-neutral-600 dark:hover:text-neutral-300 transition-colors ${
308+ className = { `px-1 h-8 w-8 bg-transparent data-[state=on]:bg-transparent hover:bg-muted data-[state=on]:text-muted-foreground hover:text-foreground transition-colors ${
263309 readonly ? "opacity-70 cursor-not-allowed" : ""
264310 } `}
265311 disabled = { readonly }
@@ -279,12 +325,12 @@ export function WorkflowNodeInspector({
279325 </ div >
280326 ) )
281327 ) : (
282- < div className = "text-sm text-neutral-500 " > No outputs</ div >
328+ < div className = "text-sm text-muted-foreground " > No outputs</ div >
283329 ) }
284330 </ div >
285- </ div >
331+ ) }
286332 </ div >
287- </ CardContent >
288- </ Card >
333+ </ div >
334+ </ div >
289335 ) ;
290336}
0 commit comments