npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

compasso

v0.9.0

Published

Standards-faithful relational, analytical and engineering diagrams (genogram, ecomap, fault tree, fishbone, pedigree, phylogenetic tree, org chart, PRISMA 2020 flow, UML class diagram, ladder logic, single-line, P&ID) as pure SVG strings. Deterministic, z

Readme

compasso

Standards-faithful technical diagrams as pure SVG strings. Deterministic, zero runtime dependencies, server-safe (no DOM, no canvas, no clock, no randomness).

Twelve diagrams shipped: the genogram (McGoldrick family-systems notation), the ecomap (radial person↔environment ties), the fault tree (NUREG-0492 / IEC 61025 distinctive-shape notation), the fishbone (Ishikawa cause-and-effect), the pedigree (Bennett 2008 standardized clinical-genetics nomenclature), the phylogenetic tree (cladogram + phylogram), the org chart (top-down reporting hierarchy), the PRISMA 2020 flow diagram (systematic-review reporting), the UML class diagram (declared-position class model, with optional inheritance auto-layout), and the electrical/industrial symbol-library familyladder logic (IEC 61131-3), single-line (IEC 60617 / IEEE 315) and P&ID (ISA-5.1). The architecture is built for more — see the roadmap.

Gallery

Every image below is a committed SVG in examples/svg/ — rendered by the current code and pinned byte-for-byte by test/snapshots/gallery.test.ts, so the pictures can never drift from what compasso emits. Reproduce them all with pnpm build && node examples/demo.mjs.

Principles

  • Pure data → string. Every renderer is a pure function: same input, same SVG, in any runtime (browser, Node, edge, tests).
  • Honesty rule. The diagram presents only what the caller declared — no synthesized structure (two declared parents without a declared union never merge into a couple), no interpretation (a line style is a conservative lexical hint from the author's own words; ambiguity abstains to neutral). Verbatim text always rides each element's <title>.
  • Overlap-proof by construction. The genogram layout is a grid of columns, rows, gutters and corridors with lane allocation — labels and routed lines cannot collide, and a test harness proves it on every commit. No diagonals, no text on edges.
  • Embedder-safe output. Numeric width/height + matching viewBox, literal presentation attributes (no CSS classes, no currentColor, no <marker> defs), XML-escaped everywhere — safe for innerHTML and for SVG-to-PDF pipelines.
  • Localizable. English defaults; vocabularies (labels, kinship words, quality lexicons) are injectable packs. compasso/locales/pt-br ships Brazilian Portuguese.

Install

pnpm add compasso

Genogram

import { genogramSvg } from "compasso/genogram";

const { svg } = genogramSvg({
  people: [
    { id: 1, label: "my mother", sex: "female", deceased: false, generation: -1 },
    { id: 2, label: "my father", sex: "male", deceased: true, generation: -1 },
    { id: 3, label: "me", sex: "unknown", deceased: false, generation: 0 },
  ],
  unions: [{ id: 1, personAId: 1, personBId: 2, status: "divorced", quality: null }],
  parentLinks: [
    { id: 1, parentId: 1, childId: 3, quality: null },
    { id: 2, parentId: 2, childId: 3, quality: "distant for years" },
  ],
  relationships: [],
});

Union statuses: married | cohabiting | dating | separated | divorced | coparental | unknown — each drawn with its McGoldrick notation (solid bar, dashed bar, one slash, two slashes). A coparental union draws no couple element at all: the co-parenthood shows only on the descents converging on the child.

Free-text relationships are classified conservatively: derived kinship words (sibling, grandparent, uncle…) draw nothing (the tree already implies them), direct parentage words may be promoted to a dotted descent oriented by declared generation, everything else stays a non-structural bond line. People left with no drawn connection are grouped and labeled, never floated silently.

Lower-level entry points: computeGenogramLayout (pure layout: nodes, routed orthogonal elements, traceable data-edge-ids) and genogramLayoutSvg (layout → string), for callers that decorate, hit-test, or post-process.

Quality color palette (opt-in)

Bond lines and relationship lines can optionally encode the quality taxonomy as stroke color. Monochrome by default — omit qualityPalette and the output is unchanged.

import { genogramSvg, type QualityPalette } from "compasso/genogram";

const palette: QualityPalette = {
  close:    "#22c55e",  // green
  distant:  "#60a5fa",  // blue
  conflict: "#f97316",  // orange
  cutoff:   "#94a3b8",  // slate
};

const { svg } = genogramSvg(input, { qualityPalette: palette });

The taxonomy buckets (close | distant | conflict | cutoff) are locale-independent and reuse the same classifier that drives line-weight/dash notation. A color is applied only when the classifier returns a single unambiguous bucket — null, empty, ambiguous, or negated quality always draws in the neutral ink. Partial palettes are fine: omit any bucket key to keep that style in neutral ink. All values must be literal hex strings (e.g. "#a855f7") — no CSS, no currentColor — so the SVG remains safe for PDF embedding.

The legend swatch for each quality style also uses the palette color when opted in.

Ecomap

import { ecomapSvg } from "compasso/ecomap";

const svg = ecomapSvg({
  centerLabel: "Me",
  ties: [
    { id: 1, label: "work", quality: "draining", direction: "out" },
    { id: 2, label: "church", quality: "very close", direction: "in" },
    { id: 3, label: "neighbors", quality: null, direction: null }, // neutral line, no arrow
  ],
});

Arrowheads appear only for a declared direction ("in" toward the center, "out" toward the system, "both"); null draws no arrow — never a default. Large sets split onto two alternating rings; node spacing is overlap-proof by construction.

Quality color palette (opt-in)

The same optional quality palette is available on the ecomap, coloring tie lines and arrowheads. The contract is identical to the genogram palette.

import { ecomapSvg, type QualityPalette } from "compasso/ecomap";

const palette: QualityPalette = {
  close:    "#22c55e",
  distant:  "#60a5fa",
  conflict: "#f97316",
  cutoff:   "#94a3b8",
};

const svg = ecomapSvg(input, { qualityPalette: palette });

Honesty rule: a color is a styling hint, conservative by design. It asserts nothing the caller did not declare — the verbatim quality word always rides the element's <title>, and a color is never emitted for unstated or ambiguous quality.

Fault tree

import { faultTreeSvg } from "compasso/fault-tree";

const { svg, layout } = faultTreeSvg({
  topId: 1,
  events: [
    { id: 1, kind: "intermediate", label: "Pump system fails", code: "TOP" },
    { id: 2, kind: "basic", label: "Motor winding failure", code: "B1" },
    { id: 3, kind: "undeveloped", label: "Control logic fault", code: "U1" },
  ],
  gates: [{ id: 1, type: "or", eventId: 1, inputIds: [2, 3] }],
});

NUREG-0492 distinctive shapes: rectangles (intermediate events), circles (basic), diamonds (undeveloped), house (expected external events), oval (conditioning events attached to INHIBIT gates), transfer triangles. Gates: and | or | xor | inhibit | vote (k-of-n, drawn with its threshold). The layout is deterministic, orthogonal and overlap-proof (proven by its own test harness).

A fault tree is a logic artifact, so structurally invalid input is refused, not repaired: faultTreeSvg throws FaultTreeValidationError listing every issue with a stable machine-readable code (event-without-gate, cycle, unknown-input, …). Honest incompleteness has standard notation instead — that's what undeveloped and transfer are for.

Fishbone

import { fishboneSvg } from "compasso/fishbone";

const svg = fishboneSvg({
  effectLabel: "Late deliveries",
  categories: [
    {
      id: 1,
      label: "People",
      causes: [
        { id: 1, label: "Driver shortage", subCauses: [{ id: 1, label: "High turnover" }] },
      ],
    },
  ],
});

Classic Ishikawa: spine into the effect head, category bones alternating above/below, horizontal cause twigs, one level of sub-causes, arrowheads converging toward the effect at every level (suppressible via arrowheads: false). Declared order is honored — significance-near-the-head is the analyst's call, never re-sorted. Label spacing is driven by measured text widths, so labels never collide.

Pedigree

import { pedigreeSvg } from "compasso/pedigree";

const { svg } = pedigreeSvg({
  conditions: [{ id: 1, label: "Cystic fibrosis" }],
  individuals: [
    { id: 1, label: "I-1", sex: "male", generation: 1, deceased: true, carrier: true, role: null, lifeStatus: "alive", affectedBy: [] },
    { id: 2, label: "I-2", sex: "female", generation: 1, deceased: false, carrier: true, role: null, lifeStatus: "alive", affectedBy: [] },
    { id: 3, label: "II-1", sex: "female", generation: 2, deceased: false, carrier: false, role: "proband", lifeStatus: "alive", affectedBy: [1] },
  ],
  matings: [{ id: 1, partnerAId: 1, partnerBId: 2, consanguineous: false }],
  sibships: [{ id: 1, matingId: 1, childIds: [3], twinGroups: [] }],
});

Standardized clinical-genetics notation (Bennett et al. 2008): square/circle/diamond by sex, filled glyph = affected (multiple conditions shown as up to four vertical partitions), carrier center dot, deceased slash, proband filled arrow / consultand open arrow, consanguineous matings as a double line, MZ/DZ/unknown-zygosity twins, Roman-numeral generations with within-generation "II-3" addresses. A pedigree is a clinical record, so structurally invalid input is refused with coded issues (PedigreeValidationError), never silently repaired. The layout is deterministic and overlap-proof — including remarriages and multi-spouse hubs.

Phylogenetic tree

import { phyloSvg } from "compasso/phylo";

const { svg } = phyloSvg({
  rootId: 1,
  nodes: [
    { id: 1, label: "root" },
    { id: 2, label: "Clade A", support: 98 },
    { id: 3, label: "Homo sapiens" },
    { id: 4, label: "Pan troglodytes" },
  ],
  edges: [
    { id: 1, parentId: 1, childId: 2, length: 0.4 },
    { id: 2, parentId: 2, childId: 3, length: 0.6 },
    { id: 3, parentId: 2, childId: 4, length: 0.55 },
  ],
}, { mode: "phylogram", showSupport: true });

Rectangular cladogram or phylogram. In phylogram mode each node's x is its cumulative branch length from the root (fit to width); in cladogram mode tips right-align with optional dotted extensions. Optional bootstrap/posterior support values at clades and a distance scale bar (phylogram only). The flat {rootId, nodes, edges} AST is Newick-importable. Branch lengths are never lost — a null/zero length is valid (the node sits at its parent's depth), and the verbatim length always rides the branch <title>.

Org chart

import { orgChartSvg } from "compasso/org-chart";

const { svg } = orgChartSvg({
  positions: [
    { id: 1, name: "Alex Mercer", title: "CEO", subtitle: null, vacancy: "filled" },
    { id: 2, name: "Priya Nair", title: "Executive Assistant", subtitle: null, vacancy: "filled" },
    { id: 3, name: "Dana Brooks", title: "VP, Engineering", subtitle: null, vacancy: "filled" },
    { id: 4, name: "", title: "Staff Engineer", subtitle: null, vacancy: "vacant" },
  ],
  reports: [
    { id: 1, managerId: 1, reportId: 2, kind: "assistant" },
    { id: 2, managerId: 1, reportId: 3, kind: "line" },
    { id: 3, managerId: 3, reportId: 4, kind: "line" },
  ],
});

A top-down reporting hierarchy with a deterministic, overlap-proof tidy-tree layout (fully orthogonal — no diagonals). Three reporting kinds: line (the solid primary report that defines the spine), assistant (a staff/EA role on a side-stem), and dotted (a secondary / matrix line, routed clear of every box). A vacant position draws a dashed open-seat box. The hierarchy is read from the declared reports — there is no depth or order field, and multiple roots (a forest) are drawn exactly as declared. An org chart is authoritative, so structurally invalid input is refused with coded issues (OrgChartValidationError: cycle, multiple-managers, unknown-manager, …), never silently repaired.

Lower-level entry points: computeOrgChartLayout (pure layout) and orgChartLayoutSvg (layout → string).

PRISMA 2020 flow

import { prismaSvg } from "compasso/prisma";

const { svg } = prismaSvg({
  variant: "flow",
  boxes: [
    { id: 1, phase: "identification", kind: "flow", column: "main", rank: 0,
      heading: "Records identified", counts: [{ label: "Databases", n: 1250 }] },
    { id: 2, phase: "screening", kind: "flow", column: "main", rank: 0,
      heading: "Records screened", counts: [{ label: "n", n: 1124 }] },
    { id: 3, phase: "screening", kind: "exclusion", column: "main", rank: 0,
      heading: "Records excluded", counts: [{ label: "n", n: 796 }] },
    { id: 4, phase: "included", kind: "flow", column: "main", rank: 0,
      heading: "Studies included", counts: [{ label: "n", n: 55 }] },
  ],
  arrows: [
    { id: 1, fromId: 1, toId: 2 },
    { id: 2, fromId: 2, toId: 3 }, // flow → exclusion side-box
    { id: 3, fromId: 2, toId: 4 },
  ],
});

The PRISMA 2020 flow diagram for systematic-review reporting. Boxes drop through fixed phase bands (identificationscreeningincluded); flow boxes stack by rank in the main column, exclusion boxes sit to the right of their same-rank flow box. Counts are verbatim and never summed — a published figure is the author's, drawn faithfully; n: null draws the localized "n = —" token, never an inferred total. Variants: flow, flow-with-prior (a two-column new/previous merge) and flow-other-methods. A mis-stated flow (duplicate id, backward arrow, non-finite count, …) is refused with coded issues (PrismaValidationError), never silently repaired. Lower-level: computePrismaLayout / prismaLayoutSvg.

UML class diagram

By default compasso does not compute UML layout. You declare the grid cell (col, row) for every class; packGrid then guarantees the boxes never overlap and every edge routes through the box-free gutters between cells. This is deliberate — provably overlap-free auto-layout of a general graph is not possible, so positions are the author's to declare (and to nudge for clarity), which is the honest, overlap-provable model.

import { umlSvg } from "compasso/uml";

const { svg } = umlSvg({
  classes: [
    { id: 1, name: "Shape", stereotype: null, isAbstract: true, col: 0, row: 0,
      attributes: [], operations: [{ visibility: "public", text: "area(): double" }] },
    { id: 2, name: "Circle", stereotype: null, col: 0, row: 1,
      attributes: [{ visibility: "private", text: "radius: double" }], operations: [] },
  ],
  relationships: [
    { id: 1, kind: "generalization", sourceId: 2, targetId: 1, label: null,
      sourceMultiplicity: null, targetMultiplicity: null, sourceRole: null, targetRole: null },
  ],
});

Three compartments (name / attributes / operations) with + − # ~ visibility glyphs, a static-member underline, italic abstract names and «stereotype» lines. You pick a relationship kind and the standard glyph follows from RELATION_GLYPHS — you cannot mis-state the notation: association, directed-association, aggregation (hollow diamond), composition (filled diamond), generalization / realization (hollow triangle; realization dashed) and dependency (dashed open arrow). Multiplicities, roles and labels are drawn verbatim, never parsed. A model error — a duplicate id, two classes in one cell, a class inheriting itself, an inheritance cycle, or more incident edges than a box side can host — is refused with coded issues (UmlValidationError: duplicate-id, cell-collision, self-generalization, generalization-cycle, too-many-side-edges, …). For valid diagrams the box, segment-through-box, collinear, non-orthogonal and glyph-overlap classes are all zero; perpendicular edge crossings are not a defect (a crossing is not a lie). Edge multiplicity/role/label text is reserved into the gutter band so it never overflows a cell; self-loop text sits in the vertical gutter strip beside the loop and carries a documented bound — an unusually long role on a self-association can exceed that strip (multiplicities, the common case, always fit). Lower-level: computeUmlLayout / umlLayoutSvg.

Optional inheritance auto-layout

When the inheritance structure should drive the layout, pass autoLayout: true and omit col/row — a pure pre-pass computes them from the generalization + realization edges by longest-path layering (every supertype strictly above its subtypes), then feeds the same declared-position layout, so the overlap proof is unchanged. It only generates cells; it never routes or draws.

import { umlSvg } from "compasso/uml";

const { svg } = umlSvg(
  {
    classes: [
      { id: 1, name: "Shape", stereotype: null, isAbstract: true, attributes: [], operations: [] },
      { id: 2, name: "Circle", stereotype: null, attributes: [], operations: [] },
      { id: 3, name: "Square", stereotype: null, attributes: [], operations: [] },
    ],
    relationships: [
      { id: 1, kind: "generalization", sourceId: 2, targetId: 1, label: null,
        sourceMultiplicity: null, targetMultiplicity: null, sourceRole: null, targetRole: null },
      { id: 2, kind: "generalization", sourceId: 3, targetId: 1, label: null,
        sourceMultiplicity: null, targetMultiplicity: null, sourceRole: null, targetRole: null },
    ],
  },
  { autoLayout: true }, // Shape lands on row 0; Circle and Square below it
);

col/row are optional only under auto-layout; with it off they are required and a missing cell is refused (missing-cell). Any declared cell is ignored when the pre-pass runs. A class in no inheritance edge is a root (top row). The graph must be acyclic — a generalization-cycle is refused as usual. Auto-layout places one supertype's subtypes across a single row, so an interface with a very large fan-in can exceed a box side's edge capacity and is then refused with too-many-side-edges (the honest reject — split it or declare cells manually). Run the pre-pass standalone with umlAutoLayout(input) to inspect or further nudge the generated cells. The diagram-agnostic layering primitive is layerByLongestPath in compasso/core.

Ladder logic (IEC 61131-3)

import { ladderSvg } from "compasso/ladder";

const { svg } = ladderSvg({
  rungs: [
    {
      id: 1,
      comment: "Motor start/stop seal-in",
      logic: {
        kind: "series",
        items: [
          { kind: "parallel", branches: [
            { kind: "contact", type: "no", operand: "Start_PB" },
            { kind: "contact", type: "no", operand: "Run" },
          ] },
          { kind: "contact", type: "nc", operand: "Stop_PB" },
        ],
      },
      coils: [{ type: "normal", operand: "Run" }],
    },
  ],
});

Two power rails with rungs stacked between them. A rung's logic is a freely-nested series/parallel network of contacts (no | nc | rising | falling) driving one or more coils (normal | negated | set | reset), right-aligned to the right rail. The layout is overlap-proof by construction: a recursive series/parallel pack centers every node on its rail line and gives each parallel its own dedicated verticals, so no two wires ever collide (proven by a permanent fuzzer). Rung comments wrap to fit between the rails; the verbatim text rides the comment's <title>. Lower-level entry points: computeLadderLayout / ladderLayoutSvg.

Single-line diagram (IEC 60617 / IEEE 315)

import { singleLineSvg } from "compasso/single-line";

const { svg } = singleLineSvg({
  devices: [
    { id: 1, kind: "utility", label: "Utility 13.8kV", rating: "3PH 60Hz" },
    { id: 2, kind: "breaker", label: "MAIN-CB", rating: "1200A" },
    { id: 3, kind: "transformer", label: "TX-1", rating: "1500kVA" },
    { id: 4, kind: "busbar", label: "MCC-1", rating: "480V" },
    { id: 5, kind: "motor", label: "P-101", rating: "150HP" },
  ],
  feeds: [
    { id: 1, fromId: 1, toId: 2 }, { id: 2, fromId: 2, toId: 3 },
    { id: 3, fromId: 3, toId: 4 }, { id: 4, fromId: 4, toId: 5, label: "Feeder 1" },
  ],
});

Sources at the top, power flowing down through utility | generator | transformer | breaker | fuse | disconnect | motor | load | busbar symbols connected by feeds (conductors). A device with several downstream feeds fans out through a busbar. Scope is radial: each device has at most one upstream feed; a second incoming feed is refused (multiple-feeds) rather than misdrawn — model ties separately. Forests (independent sources) are fine. Lower-level entry points: computeSingleLineLayout / singleLineLayoutSvg.

P&ID — Piping & Instrumentation (ISA-5.1)

import { pidSvg } from "compasso/pid";

const { svg } = pidSvg({
  components: [
    { id: 1, kind: "vessel", tag: "V-101", label: "Feed Drum", col: 0, row: 0 },
    { id: 2, kind: "pump", tag: "P-101", col: 1, row: 0 },
    { id: 3, kind: "control-valve", tag: "FCV-101", col: 2, row: 0 },
    { id: 4, kind: "exchanger", tag: "E-101", col: 3, row: 0 },
    { id: 5, kind: "instrument", tag: "FT-101", col: 2, row: 1 },
  ],
  lines: [
    { id: 1, fromId: 1, toId: 2, type: "process", label: "Feed" },
    { id: 2, fromId: 2, toId: 3, type: "process" },
    { id: 3, fromId: 3, toId: 4, type: "process" },
    { id: 4, fromId: 5, toId: 3, type: "connection" },
  ],
});

A declared-position grid (col/row, the UML-style model) of ISA-5.1 symbols (vessel | pump | compressor | exchanger | valve | control-valve | instrument) connected by typed lines (process heavy-solid with a flow arrow, signal dashed, connection thin). Pipes route as orthogonal polylines through the box-free gutter channels between cells (the same proven 3-gutter channel router as the UML diagram), so a routed line never crosses a symbol; perpendicular crossings are allowed (a crossing is not a lie in a general process graph). An instrument's tag is drawn inside its balloon. Lower-level entry points: computePidLayout / pidLayoutSvg.

Annotations

Any element you declare can be flagged annotated: true to draw a discreet, neutral marker — a small dot on a node, a short tick on an edge — plus a matching legend row. compasso never decides what an annotation means; you supply the legend wording, so it reads however your domain needs ("edited", "under review", "verified", …).

import { genogramSvg } from "compasso/genogram";

const { svg } = genogramSvg(
  {
    people: [
      { id: 1, label: "Ana", sex: "female", deceased: false, generation: 0, annotated: true },
      { id: 2, label: "Bea", sex: "female", deceased: false, generation: 0 },
    ],
    unions: [],
    parentLinks: [],
    relationships: [],
  },
  { annotationLabel: "edited" }, // legend row appears only when something is annotated
);

Supported on genogram (people, unions, parent links, relationships), ecomap (ties) and org-chart (positions, reports). The marker is presence-only — drawn as literal SVG inside the element's <g>, it never affects layout — and the legend row appears only when you pass annotationLabel and at least one element is annotated.

Localization

import { genogramSvg } from "compasso/genogram";
import {
  GENOGRAM_SVG_LABELS_PT_BR,
  GENOGRAM_TITLE_LABELS_PT_BR,
  KINSHIP_PT_BR,
  QUALITY_LEXICON_PT_BR,
} from "compasso/locales/pt-br";

genogramSvg(input, {
  kinship: KINSHIP_PT_BR,
  qualityLexicon: QUALITY_LEXICON_PT_BR,
  titleLabels: GENOGRAM_TITLE_LABELS_PT_BR,
  svgLabels: GENOGRAM_SVG_LABELS_PT_BR,
});

A locale pack is plain data — labels, whole-word kinship tokens, quality-word needles with a negation list. PRs for new locales are welcome.

compasso/locales/pt-br also exports parseUnionStatusPtBr(text) and the backing UNION_STATUS_PT_BR map for recognizing free pt-BR text (e.g. "casado", "união estável", "co-pais") as the corresponding compasso UnionStatus English value. Unrecognized input conservatively returns "unknown".

Roadmap

The goal is a family of standards-faithful technical diagrams sharing this core (text metrics, stroke vocabulary, escaping, legend machinery). Shipped: genogram, ecomap, fault tree, fishbone, pedigree, phylogenetic tree, org chart, PRISMA 2020 flow, UML class diagram, and the electrical/industrial symbol-library family kickoff — ladder logic (IEC 61131-3), single-line (IEC 60617 / IEEE 315) and P&ID (ISA-5.1), all on a shared multi-port symbol primitive + channel router. Next in that family: Function Block Diagram and Sequential Function Chart (IEC 61131-3), circuit schematic (IEC 60617), logic-gate netlist (IEEE 91), control-systems block diagram, timing/waveform, and welding symbols (AWS A2.4) — each built from its public standard. AST-first; text DSLs may come later.

Contributing

AGENTS.md is the contract for changing compasso — the house conventions (pure input→SVG, determinism, the honesty rule, the overlap harness, validation doctrine, module anatomy). docs/architecture.md covers the internal structure.

Provenance & license

MIT © Victor Canô. The genogram engine descends from a production renderer the author wrote for a clinical intake product (same copyright holder); the ecomap renderer is original to this repository. Developed independently of any third-party diagram engine's code.