Package map
What lives in each of the thirteen packages, and where to make a given change.
A TypeScript monorepo under packages/*, built with project references
(tsc -b) plus an esbuild bundle for the runtime client.
packages/
├─ diagnostics error/warning format, codes (MRW-Exxx), snippets, suggestions
├─ model the normalized in-memory topology every module speaks
├─ scene the geometric contract (boxes, polylines) + geometry helpers
├─ metrics deterministic text measurement (static glyph tables)
├─ icons icon packs: manifest, generator, search, suggestions
├─ schema YAML parse → strict validation (line/col, did-you-mean) → model
├─ layout classify → place (ELK / compose / polar / circle) → route (libavoid)
├─ lint geometry linter: L1 fails builds, L2 auto-repairs, L3 advises
├─ render-svg scene → SVG (hero tier) or canvas host (scale tier)
├─ runtime the ~20 KB client: camera, inspector, spotlight, minimap, export
├─ site collapse, page planner, single-file + multi-page compiler
├─ ui shadcn-compatible React component suite (@marrow/ui)
├─ server dev server: watch, rebuild, SSE live reload, /api
└─ cli the marrow commandMonorepo Package Architecture
The 14 monorepo packages organized by architectural layer and dependency direction
Supporting trees at the repo root:
icon-packs/ drop-in packs (manifest.json + svg/) — cisco, affinity, k8s, generic
committed; aws, azure, gcp fetched per-clone
fixtures/ valid / invalid / corpus / v1 topologies driving the test suite
input/ topology YAML (yours)
output/ generated HTML (gitignored)
docs/ this site
legacy-v1/ archived Python v1, untracked, reference onlyWhere to make a change
| You want to change | Package | Notes |
|---|---|---|
| A new YAML key, or validation behavior | schema | Also add the code to diagnostics and document it in the reference |
| A diagnostic message, hint, or suggestion | diagnostics | Codes are permanent — add, never renumber |
| Which archetype a topology gets | layout/classify.ts | Precedence order is documented and load-bearing |
| Node placement | layout/elk.ts, compose.ts, presets.ts | |
| Edge routing or the tidy pass | layout/router.ts, tidy.ts | |
| Port chip placement | layout/portchips.ts | L1 rule port-chip-overlap guards it |
| What fails a build | lint | Moving a rule between L1 and L3 is a breaking change in practice |
| How something looks | render-svg and runtime/client/canvas-engine.ts and site/styles.ts | All three, always |
| Interaction, chrome, keyboard | runtime/client/main.ts + site/shell.ts + site/styles.ts | Vanilla only — no React |
| Page splitting, what earns a page | site/planner.ts | Thresholds are exported constants |
| Dev server or its API | server | |
| A command or flag | cli | Update the CLI reference too |
| Icon search, ranking, fetching | icons |
Key files
| Path | Why it matters |
|---|---|
packages/diagnostics/src/codes.ts | The stable code registry. Public contract |
packages/layout/src/classify.ts | The six archetypes and their precedence |
packages/site/src/planner.ts | SITE_DEVICE_THRESHOLD = 150, SITE_ZONE_THRESHOLD = 5 |
packages/site/src/styles.ts | The complete diagram stylesheet and design system |
packages/site/src/brand.ts | Branding types and logo sanitizing |
packages/schema/src/zodSchema.ts | The validation shape |
packages/cli/src/index.ts | Usage strings and exit codes |
Cross-cutting gotchas
Port chips carry data-edge and data-node. Runtime element maps must use
class-qualified selectors — .m-node[data-node], .m-edge[data-edge] — or they
match chips as well as the things they're meant to match.
Text measurement is static. @marrow/metrics measures at the exact pixel
size and letter-spacing the renderers use. Don't reach for a DOM measurement;
it breaks determinism across machines.
Sorting. Anywhere order would otherwise be arbitrary, sort by id. Determinism is a hard requirement, not a nice-to-have.