Skip to content

fix(canvas): text stays inside the Pool, Source, Drain, Converter and Gate outlines at every width #337

Description

@MerciHanrim

The problem

The Pool, Source, Drain, Converter and Gate outlines are drawn in a fixed 0 0 120 H viewBox stretched to the node's width (preserveAspectRatio="none"), so every point, notch and slant gets deeper as a node widens, while the text inside is laid out at fixed px insets. Measured on main at 70a9c37 (v0.21.3), the 5 Templates in all 18 languages plus a synthetic graph of every mode, desktop and phone, at zoom 1, each painted element's clearance to the drawn fill taken as #332's e2e takes it (the line box's top + 1, middle and bottom − 1):

  • Of 2,899 Source / Drain / Converter / Gate / Pool instances, 2,493 have a text line crossing the outline and all 2,899 have one closer than 8 px; 957 of the 1,113 distinct Template nodes cross in at least one language.
  • The kind chip crosses on every Pool (1,409 of 1,409, down to −21.6 px), every Drain and Converter, and 389 of 582 Gates.
  • Worst lines: a Gate title −57.9 px (ar gacha), a Converter title −39.7 px (vi Early MMO), a Drain mode row −24.1 px across the notch, a Pool title −23.3 px on its slant, a Source title −21.7 px into the arrow.
  • Every two- and three-line Pool title (455 of 455) and every Converter and Drain mode row crosses.
  • Phone and desktop are identical (3,570 nodes, 0 differences), and so are the L2 / L1 / L0 zooms and the dark and forced-colours themes; L1 hides the mode rows, L0 all text.

Widening the node until the text fits cannot work with these outlines: the Converter's waists and the Gate's slopes deepen as fast as the node widens, so 242 Converter, 369 Gate and 75 Pool instances would need more than the 260 px maximum, and the rest would create 542 new node overlaps in Early MMO.

#332 on main. Of 1,898 Pool value and detail rows, 1,006 start at the title (±0.5 px), 892 start later (up to 10.0 px), 0 before it, 0 under 8 px from the outline. The 892 are exactly the rows whose own title is closer than 8 px to the slant: rowFit then places the row at the outline + 8. #332's e2e allows this (it fails a row only when it starts before the title or sits under 8 px), but it departs from #332's rule 1 (a row starts where the title starts). This issue fixes it in the same change: with the title kept 8 px from a fixed slant, #332's own rule puts all 1,898 rows at the title start.

The contract (decided)

  • Width-parametric silhouettes. Pool, Source, Drain, Converter and Gate keep every point, notch, slant and corner at a fixed px depth at every width and height; only the straight runs stretch (as the height caps already do, §MML1b). Depths: Pool slant 8 px, Source arrow 16 px, Drain notch 16 px, Converter waist 16 px.
  • Gate: an elongated hexagon: a 16 px point at each end, flat top and bottom, replacing the stretched diamond.
  • 8 px for every painted element. The kind chip, every title line and every row keep at least 8 px from the drawn outline at their own height (fix(canvas): Pool, Register and Parameter value rows sit too close to node silhouettes #332's ROW_CLEAR, the keyboard-focus ring plus 2), measured the way fix(canvas): Pool, Register and Parameter value rows sit too close to node silhouettes #332 measures. The chip and the title move in as one; rows start where the title starts, as in fix(canvas): Pool, Register and Parameter value rows sit too close to node silhouettes #332, and further in only where the outline needs it; a Source, Drain or Converter mode row starts at the title too; the Gate's lines stay centred.
  • Minimal widening. A node widens only by what its lines need, up to the 260 px maximum; measured, no node needs more. Heights and title wrapping do not change.
  • The bundled Templates are adjusted minimally. Without it the measured widening would create, in some languages, new node overlaps in Early MMO (73) and the gacha Template (2), and push nodes past a saved frame (gacha 34, Coffee 2). Node positions and saved frames of the affected Templates move by the least needed so that, in all 18 languages, there are 0 new node overlaps and 0 nodes past a frame.
  • Unchanged: the engine and the simulation; the engine digest (semanticDigest) of every Template and file; users' saved files and their coordinates (no migration, no automatic move); port hit areas (the ports stay on the box edges, as when a node widens today); Parameter, Register and End silhouettes; the activity overlay (feat(playback): play execution visualization — taken paths, +N badges, arrival and conversion cues #330). The full revision digest (fullContentDigest) of an adjusted Template moves, because node positions and frame rectangles are cosmetic fields of it.
  • One outline for desktop and phone, every zoom level and every theme (light, dark, forced colours).

Tests

  • Unit: the new paths at the base height reproduce the decided depths at several widths and heights; the fit is a pure function tested on a grid of widths, heights and line positions.
  • e2e: in the 5 Templates in all 18 languages and a synthetic graph of every mode, on the desktop and the phone, every chip, title line and row of the five kinds keeps 8 px from the drawn outline (isPointInFill at the line box's top + 1, middle and bottom − 1); every Pool row starts at the title; fix(canvas): Pool, Register and Parameter value rows sit too close to node silhouettes #332's existing checks keep passing.
  • e2e: no two nodes of a Template overlap and no node leaves its saved frame, in every language; each Template's engine digest equals the one recorded before the change.
  • Visual baselines in light, dark and forced colours for the new outlines.

Out of scope

Deferred after v0.24.0 (2026-10-08). Not shipped in v0.21.5, and no v0.21.5 release is made. The investigation (in the comments) found that the current node and connection structure cannot keep text inside the outline, keep each node's geometry and leave the connections where they are all at once. The work moves into one Diagram Readability design after v0.24.0, together with what a node shows inside and outside, stable node sizes and ports, connection shapes and automatic routing, what the numbers on connections mean, LOD, and separate tutorial and advanced showcase Templates. Survey, numbers and actual-size mockups were shared before this issue was opened. Related: #330 (v0.22.0 proceeds), #332 (v0.21.2).

Activity

  1. changed the title [-]fix(canvas): text stays inside the Pool, Source, Drain, Converter and Gate outlines at every width (v0.21.4)[/-] [+]fix(canvas): text stays inside the Pool, Source, Drain, Converter and Gate outlines at every width (v0.21.5)[/+] on Oct 8, 2026
  2. MerciHanrim commented on Oct 8, 2026

    @MerciHanrim
    OwnerAuthor

    Investigation result: deferred after v0.24.0 (2026-10-08)

    The fixed-depth outlines were implemented and measured in full. They are not shipping: in the current node and connection structure, keeping text inside the outline, keeping each node's geometry, and leaving the connections where they are cannot all hold at once. No code and no baseline was committed; the work is kept as patches outside the repository.

    What the implementation achieved

    • Every chip, title line and row of the Pool, Source, Drain, Converter and Gate kept 8 px inside the drawn outline in all 18 languages (least 7.96 px, within the measurement's 0.3 px tolerance); all 5,977 Pool and mode rows started at the title; Parameter, Register and End nodes (671) and every node height were unchanged.
    • With the bundled Templates' coordinates and frames adjusted, there were 0 node overlaps and 0 nodes crossing a frame on any side, in all 18 languages.

    Why it cannot ship

    Widening a node moves its ports, and the fixed-depth outlines fill corners the stretched ones left empty, so connections that passed by now cross nodes. Connections were measured against each node's drawn fill (not its box): a path crosses a node when 3 or more consecutive 1 px samples fall inside it, ignoring the node's own two ends and 8 px around a port; a label overlaps when any point of a 1 px grid over it falls inside. The same reader was applied to main and to each candidate, and representative cases were checked on screen.

    • On the adjusted Templates, Early MMO progression gained 451 connection findings over main (104 distinct cases, across 53 of its 144 connections and 69 of its 97 nodes): 336 from moved ports, 65 from orthogonal connections that found no way round and fell back to a straight line, 38 with the connection unchanged and the new outline reaching it, 12 other.
    • A user's saved document keeps its own coordinates, so the candidates were also compared at main's Template coordinates and on a dense test graph (160 nodes, 226 connections):
    Candidate New connection findings (distinct) Dense graph Ports moved, max New overlaps / frame crossings Under 8 px Titles cut Heights changed
    Widen to the right (this issue's contract) 740 (181) 65 34 px 96 / 36 0 0 0
    Widen, centred 776 (177) 60 34 px 94 / 12 0 0 0
    Keep the width, wrap the title 411 (80) 45 16 px 0 / 0 57 143 1,453
    Keep the width and the lines, ellipsis 213 (57) 18 1 px 0 / 0 0 1,598 0
    Widen by at most 8 px, centred 601 (116) 71 17 px 35 / 1 16 59 1,312
    Widen by at most 16 px, centred 697 (135) 65 18 px 50 / 6 13 39 1,081
    Keep the width, Converter waist 24 px 396 (77) 45 16 px 0 / 0 57 364 1,463
    Keep main's outline and lines, fit the text inside 0 0 1 px 0 / 0 346 2,646 0

    Counts are node instances over the 5 Templates in 18 languages plus the dense graph in 3 (3,273 instances of the five kinds). Wrapping a title in a narrower box broke words mid-word; the ellipsis candidates cut about half (1,598) and four in five (2,646) of the titles.

    The only form that adds no connection finding keeps both the ports (width and height unchanged) and an outline no larger than main's; that is main's own outline, and inside it the 8 px clearance cannot be met (346 instances short even with 81 % of the titles cut).

    Next

    Deferred after v0.24.0, to be designed as one Diagram Readability piece rather than fixed alone: what a node shows inside and what moves to labels outside it; stable node sizes and port positions; connection shapes and automatic routing; what the numbers on connections mean; LOD; separate tutorial and advanced showcase Templates.

    Found on main along the way, not changed here: in Gacha banner zones, zone1_free_w_r and zone3_pickup_w_standard end up to 22 px below their zone frame in most languages, and forced_ssr_standard overlaps sr_hit_standard by 11 px in Russian; connections crossing nodes in the Templates on main (Early MMO 1,976 instances, 166 distinct; Gacha 10, 5 distinct) are measured with the same reader and will be recorded separately.

  3. changed the title [-]fix(canvas): text stays inside the Pool, Source, Drain, Converter and Gate outlines at every width (v0.21.5)[/-] [+]fix(canvas): text stays inside the Pool, Source, Drain, Converter and Gate outlines at every width[/+] on Oct 8, 2026
  4. MerciHanrim commented on Oct 10, 2026

    @MerciHanrim
    OwnerAuthor

    Shipped in v0.25.0 (Diagram Readability, #344)

    The text-inside-the-outline change is released in v0.25.0, as part of the Diagram Readability migration.

    Why the earlier V0 was held

    The fixed-depth outlines were complete on 2026-10-08 (every chip, title line and row 8 px inside the drawn outline in all 18 languages), but they could not ship on the structure of that time: widening a node moved its ports, and the fixed-depth outlines filled corners the stretched ones had left empty, so connections that passed by now crossed nodes. At users' saved coordinates the candidate added 740 connection findings (181 distinct), 96 node overlaps and 36 frame crossings, and none of the nine measured variants reached zero with 8 px containment. Most MMO connections were default curves that re-bent through neighbouring nodes.

    The base #344 Steps 2–4 provided

    • Step 2: the guarded orthogonal router — a route never crosses a node fill (own end nodes included), same-port fans branch past a 16 px stub, labels take a free slot, and every unresolved case is reported.
    • Step 3: connection shapes (Orthogonal by default, Curved, Straight) and bend points, so converted and new connections no longer depend on curves.
    • Step 4: all five Templates placed for the grid by each node's widest box over the 18 languages with a 48 x 32 px clearance and 32 px between frames.

    What Step 5 implemented

    The Pool, Source, Drain, Converter and Gate are drawn for their own width with fixed-depth features, and every painted element keeps 8 px inside the drawn outline; ports and the Converter's conversion mark read the outline as drawn. The re-placement's upper-bound box gained the outline's reach, the Templates were placed again by the new widest boxes, and the one-time conversion reads a frozen pre-outline box for frame membership, so frames keep the nodes they held.

    Commits

    • PR #345 head 1b2824d — the tree that CI verified.
    • Squash commit on main: 24d0d91 (the same tree, 0915178), tagged v0.25.0.

    Checks

    • All 18 languages, the bundled Templates: no blocked or outer-corridor route, no fan overlap past its stub, no connection through a node, no label on a node, no frame cutting a node, no node overlap, every painted element 8 px inside its outline.
    • The same at users' coordinates (the Templates as saved before v0.25.0, converted): no blocked, outer or fan-overlapping route, no frame cut, node overlap or connection through a node.
    • CI on main (24d0d91): 2,105 browser tests, the production-bundle and PWA suites, with no failure and no retry.
    • Production (cozy-loop-studio.pages.dev, build 24d0d91), in a fresh browser context: About reads v0.25.0 · build 24d0d91, no page, console or request errors, the early MMO opening view keeps its four core start nodes whole, and all 144 of its connections are orthogonal.

    Closing as completed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions