feat(diagram-engine): design tokens + role/group composition, engine-wide theming

Paper-summary posters previously required hand-written XML: every engine
box rendered identically (white, 11px), so anything whose meaning lives
in visual hierarchy came out flat. This makes presentation a first-class,
generalised part of the declaration - not a poster feature.

Structure/presentation separation, the same split HTML and CSS settled on:

- ROLE says what a node IS: banner, heading, body, callout, good, bad,
  metric, muted. Maps to a type scale and an emphasis (filled / tinted /
  outlined / ghost), never to a colour.
- GROUP says which semantic zone a node belongs to. Each distinct group
  name gets one hue ramp (tint / base / dark), assigned in document
  order. Promoted from a draw_graph-only field to BoxNode and GroupNode,
  round-tripped via dai_group.
- themedStyle(role, hue, kind) composes the two by rule - there is no
  per-combination table to extend, so a new diagram kind gets full
  theming by tagging nodes. The model never sees a hex value.

A heading container plus a group yields the tinted section panel with a
dark title; a grouped body box takes its zone's tint; verdict roles stay
green/red regardless of zone; the banner is the page's one dark field.

Also fixed, found while building the acceptance poster:

- autoBoxSize only counted explicit newlines, so a long single-line label
  wrapped to six lines in draw.io but got a one-line-tall box, and the
  text overflowed the cell.
- Marker stamping appended without replacing, so every render of a
  recovered style grew it by one duplicate dai_* token per key -
  unnoticed because draw.io resolves duplicates last-wins. dai_* keys
  are now replaced in place; mxGraph keys still append, because
  last-wins is load-bearing for container=1 normalisation.
- Banner/heading/metric roles stretch across their container's cross
  axis, the way a masthead spans its page.
- Prompt: a poster's banner IS its title (no set_title alongside), and
  sections get their colour by naming groups.

537 unit tests, 250-flowchart corpus still zero crossing arrows, 5 e2e
tests in a real browser. Verified visually: the Transformer-paper poster
renders with a navy masthead, three hue-coded section panels, metric,
verdict and callout boxes - all engine-computed geometry.
This commit is contained in:
dayuan.jiang
2026-08-09 19:48:01 +09:00
parent 6b5fd613f2
commit e78322ca52
11 changed files with 727 additions and 60 deletions

View File

@@ -702,7 +702,9 @@ Example: If previous output ended with '<mxCell id="x" style="rounded=1', contin
restructure_diagram: {
description: `Build or edit a diagram by declaring STRUCTURE. The engine computes every coordinate.
PREFER THIS over display_diagram/edit_diagram whenever the diagram's meaning is in nesting or in a fixed frame: cloud architecture, swimlane/BPMN, sequence diagrams, mind maps, org charts. You declare what contains what; layout, sizing, alignment and arrow routing are computed. Containers always fit their contents and siblings never overlap, so the usual layout problems cannot occur.
PREFER THIS over display_diagram/edit_diagram whenever the diagram's meaning is in nesting or in a fixed frame: cloud architecture, swimlane/BPMN, sequence diagrams, mind maps, org charts — AND poster-style layouts: paper summaries, cheat sheets, infographics, comparison sheets. You declare what contains what; layout, sizing, alignment and arrow routing are computed. Containers always fit their contents and siblings never overlap, so the usual layout problems cannot occur.
For a POSTER (paper summary, cheat sheet): one col container as the page; a banner box as the masthead (do NOT also use set_title — the banner IS the title); a muted box for the byline; a row container holding 2-4 col containers as columns; each section is a container with role "heading" holding its items. Give each section a distinct group name — sections sharing a group share a hue, so groups are how the poster gets its colour. Use roles on boxes: callout for the core idea, good/bad for verdict pairs, metric for the headline number, muted for fine print.
Never write coordinates, mxCell XML, or style strings. Look AWS icon names up with search_stencils first — an invented name is rejected with suggestions.
@@ -817,6 +819,21 @@ Grouping: when the nodes fall into natural zones (remote vs local, frontend vs b
.describe(
"Semantic group name, e.g. 'remote' or 'local'. Nodes sharing a group get the same colour from the engine's palette — never pick colours yourself",
),
role: z
.enum([
"banner",
"heading",
"body",
"callout",
"good",
"bad",
"metric",
"muted",
])
.optional()
.describe(
"What this node IS: heading, callout (must-not-miss), good/bad (verdict), metric (key number), muted (fine print). The theme styles it",
),
}),
)
.describe("Every box in the diagram"),