feat(ui): build node palette sidebar with drag-and-drop onto workflow canvas#1112
feat(ui): build node palette sidebar with drag-and-drop onto workflow canvas#1112geoffjay wants to merge 2 commits intoissue-1100from
Conversation
|
This change is part of the following stack:
Change managed by git-spice. |
geoffjay
left a comment
There was a problem hiding this comment.
Review: feat(ui): build node palette sidebar with drag-and-drop onto workflow canvas
LGTM — cannot self-approve, posting as comment.
Stack note
This branch (issue-1099) is stacked on PR #1111 (issue-1100: "feat(ui): implement graph-to-API serialization for workflow canvas save/load"), which also carries review-agent. #1111 should be reviewed first. The chain above this PR — #1113 (needs-rework) and #1114 (merge-queue) — is blocked until #1113's edit-mode bug is fixed, but that doesn't affect this PR's code quality.
What was verified
- Drag transfer data:
encodeDragData/decodeDragDataroundtrip viaJSON.stringify/JSON.parse;PALETTE_DRAG_KEY = "application/agentd-palette"is a namespaced MIME type — correct for cross-origin drag safety. ✓ ALL_TRIGGER_TYPES: 13 entries, matching theTriggerConfigdiscriminated union inorchestrator.ts(github_issues, github_pull_requests, linear_issues, webhook, cron, delay, agent_lifecycle, agent_idle, dispatch_result, ask_response, manual, queue, composite). ✓TRIGGER_ICONScompleteness: one icon per type, with?? Handfallback in the render. ✓- Disabled state:
draggable={!disabled}+onDragStart={!disabled ? handleDragStart : undefined}— both attributes gated, non-running agents are non-draggable. ✓ groupedTriggersmemo: dep is[searchLower], correctly recomputes on search change. ✓filteredAgentsmemo: dep is[agents, searchLower]. ✓- Collapse: controlled path:
collapsed = collapsedProp ?? internalCollapsed— controlled prop takes precedence; uncontrolled falls through to internal state. ✓ - Exports:
NodePalette,NodePaletteProps,PaletteDragData,PALETTE_DRAG_KEY,encodeDragData,decodeDragDataall re-exported fromcanvas/index.ts. ✓ - Test coverage: 22 tests across rendering, collapse/expand, search filtering (4 cases including case-insensitive), drag data correctness (trigger + agent), disabled agent, empty agents. ✓
- Drag tests use
fireEvent: appropriate —userEventdoes not support HTML5 drag in jsdom. ✓
Non-blocking suggestions
1. Doc comment says "14 types" — should be 13
The file-level JSDoc says "all 14 types" and orchestrator.ts also has "covers all 14 backend trigger variants". Both are off by one — the actual union has 13 members and the test asserts exactly 13. Updating both comments to "13" would eliminate confusion if a 14th type is added later and someone wonders whether the palette is missing it.
2. Controlled/uncontrolled state drift in toggleCollapsed
function toggleCollapsed() {
const next = !collapsed;
setInternalCollapsed(next); // ← always runs, even in controlled mode
onCollapsedChange?.(next);
}When collapsedProp is provided, internalCollapsed still advances, though it has no visible effect (the controlled prop shadows it). If the parent later stops providing collapsedProp, the palette would snap to the stale internal value. The standard fix is to only update internal state when uncontrolled:
function toggleCollapsed() {
const next = !collapsed;
if (collapsedProp === undefined) setInternalCollapsed(next);
onCollapsedChange?.(next);
}Non-blocking — the current behaviour is correct for all documented use cases.
3. decodeDragData has no structural validation
JSON.parse(raw) as PaletteDragData | null is a TypeScript-only cast; the parsed object is not validated at runtime. If the drop handler receives malformed data (e.g., type: "agent" with no agentId), downstream code in WorkflowBuilder.handleDrop would receive undefined where a string is expected. Since this is a local UI and drag data is always set by encodeDragData, the risk is low — but a simple guard (if (!decoded || !('type' in decoded)) return null) would make the function defensive. Non-blocking.
…canvas - Categorised trigger list (External, Schedules, Events, Internal) with icons - Agents section from live API data — running agents draggable, others grayed out - HTML5 drag-and-drop: sets PALETTE_DRAG_KEY transfer data for canvas onDrop - Search/filter input (case-insensitive, matches triggers and agents) - Collapse/expand toggle (controlled + uncontrolled modes) - PaletteDragData type + encode/decode helpers exported for canvas integration - 21 tests: rendering, collapse, search, drag data, disabled states
Adds collapsible NodePalette sidebar with all 13 trigger types grouped by category, agent list, search filtering, and HTML5 drag-and-drop with encoded transfer data.
Closes #1099