From cf67df2f8c1f2f981217cffdd504c12cd52b7182 Mon Sep 17 00:00:00 2001 From: Legacynnn Date: Thu, 25 Jun 2026 21:49:00 -0300 Subject: [PATCH] feat(plan-canvas): freeform figma-style prototyping surface + resilient parsing Rework from an auto-laid mind-map into a pan/zoom figma board: agent-positioned frames embedding live previews, greyscale wireframes, and sticky notes, wired with labeled arrows and grouped via . Legacy connects=-only plans still render via auto-layout. Also harden plan MDX parsing: strip leaked agent tool-call wrapper tags (e.g. a stray trailing from a Write call) before parsing, so one stray closing tag no longer drops the whole plan to plain Markdown and blanks the canvas. --- .changeset/figma-plan-canvas.md | 8 + src-tauri/src/agents/system_prompt.rs | 8 +- .../components/canvas/auto-layout.test.ts | 84 ++++++ .../components/canvas/auto-layout.ts | 125 +++++++++ .../components/canvas/build-graph.test.ts | 217 +++++++++++----- .../components/canvas/build-graph.ts | 240 +++++++++++++----- .../components/canvas/canvas-node.tsx | 73 ------ .../components/canvas/design-toolbar.tsx | 34 +++ .../components/canvas/flow-edge.tsx | 113 +++++++++ .../components/canvas/frame-kinds.test.ts | 60 +++++ .../components/canvas/frame-kinds.ts | 75 ++++++ .../components/canvas/frames/frame-chrome.tsx | 67 +++++ .../components/canvas/frames/group-node.tsx | 39 +++ .../components/canvas/frames/note-frame.tsx | 26 ++ .../canvas/frames/preview-frame.test.tsx | 37 +++ .../canvas/frames/preview-frame.tsx | 20 ++ .../canvas/frames/wireframe-frame.tsx | 30 +++ .../plan-viewer/components/canvas/index.tsx | 19 +- .../components/canvas/layout.test.ts | 74 ------ .../plan-viewer/components/canvas/layout.ts | 42 --- .../components/canvas/node-kinds.test.ts | 20 -- .../components/canvas/node-kinds.ts | 35 --- .../canvas/plan-canvas-render.test.tsx | 59 +++++ .../components/canvas/plan-canvas-surface.tsx | 158 ++++++++++-- .../components/canvas/plan-canvas.test.tsx | 12 +- .../components/canvas/theme-modes.test.ts | 33 +++ .../components/canvas/theme-modes.ts | 96 +++++++ src/features/plan-viewer/mdx/parse.test.ts | 134 ++++++++++ src/features/plan-viewer/mdx/parse.ts | 10 +- src/features/plan-viewer/mdx/registry.tsx | 8 + .../plan-viewer/mdx/strip-leaked-tags.test.ts | 71 ++++++ .../plan-viewer/mdx/strip-leaked-tags.ts | 57 +++++ 32 files changed, 1675 insertions(+), 409 deletions(-) create mode 100644 .changeset/figma-plan-canvas.md create mode 100644 src/features/plan-viewer/components/canvas/auto-layout.test.ts create mode 100644 src/features/plan-viewer/components/canvas/auto-layout.ts delete mode 100644 src/features/plan-viewer/components/canvas/canvas-node.tsx create mode 100644 src/features/plan-viewer/components/canvas/design-toolbar.tsx create mode 100644 src/features/plan-viewer/components/canvas/flow-edge.tsx create mode 100644 src/features/plan-viewer/components/canvas/frame-kinds.test.ts create mode 100644 src/features/plan-viewer/components/canvas/frame-kinds.ts create mode 100644 src/features/plan-viewer/components/canvas/frames/frame-chrome.tsx create mode 100644 src/features/plan-viewer/components/canvas/frames/group-node.tsx create mode 100644 src/features/plan-viewer/components/canvas/frames/note-frame.tsx create mode 100644 src/features/plan-viewer/components/canvas/frames/preview-frame.test.tsx create mode 100644 src/features/plan-viewer/components/canvas/frames/preview-frame.tsx create mode 100644 src/features/plan-viewer/components/canvas/frames/wireframe-frame.tsx delete mode 100644 src/features/plan-viewer/components/canvas/layout.test.ts delete mode 100644 src/features/plan-viewer/components/canvas/layout.ts delete mode 100644 src/features/plan-viewer/components/canvas/node-kinds.test.ts delete mode 100644 src/features/plan-viewer/components/canvas/node-kinds.ts create mode 100644 src/features/plan-viewer/components/canvas/plan-canvas-render.test.tsx create mode 100644 src/features/plan-viewer/components/canvas/theme-modes.test.ts create mode 100644 src/features/plan-viewer/components/canvas/theme-modes.ts create mode 100644 src/features/plan-viewer/mdx/strip-leaked-tags.test.ts create mode 100644 src/features/plan-viewer/mdx/strip-leaked-tags.ts diff --git a/.changeset/figma-plan-canvas.md b/.changeset/figma-plan-canvas.md new file mode 100644 index 000000000..636d597fe --- /dev/null +++ b/.changeset/figma-plan-canvas.md @@ -0,0 +1,8 @@ +--- +"helmor": minor +--- + +Reworked the plan canvas into a freeform, figma-style prototyping surface and hardened plan parsing against leaked tool-call tags. + +- `` is now a pan/zoom board of agent-positioned frames that embed live previews, greyscale wireframes, and sticky notes, wired together with labeled flow arrows and grouped into labeled sections. Older `connects=`-only mind-map plans still render via an auto-layout fallback, so nothing breaks. +- Plans that accidentally contain leaked agent tool-call wrapper tags (e.g. a stray trailing `` from a Write call) no longer collapse to plain text. The parser strips the stray tags before parsing so the canvas and other rich plan components keep rendering. diff --git a/src-tauri/src/agents/system_prompt.rs b/src-tauri/src/agents/system_prompt.rs index 207f34e0c..27f5d9a2d 100644 --- a/src-tauri/src/agents/system_prompt.rs +++ b/src-tauri/src/agents/system_prompt.rs @@ -246,8 +246,10 @@ Allowed components: - `` containing a markdown list of decisions that need the user's input. - `` … code … `` for an annotated snippet. PREFER passing the code as the component's children (between the tags) — especially when it contains quotes or `<…>`. Only use a `code="…"` attribute for short, quote-free one-liners. NEVER put backslash-escaped quotes (`\"`) inside an attribute value: JSX attributes have no backslash escaping, so a value like `code=""` is invalid markup and will fail to render. - `` containing mermaid diagram source (no code fences) for architecture or data flow. - - `` containing `` children — an interactive node graph shown at the TOP of the plan. ALWAYS lead a non-trivial plan with one. It must map the ACTUAL logic of THIS task — its real subsystems/modules/files/steps and how they genuinely depend on or flow into each other (use `connects` for real edges: what calls/feeds/blocks what, not decorative links). Do NOT emit a generic five-box "overview/structure/visuals/decisions" diagram — that conveys nothing. Pick `kind` to match each node's real role (see below) so the graph is legible. Use `direction="LR"` for a pipeline/flow and `"TB"` for a hierarchy. Keep it focused (roughly 3–8 nodes) but every node and edge should carry real meaning. Do NOT close the graph into a loop — never wire the last node back to the first/overview node just to "complete" it; only add an edge where a real dependency exists. The graph should stay acyclic. - - `` … short markdown … `` — one box in the PlanCanvas. `id` must be unique; `connects` is a comma-separated list of other node ids this box links to. Keep the body to a sentence or a short list. Use a self-closing `` when there is no body. CanvasNode is ONLY valid inside a PlanCanvas. Set `kind` to the node's real role — it drives a coloured badge + icon: `resume` = the overview/summary node, `phase` = a sequenced step/milestone, `option` = an alternative/branch, `wireframe` = a UI/screen node, `note` = plain detail (the default). Choose deliberately so the badges read as a real map. + - `` containing `` / `` / `` children — a freeform, figma-style board shown at the TOP of the plan. ALWAYS lead a non-trivial UI/design plan with one. You place frames at explicit coordinates, fill each with a real live preview, a low-fi wireframe, or a sticky note, and wire the user journey with labeled flow arrows. `theme="repo"` (default) renders polished, accent-colored frames; `theme="wireframe"` renders the whole board greyscale to focus on layout over color. `height` is the board's pixel height (optional, default 720 — go taller for a busy board). Frames float freely on the open canvas (no enclosing card), so lay them out left-to-right along the primary journey — flows enter a frame on its left edge and leave on its right — and leave GENEROUS gaps between frames (roughly 80–160px of empty space, more between groups) so the board reads as a spacious canvas, not a tight cluster. Group related frames with ``. Keep it focused (roughly 3–10 frames) but every frame and arrow should carry real meaning. Cycles ARE allowed here: a real user journey may loop back. (A legacy `` with `` and no coordinates still renders via auto-layout, but prefer the coordinate-based board.) + - `` … one `` OR one `` OR short markdown … `` — one frame on the board. `id` must be unique. `x`/`y` set the top-left position and `w`/`h` the size (all optional — omit them to auto-layout, but for a figma board set them so frames sit where you intend). The frame's kind is inferred from its body: a nested `` makes a LIVE preview frame (high-fidelity, runs live on the canvas), a nested `` makes a low-fi wireframe frame, and anything else is a sticky note for rationale. `device` picks the window/device chrome; `accent` colors the frame in `repo` theme. Self-close `` for an empty placeholder frame. CanvasNode is ONLY valid inside a PlanCanvas. + - `` — a labeled arrow between two frames marking a user-flow transition. `kind` styles the line: `primary` is a solid accent path (the happy path), `secondary` and `back` are quieter dashed lines (branches / return steps). Self-closing. CanvasFlow is ONLY valid inside a PlanCanvas. + - `` — a labeled section drawn BEHIND the listed frames to group a flow or area. `contains` is a comma-separated list of frame ids; the section auto-sizes to enclose them. Self-closing. CanvasGroup is ONLY valid inside a PlanCanvas. - `` containing `` children — present 2–4 candidate approaches as cards and mark the best one with the boolean `recommended` attribute. `