mirror of
https://github.com/DayuanJiang/next-ai-draw-io.git
synced 2026-09-01 17:10:24 +08:00
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:
@@ -23,8 +23,7 @@
|
||||
*/
|
||||
|
||||
import type { Operation } from "./operations"
|
||||
import { groupColour } from "./render"
|
||||
import type { BoxShape } from "./types"
|
||||
import type { BoxShape, Role } from "./types"
|
||||
|
||||
/** A node in the graph the caller wants drawn. */
|
||||
export interface GraphNode {
|
||||
@@ -40,6 +39,8 @@ export interface GraphNode {
|
||||
* caller names the grouping and never touches a colour.
|
||||
*/
|
||||
group?: string
|
||||
/** Information role (heading, callout, metric…); the theme decides how it looks. */
|
||||
role?: Role
|
||||
}
|
||||
|
||||
/** An arrow. Direction matters: it is what determines the layering. */
|
||||
@@ -309,19 +310,8 @@ export function graphToOperations(
|
||||
},
|
||||
]
|
||||
const byId = new Map(nodes.map((n) => [n.id, n]))
|
||||
// Groups become colours here, in order of first appearance, so "the second group named
|
||||
// is green" holds for every diagram the engine draws. The caller only names groups.
|
||||
const groupIndex = new Map<string, number>()
|
||||
for (const n of nodes)
|
||||
if (n.group && !groupIndex.has(n.group))
|
||||
groupIndex.set(n.group, groupIndex.size)
|
||||
|
||||
const add = (id: string, parent: string): Operation => {
|
||||
const n = byId.get(id) as GraphNode
|
||||
const colour =
|
||||
n.group !== undefined
|
||||
? groupColour(groupIndex.get(n.group) ?? 0)
|
||||
: null
|
||||
return n.icon
|
||||
? {
|
||||
op: "add_icon",
|
||||
@@ -336,9 +326,8 @@ export function graphToOperations(
|
||||
parent,
|
||||
label: n.label,
|
||||
...(n.shape && n.shape !== "box" ? { shape: n.shape } : {}),
|
||||
...(colour
|
||||
? { fill: colour.fill, stroke: colour.stroke }
|
||||
: {}),
|
||||
...(n.role && n.role !== "body" ? { role: n.role } : {}),
|
||||
...(n.group ? { group: n.group } : {}),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user