Feature/hola modular implementation - #1
Open
gabry-at-mermaid wants to merge 129 commits into
Open
gabry-at-mermaid wants to merge 129 commits into
gabry-at-mermaid wants to merge 129 commits into
Conversation
…nd agentflow diagrams State, usecase, and agentflow diagrams get their own `wrappingWidth`, matching the flowchart option, and all four diagram types get a new `minNodeWidth`. Both default to 120 (flowchart's `wrappingWidth` was 200). `wrappingWidth` caps the label box; `minNodeWidth` widens labels narrower than it so nodes with short text render at a uniform width, with the node's own padding added on top. The minimum is applied in `createText` for HTML labels (the label div is sized in the DOM so measurement and rendering agree) and in `labelHelper` for the SVG-text path, so every shape honours it without per-shape changes. Each diagram's db stamps `wrappingWidth`/`minWidth` on its nodes; actors in usecase diagrams and group nodes are excluded, and an explicit per-node width wins over the minimum.
Several associations ending on one side of a use case had their markers stacked on a single point. Three changes, scoped to use-case diagrams except where noted: - Ovals get the same vertical padding as horizontal (160x38 -> 160x58), so a side has room for more than one marker. - Use-case ovals set a `spreadPorts` hint; the ELK renderer maps it to CENTER port alignment on that node, which spreads ELK's implicit ports as far as the side allows. Node-level `portPort`/`portsSurrounding` are ignored for implicit ports (measured with elkjs 0.9.3), so alignment is the only lever. - The ELK renderer read end-marker offsets from a private table that only knew `arrow_point`, so for extension/circle/cross markers the short on-border stub an off-centre port leaves outside a curved outline was kept, and the marker pull-back ran past it and flipped the arrow. The offsets now come from `utils/lineWithOffset`, and the stub removal is applied at the start of a path as well as the end.
Shapes position their label with `pos - (bbox.x - (bbox.left ?? 0))`, which relies on SVG text's getBBox() rect having no `left` (it is centred on x=0) while an HTML label's DOMRect does. The box widened to `minNodeWidth` fabricated `left`/`top` for both kinds, so every SVG label narrower than the minimum lost its centring correction and slid left by half its width in the ~30 shapes that use the idiom. The widened box now keeps the kind it was given, and SVG text stays centred as it grows. Adds a dev fixture covering the affected shapes with htmlLabels: false.
`build:types` failed on three counts introduced with the `wrappingWidth`
and `minNodeWidth` config, taking lint, unit-test, e2e, build-docs and
autofix down with it since they all run it during install:
- `stateDb`'s local `NodeData` never declared `wrappingWidth`/`minWidth`,
which `getData` assigns.
- `withMinWidth` reads `bbox.width` in the branch guarded by
`!('left' in bbox)`. `getBBox()` returns an SVGRect without `left`, but
the DOM typings widen it to DOMRect, so TypeScript narrows that branch
to `never`. Read the geometry before the check instead.
- `config.usecase.spec` compiled a subset schema holding only
`BaseDiagramConfig`, so ajv could not resolve the new `$ref`s into
`FlowchartDiagramConfig`. Compile against the whole `$defs` block and
cover the two new keys in the defaults, required list and key order.
The vitepress docs build failed with 8 dead links, for the state and agentflow pages of both properties. jsonschema2md only resolves a `$ref` to a definition that appears earlier in the schema file. `FlowchartDiagramConfig` sits after `StateDiagramConfig` and `AgentflowDiagramConfig`, so those two forward refs were left pointing at a page the generator never emitted. The same refs from `UsecaseDiagramConfig` resolve because it is declared after the flowchart config; the other `$ref`s on the state config all point backwards at `BaseDiagramConfig` or `GitGraphDiagramConfig`, which is why only these two broke. Inline the property definitions for the two configs that precede the flowchart one, matching how `nodeSpacing` and `rankSpacing` are already written per diagram. `config.type.ts` is byte-identical, since the type generator resolved the refs inline anyway.
`minNodeWidth` defaults to 120, so nodes with short labels are widened and
the diagrams these tests measure are wider than the bounds written before
the option existed. The bounds are updated to the measured widths, keeping
each file's existing tolerance.
State (`[*] --> State1 --> [*]`) goes from ~75 to 152: the label area is
widened to 120, plus the node's padding of 8 a side and the viewbox padding.
The hand-drawn flowchart goes from 446 to 644, where `C{Let me think}` has
three short-labelled children that each widen to 120.
These shards have not run since the option was added -- the type build failed
during install, so only shard 1 ever reported.
Lint the code that ships in the bundles (`packages/*/src`), with `compat/compat`. Safari 15.4 is the floor that we're not officially supporting in Mermaid v11, but is one we're trying to support as a rough guideline.
Node labels now state the colour the test expects ("nodes should be red"),
and the first case passes a yellow primaryColor through the render config so
the snapshot shows the directive's red taking precedence over config.
Mermaid now requires browsers that support ES2024.
We're targeting browser support for Safari 17.4, since Safari is the
main browser that tends to be out-of-date, due to iOS/MacOS devices not
being to upgrade to new iOS/MacOS versions.
Chromium v121 and Firefox 123 have also been added as targets, since
they're on the `baseline-widely-available` right now (just like Safari
17.4 in a few days), but we're not commited to supporting them, as it's
unlikely people are stuck on those browsers.
We're also officially declaring a Node.JS v22.12+ requirement for users
that install mermaid for build tools. This is mainly to avoid `npm
install` errors for our users, as we're a browser-based library and we
don't actually support running mermaid in Node.JS.
- esbuild now targets ES2024 and Safari 17.4
- TypeScript targets ES2024 with ES2024 lib; class-field semantics are pinned to
the legacy behaviour so object shapes do not change in this PR
- root and published package.json files declare engines.node >= 22.12.0
- fix an invalid `a?.b = c` assignment in architecture.spec.ts that the old ES2018
lowering used to hide
BREAKING CHANGE: Mermaid now requires ES2024 and Safari 17.4 or later.
Although Mermaid is a browser library, Mermaid also
declares a Node.JS v22.12+ requirement so it can update
using dependencies that also require Node.JS v22.12+,
without causing `npm install` errors for users.
…ri.17.4 Conflicts: packages/mermaid-layout-elk/package.json
…slint-plugin-compat Fixes the failing unit tests.
…ri.17.4 Fixes a merge conflict in `package.json` that GitHub seems to see, but git CLI can solve without conflicts.
`**/vitepress/**` and `**/scripts/**` are unnecessary, since the only thing they affect are in `packages/mermaid/src/docs`, which `**/docs/**` already handles. I've also ignored `**/__mocks__/**` since that is currently included, but is only needed for vitest.
Use a `v<MERMAID_RELEASE_VERSION>+` tag to specify that the ES2024 support will only happen in the next version. I've also added `ios_saf` and `edge` to our `browserlist`, even though they're identical to `safari` and `chrome`, so it matches our ESBuild `target`.
The gantt fixtures are dated around 1010-10-10 and three of them exercise the today marker, which the renderer places at new Date(). The inline spec they were migrated from installed a fake clock, but the mmd snapshot runner did not, so the marker rendered far outside the chart and the show/hide fixtures produced identical screenshots.
…lugin-compat build: lint for WebKit 15.4 with eslint-plugin-compat
…ri.17.4 [v12] chore!: require ES2024, Node.js 22.12, and Safari 17.4
…all three preview packages **The warning.** ELK's `ARROW_MAP` put the edge *type* `arrow_open` — which means "no arrowheads" — into the arrow-*type* slot, where `none` is the value `addEdgeMarker` reads as a deliberate absence; anything else it has no marker for is reported as unknown. Diagrams whose db sets `arrowTypeStart` itself (flowchart sets `'none'`) never reached that fallback, so this only showed up on diagrams that leave it unset — state diagrams, which warned once per edge. Rendering is unchanged. Both spellings produced no start marker, and neither appears in `markerOffsets`/`markerOffsets2`, so `markerPathOffset` returns 0 either way and no geometry moves. Only the log noise goes away. **The publish workflow.** It published `packages/mermaid` alone, so a preview could not be exercised the way users actually combine these: ELK bundled in mermaid, and ELK registered onto the tiny build that omits it. It now also publishes `@mermaid-js/layout-elk` and `@mermaid-js/tiny`, all three sharing one preview version so they install as a matching set. Both are already org-scoped, so unlike `mermaid` they need no rename. Their `mermaid` peer range is dropped from the preview artifact: the preview mermaid is published under a different name (`@mermaid-js/mermaid`), so that range can never be satisfied and would only emit an unmet-peer warning on every install. Found by a standalone browser harness that loads the packed artifacts and checks ELK/dagre in full mermaid, the dagre fallback in tiny, and registering the plugin onto tiny. The new unit test fails without the arrow-map fix. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…cation Three standalone HTML pages that load the *packaged* artifacts from node_modules and drive them in a real browser, so they exercise what consumers install rather than the source tree: - a) full mermaid runs ELK and dagre, ELK is the default, `elk.*` variants work - b) tiny, which omits ELK, still renders flowchart and state via dagre - c) registering @mermaid-js/layout-elk onto tiny actually gets you ELK `verify.mjs` serves the directory, runs all three headless, prints PASS/FAIL per check and exits non-zero on failure. `setup.mjs` packs the three packages from a checkout — the same tarballs `npm publish` uploads — or fetches published previews with `--from-registry`. Registered as a workspace package like tests/webpack, so playwright comes from `pnpm install`, and added to the changeset ignore list alongside the other private test packages. Two deliberate choices, both documented in the README because both were learned the hard way here: - Tarballs are extracted rather than `npm install`ed. The bundles are self-contained so they need no resolution, and installing a `file:` tarball whose version has not changed silently serves npm's cached copy — which means quietly verifying a stale artifact. - "Did ELK actually run?" is answered by comparing node `transform` attributes between engines, never by trusting config, so a silent fallback cannot pass a check that claims ELK was used. Warnings are attributed per render for the same reason; that is what surfaced the `arrow_open` warning fixed here. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…_internalsDoNotUse **Docs.** `config/layouts.md` now opens with what changed in v12: ELK is part of `mermaid` and is the default, the main package always includes it, and the tiny build is the supported way to have Mermaid without it — `@mermaid-js/layout-elk` stays published so tiny can opt back in. `config/usage.md` gains an "ELK and the tiny build" section explaining why tiny omits ELK, that diagrams asking for it fall back to Dagre rather than failing, and how to register the package on tiny, with snippets for both the script-tag and bundler cases. The seven diagrams whose layout now defaults to ELK — flowchart, class, ER, requirement, state, use-case, agentflow — have their "Default theme and look" section renamed to "Default theme, look and layout", naming ELK as the default, and their "previous appearance" example now pins `layout: dagre` alongside `theme: default` and `look: classic`. Without that the example no longer reproduced the pre-v12 rendering. `swimlanes`, `sequenceDiagram` and `venn` have the same section but are not ELK-laid-out, so they are untouched. `defaultAppearanceDocs.spec.ts` caught the rename, which is what it is for. It now accepts either heading and additionally requires the layout to be pinned back on ELK-laid-out pages — and requires it *not* to be pinned on the three that are not, so the two sets cannot drift. **Exports.** `createCommonLayoutRenderer`, `defaultMeasureLayout`, `paintLayoutData`, `clearLayoutRenderState` and `applyLineJumpsToSvg` are no longer top-level named exports; they live on `__internalsDoNotUse`, documented as outside SemVer. They reach straight into the renderer, so they change whenever it does — a new node shape or a change to edge painting alters their signatures — and as named exports every such change counted as breaking Mermaid's public API. Removing them outright was the other option, but `community/layout-makers-guide.md` documents building an external layout on `createCommonLayoutRenderer`, so the capability is advertised and third-party layouts would lose it. Hiding them keeps the capability and drops the SemVer promise, which was the actual concern. Guide updated with the new name and the caveat. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…t striped The hand-drawn fill drew 4px strokes 5.2px apart. Up close that is a texture; at the size a diagram is normally read it is diagonal banding, and the shape looks striped rather than filled. Strokes are now 1.5px at 1.5px spacing, so the fill reads as an even tint and the drawn edge quality is what carries the look. `solidStateFill` moves the same way. That is the striped fill behind state diagram start, end and fork markers, which now come out close to solid — which is what UML asks for there anyway, so the old stripes were arguably wrong. Swimlane lane titles pinned `fillWeight: 3` locally and so ignored the shared value entirely. They now inherit it, which is the point of the shared default. Lane bodies are unaffected either way: they pass `fill: 'none'`. Note this is a default, not a global. `userNodeOverrides` merges caller options last, so the shapes that opt out — note, classBox, erBox, hourglass and the rest that set `fillStyle: 'solid'` with `roughness: 0` — are untouched. One property worth knowing: 1.5px strokes at 1.5px spacing sit near the resolution floor, so the texture survives at full size and blurs to a flat tint when a diagram is scaled down. The previous coarser values downscaled without losing the hatch. That is the trade being made.
…ltRenderer
`defaultRenderer` in the `flowchart`, `class` and `state` config sections now
accepts `dagre`, and that is its default. `dagre-wrapper` and `dagre-d3` remain
as aliases selecting the same unified renderer, so existing configuration keeps
working untouched.
`defaultRenderer` picks the rendering engine, not the layout algorithm: a
diagram with `defaultRenderer: dagre` is still laid out by ELK, the v12 default,
unless `layout: dagre` is set as well. The two options were easy to confuse
while one of them was named after a layout library.
One behaviour change falls out of the aliasing: because `dagre-d3` now resolves
to `dagre`, `state: { defaultRenderer: 'dagre-d3' }` no longer selects the
legacy state renderer. State diagrams render through the unified renderer
whenever a renderer is configured.
Resolution lives in `diagram-api/defaultRenderer.ts` rather than being repeated
across detectors, so the three diagram families that expose the option agree on
what the names mean.
@aloisklink has opened mermaid-js#8223 against the same branch, removing `clearLayoutRenderState`, `createCommonLayoutRenderer`, `defaultMeasureLayout`, `paintLayoutData`, `applyLineJumpsToSvg` and the `CommonLayout*` types outright. That is the option he originally proposed and he owns the call, so this PR steps out of the way rather than editing the same export block and guaranteeing a conflict. Reverts `mermaid.ts`, the elk guard spec, the layout-makers guide section and the changeset. The docs work in this PR — ELK bundling, the tiny build, and the per-diagram default sections — is unrelated and stays. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Two corrections from review. `layouts.md` framed the change as "two consequences worth knowing", which buries the actual fact. It now states plainly that `elk` is the new default for the seven diagrams, lists them, and gives `layout: dagre` as the way back. `usage.md` said the tiny build "does not bundle the ELK layout engine either" from v12, which reads as though it once did. It never has — before v12 nothing bundled ELK, it was an opt-in `@mermaid-js/layout-elk` install. ELK now sits in the list of things tiny does not include, with no version qualifier, and the section says what actually changed: the main package gained ELK, so the tiny build is now the way to get a Mermaid without it rather than simply not installing the layout package. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…hot-tests fix(tests): run dagre specifik SS tests
Packet rows are numbered from bit 0 on the left up to the highest bit on the right, which suits network packets. Hardware registers are conventionally drawn the other way round, with the most significant bit on the left and bit 0 on the right. The new `packet.bitOrder` option selects between the two. It defaults to `ascending`, so existing diagrams render exactly as before; `descending` mirrors each row within `bitsPerRow`, so the row reads from its highest bit down to bit 0. Fields are still declared lowest bit first and keep their width, so switching a diagram between the two conventions only means changing `bitOrder`.
Also warns when a task end value is neither a valid date nor a valid duration, which is what makes a milestone declared without a duration fail silently. Refs mermaid-js#4121
feat(packet): support descending bit numbering
…g object
The ACTOR lexer rule required the name to sit directly against the
opening @{ of a config object, via a plain (?=@{) lookahead. Any space
in between (participant Bob @{ ... }) made every ACTOR rule miss, so
the line fell through to the INVALID token and the diagram failed to
parse with a confusing error, even though the same participant without
a config object tolerates trailing whitespace fine.
Widened the lookahead to (?=\s*@{) so the name is still recognized
once whitespace is skipped by the existing same-line whitespace rule.
…g-object-whitespace
fix(sequence): allow whitespace before a participant's @{ ... } config object
Adds the hand-curated corpus the improve loop hill-climbs on, a sweep spec over it with no floor assertion (an invalid fixture here is a target, not a build failure), and a Playwright-driven size-capture harness since the dev-explorer referenced in existing fixture metadata is not in this repo.
…nresolved-task-references fix(gantt): warn when an after/until reference or a duration cannot be resolved
… gap Targets complete_graph_k5 and edge-types, both invalid solely on edge-parallel-segment-too-close. Loop Fixtures total 51725.31 -> 52647.31, invalid 17 -> 16. HOLA parks a route's legs at whichever distance the step that made them cared about: a terminal leg at minTerminalLegLength (18), a route passing the same node at routingClearance (12). The two constants are chosen independently and nothing downstream compares them, so wherever a terminal leg runs alongside a passing route the pair lands exactly 6px apart -- under validateLayout's 7px EPS_PARALLEL_EDGE_GAP, structurally, every time. Re-tuning either constant does not work: on edge-types the colliding legs are 20 and 18, so raising the minimum fuses them into a shared subpath instead. This pass runs last, on final geometry, and separates only the pairs that actually collided. It moves interior segments only -- the first and last segment of a polyline carry the node-attached point -- and skips any shift that would invert a neighbouring leg, so routes it cannot fix safely are left as they were. Theory input unavailable: the papers-query subagent is not registered in this environment, so this rests on code evidence only and is not corpus-backed.
…ntegration docs: add Schema3D to community integrations
Targets edge-types, which the previous commit left invalid on edge-bend-near-endpoint. Loop Fixtures total 52647.31 -> 53607.31, invalid 16 -> 15. Separating two parallel runs is worthless if it parks one inside the band validateLayout keeps clear around the side an edge attaches to: the previous commit pushed edge-types' run from exactly 18 off its end node -- the threshold, and safe -- to 15, trading one hard issue for another on the same two edges. The guard mirrors nearEndpointBandDistance, both halves of it. A run is refused a target within EPS_ENDPOINT_BAND(18) of an adjacent terminal point only when it ALSO overlaps that node's extent along its own axis. Dropping the span test rejects far more than the validator would ever penalise; an earlier attempt did exactly that and was reverted. Spec set verified byte-identical to the pre-pass baseline: 31 failures before and after, none new, none fixed -- the pass does not touch it. Theory input unavailable: the papers-query subagent is not registered in this environment, so this rests on code evidence only.
Targets life-choices and nested-subgraphs-2, both invalid solely on edge-bend-near-endpoint. Loop Fixtures total 53607.31 -> 55378.06, invalid 15 -> 13. The previous commit taught the separation pass to refuse moving a run INTO the band validateLayout keeps clear around an edge's attachment side. This is the other half: a run already sitting in that band is moved out. life-choices had one at 12 where 18 is required, put there by the router, not by this pass. Runs are only ever moved directly away from the node they graze, so the terminal leg they share a corner with can only get longer -- edge-bend-near-endpoint also fails on legs under 10, and a lift that shortened one would trade the same issue straight back. Order matters: lift, then separate. Lifting can bring a run alongside another, and the separation pass that follows is what pulls those apart. Reversed, the lift would undo the separation's work. Spec set verified complete (1142 tests) and byte-identical to the pre-pass baseline: 31 failures, none new, none fixed. Theory input unavailable: the papers-query subagent is not registered in this environment, so this rests on code evidence only.
… Fixtures Corpus curation by the repo owner, recorded before the loop takes a baseline so keeps and reverts have a clean tree to work against. The fixture was invalid on three hard issues (edge-intersects-obstacle, edge-non-orthogonal, edge-shared-subpath), so it scored 0 and its removal does not move the total -- only cases 71 -> 70 and invalid 13 -> 12.
Targets domus1, invalid on the triple edge-same-port-departure + edge-shared-attachment-point + edge-shared-subpath. Loop Fixtures total 55378.06 -> 56348.06, invalid 12 -> 11. Those three issues are one geometric fact reported three ways. The orthogonal router takes a sourcePortOffset/targetPortOffset per route and defaults both to 0 -- the centre of the chosen side. Tree connectors get real offsets from spreadPorts; core edges never do. So every core edge on one side of a node attaches at the same point, departs in the same direction, and runs the same minTerminalLegLength leg before diverging. Endpoints are slid along the side they already attach to, with the bend travelling alongside, so the terminal leg only translates and the segment past the bend keeps its axis. The spread is 8, not EPS_SHARED_ATTACH's 3: clearing the attachment rule alone leaves the two legs running parallel 3px apart, which trips edge-parallel-segment-too-close instead. Two-point straight edges are collected as PINNED rather than skipped. They cannot be slid -- their other end is on a different node, so moving one end tilts the edge off-axis -- but they do occupy the port, and they are usually the reason it is contested. Skipping them entirely made the first version of this pass a no-op on domus1: the straight edge was invisible, so nothing collided. Also shrinks the shared hola/ corpus specs from 31 failures to 29: 'gives every edge touching one node its own attachment point' now passes for domus1 and for the HOLA paper main example. No new failures. Theory input unavailable: the papers-query subagent is not registered in this environment, so this rests on code evidence only.
…ther Targets 'GRAPH - hola 5 nodes loop + trees', invalid solely on edge-shared-subpath. Loop Fixtures total 56348.06 -> 57264.06, invalid 11 -> 10. The separation pass skipped exactly-collinear runs with a comment calling them 'a different defect with a different fix'. They are the same defect one step further on. Two runs 6px apart read as one thick line; two drawn over each other lose a connection entirely, which is strictly worse. There is no gap to widen and so no side worth preserving, so the pair is ordered by edge id. Any consistent choice draws both edges, and a stable one keeps the layout reproducible between runs. The existing guards still apply: a run is not moved into an endpoint band, and not past a neighbour's far end. Spec set verified complete (1142 tests) and identical to the previous commit's: 29 failures, none new. Theory input unavailable: the papers-query subagent is not registered in this environment, so this rests on code evidence only.
Targets life-choices, invalid solely on edge-bend-near-endpoint. Loop Fixtures total 57264.06 -> 58259.06, invalid 10 -> 9. The leg-inversion guard tested a candidate position against the min and max of the run's two neighbours. That is only correct when the run sits BETWEEN them. life-choices' edge is a U -- down from n2, across, up to ne -- so both neighbours are above the horizontal run, and moving it further down, which lengthens both legs, read as crossing past one and was refused. That is why the endpoint-band lift could never repair this fixture. Each neighbour is now judged on its own: the leg must keep its sign and stay at least MIN_LEG long. Strictly more permissive on U-shaped routes, no less safe anywhere else. Spec set verified complete (1142 tests) and identical to the previous commit's: 29 failures, none new. Theory input unavailable: the papers-query subagent is not registered in this environment, so this rests on code evidence only.
Targets nested-sg-outgoing-4, invalid solely on edge-corner-connection. Loop Fixtures total 58259.06 -> 59194.66, invalid 9 -> 8. An edge meeting a box at its corner gives the reader no cue which side it belongs to, and the arrowhead renders across two borders. validateLayout draws the line at EPS_CORNER(3); ports are now kept 6 clear of both ends of their own side, using the same slide as the spread pass -- endpoint and bend together, along the side -- so it inherits the same safety. It runs after the spread rather than before, because spreading a contested port is itself what pushes one out towards a corner. The interesting case is nested-sg-outgoing-4 itself: a single 'a --> b' whose ends land 1px above the bottom-right and bottom-left corners of two boxes. That edge has two points, so every pass here treats it as pinned -- moving one end of a straight edge tilts it off-axis. But its ends sit on PARALLEL OPPOSITE sides, and moving both by the same amount keeps it straight. That is the one case where a pinned endpoint can be repaired, and handling it is what took this commit from a no-op to the fix. Spec set verified complete (1142 tests) and identical to the previous commit's: 29 failures, none new. Theory input unavailable: the papers-query subagent is not registered in this environment, so this rests on code evidence only.
…-e2e fix(ci): shard scoped e2e runs to avoid snapshot timeouts
…cidence Targets both complete_graph_k4 copies, invalid on edge-same-port-departure + edge-shared-attachment-point + edge-parallel-segment-too-close. Loop Fixtures total 59194.66 -> 61002.66, invalid 8 -> 6. The port-spreading pass tested for contention with a 0.5 tolerance, so it only ever fired on ports the router had placed at identical offsets. validateLayout draws the line at EPS_SHARED_ATTACH(3). complete_graph_k4 puts three edges on one side of A, at 176.36, 177.70 and 190.36: the first two are 1.34 apart -- flagged by the instrument, invisible to a coincidence test. That is why a fixture the pass was written for survived it. Detecting at the validator's own threshold also re-spreads the whole side at 8, which moves the terminal legs with the ports and clears the fixture's edge-parallel-segment-too-close as well -- all three hard issues from one threshold. Spec set verified complete (1142 tests) and identical to the previous commit's: 29 failures, none new. Theory input unavailable: the papers-query subagent is not registered in this environment, so this rests on code evidence only.
Targets nested-subgraphs-reverse-order, invalid solely on edge-border-hugging. Loop Fixtures total 61002.66 -> 61906.89, invalid 6 -> 5. An edge may cross a group border; running ALONG it is what reads wrong, because route and frame become one line and the reader cannot see where the edge enters. 'b --> c' leaves b, turns, and runs 43.9 units 1px above the top of group A. The interior-run passes cannot reach this. That edge has three points, so it has no interior run at all -- both segments carry a node-attached point -- yet its middle leg is exactly the one skimming the frame. It is reachable as c's TERMINAL leg instead, moved by sliding c's attachment along its own side. The lift always moves the leg away from the rect it hugs. Moving it the other way clears the border by routing through the group's interior, trading edge-border-hugging for edge-intersects-obstacle. Spec set verified complete (1142 tests) and identical to the previous commit's: 29 failures, none new. Theory input unavailable: the papers-query subagent is not registered in this environment, so this rests on code evidence only.
Targets nested-subgraphs, subgraph-variation and event, all carrying node-too-close-to-group. Loop Fixtures total 61906.89 -> 63660.12, invalid 5 -> 3. validateLayout wants nodeGroupClearance (30) between a node and the border of any group it does not belong to. HOLA has no notion of the rule: nodes are placed, frames are fitted around their members, and nothing afterwards compares a frame to the foreign nodes beside it. The gap is whatever the two independent steps leave -- 26 on nested-subgraphs, four short. Moving a node after routing normally invalidates its edges, so the push is taken only when every attached edge can absorb that exact motion: an edge with a bend keeps its leg's direction if the bend travels on the axis the leg is constant in, and a two-point edge has no bend so it can only absorb motion parallel to itself. An edge carrying a LABEL blocks its node outright. A label is positioned against geometry this pass does not model, so moving the edge under it slides the label onto whatever is now there. The first version omitted this, scored identically, and broke architecture's 'keeps every edge label off every node box' -- in the shared hola/ corpus, which this loop does not even score. It was reverted; this is the label-aware variant, and it costs nothing here because the three fixtures it fixes have unlabelled edges. Spec set verified complete (1142 tests) and identical to the previous commit's: 29 failures, none new. Theory input unavailable: the papers-query subagent is not registered in this environment, so this rests on code evidence only.
…implementation 100 commits from the OSS repo since our merge-base (2026-09-03), taken from the develop head at cf83441 rather than the mermaid@12.0.0 tag, on the repo owner's explicit instruction. Conflict surface was two files: rendering-util/render.ts and rendering-util/types.ts. No upstream change touches layout-algorithms/hola/, so none of the HOLA layout work conflicts.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
📑 Summary
Brief description about the content of your PR.
Resolves #
📏 Design Decisions
Describe the way your implementation works or what design decisions you made if applicable.
📋 Tasks
Make sure you
MERMAID_RELEASE_VERSIONis used for all new features.pnpm changesetand following the prompts. Changesets that add features should beminorand those that fix bugs should bepatch. Please prefix changeset messages withfeat:,fix:, orchore:.