Skip to content

Commit 516d85b

Browse files
committed
fix(variables): fix tag dropdown and cursor alignment in variables block
1 parent f3e994b commit 516d85b

File tree

4 files changed

+68
-69
lines changed

4 files changed

+68
-69
lines changed

apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/starter/input-format.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -437,7 +437,7 @@ export function FieldFormat({
437437
<Code.Gutter width={gutterWidth}>{renderLineNumbers()}</Code.Gutter>
438438
<Code.Content paddingLeft={`${gutterWidth}px`}>
439439
<Code.Placeholder gutterWidth={gutterWidth} show={fieldValue.length === 0}>
440-
{'[\n 1, 2, 3\n]'}
440+
{'[1, 2, 3]'}
441441
</Code.Placeholder>
442442
<Editor
443443
value={fieldValue}

apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/tag-dropdown/tag-dropdown.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1782,7 +1782,7 @@ export const TagDropdown: React.FC<TagDropdownProps> = ({
17821782
<PopoverSection rootOnly>
17831783
<div className='flex items-center gap-[6px]'>
17841784
<TagIcon icon='V' color={BLOCK_COLORS.VARIABLE} />
1785-
Variables
1785+
Variable
17861786
</div>
17871787
</PopoverSection>
17881788
{variableTags.map((tag: string) => {

apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/variables-input/variables-input.tsx

Lines changed: 65 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,20 @@
1-
import { useEffect, useRef, useState } from 'react'
1+
import { useCallback, useEffect, useRef, useState } from 'react'
22
import { Plus } from 'lucide-react'
33
import { useParams } from 'next/navigation'
4+
import 'prismjs/components/prism-json'
5+
import Editor from 'react-simple-code-editor'
46
import {
57
Badge,
68
Button,
9+
Code,
710
Combobox,
811
type ComboboxOption,
12+
calculateGutterWidth,
13+
getCodeEditorProps,
14+
highlight,
915
Input,
1016
Label,
11-
Textarea,
17+
languages,
1218
} from '@/components/emcn'
1319
import { Trash } from '@/components/emcn/icons/trash'
1420
import { cn } from '@/lib/core/utils/cn'
@@ -39,6 +45,8 @@ interface VariablesInputProps {
3945
disabled?: boolean
4046
}
4147

48+
const jsonHighlight = (code: string): string => highlight(code, languages.json, 'json')
49+
4250
const DEFAULT_ASSIGNMENT: Omit<VariableAssignment, 'id'> = {
4351
variableName: '',
4452
type: 'string',
@@ -172,6 +180,23 @@ export function VariablesInput({
172180
setStoreValue(assignments.map((a) => (a.id === id ? { ...a, ...updates } : a)))
173181
}
174182

183+
const updateAssignmentRef = useRef(updateAssignment)
184+
updateAssignmentRef.current = updateAssignment
185+
186+
const editorValueChangeHandlersRef = useRef<Record<string, (newValue: string) => void>>({})
187+
188+
const getEditorValueChangeHandler = useCallback(
189+
(assignmentId: string): ((newValue: string) => void) => {
190+
if (!editorValueChangeHandlersRef.current[assignmentId]) {
191+
editorValueChangeHandlersRef.current[assignmentId] = (newValue: string) => {
192+
updateAssignmentRef.current(assignmentId, { value: newValue })
193+
}
194+
}
195+
return editorValueChangeHandlersRef.current[assignmentId]
196+
},
197+
[]
198+
)
199+
175200
const handleVariableSelect = (assignmentId: string, variableId: string) => {
176201
const selectedVariable = currentWorkflowVariables.find((v) => v.id === variableId)
177202
if (selectedVariable) {
@@ -402,70 +427,44 @@ export function VariablesInput({
402427
disabled={isReadOnly}
403428
/>
404429
) : assignment.type === 'object' || assignment.type === 'array' ? (
405-
<div className='relative'>
406-
<Textarea
407-
ref={(el) => {
408-
if (el) valueInputRefs.current[assignment.id] = el
409-
}}
410-
value={assignment.value || ''}
411-
onChange={(e) =>
412-
handleValueInputChange(
413-
assignment.id,
414-
e.target.value,
415-
e.target.selectionStart ?? undefined
416-
)
417-
}
418-
onKeyDown={handleKeyDown}
419-
onFocus={() => {
420-
if (!isReadOnly && !assignment.value?.trim()) {
421-
setActiveFieldId(assignment.id)
422-
setCursorPosition(0)
423-
setShowTags(true)
424-
}
425-
}}
426-
onScroll={(e) => {
427-
const overlay = overlayRefs.current[assignment.id]
428-
if (overlay) {
429-
overlay.scrollTop = e.currentTarget.scrollTop
430-
overlay.scrollLeft = e.currentTarget.scrollLeft
431-
}
432-
}}
433-
placeholder={
434-
assignment.type === 'object'
435-
? '{\n "key": "value"\n}'
436-
: '[\n 1, 2, 3\n]'
437-
}
438-
disabled={isReadOnly}
439-
className={cn(
440-
'min-h-[120px] font-mono text-sm text-transparent caret-foreground placeholder:text-muted-foreground/50',
441-
dragHighlight[assignment.id] && 'ring-2 ring-blue-500 ring-offset-2'
442-
)}
443-
style={{
444-
wordBreak: 'break-word',
445-
whiteSpace: 'pre-wrap',
446-
}}
447-
onDrop={(e) => handleDrop(e, assignment.id)}
448-
onDragOver={(e) => handleDragOver(e, assignment.id)}
449-
onDragLeave={(e) => handleDragLeave(e, assignment.id)}
450-
/>
451-
<div
452-
ref={(el) => {
453-
if (el) overlayRefs.current[assignment.id] = el
454-
}}
455-
className={cn(
456-
'absolute inset-0 flex items-start overflow-auto bg-transparent px-3 py-2 font-mono text-sm',
457-
!isReadOnly && 'pointer-events-none'
458-
)}
459-
style={{ scrollbarWidth: 'none' }}
460-
>
461-
<div className='w-full whitespace-pre-wrap break-words'>
462-
{formatDisplayText(assignment.value || '', {
463-
accessiblePrefixes,
464-
highlightAll: !accessiblePrefixes,
465-
})}
466-
</div>
467-
</div>
468-
</div>
430+
(() => {
431+
const fieldValue = assignment.value || ''
432+
const lineCount = fieldValue.split('\n').length
433+
const gutterWidth = calculateGutterWidth(lineCount)
434+
435+
return (
436+
<Code.Container className='min-h-[120px]'>
437+
<Code.Gutter width={gutterWidth}>
438+
{Array.from({ length: lineCount }, (_, i) => (
439+
<div
440+
key={i}
441+
className='font-medium font-mono text-[var(--text-muted)] text-xs'
442+
style={{ height: `${21}px`, lineHeight: `${21}px` }}
443+
>
444+
{i + 1}
445+
</div>
446+
))}
447+
</Code.Gutter>
448+
<Code.Content paddingLeft={`${gutterWidth}px`}>
449+
<Code.Placeholder
450+
gutterWidth={gutterWidth}
451+
show={fieldValue.length === 0}
452+
>
453+
{assignment.type === 'object'
454+
? '{\n "key": "value"\n}'
455+
: '[1, 2, 3]'}
456+
</Code.Placeholder>
457+
<Editor
458+
value={fieldValue}
459+
onValueChange={getEditorValueChangeHandler(assignment.id)}
460+
highlight={jsonHighlight}
461+
disabled={isReadOnly}
462+
{...getCodeEditorProps({ disabled: isReadOnly })}
463+
/>
464+
</Code.Content>
465+
</Code.Container>
466+
)
467+
})()
469468
) : (
470469
<div className='relative'>
471470
<Input

apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/variables/variables.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ const STRINGS = {
7878
boolean: 'true',
7979
plain: 'Plain text value',
8080
object: '{\n "key": "value"\n}',
81-
array: '[\n 1, 2, 3\n]',
81+
array: '[1, 2, 3]',
8282
},
8383
emptyState: 'No variables yet',
8484
}

0 commit comments

Comments
 (0)