Web Editor
Overview
A visual editor for mdd models — place boxes, connect them, and export the exact same Unicode ASCII the CLI renders. The editor is a thin shell around the engine: it has no diagram-rendering logic of its own, so the canvas shows the engine's real character grid, byte for byte. Work autosaves to your browser as you edit.
Tools
Select V
Click a box or connection to select and edit it. Drag an existing box to reorder it — a dotted ghost previews the target row and the grid reflows on drop. Dragging from empty canvas draws a selection marquee: every box the rectangle touches ends up selected, and a marquee that touches nothing deselects — same as a plain background click. A drag shorter than one cell counts as a click, not a marquee. Shift-click adds a box to the selection (or removes an already-selected one), so you can pick boxes the marquee can't isolate — say, the outer two of a row of three, leaving the middle one out.
To pan the board, hold Space and drag (the cursor turns into a hand), or drag with the middle mouse button. Panning works with any tool selected for the middle button, and with the Select tool for Space.
Box B
Click the canvas to add a box, or drag the Box tool onto the canvas to drop it at a snapped row/slot. You can drop above the top row — existing boxes shift down to make room.
Table T
Click or drag onto the canvas to add a table box — a 2-column starter with a header row and two empty body rows. A table box has no label; its cells hold the text.
Connector C
Click a source, then a target, to link them — either endpoint can be a box or a cluster's outline ring. Clicking a table box picks the hovered body row as the connection's anchor (a faint band previews it) — picking two distinct rows of the same table draws a self-referential link. Routed connectors have a generous hit area, so clicking anywhere along the line selects it.
Editing tables
With a table box selected, click a cell to select that cell. Double-click a table to edit the nearest cell in place. While editing, Tab and Shift+Tab commit and move to the next or previous cell — Tab past the last cell adds a new row and keeps going, so you can type out a whole table without touching the mouse.
Edits that would break the table are rejected with an explanatory notice and leave the diagram untouched: removing a column that still contains text, deleting the last remaining row, or widening a cell's span past the room in its row.
Grouping into clusters
Select one or more boxes — click one, drag a marquee across several, or Shift-click them one at a time — and the Group button (G) in the tool rail undims; click it (or press G) to wrap the selection in a new cluster outline, selected and ready to rename. A multi-box selection also gets its own Group button in the property panel. With the Select tool, clicking an existing outline's ring selects the cluster; Delete removes it and keeps its member boxes untouched.
The property panel's Group inspector edits Label, Weight, Padding X / Padding Y (min 0), and a Members list — each member has a remove button, and a select adds any box not already in the cluster. Cluster-scoped validation problems (an empty cluster, a degenerate connection) surface inline in the panel as well as the messages strip.
The Connector tool treats a cluster's outline exactly like a box: click a ring to start a pending connection from that cluster, or to complete one onto it. A connection that would join a cluster to its own member (or to an ancestor/descendant cluster) isn't blocked by the gesture — it's created, and validation flags it afterward, the same as any other connection mistake.
Document tabs
Keep several diagrams open in tabs; add, rename, and close them from the tab bar. Closing a tab that still has boxes asks for confirmation first — empty tabs close silently. The whole tab session is restored when you reload the page.
Property panel
The right panel is context-sensitive:
- Box — ID, Label (multi-line), Shape (Box or Diamond — a decision node; see Decision diamonds in the format docs), Weight (1–4), Row, Padding X / Padding Y (interior space, min 1), and Margin X / Margin Y (minimum gap to neighbors, min 0).
- Table — same ID and style fields as a box, plus Structure: a Columns stepper (it won't shrink below a column that still holds text), a per-row list with header toggle and delete, and an Add row button.
- Cell — the cell's Text and its Span, which grows by absorbing cells to its right.
- Connection — From → To, Weight, Arrow (to / from / both / none), an optional Label drawn on the line, and — when an endpoint is a table — From row / To row anchor pickers, where "Whole box" means no anchor.
- Group — Label, Weight (dotted by default), Padding X / Padding Y, and a Members list with per-row remove and an add-member picker — see Grouping into clusters.
- Multiple boxes (a marquee selection) — how many boxes are selected and which, plus a Group button. It is not a bulk editor: per-box fields stay in the single-box panel.
- Nothing selected — a diagram summary (boxes, rows, links) plus the global Padding X / Y and Margin X / Y.
Import / Export
Import loads a .diagram.json into the current tab, which takes the file's name; a malformed file shows a notice and leaves your work untouched. Export downloads the JSON sidecar and opens the Markdown panel, which has Markdown and JSON tabs, a Copy button, and a resizable height — copy whichever representation you need.
Feedback & diagnostics
Problems never fail silently. A messages strip below the canvas lists validation problems and render warnings in plain language — clicking one selects the offending box or connection — and the top-bar status badge summarizes save state, problems, and warnings at a glance.
Transient events surface as a toast: a rejected table edit, a failed import, or autosave being unable to write to browser storage. If the editor itself crashes, an error screen offers a reload — autosave means reloading restores your last good state.
Keyboard shortcuts
Running locally
The app is the web workspace of the root package. From the repo root:
npm install npm run dev --workspace web
Requires Node ≥ 20.