@@ -9,10 +9,11 @@ import {
99 Input ,
1010 Label ,
1111 Textarea ,
12+ Tooltip ,
1213} from '@sim/emcn'
1314import { Trash } from '@sim/emcn/icons'
1415import { generateId } from '@sim/utils/id'
15- import { Plus } from 'lucide-react'
16+ import { ArrowLeftRight , Plus } from 'lucide-react'
1617import { useParams } from 'next/navigation'
1718import { formatDisplayText } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/formatted-text'
1819import {
@@ -62,6 +63,12 @@ const BOOLEAN_OPTIONS: ComboboxOption[] = [
6263 { label : 'false' , value : 'false' } ,
6364]
6465
66+ /**
67+ * Values representable by the boolean selector; anything else (e.g. a block
68+ * reference) requires the manual input.
69+ */
70+ const BOOLEAN_LITERALS = new Set ( [ '' , 'true' , 'false' ] )
71+
6572/**
6673 * Parses a value that might be a JSON string or already an array of VariableAssignment.
6774 * This handles the case where workflows are imported with stringified values.
@@ -116,6 +123,7 @@ export function VariablesInput({
116123 const overlayRefs = useRef < Record < string , HTMLDivElement > > ( { } )
117124 const [ dragHighlight , setDragHighlight ] = useState < Record < string , boolean > > ( { } )
118125 const [ collapsedAssignments , setCollapsedAssignments ] = useState < Record < string , boolean > > ( { } )
126+ const [ manualBooleanModes , setManualBooleanModes ] = useState < Record < string , boolean > > ( { } )
119127
120128 const currentWorkflowVariables = Object . values ( workflowVariables ) . filter (
121129 ( v : Variable ) => v . workflowId === workflowId
@@ -375,6 +383,9 @@ export function VariablesInput({
375383 valuePath : [ index , 'variableName' ] ,
376384 label : variableLabel ,
377385 } )
386+ const isManualBoolean =
387+ assignment . type === 'boolean' &&
388+ ( manualBooleanModes [ assignment . id ] ?? ! BOOLEAN_LITERALS . has ( assignment . value ?? '' ) )
378389 const booleanLabelHighlight = getWorkflowSearchLabelHighlight ( {
379390 activeSearchTarget,
380391 blockId,
@@ -467,8 +478,44 @@ export function VariablesInput({
467478 </ div >
468479
469480 < div className = 'flex flex-col gap-1.5' >
470- < Label className = 'text-small' > Value</ Label >
471- { assignment . type === 'boolean' ? (
481+ < div className = 'flex items-center justify-between' >
482+ < Label className = 'text-small' > Value</ Label >
483+ { assignment . type === 'boolean' && (
484+ < Tooltip . Root >
485+ < Tooltip . Trigger asChild >
486+ < button
487+ type = 'button'
488+ className = 'flex size-[12px] flex-shrink-0 items-center justify-center bg-transparent p-0 disabled:cursor-not-allowed disabled:opacity-50'
489+ onClick = { ( ) =>
490+ setManualBooleanModes ( ( prev ) => ( {
491+ ...prev ,
492+ [ assignment . id ] : ! isManualBoolean ,
493+ } ) )
494+ }
495+ disabled = { isReadOnly }
496+ aria-label = {
497+ isManualBoolean ? 'Switch to selector' : 'Switch to manual value'
498+ }
499+ >
500+ < ArrowLeftRight
501+ className = { cn (
502+ '!h-[12px] !w-[12px]' ,
503+ isManualBoolean
504+ ? 'text-[var(--text-primary)]'
505+ : 'text-[var(--text-secondary)]'
506+ ) }
507+ />
508+ </ button >
509+ </ Tooltip . Trigger >
510+ < Tooltip . Content side = 'top' >
511+ < p >
512+ { isManualBoolean ? 'Switch to selector' : 'Switch to manual value' }
513+ </ p >
514+ </ Tooltip . Content >
515+ </ Tooltip . Root >
516+ ) }
517+ </ div >
518+ { assignment . type === 'boolean' && ! isManualBoolean ? (
472519 < Combobox
473520 options = { BOOLEAN_OPTIONS }
474521 value = { assignment . value ?? '' }
0 commit comments