Skip to content

Commit f5063af

Browse files
fix[ux]: referenced block renaming (#132)
* fix[ui]: referenced block renaming #120 * improvement[block-renaming]: added support for condition/function blocks --------- Co-authored-by: Braulio Fernandes <brauliopf+github@gmail.com>
1 parent 73f224d commit f5063af

File tree

2 files changed

+127
-41
lines changed

2 files changed

+127
-41
lines changed

sim/app/stores/workflows/workflow/store.ts

Lines changed: 63 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -402,18 +402,80 @@ export const useWorkflowStore = create<WorkflowStoreWithHistory>()(
402402
},
403403

404404
updateBlockName: (id: string, name: string) => {
405+
const oldBlock = get().blocks[id]
406+
if (!oldBlock) return
407+
408+
// Create a new state with the updated block name
405409
const newState = {
406410
blocks: {
407411
...get().blocks,
408412
[id]: {
409-
...get().blocks[id],
413+
...oldBlock,
410414
name,
411415
},
412416
},
413417
edges: [...get().edges],
414418
loops: { ...get().loops },
415419
}
416420

421+
// Update references in subblock store
422+
const subBlockStore = useSubBlockStore.getState()
423+
const activeWorkflowId = useWorkflowRegistry.getState().activeWorkflowId
424+
if (activeWorkflowId) {
425+
// Get the workflow values for the active workflow
426+
// workflowValues: {[block_id]:{[subblock_id]:[subblock_value]}}
427+
const workflowValues = subBlockStore.workflowValues[activeWorkflowId] || {}
428+
const updatedWorkflowValues = { ...workflowValues }
429+
430+
// Loop through blocks
431+
Object.entries(workflowValues).forEach(([blockId, blockValues]) => {
432+
if (blockId === id) return // Skip the block being renamed
433+
434+
// Loop through subblocks and update references
435+
Object.entries(blockValues).forEach(([subBlockId, value]) => {
436+
const oldBlockName = oldBlock.name.replace(/\s+/g, '').toLowerCase()
437+
const newBlockName = name.replace(/\s+/g, '').toLowerCase()
438+
const regex = new RegExp(`<${oldBlockName}\\.`, 'g')
439+
440+
// Use a recursive function to handle all object types
441+
updatedWorkflowValues[blockId][subBlockId] = updateReferences(value, regex, `<${newBlockName}.`)
442+
443+
// Helper function to recursively update references in any data structure
444+
function updateReferences(value: any, regex: RegExp, replacement: string): any {
445+
// Handle string values
446+
if (typeof value === 'string') {
447+
return regex.test(value) ? value.replace(regex, replacement) : value
448+
}
449+
450+
// Handle arrays
451+
if (Array.isArray(value)) {
452+
return value.map(item => updateReferences(item, regex, replacement))
453+
}
454+
455+
// Handle objects
456+
if (value !== null && typeof value === 'object') {
457+
const result = { ...value }
458+
for (const key in result) {
459+
result[key] = updateReferences(result[key], regex, replacement)
460+
}
461+
return result
462+
}
463+
464+
// Return unchanged for other types
465+
return value
466+
}
467+
})
468+
})
469+
470+
// Update the subblock store with the new values
471+
useSubBlockStore.setState({
472+
workflowValues: {
473+
...subBlockStore.workflowValues,
474+
[activeWorkflowId]: updatedWorkflowValues,
475+
},
476+
})
477+
}
478+
417479
set(newState)
418480
pushHistory(set, get, newState, `${name} block name updated`)
419481
get().updateLastSaved()

sim/app/w/[id]/components/workflow-block/components/sub-block/components/condition-input.tsx

Lines changed: 64 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -36,12 +36,6 @@ interface ConditionInputProps {
3636

3737
export function ConditionInput({ blockId, subBlockId, isConnecting }: ConditionInputProps) {
3838
const [storeValue, setStoreValue] = useSubBlockValue(blockId, subBlockId)
39-
const [lineCount, setLineCount] = useState(1)
40-
const [showTags, setShowTags] = useState(false)
41-
const [showEnvVars, setShowEnvVars] = useState(false)
42-
const [searchTerm, setSearchTerm] = useState('')
43-
const [cursorPosition, setCursorPosition] = useState(0)
44-
const [activeSourceBlockId, setActiveSourceBlockId] = useState<string | null>(null)
4539
const editorRef = useRef<HTMLDivElement>(null)
4640
const [visualLineHeights, setVisualLineHeights] = useState<{
4741
[key: string]: number[]
@@ -50,6 +44,11 @@ export function ConditionInput({ blockId, subBlockId, isConnecting }: ConditionI
5044
const removeEdge = useWorkflowStore((state) => state.removeEdge)
5145
const edges = useWorkflowStore((state) => state.edges)
5246

47+
// Use a ref to track the previous store value for comparison
48+
const prevStoreValueRef = useRef<string | null>(null)
49+
// Use a ref to track if we're currently syncing from store to prevent loops
50+
const isSyncingFromStoreRef = useRef(false)
51+
5352
// Initialize conditional blocks with if and else blocks
5453
const [conditionalBlocks, setConditionalBlocks] = useState<ConditionalBlock[]>([
5554
{
@@ -74,46 +73,71 @@ export function ConditionInput({ blockId, subBlockId, isConnecting }: ConditionI
7473
},
7574
])
7675

77-
// Sync store value with conditional blocks on initial load
76+
// Sync store value with conditional blocks when storeValue changes
7877
useEffect(() => {
79-
if (storeValue !== null) {
80-
try {
81-
const parsedValue = JSON.parse(storeValue.toString())
82-
if (Array.isArray(parsedValue)) {
83-
setConditionalBlocks(parsedValue)
84-
}
85-
} catch {
86-
// If the store value isn't valid JSON, initialize with default blocks
87-
setConditionalBlocks([
88-
{
89-
id: crypto.randomUUID(),
90-
title: 'if',
91-
value: '',
92-
showTags: false,
93-
showEnvVars: false,
94-
searchTerm: '',
95-
cursorPosition: 0,
96-
activeSourceBlockId: null,
97-
},
98-
{
99-
id: crypto.randomUUID(),
100-
title: 'else',
101-
value: '',
102-
showTags: false,
103-
showEnvVars: false,
104-
searchTerm: '',
105-
cursorPosition: 0,
106-
activeSourceBlockId: null,
107-
},
108-
])
78+
// Skip if we don't have a store value
79+
if (storeValue === null) return
80+
81+
// Skip if the store value hasn't changed
82+
const storeValueStr = storeValue.toString()
83+
if (storeValueStr === prevStoreValueRef.current) return
84+
85+
// Update the previous store value ref
86+
prevStoreValueRef.current = storeValueStr
87+
88+
// Set that we're syncing from store to prevent loops
89+
isSyncingFromStoreRef.current = true
90+
91+
try {
92+
const parsedValue = JSON.parse(storeValueStr)
93+
if (Array.isArray(parsedValue)) {
94+
setConditionalBlocks(parsedValue)
10995
}
96+
} catch (error) {
97+
// If parsing fails, set default blocks
98+
setConditionalBlocks([
99+
{
100+
id: crypto.randomUUID(),
101+
title: 'if',
102+
value: '',
103+
showTags: false,
104+
showEnvVars: false,
105+
searchTerm: '',
106+
cursorPosition: 0,
107+
activeSourceBlockId: null,
108+
},
109+
{
110+
id: crypto.randomUUID(),
111+
title: 'else',
112+
value: '',
113+
showTags: false,
114+
showEnvVars: false,
115+
searchTerm: '',
116+
cursorPosition: 0,
117+
activeSourceBlockId: null,
118+
},
119+
])
120+
} finally {
121+
// Reset the syncing flag
122+
setTimeout(() => {
123+
isSyncingFromStoreRef.current = false
124+
}, 0)
110125
}
111-
}, [])
126+
}, [storeValue])
112127

113128
// Update store whenever conditional blocks change
114129
useEffect(() => {
115-
setStoreValue(JSON.stringify(conditionalBlocks))
116-
updateNodeInternals(`${blockId}-${subBlockId}`)
130+
// Skip if we're currently syncing from store to prevent loops
131+
if (isSyncingFromStoreRef.current) return
132+
133+
const newValue = JSON.stringify(conditionalBlocks)
134+
135+
// Only update if the value has actually changed
136+
if (newValue !== prevStoreValueRef.current) {
137+
prevStoreValueRef.current = newValue
138+
setStoreValue(newValue)
139+
updateNodeInternals(`${blockId}-${subBlockId}`)
140+
}
117141
}, [conditionalBlocks, blockId, subBlockId])
118142

119143
// Update block value with trigger checks - handle both tag and env var triggers consistently

0 commit comments

Comments
 (0)