-
Notifications
You must be signed in to change notification settings - Fork 3.3k
feat(canvas): added the ability to lock blocks #3102
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: staging
Are you sure you want to change the base?
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
Resolved conflicts: - Removed addBlock from store (now using batchAddBlocks) - Updated lock tests to use test helper addBlock function - Kept both staging's optimization tests and lock feature tests Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
When duplicating a block that's inside a locked loop/parallel, the duplicate is now placed outside the container since nothing should be added to a locked container. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Server-side workflow duplication now sets locked: false for all blocks - regenerateWorkflowStateIds also unlocks blocks for templates - Client-side regenerateBlockIds already handled this (for paste/import) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Greptile OverviewGreptile SummaryThis PR implements a comprehensive block locking feature for workflows, allowing admins to lock blocks to prevent accidental modifications. Key Changes
Implementation QualityThe implementation is thorough and well-tested with comprehensive unit tests covering edge cases. The feature integrates cleanly with existing systems (undo/redo, collaborative editing, socket operations). The cascading lock behavior for containers ensures consistency. Confidence Score: 4/5
Important Files Changed
Sequence DiagramsequenceDiagram
participant User
participant UI as Action Bar/Context Menu
participant Hook as useCollaborativeWorkflow
participant Store as WorkflowStore
participant Socket as Socket Operations
participant DB as Database
participant UndoRedo as Undo/Redo System
User->>UI: Click Lock/Unlock button
UI->>Hook: collaborativeBatchToggleLocked(blockIds)
Hook->>Store: Get current block states
Store-->>Hook: Return blocks with locked states
Hook->>Hook: Collect blocks + children (if container)
Hook->>Hook: Build previousStates map
Hook->>Socket: addToQueue(BATCH_TOGGLE_LOCKED)
Hook->>Store: batchToggleLocked(blockIds)
Store->>Store: Toggle locked state for blocks
Store->>Store: If container, cascade to all children
Store-->>Hook: State updated
Hook->>UndoRedo: recordBatchToggleLocked(blockIds, previousStates)
UndoRedo-->>Hook: Operation recorded
Socket->>DB: Update locked column in workflow_blocks
DB-->>Socket: Update confirmed
Socket-->>UI: State synchronized
Note over User,DB: Locked blocks prevent:
Note over User,DB: - Dragging (draggable=false)
Note over User,DB: - Deletion (filtered out)
Note over User,DB: - Connection changes (blocked)
Note over User,DB: - Editing (canEditBlock=false)
Note over User,DB: - Enable/disable toggle
Note over User,DB: - Handle orientation change
User->>UI: Try to drag locked block
UI->>UI: Check block.locked
UI-->>User: Show notification (cannot drag)
User->>UI: Try to delete locked block
UI->>UI: Filter out locked blocks
UI-->>User: Show notification (cannot delete)
User->>UI: Duplicate locked block
UI->>Store: duplicateBlock(id)
Store->>Store: Check if parent is locked
alt Parent is locked
Store->>Store: Place duplicate outside container
Store->>Store: Remove parentId from duplicate
else Parent not locked
Store->>Store: Place duplicate with offset
end
Store->>Store: Set locked=false on duplicate
Store-->>UI: New unlocked block created
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
7 files reviewed, 1 comment
apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/action-bar/action-bar.tsx
Outdated
Show resolved
Hide resolved
...sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/editor.tsx
Show resolved
Hide resolved
apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/action-bar/action-bar.tsx
Show resolved
Hide resolved
apps/sim/app/workspace/[workspaceId]/w/[workflowId]/workflow.tsx
Outdated
Show resolved
Hide resolved
- Fix toggle enabled using first toggleable block, not first block - Delete button now checks isParentLocked - Lock button now has disabled state - Editor lock icon distinguishes block vs parent lock state Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Editor: can't unlock block if parent container is locked - Action bar: can't unlock block if parent container is locked - Shows "Parent container is locked" tooltip in both cases Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Block Menu, Editor, Action Bar now all have identical behavior: - Enable/Disable: disabled when locked OR parent locked - Flip Handles: disabled when locked OR parent locked - Delete: disabled when locked OR parent locked - Remove from Subflow: disabled when locked OR parent locked - Lock: always available for admins - Unlock: disabled when parent is locked (unlock parent first) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Same pattern as lock: must enable parent container first before enabling children inside it. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Added documentation for the lock/unlock block feature (admin only). Note: Image placeholder added, pending actual screenshot. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/block-menu/block-menu.tsx
Show resolved
Hide resolved
Paste creates new blocks, doesn't modify selected ones. Changed from disableEdit (includes lock state) to !userCanEdit (permission only), matching the Duplicate action behavior. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
…tion Add isEdgeProtected, filterUnprotectedEdges, and hasProtectedBlocks utilities. Refactor workflow.tsx to use these helpers for: - onEdgesChange edge removal filtering - onConnect connection prevention - onNodeDragStart drag prevention - Keyboard edge deletion - Block menu disableEdit calculation Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
apps/sim/app/workspace/[workspaceId]/w/[workflowId]/utils/block-protection-utils.ts
Outdated
Show resolved
Hide resolved
1. Store batchToggleEnabled now uses continue to skip locked blocks entirely, matching database operation behavior 2. Copilot add operation now checks if parent container is locked before adding nested nodes (defensive check for consistency) 3. Remove unused filterUnprotectedEdges function Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- insert_into_subflow: Check if existing block being moved is locked - extract_from_subflow: Check if block or parent subflow is locked These operations now match the UI behavior where locked blocks cannot be moved into/out of containers. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
apps/sim/app/workspace/[workspaceId]/w/[workflowId]/workflow.tsx
Outdated
Show resolved
Hide resolved
…BlockIds 1. regenerateBlockIds now checks if existing parent is locked before keeping the block inside it. If parent is locked, the duplicate is placed outside (parentId cleared) instead of creating an inconsistent state. 2. Remove unnecessary effectivePermissions.canAdmin and potentialParentId from onNodeDragStart dependency array. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
apps/sim/app/workspace/[workspaceId]/w/[workflowId]/workflow.tsx
Outdated
Show resolved
Hide resolved
1. BATCH_TOGGLE_LOCKED now uses first block from blocksToToggle set instead of blockIds[0], matching BATCH_TOGGLE_ENABLED pattern. Also added early exit if blocksToToggle is empty. 2. Blocks inside locked containers are now properly non-draggable. Changed draggable check from !block.locked to use isBlockProtected() which checks both block lock and parent container lock. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Both edit and delete operations now check if the block's parent container is locked, not just if the block itself is locked. This ensures consistent behavior with the UI which uses isBlockProtected utility that checks both direct lock and parent lock. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1. BATCH_TOGGLE_LOCKED now requires admin role - non-admin users with write role can no longer bypass UI restriction via direct socket messages 2. BATCH_REMOVE_BLOCKS now validates lock status server-side - filters out protected blocks (locked or inside locked parent) before deletion 3. Remove duplicate/outdated comment in regenerateBlockIds Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
batch-toggle-locked is now admin-only, so write role should be denied. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The redo logic for BATCH_TOGGLE_ENABLED and BATCH_TOGGLE_LOCKED was incorrectly computing each block's new state as !previousStates[blockId]. However, the store's batchToggleEnabled/batchToggleLocked set ALL blocks to the SAME target state based on the first block's previous state. Now redo computes targetState = !previousStates[firstBlockId] and applies it to all blocks, matching the store's behavior. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
apps/sim/app/workspace/[workspaceId]/w/[workflowId]/workflow.tsx
Outdated
Show resolved
Hide resolved
Based on audit findings, adds lock validation to multiple operations: 1. BATCH_TOGGLE_HANDLES - now skips locked/protected blocks at: - Store layer (batchToggleHandles) - Collaborative hook (collaborativeBatchToggleBlockHandles) - Server socket handler 2. BATCH_ADD_BLOCKS - server now filters blocks being added to locked parent containers 3. BATCH_UPDATE_PARENT - server now: - Skips protected blocks (locked or inside locked container) - Prevents moving blocks into locked containers All validations use consistent isProtected() helper that checks both direct lock and parent container lock. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
contextMenuBlocks already has locked and isParentLocked properties computed in use-canvas-context-menu.ts, so there's no need to look up blocks again via hasProtectedBlocks. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
Bugbot Autofix is OFF. To automatically fix reported issues with Cloud Agents, enable Autofix in the Cursor dashboard.
| blocks: Record<string, BlockState> | ||
| ): boolean { | ||
| return blockIds.some((id) => isBlockProtected(id, blocks)) | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Exported function hasProtectedBlocks is never used
Low Severity
The hasProtectedBlocks function is exported but never used anywhere in the codebase. The grep search found only its definition (line 82) with no imports or usages. Other functions from this file (isBlockProtected, isEdgeProtected, filterProtectedBlocks) are all used in workflow.tsx, but this one is dead code.


Summary
Type of Change
Testing
Tested manually
Checklist