From 78cb382bc0bdab3afd6353342a9f0e4eb49e255f Mon Sep 17 00:00:00 2001 From: Vikhyath Mondreti Date: Fri, 26 Dec 2025 12:46:13 -0800 Subject: [PATCH 1/2] fix(block-name): updating block name should update downstream var refs --- .../panel/components/editor/editor.tsx | 6 +- apps/sim/hooks/use-collaborative-workflow.ts | 47 +++++++++++-- apps/sim/stores/workflows/workflow/store.ts | 68 +++++++++++-------- 3 files changed, 84 insertions(+), 37 deletions(-) diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/editor.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/editor.tsx index 82351ac8a9..7e459d58fb 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/editor.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/editor.tsx @@ -134,7 +134,11 @@ export function Editor() { const trimmedName = editedName.trim() if (trimmedName && trimmedName !== currentBlock?.name) { - collaborativeUpdateBlockName(currentBlockId, trimmedName) + const result = collaborativeUpdateBlockName(currentBlockId, trimmedName) + if (!result.success) { + // Keep rename mode open on error so user can correct the name + return + } } setIsRenaming(false) }, [currentBlockId, isRenaming, editedName, currentBlock?.name, collaborativeUpdateBlockName]) diff --git a/apps/sim/hooks/use-collaborative-workflow.ts b/apps/sim/hooks/use-collaborative-workflow.ts index 7730c9c932..3546255087 100644 --- a/apps/sim/hooks/use-collaborative-workflow.ts +++ b/apps/sim/hooks/use-collaborative-workflow.ts @@ -8,6 +8,7 @@ import { TriggerUtils } from '@/lib/workflows/triggers/triggers' import { useSocket } from '@/app/workspace/providers/socket-provider' import { getBlock } from '@/blocks' import { useUndoRedo } from '@/hooks/use-undo-redo' +import { useNotificationStore } from '@/stores/notifications' import { registerEmitFunctions, useOperationQueue } from '@/stores/operation-queue/store' import { usePanelEditorStore } from '@/stores/panel/editor/store' import { useVariablesStore } from '@/stores/panel/variables/store' @@ -15,7 +16,7 @@ import { useUndoRedoStore } from '@/stores/undo-redo' import { useWorkflowDiffStore } from '@/stores/workflow-diff/store' import { useWorkflowRegistry } from '@/stores/workflows/registry/store' import { useSubBlockStore } from '@/stores/workflows/subblock/store' -import { getUniqueBlockName, mergeSubblockState } from '@/stores/workflows/utils' +import { getUniqueBlockName, mergeSubblockState, normalizeName } from '@/stores/workflows/utils' import { useWorkflowStore } from '@/stores/workflows/workflow/store' import type { BlockState, Position } from '@/stores/workflows/workflow/types' @@ -1016,14 +1017,46 @@ export function useCollaborativeWorkflow() { ) const collaborativeUpdateBlockName = useCallback( - (id: string, name: string) => { - executeQueuedOperation('update-name', 'block', { id, name }, () => { - const result = workflowStore.updateBlockName(id, name) + (id: string, name: string): { success: boolean; error?: string } => { + const trimmedName = name.trim() + const normalizedNewName = normalizeName(trimmedName) + + // Check for empty name + if (!normalizedNewName) { + logger.error('Cannot rename block to empty name') + useNotificationStore.getState().addNotification({ + level: 'error', + message: 'Block name cannot be empty', + workflowId: activeWorkflowId || undefined, + }) + return { success: false, error: 'Block name cannot be empty' } + } + + // Check for duplicate block names before queueing the operation + const currentBlocks = workflowStore.blocks + const conflictingBlock = Object.entries(currentBlocks).find( + ([blockId, block]) => blockId !== id && normalizeName(block.name) === normalizedNewName + ) + + if (conflictingBlock) { + const conflictName = conflictingBlock[1].name + logger.error(`Cannot rename block to "${trimmedName}" - conflicts with "${conflictName}"`) + useNotificationStore.getState().addNotification({ + level: 'error', + message: `Block name "${trimmedName}" already exists`, + workflowId: activeWorkflowId || undefined, + }) + return { success: false, error: `Block name "${trimmedName}" already exists` } + } + + // Name is valid, proceed with the operation + executeQueuedOperation('update-name', 'block', { id, name: trimmedName }, () => { + const result = workflowStore.updateBlockName(id, trimmedName) if (result.success && result.changedSubblocks.length > 0) { logger.info('Emitting cascaded subblock updates from block rename', { blockId: id, - newName: name, + newName: trimmedName, updateCount: result.changedSubblocks.length, }) @@ -1043,7 +1076,7 @@ export function useCollaborativeWorkflow() { operation: { operation: 'subblock-update', target: 'subblock', - payload: { blockId, subBlockId, value: newValue }, + payload: { blockId, subblockId: subBlockId, value: newValue }, }, workflowId: activeWorkflowId || '', userId: session?.user?.id || 'unknown', @@ -1052,6 +1085,8 @@ export function useCollaborativeWorkflow() { ) } }) + + return { success: true } }, [executeQueuedOperation, workflowStore, addToQueue, activeWorkflowId, session?.user?.id] ) diff --git a/apps/sim/stores/workflows/workflow/store.ts b/apps/sim/stores/workflows/workflow/store.ts index 297d1ee85a..41d3051637 100644 --- a/apps/sim/stores/workflows/workflow/store.ts +++ b/apps/sim/stores/workflows/workflow/store.ts @@ -716,54 +716,62 @@ export const useWorkflowStore = create()( const workflowValues = subBlockStore.workflowValues[activeWorkflowId] || {} const updatedWorkflowValues = { ...workflowValues } + // Helper function to recursively update references in any data structure + function updateReferences(value: any, regex: RegExp, replacement: string): any { + // Handle string values + if (typeof value === 'string') { + return regex.test(value) ? value.replace(regex, replacement) : value + } + + // Handle arrays + if (Array.isArray(value)) { + return value.map((item) => updateReferences(item, regex, replacement)) + } + + // Handle objects + if (value !== null && typeof value === 'object') { + const result = { ...value } + for (const key in result) { + result[key] = updateReferences(result[key], regex, replacement) + } + return result + } + + // Return unchanged for other types + return value + } + + const oldBlockName = normalizeName(oldBlock.name) + const newBlockName = normalizeName(name) + const regex = new RegExp(`<${oldBlockName}\\.`, 'g') + // Loop through blocks Object.entries(workflowValues).forEach(([blockId, blockValues]) => { if (blockId === id) return // Skip the block being renamed + let blockHasChanges = false + const updatedBlockValues = { ...blockValues } + // Loop through subblocks and update references Object.entries(blockValues).forEach(([subBlockId, value]) => { - const oldBlockName = normalizeName(oldBlock.name) - const newBlockName = normalizeName(name) - const regex = new RegExp(`<${oldBlockName}\\.`, 'g') - // Use a recursive function to handle all object types const updatedValue = updateReferences(value, regex, `<${newBlockName}.`) // Check if the value actually changed if (JSON.stringify(updatedValue) !== JSON.stringify(value)) { - updatedWorkflowValues[blockId][subBlockId] = updatedValue + updatedBlockValues[subBlockId] = updatedValue + blockHasChanges = true changedSubblocks.push({ blockId, subBlockId, newValue: updatedValue, }) } - - // Helper function to recursively update references in any data structure - function updateReferences(value: any, regex: RegExp, replacement: string): any { - // Handle string values - if (typeof value === 'string') { - return regex.test(value) ? value.replace(regex, replacement) : value - } - - // Handle arrays - if (Array.isArray(value)) { - return value.map((item) => updateReferences(item, regex, replacement)) - } - - // Handle objects - if (value !== null && typeof value === 'object') { - const result = { ...value } - for (const key in result) { - result[key] = updateReferences(result[key], regex, replacement) - } - return result - } - - // Return unchanged for other types - return value - } }) + + if (blockHasChanges) { + updatedWorkflowValues[blockId] = updatedBlockValues + } }) // Update the subblock store with the new values From 1072e4af83b7fb0e1d6c5304d51e9ac8eac96506 Mon Sep 17 00:00:00 2001 From: Vikhyath Mondreti Date: Fri, 26 Dec 2025 12:49:24 -0800 Subject: [PATCH 2/2] remove random comments --- apps/sim/hooks/use-collaborative-workflow.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/apps/sim/hooks/use-collaborative-workflow.ts b/apps/sim/hooks/use-collaborative-workflow.ts index 3546255087..37e599a27c 100644 --- a/apps/sim/hooks/use-collaborative-workflow.ts +++ b/apps/sim/hooks/use-collaborative-workflow.ts @@ -1021,7 +1021,6 @@ export function useCollaborativeWorkflow() { const trimmedName = name.trim() const normalizedNewName = normalizeName(trimmedName) - // Check for empty name if (!normalizedNewName) { logger.error('Cannot rename block to empty name') useNotificationStore.getState().addNotification({ @@ -1032,7 +1031,6 @@ export function useCollaborativeWorkflow() { return { success: false, error: 'Block name cannot be empty' } } - // Check for duplicate block names before queueing the operation const currentBlocks = workflowStore.blocks const conflictingBlock = Object.entries(currentBlocks).find( ([blockId, block]) => blockId !== id && normalizeName(block.name) === normalizedNewName @@ -1049,7 +1047,6 @@ export function useCollaborativeWorkflow() { return { success: false, error: `Block name "${trimmedName}" already exists` } } - // Name is valid, proceed with the operation executeQueuedOperation('update-name', 'block', { id, name: trimmedName }, () => { const result = workflowStore.updateBlockName(id, trimmedName)