Compare ======= What people ask other diagram-as-code tools for, and what SheepText does about it today. Every row names open issues on two public trackers: D2 (``d2lang/d2``) and Mermaid (``mermaid-js/mermaid``), read on 2026-09-02. It states what SheepText does, and names the checked-in fixture or test in the SheepText repository that proves it. Reaction counts are the trackers' own at that date. There are no pixel comparisons on this page and no claim without a fixture. The rows are limited to what ships now: relative order without alignment (``A left of B``), a row over some of a group's members, and size budgets are in progress and deliberately absent. Why the issues cluster ---------------------- Both trackers show one pattern. Every layout-control knob those tools offer is a *mode* that switches off part of the layout engine. D2's ``near`` pin refuses edges that cross a container boundary, its grid layout refuses edges altogether, Mermaid users draw invisible ``~~~`` links to force placement, and the bugs live where modes interact. Neither tool can say which of the author's statements conflict. SheepText represents "same row", "same column", "keep this order" and "wrap these in a container" as constraints on one solver, so a pinned node is still a node edges route into. When two statements cannot both hold, the diagnostic names them. Same row, same column, and edges into them ------------------------------------------ **The ask.** Mermaid: put two nodes on the same rank (`#3723 `_, 91 reactions, a re-file of an issue that was auto-closed), same level plus a chosen side (`#1360 `_, 120), ELK horizontal and vertical order hints (`#5420 `_, 48), an edge that does not alter the layout (`#2834 `_, `#3471 `_), force the order of subgraphs (`#7741 `_). D2: enforce horizontal alignment (`#949 `_), a horizontal relationship in flowcharts (`#260 `_), ``near`` refuses connections across a container boundary (`#1285 `_), grid layouts refuse edges (`#1687 `_). **What SheepText does.** ``row: A B`` and ``column: A B`` are statements of the language. A bare statement is a preference; ``require`` in front of it makes it a hard requirement the diagram is refused for if it cannot hold. Members can be nodes or groups, a member can sit inside a group while its row-mate sits outside, and edges route into constrained members like any other node. **Proof.** - ``site/landing/diagrams/flowchart_manual.sheep``: one ``require column`` and two ``require row`` statements over nodes, one of them inside a labelled group, with edges into every constrained node. Pinned by ``crates/core/tests/misc/landing_diagrams.rs`` (``manually_constrained_flowchart_renders``), which asserts the realized coordinates of every node in the column and both rows. - ``crates/core/tests/misc/leading_strength_syntax.rs`` (``a_bare_constraint_is_a_preference_and_a_name_changes_no_geometry``): a bare statement and its ``prefer`` spelling produce identical geometry. Constraints across a group boundary ----------------------------------- **The ask.** Mermaid: ``direction`` inside a subgraph is ignored (`#6438 `_, 14; `#2410 `_, 13), TB and LR swapped (`#7477 `_), a regression in a minor release (`#8066 `_). D2: per-container direction (`#495 `_, 9), and ``near`` refusing any edge that crosses the container it pins (`#1285 `_). **What SheepText does.** A group is a visible container, not a layout mode. A ``row`` or ``column`` may name nodes inside a group and nodes outside it in one statement, the constraint holds across the boundary, and edges cross it freely. A ``column`` scoped to the nodes of one group holds while the groups around it are laid out. **Proof.** - ``site/landing/diagrams/flowchart_manual.sheep``: ``require column MainSpine: Start Authn Authz Ok`` where ``Ok`` sits inside the ``Out`` group and the other three outside it; two ``require row`` statements likewise pair a node outside the group with one inside. Pinned by ``crates/core/tests/misc/landing_diagrams.rs`` (``manually_constrained_flowchart_renders``) on realized coordinates. - ``site/examples/sources/nested-layout.sheeptext``: ``column App: Web Api`` over one group's nodes, asserted satisfied by ``crates/core/tests/misc/landing_diagrams.rs`` (``published_group_scoped_layout_statements_are_honoured_not_dropped``). What is not claimed here. A ``direction`` written inside a group body: ``direction`` is diagram-wide today and no fixture exercises a per-group one. And a ``row`` over group *handles*: in that same example ``row Tiers: Edge App Data`` is a preference the engine currently drops. The groups still land side by side, top-aligned, and the outcome records the drop, and the law above pins that exact state so the page changes when the engine does. Orthogonal routes and no node overlap ------------------------------------- **The ask.** Mermaid: straight lines (`#2817 `_, 130), right-angle arrows instead of curves (`#2549 `_, 75), subgraph labels overlapped by nodes (`#3806 `_, 54; `#1209 `_, 62). D2: completely straight connections (`#1537 `_), lines overlapping (`#1518 `_). **What SheepText does.** Every automatic route is orthogonal: axis-aligned segments, checked against the padded rectangle of every node it does not attach to. Rendered node ink never overlaps: node clearance is a hard admissibility predicate, and a diagram for which the search finds no admissible layout is *refused* with a diagnostic rather than drawn with overlaps. That refusal is real: two of the nine catalogued showcase diagrams currently refuse with ``layout/no-admissible-terminal-found`` because the search found no layout passing every hard predicate, of which node clearance is one, and are therefore not on the examples page. **Proof.** - ``crates/core/tests/rendered_node_clearance.rs``: asserts disjoint node ink on the published flowchart and incident-response diagrams (``production_frontdoor_http_auth_renders_with_disjoint_node_ink``, ``production_frontdoor_incident_response_renders_with_disjoint_node_ink``), on adversarial fixture families, and on multi-line icon captions on every side. - ``crates/core/src/layout/baseline/routing/orthogonal_evidence.rs``: the per-route evidence every automatic route carries: which obstacles it was checked against and whether it cleared them. - ``ci/published-diagram-accepted-failures.toml``: the two refusing showcases, each with its cause code and owner task. The inventory records the refusal, not which predicate blocked the search; the point is that a diagram the engine cannot lay out admissibly is refused rather than drawn. Diagnostics that name your statements ------------------------------------- **The ask.** Mermaid: an error that says only "Syntax error in text" (`#4432 `_), imprecise errors (`#6904 `_), the wrong line number (`#3853 `_), input silently truncated (`#8054 `_). D2: a ``validate`` command with structured output (`#2703 `_, `#2628 `_); the refusals D2 does issue name a rule, not the statements involved (`#1285 `_). **What SheepText does.** Three distinct outcomes, distinguished by class rather than by message text: - A ``require`` that contradicts a pinned position refuses the diagram, and the message quotes the name you gave the statement or, for an unnamed one, names its members, never a derived identifier. That is the path the law below pins; other refusal paths still quote engine identifiers today, and the repository backlog records them with a fix task rather than this page claiming otherwise. - A preference that was dropped still renders, and the outcome carries a ``layout/intent-degraded`` warning with the statement's id, its authored strength and its source span, in the serialized layout outcome, the language service and the editor. - A set of constraints that cannot all hold is reported with *every* member of the contradiction, not a representative one. **Proof.** - ``crates/core/tests/misc/leading_strength_syntax.rs`` (``a_refusal_quotes_the_authored_name_or_names_the_members``). - ``crates/core/tests/layout_outcome_frontdoors.rs`` (``static_failure_is_tagged_and_structurally_has_no_geometry``): a refused outcome carries a typed failure class and no geometry. - ``crates/web-leptos/src/compile_payload/tests.rs`` and ``crates/language-service/src/diagnostics.rs``: the ``layout/intent-degraded`` warning on a successful render, with span. - ``crates/web-leptos/end2end/fixtures/required_row_pin_conflict.outcome.json`` contains the same refusal as the browser editor publishes it. - ``crates/core/src/layout/constraint_aware_separation/witness/tests.rs`` (``two_disjoint_cycles_do_not_produce_a_mixed_witness``, ``members_that_resolve_equal_are_both_kept``): every member of an unsatisfiable cycle is retained. - ``crates/core/tests/fixtures/declarative_closure/layout-intent/conflict_*.sheep`` with their snapshots under ``crates/core/tests/misc/snapshots/``: refusals for an ambiguous container, an unresolved handle and a route policy that contradicts a manual path, each deterministic. Icon packs that work offline ---------------------------- **The ask.** Mermaid: icon packs that work in the live editor, GitHub and the CLI (`#5950 `_, 75), official AWS, GCP and Azure icons (`#6109 `_, 70), SVG images as nodes (`#1723 `_, 143), custom icons that work across environments (`#7308 `_). **What SheepText does.** Ten icon packs (AWS, Azure, Google Cloud, Kubernetes, devicon, heroicons, lucide, simple icons, tabler and the Open Security Architecture set) are compiled into the native binary and shipped as lazily loaded shards inside the editor bundle. ``use icons aws`` then ``icon aws:aws-lambda`` resolves without a network request to anyone. The licences are on the :doc:`licences page `. **Proof.** - ``crates/core/assets/generated-icon-packs/``: the ten packs, embedded with ``include_bytes!``. - ``site/landing/diagrams/hero.sheep`` and ``site/examples/showcases/aws_chat.sheep``: icon diagrams rendered at build time by the CLI on the build machine, with no icon fetched. - ``crates/cli/tests/qualified_icon_bindings.rs``: qualified pack bindings through the CLI. Deterministic output, native and in the browser ----------------------------------------------- **The ask.** Mermaid: architecture diagrams re-laid out on every reload (`#6024 `_, 12, Approved; `#6166 `_), ELK reshuffles on every edit (`#6527 `_), inconsistent orientation and ordering (`#2789 `_). D2: adding one label reorganizes the diagram (`#1141 `_), symmetric input gives asymmetric output (`#724 `_), nodes lose their declared order once nested (`#1995 `_). **What SheepText does.** The same source produces the same geometry and, natively, the same SVG bytes. The layout search runs a fixed, seeded iteration budget of deterministic work, not a wall-clock cut-off, so the result does not depend on the machine's speed. Geometry and SVG for the published diagrams are snapshot-pinned on the native build, and the browser build is the same engine compiled to WebAssembly, surveyed against native on its layout evidence over a fixed corpus. No checked-in test compares native and browser SVG bytes, so that stronger claim is not made here. **Proof.** - ``crates/core/tests/layout_schema_snapshots.rs`` (``baseline_layout_geometry_snapshots_are_deterministic_for_migration_fixtures``) checks each fixture twice; the two results must be equal, and the result is pinned as a snapshot. - ``crates/core/tests/misc/landing_diagrams.rs``: byte-for-byte SVG snapshots of every diagram on the front page. - ``crates/web-leptos/end2end/tests/layout-identity-survey.spec.ts`` with ``mise run layout:identity-survey``: the native and WebAssembly identity survey over ``crates/core/tests/fixtures/experiment_corpus/manifest-v1.json``, hashing layout evidence rather than SVG. This is an on-demand survey, not a per-commit gate. What is not claimed here: that adding one edge moves nothing else. That is a property SheepText does not yet pin with a fixture. Rendering without a browser --------------------------- **The ask.** Mermaid: server-side rendering (`#3650 `_, 68), SVG that works outside a browser (`#58 `_, open since 2014). D2: drop the Playwright driver check (`#449 `_), use resvg instead of Chromium (`#1560 `_). **What SheepText does.** ``sheeptext render`` is one native binary. Text is shaped with bundled fonts and written into the SVG as glyph outlines, so the file looks the same in every viewer and embeds without a font dependency; PNG goes through ``resvg`` in-process. No browser, no driver, no headless anything. The editor at ``/editor/`` runs the same engine compiled to WebAssembly. **Proof.** - ``crates/cli/src/cli/commands/render.rs``: the ``render`` command, SVG and PNG. - ``crates/core/tests/render/png_renderer_behavior.rs``: PNG rasterization without a browser, including the refusal of any ```` element that would need one. - This site. Every diagram on it, including the front page and every card on the :doc:`examples page `, was rendered at build time by that CLI (``mise-tasks/site/build``, ``site/_ext/sheeptext_gallery.py``).