Files
markdown-converter/docs/superpowers/specs/2026-09-14-flowchart-editor-design.md
amitwh 43c26c6521 docs(specs): add theme-registry + ascii-art-upgrade + flowchart-editor designs
Three new specs for the v4.8.0+ feature wave:

* Theme registry: replace hardcoded 25-theme menu in main.js with a pure
  ThemeRegistry module + per-theme CSS file convention. Add 12 new themes
  (Catppuccin x4, One Light, Tokyo Night Storm, Synthwave '84, Outrun,
  Winter is Coming Light+Dark, Solarized Dark HC, Spring Light).
* ASCII art upgrade: consolidate dual implementations (standalone window
  vs dead in-app modal) into a single path; add 12 hand-coded fonts +
  figlet npm library for 400+ fonts; add copy/save/insert output
  destinations; comprehensive tests for the previously-zero-coverage
  textToASCII/createASCIIBox/getASCIITemplate machinery.
* Flow chart editor: sidebar panel with hand-rolled SVG canvas, node-graph
  data model, drag/drop editing, live Mermaid source preview, undo/redo,
  session persistence. Emits Mermaid which the existing preview pane
  already renders natively.

Amit Haridas
2026-09-14 19:01:09 +05:30

16 KiB
Raw Permalink Blame History

MarkdownConverter — Flow Chart Editor Design

Date: 2026-09-14 Status: Draft — pending review Author: Amit Haridas

Overview

Add a sidebar-panel flow chart editor that lets users build flowcharts visually (drag nodes, connect edges, edit labels in-place) without writing Mermaid syntax by hand. The editor produces Mermaid flowchart source, which the existing preview pane already renders natively. Insert at cursor writes a ```mermaid fenced block. The feature is purely renderer-side — no main-process changes needed because Mermaid is already a bundled dependency.

Goals

  1. Visual node editor. Click to add nodes (5 shapes: process, decision, terminator, subroutine, document). Drag to move. Double-click to edit label inline. Right-click for shape submenu.
  2. Edge editor. Drag from node edge handle to another node to connect. Click edge to set type (solid arrow, dotted arrow, thick arrow) and add a label.
  3. Live Mermaid preview. Right pane of the sidebar panel shows the generated Mermaid source and re-renders the actual SVG on every change (debounced 250 ms). User can copy the source or insert it at the cursor.
  4. Undo / redo. Ctrl+Z / Ctrl+Shift+Z within the panel. Snapshot-based, bounded depth (50 steps).
  5. Persistence. The current flowchart is auto-saved to <userData>/flowchart-session.json so reopening the panel restores the user's work.
  6. Comprehensive tests. Pure-data module (flowchart-store.js) and the Mermaid translator (flowchart-mermaid.js) are fully unit-tested. The canvas (flowchart-canvas.js) is DOM-tested with jsdom pointer events.

Non-Goals (v1)

  • No swimlanes / subgraphs (Mermaid supports them; deferring for v2).
  • No collaboration / multi-user editing.
  • No export to PNG/SVG (the preview pane already renders; "Save as SVG" is a thin wrapper that can come later).
  • No import of existing Mermaid source (parse, validate, place into canvas) — defer to v2.
  • No theme sync (the SVG renders in the preview pane, which already respects the active theme).
  • No infinite canvas / zoom-pan — fixed viewport sized to the panel.
  • No copy/paste of nodes between editor instances.

Decisions Locked

Decision Choice Reason
Side location New sidebar panel flowchart Matches the established src/sidebar/<name>-panel.js pattern (search-panel, daily-notes-panel, git-panel)
Canvas technology SVG (no D3, no Konva, no library) Hand-rolled SVG keeps the bundle small; the canvas is bounded (~100 nodes max); drag/drop is straightforward with pointer events
Graph model Pure data structure (nodes + edges) stored in a single flowchart-store.js module Matches the codebase's "pure module + injectable IO" pattern; trivially testable
Node shapes 5 SVG shape templates per type, sized to text width Mermaid supports many shapes; 5 covers 95% of real flowcharts
Edge routing Straight lines between node centers (no orthogonal/Manhattan routing) Simpler; orthogonal routing can come in v2; the visual is still clear
Undo/redo Snapshot stack with bounded depth 50 Predictable memory; matches editor undo conventions
Persistence Auto-save to <userData>/flowchart-session.json on every change (debounced 500 ms) Survives app restart; user doesn't lose work
Mermaid emission Always flowchart TD (top-down) for v1 Top-down is the most common flowchart direction; horizontal can be a per-node option in v2
Debouncing Preview re-render 250 ms; persistence 500 ms Responsive but not jittery
Testing Pure store/mermaid unit tests; canvas DOM tests with jsdom No real-browser testing needed
Accessibility Keyboard shortcuts for add/delete/undo; visible focus rings on nodes; ARIA labels on the SVG The panel is mouse-first but keyboard-accessible

Architecture

   ┌──────────────────────────────────────────┐
   │  src/sidebar/flowchart-panel.js          │
   │  - mounts the panel                      │
   │  - splits left (canvas) | right (preview)│
   │  - wires pointer events to canvas        │
   │  - subscribes to store changes           │
   └──────────┬────────────────┬───────────────┘
              │                │
              ▼                ▼
   ┌──────────────────┐  ┌─────────────────────┐
   │ flowchart-       │  │ flowchart-mermaid.js│
   │ canvas.js        │  │ - toMermaid(graph)  │
   │ - renders SVG    │  │ - validate(shape)   │
   │ - pointer events │  └─────────────────────┘
   │ - hit-testing    │
   └────────┬─────────┘
            │ reads/writes
            ▼
   ┌──────────────────────────────────────────┐
   │ flowchart-store.js  (pure module)        │
   │ - graph { nodes, edges }                 │
   │ - addNode / moveNode / removeNode        │
   │ - connect / disconnect / setEdgeType     │
   │ - undo / redo (snapshot stack)           │
   │ - subscribe(fn) → emits on change        │
   │ - serialize() / deserialize(json)        │
   │ - persistence IO injected                │
   └──────────────────────────────────────────┘
            │
            │ debounced 500 ms write
            ▼
   ┌──────────────────────────────────────────┐
   │ <userData>/flowchart-session.json        │
   └──────────────────────────────────────────┘

New Modules

File Role
src/sidebar/flowchart-panel.js Renderer-only panel. Mounts the canvas + preview; registers keyboard shortcuts; wires the "Insert at Cursor" button.
src/flowchart/flowchart-store.js Pure data module. The graph is { nodes: Node[], edges: Edge[] }. Node: { id, kind, x, y, label }. Edge: { id, fromNodeId, toNodeId, kind: 'solid'|'dotted'|'thick', label? }. Exports create(), addNode, moveNode, setNodeLabel, setNodeKind, removeNode, connect, disconnect, setEdgeKind, setEdgeLabel, undo, redo, subscribe, serialize, deserialize, toJSON. Constructor takes injected IO { persistencePath, readFile, writeFile, now }.
src/flowchart/flowchart-canvas.js Renderer-only SVG canvas. Owns an <svg> element. Renders nodes as <g> containing shape <path>/<rect>/<ellipse> and a <text> label. Renders edges as <line> or <polyline>. Listens for pointer events: drag to move, double-click to edit label, right-click for shape menu. Hit-testing walks the DOM in reverse z-order.
src/flowchart/flowchart-mermaid.js Pure translator. toMermaid(graph) returns the Mermaid flowchart TD source string. Validates each shape against the 5 supported kinds; throws on unknown kind. Handles label escaping (", newlines → \n).
src/flowchart/flowchart-shapes.js Pure module: shapeSvg(kind, x, y, width, height) → string. Defines the 5 SVG shape templates (process=rect, decision=diamond, terminator=stadium, subroutine=rect-with-double-border, document=parallelogram-approx). Pure functions; unit-tested.
tests/flowchart-store.test.js Pure store tests: add/move/connect/disconnect/delete; undo/redo round-trip; subscribe fires once per change; serialize/deserialize round-trip; throws on invalid input.
tests/flowchart-mermaid.test.js Translation tests: each node kind emits the correct Mermaid syntax ([], {}, (()), [[]], []/]); edge kinds (-->, -.->, ==>); label escaping. Snapshot tests for representative graphs.
tests/flowchart-shapes.test.js Each shape function returns SVG that matches the expected viewBox and contains the expected primitive.
tests/flowchart-canvas.test.js jsdom tests: mount canvas with a 3-node graph; assert SVG structure; simulate a pointerdown + pointermove + pointerup; assert store updated with new position.
tests/flowchart-panel.test.js jsdom test: mount the panel; assert canvas + preview panes exist; assert preview re-renders on store change.
tests/preload-flowchart.test.js (No new preload channels — feature is renderer-only.)

Modified Modules

File Change
src/renderer.js:2300 (sidebar registration) Add sidebarManager.registerPanel('flowchart', { title: 'Flow Chart', render: renderFlowChartPanel, icon: '<svg>…flowchart icon…</svg>' }).
src/index.html Add a sidebar rail button with data-panel="flowchart" and the matching icon.
src/styles/sidebar.css (or wherever sidebar styles live) Add minimal layout for the panel's split: display: flex; flex: 1; with left pane ~70% (canvas) and right pane ~30% (preview).
README.md:54-70 (Advanced Features) Add row: "Visual flow chart editor — Build Mermaid flowcharts visually; drag nodes, connect edges, live preview. Insert at cursor."
README.md:120-128 (Keyboard Shortcuts) Add row: | Add Flow Chart Node | Insert (when panel focused) |, | Undo | Ctrl+Z |, | Redo | Ctrl+Shift+Z | (panel-scoped).

Node Shapes

kind Mermaid syntax SVG shape Use case
process A[Label] <rect> Generic step
decision A{Label} <polygon points="…"> (diamond) Yes/no, branch
terminator A([Label]) <rect rx=…> (stadium) Start/end
subroutine A[[Label]] <rect> with double border Named subroutine call
document A[/Label/] <polygon> (parallelogram-approx) Document/file reference

Sizes auto-grow to fit the label text (measure with getComputedTextLength() on the <text> after first render).

Edge Kinds

kind Mermaid syntax SVG style
solid A --> B Solid <line> with marker arrow
dotted A -.-> B stroke-dasharray="4,4"
thick A ==> B stroke-width="3"

Edge labels render as <text> at the midpoint of the edge with a small white background rect for legibility.

Data Flow

  1. User opens the panel via sidebar rail button (data-panel="flowchart").
  2. flowchart-panel.js constructs a flowchart-store.js instance, calls deserialize() with whatever is in <userData>/flowchart-session.json (empty graph if absent).
  3. Panel mounts the canvas SVG and the preview pane side by side.
  4. Canvas subscribes to store changes; on every change, re-renders the SVG and notifies the panel.
  5. Panel debounces (250 ms) and calls flowchart-mermaid.toMermaid(graph); updates the preview pane.
  6. Panel debounces (500 ms) and calls store.serialize() → writes to <userData>/flowchart-session.json via injected writeFile.
  7. User clicks "Insert at Cursor": panel emits insert-content IPC with the Mermaid source wrapped in a fenced code block (same pattern as ASCII art's insert).
  8. User triggers undo/redo: panel sends Ctrl+Z / Ctrl+Shift+Z to the canvas's keyboard handler when the panel is focused (the editor's existing undo is left alone — undo only fires when the panel has focus).

Error Handling

Scenario Handling
Persistence file is corrupt JSON deserialize() catches, logs warning, returns empty graph. User loses prior session but can start fresh.
Persistence write fails (disk full, perms) writeFile rejects; the panel logs the error but continues functioning. The next save attempt retries.
toMermaid() encounters unknown node kind Throws a typed error; the preview pane shows "Invalid graph: "; the canvas still renders. User must fix the kind (e.g. via right-click menu).
toMermaid() label contains newlines Escapes \n to literal \n (Mermaid's escape).
Canvas hit-test misidentifies a node Defensive: the canvas uses data-node-id attribute on every <g>; lookup by id is O(1).
User adds a node with empty label Allowed; rendered as a single space. Mermaid will accept it.
User tries to connect a node to itself connect() throws TypeError; canvas shows toast.
Undo stack overflow (>50 entries) Oldest snapshot dropped; no memory growth.

Testing

  1. Unit (tests/flowchart-store.test.js) — Pure store operations. 30+ tests across add/move/connect/disconnect/delete/undo/redo/subscribe/serialize/deserialize. Includes invalid-input rejection.
  2. Unit (tests/flowchart-mermaid.test.js) — Translation for each shape kind × each edge kind × with-label × without-label. Snapshot tests for 5 representative graphs (linear chain, decision diamond, parallel branches, cycle, large 20-node graph).
  3. Unit (tests/flowchart-shapes.test.js) — shapeSvg('process', 10, 10, 100, 50) returns SVG containing a <rect> at the right coordinates.
  4. DOM (tests/flowchart-canvas.test.js) — Mount with 3-node graph; assert <svg> contains 3 <g data-node-id="…">. Simulate pointerdown on node 1's center, pointermove +100px, pointerup; assert store's moveNode was called with the new position.
  5. DOM (tests/flowchart-panel.test.js) — Mount panel; assert two panes exist; trigger a store change; assert preview re-renders within 300 ms (jest fake timers).

Risks

Risk Likelihood Mitigation
Drag/drop in jsdom is fragile (no real layout) Medium Use getBoundingClientRect() mocks; canvas tests focus on store wiring, not pixel-perfect rendering.
Hand-rolled SVG shapes look amateurish next to Mermaid's rendered output Medium Use the same color tokens as the active theme via CSS variables; reuse --accent, --text-primary from the theme CSS.
Undo/redo memory growth on large graphs Low Bounded depth (50 snapshots) + snapshot diff-based compression (only store changed nodes).
Persistence path conflicts on multi-window future Low Use <userData>/flowchart-session.json — single user, single app instance.
Mermaid preview re-render flickers on every keystroke Medium Debounce 250 ms; show a "Rendering…" status indicator during re-render.
Edge routing looks ugly on dense graphs Medium Document as a known limitation in the panel tooltip; v2 orthogonal routing is on the roadmap.

Acceptance Criteria

  • src/sidebar/flowchart-panel.js exists and is registered via sidebarManager.registerPanel('flowchart', …).
  • Sidebar rail button with data-panel="flowchart" added to src/index.html; clicking it opens the panel.
  • User can add a node by clicking the canvas (default: process shape at click position).
  • User can drag a node to a new position; the store's moveNode is called with correct coordinates.
  • User can double-click a node to edit its label inline; on blur or Enter, the store is updated.
  • User can right-click a node to change its shape (5 options: process, decision, terminator, subroutine, document).
  • User can drag from a node's edge handle to another node to create an edge.
  • User can click an edge to change its type (3 options: solid, dotted, thick) and add an optional label.
  • Ctrl+Z / Ctrl+Shift+Z (when panel is focused) undoes / redoes the last change.
  • The right preview pane shows the current Mermaid source as text AND renders the SVG via the existing Mermaid render path.
  • "Insert at Cursor" wraps the Mermaid source in a ```mermaid fenced code block and inserts it at the editor cursor.
  • Closing and reopening the app restores the last graph from <userData>/flowchart-session.json.
  • npm test passes with the new test files added (target +30 new tests across 5 files).
  • npm run lint clean, npm run format:check clean.
  • npm run build:linux succeeds.
  • README updated to mention the visual flow chart editor.