fix(web): allow deleting nodes from Workflow Builder (#971) #1113

PR
PR description

Summary

  • Problem: Nodes added to the Workflow Builder canvas cannot be deleted — Delete/Backspace keys do nothing after drag-and-drop, no right-click context menu exists, and the Delete Node button is either hidden below the viewport (Prompt/Command) or entirely absent (Bash nodes).
  • Why it matters: Bash nodes are impossible to remove through any UI mechanism once placed. Other node types require obscure scrolling to find the delete button.
  • What changed: Auto-select dropped nodes, added right-click context menu, moved Delete button to inspector header, added Backspace key support.
  • What did not change (scope boundary): No new dependencies, no changes to workflow execution, no backend changes. Purely frontend UX fix in @archon/web.

UX Journey

Before

User                        Workflow Builder             ReactFlow
────                        ────────────────             ─────────
drags Prompt node ────────▶ onDrop() fires
                            creates node
                            [X] never calls onNodeSelect()
                            selectedNodeId stays null
presses Delete ───────────▶ useBuilderKeyboard
                            [X] if (!selectedNodeId) return
                            (silent no-op)
right-clicks node ────────▶ [X] no onContextMenu handler
                            (no menu rendered)
opens inspector ──────────▶ NodeInspector renders
navigates to Advanced tab ▶ Delete button present
                            [X] button below viewport
                            (requires scroll to discover)

drags Bash node ──────────▶ onDrop() fires, creates node
opens inspector ──────────▶ NodeInspector renders
                            isBash = true
                            [X] Advanced tab hidden
                            Delete button absent from DOM
user cannot delete ◀─────── zero UI mechanism for deletion

After

User                        Workflow Builder             ReactFlow
────                        ────────────────             ─────────
drags any node ───────────▶ onDrop() fires
                            creates node
                            *calls onNodeSelect(id)*
                            selectedNodeId = new node
presses Delete/Backspace ─▶ useBuilderKeyboard
                            *selectedNodeId is set*
                            *node deleted* ✓
right-clicks node ────────▶ *handleNodeContextMenu*
                            *context menu rendered*
clicks "Delete node" ─────▶ *onNodeDelete(nodeId)*
                            *node deleted* ✓
opens inspector ──────────▶ NodeInspector renders
                            *Delete button in header*
                            *visible for ALL types* ✓
clicks header Delete ─────▶ *node deleted* ✓

Architecture Diagram

Before

WorkflowBuilder
├── WorkflowCanvas (onDrop creates node, no select)
│   └── ReactFlow (no onNodeContextMenu)
├── NodeInspector
│   └── AdvancedTab (Delete button here, hidden for bash)
└── useBuilderKeyboard (Delete key only, needs selectedNodeId)

After

WorkflowBuilder
├── WorkflowCanvas (onDrop creates node + [~] auto-selects)
│   ├── ReactFlow ([+] onNodeContextMenu)
│   └── [+] Context menu div (Delete node button)
├── NodeInspector
│   ├── [~] Header (Delete button moved here, all node types)
│   └── AdvancedTab ([-] Delete button removed)
└── useBuilderKeyboard ([~] Delete + Backspace keys)

Connection inventory:

From To Status Notes
WorkflowBuilder WorkflowCanvas modified New onNodeDelete prop
WorkflowCanvas ReactFlow modified Added onNodeContextMenu handler
WorkflowCanvas Context menu new Inline positioned div with delete action
WorkflowBuilder NodeInspector unchanged onDelete prop unchanged
NodeInspector AdvancedTab modified Removed onDelete prop from AdvancedTab
useBuilderKeyboard WorkflowBuilder modified Now handles Backspace alongside Delete

Label Snapshot

  • Risk: risk: low
  • Size: size: S
  • Scope: web
  • Module: web:WorkflowCanvas, web:NodeInspector, web:WorkflowBuilder, web:useBuilderKeyboard

Change Metadata

  • Change type: bug
  • Primary scope: web

Linked Issue

  • Closes #971

Validation Evidence (required)

bun run type-check  ✅ clean across all 9 packages
bun run lint        ✅ clean (--max-warnings 0)
bun run format:check ✅ all source files pass (only HANDOFF.md flagged — not part of PR)
bun run test        ✅ all tests pass across all 9 packages
  • Evidence provided: Full bun run validate output and manual browser testing
  • If any command is intentionally skipped, explain why: N/A — all passed

Security Impact (required)

  • New permissions/capabilities? No
  • New external network calls? No
  • Secrets/tokens handling changed? No
  • File system access scope changed? No

Compatibility / Migration

  • Backward compatible? Yes
  • Config/env changes? No
  • Database migration needed? No

Human Verification (required)

What was personally validated beyond CI:

  • Verified scenarios:
    • Drag Prompt node → press Delete → node removed
    • Drag Prompt node → press Backspace → node removed
    • Drag Command node → right-click → "Delete node" → node removed
    • Drag Bash node → press Delete → node removed
    • Drag Bash node → open inspector → header Delete button visible → click → node removed
    • Right-click → Escape → menu dismissed, node remains
    • Right-click → click canvas → menu dismissed, node remains
    • Type in NodeInspector textarea → Backspace edits text (does NOT delete node)
    • Delete connected node → edges also removed
  • Edge cases checked:
    • Backspace in text fields does not trigger node deletion (isInputTarget guard)
    • Context menu positioned correctly and doesn't overflow viewport
    • Dark mode styling uses existing Tailwind tokens
  • What was not verified: Automated component tests (no Vitest/RTL setup exists in @archon/web)

Side Effects / Blast Radius (required)

  • Affected subsystems/workflows: Workflow Builder UI only
  • Potential unintended effects: None — changes are isolated to 4 frontend files, no backend or workflow engine impact
  • Guardrails/monitoring for early detection: Type-check + lint + existing unit tests all pass

Rollback Plan (required)

  • Fast rollback command/path: git revert <commit-sha> — single atomic commit
  • Feature flags or config toggles (if any): None
  • Observable failure symptoms: Node deletion not working in the Workflow Builder (same as current state before fix)

Risks and Mitigations

  • Risk: e.preventDefault() on Backspace could cause unexpected behavior if focus escapes a text input
    • Mitigation: isInputTarget() guard in useBuilderKeyboard checks for input, textarea, and [contenteditable] before the keydown handler fires
  • Risk: Inline context menu styling may look off in certain themes
    • Mitigation: Uses existing project Tailwind tokens (bg-surface-elevated, border-border, text-error) consistent with the rest of the UI

Summary by CodeRabbit

  • New Features

    • Right-click context menu on the workflow canvas to delete nodes.
    • Prominent Delete button moved to the inspector header (visible for all node types).
    • Newly created nodes are auto-selected for immediate editing.
  • Bug Fixes / Improvements

    • Backspace now works alongside Delete to remove selected nodes.
    • Removed duplicate Delete control from the Advanced tab.
  • Documentation

    • Workflow Builder docs updated to describe node deletion options.
CUT
cutter bot commented just now

Cutter Summary

On the before side none of the captured canvases show a node inspector, a header delete control, or a right-click context menu after a node is placed; on the after side the dropped node is selected, the inspector opens with a delete control in its header, and right-clicking a node surfaces a delete menu item, including for Bash nodes.