diff --git a/app/api/chat/route.ts b/app/api/chat/route.ts index 2c83634..f8fefda 100644 --- a/app/api/chat/route.ts +++ b/app/api/chat/route.ts @@ -704,7 +704,7 @@ Example: If previous output ended with ', , ,
— so one box carries a bold keyword, a second paragraph, a coloured verdict line. Emoji in headings (💡 Core Idea) read instantly. +The layout model is FLEXBOX. row/col containers nest freely; a box with internal structure is an invisible col container (pad 10-14) holding smaller boxes. Three knobs: grow (columns split leftover WIDTH by weight — grow 3 / grow 2 gives a 3:2 page; for containers in a row, not for leaf boxes), align "stretch" (child fills its column's width; content keeps natural height and packs to the top — the engine leaves leftover vertical space at the bottom, never inflates boxes to fill it, so balance columns by moving content between them), pad (8-14 tight card, default 24 roomy section). Labels take inline HTML — , , ,
— so one box carries a bold keyword, a second paragraph, a coloured verdict line. Paragraphs set themselves flush-left automatically; short labels centre. Emoji in headings (💡 Core Idea) read instantly. For a POSTER (paper summary, cheat sheet): one col container as the page; a banner box as the masthead with align stretch (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 with grow weights as columns; each section a heading-role box + content boxes, all align stretch. 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. A comparison card: add_container dir=col gap=8 pad=12 grow=1 role=bad, then a bold title box, the body text, a role=bad answer bar (all align stretch), and a coloured "✗ Often Wrong" verdict with align start. diff --git a/lib/diagram-engine/layout.ts b/lib/diagram-engine/layout.ts index 3e584d3..1b3c793 100644 --- a/lib/diagram-engine/layout.ts +++ b/lib/diagram-engine/layout.ts @@ -722,11 +722,18 @@ function place(p: Placed, x: number, y: number, links: LayoutLinks): void { slack = 0 } - const gap = k > 1 ? n.gap + Math.min(n.gap, slack / (k - 1)) : n.gap + // Slack policy differs by axis. A ROW spreads and centres — a flowchart layer + // reads as a pyramid, and dead space at the right edge of a row looks like a + // mistake. A COLUMN packs to the top and leaves the slack at the bottom: a column + // is usually tall because a SIBLING made it tall, and stretching its gaps (or its + // boxes, via grow) turns every panel into a huge frame with three lines floating + // in the middle — the single ugliest thing in the poster this replaced. + const gap = + alongRow && k > 1 ? n.gap + Math.min(n.gap, slack / (k - 1)) : n.gap const span = kids.reduce((s, kid) => s + (alongRow ? kid.rect.w : kid.rect.h), 0) + gap * Math.max(0, k - 1) - let cur = (alongRow ? innerX : innerTop) + Math.max(0, (extent - span) / 2) + let cur = alongRow ? innerX + Math.max(0, (extent - span) / 2) : innerTop for (const kid of kids) { // A stretching role fills the cross axis: a masthead spans its page, a section diff --git a/lib/diagram-engine/render.ts b/lib/diagram-engine/render.ts index 871601a..1bf52dc 100644 --- a/lib/diagram-engine/render.ts +++ b/lib/diagram-engine/render.ts @@ -69,6 +69,18 @@ export function esc(s: string): string { // tags DO need handling is the measure pass (layout.ts autoBoxSize), which must not // count markup as text. +/** + * Is this label a paragraph rather than a short caption? + * + * Typography's basic rule: labels centre, paragraphs set flush left. The split is by + * content — explicit line breaks, or enough text that draw.io will wrap it — because + * the model declares WHAT the text is, never how to align it. + */ +function isParagraph(label: string): boolean { + const plain = label.replace(/<[^<>]+>/g, "") + return /\n/.test(label) || /
60 +} + /** Resolve a catalog name to a style. Injected so the engine does not own the catalog. */ export type StyleResolver = ( name: string, @@ -168,6 +180,14 @@ function styleFor( n.role || n.group ? themedStyle(n.role ?? "body", hue(n.group), "leaf") : undefined, + // Typography, decided by the content: a PARAGRAPH sets left and top — + // a three-line body floating dead-centre in its box was the poster's + // second-ugliest defect — while a short label stays centred. Only for + // rectangles: inside a rhombus or a cloud the safe text area is the + // middle, which is exactly where centring puts it. + isParagraph(n.label) && !n.shape + ? "align=left;verticalAlign=top;spacingLeft=10;spacingRight=10;spacingTop=8;" + : undefined, n.fill ? `fillColor=${n.fill};` : undefined, n.stroke ? `strokeColor=${n.stroke};` : undefined, n.bold ? "fontStyle=1;" : undefined, diff --git a/lib/system-prompts.ts b/lib/system-prompts.ts index d4f1cbd..a3a502f 100644 --- a/lib/system-prompts.ts +++ b/lib/system-prompts.ts @@ -114,8 +114,11 @@ Use restructure_diagram ALSO for poster-style layouts — paper summaries, cheat freely, and a box with INTERNAL structure is just an invisible col container (pad 10-14) holding smaller boxes. Three knobs, use them everywhere: - grow: columns split leftover width by weight (grow 3 / grow 2 makes a 3:2 page). - - align "stretch": a child fills its parent's cross axis — headings, highlight bars and - body boxes should almost always stretch, or the column looks ragged. + - align "stretch": a child fills its parent's cross axis (width, in a column). Use it on + headings, highlight bars and body boxes so a column's edges line up. Note stretch is + about WIDTH — content keeps its natural height and packs to the top of its column; the + engine leaves leftover vertical space at the bottom, never inflates boxes to fill it. + So do NOT give leaf boxes grow to "fill" a column — balance columns by moving content. - pad: small (8-14) for tight cards, default 24 for roomy sections. Labels take inline HTML — , , ,
— so one box can hold a bold keyword, a second paragraph, a coloured verdict line. Emoji in headings (💡 Core Idea)