Skip to content

feat(canvas): Phase 1 — Canvas core with home view and UX flow#10

Merged
prosdev merged 22 commits intomainfrom
canvas/c1-core
Mar 16, 2026
Merged

feat(canvas): Phase 1 — Canvas core with home view and UX flow#10
prosdev merged 22 commits intomainfrom
canvas/c1-core

Conversation

@prosdev
Copy link
Contributor

@prosdev prosdev commented Mar 16, 2026

Summary

  • Canvas foundation: React Flow-based graph editor with Start, LLM, and End nodes, bezier edges, edge reconnection, and strict connection validation
  • Home view: Graph list with create/rename/delete, save/load via API, and editable graph names
  • Floating toolbar: Stamp-to-place and drag-to-drop node creation with ghost preview, drop-on-edge insertion, and singleton prevention (Start/End buttons disabled when already present, toast feedback)
  • Node config panel: Side sheet with per-node-type config forms (LLM provider/model/prompt/temperature)
  • UI components: Button, Card, Dialog, Sheet, Tooltip, DropdownMenu, Select, Input, Textarea, Toast
  • Infrastructure: Vitest setup, path aliases, Zustand store with graph + UI slices, API client with key auth

Test plan

  • pnpm --filter @graphweave/canvas typecheck passes
  • pnpm --filter @graphweave/canvas test — 156 tests pass (27 test files)
  • Manual: create graph from home, add nodes via toolbar, connect edges, save and reload
  • Manual: Start/End buttons grey out when those nodes exist; toast appears on blocked placement
  • Manual: rename/delete graphs from home list view

🤖 Generated with Claude Code

prosdev and others added 22 commits March 14, 2026 23:11
- Overview: user stories, UI flow diagrams, component tree, Context vs Zustand
  split, layer boundaries, file layout, engineering decisions
- Phase 1.1: Vitest + testing-library, lucide-react, 9 shadcn UI components
  (Button, Input, Select, Textarea, Dialog, Sheet, Sidebar, Tooltip, Card)
- Phase 1.2: BaseNodeShell + Start/LLM/End node presenters with Lucide icons
- Phase 1.3: GraphCanvas container, Sidebar-based Toolbar, connection validation,
  canvas hint, starter template (Start+End pre-placed), graphSlice extensions
- Phase 1.4: NodeConfigPanel using Sheet component, config forms, slide transition
- Phase 1.5: HomeView with GraphCard grid, NewGraphDialog, editable graph name,
  save/load flow, view routing (home/canvas)
- Updated plan READMEs to reflect Phase 5 merged, Phase 1 in progress

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add Vitest + @testing-library/react + jsdom test infrastructure
- Add lucide-react for consistent iconography
- Create 9 shadcn-style UI components: Button, Input, Select, Textarea,
  Dialog (native <dialog>), Sheet (slide-over panel), Sidebar (collapsible),
  Tooltip (CSS-only), Card (with interactive variant)
- Create CanvasContext (selectedNodeId + ReactFlow instance)
- Add @contexts path alias to tsconfig, vite, and vitest configs
- Add canvas types (CanvasNode, CanvasEdge, C1NodeType)
- 33 tests passing across 7 test files

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- BaseNodeShell: shared chrome with Lucide icon, label, type badge,
  configurable handles, selected state highlight
- StartNode: Play icon, green accent, source handle only
- LLMNode: Brain icon, blue accent, both handles, provider/model badge
- EndNode: Square icon, red accent, target handle only
- nodeTypes registry for React Flow
- Node CSS styles: accent borders, selected ring, handle styles,
  pulse animation (for C2 run highlighting)
- 16 tests for node components

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- GraphCanvas: container bridging React Flow <-> Zustand with connection
  validation (no self-connect, no Start as target, no End as source,
  no duplicate edges), drag-end position sync, edge deletion
- Toolbar: Sidebar-based with Lucide icons, tooltips, collapse to icons
- CanvasHint: contextual hint when canvas has <= 2 nodes
- useNodeDrop: handles drag-to-add with node type defaults
- graphSlice: updateNodePosition, addEdge, removeEdge, removeNodes,
  newGraph (starter template with Start+End connected), renameGraph
- Mapping utilities: toRFNode, toNodeSchema, toRFEdge, toEdgeSchema
- 37 new tests (70 total)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- NodeConfigPanel: container using Sheet component, dispatches to
  config forms based on node type with slide transition
- StartNodeConfig: label-only form (dumb presenter)
- LLMNodeConfig: provider, model, system prompt, temperature, max tokens
- EndNodeConfig: label-only form (dumb presenter)
- graphSlice.updateNodeConfig: partial merge for label and config
- Delete node button with Trash2 icon
- 19 new tests (89 total)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- HomeView: graph card grid with empty state illustration and CTA
- GraphCard: dumb presenter with mini-preview dots, relative time
- NewGraphDialog: modal for naming graphs before creation
- CanvasHeader: back button, inline-editable graph name, save button
- uiSlice: view routing (home/canvas), newGraphDialogOpen state
- graphSlice: saveGraph, loadGraph, loadGraphList, deleteGraphById,
  persisted flag for create vs update distinction
- useBeforeUnload: warns on tab close with unsaved changes
- API layer: updateGraph, deleteGraph, listGraphs pagination unwrap
- App.tsx: conditional rendering of HomeView or CanvasView
- 30 new tests (119 total across 23 test files)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- API layer: match backend's {name, schema_json} request shape and
  GraphResponse format. Add toGraphSchema converter for responses.
- GraphCanvas: replace direct Zustand-driven RF state with useReducer.
  RF owns visual state (drag positions, selection, dimensions) locally
  via applyNodeChanges/applyEdgeChanges. Meaningful events (drag end,
  connect, delete) sync back to Zustand as source of truth. This
  prevents stale-closure bugs during rapid RF interactions.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Indigo theme: replace blue with indigo across all components, nodes,
  handles, focus rings, pulse animation, and button primary variant
- Design tokens: add @styles/tokens.ts as single source of truth for
  brand, node accent, surface, and feedback colors
- IconButton: new rounded icon button component used in Dialog and Sheet
  close buttons with cursor-pointer
- Toolbar: restructure as Card-based layout with icon, label, description,
  accent border, "Nodes" section header. Collapsed mode shows icon cards.
- Dialog: center on page, remove border, use IconButton for close
- NewGraphDialog: update title to "Create a new graph"
- Button: add cursor-pointer to all variants
- Add @Styles path alias to tsconfig, vite, and vitest configs

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Remove redundant type badges (START/LLM/END) — icon + accent is enough
- Add tinted icons matching node accent colors
- Compact nodes: reduce min-width to 120px, add depth shadow
- Bezier edges with arrowhead markers showing flow direction
- Edge reconnection: drag existing edge handle to rewire connections
- Handle hover: scale up and accent-color on hover for discoverability
- Prevent duplicate Start/End nodes in useNodeDrop
- Edge hover: highlight in indigo on hover/select

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- api client: send X-API-Key from VITE_API_KEY env var, parse error
  detail from response body
- Toast component: slide-in notification with auto-dismiss, replaces
  inline error text next to Save button
- Drop-on-edge: dropping a node near an existing edge splits it,
  inserting the new node between source and target
- Handle hover: subtle brightness change instead of jarring purple

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Switch from ConnectionMode.Loose to Strict so only source→target
connections are allowed and valid handles highlight during drag.
Add custom SnapConnectionLine component to match edge stroke styling.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add dropdown menu on GraphCard with Delete and Rename options. Delete
opens a confirmation dialog; Rename opens a modal with pre-filled input.
Refactor Card to use CardHeader/CardMedia/CardContent/CardFooter structure.
Menu appears on card hover only. Add danger Button variant and
DropdownMenu component. Add renameGraphById to store.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Remove isPanningRef pan-vs-click disambiguation that blocked all stamp
placements (onPaneClick already only fires for clicks, not pans).
Center floating toolbar vertically on left edge instead of bottom.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Disable singleton node buttons in the toolbar when they already exist
on the canvas, and show an info toast when placement is blocked via
drag-drop or stamp mode. Toast auto-dismisses after 3 seconds.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add floating toolbar with stamp-to-place and drag-to-drop node creation,
stamp ghost preview, canvas routing, API key auth client, and remove
obsolete Sidebar/Toolbar components replaced by the new FloatingToolbar.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Move API key injection to Vite proxy (server-side only) so secrets
never reach the browser bundle. Fix header spread order so caller
options can override defaults.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add spliceEdge action to graphSlice that removes old edge and adds
node + two new edges in a single set() call. Read nodes/edges via
getState() inside placeNode to avoid stale closure captures.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace the inner try/catch that silently retried createGraph on 404
with a simple persisted ternary. Add ApiError with status property
for structured error handling. Show toast on save failure for
immediate user feedback.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
FloatingToolbar already handles Escape for both stamp clearing and
toolbar collapse. The GraphCanvas handler was redundant and could
fire out of order.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Wrap id with encodeURIComponent in getGraph, updateGraph, and
deleteGraph to prevent path traversal if IDs contain special chars.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- CanvasHeader: replace local toast state with useUIStore.showToast
  so all error toasts go through the global toast system
- NodeConfigPanel: replace unsafe `as unknown as` cast with isLLMNode
  type guard for proper type narrowing
- FloatingToolbar: remove memo wrapper — it reads two stores on every
  render so memo provides no benefit

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@prosdev prosdev merged commit 3c85f0a into main Mar 16, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant