Skip to content

Commit 89ba7fa

Browse files
committed
improvement(loops): added vars to loop-input
1 parent db8b564 commit 89ba7fa

File tree

5 files changed

+381
-42
lines changed

5 files changed

+381
-42
lines changed

sim/app/w/[id]/components/workflow-loop/components/loop-input/loop-input.tsx

Lines changed: 107 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { NodeProps } from 'reactflow'
88
import { Badge } from '@/components/ui/badge'
99
import { Input } from '@/components/ui/input'
1010
import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover'
11+
import { checkTagTrigger, TagDropdown } from '@/components/ui/tag-dropdown'
1112
import { cn } from '@/lib/utils'
1213
import { useWorkflowStore } from '@/stores/workflows/workflow/store'
1314

@@ -28,6 +29,11 @@ export function LoopInput({ id }: NodeProps) {
2829
const [open, setOpen] = useState(false)
2930
const editorRef = useRef<HTMLDivElement | null>(null)
3031

32+
// State for tag dropdown
33+
const [showTags, setShowTags] = useState(false)
34+
const [cursorPosition, setCursorPosition] = useState(0)
35+
const [activeSourceBlockId, setActiveSourceBlockId] = useState<string | null>(null)
36+
3137
// Initialize editor value from the store
3238
useEffect(() => {
3339
if (loopType === 'forEach' && loop?.forEachItems) {
@@ -79,17 +85,80 @@ export function LoopInput({ id }: NodeProps) {
7985
}
8086
}
8187

88+
// Handle tag selection
89+
const handleTagSelect = (newValue: string) => {
90+
setEditorValue(newValue)
91+
updateLoopForEachItems(loopId, newValue)
92+
setShowTags(false)
93+
}
94+
8295
const handleEditorChange = (value: string) => {
8396
// Always set the editor value to exactly what the user typed
8497
setEditorValue(value)
8598

99+
// Get current cursor position (approximation for editor)
100+
const textArea = editorRef.current?.querySelector('textarea')
101+
if (textArea) {
102+
setCursorPosition(textArea.selectionStart || 0)
103+
104+
// Check for tag trigger
105+
const tagTrigger = checkTagTrigger(value, textArea.selectionStart || 0)
106+
setShowTags(tagTrigger.show)
107+
}
108+
86109
// Save the items to the store for forEach loops
87110
if (loopType === 'forEach') {
88111
// Pass the exact string to preserve formatting
89112
updateLoopForEachItems(loopId, value)
90113
}
91114
}
92115

116+
// Handle editor focus
117+
const handleEditorFocus = () => {
118+
// Reset tag dropdown state
119+
setShowTags(false)
120+
setActiveSourceBlockId(null)
121+
}
122+
123+
// Handle editor blur
124+
const handleEditorBlur = () => {
125+
// We don't immediately hide the tag dropdown to allow clicking on it
126+
setTimeout(() => {
127+
if (!document.activeElement?.closest('.tag-dropdown')) {
128+
setShowTags(false)
129+
}
130+
}, 100)
131+
}
132+
133+
// Add and remove keyboard event listener for editor
134+
useEffect(() => {
135+
const textArea = editorRef.current?.querySelector('textarea')
136+
if (!textArea) return
137+
138+
const handleKeyboardEvent = (e: KeyboardEvent) => {
139+
// Update cursor position when key is pressed in editor
140+
setCursorPosition(textArea.selectionStart || 0)
141+
142+
// Check for tag trigger
143+
const tagTrigger = checkTagTrigger(editorValue, textArea.selectionStart || 0)
144+
145+
if (e.key === 'Escape') {
146+
setShowTags(false)
147+
} else if (e.key === '<') {
148+
// Show tags dropdown when '<' is typed
149+
setShowTags(true)
150+
} else if (e.key === '>') {
151+
// Hide tags dropdown when '>' is typed
152+
setShowTags(false)
153+
} else {
154+
setShowTags(tagTrigger.show)
155+
}
156+
}
157+
158+
textArea.addEventListener('keyup', handleKeyboardEvent)
159+
return () => textArea.removeEventListener('keyup', handleKeyboardEvent)
160+
}, [editorValue, editorRef])
161+
93162
// Determine label based on loop type
94163
const getLabel = () => {
95164
switch (loopType) {
@@ -149,27 +218,45 @@ export function LoopInput({ id }: NodeProps) {
149218
/>
150219
</div>
151220
) : (
152-
// Code editor for 'forEach' loops
153-
<div
154-
className="relative min-h-[80px] rounded-md bg-background font-mono text-sm px-3 pt-2 pb-3 border border-input"
155-
ref={editorRef}
156-
>
157-
{editorValue === '' && (
158-
<div className="absolute top-[8.5px] left-3 text-muted-foreground/50 pointer-events-none select-none">
159-
{getPlaceholder()}
160-
</div>
161-
)}
162-
<Editor
163-
value={editorValue}
164-
onValueChange={handleEditorChange}
165-
highlight={(code) => highlight(code, languages.javascript, 'javascript')}
166-
padding={0}
167-
style={{
168-
fontFamily: 'monospace',
169-
lineHeight: '21px',
221+
// Code editor for 'forEach' loops with tag dropdown support
222+
<div className="relative">
223+
<div
224+
className="relative min-h-[80px] rounded-md bg-background font-mono text-sm px-3 pt-2 pb-3 border border-input"
225+
ref={editorRef}
226+
>
227+
{editorValue === '' && (
228+
<div className="absolute top-[8.5px] left-3 text-muted-foreground/50 pointer-events-none select-none">
229+
{getPlaceholder()}
230+
</div>
231+
)}
232+
<Editor
233+
value={editorValue}
234+
onValueChange={handleEditorChange}
235+
highlight={(code) => highlight(code, languages.javascript, 'javascript')}
236+
padding={0}
237+
style={{
238+
fontFamily: 'monospace',
239+
lineHeight: '21px',
240+
}}
241+
className="focus:outline-none w-full"
242+
textareaClassName="focus:outline-none focus:ring-0 bg-transparent resize-none w-full overflow-hidden whitespace-pre-wrap"
243+
onFocus={handleEditorFocus}
244+
onBlur={handleEditorBlur}
245+
/>
246+
</div>
247+
<TagDropdown
248+
visible={showTags}
249+
onSelect={handleTagSelect}
250+
blockId={id}
251+
activeSourceBlockId={activeSourceBlockId}
252+
inputValue={editorValue}
253+
cursorPosition={cursorPosition}
254+
onClose={() => {
255+
setShowTags(false)
256+
setActiveSourceBlockId(null)
170257
}}
171-
className="focus:outline-none w-full"
172-
textareaClassName="focus:outline-none focus:ring-0 bg-transparent resize-none w-full overflow-hidden whitespace-pre-wrap"
258+
className="w-[calc(100%)] tag-dropdown"
259+
style={{ top: 'calc(100% + 4px)' }}
173260
/>
174261
</div>
175262
)}

sim/executor/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ export class Executor {
4545
this.validateWorkflow()
4646
this.workflowInput = workflowInput || {}
4747

48-
this.loopManager = new LoopManager(workflow.loops || {})
48+
this.loopManager = new LoopManager(workflow.loops || {}, 5, workflowVariables)
4949
this.resolver = new InputResolver(workflow, environmentVariables, workflowVariables, this.loopManager)
5050
this.pathTracker = new PathTracker(workflow)
5151

0 commit comments

Comments
 (0)