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: onerequire columnand tworequire rowstatements over nodes, one of them inside a labelled group, with edges into every constrained node. Pinned bycrates/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 itspreferspelling 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 OkwhereOksits inside theOutgroup and the other three outside it; tworequire rowstatements likewise pair a node outside the group with one inside. Pinned bycrates/core/tests/misc/landing_diagrams.rs(manually_constrained_flowchart_renders) on realized coordinates.site/examples/sources/nested-layout.sheeptext:column App: Web Apiover one group’s nodes, asserted satisfied bycrates/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
requirethat 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-degradedwarning 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.rsandcrates/language-service/src/diagnostics.rs: thelayout/intent-degradedwarning on a successful render, with span.crates/web-leptos/end2end/fixtures/required_row_pin_conflict.outcome.jsoncontains 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_*.sheepwith their snapshots undercrates/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 licences page.
Proof.
crates/core/assets/generated-icon-packs/: the ten packs, embedded withinclude_bytes!.site/landing/diagrams/hero.sheepandsite/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.tswithmise run layout:identity-survey: the native and WebAssembly identity survey overcrates/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: therendercommand, SVG and PNG.crates/core/tests/render/png_renderer_behavior.rs: PNG rasterization without a browser, including the refusal of any<text>element that would need one.This site. Every diagram on it, including the front page and every card on the examples page, was rendered at build time by that CLI (
mise-tasks/site/build,site/_ext/sheeptext_gallery.py).