mirror of
https://github.com/DayuanJiang/next-ai-draw-io.git
synced 2026-09-02 01:20:23 +08:00
feat(diagram-engine): flex knobs (grow/align/pad) + inline rich-text labels
The expressiveness gap between engine output and hand-written XML came down to two missing capabilities, both generic: - Block layout inside a box: nested containers already existed, but there was no way to split space by weight, pin a child to an edge, or tighten padding. Added grow (flex-grow over the parent's leftover flow-axis space, TeX's glue), align (start/center/end/stretch on the cross axis) and pad (per-group interior padding). All three round-trip via dai_grow/dai_align/ dai_pad markers. - Inline rich text: labels already render HTML (html=1 on every style, esc() entities decode back), but the measure pass counted markup as text. The visibleText() strip makes autoBoxSize measure what draw.io draws: <br> is a line, other inline tags are invisible. Graphviz (HTML-like table labels), D2 (grid containers + markdown-in-shape) and TeX (box+glue) converged on exactly this design: nested boxes for block structure, proportional glue, a small inline set for text — never full HTML. Prompts teach the composition with a comparison-card recipe; verified by rebuilding the CoT poster end to end in the real editor.
This commit is contained in:
@@ -64,6 +64,12 @@ export const MARKER = {
|
||||
role: "dai_role",
|
||||
/** The node's semantic zone, whose hue ramp colours it. */
|
||||
group: "dai_group",
|
||||
/** Share of the parent's leftover flow-axis space — flex-grow. */
|
||||
grow: "dai_grow",
|
||||
/** Cross-axis position within the parent: "start" | "center" | "end". */
|
||||
align: "dai_align",
|
||||
/** A container's interior padding, px. */
|
||||
pad: "dai_pad",
|
||||
/**
|
||||
* Marks a cell as chrome the engine draws and owns: a pool's lane bands, its label
|
||||
* columns, its milestone strip. The parser must not read these back as nodes — they are
|
||||
@@ -382,6 +388,28 @@ export function stampGroup(style: string, group: string): string {
|
||||
return append(style, MARKER.group, encodeURIComponent(group))
|
||||
}
|
||||
|
||||
type FlexAlign = "start" | "center" | "end" | "stretch"
|
||||
|
||||
/** Stamp the flex fields a node carries, so a round-trip preserves them. */
|
||||
export function stampFlex(
|
||||
style: string,
|
||||
opts: { grow?: number; align?: FlexAlign; pad?: number },
|
||||
): string {
|
||||
let s = style
|
||||
if (opts.grow != null && opts.grow > 0)
|
||||
s = append(s, MARKER.grow, opts.grow)
|
||||
if (opts.align && opts.align !== "center")
|
||||
s = append(s, MARKER.align, opts.align)
|
||||
if (opts.pad != null) s = append(s, MARKER.pad, Math.round(opts.pad))
|
||||
return s
|
||||
}
|
||||
|
||||
/** Read the align marker back. Anything unrecognised means the default (center). */
|
||||
export function readAlign(style: string): Exclude<FlexAlign, "center"> | null {
|
||||
const v = readMarker(style, MARKER.align)
|
||||
return v === "start" || v === "end" || v === "stretch" ? v : null
|
||||
}
|
||||
|
||||
/** Strip every `dai_*` marker — for exporting a clean file, or comparing styles. */
|
||||
export function stripMarkers(style: string): string {
|
||||
return style
|
||||
|
||||
Reference in New Issue
Block a user