feat(diagram-engine): open shape vocabulary — catalog + pass-through, structured style merge

The expressiveness gap traced to vocabulary: draw.io has hundreds of shapes,
the declarative layer allowed six. Opening it, with the failure modes from
design review handled:

- shapes.ts: a ~20-entry curated catalog (full style fragment incl. the
  matching perimeter — required or edges connect to the bounding box; a
  text-scale factor verified in the real editor — the same sentence overflows
  a 1.0x rhombus and fits a 1.5x one; labelOutside+glyph for umlActor-style
  figures). Any other token passes through verbatim: draw.io degrades unknown
  shapes to rectangles safely (verified). Injection-capable tokens (;/=) are
  rejected outright.
- Pass-through emits a WARNING with a nearest-catalog hint (bounded edit
  distance), so a typo'd 'cyclinder' is a one-turn fix instead of a silently
  rectangular node forever. set_shape/set_role/set_group operations make the
  fix possible without remove+re-add (which would drop links).
- mergeStyle(): style fragments merge per-key (later wins, bare shape classes
  displace each other) instead of string concatenation. This is what makes
  shape and theme composable by rule — shape owns geometry keys, theme owns
  colour/type keys, and an overlap resolves by order instead of emitting
  contradictory duplicates.
- dai_shape marker carries the declared token through the round trip:
  appearance-based reverse mapping cannot distinguish aliases (diamond vs
  decision) or a rotated queue from a cylinder.
- dai_auto marker separates engine-measured size from user-fixed size: parsed
  boxes no longer freeze the first layout's numbers, so changing a label
  re-measures. Pinned nodes keep everything, as before.
- draw_graph's closed shape enum opened to match (first instance of the
  schema-drift problem the review predicted).

14 new tests: merge ownership, injection rejection, near-match hints,
alias-preserving round trip, re-measure on label change, mxgraph.* tokens
staying boxes with role/group intact. 556 unit tests green; acceptance
diagram (person/hexagon/cylinder/queue/cloud/decision/callout) verified in
the real editor.
This commit is contained in:
dayuan.jiang
2026-08-09 21:56:09 +09:00
parent db9db1ff4e
commit 50826c0ac8
11 changed files with 644 additions and 75 deletions

View File

@@ -73,7 +73,7 @@ parameters: {
tool name: draw_graph
description: Draw a flowchart, decision tree, dependency graph, ER diagram or site map from nodes and arrows alone. You give NO positions and NO nesting; the engine works out how many rows there are, who shares a row, and who goes left of whom, so arrows do not cross or run through unrelated boxes. Replaces the whole diagram — use restructure_diagram to edit afterwards.
parameters: {
nodes: Array<{id: string, label: string, shape?: "box"|"decision"|"terminator"|"round"|"data"|"document", icon?: string, group?: string}>
nodes: Array<{id: string, label: string, shape?: string, icon?: string, group?: string}>
edges: Array<{source: string, target: string, label?: string, dashed?: boolean}>
title?: string
flow?: "col" | "row" // col (default): top to bottom. row: left to right
@@ -166,10 +166,15 @@ Mind maps and org charts (add_radial):
- spread: "radial" for a mind map (branches on both sides, compact). "down" for an org chart
(everything below its manager, which is the only way a reporting line reads correctly).
Flowchart box shapes, for both draw_graph and add_box:
- "decision" for a branch (a diamond), "terminator" for a start or end point, "data" for input or
output, "document" for a report, "round" for a soft-edged step. Use them: a reader takes a
diamond to mean a choice, so drawing every step as the same rectangle loses that.
Box shapes, for both draw_graph and add_box — a shape says what a node IS:
- Flowchart: "decision" (a diamond) for a branch, "terminator" for a start or end point, "data"
for input or output, "document" for a report, "round" for a soft-edged step.
- Semantic: "cylinder" for a database, "queue" for a message queue, "person" for an actor or
user, "cloud" for an external system, "hexagon" for a service, "ellipse" for a concept,
"callout" for a note, "step" for a pipeline stage, "note", "card", "process", "tape", "cube".
- Any other draw.io shape token also works verbatim (unknown ones render as rectangles).
Use shapes: a database drawn as a cylinder needs no "database" caption; a reader takes a
diamond to mean a choice. Drawing everything as the same rectangle throws that away.
Core capabilities:
- Create professional flowcharts, mind maps, entity diagrams, and technical illustrations