fix(block-name): updating block name should update downstream var refs (#2592)

* fix(block-name): updating block name should update downstream var refs

* remove random comments
This commit is contained in:
Vikhyath Mondreti
2025-12-26 12:56:02 -08:00
committed by GitHub
parent 27ec4120bc
commit 66b8434861
3 changed files with 81 additions and 37 deletions

View File

@@ -134,7 +134,11 @@ export function Editor() {
const trimmedName = editedName.trim() const trimmedName = editedName.trim()
if (trimmedName && trimmedName !== currentBlock?.name) { 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) setIsRenaming(false)
}, [currentBlockId, isRenaming, editedName, currentBlock?.name, collaborativeUpdateBlockName]) }, [currentBlockId, isRenaming, editedName, currentBlock?.name, collaborativeUpdateBlockName])

View File

@@ -8,6 +8,7 @@ import { TriggerUtils } from '@/lib/workflows/triggers/triggers'
import { useSocket } from '@/app/workspace/providers/socket-provider' import { useSocket } from '@/app/workspace/providers/socket-provider'
import { getBlock } from '@/blocks' import { getBlock } from '@/blocks'
import { useUndoRedo } from '@/hooks/use-undo-redo' import { useUndoRedo } from '@/hooks/use-undo-redo'
import { useNotificationStore } from '@/stores/notifications'
import { registerEmitFunctions, useOperationQueue } from '@/stores/operation-queue/store' import { registerEmitFunctions, useOperationQueue } from '@/stores/operation-queue/store'
import { usePanelEditorStore } from '@/stores/panel/editor/store' import { usePanelEditorStore } from '@/stores/panel/editor/store'
import { useVariablesStore } from '@/stores/panel/variables/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 { useWorkflowDiffStore } from '@/stores/workflow-diff/store'
import { useWorkflowRegistry } from '@/stores/workflows/registry/store' import { useWorkflowRegistry } from '@/stores/workflows/registry/store'
import { useSubBlockStore } from '@/stores/workflows/subblock/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 { useWorkflowStore } from '@/stores/workflows/workflow/store'
import type { BlockState, Position } from '@/stores/workflows/workflow/types' import type { BlockState, Position } from '@/stores/workflows/workflow/types'
@@ -1016,14 +1017,43 @@ export function useCollaborativeWorkflow() {
) )
const collaborativeUpdateBlockName = useCallback( const collaborativeUpdateBlockName = useCallback(
(id: string, name: string) => { (id: string, name: string): { success: boolean; error?: string } => {
executeQueuedOperation('update-name', 'block', { id, name }, () => { const trimmedName = name.trim()
const result = workflowStore.updateBlockName(id, name) const normalizedNewName = normalizeName(trimmedName)
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' }
}
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` }
}
executeQueuedOperation('update-name', 'block', { id, name: trimmedName }, () => {
const result = workflowStore.updateBlockName(id, trimmedName)
if (result.success && result.changedSubblocks.length > 0) { if (result.success && result.changedSubblocks.length > 0) {
logger.info('Emitting cascaded subblock updates from block rename', { logger.info('Emitting cascaded subblock updates from block rename', {
blockId: id, blockId: id,
newName: name, newName: trimmedName,
updateCount: result.changedSubblocks.length, updateCount: result.changedSubblocks.length,
}) })
@@ -1043,7 +1073,7 @@ export function useCollaborativeWorkflow() {
operation: { operation: {
operation: 'subblock-update', operation: 'subblock-update',
target: 'subblock', target: 'subblock',
payload: { blockId, subBlockId, value: newValue }, payload: { blockId, subblockId: subBlockId, value: newValue },
}, },
workflowId: activeWorkflowId || '', workflowId: activeWorkflowId || '',
userId: session?.user?.id || 'unknown', userId: session?.user?.id || 'unknown',
@@ -1052,6 +1082,8 @@ export function useCollaborativeWorkflow() {
) )
} }
}) })
return { success: true }
}, },
[executeQueuedOperation, workflowStore, addToQueue, activeWorkflowId, session?.user?.id] [executeQueuedOperation, workflowStore, addToQueue, activeWorkflowId, session?.user?.id]
) )

View File

@@ -716,54 +716,62 @@ export const useWorkflowStore = create<WorkflowStore>()(
const workflowValues = subBlockStore.workflowValues[activeWorkflowId] || {} const workflowValues = subBlockStore.workflowValues[activeWorkflowId] || {}
const updatedWorkflowValues = { ...workflowValues } 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 // Loop through blocks
Object.entries(workflowValues).forEach(([blockId, blockValues]) => { Object.entries(workflowValues).forEach(([blockId, blockValues]) => {
if (blockId === id) return // Skip the block being renamed if (blockId === id) return // Skip the block being renamed
let blockHasChanges = false
const updatedBlockValues = { ...blockValues }
// Loop through subblocks and update references // Loop through subblocks and update references
Object.entries(blockValues).forEach(([subBlockId, value]) => { 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 // Use a recursive function to handle all object types
const updatedValue = updateReferences(value, regex, `<${newBlockName}.`) const updatedValue = updateReferences(value, regex, `<${newBlockName}.`)
// Check if the value actually changed // Check if the value actually changed
if (JSON.stringify(updatedValue) !== JSON.stringify(value)) { if (JSON.stringify(updatedValue) !== JSON.stringify(value)) {
updatedWorkflowValues[blockId][subBlockId] = updatedValue updatedBlockValues[subBlockId] = updatedValue
blockHasChanges = true
changedSubblocks.push({ changedSubblocks.push({
blockId, blockId,
subBlockId, subBlockId,
newValue: updatedValue, 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 // Update the subblock store with the new values