# Schematex complete documentation Canonical site: https://schematex.js.org Documentation index: https://schematex.js.org/llms.txt Interactive capability JSON: https://schematex.js.org/api/interactive-capabilities # Using Schematex with AI > Integrate Schematex into AI agents via the Vercel AI SDK, the hosted MCP endpoint, or the @schematex/mcp npm package. Canonical URL: https://schematex.js.org/docs/ai-integration Markdown URL: https://schematex.js.org/docs/ai-integration.md [Connect to the hosted Schematex MCP](https://schematex.js.org/mcp) Machine clients can discover the complete public surface without scraping the rendered site: [`/llms.txt`](/llms.txt) is the curated index, [`/llms-full.txt`](/llms-full.txt) contains the full English documentation, every docs URL has a `.md` mirror, and [`/api/interactive-capabilities`](/api/interactive-capabilities) exposes the canonical editor capability registry as JSON. ## TL;DR Schematex ships a tool layer built for LLMs — eight tools that let an AI agent **discover**, **generate**, **validate**, **render**, **inspect**, and **safely edit** diagrams. Three integration paths, same contracts: | Path | When | Install | | ------------------- | ---------------------------------------------------- | ---------------------------------------------- | | **Vercel AI SDK** | Building your own AI feature in a Next.js / Node app | `npm i schematex ai` | | **Hosted MCP** | Connecting Claude Desktop, ChatGPT, Cursor, Windsurf | Point client at `https://schematex.js.org/mcp` | | **Local stdio MCP** | Offline / custom hosts | `npx @schematex/mcp` | *** ## The eight tools Every path exposes the same eight tools with identical semantics. ### `listDiagrams()` Returns all 50 diagram types with tagline, "use when" hint, cluster, authoritative standard, and interactive capability summary. Call first to pick a type. ### `getSyntax({ type, detail? })` Canonical syntax for one diagram. Call after picking a type. The default `detail: "canonical"` response is designed for first-shot LLM generation: canonical header, preferred mode, core forms, avoid/repair guidance, and shared generation rules. Use `detail: "reference"` only when the request needs an advanced feature or an imported adapter; that returns the trimmed public docs slice from `## 1.` through the grammar section. ### `getExamples({ type, limit?, preferFeatured?, maxComplexity? })` Curated real-world DSL examples with scenario notes. Use as few-shot context before generation. ### `validateDsl({ type?, dsl })` Parse-only check. Returns `{ ok: true }` or `{ ok: false, errors: [{line, column, message, source, hint}] }`. Pass the selected canonical `type` once you know it, and **always call before returning DSL to the user** — agents that self-correct with this loop are dramatically more reliable. ### `renderDsl({ type?, dsl, theme?, padding? })` Render DSL to an SVG string. Success returns `{ ok: true, status, svg }`. Failure returns `{ ok: false, status: 'invalid', svg, errors }`, where `svg` is a visible diagnostic fallback instead of an empty preview. **If your app already renders diagrams** (you're using the `schematex` package directly), skip `renderDsl` — have the agent return the validated DSL and render it yourself. Calling the tool just adds an unnecessary round-trip. **Use `renderDsl` only when** you need the SVG returned inside the conversation itself: Claude Desktop, ChatGPT, Cursor, or any pure chat client that has no rendering pipeline of its own. ### `getDiagramCapabilities({ type })` Returns the diagram's safe text and position-editing contract. Call before proposing direct manipulation or geometry changes. ### `inspectDiagram({ type?, dsl })` Returns a source revision plus stable editable target keys, labels, bounds, and allowed operations. It intentionally does not expose raw source offsets to the model. ### `applyDiagramEdits({ type?, dsl, revision, edits })` Atomically applies up to 50 `setLabel` / `setPosition` operations. A stale revision, unknown target, unsafe operation, or invalid result returns the original DSL—never a partially edited document. For an in-app AI preview, render the returned DSL with `renderResult()` or `renderPreview()`: ```ts import { renderResult } from 'schematex'; const result = renderResult(dsl, { type }); preview.innerHTML = result.svg; if (!result.ok) { queueRepair(result.diagnostics); } ``` The preview can stay visible while your app still treats `ok: false` as an invalid user result for repair, retry, telemetry, or billing policy. *** ## Path 1 — Vercel AI SDK (in-app) The quickest way to add Schematex generation to a Next.js / Node app. ```ts import { streamText } from 'ai'; import { schematexTools } from 'schematex/ai/sdk'; const result = streamText({ model: 'anthropic/claude-opus-4-7', tools: schematexTools, maxSteps: 5, system: `You write Schematex DSL. First call listDiagrams to pick a type. Then call getSyntax and getExamples for that type. Write the DSL, then call validateDsl and self-correct on errors before returning to the user.`, prompt: userMessage, }); ``` The `schematex/ai/sdk` subpath only loads if the optional `ai` peer dependency is installed. It uses JSON Schema directly, so your app does not need Zod. If you don't use the AI SDK, import the plain functions from `schematex/ai` and wire them to any framework: ```ts import { listDiagrams, getSyntax, getExamples, validateDsl, renderDsl, getDiagramCapabilities, inspectDiagram, applyDiagramEdits, } from 'schematex/ai'; ``` ### Single-shot generation (no tool loop) High-volume backends often generate with **one** model call instead of a multi-step tool loop — cheaper and lower latency. For that, `buildPromptContext(type)` assembles the canonical grammar card **and** a featured few-shot example into one inject-ready block, so you don't have to stitch `getSyntax` + `getExamples` together yourself: ```ts import { buildPromptContext, validateDsl } from 'schematex/ai'; const ctx = buildPromptContext('genogram'); // grammar card + featured example const system = `Generate only Schematex DSL.\n\n${ctx.text}`; const dsl = await generate(system, userMessage); // your single model call const check = validateDsl('genogram', dsl); // always gate server-side if (!check.ok) { // one repair pass: re-prompt with check.errors[].message + .hint } ``` `buildPromptContext` is pure sugar over `getSyntax({ detail: 'canonical' })` + `getExamples({ preferFeatured: true })` — both remain available if you'd rather compose the prompt yourself. Options: `{ examples?: number, detail?, preferFeatured?, maxComplexity? }`. *** ## Path 2 — Hosted MCP (zero install) The Schematex MCP server is hosted at: ``` https://schematex.js.org/mcp ``` Any MCP client that speaks JSON-RPC over HTTP can connect. No install, no local process, always up to date with the latest schematex release. ### Claude.ai (web) Use the connect block at the top of this page — one click opens the connector dialog, paste the URL, done. The connector then appears under Settings → Connectors and is available in every conversation. ### Claude Desktop (app) Settings → Connectors → Add custom connector → paste `https://schematex.js.org/mcp`. ### ChatGPT / Cursor / Windsurf Follow your client's "Add MCP server" flow and use the HTTP transport with the URL above. *** ## Path 3 — Local stdio MCP For offline development or clients that only support stdio transport. ```bash npm i -g @schematex/mcp # or npx @schematex/mcp ``` ### Claude Desktop config `~/Library/Application Support/Claude/claude_desktop_config.json`: ```json { "mcpServers": { "schematex": { "command": "npx", "args": ["-y", "@schematex/mcp"] } } } ``` *** ## Recommended agent loop The single biggest reliability win is making the agent validate its own output and self-correct. ``` 1. listDiagrams() → pick the right type 2. getSyntax({ type }) → get canonical generation syntax 3. getExamples({ type, maxComplexity: 2 }) → get few-shot context 4. write DSL 5. validateDsl({ type, dsl }) ├─ ok: return DSL to user └─ error: read line/column/message, fix, goto 5 6. (optional) renderDsl({ type, dsl }) → return SVG (skip if app renders natively) 7. (for edits) inspectDiagram({ type, dsl }) → applyDiagramEdits({ dsl, revision, edits }) ``` A simple system prompt that implements this loop: ```` You generate Schematex DSL for diagrams. Always follow this procedure: 1. If you don't already know which diagram type matches the user's request, call listDiagrams and pick the best match. 2. Call getSyntax for the chosen type. Stay on the canonical path unless an advanced feature requires getSyntax({ type, detail: "reference" }). 3. Call getExamples (maxComplexity: 2) for few-shot context. 4. Write the DSL. 5. Call validateDsl. If ok:false, read the error's line, column, and message, fix the DSL, and call validateDsl again. Repeat up to 3 times. 6. Return the validated DSL to the user, inside a ```schematex code block. ```` *** ## Error shape `validateDsl` and `renderDsl` return structured errors on failure: ```ts { ok: false, status: 'invalid', // renderDsl only type: 'genogram' | null, svg: string, // renderDsl only: visible diagnostic fallback errors: [ { line?: number; // 1-based, if the parser reported it column?: number; source?: string; // the offending line text message: string; // human-readable hint?: string; } ] } ``` Not all parsers emit line info yet (genogram, pedigree, ecomap, venn, sld, fishbone do today; others return message-only). The shape is stable — `line` is optional. *** ## Ready to try it? [Connect to the hosted Schematex MCP](https://schematex.js.org/mcp) *** ## Licensing notes Schematex is AGPL-3.0. For closed-source commercial integrations contact [victor@mymap.ai](mailto:victor@mymap.ai) for a commercial licence. --- # API reference > Schematex JavaScript/TypeScript API — strict render/parse calls, preview-safe result APIs, config options, and tree-shakable imports. Canonical URL: https://schematex.js.org/docs/api Markdown URL: https://schematex.js.org/docs/api.md ## `render(text, config?)` Renders a DSL string into an SVG string. Dispatches by first keyword and throws when detection, parsing, layout, or rendering fails. ```ts import { render } from 'schematex'; const svg: string = render(text, { theme: 'monochrome' }); ``` Use the strict API when invalid DSL must stop the workflow, for example during a test, export job, or server-side validation gate. ## `parse(text, config?)` Parses a DSL string into the typed AST without rendering. This API is also strict and throws on invalid DSL. ```ts import { parse } from 'schematex'; const ast = parse(text); ``` ## Preview-safe rendering Live editors, AI previews, and chat canvases should not turn a parser failure into an empty surface. Use `renderPreview()` when you only need an SVG string: ```ts import { renderPreview } from 'schematex'; const svg = renderPreview(text, { type: 'circuit' }); ``` `renderPreview()` always returns SVG. If strict rendering fails, that SVG is a visible diagnostic fallback. `render(text, { mode: 'preview' })` is the same preview boundary for integrations that already call `render()`. ## Result APIs Use result APIs when the caller needs both a visible output and validity state: ```ts import { parseResult, renderResult } from 'schematex'; const renderState = renderResult(text); container.innerHTML = renderState.svg; if (!renderState.ok) { console.error(renderState.diagnostics); } const parseState = parseResult(text); ``` `renderResult()` returns `svg`, `diagnostics`, `type`, and a `status` of `valid`, `partial`, or `invalid`. Invalid render results still carry a diagnostic SVG so the preview remains visible. `parseResult()` returns either an AST or structured diagnostics without throwing. The `partial` status is reserved for parsers that can recover a partial AST or partial rendered diagram. Do not treat "an SVG string exists" as proof that the DSL is valid. Preview surfaces should show the SVG and use `ok`, `status`, and `diagnostics` for retry, repair, telemetry, or billing decisions. ## Browser and React The browser subpath exposes strict DOM helpers and preview-safe DOM helpers: ```ts import { renderToContainer, renderPreviewToContainer, } from 'schematex/browser'; renderToContainer(text, exportContainer); // strict renderPreviewToContainer(text, previewContainer); // visible fallback ``` Use the small read-only React component when the canvas should not modify DSL: ```tsx import { SchematexDiagram } from 'schematex/react'; console.error(error)} />; ``` `onError` fires on strict parser/layout/render failure before the component shows the diagnostic fallback SVG. For a controlled editor, use `InteractiveSchematexDiagram`: ```tsx 'use client'; import { useState } from 'react'; import { InteractiveSchematexDiagram } from 'schematex/react'; export function DiagramEditor({ initialDsl }: { initialDsl: string }) { const [dsl, setDsl] = useState(initialDsl); return ( { setDsl(nextDsl); console.log(detail.reason, detail.item?.semanticId); }} debounceMs={120} canvasClassName="diagramCanvas" /> ); } ``` This component is controlled: persistence, undo, source-code editing, and collaboration stay in your application. `canvasClassName` applies to the inner host `
` that directly contains the generated SVG; it does not mutate the SVG's class attribute. See [Interactive editing](/docs/interactive-editing) for the complete Props table, styling contract, event semantics, capability discovery, and Vanilla DOM API. ## Tree-shakable imports ```ts import { render } from 'schematex/genogram'; import { render } from 'schematex/ecomap'; import { render } from 'schematex/pedigree'; import { render } from 'schematex/phylo'; import { render } from 'schematex/sociogram'; ``` ## `SchematexConfig` ```ts interface SchematexConfig { type?: string; // optional diagram type override width?: number; height?: number; padding?: number; theme?: string; // e.g. 'default' | 'monochrome' | 'dark' fontFamily?: string; mode?: 'strict' | 'preview'; scene?: boolean; // include editable SceneItem metadata } ``` See [`src/core/types.ts`](https://github.com/SchemaTex/SchemaTex/blob/main/src/core/types.ts) and [`src/core/api.ts`](https://github.com/SchemaTex/SchemaTex/blob/main/src/core/api.ts) for the complete type definitions. --- # Block diagram Canonical URL: https://schematex.js.org/docs/block Markdown URL: https://schematex.js.org/docs/block.md ## About block diagrams A **block diagram** models a system as a set of functional blocks connected by directed signal lines. Each block represents a component whose behavior is captured by a transfer function or a descriptive label; signals flow through the blocks in a defined direction. Control engineers use them to design feedback loops, signal-processing engineers use them to document filter chains, and systems engineers use them to decompose complex architectures. The defining visual elements — rectangular blocks and circular summing junctions — are conventions drawn from control systems analysis (Ogata, Franklin, Nise) and carried into every datasheet and textbook that describes a feedback system. Schematex follows the **Laplace-domain transfer-function convention** used in control systems textbooks: blocks are labeled with their transfer functions, summing junctions carry explicit `+`/`−` polarity signs, and a `discrete` flag on signals renders a dashed line for sampled-data paths. This page documents what the parser accepts today. Authoritative references: Ogata (2010) *Modern Control Engineering*; Franklin, Powell & Emami-Naeini (2018) *Feedback Control of Dynamic Systems*. ```schematex blockdiagram "Audio dynamics processor" # Signal chain in_s = signal("Input") gate_s = signal("gated") comp_s = signal("compressed") out_s = signal("Output") # Blocks gate = block("Noise gate") [role: controller] comp = block("Compressor") [role: plant] eq = block("3-band EQ") [role: generic] limiter = block("Limiter") [role: actuator] # Level detection side-chain detect = block("Level detect") [role: sensor] sum_g = sum(+in_s, -detect) # Forward path in -> in_s in_s -> sum_g sum_g -> gate gate -> gate_s gate_s -> comp comp -> comp_s comp_s -> eq eq -> limiter limiter -> out_s # Side-chain feedback limiter -> detect ``` *** ## 1. Your first block diagram The smallest useful block diagram: one controller, one plant, one feedback loop. ```schematex blockdiagram "Temperature control" ctrl = block("PID") [role: controller] plant = block("Heater") [role: plant] sensor = block("Thermocouple") [role: sensor] err = sum(+ref, -measured) ref = signal("Setpoint") measured = signal("T_measured") in -> ref ref -> err err -> ctrl ctrl -> plant plant -> measured measured -> sensor sensor -> err ``` Four rules cover 80% of usage: 1. Start with `blockdiagram`, optionally followed by a quoted title. 2. Declare each component with `ID = block("label")`, each summing junction with `ID = sum(+a, -b)`, and each named signal with `ID = signal("label")`. 3. Connect components with `->`. Chain multiple hops on one line: `A -> B -> C`. 4. Optionally annotate connections with a trailing label: `A -> B ["E(s)"]`. > Comments must start with `#` on their own line. *** ## 2. Blocks A block represents any functional element — controller, plant, filter, actuator, sensor. The label is typically a transfer function or a descriptive name. **Syntax:** `ID = block("label") [role: X]` | Attribute | Values | Effect | | -------------- | ---------------------------------------------------------------------------------- | ------------------------------------------------ | | `role: plant` | `plant`, `controller`, `sensor`, `actuator`, `reference`, `disturbance`, `generic` | Visual color coding; `generic` is the default | | `route: above` | `above`, `below` | Routing hint for feedback and feedforward blocks | **ID rules.** Must start with a letter or underscore, followed by letters, digits, or underscores: `[A-Za-z_]\w*`. ```schematex blockdiagram "Block roles" ref_block = block("r(t)") [role: reference] ctrl = block("C(s)") [role: controller] act = block("Actuator") [role: actuator] plant = block("G(s)") [role: plant] sensor = block("H(s)") [role: sensor] dist = block("d(t)") [role: disturbance] ref_block -> ctrl ctrl -> act act -> plant plant -> sensor dist -> plant ``` *** ## 3. Summing junctions A summing junction combines multiple signals into one, with explicit polarity for each input. It renders as a circle with `+`/`−` signs — the standard control-systems symbol. **Syntax:** `ID = sum(+a, -b, +c, …)` * Each input is a signed ID: `+x` adds signal `x`, `-y` subtracts signal `y`. * An input without a sign is treated as positive. * The summing junction ID is then used as the target of connection lines, just like a block ID. ```schematex blockdiagram "Error with disturbance rejection" ctrl = block("PI C(s)") [role: controller] plant = block("G(s)") [role: plant] sensor = block("H(s)") [role: sensor] # Main error junction: add reference, subtract feedback err = sum(+r, -ym) # Disturbance junction: add plant input, add disturbance disturb = sum(+ctrl_out, +d) r = signal("r (setpoint)") ym = signal("y_m") ctrl_out = signal("u(t)") in -> r r -> err err -> ctrl ctrl -> ctrl_out ctrl_out -> disturb disturb -> plant plant -> ym ym -> sensor sensor -> err ``` *** ## 4. Signals A signal declaration creates a named signal node that the parser inlines as an edge label. Signals are pass-through: when the parser sees `A -> sig` and `sig -> B`, it merges them into a single edge from `A` to `B`, labeling it with the signal's display text. **Syntax:** `ID = signal("label") [discrete]` * Omit `[discrete]` for continuous signals (solid line). * Add `[discrete]` for sampled-data signals (dashed line). ``` e_sig = signal("E(s)") u_sig = signal("U(s)") [discrete] ``` Signals are purely a labeling convenience — you can also label edges directly with a trailing attribute (see §5). *** ## 5. Connections A connection line is `from -> to`. The `->` operator always produces a directed, arrowed line. **Single hop:** `A -> B` **Chain:** `A -> B -> C` — equivalent to `A -> B` and `B -> C`. Both are written in one line. **With a signal label:** append `["label text"]` at the end of the chain. The label applies to the last hop only. ``` ctrl -> plant ["U(s)"] ``` **With a discrete flag:** append `[discrete]` to make the last-hop arrow dashed. ``` plant -> adc ["y"] [discrete] ``` **Both label and discrete:** use `[label: "Y(s)", discrete]` (comma-separated). ``` adc -> ctrl [label: "y[k]", discrete] ``` ```schematex blockdiagram "Mixed continuous/discrete" ctrl = block("Digital PID") [role: controller] dac = block("DAC") [role: actuator] plant = block("G(s)") [role: plant] adc = block("ADC") [role: sensor] err = sum(+r, -yk) r = signal("r[k]") [discrete] yk = signal("y[k]") [discrete] in -> r r -> err err -> ctrl ctrl -> dac [label: "u[k]", discrete] dac -> plant ["u(t)"] plant -> adc ["y(t)"] adc -> yk [discrete] yk -> err ``` *** ## 6. Labels & comments * **Title:** `blockdiagram "My System"` — first line, quoted. * **Block label:** the quoted string inside `block("…")` — appears inside the box. * **Signal label:** the quoted string inside `signal("…")` — appears on the merged edge. * **Edge label:** trailing `["text"]` or `[label: "text"]` on a connection line — appears on that arrow. * **Comments:** `#` at the start of a line (after leading whitespace). Inline trailing comments are not supported. *** ## 7. Reserved words & escaping **Reserved at line start:** `blockdiagram` (header). **Structural keywords** (avoid as block/signal/sum IDs to prevent ambiguity): `block`, `signal`, `sum`. **`in` and `out`** are conventional IDs for the external boundary of a diagram — the parser treats them as ordinary identifiers, but the renderer uses them as implicit source/sink nodes. Using `in -> r` and `plant -> out` is idiomatic. **Strings with spaces** must be double-quoted in `block("…")` and `signal("…")` labels. *** ## 8. Common mistakes | You wrote | Parser says | Fix | | --------------------------------------------------- | ---------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------- | | `G = block(G(s))` (no quotes) | Parse fails — label must be quoted | `G = block("G(s)")` | | `err = sum(r, -ym)` (no `+`) | `r` treated as `+r` — works, but ambiguous | Write `sum(+r, -ym)` for clarity | | `ctrl -> plant, plant -> out` (comma on one line) | `,` is not a separator — parse fails | One connection per line or use chain: `ctrl -> plant -> out` | | `s1 = signal("E(s)") [label: "E"]` | `label:` not valid on signal; use it on connections | Drop `label:` from signal declaration | | `role: filter` | Unknown role — silently defaults to `generic` | Use `plant`, `controller`, `sensor`, `actuator`, `reference`, `disturbance`, or `generic` | | `A -> B [discrete, label: "e"]` — label first fails | Order of attrs inside `[…]` doesn't matter, but bare `"text"` shorthand only works when it's the only item | Use `[label: "e", discrete]` | *** ## 9. Grammar (EBNF) ```text document = header (blank | comment | block-def | sum-def | signal-def | connection)* header = "blockdiagram" ( WS quoted-string )? NEWLINE quoted-string = '"' any-char-but-quote* '"' block-def = id WS "=" WS "block" "(" quoted-string ")" ( "[" block-attrs "]" )? NEWLINE block-attrs = block-attr ("," block-attr)* block-attr = "role:" role | "route:" ("above" | "below") role = "plant" | "controller" | "sensor" | "actuator" | "reference" | "disturbance" | "generic" sum-def = id WS "=" WS "sum" "(" sum-inputs ")" NEWLINE sum-inputs = sum-input ("," sum-input)* sum-input = ("+" | "-")? id signal-def = id WS "=" WS "signal" "(" quoted-string ")" ( "[" "discrete" "]" )? NEWLINE connection = id ("->" id)+ ( "[" conn-attrs "]" )? NEWLINE conn-attrs = quoted-string # shorthand: bare label only | conn-attr ("," conn-attr)* conn-attr = "label:" quoted-string | "discrete" id = [A-Za-z_] \w* comment = "#" any NEWLINE ``` Authoritative source: `src/diagrams/blockdiagram/parser.ts`. If this diverges from the parser, the parser wins — please open an issue. *** ## 10. Standard compliance Schematex block diagrams follow the **Laplace-domain transfer-function** conventions from Ogata (2010) and Franklin et al. (2018) — the block, summing junction, and directed signal-line symbols found in every control systems textbook. What is implemented today: * ✅ Rectangular blocks with transfer-function labels * ✅ Circular summing junctions with `+`/`−` polarity inputs * ✅ Named signal nodes (pass-through, merge to edge label) * ✅ Directed connections with chaining (`A -> B -> C`) * ✅ Edge labels — signal names on arrows * ✅ `discrete` flag — dashed line for sampled-data signals * ✅ Role annotations (`plant`, `controller`, `sensor`, `actuator`, `reference`, `disturbance`, `generic`) * ✅ `route: above | below` hint for feedback/feedforward placement * ⏳ Branch/pickoff point — explicit dot symbol where one signal fans out to two destinations * ⏳ Boundary box — dashed subsystem enclosure with label * ⏳ Bidirectional arrows — `<->` for two-way signal exchange * ⏳ Bus notation — thick line representing a vector of signals References: * Ogata, K. (2010). *Modern Control Engineering*, 5th ed. Prentice Hall. * Franklin, G.F., Powell, J.D. & Emami-Naeini, A. (2018). *Feedback Control of Dynamic Systems*, 8th ed. Pearson. *** ## 11. Related examples [Browse related examples](https://schematex.js.org/examples) *** ## 12. Roadmap **Planned — not yet parseable.** Do not use these in generated DSL today; the parser will reject or ignore them. * **Branch/pickoff point symbol** — explicit `dot` primitive where one output fans to multiple destinations (currently the renderer auto-inserts a branch when an ID is used as a source more than once, but there is no explicit syntax). * **Subsystem boundary box** — `boundary "label" { … }` block that draws a dashed rectangle enclosing the contained blocks, used for multi-loop and subsystem views. * **Bidirectional connection** — `A <-> B` for components with mutual information exchange (e.g. a bus interface). * **Bus (vector signal) notation** — thick line with a slash-and-count annotation `//n` indicating an n-wide signal bus. * **Nested subsystems** — a `block` whose interior is itself a block diagram, collapsible in the rendered output. Track in the GitHub issues if you need any of these sooner. --- # Bowtie Risk Diagram Canonical URL: https://schematex.js.org/docs/bowtie Markdown URL: https://schematex.js.org/docs/bowtie.md ## About bowtie diagrams A **bowtie** is the most recognisable picture in barrier-based risk management: a central **top event** (the moment control of a hazard is lost) with **threats** fanning in from the left through chains of **preventative barriers**, and **consequences** fanning out to the right through chains of **mitigative barriers** — the whole thing shaped like a bow tie. It answers, for one hazard, the two questions a regulator or board actually asks: *what could make this go wrong, and what stops it?* (left), and *if it does go wrong, what happens, and what limits the damage?* (right). Mandated across oil & gas, chemical processing, aviation ([ICAO Doc 9859](https://www.icao.int/) SMS), rail, mining, and healthcare. Codified by **[CCPS / Energy Institute 2018](https://www.aiche.org/ccps)** (*Bow Ties in Risk Management*) and named as a technique by **IEC 31010:2019** §B.4.6. Schematex's edge here is **not** computation (that is `faulttree`'s and `pert`'s story) — a basic bowtie carries no probabilities. It is two things: a **rigid, correct-by-construction symmetric layout** that no general-purpose box-and-arrow tool produces, and **structural validation of the CCPS/EI barrier rule set** — every threat must reach the top event through ≥ 1 barrier, every consequence must hang off it through ≥ 1 barrier, and every escalation factor must attach to a specific named barrier. A threat with no barrier is *rejected*, not silently drawn. ```schematex bowtie "LPG storage — loss of containment" hazard "LPG stored under pressure" topevent "Loss of containment" threat "Corrosion of vessel wall" prevent "Corrosion-resistant coating" prevent "UT thickness inspection" escalation "Inspection interval too long" barrier "Risk-based inspection scheme" threat "Overpressure during filling" prevent "High-pressure trip (SIL 2)" prevent "Pressure relief valve" consequence "Jet fire" mitigate "Gas detection + ESD" mitigate "Deluge / water spray" consequence "Toxic exposure" mitigate "Personal gas monitors" mitigate "Emergency evacuation plan" ``` *** ## 1. Your first diagram Every document starts with the `bowtie` keyword, an optional title, then the hazard, the top event, and the two wings: ``` bowtie topevent "Loss of containment" threat "Corrosion" prevent "Inspection programme" consequence "Release to atmosphere" mitigate "Gas detection + ESD" ``` `topevent` declares the single knot at the centre (mandatory, exactly one). Each `threat` starts a left-wing line; each `consequence` ends a right-wing line. A barrier under a `threat` is **preventative** (`prevent`); under a `consequence` it is **mitigative** (`mitigate`). The diagram is laid out symmetrically about the knot — threats fan in from the left, consequences fan out to the right. The DSL is **indentation-structured** and mirrors the CCPS 7-step build methodology: identify the hazard → the top event → the threats → the consequences → the preventative barriers → the mitigative barriers → the escalation factors. Header directives (any order): * `layout: symmetric | compact` — band model (default `symmetric`). * `legend: on | off | bottom | top` — the auto-derived colour legend (default on). *** ## 2. Hazard and top event ``` hazard "Working at height" topevent "Person falls from height" ``` * **`hazard`** — the operation or material with the potential to cause harm: the *context* the bowtie is about (e.g. "Working at height", "Hydrocarbon under pressure"). Optional; renders as a header box above the knot with a tie-line down to it. At most one. * **`topevent`** — the moment control of the hazard is **lost** (e.g. "Loss of containment", "Person falls from height"). The knot of the bowtie, drawn as a green circle. **Mandatory, exactly one.** A hazard is a *thing/activity*, not a failure; the top event is the precise moment of *loss of control* — not a cause, and not yet a consequence. *** ## 3. Threats and consequences ``` threat "Guardrail removed for access" prevent "Permit-to-work system" consequence "Fatality" mitigate "Fall-arrest harness + lanyard" ``` * A **threat** is a credible cause that, *on its own*, could trigger the top event. Each threat is the start of one left-wing line, drawn as an orange box on the left edge. * A **consequence** is a credible outcome *of the top event* (not of the threat), drawn as a red box on the right edge. Threats and consequences may be declared in any interleaved order — the parser groups all left-wing blocks and all right-wing blocks regardless of sequence. A bowtie needs **at least one of each**: a one-wing diagram is a fault tree (see `faulttree`) or an event tree, not a bowtie. *** ## 4. Barriers (the controls in between) ``` threat "Guardrail removed for access" prevent "Permit-to-work system" prevent "Temporary edge protection" prevent "Spotter / banksman" ``` A **barrier** is a control that interrupts the threat → top-event path (preventative) or reduces the consequence after the top event (mitigative). Each is a grey box *on the line*. Chains are free length (1..n) — the wing simply extends. **Barrier order is declaration order**: the first declared is the **outermost** (closest to the threat/consequence, the first line of defence); the last declared is the **innermost** (closest to the knot). This matches the left-to-right reading of a real bowtie. Each barrier carries `data-order` (0 = outermost) and `data-side` (`prevent` / `mitigate`) for downstream interactivity. When chains differ in length, barriers are **centre-anchored**: the innermost barriers align in a neat column near the knot, and the threat/consequence boxes are ragged by chain depth — reading as defence-in-depth. *** ## 5. Escalation factors ``` threat "Corrosion" prevent "UT thickness inspection" escalation "Inspection interval too long" barrier "Risk-based inspection scheme" ``` An **escalation factor** (or degradation factor) is a condition that *degrades a specific barrier's effectiveness* — e.g. "Edge protection not inspected", "Operator fatigue". It attaches to **one** barrier (not to the line) and drops vertically below it as an amber box, joined by a muted "degrades" connector. An **escalation-factor barrier** is a control placed on the escalation factor itself — it protects the barrier from being degraded (e.g. a pre-use inspection regime). It nests one level deeper, under the escalation factor, and renders as a grey box below it. Indentation binds the nesting: `prevent`/`mitigate` at 2 spaces, `escalation` at 4, `barrier` at 6. *** ## 6. Correct by construction (the barrier rule set) Before it draws a single shape, the engine validates the structural half of the CCPS/EI barrier rule set and **refuses to render** on failure — exactly as `prisma` refuses missing counts: * **Exactly one top event** — zero or several is an error. * **Every threat has ≥ 1 preventative barrier** — a bare threat is a Swiss-cheese cartoon, not a bowtie. * **Every consequence has ≥ 1 mitigative barrier** — the mirror rule. * **Every escalation factor is attached to a barrier** — it cannot float on a line or on the top event. * **At least one threat and one consequence** — a one-wing diagram is an FTA or an ETA. Messages name the offending element and the rule in plain English, e.g. *"Threat 'Corrosion' has no preventative barrier — every threat must reach the top event through at least one barrier (CCPS/EI barrier rule). Add a `prevent` line under it."* This is what separates a real bowtie from a doodle. The engine does **not** judge whether a barrier is truly *effective* or *independent* — that is the analyst's qualitative judgement. *** ## 7. Bowtie vs fault tree A fully-developed bowtie *is* a fault tree glued to an event tree at the top event: the left wing read backwards is the fault tree whose top event is the bowtie's knot, and the right wing is the event tree that propagates it into consequences. Schematex keeps them as two engines because their use differs: * **`bowtie`** is qualitative and symmetric — the barrier inventory and the at-a-glance defence-in-depth story; no probability arithmetic. * **`faulttree`** is quantitative and Boolean — AND/OR gates, basic-event probabilities, minimal cut sets, a probability rollup. Where you want the gate-level detail behind a single threat, draw a separate `faulttree`. *** ## 8. Theming `default` uses the recognised BowTieXP / bowtiemaster palette — **orange threats** (left), **grey barriers** on the line, a **green top-event disc** (centre knot), **red consequences** (right), **amber escalation factors** dropping below — mapped onto Schematex's semantic slots so it stays coherent with `prisma` / `pert` / `petri`. `monochrome` reproduces the regulator-print black-and-white look, where element distinction rides on shape/border + position (escalation factors get a dashed border, the knot a doubled ring) rather than colour. `dark` follows Schematex slate/blue dark palette. All strokes/fills come from `BowtieTokens`; every element carries `data-*` (`data-role`, `data-side`, `data-line`, `data-order`, `data-barrier`) so the structure is inspectable downstream. --- # BPMN / Business Process Canonical URL: https://schematex.js.org/docs/bpmn Markdown URL: https://schematex.js.org/docs/bpmn.md ## About BPMN diagrams A **BPMN diagram** documents a business process — the activities, decisions, events, and message exchanges that happen across roles, departments, and systems. It's the dominant notation in enterprise BPM, ISO-9001 / SOX audits, and process-mining tooling. The *only* official serialization is BPMN 2.0 XML, which is verbose and hostile to LLM generation; Schematex provides a compact text DSL that produces a conformant visual subset. Schematex implements OMG **BPMN 2.0.2 / ISO/IEC 19510:2013** for the elements that real-world business analysts actually draw: pools, lanes, events (start / intermediate / end with none / message / timer triggers), activities (tasks with markers + collapsed subprocesses), gateways (XOR / OR / AND / event-based), and sequence / conditional / default / message flows. > **Note** — Schematex is a rendering library, not a process-execution engine. There is no token simulation, no XML round-trip, and no DI (Diagram Interchange) layer. v0.1 covers the visual subset most teams use; boundary events, expanded subprocesses, and the rare trigger types (cancel / compensation / escalation / signal / link) are deferred to v0.2+. ```schematex bpmn direction: LR title: "Loan Application Approval" pool "Bank" { lane "Clerk" { A: start "Application received" B: task user "Check completeness" G1: gateway xor "Complete?" } lane "Underwriter" { C: task service "Risk score" D: task user "Underwriter review" G2: gateway xor "Decision" E: end "Approved" F: end "Rejected" } } flows A --> B B --> G1 G1 --? "yes" --> C G1 --* "no" --> F C --> D D --> G2 G2 --? "approve" --> E G2 --* "reject" --> F ``` *** ## 1. Your first BPMN diagram Three sections: a one-line `bpmn` header, one or more `pool { … }` blocks, and a `flows` block. Inside each pool you put `lane { … }` blocks, and inside each lane you list flow objects as `id: kind "label"`. ```schematex bpmn pool "Service" { lane "Worker" { A: start "Request" B: task service "Process" F: end "Done" } } flows A --> B B --> F ``` Every flow object starts with an **id**, a colon, the **kind**, an optional sub-keyword (trigger / marker / gateway type), and a quoted **label**. Ids are referenced by flow lines. *** ## 2. Pools and lanes A **pool** represents one participant — an organisation, a department, or a system. A **lane** subdivides a pool into roles. The pool label is rendered rotated 90° on the left edge of a horizontal pool. ``` pool "Customer" blackbox // black-box pool — no internal flow pool "Bank" { lane "Clerk" { … } lane "Underwriter" { … } } ``` A **black-box pool** is a participant whose internal process you don't model — typically an external customer or partner. Black-box pools must contain zero flow objects (Schematex enforces this in the parser). Sequence flow (`-->`) is **not allowed** to cross pool boundaries. Use a message flow (`~~>`) for cross-pool communication. *** ## 3. Events Events are circles. Stroke weight encodes lifecycle role: | Kind | Stroke | DSL | | ------------ | ---------------- | -------------- | | Start | thin (1px) | `start` | | Intermediate | thin double ring | `intermediate` | | End | thick (3px) | `end` | The optional **trigger** keyword adds an inner glyph. v0.1 supports the three most common triggers — `none` (no glyph), `message` (envelope), and `timer` (clock face): ``` A: start // none-trigger A: start message "Inbound" // message-catch (unfilled envelope) T: intermediate timer "60 min" // timer (clock face) F: end "Done" ``` Filled glyph = **throw**, unfilled glyph = **catch**. Schematex picks the right fill automatically based on event kind. *** ## 4. Activities — tasks and subprocesses Activities are rounded rectangles. The two v0.1 forms are `task` and a **collapsed** subprocess (the `+` marker indicates expandable detail): ``` B: task "Generic abstract task" U: task user "User decides" S: task service "API call" SE: task send "Send email" RE: task receive "Wait for reply" M: task manual "Hand-stamp the form" SC: task script "Run rule engine" X: subprocess "Verify identity" collapsed ``` The **task marker** (small icon top-left) communicates *who or what* performs the work — a person (`user`), a software service (`service`), an outbound message (`send`), an inbound wait (`receive`), an out-of-system manual step (`manual`), or an automated script (`script`). When in doubt, omit the marker — it defaults to abstract. *** ## 5. Gateways Gateways are diamonds. The inner glyph encodes branching semantics: | Kind | Glyph | Meaning | DSL | | --------------- | ------------------ | --------------------------------------------- | --------------- | | Exclusive (XOR) | **X** | Take exactly one outgoing branch (data-based) | `gateway xor` | | Inclusive (OR) | **O** | Take one or more outgoing branches | `gateway or` | | Parallel (AND) | **+** | Take all outgoing branches concurrently | `gateway and` | | Event-based | pentagon-in-circle | Pick the branch whose event fires first | `gateway event` | Schematex uses Bruce Silver's **X glyph** for XOR by default — that's the convention real BPMN audits expect. Most diagrams use XOR (data branch) and AND (parallel split / join); OR is rare and event-based mostly appears in race-condition models. *** ## 6. Connectors Four connector types. Three of them stay inside a pool; only message flow is allowed to cross pool boundaries. ``` A --> B // sequence flow (default) G --? "yes" --> C // conditional sequence (label is a guard) G --* "default" --> D // default flow (one per gateway, max) "Customer" ~~> A : "Submit application" // message flow E ~~> "Customer" : "Notify approval" // message flow back ``` * `-->` is the workhorse — solid line + filled triangle arrowhead. * `--? "label" -->` adds a small unfilled diamond at the source. Use it when leaving an activity *directly* on a guarded outcome. (At a gateway, the conditional label suffices; the diamond glyph isn't drawn.) * `--* "label" -->` adds a slash mark at the source. **One default flow maximum per gateway** — Schematex enforces this. * `~~>` is dashed with an open arrowhead and a small unfilled circle at the source. Source or target may be a quoted **pool name** (for black-box participants) or an object id. *** ## 7. Validation The parser refuses diagrams that violate BPMN semantics, with line-numbered errors: | Rule | Error | | ----------------------------------- | ------------------------------------------------------------------------ | | Sequence flow crosses pool | `sequence flow 'A --> B' crosses pool boundary — use message flow (~~>)` | | Message flow inside one pool | `message flow 'A ~~> B' must cross pool boundaries` | | Black-box pool has internals | `black-box pool "X" cannot contain lanes` | | Two default flows from same gateway | `gateway 'G' has 2 default flows (max 1)` | | Duplicate id within a pool | `duplicate id 'A'` | | Unknown source / target | `unknown source 'X' in sequence flow` | These checks fire during parse, so an LLM gets a usable signal before the layout pass. *** ## 8. Larger example — pizza order with black-box customer A canonical BPMN tutorial: an external customer (whose process we don't model) places an order with a pizzeria split into Clerk / Chef / Delivery lanes, with a rework loop on the chef's quality check. ```schematex bpmn direction: LR title: "Pizza order" pool "Customer" blackbox pool "Pizzeria" { lane "Clerk" { A: start message "Order received" B: task user "Take order" } lane "Chef" { C: task manual "Make pizza" G1: gateway xor "Pizza ok?" D: task manual "Rework" } lane "Delivery" { E: task send "Deliver" F: end "Done" } } flows A --> B B --> C C --> G1 G1 --? "yes" --> E G1 --* "no" --> D D --> C E --> F "Customer" ~~> A : "Place order" E ~~> "Customer" : "Pizza delivered" ``` The rework loop (`D --> C`) creates a back-edge that the layout's cycle-break detects via DFS — the longest-path layering then proceeds on the forward DAG so columns stay sensible. *** ## 9. Limitations of v0.1 These are deferred to a later release. If your diagram needs one, file an issue with the use case: * **Boundary events** — events attached to an activity edge (timer, error, escalation, compensation). Currently you have to model the boundary as a free-floating intermediate event with manual flows. * **Expanded subprocesses** — collapsed `subprocess` works; expanded inline blocks (`subprocess "X" { … }`) are deferred. * **Rare event triggers** — error / escalation / cancel / compensation / signal / link / conditional / multiple / parallel-multiple. Use `none` or `message` as a placeholder. * **Transaction / call activities** — render as normal tasks for v0.1. * **Loop and multi-instance markers** — bottom-center activity glyphs; deferred. * **Artifacts** — data object / data store / group / text annotation. Deferred. * **BPMN 2.0 XML import / export** — out of scope. Schematex computes layout from DSL; no DI layer to round-trip. *** ## Related examples Ready-to-use scenarios from the examples gallery: [Browse related examples](https://schematex.js.org/examples) --- # Breadboard / Physical Wiring Canonical URL: https://schematex.js.org/docs/breadboard Markdown URL: https://schematex.js.org/docs/breadboard.md ## About breadboard diagrams A **breadboard diagram** shows how to physically wire components on a solderless prototyping board — the iconic visual genre of every Arduino / ESP32 / Raspberry Pi tutorial. The reader replicates what they see, hole-for-hole, on real hardware. This is *complementary to* the abstract circuit schematic (`circuit`, IEEE 315): the schematic is the engineer's reasoning view; the breadboard is the maker's replication view. Schematex implements **Fritzing-style** stylized breadboard rendering. Components are drawn as recognisable bodies (resistors with color bands, LEDs as colored domes, DIPs with notches, MCU PCBs with labelled pin headers). Jumper wires are drawn as **smooth cubic Bézier arcs** between specific tie-points — not Manhattan right angles. The DSL addresses every part and every wire by **breadboard-native coordinates** (`@col-row`, e.g. `@5e`), not pixel positions, so the file is hand-authorable and version-controllable. > **Note** — this engine is *not* a circuit schematic and does not enforce electrical rules. Use it for tutorials, lab handouts, READMEs, and learning material. For nodal analysis or formal schematics, use the `circuit` engine instead. ```schematex breadboard board: half title: "Blink LED — Arduino Uno hello-world" parts uno: mcu uno @beside-left r1: resistor 220 @5e..9e d1: led red @10e..10f wires uno:5V --red-- @+t1 uno:GND --black-- @-t1 @+t1 --red-- @5a uno:D13 --yellow-- @9a @10j --black-- @-t1 ``` *** ## 1. Your first breadboard Three sections: a one-line `breadboard` header, a `parts` block, and a `wires` block. Optional `board:` and `title:` lines come right after the header. ```schematex breadboard parts uno: mcu uno @beside-left r1: resistor 220 @5e..9e d1: led red @10e..10f wires uno:5V --red-- @+t1 uno:GND --black-- @-t1 uno:D13 --yellow-- @5a @10j --black-- @-t1 ``` Every part is `id: kind [args] @placement`. Every wire is ` --color-- `. That's the whole grammar. *** ## 2. Coordinates Breadboards have a 2D address grid. Schematex coordinates always start with `@`. | Form | Meaning | Example | | -------------- | ----------------------------------------------------------- | ---------------------- | | `@` | Main grid hole. Rows `a–e` (top half), `f–j` (bottom half). | `@5e`, `@12g` | | `@+t` | Top **positive** rail (red stripe). | `@+t8` | | `@-t` | Top **negative / GND** rail (blue stripe). | `@-t8` | | `@+b` | Bottom positive rail. | `@+b14` | | `@-b` | Bottom negative rail. | `@-b14` | | `@..` | Span — used in part placement (resistor, diode, LED). | `@5e..9e` | | `@beside-left` | Off-board placement for MCU boards. | `mcu uno @beside-left` | Mini boards (`board: mini`) have **no power rails** — `@+t…` / `@-b…` are rejected by the parser. *** ## 3. Board sizes ``` breadboard board: half // default — 30 columns, 400 tie-points, rails (continuous) ``` | Form | Tie points | Columns | Power rails | | ---------------- | ---------- | ------- | --------------------- | | `mini` | 170 | 17 | none | | `half` (default) | 400 | 30 | continuous | | `full` | 830 | 63 | break at column 30/31 | > **Pitfall** — on full-size boards the rails break at the middle. If your circuit uses both halves you must jumper the rails together explicitly. *** ## 4. Parts catalog Each part is `id: [args] @`. The catalog covers the most common Arduino / ESP32 maker components: **Discrete components** (sit on the breadboard): | DSL | Args | Example | | ------------- | -------------------------------------------- | -------------------------- | | `resistor` | `value` (Ω; supports `k`/`M`) | `r1: resistor 220 @5e..9e` | | `led` | `color` (red/green/blue/yellow/white/orange) | `d1: led red @10e..10f` | | `cap-elec` | — | `c1: cap-elec @4e..4f` | | `cap-ceramic` | — | `c2: cap-ceramic @6e..6f` | | `diode` | — | `d2: diode @5e..8e` | | `button` | — | `btn: button @8e` | | `dip` | `pins=N` | `ic: dip pins=8 @4e` | | `header` | `pins=N` | `h1: header pins=4 @20a` | **Microcontroller boards** (placed beside / above / below the substrate): | DSL | Pin labels | | ----------- | --------------------------------------------------------------- | | `mcu uno` | `5V`, `3V3`, `GND`, `VIN`, `RST`, `D2…D13`, `A0…A5`, `RX`, `TX` | | `mcu nano` | Subset of Uno labels | | `mcu esp32` | `3V3`, `GND`, `VIN`, `GPIO2`, `GPIO4`, `GPIO5`, `GPIO12…GPIO33` | | `mcu pico` | Same generic GPIO labels | **Sensors / displays / actuators** (modules sit on the breadboard with pin row anchored at the supplied coordinate): | DSL | Pins | | ------------------------------- | ------------------------------- | | `sensor hcsr04` | `VCC`, `TRIG`, `ECHO`, `GND` | | `sensor dht11` / `sensor dht22` | `VCC`, `DATA`, `GND` | | `display oled-ssd1306` | `GND`, `VCC`, `SCL`, `SDA` | | `display lcd-1602-i2c` | `GND`, `VCC`, `SDA`, `SCL` | | `module rotary-ky040` | `CLK`, `DT`, `SW`, `VCC`, `GND` | | `actuator servo-sg90` | `GND`, `VCC`, `SIG` | Resistor color bands are decorated automatically from `value` — `220` → red-red-brown-gold, `10000` → brown-black-orange-gold. *** ## 5. Wires Every wire connects two endpoints. An endpoint is either a **part pin** (`partId:pinName`) or a **breadboard coordinate** (`@…`). ``` wires uno:5V --red-- @+t1 uno:GND --black-- @-t1 uno:D9 --yellow-- @9c @9a --green-- @+t9 ``` | Color | Conventional role | | -------------------------------------------------- | ------------------ | | `red` | +V (5V, 3.3V, VCC) | | `black` / `blue` | GND | | `yellow` / `orange` / `green` / `white` / `purple` | Signal | | `brown` / `grey` | Arbitrary signal | Color is purely visual — the engine does not validate it against electrical role. For visually crowded boards, `via @` lets you pin an intermediate hole that biases the Bézier control points: ``` wires uno:D13 --yellow-- @9a via @8c ``` Most wires don't need `via` — the layout engine produces a natural arc on its own. *** ## 6. Sensor with pull-up resistor (DHT11 motif) The iconic Arduino tutorial pattern: a 10 kΩ pull-up between VCC and the sensor's data line. ```schematex breadboard board: half title: "DHT11 + 10kΩ pull-up" parts uno: mcu uno @beside-left s1: sensor dht11 @6a r1: resistor 10000 @8e..14e wires s1:VCC --red-- @+t1 s1:GND --black-- @-t1 s1:DATA --yellow-- @8e @14e --red-- @+t14 uno:5V --red-- @+t1 uno:GND --black-- @-t1 uno:D2 --yellow-- @8a ``` *** ## 7. ESP32 + I²C OLED ESP32 runs at 3.3 V (not 5 V). I²C convention: green = SDA, white = SCL. ```schematex breadboard board: half title: "ESP32 + SSD1306 OLED I²C" parts esp: mcu esp32 @beside-left oled: display oled-ssd1306 @8a wires oled:GND --black-- @-t1 oled:VCC --red-- @+t1 oled:SCL --white-- @10c oled:SDA --green-- @11c esp:3V3 --red-- @+t1 esp:GND --black-- @-t1 esp:GPIO22 --white-- @10c esp:GPIO21 --green-- @11c ``` *** ## 8. Limitations of v0.1 * **No leader-line callouts** — reference designators (R1, C2) are drawn near the part body. Off-board callout boxes are deferred. * **No `.fzz` import** — Schematex consumes only its own DSL; Fritzing files are not parsed. * **No simulation** — this is a renderer, not a Wokwi-style simulator. Component-value validation (Ohm's law, current limits) is out of scope. * **No PCB / schematic round-trip** — `breadboard` and `circuit` are independent engines. Authoring two views of the same prototype currently means writing two DSLs. * **Fixed parts catalog** — user-defined part types are deferred. v0.1 ships the maker-tutorial 80% catalog (resistors, LEDs, caps, DIPs, headers, four MCU families, six sensor / display / actuator modules). * **Power-rail break visual** — full-size boards mark the 30/31 break with a hole-wide gap; the rail stripes still draw through the gap (cosmetic). *** ## Related examples Ready-to-use scenarios from the examples gallery: [Browse related examples](https://schematex.js.org/examples) ## Interactive editing The title is editable and movable. On-board parts and jumper endpoints use native x/y handles; drops snap authored point/span placements to valid holes while connected wires follow live. --- # Causal Loop Diagram > System-dynamics feedback diagrams — the engine enumerates every feedback loop and classifies it reinforcing or balancing by polarity. Canonical URL: https://schematex.js.org/docs/causalloop Markdown URL: https://schematex.js.org/docs/causalloop.md ## About causal loop diagrams A **causal loop diagram** (CLD) is the core mapping tool of **system dynamics** (Jay Forrester, MIT, 1960s): variables connected by signed causal links, where the *feedback loops* — not the individual arrows — explain a system's behaviour. A loop is **reinforcing (R)** when it amplifies change and **balancing (B)** when it counteracts it. The canonical reference is Sterman, *Business Dynamics* (2000). Schematex's edge is that the engine **finds and classifies the loops for you**. Drawing tools let you place arrows; they don't tell you which loops exist or whether each is reinforcing or balancing. Schematex enumerates every elementary feedback loop (Johnson's algorithm) and applies Sterman's even/odd polarity rule — labelling them R1, B1, R2… ```schematex causalloop "Sales force flywheel" var "Training quality" "Training quality" -> "Salesperson skills" : + delay "Salesperson skills" -> Revenue : + Revenue -> "Training quality" : + Revenue -> "Hiring rate" : + "Hiring rate" -> "Salesperson skills" : - loop R1 "Skill flywheel" loop B1 "Dilution" ``` *** ## 1. Your first causal loop Start with the `causalloop` keyword (alias `cld`), an optional title, then **signed links**. Variables are auto-created from the links — you rarely declare them: ``` causalloop "Adoption model" "Adoption rate" -> Adopters : + Adopters -> "Adoption rate" : + loop R1 "Word of mouth" ``` A link is `SOURCE -> TARGET : POLARITY`. Multi-word variable names are quoted (`"Adoption rate"`); single words need no quotes (`Adopters`). At least one link is required. *** ## 2. Link polarity Polarity is the sign of the causal influence and is **mandatory** on every link: ``` A -> B : + # same direction (more A → more B) B -> C : - # opposite direction (more B → less C) A -> B : s # alias for + (same) B -> C : o # alias for − (opposite) C -> D : same # alias for + D -> E : opposite # alias for − A -> B + # the colon is optional ``` `+` / `s` / `same` mean *same direction*; `−` / `o` / `opposite` mean *opposite*. A link with no polarity is rejected. *** ## 3. Delays and explicit variables ``` "Training quality" -> "Salesperson skills" : + delay # marked delay (∥ hash on the arrow) A -> B : + ~delay # the ~delay form also works var "Adoption rate" # pin a variable so it isn't auto-created loop R1 "Word of mouth" # name/annotate a loop ``` * **`delay`** / **`~delay`** marks a link as delayed (the system-dynamics hash mark). * **`var "name"`** declares a variable explicitly (fixes its label; not auto-created). * **`loop ID "phrase"`** attaches a human-readable name to a loop the engine detects. *** ## 4. Computed feedback loops This is the differentiator. The engine: 1. Builds the **signed directed graph** (nodes = variables, edges = signed links). 2. Enumerates **every elementary feedback loop** (simple directed cycle) with Johnson's algorithm — deterministically, in declaration order. 3. **Classifies** each loop by counting negative links: * **even** count (including 0) → **R** (reinforcing); product of signs = +1 * **odd** count → **B** (balancing); product of signs = −1 This is exactly Sterman's even/odd rule. Loops are numbered in detection order by kind (R1, B1, R2…) and drawn with their `R`/`B` rotation glyph at the loop centre. Each loop carries `data-loop` and `data-kind`. *** ## 5. Common mistakes ``` # WRONG — link with no polarity A -> B # WRONG — a diagram with no links at all cld ``` Every link needs a polarity (`: +` or `: -`); a CLD with no links is rejected. Remember polarity is about *direction of change*, not desirability — a link from "deaths" to "population" is still `-` (more deaths → less population) even though deaths are bad. *** ## 6. Standard compliance Notation follows Sterman, *Business Dynamics* (2000) and standard system-dynamics convention: signed link polarity, delay hash marks, and R/B loop identifiers. The even/odd negative-link rule for loop polarity is the textbook classification, implemented exactly. ## 7. Roadmap Deferred: stock-and-flow promotion, link-strength weighting, and dominant-loop (loop-eigenvalue) analysis over time. --- # Circuit schematic Canonical URL: https://schematex.js.org/docs/circuit Markdown URL: https://schematex.js.org/docs/circuit.md ## About circuit schematics A **circuit schematic** is the standard graphical representation of an electronic circuit — components drawn as standardized symbols, connected by wires, with enough information to build or simulate the circuit. Electronics engineers use them throughout the product lifecycle: from initial concept to PCB layout review and datasheet documentation. Schematex follows **[IEEE Std 315-1975 / ANSI Y32.2](https://standards.ieee.org/ieee/315/738/)** and **[IEC 60617](https://webstore.iec.ch/publication/2765)** for component symbols. The DSL has two modes. **Netlist mode (recommended)** is SPICE-style: you list components and the nodes they connect to, and the engine lays everything out automatically — each line is self-contained, with no spatial state to track. **Positional mode** is for hand-drawing: components chain in a direction like Schemdraw. Both produce the same SVG. **For generated diagrams (e.g. by an LLM), always use netlist mode** — it has by far the smaller error surface. ```schematex circuit "CE Amp (netlist)" netlist V1 vcc 0 9V Rc vcc c 2.2k Rb vcc b 100k Q1 c b e npn Re e 0 1k ``` *** ## 1. A minimal circuit (netlist mode — recommended) The smallest useful circuit: a voltage source, a resistor, and a capacitor to ground — an RC low-pass filter. ```schematex circuit "RC Low-Pass" netlist V1 in 0 5V R1 in out 1k C1 out 0 100n ``` Three rules cover \~90% of netlist usage: 1. Start with `circuit "Title" netlist` (the `netlist` keyword switches on this mode). 2. Each line is `componentId nodeA nodeB value` — one component, the two (or more) named nodes it connects to, then its value. 3. **Two components that share a node name are wired together.** `0`, `gnd`, or `GND` is the ground net (a ground symbol is drawn automatically). The component-id prefix sets the symbol: `R*`→resistor, `C*`→capacitor, `L*`→inductor, `V*`→voltage source, `D*`→diode, `Q*`→BJT. When the prefix is ambiguous, add `type=` (e.g. `X1 a b type=opamp`). You never compute coordinates — the engine derives placement from the connectivity. > Comments must start with `#` on their own line. *** ## 2. Components ### 2.1 Netlist mode syntax A netlist line has the form: ``` componentId node... [value] [type=…] [label="…"] ``` The positional nodes come first; a trailing token that doesn't look like a node becomes the value. Example — a transistor (4 nodes) and a resistor: ``` Q1 c b e npn # collector, base, emitter nodes + model Rc vcc c 2.2k # two nodes + value ``` **Optional orientation hint.** The engine auto-orients symbols by role (sources up, ground down, the rest horizontal). To nudge a single symbol, add `dir=right|left|up|down` — connectivity is unchanged, only the symbol's facing rotates: ``` C1 out 0 100n dir=down # draw C1 as a shunt cap hanging to ground ``` This is the lightweight layout-control layer (like Lcapy's per-component orientation): netlist connectivity does the heavy lifting, `dir=` only refines appearance. For full geometric control, use positional mode below. ### 2.2 Positional mode syntax (hand-drawing) > Positional mode is for manually laying out a schematic geometrically. **Prefer netlist mode for generated output** — positional mode requires tracking a moving "cursor" across lines, which is error-prone for LLMs. A named component line has the form: ``` id: type direction [value="…"] [label="…"] ``` An anonymous component omits the `id:` prefix — the parser assigns an auto ID. ``` R1: resistor right value="4.7k" label="R1" capacitor down value="100n" ``` **Direction** is one of `right` (default), `left`, `up`, `down`. It controls which way the component extends from the current cursor position. ### 2.3 Passive components | DSL type | Description | | ------------------- | ------------------------------------------- | | `resistor` | Zigzag (ANSI) or rectangle (IEC) | | `potentiometer` | Resistor + wiper arrow, 3-pin | | `rheostat` | 2-pin variable resistor | | `thermistor_ntc` | NTC thermistor (also: `therm`, `ntc`) | | `thermistor_ptc` | PTC thermistor (also: `ptc`) | | `ldr` | Light-dependent resistor | | `varistor` | Voltage-dependent resistor | | `fuse` | Standard fuse | | `fuse_slow` | Slow-blow fuse (`T` designation) | | `capacitor` | Non-polar capacitor | | `electrolytic_cap` | Polar/electrolytic capacitor (also: `ecap`) | | `variable_cap` | Variable capacitor | | `inductor` | Air-core inductor | | `inductor_iron` | Iron-core inductor | | `inductor_ferrite` | Ferrite-core inductor | | `variable_inductor` | Variable inductor | | `ferrite_bead` | EMI ferrite bead | | `crystal` | Quartz crystal oscillator (also: `xtal`) | | `transformer` | Coupled coils (also: `xfmr`) | ```schematex circuit "Passive components gallery" # Row 1: resistor → capacitor → inductor R1: resistor right value="1k" wire right C1: capacitor right value="100n" wire right L1: inductor right value="10u" # Row 2: crystal and transformer, offset below at: R1.start wire down wire down X1: crystal right wire right wire right T1: transformer right ``` ### 2.4 Sources and power | DSL type | Description | | ---------------- | ----------------------------------------------- | | `voltage_source` | Circle + polarity (also: `vsource`) | | `current_source` | Circle + arrow (also: `isource`) | | `ac_source` | Circle + sine symbol (also: `acsource`) | | `battery` | Alternating long/short terminal lines | | `vcc` | Power rail arrow (pointing up) | | `ground` | Earth ground — 3 decreasing lines (also: `gnd`) | | `gnd_signal` | Signal ground — solid triangle | | `gnd_chassis` | Chassis ground | | `gnd_digital` | Digital ground | ```schematex circuit "Sources and power gallery" # voltage source with ground V1: voltage_source down value="5V" wire down ground at: V1.start wire right wire right # battery B1: battery down value="9V" wire down ground at: B1.start wire right wire right # ac source A1: ac_source down value="120V" wire down ground at: A1.start wire right wire right # vcc rail vcc up wire down gnd_signal down ``` ### 2.5 Semiconductors — diodes | DSL type | Description | | ------------------ | --------------------------------- | | `diode` | Triangle + cathode bar | | `zener` | Diode + bent cathode bar | | `schottky` | Diode + S-bar | | `led` | Diode + outward emission arrows | | `photodiode` | Diode + inward light arrows | | `varactor` | Diode + variable capacitor | | `tvs_diode` | Bidirectional TVS (two bent bars) | | `bridge_rectifier` | 4-diode bridge, 4-pin | ```schematex circuit "Diode types gallery" D1: diode right wire right D2: zener right wire right D3: led right wire right D4: schottky right wire right D5: photodiode right wire right ground at: D1.start wire left ground ``` ### 2.6 Semiconductors — transistors | DSL type | Description | | ----------------- | ----------------------------------------------- | | `npn` | NPN BJT (also: `transistor`, `bjt_npn`) | | `pnp` | PNP BJT (also: `bjt_pnp`) | | `darlington_npn` | NPN Darlington pair | | `darlington_pnp` | PNP Darlington pair | | `nmos` | N-channel MOSFET enhancement (also: `mosfet_n`) | | `pmos` | P-channel MOSFET enhancement (also: `mosfet_p`) | | `nmos_depletion` | N-channel MOSFET depletion | | `jfet_n` | N-channel JFET | | `jfet_p` | P-channel JFET | | `igbt` | IGBT | | `scr` | SCR / thyristor | | `triac` | TRIAC | | `diac` | DIAC | | `phototransistor` | NPN with light arrows | | `optocoupler` | LED + phototransistor in isolation box | ```schematex circuit "Transistor types gallery" # NPN BJT Q1: npn right wire right wire right # PNP BJT Q2: pnp right wire right wire right # N-channel MOSFET Q3: nmos right wire right wire right # P-channel MOSFET Q4: pmos right ``` ### 2.7 Analog ICs and op-amps | DSL type | Description | | --------------------- | ------------------------------------------------ | | `opamp` | Triangle: +/− inputs, output | | `comparator` | Same shape, open-collector output | | `schmitt_buffer` | Buffer + hysteresis symbol | | `tri_state_buffer` | Buffer + enable pin | | `instrumentation_amp` | Three-op-amp INA block | | `generic_ic` | Configurable rect with labeled pins (also: `ic`) | | `voltage_regulator` | 3-terminal block: IN/GND/OUT (also: `reg`) | | `dc_dc_converter` | 2-port block with DC/DC label | | `555_timer` | 8-pin 555 pinout block (also: `timer555`) | ```schematex circuit "Analog IC gallery" # op-amp with input/output wires wire right U1: opamp right wire right wire right wire right # comparator U2: comparator right wire right wire right wire right # generic IC block U3: generic_ic right ``` ### 2.8 Switches and relays | DSL type | Description | | ------------- | ----------------------------- | | `switch_spst` | Single-pole single-throw | | `switch_spdt` | Single-pole double-throw | | `switch_dpdt` | Double-pole double-throw | | `push_no` | Push button normally-open | | `push_nc` | Push button normally-closed | | `relay_coil` | Relay coil (2-pin rect) | | `relay_no` | Relay contact normally-open | | `relay_nc` | Relay contact normally-closed | ```schematex circuit "Switch and relay gallery" # SPST switch S1: switch_spst right wire right wire right # SPDT switch S2: switch_spdt right wire right wire right # normally-open push button S3: push_no right wire right wire right # relay coil + contact pair K1: relay_coil right wire right K2: relay_no right ``` ### 2.9 Electromechanical and measurement | DSL type | Description | | -------------- | ---------------------------------------------------- | | `motor` | Circle + M | | `lamp` | Lighting load: circle + X (aliases: `light`, `bulb`) | | `speaker` | Cone + box | | `microphone` | Capsule symbol | | `buzzer` | Piezo buzzer | | `ammeter` | Circle + A | | `voltmeter` | Circle + V | | `wattmeter` | Circle + W | | `oscilloscope` | Circle + waveform | ### 2.10 Connectors and annotations | DSL type | Description | | ------------ | --------------------------------- | | `wire` | Plain wire segment | | `dot` | Junction dot (T-junction marker) | | `label` | Net label / flag | | `port` | Named port (hollow circle) | | `test_point` | TP marker | | `no_connect` | X — intentionally unconnected pin | | `antenna` | Antenna stub | ```schematex circuit "Passive components" R1: resistor right value="1k" label="R1" wire right C1: capacitor down value="100n" label="C1" wire down ground at: R1.start wire up battery up label="9V" ``` *** ## 3. Wiring and branching ### 3.1 Wire segments `wire direction [N]` draws a bare wire from the current cursor in the given direction. An optional number sets the length in pixels. ``` wire right wire down 40 wire left 20 ``` ### 3.2 Jumping the cursor with `at:` `at: id.end` moves the cursor to a named anchor without drawing anything. Use it to branch from a previously placed component. ``` R1: resistor right value="10k" at: R1.end C1: capacitor down value="100n" ``` Named anchor suffixes: `end`, `start`. Components retain their ID across the whole diagram, so you can jump back to any previously placed component. ### 3.3 Junction dots Place a `dot` (or use `net NAME: dot`) to mark a T-junction — a point where three or more wires meet. Without a dot, crossed wires are drawn as a crossover (no connection). ``` R1: resistor right dot wire right # continues from R1.end at: R1.end C1: capacitor down # branches down from the same point ``` ### 3.4 Named nets `net NAME` declares a named net. `net NAME: dot` declares the net and places a junction dot at the current cursor, remembering that location. Later, `at: NAME` jumps back to that net's anchor. ``` net VOUT: dot R2: resistor right value="10k" at: VOUT C1: capacitor down value="470n" ``` ### 3.5 Net labels `label "text" direction?` places a text label at the current cursor position. Labels do not advance the cursor. They are useful for naming power rails or inter-sheet connections. ``` label "VCC" up label "GND" down ``` ```schematex circuit "RC filter" V1: voltage_source down value="5V" wire right R1: resistor right value="1k" label="R1" net OUT: dot wire right label "Vout" right at: OUT C1: capacitor down value="100n" label="C1" wire down ground ``` *** ## 4. Netlist mode Add `netlist` after the title on the header line to switch to SPICE-style netlist parsing. The auto-layout engine computes component positions from the net connectivity. ``` circuit "Low-pass filter" netlist ``` ### 4.1 Netlist line format Each line is: `ID net1 net2 [net3…] [value] [key=value…]` * **ID** — component identifier. The first letter determines the default type (SPICE prefix convention). * **net1, net2, …** — net names the pins connect to. Net names matching `0`, `gnd`, `ground`, `earth`, `pe`, `agnd`, `dgnd`, `gnda`, `gndd`, `vss`, or `com` (case-insensitive, with optional `_` or numeric suffix — e.g. `gnd_ref`, `AGND_DIG`, `EARTH1`) all canonicalize to the ground net. * **value** (optional bare token) — component value or model name. * **key=value** (optional) — `label=`, `value=`, `type=` overrides. ### 4.2 SPICE prefix → component type | Prefix | Default type | Pin order | | -------- | ---------------- | --------------------------------------------- | | `R` | `resistor` | p1, p2 | | `C` | `capacitor` | p1, p2 | | `L` | `inductor` | p1, p2 | | `D` | `diode` | anode (start), cathode (end) | | `V` | `voltage_source` | plus, minus | | `I` | `current_source` | plus, minus | | `Q` | `npn` | c, b, e | | `M` | `nmos` | d, g, s | | `J` | `jfet_n` | d, g, s | | `S` | `switch_spst` | p1, p2 | | `F` | `fuse` | p1, p2 | | `B` | `battery` | plus, minus | | `K` | `relay_coil` | p1, p2 | | `U`, `X` | `generic_ic` | custom via `pins=` | | `W` | `wire` | start, end | | `T` | `terminal_block` | custom via `pins=` (also `type=junction_box`) | > **Scope:** schematex circuit covers **electrical schematics only** (IEEE 315 / IEC 60617). Hydraulic and pneumatic schematics (ISO 1219) use a fundamentally different visual grammar — directional valve envelopes, cylinder symbols, line styles for pressure/return/drain — and are not supported by this engine. Hydraulic prefixes such as `EV*` (electrovalve), `BOMBA*` (pump), `TANK*`, `DIPOSIT*` will be rejected with a "cannot infer type" error. ### 4.3 Transistor model override For `Q` lines, a trailing model name overrides the type: ``` Q1 c b e npn # NPN BJT Q2 c b e pnp # PNP BJT M1 d g s nmos # N-channel MOSFET M2 d g s pmos # P-channel MOSFET ``` For `D` lines, similarly: ``` D1 anode cathode zener D2 anode cathode led D3 anode cathode schottky D4 anode cathode photodiode ``` ### 4.4 Netlist example ```schematex circuit "CE Amp (netlist)" netlist V1 vcc 0 9V Rc vcc c 2.2k Rb vcc b 100k Q1 c b e npn Re e 0 1k ``` ### 4.5 Household lighting circuits Use explicit `type=lamp` for a lighting load because a bare `L` prefix means **inductor** in SPICE. Keep the source on named `live` / `neutral` nets; auto-layout places the protected live path above a lower neutral return rail. ```text circuit "Single-pole household light" netlist V1 live neutral 220Vac type=acsource label="V_mains" F1 live protected 16A S1 protected switched type=switch_spst label="S1" L1 switched neutral type=lamp label="Lamp" ``` For two-location (stair) switching, use two SPDT switches sharing two traveler nets. Series SPST switches describe a different circuit and cannot provide normal two-way control. ```text circuit "Two-way stair light" netlist V1 live neutral 220Vac type=acsource label="V_mains" F1 live feed 16A S1 feed traveler_a traveler_b type=switch_spdt label="S1" S2 switched traveler_a traveler_b type=switch_spdt label="S2" L1 switched neutral type=lamp label="Lamp" ``` Use `L1` (or another load-appropriate designator) for a lamp. Reserve `RL1` for an actual relay or load resistor. *** ## 5. Attributes Both positional and netlist modes accept these key=value attributes: | Attribute | Accepted by | Effect | | ----------- | --------------------- | -------------------------------------- | | `label="…"` | all components | Display label (reference designator) | | `value="…"` | all components | Value annotation (1kΩ, 100nF, 5V) | | `at=id.end` | positional components | Start this component at a named anchor | | `length=N` | `wire`, some passives | Length in pixels | In positional mode, `at=` inside the component line is equivalent to a preceding `at:` line: ``` C1: capacitor down at=R1.end value="100n" ``` *** ## 6. Labels & comments * **Diagram title:** `circuit "RC Filter"` — first line only. * **Component label:** `label="R1"` attribute — reference designator shown beside the symbol. * **Value annotation:** `value="4.7k"` — shown beside or below the component. * **Net label:** `label "VOUT" right` — standalone net flag at the current cursor. * **Comments:** `#` at the start of a line (after leading whitespace). *** ## 7. Reserved words & escaping **Reserved at line start (positional):** `circuit` (header), `at:`, `net`, `wire`, `label`. **Reserved in netlist mode:** same header rules apply; all other lines are SPICE component lines. **Ground net aliases (netlist only):** `0`, `gnd`, `GND`, `Gnd`, `ground`, `Ground` — all treated as the same node. **Component IDs** must match `[a-zA-Z_][a-zA-Z0-9_]*`. Spaces in values must be quoted: `value="10 kΩ"`. *** ## 8. Common mistakes | You wrote | Parser says | Fix | | ------------------------------------------------- | ----------------------------------------------------------------------------- | -------------------------------------------------------- | | `resistor right 1k` (bare value without `value=`) | `1k` is parsed as an unknown attribute flag and ignored | Use `value="1k"`: `resistor right value="1k"` | | `at: R1.center` | `center` is not a recognized anchor suffix — cursor stays at current position | Use `at: R1.end` or `at: R1.start` | | `wire 40` (no direction) | Direction defaults to `right`; length `40` is accepted | Explicit direction recommended: `wire right 40` | | `R1 vcc out 10k` in positional mode | Line matches the bare-type pattern; `R1` is read as a type name, fails lookup | In positional mode, use `R1: resistor right value="10k"` | | `Q1 c b e` (netlist, no model) | Type defaults to `npn` from `Q` prefix — correct | OK; add `npn` explicitly for clarity | | `net OUT` then `at: OUT` without `net OUT: dot` | `OUT` net exists but has no anchor; jump has no destination | Use `net OUT: dot` to register the cursor position | | `label VCC up` (unquoted label) | `VCC` is parsed as a direction token, then `up` — the label text is lost | Quote the text: `label "VCC" up` | *** ## 9. Grammar (EBNF) ```text document = header statement* -- Positional mode -- header = "circuit" ( WS quoted-string )? NEWLINE statement = blank | comment | component | wire | at | net-decl | label-stmt component = ( id ":" WS )? type WS direction? attrs* NEWLINE wire = "wire" ( WS direction )? ( WS integer )? NEWLINE at = "at:" WS anchor NEWLINE anchor = id "." ( "start" | "end" ) | id // net name anchor net-decl = "net" WS id NEWLINE // declare net only | "net" WS id ":" WS "dot" NEWLINE // declare + place dot label-stmt = "label" WS quoted-string ( WS direction )? NEWLINE component-attr = "value=" quoted-string | "label=" quoted-string | "at=" anchor | "length=" integer direction = "right" | "left" | "up" | "down" type = // any value from §2 component tables -- Netlist mode -- netlist-header = "circuit" ( WS quoted-string )? WS "netlist" NEWLINE netlist-stmt = id WS net-ref+ ( WS kv-pair )* NEWLINE | comment net-ref = id | "0" // net name or ground alias kv-pair = id "=" ( quoted-string | bare-value ) id = [a-zA-Z_] [a-zA-Z0-9_]* integer = [0-9]+ quoted-string = '"' any-char-but-quote* '"' comment = "#" any NEWLINE ``` Authoritative source: `src/diagrams/circuit/parser.ts` and `src/diagrams/circuit/netlist.ts`. If this diverges from the parser, the parser wins — please open an issue. *** ## 10. Standard compliance Schematex circuit schematics follow **IEEE Std 315-1975 / ANSI Y32.2** for component symbol shapes and **IEC 60617** for international variants. The netlist syntax follows **SPICE** prefix conventions (Berkeley SPICE3 / LTspice / ngspice). What is implemented today: * ✅ Full passive component set: resistor variants, capacitor variants, inductor variants, crystal, transformer * ✅ Sources and power: voltage source, current source, AC source, battery, VCC, four ground styles * ✅ Diode family: diode, zener, schottky, LED, photodiode, varactor, TVS, bridge rectifier * ✅ BJTs: NPN, PNP, Darlington NPN/PNP * ✅ FETs: NMOS/PMOS enhancement, NMOS depletion, N/P-channel JFET * ✅ Power semiconductors: IGBT, SCR, TRIAC, DIAC * ✅ Optoelectronics: phototransistor, optocoupler * ✅ Analog ICs: op-amp, comparator, Schmitt buffer, tri-state buffer, INA, generic IC, voltage regulator, DC-DC converter, 555 timer * ✅ Switches: SPST, SPDT, DPDT, push-NO, push-NC * ✅ Relays: coil, NO contact, NC contact * ✅ Electromechanical: motor, speaker, microphone, buzzer * ✅ Measurement: ammeter, voltmeter, wattmeter, oscilloscope * ✅ Annotations: wire, dot, label, port, test point, no-connect, antenna * ✅ Positional DSL: direction chaining, `at:` branching, `net` declarations * ✅ Netlist DSL: SPICE prefix mapping, model overrides, auto-ground synthesis * ⏳ Pin-level `at:` for BJT/FET/op-amp named pins (base, collector, plus, minus, out) * ⏳ Auto-routed wires in positional mode (routing around placed symbols) * ⏳ Bus wires with bit-width annotation (`/8` slash) References: * IEEE Std 315-1975 (ANSI Y32.2): *Graphic Symbols for Electrical and Electronics Diagrams* * IEC 60617: *Graphical symbols for diagrams* * SPICE3 User's Manual, UC Berkeley — netlist line format conventions *** ## 11. Related examples [Browse related examples](https://schematex.js.org/examples) *** ## 12. Roadmap **Planned — not yet parseable.** Do not use these in generated DSL today; the parser will reject or ignore them. * **Named pin anchors for multi-pin components** — `at: Q1.base`, `at: U1.out`, `at: U1.plus` so op-amp and BJT feedback loops can be wired without long `wire` detours. * **Bus wire** — `wire right bus=8` drawing a thick wire with a `/8` bit-width slash annotation. * **Auto-routed wires** — `connect R1.end U1.minus` letting the engine route the wire around placed symbols. * **`flip` and `reverse` attributes** — mirror or reverse a component's polarity/orientation along the direction axis. * **`dashed` wire style** — dashed line for RF shields, cable bundles, or virtual connections. * **Hierarchical sheets** — `module "name" { … }` grouping analogous to logic gate modules, for documenting multi-sheet schematics. Track in the GitHub issues if you need any of these sooner. ## Interactive editing The title and explicit component label/value attributes are editable. Stable component IDs move freely. In netlist mode every attached net previews from the moving component and is regenerated as horizontal/vertical orthogonal segments after the drop; generated wire identities remain protected. --- # Comparison & Decision Matrix Canonical URL: https://schematex.js.org/docs/comparison Markdown URL: https://schematex.js.org/docs/comparison.md ## About comparison diagrams **comparison** is one engine for the whole "put things side by side and decide" family. Pick a `mode:` and the same DSL renders five different professional artifacts: * **`tchart`** — 2–N labelled columns of bullet points (the classic compare/contrast T-chart; three columns gives a Y-chart). * **`pros-cons`** — a two-column list with green ✓ / red ✗ valence. * **`matrix`** — an options × criteria grid; cells take free text or `yes` / `no` / `partial` marks. * **`decision`** (alias header `pugh`) — the **weighted decision matrix**: each criterion carries a `weight:`, each option a numeric score, and **the engine computes** every option's weighted total Σ(weight × score), ranks them, and highlights the winner. This is Stuart Pugh's controlled-convergence method (ASQ / Six-Sigma concept selection) — the same "engine computes the answer" stance as `pert` and `faulttree`. * **`double-bubble`** — the Thinking-Maps compare/contrast organizer: two centres, shared traits in the middle, unique traits fanning out. > **Not to be confused with [`matrix`](/docs/matrix).** That engine *positions* items on two continuous axes (Eisenhower, BCG, impact–effort). `comparison` lays out a **table** and, in `decision` mode, **computes the decision**. They are the two halves people conflate. ```schematex comparison "Selecting a CI/CD platform" mode: decision baseline: "Jenkins" option "GitHub Actions" option "GitLab CI" option "Jenkins" criterion "Ease of setup" weight: 5 GitHub Actions: 5 GitLab CI: 4 Jenkins: 2 criterion "Build speed" weight: 4 GitHub Actions: 4 GitLab CI: 4 Jenkins: 3 criterion "Cost at our scale" weight: 4 GitHub Actions: 4 GitLab CI: 3 Jenkins: 5 criterion "Self-host control" weight: 2 GitHub Actions: 2 GitLab CI: 5 Jenkins: 5 ``` ## 1. Header and mode The header keyword is `comparison` (aliases `compare`, `vs`). The header keywords `tchart` and `pugh` set the mode directly. Otherwise choose with the `mode:` directive: ``` comparison "Title" mode: tchart | pros-cons | matrix | decision | double-bubble legend: on | off ``` If you omit `mode:`, it is inferred from the keywords you use — but generating it explicitly is more reliable. ## 2. T-chart (and Y-chart) Declare each `column`, then list its points with `-` bullets. Three columns reads as a Y-chart. ``` tchart "TCP vs UDP" column "TCP" - Connection-oriented (handshake) - Guaranteed, ordered delivery column "UDP" - Connectionless, fire-and-forget - Minimal header, low latency ``` ## 3. Pros / cons ``` comparison "Migrate to microservices?" mode: pros-cons pro "Independent team deploys" pro "Scale hot paths in isolation" con "Distributed-systems complexity" con "Operational + infra cost goes up" ``` `pro` lines fill the green column, `con` the red — order independent. ## 4. Comparison matrix Declare every `option` (the columns), then each `criterion` (a row) with one indented `OptionName: value` cell per option. Cell values: `yes` / `no` / `partial` render as ✓ / ✗ / \~, numbers are scores, quoted text is shown verbatim. The option name must match an `option` exactly (a typo is flagged, not dropped). ``` comparison "Cloud provider — managed services" mode: matrix option "AWS" option "GCP" option "Azure" criterion "Free tier" AWS: "12 months" GCP: "Always-free" Azure: "12 months" criterion "Managed Postgres" AWS: yes GCP: yes Azure: partial ``` A compact pipe form is also accepted: `criterion "Free tier" | "12 months" | "Always-free" | "12 months"` (positional to option order). ## 5. Decision matrix (computed) Add a `weight:` to each criterion and a numeric score to each cell. The engine appends a **Weighted total** row, ranks the options (`#1`, `#2`, …), and highlights the winner. Add `baseline: "Option"` for a Pugh datum — that column is shaded and a **vs datum** delta row is added. ``` pugh "Database for the new service" baseline: "PostgreSQL" option "PostgreSQL" option "MongoDB" option "DynamoDB" criterion "Query flexibility" weight: 5 PostgreSQL: 5 MongoDB: 3 DynamoDB: 2 criterion "Horizontal scaling" weight: 4 PostgreSQL: 3 MongoDB: 4 DynamoDB: 5 criterion "Operational cost" weight: 3 PostgreSQL: 4 MongoDB: 3 DynamoDB: 3 ``` You never write the totals — the engine computes Σ(weight × score), so getting a score wrong changes the computed winner. ## 6. Double-bubble (compare & contrast) ``` comparison "Plant cell vs Animal cell" mode: double-bubble left "Plant cell" right "Animal cell" shared "Has a nucleus" shared "Mitochondria" left-only "Cell wall" left-only "Chloroplasts" right-only "Centrioles" right-only "Lysosomes" ``` `shared` traits sit in the middle, connected to both centres; `left-only` / `right-only` fan out to their own centre. ## 7. Themes `default` is the house blue with green/red/amber valence; `monochrome` drops colour (valence rides on ✓/✗/\~, the winner on a heavy border) for B\&W print; `dark` is Schematex slate/blue. ## Standard Pugh, *Total Design* (1991) controlled convergence · ASQ decision matrix · Hyerle Thinking Maps (double-bubble) · K-12 graphic-organizer convention. See `docs/reference/51-COMPARISON-STANDARD.md`. --- # Contributing a new diagram type > Step-by-step guide for adding a new diagram plugin to Schematex — from standard spec to published website example. Canonical URL: https://schematex.js.org/docs/contributing Markdown URL: https://schematex.js.org/docs/contributing.md > A step-by-step guide for adding a new diagram plugin to Schematex — from standard spec to published website example. Read [`00-OVERVIEW.md`](/docs) first for the overall architecture. *** ## 1. The Pipeline Every diagram type follows the same pipeline: ``` Text (DSL) ──► Parser ──► AST ──► Layout ──► LayoutResult ──► Renderer ──► SVG ``` * **Parser** — hand-written recursive descent; no parser generators, no dependencies. * **Layout** — pure functions over the AST that produce absolute geometry. Deterministic, no randomness. * **Renderer** — string-building SVG via [`src/core/svg.ts`](https://github.com/SchemaTex/SchemaTex/blob/main/src/core/svg.ts); no DOM, SSR-safe. Small diagrams (e.g. timing) may fuse layout into the renderer. Complex diagrams (genogram, SLD) must keep them separate and tested independently. *** ## 2. Hard Constraints (non-negotiable) 1. **Zero runtime dependencies.** No D3, no dagre, no parser generators. Hand-write everything. 2. **Strict TypeScript.** No `any`, no un-commented `as`. Types in [`src/core/types.ts`](https://github.com/SchemaTex/SchemaTex/blob/main/src/core/types.ts) are the spec. 3. **Semantic SVG.** Every rendered diagram must include ``, `<desc>`, CSS classes for theming, and `data-*` attributes for interactivity. No inline styles. 4. **Use the SVG builder.** Never concatenate raw SVG strings — use [`src/core/svg.ts`](https://github.com/SchemaTex/SchemaTex/blob/main/src/core/svg.ts). 5. **Test-first layout.** Write failing layout tests before writing the layout code. 6. **Standards-compliant.** Each diagram implements a published domain standard — not our invention. Cite the reference in the standard doc (IEEE, IEC, ISO, McGoldrick, etc.). *** ## 3. Step-by-Step Checklist ### Step 1 — Write the standard doc Create `docs/reference/NN-{TYPE}-STANDARD.md` (next free number). It must contain: * Scope & references (IEEE / IEC / published paper). * Symbol table with ASCII/Unicode references. * DSL grammar (EBNF or equivalent). * Layout rules (axes, alignment, spacing). * 3–5 canonical test cases with expected rendering notes. Look at [`06-TIMING-STANDARD.md`](/docs/timing) or [`11-SINGLE-LINE-STANDARD.md`](/docs/sld) as templates. ### Step 2 — Add AST types to `src/core/types.ts` Types are the spec. Before writing any code, commit to: * The `DiagramType` literal — extend the union in `types.ts`. * The AST shape: nodes, edges, metadata, any diagram-specific fields. * The LayoutResult shape (positions, sizes, computed routing). Ship this as its own commit so reviewers can critique the contract separately. ### Step 3 — Scaffold the plugin directory ``` src/diagrams/{type}/ index.ts # DiagramPlugin export parser.ts # text → AST layout.ts # AST → LayoutResult (optional; skip for simple diagrams) renderer.ts # LayoutResult → SVG string ``` `index.ts` is always shaped like this: ```ts import type { DiagramPlugin } from "../../core/types"; import { parseMyType } from "./parser"; import { renderMyType } from "./renderer"; export const myType: DiagramPlugin = { type: "mytype", detect(text) { const first = text.trim().split("\n")[0]?.trim().toLowerCase() ?? ""; return first.startsWith("mytype"); }, render(text) { const ast = parseMyType(text); return renderMyType(ast); }, }; ``` ### Step 4 — Write tests first ``` tests/{type}/ parser.test.ts layout.test.ts renderer.test.ts e2e.test.ts # full text → SVG, snapshot string for stability ``` Cover every test case you committed to in the standard doc. Layout tests should assert absolute coordinates — that's what catches regressions. ### Step 5 — Implement parser → layout → renderer Follow the tests. Keep each module pure — parser takes a string and returns an AST, layout takes an AST and returns geometry, renderer takes geometry and returns a string. Use the SVG builder: ```ts import { svg, g, rect, text } from "../../core/svg"; ``` Never `'<svg>' + ... + '</svg>'`. ### Step 6 — Register the plugin Edit [`src/core/api.ts`](https://github.com/SchemaTex/SchemaTex/blob/main/src/core/api.ts): 1. Import `{ myType }` from `../diagrams/mytype`. 2. Add it to the `plugins[]` array. 3. Extend the `SchematexConfig.type` literal union. 4. Update the `detectPlugin` error message with the new keyword. ### Step 7 — Quality gate ```bash npm run typecheck npm run test npm run lint npm run build ``` All four must pass. If `dts` fails on unused locals, fix them — don't suppress. ### Step 8 — Wire into the website 1. **Gallery tile** — add an entry to [`website/lib/gallery-data.ts`](https://github.com/SchemaTex/SchemaTex/blob/main/website/lib/gallery-data.ts). The `dsl` field must parse — validate with `node scripts/validate-gallery.mjs`. 2. **Static SVG** — add an entry to [`scripts/generate-gallery-svgs.mjs`](https://github.com/SchemaTex/SchemaTex/blob/main/scripts/generate-gallery-svgs.mjs) and run it. The generated SVG ships with the repo and is referenced from the README. 3. **Docs page** — create `website/content/docs/{type}.mdx` with a `<Playground initial={…}>` and the body of the standard doc inlined. 4. **Example page (optional)** — for a real-world case study, add `website/content/examples/{slug}.mdx`. 5. **README** — add a row to the gallery table with the generated SVG. ### Step 9 — Update the top-level docs * [`README.md`](https://github.com/SchemaTex/SchemaTex/blob/main/README.md) — gallery row. * [`CLAUDE.md`](https://github.com/SchemaTex/SchemaTex/blob/main/CLAUDE.md) — "Completed" list at the bottom. * [`docs/reference/00-OVERVIEW.md`](/docs) — status table. *** ## 4. Common Pitfalls * **Forgetting `detect()`** — if two plugins both return `true`, the first one wins. Make your header keyword unique. * **Coordinate drift** — layout tests that use relative numbers (`width / 2 + padding`) mask bugs. Assert on concrete expected values. * **Inline `style=` attributes** — blocked by the semantic-SVG rule. Use CSS classes and expose them as theme tokens in [`src/core/theme.ts`](https://github.com/SchemaTex/SchemaTex/blob/main/src/core/theme.ts). * **Runtime deps creeping in** — if you think you need one, open an issue first. The answer is almost always "hand-write a 30-line version." * **Gallery DSL stubs that don't parse** — run `node scripts/validate-gallery.mjs` before committing. This script exists precisely because it kept happening. *** ## 5. Reference Plugins Good examples to study, by complexity: | Complexity | Plugin | Study for | | ---------- | ------------------------------------------------------------------------------------ | --------------------------------------------------------- | | Minimal | [`timing`](https://github.com/SchemaTex/SchemaTex/tree/main/src/diagrams/timing) | Parser + renderer only, no separate layout. | | Medium | [`ecomap`](https://github.com/SchemaTex/SchemaTex/tree/main/src/diagrams/ecomap) | Clean AST → layout → renderer split. | | Advanced | [`genogram`](https://github.com/SchemaTex/SchemaTex/tree/main/src/diagrams/genogram) | Generational layout, multi-pass routing, rich symbol set. | | Advanced | [`sld`](https://github.com/SchemaTex/SchemaTex/tree/main/src/diagrams/sld) | Voltage-level banding, bus routing, device clustering. | *** ## 6. Candidate Diagrams on the Roadmap Not yet implemented — PRs welcome. Start with the standard doc (Step 1) and open a draft PR before coding. * **Fishbone / Ishikawa** — cause-and-effect analysis. AST and standard doc are the main unknowns. * **Sequence diagram** — UML sequence without depending on PlantUML/Mermaid conventions. * **State machine** — UML state chart with hierarchical states. * **Gantt** — project scheduling with dependencies and critical path. * **Network topology** — L2/L3 network diagrams with device icons. If you're adding one of these, the standard doc is doing most of the work — don't skimp on it. --- # Decision tree diagram Canonical URL: https://schematex.js.org/docs/decisiontree Markdown URL: https://schematex.js.org/docs/decisiontree.md ## About decision trees A **decision tree** is a branching diagram that represents a sequence of choices and their consequences as a rooted tree: each internal node is a question or decision, each edge is an answer or action, and each leaf is an outcome. The format appears across three quite different practices — troubleshooting flowcharts and clinical decision rules (taxonomy trees), risk and investment analysis using expected value (decision analysis), and machine learning model inspection (classifier trees). Despite surface differences all three share the same tree structure, which is why Schematex encodes them under one keyword with a mode selector. Schematex decision trees cover: (1) **taxonomy mode** — the yes/no question flows used in medical triage ([Turing 1937](https://en.wikipedia.org/wiki/Decision_tree) lineage; now standard in clinical decision support), (2) **decision analysis mode** — the expected-value rollback method developed in management science ([Raiffa & Schlaifer, 1961](https://en.wikipedia.org/wiki/Decision_tree#Decision_analysis)), and (3) **ML mode** — the CART split/leaf format used to visualize scikit-learn and similar trained classifiers ([Breiman et al., 1984](https://en.wikipedia.org/wiki/Decision_tree_learning)). ```schematex decisiontree "Customer Support Triage" direction: top-down question "Is the service completely down?" yes: question "Outage confirmed on status page?" yes: answer "Follow incident protocol — page on-call" no: answer "Check monitoring — open severity-1 ticket" no: question "Is the issue affecting billing?" yes: answer "Escalate to billing team — SLA breach risk" no: question "Can user reproduce consistently?" yes: answer "Collect HAR trace — file bug report" no: answer "Ask for screenshot — watch for recurrence" ``` *** ## 1. Your first decision tree The smallest useful decision tree: a root question with two branches. ```schematex decisiontree "Laptop troubleshoot" question "Does it power on?" yes: answer "Check display — connect external monitor" no: question "Is the charger light on?" yes: answer "Hold power button 10 s — try again" no: answer "Check outlet and charging cable" ``` Four rules cover 80% of usage: 1. Start with `decisiontree`, optionally with `:mode` and a quoted title. 2. Each question node uses `question "text"` (or shorthand `q "text"`). 3. Each answer/leaf uses `answer "text"` (or `a "text"` or `leaf "text"`). 4. Branch labels — `yes:`, `no:`, or a custom `label "X":` — prefix the child node on the same line. Indentation controls nesting: each level adds 2 spaces. The parser computes parent-child relationships from indent depth. > Comments must start with `#` or `//` on their own line. *** ## 2. Modes The mode is set in the header line: | Header | Mode | Used for | | ----------------------------------------------- | ----------------- | ----------------------------------------------------------------------------- | | `decisiontree` | taxonomy | Yes/no question flows, troubleshooting guides, clinical decision support | | `decisiontree:decision` (or `decisiontree:da`) | decision analysis | Investment decisions, risk analysis, expected value calculation | | `decisiontree:influence` (or `mode: influence`) | influence diagram | Compact DAG view of a decision problem — structure before unrolling to a tree | | `decisiontree:ml` | machine learning | Visualizing trained CART classifiers (scikit-learn, XGBoost, etc.) | Default direction is `top-down` for taxonomy and ML, `left-right` for decision analysis. *** ## 3. Taxonomy mode Best for: troubleshooting guides, FAQs, clinical protocols, product recommendation flows. ### Node keywords | Keyword | Aliases | Meaning | | -------------- | ------------------- | ---------------------------------------- | | `question "…"` | `q "…"` | Internal node — a question with children | | `answer "…"` | `a "…"`, `leaf "…"` | Leaf node — a terminal outcome | ### Branch labels | Syntax | Meaning | | --------------------------------- | ---------------------------- | | `yes: question "…"` | Branch labeled "yes" | | `no: answer "…"` | Branch labeled "no" | | `label "Custom text": answer "…"` | Branch with any custom label | Custom labels let you go beyond yes/no for multi-way decisions from one question. ```schematex decisiontree "Triage — chest pain onset" q "Onset sudden?" yes: q "ECG changes present?" yes: a "ACS protocol — cardiology consult" no: q "D-dimer elevated?" yes: a "PE workup — CT pulmonary angiography" no: a "Aortic dissection — CT angiography" no: q "Pain reproducible on palpation?" yes: a "Musculoskeletal — NSAIDs, follow-up PCP" no: a "GI / anxiety — further history" ``` ```schematex decisiontree "Pain level triage" question "Reported pain level?" label "Severe (8-10)": answer "Emergency — send to ER immediately" label "Moderate (4-7)": answer "Urgent care — within 2 hours" label "Mild (1-3)": answer "Schedule next available — OTC care" label "None": answer "Monitor — patient may be post-medication" ``` *** ## 4. Decision analysis mode Best for: investment decisions, build-vs-buy analysis, risk-weighted strategy evaluation. ### Node keywords | Keyword | Aliases | Meaning | | -------------- | ------------- | ------------------------------------------ | | `decision "…"` | — | Decision node — the actor chooses a branch | | `chance "…"` | — | Chance node — an uncertain outcome | | `end "…"` | `outcome "…"` | Terminal node — final payoff | ### Branch keywords | Keyword | Meaning | | ---------------- | -------------------------------------------------------------------- | | `choice "label"` | Names the incoming branch from a decision node | | `prob N` | Sets the probability (0–1) on the incoming branch from a chance node | ### Payoff attribute `payoff=N` on any node sets the payoff value. On `end` / `outcome` nodes it defines the terminal value. The parser runs expected-value rollback automatically: each `chance` node's EV is the probability-weighted sum of its children's EVs; each `decision` node's EV is the maximum child EV, and the optimal branch is flagged. **Constraint:** probabilities on all direct children of a `chance` node must sum to 1.0 (±0.01). The parser throws a `DTreeParseError` if they do not. ```schematex decisiontree:decision "Cloud vendor selection" decision "Which vendor?" choice "Build in-house" chance "Project outcome" prob 0.6 end "On-time delivery" payoff=900000 prob 0.4 end "Over budget / delayed" payoff=150000 choice "Managed SaaS vendor" end "Predictable cost" payoff=500000 choice "Hybrid approach" chance "Integration complexity" prob 0.5 end "Smooth integration" payoff=700000 prob 0.5 end "Integration rework" payoff=300000 ``` *** ## 5. Influence diagram mode Best for: framing a decision problem **compactly** before you unroll it. Where decision-analysis mode draws every branch of every outcome as an explicit tree, an **influence diagram** ([Howard & Matheson, 1981](https://en.wikipedia.org/wiki/Influence_diagram)) draws the *same* problem as a directed acyclic graph (DAG) of variables and the dependencies between them — one node per decision, uncertainty, and objective, no matter how many states each can take. It is the diagram decision analysts reach for first, because it shows the structure (what informs what, what affects the payoff) without the combinatorial blow-up of a tree. This mode is **structural, not computational.** Unlike decision-analysis mode, it does not solve for expected value — the compact graph deliberately omits the probability and payoff tables that an EV rollback would need. Use it to communicate and validate the shape of the problem; use decision-analysis mode (section 4) when you want the numbers folded back. ### Header forms Two equivalent ways to select the mode: ``` decisiontree:influence "Oil Wildcatter" ``` or, as a directive on its own line after the header: ``` decisiontree "Market Entry" mode: influence ``` ### Node keywords Each node is declared as `kind Id "label"` — an id (used to wire arcs) followed by a quoted display label. | Keyword | Shape | Meaning | | ----------------- | ------------- | -------------------------------------------- | | `decision Id "…"` | **rectangle** | A choice the decision-maker controls | | `chance Id "…"` | **oval** | An uncertain variable (a state of the world) | | `value Id "…"` | **octagon** | The objective / payoff being optimized | The shapes follow the standard influence-diagram convention: decisions are rectangles, uncertainties are ovals, and the value node is an octagon. Add `utility=N` to a value node to annotate the payoff it represents (`value Profit "Net profit" utility=42`). ### Arcs and their semantics Arcs are written `Source -> Target` on their own lines, by node id. **An arc's meaning is read from its destination**, exactly as in the published standard: | Arc into a… | Meaning | Drawn as | | ----------- | --------------------------------------------------------------------- | ----------- | | `decision` | **Informational** — this is known *before* the decision is made | dashed line | | `chance` | **Relevance / conditioning** — the source conditions this uncertainty | solid line | | `value` | **Functional** — the source is an argument of the payoff function | solid line | The dashed informational arc is the one to watch: `Seismic -> Drill` means "the seismic test result is observed before choosing whether to drill," which is precisely what makes the decision worth modelling. ### Validation rules * The graph must be **acyclic** — a cycle (e.g. `A -> B` and `B -> A`) is rejected. * At least **one `value` node** is required; an influence diagram with no objective is not a decision problem. * Arcs reference node ids that must be declared. ### Examples The Oil Wildcatter — the canonical teaching problem. The seismic test result is observed before the drill decision (dashed informational arc `Seismic -> Drill`), the test is relevant to whether oil is actually present (`Seismic -> Oil`), and both the oil state and the drill choice feed the profit (`Oil -> Profit`, `Drill -> Profit`). ```schematex decisiontree:influence "Oil Wildcatter" decision Drill "Drill?" chance Oil "Oil present" chance Seismic "Seismic test" value Profit "Net profit" utility=42 Seismic -> Oil Seismic -> Drill Oil -> Profit Drill -> Profit ``` A market-entry decision using the `mode: influence` directive form. Demand is observed before entering (`Demand -> Enter`, informational/dashed) and also drives profit directly, while the competitor's response feeds only the payoff. ```schematex decisiontree "Market Entry" mode: influence decision Enter "Enter market?" chance Demand "Market demand" chance Competition "Competitor response" value V "Profit" utility=120 Demand -> Enter Demand -> V Competition -> V Enter -> V ``` *** ## 6. Machine learning mode Best for: explaining trained CART classifiers, model transparency reports, feature importance analysis. ### Node keywords | Keyword | Meaning | | ----------- | --------------------------------------------- | | `split "…"` | Internal split node — contains a feature test | | `leaf "…"` | Leaf node — class or regression value | ### Branch prefixes `true` and `false` prefix child nodes to mark which branch each child represents. ### Properties (key=value, no colon, no quotes around values) | Property | Applies to | Meaning | | --------------- | ----------- | ----------------------------------------------------------- | | `feature=name` | split | Feature name used at the split | | `op="<="` | split | Comparison operator (quote if contains special chars) | | `threshold=5.9` | split | Split threshold value | | `samples=150` | split, leaf | Sample count at this node | | `gini=0.5` | split, leaf | Gini impurity | | `entropy=0.5` | split, leaf | Entropy impurity | | `mse=0.3` | split, leaf | Mean squared error (regression) | | `gain=0.2` | split, leaf | Information gain | | `class=name` | leaf | Predicted class name | | `value=50` | leaf | Sample count; use `value=[50,30,20]` for class distribution | ```schematex decisiontree:ml "Iris classification (CART)" direction: top-down impurity: gini split "Petal length ≤ 2.45" feature=petal_length op="<=" threshold=2.45 samples=150 gini=0.667 true leaf "Setosa" class=Iris-setosa value=50 gini=0.0 false split "Petal width ≤ 1.75" feature=petal_width op="<=" threshold=1.75 samples=100 gini=0.5 true leaf "Versicolor" class=Iris-versicolor value=50 gini=0.0 false leaf "Virginica" class=Iris-virginica value=50 gini=0.0 ``` *** ## 7. Config options Config lines appear between the header and the first node. Each is `key: value` (colon, no `config` keyword). ### Shared config (all modes) | Key | Values | Default | Effect | | ------------------------------- | ----------------------------------- | ------------------------------------------------- | ------------------ | | `direction:` | `top-down`, `left-right` | `top-down` (taxonomy/ML), `left-right` (decision) | Layout direction | | `edgeStyle:` (or `edge-style:`) | `diagonal`, `orthogonal`, `bracket` | mode-dependent | Edge drawing style | ### Taxonomy config | Key | Values | Default | Effect | | ------------------------------------- | --------------------- | --------- | ------------------ | | `branchLabels:` (or `branch-labels:`) | `boolean`, `relation` | `boolean` | Branch label style | ### Decision analysis config | Key | Values | Default | Effect | | ------------------------------------- | ------------- | ------- | ----------------------------------------------- | | `branchLength:` (or `branch-length:`) | `probability` | off | Scale branch length proportional to probability | ### ML config | Key | Values | Default | Effect | | ----------- | -------------------------------- | ------- | ------------------------------ | | `impurity:` | `gini`, `entropy`, `mse`, `gain` | `gini` | Impurity metric shown on nodes | | `classes:` | comma-separated list | — | Class label names for display | ``` decisiontree:ml "Loan classifier" direction: top-down impurity: gini classes: Approved, Denied, Review ``` *** ## 8. Labels & comments * **Diagram title:** `decisiontree "Title"` — the quoted string after the header keyword. * **Node label:** the quoted string immediately after the node keyword — `question "Is the fee waived?"`. * **Branch label:** `yes:`, `no:`, or `label "Custom":` before the child node — on the same line as the child. * **Payoff:** `payoff=250000` at the end of a decision/end node line. * **ML properties:** `key=value` tokens after the node's label string (no `[…]` brackets, no colons). * **Comments:** `#` or `//` at the start of a line (after optional leading whitespace). Only full-line comments are supported — inline trailing comments are not. *** ## 9. Reserved words & escaping **Reserved node keywords:** `decision`, `chance`, `end`, `outcome`, `choice`, `prob`, `split`, `leaf`, `question`, `q`, `answer`, `a`. **Reserved branch prefixes:** `yes:`, `no:`, `true`, `false`, `label`. **Reserved header forms:** `decisiontree`, `decisiontree:decision`, `decisiontree:da`, `decisiontree:ml`. **Strings with spaces** must be double-quoted: `question "Annual revenue > $1M?"`. Node labels, branch labels from `label "…":` syntax, and the diagram title all require double quotes. | Reserved token | Context | Notes | | ---------------- | --------------------------- | ----------------------------------------------------------------------------- | | `yes:` / `no:` | Line start in taxonomy | Cannot be used as a label — use `label "yes":` if you need literal text "yes" | | `true` / `false` | Line start in ML mode | Cannot be a node label | | `choice` | Line start in decision mode | Acts as branch wrapper, not a node | | `prob` | Line start in decision mode | Must be followed by a number | *** ## 10. Common mistakes | You wrote | Parser says | Fix | | ------------------------------------------------------- | ---------------------------------------------------- | ------------------------------------------------------- | | `yes: "Approve"` (no node keyword) | `DTreeParseError: Missing taxonomy node kind` | `yes: answer "Approve"` | | Probabilities on `chance` children summing to 0.8 | `DTreeParseError: probabilities do not sum to 1.0` | Adjust so all `prob` values sum to exactly 1.0 (±0.01) | | `question "text"` with a child at the same indent level | Child not parsed as a child — becomes a sibling | Indent children by 2 more spaces than the parent | | `config direction = top-down` (using `config` keyword) | `config` is a fishbone keyword — not recognized here | Use `direction: top-down` (no `config` prefix) | | `feature = petal_length` (spaces around `=`) | Parsed as separate tokens; property not recognized | No spaces: `feature=petal_length` | | `[payoff: 500000]` bracket syntax | Not recognized — parser ignores brackets for payoff | Use `payoff=500000` (no brackets, no spaces around `=`) | | `decisiontree:taxonomy` | `DTreeParseError: Invalid header` | Use `decisiontree` (no mode suffix for taxonomy) | *** ## 11. Grammar (EBNF) ```text document = header ( config-line )* node header = "decisiontree" ( ":" mode )? ( WS quoted-string )? NEWLINE mode = "decision" | "da" | "ml" // omitted → taxonomy config-line = config-key ":" WS config-value NEWLINE config-key = "direction" | "edgeStyle" | "edge-style" | "branchLabels" | "branch-labels" | "branchLength" | "branch-length" | "impurity" | "classes" // ── Taxonomy mode ────────────────────────────── node = ( branch-prefix WS )? tax-node ( WS "[" tax-attrs "]" )? NEWLINE INDENT child-node* tax-node = ( "question" | "q" ) WS quoted-string | ( "answer" | "a" | "leaf" ) WS quoted-string branch-prefix = "yes:" | "no:" | "label" WS quoted-string ":" // ── Decision-analysis mode ───────────────────── da-node = "decision" WS quoted-string NEWLINE INDENT da-child+ | "chance" WS quoted-string NEWLINE INDENT da-prob-child+ | ( "end" | "outcome" ) WS quoted-string ( WS "payoff=" number )? NEWLINE da-child = "choice" WS quoted-string NEWLINE INDENT da-node da-prob-child = "prob" WS number WS da-node // prob, value, and child all on one line // ── ML mode ─────────────────────────────────── ml-node = ( "true" | "false" )? ml-kind WS quoted-string ml-prop* NEWLINE INDENT ml-child* // "true"/"false" and ml-kind must be on the same line ml-kind = "split" | "leaf" ml-prop = WS key "=" value // no spaces around "=" comment = ( "#" | "//" ) any NEWLINE quoted-string = '"' any-char-but-quote* '"' ``` Authoritative source: `src/diagrams/decisiontree/parser.ts`. If this diverges from the parser, the parser wins — please open an issue. *** ## 12. Standard compliance Schematex decision trees cover four established conventions: **Taxonomy mode** follows the question/answer flowchart conventions common in clinical decision support systems (Arden Syntax lineage) and ISO 9001 troubleshooting procedures. **Decision analysis mode** follows the expected-value rollback method from management science: decision nodes (square, actor chooses), chance nodes (circle, probabilistic), terminal nodes with payoffs. EV is computed automatically via backward induction (Raiffa & Schlaifer, 1961). **Influence diagram mode** follows the Howard & Matheson (1981) convention: decision (rectangle), chance (oval), and value (octagon) nodes wired as an acyclic graph, with arc meaning read from the destination (informational into decisions, drawn dashed; relevance into chance; functional into value). It is a structural diagram — it does not solve for EV. **ML mode** follows the CART (Classification and Regression Trees) split/leaf notation (Breiman et al., 1984) compatible with scikit-learn's `export_text` and `plot_tree` output. What is implemented today: * ✅ All four modes: taxonomy, decision analysis, influence diagram, ML * ✅ Taxonomy: `question`/`q`, `answer`/`a`/`leaf`, `yes:`, `no:`, `label "X":` branch labels * ✅ Decision: `decision`, `chance`, `end`/`outcome`, `choice`, `prob`, `payoff=N`, automatic EV rollback * ✅ Influence: `decision`/`chance`/`value` nodes, `utility=N`, `Source -> Target` arcs, destination-derived arc semantics, dashed informational arcs, acyclicity + value-node validation * ✅ ML: `split`, `leaf`, `true`/`false` branch prefixes, `feature=`, `threshold=`, `gini=`, `entropy=`, `mse=`, `class=`, `value=` * ✅ Config: `direction:`, `edgeStyle:`, `impurity:`, `classes:`, `branchLabels:`, `branchLength:`, `mode:` * ⏳ Probability-weighted branch length rendering (`branchLength: probability` parsed but not yet applied visually) * ⏳ Sensitivity analysis overlays (tornado chart-style annotations on decision trees) * ⏳ Forest / ensemble view — multiple trees side-by-side References: * Raiffa, H. & Schlaifer, R. (1961). *Applied Statistical Decision Theory.* Harvard Business School. * Howard, R. A. & Matheson, J. E. (1981/2005). *Influence Diagrams.* Decision Analysis 2(3). * Breiman, L. et al. (1984). *Classification and Regression Trees.* Wadsworth. * Wikipedia — [Decision tree](https://en.wikipedia.org/wiki/Decision_tree) · [Influence diagram](https://en.wikipedia.org/wiki/Influence_diagram) · [Decision tree learning](https://en.wikipedia.org/wiki/Decision_tree_learning) *** ## 13. Roadmap **Planned — not yet parseable.** Do not use these in generated DSL today; the parser will reject or ignore them. * **`branchLength: probability` visual rendering** — the config key parses but the layout engine does not yet scale branch lengths by probability. * **Sensitivity / tornado analysis** — `sensitivity:` block annotating which `prob` values most affect the final EV. * **Export annotations** — structured table output (decision matrix) alongside the diagram SVG. * **Forest view** — `forest` keyword wrapping multiple `decisiontree` blocks in a side-by-side comparison layout. * **Collapse/expand interactive markers** — `collapsed: true` on a node to render a triangle placeholder for deep subtrees. Track in the GitHub issues if you need any of these sooner. *** ## Related examples Ready-to-use scenarios from the examples gallery: [Browse related examples](https://schematex.js.org/examples) ## Interactive editing Questions, answers, and the title edit their exact source ranges. Branch topology and rollback order own the automatic tree layout, so cards intentionally expose no persistent position drag. --- # Ecomap Canonical URL: https://schematex.js.org/docs/ecomap Markdown URL: https://schematex.js.org/docs/ecomap.md ## About ecomaps An **ecomap** is a single page that shows a person or family at the center and the web of outside systems — extended family, school, church, clinic, employer, support groups — arrayed around them. Each connecting line records the *quality* (strong, tenuous, stressful) and *direction* (who gives, who takes) of the relationship. Social workers, school counselors, case managers, and community-health nurses use ecomaps to see at a glance where a client has anchors and where the safety net is thin. Schematex follows the **[Hartman (1978) ecomap model](https://journals.sagepub.com/doi/10.1177/104438947805900803)** — the original *Social Casework* paper that introduced the notation — extended with conventions from contemporary clinical social work practice. For background see [Wikipedia: Ecomap](https://en.wikipedia.org/wiki/Ecomap). This page documents what the parser accepts today. ```schematex ecomap "Nguyen Family Resettlement" center: family [label: "Nguyen Family"] resettlement [label: "IRC Office", category: government] school [label: "Lincoln Elementary", category: education] clinic [label: "Community Clinic", category: health] caseworker [label: "Ms. Patel", category: mental-health] temple [label: "Vietnamese Temple", category: cultural] neighbors [label: "Sponsor Family", category: community] employer [label: "Warehouse Job", category: work] family === resettlement [label: "active case"] family === school clinic --> family [label: "vaccinations"] caseworker <-> family [label: "weekly"] family === temple [label: "anchor"] neighbors === family [label: "housing host"] family --- employer [label: "new, part-time"] ``` *** ## 1. Your first ecomap The smallest useful ecomap: one center and three outside systems. ```schematex ecomap center: client [label: "Maria"] mom [label: "Mother", category: family] work [label: "Tech Corp", category: work] therapist [label: "Dr. Patel", category: mental-health] mom === client work --- client therapist <-> client [label: "weekly"] ``` Four rules cover 80% of usage: 1. Start with the keyword `ecomap`, optionally followed by a quoted title. 2. Declare the center on its own line: `center: id [label: "…"]`. Exactly one center per diagram. 3. Declare each outside system on its own line: `id [label: "…", category: …]`. 4. Connect any two declared IDs with a **connection operator** — `===` (strong), `---` (normal), `<->` (reciprocal), etc. See §3 for the full table. A trailing `[label: "…"]` adds an edge label. > Comments must be on their own line, starting with `#`, `//`, or Mermaid-style `%%`. Inline trailing comments will break the parser. *** ## 2. Center and outside systems Every ecomap has one **center** and any number of **outside systems**. Both use the same `id [attrs]` syntax; the only difference is the `center:` prefix. **ID rules.** Must match `[a-zA-Z][a-zA-Z0-9_-]*`. IDs are case-insensitive; the original token is kept as the default label. **Attributes accepted by the parser today:** | Attribute | Values | Effect | | ----------------------------- | -------------------------------- | ---------------------------------------------------- | | `label:"…"` | any quoted string | Display label override | | `category:…` | see §2.1 | Color / grouping of a system node | | `size:…` | `small`, `medium`, `large` | Node size | | `importance:…` | `major`, `moderate`, `minor` | Visual weight | | `sector:…` | `top`, `right`, `bottom`, `left` | Hint for which side of the center the system sits on | | `age:N` | e.g. `age:34` | Age shown inside a person-typed center | | `male` / `female` / `unknown` | flag | Sex for a person-typed center | ### 2.1 System categories Categories color-code outside systems by the life domain they belong to. The parser accepts any string — these are the values the renderer has themed palettes for: | Category | Typical examples | | --------------- | ----------------------------------------------------- | | `family` | Extended family, in-laws, cousins | | `friends` | Friends, neighbors | | `work` | Employer, coworkers | | `education` | School, college, training program | | `health` | Primary care, specialist, hospital | | `mental-health` | Therapist, psychiatrist, support group | | `religion` | Church, temple, spiritual community | | `recreation` | Sports, hobbies, clubs | | `legal` | Lawyer, probation, court | | `government` | Social services, housing, immigration | | `financial` | Bank, benefits, financial aid | | `community` | Neighborhood groups, sponsors | | `cultural` | Cultural/ethnic organizations | | `substance` | Recovery programs or, if negative, active-use sources | | `technology` | Online community, support forum | | `pet` | Pets, service animals | ```schematex ecomap center: client [label: "Marcus, age 15"] mom [label: "Mother", category: family] dad [label: "Father (divorced)", category: family] school [label: "East High School", category: education] coach [label: "Soccer Coach", category: community] therapist [label: "Ms. Chen", category: mental-health] mom === client dad --- client [label: "EOW weekends"] school === client coach --> client [label: "mentor"] therapist <-> client [label: "weekly"] ``` *** ## 3. Connections A connection is one line: `fromId OP toId` optionally followed by `[label: "…"]`. Both IDs must already be declared (center counts). ### 3.1 Relationship-quality operators | Operator | Type | Meaning | | -------- | ---------------- | --------------------------------- | | `===` | strong | Close, supportive, high-frequency | | `==` | moderate | Positive, moderate involvement | | `---` | normal | Neutral / average | | `- -` | weak | Tenuous, fragile, early-stage | | `~~~` | stressful | Stressful relationship | | `~=~` | stressful-strong | Close *and* stressful | | `~x~` | conflictual | Active conflict | | `-/-` | broken | Severed, estranged, cutoff | ### 3.2 Energy-flow operators Layer arrow direction onto strong or normal lines: | Operator | Meaning | | -------- | ------------------------------------------- | | `-->` | One-way: energy flows from center to system | | `<--` | One-way: energy flows from system to center | | `<->` | Reciprocal / bidirectional | | `===>` | Strong one-way outflow (draining) | | `<===` | Strong one-way inflow (nourishing) | | `<=>` | Strong reciprocal | | `==>` | Moderate one-way outflow | | `<==` | Moderate one-way inflow | The parser normalizes direction so arrows read relative to the center. Writing `family === resettlement` and `resettlement === family` produces the same diagram; writing `clinic --> family` vs `family <-- clinic` likewise produces the same arrow pointing *from* the clinic *to* the family. ```schematex ecomap center: client [label: "Rosa"] mom [category: family] ex [category: family] aa [label: "AA Group", category: substance] job [label: "Warehouse", category: work] mom === client [label: "daily calls"] ex ~x~ client [label: "custody disputes"] aa <== client [label: "sponsor support"] job - - client [label: "unstable hours"] ``` ### 3.3 Edge labels A trailing `[label: "…"]` is the only attribute a connection line accepts. Put schedule, nature, or a short note here: ``` family === temple [label: "weekly service"] clinic --> family [label: "vaccinations"] caseworker <-> family [label: "every Tuesday"] ``` *** ## 4. Labels & comments * **Title:** `ecomap "Nguyen Family"` — first line only. * **Node label override:** `family [label: "The Nguyens"]`. * **Edge label:** trailing `[label: "…"]` on a connection line. * **Mode suffix:** `ecomap:strengths "Smith family"` is accepted. The suffix is stored as `metadata.mode`; current renderers ignore it. * **Comments:** `#`, `//`, or `%%` at the start of a line (after leading whitespace). Inline trailing comments are **not** supported. ``` ecomap "Marcus Intake" # caseworker's notes — fine // also fine %% Mermaid-style comments are fine mom [category: family] # ← THIS trailing comment breaks the parser ``` *** ## 5. Reserved words & escaping **Reserved at line start:** `ecomap`, `center:`. **Reserved operator tokens** inside a line — avoid using these sequences in IDs: `===`, `==`, `---`, `- -`, `~~~`, `~=~`, `~x~`, `-/-`, and the directional variants listed in §3.2. **Strings with spaces** must be double-quoted: titles and any label. Single quotes and backticks are not recognized. *** ## 6. Common mistakes Real parser errors, what triggers them, and how to fix. | You wrote | Parser says | Fix | | ----------------------------------------------------- | ------------------------------------------------------------------------ | ----------------------------------------------------------------------- | | `family -- school` | `Unexpected: family -- school` | Ecomap uses `===`/`---`/`<->` etc. `--` is a genogram/pedigree operator | | `family === school` where `school` was never declared | Silently creates an empty `school` node with no label/category | Declare systems above their connection lines | | No `center:` anywhere | Renders but with no visual center anchor | Every ecomap needs exactly one `center:` line | | Two `center:` lines | Only the first is treated as center; the second becomes a regular system | Pick one | | `family==school` (no spaces) | `Unexpected: family==school` | Operators require a space on each side | | `family === school [weekly]` | Bare token `weekly` is parsed as a property flag, no label shown | Use `[label: "weekly"]` | | `family === school # daily` | Trailing `#` is consumed as part of the line | Move the comment above | *** ## 7. Grammar (EBNF) ```text document = header (blank | comment | center | system | connection)* header = "ecomap" ( ":" mode )? ( WS quoted-string )? NEWLINE mode = [A-Za-z] [A-Za-z0-9_-]* quoted-string = '"' any-char-but-quote* '"' center = "center:" WS id ( "[" attrs "]" )? NEWLINE system = id ( "[" attrs "]" )? NEWLINE connection = id WS op WS id ( WS "[" "label:" quoted-string "]" )? NEWLINE op = "===" | "==" | "---" | "- -" | "~~~" | "~=~" | "~x~" | "-/-" | "===>" | "<===" | "<=>" | "==>" | "<==" | "-->" | "<--" | "<->" id = [a-zA-Z] [a-zA-Z0-9_-]* attrs = attr ("," attr)* attr = "label" ":" quoted-string | "category" ":" category | "size" ":" ("small" | "medium" | "large") | "importance" ":" ("major" | "moderate" | "minor") | "sector" ":" ("top" | "right" | "bottom" | "left") | "age" ":" digits | "male" | "female" | "unknown" | key ":" value // custom, stored as metadata category = "family" | "friends" | "work" | "education" | "health" | "mental-health" | "religion" | "recreation" | "legal" | "government" | "financial" | "community" | "cultural" | "substance" | "technology" | "pet" | "other" comment = ( "#" | "//" | "%%" ) any NEWLINE ``` Authoritative source: `src/diagrams/ecomap/parser.ts`. If this diverges from the parser, the parser wins — please open an issue. *** ## 8. Standard compliance Schematex ecomaps follow **Hartman (1978), *Diagrammatic Assessment of Family Relationships*** for the core notation: a bounded center surrounded by outside systems, connecting lines that encode both quality and directionality. The category palette and the expanded operator set (stressful-strong, reciprocal arrows) follow conventions from contemporary clinical social work and nursing research. What is implemented today: * ✅ Center + outside-system radial structure * ✅ 8 quality operators (strong / moderate / normal / weak / stressful / stressful-strong / conflictual / broken) * ✅ 8 directional operators (one-way and reciprocal, at three intensities) * ✅ Category color-coding (17 categories) * ✅ Edge labels * ⏳ Embedded mini-genogram as the center (see §10) * ⏳ Sector-based layout hints beyond the current four cardinals References: * Hartman, A. (1978). Diagrammatic assessment of family relationships. *Social Casework*, 59(8), 465–476. * Ray, R.A. & Street, A.F. (2005). Ecomapping: An innovative research tool for nurses. *Journal of Advanced Nursing*, 50(5), 545–552. * Rempel, G.R., Neufeld, A., & Kushner, K.E. (2007). Interactive use of genograms and ecomaps in family caregiving research. *Journal of Family Nursing*, 13(4), 403–419. *** ## 9. Related examples Ready-to-use scenarios from the examples gallery: [Browse related examples](https://schematex.js.org/examples) *** ## 10. Roadmap **Planned — not yet parseable.** Do not use these in generated DSL today; the parser will reject or silently ignore them. * **Embedded mini-genogram as the center** — declaring a full family block inside the `center:` node (indented couples and children) so the center is a small genogram rather than a single shape. The parser currently reads indented content under `center:` as skipped lines. * **Per-category default arrow semantics** — e.g. auto-directional arrows for `health` systems (in) vs `work` systems (out). * **Multiple centers** — ecomaps for two clients sharing some systems. * **Custom color overrides** — `[color: "#…"]` on a system or connection. Track in the GitHub issues if you need any of these sooner. --- # Entity structure diagram Canonical URL: https://schematex.js.org/docs/entity Markdown URL: https://schematex.js.org/docs/entity.md ## About entity structure diagrams An **entity structure diagram** maps the legal and economic relationships between organizations and people — who owns what, through which entity form, and in which jurisdiction. Corporate counsel use them to document holding structures and subsidiary chains. Tax advisors draw them to show cross-border IP licensing flows and transfer-pricing arrangements. Startup attorneys produce them for cap tables and board consents. Estate planners use them to show grantor-trust-beneficiary arrangements. The diagram is the first document requested in any M\&A due diligence process and the standard attachment to an OECD transfer-pricing master file. Schematex follows the practitioner conventions synthesized from **SEC Regulation S-K Exhibit 21** (subsidiary disclosure), **IRS Form 8832** entity classifications, **ISO 3166-1** jurisdiction codes, **ISO 20275** legal entity forms, and Big Four tax memo conventions. Entity types map to distinct shapes (rectangle for corp, rounded rectangle for LLC, ellipse for trust) so any reader can identify the legal form at a glance. This page documents what the parser accepts today. ```schematex entity-structure "Acme Global Holdings" entity parent "Acme Global, Inc." corp@US [note: "Ultimate Parent"] entity ie-holdco "Acme Ireland Holdings" corp@IE entity ie-ip "Acme IP Ltd" corp@KY [note: "Holds group IP"] entity nl-bv "Acme EU Distribution" llc@NL entity sg-apac "Acme APAC Trading" corp@SG entity esop "Employee Option Pool" pool parent -> ie-holdco : 100% ie-holdco -> ie-ip : 100% ie-holdco -> nl-bv : 100% ie-ip -~-> nl-bv [label: "IP License · royalty"] parent -> sg-apac : 100% esop -.-> ie-holdco ``` *** ## 1. Your first entity structure The smallest useful entity structure: a parent owning two subsidiaries. ```schematex entity-structure "Simple holding" entity holdco "Holdco LLC" llc@DE entity opco "OpCo Inc." corp@DE entity sub_uk "UK Sub Ltd." llc@UK holdco -> opco : 100% holdco -> sub_uk : 100% ``` Four rules cover 80% of usage: 1. Start with `entity-structure`, optionally followed by a quoted title. 2. Each legal entity is a node: `entity ID "Display Name" type` — the type determines the shape. 3. Connect entities with `->`. Append `: pct` to label ownership percentage: `parent -> child : 60%`. 4. Add `@jurisdiction` after the type to show a jurisdiction badge: `corp@DE`. > Comments must start with `#` on their own line. *** ## 2. Entity types The entity type determines the shape rendered for that node. Schematex maps several common aliases to a canonical type. | Canonical type | Aliases accepted | Rendered shape | Typical use | | -------------- | -------------------- | ------------------------- | --------------------------------------- | | `corp` | `corporation`, `inc` | Rectangle (sharp corners) | C-corp, S-corp, Ltd., SA, AG | | `llc` | `llp`, `gmbh`, `bv` | Rounded rectangle | LLC, LLP, GmbH, BV | | `lp` | `lllp`, `fund` | Notched rectangle | LP, LLLP, investment fund | | `trust` | — | Ellipse | Family trust, statutory trust | | `individual` | `person` | Circle | Founder, grantor, natural person | | `foundation` | `npo` | Pentagon (shield) | Non-profit, charitable foundation | | `disregarded` | `branch` | Dashed rectangle | Disregarded entity, foreign branch | | `pool` | — | Dashed rounded rectangle | Option pool, ESOP, unissued shares | | `placeholder` | `tbf` | Dashed rectangle (faded) | To-be-formed entity, acquisition target | **Entity syntax:** ``` entity ID "Display Name" type entity ID "Display Name" type@JURISDICTION entity ID "Display Name" type@JURISDICTION [properties] ``` **ID rules.** Must start with a letter, followed by letters, digits, underscores, or hyphens: `[A-Za-z][A-Za-z0-9_-]*`. ```schematex entity-structure "Entity type shapes" entity c1 "Delaware C-Corp" corp@DE entity l1 "California LLC" llc@CA entity p1 "Cayman Fund LP" lp@KY entity t1 "Family Trust" trust@SD entity i1 "Jane Smith" individual entity f1 "Acme Foundation" foundation entity d1 "Irish Branch" disregarded@IE entity pool1 "ESOP Pool" pool entity tbf1 "Acquisition Target" placeholder c1 -> l1 : 100% c1 -> p1 : 80% c1 -> f1 i1 -> t1 t1 -> c1 : 100% ``` *** ## 3. Node properties Optional properties inside `[…]` annotate the entity with additional context visible in the rendered node. | Property | Syntax | Effect | | ------------------- | ----------------------------------------- | ------------------------------------------ | | `status: new` | `new`, `eliminated`, `modified`, `normal` | Visual badge for transaction-step diagrams | | `tax: ccorp` | quoted string | Tax classification label below entity name | | `role: "Grantor"` | quoted string | Role label (for individuals and trustees) | | `note: "…"` | quoted string | Small note displayed inside the node | | `est: "2024-03-15"` | quoted string | Formation date | Any key not in the reserved set (`status`, `tax`, `role`, `note`, `est`) is stored as a custom property. ``` entity alice "Alice Chen" individual [role: "Founder & CEO"] entity trust1 "Smith Irrevocable Trust" trust@SD [est: "2019-06-01", note: "Spendthrift provisions"] entity opco "OpCo, Inc." corp@DE [status: new, tax: ccorp] ``` *** ## 4. Jurisdiction Append `@CODE` after the entity type to display a jurisdiction badge on the node. The code is a 2–3 letter string — ISO 3166-1 alpha-2 country codes and US state abbreviations are both accepted. ``` entity parent "Parent Corp" corp@US entity ie_sub "Ireland Sub" corp@IE entity ky_fund "Cayman Fund" lp@KY entity de_llc "Delaware LLC" llc@DE ``` Common codes: `US` United States · `DE` Delaware · `CA` California · `NY` New York · `UK` United Kingdom · `IE` Ireland · `NL` Netherlands · `KY` Cayman Islands · `SG` Singapore · `HK` Hong Kong · `JP` Japan · `BM` Bermuda · `VG` British Virgin Islands · `CH` Switzerland · `LU` Luxembourg · `SD` South Dakota. `@DE` resolves as Delaware by default (most common in US legal contexts). ### Jurisdiction clusters Declare a jurisdiction with `jurisdiction CODE "Name" [color: "#hex"]` to group all entities sharing that code into a labeled, dashed-border cluster region on the diagram. ``` jurisdiction US "United States" [color: "#3b82f6"] jurisdiction IE "Ireland" [color: "#059669"] ``` When an entity's `@CODE` matches a declared `jurisdiction`, it is automatically placed inside that cluster. If `jurisdiction` is not declared, the badge still appears but no cluster region is drawn. ```schematex entity-structure "IP holding structure" jurisdiction US "United States" [color: "#3b82f6"] jurisdiction IE "Ireland" [color: "#059669"] jurisdiction KY "Cayman Islands" [color: "#d97706"] entity parent "TopCo, Inc." corp@US entity ie_hold "Ireland Holdings" corp@IE entity ie_ip "IP HoldCo" corp@KY [note: "Group IP"] entity ie_op "EU Opco" llc@IE parent -> ie_hold : 100% ie_hold -> ie_ip : 100% ie_hold -> ie_op : 100% ie_ip -~-> ie_op [label: "IP License"] ``` ### Manual clusters Use `cluster "Label" [members: [id1, id2], color: "#hex"]` to group entities that don't share a single jurisdiction code. ``` cluster "Ireland / Cayman IP" [members: [ie_ip, nl_bv], color: "#059669"] ``` *** ## 5. Ownership edges An edge line connects two entity IDs with an operator. The operator determines the line style and visual semantics. | Operator | Renders as | Meaning | | -------- | ------------------- | ------------------------------------------------------ | | `->` | Solid arrow | Equity ownership (default) | | `==>` | Double line arrow | Voting-only control (no economic interest) | | `-.->` | Dashed grey arrow | Option pool / conditional ownership | | `-~->` | Dashed purple arrow | IP license, management agreement, intercompany service | | `-->` | Dashed green arrow | Distribution (trust to beneficiaries) | **Ownership percentage:** append `: pct` after the target ID. ``` parent -> subsidiary : 60% ``` **Share class:** add `[class: "Series A Pref"]` to label the share class on the edge. ``` vc -> startup : 22% [class: "Series A Pref"] ``` **Non-equity label:** use `[label: "…"]` for descriptive edge text on license or distribution edges. ``` ip_co -~-> opco [label: "IP License · royalty"] trust --> beneficiary [label: "Discretionary distributions"] ``` **Combining:** `class:` and `label:` can appear together. ``` fund -> portfolio : 35% [class: "Common", label: "Post-Series B"] ``` ```schematex entity-structure "Mixed edge types" entity holdco "HoldCo LLC" llc@DE entity opco "OpCo Inc." corp@DE entity ip_co "IP Ltd" corp@KY entity fund "Growth Fund" lp@DE entity esop "ESOP Pool" pool entity trust1 "Family Trust" trust@SD entity founder "J. Smith" individual # Equity ownership holdco -> opco : 100% holdco -> ip_co : 100% # IP license (non-equity) ip_co -~-> opco [label: "Exclusive license"] # Option pool (dashed) esop -.-> opco : 10% # Voting control fund ==> holdco # Trust distribution trust1 --> founder [label: "Income distributions"] founder -> trust1 : 100% ``` *** ## 6. Labels & comments * **Title:** `entity-structure "Acme Holdings"` — first line, quoted. * **Entity display name:** the quoted string in `entity ID "Name" type` — appears inside the node. * **Jurisdiction badge:** `@CODE` after type — 2–3 letter code shown in the node corner. * **Node properties:** `[note: "…", role: "…", status: new, …]` — annotations inside the node. * **Edge percentage:** `: pct` after the target ID — appears on the ownership arrow. * **Edge class:** `[class: "…"]` — share class label on the arrow. * **Edge label:** `[label: "…"]` — descriptive text on the arrow. * **Comments:** `#` at the start of a line (after leading whitespace). Inline trailing `#` comments inside bracket blocks are also stripped. *** ## 7. Reserved words & escaping **Reserved at line start:** `entity-structure` (header), `entity`, `jurisdiction`, `cluster`. **Entity type keywords** — these strings are parsed as entity types and must not be used as entity IDs: `corp`, `corporation`, `inc`, `llc`, `llp`, `gmbh`, `bv`, `lp`, `lllp`, `fund`, `trust`, `individual`, `person`, `foundation`, `npo`, `disregarded`, `branch`, `pool`, `placeholder`, `tbf`. **Edge operator tokens** — avoid these sequences inside IDs: `->`, `==>`, `-.->`, `-~->`, `-->`. **Strings with spaces** must be double-quoted in display names, notes, role labels, and color values. **Jurisdiction codes** are case-insensitive in the source but normalized to uppercase: `@de` and `@DE` are equivalent. *** ## 8. Common mistakes | You wrote | Parser says | Fix | | ---------------------------------------------------------- | --------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------- | | `entity acme Acme Inc. corp@DE` (name unquoted) | Parse fails — name must be quoted | `entity acme "Acme Inc." corp@DE` | | `acme -> sub [50%]` (percent inside brackets) | `50%` not recognized as a property key | `acme -> sub : 50%` (colon before percent, outside brackets) | | `entity acme "Acme" Ltd@DE` | `Ltd` is not a recognized type keyword | Use `corp` or `llc`; `Ltd` is not in the alias table | | `acme -> unknown_id : 100%` | `EntityParseError: Edge references unknown entity "unknown_id"` | Declare every entity before using it in an edge | | `cluster "US entities" [ids: [a, b]]` | `ids:` not recognized; property is silently ignored | Use `members: [a, b]` | | `jurisdiction DE "Delaware"` then `entity co "Co" corp@DE` | Cluster draws around `co` — correct. But `@DE` in a US context is Delaware, not Germany | Use `@DE` for Delaware, `@DEU` is not valid — there is no ambiguity workaround; document the intent in a `note:` | | `entity x "X" corp [status: pending]` | `pending` is not a valid status — property silently stored as custom prop | Use `new`, `eliminated`, `modified`, or `normal` | *** ## 9. Grammar (EBNF) ```text document = header (blank | comment | jurisdiction-def | cluster-def | entity-def | edge)* header = "entity-structure" ( WS quoted-string )? NEWLINE quoted-string = '"' any-char-but-quote* '"' jurisdiction-def = "jurisdiction" CODE WS quoted-string ( "[" jur-attrs "]" )? NEWLINE jur-attrs = jur-attr ("," jur-attr)* jur-attr = "color:" quoted-string cluster-def = "cluster" WS quoted-string ( "[" cluster-attrs "]" )? NEWLINE cluster-attrs = cluster-attr ("," cluster-attr)* cluster-attr = "members:" "[" id ("," id)* "]" | "color:" quoted-string entity-def = "entity" WS id WS quoted-string WS entity-type ( "@" CODE )? ( "[" entity-attrs "]" )? NEWLINE entity-attrs = entity-attr ("," entity-attr)* entity-attr = "status:" status | "tax:" quoted-string | "role:" quoted-string | "note:" quoted-string | "est:" quoted-string | key ":" quoted-string # custom property entity-type = "corp" | "corporation" | "inc" | "llc" | "llp" | "gmbh" | "bv" | "lp" | "lllp" | "fund" | "trust" | "individual" | "person" | "foundation" | "npo" | "disregarded" | "branch" | "pool" | "placeholder" | "tbf" edge = id WS op WS id ( ":" WS pct-text )? ( "[" edge-attrs "]" )? NEWLINE op = "->" | "==>" | "-.->" | "-~->" | "-->" pct-text = any text up to "[" or end of line # e.g. "100%" or "V 75% / E 50%" edge-attrs = edge-attr ("," edge-attr)* edge-attr = "class:" quoted-string | "label:" quoted-string status = "new" | "eliminated" | "modified" | "normal" CODE = [A-Za-z]{2,3} id = [A-Za-z] [A-Za-z0-9_-]* comment = "#" any NEWLINE ``` Authoritative source: `src/diagrams/entity/parser.ts`. If this diverges from the parser, the parser wins — please open an issue. *** ## 10. Standard compliance Schematex entity structure diagrams synthesize conventions from: * **SEC Regulation S-K, Item 601(b)(21)** — Exhibit 21 subsidiary disclosure conventions * **IRS Form 8832** — C-corp / pass-through / disregarded entity classifications * **ISO 3166-1 alpha-2** — jurisdiction codes on node badges * **ISO 20275:2017** — Legal Entity Identifier (LEI) entity form classification * **OECD Transfer Pricing Guidelines (2022)** — cross-border IP license and royalty flow conventions * **Big Four tax memo conventions** (EY / PwC / KPMG / Deloitte) — de-facto standard for cross-border structure diagrams What is implemented today: * ✅ Nine entity types with distinct shapes: `corp`, `llc`, `lp`, `trust`, `individual`, `foundation`, `disregarded`, `pool`, `placeholder` * ✅ Type aliases: `corporation`, `inc`, `llp`, `gmbh`, `bv`, `lllp`, `fund`, `person`, `npo`, `branch`, `tbf` * ✅ Five edge operators: `->` (ownership), `==>` (voting), `-.->` (pool), `-~->` (license), `-->` (distribution) * ✅ Ownership percentage labels (`: pct`) and share class (`[class: "…"]`) * ✅ Jurisdiction badges (`@CODE`) with ISO 3166-1 and US state codes * ✅ Jurisdiction cluster regions (dashed-border grouping by `@CODE`) * ✅ Manual clusters (`cluster "…" [members: […]]`) * ✅ Node properties: `status`, `tax`, `role`, `note`, `est`, custom key-value pairs * ⏳ V/E split percentage — `V 75% / E 50%` stored in `percentage` string but rendering as two-line label not yet implemented * ⏳ Transaction-step diff rendering — `status: new` / `eliminated` / `modified` stored but visual diff badges not yet rendered * ⏳ Inline share-count annotation — `100 shares Common` alongside percentage *** ## 11. Related examples [Browse related examples](https://schematex.js.org/examples) *** ## 12. Roadmap **Planned — not yet parseable.** Do not use these in generated DSL today; the parser will reject or ignore them. * **V/E split rendering** — `V 75% / E 50%` on a single edge, displayed as a two-line percentage label distinguishing voting interest from economic interest. * **Transaction-step diff badges** — `status: new` renders a green "NEW" badge; `status: eliminated` renders a red strikethrough; used for pre/post M\&A structure comparison on a single diagram. * **Share count annotation** — `-> sub : 100 shares Common` alongside a percentage to capture the exact share count from a capitalization table. * **Bidirectional ownership** — explicit `<->` edge for cross-ownership (two entities holding each other), with S-curve routing to avoid clutter. * **Legend block** — auto-generated legend showing shape-to-entity-type and operator-to-relationship-type mapping. Track in the GitHub issues if you need any of these sooner. --- # EPC (Event-driven Process Chain) > ARIS business-process diagrams — alternating events and functions wired by AND/OR/XOR connectors, with structural well-formedness validation. Canonical URL: https://schematex.js.org/docs/epc Markdown URL: https://schematex.js.org/docs/epc.md ## About EPC An **Event-driven Process Chain** (EPC) is the business-process notation at the heart of **ARIS** (Scheer, 1990s): a control flow that strictly **alternates events** (passive states — "Order received") and **functions** (active tasks — "Check credit"), routed by **AND / OR / XOR connectors** for splits and joins. It is widely used for SAP process documentation and BPM. Schematex's edge is **structural validation**. The engine doesn't compute a number (contrast `pert` or `faulttree`); it checks the EPC well-formedness rules — event/function alternation, the *event-cannot-decide* signature rule, single-in/single-out multiplicity, reachability — and highlights violations with AI-readable messages instead of silently drawing a broken model. ```schematex epc "Credit check" event E1 "Order received" function F1 "Check credit" xor X1 event E2 "Credit approved" event E3 "Credit refused" E1 -> F1 -> X1 X1 -> E2 X1 -> E3 ``` *** ## 1. Your first EPC Start with the `epc` keyword, an optional title, then declare **nodes by id** and **wire them with arrows**: ``` epc "Order fulfilment" event E1 "Order received" function F1 "Check credit" event E2 "Credit OK" E1 -> F1 -> E2 ``` Nodes carry an id and an optional quoted label; arrows reference ids. The canonical form declares connectors as nodes (`xor X1`) and wires everything by id — closest to how ARIS stores an EPC. *** ## 2. Node kinds ``` event E1 "Order received" # passive state (rounded hexagon) function F1 "Check credit" # active task (rounded rectangle); alias: func func F2 "Send invoice" # `func` is shorthand for `function` and A1 # AND connector (∧) or O1 # OR connector (∨) xor X1 # exclusive-OR connector (×) ``` A connector fans either way (split or join) depending on its incoming/outgoing arcs, so the same glyph serves both. Connector labels are optional. *** ## 3. Wiring control flow ``` E1 -> F1 -> X1 # a chain is sugar for the pairwise arcs E1→F1, F1→X1 X1 -> E2 # connector split into two branches X1 -> E3 F2 -> E2 : sent # a single arc may carry a ': label' ``` * A `->` **chain** expands to pairwise edges. * A trailing **`: label`** annotates a single arc. * An arrow endpoint not yet declared is **auto-created** (and flagged by the validator), so you can sketch fast and clean up later. ``` epc "Procure-to-pay" layout: tb event E1 "Need identified" function F1 "Create PO" and A1 function F2 "Notify supplier" function F3 "Update budget" E1 -> F1 -> A1 A1 -> F2 A1 -> F3 ``` `layout: tb` lays the chain top-to-bottom (default is also vertical-friendly). *** ## 4. Computed well-formedness This is the differentiator. The engine validates (flags, doesn't throw): 1. **Bipartite alternation** — events and functions strictly alternate along any path; connectors don't break it. 2. **Start/end must be events** — a function may not be a start or end node. 3. **Signature rule** — an *event must not be the source of an OR/XOR split* (a passive event cannot decide); an AND-split after an event is allowed. 4. **Split/join balancing** — a split of type T should be closed by a join of type T; mismatches are warnings (real EPCs are sometimes unbalanced). 5. **Single-in / single-out** per event and function — connectors carry the multiplicity. 6. **Reachability** — every node reachable from a start and reaching an end. Offending nodes are highlighted with `data-*` flags; the diagram still renders. *** ## 5. Common mistakes ``` # WRONG — invalid id (must start with a letter) event 9bad # AVOID — redeclaring a node (the first wins, a warning is raised) event E1 "first" event E1 "second" ``` Ids must start with a letter; a redeclared node keeps the first definition and warns. Because event→XOR/OR splits are flagged, route a decision through a **function** that produces the decision, then split. *** ## 6. Standard compliance Notation and rules follow ARIS EPC convention (Scheer), van der Aalst (1999), and Mendling (2008): hexagonal events, rounded-rectangle functions, ∧/∨/× connectors, and the event-cannot-decide signature constraint. ## 7. Roadmap Deferred: process-interface (off-page) nodes, organisational-unit/data-object lanes (eEPC), and split/join automatic repair suggestions. --- # ERD (Entity-Relationship Diagram) Canonical URL: https://schematex.js.org/docs/erd Markdown URL: https://schematex.js.org/docs/erd.md ## About ERDs An **Entity-Relationship Diagram (ERD)** documents the structure of a relational database: tables (entities), their columns (attributes), and the foreign-key relationships between them — including cardinality (1..1 / 0..1 / 1..N / 0..N). Backend engineers draw them to onboard new teammates onto a codebase. Data architects sketch them on a whiteboard before writing any DDL. Database educators (Elmasri & Navathe; Silberschatz / Korth / Sudarshan) put them at the centre of the conceptual-design unit in every CS database course. Schematex implements **crow's-foot notation** — the de-facto modern style used by MySQL Workbench, dbdiagram.io, ERDPlus, Lucidchart, draw\.io, Mermaid `erDiagram`, and Oracle SQL Developer Data Modeler. The DSL is DBML-compatible-ish so engineers can copy-paste; it also **accepts the full Mermaid `erDiagram` dialect** — start with the `erDiagram` header and paste Mermaid relationships (`CUSTOMER ||--o{ ORDER : places`) and type-first entity blocks (`ORDER { int id PK }`) unchanged; entities auto-create from relationships. The Chen 1976 notation (rectangles + diamonds + ovals + ISA hierarchies) and Barker overlay are deferred to v0.2; today, every diagram is rendered as crow's foot. > **Note** — This is **not** the same as the `entity` engine. `entity` (entity structure) is for **corporate / legal ownership** hierarchies (subsidiaries, percentage rollup, trusts). `erd` is for **database schemas** (tables, columns, foreign keys). Different domain, different layout, different audience. ```schematex erd title: "University Schema" table Student { student_id int PK name varchar email varchar UK major_id int FK -> Major.major_id } table Major { major_id int PK name varchar } table Course { course_id int PK title varchar credits int } table Enrollment { student_id int PK FK -> Student.student_id course_id int PK FK -> Course.course_id grade char } ref Student.major_id many-mandatory -- one-mandatory Major.major_id : "majors in" ref Enrollment.student_id many-mandatory -- one-mandatory Student.student_id ref Enrollment.course_id many-mandatory -- one-mandatory Course.course_id ``` *** ## 1. Your first ERD The smallest useful ERD: a parent table referenced by a child via foreign key. ```schematex erd table Customer { customer_id int PK email varchar UK } table Order { order_id int PK customer_id int FK -> Customer.customer_id } ref Order.customer_id many-mandatory -- one-mandatory Customer.customer_id : "places" ``` Four rules cover 80 % of usage: 1. Start with `erd`. Optional headers `title:`, `direction: LR | TB`. 2. Each table is a block: `table Name { col type marker }`. Markers are `PK`, `FK`, `UK`, `NN` (or `*`) for NOT NULL. 3. Inline foreign-key target: append `FK -> Other.column` to a column. The renderer adds an `FK` pill automatically. 4. Connect tables with a `ref` line: `ref Source <left-card> -- <right-card> Target [: "label"]`. Cardinality is one of `one-mandatory`, `one-optional`, `many-mandatory`, `many-optional` (named form), `1..1` / `0..1` / `1..N` / `0..N` (Min-Max), or Mermaid glyphs (`}o--||`, etc.) as alias. > Comments use `//` or `#`. Block forms can be either multi-line (one column per line) or inline (`table A { id int PK; name varchar }`). *** ## 2. Cardinality glyphs Crow's foot encodes the cardinality at each end of the relationship line: | Glyph | Reading | Min..Max | Named token | | ---------------------------- | ---------------------------- | -------- | ---------------- | | `─┃` (perpendicular bar) | Exactly one (mandatory one) | 1..1 | `one-mandatory` | | `─○` (open circle) | Zero or one (optional one) | 0..1 | `one-optional` | | `─┃<` (bar + crow's foot) | One or more (mandatory many) | 1..N | `many-mandatory` | | `─○<` (circle + crow's foot) | Zero or more (optional many) | 0..N | `many-optional` | Each end of a line is annotated independently. A typical 1:N relationship reads "exactly one CUSTOMER places zero-or-more ORDERs": ``` ref Order.customer_id many-mandatory -- one-mandatory Customer.customer_id : "places" ``` Reading right-to-left: the **right end** of the line attaches to Customer with a single bar (one-mandatory); the **left end** attaches to Order with a bar + crow's foot (many-mandatory). *** ## 3. Mermaid alias If you already use Mermaid `erDiagram`, the same glyphs work as input: ```schematex erd table Customer { customer_id int PK; email varchar UK } table Order { order_id int PK; customer_id int FK -> Customer.customer_id } table Product { product_id int PK; name varchar } ref Order }o--|| Customer : "places" ref Order }o--o{ Product : "contains" ``` | Mermaid token | Schematex meaning | | ------------------------ | ----------------------------- | | `\|o` left / `o\|` right | 0..1 | | `\|\|` | 1..1 | | `}o` left / `o{` right | 0..N | | `}\|` left / `\|{` right | 1..N | | `--` | identifying / solid line | | `..` | non-identifying / dashed line | *** ## 4. Identifying vs non-identifying Use `--` for identifying relationships (solid line) and `..` for non-identifying (dashed): ```schematex erd table User { id int PK; email varchar } table SessionLog { id int PK; user_id int FK -> User.id; created_at timestamp } ref SessionLog.user_id many-optional .. one-optional User.id : "logs (optional FK)" ``` The dashed line follows the Barker / IDEF1X non-identifying convention. *** ## 5. Composite primary keys (associative tables) Associative ("junction") tables resolve M:N relationships. Mark each PK + FK column with both `PK` and `FK`: ```schematex erd table Student { student_id int PK; name varchar } table Course { course_id int PK; title varchar } table Enrollment { student_id int PK FK -> Student.student_id course_id int PK FK -> Course.course_id grade char } ref Enrollment.student_id many-mandatory -- one-mandatory Student.student_id ref Enrollment.course_id many-mandatory -- one-mandatory Course.course_id ``` Schematex renders both pills (PK + FK) on the same row. The PK underline applies to the column name when any `PK` marker is present. *** ## 6. Layout direction `direction: LR` (default) places parent tables left, child tables right. `direction: TB` flips to top-to-bottom — useful for narrow embeds: ```schematex erd title: "Library (top-down)" direction: TB table Member { id int PK; name varchar; email varchar UK } table Book { id int PK; title varchar; author varchar } table Loan { id int PK; member_id int FK -> Member.id; book_id int FK -> Book.id; due_date date } ref Loan.member_id many-mandatory -- one-mandatory Member.id : "borrowed by" ref Loan.book_id many-mandatory -- one-mandatory Book.id : "of" ``` Layered orthogonal Manhattan routing with single-bend edges. v0.1 uses appearance-order stacking inside layers; barycenter crossing-reduction is deferred to v0.2. *** ## 7. Notation modes The DSL header can pin the notation: ``` erd notation: crowsfoot // default — only mode supported in v0.1 ``` `notation: chen` (rectangle-diamond-oval, weak entities, ternary, ISA) and `notation: barker` (per-half dashed) are documented in `27-ERD-STANDARD.md` but rejected by the parser today. Targets v0.2. *** ## 8. Limitations of v0.1 * **Crow's foot only.** Chen and Barker rendering deferred. * **No edge-crossing minimization.** Layered placement uses declaration order within layers; large ERDs (10+ tables, dense FKs) will show some crossings. Reorder `table` blocks if a specific layout matters. * **No self-referential C-loops.** Recursive relationships (`Employee.manager_id -> Employee.id`) parse but route as straight orthogonal lines, not the canonical C-shape. * **No M:N auto-resolution.** Express the associative entity explicitly (this is the standard practice in production schemas anyway). For the full standard reference, see `docs/reference/27-ERD-STANDARD.md`. *** ## Related examples Ready-to-use scenarios from the examples gallery: [Browse related examples](https://schematex.js.org/examples) ## Interactive editing The title, table aliases, column names, and data types expose independent source fields. Tables move only across relationship ranks; schema semantics own the primary layout axis and relationships reroute after every edit. --- # Event Tree Analysis > Inductive forward risk analysis — one initiating event branches success/failure through safety functions to quantified end states. Canonical URL: https://schematex.js.org/docs/eventtree Markdown URL: https://schematex.js.org/docs/eventtree.md ## About event trees An **event tree** is the forward, inductive twin of the fault tree. Start from one **initiating event** (a pipe break, a fire, a demand on a safety system) and ask, in order, whether each downstream **safety function** succeeds or fails. Every path through the branching ladder ends at a quantified **outcome** (OK, contained, core damage…). It is the workhorse of nuclear PRA and process QRA, standardised through **IEC 62502** and **[NUREG-0492](https://www.nrc.gov/docs/ML1007/ML100780465.pdf)**-era WASH-1400 practice. Schematex's edge is the same as the fault tree's: the engine **computes the answer**, not just the ladder. Given the initiating frequency and each function's failure probability, it derives every path frequency (`f₀ · ∏ branch-probabilities`), rolls outcomes up across paths, and highlights the **dominant sequence** in red. draw\.io draws a forking ladder and stops; that is a picture, not an analysis. ```schematex eventtree "Loss of coolant accident" initiating LOCA "Large LOCA" freq: 1e-4 function A "ECCS injects" p: 0.001 function B "Containment spray" p: 0.01 function C "Containment integrity" p: 0.005 outcome s s s -> "OK" outcome s s f -> "Late release" outcome s f * -> "Early release" outcome f * * -> "Core damage" ``` *** ## 1. Your first event tree Every document starts with the `eventtree` keyword (alias `eta`), an optional title, then a flat list of declarations: ``` eventtree "Smoke detector demand" initiating FIRE "Fire starts" freq: 0.01 function D "Detector actuates" p: 0.02 function S "Suppression works" p: 0.05 outcome s s -> "Controlled" outcome s f -> "Damage, contained" outcome f * -> "Uncontrolled fire" ``` * **`initiating ID "label" freq: N`** — exactly one. The challenge frequency, accepting decimals or scientific notation (`freq: 0.01` or `freq: 1e-4`). * **`function ID "label" p: N`** — one per branch column, **declared left→right in query order**. `p:` is the **failure** probability; the engine derives the success leg as its complement `1 − p` (you never state both). * **`outcome <pattern> -> "end state"`** — one realised leaf each. *** ## 2. The s / f / \* outcome pattern Each `outcome` row reads left→right over the function columns: ``` outcome s s s -> "OK" # every function succeeds outcome s s f -> "Late release" # C fails on the last query outcome s f * -> "Early release"# B fails; C is never queried (pruned) outcome f * * -> "Core damage" # A fails; path terminates immediately ``` * **`s`** — success leg (upper branch). * **`f`** — failure leg (lower branch). * **`*`** — pruned: the path is not queried here, it runs flat to its leaf. This is how an event tree avoids being a full balanced 2ⁿ tree: once a function failure makes later questions moot, you write `*` and the sequence terminates early. Two hard rules: a pattern may not be **longer** than the column count, and **once a column is pruned (`*`) every later column must also be `*`** — a path that has terminated cannot resume querying. *** ## 3. Computed path frequencies & outcomes This is the differentiator. With the failure probabilities and `freq`, the engine computes: * **Each path frequency** = `f₀ · ∏ branch-probabilities` along its `s`/`f` legs (success legs contribute `1 − p`, failure legs `p`). * **Outcome roll-up**: outcomes with the same end-state label are summed across every path that reaches them (every `"Core damage"` leaf adds up). * **The dominant sequence** — the largest-frequency path — gets the reserved-red accent, the ETA analogue of the fault tree's single point of failure. Every leaf carries `data-*` (`data-freq`, `data-outcome`) so the computed numbers are inspectable downstream. *** ## 4. Common mistakes ``` # WRONG — function with no failure probability function A "ECCS" # WRONG — querying after a pruned column (path already terminated) outcome * s -> "bad" # WRONG — more tokens than declared columns function A p: 0.1 outcome s s -> "ok" # WRONG — initiating event with no frequency initiating LOCA "Large LOCA" ``` Each is rejected with a plain-English message naming the line. State `p:` as a **failure** probability (small), give the initiating event a `freq:`, keep prunes trailing, and you are correct by construction. *** ## 5. Standard compliance Form follows IEC 62502 and classical PRA practice (WASH-1400 / NUREG): functions as ordered header columns, the binary success/failure split, complement-derived success legs, and frequency propagation by multiplication. The `monochrome` theme reproduces the textbook black-and-white look; `default` reserves red for the dominant sequence. ## 6. Roadmap Deferred: linked fault-tree fragments per branch (shared basic events), uncertainty propagation, and consequence-category grouping. --- # Fault Tree Analysis Canonical URL: https://schematex.js.org/docs/faulttree Markdown URL: https://schematex.js.org/docs/faulttree.md ## About fault trees A **fault tree** is the most widely deployed reliability/safety technique in regulated engineering: start from one undesired **top event** (a system failure) and decompose it through Boolean **gates** (AND / OR / voting / …) down to **basic events** (component failures) whose probabilities are known. Invented at Bell Labs in 1962 and standardised by **[NUREG-0492](https://www.nrc.gov/docs/ML1007/ML100780465.pdf)** (nuclear), **IEC 61025** (cross-industry), and SAE ARP4761 (aerospace). Schematex's edge is that the engine **computes the answer**, not just the picture: it runs MOCUS (Fussell-Vesely 1972) to find the **minimal cut sets** — the irreducible failure combinations — and the **top-event probability**, then highlights the cut sets in red (single points of failure in the strongest red). That is the whole point of drawing a fault tree, and it is the same stance `pert` takes toward scheduling and `petri` toward the marking. Distinct from `logic` (left-to-right signal netlist), `decisiontree` (expected-value rollback), and `fishbone` (qualitative, unquantified). ```schematex faulttree "Product not removed" analysis: cutsets, probability top T "Failure to remove product" = OR(G1, G2) gate G1 "Arm jams or collides" = AND(MSF, G3) gate G2 "Wrong slot commanded" = OR(CDM, MSF) gate G3 "Loss of position feedback" = OR(ESF, RCF) basic MSF "Manipulator system failure" p: 0.0035 basic CDM "Controller command error" p: 0.0009 basic ESF "Encoder sensor failure" p: 0.0021 basic RCF "Resolver cable fault" p: 0.0012 ``` *** ## 1. Your first diagram Every document starts with the `faulttree` keyword (alias `fta`), an optional title, then a flat list of declarations wired by id: ``` faulttree "Both pumps fail" analysis: cutsets, probability top T "Both redundant pumps fail" = AND(PA, PB) basic PA "Pump A fails" p: 0.01 basic PB "Pump B fails" p: 0.01 ``` `top` declares the single root event and the gate that produces it; `basic` declares a leaf component failure with a probability. The engine computes the minimal cut set `{PA, PB}` and P(top) = 1.0e-4. The DSL is **flat declaration + reference** (not nested by indentation) — a fault tree is a DAG, so a shared event is just referenced by id from several gates. Header directives: * `analysis: cutsets, probability` — what to compute (also `pathsets`, `none`). * `prob: rare | mcub | exact` — the probability method (default `rare`, a conservative upper bound). * `layout: tb | bt` — top-down (default) or bottom-up. *** ## 2. Events ``` top T "System fails" = OR(A, B) gate G1 "Sub-fault" = AND(A, B) basic A "Component A fails" p: 0.01 undeveloped EXT "External fire (not modelled)" house HX "Power on" state: 1 ``` * **`top`** — the one undesired event being analysed (rectangle, emphasised border). Exactly one is required. * **`gate`** — an intermediate event that is itself a gate output (rectangle). * **`basic`** — a primary component failure, the leaf that carries `p:` (circle). * **`undeveloped`** — an event not developed further (diamond); may carry `p:`. * **`house`** — a normally-expected event forced `state: 0` or `state: 1` (house glyph) — switches branches of the tree on/off. Probabilities accept decimals or scientific notation (`p: 0.004` or `p: 1e-6`); `p:` and `prob:` are interchangeable. An event with no probability is treated symbolically (cut sets still computed; P(top) reported as n/a). *** ## 3. Gates ``` top T1 = AND(A, B, C) # all inputs occur top T2 = OR(A, B) # at least one input occurs top T3 = XOR(A, B) # exactly one (treated as OR for cut sets) top T4 = VOTING(2/3; A, B, C) # at least k of n gate G1 = INHIBIT(A) if COND # A occurs AND the condition holds gate G2 = PAND(A, B) order: A, B # A then B (order rendered, AND for cut sets) ``` Gates are drawn **output-up, inputs-down** (the mirror of a `logic` gate): AND is a flat-bottomed dome, OR/XOR/VOTING a shield, INHIBIT a hexagon. The conditioning event of an `INHIBIT`/`PAND` renders as an ellipse to the side. A gate may reference another gate or any event — and the same basic event may feed several gates (a repeated event). *** ## 4. Computed cut sets & probability This is the differentiator. With `analysis: cutsets`, the engine runs **MOCUS**: AND gates grow the order of a cut set, OR gates multiply the number of cut sets, then idempotence (`A∧A=A`) and absorption (`X ⊆ Y ⇒ drop Y`) minimise the result. * Each **minimal cut set** is boxed in red; an **order-1 cut set is a single point of failure** (strongest red, `data-spof`). * A **repeated event** (one basic event under several gates) is handled by absorption — the case a naive expander gets wrong. * **P(top)** is computed from per-event probabilities: `rare` (Σ cut-set probs, default), `mcub` (1 − ∏(1−P)), or `exact` (inclusion-exclusion, which correctly de-duplicates a shared event). The method is shown next to the top event and noted in `<desc>`. * A `house state: 0` can make the top event unsatisfiable — reported as "no cut sets." *** ## 5. Repeated events ``` faulttree "Safety function fails" analysis: cutsets, probability prob: exact top T "Safety function fails" = OR(C1, C2) gate C1 "Channel 1" = AND(S1, L1) gate C2 "Channel 2" = AND(S2, L1) basic S1 "Sensor 1 fails" p: 0.05 basic S2 "Sensor 2 fails" p: 0.05 basic L1 "Shared logic solver fails" p: 0.05 ``` `L1` feeds both channels — it is drawn once per reference with a shared-event mark, but the cut-set engine treats every instance as one Boolean variable. Cut sets are `{S1, L1}` and `{S2, L1}`; `prob: exact` subtracts the overlap (L1 counted once over the union), giving 0.004875 rather than the rare-event 0.005. *** ## 6. Transfers ``` gate OVP "Sustained over-pressure" = INHIBIT(PUMP) if HEATER transfer OVP -> "OVP-detail" ``` A `transfer ID -> "name"` draws the transfer-out triangle below an event whose development lives elsewhere; a matching `transfer "name" = gate_expr` defines that named subtree and is spliced in for cut-set computation. v0.1 resolves transfers in-document. *** ## 7. Validation The parser fails fast with readable errors so the cut-set math is meaningful: * exactly one `top` is required (zero or several is an error); * a gate referencing an undeclared id reports the id by name; * cycles are rejected (a fault tree is a DAG); * a probability outside `[0, 1]`, a `VOTING k/n` with `k > n` or `n ≠ input count`, or an `if` condition on a non-INHIBIT/PAND gate are all reported in plain English. *** ## 8. Theming `default` uses the house palette — soft slate-blue event boxes, **green gates** ("logic proceeds"), **red cut-set boxes** (the computed risk), blue probability numerals, a yellow house. `monochrome` reproduces the NUREG-0492 black-and-white textbook look, where the dome/shield shape (not colour) carries the gate type. All strokes/fills come from `ReliabilityTokens`; every element carries `data-*` (`data-cutset`, `data-spof`, `data-prob`, `data-gate`) so the computed analysis is inspectable downstream. --- # Function Block Diagram (FBD) Canonical URL: https://schematex.js.org/docs/fbd Markdown URL: https://schematex.js.org/docs/fbd.md ## About function block diagrams **Function Block Diagram (FBD)** is one of the five PLC programming languages defined by **[IEC 61131-3:2013](https://en.wikipedia.org/wiki/IEC_61131-3)** — the international standard for industrial automation. It's the second-most-drawn PLC language in production code (after ladder), and the natural choice when a chunk of program is easier to read as **data flow** than as power-rail-and-rung relays. AND/OR logic, timers (TON/TOF/TP), counters (CTU/CTD), comparison (EQ/NE/GT/GE/LT/LE), math (ADD/SUB/MUL/DIV/MOVE), edge detectors (R\_TRIG/F\_TRIG), bistable latches (SR/RS) — all rendered as named-port boxes wired left-to-right. Schematex follows the **IEC 61131-3 §6.4** visual conventions with **IEC 60617-12** distinctive symbols (`&` for AND, `≥1` for OR, `=1` for XOR, `1` for NOT/BUF). Wires are colored by data type (BOOL black, INT blue, REAL orange, TIME magenta) following TIA Portal de-facto convention. Sister language to **[ladder](/docs/ladder)** (§6.3, rung-based) and **[sfc](/docs/sfc)** (§6.5, sequence-based); together they form the visual half of IEC 61131-3. ```schematex fbd "Motor Control" var Start: bool var Stop: bool var Latch: bool var MotorOut: bool network 0 "Start/stop latch": Latch = OR(Start, AND(Latch, ~Stop)) network 1 "Drive output": MotorOut = MOVE(Latch) ``` *** ## 1. Your first FBD network The smallest useful FBD network: one block, two inputs, one output. ``` fbd network 0: Out = AND(A, B) ``` `A` and `B` are auto-declared as BOOL inputs (left-side terminals), and `Out` is auto-wired to the AND block's `OUT` port (right-side terminal). The block sits between them with port stubs and labels. *** ## 2. Variables Declare variables before any networks. Each variable has a name, an IEC data type, and an optional initial value. ``` fbd "Tank Control" var StartBtn: bool var TankLevel: real var SetPoint: real = 80.0 var DwellTimer: timer var Pulse: counter ``` Supported types: `bool`, `int`, `dint`, `uint`, `udint`, `real`, `lreal`, `time`, `date`, `tod`, `string`, `wstring`, `byte`, `word`, `dword`, `timer`, `counter`. Any other identifier is treated as a user-defined function-block type. Optional scope prefixes (default = local): `var_input`, `var_output`, `var_in_out`, `var_global`, `var_external`. *** ## 3. Networks A **network** is one independent piece of data flow, evaluated left-to-right within itself; networks evaluate top-to-bottom across the program per scan. ``` network 0 "Start latch": ... network 1: ... ``` The number is optional — networks are auto-numbered if absent. The title (in quotes) renders at the top-left of the network frame. *** ## 4. Block calls — inline expression notation The clearest way to write a combinational network is as a single nested expression: ``` network 0: Out = OR(A, AND(B, ~C)) ``` The parser builds the call tree: outer OR with `A` on input 1 and the AND result on input 2; the AND has `B` and the negated `C`. The renderer lays them out left-to-right (inputs at layer 0, AND at layer 1, OR at layer 2, output at layer 3). `~C` adds a **negation bubble** (small open circle) at the input port — equivalent to inserting a NOT block on that wire, but cleaner. *** ## 5. Block calls — instance-named notation When you need to reference a block's outputs from elsewhere, give it an instance tag: ``` network 0: Pulse = R_TRIG(CLK: Sensor) Count = CTU(CU: Pulse.Q, R: Reset, PV: 100) Done = GE(IN1: Count.CV, IN2: 100) ``` `Pulse.Q`, `Count.CV` reference output ports of named instances. The instance tag renders italicized above the block header. Argument lists accept **named ports** (`CU: Pulse.Q`) or **positional** (`CTU(Pulse.Q, Reset, 100)`) — named is recommended for readability and required when you skip a port. *** ## 6. Inline constants Input ports can take literals directly — no wire needed: ``` network 0: Dwell = TON(IN: BottleSensor, PT: T#50ms) Cap = LIMIT(MN: 0.0, IN: Setpoint, MX: 95.0) Mode = SEL(G: ManualSwitch, IN0: AutoMode, IN1: ManualMode) ``` `T#50ms`, `0.0`, `95.0` render as small yellow boxed text to the left of their port. Time literals follow IEC 61131-3: `T#10ms`, `T#5s`, `T#3m20s`, `T#1h`. Booleans are `true` / `false` (case-insensitive). *** ## 7. Standard block library | Category | Blocks | | ----------- | ------------------------------------------------------- | | Boolean | `AND`, `OR`, `NOT`, `NAND`, `NOR`, `XOR`, `XNOR`, `BUF` | | Edge detect | `R_TRIG`, `F_TRIG` | | Bistable | `SR`, `RS` | | Timer | `TON`, `TOF`, `TP` | | Counter | `CTU`, `CTD` | | Math | `ADD`, `SUB`, `MUL`, `DIV`, `MOD`, `ABS`, `NEG`, `MOVE` | | Comparison | `EQ`, `NE`, `GT`, `GE`, `LT`, `LE` | | Selection | `SEL`, `MUX`, `MAX`, `MIN`, `LIMIT` | AND, OR, NAND, NOR, ADD, MUL, MAX, MIN accept any number of inputs (default 2). Pass extra positional args or use `[inputs: N]` to extend. ``` network 0: All4 = AND(A, B, C, D) Sum = ADD(X, Y, Z) ``` *** ## 8. Larger example — bottle counter ``` fbd "Bottle Counter" var ConveyorRunning: bool var BottleSensor: bool var BatchDone: bool var BatchSize: counter var DwellTimer: timer network 0 "Debounce sensor with 50ms dwell": Dwell = TON(IN: BottleSensor, PT: T#50ms) network 1 "Count one bottle on rising edge of debounced signal": Pulse = R_TRIG(CLK: Dwell.Q) BatchSize = CTU(CU: Pulse.Q, R: BatchDone, PV: 24) network 2 "Batch done": BatchDone = MOVE(BatchSize.Q) ``` Three networks: debounce → edge-detect → count → flag. Each network is one DAG; the renderer routes wires through Manhattan paths. *** ## 9. v0.1 limitations The current engine implements the standard-block subset most teams use day-to-day. The following are deferred and will be added in a follow-up: * **EN/ENO power-flow rails** (`[en]` block attribute, `[rail: on]` header) — adds a top-of-network enable rail, vendor convention from Studio 5000 / TIA Portal. * **User-defined function blocks** with `pins_in:` / `pins_out:` declarations — for custom motor controllers, PID instances, etc. * **Page connectors** (`connector_out` / `connector_in`) for wires that span multiple pages. * **Bit-string blocks** (SHL, SHR, ROL, ROR, AND\_BIT, OR\_BIT, etc.). * **Extended math** (SQRT, LN, LOG, EXP, SIN, COS, TAN, ASIN, ACOS, ATAN). * **ANSI distinctive shape mode** (`[shape: ansi]`) — the `logic` engine already provides this for pure-Boolean diagrams. * **CTUD** bidirectional counter, **TP** retentive timer (RTO). If you need any of these now, open an issue or use the `ladder` engine for the full IEC 61131-3 LD subset. *** ## Related examples Ready-to-use scenarios from the examples gallery: [Browse related examples](https://schematex.js.org/examples) ## Interactive editing The title is editable and movable. Named IEC function-block instances move vertically; left-to-right signal flow stays fixed, synthetic inline-expression blocks remain automatic, and wires reroute from named ports. --- # Fishbone diagram Canonical URL: https://schematex.js.org/docs/fishbone Markdown URL: https://schematex.js.org/docs/fishbone.md ## About fishbone diagrams A **fishbone diagram** (also called an Ishikawa or cause-and-effect diagram) maps the potential causes of a problem onto a horizontal spine that ends at the "effect" — the problem statement. Each major branch represents a category of causes; sub-branches carry contributing details. Kaoru Ishikawa introduced the technique in 1968 as a quality-control tool for manufacturing; it has since spread to healthcare, software engineering, and business operations as a structured brainstorming method for root-cause analysis. Teams run it in retrospectives, DMAIC improve phases, and incident post-mortems to prevent premature fixation on the loudest hypothesis. Schematex follows **Ishikawa (1968)** cause-and-effect conventions with the standard 6M manufacturing categories (Man, Machine, Material, Method, Measurement, Mother Nature/Environment) and their service variants (People, Process, Place, Policy, Procedures, Patron). Two authoring styles — structured and compact — can be mixed in one document. External references: [Ishikawa, K. — Guide to Quality Control (1968)](https://en.wikipedia.org/wiki/Ishikawa_diagram) · [ASQ Cause-and-Effect Diagram](https://asq.org/quality-resources/cause-analysis-tools/fishbone) · [ISO 9001:2015 §10.2 — Corrective Action](https://www.iso.org/standard/62085.html). ```schematex fishbone "Solder Joint Defect Rate — Root Cause Analysis" effect "3.2% solder joint failure rate" category man "Man" category machine "Machine" category material "Material" category method "Method" category meas "Measurement" category env "Environment" man : "Operator training gap" man : "Shift handover not documented" machine : "Reflow oven temp drift" machine : "Squeegee blade worn" material : "Solder paste past shelf life" material : "PCB pad oxidation" method : "Stencil aperture undersized" method : "Pick-and-place speed too high" meas : "AOI false-accept rate rising" env : "Humidity spike in Q3" env : "ESD grounding gaps" ``` *** ## 1. Your first fishbone The smallest useful fishbone: three categories, one cause each, one with a sub-cause. ```schematex fishbone "API latency spike" effect "P99 > 2 s after deploy" category code "Code" category infra "Infra" category data "Data" code : "N+1 query in new endpoint" - "Missing eager-load on orders" infra : "DB connection pool exhausted" data : "Index missing on accounts table" ``` Four rules cover 80% of usage: 1. Start with `fishbone`, optionally followed by a quoted title. 2. Declare each branch with `category id "Label"` — the `id` is a short internal key, `"Label"` is what prints on the diagram. 3. Add causes with `id : "cause text"` on their own lines. 4. Indent a line by at least 2 spaces and start it with `-` to create a sub-cause (second-order branch) under the preceding cause. > Comments may start with `#`, `//`, or Mermaid-style `%%` on their own line. *** ## 2. Building blocks ### The spine and effect `effect "Problem statement"` places text in the fish's head. If `effect` is omitted the parser falls back to the diagram title. ``` fishbone "Title" effect "Specific problem statement" ``` The head sits on the right by default (`config direction = right`). Use `config direction = left` to flip it. ### Categories (major bones) `category id "Label"` declares a branch. The `id` is used internally to assign causes; the quoted `"Label"` appears on the diagram. Categories also accept optional properties in `[…]`: | Property | Values | Effect | | ---------------------------- | ---------------- | ------------------------------------------------------------------- | | `color: "#hex"` | hex color string | Branch and label color | | `side: top` / `side: bottom` | `top`, `bottom` | Forces this branch to the top or bottom rail (default: alternating) | | `order: N` | integer | Position within its rail — lower numbers sit closer to the tail | ``` category rework "Rework" [color: "#E53935", side: top, order: 1] ``` ### Causes (minor bones) Two styles are accepted and can be mixed in one diagram: **Style A — structured.** Declare categories first, then assign causes with `id : "text"`: ``` category code "Code" category infra "Infra" code : "N+1 query in endpoint" code : "Missing cache layer" infra : "Auto-scaling lag" ``` **Style B — compact.** Category label and causes in one line, separated by `;` or `,`: ``` category Code: N+1 query; Missing cache; Synchronous call category Infra: Auto-scaling lag; CDN misconfigured ``` In compact style the `id` is auto-derived from the label text (lowercased, spaces → hyphens). Quotes are optional for cause text. ```schematex fishbone "Conversion rate drop" effect "Checkout conversion -12% MoM" # Style A — structured category ux "UX" category trust "Trust" ux : "Confusing multi-step form" ux : "Slow page on mobile" trust : "No payment security badge" # Style B — compact category Pricing: Price-anchoring missing; No annual discount shown; Coupon field too prominent ``` **Style C — Mermaid-mindmap shorthand.** A top-level bare line becomes a category, and indented `-` items become sibling Level-1 causes under that category: ```txt fishbone "Why is the site slow?" effect "Page LCP > 4s" Content - heavy hero image - too much above-the-fold text Tech - JS bundle too large - render-blocking CSS ``` *** ## 3. Sub-causes (second-order branches) Indent a `-` line by at least 2 spaces after a Level-1 cause to attach a sub-cause to it. The `-` dash is part of the syntax; the text follows it. ``` method : "Stencil aperture undersized" - "Tolerance spec from 2018 board revision" - "No re-validation after material change" method : "Pick-and-place speed too high" - "Speed limit lifted during overtime run" ``` Sub-causes appear as shorter, narrower twigs branching off their parent rib. ```schematex fishbone "Medication error increase" effect "Errors up 18% in Q3" category process "Process" category people "People" process : "CPOE alert fatigue" - "47 non-critical alerts per shift" - "Override too easy — one click" process : "5-Rights verification skipped" - "No barcode scanner at bedside" people : "Float staff unfamiliar with unit" - "No unit-specific orientation checklist" people : "Handoff communication gaps" ``` *** ## 4. Config options `config key = value` lines can appear anywhere after the header. Unknown keys and values are silently ignored. | Config key | Values | Default | Effect | | ----------------------------- | ---------------------------------------------- | -------------- | ------------------------------------------------------------------- | | `direction` | `right` / `left` (also `ltr` / `rtl`) | `right` | Which side the effect head appears on | | `sides` | `both`, `top`, `bottom` | `both` | Which half of the spine hosts branches | | `density` | `compact`, `normal`, `spacious` | `normal` | Spacing between ribs — affects how many branches fit before overlap | | `slope` (or `ribslope`) | `gentle`, `normal`, `steep`, or a number (0–3) | `normal` (0.6) | Rib angle — shallow vs. steep diagonal | | `causeside` (or `cause-side`) | `head`, `tail`, `both` | `head` | Which side of a rib sub-causes branch off from | | `width` | integer px | auto | Override canvas width | | `height` | integer px | auto | Override canvas height | ``` config direction = left config density = compact config slope = gentle config sides = top ``` *** ## 5. Labels & comments * **Diagram title:** `fishbone "Website Traffic Drop"` — first line, optional. * **Effect label:** `effect "30% organic traffic decline"` — the problem at the fish's head. * **Category label:** `category id "Human-readable name"` — printed on the branch. * **Cause text:** quoted `"like this"` or unquoted (spaces allowed in compact style). * **Sub-cause text:** after the leading `-`, quoted or unquoted. * **Comments:** `#`, `//`, or `%%` at the start of a line (after optional leading whitespace). The same markers also start trailing comments outside double-quoted strings. *** ## 6. Reserved words & escaping **Reserved at line start:** `fishbone` (header), `effect`, `category`, `config`. **The `-` prefix** on an indented line is reserved as the sub-cause marker. To include a literal hyphen-dash at the start of cause text, quote it: `code : "- old deprecated path"`. **Strings with spaces** in structured-style cause text should be double-quoted: `code : "N+1 query"`. In compact style (`category Label: ...`) the text runs to the `;` or `,` separator and quoting is optional. **Comment markers** (`#`, `//`, `%%`) start a comment unless inside a double-quoted string. | Reserved sequence | Context | Alternative | | ------------------------------------------ | ------------------- | ---------------------------------------- | | `#` at line start | Comment marker | Quote the text if `#` is part of content | | `-` at start after ≥2-space indent | Sub-cause marker | Quote: `- "- text with dash"` | | `category`, `effect`, `config`, `fishbone` | Line-start keywords | Cannot be used as category IDs | *** ## 7. Common mistakes | You wrote | Parser says | Fix | | --------------------------------------------------------------------- | ---------------------------------------------------------------- | ---------------------------------------------------------------------- | | `cause1 : "text"` with no prior `category cause1` | `FishboneParseError: Unknown category "cause1"` | Declare `category cause1 "Label"` before assigning causes | | `- "sub-cause"` at the start of the file (no preceding Level-1 cause) | `FishboneParseError: Sub-cause … has no preceding Level-1 cause` | Place the sub-cause line immediately after a `id : "cause"` line | | `- "sub-cause"` with only 1-space indent | Treated as a cause line, not a sub-cause | Indent with at least 2 spaces | | `category Code: cause one, cause two` | Parsed as compact style — `,` and `;` are both separators | Intended behavior; both separators work | | `config direction = center` | Unknown value — silently ignored, stays `right` | Use `right` or `left` | | `config slope = 45` | Out of range (must be 0–3 exclusive); silently ignored | Use a preset (`gentle`, `normal`, `steep`) or a value like `0.5` | | `fishbone: "Title"` | Parsed correctly — colon after keyword is optional | Both `fishbone "Title"` and `fishbone: "Title"` work | | Mermaid mindmap-style bare category | Parsed as an implicit category | `Content` followed by indented `- item` lines works without `category` | *** ## 8. Grammar (EBNF) ```text document = header (blank | comment | effect | category | config | cause | sub-cause | implicit-category)* header = "fishbone" ":"? ( WS quoted-string )? NEWLINE effect = "effect" ":"? WS quoted-string NEWLINE config = "config" WS config-key WS "=" WS config-value NEWLINE config-key = "direction" | "width" | "height" | "sides" | "slope" | "ribslope" | "density" | "causeside" | "cause-side" config-value = bare-word | number | quoted-string category = "category" WS id WS label-or-compact ( "[" category-attrs "]" )? NEWLINE implicit-category = bare-text NEWLINE # top-level, no ":" label-or-compact = quoted-string # structured form: category id "Label" | id WS ":" WS compact-causes # compact form: category Label: cause; cause category-attrs = category-attr ("," category-attr)* category-attr = "color:" quoted-string | "side:" ( "top" | "bottom" ) | "order:" integer cause = id WS ":" WS cause-text NEWLINE # structured form cause-text = quoted-string | bare-text sub-cause = INDENT≥2 "-" WS cause-text NEWLINE compact-causes = compact-cause ( (";" | ",") compact-cause )* compact-cause = quoted-string | bare-text comment = ( "#" | "//" | "%%" ) any NEWLINE id = [a-zA-Z] [a-zA-Z0-9_-]* quoted-string = '"' any-char-but-unescaped-quote* '"' ``` Authoritative source: `src/diagrams/fishbone/parser.ts`. If this diverges from the parser, the parser wins — please open an issue. *** ## 9. Standard compliance Schematex fishbone diagrams follow **Ishikawa (1968)** cause-and-effect conventions: a horizontal spine with a labeled head (the effect) and diagonal ribs for major causal categories, each carrying minor bones (individual causes). The two-level hierarchy (category → cause → sub-cause) matches the traditional "5 Whys" drill-down depth used in Six Sigma DMAIC and ISO 9001 corrective action workflows. What is implemented today: * ✅ Structured style: `category id "Label"` + `id : "cause"` * ✅ Compact style: `category Label: cause; cause; cause` * ✅ Sub-causes (Level 2) via indented `-` prefix * ✅ Per-category color and side override * ✅ `direction`, `density`, `slope`, `sides`, `causeside` config * ✅ Optional explicit `width` / `height` * ⏳ Level 3+ sub-sub-causes (parser stores children but renderer clips at depth 2) * ⏳ Auto-suggest standard category sets (6M, 8P, 5P) * ⏳ Per-category `order` rendering (parsed but not yet applied by the layout engine) References: * Ishikawa, K. (1968). *Guide to Quality Control.* JUSE Press. * ASQ — [Cause-and-Effect (Fishbone) Diagram](https://asq.org/quality-resources/cause-analysis-tools/fishbone) * ISO 9001:2015 §10.2 — Nonconformity and corrective action *** ## 10. Related examples [Browse related examples](https://schematex.js.org/examples) *** ## 11. Roadmap **Planned — not yet parseable.** Do not use these in generated DSL today; the parser will reject or ignore them. * **Level 3 sub-sub-causes** — a third indent tier; the AST structure supports it but the renderer currently stops at Level 2. * **`causeside` per-category override** — set `cause-side` on individual categories rather than globally. * **Auto-suggest standard categories** — `template: 6M` / `template: 8P` shorthand that pre-populates the standard manufacturing or service category names. * **Legend block** — `legend` keyword to declare a color-coding key rendered alongside the diagram. * **`metadata:` block** — structured key-value metadata (facilitator, date, revision) displayed in a corner annotation. Track in the GitHub issues if you need any of these sooner. ## Interactive editing Effect, category, cause, sub-cause text, and the title are editable. Bone geometry remains automatic because placement directly encodes the Ishikawa hierarchy. --- # Floor plan Canonical URL: https://schematex.js.org/docs/floorplan Markdown URL: https://schematex.js.org/docs/floorplan.md ## About floor plans A **floor plan** is the standard 2D architectural view of a space: rooms with real dimensions, walls drawn as solid **poché** bands, doors with quarter-circle swing arcs, windows as glazing lines, and furniture as plan-view symbols — the visual language documented in *Architectural Graphic Standards* and used by every professional plan set. Schematex renders it from text: you declare rooms, openings, and furniture with real measurements; the engine merges shared walls, computes room areas, draws dimension lines, auto-seats tables, and validates the geometry. Unlike image generators, the output is *measurable and editable* — every room is a labeled element with its computed area, and adding a fourth row of desks is a one-line edit. ```schematex floorplan "Studio Apartment" unit m room main "Living / Sleeping" at 0,0 size 5.2x4.0 room bath "Bath" right-of main size 2.0x2.2 room kitchen "Kitchenette" right-of main align end size 2.0x1.8 door main south at 15% width 0.9 door between main bath at 50% opening between main kitchen at 50% width 1.2 window main north at 30% width 1.8 furniture bed-double in main at 3.4,0.3 furniture sofa in main at 0.2,3.0 furniture toilet in bath at 0.2,0.2 furniture sink in bath at 1.3,0.2 furniture counter in kitchen at 0.15,1.05 size 1.7x0.6 furniture kitchen-sink in kitchen at 0.6,1.05 ``` *** ## 1. Your first floor plan A header, one room, a door, and a window: ``` floorplan "Studio" room main "Studio" at 0,0 size 4x3 door main south at 20% window main north at 50% ``` Three rules cover most usage: 1. Start with `floorplan`, an optional quoted title, and `unit m` (default) or `unit ft`. **All numbers are in this unit.** 2. Rooms are rectangles: `room id "Label" at x,y size WxH`. The label and computed area render centered in the room. 3. Openings hang on walls: a wall reference (`main south`) positions along that wall at a percentage; `between A B` finds the shared wall automatically. *** ## 2. Rooms and placement Place the first room at `0,0` and chain the rest relatively — adjacent rooms share an edge exactly, and their walls merge into a single band: ``` room living "Living Room" at 0,0 size 5.2x4.2 room kitchen "Kitchen" right-of living size 3.0x4.2 room hall "Hallway" below living size 2.0x2.6 room bed1 "Bedroom 1" right-of hall size 3.2x2.6 ``` * `right-of` / `left-of` / `above` / `below` snap to the reference room's edge. * `align start|center|end` aligns the cross axis (default `start` = top/left edges flush); `offset n` shifts it. * `fill #e0f2fe` tints the floor; `nolabel` suppresses the name + area label (single-space plans like classrooms). * Coordinates are y-down: `at 0,0` is the top-left corner. **L/T/U-shaped rooms** use `extend` — declare the main rectangle, then grow it with edge-sharing rectangles. The walls merge along the seam, the area is summed into one number (exactly how professionals measure L-rooms), and the label centers on the largest part: ``` room living "Living Room" at 0,0 size 5x4 extend living at 5,2 size 2x2 # L-shape: notch at top-right ``` An extension that doesn't touch the room, or overlaps it, is rejected with a quantified error. `north` (optionally `north 30` for rotated plans) adds the compass at the top right. *** ## 3. Doors, windows, openings ``` door hall west at 50% width 1.0 swing in # exterior door on a wall door between hall bed1 at 50% hinge right # interior door on the shared wall door between bed1 bath at 30% type sliding # sliding door — no arc opening between living kitchen at 35% width 1.2 # archway, no leaf window living north at 30% width 1.8 ``` * `between A B` resolves the shared wall segment and positions at the percentage **along the overlap** — no coordinates needed. Non-adjacent rooms are rejected with the measured gap. * Doors default to 0.9 m wide on exterior walls, 0.8 m on `between` walls; windows default to 1.2 m. * `hinge left|right` picks the jamb; `swing in|out` flips the quarter-arc (default swings into the owning room — the first room named). * Door `type single|double|sliding|pocket|bifold`: double draws two mirrored arcs; sliding/pocket draw offset leaf lines without an arc; bifold draws the two closet-door tent peaks. * Window `type fixed|sliding|casement|bay`: sliding = two offset panels, casement adds the outward swing arc, bay projects a splayed trapezoid outside the wall. * Openings clamp to fit their wall segment (with a warning) rather than overflowing. *** ## 4. Furniture Furniture is placed **relative to its room's interior top-left corner**, with optional `size`, `rotate`, and a label: ``` furniture sofa in living at 0.25,2.9 furniture desk "Teacher" in class at 2,1.5 size 5x2.5 rotate 20 furniture counter "Cubbies" in class at 6,24.4 size 10x1.2 ``` The catalog spans residential, commercial, and site work (sizes default to industry-standard footprints): | Cluster | Types | | ------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | Residential | `bed-double` `bed-single` `bed-queen` `bed-king` `bunk-bed` `crib` `sofa` `loveseat` `sectional` `armchair` `ottoman` `coffee-table` `side-table` `tv` `tv-stand` `fireplace` `floor-lamp` `rug` `wardrobe` `dresser` `nightstand` `bookshelf` `plant` `piano` `piano-upright` `pool-table` `ceiling-fan` `dining-table` | | Kitchen / bath | `counter` `wall-cabinet` `kitchen-sink` `stove` `range-hood` `fridge` `dishwasher` `island` `bar-stool` `toilet` `sink` `vanity` `bidet` `urinal` `bathtub` `shower` `washer` `dryer` | | Classroom / office | `desk-chair` `desk` `desk-l` `chair` `whiteboard` `smartboard` `bookcase` `cubbies` `filing-cabinet` `lockers` `kidney-table` `round-table-4/6/8/10` `conference-table` | | Event / banquet | `banquet-table` `head-table` `stage` `dance-floor` `bar` `dj-booth` `cocktail-table` `podium` `row-chairs` | | Retail / warehouse | `shelving` `checkout` `clothing-rack` `fitting-room` `pallet-rack` `loading-dock` `forklift` | | Salon / gym | `salon-chair` `shampoo-bowl` `manicure-table` `treadmill` `weight-bench` `power-rack` `yoga-mat` | | Restaurant / commercial kitchen | `booth` `prep-table` `range` `walk-in` `commercial-sink` `fryer` | | Stairs / structural | `stairs` `stairs-l` `stairs-u` `spiral-stairs` `elevator` `column` | | Site / outdoor | `tree` `car` | **Auto-seating** is built in: `round-table-8` draws 8 chairs on its circumference (60″ top; `round-table-10` uses 72″), `dining-table` / `banquet-table` / `conference-table` seat both long edges at one chair per 0.65 m, `head-table` seats one side facing the room, `manicure-table` seats a client and technician chair, and `row-chairs` places a theater strip at 0.55 m pitch. `rug`, `dance-floor`, `yoga-mat`, `counter`, `island`, `wall-cabinet`, `range-hood`, and `ceiling-fan` are underlays/overheads — other furniture can overlap them without a collision warning. **Seating charts** — name the occupants of any auto-seating table with a `seats` clause, and the engine writes each name onto its chair (in placement order: round tables clockwise from the top; rectangular tables fill the top edge left-to-right, then the bottom edge). This turns a venue floor plan into the seating chart guests actually read: ``` furniture round-table-8 "Table 3" in hall at 11,4 seats "Alice" "Bob" "Carol" "Dave" furniture head-table "Head Table" in hall at 5,0.6 size 6x0.9 seats "Bride" "Groom" ``` Extra chairs without a name stay empty; extra names past the chair count are ignored. CJK-quoted names (`seats "张伟" "李娜"`) work like every other label. Names read horizontally, so keep the table unrotated for a clean chart. `tree` and `car` are sized for the outdoors (canopy disc, parking-stall footprint), so a **site plan** is just zones tiled as adjacent rooms — front yard, house footprint, driveway, back yard — with trees and parked cars placed on top. **Stairs** follow the drafting conventions: tread lines at 0.28 m (11″), a direction arrow starting at the lowest tread labeled `UP` (give the item a `"DN"` label for a descending run), and the 45° zigzag break line at the imaginary 4-ft cut plane, with dashed treads beyond. `stairs` is a straight run (orient with `size`/`rotate`), `stairs-l` turns 90° over a landing, `stairs-u` switches back 180°, `spiral-stairs` is a circle with radial treads and a center pole. *** ## 5. Arrays — grid, row, arc Repeated furniture is one statement, not thirty: ``` grid desk-chair in class rows 5 cols 6 count 27 area 5,8 25,24 itemsize 2x2.5 row round-table-8 in hall cols 3 area 8.8,13.4 15.2,13.4 itemsize 2.3x2.3 arc chair in hall count 13 center 12,8 radius 5 from 200 to 340 ``` * `area x1,y1 x2,y2` gives the first and last item **centers**; items spread evenly between them. * `count` truncates **row-major** — 27 desks in a 5×6 grid drops the last row's tail, exactly like a real classroom. * `arc` places items on a circular arc facing the center — semicircle classrooms, ceremony seating. *** ## 6. Units, areas, dimension lines * `unit ft` makes every number feet; dimension lines format as `32'` / `15'1"` and areas as `sq ft`. Internally everything is metric (1 ft = 0.3048 m). * Room areas are **computed by the engine** from the declared geometry, never typed by hand. * Dimension lines render outside the plan with architectural slash ticks: overall width + height always, plus per-room segments along the top and left exteriors. *** ## 7. Validation The engine validates what LLMs (and humans) actually get wrong, with errors that name the offending elements and a fix direction: **Errors** (block rendering, shown in an error panel): * Room overlap — `rooms "bed1" and "bath" overlap by 0.40×2.60 m — move "bath" right-of "bed1" or shrink size` * Door between non-adjacent rooms — `door between "kitchen" and "bed2": rooms share no wall (gap 2 m on x-axis)` * Furniture outside its room — `furniture sofa #1 extends 1.7 m outside room "c" — move it or shrink size` **Warnings** (render anyway, listed under the plan): * Furniture collision — bounding boxes including **chair-ring envelopes**, so two banquet rounds whose chairs touch get flagged even when the table tops don't. * Opening clamped to fit its wall segment. *** ## 8. Grammar (EBNF) ```text plan ::= "floorplan" string? ("unit" ("m"|"ft"))? NL statement* statement ::= room | extend | north | door | window | opening | furniture | array room ::= "room" id string? placement "size" dims ("fill" color)? ("nolabel")? extend ::= "extend" id placement "size" dims north ::= "north" num? placement ::= "at" coord | ("right-of"|"left-of"|"above"|"below") id ("offset" num)? ("align" ("start"|"center"|"end"))? door ::= "door" (wallref | "between" id id) "at" pct ("width" num)? ("hinge" ("left"|"right"))? ("swing" ("in"|"out"))? ("type" ("single"|"double"|"sliding"|"pocket"|"bifold"))? window ::= "window" wallref "at" pct ("width" num)? ("type" ("fixed"|"sliding"|"casement"|"bay"))? opening ::= "opening" (wallref | "between" id id) "at" pct ("width" num)? furniture ::= "furniture" type ("in" id) "at" coord ("size" dims)? ("rotate" num)? string? ("seats" string+)? array ::= ("grid"|"row"|"arc") type "in" id ("rows" int)? ("cols" int)? ("count" int)? ("area" coord coord)? ("itemsize" dims)? ("rotate" num)? ("center" coord)? ("radius" num)? ("from" num "to" num)? wallref ::= id ("north"|"south"|"east"|"west") coord ::= num "," num dims ::= num "x" num pct ::= num "%"? ``` Comments run from `#` to end of line. CJK quotes (`“”`) are accepted as ASCII quotes. *** ## Related examples * [Two-bedroom apartment](/examples#floorplan) — relative placement, 7 doors, full furnishing * [27-desk classroom](/examples#floorplan) — `grid … count` truncation, `unit ft` * [Wedding reception for 120](/examples#floorplan) — auto-seated banquet rounds, dance floor ## Interactive editing Furniture and electrical fixtures move independently in room coordinates. A simple room exposes east, south, and corner resize handles that rewrite native dimensions; the room body itself does not drag, keeping furniture selection unambiguous. Shared-wall openings remain topology-validated. --- # Flowchart Canonical URL: https://schematex.js.org/docs/flowchart Markdown URL: https://schematex.js.org/docs/flowchart.md ## About flowcharts A **flowchart** maps the steps of a process — decisions, operations, inputs, outputs, and the paths between them — using a standardized set of symbols connected by arrows. Engineers use them to specify algorithms; business analysts use them to document workflows; quality teams use them to trace failure modes. They are among the most universally understood technical diagrams across every industry. Schematex follows the **[ISO 5807:1985](https://www.iso.org/standard/11955.html)** symbol conventions for flowchart shapes and uses a **[Mermaid-compatible](https://mermaid.js.org/syntax/flowchart.html)** DSL so existing Mermaid flowcharts transfer directly. This page documents what the parser accepts today. ```schematex flowchart LR start([New order received]) start --> validate{Inventory available?} validate -->|Yes| reserve[Reserve items] validate -->|No| notify[Notify customer] notify --> done([End]) reserve --> payment{Payment authorized?} payment -->|Yes| ship[Ship order] payment -->|No| cancel[Cancel & release] ship --> confirm[Send confirmation email] confirm --> done cancel --> done ``` *** ## 1. Your first flowchart The smallest useful flowchart: a decision with two outcomes. ```schematex flowchart TD A([Start]) --> B{File exists?} B -->|Yes| C[Read file] B -->|No| D[Return error] C --> E([Done]) ``` Four rules cover 80% of usage: 1. Start with `flowchart` followed by a direction: `TD`, `LR`, `BT`, or `RL`. 2. Each node is `ID[Label]` — the shape brackets determine the node type (see §2). 3. Connect nodes with `-->`. Add a label between pipe characters: `-->|Yes|`. 4. Nodes are created automatically when first referenced in an edge — but explicit declarations let you set shapes and labels independently. > Comments start with `%%` on their own line. *** ## 2. Node shapes Each node shape is written as `ID<brackets>Label<brackets>`. The ID must start with a letter and may contain letters, digits, `_`, and `-`. | Syntax | Shape | Typical use | | -------------- | ------------------- | -------------------------- | | `A[Label]` | Rectangle | Process step, operation | | `A(Label)` | Rounded rectangle | Subprocess, soft step | | `A([Label])` | Stadium (pill) | Start / end terminal | | `A{Label}` | Diamond | Decision / condition | | `A{{Label}}` | Hexagon | Preparation, configuration | | `A[[Label]]` | Subroutine | Predefined process | | `A[(Label)]` | Cylinder | Database, storage | | `A((Label))` | Circle | Connector, junction | | `A(((Label)))` | Double circle | End state | | `A[/Label/]` | Parallelogram | Input / output | | `A[\Label\]` | Parallelogram (alt) | Manual operation | | `A[/Label\]` | Trapezoid | Manual input | | `A[\Label/]` | Trapezoid (alt) | Off-page connector | | `A>Label]` | Asymmetric | Tag, annotation | ```schematex flowchart TD t([Terminal / stadium]) r[Rectangle process] d{Diamond decision} p[/Parallelogram input/] db[(Cylinder database)] sub[[Subroutine]] t --> r --> d d -->|branch A| p d -->|branch B| db p --> sub db --> sub ``` *** ## 3. Edges An edge connects two nodes. The connector symbol determines the visual style and whether a label or arrowhead is present. ### 3.1 Edge types ```schematex flowchart TD A --> B C --- D E -.-> F G ==> H I <--> J K --x L M --o N ``` | Syntax | Style | Arrow | Typical use | | ---------- | ------ | --------- | ---------------------------- | | `A --> B` | Solid | Arrow | Normal flow | | `A --- B` | Solid | None | Association, undirected link | | `A -.-> B` | Dotted | Arrow | Optional / async path | | `A ==> B` | Thick | Arrow | Critical / primary path | | `A <--> B` | Solid | Both ends | Bidirectional flow | | `A --x B` | Solid | Cross | Blocked / rejected path | | `A --o B` | Solid | Circle | Aggregation / composition | ### 3.2 Edge labels Two syntaxes attach a label to an edge: **Pipe label** — placed between `|` characters directly after the arrow: ``` A -->|Yes| B A -.->|optional| B A ==>|critical| B ``` **Inline label** — text placed between the dashes, before the arrow character: ``` A -- success --> B A -- error --x C ``` Both produce identical results. Pipe label is more common when sharing a diagram with Mermaid tools. ```schematex flowchart TD req[Request received] req -->|valid| proc[Process] req -->|invalid| err[Return 400] proc -- success --> ok([Done]) proc -.->|timeout| retry[Retry queue] retry ==>|max retries| dead[(Dead letter)] ``` ### 3.3 Chains Connect three or more nodes in a single line: ``` A --> B --> C --> D ``` This is equivalent to three separate edge statements. ### 3.4 Fan-out with `&` Use `&` to include multiple nodes on either side of an arrow. The parser generates the full cross-product of edges: ``` A & B --> C %% A→C and B→C A --> B & C %% A→B and A→C A & B --> C & D %% four edges: A→C, A→D, B→C, B→D ``` ```schematex flowchart LR deploy[Deploy service] smoke[Smoke test] health[Health check] notify_slack[Slack alert] notify_email[Email alert] deploy --> smoke & health smoke & health -->|fail| notify_slack & notify_email ``` *** ## 4. Subgraphs A `subgraph` groups related nodes into a labeled cluster with a visible border. ``` subgraph "Title" A --> B end ``` Three subgraph header forms are accepted: | Form | ID | Label | | ------------------------- | -------------- | ---------- | | `subgraph "My Group"` | auto-generated | `My Group` | | `subgraph sg1 "My Group"` | `sg1` | `My Group` | | `subgraph sg1 [My Group]` | `sg1` | `My Group` | Subgraphs can have their own `direction` override: ``` subgraph sg1 "Frontend" direction LR ui[React App] --> api[API Client] end ``` ```schematex flowchart TB subgraph ingestion [Ingestion] raw[/Raw events/] --> parse[Parse & validate] parse --> enrich[Enrich] end subgraph storage [Storage] dw[(Data warehouse)] cache[(Redis cache)] end enrich --> dw enrich --> cache dw --> report[Generate report] ``` *** ## 5. Styling ### 5.1 Semantic classes Assign CSS class names to nodes for theme-level visual grouping. Classes are defined with `classDef` and applied with `class`. Mermaid inline class syntax is also accepted: `A[Start]:::critical`. ``` classDef danger fill:#f9c,stroke:#c00 classDef safe fill:#cfc,stroke:#090 class errorNode danger class successNode safe B[Review]:::danger ``` ### 5.2 Per-node style overrides ``` style nodeId fill:#f9f,stroke:#333,stroke-width:4px ``` Accepts standard CSS property names. Multiple properties are comma-separated. ### 5.3 Per-edge style overrides `linkStyle` targets edges by their **declaration index** (0-based, in the order they appear in the source). Multiple comma-separated indices apply the same props to several edges: ``` flowchart TD A --> B B ==> C B -.-> D C --> E D --> E linkStyle 1 stroke:#d32f2f,stroke-width:4px linkStyle 2,4 stroke:#f57c00,stroke-dasharray:5 5 ``` Use this to highlight a critical path or distinguish an alternate flow. ### 5.4 Inline label formatting Node labels accept three inline formatting tags: | Tag | Effect | | ----------------- | ---------- | | `<br/>` or `<br>` | Line break | | `<b>…</b>` | Bold | | `<i>…</i>` | Italic | ``` flowchart TD M1["0 \| 0<br/><b>START</b>"] M2["4 \| 4<br/><b>Phase 1</b><br/><i>est. 4h</i>"] M1 --> M2 ``` Tags can be nested and mixed mid-line (`Hello <b>world</b>!`). Edge labels are single-line and do not currently support these tags. *** ## 6. Labels & comments * **Direction:** `flowchart TD` — first token after `flowchart` or `graph`. `TD` and `TB` are equivalent. * **Title:** `flowchart LR "My diagram"` — optional quoted string after the direction. * **Edge labels:** pipe syntax `-->|label|` or inline `-- label -->`. * **Comments:** `%%` at the start of a line (after leading whitespace). ``` flowchart LR %% This is a comment — ignored by the parser A[Step 1] --> B[Step 2] %% inline %% is NOT supported — only line-start %% ``` *** ## 7. Reserved words & escaping **Reserved at line start:** `flowchart`, `graph` (header), `subgraph`, `end`, `direction`, `class`, `classDef`, `style`, `linkStyle`. **Reserved ID characters:** IDs match `[A-Za-z0-9_-]` starting with a letter. Do not use spaces or operator characters in node IDs. **Operator tokens to avoid inside IDs:** `-->`, `---`, `-.->`, `==>`, `<-->`, `--x`, `--o`, `|`, `&`. **Labels with special characters:** The label is everything inside the shape brackets. Special characters are supported inside labels as-is — brackets/braces that would be ambiguous are closed by the matching closing token. *** ## 8. Common mistakes | You wrote | Parser says | Fix | | ------------------------------------------ | ---------------------------------------------------------------------------- | --------------------------------------------------------------- | | `flowchart` with no direction | Direction defaults to `TB` | Add a direction: `flowchart TD` | | `A --> B` before declaring shapes | Works — nodes created as rectangles with the ID as label | Declare explicitly when you need a non-rect shape: `A([Start])` | | `A[Label with [brackets]]` | Inner `]` closes the shape early | Avoid nested brackets in labels | | `subgraph My Group` (unquoted, with space) | Parser takes `My` as subgraph id, `Group` as unknown token | Quote: `subgraph "My Group"` | | `%% comment` mid-line after code | Inline comments are not supported; `%%` must be at line start | Move comments to their own line | | `A --> B --> C` mixed with `A --> B` | Chains are additive — duplicate edges may appear | Use chains OR separate lines, not both for the same pair | | `direction LR` outside a subgraph | Silently ignored — `direction` override only applies inside `subgraph … end` | Set direction on the `flowchart` header line | *** ## 9. Grammar (EBNF) ```text document = header (blank | comment | subgraph-block | direction-stmt | class-stmt | classdef-stmt | style-stmt | linkstyle-stmt | chain-stmt)* header = ("flowchart" | "graph") ( WS direction )? ( WS title )? NEWLINE direction = "TD" | "TB" | "BT" | "LR" | "RL" title = '"' any-char-but-quote* '"' | bare-word subgraph-block = "subgraph" ( WS subgraph-header )? NEWLINE ( WS? "direction" WS direction NEWLINE )? statement* "end" NEWLINE subgraph-header = id WS "[" label "]" | id WS quoted-string | quoted-string | id chain-stmt = node-group ( WS edge-op WS pipe-label? WS node-group )* NEWLINE node-group = node-ref ( WS "&" WS node-ref )* node-ref = id shape-suffix? shape-suffix = "[" label "]" %% rect | "(" label ")" %% round | "([" label "])" %% stadium | "{" label "}" %% diamond | "{{" label "}}" %% hexagon | "[[" label "]]" %% subroutine | "[(" label ")]" %% cylinder | "((" label "))" %% circle | "(((" label ")))" %% double-circle | "[/" label "/]" %% parallelogram | "[\" label "\]" %% parallelogram-alt | "[/" label "\]" %% trapezoid | "[\" label "/]" %% trapezoid-alt | ">" label "]" %% asymmetric edge-op = "-->" | "---" | "-."-".->" | "==>" | "<-->" | "--x" | "--o" | inline-label variants of the above pipe-label = "|" text "|" class-stmt = "class" WS id-list WS class-name NEWLINE | node-ref ":::" class-name NEWLINE # Mermaid inline form classdef-stmt = "classDef" WS class-name WS css-props NEWLINE style-stmt = "style" WS id WS css-props NEWLINE linkstyle-stmt = "linkStyle" WS index-list WS css-props NEWLINE index-list = NUMBER ( "," NUMBER )* | "default" comment = "%%" any NEWLINE id = [A-Za-z] [A-Za-z0-9_-]* ``` Authoritative source: `src/diagrams/flowchart/parser.ts`. If this diverges from the parser, the parser wins — please open an issue. *** ## 10. Standard compliance Schematex flowcharts follow **ISO 5807:1985** symbol conventions. The DSL syntax is intentionally compatible with **[Mermaid flowchart](https://mermaid.js.org/syntax/flowchart.html)** so diagrams created in one tool can be used in the other. What is implemented today: * ✅ All five directions: `TD`, `TB`, `BT`, `LR`, `RL` * ✅ 13 node shapes (rect through asymmetric) * ✅ 7 edge kinds: solid, no-arrow, dotted, thick, bidirectional, crossed, round-end * ✅ Pipe labels (`-->|text|`) and inline labels (`-- text -->`) * ✅ Edge chains (`A --> B --> C`) * ✅ Fan-out with `&` (cross-product edges) * ✅ Subgraphs with optional id, label, and per-subgraph direction * ✅ `classDef` / `class` semantic grouping * ✅ Mermaid inline node classes: `A[Label]:::className` * ✅ Per-node `style` CSS overrides * ✅ `linkStyle` rendering — `linkStyle 1,5,6 stroke:#f00,stroke-width:4px` applies CSS to the matching edges by declaration order * ✅ Inline `<b>` / `<i>` in node labels (combines with existing `<br/>` line breaks) * ⏳ Shape rendering for all 13 shapes — M1 fully renders rect/round/stadium/diamond/parallelogram; remaining shapes fall back to rect * ⏳ Swim-lane layout variant References: * ISO 5807:1985 — *Information processing — Documentation symbols and conventions for data, program and system flowcharts, program network charts and system resources charts* * Mermaid flowchart documentation — [https://mermaid.js.org/syntax/flowchart.html](https://mermaid.js.org/syntax/flowchart.html) *** ## 11. Roadmap **Planned — not yet parseable.** Do not use these in generated DSL today; the parser will reject or ignore them. * **Full shape rendering for all 13 shapes** — subroutine, cylinder, circle, double-circle, hexagon, asymmetric, trapezoid, and parallelogram-alt fall back to rect in M1. * **Swim-lane layout** — horizontal bands grouping nodes by actor or system (Mermaid `subgraph` with `direction TB` inside `LR` root). * **`%%{init: {…}}%%` init block** — Mermaid-compatible theme and layout initialization block. * **Markdown formatting in labels** — `**bold**` / `*italic*` markdown syntax (HTML `<b>`/`<i>` already work — see §5). Track in the GitHub issues if you need any of these sooner. *** ## Related examples Ready-to-use scenarios from the examples gallery: [Browse related examples](https://schematex.js.org/examples) ## Interactive editing Branches, shape labels, edge labels, and the title are exact-range text targets. Stable nodes move freely on both axes; connected edges preview during the gesture and reroute from their semantic endpoints after the drop. Both top-down and left-to-right layouts preserve these guarantees. --- # FMEA Worksheet > Failure Mode and Effects Analysis — the engine computes RPN and AIAG-VDA Action Priority from a nested item → mode → effect/cause tree. Canonical URL: https://schematex.js.org/docs/fmea Markdown URL: https://schematex.js.org/docs/fmea.md ## About FMEA **FMEA** (Failure Mode and Effects Analysis) is the most widely used proactive reliability method in manufacturing: for every part of a system, list how it can fail (**failure modes**), what each failure does (**effects**), why it happens (**causes**), and how it is caught (**controls**). Each row is rated on three 1–10 scales — **Severity**, **Occurrence**, **Detection** — and prioritised. Standardised by **AIAG-VDA FMEA Handbook (2019)** and **IEC 60812**. Schematex's edge is that the engine **computes the priority**, not just the table. It flattens the failure chain to one worksheet row per (item, mode, cause), computes **RPN = S × O × D**, and — crucially — the AIAG-VDA **Action Priority** (severity-primary banding), which deliberately fixes the bug a raw RPN sort introduces: an airbag failure at S10·O2·D3 is *High* even though its RPN (60) looks low. ```schematex fmea "Brake system DFMEA" type: design rank: ap flag: ap >= High item "Master cylinder" fn "Generate hydraulic pressure" mode "Internal seal leak" effect "Loss of braking" sev: 9 cause "Seal material degradation" occ: 3 controls prevention: "Material spec", detection: "Bench test" det: 4 cause "Contamination" occ: 2 controls detection: "Fluid analysis" det: 5 ``` *** ## 1. Your first worksheet Every document starts with `fmea`, an optional title, optional directives, then a **nested** failure chain. Structure comes from the keyword, not the indent depth: ``` fmea "Pump DFMEA" item "Impeller" fn "Move fluid" mode "No flow" effect "Process stops" sev: 8 cause "Impeller wear" occ: 4 controls detection: "Flow sensor" det: 5 ``` * **`item "name" fn "function"`** — a part and the function it delivers. `fn` is optional. * **`mode "failure mode"`** — how that function fails. An item may hold several. * **`effect "consequence" sev: 1..10`** — what the failure causes; carries its own **Severity**. A mode may have several (the engine uses the *worst* for every row of that mode). * **`cause "root cause" occ: 1..10`** — why it happens; carries **Occurrence**. * **`controls prevention: "…", detection: "…" det: 1..10`** — current controls and the **Detection** rating they earn. With no control, Detection defaults to **10** (undetectable). All three ratings are integers 1–10; anything outside that range is rejected. *** ## 2. Header directives ``` type: design # design (dfmea) | process (pfmea) | msr rank: ap # ap (default) | rpn — the sort/priority key flag: ap >= High # or `rpn > 100` — highlight rows over a threshold number: FMEA-2026-014 # free metadata: number/team/author/date/revision/dept/process/product ``` `rank` chooses how rows are prioritised; `flag` highlights the rows that breach a threshold (`ap >= High` / `ap == High`, or `rpn > 100` / `rpn >= 120`). *** ## 3. Computed RPN & Action Priority This is the differentiator. From the nested AST the engine: 1. **Flattens** to one row per (item, mode, cause); the mode's worst effect severity governs every row of that mode. 2. **RPN = S × O × D** (1–1000). 3. **Action Priority** by the AIAG-VDA band structure — **Severity is the primary axis**, Occurrence second, Detection third. S = 9–10 (safety/regulatory) is High for *every* O and D; mid-severity degrades High→Medium→Low as Occurrence falls. 4. **Sorts** by the chosen key. For `ap`: High > Medium > Low, tie-broken by Severity then RPN — *never* RPN alone, which would re-introduce the bug AP was designed to fix. 5. **Flags** rows over the threshold. Each row carries `data-rpn` and `data-ap` so the computed priority is inspectable. *** ## 4. After-action follow-up Record corrective actions and the revised ratings to show the before/after delta: ``` fmea "Brake DFMEA" item "MC" mode "Seal leak" effect "Loss of braking" sev: 9 cause "Degradation" occ: 3 det: 4 action "Seal leak" / "Degradation" do: "Upgrade seal to EPDM" owner: "J. Lee" target: 2026-Q3 revised sev: 9 occ: 1 det: 4 ``` `action "Mode" / "Cause"` targets a chain by its quoted mode and (optional) cause; `do:` is the recommendation; `revised sev/occ/det` recomputes the after-action RPN and AP so the engine reports the risk reduction. *** ## 5. Common mistakes ``` # WRONG — rating out of the 1..10 band effect "Leak" sev: 11 # WRONG — mode before any item mode "orphan mode" # WRONG — effect before any mode item "x" effect "e" sev: 3 ``` Ratings must be integers 1–10; the failure chain must nest `item → mode → effect/cause`; out-of-order keywords are rejected by name. Detection silently defaults to 10 if no control rates it — state `det:` (on the cause or its `controls`) when you have a detection control. *** ## 6. Standard compliance The failure chain and the Severity/Occurrence/Detection scales follow the AIAG-VDA FMEA Handbook (2019) and IEC 60812. The Action Priority implements the published band *structure* (severity-primary), not the paywalled verbatim 1000-cell table. RPN remains available via `rank: rpn` for legacy worksheets. ## 7. Roadmap Deferred: the exact AIAG-VDA AP/MSR decision tables, multi-effect-per-cause weighting, and structure/function-tree views. --- # Genogram Canonical URL: https://schematex.js.org/docs/genogram Markdown URL: https://schematex.js.org/docs/genogram.md ## About genograms A **genogram** is a family diagram that goes beyond the names-and-dates of a traditional family tree: it records the *emotional* and *medical* texture of a family across three or more generations. Therapists, social workers, and genetic counselors use them to surface patterns — cut-offs, over-involvement, recurring illnesses — that are hard to see in prose case notes. Schematex follows the **[McGoldrick, Gerson & Petry (2020) standard](https://www.guilford.com/books/Genograms/McGoldrick-Gerson-Petry/9781462542055)** used in clinical training, plus the widely adopted **[GenoPro](https://genopro.com/genogram/) 32-type emotional-relationship taxonomy**. For theory and history see [Wikipedia: Genogram](https://en.wikipedia.org/wiki/Genogram). This page documents what the parser accepts today. ```schematex genogram "The Potter Family" fleamont [male, 1909, 1979, deceased] euphemia [female, 1920, 1979, deceased] fleamont -- euphemia james [male, 1960, 1981, deceased] mr_evans [male, 1925, deceased] mrs_evans [female, 1928, deceased] mr_evans -- mrs_evans lily [female, 1960, 1981, deceased] petunia [female, 1958] james -- lily "m. 1978" harry [male, 1980, index] petunia -- vernon [male, 1951] dudley [male, 1980] harry -cutoff- petunia harry -hostile- dudley harry -close- lily ``` *** ## 1. Your first genogram The smallest clinically useful genogram: two parents, one child. ```schematex genogram alice [female, 1980] bob [male, 1978] alice -- bob "m. 2005" carol [female, 2008] ``` Four rules cover 80% of usage: 1. Start with the keyword `genogram`, optionally followed by a quoted title. 2. Declare each person on their own line: `id [attributes]`. Attributes go in square brackets, comma-separated. 3. Connect two people with a **couple operator** — `--` (marriage) here; see §4.1 for all six. A trailing quoted string is the relationship label. 4. **Indent under the couple line** to add their children. > Comments must be on their own line, starting with `#`, `//`, or Mermaid-style `%%`. Trailing inline comments (`bob [male, 1978] # ...`) are not supported and will break the parser — see §8. *** ## 2. Individuals An individual line is `id [attr1, attr2, …]`. Attributes are comma-separated, order-independent, all optional. **ID rules.** Must match `[a-zA-Z][a-zA-Z0-9_-]*`. IDs are case-insensitive internally but preserve their original casing as the display label (override with `label:"…"`). **Attributes accepted by the parser today:** | Attribute | Values | Effect | | ------------------ | -------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------- | | Sex | `male`, `female`, `unknown`, `other` | Shape: square, circle, diamond, diamond | | Status | `deceased`, `stillborn`, `miscarriage`, `abortion` | Visual modifier (X-out, scaled shape, etc.) | | Birth year | 4-digit number, e.g. `1980` | First 4-digit token = birth year | | Death year | 4-digit number after birth, e.g. `1980, 2055` | Second 4-digit token = death year | | `index` | flag | Concentric shape = identified patient | | `unknown-siblings` | flag | Diamond with `?` — placeholder for ≥1 siblings of unknown count | | `age:N` | e.g. `age:42` | Age shown inside shape | | `death:YYYY` | e.g. `death:2020` | Explicit death year | | `label:"…"` | e.g. `label:"Dr. Smith"` | Display label override | | `sibling-of:<id>` | e.g. `sibling-of:monica` | Pins same generation as the referenced sibling, draws a dashed bracket — for known relatives with unknown ancestry. | | `conditions:…` | see §5 | Medical/psychological conditions | | `key:value` | any custom | Stored as metadata | ```schematex genogram grandma [female, 1920, 2002, deceased] dad [male, 1950, age:74] me [male, 1985, index] daughter [female, 2012, label:"Em"] ``` *** ## 3. Shapes | Visual | Sex value | Meaning | | --------- | ------------------------------------------ | --------------------- | | ☐ Square | `male` | Male | | ○ Circle | `female` | Female | | ◇ Diamond | `unknown`, `other`, *or* attribute omitted | Unknown / unspecified | Status modifiers layer on top of the base shape: ```schematex genogram alive [male, 1960] passed [male, 1930, 2010, deceased] stillborn_child [unknown, stillborn] lost [unknown, miscarriage] ``` *** ## 4. Connections ### 4.1 Couple operators The parser tries these in order. The first one that matches wins — so `-x-` beats `--`. | Operator | Type | Example | Meaning | | -------- | ---------------- | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `-x-` | divorced | `a -x- b` | Divorce | | `-/-` | separated | `a -/- b` | Separation (married) | | `-//` | separated | `a -// b` | Separation (alias for `-/-`) | | `-o-` | engaged | `a -o- b` | Engagement | | `==` | consanguineous | `a == b` | Blood-related couple | | `--` | married | `a -- b` | Marriage | | `~` | cohabiting | `a ~ b` | Cohabiting / LTR (current) | | `~/~` | cohabiting-ended | `a ~/~ b` | Cohabitation has ended (never-married). Common in LATAM child-protection caseloads where biological parents lived together unmarried and the relationship has since broken — distinct from `-x-` divorce (no marriage) and `-/-` separation (still married). | A trailing quoted string becomes the relationship label (`a -- b "m. 2005"`). ### 4.2 Inline individual on the right side If the right-hand person hasn't been declared yet, you can declare them in-place: ```schematex genogram ann [female, 1970] ann -- ben [male, 1968] "m. 1995" kim [female, 1997] ``` ### 4.3 Children (indented under a couple) Indentation under a couple line = "these are the children of this couple." Any indent greater than the couple's indent works; by convention use 2 more spaces. Children are rendered in order of declaration (render also sorts by birth year when present). ```schematex genogram dad [male, 1950] mom [female, 1952] dad -- mom eldest [male, 1975] middle [female, 1978, adopted] twin_a [male, 1985, twin-identical] twin_b [male, 1985, twin-identical] ``` **Special child attributes:** | Attribute | Effect | | ------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `adopted` | Adoption line style | | `foster` | Foster relationship | | `guardian` | Guardianship by a non-parent relative (e.g. grandparent custody). Same primitive as `foster` — drawn as a secondary "current caregiver" link when biological parents are also declared. | | `twin-identical` | Grouped with other `twin-identical` children of the same couple | | `twin-fraternal` | Grouped with other `twin-fraternal` children | | `unknown-siblings` | Single diamond with `?` glyph — "≥1 siblings, count and identities unknown" (pedigree convention). | ### 4.3.1 Dual-parent families (foster, adoption, guardianship) Children placed with a non-biological caregiver while biological parents are still part of the case can be declared under both couples. **Declare the child with full attributes the first time** (under the biological couple), then **redeclare with just `[foster]` / `[adopted]` / `[guardian]`** under the current caregiver. The first declaration wins layout; the second is drawn as a secondary dotted "current caregiver" link that does not pull the child away from their biological position. ```schematex genogram "Foster placement" bp1 [male, label: "Bio dad"] bp2 [female, label: "Bio mom"] bp1 ~/~ bp2 child [male, 2018, index] fp1 [male, label: "Foster dad"] fp2 [female, label: "Foster mom"] fp1 -- fp2 own [male, 2010] child [foster] ``` The same primitive serves adoption (closed/open), foster placement, and guardianship by a relative — only the keyword differs. Re-declaration **merges** non-conflicting attributes (sex, birth year, label, `index` marker) into the original; declaring a conflicting `male` vs `female` raises a parse error rather than silently overwriting. ### 4.3.2 Unknown-count siblings When a case file mentions "the child has siblings" without naming them, use either the `?` shorthand on its own line, or `[unknown-siblings]` on a regular id. Both render as a single diamond with a "?" glyph — the standard pedigree marker for "one or more siblings, identities unknown." ```schematex genogram dad [male] mom [female] dad -- mom ? known_kid [male, 2018] ``` ### 4.3.3 Sibling-of (known relative, unknown ancestry) To express "X is a sibling of Y" without inventing parents, use the `sibling-of: <id>` property. The renderer pins X to Y's generation and draws a dashed bracket above the two — the standard pedigree convention for a known relative whose ancestry is not part of the case. ```schematex genogram monica [female, 1990] uncle [male, label: "Tío materno", sibling-of: monica] ``` ### 4.4 Emotional relationships Separate line, parser pattern `A -TYPE- B` (non-directional) or `A -TYPE-> B` (directional). An optional quoted label goes at the end. **Both individuals must already be declared** before the emotional line. ``` harry -cutoff- petunia # non-directional harry -hostile- dudley "since 1991" uncle -abuse-> nephew # directional (arrow) ``` All 32 types the parser accepts today: | Category | Types | | ---------------------------- | ----------------------------------------------------------------------- | | Positive / close | `harmony`, `close`, `bestfriends`, `love`, `inlove`, `friendship` | | Negative / hostile | `hostile`, `conflict`, `enmity`, `distant-hostile`, `cutoff` | | Ambivalent | `close-hostile`, `fused`, `fused-hostile` | | Distance | `distant`, `normal`, `nevermet` | | Abuse *(directional)* | `abuse`, `physical-abuse`, `emotional-abuse`, `sexual-abuse`, `neglect` | | Control *(directional)* | `manipulative`, `controlling`, `jealous` | | Special | `focused`, `focused-neg`, `distrust`, `admirer`, `limerence` | ```schematex genogram dad [male, 1950] son [male, 1985] daughter [female, 1988] dad -close- daughter dad -conflict- son son -cutoff- dad "since 2010" ``` *** ## 5. Medical conditions Syntax: `conditions: name(fill) [+ name(fill, #color)]…` ``` father [male, 1945, conditions: heart(full, #E53935)] mother [female, 1948, conditions: diabetes(half-left) + anxiety(half-right, #26A69A)] ``` * **`name`** — any identifier you choose (displayed in legend/tooltip). * **`fill`** — required, controls which region of the shape is colored. See table below. * **`color`** — optional hex. Default depends on the renderer theme. * Multiple conditions are joined with `+`. Each needs its own `(fill)`. **Fill positions:** | `fill` value | Region | | --------------------------------------------- | ---------------------------------------------- | | `full` | Entire shape | | `half-left` / `half-right` | Left / right half | | `half-top` / `half-bottom` | Top / bottom half | | `quad-tl` / `quad-tr` / `quad-bl` / `quad-br` | One quadrant | | `striped` | Diagonal stripe pattern (asymptomatic carrier) | | `dotted` | Dot pattern | ```schematex genogram dad [male, 1950, conditions: heart(full, #E53935)] mom [female, 1952, conditions: diabetes(half-left) + depression(half-right, #5C6BC0)] dad -- mom son [male, 1980, conditions: carrier(striped)] ``` *** ## 6. Labels & comments * **Title:** `genogram "Smith Family"` — first line only. * **Person label override:** `alice [female, label:"Dr. Alice Smith"]`. * **Relationship label:** trailing quoted string on a couple or emotional line — `alice -- bob "m. 2005"`. * **Comments:** `#`, `//`, or `%%` at the start of a line (after leading whitespace). Inline comments are **not** supported. ``` genogram "Smith Family" # this line is a comment — fine %% Mermaid-style comment — also fine alice [female, 1980] # ← THIS trailing comment breaks the parser ``` *** ## 7. Reserved words & escaping **Reserved at line start:** `genogram` (header keyword). **Reserved operator tokens** inside a line — avoid using these sequences in IDs: `--`, `~`, `~/~`, `==`, `-x-`, `-/-`, `-//`, `-o-`, and any `-<type>-` / `-<type>->` matching an emotional-relationship type. **Reserved id `?`** — bare `?` on a child line auto-generates a synthetic placeholder with the `unknown-siblings` marker. Do not use `?` as a real id. **Strings with spaces** must be double-quoted: titles, labels, `label:"…"`. Single quotes and backticks are not recognized. *** ## 8. Common mistakes Real parser errors, what triggers them, and how to fix. | You wrote | Parser says | Fix | | ------------------------------------------------------------------------ | --------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------- | | `alex [nonbinary, 1995]` | `Unknown property 'nonbinary'` | Use `unknown` or `other` (nonbinary is §13 Roadmap) | | `alice [female, transgender]` | `Unknown property 'transgender'` | Not yet parseable (§13 Roadmap) | | `dad -- mom` ← followed by `child [male, 2010]` at the **same indent** | Child parsed as a new top-level individual, not as their child | Indent the child line deeper than the couple line (2 spaces is enough) | | `A -- B` where `A` was never declared | `Unknown individual 'A'` | Declare `A [sex, year]` on a line above | | `father -- mother "married"` on line 1 (no `genogram` header) | `Expected "genogram" header` | Start the file with `genogram` or `genogram "Title"` | | `conditions: diabetes + cancer` (no parens) | `Invalid condition format 'diabetes'` | Add fill: `conditions: diabetes(half-left) + cancer(half-right)` | | `[triplet-identical]` | `Unknown property 'triplet-identical'` | Triplets not yet parseable (§13 Roadmap) | | `dad -- mom # first marriage` | Trailing inline `#` comment is treated as part of the label / errors | Move the comment to its own line | | Same id declared twice with different sex (`x [male]` then `x [female]`) | `Conflicting sex for 'x': previously 'male', now 'female'` | Pick one or rename one of the ids | | `child [foster]` redeclared but biological parents never declared | `child` becomes the foster couple's regular child (no secondary link drawn) | This is intentional — secondary links require an existing primary parent-child rel from a prior declaration | *** ## 9. Grammar (EBNF) ```text document = header (blank | comment | individual | couple-block | emotional)* header = "genogram" ( WS quoted-string )? NEWLINE quoted-string = '"' any-char-but-quote* '"' individual = INDENT id ( "[" attrs "]" )? NEWLINE couple-block = INDENT id WS coupleOp WS right-side ( WS quoted-string )? NEWLINE ( deeper-indent child )* child = INDENT id ( "[" attrs "]" )? NEWLINE | INDENT "?" NEWLINE // unknown-count sibling shorthand right-side = id ( "[" attrs "]" )? emotional = INDENT id WS "-" type "-" id ( WS quoted-string )? NEWLINE | INDENT id WS "-" type "->" id ( WS quoted-string )? NEWLINE coupleOp = "~/~" | "-//" | "-x-" | "-/-" | "-o-" | "==" | "--" | "~" type = "harmony" | "close" | "bestfriends" | "love" | "inlove" | "friendship" | "hostile" | "conflict" | "enmity" | "distant-hostile" | "cutoff" | "close-hostile" | "fused" | "fused-hostile" | "distant" | "normal" | "nevermet" | "abuse" | "physical-abuse" | "emotional-abuse" | "sexual-abuse" | "neglect" | "manipulative" | "controlling" | "jealous" | "focused" | "focused-neg" | "distrust" | "admirer" | "limerence" id = [a-zA-Z] [a-zA-Z0-9_-]* attrs = attr ("," attr)* attr = "male" | "female" | "unknown" | "other" | "deceased" | "stillborn" | "miscarriage" | "abortion" | "adopted" | "foster" | "guardian" | "twin-identical" | "twin-fraternal" | "index" | "unknown-siblings" | digit digit digit digit // year | "age" ":" digits | "death" ":" digit digit digit digit | "label" ":" quoted-string | "sibling-of" ":" id | "conditions" ":" condition ("+" condition)* | key ":" value // custom condition = name "(" fill ("," "#" hex)? ")" fill = "full" | "half-left" | "half-right" | "half-top" | "half-bottom" | "quad-tl" | "quad-tr" | "quad-bl" | "quad-br" | "striped" | "dotted" comment = INDENT ( "#" | "//" | "%%" ) any NEWLINE ``` Authoritative source: `src/diagrams/genogram/parser.ts`. If this diverges from the parser, the parser wins — please open an issue. *** ## 10. Standard compliance Schematex genograms follow **McGoldrick, Gerson & Petry (2020), *Genograms: Assessment and Treatment*, 4th ed.** for structural symbols, couple operators, and generation alignment. Emotional-relationship taxonomy follows **GenoPro's 32-type classification** (we do not implement GenoPro's 34-type set in full; `focused-admirer` and one duplicate are folded into `focused` and `admirer`). What is implemented today vs. the standard: * ✅ Core structural symbols (male/female/unknown, deceased, stillborn, miscarriage, abortion) * ✅ McGoldrick 6-operator couple system * ✅ GenoPro 32 emotional types, directional where clinically specified * ✅ Medical condition quadrant fill system (4-position + stripe/dot patterns) * ✅ Index person (concentric shape) * ✅ Twin groupings (identical / fraternal) * ⏳ Bennett 2022 gender inclusivity additions — see §13 * ⏳ Donor / surrogate / step-child connection types — see §13 References: * McGoldrick, M., Gerson, R., & Petry, S. (2020). *Genograms: Assessment and Treatment* (4th ed.). * Hardy, K.V. & Laszloffy, T.A. (1995). The cultural genogram. *J Marital Fam Ther*, 21(3), 227–237. * GenoPro symbol reference — [https://genopro.com/genogram/](https://genopro.com/genogram/) *** ## 11. Related examples Ready-to-use scenarios from the examples gallery: [Browse related examples](https://schematex.js.org/examples) *** ## 13. Roadmap **Planned — not yet parseable.** Do not use these in generated DSL today; the parser will reject them. * **Bennett 2022 gender inclusivity** — `nonbinary`, `intersex` as sex values; `transgender` as a marker. Currently typed in `src/core/types.ts` but the parser's `VALID_SEX` set does not yet include them. * **Triplets and higher-order births** — `triplet-identical`, `triplet-fraternal`. * **Modern family structures** — `surrogate`, `donor`, `step` as child-type attributes. * **Category shorthand for conditions** — `conditions: cardiovascular + depression` without `(fill)`, auto-assigning quadrants and default colors. * **Domestic partnership operator** — `~dp~` / explicit DP label. * **Heritage annotations** — cultural-genogram heritage identifiers on individuals. * **Household boundary boxes** — group nodes that share a roof (foster placement, multi-generational household). Track in the GitHub issues if you need any of these sooner. ## Interactive editing The title and explicit person labels are editable. Individuals move horizontally within a generation; generation depth remains semantic while structural and emotional relationship lines follow live. --- # Getting started > Install Schematex and render your first diagram in under a minute. Canonical URL: https://schematex.js.org/docs/getting-started Markdown URL: https://schematex.js.org/docs/getting-started.md ## Install ```bash npm install schematex ``` ## Render ```ts import { render } from 'schematex'; const svg = render(` genogram "Smiths" john [male, 1950] mary [female, 1952] john -- mary alice [female, 1975, index] `); ``` The diagram type is inferred from the first keyword. The output is a plain SVG string — safe to inject via `dangerouslySetInnerHTML`, `innerHTML`, or write to disk. ## Live previews `render()` is strict: invalid DSL throws. For an AI canvas or editor preview, use the preview API so a parser failure stays visible instead of leaving an empty surface. ```ts import { renderPreview, renderResult } from 'schematex'; const svg = renderPreview(dsl); const state = renderResult(dsl); preview.innerHTML = state.svg; if (!state.ok) console.error(state.diagnostics); ``` `renderPreview()` always returns SVG. A failed strict render becomes a visible diagnostic SVG. Use `renderResult()` when you also need `ok`, `status`, and diagnostics for retry or repair logic. ### Optional frontmatter Mermaid-style YAML frontmatter is accepted before the DSL body. A `title:` value is merged into the diagram title when the header line does not already provide one. ```txt --- title: Smith Family --- genogram john [male, 1950] ``` Leading blank lines and pure comment lines are skipped before diagram detection for parsers that support comments. For the Mermaid-compatible parsers touched in v0.4.3, `%%` is accepted as a full-line comment marker. ## Tree-shakable imports Import only what you need: ```ts import { render } from 'schematex/genogram'; import { render } from 'schematex/ladder'; import { render } from 'schematex/sld'; ``` Each sub-module ships its own parser / layout / renderer and is fully independent. ## Frameworks * **React** — use `<SchematexDiagram />` from `schematex/react`, or inject a strict/preview SVG string yourself. The component shows a diagnostic fallback SVG when its DSL cannot render. * **SSR** — Schematex is pure string output, no DOM. Works in Node, edge runtimes, Cloudflare Workers, and the browser. * **Markdown** — a `remark-schematex` plugin is planned. ```tsx import { SchematexDiagram } from 'schematex/react'; <SchematexDiagram dsl={dsl} theme="monochrome" />; ``` ## Use with AI Schematex was designed for LLMs. The hosted MCP server lets Claude generate validated, standards-compliant DSL on its own — no plumbing required. [Connect to the hosted Schematex MCP](https://schematex.js.org/mcp) → Building your own AI feature? See **[Use Schematex with AI](/docs/ai-integration)** for the Vercel AI SDK adapter, eight generation/editing tools, and the recommended self-correcting agent loop. ## What's next? Pick your diagram type from the sidebar and read its syntax guide. --- # Git Graph > Mermaid-compatible commit-history graphs — branches, merges, tags, cherry-picks — rendered from a replayed sequence of git operations. Canonical URL: https://schematex.js.org/docs/gitgraph Markdown URL: https://schematex.js.org/docs/gitgraph.md ## About git graphs A **git graph** visualises a repository's commit history: a main line of commits, **branches** that fork off, **merges** that bring them back, plus **tags** and **cherry-picks**. It is the standard way to teach branching strategies (Git Flow, trunk-based) and to document a release history. Schematex implements the **Mermaid `gitGraph`** dialect, so existing Mermaid sources render unchanged. Schematex's edge here is fidelity and a clean, dependency-free layout: it **replays the operation sequence** to assign each commit to a lane, routes branch and merge connectors without crossings, and renders type-styled commit nodes (normal / highlight / reverse) — all from a tiny KB-scale bundle. ```schematex gitGraph commit id: "init" branch develop checkout develop commit commit tag: "v0.1" checkout main merge develop tag: "v1.0" ``` *** ## 1. Your first git graph Start with the `gitGraph` keyword (case-insensitive; a trailing colon is allowed), then one operation per line. The first commit lands on the main branch: ``` gitGraph commit branch develop checkout develop commit id: "feature work" checkout main merge develop tag: "v1.0" ``` Every operation runs against the **currently checked-out branch**. `commit` appends to it; `branch` creates a new line from the current commit; `checkout` (alias `switch`) moves the cursor; `merge` brings another branch into the current one. *** ## 2. Operations ``` commit # plain commit on the current branch commit id: "init" # give the commit an explicit id commit tag: "v0.1" # attach a release tag commit type: HIGHLIGHT # NORMAL (default) | HIGHLIGHT | REVERSE branch develop # fork a branch from the current commit branch hotfix order: 3 # pin its lane order checkout develop # switch the cursor (alias: switch) merge develop tag: "v1.0" # merge a branch into the current one cherry-pick id: "abc" # copy a commit onto the current branch ``` * **`commit`** takes optional `id:`, `tag:`, and `type:` (`NORMAL` / `HIGHLIGHT` / `REVERSE`). * **`branch NAME`** takes an optional `order:` to control lane placement. * **`checkout` / `switch`** are interchangeable. * **`cherry-pick id:`** copies a commit; an optional `parent:` disambiguates a merge commit. *** ## 3. Orientation The graph defaults to left-to-right. Set the direction inline on the header: ``` gitGraph TB: commit branch feature checkout feature commit checkout main merge feature ``` `LR` (default), `TB`, and `BT` are accepted. Config can also be supplied via a leading `%%{init: {'gitGraph': {...}}}%%` directive or a YAML frontmatter `config:` block for Mermaid compatibility (`mainBranchName`, `showCommitLabel`, `rotateCommitLabel`). *** ## 4. How the engine lays it out Mermaid-compatibility is the differentiator, but the layout is the work: * The operation list is **replayed in order** to build the commit DAG and assign each commit to its branch lane. * Branch lanes are ordered by appearance (overridable with `order:`); merge connectors are routed from the merged branch's tip to the new merge commit. * Commit nodes are styled by `type:` — a HIGHLIGHT commit is emphasised, a REVERSE commit marked — and tags render as flags. Cherry-picks draw a dashed copy edge to the source. Every commit carries `data-*` (branch, id, type) for downstream interaction. *** ## 5. Common mistakes ``` # WRONG — no gitGraph header commit commit # WRONG — unknown operation gitGraph rebase main # WRONG — unknown commit type gitGraph commit type: SQUASH ``` The document must start with `gitGraph`; only `commit` / `branch` / `checkout` / `switch` / `merge` / `cherry-pick` are valid operations; `type:` must be `NORMAL`, `HIGHLIGHT`, or `REVERSE`. `%%` starts a comment, matching Mermaid. *** ## 6. Standard compliance Syntax tracks the Mermaid `gitGraph` grammar — operation keywords, `id:`/`tag:`/`type:` options, `order:`, the `%%{init}%%` directive, and YAML frontmatter config — so Mermaid sources port directly while rendering through Schematex's zero-dependency engine. ## 7. Roadmap Deferred: parallel-commit compaction, custom commit themes via classDef, and per-branch colour overrides beyond lane order. --- # IDEF0 Function Model > Structured-analysis activity models — function boxes wired by ICOM arrows (Input/Control/Output/Mechanism), correct by construction. Canonical URL: https://schematex.js.org/docs/idef0 Markdown URL: https://schematex.js.org/docs/idef0.md ## About IDEF0 **IDEF0** (Integration Definition for Function Modeling) is the US-federal standard for **functional decomposition** — modelling *what* a system does. Each activity is a box; arrows connect on four fixed sides by role: **I**nput (left), **C**ontrol (top), **O**utput (right), **M**echanism (bottom) — the **ICOM** convention. Standardised as **[FIPS PUB 183](https://www.nist.gov/publications/integration-definition-function-modeling-idef0)** (1993). Schematex's edge is that the model is **correct by construction**. The arrow's role *is* the box edge it attaches to, so the engine enforces ICOM placement, resolves every reference, assigns decomposition numbers, codes boundary arrows (I1/C1/O1/M1…), and applies the FIPS 3-to-6-box guideline — rejecting a model that violates the standard rather than letting you draw an arrow on the wrong side. ```schematex idef0 "Manufacture product" node A0 function A1 "Plan production" function A2 "Make parts" function A3 "Assemble product" input A1 "Sales orders" control A1 "Production schedule" A1 -> A2 "Work plan" input A2 "Raw material" mechanism A2 "CNC machines" A2 -> A3 "Finished parts" output A3 "Product" mechanism A3 "Assembly line" ``` *** ## 1. Your first IDEF0 diagram Start with the `idef0` keyword, an optional title, an optional `node` (the diagram's node number), then **function boxes** and their **ICOM arrows**: ``` idef0 "Fill order" function A1 "Receive order" input A1 "Customer request" control A1 "Order policy" mechanism A1 "Order clerk" output A1 "Confirmed order" ``` `function ID "name"` declares a box (in declaration order). `node A0` sets the parent node number used to derive child node numbers (A0 → A1..An). FIPS guidance is 3–6 boxes per diagram; outside that range the engine warns. *** ## 2. ICOM boundary arrows Each keyword pins an arrow to a specific side of a box, and that *is* its role: ``` input A1 "Sales orders" # enters the LEFT edge control A1 "Production schedule" # enters the TOP edge (governs the activity) output A1 "Product" # leaves the RIGHT edge mechanism A1 "CNC machines" # enters the BOTTOM edge (the resource) ``` `input`, `control`, and `mechanism` route from the diagram frame **into** the box; `output` routes from the box **out** to the frame. The engine codes these boundary arrows down each edge (I1, I2 / C1 / O1 / M1). *** ## 3. Flow arrows between boxes A `->` arrow connects two boxes; by default it lands on the target's **input**, but you can name the target's ICOM side: ``` A1 -> A2 "Work plan" # box→box; defaults to A2's input A2 -> A3.control "Parts spec" # land on A3's control (top) edge input A2 "Raw material" (tunnel) # (tunnel) hides the arrow at this level ``` * `target.control` / `target.input` / `target.mechanism` picks the landing side. * A flow **cannot land on the target's `.output`** — an output leaves a box, it does not enter one. * `(tunnel)` marks a tunnelled arrow (suppressed on the parent/child diagram per FIPS). *** ## 4. Computed structural enforcement This is the differentiator — what makes the model correct where a drawing tool is not: 1. **ICOM placement enforcement** — the role is resolved against the box's geometry side; a malformed role, or a flow asked to *enter* a box via `.output`, is rejected. 2. **Reference resolution** — every box id named by an arrow must be declared. 3. **Decomposition numbering** — boxes get contiguous box numbers 1..n (lower-right corner) and node numbers (A0 → A1..An); explicit `#N` numbers are checked for contiguity, range, and duplicates. 4. **Boundary coding** — boundary arrows are coded I1/C1/O1/M1 down each edge. 5. **Box-count guideline** — fewer than 3 or more than 6 boxes raises a FIPS-183 warning. *** ## 5. Common mistakes ``` # WRONG — no idef0 header function A1 "x" # WRONG — a flow landing on the target's output A1 -> A2.output "bad" # WRONG — an unknown ICOM side word A1 -> A2.sideways "bad" ``` The document must start with `idef0`; arrows may target `.input` / `.control` / `.mechanism` only; every referenced box id must be declared. Because the keyword encodes the side, you cannot accidentally draw a control as an input — the standard is enforced, not suggested. *** ## 6. Standard compliance Notation, ICOM placement, decomposition numbering, boundary coding, tunnelled arrows, and the 3-to-6-box guideline follow FIPS PUB 183 (IDEF0). The `monochrome` theme reproduces the standard's black-and-white box-and-arrow look. ## 7. Roadmap Deferred: multi-page decomposition splicing (child diagrams), node-tree / FEO pages, and call arrows to referenced models. --- # Introduction > Every diagram a doctor, engineer, or lawyer would actually use. Free. Fully open source. Made for AI. Industry-standard diagrams — genogram to fault tree — from a text DSL. Canonical URL: https://schematex.js.org/docs Markdown URL: https://schematex.js.org/docs.md **Schematex** renders every diagram a doctor, engineer, or lawyer would actually use — clinical genograms, IEC 61131-3 ladder logic, NSGC pedigrees, IEEE 315 single-line diagrams, cap tables, and many more. A tiny text DSL in; standards-compliant SVG out. **Free. Fully open source. Made for AI.** AGPL-3.0, zero runtime dependencies, and a DSL designed around how LLMs actually write text — paste output from ChatGPT or Claude and get a professional diagram back on the first try. ## What can I draw? [Browse all supported diagram types](https://schematex.js.org/diagrams) ## Quick example ```schematex genogram "The Smiths" john [male, 1950] mary [female, 1952] john -- mary alice [female, 1975, index] bob [male, 1978] alice -close- mary ``` ## Use with AI Schematex ships a tool layer built for LLMs. Connect Claude.ai to the hosted MCP server in 10 seconds and let it generate validated diagrams in any conversation — no install, no setup. [Connect to the hosted Schematex MCP](https://schematex.js.org/mcp) Or build your own AI feature on top of the Vercel AI SDK. See **[Use Schematex with AI](/docs/ai-integration)**. ## Next steps * [Getting started](/docs/getting-started) — install, import, render * [Use Schematex with AI](/docs/ai-integration) — MCP + Vercel AI SDK + agent loop * [Playground](/playground) — try any diagram type live * [API reference](/docs/api) *** ## How it works Each diagram type follows the same pipeline: ``` Text DSL ──→ Parser ──→ AST ──→ Layout Engine ──→ LayoutResult ──→ SVG Renderer ──→ SVG string ``` Every diagram implements a `DiagramPlugin` with four methods: ```ts interface DiagramPlugin { detect(text: string): boolean; // auto-detects diagram type parse(text: string): DiagramAST; // text → typed AST layout(ast, config): LayoutResult; // AST → positioned nodes/edges render(layout, config): string; // LayoutResult → SVG string } ``` Layout algorithms are domain-specific by design — genogram uses a generation-based layered layout, ecomap uses radial/polar, logic gates use DAG topological sort, ladder logic uses a fixed power-rail layout. Generic layout engines (dagre, ELK) cannot produce standards-compliant output for these diagram types. *** ## Design principles 1. **Diagrams professionals actually use** — each diagram type implements a published domain specification: McGoldrick 2020 (genogram), Hartman 1978 (ecomap), Bennett 2022 (pedigree), Moreno 1934 (sociogram), IEEE Std 91 (logic gates), IEC 61131-3 (ladder logic), IEEE 315 (SLD), and more. 2. **Free & fully open source** — AGPL-3.0, zero runtime dependencies (no D3, no dagre, no parser generators), everything hand-written. Small bundle, no supply-chain risk. Commercial license available for closed-source use. 3. **Made for AI** — the DSL is designed around how LLMs actually write text (CJK quotes, nesting ambiguity, AI-readable errors). Paste output from ChatGPT or Claude and get a professional diagram back on the first try. 4. **Semantic SVG output** — every SVG includes `<title>` and `<desc>` for accessibility, CSS classes for theming, and `data-*` attributes for interactivity. *** ## Theming Three built-in presets: `default` (blue-gray), `monochrome` (black-and-white / print), `dark` (Schematex slate/blue dark palette). Override any token via CSS custom properties injected into each SVG's `<style>` block: ```css --schematex-stroke: #1a1a1a; --schematex-fill: #f5f5f5; ``` --- # Interactive editing > Embed the open-source React or Vanilla Schematex editor, understand its complete API, and persist safe DSL edits. Canonical URL: https://schematex.js.org/docs/interactive-editing Markdown URL: https://schematex.js.org/docs/interactive-editing.md Schematex 1.0 ships the editor in the same repository and npm package as the renderer. Every canvas operation produces ordinary DSL: there is no private document model and no second JSON format to synchronize. ## Install and runtime ```bash npm install schematex ``` `InteractiveSchematexDiagram` requires `schematex >= 1.0.0` and React 18 or newer. React and React DOM are optional peer dependencies because non-React users can continue using the core renderer without installing them. No Schematex stylesheet import is required. The component injects its minimal selection, cursor, and label-editor styles; use `className`, `canvasClassName`, and `labelEditorStyle` for application-specific layout and appearance. ## React quick start ```tsx 'use client'; import { useState } from 'react'; import { InteractiveSchematexDiagram } from 'schematex/react'; export function Editor({ initialDsl }: { initialDsl: string }) { const [dsl, setDsl] = useState(initialDsl); return ( <InteractiveSchematexDiagram value={dsl} onChange={(nextDsl, { reason, item }) => { setDsl(nextDsl); // Save nextDsl or push it into your own undo/collaboration model. console.log(reason, item?.semanticId); }} onSelect={(item) => console.log(item?.key)} ariaLabel="Architecture diagram editor" /> ); } ``` The component is controlled. It owns selection, pointer gestures, live connector previews, and the WYSIWYG label input. Your application owns source state, persistence, undo, permissions, and collaboration. In a Next.js App Router project, render it from a Client Component as shown above. Vite and other client-rendered React applications use the same component without the `'use client'` directive. You do not need `dynamic(..., { ssr: false })`. ## Complete React API ```ts interface InteractiveEditDetail { reason: 'label' | 'position'; item: SceneItem | null; } interface InteractiveSchematexDiagramProps { value: string; onChange: (value: string, detail: InteractiveEditDetail) => void; type?: DiagramType; theme?: string; fontFamily?: string; padding?: number; readOnly?: boolean; debounceMs?: number; className?: string; canvasClassName?: string; style?: React.CSSProperties; ariaLabel?: string; labelEditorClassName?: string; labelEditorStyle?: React.CSSProperties; selectedKey?: string | null; onSelect?: (item: SceneItem | null) => void; onPreviewChange?: (value: string | null, detail: InteractiveEditDetail) => void; onRender?: (result: SchematexRenderResult) => void; onError?: (error: Error) => void; } ``` | Prop | Default | Contract | | ---------------------- | ---------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `value` | required | Controlled Schematex DSL. Update it from `onChange`; the component never owns the canonical document. | | `onChange` | required | Called synchronously after a label commit or completed position gesture. The second argument identifies the reason and selected semantic item. | | `type` | auto-detect | Optional canonical diagram type. Pass it when the surrounding UI has already chosen a type. | | `theme` | renderer default | Theme name forwarded to `renderResult()`. | | `fontFamily` | renderer default | Font family forwarded to the diagram renderer. | | `padding` | engine default | Outer SVG padding in pixels. | | `readOnly` | `false` | Keeps the same controlled render surface but disables scene metadata, selection, label editing, and dragging. | | `debounceMs` | `0` | Delays expensive rendering when `value` changes from an external source editor. Canvas commits still use revision guards. | | `className` | none | Class on the outer focusable editor region. | | `canvasClassName` | none | Class on the inner canvas host `<div>` that directly contains the generated `<svg>`. | | `style` | none | Inline style on the outer editor region. | | `ariaLabel` | `Editable Schematex diagram` | Accessible name for the editor region. Use a document-specific label. | | `labelEditorClassName` | `sx-label-editor` | Class on the temporary WYSIWYG `<input>` rendered into `document.body`. | | `labelEditorStyle` | none | Inline overrides merged after the measured WYSIWYG input styles. | | `selectedKey` | uncontrolled | Optional controlled `SceneItem.key`. Use it to mirror a source-editor cursor or an external inspector onto the canvas selection. | | `onSelect` | no-op | Called with the selected `SceneItem`, or `null` when selection clears. | | `onPreviewChange` | no-op | Emits a revision-guarded transient source document while a position gesture is active. Render it in a source editor, but persist only `onChange`; `null` discards a cancelled preview. | | `onRender` | no-op | Called with every new `SchematexRenderResult`, including invalid preview results. | | `onError` | no-op | Called when parsing, layout, or rendering fails; the diagnostic fallback SVG remains visible. | `SceneItem` keys are stable edit targets for the current source revision. Do not persist a raw source range or assume that an item index remains stable after structural DSL edits. ## Sizing and styling `canvasClassName` names the SVG host, not the SVG itself. Target its direct SVG child when you want a responsive canvas: ```tsx <InteractiveSchematexDiagram value={dsl} onChange={setDsl} className="diagramEditor" canvasClassName="diagramCanvas" /> ``` ```css .diagramEditor { min-height: 28rem; border: 1px solid #e8eef6; overflow: auto; } .diagramCanvas { min-height: inherit; display: grid; place-items: center; } .diagramCanvas > svg { display: block; max-width: 100%; max-height: 100%; } ``` The WYSIWYG input is portalled into `document.body` so it stays aligned when the canvas scrolls or its container clips overflow. Press Enter or blur to commit; press Escape to cancel. ## Persistence, undo, and autosave Treat each `onChange` value as the next complete document. A minimal undo and autosave integration looks like this: ```tsx const [dsl, setDsl] = useState(initialDsl); const history = useRef<string[]>([initialDsl]); function commit(nextDsl: string) { history.current.push(nextDsl); setDsl(nextDsl); queueAutosave(nextDsl); } <InteractiveSchematexDiagram value={dsl} onChange={commit} />; ``` For collaboration, broadcast the returned DSL or translate its change into your existing document/CRDT layer. Apply permissions with `readOnly`; the component does not implement authentication or server-side authorization. ## Error and preview contract The editor uses `renderResult()` rather than throwing into the React tree. Invalid DSL therefore produces a visible diagnostic SVG and an `onError` callback. Use `onRender` when the application also needs `ok`, `status`, `diagnostics`, or the detected diagram type: ```tsx <InteractiveSchematexDiagram value={dsl} onChange={setDsl} onRender={(result) => setIsValid(result.ok)} onError={(error) => reportEditorError(error)} /> ``` Do not treat “an SVG string exists” as proof that the DSL is valid. Save, export, telemetry, and billing decisions should inspect the result status. ## Vanilla DOM ```ts import { renderResult } from 'schematex'; import { attachInteraction, sourceRevision } from 'schematex/interactive'; let source = initialDsl; let result = renderResult(source, { scene: true }); host.innerHTML = result.svg; if (result.ok) { attachInteraction(host.querySelector('svg')!, { getSource: () => source, getScene: () => ({ rev: sourceRevision(source), items: result.scene ?? [], }), onSourceChange: (nextSource) => { source = nextSource; rerender(); }, }); } ``` Use the React component unless you need to integrate gestures into an existing non-React canvas. `attachInteraction()` is deliberately low level; its caller must rerender after a committed edit and detach listeners before replacing the SVG. ## Capability discovery ```ts import { getInteractiveCapabilities, INTERACTIVE_DIAGRAM_COUNT, POSITION_EDITABLE_DIAGRAM_COUNT, } from 'schematex'; getInteractiveCapabilities('sequence'); // { // type: 'sequence', // text: ['title', 'labels'], // position: 'move-x', // reason: 'Horizontal lifeline order is editable, while the vertical axis …' // } console.log(INTERACTIVE_DIAGRAM_COUNT); // 20 console.log(POSITION_EDITABLE_DIAGRAM_COUNT); // 17 ``` `INTERACTIVE_DIAGRAM_COUNT` is `20`: only engines with parser-native source ranges are advertised as canvas-editable. Seventeen also expose a position model. The other 30 engines remain fully renderable and editable through the DSL source, but return empty `text` plus `position: "none"` and emit no guessed canvas handles. A value such as `free`, `move-x`, `cross-axis`, or `native-xy` is a semantic constraint, not a UI hint. `reason` explains the standard or layout rule behind that constraint and is the canonical copy for product UI, documentation, and AI integrations. For web agents and non-JavaScript tooling, the same registry is available as [machine-readable JSON](/api/interactive-capabilities). The [complete human capability matrix](https://github.com/SchemaTex/SchemaTex/blob/main/docs/system/INTERACTIVE-EDITING-CAPABILITIES.md) documents each diagram's axes, editable fields, routing behavior, and limits. ## How edits persist * Stable semantic objects persist presentation-only positions under `@overrides`, for example `pin R1 153.1,73.1`. * Domain geometry—dates, room sizes, floorplan furniture, site coordinates, breadboard holes, waveform boundaries—rewrites its native DSL token. * Authored labels replace only their exact source range. Computed labels and identifiers without a safe rename contract remain read-only. * Core records the source revision and exact expected text on every editable scene item. `setLabel` fails closed if either changed; callers must rerender instead of retrying an old target. * Connected lines preview during drag and are recomputed from semantic endpoints after drop. Engines that promise orthogonal routing remain horizontal/vertical after the rerender. The [interactive workspace](/playground) includes published examples for all 50 diagram families. Its capability bar is generated from the same registry, so parser-native canvas editing and source-only behavior cannot drift apart. ## AI-safe edits AI agents should never invent source offsets. The `schematex/ai` package and both MCP transports expose this revision-guarded flow: ```ts import { inspectDiagram, applyDiagramEdits } from 'schematex/ai'; const inspected = inspectDiagram('flowchart', dsl); if (inspected.ok) { const result = applyDiagramEdits('flowchart', dsl, inspected.revision, [ { target: inspected.items[0].key, op: 'setLabel', value: 'Approved' }, ]); } ``` The batch is atomic: a stale revision, unknown target, unsafe operation, or invalid final render returns the original DSL instead of a partial mutation. ## Read-only React renderer When the canvas must never modify DSL, use the smaller read-only component: ```tsx import { SchematexDiagram } from 'schematex/react'; <SchematexDiagram dsl={dsl} type="circuit" onError={(error) => console.error(error)} />; ``` Schematex is AGPL-3.0. Closed-source commercial products should review the [licensing notes](/docs/ai-integration#licensing-notes) before distribution. --- # Ladder logic Canonical URL: https://schematex.js.org/docs/ladder Markdown URL: https://schematex.js.org/docs/ladder.md ## About ladder logic diagrams **Ladder logic** is the graphical programming language used to describe control programs in Programmable Logic Controllers (PLCs) — the industrial computers that run factory automation, HVAC systems, conveyor lines, and process equipment worldwide. The name comes from the diagram's appearance: two vertical power rails (rails) connected by horizontal rungs, each rung expressing one condition-to-action rule. Electricians and controls engineers adopted it because it mirrors the relay-contact schematics they already knew. Schematex follows **[IEC 61131-3:2013](https://en.wikipedia.org/wiki/IEC_61131-3)**, the international standard for PLC programming languages, with Allen-Bradley (Rockwell) tag-address-name naming conventions common in North American industrial practice. This page documents what the parser accepts today. ```schematex ladder "Motor Start/Stop" rung 1 "Seal-in circuit": parallel: branch: XIC(START_PB, "IN 1.0", name="Start Button") branch: XIC(MOTOR_AUX, "BIT 3.0", name="Aux Contact") XIO(STOP_PB, "IN 1.1", name="Stop Button") OTE(MOTOR_CMD, "OUT 2.0", name="Motor Command") rung 2 "Mode select with Set/Reset": XIC(AUTO_HMIPB, "BIT 5.10", name="Auto Mode Button") XIO(SYS_FAULT, "BIT 3.0", name="System Fault") parallel: branch: OTL(SYS_AUTO, "BIT 3.1", name="System Auto Mode") branch: OTU(SYS_MANUAL, "BIT 3.2", name="System Manual Mode") rung 3 "Run timer": XIC(MOTOR_CMD) TON(RUN_TMR, PT=5000) rung 4 "Alarm on cycle complete": XIC(CYCLE_DONE, "BIT 4.0", name="Cycle Done") OTN(ALARM_OUT, "OUT 2.5", name="Alarm Output") ``` *** ## 1. Your first ladder diagram The smallest useful ladder program: one rung, two contacts, one coil. ```schematex ladder "First Rung" rung 1 "Start when button pressed, stop on fault": XIC(START_PB) XIO(FAULT) OTE(MOTOR_RUN) ``` Four rules cover 80% of usage: 1. Start with `ladder`, optionally followed by a quoted title. 2. Each **rung** begins with `rung N "optional comment":` on its own line. The trailing colon is optional. 3. Elements are listed one per line, indented under the rung — left to right means series (AND logic). 4. A `parallel:` / `branch:` block introduces OR logic. Every branch holds its own element list. > Comments may start with `#`, `//`, or Mermaid-style `%%` on their own line. *** ## 2. Contacts Contacts represent input conditions — they pass power when the associated bit matches the contact type. | Type | Name | Passes power when… | | ----- | ----------------- | ---------------------------------------------- | | `XIC` | Examine If Closed | Tag bit = 1 (normally open) | | `XIO` | Examine If Open | Tag bit = 0 (normally closed) | | `ONS` | One-Shot Rising | Tag transitions 0 → 1 (rising edge, one scan) | | `OSF` | One-Shot Falling | Tag transitions 1 → 0 (falling edge, one scan) | **Syntax:** ``` XIC(tag) XIC(tag, "address") XIC(tag, "address", name="Description") XIC(tag, address="address", name="Description") ``` * `tag` — required. The PLC tag name (displayed below the contact symbol). * `"address"` — optional positional second argument. The I/O address (e.g. `"IN 1.0"`, `"BIT 3.1"`). * `name="…"` — optional key-value. Human-readable description (displayed above the symbol). ```schematex ladder "Contact types" rung 1 "All four contact types": XIC(START_PB, "IN 1.0", name="Start Button") XIO(E_STOP, "IN 1.5", name="Emergency Stop NC") ONS(PULSE_IN, "BIT 5.0", name="One-Shot Rising") OSF(RESET_SIG, "BIT 5.1", name="One-Shot Falling") OTE(OUT_RLY, "OUT 2.0", name="Output Relay") ``` *** ## 3. Coils Coils represent output actions — they act on a tag bit when the rung has power flow. | Type | Name | Effect on tag bit | | ----- | --------------- | --------------------------------------------------------------- | | `OTE` | Output Energize | Sets bit = 1 while rung is true; clears to 0 when rung is false | | `OTL` | Output Latch | Sets bit = 1; **retains** even after rung goes false (latches) | | `OTU` | Output Unlatch | Clears bit = 0; retains even after rung goes false | | `OTN` | Output Negate | Sets bit = 0 while rung is true; sets to 1 when rung is false | | `RES` | Reset | Rockwell / Allen-Bradley counter or timer reset coil | **Syntax:** identical to contacts — `OTE(tag)`, `OTE(tag, "address")`, `OTE(tag, "address", name="…")`. `OTL` and `OTU` are used in pairs to build Set/Reset flip-flops. The last rung to write wins. ```schematex ladder "Set-Reset latch" rung 1 "Set on start": XIC(START_PB, "IN 1.0", name="Start") OTL(MOTOR_ON, "BIT 3.0", name="Motor Latch") rung 2 "Reset on stop or fault": parallel: branch: XIC(STOP_PB, "IN 1.1", name="Stop") branch: XIC(E_STOP, "IN 1.5", name="E-Stop") OTU(MOTOR_ON, "BIT 3.0", name="Motor Latch") ``` *** ## 4. Function blocks Function blocks perform timer, counter, math, and comparison operations. They appear inline in a rung and have keyword parameters after the mandatory tag argument. ### 4.1 Timers | Type | Name | Key parameters | | ------ | --------------- | --------------------------------- | | `TON` | Timer On-Delay | `PT=` preset time in milliseconds | | `TOFF` | Timer Off-Delay | `PT=` preset time in milliseconds | | `TP` | Timer Pulse | `PT=` preset time in milliseconds | ``` TON(timer_tag, PT=5000) ``` The timer tag stores elapsed time. The timer's `Q` bit (done output) is accessed by tag name in downstream contacts. ### 4.2 Counters | Type | Name | Key parameters | | ------ | ------------- | ---------------------------- | | `CTU` | Count Up | `PV=` preset value (integer) | | `CTD` | Count Down | `PV=` preset value | | `CTUD` | Count Up/Down | `PV=` preset value | ``` CTU(cycle_counter, PV=100) ``` ### 4.3 Math | Type | Operation | | ----- | ----------- | | `ADD` | Add | | `SUB` | Subtract | | `MUL` | Multiply | | `DIV` | Divide | | `MOV` | Move (copy) | ``` ADD(result_tag, IN1=setpoint, IN2=offset) MOV(dest_tag, IN1=source_tag) ``` ### 4.4 Comparisons | Type | Meaning | | ----- | --------------------- | | `EQU` | Equal | | `NEQ` | Not equal | | `GRT` | Greater than | | `LES` | Less than | | `GEQ` | Greater than or equal | | `LEQ` | Less than or equal | ``` EQU(compare_tag, IN1=speed_actual, IN2=speed_setpoint) ``` ```schematex ladder "Timer and counter" rung 1 "Start run timer": XIC(MOTOR_CMD, "BIT 3.0", name="Motor Running") TON(RUN_TIMER, PT=10000) rung 2 "Count completed cycles": XIC(CYCLE_SENSOR, "IN 2.0", name="Cycle Sensor") CTU(PART_COUNT, PV=500) rung 3 "Alarm when batch complete": XIC(PART_COUNT, name="Count Done") OTE(BATCH_ALARM, "OUT 3.0", name="Batch Complete Alarm") ``` *** ## 5. Parallel branches A `parallel:` block introduces OR logic — the rung has power if **any** branch conducts. Each branch is a `branch:` sub-block with its elements indented below it. ``` parallel: branch: XIC(LOCAL_START) branch: XIC(REMOTE_START) ``` Branches in a `parallel:` are evaluated simultaneously. The block closes when indentation returns to the level before `parallel:`. **Rules:** * `parallel:` must appear inside a rung. * `branch:` must appear inside a `parallel:` — using it alone throws `LadderParseError`. * Each branch holds one or more elements. * Elements after the `parallel:` block are series with it (AND logic). ```schematex ladder "Parallel OR logic" rung 1 "Start from either local or remote": parallel: branch: XIC(LOCAL_START, "IN 1.0", name="Local Start") branch: XIC(REMOTE_START, "BIT 5.2", name="Remote Start") XIO(STOP_ALL, "IN 1.5", name="Stop All") OTE(CONVEYOR, "OUT 2.0", name="Conveyor Run") ``` *** ## 6. Labels & comments * **Title:** `ladder "Motor Control"` — first line only, quoted string. * **Rung number:** required integer after `rung`. * **Rung comment:** optional quoted string after the rung number, before the optional colon: `rung 3 "Run indicator":` or `rung 3 "Run indicator"`. * **Tag:** first argument inside the parentheses — displayed below the symbol. * **Address:** second positional argument (quoted): `XIC(START_PB, "IN 1.0")`. * **Name:** `name="…"` keyword argument — human-readable description displayed above the symbol. * **Line comments:** `#`, `//`, or `%%` at the start of a line (after leading whitespace). The same markers also start trailing comments. *** ## 7. Reserved words & escaping **Reserved at line start (case-insensitive):** `ladder`, `rung`, `parallel:`, `branch:`. **Element names** are all uppercase ASCII: `XIC`, `XIO`, `ONS`, `OSF`, `OTE`, `OTL`, `OTU`, `OTN`, `TON`, `TOFF`, `TP`, `CTU`, `CTD`, `CTUD`, `ADD`, `SUB`, `MUL`, `DIV`, `MOV`, `EQU`, `NEQ`, `GRT`, `LES`, `GEQ`, `LEQ`. **Tag IDs** — must match `[A-Z][A-Z0-9_]*` (the parser matches `[A-Z][A-Z0-9_]*` for the element name prefix). Lowercase tags are accepted inside the parentheses. **Quoted strings** in address or name arguments must use double quotes `"…"`. *** ## 8. Common mistakes | You wrote | Parser says | Fix | | ------------------------------------------- | ------------------------------------------------------------------ | ------------------------------------------------- | | `rung 1` (no colon) | Parsed correctly | The trailing colon is optional | | `ONF(TAG)` | `LadderParseError: unknown element type "ONF"` | Falling-edge contact is `OSF`, not `ONF` | | `parallel:` without `branch:` | Empty parallel block — rung has no element | Add at least one `branch:` inside the `parallel:` | | `branch:` before `parallel:` | `LadderParseError: branch: without parallel:` | Always open `parallel:` first | | `OTE()` — no tag | `LadderParseError: element missing tag` | Tag is required: `OTE(MY_TAG)` | | `var StartBtn: bool` (variable declaration) | `LadderParseError: invalid element syntax` | No variable declarations — tags are used directly | | Empty rung (no elements after `rung N:`) | `LadderParseError: Rung N: empty rung` | Add at least one element to each rung | | `TON(T1, T#5s)` | `LadderParseError: invalid element syntax` (T# not a valid number) | Use milliseconds integer: `TON(T1, PT=5000)` | *** ## 9. Grammar (EBNF) ```text document = header NEWLINE rung+ header = "ladder" ( WS quoted-string )? NEWLINE quoted-string = '"' any-char-but-quote* '"' rung = "rung" WS integer ( WS quoted-string )? ":"? NEWLINE element+ element = contact-line | coil-line | fb-line | parallel-block contact-line = contact-type "(" tag ( "," arg )* ")" NEWLINE contact-type = "XIC" | "XIO" | "ONS" | "OSF" coil-line = coil-type "(" tag ( "," arg )* ")" NEWLINE coil-type = "OTE" | "OTL" | "OTU" | "OTN" | "RES" fb-line = fb-type "(" tag ( "," arg )* ")" NEWLINE fb-type = "TON" | "TOFF" | "TP" | "CTU" | "CTD" | "CTUD" | "ADD" | "SUB" | "MUL" | "DIV" | "MOV" | "EQU" | "NEQ" | "GRT" | "LES" | "GEQ" | "LEQ" arg = quoted-string // positional (address) | key "=" quoted-string // keyword (e.g. name="…") | key "=" number // keyword (e.g. PT=5000) parallel-block = INDENT≥2 "parallel:" NEWLINE ( INDENT branch-block )+ branch-block = "branch:" NEWLINE ( INDENT element )+ tag = [A-Za-z][A-Za-z0-9_]* key = [A-Za-z][A-Za-z0-9_]* integer = [0-9]+ number = [0-9]+ ( "." [0-9]+ )? comment = ( "#" | "//" | "%%" ) any NEWLINE ``` Authoritative source: `src/diagrams/ladder/parser.ts`. If this diverges from the parser, the parser wins — please open an issue. *** ## 10. Standard compliance Schematex ladder logic follows **IEC 61131-3:2013** Part 3 for the Ladder Diagram language and uses **Allen-Bradley (Rockwell) tag-address-name** conventions common in North American PLC practice. What is implemented today: * ✅ Four contact types: XIC (NO), XIO (NC), ONS (rising edge), OSF (falling edge) * ✅ Five coil types: OTE (output), OTL (latch/set), OTU (unlatch/reset), OTN (negate), RES (counter/timer reset) * ✅ Three timer function blocks: TON, TOFF, TP with millisecond `PT=` parameter * ✅ Three counter function blocks: CTU, CTD, CTUD with integer `PV=` parameter * ✅ Math function blocks: ADD, SUB, MUL, DIV, MOV * ✅ Comparison function blocks: EQU, NEQ, GRT, LES, GEQ, LEQ * ✅ Parallel / branch blocks for OR logic * ✅ Tag, address, and name annotations per Allen-Bradley Studio 5000 conventions * ⏳ Retentive timer (RTO) — in the standard; not yet in the parser * ⏳ Jump (JMP) / Label (LBL) instructions * ⏳ Master Control Reset (MCR) zone * ⏳ Immediate I/O instructions (IIN, IOT) * ⏳ Structured Text or Function Block Diagram elements embedded in rungs References: * IEC 61131-3:2013 — *Programmable controllers, Part 3: Programming languages* * NEMA ICS 1-2009 — *General Standards for Industrial Control and Systems* * Rockwell Automation Studio 5000 Logix Designer — [Ladder Diagram Programming Manual](https://literature.rockwellautomation.com/) *** ## 11. Related examples [Browse related examples](https://schematex.js.org/examples) *** ## 12. Roadmap **Planned — not yet parseable.** Do not use these in generated DSL today; the parser will reject or ignore them. * **Retentive Timer On (RTO)** — timer that holds elapsed time through power loss; requires a separate reset contact. * **Jump (JMP) / Label (LBL)** — branch to a labeled rung to skip logic conditionally; used in large programs for performance. * **Master Control Reset (MCR) zone** — a bracketed zone that de-energizes all non-retentive outputs when the MCR input is false. * **Structured text in rungs** — inline expression evaluation (e.g., `CALC(result = a * b + c)`). * **Immediate I/O (IIN / IOT)** — force I/O scan mid-program for time-critical control. Track in the GitHub issues if you need any of these sooner. --- # Logic gate diagram Canonical URL: https://schematex.js.org/docs/logic Markdown URL: https://schematex.js.org/docs/logic.md ## About logic gate diagrams A **logic gate diagram** shows how Boolean functions are implemented in hardware — inputs flow left through combinational gates and flip-flops to produce outputs on the right. Digital design engineers use them to document RTL intent, verify gate-level netlists, and teach Boolean algebra. Schematex derives its symbol set from **[IEEE Std 91-1984 / ANSI Y32.14](https://standards.ieee.org/ieee/91/749/)** (distinctive-shape ANSI symbols, the US default) and **[IEC 60617-12](https://webstore.iec.ch/publication/2765)** (uniform rectangle symbols, the international default), selectable per diagram. The DSL is functional: you declare signals and describe each gate's inputs by name. Layout and wiring are computed automatically from the dependency graph — no manual coordinates. ```schematex logic "1-bit Full Adder" style: ansi input A, B, Cin output Sum, Cout # XOR stage — sum bits s1 = XOR(A, B) Sum = XOR(s1, Cin) # AND/OR stage — carry c1 = AND(A, B) c2 = AND(s1, Cin) Cout = OR(c1, c2) ``` *** ## 1. Your first logic gate diagram The smallest useful diagram: two inputs, one gate, one output. ```schematex logic "NAND check" input A, B output F F = NAND(A, B) ``` Four rules cover 80% of usage: 1. Start with the keyword `logic`, optionally followed by a quoted title and `style: ansi` or `style: iec`. 2. Declare ports with `input` and `output` lines — comma-separated signal names. 3. Each gate is `id = GATE_TYPE(input1, input2, …)`. The `id` becomes a named signal wire. 4. An `output` name that matches a gate `id` is automatically wired; use `OUTPUT <- gate_id` when the names differ. > Comments must start with `#` or `--` on their own line (or after the last token on a gate line). *** ## 2. Gate types ### 2.1 Combinational gates | DSL keyword | Function | ANSI shape | IEC symbol | | ----------- | ---------- | ------------------- | ------------------------- | | `AND` | A · B | D-shaped body | Rectangle + `&` | | `OR` | A + B | Curved body | Rectangle + `≥1` | | `NOT` | Ā | Triangle + bubble | Rectangle + `1` + bubble | | `NAND` | ¬(A · B) | AND + bubble | Rectangle + `&` + bubble | | `NOR` | ¬(A + B) | OR + bubble | Rectangle + `≥1` + bubble | | `XOR` | A ⊕ B | OR + extra arc | Rectangle + `=1` | | `XNOR` | ¬(A ⊕ B) | XOR + bubble | Rectangle + `=1` + bubble | | `BUF` | A (buffer) | Triangle, no bubble | Rectangle + `1` | ```schematex logic "Gate gallery" style: ansi input A, B, C output Y_and, Y_or, Y_xor, Y_nand, Y_not Y_and = AND(A, B) Y_or = OR(A, B) Y_xor = XOR(A, B) Y_nand = NAND(A, B) Y_not = NOT(C) ``` ### 2.2 Special-output buffers | DSL keyword | Function | | -------------- | -------------------------------------------------------------- | | `TRISTATE_BUF` | Three-state buffer — Z output when enable is low | | `TRISTATE_INV` | Three-state inverting buffer | | `OPEN_DRAIN` | Open-drain / open-collector output (external pull-up required) | | `SCHMITT` | Schmitt trigger — hysteresis symbol inside body | ```schematex logic "Special buffers" style: ansi input A, EN output Y_tri, Y_od, Y_sch Y_tri = TRISTATE_BUF(A, EN) Y_od = OPEN_DRAIN(A) Y_sch = SCHMITT(A) ``` ### 2.3 Flip-flops and latches | DSL keyword | Type | Key pins | | ----------- | ------------------------------------ | ---------------- | | `DFF` | D flip-flop (edge-triggered) | D, CLK, Q, Q̄ | | `JKFF` | JK flip-flop | J, K, CLK, Q, Q̄ | | `SRFF` | SR flip-flop | S, R, CLK, Q, Q̄ | | `TFF` | T (toggle) flip-flop | T, CLK, Q, Q̄ | | `LATCH_SR` | SR latch (level-sensitive, no clock) | S, R, Q, Q̄ | | `LATCH_D` | D latch (transparent when enable=1) | D, EN, Q, Q̄ | ```schematex logic "Flip-flop gallery" style: ansi input D, J, K, CLK, EN output Q_dff, Q_jk, Q_latch Q_dff = DFF(D, CLK) Q_jk = JKFF(J, K, CLK) Q_latch = LATCH_D(D, EN) ``` ### 2.4 Complex combinational | DSL keyword | Function | | ----------- | ---------------- | | `MUX` | Multiplexer | | `DEMUX` | Demultiplexer | | `DECODER` | Binary decoder | | `ENCODER` | Priority encoder | ```schematex logic "Combinational MSI" style: ansi input A, B, S output Y_mux, Y_dec Y_mux = MUX(A, B, S) Y_dec = DECODER(A, B) ``` ### 2.5 Sequential complex | DSL keyword | Function | | ----------- | ----------------------------------------------------- | | `COUNTER` | Generic binary counter (`CTR` label, CLK/RESET/Q0–Q3) | | `SHIFT_REG` | Generic shift register (`SRG` label, CLK/SER/Q0–Q7) | ```schematex logic "Sequential MSI" style: ansi input DATA, CLK, RESET output Q_cnt, Q_sr Q_cnt = COUNTER(CLK, RESET) Q_sr = SHIFT_REG(DATA, CLK) ``` *** ## 3. Inputs and outputs ### 3.1 Declaring ports ``` input A, B, Cin # three input ports output Sum, Cout # two output ports ``` Each name in an `input` or `output` list becomes a named signal wire available throughout the diagram. ### 3.2 Active-low inputs Prefix a signal name with `~` in the input list to mark it as active-low. The renderer draws a bubble at the port symbol. ``` input ~nRESET, CLK, DATA ``` Active-low notation also works inside gate input lists: ``` g1 = AND(~nRESET, CLK) ``` ### 3.3 Wiring outputs to gates If the output ID matches a gate ID, the connection is implicit: ``` output Sum # Sum is also a gate id → auto-wired Sum = XOR(s1, Cin) ``` When the names differ, use the explicit assignment operator: ``` output F q1 = NOR(A, B) F <- q1 # F draws from q1's output ``` ```schematex logic "SR latch from NOR gates" input S, R output Q, Qn q_gate = NOR(R, Qn) qn_gate = NOR(S, Q) Q <- q_gate Qn <- qn_gate ``` *** ## 4. Symbol style The `style:` option on the header line selects the symbol standard. It applies to every gate in the diagram. | Value | Standard | Use when | | ---------------- | -------------------------------------------------- | -------------------------------- | | `ansi` (default) | IEEE Std 91 — distinctive curved shapes | US education, hardware docs | | `iec` | IEC 60617-12 — uniform rectangles + function label | International, European industry | ``` logic "ALU slice" style: iec ``` ```schematex logic "1-bit Full Adder" style: iec input A, B, Cin output Sum, Cout # XOR stage — sum bits s1 = XOR(A, B) Sum = XOR(s1, Cin) # AND/OR stage — carry c1 = AND(A, B) c2 = AND(s1, Cin) Cout = OR(c1, c2) ``` ```schematex logic "Gate gallery — IEC style" style: iec input A, B, C output Y_and, Y_or, Y_xor, Y_nand, Y_not Y_and = AND(A, B) Y_or = OR(A, B) Y_xor = XOR(A, B) Y_nand = NAND(A, B) Y_not = NOT(C) ``` *** ## 5. Module blocks Use `module` to group gates into a labeled sub-circuit box. Module blocks are useful for documenting hierarchical designs — each module renders as a named rectangle around its member gates. ``` logic "Hierarchical adder" input A, B, Cin output Sum, Cout module "Half Adder" { s1 = XOR(A, B) c1 = AND(A, B) } Sum = XOR(s1, Cin) Cout = OR(c1, AND(s1, Cin)) ``` Module syntax rules: * `module "Label" {` — opens a module (quoted label or bare identifier). The `{` must be on the same line. * `}` on its own line closes the most recently opened module. * Modules can be nested. *** ## 6. Labels & comments * **Diagram title:** `logic "Full Adder"` — first line only. * **Gate signal names:** the `id` in `id = GATE(…)` is both the gate name and the output wire name. * **Output labels:** `output Sum` — the output port label matches the signal name by default. * **Active-low marker:** `~` prefix on a port or gate input. * **Comments:** `#` or `--` at the start of a line, or after the last meaningful token on a line. *** ## 7. Reserved words & escaping **Reserved at line start:** `logic` (header), `input`, `output`, `module`, `}`. **Reserved operator tokens** — avoid these inside signal names: `=`, `(`, `)`, `,`, `<-`, `~`. **Signal name rules:** must match `[a-zA-Z_][a-zA-Z0-9_]*`. Lowercase and uppercase are both accepted; gate type keywords (`AND`, `OR`, etc.) are case-insensitive in the parser. *** ## 8. Common mistakes | You wrote | Parser says | Fix | | ----------------------------------------------- | ----------------------------------------------------- | -------------------------------------------------------- | | `f = and(A, B)` (lowercase gate) | Accepted — gate types are case-insensitive | Both `AND` and `and` work | | `output F` then `F <- q1` but `q1` not declared | `LogicParseError: Unknown signal "q1"` | Declare `q1` as a gate before referencing it | | `input A B C` (spaces, no commas) | Parser takes `A` only; `B` and `C` are ignored | Use commas: `input A, B, C` | | `F = BUFFER(A)` | `LogicParseError: Unknown gate type: BUFFER` | Use `BUF` | | `module FullAdder {` (no `{` brace) | Line does not match module pattern — skipped silently | The opening `{` is required on the same line as `module` | | `style: IEEE` on the header | Unknown style value — silently defaults to `ansi` | Use `style: ansi` or `style: iec` | *** ## 9. Grammar (EBNF) ```text document = header statement* header = "logic" ( WS quoted-string )? ( WS "style:" WS style )? NEWLINE style = "ansi" | "iec" quoted-string = '"' any-char-but-quote* '"' statement = blank | comment | input-decl | output-decl | gate-def | assign | module-block comment = ( "#" | "--" ) any NEWLINE input-decl = "input" WS port-list NEWLINE output-decl = "output" WS port-list NEWLINE port-list = port-id ( "," WS? port-id )* port-id = "~"? id gate-def = id WS "=" WS gate-type "(" input-list ")" NEWLINE input-list = ( "~"? id ) ( "," WS? ( "~"? id ) )* assign = id WS "<-" WS id NEWLINE module-block = module-open ( statement | module-block )* module-close module-open = "module" WS ( quoted-string | id ) WS? "{" NEWLINE module-close = "}" NEWLINE gate-type = "AND" | "OR" | "NOT" | "NAND" | "NOR" | "XOR" | "XNOR" | "BUF" | "TRISTATE_BUF" | "TRISTATE_INV" | "OPEN_DRAIN" | "SCHMITT" | "DFF" | "JKFF" | "SRFF" | "TFF" | "LATCH_SR" | "LATCH_D" | "MUX" | "DEMUX" | "DECODER" | "ENCODER" | "COUNTER" | "SHIFT_REG" // all case-insensitive id = [a-zA-Z_] [a-zA-Z0-9_]* ``` Authoritative source: `src/diagrams/logic/parser.ts`. If this diverges from the parser, the parser wins — please open an issue. *** ## 10. Standard compliance Schematex logic gate diagrams follow **IEEE Std 91-1984 / ANSI Y32.14** (distinctive-shape symbols) and **IEC 60617-12** (rectangular symbols with function qualifiers). What is implemented today: * ✅ All eight combinational gates: AND, OR, NOT, NAND, NOR, XOR, XNOR, BUF * ✅ Special-output buffers: TRISTATE\_BUF, TRISTATE\_INV, OPEN\_DRAIN, SCHMITT * ✅ Four edge-triggered flip-flops: DFF, JKFF, SRFF, TFF * ✅ Two latches: LATCH\_SR, LATCH\_D * ✅ Combinational MSI: MUX, DEMUX, DECODER, ENCODER * ✅ Sequential MSI: COUNTER, SHIFT\_REG * ✅ Active-low (`~`) notation on inputs and ports * ✅ ANSI and IEC symbol styles, selectable per diagram * ✅ Module grouping blocks * ✅ Automatic DAG layout (topological sort, left-to-right signal flow) * ⏳ Explicit fan-out wire routing (shared net with junction dot) * ⏳ Multi-bit bus notation (`/N` slash annotation on wire) * ⏳ Active-low clock inputs on flip-flops References: * IEEE Std 91-1984 / ANSI Y32.14: *IEEE Standard Graphic Symbols for Logic Functions* * IEEE Std 91a-1991: Supplement to IEEE Std 91 * IEC 60617-12: *Graphical symbols for diagrams — binary logic elements* *** ## 11. Related examples [Browse related examples](https://schematex.js.org/examples) *** ## 12. Roadmap **Planned — not yet parseable.** Do not use these in generated DSL today; the parser will reject or ignore them. * **Explicit fan-out / junction dot** — named wire shared by multiple gate inputs, rendered with a junction dot at the branch point. * **Multi-bit bus notation** — `bus N` annotation on a wire to denote an N-bit signal group. * **Active-low clock** — `~CLK` in a flip-flop input list rendering a bubble on the clock triangle. * **Feedback arc** — explicit wire from a gate output back to an earlier gate input, routed above the main signal path. * **Parameterized gate fan-in** — `AND(A, B, C, D)` rendering a 4-input AND gate directly. Track in the GitHub issues if you need any of these sooner. --- # Markov Chain > State-transition diagrams where the engine validates the stochastic matrix, classifies states, and computes the stationary or absorption answer. Canonical URL: https://schematex.js.org/docs/markov Markdown URL: https://schematex.js.org/docs/markov.md ## About Markov chains A **Markov chain** models a system that hops between **states** with fixed **transition probabilities**, where the next state depends only on the current one (the *memoryless* property). They underpin reliability modelling, queueing, PageRank, gambler's-ruin, and weather models. The transition probabilities out of each state must sum to 1 (a **row-stochastic** matrix). Schematex's edge is that the engine **does the linear algebra**, not just the circles-and-arrows. It validates the row-stochastic matrix, classifies states (transient / recurrent / absorbing) via SCC analysis, and computes the **stationary distribution** π or, for absorbing chains, the **absorption probabilities** and expected steps — all dependency-free, hand-written. ```schematex markov "Weather" Sunny -> Sunny : 0.9 Sunny -> Rainy : 0.1 Rainy -> Sunny : 0.5 Rainy -> Rainy : 0.5 ``` *** ## 1. Your first chain Start with the `markov` keyword (alias `markovchain`), an optional title, then **probability-weighted transitions**. States are auto-created from the first arc that mentions them: ``` markov "Weather" Sunny -> Sunny : 0.9 Sunny -> Rainy : 0.1 Rainy -> Sunny : 0.5 Rainy -> Rainy : 0.5 ``` A transition is `FROM -> TO : PROBABILITY`. A self-loop (`Sunny -> Sunny`) is the probability of staying put. Every probability must be in `[0, 1]`, and **the probabilities leaving each state must sum to 1**. *** ## 2. Declaring states You can declare states explicitly to fix label, order, or mark them absorbing: ``` markov "Gambler's ruin" state Broke "$0" absorbing state Rich "$4" absorbing state S1 state S2 Broke -> Broke : 1 Rich -> Rich : 1 S1 -> Broke : 0.5 S1 -> S2 : 0.5 S2 -> S1 : 0.5 S2 -> Rich : 0.5 ``` `state ID "label" absorbing` — the label is optional; `absorbing` asserts the state is a sink (a self-loop of probability 1). The engine cross-checks the assertion against the matrix. *** ## 3. Directives ``` layout: layered # layout mode normalize: true # scale each row to sum to 1 instead of hard-erroring analysis: classify, absorbing # what to compute: classify | absorbing | stationary ``` * **`normalize: true`** rescales each row to sum to 1 (handy for unnormalised weights like `A -> B : 1`, `A -> C : 1`). Without it, a row that does not sum to 1 is a hard error. * **`analysis:`** selects which computations to run and surface. *** ## 4. The computed answer This is the differentiator. Three hand-written, dependency-free linear-algebra passes: 1. **Matrix assembly + validation** — build P and enforce the row-sum policy (hard error, or rescale under `normalize`). 2. **State classification** — Tarjan SCC → communicating classes; a class is **recurrent** iff closed, **transient** otherwise; a closed singleton with a probability-1 self-loop is **absorbing**. 3. **The quantitative answer**: * **Stationary distribution** π (πP = π, Σπ = 1) by power iteration with an exact Gaussian-elimination fallback for periodic chains. * For **absorbing chains**: the fundamental matrix N = (I−Q)⁻¹, absorption probabilities B = N·R, and expected steps to absorption t = N·1. Transient states, recurrent classes, and absorbing states are rendered distinctly; computed values are carried in `data-*`. *** ## 5. Common mistakes ``` # WRONG — probability outside [0,1] A -> B : 1.5 # WRONG — transition with no probability A -> B # WRONG — rows that don't sum to 1 (without `normalize: true`) A -> B : 0.3 A -> C : 0.3 ``` Every transition needs a probability in `[0, 1]`; each state's outgoing probabilities must sum to 1, or you must set `normalize: true`. An empty chain (no transitions) is rejected. *** ## 6. Standard compliance The model is a discrete-time, time-homogeneous Markov chain with a row-stochastic transition matrix; classification (transient/recurrent/absorbing), stationary distributions, and absorbing-chain fundamental-matrix analysis follow standard texts (Kemeny & Snell, *Finite Markov Chains*). ## 7. Roadmap Deferred: continuous-time chains (rate matrices), hidden Markov models, mean-first-passage times, and periodicity reporting. --- # Matrix / Quadrant diagram Canonical URL: https://schematex.js.org/docs/matrix Markdown URL: https://schematex.js.org/docs/matrix.md ## About matrix diagrams A **matrix diagram** places items in a two-dimensional space defined by two intersecting axes — most commonly a 2×2 quadrant grid — so that position conveys meaning at a glance. Product managers use the Eisenhower matrix to separate urgent work from important work; strategy consultants use the BCG matrix to allocate portfolio investment; HR teams use the 9-box grid to map performance against potential. The visual convention dates to the Boston Consulting Group's 1970s portfolio work and has been extended by frameworks like Ansoff, Johari, and RICE. Schematex supports three matrix modes: **quadrant** (2×2 or 3×3 bubble plots with labeled axes), **heatmap** (N×M colored-cell grids), and **correlation** (N×M dot-intensity tables). Eight pre-built templates cover the most common frameworks out of the box, and all axis labels, quadrant names, and point properties are fully customizable. ```schematex matrix impact-effort "Q3 Planning" "Fix checkout timeout" at (0.15, 0.82) size: 4 highlight: true category: reliability "Redesign onboarding" at (0.72, 0.78) size: 5 category: growth "Add dark mode" at (0.78, 0.38) size: 3 category: polish "Write runbook" at (0.18, 0.32) size: 2 category: reliability "API rate limiting" at (0.55, 0.65) size: 4 category: reliability "Blog post series" at (0.35, 0.55) size: 2 category: growth ``` *** ## 1. Your first matrix The smallest useful matrix: a custom 2×2 with two labeled axes and three points. ```schematex matrix "Feature Prioritization" x-axis: Low Effort → High Effort y-axis: Low Value → High Value "Add search" at (0.3, 0.8) "Rebuild pipeline" at (0.85, 0.7) "Update footer" at (0.2, 0.2) ``` Four rules cover 80% of usage: 1. Start with the keyword `matrix`, optionally followed by a template name or a quoted title. 2. Set the axes with `x-axis:` and `y-axis:` — or use a built-in template and skip this step entirely. 3. Each point is `"Label" at (x, y)` where `x` and `y` are decimal fractions from 0.0 (low/left/bottom) to 1.0 (high/right/top). 4. Add optional properties — `size:`, `category:`, `color:`, `shape:`, `highlight:` — after the coordinates. > Comments must start with `#` anywhere on a line (outside quoted strings). *** ## 2. Built-in templates A template pre-configures axes, quadrant labels, and grid size. Just use the template name as the second token on the header line. | Template | Grid | Use case | | --------------- | ---- | --------------------------------------------------- | | `eisenhower` | 2×2 | Urgency / Importance task prioritization | | `impact-effort` | 2×2 | Feature prioritization by impact vs. effort | | `rice` | 2×2 | RICE scoring — Reach × Impact vs. Effort | | `bcg` | 2×2 | Portfolio — Market Share vs. Growth rate | | `ansoff` | 2×2 | Product/market growth strategy | | `johari` | 2×2 | Self-awareness — known-to-self vs. known-to-others | | `9-box` | 3×3 | HR talent review — Performance vs. Potential | | `risk-matrix` | 5×5 | Risk assessment — Likelihood vs. Severity (heatmap) | ```schematex matrix eisenhower "This Week" "Ship hotfix" at (0.1, 0.9) size: 5 highlight: true "Team 1:1s" at (0.1, 0.7) size: 3 "Write Q3 OKRs" at (0.8, 0.85) size: 4 "Inbox zero" at (0.1, 0.3) size: 2 "Refactor auth" at (0.75, 0.4) size: 3 ``` Axes and quadrant labels from a template can be overridden with explicit `x-axis:` / `y-axis:` / `quadrant` directives. *** ## 3. Axes Axis lines declare the semantic poles of each dimension. ``` x-axis: Low Effort → High Effort y-axis: Low Value → High Value ``` The arrow separates the low label (left / bottom) from the high label (right / top). All of these separators are equivalent: | Separator | Example | | ---------------- | ----------------------------------------- | | `→` (Unicode) | `x-axis: Rare → Certain` | | `->` (ASCII) | `x-axis: Rare -> Certain` | | `↑` | `y-axis: Cheap ↑ Expensive` | | `←` / `<-` / `<` | Reversed axis — high label is on the left | A **reversed axis** is for conventions where the "high" value sits at the left or bottom: ``` x-axis: High Market Share ← Low Market Share ``` ```schematex matrix "Product Portfolio" x-axis: High Market Share ← Low Market Share y-axis: Low Growth → High Growth quadrant Q1 "Question Marks" quadrant Q2 "Stars" quadrant Q3 "Cash Cows" quadrant Q4 "Dogs" "Analytics Suite" at (0.25, 0.35) size: 5 "ChatBot Pro" at (0.2, 0.8) size: 4 highlight: true "Legacy CRM" at (0.75, 0.25) size: 6 "Mobile App" at (0.65, 0.75) size: 3 ``` *** ## 4. Points Each point is a bubble positioned by a normalized (x, y) coordinate pair. ``` "Label" at (x, y) "Label" at (x, y) size: 4 category: design color: #7B1FA2 highlight: true note: "clarify spec" ``` | Property | Values | Meaning | | ------------ | ----------------------------------------------- | -------------------------------------------------- | | `size:` | positive number | Bubble area weight (default: 3) | | `category:` | bareword | Color group; drives the legend | | `color:` | hex string | Override bubble color for this point | | `shape:` | `circle` \| `square` \| `triangle` \| `diamond` | Bubble shape (default: `circle`) | | `highlight:` | `true` | Draws an emphasis ring around the bubble | | `note:` | quoted string | Tooltip annotation | | `label:` | quoted string | Replaces the display label (different from the ID) | Coordinates outside `[0, 1]` are clamped to the chart boundary and flagged with a badge — the original value is stored for tooltip display. ```schematex matrix "Risk Register" x-axis: Low Impact → High Impact y-axis: Rare → Certain "Vendor delay" at (0.45, 0.7) size: 4 category: schedule highlight: true "Security breach" at (0.9, 0.3) size: 5 category: security shape: diamond "Budget overrun" at (0.5, 0.65) size: 3 category: finance "Key hire falls through" at (0.6, 0.55) size: 3 category: people "Scope creep" at (0.4, 0.8) size: 4 category: schedule ``` *** ## 5. Quadrant labels Label each quadrant with a name and an optional subtitle. ``` quadrant Q1 "Do First" quadrant Q2 "Schedule" quadrant Q3 "Delete" quadrant Q4 "Delegate" # With an optional subtitle: quadrant Q1 "Do First" description: "High urgency, high importance" ``` Quadrant numbering follows the standard mathematical convention: **Q1 = top-right, Q2 = top-left, Q3 = bottom-left, Q4 = bottom-right**. The `Q` prefix is optional — `quadrant 1 "Label"` is equally valid. *** ## 6. Heatmap mode Heatmap mode fills N×M cells with color intensity instead of plotting bubble positions. ``` matrix heatmap 4x3 "Skill Matrix" rows: [Strategy, Execution, Communication, Technical] cols: [Junior, Mid, Senior] cell (0,0) level: weak cell (1,0) level: medium cell (2,0) level: strong cell (0,1) value: 7 cell (1,2) label: "Top 10%" ``` * `matrix heatmap COLxROW` — header sets the grid dimensions. * `rows:` and `cols:` — comma-separated or bracket-list of axis labels. * `cell (col, row)` — zero-indexed, column first, row second (row 0 = bottom). * `level:` — `strong` (3), `medium` (2), or `weak` (1) — shorthand for heat intensity. * `value:` — explicit numeric value (overrides `level:`). * `label:` — quoted text placed inside the cell. ```schematex matrix heatmap 4x4 "Competency Heat Map" rows: [Leadership, Execution, Communication, Technical] cols: [Junior, Mid, Senior, Staff] cell (0,0) level: weak cell (1,0) level: medium cell (2,0) level: strong cell (3,0) level: strong cell (0,1) level: medium cell (1,1) level: medium cell (2,1) level: strong cell (3,1) level: strong cell (0,2) level: weak cell (1,2) level: medium cell (2,2) level: medium cell (3,2) level: strong cell (0,3) level: weak cell (1,3) level: weak cell (2,3) level: medium cell (3,3) level: strong ``` *** ## 7. Correlation mode Correlation mode renders an N×M dot matrix where intensity represents the relationship strength between row and column variables. ``` matrix correlation 4x4 "Product Metrics" rows: [DAU, Retention, Revenue, NPS] cols: [DAU, Retention, Revenue, NPS] cell (0,0) value: 1 cell (1,0) value: 0.82 cell (2,0) value: 0.54 cell (3,0) value: 0.71 ``` The same `cell` syntax applies. `level: strong | medium | weak` is also accepted in correlation mode. *** ## 8. SIPOC mode A **SIPOC** is the one-page scoping table that opens the *Define* phase of a Six Sigma DMAIC project. It names, in five fixed columns left to right, everyone and everything the process touches: **S**uppliers · **I**nputs · **P**rocess · **O**utputs · **C**ustomers. Before a team measures or improves anything, SIPOC pins down the boundary — "where does this process start, where does it end, and who hands work in and out of it." ``` matrix sipoc "Order fulfilment" suppliers: "Vendor", "Warehouse" inputs: "PO", "Stock levels" process: "Receive order", "Pick", "Pack", "Ship" outputs: "Shipped package", "Invoice" customers: "End customer", "Finance" ``` * Start with `matrix sipoc`, optionally followed by a quoted title. * Each of the five columns is its own directive: `suppliers:`, `inputs:`, `process:`, `outputs:`, `customers:`. * After the colon, list the entries as **comma-separated quoted strings**. A column may have any number of entries; the rows simply stack top-down inside that column. * The `process:` column is the high-level step sequence (typically 4–7 steps) — keep it to the major stages, not a detailed flowchart. The five columns always render in the canonical S-I-P-O-C order regardless of the order you declare them, so the diagram reads correctly even if an LLM emits the blocks out of sequence. ```schematex matrix sipoc "Order fulfilment" suppliers: "Vendor", "Warehouse" inputs: "PO", "Stock levels" process: "Receive order", "Pick", "Pack", "Ship" outputs: "Shipped package", "Invoice" customers: "End customer", "Finance" ``` *** ## 9. QFD mode (House of Quality) **Quality Function Deployment (QFD)** — the *House of Quality*, introduced by Yoji Akao — translates what customers want into the engineering characteristics that deliver it. Rows are the **WHATs** (customer requirements, each with an importance weight); columns are the **HOWs** (the measurable engineering characteristics the team controls). The body of the grid records how strongly each HOW serves each WHAT. The differentiator: the engine **computes** the bottom row for you. Each HOW's *technical importance* is the sum down its column of `weight × relationship strength` — a ranked answer to "which engineering characteristic moves the most customer value, and is therefore worth the most effort." And the roof of the house — a half-matrix of diamond cells above the columns — records whether two HOWs help or fight each other. ``` matrix qfd "Coffee maker" what: "Quiet operation" weight: 5 what: "Brews fast" weight: 3 what: "Energy efficient" weight: 4 how: "Fan RPM" dir: down how: "Heater watts" dir: up how: "Insulation" dir: up rel (0,0): 9 rel (0,2): 3 rel (1,1): 9 rel (2,1): 3 rel (2,2): 9 roof (0,1): -- roof (1,2): + ``` ### WHATs and HOWs | Directive | Form | Meaning | | --------- | ---------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `what:` | `what: "Label" weight: N` | A customer requirement (one row). `weight:` is its importance, conventionally 1–5. Declaration order is the row order, indexed from 0. | | `how:` | `how: "Label" dir: up\|down` | An engineering characteristic (one column). Declaration order is the column order, indexed from 0. `dir:` is the optimization target — `up` = more is better, `down` = less is better. | ### Relationship cells `rel (i, j): strength` records how strongly column-`j` HOW serves row-`i` WHAT. The index is **(row, column)**, both zero-based. | Strength | Meaning | | ----------- | ------------------------------------ | | `9` | Strong relationship | | `3` | Medium relationship | | `1` | Weak relationship | | *(omitted)* | No relationship — leave the cell out | This 9 / 3 / 1 scale is the QFD convention: it is deliberately non-linear so that one strong link outweighs several weak ones when the importance row is summed. ### Computed technical-importance row The engine sums each column to produce the technical-importance row at the foot of the house: ``` importance(j) = Σ over rows i ( weight(i) × strength(i, j) ) ``` For the coffee-maker example above the row computes to **45 / 39 / 51** — Insulation (51) is the highest-leverage characteristic, Heater watts (39) the lowest. This ranking is the deliverable: it tells the team where to spend engineering effort. Add `normalize: true` (its own line, anywhere in the block) to show each column as a **percentage of the total** instead of a raw sum — for this example, **33% / 29% / 38%**. Percentages make the relative priorities easier to read across very different weight scales. ### The roof — HOW × HOW correlations The **roof** is the triangular half-matrix sitting above the columns. `roof (i, j): glyph` records whether HOW `i` and HOW `j` reinforce or conflict with each other — the synergies and trade-offs a team must reconcile. | Glyph | Correlation | | ----------- | ------------------------------------------------------------- | | `++` | Strong positive — improving one strongly helps the other | | `+` | Positive | | `-` | Negative | | `--` | Strong negative — improving one hurts the other (a trade-off) | | *(omitted)* | No correlation — leave the cell out | Each roof entry renders as a diamond cell in the standard QFD pitched-roof grid. In the example, `roof (0,1): --` flags that pushing Fan RPM down while pushing Heater watts up is a trade-off, and `roof (1,2): +` flags that Heater watts and Insulation reinforce each other. ```schematex matrix qfd "Coffee maker" what: "Quiet operation" weight: 5 what: "Brews fast" weight: 3 what: "Energy efficient" weight: 4 how: "Fan RPM" dir: down how: "Heater watts" dir: up how: "Insulation" dir: up rel (0,0): 9 rel (0,2): 3 rel (1,1): 9 rel (2,1): 3 rel (2,2): 9 roof (0,1): -- roof (1,2): + ``` *** ## 10. Punnett mode (Mendelian genetics) A **Punnett square** predicts the offspring of a genetic cross. You write only the two parental genotypes; the engine does the Mendelian bookkeeping — it enumerates each parent's **gametes** (one allele per gene locus), fills the grid with every gamete combination, and **computes the genotype and phenotype ratios**. The user never fills the grid. ``` matrix punnett "Eye color (Bb × Bb)" cross: Bb x Bb trait B: "Brown eyes" / "Blue eyes" ``` ### The cross | Directive | Form | Meaning | | --------- | ----------------------------------- | ---------------------------------------------------------------------------------------------------------------- | | `cross:` | `cross: Bb x Bb` | The two parental genotypes, separated by `x`, `×`, or `*`. `parents:` is an accepted alias. | | `trait:` | `trait B: "Dominant" / "Recessive"` | *(optional)* Names the two phenotypes for gene `B`, so the legend reads in plain English instead of `B_` / `bb`. | **Allele case sets dominance** — this is the standard genetics convention. An uppercase letter is the **dominant** allele, the matching lowercase letter is **recessive**. A genotype groups alleles by letter: `RrYy` is two loci, `R/r` (round/wrinkled) and `Y/y` (yellow/green). One gene is a **monohybrid** cross (2×2 grid), two genes a **dihybrid** (4×4), three a trihybrid (8×8). ### Computed ratios (the differentiator) The engine derives, from the genotypes alone: * the **gametes** of each parent — the column and row headers — by taking one allele per locus (so a heterozygote `Bb` yields `B` and `b`); * the **offspring grid** — every gamete pairing, written dominant-allele-first (`Bb`, never `bB`); * the **phenotype ratio** — boxes grouped by which phenotype they express, reduced to lowest terms. A monohybrid `Bb × Bb` gives the classic **3:1**; a dihybrid `RrYy × RrYy` gives the famous **9:3:3:1**; * the **genotype ratio** — e.g. `1:2:1` (1 BB, 2 Bb, 1 bb) for the monohybrid. Each box is tinted by its phenotype class, and the footer lists the phenotype ratio with a legend plus the genotype ratio. ```schematex matrix punnett "Seed shape & colour (RrYy × RrYy)" cross: RrYy x RrYy trait R: "Round" / "Wrinkled" trait Y: "Yellow" / "Green" ``` *** ## 11. Config options A `config:` block tunes visual rendering. Each option goes on its own indented line below the `config:` header. ``` config: quadrantBg: true gridLines: true axisArrows: true bubbleScale: area legendPosition: bottom-right ``` | Key | Values | Default | Effect | | --------------------- | ------------------------------------------------------ | -------------- | -------------------------------------------- | | `quadrantBg` | `true` \| `false` | `true` | Colored quadrant background fills | | `gridLines` | `true` \| `false` | `true` | Grid lines overlay | | `axisArrows` | `true` \| `false` | `true` | Arrows at axis ends | | `bubbleScale` | `area` \| `radius` | `area` | Whether `size:` scales bubble area or radius | | `quadrantAnnotations` | `true` \| `false` | `true` | Show quadrant label text in corners | | `legendPosition` | `bottom-right` \| `right` \| `bottom-center` \| `none` | `bottom-right` | Category legend placement | | `labelCollision` | `auto` \| `offset-only` \| `leader-only` \| `off` | `auto` | Overlap avoidance strategy for point labels | | `offChartPolicy` | `clamp-badge` \| `drop` | `clamp-badge` | What to do with points outside \[0,1] | Two shorthand directives also work at the top level (not inside the `config:` block): ``` axis: off # off | on | auto — show or hide the axis lines margins: true # true | false — show Score + Rank margins (correlation mode) ``` *** ## 12. Labels & comments * **Title:** `matrix "My Title"` or `title: My Title` as a standalone line. * **Point label:** the quoted string before `at (…)`. * **Axis labels:** `x-axis:` and `y-axis:` directives. * **Quadrant labels:** `quadrant Q1 "Name"` directive. * **Comments:** `#` anywhere on a line, outside quoted strings. ``` matrix "Prioritization" # This is a comment x-axis: Low Cost → High Cost # inline comment after a directive "Fix bug" at (0.1, 0.9) size: 3 # comment after a point ``` *** ## 13. Table mode (`style: table`) The default matrix rendering is a **scatter / bubble chart** — points float at (x, y) coordinates. For frameworks where the output is a list of items grouped by quadrant (Eisenhower, Johari, Impact-Effort, 9-box), use `style: table` to switch to a **text-in-cell layout** instead. ``` matrix eisenhower "This Week" style: table Q2: "Ship hotfix" Q2: "Customer demo prep" Q1: "Write Q3 OKRs" Q1: "Refactor auth layer" Q4: "LinkedIn updates" Q3: "Reorganize Slack channels" ``` `style: table` applies these changes automatically: | Effect | Detail | | --------------------------------- | ------------------------------------------------------------ | | Axes and arrows hidden | No axis lines, labels, or arrowheads | | Grid lines hidden | Only the outer border and cell dividers remain | | Quadrant titles move inside cells | Each title becomes a cell header instead of a corner overlay | | Items stack as a bullet list | Multiple entries for the same quadrant stack top-down | ### `Q1` … `Q4` shorthand (2×2 only) For 2×2 templates, use `Qn: "item"` instead of the longer `cell (col, row) label: "item"` form. Mapping: | Shorthand | Cell | Eisenhower | Johari | | --------- | ------------ | ---------- | --------------- | | `Q1:` | top-right | Schedule | Blind | | `Q2:` | top-left | Do First | Open / Arena | | `Q3:` | bottom-left | Delete | Hidden / Façade | | `Q4:` | bottom-right | Delegate | Unknown | Repeat a shorthand key to add multiple items to the same cell: ``` Q2: "Ship hotfix" Q2: "Customer demo prep" ``` For 3×3 grids (9-box), use `cell (col, row) label: "…"` directly — the `Q` shorthand is 2×2 only. ### When to use table vs scatter | Use `style: table` for | Use scatter (default) for | | --------------------------------------------- | --------------------------------------------------------- | | Eisenhower with task lists | Eisenhower with `size:` effort weights | | Johari window coaching | Impact-Effort with bubble = revenue | | Backlog grouping (no numeric third dimension) | RICE / BCG portfolio (third dimension IS the bubble size) | | 9-box talent review | Risk heatmap (5×5 with numeric severity) | ```schematex matrix eisenhower "This Week" style: table Q2: "Ship hotfix" Q2: "Customer demo prep" Q1: "Write Q3 OKRs" Q1: "Refactor auth layer" Q4: "LinkedIn updates" Q3: "Reorganize Slack channels" ``` *** ## 14. Reserved words & escaping **Reserved at line start:** `matrix` (header), `x-axis:`, `y-axis:`, `quadrant`, `config:`, `title:`, `rows:`, `cols:`, `grid:`, `axis:`, `margins:`, `cell`. In **SIPOC** mode: `suppliers:`, `inputs:`, `process:`, `outputs:`, `customers:`. In **QFD** mode: `what:`, `how:`, `rel`, `roof`, `normalize:`. **Point lines must start with a quote character** (`"` or `'`). A line that does not start with a quote is not treated as a point. **Strings with spaces** in axis labels do not need quoting — the text after the colon (and after the arrow) is taken verbatim. In `note:` and `label:` point properties, use double quotes. *** ## 15. Common mistakes | You wrote | Parser says | Fix | | ----------------------------------------------------------- | ---------------------------------------------------------------------- | --------------------------------------------------------- | | `"Fix bug" at (1, 2)` | Point parsed; x=1 clamped, y=1 clamped; off-chart badge shown | Keep coordinates in \[0.0, 1.0] or accept the clamp-badge | | `quadrant 1 "Quick Wins"` (no Q prefix) | Accepted — `Q` prefix is optional | Both `quadrant 1` and `quadrant Q1` work | | `config: gridLines: false` (on same line) | Only `config:` keyword recognized; `gridLines: false` silently ignored | Put options on their own indented lines below `config:` | | `x-axis: "Low" → "High"` (quoted labels) | Arrow not found inside quotes — treated as plain text | Remove quotes: `x-axis: Low → High` | | `matrix heatmap` without dimensions | Defaults to 2×2; rows/cols directives set actual size | Specify dimensions on the header: `matrix heatmap 4x4` | | `cell (0, 0) level: Strong` (capital S) | `level` match is case-insensitive — accepted | Both `strong` and `Strong` work | | `shape: oval` | Unknown shape value — silently ignored | Use `circle`, `square`, `triangle`, or `diamond` | | `"Fix bug" at (0.1, 0.9)` on an Eisenhower with a task list | Valid scatter point — but you probably wanted a list in a cell | Add `style: table` and use `Q2: "Fix bug"` instead | | `Q1: "item"` on a 3×3 template | `Q` shorthand is parsed as a point line — silently dropped | Use `cell (col, row) label: "item"` for 3×3 grids | *** ## 16. Grammar (EBNF) ```text document = header directive* header = "matrix" ( template-name | mode-header | title )? NEWLINE template-name = "eisenhower"|"impact-effort"|"rice"|"bcg"|"ansoff"|"johari"|"9-box"|"risk-matrix" mode-header = ( "heatmap" | "correlation" ) ( number "x" number )? title? | ( "sipoc" | "qfd" | "punnett" ) title? title = quoted-string | bare-text directive = x-axis | y-axis | quadrant-dir | config-block | point | cell | q-short | rows-dir | cols-dir | grid-dir | style-dir | title-dir | axis-dir | margins-dir | sipoc-col | qfd-what | qfd-how | qfd-rel | qfd-roof | normalize-dir | punnett-cross | punnett-trait | comment | blank # SIPOC mode sipoc-col = ( "suppliers:" | "inputs:" | "process:" | "outputs:" | "customers:" ) WS quoted-string ( "," quoted-string )* NEWLINE # QFD / House of Quality mode qfd-what = "what:" WS quoted-string WS "weight:" number NEWLINE qfd-how = "how:" WS quoted-string ( WS "dir:" ( "up" | "down" ) )? NEWLINE qfd-rel = "rel" WS "(" number "," number ")" ":" WS ( "9" | "3" | "1" ) NEWLINE # (row, col) qfd-roof = "roof" WS "(" number "," number ")" ":" WS ( "++" | "+" | "-" | "--" ) NEWLINE # (how, how) normalize-dir = "normalize:" WS "true" NEWLINE # Punnett (Mendelian genetics) mode punnett-cross = ( "cross:" | "parents:" ) WS genotype WS ( "x" | "×" | "*" ) WS genotype NEWLINE punnett-trait = "trait" WS letter ":" WS quoted-string WS "/" WS quoted-string NEWLINE # dominant / recessive genotype = ( letter letter )+ # allele pairs grouped by letter; case = dominance, e.g. "RrYy" x-axis = "x-axis:" WS axis-spec NEWLINE y-axis = "y-axis:" WS axis-spec NEWLINE axis-spec = text arrow text | text # plain text → high label only arrow = "→" | "->" | "↑" | "←" | "<-" | "<" | "↓" quadrant-dir = "quadrant" WS "Q"? digit WS quoted-string ( WS "description:" quoted-string )? NEWLINE config-block = "config:" NEWLINE ( INDENT key ":" WS value NEWLINE )* point = quoted-string WS "at" WS "(" number "," number ")" ( WS point-prop )* NEWLINE point-prop = "size:" number | "category:" bareword | "color:" hex-color | "shape:" ( "circle"|"square"|"triangle"|"diamond" ) | "highlight:" "true" | "note:" quoted-string | "label:" quoted-string cell = "cell" WS "(" digit "," digit ")" ( WS cell-prop )* NEWLINE cell-prop = "value:" number | "label:" quoted-string | "level:" ( "strong" | "medium" | "weak" ) style-dir = "style:" WS "table" NEWLINE q-short = "Q" ( "1" | "2" | "3" | "4" ) ":" WS quoted-string NEWLINE # 2×2 only rows-dir = "rows:" WS label-list NEWLINE cols-dir = "cols:" WS label-list NEWLINE grid-dir = "grid:" WS number "x" number NEWLINE axis-dir = "axis:" WS ( "off" | "on" | "auto" ) NEWLINE margins-dir = "margins:" WS ( "true" | "false" | "on" | "1" ) NEWLINE label-list = "[" text ("," text)* "]" | text ("," text)* quoted-string = '"' any-char-but-quote* '"' | "'" any-char-but-quote* "'" comment = "#" any NEWLINE ``` Authoritative source: `src/diagrams/matrix/parser.ts`. If this diverges from the parser, the parser wins — please open an issue. *** ## 17. Standard compliance Schematex matrix diagrams implement the standard 2×2 quadrant convention used by the **Boston Consulting Group (1970)**, **Eisenhower decision matrix**, **Ansoff growth matrix**, and **Johari Window** frameworks. The 9-box grid follows the **McKinsey/GE talent review** convention (3×3, performance × potential). Heatmap and correlation modes follow no named external standard but use the universal cell-intensity encoding found in tools like Excel conditional formatting and R's `corrplot`. What is implemented today: * ✅ 2×2 quadrant mode with custom axes and quadrant labels * ✅ 3×3 quadrant mode (`9-box` template) * ✅ N×M heatmap mode with `level:` and `value:` cells * ✅ N×M correlation mode * ✅ SIPOC mode — five-column Suppliers · Inputs · Process · Outputs · Customers scoping table (Six Sigma DMAIC Define) * ✅ QFD / House of Quality mode — WHATs × HOWs grid, 9 / 3 / 1 relationship scale, **computed** technical-importance row (with `normalize: true` percentages), and the HOW × HOW correlation roof (`++` / `+` / `-` / `--`) * ✅ Punnett mode — Mendelian cross from two parental genotypes; the engine **computes** the gametes, the offspring grid, and the genotype + phenotype ratios (monohybrid 3:1, dihybrid 9:3:3:1); allele case = dominance; optional `trait` phenotype names; mono/di/trihybrid (2×2 / 4×4 / 8×8) * ✅ Eight built-in templates (eisenhower, impact-effort, rice, bcg, ansoff, johari, 9-box, risk-matrix) * ✅ Point properties: size, category, color, shape, highlight, note * ✅ Reversed axes (`←` / `<-`) * ✅ Config block (quadrantBg, gridLines, axisArrows, bubbleScale, legendPosition, labelCollision, offChartPolicy) * ✅ `style: table` — text-in-cell layout with `Q1`…`Q4` shorthand (2×2) and stacked bullet lists * ⏳ `label:` override on points (parsed, renderer support pending) * ⏳ Off-chart `drop` policy (parsed, renderer always clamp-badges today) * ⏳ `margins:` correlation score/rank sidebar (parsed, not yet rendered) References: * Henderson, B. (1970). *The Product Portfolio.* Boston Consulting Group. * Covey, S. (1989). *The 7 Habits of Highly Effective People.* (Eisenhower matrix popularization) * Ansoff, H.I. (1957). "Strategies for Diversification." *Harvard Business Review*. * Akao, Y. (1990). *Quality Function Deployment: Integrating Customer Requirements into Product Design.* Productivity Press. (House of Quality) * Pyzdek, T. & Keller, P. (2018). *The Six Sigma Handbook* (5th ed.). McGraw-Hill. (SIPOC in DMAIC Define) * Punnett, R.C. (1905). *Mendelism.* Macmillan. (the Punnett square); Mendel, G. (1866). "Versuche über Pflanzenhybriden." *** ## 18. Roadmap **Planned — not yet parseable.** Do not use these in generated DSL today; the parser will reject or ignore them. * **Swimlane / zone overlays** — named rectangular highlight regions drawn behind the grid. * **Threshold lines** — horizontal or vertical reference lines with labels (e.g. "break-even" line). * **Bubble labels inside** — option to print the point label inside the bubble rather than beside it. * **Export to table** — structured CSV / JSON output alongside the SVG for spreadsheet import. * **4×4 and custom-label quadrant mode** — arbitrary NxM with labeled cells in quadrant (bubble-plot) mode, not just heatmap. Track in the GitHub issues if you need any of these sooner. *** ## Related examples Ready-to-use scenarios from the examples gallery: [Browse related examples](https://schematex.js.org/examples) --- # Mind map Canonical URL: https://schematex.js.org/docs/mindmap Markdown URL: https://schematex.js.org/docs/mindmap.md ## About mind maps A **mind map** is a radial diagram that organizes ideas around a central topic, branching outward into subtopics and details. Tony Buzan popularized the format in the 1970s as a note-taking and brainstorming tool; the method has since been adopted widely in education, project planning, meeting facilitation, and knowledge management. The key insight is that non-linear branching mirrors how associative thinking works — faster than outlining, more structured than free-writing. Schematex mind maps use a **Markdown-heading + bullet-list** DSL inspired by [markmap](https://markmap.js.org/) — a format most people already know. Two layout styles are available: the classic radial **map** (branches in all directions) and a **logic-right** horizontal tree. This page documents what the parser accepts today. ```schematex mindmap # Product Launch Plan ## Market readiness ### Competitive analysis - Direct competitors - Pricing benchmarks ### Target segments - SMB customers - Enterprise pilot ## Engineering ### Feature freeze - Core API complete - Edge cases resolved ### Infrastructure - Load testing - CDN configuration - Cache rules - Geo routing ## Go-to-market - Landing page live - Email campaign - Press outreach - TechCrunch pitch - Newsletter sponsors ## Success metrics - Week 1 signups - Activation rate - NPS at day 30 ``` *** ## 1. Your first mind map The smallest useful mind map: a central topic with two branches, one with a sub-item. ```schematex mindmap # Team retrospective ## What went well - Clear sprint goals - Good test coverage ## What to improve - Slower PR reviews - Add a review SLA ``` Four rules cover 80% of usage: 1. Start with an optional `mindmap` keyword on its own line, then a blank line. 2. The root is the single `#` heading — exactly one is allowed. 3. Use `##`, `###`, and deeper headings to set branch depth. Heading level equals tree depth. 4. Use `-`, `*`, or `+` bullets to add sub-items under any heading. Each 2-space indent adds one more depth level. > Comments are not supported. Use `%%` directives (before the `#` root) for configuration only. *** ## 2. Headings and depth Heading level maps directly to tree depth. `#` is always the root (depth 0). `##` is depth 1. `###` is depth 2, and so on up to `######` (depth 5). ``` mindmap # Root ## Branch A ← depth 1 ### Sub-branch ← depth 2 #### Leaf ← depth 3 ## Branch B ``` Headings can jump levels — `####` after `##` is valid and produces a node at depth 3. The tree depth is relative to the root, not to the previous heading. *** ## 3. Bullets Bullets extend a heading branch with further detail. Any of `-`, `*`, or `+` is accepted as the bullet marker. Each **2 spaces** of indentation adds one level of depth relative to the enclosing heading. ``` ## Risks - Technical complexity ← depth 2 (one level under ## Risks) - Legacy integrations ← depth 3 (2 spaces indent) - Auth service ← depth 4 (4 spaces indent) - Team availability ← depth 2 again ``` ```schematex mindmap # Book outline ## Chapter 1 — Introduction - Why this matters - Historical context - Current state - What you will learn ## Chapter 2 — Core concepts - Concept A - Definition - Examples - Concept B - Definition - Worked example - Step-by-step walkthrough ``` *** ## 4. Inline formatting Node labels support a subset of Markdown inline formatting. The parser tokenizes labels at parse time; the renderer uses the tokens to emit styled text. | Syntax | Effect | Example | | ------------- | -------------- | ------------------------------------------------- | | `**text**` | Bold | `**Critical path**` | | `*text*` | Italic | `*optional*` | | `` `code` `` | Monospace code | `` `npm install` `` | | `[text](url)` | Link | `[RFC 7519](https://tools.ietf.org/html/rfc7519)` | | `[ ] item` | Unchecked task | `[ ] Write tests` | | `[x] item` | Checked task | `[x] Design review` | The checkbox must be at the very start of the label (before any other text). Inline formatting can be nested: `**[bold link](url)**`. ```schematex mindmap # Sprint 24 review ## Completed - [x] **Auth redesign** — JWT + refresh tokens - [x] API rate limiting \`per-user\` - [x] [Error budget dashboard](https://metrics.example.com) ## In progress - [ ] *Mobile push notifications* - [ ] iOS APNs integration - [ ] Android FCM setup ## Blocked - [ ] **Payment webhook** — waiting on Stripe team - *Escalated to account manager* ``` *** ## 5. Layout styles The `%% style:` directive selects the layout algorithm. Place it before the `#` root heading. | Style | Layout | Best for | | --------------- | ------------------------------------------------------------------------------- | -------------------------------------------------------- | | `map` (default) | Radial — branches spread in all directions from the center | Brainstorming, concept maps, free-form exploration | | `logic-right` | Horizontal tree — all branches extend to the right | Structured outlines, hierarchies, sequential breakdowns | | `futureswheel` | Concentric rings — the root at the hub, each heading level on its own ring | Foresight, consequence mapping, structured brainstorming | | `driver` | Horizontal tree — aim on the left flowing right through drivers to change ideas | Improvement programs, aim → driver → action breakdowns | ``` %% style: map %% style: logic-right %% style: futureswheel %% style: driver ``` **`map`** (default) — radial layout, branches spread in all directions from the center. Best for brainstorming and concept maps. ```schematex mindmap # Machine learning ## Supervised ### Classification - Decision tree - SVM - Neural net ### Regression - Linear - Gradient boosting ## Unsupervised ### Clustering - K-means - DBSCAN ### Reduction - PCA - t-SNE ## Reinforcement - Q-learning - Policy gradient ``` **`logic-right`** — horizontal tree, all branches extend to the right. Best for structured outlines and sequential hierarchies. ```schematex mindmap %% style: logic-right # Machine learning ## Supervised ### Classification - Decision tree - SVM - Neural net ### Regression - Linear - Gradient boosting ## Unsupervised ### Clustering - K-means - DBSCAN ### Reduction - PCA - t-SNE ## Reinforcement - Q-learning - Policy gradient ``` **`futureswheel`** — a [Futures Wheel](https://en.wikipedia.org/wiki/Futures_wheel) (Jerome Glenn, 1971/72), the classic structured-brainstorming format for thinking through consequences. The central event or trend sits at the hub; first-order consequences land on the inner ring, second-order consequences on the next ring out, and so on. Each child stays inside the angular sector of its parent, and every ring is color-coded by order, so a reader can see at a glance how far a ripple is from the original event. Depth maps to rings: `#` is the hub, `##` is the first ring (1st-order), `###` / bullets under a heading push out to the next ring (2nd-order), and deeper levels keep stepping outward. ```schematex mindmap %% style: futureswheel # Remote work becomes default ## Less commuting - Lower carbon emissions - Cheaper city living ## Distributed teams - Async communication norms - Global hiring pools ## Empty offices - Commercial real estate slump - Repurposed to housing ``` **`driver`** — a [Driver Diagram](https://www.ihi.org/resources/tools/driver-diagram), the planning tool from the IHI (Institute for Healthcare Improvement) model for improvement. It reads left to right as a tidy tree: the **aim** on the far left, the **primary drivers** (the few high-leverage areas that move the aim) in the next column, then **secondary drivers** and concrete **change ideas** branching further right. Tree levels map cleanly to the structure: `#` is the aim, `##` are primary drivers, and bullets / deeper headings under each become the secondary drivers and change ideas. Use it whenever you need to show *how* a goal will actually be reached. ```schematex mindmap %% style: driver # Reduce 30-day readmissions ## Reliable discharge process - Teach-back at bedside - Med reconciliation ## Timely follow-up - Appointment within 7 days - Post-discharge phone call ``` *** ## 6. Directives Directives are `%%` lines placed **before** the `#` root heading. They configure the diagram globally. | Directive | Values | Default | Effect | | --------------------- | ---------------------------------------------- | ------- | ---------------------------------- | | `%% style: …` | `map`, `logic-right`, `futureswheel`, `driver` | `map` | Layout algorithm | | `%% theme: …` | any string | (none) | Theme override passed to renderer | | `%% maxLabelWidth: …` | integer 80–1000 | `240` | Max pixel width before label wraps | ``` mindmap %% style: logic-right %% maxLabelWidth: 320 # Wide label root ``` ```schematex mindmap %% style: logic-right %% maxLabelWidth: 200 # Schematex features ## DSL-first design - One keyword per diagram - AI-friendly syntax - CJK support ## Zero dependencies - Hand-written parser - No D3, no dagre - ~KB-level bundle ## Standards-compliant - IEEE for logic gates - IEC for circuits - McGoldrick for genograms ``` *** ## 7. Labels & comments * **Root title:** the text after `#` on the root heading line. * **Branch labels:** the text after `##`, `###`, etc. * **Bullet labels:** the text after the `- ` / `* ` / `+ ` marker. * **Inline formatting:** `**bold**`, `*italic*`, `` `code` ``, `[text](url)`, `[ ]` / `[x]`. * **Comments:** not supported in the body. Use `%%` directives before the `#` root for configuration; `%%` lines in the body are treated as directives (silently ignored if unrecognized). *** ## 8. Reserved words & escaping **Reserved at document start:** `mindmap` (optional keyword) and `%%` (directive prefix). **Reserved as root:** exactly one `#` heading; a second `#` heading throws a parse error. **Bullet markers:** `-`, `*`, `+` followed by a space. A `*` that is not followed by a space is treated as an italic marker if it appears inside label text. **Inline conflicts:** a label beginning with `[ ]` or `[x] ` is parsed as a checkbox, not a Markdown link. If you need a label that literally starts with `[`, write `\[` — the backslash escapes the bracket. *** ## 9. Common mistakes | You wrote | Parser says | Fix | | ---------------------------------- | --------------------------------------------------------------------------------------------------- | ----------------------------------------------------- | | Two `#` headings | `Error: multiple # center nodes not allowed` | Use exactly one `#` heading as the root | | `##Branch` (no space after `##`) | Line is not recognized as a heading; silently skipped | Always put a space: `## Branch` | | Bullet indented 3 spaces | Depth = `lastHeadingDepth + 1 + floor(3/2) = lastHeadingDepth + 2` — may create an unexpected level | Use multiples of 2 spaces: 0, 2, 4, 6… | | `%% style: radial` | Unknown value silently ignored; layout stays `map` | Use `map`, `logic-right`, `futureswheel`, or `driver` | | `mindmap` keyword mid-document | Treated as a plain text line (the keyword is only recognized on the very first line) | Place `mindmap` on line 1, before any content | | `[ ]text` (no space after bracket) | Checkbox not recognized; rendered as literal `[ ]text` | `[ ] text` — space required after the closing bracket | *** ## 10. Grammar (EBNF) ```text document = ("mindmap" NEWLINE)? (blank | directive)* node* directive = "%%" WS key ":" WS value NEWLINE key = "style" | "theme" | "maxlabelwidth" node = heading | bullet heading = INDENT? "#"+ SPACE label NEWLINE bullet = SPACE* bullet-marker SPACE label NEWLINE bullet-marker = "-" | "*" | "+" label = inline-token* inline-token = checkbox | "**" inline-token* "**" | "*" inline-token* "*" | "`" code-text "`" | "[" inline-token* "]" "(" url ")" | plain-text checkbox = "[ ]" SPACE | "[x]" SPACE | "[X]" SPACE INDENT = WS* %% headings may have leading whitespace (ignored) SPACE = " " | "\t" ``` **Depth rules:** * Heading `#` → depth 0 (root) * Heading `##` → depth 1, `###` → depth 2, etc. * Bullet at `n` leading spaces → depth = `lastHeadingDepth + 1 + floor(n / 2)` Authoritative source: `src/diagrams/mindmap/parser.ts`. If this diverges from the parser, the parser wins — please open an issue. *** ## 11. Roadmap **Planned — not yet parseable.** Do not use these in generated DSL today; the parser will reject or ignore them. * **`%%{init: {…}}%%` block** — Mermaid-style init block for theme/config; currently only `%%` line directives are supported. * **Auto-numbered branches** — `%% numbering: true` to prefix each branch with 1., 1.1., etc. * **Callout / note nodes** — a special marker to attach a floating annotation box to any node. * **Image nodes** — `![alt](url)` as an entire node label rendered as an inline image. * **Collapsed branches** — `%% collapsed: branchId` to render a subtree as a single folded indicator. Track in the GitHub issues if you need any of these sooner. *** ## Related examples Ready-to-use scenarios from the examples gallery: [Browse related examples](https://schematex.js.org/examples) ## Interactive editing Every authored node label is an exact source edit target. Hierarchy determines automatic tree layout, so persistent node drag stays disabled rather than writing unstable presentation coordinates. --- # Network Topology Canonical URL: https://schematex.js.org/docs/network Markdown URL: https://schematex.js.org/docs/network.md ## About network diagrams A **network diagram** is the single most-drawn professional infrastructure diagram — every network engineer, sysadmin, MSP, and physical-security (CCTV) integrator draws them for design proposals, as-built documentation, audits, and customer hand-off. They are convention-rich: a router doesn't look like a switch, a fiber link doesn't look like a copper one, and a camera subnet is a recognisable shape. Schematex's `network` engine renders the de-facto **Cisco-convention icon silhouettes** as original line-art, lays out the topology classes the way the standard says they look, and — unlike an LLM emitting raw Mermaid — **never silently drops a device, a port, or a link**. What makes it different from a drag-and-drop stencil library: it understands **topology classes** (a star has a hub, a ring closes a loop, a spine-leaf is two meshed rows, the three-tier model is core-over-distribution-over-access bands), renders each **link type** with its conventional appearance, and **validates** structural facts — a device's IP must fall inside its subnet's CIDR, a VLAN id must be 1–4094. ```schematex network "Acme HQ — CCTV" layout: tiered internet net "Internet" firewall fw1 "Perimeter FW" tier: edge l3switch core1 "Core SW" tier: core poeswitch poe1 "PoE Switch" tier: access nvr nvr1 "Recorder" subnet cams "192.168.20.0/24" { camera cam1 "Lobby" type: dome ip: 192.168.20.11 camera cam2 "Gate" type: ptz ip: 192.168.20.12 poe1 } net -- fw1 : wan "ISP 1Gbps" fw1 -- core1 : fiber 10G core1 -- poe1 : trunk vlan: 20 1G core1 -- nvr1 poe1 -- cam1 : poe poe1 -- cam2 : poe ``` The cameras sit in a dashed `192.168.20.0/24` subnet (their IPs validated against the CIDR), PoE links are green, the fiber uplink is orange, and devices band by `tier:`. *** ## 1. Your first diagram A complete network diagram needs only two kinds of line: **device declarations** and **links**. Nothing else is required. ```schematex network "Tiny LAN" router r1 "Edge Router" switch sw1 "Core Switch" pc pc1 "Workstation" r1 -- sw1 sw1 -- pc1 ``` That's it — a valid, laid-out diagram. Just two rules: * `<kind> <id> ["label"]` — a typed device. The kind picks the icon. * `<a> -- <b>` — an undirected link between two declared devices. **Everything else is optional and additive — but not all of it is equal.** Two cheap, high-value structural hints are worth adding whenever hierarchy matters: `layout:` (tiered/tree/star/ring/bus/mesh/spine-leaf) and `tier:` (edge/core/distribution/access). They drive a readable top-down hierarchy at almost no syntax cost: ``` network "Branch" layout: tiered router r1 "Edge Router" tier: edge l3switch core1 "Core SW" tier: core switch acc1 "Access SW" tier: access pc pc1 "Workstation" r1 -- core1 core1 -- acc1 acc1 -- pc1 ``` By contrast, the **per-link annotations** — link types (`fiber`/`wireless`/`poe`…), speeds, `vlan:`, `port:`, `trunk`/`access`, and `subnet { }` boundaries — don't affect layout and are where generation most often breaks. Add them only when the request calls for them. Rule of thumb: keep the structural hints, drop the decorative annotations unless asked. Devices are **not** auto-declared from links — an undeclared id can't be safely typed, so a link to an unknown device is a readable error. Use `;` to put several statements on one line, and `a b c : kind` shorthand to declare several same-kind devices at once. Once the skeleton works, you can layer on direction and annotations — `->` is a directed link, `==` is a LAG, and anything after `:` is the link spec: ``` network "Home" layout: star router gw "Gateway" pc pc1 laptop lt1 gw -- pc1 gw -- lt1 : wireless ``` *** ## 2. Device kinds Pick the kind that matches the box; the icon follows the Cisco-convention silhouette. * **Infrastructure** — `router`, `switch`, `l3switch`, `firewall`, `loadbalancer`, `ap`, `wlc`, `gateway`, `modem`, `ids`, `proxy`, `vpngw` * **Endpoints** — `server`, `serverfarm` (`count: n`), `pc`, `laptop`, `mobile`, `ipphone`, `printer`, `storage` * **CCTV / security** — `camera` (with `type: fixed | bullet | dome | ptz | turret`), `nvr`, `dvr`, `poeswitch`, `encoder`, `monitor` * **Clouds** — `internet`, `wan`, `pstn`, `cloud`, plus `lan` (a bus bar) Aliases are accepted: `multilayer`→`l3switch`, `workstation`→`pc`, `wifi`→`ap`, `nas`/`san`→`storage`, `voip`→`ipphone`. ``` camera cam1 type: dome ip: 192.168.20.11 serverfarm farm "Server Farm" count: 4 l3switch core1 tier: core model: "C9500" ``` *** ## 3. Links & annotations A link's appearance follows its type; everything after `:` is order-free. ``` a -- b # copper / ethernet (default solid) a -- b : fiber 10G # fiber — orange with slash ticks a -- b : wireless # dashed a -- b : serial # leased / WAN circuit a -- b : poe # Power-over-Ethernet (green + tag) a -- b : vpn "site-to-site" # dashed tunnel a == b : lag 40G # aggregated / EtherChannel (double line) a -- b : trunk vlan: 10,20 1G port: Gi0/1>Gi1/0/24 ``` * `trunk` / `access` — port mode (a trunk should connect switch-class devices). * `vlan: 10` or `vlan: 10,20` — a single VLAN tints the link (skipping the reserved alarm-red). * `1G` / `10G` / `100M` / `40G` — speed, shown mid-link. * `port: near>far` — interface labels at each end. *** ## 4. Layout modes ``` layout: tiered # default — band by tier: edge → core → distribution → access layout: tree # hierarchical from the root layout: star # hub at center, spokes on a ring layout: ring # nodes on a circle layout: bus # shared backbone layout: mesh # full/partial mesh on a circle layout: spine-leaf # two rows, every leaf auto-meshed to every spine layout: manual # explicit at: x,y per device direction: tb | lr # flow axis for tiered/tree ``` For `tiered`, set `tier:` (`edge` / `core` / `distribution` / `access`) on infrastructure; untiered endpoints are placed below their switch. For `spine-leaf`, declare `spines:` and `leaves:` and the spine↔leaf links are generated for you. *** ## 5. Boundaries: sites, racks, subnets, VLANs A device can live inside nested boundary blocks. **Physical** containers (site/rack) draw a solid border; **logical** overlays (subnet/VLAN/zone/DMZ) draw a dashed tinted region. ``` network "Branch" site hq "HQ Building" { rack mdf "MDF Rack" { firewall fw1 tier: edge l3switch core1 tier: core } } subnet lan "10.0.10.0/24" { switch a1 tier: access pc u1 "User PC" ip: 10.0.10.50 } zone dmz "DMZ" { server web } fw1 -- core1 : 10G core1 -- a1 : trunk vlan: 10 a1 -- u1 ``` A device declared inside a `subnet` whose label is a CIDR has its `ip:` validated — an address outside the range is a readable error. A bare id on its own line inside a block adds an already-declared device to that group. *** ## 6. Validation & the no-drop guarantee The engine guarantees every declared device and link renders — the dropped-device failure of generic tools is structurally impossible. It also checks: * **duplicate id** → error; * **unknown kind** → error with the nearest suggestion (`"swtich" → did you mean "switch"?`); * **link to an undeclared device** → error; * **VLAN id outside 1–4094** → warning (still renders); * **device IP outside its subnet CIDR** → error. The SVG `<desc>` records device/link counts, the detected topology class (star / ring / bus / mesh / tree / hierarchical / spine-leaf), and any warnings. *** ## 7. Themes ``` theme: default # house "network blue" Cisco-style bodies theme: monochrome # clean line-art for print/audit (link meaning via line-style + tags) theme: dark # Schematex slate/blue dark palette ``` CJK labels and `「…」` / `"…"` quotes parse cleanly: ``` network "办公室" multilayer core1 「核心交换机」 poeswitch poe1 camera cam1 type: dome core1 -- poe1 : trunk vlan: 10 poe1 -- cam1 : poe ``` *** ## 8. Standard compliance Network topology has no single formal drawing standard; Schematex composes its baseline from the de-facto sources — **Cisco network-topology icons** (silhouettes redrawn as original line-art), the **Cisco hierarchical model** and **spine-leaf (Clos)** for layout, **ANSI/TIA-606** colour conventions for cabling, and **ONVIF** roles for the CCTV cluster. Full specification: [Network Topology Standard Reference](https://github.com/schematex/schematex/blob/main/docs/reference/35-NETWORK-STANDARD.md). *** ## Related examples Ready-to-use scenarios from the examples gallery: [Browse related examples](https://schematex.js.org/examples) ## Interactive editing Device labels, link labels, and the title are exact-range edit targets. Stable device IDs move freely and attached topology links reroute live; subnet and boundary membership remains authored structure. --- # Org chart Canonical URL: https://schematex.js.org/docs/orgchart Markdown URL: https://schematex.js.org/docs/orgchart.md ## About org charts An **org chart** (organizational chart) maps the formal reporting structure of an organization — who manages whom, which teams sit under which leaders, and where open roles and external advisors fit in. HR teams reach for them during headcount planning; founders use them before board meetings; operations managers circulate them when restructuring. Unlike a generic flowchart, an org chart treats people (and positions) as the primary entities, with the hierarchy encoded by indentation or explicit edges. Schematex follows general org-chart conventions with extensions for open/unfilled positions, matrix (dotted-line) reporting, and assistant relationships. There is no single ISO standard for org charts; the conventions implemented here are drawn from HR practice and software-industry norms. For the authoritative academic background see [Fayol (1916)](https://en.wikipedia.org/wiki/Henri_Fayol) and the [Wikipedia article on org charts](https://en.wikipedia.org/wiki/Organizational_chart). ```schematex orgchart "Acme Corp — Q3 2025" ceo: "Jamie Torres" | CEO [role: ceo] cto: "Raj Patel" | CTO [role: cto] lead_fe: "Priya Nair" | Engineering Lead | Frontend [role: engineer] eng1: "Alex Kim" | Senior Engineer [role: engineer] eng2: "Jordan Lee" | Engineer [role: engineer, status: new] open1: open "TBH" | Frontend Engineer [role: engineer] lead_be: "Omar Hassan" | Engineering Lead | Backend [role: engineer] eng3: "Yuki Tanaka" | Staff Engineer [role: engineer] draft1: draft "TBH" | Senior Engineer [role: engineer] cfo: "Maria Santos" | CFO [role: cfo] fin1: "Nour Ahmed" | Finance Manager [role: ops] cpo: "Ellen Wu" | CPO [role: cpo] pm1: "Tyler Brooks" | Product Lead | Core [role: product] pm2: "Suki Ito" | Product Lead | Growth [role: product] advisor adv1: "Dr. Alan Ford" | Board Advisor [role: advisor] pm1 -.-> lead_fe pm2 -.-> lead_be ``` *** ## 1. Your first org chart The smallest useful org chart: a three-level hierarchy with one open role. ```schematex orgchart "Engineering Team" cto: "Wei Zhang" | CTO [role: cto] lead: "Sam Obi" | Engineering Lead [role: engineer] eng: "Ana Rossi" | Engineer [role: engineer] open1: open "TBH" | Engineer [role: engineer] ``` Four rules cover 80% of usage: 1. Start with the keyword `orgchart`, optionally followed by a quoted title. 2. Each person is a **node** — `id: "Name" | "Title" | "Department" [props]`. The `|` separates the name, title, and department fields. 3. **Indentation determines hierarchy** — each extra two (or more) spaces moves a node one level deeper under the nearest less-indented node above it. 4. Declare open roles with `open id:` or `draft id:`, and external advisors with `advisor id:`. > Comments must start with `#` on their own line (inline trailing `//` is also stripped). *** ## 2. Nodes A node line has the form `[kind] id: fields [props]`. The `id` must match `[A-Za-z][A-Za-z0-9_-]*`. ### 2.1 Fields (pipe-separated) The part after the `:` and before the optional `[props]` block is split on `|`: ``` alice: "Alice Zhang" # name only alice: "Alice Zhang" | "VP Engineering" # name + title alice: "Alice Zhang" | "VP Eng" | "Platform" # name + title + department alice: "Alice Zhang" | "VP Eng" | "Platform" | "x@co.com" # + info line ``` | Position | Content | Notes | | -------- | ----------------- | -------------------------------------------------------------------------- | | 1st | Name | Quoted or unquoted | | 2nd | Title / job level | Optional | | 3rd | Department | Optional | | 4th | Info line | Optional; also settable via `note:`, `email:`, `phone:`, `location:` props | ```schematex orgchart "Field count demo" alice: "Alice" bob: "Bob" | "CTO" carol: "Carol" | "Engineer" | "Engineering" dave: "Dave" | "VP Sales" | "Sales" | "dave@co.com" ``` ### 2.2 Node kinds The optional **kind keyword** before the id changes how the node is rendered: | Keyword(s) | Kind | Meaning | | --------------------- | --------- | --------------------------------------------- | | *(none)* | `person` | Regular person | | `role`, `open` | `role` | Open / unfilled position | | `draft`, `tbh` | `draft` | Planned position, not actively recruiting | | `advisor`, `external` | `advisor` | External advisor, board member, or contractor | ```schematex orgchart "Node kinds" ceo: "Jordan Kim" | CEO [role: ceo] eng_lead: "Priya Nair" | Engineering Lead [role: engineer] open eng_open: "TBH" | Senior Engineer [role: engineer] draft eng_draft: "TBH" | Staff Engineer [role: engineer] advisor adv1: "Dr. Lee" | Board Advisor [role: advisor] ``` ### 2.3 Node properties Properties go in `[key: value, …]` at the end of a node line. | Property | Values | Effect | | ---------------- | ---------------------------------- | ------------------------------------------------------------ | | `role:` | see table below | Role icon displayed in the avatar | | `icon:` | same as `role:` | Alias for `role:` | | `department:` | text | Overrides the department field | | `status:` | `new` \| `leaving` \| `on-leave` | Status pill on the card | | `avatar-color:` | hex color (e.g. `"#7B1FA2"`) | Avatar background color | | `gender:` | `male` \| `female` | Avatar silhouette (used when no role icon is set) | | `note:` | text | Info line (first one wins) | | `email:` | text | Info line | | `phone:` | text | Info line | | `location:` | text | Info line | | `assistant-of:` | node id | Renders this node as an assistant to the named node | | `matrix:` | space- or comma-separated node ids | Adds dotted matrix lines from those nodes to this one | | `reports:` | node id | Explicit parent override (instead of indentation) | | `open` | *(bare flag)* | Marks node as open; equivalent to using `role` kind keyword | | `draft` or `tbh` | *(bare flag)* | Marks node as draft; equivalent to `draft` kind keyword | | `external` | *(bare flag)* | Marks node as external; equivalent to `advisor` kind keyword | **Role icons** — the `role:` value resolves to a display icon. Accepted keywords (case-insensitive): | Keywords | Icon | | ------------------------- | --------- | | `ceo` | CEO | | `cto` | CTO | | `cfo` | CFO | | `coo` | COO | | `cmo` | CMO | | `cpo` | CPO | | `vp` | VP | | `engineer`, `engineering` | Engineer | | `designer`, `design` | Designer | | `sales` | Sales | | `hr` | HR | | `legal` | Legal | | `ops`, `operations` | Ops | | `marketing` | Marketing | | `product` | Product | | `data` | Data | | `advisor` | Advisor | | `intern` | Intern | | `vacant` | Vacant | ```schematex orgchart "People directory" ceo: "Jamie Torres" | CEO [role: ceo, avatar-color: "#1E88E5"] cto: "Raj Patel" | CTO [role: cto] eng1: "Priya Nair" | Staff Engineer [role: engineer, status: new] eng2: "Jordan Lee" | Senior Engineer [role: engineer] cfo: "Maria Santos" | CFO [role: cfo] fin1: "Nour Ahmed" | Finance Manager [role: ops, status: on-leave] advisor adv1: "Dr. Alan Ford" | Board Advisor [role: advisor] draft draft1: "TBH" | General Counsel [role: legal] ``` *** ## 3. Hierarchy Hierarchy is expressed by **indentation** — the most common pattern. Each node becomes a child of the nearest node above it that has a smaller indent. Tabs are treated as two spaces. ``` ceo: "CEO" cto: "CTO" # child of ceo (indent 2) eng: "Engineer" # child of cto (indent 4) cfo: "CFO" # child of ceo (indent 2, same level as cto) ``` Alternatively, use the `reports:` property to set a parent explicitly regardless of indentation: ``` orgchart "Flat file" ceo: "CEO" cto: "CTO" [reports: ceo] eng: "Engineer" [reports: cto] ``` ```schematex orgchart "Series A Startup" ceo: "Lena Brandt" | CEO [role: ceo] cto: "James Osei" | CTO [role: cto] be: "Platform Lead" | Engineering [role: engineer] be1: "Fatima Al-Rashid" | Backend Engineer [role: engineer] be2: "Marco Ricci" | Backend Engineer [role: engineer] fe: "Frontend Lead" | Engineering [role: engineer] fe1: "Yumi Tanaka" | Frontend Engineer [role: engineer, status: new] open_fe: open "TBH" | Frontend Engineer [role: engineer] cpo: "Diana Russo" | CPO [role: cpo] pm1: "Chris Obi" | Product Manager [role: product] cmo: "Kai Nakamura" | CMO [role: cmo] mkt1: draft "TBH" | Growth Marketer [role: marketing] ``` *** ## 4. Edges Two kinds of edges exist. Most reporting lines come from the indentation hierarchy automatically. You can also write them explicitly — or add matrix (dotted) lines. | Operator | Edge kind | Meaning | | -------------- | --------- | --------------------------------------- | | `from -> to` | `report` | Solid reporting line | | `from -.-> to` | `matrix` | Dotted matrix (indirect) reporting line | Explicit edges require that both node ids are already declared. A `report` edge created explicitly is not duplicated if the same relationship is already implied by indentation. Edge labels are supported: ``` pm1 -.-> design [label: "product partnership"] ``` ```schematex orgchart "Matrix org" config: direction = LR cpo: "Ellen Wu" | CPO [role: cpo] pm_core: "Core PM" | Product Manager [role: product] pm_growth: "Growth PM" | Product Manager [role: product] design: "Suki Ito" | Design Lead [role: designer] data_lead: "Ben Park" | Data Lead [role: data] pm_core -.-> design [label: "design partner"] pm_growth -.-> design pm_growth -.-> data_lead ``` *** ## 5. Configuration `config:` lines adjust layout and orientation. Each goes on its own line. | Config key | Values | Default | Effect | | ----------- | -------------------------------------- | ------- | --------------------------- | | `direction` | `TD`, `LR` | `TD` | Top-down or left-right flow | | `layout` | `tree`, `list`, `directory`, `compact` | `tree` | Visual layout mode | **Layout notes:** * `tree` — standard hierarchical tree with branching connectors. Best for most org charts. * `list` / `directory` / `compact` — compact indented directory view. Good for large headcount lists where tree branching becomes unwieldy. **Tree layout** (default) — avatar cards with branching connectors and department color-coding. Best for teams up to \~30 people. ```schematex orgchart "Acme Engineering — tree" cto: "Wei Zhang" | CTO [role: cto] platform: "Platform Lead" | Engineering [role: engineer] p1: "Amara Diallo" | Staff Engineer [role: engineer] p2: "Ben Novak" | Senior Engineer [role: engineer] p3: draft "TBH" | Engineer [role: engineer] growth: "Growth Lead" | Engineering [role: engineer, status: new] g1: "Fatima Al-Rashid" | Senior Engineer [role: engineer] g2: "Marco Ricci" | Engineer [role: engineer] ``` **List layout** — compact directory rows with indent guides. Best for large teams where tree branching becomes unwieldy. ```schematex orgchart "Engineering Directory — list" config: layout = list cto: "Wei Zhang" | CTO [role: cto] platform: "Platform Team" p1: "Amara Diallo" | Staff Eng [role: engineer] p2: "Ben Novak" | Senior Eng [role: engineer] p3: draft "TBH" | Engineer [role: engineer] growth: "Growth Team" g1: "Fatima Al-Rashid" | Senior Eng [role: engineer, status: new] g2: "Marco Ricci" | Engineer [role: engineer, status: leaving] ``` *** ## 6. Labels & comments * **Title:** `orgchart "Acme Corp"` — first line only. * **Name field:** first pipe-delimited field after the colon; may be quoted (`"Alice Zhang"`) or unquoted (`Alice`). * **Title/department fields:** second and third pipe-delimited fields. * **Info line:** fourth pipe-delimited field, or set via `note:`, `email:`, `phone:`, or `location:` props. First one encountered wins. * **Comments:** `#` at the start of a line (after leading whitespace). Inline `//` is also stripped. *** ## 7. Reserved words & escaping **Reserved at line start:** `orgchart` (header), `config:`, `role`, `open`, `draft`, `tbh`, `advisor`, `external`. **Reserved operator tokens** — avoid these sequences inside ids: `->`, `-.->` **ID rules:** must match `[A-Za-z][A-Za-z0-9_-]*`. Names with spaces go in the quoted name field, not the id. **Strings with spaces** in props values (e.g. `department: "Platform Eng"`) must be double-quoted. *** ## 8. Common mistakes | You wrote | Parser says | Fix | | ----------------------------------------------- | --------------------------------------------------------- | ---------------------------------------------------- | | `alice: Alice Zhang` (unquoted name with space) | Parses `Alice` as the name, `Zhang` is lost or misread | Quote the name: `alice: "Alice Zhang"` | | `open1 open: "TBH"` | `open` after the id is not a kind keyword; fails id regex | Kind keyword comes first: `open open1: "TBH"` | | `alice -> bob` before either node is declared | `OrgchartParseError: Edge references unknown node` | Declare nodes first, then write edges at the end | | `config: direction = top-down` | Unknown value ignored; direction stays `TD` | Use `TD` or `LR` | | `config: layout = compact` | Accepted — maps to `list` layout | Correct; `compact`, `directory`, and `list` all work | | `alice [matrix: bob charlie]` | Space-separated ids in `matrix:` — both are added | Also works with commas: `matrix: "bob, charlie"` | | Node id with a space: `fe lead` | Parser takes `fe` as the id; `lead` fails | Use underscore: `fe_lead` | | Duplicate id | `OrgchartParseError: Duplicate node id` | Each node needs a unique id | *** ## 9. Grammar (EBNF) ```text document = header (blank | comment | config | edge | node)* header = "orgchart" ( WS quoted-string )? NEWLINE quoted-string = '"' any-char-but-quote* '"' config = "config" WS ":" WS key WS "=" WS value NEWLINE key = "direction" | "layout" node = INDENT* kind? id ":" WS fields ( "[" node-attrs "]" )? NEWLINE kind = "role" | "open" | "draft" | "tbh" | "advisor" | "external" | "person" fields = field ( "|" field )* field = quoted-string | unquoted-text node-attrs = node-attr ("," node-attr)* node-attr = "role:" role-keyword | "icon:" role-keyword | "department:" text | "status:" ( "new" | "leaving" | "on-leave" ) | "avatar-color:" quoted-hex | "gender:" ( "male" | "female" ) | "note:" text | "email:" text | "phone:" text | "location:" text | "assistant-of:" id | "matrix:" id-list | "reports:" id | bare-flag bare-flag = "open" | "draft" | "tbh" | "external" edge = id WS edge-op WS id ( "[" edge-attrs "]" )? NEWLINE edge-op = "->" | ".->" // -.-> for matrix edge-attrs = "label:" quoted-string id = [A-Za-z] [A-Za-z0-9_-]* comment = ( "#" | "//" ) any NEWLINE ``` Authoritative source: `src/diagrams/orgchart/parser.ts`. If this diverges from the parser, the parser wins — please open an issue. *** ## 10. Roadmap **Planned — not yet parseable.** Do not use these in generated DSL today; the parser will reject or ignore them. * **`assistant-of:` visual rendering** — the property is parsed and stored in the AST but the renderer does not yet draw the assistant elbow connector. * **Config `coloring`** — department-based color themes (`coloring: department`). * **Config `compact` size tiers** — explicit card-size control (`size: small | medium | large`). * **`span:` node width** — spanning a node across multiple sibling columns in tree layout. * **Photo / avatar URL** — `avatar: "https://…"` to display a real headshot. * **Export to HRIS formats** — JSON/CSV structured output alongside the SVG. Track in the GitHub issues if you need any of these sooner. *** ## Related examples Ready-to-use scenarios from the examples gallery: [Browse related examples](https://schematex.js.org/examples) ## Interactive editing Names, roles, and the title expose exact source fields. Cards move horizontally within their reporting level; vertical depth stays locked to the reporting hierarchy. --- # Pedigree Canonical URL: https://schematex.js.org/docs/pedigree Markdown URL: https://schematex.js.org/docs/pedigree.md ## About pedigrees A **pedigree chart** is a standardized diagram used in clinical genetics to trace a single condition — or a small set of related conditions — through a family across multiple generations. Unlike a genogram, which records emotional and social texture, a pedigree is purely structural: who is affected, who is a carrier, who has been tested, and who the index case is. Genetic counselors, clinical geneticists, and referring physicians use it to judge inheritance pattern, recurrence risk, and who else in the family should be offered testing. Schematex follows the **[Bennett et al. (2022) NSGC recommendations](https://onlinelibrary.wiley.com/doi/10.1002/jgc4.1621)** — the most recent update from the National Society of Genetic Counselors — which supersedes the 1995 and 2008 revisions. For background see [Wikipedia: Pedigree chart](https://en.wikipedia.org/wiki/Pedigree_chart). This page documents what the parser accepts today. ```schematex pedigree "BRCA1 — Hereditary Breast/Ovarian Cancer" I-1 [male, unaffected] I-2 [female, affected, deceased] I-1 -- I-2 II-1 [female, affected] II-2 [male, unaffected] II-3 [female, carrier] II-1 -- II-4 [male, unaffected] III-1 [female, affected, proband] III-2 [male, unaffected] III-3 [female, presymptomatic] II-3 -- II-6 [male, unaffected] III-6 [female, carrier] III-7 [male, unaffected] ``` *** ## 1. Your first pedigree The smallest clinically useful pedigree: two parents and their affected child. ```schematex pedigree I-1 [male, carrier] I-2 [female, carrier] I-1 -- I-2 II-1 [male, affected, proband] II-2 [female, unaffected] ``` Four rules cover 80% of usage: 1. Start with the keyword `pedigree`, optionally followed by a quoted title. 2. Declare each individual on their own line: `id [attributes]`. Conventionally IDs are `I-1`, `II-3`, etc. — Roman-numeral generation, dash, position within the generation. 3. Connect two individuals with a **couple operator** — `--` (mated), `==` (consanguineous), `-/-` (separated), `~` (no offspring). See §4. 4. **Indent under the couple line** to add their children. Any deeper indent works; two spaces is conventional. > Comments must be on their own line, starting with `#`, `//`, or Mermaid-style `%%`. Inline trailing comments will break the parser. *** ## 2. Individuals An individual line is `id [attr1, attr2, …]`. Attributes are comma-separated, order-independent, all optional. **ID rules.** Must match `[a-zA-Z][a-zA-Z0-9_-]*`. IDs are case-insensitive internally but preserve their original casing as the display label (override with `label:"…"`). **Attributes accepted by the parser today:** | Attribute | Values | Effect | | ------------------------- | -------------------------------------------------------------------------------------- | --------------------------------------- | | Sex | `male`, `female`, `unknown`, `amab`, `afab`, `uaab` | Shape: square, circle, diamond (see §3) | | Genetic status | `unaffected`, `affected`, `carrier`, `carrier-x`, `obligate-carrier`, `presymptomatic` | Fill / inner marker (see §3) | | Marker | `proband`, `consultand`, `evaluated` | Arrow + letter annotation (see §3.3) | | Life status | `deceased`, `stillborn`, `pregnancy`, `sab`, `tab`, `ectopic` | Visual modifier | | Birth year | 4-digit number, e.g. `1958` | Shown below shape | | `label:"…"` | any quoted string | Display label override | | `affected: trait1+trait2` | see §5 | Multi-trait quadrant fill | ```schematex pedigree I-1 [male, 1942, deceased] I-2 [female, 1945, affected, deceased] II-1 [female, affected, proband, label: "Jane (42)"] II-2 [male, evaluated] II-3 [female, presymptomatic] ``` *** ## 3. Shapes, status, markers ### 3.1 Shapes (Bennett 2022) | Visual | Sex value | Meaning | | --------- | ----------------------------- | ---------------------------------------- | | ☐ Square | `male` or `amab` | Assigned male at birth | | ○ Circle | `female` or `afab` | Assigned female at birth | | ◇ Diamond | `unknown`, `uaab`, or omitted | Unknown / DSD / not disclosed / in utero | Bennett 2022 formalized that square and circle represent **assigned sex at birth**, not gender identity. If gender identity differs, record it in the label (`[female, label: "Trans man (AFAB)"]`) — do not change the shape. ### 3.2 Genetic status (fill) | Status | Meaning | | -------------------------- | -------------------------------------------------------------------- | | (default, no status token) | Unaffected — empty shape | | `unaffected` | Explicit unaffected | | `affected` | Fully filled shape | | `carrier` | Half-filled — autosomal carrier | | `carrier-x` | Center dot — X-linked carrier female | | `obligate-carrier` | Center dot — inferred from pedigree structure | | `presymptomatic` | Vertical line through shape — tested positive, no clinical signs yet | ```schematex pedigree I-1 [male, unaffected] I-2 [female, affected] I-3 [female, carrier] I-4 [female, carrier-x] I-5 [male, obligate-carrier] I-6 [female, presymptomatic] ``` ### 3.3 Markers | Marker | Meaning | | ------------ | ------------------------------------------------------- | | `proband` | Arrow + "P" — the index case who triggered the referral | | `consultand` | Arrow + "C" — the person who sought genetic counseling | | `evaluated` | "E" — evaluated but no positive finding recorded | ### 3.4 Life status | Value | Meaning | | ----------- | ---------------------------------------------- | | `deceased` | Diagonal slash across the shape | | `stillborn` | Small shape + "SB" label | | `pregnancy` | Shape + "P" label, or diamond if sex unknown | | `sab` | Small triangle — spontaneous abortion | | `tab` | Small triangle with bar — terminated pregnancy | | `ectopic` | Small triangle + "ECT" label | Multiple tokens combine: `[female, affected, deceased]`, `[male, sab]`, `[unknown, pregnancy, presymptomatic]`. *** ## 4. Couples and children ### 4.1 Couple operators The parser tries these in order. The first match wins — so `-/-` beats `--`. | Operator | Type | Example | Meaning | | -------- | -------------- | --------- | ----------------------------------------- | | `-/-` | separated | `a -/- b` | Mated pair, no longer together | | `==` | consanguineous | `a == b` | Blood-related union (clinically critical) | | `--` | married | `a -- b` | Mated pair with offspring | | `~` | cohabiting | `a ~ b` | Partners without offspring | ### 4.2 Inline individual on the right side If the right-hand individual has not been declared yet, declare them in place: ```schematex pedigree I-1 [female, carrier] I-1 -- I-2 [male, unaffected] II-1 [female, affected, proband] II-2 [male, carrier] ``` ### 4.3 Children (indented under a couple) Indentation under a couple line = "these are the children of this couple." Any indent greater than the couple's indent works; two spaces is conventional. ```schematex pedigree "Cystic Fibrosis — autosomal recessive" I-1 [male, carrier] I-2 [female, carrier] I-1 -- I-2 II-1 [male, affected, proband] II-2 [female, carrier] II-3 [male, unaffected] ``` ### 4.4 Consanguineous unions Consanguinity is rendered as a double line and must be made visible — it is the single most load-bearing piece of information on many pedigrees. ```schematex pedigree "Consanguineous union" I-1 [male, carrier] I-2 [female, unaffected] I-1 -- I-2 II-1 [male, carrier] I-3 [male, unaffected] I-4 [female, carrier] I-3 -- I-4 II-3 [female, carrier] II-1 == II-3 III-1 [male, affected, proband] ``` *** ## 5. Multi-trait pedigrees For families that carry more than one heritable condition, use `legend:` lines to define which quadrant of the shape represents which trait, then tag individuals with `affected: trait1+trait2`. ``` pedigree "Cancer Family Syndrome" legend: breast = "Breast cancer" (fill: quad-tl) legend: ovarian = "Ovarian cancer" (fill: quad-tr) legend: prostate = "Prostate cancer" (fill: quad-bl) legend: colon = "Colon cancer" (fill: quad-br) I-1 [male, affected: prostate, deceased] I-2 [female, affected: breast, deceased] I-1 -- I-2 II-1 [female, affected: breast+ovarian] II-2 [male, unaffected] ``` **Legend syntax:** `legend: id = "Human label" (fill: POSITION)`. | `fill` position | Region | | ------------------------------------------------------- | ---------------------------------------------------------- | | `full` | Entire shape (default if `(fill: …)` omitted) | | `quad-tl` / `quad-tr` / `quad-bl` / `quad-br` | Top-left / top-right / bottom-left / bottom-right quadrant | | `half-left` / `half-right` / `half-top` / `half-bottom` | A half of the shape | Individuals use the legend trait IDs: `[affected: breast]`, `[affected: breast+ovarian]`. The `+` joins traits; quadrants fill cumulatively. *** ## 6. Labels & comments * **Title:** `pedigree "BRCA1 Family"` — first line only. * **Individual label override:** `II-1 [female, affected, label: "Jane Smith (42)"]`. * **Legend entry:** `legend: id = "Label" (fill: POSITION)` — see §5. * **Mode suffix:** `pedigree:autosomal-dominant "Family X"` is accepted. The suffix is stored as `metadata.mode`; current renderers ignore it. * **Comments:** `#`, `//`, or `%%` at the start of a line (after leading whitespace). Inline trailing comments are **not** supported. *** ## 7. Reserved words & escaping **Reserved at line start:** `pedigree` (header), `legend:` (legend entry). **Reserved operator tokens** inside a line — avoid using these sequences in IDs: `--`, `==`, `-/-`, `~`. **Reserved attribute tokens** inside `[…]` — the parser will interpret these regardless of position: sex tokens (`male`, `female`, `unknown`, `amab`, `afab`, `uaab`), genetic statuses (`affected`, `carrier`, `carrier-x`, `obligate-carrier`, `presymptomatic`, `unaffected`), markers (`proband`, `consultand`, `evaluated`), and life statuses (`deceased`, `stillborn`, `pregnancy`, `sab`, `tab`, `ectopic`). **Strings with spaces** must be double-quoted. Single quotes and backticks are not recognized. *** ## 8. Common mistakes Real parser errors, what triggers them, and how to fix. | You wrote | Parser says | Fix | | ------------------------------------------------------------- | ----------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------- | | `II-1 -- II-4` where `II-4` was never declared | `Unknown individual 'II-4'` | Declare `II-4` above, or use inline form: `II-1 -- II-4 [male, unaffected]` | | `II-1 [nonbinary]` | Silently stored as a custom property; shape stays diamond | Bennett 2022 distinguishes assigned sex from gender — use `amab`/`afab`/`uaab` and record identity in `label:` | | `II-3 [twin-mz]` | Stored as a custom property; no twin line rendered | Twin notation is §10 Roadmap | | `I-1 -- I-2` followed by `II-1 [male]` at the **same indent** | Child parsed as a new top-level individual, not as offspring | Indent the child line deeper than the couple line | | `I-1 [affected: breast]` with no matching `legend:` | Trait ID stored but no legend-keyed fill is rendered | Add `legend: breast = "…" (fill: quad-tl)` above | | `II-1[affected]` (no space, no attrs split) | Works for a single token; breaks when a second attribute is added | Always separate `id` and `[…]` with a space | | Line 1 is `I-1 [male]` with no `pedigree` header | `Expected "pedigree" header` | Start with `pedigree` or `pedigree "Title"` | *** ## 9. Grammar (EBNF) ```text document = header (blank | comment | legend | individual | couple-block)* header = "pedigree" ( ":" mode )? ( WS quoted-string )? NEWLINE mode = [A-Za-z] [A-Za-z0-9_-]* quoted-string = '"' any-char-but-quote* '"' legend = INDENT "legend:" WS id WS "=" WS quoted-string ( WS "(" "fill:" fill-value ")" )? NEWLINE individual = INDENT id ( "[" attrs "]" )? NEWLINE couple-block = INDENT id WS coupleOp WS right-side NEWLINE ( deeper-indent child )* child = INDENT id ( "[" attrs "]" )? NEWLINE right-side = id ( "[" attrs "]" )? coupleOp = "-/-" | "==" | "--" | "~" id = [a-zA-Z] [a-zA-Z0-9_-]* attrs = attr ("," attr)* attr = sex | genetic-status | marker | life-status | digit digit digit digit // birth year | "label" ":" quoted-string | "affected" ":" trait-id ( "+" trait-id )* | key ":" value // custom sex = "male" | "female" | "unknown" | "amab" | "afab" | "uaab" genetic-status = "unaffected" | "affected" | "carrier" | "carrier-x" | "obligate-carrier" | "presymptomatic" marker = "proband" | "consultand" | "evaluated" life-status = "deceased" | "stillborn" | "pregnancy" | "sab" | "tab" | "ectopic" fill-value = "full" | "half-left" | "half-right" | "half-top" | "half-bottom" | "quad-tl" | "quad-tr" | "quad-bl" | "quad-br" comment = INDENT ( "#" | "//" | "%%" ) any NEWLINE ``` Authoritative source: `src/diagrams/pedigree/parser.ts`. If this diverges from the parser, the parser wins — please open an issue. *** ## 10. Standard compliance Schematex pedigrees follow **Bennett, R.L. et al. (2022), *The evolving pedigree: Updating to reflect modern family structures, sex, and gender*** for shape semantics, status fills, and the proband/consultand conventions. Earlier revisions (Bennett 1995, 2008) remain a valid reference for the classical subset. What is implemented today vs. the standard: * ✅ Core shapes (square / circle / diamond) with Bennett 2022 AMAB/AFAB/UAAB semantics * ✅ Full genetic-status set (affected / carrier / carrier-x / obligate-carrier / presymptomatic / unaffected) * ✅ Proband, consultand, evaluated markers * ✅ Life-status modifiers (deceased, stillborn, pregnancy, SAB, TAB, ectopic) * ✅ Couple operators: mated / separated / consanguineous / no-offspring * ✅ Multi-trait quadrant fills with `legend:` entries * ⏳ Twin notation (monozygotic / dizygotic / unknown zygosity) * ⏳ Adopted-in / adopted-out bracket notation * ⏳ Assisted reproduction (donor egg/sperm/embryo, surrogate, IVF) * ⏳ No-offspring-by-choice and infertility markers References: * Bennett, R.L., French, K.S., Resta, R.G., & Austin, J. (2022). *Practice resource-focused revision: Standardized pedigree nomenclature update centered on sex and gender inclusivity.* Journal of Genetic Counseling, 31(6), 1238–1249. * Bennett, R.L., Steinhaus French, K., Resta, R.G., & Doyle, D.L. (2008). Standardized human pedigree nomenclature: Update and assessment. *J Genet Couns*, 17(5), 424–433. * Bennett, R.L. et al. (1995). Recommendations for standardized human pedigree nomenclature. *Am J Hum Genet*, 56(3), 745–752. *** ## 11. Related examples Ready-to-use scenarios from the examples gallery: [Browse related examples](https://schematex.js.org/examples) *** ## 12. Roadmap **Planned — not yet parseable.** Do not use these in generated DSL today; the parser will reject them or store them as unused custom properties. * **Twin notation** — `twin-mz` (monozygotic), `twin-dz` (dizygotic), `twin-unknown`. * **Adoption brackets** — `adopted-in`, `adopted-out` (bracketed shape, dashed line to biological parents). * **Assisted reproduction** — `donor-egg`, `donor-sperm`, `donor-embryo`, `surrogate`, and an `ivf` annotation on the couple line. * **No-offspring markers** — `no-children` (by choice) and `infertile` drop-line annotations. * **Gender-identity annotation** — a structured attribute distinct from sex, rather than stuffing it into `label:`. * **Triplets and higher-order births** — `triplet-mz`, `triplet-dz`. Track in the GitHub issues if you need any of these sooner. --- # PERT / CPM Network Canonical URL: https://schematex.js.org/docs/pert Markdown URL: https://schematex.js.org/docs/pert.md ## About PERT charts A **PERT chart** (Program Evaluation and Review Technique) — and its near-twin **CPM** (Critical Path Method) — is the foundational project-scheduling diagram of modern project management. Born in 1959 (PERT from the US Navy Polaris program, CPM from DuPont), the two converged into the **Activity-on-Node / Precedence Diagramming Method** taught by [PMI's PMBOK Guide](https://www.pmi.org/pmbok-guide-standards). You write the tasks, their durations, and which task depends on which; the schedule — Early Start, Early Finish, Late Start, Late Finish, slack, and the **critical path** — is *computed*. That last point is what makes Schematex's `pert` engine different from every drag-and-drop "PERT chart maker". Most tools ship a shape library and make you fill the six-field box and compute the critical path with a calculator. Schematex runs the forward pass and backward pass itself and highlights the critical path in red automatically. Distinct from **[flowchart](/docs/flowchart)** (no scheduling), **[timeline](/docs/timeline)** (no critical-path math), and **[bpmn](/docs/bpmn)** (an organisational process, not a one-off project schedule). ```schematex pert title: "Q3 Product Launch" unit: days task A "Market research" duration: 5 task B "Design mockups" duration: 8 after: A task C "Backend API" duration: 15 after: A task D "Frontend build" duration: 10 after: B, C task E "QA / testing" duration: 5 after: D task F "Marketing collateral" duration: 7 after: B task G "Launch event" duration: 2 after: E, F ``` *** ## 1. Your first diagram Every document starts with the `pert` keyword, an optional header, then one `task` line per activity: ``` pert unit: days task A "Market research" duration: 5 task B "Design mockups" duration: 8 after: A task C "Backend API" duration: 15 after: A task D "Frontend build" duration: 10 after: B, C ``` Each task carries an `<id>`, a quoted `<label>`, a `duration:`, and an optional `after:` list of predecessors. The engine adds an invisible Start and Finish, runs the schedule, and draws the six-field activity box for every task. You never type ES/EF/LS/LF — they are computed. The header accepts: * `title: "…"` — a heading drawn above the diagram. * `unit: days | weeks | hours | abstract` — the time unit (default `days`; purely a label, the engine is calendar-agnostic). * `direction: LR | TB` — left-to-right (default) or top-to-bottom. * `layout: network | timescaled` — the layered network (default) or a time-proportional view (§6). * `critical-tolerance: <n>` — slack ≤ this counts as critical (default `0`; set `0.001` for three-point projects). * `show-sentinels: true` — draw the synthetic Start / Finish nodes (hidden by default). *** ## 2. The six-field activity box Every task renders as the canonical 3×2 PERT/CPM rectangle: ``` ┌──────────┬────────────┬──────────┐ │ ES │ Duration │ EF │ ├──────────┴────────────┴──────────┤ │ Task Name (ID) │ ├──────────┬────────────┬──────────┤ │ LS │ Slack │ LF │ └──────────┴────────────┴──────────┘ ``` * **ES / EF** — Early Start / Early Finish, from the forward pass. * **LS / LF** — Late Start / Late Finish, from the backward pass. * **Slack** (total float) = LS − ES = LF − EF. Zero slack means the activity is on the critical path. Critical activities get a **red border** and a bold `0` slack; the project's critical chain reads as an unbroken red line. Every field is also mirrored onto `data-*` attributes (`data-es`, `data-slack`, `data-critical`, …) so you can query the SVG without re-running the math. **On the two-colour palette.** A PERT chart is deliberately drawn in just two colours. Red for the critical path is a genuine industry convention — MS Project, Oracle Primavera P6, and PMBOK figures all use it, because "critical vs. not" is the one distinction the diagram exists to surface. Everything else stays a neutral house-blue. Adding more colours would imply categories that PERT's semantics don't have; if you need to group by team or phase, use [swimlanes](#7-swimlanes-tags-classes-and-comments) (`lane:`) instead of colour. *** ## 3. Dependencies (FS / SS / FF / SF + lag/lead) `after:` takes a comma-separated list of predecessor references. The default relationship is **Finish-to-Start (FS)** — a task starts once its predecessor finishes: ``` task D "Frontend build" duration: 10 after: B, C ``` Modern scheduling needs the other three Precedence-Diagramming relationships and **lag** (delay) or **lead** (negative lag): ``` task B "Stakeholder interviews" duration: 6 after: A SS+1 # start 1d after A starts task I "Documentation" duration: 4 after: D+3 # FS with a 3-day lag task J "Translation" duration: 3 after: I SS-1 # start 1d before I starts (lead) task K "Sign-off" duration: 1 after: I FF # finishes when I finishes task L "Press release" duration: 2 after: G SF+1 # start-to-finish (rare) ``` | Form | Meaning | | ------------------------------- | --------------------------------------------------- | | `after: A` | Finish-to-Start, no lag (the common case) | | `after: A+2` or `after: A+2d` | FS with a 2-unit lag | | `after: A SS` / `A FF` / `A SF` | Start-to-Start / Finish-to-Finish / Start-to-Finish | | `after: A SS+2d` / `A FF-1d` | any type with lag (`+`) or lead (`-`) | A lag unit suffix (`d` / `w` / `h`) must match the diagram's `unit:` or be omitted; mixed units are rejected. FS with zero lag is unlabelled; SS/FF/SF always show their type on the edge. *** ## 4. Three-point (PERT) estimation Write a duration as `O/M/P` (optimistic / most-likely / pessimistic) and the engine computes the beta-distribution expected duration **te = (O + 4M + P) / 6** and the variance **σ² = ((P − O)/6)²**: ``` pert critical-tolerance: 0.01 task A "Spec" duration: 2/3/5 # te = 3.17, σ² = 0.25 task B "Build" duration: 5/8/14 after: A # te = 8.50, σ² = 2.25 task C "Test" duration: 3/4/6 after: B task D "Deploy" duration: 1/2/3 after: C ``` The Duration field shows `te`; a small `σ=…` annotation appears under the name; and the project-level standard deviation (√ of the summed critical-path variances) is reported in the footer. Use `critical-tolerance: 0.01` so floating-point `te` values don't displace the visible critical path. *** ## 5. Milestones A milestone is a zero-duration checkpoint, drawn as a diamond. Use the `milestone` flag or `duration: 0`: ``` task P "Cutover weekend" milestone after: O task Q "Go-live" duration: 0 after: P ``` *** ## 6. Time-scaled layout `layout: timescaled` switches from the layered network to a network-Gantt hybrid: each activity's **x-position is proportional to its ES** and its **width is proportional to its duration**, with a unit time axis along the bottom. Activities are packed into lanes so nothing overlaps. ``` pert layout: timescaled unit: days task A "Inventory" duration: 5 task B "Interviews" duration: 6 after: A SS+1 task C "Design" duration: 10 after: A, B task D "Build" duration: 15 after: C task E "Pilot" duration: 7 after: D ``` This is the network-flavoured time view; for a calendar Gantt chart with one row per task, use `layout: gantt` (next). ### Gantt chart (`gantt` / `layout: gantt`) Start a document with the **`gantt`** header (sugar for `pert` + `layout: gantt`) for a calendar Gantt. It is the *same scheduler* — so the bars are placed from the **computed** ES/EF, and the **critical path is drawn in red**, the thing a hand-placed Gantt (Mermaid) can't do: there you type the dates yourself, here you type dependencies and the engine schedules them. ``` gantt "Website Relaunch" start: 2026-07-01 calendar: 5day task A "Discovery" duration: 5 lane: "Plan" task B "Wireframes" duration: 8 after: A lane: "Design" task C "Visual design" duration: 6 after: B lane: "Design" progress: 40% task D "Frontend build" duration: 12 after: C lane: "Build" task E "Backend API" duration: 10 after: A lane: "Build" task F "Integration" duration: 5 after: D, E lane: "Build" task LAUNCH "Go live" milestone after: F lane: "Build" today: 2026-07-20 ``` * **`start: YYYY-MM-DD`** turns the axis into calendar dates (omit it for a numeric day-offset axis). * **`calendar: continuous`** (default) spans weekends; **`calendar: 5day`** excludes Sat/Sun from the timeline. * **`lane: "…"`** groups tasks into labelled **sections**; **`progress: 60%`** draws a completion overlay; **`milestone`** is a diamond; **`today: YYYY-MM-DD`** drops a marker line. * One row per task, in declaration order. Off-critical-path bars are drawn in the resting blue with their **slack** annotated; critical bars are red. ### Activity-on-arrow (`layout: aoa`) `layout: aoa` renders the older **activity-on-arrow** (ADM) notation you'll find in textbooks and on Investopedia: numbered **event** circles, **activities as labelled arrows**, and dotted **dummy activities** auto-inserted wherever an activity has two or more predecessors. You write the same activity-on-node DSL — Schematex builds the event graph and numbers the events for you. ``` pert layout: aoa unit: days task A "create schedule" duration: 10 task B "buy hardware" duration: 5 task C "programming" duration: 20 after: A task D "installation" duration: 5 after: B task E "conversion" duration: 15 after: D task F "test code" duration: 20 after: C, E task G "write manual" duration: 15 after: E ``` AOA is a **legacy** notation (PMBOK 7 dropped it; AON is the modern standard) and it can only express **finish-to-start** logic — SS/FF/SF and lag/lead are flattened to FS with a warning. Use it for teaching, exam prep, or matching an existing textbook figure; use the default `network` (AON) layout for real scheduling. *** ## 7. Swimlanes, tags, classes, and comments Add `lane: "…"` to any task and the network re-groups into horizontal swimlanes — by responsible team, phase, or owner — while still computing the schedule exactly as before: ``` task T1 "Support Account Deletion" duration: 3 lane: "Customer Account" task T2 "Design a New Theme" duration: 8 lane: "Shopping Site" task T3 "Apply New Theme" duration: 15 after: T2 lane: "Shopping Site" ``` Lanes appear in first-declared order, each as a banded row with a label gutter on the left. Swimlanes are a presentation of the same AON network, not a different layout mode — they activate automatically when any task declares a lane. ``` task X "External vendor work" duration: 10 after: A tags: vendor, external class: secondary # this is a comment ``` `tags:` emit `data-tag="…"` on the node group and `class:` adds a CSS class for downstream theming. `#` and `//` start a comment to end of line. *** ## 8. Grammar (EBNF) ```text document = "pert" NEWLINE header* task+ header = "title:" quoted | "unit:" ("days" | "weeks" | "hours" | "abstract") | "direction:" ("LR" | "TB") | "layout:" ("network" | "timescaled" | "aoa") | "critical-tolerance:" number | "show-sentinels:" boolean task = "task" IDENT quoted "duration:" duration ("after:" reflist)? "milestone"? attrs? | "task" IDENT quoted "milestone" ("after:" reflist)? attrs? duration = number | number "/" number "/" number ; deterministic or O/M/P reflist = ref ("," ref)* ref = IDENT (WS deptype)? laglead? | IDENT "+" number unit? ; attached FS lag sugar deptype = "FS" | "SS" | "FF" | "SF" laglead = ("+" | "-") number unit? unit = "d" | "w" | "h" attrs = ("tags:" idlist)? ("class:" IDENT)? ("lane:" quoted)? ``` *** ## 9. Standard compliance Schematex `pert` implements the **Activity-on-Node / Precedence Diagramming Method** per **PMI PMBOK 7** and Moder, Phillips & Davis (1983), with the six-field box convention from Kerzner and Oracle Primavera P6. The forward/backward pass, total slack, critical path, all four PDM dependency types with lag/lead, and three-point estimation (te + variance) are computed exactly. The older **Activity-on-Arrow** notation is available as an opt-in legacy view (`layout: aoa`) for teaching and textbook parity. Out of scope for v0.1: resource leveling / RCPSP, Monte Carlo schedule-risk simulation, calendar-aware durations, and MS Project / Primavera import-export. See `docs/reference/32-PERT-STANDARD.md` for the full specification. *** ## Related examples Ready-to-use scenarios from the examples gallery: [Browse related examples](https://schematex.js.org/examples) --- # Petri Net Canonical URL: https://schematex.js.org/docs/petri Markdown URL: https://schematex.js.org/docs/petri.md ## About Petri nets A **Petri net** is the foundational formalism for modelling concurrency, synchronisation, and resource flow in discrete-event systems — invented by [Carl Adam Petri in 1962](https://en.wikipedia.org/wiki/Petri_net) and used continuously since across computer science, control engineering, manufacturing, and business-process modelling. The notation is small and instantly recognisable: a **place** is a circle, a **transition** is a bar, an arc carries a weight, and the dynamic state of the system is a sprinkle of **tokens** sitting in the places. What makes Schematex's `petri` engine different from a drag-and-drop shape library is that it **understands the dynamics**. It knows a *marking* (which places hold how many tokens), computes which transitions are *enabled* (every input place has at least the arc weight of tokens — highlighted green), and can *fire* a transition to produce the next marking. The render is downstream of the semantics — the same stance **[pert](/docs/pert)** takes toward scheduling. Distinct from **[state diagrams](/docs/state)** (one active state, not a token distribution), **[sfc](/docs/sfc)** (a restricted safe Petri net for PLCs), and **[bpmn](/docs/bpmn)** (whose token semantics are *defined by reduction to* workflow Petri nets). ```schematex petri "Classic" place P1 *1 place P2 place P3 *2 place P4 *1 transition T1 transition T2 P1 -> T1 T1 -> P2 T1 -> P3 P2 -> T2 P3 -> T2 T2 -> P4 P4 -> T1 ``` Under the initial marking, **T1** is enabled (its inputs P1 and P4 each hold a token) so it gets the green ring; **T2** is not (P2 is empty). The `P4 -> T1` feedback arc is routed as a back-edge curve. *** ## 1. Your first net Every document starts with the `petri` keyword and an optional title, then declares **places** and **transitions** before connecting them with **arcs**: ``` petri "Minimal" place P1 *1 transition T1 place P2 P1 -> T1 T1 -> P2 ``` * `place <id>` — a circle. `*1` sets its initial token count (the marking). * `transition <id>` — a bar (an event/action). * `<a> -> <b>` — a directed arc. Arcs are **bipartite**: every arc goes place→transition or transition→place, never place→place or transition→transition. If you write one wrong, the engine tells you which line. Unlike some Schematex diagrams, nodes are **not** auto-declared from arcs — because an undeclared id can't be safely typed as a place or a transition. An arc that references an unknown node is a readable error. *** ## 2. Marking & tokens The **marking** is how many tokens each place holds. Three equivalent ways to set it: ``` place P1 *3 # *n shorthand place P2 tokens: 3 # explicit place P3 ••• # literal dots (1–4) ``` Or set several at once with a `marking:` line: ``` marking: P1=3, P3=2 ``` Tokens render as dots (up to 4, laid out in a grid) and as a numeral beyond that. Force one style with `tokens: dots | count | auto` (default `auto`). *** ## 3. Transitions: immediate vs timed ``` transition fast # immediate — a solid bar (the default) transition slow timed rate: 0.8 # timed — a hollow box with a rate label λ ``` Immediate transitions fire in zero time and render as the classic filled bar; **timed** transitions (the [GSPN](https://en.wikipedia.org/wiki/Stochastic_Petri_net) convention) render as a hollow box and carry an optional `rate:` (λ). A transition with a `rate:` is treated as timed automatically. `prio: n` sets a priority, and a `[guard]` is rendered as a label (not evaluated in v0.1). *** ## 4. Arc types Four arc heads cover the standard concurrency vocabulary: ``` P -> T # standard arc (filled arrowhead) P -> T weight: 2 # weight > 1 is labelled (weight: n or *n) P -o T # inhibitor — enabled only while the place is empty (hollow-circle head) P -- T # read / test — tests presence without consuming (no head) P => T # reset — empties the place when the transition fires (double head) ``` Inhibitor and reset arcs are **place→transition only** — the parser rejects the reverse direction. *** ## 5. Capacity A place can be capped. Firing that would overflow it is disabled, and the place is drawn with a dashed border and a `K=n` label: ``` place Buffer capacity: 3 ``` *** ## 6. The dynamics: enabled & fire The engine computes the semantics on every render: * **Enabled** transitions (every input satisfied; inhibitor inputs empty; no output would overflow capacity) get a green ring. * **Dead** transitions — those that can never fire from the current marking — are muted. * A **`fire:`** line replays a firing sequence and renders the *resulting* marking: ``` petri place P1 *1 transition T1 place P2 transition T2 place P3 P1 -> T1 T1 -> P2 P2 -> T2 T2 -> P3 fire: T1 ``` After firing `T1`, the token has moved P1 → P2, and now **T2** is the enabled transition. The SVG `<desc>` records the marking, the enabled set, and any detected subclass (state machine / marked graph / workflow net). *** ## 7. Layout & themes ``` layout: lr # left-to-right (default) layout: tb # top-to-bottom ``` Places and transitions land on alternating layers automatically; cycles are detected and their feedback arcs routed as back-edge curves. Three themes: * **`default`** — house blue-grey, with green reserved for *enabled* and red for *inhibitor*. * **`monochrome`** — the faithful Murata-1989 textbook look; enabled shows as a doubled black ring (colour falls back to shape). * **`dark`** — Schematex slate/blue dark palette. CJK labels and `「…」` / `"…"` quotes parse cleanly: ``` petri "生产流程" place 原料 *2 「原材料」 transition 加工 place 成品 原料 -> 加工 weight: 2 加工 -> 成品 ``` *** Full specification: [Petri Net Standard Reference](https://github.com/schematex/schematex/blob/main/docs/reference/34-PETRINET-STANDARD.md). *** ## Related examples Ready-to-use scenarios from the examples gallery: [Browse related examples](https://schematex.js.org/examples) ## Interactive editing The title is editable and movable. Places and transitions keep their automatic flow-layer order but move on the safe cross-axis; standard, inhibitor, read, and reset arcs remain attached. --- # Phylogenetic tree Canonical URL: https://schematex.js.org/docs/phylo Markdown URL: https://schematex.js.org/docs/phylo.md ## About phylogenetic trees A **phylogenetic tree** (also called a phylogram or cladogram) shows the inferred evolutionary history of a group of species, genes, or sequences. Internal nodes represent hypothetical common ancestors; tips represent observed taxa; branch lengths encode evolutionary distance or divergence time. Evolutionary biologists, molecular ecologists, and clinical microbiologists use phylogenetic trees to reconstruct the history of life, track pathogen outbreaks, and understand how gene families evolved. Schematex accepts trees in **[Newick format](https://phylipweb.github.io/phylip/newicktree.html)** — the universal interchange standard used by PAUP\*, IQ-TREE, RAxML, BEAST, and virtually every phylogenetics program — extended with **[NHX annotations](https://home.cc.umanitoba.ca/~psgendb/doc/atv/NHX.pdf)** for bootstrap values and clade metadata. An indentation-based DSL is also supported for hand-authored trees. This page documents what the parser accepts today. ```schematex phylo "Vertebrate Evolution" [layout: circular, mode: phylogram] newick: "(((Human:0.1,Chimp:0.08,Gorilla:0.12):0.15,(Mouse:0.45,Rat:0.42):0.3):0.05,((Dog:0.35,(Cat:0.30,Tiger:0.32):0.1):0.2,(Whale:0.6,Dolphin:0.55):0.15):0.3,(Salmon:0.5,Zebrafish:0.45):0.4);" clade Primates = (Human, Chimp, Gorilla) [color: "#1E88E5", label: "Primates", highlight: both] clade Rodents = (Mouse, Rat) [color: "#E53935", label: "Rodents", highlight: background] clade Carnivora = (Dog, Cat, Tiger) [color: "#43A047", label: "Carnivora", highlight: both] clade Cetacea = (Whale, Dolphin) [color: "#FB8C00", label: "Cetacea", highlight: background] scale "substitutions/site" ``` *** ## 1. Your first phylogenetic tree The smallest useful tree: four taxa, two clades. ```schematex phylo "Vertebrates" newick: "((Human:0.1,Chimp:0.08):0.03,(Dog:0.35,Cat:0.30):0.2);" ``` Three rules cover 80% of usage: 1. Start with `phylo`, optionally followed by a quoted title and bracket props. 2. Provide the tree topology in `newick:` format — the standard Newick string, quoted, on one line. The trailing `;` is optional. 3. Optionally define **clade** highlight groups and a **scale** label below the newick line. > Comments must start with `#` on their own line. Inline trailing comments are not supported. *** ## 2. Input formats ### 2.1 Newick format Newick is the primary input. The full grammar is: ``` (A,B,(C,D)); # topology only (A:0.1,B:0.2,(C:0.3,D:0.4):0.5); # with branch lengths ((A:0.1,B:0.2):0.05[&&NHX:B=98],(C,D):0.08); # NHX bootstrap ('Homo sapiens':0.1,'Mus musculus':0.2); # quoted names with spaces ``` Branch lengths follow the node name after a colon. Internal node support values can appear as plain brackets `[95]` or as NHX `[&&NHX:B=95]`. ```schematex phylo "Newick examples" newick: "((A:0.1,B:0.2):0.05[&&NHX:B=98],(C:0.3,D:0.4):0.08[&&NHX:B=87]);" ``` **Newick rules the parser accepts:** | Feature | Syntax | Notes | | ------------------ | ------------------- | ---------------------------------------------------- | | Leaf name | `A`, `Homo_sapiens` | No spaces — use `_` or quote | | Quoted leaf name | `'Homo sapiens'` | Single quotes; `''` is a literal quote inside | | Branch length | `:0.035` after name | Float; optional | | Internal node name | `(A,B)ancestor` | After closing `)` | | Bootstrap (plain) | `(A,B)[95]` | Integer or float in brackets | | Bootstrap (NHX) | `(A,B)[&&NHX:B=95]` | `B=` field; other NHX fields stored but not rendered | | Semicolon | `;` at end | Optional — parser strips it | | Polytomy | `(A,B,C)` | More than 2 children | ### 2.2 Indent DSL For hand-written or small trees, Schematex offers an indentation-based alternative that is easier to read and edit than raw Newick: ```schematex phylo "Vertebrates (indent DSL)" [mode: phylogram] root: :0.03 Human: 0.1 Chimp: 0.08 :0.2 Dog: 0.35 Cat: 0.30 scale "substitutions/site" ``` **Indent DSL rules:** | Syntax | Meaning | | -------------- | --------------------------------------------- | | `Name: length` | Leaf node with branch length | | `: length` | Unnamed internal node with branch length | | `Name` | Leaf node, no branch length (cladogram) | | `Name [N]` | Node with support value N | | Deeper indent | Child of the node above at a shallower indent | | `#` line | Comment, ignored | The first line that ends with `:` and has no spaces triggers indent-tree mode (e.g. `root:`). The name before the colon becomes the root label; all indented lines below become its children. *** ## 3. Layout Set the layout in the header brackets: `phylo "Title" [layout: rectangular]`. | Layout | Value | Description | | ----------- | ------------- | ------------------------------------------------------- | | Rectangular | `rectangular` | Default. L-shaped branches; root on left, tips on right | | Slanted | `slanted` | Diagonal lines from parent to child; more compact | | Circular | `circular` | Root at center, tips around the circumference | | Unrooted | `unrooted` | Equal-angle radial; emphasizes distance, not ancestry | `[unrooted]` as a bare flag is equivalent to `[layout: unrooted]`. **Circular** — root at center, tips fanning outward. Most visually striking for many-taxa trees with clade highlights. ```schematex phylo "Vertebrates — circular" [layout: circular] newick: "((Human:0.1,Chimp:0.08,Gorilla:0.12):0.15,(Dog:0.35,Cat:0.30,Wolf:0.32):0.2,(Salmon:0.5,Zebrafish:0.45):0.3);" clade Primates = (Human, Chimp, Gorilla) [color: "#1E88E5", label: "Primates", highlight: both] clade Carnivora = (Dog, Cat, Wolf) [color: "#E53935", label: "Carnivora", highlight: both] ``` **Rectangular** — L-shaped branches; root on the left, tips on the right. The classic phylogram form for published figures. ```schematex phylo "Bacterial Diversity" [layout: rectangular, mode: phylogram] newick: "((((Ecoli:0.1,Salmonella:0.12):0.05[&&NHX:B=98],Vibrio:0.2):0.08,((Bacillus:0.15,Staph:0.18):0.06[&&NHX:B=92],Listeria:0.22):0.1):0.15,((Myco_tb:0.3,Myco_leprae:0.28):0.12[&&NHX:B=100],(Strepto:0.25,Lactobacillus:0.2):0.08):0.2);" clade Gamma = (Ecoli, Salmonella, Vibrio) [color: "#1E88E5", label: "γ-Proteobacteria"] clade Firmi = (Bacillus, Staph, Listeria) [color: "#E53935", label: "Firmicutes"] scale "substitutions/site" ``` **Slanted** — diagonal lines from parent to child; more compact than rectangular, same left-to-right reading direction. ```schematex phylo "Vertebrates — slanted" [layout: slanted] newick: "((Human:0.1,Chimp:0.08,Gorilla:0.12):0.15,(Dog:0.35,Cat:0.30,Wolf:0.32):0.2,(Salmon:0.5,Zebrafish:0.45):0.3);" clade Primates = (Human, Chimp, Gorilla) [color: "#1E88E5", label: "Primates"] clade Carnivora = (Dog, Cat, Wolf) [color: "#E53935", label: "Carnivora"] scale "substitutions/site" ``` **Unrooted** — equal-angle radial layout; de-emphasizes the root, emphasizes pairwise distance between all taxa. ```schematex phylo "Vertebrates — unrooted" [unrooted] newick: "((Human:0.1,Chimp:0.08,Gorilla:0.12):0.15,(Dog:0.35,Cat:0.30,Wolf:0.32):0.2,(Salmon:0.5,Zebrafish:0.45):0.3);" clade Primates = (Human, Chimp, Gorilla) [color: "#1E88E5", label: "Primates"] clade Carnivora = (Dog, Cat, Wolf) [color: "#E53935", label: "Carnivora"] ``` *** ## 4. Mode Set with `[mode: …]` in the header (or in a `style [mode: …]` line). | Mode | Value | Branch length meaning | | ---------- | ------------ | --------------------------------------------------------------------------- | | Phylogram | `phylogram` | Default. Proportional to evolutionary distance (substitutions/site) | | Cladogram | `cladogram` | Ignored — tips align; only topology matters | | Chronogram | `chronogram` | Proportional to divergence time; all tips align to "present" | | Dendrogram | `dendrogram` | Branch length is **merge height** — the distance at which two clusters join | Chronogram requires branch lengths in units of time plus `[mrsd: "YYYY"]` (most-recent sampling date) in the header so the renderer can align tips to present. ``` phylo "SARS-CoV-2 variants" [mode: chronogram, mrsd: "2023"] newick: "((Alpha:0.5,Delta:0.4):0.3,Omicron:0.8);" scale "years" ``` **Dendrogram** — the standard output of hierarchical agglomerative clustering, not evolution. Each internal node is placed at its **merge height** (the cophenetic distance at which its two child clusters fuse), all leaves align at a common baseline, and the branches are rectangular elbow connectors. A height axis is drawn so you can read off the distance at which any two leaves first share a cluster. Reach for this mode when the same Newick/indent tree describes a clustering result — gene-expression clusters, sample similarity, survey-response groups — rather than a phylogeny. Add a `cut <value>` line to slice the tree at a chosen height: every subtree whose merge height falls below the threshold becomes one flat cluster, each colored distinctly, and a dashed threshold line is drawn across the tree at that height. This is the dendrogram equivalent of `fcluster` in scipy — turning a continuous tree into a discrete set of groups. ```schematex phylo "Gene expression clusters" [mode: dendrogram] newick: "(((A:1,B:1):2,C:3):2,(D:2,E:2):3);" cut 4 scale "cluster distance" ``` Omit `cut` to show the bare dendrogram with no flat-cluster coloring: ```schematex phylo "Sample clustering" [mode: dendrogram] newick: "(((A:1,B:1):2,C:3):2,(D:2,E:2):3);" scale "cluster distance" ``` *** ## 5. Clade highlighting A `clade` line marks a monophyletic group with a color, an optional label, and an optional highlight mode. ``` clade ID = (member1, member2, ...) [color: "#hex", label: "text", highlight: mode] ``` | Prop | Values | Effect | | ------------ | ------------------------------ | ----------------------------------------------------------------------- | | `color:` | hex string e.g. `"#1E88E5"` | Branch and/or background color | | `label:` | quoted string | Clade label shown at right margin | | `highlight:` | `branch`, `background`, `both` | `branch` colors lines; `background` shades the region; `both` does both | Members are tip (leaf) IDs from the Newick string. The renderer computes the MRCA of the listed tips and highlights the entire subtree rooted there. ```schematex phylo "Mammal clades" [layout: rectangular] newick: "(((Human:0.1,Chimp:0.08,Gorilla:0.12):0.15,Mouse:0.45):0.05,(Dog:0.35,(Cat:0.30,Tiger:0.32):0.1):0.2);" clade Primates = (Human, Chimp, Gorilla) [color: "#1E88E5", label: "Primates", highlight: both] clade Carnivora = (Dog, Cat, Tiger) [color: "#E53935", label: "Carnivora", highlight: branch] ``` *** ## 6. Scale bar and outgroup **Scale bar:** `scale "label"` — adds a bar at the bottom. The label describes the unit (e.g. `"substitutions/site"`, `"Mya"`). Omit for cladogram mode where branch lengths have no meaning. **Outgroup:** `outgroup: taxonId` — records the outgroup for documentation; the renderer may use it to visually mark the outgroup taxon. ``` phylo "Vertebrates" newick: "((Human:0.1,Chimp:0.08):0.03,Lamprey:0.8);" outgroup: Lamprey scale "substitutions/site" ``` *** ## 7. Header props reference All options go inside `[…]` on the `phylo` header line, or in a `style […]` line anywhere in the body. | Prop | Values | Default | Effect | | --------------- | ---------------------------------------------------- | ------------- | ------------------------------------------- | | `layout:` | `rectangular`, `slanted`, `circular`, `unrooted` | `rectangular` | Tree layout | | `mode:` | `phylogram`, `cladogram`, `chronogram`, `dendrogram` | `phylogram` | Branch length semantics | | `unrooted` | (flag) | — | Equivalent to `layout: unrooted` | | `branch-width:` | number | `1.5` | Stroke width of branches | | `openAngle:` | number (degrees) | `0` | Fan gap for circular layout (0 = full 360°) | | `mrsd:` | quoted year string | — | Most-recent sampling date for chronograms | *** ## 8. Labels & comments * **Title:** `phylo "Tree of Life"` — first line only. * **Scale label:** `scale "substitutions/site"` — one per document. * **Clade label:** `[label: "Primates"]` inside a `clade` line. * **Comments:** `#` at the start of a line (after leading whitespace). Inline trailing comments are not supported. *** ## 9. Common mistakes | You wrote | Parser says | Fix | | ---------------------------------------------------- | ---------------------------------------------------------------------------------------------- | ------------------------------------------------------------------ | | `newick: (A,B,C);` (unquoted) | `PhyloParseError: Phylo document must start with 'phylo'` | Quote the Newick string: `newick: "(A,B,C);"` | | Tip name with a space: `Homo sapiens:0.1` | Parsed as `Homo` — space terminates an unquoted name | Use underscore (`Homo_sapiens`) or single-quote (`'Homo sapiens'`) | | Leaf ID in `clade` doesn't match Newick name | Clade silently has 0 members; no highlight | Copy names exactly as they appear in the Newick string | | `clade X = (A, B)` with no `newick:` or indent tree | `PhyloParseError: No tree definition found` | Add a `newick:` line or an indent tree block | | `mode: chronogram` with no branch lengths | Renderer treats all lengths as 0; tips overlap at root | Add `:length` to every edge in the Newick string | | `root:` line not detected | If the `root:` line has a space in the name (e.g. `My root:`) the indent tree is not triggered | Use a single-word root label or `root:` | | Newick with internal node names: `(A,B)ancestor:0.5` | Parses fine — `ancestor` is the internal node label | Supported; internal names appear on internal nodes | *** ## 10. Grammar (EBNF) ```text document = header (blank | comment | newick-line | scale-line | outgroup-line | clade-line | style-line | cut-line | indent-line)* header = "phylo" ( WS quoted-string )? ( WS "[" props "]" )? NEWLINE quoted-string = '"' any-char-but-quote* '"' newick-line = "newick:" WS quoted-newick NEWLINE scale-line = "scale" ( WS quoted-string )? NEWLINE outgroup-line = "outgroup:" WS id NEWLINE cut-line = "cut" WS number NEWLINE // dendrogram mode: flat-cluster threshold height clade-line = "clade" WS id WS "=" WS "(" id ("," id)* ")" ( WS "[" clade-props "]" )? NEWLINE style-line = "style" WS "[" props "]" NEWLINE // Indent tree — triggered by a line ending in ":" with no spaces indent-tree = root-line indent-node* root-line = id ":" NEWLINE indent-node = INDENT ( id ":" length | ":" length | id ) ( WS "[" number "]" )? NEWLINE props = prop ("," prop)* prop = "layout:" layout-value | "mode:" mode-value | "unrooted" | "branch-width:" number | "openAngle:" number | "mrsd:" quoted-string clade-props = clade-prop ("," clade-prop)* clade-prop = "color:" quoted-string | "label:" quoted-string | "highlight:" ( "branch" | "background" | "both" ) layout-value = "rectangular" | "slanted" | "circular" | "unrooted" mode-value = "phylogram" | "cladogram" | "chronogram" | "dendrogram" // Newick grammar (embedded, parsed separately) newick = subtree ";"? subtree = leaf | internal internal = "(" subtree ("," subtree)* ")" name? nhx? length? leaf = name nhx? length? name = unquoted-name | "'" single-quoted "'") length = ":" number nhx = "[" number "]" // plain bootstrap | "[&&NHX:" nhx-pair (":" nhx-pair)* "]" nhx-pair = key "=" value id = [a-zA-Z] [a-zA-Z0-9_-]* number = /[+-]?[0-9]+(\.[0-9]+)?([eE][+-]?[0-9]+)?/ comment = INDENT "#" any NEWLINE ``` Authoritative source: `src/diagrams/phylo/parser.ts`. If this diverges from the parser, the parser wins — please open an issue. *** ## 11. Standard compliance Schematex phylogenetic trees follow the **[Newick format specification](https://phylipweb.github.io/phylip/newicktree.html)** (as documented in the PHYLIP package) for the core tree serialization, and the **[NHX (New Hampshire Extended)](https://home.cc.umanitoba.ca/~psgendb/doc/atv/NHX.pdf)** convention for bootstrap support values. The `B=` field in NHX brackets is the only NHX field rendered visually today; all other fields are parsed and stored but not displayed. What is implemented today: * ✅ Newick topology, branch lengths, quoted names, polytomies * ✅ Bootstrap values — plain `[95]` and NHX `[&&NHX:B=95]` * ✅ Rectangular, slanted, circular, and unrooted layouts * ✅ Phylogram, cladogram, and chronogram modes * ✅ Clade highlighting (branch color, background shading, both) * ✅ Scale bar * ✅ Indent DSL alternative * ⏳ Multi-tree documents (forest) — see §12 * ⏳ Time-calibrated axis for chronograms (geological scale) * ⏳ Per-tip icons or images * ⏳ NHX fields beyond `B=` (species, taxonomy, duplication events) References: * Felsenstein, J. (1986). The Newick tree format. PHYLIP documentation. * Zmasek, C.M. & Eddy, S.R. (2001). ATV: Display and manipulation of annotated phylogenetic trees. *Bioinformatics*, 17(4), 383–384. (NHX specification) * Felsenstein, J. (2004). *Inferring Phylogenies.* Sinauer Associates. *** ## 12. Related examples [Browse related examples](https://schematex.js.org/examples) *** ## 13. Roadmap **Planned — not yet parseable.** Do not use these in generated DSL today; the parser will reject or ignore them. * **Multi-tree documents** — a `phylo` file with more than one `newick:` block (e.g. gene trees vs species tree). * **Geological time axis for chronograms** — an epoch-labelled X axis (Cenozoic / Mesozoic etc.) instead of a plain numeric scale. * **Per-tip metadata** — attaching traits or colored markers to individual tips without declaring a full clade (e.g. `tip Ecoli [color: "#F00", shape: star]`). * **NHX fields beyond bootstrap** — rendering species (`S=`), duplication (`D=`), and transfer events (`Tr=`) as branch symbols. * **Tanglegram** — two trees displayed side by side with connecting lines linking corresponding tips. Track in the GitHub issues if you need any of these sooner. --- # P&ID (Piping & Instrumentation Diagram) Canonical URL: https://schematex.js.org/docs/pid Markdown URL: https://schematex.js.org/docs/pid.md ## About P\&IDs A **piping & instrumentation diagram (P\&ID)** is the engineering "wiring diagram" of a process plant — every vessel, pump, heat exchanger, valve, and instrument loop, drawn with standardized symbols and connected by piping and signal lines. Process engineers, control engineers, and HSE auditors all read the same P\&ID to specify the plant, commission it, run hazard reviews (HAZOP), and operate it in production. They are the legally-required engineering deliverable for chemical, petrochemical, pharmaceutical, water-treatment, and power generation projects under OSHA PSM and EPA RMP. Schematex implements the **[ANSI/ISA-5.1-2009](https://www.isa.org/products/ansi-isa-5-1-2009-instrumentation-symbols-and-iden)** symbol catalog (instrument bubbles, tag letter codes, signal-line types) plus equipment symbols from **[ISO 10628-1:2014](https://www.iso.org/standard/51840.html)** (vessels, columns, pumps, heat exchangers). The DSL is intentionally compact so an LLM can generate a control-loop P\&ID from a process description in one shot. ```schematex pid "Pump with Flow Control" equip T-101 : tank_atm [tag: "Feed Tank"] equip P-101 : pump_centrifugal [tag: "P-101"] equip V-101 : valve_control [tag: "V-101"] equip F-101 : filter [tag: "Filter"] equip V-201 : vessel_v [tag: "V-201"] line L1 from T-101.bottom to P-101.in [size: "4\\"", service: "water", type: "process"] line L2 from P-101.out to V-101.in [size: "4\\"", type: "process"] line L3 from V-101.out to F-101.in [size: "4\\"", type: "process"] line L4 from F-101.out to V-201.in [size: "4\\"", type: "process"] inst FT-101 : field_discrete measures P-101 inst FIC-101 : cr_shared controls V-101 ``` *** ## 1. Your first P\&ID A minimal P\&ID has at least one piece of equipment and one process line. ```schematex pid equip T-1 : tank_atm equip P-1 : pump_centrifugal equip V-1 : vessel_v line L1 from T-1.bottom to P-1.in [size: "2\\"", type: "process"] line L2 from P-1.out to V-1.in [size: "2\\"", type: "process"] ``` Three rules cover 80% of usage: 1. Start the document with `pid` (optional title and `[direction: LR]` attrs). 2. Declare each piece of equipment: `equip <ID> : <type> [tag: "label"]`. 3. Connect them with `line <ID> from <equip>.<port> to <equip>.<port> [type: "process", size: "4\""]`. Instrumentation is added separately with `inst <TAG> : <category>` plus indented `measures` / `controls` clauses. > Comments use `#` at the start of a line. *** ## 2. Equipment The `equip` statement declares process equipment. The catalog follows ISO 10628 / ISA-5.1 conventions. ``` equip T-101 : tank_atm [tag: "Feed Tank"] equip P-101 : pump_centrifugal equip E-201 : hx_shell_tube [tag: "Overhead Cond"] equip T-201 : column_tray [tag: "Stripper"] ``` ### 2.1 Equipment catalog | Type | Symbol | Purpose | | ------------------ | ------------------------------------ | ----------------------------- | | `tank_atm` | Cylinder + dome top | Atmospheric storage tank | | `tank_cone_roof` | Cylinder + cone roof | Cone-roof storage tank | | `vessel_v` | Vertical capsule | Vertical pressure vessel | | `vessel_h` | Horizontal capsule | Horizontal pressure vessel | | `sphere` | Filled circle | LPG / ammonia sphere | | `column_tray` | Tall capsule + horizontal tray lines | Distillation tray column | | `column_packed` | Tall capsule + cross-hatch | Packed absorption column | | `hx_shell_tube` | Horizontal capsule + tube bundle | Shell-and-tube heat exchanger | | `hx_air_cooled` | Rectangle + fan circle | Air-cooled (fin-fan) cooler | | `reboiler` | Capsule + parallel tube lines | Kettle reboiler | | `condenser` | Horizontal capsule + tubes | Overhead condenser | | `pump_centrifugal` | Circle + right-side triangle outlet | Centrifugal pump | | `pump_pd` | Circle + internal gears | Positive-displacement pump | | `compressor` | Trapezoid (narrow on right) | Centrifugal compressor | | `blower` | Circle + 3-blade fan | Blower / fan | | `reactor_cstr` | Vertical capsule + agitator | Stirred tank reactor (CSTR) | | `reactor_pfr` | Horizontal capsule + packed bed dots | Plug-flow / fixed-bed reactor | | `filter` | Rectangle + diagonal hatch | Filter | | `cyclone` | Cylinder + cone bottom | Cyclone separator | | `flare` | Tall stack + flame | Flare stack | | `cooling_tower` | Hourglass | Induced-draft cooling tower | ### 2.2 Valve catalog Valves are equipment that sit on the piping line. Render in `bowtie` style with type-specific actuator decoration. | Type | Decoration | Purpose | | ----------------- | ---------------------------------- | ----------------------------------------- | | `valve_gate` | Plain bowtie | Manual on/off (full-port) | | `valve_ball` | Bowtie + filled center circle | Manual on/off (quarter-turn) | | `valve_globe` | Bowtie + small top circle | Manual flow control | | `valve_butterfly` | Bowtie + center vertical line | Quarter-turn throttle | | `valve_check` | Bowtie + arc | Non-return check valve | | `valve_control` | Bowtie + diaphragm actuator | Pneumatic control valve (paired with FIC) | | `valve_psv` | Bowtie + 45° outlet + spring stack | Pressure safety relief valve | ``` equip V-101 : valve_control [tag: "V-101 (FC)"] equip V-303 : valve_psv [tag: "V-303 · 150 psig"] ``` *** ## 3. Piping & signal lines The `line` statement connects two anchor points (equipment ports or instrument tags). ``` line L1 from T-101.bottom to P-101.in [size: "4\"", service: "water", type: "process"] line s1 from FT-101 to FIC-101 [type: "electric"] line s2 from FIC-101 to V-101 [type: "pneumatic"] ``` ### 3.1 Anchor syntax Each end of a line is either: * `<equip-id>.<port>` — port name from §2.2 (`in`, `out`, `top`, `bottom`, `feed`, `shell_in`, `tube_out`, `reflux`, etc.) * `<equip-id>` — port omitted; defaults to `in` (target) / `out` (source) per equipment family * `<inst-tag>` — instrument bubble center (signal lines) ### 3.2 Line types (ISA-5.1 §5) | `type:` | Stroke | Use | | --------------- | --------------------------- | ---------------------------- | | `process` | Solid, thick | Major process line (default) | | `process_minor` | Solid, thin | Auxiliary / utility | | `pneumatic` | Solid + diagonal tick marks | Air-actuator signal | | `electric` | Long-dash | Electric / 4–20 mA signal | | `hydraulic` | Long-dash + pause | Hydraulic actuator | | `capillary` | Dotted (round caps) | Filled-system temperature | | `software` | Short-dash, light | DCS / PLC internal data link | | `mechanical` | Mixed dash | Mechanical linkage | ### 3.3 Line tags The standard PIP PIC001 tag format is `<size>"-<service>-<sequence>-<spec>`. Pass it via the `tag:` attribute and the renderer places a small white tag rectangle at the line midpoint. ``` line L1 from T-101.bottom to P-101.in [size: "4\"", service: "PG", tag: "4\"-PG-101-A1B"] ``` *** ## 4. Instrumentation (ISA-5.1 §4) The `inst` statement declares an instrument bubble. The **tag** uses the ISA letter-code convention: first letter is the *measured variable*, subsequent letters are *modifiers* and *function*. ``` inst FT-101 : field_discrete %% Flow Transmitter, loop 101 inst FIC-101 : cr_shared %% Flow Indicating Controller (DCS) inst PSHH-301: cr_plc %% Pressure Switch High-High (PLC) inst LIC-201 : cr_shared measures D-201 controls V-202 ``` ### 4.1 Letter codes (first letter) Most-used: `F` flow · `L` level · `P` pressure · `T` temperature · `A` analysis · `S` speed · `H` hand · `Y` event/state. Full list in ISA-5.1 Table 1. ### 4.2 Function modifiers `I` indicator · `R` recorder · `C` controller · `T` transmitter · `E` element · `V` valve · `S` switch · `A` alarm · `H`/`L` high/low. Combine into the multi-letter tag: `FIC` = Flow Indicating Controller; `PSHH` = Pressure Switch High-High. ### 4.3 Bubble categories ISA-5.1 distinguishes **location** (where the instrument lives) and **type** (analog vs. shared vs. computer vs. PLC). Schematex implements the four most common combinations: | Category | Bubble shape | Use | | ---------------- | -------------------------------------------- | ---------------------------------------- | | `field_discrete` | Plain circle | Field-mounted analog instrument (FT, PT) | | `cr_shared` | Circle + horizontal line + inscribed hexagon | DCS-controlled HMI display | | `cr_computer` | Circle + horizontal line + inscribed diamond | Computer function (FY, calculation) | | `cr_plc` | Circle + horizontal line + inscribed square | PLC-driven logic | `field_*` variants omit the horizontal centerline; `local_*` variants use a dashed centerline; `cr_*` variants use a solid centerline indicating "main control panel — front." ### 4.4 measures / controls Indented under an `inst` declaration: | Clause | Effect | | --------------------- | ------------------------------------------------------------------------------------------------ | | `measures <equip-id>` | Auto-routed dashed-electric signal line from the equipment to the bubble | | `controls <equip-id>` | Auto-routed pneumatic signal line from the bubble to the equipment (typically a `valve_control`) | ``` inst FT-101 : field_discrete measures P-101 inst FIC-101 : cr_shared controls V-101 ``` These auto-signals are independent of the explicit `line` statements — they get rendered with the appropriate signal-line style based on the relation type. *** ## 5. Layout direction The default direction is **`LR`** (left-to-right) — process feed enters on the left, product exits on the right. Override on the header: ``` pid "Distillation Tower" [direction: TB] equip T-201 : column_tray … ``` The MVP layout places equipment in declaration order along the primary direction with Manhattan signal-line routing. **Multi-row / parallel-flow layouts and tee junctions are roadmap items** — see §9. *** ## 6. Worked example: Distillation column A real overhead-condenser loop with reboiler, reflux drum, and instrumentation: ```schematex pid "Distillation T-201" equip T-201 : column_tray [tag: "T-201"] equip E-201 : condenser [tag: "Overhead Cond"] equip D-201 : vessel_h [tag: "Reflux Drum"] equip P-201 : pump_centrifugal [tag: "Reflux Pump"] equip E-202 : reboiler [tag: "Reboiler"] line L1 from T-201.top to E-201.shell_in [size: "8\\"", service: "vapor", type: "process"] line L2 from E-201.shell_out to D-201.in [size: "8\\"", type: "process"] line L3 from D-201.bottom to P-201.in [size: "3\\"", type: "process"] line L4 from P-201.out to T-201.reflux [size: "3\\"", type: "process"] line L5 from T-201.bottom to E-202.in [size: "6\\"", type: "process"] inst PT-201 : field_discrete measures T-201 inst LIC-201 : cr_shared measures D-201 inst TIC-201 : cr_shared measures T-201 ``` *** ## 7. Grammar (EBNF) ```text document = header statement* header = "pid" ( title )? ( "[" attrs "]" )? NEWLINE attrs = attr ("," attr)* attr = "direction:" ("LR" | "TB") | "units:" ("imperial" | "metric") statement = comment | equipment-decl | line-decl | instrument-decl equipment-decl = "equip" ID ":" equip-type ( "[" attr-list "]" )? NEWLINE equip-type = "tank_atm" | "tank_cone_roof" | "vessel_v" | "vessel_h" | "sphere" | "column_tray" | "column_packed" | "hx_shell_tube" | "hx_air_cooled" | "reboiler" | "condenser" | "pump_centrifugal" | "pump_pd" | "compressor" | "blower" | "reactor_cstr" | "reactor_pfr" | "filter" | "cyclone" | "flare" | "cooling_tower" | "valve_gate" | "valve_ball" | "valve_globe" | "valve_butterfly" | "valve_check" | "valve_control" | "valve_psv" line-decl = "line" ID "from" anchor "to" anchor ( "[" attr-list "]" )? NEWLINE anchor = ID ( "." port )? port = "in" | "out" | "top" | "bottom" | "left" | "right" | "feed" | "reflux" | "shell_in" | "shell_out" | "tube_in" | "tube_out" | "vapor_out" | "liquid_out" | "bottom_return" instrument-decl = "inst" tag ":" inst-category ( "[" attr-list "]" )? NEWLINE ( indented "measures" anchor NEWLINE )* ( indented "controls" ID NEWLINE )* tag = letter-code "-" loop-num %% e.g., "FIC-101" inst-category = "field_discrete" | "field_shared" | "field_computer" | "field_plc" | "cr_discrete" | "cr_shared" | "cr_computer" | "cr_plc" | "local_discrete" | "local_shared" attr-list = attr ("," attr)* attr = key ":" value key = "tag" | "size" | "service" | "type" | "set_pressure" | "actuator" | "fail" | "trays" | … value = quoted-string | bare-word ID = [A-Za-z] [A-Za-z0-9_-]* ``` Authoritative source: `src/diagrams/pid/parser.ts`. If this diverges from the parser, the parser wins — please open an issue. *** ## 8. Standard compliance What is implemented today (P0 MVP): * ✅ 22 process-equipment symbols (vessels, columns, pumps, exchangers, reactors, separators, flare, cooling tower) * ✅ 7 valve symbols (gate, ball, globe, butterfly, check, control with diaphragm actuator, PSV with diagonal outlet + spring) * ✅ 4 instrument-bubble categories × 2 location classes = 8 ISA-5.1 bubble variants (field/CR × discrete/shared/computer/PLC) * ✅ ISA letter-code tag parsing (`FT-101`, `LIC-203`, `PSHH-301`) * ✅ 8 line-type styles (process / process\_minor / pneumatic / electric / hydraulic / capillary / software / mechanical) * ✅ Auto-routed `measures` and `controls` signal lines * ✅ Line tags rendered as white-bg rectangles at line midpoint * ✅ Manhattan routing, single-row equipment layout **Not yet implemented** (see roadmap): * ⏳ Multi-row / parallel-flow placement (e.g. two pumps merging into a mixer) * ⏳ Tee junctions and branch piping * ⏳ Crossing detection (jumper hops at pipe crossings) * ⏳ Function block overlays (Σ summer, PID, selectors) * ⏳ Interlock diamond and permissive circle (ISA-5.06) * ⏳ Heat-traced / jacketed line decoration * ⏳ Reducer (concentric / eccentric) in-line * ⏳ Nozzle annotations on vessels References: * ANSI/ISA-5.1-2009 — *Instrumentation Symbols and Identification* (US standard) * ISO 10628-1:2014 — *Diagrams for the chemical and petrochemical industry* (international) * ISA-5.06.01-2007 — *Functional Requirements Documentation for Control Software* * PIP PIC001 — Piping & Instrumentation Diagram Documentation Criteria (industry supplement) *** ## 9. Roadmap The MVP P\&ID covers a single linear control loop (tank → pump → control valve → instrumentation). Real plants need 2D placement and tee junctions. Planned for v0.4: * **DSL extension**: `equip ... [row: 0, col: 2]` lane / grid hints for multi-flow layouts * **`tee` primitive**: `tee T1 on L1` to express a 3-way branch on a process line * **`junction` primitive**: ISA junction dot for piping connection * **Crossing detection**: when two pipes cross, render the lower one with a small arc bump * **Function blocks**: `Σ` summer, `PID` controller, `LS`/`HS` low/high selector * **Interlock symbols**: `interlock I-301` diamond, `permissive P-202` circle * **Reducer in-line**: concentric / eccentric reducers as line decorations * **Nozzle list**: side-port annotations on vessel symbols (`N1`, `N2`, …) For now, single-loop control schemes render cleanly out of the box; complex multi-stream P\&IDs need manual position hints (deferred). *** ## Related examples Ready-to-use scenarios from the examples gallery: [Browse related examples](https://schematex.js.org/examples) ## Interactive editing The title is editable and movable. Equipment and instruments move freely; process and signal endpoints stay attached during the gesture and are recomputed from named ports after the drop. --- # Sports playbook Canonical URL: https://schematex.js.org/docs/playbook Markdown URL: https://schematex.js.org/docs/playbook.md ## About sports playbooks A **sports playbook** is the coach's diagram of a single play, set, or team shape — dots for players, lines for movement, drawn in a notation every coach reads at a glance. Schematex renders one from text for the three biggest team sports: **American football** (X\&O play diagrams), **basketball** (half-court sets), and **soccer / association football** (team shapes and movement patterns). Each sport is drawn in its own coaching-standard notation on its own correctly-scaled field, court, or pitch. You name a sport and a formation; the engine places the players. You add movement verbs (`route`, `pass`, `cut`, `dribble`, `run`, `screen`, `shot`); the engine draws each in the line style that sport's coaches actually use. Unlike an image generator, the output is *editable* — adding a fourth receiver or moving a screen is a one-line change. ```schematex playbook "Four Verticals" sport football field down 2 distance 7 los 40 formation spread defense cover-2 route X go route H seam route Y seam route Z go route RB flat right ``` *** ## 1. Your first play Every diagram starts with a header naming the **sport**, then a **formation** (which places the players), then **assignments**: ```schematex playbook "Give & Go" sport basketball set 5-out pass 1 2 cut 1 rim pass 2 1 ``` `pass 1 2` draws a pass from player 1 to player 2; `cut 1 rim` sends player 1 to the rim. Basketball draws passes **dashed** and cuts **solid** — the convention on every coaching whiteboard. *** ## 2. The three sports Pick the sport in the header (`sport football|basketball|soccer`). Each uses its real unit and the conventional coaching viewpoint: | Sport | Unit | View | Surface | | ------------ | ------ | ------------------------------------------------------------------- | --------------------------------------------- | | `football` | yards | offense at the bottom attacking **up**; downfield = up | green field with yard lines, hashes, end zone | | `basketball` | feet | NBA **half-court**; baseline + hoop at the top | light maple hardwood | | `soccer` | metres | full **105 × 68 m** pitch (attack toward the right); or `view half` | green pitch with IFAB markings | *** ## 3. Players & formations The fastest way to place players is a **formation** (football/soccer) or **set** (basketball): * **Football** — `formation i-form | shotgun | singleback | pistol | spread | trips | empty | goal-line | wishbone` with optional strength `left`/`right`. Receivers are `X Z H Y` (Y = tight end), backs `QB RB FB`, line `LT LG C RG RT`. * **Basketball** — `set horns | 1-4-high | 1-4-low | box | spread-pnr | 4-out | 5-out`. Players are numbered `1`–`5`. * **Soccer** — `formation 4-3-3 | 4-4-2 | 4-2-3-1 | 4-5-1 | 4-4-1-1 | 3-5-2 | 3-4-3`. Players are numbered `1` (GK) … `11`. For set-pieces or free-form diagrams, place players individually and crop to a half: ```schematex playbook "Overlap & Cross" sport soccer view half player 7 o at 75,12 label 7 player 2 o at 60,15 label 2 player 9 o at 88,30 label 9 player 11 o at 85,52 label 11 player 10 o at 80,38 label 10 dribble 7 to 82,20 run 2 to 90,8 pass 7 to 90,8 pass 2 to 102,34 run 9 to 101,31 run 11 to 101,40 ``` *** ## 4. Movement verbs & line styles The same line style means different things in different sports — Schematex draws each sport's own convention, and the legend always matches: | Verb | Football | Basketball | Soccer | | ------------------ | -------------- | ---------------- | ---------------- | | `pass` | dashed (throw) | **dashed** | **solid** | | `run` / `cut` | solid | **solid** (cut) | **dashed** (run) | | `dribble` | — | wavy | wavy | | `screen` / `block` | T-bar ⊥ | T-bar ⊥ (screen) | T-bar ⊥ | | `shot` | — | solid | double line | **Note the inversion:** basketball draws a pass *dashed* and a cut *solid*; soccer draws a pass *solid* and a run *dashed*. That is how the two coaching communities actually diagram — Schematex honours each. Move targets can be a **player id**, a **landmark** name, or explicit **coordinates** (`to x,y`). *** ## 5. Football — routes, runs, defense Pass routes use the **route tree**: `go fly streak slant flat hitch out in dig curl comeback corner post wheel cross drag seam`. Run concepts: `dive iso power counter sweep toss draw trap`. Blocking uses `block`, `pull`, and `handoff`. Set `goal N` to draw the end zone and goalposts: ```schematex playbook "Red Zone — Play-Action Fade" sport football field down 1 distance 5 los 5 goal 5 hash nfl formation i-form right defense cover-1 handoff QB RB route Z corner 4 route X slant route Y out 3 run RB dive right ``` `defense cover-0/1/2/3/4/6` draws the coverage shell; `defense 4-3 | 3-4 | nickel | dime` sets the front. `hash nfl|college|none` controls the hash marks. *** ## 6. Basketball — sets, landmarks, screens Cuts and passes target **named landmarks** — `rim elbow wing corner short-corner block slot top high-post dunker` (prefix `l`/`r` for left/right). `screen A B` draws a ball-screen (T-bar) for player B; `dribble` is a wavy line: ```schematex playbook "Spread Pick & Roll" sport basketball set spread-pnr screen 5 1 dribble 1 to 11,17 cut 5 rim pass 1 2 ``` `defense man` matches each defender to a man; `defense zone-2-3 | zone-3-2 | zone-1-3-1` draws a zone front. *** ## 7. Soccer — shapes, runs, build-up A formation alone draws the team shape. Add `pass` (solid), `run` (dashed), and `dribble` (wavy) to show a phase of play: ```schematex playbook "Build-Up From the Back" sport soccer formation 4-3-3 pass 1 4 pass 4 2 run 2 to 40,10 pass 4 6 run 6 to 62,24 ``` Landmarks include `box top-box d penalty-spot near-post far-post six-yard center`. `defense low-block | mid-block | high-press` overlays the opponent's shape. Soccer renders daylight-only — `theme: dark` falls back to the default pitch. *** ## 8. Validation The engine rejects the mistakes models actually make and lists the valid options: * unknown `sport`, `formation` / `set`, `defense`, or named route; * a move referencing an undeclared player id; * a malformed coordinate or a missing `to` target. Softer issues (e.g. a zero-length move) render with a warning rather than failing. *** ## 9. Grammar (EBNF) ```text playbook = "playbook" string "sport" sport NL { stmt NL } ; sport = "football" | "basketball" | "soccer" ; stmt = field | formation | defense | player | move | zone | "view" view ; field = "field" { "down" num | "distance" num | "los" num | "goal" num | "hash" hash | "view" view } ; formation = ( "formation" | "set" ) name [ "left" | "right" ] ; defense = "defense" scheme ; player = "player" id pos "at" coord "label" text ; move = route | run | pass | cut | dribble | screen | shot | motion | handoff | pull | block ; route = "route" id namedRoute [ num ] [ "left" | "right" ] ; run = "run" id ( concept [ "left" | "right" ] | "to" coord ) ; pass = "pass" id ( id | landmark | "to" coord ) ; cut = "cut" id ( landmark | "to" coord ) ; dribble = "dribble" id "to" coord ; screen = "screen" id id ; shot = "shot" id [ "to" coord ] ; zone = "zone" coord coord string ; coord = num "," num ; view = "full" | "half" ; hash = "nfl" | "college" | "none" ; ``` *** ## Related examples Five canonical plays per sport ship as examples — Four Verticals, Mesh, Smash, Power O, and a Red-Zone fade for football; Pick & Roll, Horns, Give & Go, Floppy, and a Backdoor cut for basketball; 4-3-3 shape, Build-Up, Overlap, High Press, and Counter-Attack for soccer. --- # PRISMA 2020 flow diagram Canonical URL: https://schematex.js.org/docs/prisma Markdown URL: https://schematex.js.org/docs/prisma.md ## About PRISMA flow diagrams The **PRISMA 2020 flow diagram** (Page MJ et al., *BMJ* 2021;372:n71) is the mandatory figure in every systematic review, meta-analysis, and scoping review published in BMJ, Lancet, JAMA, Cochrane, JBI, and 200+ other journals. It is a *single, rigid, four-row* figure — **Identification → Screening → Eligibility → Included** — with record counts in every box and parallel "excluded" side-boxes. It is conceptually a flowchart, but the layout, mandatory `n =` fields, and the dual-pipeline variant are *prescribed*. Schematex ships `prisma` as a separate, opinionated engine so the author writes counts and exclusion reasons and the diagram is **correct by construction** — you cannot accidentally omit a count or mis-order a stage. Spec: [`28-PRISMA-STANDARD.md`](https://github.com/SchemaTex/SchemaTex/blob/main/docs/reference/28-PRISMA-STANDARD.md). Distinct from [flowchart](/docs/flowchart): a generic flowchart has no notion of mandatory stages, record counts, exclusion side-boxes, or the dual-pipeline merge. Use `prisma` whenever the figure is a PRISMA review flow. ```schematex prisma mode: 2020-single title: Effect of exercise on chronic low-back pain — SR identification: databases: n: 1418 sources: PubMed=600, Embase=450, Cochrane=184, Web of Science=184 duplicates-removed: 318 screening: records-screened: 1100 excluded: n: 870 reasons: irrelevant title=750, non-English=120 eligibility: full-text-assessed: 230 excluded: n: 195 reasons: wrong population=80, wrong intervention=60, wrong outcome=55 included: studies: 35 reports: 38 ``` *** ## 1. Your first diagram The minimum is the four stage blocks. Counts are mandatory; the parser refuses to lay out a diagram with a missing total. ``` prisma identification: databases: n: 1000 screening: records-screened: 900 excluded: n: 600 eligibility: full-text-assessed: 300 excluded: n: 250 included: studies: 50 ``` Indentation is significant — **two spaces per level**, like genogram and SLD. The first non-blank line must be `prisma`. Comments use `#` or `//`. *** ## 2. Meta lines Top-level `key: value` lines, written before the stage blocks: ``` prisma mode: 2020-single kind: systematic-review title: My review validate-counts: warn ``` | Key | Values | Default | Meaning | | ----------------- | ------------------------------------------------------ | ------------------- | ------------------------------------------------------- | | `mode` | `2020-single` · `2020-dual` · `2009` | `2020-single` | Single column, or dual ("other methods") column. | | `kind` | `systematic-review` · `scoping-review` · `ipd` · `nma` | `systematic-review` | Swaps stage vocabulary (see §6). | | `title` | string | — | Rendered above the diagram. | | `validate-counts` | `warn` · `strict` · `off` | `warn` | Arithmetic checking (see §7). | | `direction` | `TB` / `TD` | `TB` | PRISMA is vertical by standard; horizontal is rejected. | *** ## 3. Identification The `identification:` block holds a `databases:` sub-block (always) and an optional `other:` sub-block (dual mode). ``` identification: databases: n: 1418 sources: PubMed=600, Embase=450, Cochrane=184 duplicates-removed: 318 ineligible-automation: 0 other-removed: 0 ``` * `n:` — total records identified (**mandatory**). * `sources:` — `name=count` pairs, comma-separated. Rendered as an indented breakdown. Names with spaces or punctuation can be quoted: `"Web of Science"=184`. * `duplicates-removed:`, `ineligible-automation:`, `other-removed:` — optional removal counts. When any are present they render as a separate **"Records removed before screening"** box in the right column, connected by a horizontal arrow. Large numbers may use commas: `n: 1,418` is the same as `n: 1418`. *** ## 4. Screening & Eligibility Both stages carry a main count plus an `excluded:` block. The excluded block has its own `n:` and an optional `reasons:` breakdown. ``` screening: records-screened: 1100 excluded: n: 870 reasons: irrelevant title=750, non-English=120 reports-sought: 226 # optional reports-not-retrieved: 12 # optional eligibility: full-text-assessed: 230 excluded: n: 195 reasons: wrong population=80, wrong intervention=60, wrong outcome=55 ``` `reasons:` are `name=count` pairs. If you list more than 8, the renderer sorts them descending and aggregates the tail as `Other (n = …)` so the side-box stays readable. *** ## 5. Included ``` included: studies: 35 reports: 38 # one study may yield several reports participants: 28741 # PRISMA-IPD only ``` `studies:` is mandatory. `reports:` and `participants:` are optional extra count lines. *** ## 6. Dual pipeline & review kinds **Dual pipeline** — the PRISMA 2020 update added a second "Identification via other methods" column (citation searching, hand searches, expert recommendations). Add an `other:` block; the two columns merge into Screening via a Y-junction. ``` prisma mode: 2020-dual identification: databases: n: 1234 duplicates-removed: 254 other: n: 56 sources: citation-search=30, hand-search=20, expert-recommendation=6 screening: records-screened: 1036 excluded: n: 810 eligibility: full-text-assessed: 226 excluded: n: 195 included: studies: 31 ``` **Scoping review** — `kind: scoping-review` swaps "studies" → "sources of evidence" and re-labels the stages per Tricco et al. 2018, without changing geometry. **Updated review** — an optional `previous-studies:` block draws a dashed box on top that feeds into the identification section: ``` previous-studies: n: 19 sources: previous review=19 ``` *** ## 7. Count arithmetic validation With `validate-counts: warn` (default) the engine checks that the counts reconcile across stages — e.g. `databases.n + other.n − duplicates-removed = records-screened`, and that source/reason breakdowns sum to their totals. Mismatches render a small warning under the diagram (also surfaced in the SVG `<desc>` for screen readers). `validate-counts: strict` turns a mismatch into a parse error with an "off by N" message. `off` skips checking entirely. *** ## 8. Grammar (EBNF) ``` prisma-document = "prisma", { meta-line }, stage-block, { stage-block } ; meta-line = ("mode:" | "kind:" | "title:" | "review-id:" | "validate-counts:" | "direction:") value ; stage-block = previous-block | identification-block | screening-block | eligibility-block | included-block ; previous-block = "previous-studies:" , indent, "n:" int, [ "reports:" int ], { "sources:" pairs } ; identification-block = "identification:" , indent, "databases:" , indent, "n:" int, { "sources:" pairs }, [ "duplicates-removed:" int ], [ "ineligible-automation:" int ], [ "other-removed:" int ], [ "other:" , indent, "n:" int, { "sources:" pairs } ] ; screening-block = "screening:" , indent, "records-screened:" int, "excluded:" , indent, "n:" int, { "reasons:" pairs }, [ "reports-sought:" int ], [ "reports-not-retrieved:" int ] ; eligibility-block = "eligibility:" , indent, "full-text-assessed:" int, "excluded:" , indent, "n:" int, { "reasons:" pairs } ; included-block = "included:" , indent, "studies:" int, [ "reports:" int ], [ "participants:" int ] ; pairs = pair, { "," pair } ; pair = (string | quoted) "=" int ; int = digit, { digit | "," } ; (* commas stripped: 1,234 == 1234 *) ``` Indentation is two spaces per level. Unknown keys inside a stage block are a parse error, keeping each stage well-defined. *** ## 9. Standard compliance Follows the **PRISMA 2020 statement** (Page MJ, McKenzie JE, Bossuyt PM, et al. *The PRISMA 2020 statement: an updated guideline for reporting systematic reviews.* BMJ 2021;372:n71) and the reference renderer conventions of the **PRISMA2020 R package** (Haddaway et al. 2022). Scoping-review vocabulary follows **PRISMA-ScR** (Tricco et al. 2018); the participants count follows **PRISMA-IPD** (Stewart et al. 2015). Template: [prisma-statement.org/prisma-2020-flow-diagram](https://www.prisma-statement.org/prisma-2020-flow-diagram). *** ## Related examples Ready-to-use scenarios from the examples gallery: [Browse related examples](https://schematex.js.org/examples) --- # Reliability Block Diagram Canonical URL: https://schematex.js.org/docs/rbd Markdown URL: https://schematex.js.org/docs/rbd.md ## About reliability block diagrams A **reliability block diagram (RBD)** shows the **success logic** of a system: each component is a block on a path from an input node to an output node, and the system *works* as long as an unbroken path of working blocks connects the two. Components in **series** all have to work; components in **parallel** give redundancy (any one suffices); a **k-of-n** group works when at least *k* of its *n* members do. It is the standard tool of RAMS (reliability, availability, maintainability, safety) engineering, defined by **[IEC 61078:2016](https://webstore.iec.ch/publication/24566)** and MIL-HDBK-338B. Schematex's edge is that the engine **computes the answer**, not just the picture: from the per-block reliabilities it reduces the structure to the **system reliability** (∏ for series, 1−∏(1−Rᵢ) for parallel, exact enumeration for k-of-n), derives the **Birnbaum reliability importance** of every block (which one moves the needle most), and flags **single points of failure** — blocks whose failure alone fails the system — in red. That is the same stance `faulttree` takes toward cut sets and `pert` toward scheduling. RBD is the success-space dual of the fault tree's failure-space. ```schematex rbd "Redundant Server" series { block PSU "Power Supply" R=0.99 parallel { block FAN1 "Fan A" R=0.95 block FAN2 "Fan B" R=0.95 } kofn 2/3 { block D1 "Disk 1" R=0.97 block D2 "Disk 2" R=0.97 block D3 "Disk 3" R=0.97 } } ``` *** ## 1. Your first diagram Every document starts with the `rbd` keyword (alias `reliability`), an optional title, then nested success-logic groups around `block` leaves: ``` rbd "Two redundant pumps" parallel { block A "Pump A" R=0.9 block B "Pump B" R=0.9 } ``` The engine draws the two pumps on parallel rails between a split node and a join node, computes the system reliability `1 − (1−0.9)(1−0.9) = 0.99`, and prints it as the headline. A bare top-level list of blocks (no outer group) is treated as a **series** chain. ## 2. Blocks A `block` is one component on a success path: ``` block ID "Label" R=0.99 ``` * `ID` — a short identifier (shown when no label is given). * `"Label"` — an optional display name (CJK quotes welcome). * Reliability is given as **`R=0.99`** (reliability/availability), **`p=0.01`** (probability of *failure*, → R = 1−p), or a percentage **`R=99%`**. A block with no reliability leaves the system figure symbolic (`n/a`). ## 3. Success-logic groups Groups nest freely, so you can model redundant chains, voting banks, and standby pairs: | Group | Succeeds when | Reliability | | ---------------- | -------------------------- | ----------------------- | | `series { … }` | **every** child works | ∏ Rᵢ | | `parallel { … }` | **any** child works | 1 − ∏(1 − Rᵢ) | | `kofn k/n { … }` | **≥ k of n** children work | exact state enumeration | ``` series { block CTRL "Controller" R=0.995 parallel { series { block P1 "Path 1 sensor" R=0.97 block A1 "Path 1 actuator" R=0.98 } series { block P2 "Path 2 sensor" R=0.97 block A2 "Path 2 actuator" R=0.98 } } } ``` ## 4. Computed reliability, importance & SPOF After parsing, the engine computes: * **System reliability** — the headline figure, by recursive series/parallel/k-of-n reduction. * **Birnbaum importance** `Iᴮ(i) = R_sys(Rᵢ=1) − R_sys(Rᵢ=0)` for every block; the highest-importance block (the improvement target) is accented. * **Criticality importance** `I_C(i) = Iᴮ(i)·(1−Rᵢ)/(1−R_sys)` — the probability block i is failed *and* critical, given the system is failed. * **Single points of failure** — any block where `R_sys(Rᵢ=0) = 0` (its failure alone fails the system) is drawn with a red border. A non-redundant block in series is always a SPOF. ## 5. Time-dependent reliability — R(t) A static `R=` is the entry point; in practice reliability is a function of mission time. Set a **`mission: <t>`** and give blocks a failure distribution instead of a constant — the engine evaluates **R(t)** and rolls it up exactly as before. Use **consistent time units** across `mission` and the rates. | Block attribute | Model | R(t) | | ----------------- | ------------------------------- | ------------ | | `rate=0.0001` | exponential (constant hazard λ) | e^(−λt) | | `mtbf=10000` | exponential (λ = 1/MTBF) | e^(−t/MTBF) | | `weibull=2,10000` | Weibull(β shape, η scale) | e^(−(t/η)^β) | ``` rbd "Pump station — 1-year mission" mission: 8760 # hours parallel { block A "Pump A" mtbf=10000 block B "Pump B" weibull=1.5,12000 } ``` The headline becomes `R(t=8760) = …`. A block with a distribution but no `mission:` warns and falls back to its constant `R=` (if any). ## 6. Validation The parser reports non-fatal warnings rather than failing: * a `kofn k/n` threshold with `k > n` is clamped to `n` (and `k < 1` to `1`); * a reliability outside `0..1` is clamped; * a duplicate block id is flagged. ## 7. Theming `theme: default` uses the shared risk-reliability palette (neutral blocks, blue reliability numerals, red single-point-of-failure borders). `theme: monochrome` renders a black-and-white print version (SPOF by border weight); `theme: dark` is the Schematex slate/blue dark variant. --- # UML Sequence Diagram Canonical URL: https://schematex.js.org/docs/sequence Markdown URL: https://schematex.js.org/docs/sequence.md ## About sequence diagrams A **sequence diagram** shows how participants exchange messages **over time**: lifelines run top→bottom, messages run left→right between them, and the vertical axis is order. Defined by **[UML](https://www.omg.org/spec/UML/2.5.1/) 2.5.1 §17 (Interactions)**, it's the diagram engineers reach for to pin down an API call flow, an auth handshake, or a distributed protocol — *who calls whom, in what order, and what comes back*. Schematex implements the full UML notation, and for generation it also **accepts the Mermaid `sequenceDiagram` dialect** — paste Mermaid in unchanged and it renders. Under the `sequenceDiagram` header the arrows take Mermaid meaning (`->>` synchronous call, `-->>` reply, `-)` async); under the native `sequence "Title"` header the long-standing Schematex meaning is kept (`->>` = async). Where most tools stop at the common subset (`alt`/`opt`/`loop`/`par`), Schematex carries all **twelve** combined-fragment operators and `ref` interaction-use frames, because those are the parts professionals reach for when an interaction gets real. Distinct from **[usecase](/docs/usecase)** (system scope, not message order), **[state](/docs/state)** (one object's modes, not inter-object messages), and **[bpmn](/docs/bpmn)** (organisational process, not a call sequence). ```schematex sequence "Login flow" actor User participant Web as "Web App" control Auth database DB User -> Web : submit(credentials) activate Web Web ->+ Auth : verify(credentials) Auth ->+ DB : SELECT user DB --> Auth : row deactivate DB alt [credentials valid] Auth --> Web : token Web --> User : 200 OK else [invalid] Auth --> Web : 401 Web --> User : error end deactivate Auth deactivate Web note over User, Web : session cookie set ``` *** ## 1. Your first diagram Every document starts with the `sequence` keyword and an optional `"title"`. Participants don't need to be declared — the first time you mention one in a message, it becomes a lifeline: ``` sequence Alice -> Bob : Authentication Request Bob --> Alice : Authentication Response ``` Lifelines appear left-to-right in first-use order. `->` is a synchronous call (solid line, filled arrowhead); `-->` is a reply (dashed line, open arrowhead). Text after `:` is the message label. *** ## 2. Participants Declare a participant explicitly to set its **kind**, give it an **alias**, or fix its **order**: ``` sequence actor User participant Web as "Web App" boundary LoginUI control Auth entity Account database DB collections Sessions queue Events ``` | Kind | Rendered as | | --------------------------------- | -------------------------------------------------------------------- | | `participant` (default) | rounded classifier box | | `actor` | stick figure | | `boundary` / `control` / `entity` | the Jacobson robustness icons (⊢◯ / ◯ with arrow / ◯ with underline) | | `database` | cylinder | | `collections` / `queue` | box with the kind as a `«stereotype»` | * `as "Label"` sets the display name (quotes optional; `「…」` CJK quotes accepted). * A custom **stereotype** goes in guillemets or ASCII angle brackets after the declaration — it overrides the default label: `actor Printer «system»`, `participant Bus as "Event Bus" <<service>>`. *** ## 3. Messages The arrow token chooses the UML semantics: | DSL | Meaning | Rendering | | --------- | ------------------- | ----------------------------------- | | `A -> B` | synchronous call | solid line, **filled** arrowhead | | `A ->> B` | asynchronous signal | solid line, **open** arrowhead | | `A --> B` | reply / return | **dashed** line, open arrowhead | | `A -x B` | lost message | line ending at a filled circle | | `o-> B` | found message | line starting from a filled circle | | `A -> A` | self message | bent loop back to the same lifeline | Whitespace around arrows is optional (`A->B` works), and labels are free text — including a return value, e.g. `aHotel -> aHotel : available(roomId, date): isRoom`. *** ## 4. Activations (execution specifications) The thin bar on a lifeline shows it is active. Open and close it with explicit statements, or with `+` / `-` suffixes on the messages themselves: ``` sequence participant Client participant Server Client ->+ Server : request() Server ->> Server : validate() Server -->- Client : response ``` `+` after the arrow activates the **receiver** on arrival; `-` deactivates the **sender** after the message is sent. The explicit form is `activate X` / `deactivate X`. Overlapping bars on one lifeline nest with a horizontal offset. *** ## 5. Object creation & destruction ``` sequence participant Factory Factory -> *Worker : «create» Factory -> Worker : work() destroy Worker ``` Prefix the receiver with `*` to make the message **instantiate** it — the new lifeline's head is drawn at the arrival row, not at the top. `destroy X` ends a lifeline with a ✕ and stops its time axis. *** ## 6. Combined fragments A combined fragment is a labelled frame around a region. Schematex implements the full UML `InteractionOperatorKind` set: | Operator | Meaning | Operands | | --------------------- | -------------------------------- | ----------------------------- | | `alt` | alternatives (if/else-if/else) | `else`, each guarded | | `opt` | runs iff the guard holds | one, guarded | | `loop` | repeat | one; guard may be `(min,max)` | | `par` | concurrent operands | `and` | | `break` | exceptional exit | one, guarded | | `critical` | atomic / no interleaving | one | | `seq` / `strict` | weak / strict sequencing | `and` | | `neg` | invalid traces (rendered tinted) | one | | `ignore` / `consider` | message-set filter `{m1, m2}` | one | | `assert` | the only valid continuation | one | ``` sequence actor User participant API User -> API : GET /resource alt [authorized] API --> User : 200 + body else [forbidden] API --> User : 403 end ``` Guards go in `[brackets]` after the operator (and after `else`). Fragments nest, and inner frames inset automatically so they sit cleanly inside their parent. *** ## 7. Interaction use (`ref`) Reference another interaction instead of inlining it — the way UML keeps large diagrams composable: ``` sequence participant A participant B ref over A, B : Establish session A -> B : poll() ``` `ref over <lifelines> : Name` draws a framed box across the named lifelines. Most tools omit this; it's how real systems decompose long flows. *** ## 8. Notes, dividers, invariants, numbering ``` sequence autonumber 1 1 actor Shopper participant Cart == Phase 1: review == Shopper -> Cart : view items note over Cart : cart persisted in Redis state Cart : ready ``` * `note over A` / `note over A, B` / `note left of A` / `note right of A` — folded-corner annotations. * `== text ==` — a full-width section divider. * `state X : text` — a state-invariant capsule on a lifeline. * `autonumber [start] [step]` — prefix every message with an incrementing number. *** ## 9. Grammar (EBNF) ```text diagram = "sequence" [ string ] NEWLINE statement* statement = participant | message | activation | note | fragment | ref | divider | invariant | destroy | autonumber participant = kind IDENT ("as" label)? stereotype? kind = "participant" | "actor" | "boundary" | "control" | "entity" | "database" | "collections" | "queue" stereotype = "«" TEXT "»" | "<<" TEXT ">>" message = IDENT? act? arrow act? ("*")? IDENT? (":" TEXT)? arrow = "->" | "->>" | "-->" | "-x" | "o->" act = "+" | "-" activation = ("activate" | "deactivate") IDENT fragment = ("alt"|"opt"|"loop"|"par"|"break"|"critical"|"seq" |"strict"|"neg"|"ignore"|"consider"|"assert") guard? NEWLINE statement* (("else"|"and") guard? NEWLINE statement*)* "end" guard = "[" TEXT "]" | "(" NUMBER ("," NUMBER)? ")" ref = "ref" "over" IDENT ("," IDENT)* ":" TEXT note = "note" ("over"|"left of"|"right of") IDENT ("," IDENT)? ":" TEXT divider = "==" TEXT "==" invariant = "state" IDENT ":" TEXT destroy = "destroy" IDENT autonumber = "autonumber" NUMBER? NUMBER? ``` *** ## 10. Standard compliance Schematex follows the OMG **UML 2.5.1 §17 (Interactions)** notation: synchronous (filled) vs. asynchronous (open) vs. reply (dashed) arrowheads, found/lost message endpoints, execution-specification bars, all twelve combined-fragment operators with guarded operands, `ref` interaction-use frames, and the Jacobson analysis-class icons for `boundary`/`control`/`entity`. Gates, coregion, and time/duration constraints are out of scope for v0.1 (they need new layout primitives) and tracked for a later release. See `docs/reference/33-SEQUENCE-STANDARD.md` for the full specification. *** ## Related examples Ready-to-use scenarios from the examples gallery: [Browse related examples](https://schematex.js.org/examples) ## Interactive editing Participant aliases, message labels, and the title are editable. Lifelines move horizontally; vertical message order remains source-defined because that axis represents time. --- # Sequential Function Chart (SFC) Canonical URL: https://schematex.js.org/docs/sfc Markdown URL: https://schematex.js.org/docs/sfc.md ## About sequential function charts **Sequential Function Chart (SFC)** is the **state-machine view** of a cyclic PLC program — what's happening *now* and what triggers the *next* phase. It's the fifth language defined by **[IEC 61131-3:2013 §6.5](https://en.wikipedia.org/wiki/Sequential_function_chart)** and the engineering subset of **IEC 60848 GRAFCET**. Where **[ladder](/docs/ladder)** and **[fbd](/docs/fbd)** describe per-scan combinational logic, SFC describes the temporal sequencing across them: which step is active, when it hands off, what runs in parallel. In production code, \~10% of networks are written in SFC, but \~100% of nontrivial sequential machines have at least one SFC chart — batch reactors, robotic cells, packaging lines, assembly stations. Until now there was no good open-source SFC DSL: vendor IDEs (Studio 5000, TIA Portal, CODESYS) were the only option. Schematex ships an IEC 61131-3 SFC subset designed for AI generation. Distinct from **[state](/docs/state)** (UML statechart for reactive UIs and lifecycle FSMs): SFC has *cyclic-scan* semantics, double-bordered initial steps, action-block qualifiers (N/S/R/L/D/P), and bar-based branches (single bar = OR, double bar = AND). ```schematex sfc "Bottle Filling" var StartBtn: bool var TankLevel: real var DoneBtn: bool step S0 [initial] N FillValve_Closed step S1 [label: "Filling"] N FillValve_Open step S2 [label: "Done"] N Confirm_Done transition from: S0 to: S1: StartBtn transition from: S1 to: S2: TankLevel >= 80.0 transition from: S2 to: S0: DoneBtn ``` *** ## 1. Your first chart Two steps, one transition, an initial marker: ``` sfc step S0 [initial] step S1 transition from: S0 to: S1: Trigger ``` `S0` renders as a **double-bordered rectangle** (the IEC initial-step convention); `S1` as a single-border rectangle. Between them is a horizontal **transition bar** with the condition text `Trigger` to its right. If you forget `[initial]`, the first declared step is auto-promoted to initial. *** ## 2. Steps ``` step S_Filling [label: "Filling tank"] N FillValve_Open D Mixer_Run T#30s P StartChime ``` A step has: * An **id** (unique across the chart) — used in transitions and jumps. * An optional `[label: "..."]` for display. * An optional `[initial]` (one allowed) or `[final]` (vendor stop step, three borders). * Zero or more **action blocks**, indented one level, each with a qualifier letter. *** ## 3. Transitions A **transition** declares a directed link between two steps with a boolean condition: ``` transition from: S0 to: S1: StartBtn transition from: S1 to: S2: TankLevel >= 80.0 AND NOT EmergencyStop transition T_Reset from: S5 to: S0: ResetBtn ``` The condition text is opaque — Schematex stores it verbatim and renders it next to the bar. Every transition must have a non-empty condition; use `TRUE` for unconditional links. Transitions whose `from` and `to` are linearly adjacent in the body render as inline bars between the steps. Transitions whose pair is *not* linearly adjacent (e.g. a jump back to an earlier step) render as **margin arrows** on the left or right side of the chart. *** ## 4. Action qualifiers Actions attach to the right side of a step and run according to their qualifier letter: | Qualifier | Behavior | | --------- | ---------------------------------------------------- | | `N` | Active while step is active (most common) | | `S` | Stored — set true on entry, stays until matching `R` | | `R` | Reset — clears a previously-stored action | | `L` | Time-Limited — active up to T after step entry | | `D` | Time-Delayed — activates T after entry | | `P` | Pulse — true for one PLC scan only | | `P0` | Pulse on deactivate (Siemens) | | `P1` | Synonym for `P` (Siemens) | | `SD` | Stored & Delayed | | `DS` | Delayed & Stored | | `SL` | Stored & Time-Limited | Time-parameterized qualifiers (L, D, SD, DS, SL) take a duration literal: ``` step S1 L LimitedRun T#5s D DelayedRun T#2s ``` *** ## 5. Alternative branches (single bar — OR) Only **one** branch fires per scan, picked by transition condition: ``` step S0 [initial] step S_Pick alt from: S_Pick: branch [priority: 1]: transition: IsExpressShipping step S_Express N PrepExpressBox transition: TRUE branch [priority: 2]: transition: IsStandardShipping step S_Standard N PrepStandardBox transition: TRUE merge_to: S_Ship step S_Ship transition from: S0 to: S_Pick: ProductOrdered transition from: S_Ship to: S0: Shipped ``` The single horizontal lines above and below the branches are the divergence and convergence bars. Each branch starts with its **entry transition** (between div bar and first step) and ends with an **exit transition** (between last step and conv bar). *** ## 6. Simultaneous branches (double bar — AND) **All** branches run concurrently; the chart waits at the convergence until every branch finishes: ``` sim from: S_Heat: TRUE branch: step S_Bake D Oven_Run T#15m branch: step S_Cool L Cooler_On T#5m merge_to: S_Done: Bake_Done AND Cool_Done ``` The two parallel horizontal lines (gap 4px) above and below the branches are the simultaneous bars. The shared **transition above** (`TRUE` here) triggers the divergence; the shared **transition below** (`Bake_Done AND Cool_Done`) is checked before convergence fires. *** ## 7. Jumps (loops) A transition whose target is an earlier step renders as a margin arrow: ``` step S0 [initial] step S1 step S2 transition from: S0 to: S1: A transition from: S1 to: S2: B transition T_Reset from: S2 to: S0: ResetBtn transition from: S2 to: S1: NOT ResetBtn ``` The forward `S2 → S1` (back-edge) gets the margin arrow on the right; the `T_Reset` jump back to `S0` goes on the left. Each margin arrow shows its target id and condition. *** ## 8. Variables Reused from `ladder` and `fbd`: ``` var StartBtn: bool var TankLevel: real var BakeReady: bool var Counter: counter var T1: timer ``` Variables declared in conditions and actions are not validated — Schematex treats condition / action body text as opaque strings, matching how `state` handles guards and actions. *** ## 9. v0.1 limitations * **Nested branches** (alt-in-sim, sim-in-alt) parse but layout collapse heuristics are basic; deep nests may overlap. * **S/R action-pair dashed connectors** (visually link an `S` action with its matching `R` elsewhere) are deferred. * **Active-step runtime indicator** (yellow fill on the currently active step) is deferred — useful for debugging integrations that surface PLC runtime state. * **GRAFCET forcing orders** (out-of-scope per IEC 60848-only feature). * **Final step** parses with `[final]` but renders with the same double border as initial; the IEC triple-border convention is deferred. *** ## Related examples Ready-to-use scenarios from the examples gallery: [Browse related examples](https://schematex.js.org/examples) --- # Site plan Canonical URL: https://schematex.js.org/docs/siteplan Markdown URL: https://schematex.js.org/docs/siteplan.md ## About site plans A **site plan** shows the land around a property: parcel boundaries, road frontage, building footprints, driveways, walkways, setbacks, easements, fences, utilities, parking, trees, dimensions, callouts, north arrow, and scale bar. Schematex renders these as presentation-grade SVG sketches for listings, proposals, and early planning. This is deliberately different from `floorplan`: use `floorplan` for interior rooms, walls, doors, and furniture; use `siteplan` for the parcel and exterior site context. The output is not CAD, not survey-grade, and not permit-ready. ```schematex siteplan "Residential Listing Site Plan" unit ft parcel lot points 0,0 62,0 58,96 8,104 -4,42 road maple "Maple Ave" from -12,-16 to 74,-16 width 22 frontage front from 0,0 to 62,0 setback frontSetback "Front setback" from 5,8 to 57,8 easement util "Utility easement" from 48,0 to 43,96 structure house "Residence" points 15,28 45,28 45,64 34,64 34,78 15,78 structure garage "Garage" points 45,32 58,32 58,55 45,55 driveway drive points 52,0 52,32 width 10 walkway walk points 31,0 31,28 width 4 tree oak at 9,22 size 8 "Oak" tree mapleTree at 12,80 size 9 car car1 at 52,14 size 15 rotate 0 dim "62 ft frontage" from 0,-30 to 62,-30 callout "Covered patio" at 20,90 to 22,76 north scale 20 legend on ``` *** ## 1. Parcel and footprints Use `parcel` for the outer lot boundary and `structure` for buildings, garages, sheds, decks, or patios: ```text siteplan "Listing Site Plan" unit ft parcel lot points 0,0 62,0 58,96 8,104 -4,42 structure house "Residence" points 15,28 45,28 45,64 34,64 34,78 15,78 structure garage "Garage" points 45,32 58,32 58,55 45,55 ``` Polygons close automatically. Keep them simple and use a human-authored label when the shape is part of a sales or proposal page. ## 2. Roads and paths Roads, driveways, walkways, and trails are centerline paths with a visual width: ```text road maple "Maple Ave" from -12,-16 to 74,-16 width 22 driveway drive "Driveway" points 52,0 52,32 width 10 walkway walk "Entry walk" points 31,0 31,28 width 4 ``` Use `from x,y to x,y` for a straight segment or `points x,y x,y ...` for a bent path. Driveways render as paved aisles with edge lines. Commercial-width driveways (`width` 12+) add lane markings, and wider drive aisles (`width` 14+) add subtle directional arrows. ## 3. Planning overlays Line features use standard site-plan vocabulary: ```text frontage front "Road frontage" from 0,0 to 62,0 setback frontSetback "Front setback" from 5,8 to 57,8 easement util "Utility easement" from 48,0 to 43,96 fence rearFence "Fence" from 8,104 to 58,96 utility water "Water service" points 20,0 20,28 ``` These are presentation overlays. The engine draws them clearly but does not calculate zoning compliance. ## 4. Markers, dimensions, and callouts ```text tree oak at 9,22 size 8 "Oak" car car1 at 52,14 size 15 rotate 0 pin sign at 10,4 size 6 "Sign" dim "62 ft frontage" from 0,-30 to 62,-30 callout "Covered patio" at 20,90 to 22,76 north scale 20 legend on ``` `dim` labels are authored by you. This keeps the diagram honest: it is a listing/proposal sketch, not a legal survey. ## 5. Grammar ```text plan ::= ("siteplan"|"plotplan"|"parcelmap"|"propertymap") string? ("unit" ("ft"|"m"))? NL statement* polygon ::= ("parcel"|"structure"|"zone"|"landscape"|"parking") id string? "points" coord coord coord+ ("fill" color)? path ::= ("road"|"driveway"|"walkway"|"trail") id string? ("from" coord "to" coord | "points" coord coord+) ("width" num)? line ::= ("setback"|"easement"|"fence"|"utility"|"frontage"|"boundary") id string? ("from" coord "to" coord | "points" coord coord+) marker ::= ("tree"|"car"|"pin"|"entry"|"hydrant"|"well") id? "at" coord ("size" num|dims)? ("rotate" num)? string? dim ::= ("dim"|"measure") string? "from" coord "to" coord callout ::= "callout" string "at" coord "to" coord north ::= "north" num? scale ::= "scale" num legend ::= "legend" ("on"|"off") ``` ## Related examples * [Residential listing site plan](/examples#siteplan) — realtor-ready parcel, house footprint, driveway, easement, setbacks * [Backyard landscape sketch](/examples#siteplan) — house, patio, lawn, planting beds, trees, fence * [Corner commercial site](/examples#siteplan) — two-road frontage, parking, entry, retail footprint * [Mall parking lot concept](/examples#siteplan) — mall footprint, parking fields, loop drive, entry access, service court * [Lifestyle center parking plan](/examples#siteplan) — retail rows, pedestrian plaza, surface parking, parking deck * [Townhome infill concept](/examples#siteplan) — internal lane, phase zones, landscape/open space ## Interactive editing The title is editable and movable. Polygon, path, and line endpoints plus markers expose native x/y handles; each drop rewrites the corresponding coordinate token in site units. --- # Single-line diagram (SLD) Canonical URL: https://schematex.js.org/docs/sld Markdown URL: https://schematex.js.org/docs/sld.md ## About single-line diagrams A **single-line diagram** (also called a one-line diagram) represents the electrical power system of a facility or substation using a single line to stand in for all three phases of a three-phase AC system. Equipment — transformers, circuit breakers, buses, motors, loads — is shown with standardized symbols, and the power flow path connects them top-to-bottom from source to load. Electrical engineers, utility planners, and facility managers use SLDs as the primary reference document for every power system project: it is the first deliverable in any interconnection application, arc-flash study, or commissioning package. Schematex follows **[IEEE Std 315 (ANSI Y32.2)](https://standards.ieee.org/ieee/315/1099/)** graphic symbol conventions for equipment, extended with IEC 60617 winding-configuration notation for transformer variants. This page documents what the parser accepts today. ```schematex sld "13.8 kV Substation" utility = utility [label: "Grid 138 kV"] bus_hv = bus [voltage: "138kV", label: "HV Bus"] xfmr1 = transformer_dy [rating: "15 MVA", voltage: "138kV/13.8kV", label: "Main Xfmr"] bus_mv = bus [voltage: "13.8kV", label: "MV Bus"] brk1 = breaker [rating: "1200A", label: "BKR-1"] brk2 = breaker [rating: "1200A", label: "BKR-2"] brk3 = breaker [rating: "1200A", label: "BKR-3"] feeder1 = load [label: "Feeder 1"] feeder2 = load [label: "Feeder 2"] feeder3 = load [label: "Feeder 3"] utility -> bus_hv bus_hv -> xfmr1 xfmr1 -> bus_mv bus_mv -> brk1 bus_mv -> brk2 bus_mv -> brk3 brk1 -> feeder1 brk2 -> feeder2 brk3 -> feeder3 ``` *** ## 1. Your first single-line diagram The simplest SLD: a utility source, a transformer, a breaker, and a load. ```schematex sld "Simple feeder" util = utility [label: "Utility 13.8kV"] xfmr = transformer [rating: "500 kVA", voltage: "13.8kV/480V"] bus1 = bus [voltage: "480V", label: "480V Bus"] cb1 = breaker [rating: "200A"] load1 = load [label: "Panel LP-1"] util -> xfmr xfmr -> bus1 bus1 -> cb1 cb1 -> load1 ``` Four rules cover 80% of usage: 1. Start with `sld`, optionally followed by a quoted title. 2. Declare each equipment item as `id = nodeType [attributes]` — one per line. 3. Connect items with `from -> to`, optionally adding `[cable: "…", label: "…"]`. 4. IDs may contain letters, digits, underscores, and hyphens — but must start with a letter. > Comments may start with `#`, `//`, or Mermaid-style `%%` on their own line. *** ## 2. Node types A node line is `id = nodeType [attr: value, …]`. The node type determines the symbol drawn. ### 2.1 Sources | Type | Symbol | Typical use | | ----------- | -------------------- | ------------------------------ | | `utility` | Utility source arrow | Infinite bus / grid connection | | `generator` | Circle with `G` | Diesel, gas, or hydro genset | | `solar` | PV panel symbol | Photovoltaic array | | `wind` | Turbine symbol | Wind turbine | | `ups` | Block with battery | Uninterruptible power supply | ```schematex sld "Generation sources" util = utility [label: "Grid 115 kV"] gen = generator [rating: "2 MW", label: "Diesel Gen"] sol = solar [rating: "500 kW", label: "PV Array"] wnd = wind [rating: "1 MW", label: "Wind Turbine"] ups = ups [rating: "100 kVA", label: "UPS System"] util -> gen util -> sol util -> wnd util -> ups ``` ### 2.2 Transformers | Type | Winding configuration | Notes | | ---------------------- | --------------------------- | ------------------------ | | `transformer` | Generic two-winding | No winding spec | | `transformer_dy` | Delta → Wye grounded (Δ-Yg) | Most common distribution | | `transformer_yd` | Wye grounded → Delta (Yg-Δ) | | | `transformer_yy` | Wye-Wye (both grounded) | | | `transformer_dd` | Delta-Delta | | | `autotransformer` | Single-winding with tap | Zigzag coil symbol | | `transformer_3winding` | Three-winding | HV / MV / LV taps | ```schematex sld "Transformer configurations" src = utility [label: "138kV Grid"] t_dy = transformer_dy [rating: "30 MVA", voltage: "138kV/13.8kV", label: "Δ-Yg (most common)"] t_yy = transformer_yy [rating: "10 MVA", voltage: "138kV/13.8kV", label: "Yg-Yg"] t_auto = autotransformer [rating: "50 MVA", voltage: "138kV/69kV", label: "Autotransformer"] t_3w = transformer_3winding [rating: "40 MVA", voltage: "138/13.8/4.16kV", label: "3-Winding"] src -> t_dy src -> t_yy src -> t_auto src -> t_3w ``` ### 2.3 Buses and nodes | Type | Symbol | Typical use | | --------- | --------------------- | -------------------------------------------- | | `bus` | Thick horizontal line | Main voltage bus bar | | `bus_tie` | Bus-tie breaker | Links two parallel buses at the same voltage | | `hub` | Wide rectangle | Multi-feeder combining point | ### 2.4 Switching and protection | Type | Symbol | Device number | | ---------------- | ------------------------ | -------------------------- | | `breaker` | Diagonal + arc | 52 (AC circuit breaker) | | `breaker_vacuum` | Diagonal + V-oval | 52 vacuum type | | `switch` | Diagonal (no arc) | 89 (disconnect / isolator) | | `switch_load` | Load interrupter switch | — | | `ground_switch` | Diagonal + ground symbol | Grounding disconnect | | `ats` | Transfer switch symbol | Automatic transfer switch | | `recloser` | Diagonal + arc + arrow | Auto-reclosing breaker | | `sectionalizer` | Diagonal + S | Distribution sectionalizer | | `fuse` | Oval with diagonal | Expulsion fuse cutout | | `fuse_cl` | Rectangle with diagonal | Current-limiting fuse | ```schematex sld "Switching and protection" src = utility [label: "Source"] rclsr = recloser [label: "Recloser"] sect = sectionalizer [label: "Sectionalizer"] fuse1 = fuse [label: "Fuse"] sw = switch [label: "Disconnect"] gnd_sw = ground_switch [label: "Ground SW"] src -> rclsr rclsr -> sect sect -> fuse1 sect -> sw sw -> gnd_sw ``` ### 2.5 Protection and monitoring | Type | Symbol | Typical use | | ---------------- | ------------------------------- | -------------------------------------------- | | `ct` | Small circle with line through | Current transformer | | `pt` | Small circle | Potential / voltage transformer | | `relay` | Small circle with device number | Protection relay (ANSI number via `device:`) | | `surge_arrester` | Arrow + ground | Lightning arrester | | `ground_fault` | GFI symbol | Ground-fault detector | ### 2.6 Loads and equipment | Type | Symbol | Typical use | | ----------------- | ------------------- | ------------------------ | | `motor` | Circle with `M` | Three-phase motor | | `load` | Rectangle | Generic load or feeder | | `capacitor_bank` | Two plates + switch | Power factor correction | | `harmonic_filter` | LC symbol | Passive harmonic filter | | `vfd` | Rectangle with VFD | Variable-frequency drive | ### 2.7 Metering | Type | Symbol | Typical use | | ---------------- | ---------------- | ------------ | | `watthour_meter` | Circle with `Wh` | Energy meter | | `demand_meter` | Circle with `D` | Demand meter | ```schematex sld "Equipment types" src = utility [label: "Grid 13.8kV"] tx = transformer_dy [rating: "1000 kVA", voltage: "13.8kV/480V", label: "Main TX"] bk = breaker [rating: "2000A", label: "Main Breaker"] bus = bus [voltage: "480V", label: "480V MV Bus"] ct1 = ct [label: "CT-1"] rly = relay [device: "51", label: "Overcurrent Relay"] cap = capacitor_bank [rating: "150 kVAR", label: "PF Cap"] mtr = motor [rating: "100HP", label: "Pump Motor"] gen = generator [rating: "500kW", label: "Emergency Gen"] ats = ats [rating: "800A", label: "ATS-1"] src -> tx tx -> bk bk -> bus bus -> ct1 ct1 -> rly bus -> cap bus -> mtr gen -> ats ats -> bus ``` *** ## 3. Node attributes Attributes are written inside `[…]` after the node type, comma-separated. | Attribute | Values | Effect | | -------------- | ------------------------------------------ | ---------------------------------------------------- | | `label: "…"` | quoted string | Display name on the diagram | | `voltage: "…"` | quoted string, e.g. `"13.8kV"`, `"480V"` | Voltage level annotation | | `rating: "…"` | quoted string, e.g. `"1000 kVA"`, `"200A"` | Equipment rating annotation | | `device: "…"` | ANSI device number, e.g. `"51"`, `"87"` | Used with `relay` nodes | | any other key | quoted string | Stored as nameplate data (transformer kVA, %Z, etc.) | **Example with all common attributes:** ``` xfmr = transformer_dy [ label: "Main Transformer", voltage: "13.8kV/480V", rating: "1000 kVA", impedance: "5.75%Z" ] ``` The attribute block may span multiple lines — the parser joins lines until the `]` is balanced. *** ## 4. Connections A connection line is `fromId -> toId`, optionally followed by `[cable: "…", label: "…"]`. ``` bus1 -> cb1 bus1 -> cb1 [cable: "3#2/0 AWG"] bus1 -> cb1 [cable: "3#2/0 AWG", label: "Feeder A"] ``` **Rules:** * Both IDs must be declared before or after the connection — all connections are validated at end of parse. * Only `->` (directed, source-to-load) is accepted. The connection direction is used for layout. * An unknown node ID throws `SLDParseError: Connection references unknown node "…"`. ```schematex sld "ATS backup with cable labels" UTIL = utility [label: "Utility 480V"] GEN = generator [rating: "500 kW", label: "Emergency Gen"] ATS1 = ats [rating: "800A", label: "ATS-1"] BUS1 = bus [voltage: "480V", label: "Critical Bus"] CB1 = breaker [rating: "200A", label: "CB-1"] CB2 = breaker [rating: "200A", label: "CB-2"] L1 = load [label: "Server Room"] L2 = load [label: "Life Safety"] UTIL -> ATS1 [label: "Normal source"] GEN -> ATS1 [label: "Emergency source"] ATS1 -> BUS1 [cable: "3#2/0 AWG"] BUS1 -> CB1 BUS1 -> CB2 CB1 -> L1 [cable: "3#4 AWG"] CB2 -> L2 [cable: "3#4 AWG"] ``` *** ## 5. Labels & comments * **Title:** `sld "Substation One-Line"` — first line only. * **Node label:** `id = type [label: "…"]` — the display name. * **Connection label:** `A -> B [label: "…"]` — appears alongside the connecting line. * **Cable annotation:** `A -> B [cable: "3#2/0 AWG, 200ft"]` — conductor specification. * **Comments:** `#` at the start of a line. Inline `#` on the same line as a node or connection is also stripped. * **Residential aliases:** IEC / REBT vocabulary such as `mcb`, `rcd`, `rcbo`, `rccb`, `pia`, `iga`, `main_switch`, `consumer_unit`, `distribution_board`, `panel`, and `panelboard` is accepted as input and mapped to existing SLD primitives. *** ## 6. Reserved words & escaping **Reserved at line start:** `sld` (header). **Operator token** — avoid `->` inside node IDs. IDs may contain `[A-Za-z][A-Za-z0-9_-]*` — hyphens are valid (e.g. `CB-101` is a legal ID). **Attribute block** — `[…]` brackets may span multiple physical lines. The parser joins continuation lines until the bracket depth reaches zero. **Duplicate IDs** throw `SLDParseError: Duplicate node id "…"`. *** ## 7. Common mistakes | You wrote | Parser says | Fix | | ---------------------------------------- | ---------------------------------------------------------------- | ------------------------------------------------------------------------------------------- | | `xfmr1 [type: transformer]` | `SLDParseError: Cannot parse line` | Use `=` assignment: `xfmr1 = transformer [...]` | | `id = battery [...]` | `SLDParseError: Unknown node type "battery"` | No `battery` type — use `ups` or `generator` | | `id = breakerz` | `SLDParseError: Unknown node type ... (did you mean 'breaker'?)` | Use the suggested canonical type or alias | | `A -- B` (bidirectional) | `SLDParseError: Cannot parse line` | Only `->` is accepted; use two `->` lines if needed | | `A -> B -> C` (chained) | `SLDParseError: Cannot parse line` | Each connection is one `->` per line | | `relay [label: "OC"]` (no device number) | Relay renders with blank number | Add `device: "51"` for the ANSI device number | | `voltage: 480V` (unquoted) | Attribute value not recognized | Quote all values: `voltage: "480V"` | | Node ID starting with digit: `2BUS` | `SLDParseError: Cannot parse line` | IDs must start with a letter: `BUS2` | | Connection before node declared | `SLDParseError: Connection references unknown node "…"` | Declare nodes before or after connections — validated at end of parse, so order is flexible | *** ## 8. Grammar (EBNF) ```text document = header NEWLINE ( blank | comment | node-def | connection )* header = "sld" ( WS quoted-string )? NEWLINE quoted-string = '"' any-char-but-quote* '"' node-def = id WS "=" WS node-type ( WS "[" attr-list "]" )? NEWLINE node-type = "utility" | "generator" | "solar" | "wind" | "ups" | "transformer" | "transformer_dy" | "transformer_yd" | "transformer_yy" | "transformer_dd" | "autotransformer" | "transformer_3winding" | "bus" | "bus_tie" | "hub" | "breaker" | "breaker_vacuum" | "switch" | "switch_load" | "ground_switch" | "ats" | "recloser" | "sectionalizer" | "fuse" | "fuse_cl" | "ct" | "pt" | "relay" | "surge_arrester" | "ground_fault" | "motor" | "load" | "capacitor_bank" | "harmonic_filter" | "vfd" | "watthour_meter" | "demand_meter" | residential-alias residential-alias = "mcb" | "mccb" | "rcd" | "rcbo" | "rccb" | "differential" | "diferencial" | "pia" | "iga" | "main_switch" | "isolator" | "disconnector" | "consumer_unit" | "distribution_board" | "panel" | "panelboard" attr-list = attr ( "," attr )* attr = key ":" WS quoted-string connection = id WS "->" WS id ( WS "[" conn-attrs "]" )? NEWLINE conn-attrs = conn-attr ( "," conn-attr )* conn-attr = "cable" ":" WS quoted-string | "label" ":" WS quoted-string id = [A-Za-z] [A-Za-z0-9_-]* key = [A-Za-z] [A-Za-z0-9_]* comment = ( "#" | "//" | "%%" ) any NEWLINE ``` The attribute block `[…]` may span multiple physical lines — the parser joins continuation lines until the bracket depth returns to zero. Authoritative source: `src/diagrams/sld/parser.ts`. If this diverges from the parser, the parser wins — please open an issue. *** ## 9. Standard compliance Schematex SLDs follow **IEEE Std 315 (ANSI Y32.2)** graphic symbol conventions for switching equipment, transformers, and sources. Transformer winding configuration variants (`transformer_dy`, `transformer_yy`, etc.) use **IEC 60617** Δ/Y notation. Protection relay device numbers follow **ANSI/IEEE C37.2**. What is implemented today: * ✅ All source types: utility, generator, solar, wind, UPS * ✅ Six transformer variants plus autotransformer and three-winding * ✅ Bus, bus-tie, hub * ✅ IEC / REBT residential vocabulary aliases mapped onto canonical primitives * ✅ Full switching set: breaker, vacuum breaker, switch, load switch, ground switch, ATS, recloser, sectionalizer, fuse, current-limiting fuse * ✅ Protection and monitoring: CT, PT, relay (with ANSI device number), surge arrester, ground-fault detector * ✅ Load equipment: motor, load, capacitor bank, harmonic filter, VFD * ✅ Metering: watt-hour meter, demand meter * ✅ Directed connections with cable and label annotations * ✅ `label`, `voltage`, `rating`, `device` node attributes; arbitrary nameplate data * ⏳ Bus-tie switching with open/closed state indicator * ⏳ Protection zones (dashed boundary lines enclosing relay + CT) * ⏳ Arc-flash label blocks (incident energy, PPE level, working distance) * ⏳ Voltage-level banding (automatic color-coded horizontal bands by kV level) References: * IEEE Std 315-1975 / ANSI Y32.2-1975 — *Graphic Symbols for Electrical and Electronics Diagrams* * IEC 60617:2025 (BS 3939) — *Graphical symbols for diagrams* * ANSI/IEEE C37.2 — *Electrical Power System Device Function Numbers* *** ## 10. Related examples [Browse related examples](https://schematex.js.org/examples) *** ## 11. Roadmap **Planned — not yet parseable.** Do not use these in generated DSL today; the parser will reject or ignore them. * **Bus-tie open/closed state** — `bus_tie` with explicit open/closed attribute so the symbol renders in the correct switching position. * **Protection zones** — a `zone:` or `boundary:` block that draws a dashed rectangle around a set of nodes (relay + CT + breaker) to indicate a protection zone. * **Arc-flash annotation blocks** — `arc_flash: [incident_energy: "8 cal/cm²", ppe: "2", distance: "18in"]` per IEEE 1584. * **Voltage-level banding** — automatic horizontal shading bands by kV level; nodes are placed in their voltage tier automatically. * **Bidirectional connection** (`<->`) — for bus-tie or normally-open tie paths where direction is undefined. Track in the GitHub issues if you need any of these sooner. --- # Sociogram Canonical URL: https://schematex.js.org/docs/sociogram Markdown URL: https://schematex.js.org/docs/sociogram.md ## About sociograms A **sociogram** maps the web of choices, rejections, and alliances *within* a group — who likes whom, who is isolated, where cliques form. Jacob Moreno introduced the method in 1934 as a clinical tool for group therapy; it has since been adopted by classroom teachers (who use it to detect exclusion and bullying), HR teams (informal influence mapping), and organizational researchers. Unlike an ecomap, which centers on one person's external environment, a sociogram treats every member of the group symmetrically. Schematex follows the **[Moreno (1934) sociometry conventions](https://en.wikipedia.org/wiki/Sociometry)** for node roles and edge types, extended with modern social network analysis notations for valence, direction, and weight. This page documents what the parser accepts today. ```schematex sociogram "Group Therapy — Week 6" config: layout = circular therapist [label: "Dr. Park", role: star] james [label: "James"] maria [label: "Maria"] lee [label: "Lee"] sarah [label: "Sarah"] tom [label: "Tom"] nina [label: "Nina"] james -> therapist james <-> maria [weight: 3, label: "strong bond"] james -> lee maria -> therapist maria -> sarah lee -> therapist lee -x> nina [label: "tension"] sarah <-> nina sarah -> therapist tom -> therapist tom -.- nina nina -> maria ``` *** ## 1. Your first sociogram The smallest useful sociogram: four people, three different relationship types. ```schematex sociogram "Study group" alice [label: "Alice"] bob [label: "Bob"] carol [label: "Carol"] dave [label: "Dave"] alice <-> bob [label: "lab partners"] carol -> alice dave -x> bob [label: "rivalry"] ``` Four rules cover 80% of usage: 1. Start with the keyword `sociogram`, optionally followed by a quoted title. 2. Each person is a **node** — declared explicitly with `id [label: "…"]` or auto-created the first time they appear in an edge. 3. Connect two nodes with an **edge operator** — `<->` (mutual), `->` (one-way), `-x>` (rejection), `-.-` (neutral). See §3. 4. Optionally declare **groups** and **config** lines to control layout and coloring. > Comments must start with `#` on their own line. *** ## 2. Nodes A node line is `id [attr: value, …]`. Nodes are also created implicitly when first referenced in an edge — but explicit declaration lets you set labels, groups, and roles. **ID rules.** Must match `[a-zA-Z][a-zA-Z0-9_-]*`. The ID is used internally; the `label:` attribute sets the display name. **Node attributes:** | Attribute | Values | Effect | | ------------ | ---------------------------------------------------- | --------------------------------------------- | | `label: "…"` | quoted string | Display name (defaults to the ID) | | `group: id` | group ID | Associates the node with a group for coloring | | `role: …` | `star`, `isolate`, `bridge`, `neglectee`, `rejected` | Explicit sociometric role annotation | | `size: …` | `small`, `medium`, `large` | Node size override | ```schematex sociogram "Group roles demo" config: layout = circular dr_park [label: "Dr. Park", role: star] james [label: "James"] nina [label: "Nina", role: neglectee] loner [label: "Alex", role: isolate] bridge [label: "Sam", role: bridge, size: large] james -> dr_park james -> bridge bridge -> dr_park bridge -> nina nina -> james ``` *** ## 3. Edges An edge line is `leftId OP rightId` optionally followed by `[label: "…", weight: N]`. Both IDs are auto-registered as nodes if not yet declared. ### 3.1 Direction and valence ```schematex sociogram "Edge types" config: layout = circular a [label: "A"] b [label: "B"] c [label: "C"] d [label: "D"] e [label: "E"] f [label: "F"] # Positive a -> b [label: "chose B"] b <-> c [label: "mutual"] # Negative c -x> d [label: "rejects D"] d <x-> e [label: "mutual reject"] # Neutral e -.- f [label: "indifferent"] f <.-> a [label: "mutual neutral"] ``` | Operator | Direction | Valence | Meaning | | ---------- | ---------- | -------- | ------------------------------------------ | | `A -> B` | one-way | positive | A chose B | | `A <- B` | one-way | positive | B chose A (same as `B -> A`) | | `A <-> B` | mutual | positive | Both chose each other | | `A -- B` | undirected | positive | Relationship known; direction not recorded | | `A -x> B` | one-way | negative | A rejects B | | `A <x- B` | one-way | negative | B rejects A | | `A <x-> B` | mutual | negative | Mutual rejection | | `A -x- B` | undirected | negative | Conflict; direction unknown | | `A -.> B` | one-way | neutral | A is indifferent toward B | | `A <.-> B` | mutual | neutral | Mutual indifference | | `A -.- B` | undirected | neutral | Neutral relationship | ### 3.2 Weight / strength Higher weight = thicker line. Use the shorthand operators or override explicitly with `[weight: N]`. | Weight | Shorthand | Direction | Meaning | | ----------- | --------------------------- | ----------------------------- | ------------------- | | 2 (default) | `->` `<->` `--` `-x>` `-.-` | any | Standard connection | | 3 | `==>` `<==` `<==>` `===` | one-way / mutual / undirected | Strong | | 4 | `===>` `<===` `<===>` | one-way / mutual | Very strong | | custom | `[weight: N]` | — | Any integer | ```schematex sociogram "Relationship strengths" config: layout = circular a [label: "A"] b [label: "B"] c [label: "C"] d [label: "D"] a -> b [label: "weight 2 (default)"] b ==> c [label: "weight 3 (strong)"] c ===> d [label: "weight 4 (very strong)"] d -> a [weight: 1, label: "weight 1 (weak)"] ``` ### 3.3 Edge labels `A -> B [label: "best friend"]` — the label appears on the connecting line. *** ## 4. Groups A `group` block collects nodes into a named subgroup for coloring and layout clustering. **Group syntax:** * `group id [label: "…", color: "#hex"]` — the group header line. * Member lines follow, each indented **at least 4 spaces**, one node per line. * A non-indented line (or the next `group`) closes the current group. * Members can carry their own props: `anna [label: "Anna K.", size: large]`. Nodes can also be assigned inline: `alice [group: girls]`. ```schematex sociogram "Classroom dynamics" config: layout = force-directed group boys [label: "Boys", color: "#42A5F5"] tom [label: "Tom"] jack [label: "Jack"] mike [label: "Mike"] leo [label: "Leo"] group girls [label: "Girls", color: "#EF5350"] anna [label: "Anna"] beth [label: "Beth"] chloe [label: "Chloe"] diana [label: "Diana"] tom <-> jack anna <-> beth anna <-> chloe beth <-> chloe mike -x> leo [label: "conflict"] diana -> anna tom -> anna [label: "cross-group"] ``` *** ## 5. Configuration `config:` lines tune layout and visual encoding. Each is its own line. | Config key | Values | Default | Effect | | ----------- | ------------------------------------------ | ---------------- | -------------------------- | | `layout` | `circular`, `force-directed`, `concentric` | `circular` | Placement algorithm | | `sizing` | `uniform`, `in-degree`, `betweenness` | `uniform` | Node size by metric | | `coloring` | `default`, `group`, `role` | `default` | Node color scheme | | `highlight` | comma list: `stars`, `isolates`, `cliques` | `stars,isolates` | Which patterns to annotate | **Layout notes:** * `circular` — nodes evenly spaced on a ring. Best for small groups (≤15). * `force-directed` — spring model; clusters emerge automatically. Best for medium-sized groups with distinct subgroups. * `concentric` — inner rings hold high-in-degree nodes. Best for showing core-periphery structure. **Circular** — uniform ring placement; every node equally visible. Best for small, tightly-knit groups. ```schematex sociogram "Therapy group — circular" config: layout = circular dr_park [label: "Dr. Park", role: star] james [label: "James"] maria [label: "Maria"] lee [label: "Lee"] sarah [label: "Sarah"] tom [label: "Tom"] nina [label: "Nina", role: neglectee] james -> dr_park james <-> maria [weight: 3] james -> lee maria -> dr_park lee -> dr_park lee -x> nina sarah <-> nina sarah -> dr_park tom -> dr_park nina -> maria ``` **Force-directed** — spring physics pulls connected nodes together and pushes disconnected ones apart. Subgroups cluster organically. ```schematex sociogram "Classroom dynamics — force-directed" config: layout = force-directed group boys [label: "Boys", color: "#42A5F5"] tom [label: "Tom"] jack [label: "Jack"] mike [label: "Mike"] leo [label: "Leo"] group girls [label: "Girls", color: "#EF5350"] anna [label: "Anna"] beth [label: "Beth"] chloe [label: "Chloe"] diana [label: "Diana"] tom <-> jack anna <-> beth anna <-> chloe beth <-> chloe mike -x> leo [label: "conflict"] diana -> anna tom -> anna [label: "cross-group"] jack -> beth ``` **Concentric** — nodes sorted by in-degree; high-centrality nodes appear on inner rings, peripheral nodes on outer rings. ```schematex sociogram "Informal influence — concentric" config: layout = concentric config: sizing = in-degree vp [label: "VP Eng"] lead_a [label: "Lead A"] lead_b [label: "Lead B"] alice [label: "Alice"] bob [label: "Bob"] carol [label: "Carol"] dave [label: "Dave"] alice -> lead_a bob -> lead_a carol -> lead_a carol -> lead_b dave -> lead_b lead_a -> vp lead_b -> vp alice <-> bob carol <-> dave ``` *** ## 6. Sociometric roles The parser stores role annotations on nodes. The renderer uses them to apply visual badges — a star marker for `star`, a dashed border for `isolate`, and so on. | Role | Meaning | | ----------- | ---------------------------------------------- | | `star` | Central figure chosen by many (high in-degree) | | `isolate` | No connections in or out | | `neglectee` | Reaches out to others but receives no choices | | `rejected` | Receives rejection edges from multiple members | | `bridge` | Connects two otherwise separate clusters | ```schematex sociogram "Role annotations" config: layout = circular center [label: "Maria", role: star] bridge_node [label: "Sam", role: bridge] newcomer [label: "New Kid", role: neglectee] loner [label: "Alex", role: isolate] outcast [label: "Pat", role: rejected] center <-> bridge_node bridge_node -> outcast newcomer -> center newcomer -> bridge_node center -x> outcast bridge_node -x> outcast ``` *** ## 7. Labels & comments * **Title:** `sociogram "Study group"` — first line only. * **Node label:** `alice [label: "Alice K."]`. * **Group label:** `group boys [label: "Boys"]`. * **Edge label:** `alice -> bob [label: "lab partners"]`. * **Comments:** `#` at the start of a line (after leading whitespace). *** ## 8. Reserved words & escaping **Reserved at line start:** `sociogram` (header), `group`, `config:`. **Reserved operator tokens** — avoid these sequences inside IDs: `->`, `<-`, `<->`, `--`, `===`, `==>`, `<==`, `<===>`, `-x>`, `<x-`, `-x-`, `<x->`, `-.>`, `<.->`, `-.-`. **Strings with spaces** must be double-quoted in `label:` and `color:` values. *** ## 9. Common mistakes | You wrote | Parser says | Fix | | -------------------------------------- | ------------------------------------------------------- | ------------------------------------------------- | | `tom; jack; mike` on one group line | `tom;` fails the ID regex — silently ignored | One node per line, each indented ≥4 spaces | | Group members indented 2 spaces | Not treated as group members (parser requires ≥4) | Use 4+ spaces indent | | `alice <> bob` | No matching operator — not parsed as an edge | Use `<->` for mutual positive | | `config: layout = grid` | Unknown value silently ignored; layout stays `circular` | Use `circular`, `force-directed`, or `concentric` | | Node with a space in the ID: `dr park` | Parser takes `dr` as the ID and `park` as a stray token | Use underscore: `dr_park [label: "Dr. Park"]` | *** ## 10. Grammar (EBNF) ```text document = header (blank | comment | config | group-block | edge | node)* header = "sociogram" ( WS quoted-string )? NEWLINE quoted-string = '"' any-char-but-quote* '"' config = "config:" WS key WS "=" WS value NEWLINE key = "layout" | "sizing" | "coloring" | "highlight" group-block = "group" WS id ( "[" group-attrs "]" )? NEWLINE ( INDENT≥4 member-line )* member-line = id ( "[" node-attrs "]" )? NEWLINE group-attrs = group-attr ("," group-attr)* group-attr = "label:" quoted-string | "color:" quoted-string node = id ( "[" node-attrs "]" )? NEWLINE node-attrs = node-attr ("," node-attr)* node-attr = "label:" quoted-string | "group:" id | "role:" role | "size:" ("small" | "medium" | "large") edge = id WS op WS id ( "[" edge-attrs "]" )? NEWLINE edge-attrs = edge-attr ("," edge-attr)* edge-attr = "label:" quoted-string | "weight:" number op = // positive "<===>" | "===>" | "<===" | "<==>"|"==>"|"<==" | "===" | "<->" | "->" | "<-" | "--" // negative | "<x->" | "-x>" | "<x-" | "-x-" // neutral | "<.->" | "-\.>" | "-.-" role = "star" | "isolate" | "bridge" | "neglectee" | "rejected" id = [a-zA-Z] [a-zA-Z0-9_-]* comment = "#" any NEWLINE ``` Authoritative source: `src/diagrams/sociogram/parser.ts`. If this diverges from the parser, the parser wins — please open an issue. *** ## 11. Standard compliance Schematex sociograms follow **Moreno (1934) sociometry** conventions for node roles (star, isolate, neglectee) and directed-choice semantics. The edge operator set is extended with valence (positive / negative / neutral) and weight levels following modern social network analysis practice (Hanneman & Riddle, 2005). What is implemented today: * ✅ Directed, mutual, and undirected edges * ✅ Positive, negative, and neutral valence operators * ✅ Four weight levels (1–4) with shorthand operators and `[weight: N]` * ✅ Groups with color * ✅ Node role declarations (`star`, `isolate`, `bridge`, `neglectee`, `rejected`) * ✅ Three layouts: circular, force-directed, concentric * ✅ Three sizing modes: uniform, in-degree, betweenness * ⏳ Auto-detected clique highlighting — shaded convex hull (see §13) * ⏳ Social atom view (ego-centered layout) References: * Moreno, J.L. (1934). *Who Shall Survive? Foundations of Sociometry, Group Psychotherapy and Sociodrama.* Beacon House. * Hanneman, R.A. & Riddle, M. (2005). *Introduction to Social Network Methods.* UC Riverside. *** ## 12. Related examples [Browse related examples](https://schematex.js.org/examples) *** ## 13. Roadmap **Planned — not yet parseable.** Do not use these in generated DSL today; the parser will reject or ignore them. * **Auto-detected clique highlighting** — shaded convex hull drawn around mutual-choice subgroups of ≥3. * **Bridge auto-detection** — `role: bridge` inferred from betweenness centrality without explicit declaration. * **Social atom view** — ego-centered layout where one nominated node sits at the center. * **Reciprocity matrix export** — structured table output alongside the diagram. Track in the GitHub issues if you need any of these sooner. --- # State diagram Canonical URL: https://schematex.js.org/docs/state Markdown URL: https://schematex.js.org/docs/state.md ## About state diagrams A **state diagram** (also called a *statechart* or *state machine diagram*) describes the lifecycle of a reactive system — what states it can be in, what events cause it to move between them, and what actions run on entry, exit, or during a state. Software architects use them to specify workflows; controls engineers use them to design reactive controllers; UI designers use them to model screen lifecycles. They are formalized in **OMG UML 2.5.1 §14** (state machines) and Harel's 1987 *Statechart* extension that introduced composite states, history, and orthogonal regions. Schematex implements a strict superset of **[Mermaid `stateDiagram-v2`](https://mermaid.js.org/syntax/stateDiagram.html)** — every Mermaid example pastes in unchanged, plus you get UML 2.5 features Mermaid doesn't expose: `entry / exit / do` activities, full `trigger [guard] / action` transition labels, `terminate` and history pseudo-states, junction, and Schematex-style block notes. Layout uses the same Sugiyama-layered-DAG engine as flowcharts, so cycles, composite states, and cross-boundary transitions all route cleanly. ```schematex stateDiagram-v2 direction TB [*] --> Idle Idle --> Authenticating : submit [form_valid] / clearErrors() Authenticating --> Authenticated : ok / storeToken() Authenticating --> Idle : fail [retries < 3] / incrementRetries() Authenticating --> Locked : fail [retries >= 3] state Decide <<choice>> Authenticated --> Decide Decide --> Admin : [role == "admin"] Decide --> User : [role == "user"] ``` *** ## 1. Your first state diagram The smallest useful state diagram has an initial state, a final state, and one transition. ```schematex stateDiagram-v2 [*] --> Running Running --> [*] : done ``` Three rules cover 80% of usage: 1. Start with `stateDiagram-v2` (Mermaid-style) or `state` (Schematex-style) header. 2. Use `[*]` for the implicit start node (when on the left of `-->`) or end node (when on the right). 3. Connect states with `-->`. Add a label after `:` — full UML form is `trigger [guard] / action`. The default direction is **TB** (top-to-bottom) to match Mermaid. Override with `direction LR` on its own line, or `[direction: LR]` on the header. > Comments use `%%` (Mermaid), `#`, or `//`. *** ## 2. State declarations States are auto-created when first referenced in a transition, but explicit declarations let you control the label and shape. ``` state Authenticating state "Awaiting Approval" as Approval Idle: Waiting for input ``` | Form | Effect | | --------------------------------------- | ------------------------------------------------------------------------------- | | `Idle` | Bare ID — created as a simple state with `Idle` as both id and label | | `state Idle` | Explicit declaration; same effect | | `state "Awaiting Approval" as Approval` | Aliased — display `Awaiting Approval`, refer to it by `Approval` in transitions | | `Idle: Waiting for input` | Inline label — `Idle` is the id, `Waiting for input` is the visible label | *** ## 3. Pseudo-states Pseudo-states control the flow of a state machine without representing a stable resting state. | Mermaid | Schematex | Symbol | Purpose | | -------------------- | -------------------------------- | --------------------------------- | -------------------------------------------- | | `[*]` (source) | `initial id` | Filled black circle | Entry point of a region | | `[*]` (target) | `final id` | Filled circle inside outer ring | Successful exit | | `state X <<choice>>` | `choice X` | Diamond | Dynamic branch (guards evaluated at runtime) | | `state X <<fork>>` | `fork X` | Thick black bar | One-input → N parallel outputs | | `state X <<join>>` | `join X` | Thick black bar | N inputs → one output | | — | `junction X` | Small filled circle | Static merge point | | — | `history X` | Circle with `H` | Re-enter at last visited sub-state | | — | `dhistory X` | Circle with `H*` | Deep history (recursive) | | — | `terminate X` | `×` mark | Abnormal termination (no cleanup) | | — | `entry_point X` / `exit_point X` | Hollow circle on composite border | Named entry / exit points | `[*]` is the Mermaid alias and is always direction-resolved: on the **source** side of `-->` it's an `initial`, on the **target** side it's a `final`. Each composite scope gets its own pair. ```schematex stateDiagram-v2 [*] --> Idle state Decide <<choice>> state Joiner <<join>> Idle --> Decide : evaluate Decide --> Authorized : [role == "admin"] Decide --> Joiner : [role == "user"] Authorized --> Joiner Joiner --> [*] ``` *** ## 4. Transitions The full UML 2.5 transition label has three optional parts: ``` trigger [guard] / action ``` | Field | Meaning | Example | | ---------- | -------------------------------------------- | ---------------------------------- | | `trigger` | Event that fires the transition | `submit`, `tick`, `timeout(30s)` | | `[guard]` | Boolean expression evaluated at trigger time | `[count > 0]`, `[role == "admin"]` | | `/ action` | Action(s) executed when transition is taken | `/ log(); increment()` | All three are optional — an unlabeled transition is anonymous (completion transition). ``` A --> B %% anonymous completion A --> B : tick %% trigger only A --> B : [count > 0] %% guard only A --> B : / clearErrors() %% action only A --> B : tick [count > 0] / log() %% all three A --> B : tick, tock [enabled] / handle() %% multi-trigger ``` Long labels wrap automatically when they exceed the lane width. *** ## 5. Composite states A composite state contains a nested sub-statechart. The outer state acts as a container with its own initial / final pseudo-states. ``` state Playing { [*] --> Buffering Buffering --> Streaming : buffer_full Streaming --> Buffering : underflow } ``` Both Mermaid syntax (`state X { … }`) and the Schematex form (`composite X { … }`) are accepted. Activities can be declared inside the composite block: ``` state Playing { entry / startBuffer() exit / stopBuffer() do / decodeFrames() [*] --> Buffering Buffering --> Streaming : buffer_full Streaming --> Buffering : underflow } ``` The renderer draws composites as a styled cluster with title bar + activity compartment. ```schematex stateDiagram-v2 [*] --> Stopped Stopped --> Playing : play / loadSource() Playing --> Paused : pause Paused --> Playing : play Playing --> Stopped : stop / releaseSource() state Playing { entry / startBuffer() exit / stopBuffer() do / decodeFrames() [*] --> Buffering Buffering --> Streaming : buffer_full Streaming --> Buffering : underflow } ``` Cross-boundary transitions (`Outside --> Inside`) are routed automatically — the Sugiyama layout pulls the source/target through the composite border. *** ## 6. Concurrent regions Inside a composite, the `--` separator (Mermaid) or `---` (Schematex) splits the body into orthogonal regions that run concurrently. ``` state Active { [*] --> r1_idle r1_idle --> Connected : connect -- [*] --> r2_idle r2_idle --> Working : start } ``` Use `fork` and `join` to spawn / synchronize across regions: ```schematex stateDiagram-v2 [direction: LR] [*] --> F state F <<fork>> state J <<join>> F --> A F --> B A --> J : done_a B --> J : done_b J --> [*] ``` *** ## 7. Notes A short annotation can be attached to either side of any state. ``` note right of Checking : Calls /api/verify synchronously. note left of Idle : Anonymous landing state ``` Multi-line notes use the Mermaid `end note` block form, or the Schematex `{ … }` form: ``` note right of Authenticating Stores the JWT in localStorage on success. end note note left_of Idle { Anonymous landing state. Returns here on 401. } ``` *** ## 8. Self-transitions A transition `A --> A` renders as a curved arc on the right side of the node. ``` Idle --> Idle : poll / refresh() ``` The label is placed beside the arc, outside the bounding box. *** ## 9. Layout direction Schematex defaults to **`TB`** (top-to-bottom), matching Mermaid. Override on the header: ``` stateDiagram-v2 direction LR [*] --> Loading Loading --> Ready ``` Or with the Schematex bracket-attr form: ``` state "Order Lifecycle" [direction: TB] [*] --> Pending Pending --> Paid ``` `BT` and `RL` are accepted by the parser but normalized to `TB` and `LR` respectively (the layout engine doesn't yet flip the visual order). *** ## 10. Common mistakes | You wrote | Parser says | Fix | | ------------------------------- | -------------------------------------------------------------- | ---------------------------------------------------------- | | `[*] -> [*]` | Treated as both initial alias and final alias on the same line | Always have at least one named state between `[*]` aliases | | `state X <<branch>>` | `branch` is not a stereotype | Use `<<choice>>` (dynamic) or `<<fork>>` / `<<join>>` | | `note right of` `text` | Multi-line note must end with `end note` | Add `end note` on its own line | | `composite X` (no braces) | Treated as a bare state declaration | Open the block: `composite X {` | | `direction LR` inside composite | Per-region direction not yet supported | Set direction on the header line | *** ## 11. Grammar (EBNF) ```text document = header statement* header = ("stateDiagram-v2" | "stateDiagram" | "state") ( title )? ( "[" attrs "]" )? NEWLINE attrs = attr ("," attr)* attr = "direction:" ("TB" | "LR") statement = comment | direction-stmt %% direction LR / TB / BT / RL | state-decl | alias-decl %% state "Long" as ID | stereotype-decl %% state ID <<choice|fork|join|end>> | pseudo-decl %% initial / final / choice / ... ID | composite-block %% (state | composite) ID { ... } | label-stmt %% ID : description | transition | note-stmt | region-sep %% -- or --- transition = (ID | "[*]") "-->" (ID | "[*]") ( ":" trans-label )? NEWLINE trans-label = trigger? ( "[" guard "]" )? ( "/" action )? note-stmt = "note" side ID ":" inline-text NEWLINE | "note" side ID NEWLINE text-line+ ("end note" | "}") NEWLINE side = "left of" | "right of" | "left_of" | "right_of" comment = "%%" any | "#" any | "//" any ID = [A-Za-z_] [A-Za-z0-9_]* ``` Authoritative source: `src/diagrams/state/parser.ts`. If this diverges from the parser, the parser wins — please open an issue. *** ## 12. Standard compliance | Feature | UML 2.5 | Harel 1987 | Mermaid | Schematex | | --------------------------------- | :-----: | :--------: | :------------: | :-------: | | Simple state | ✅ | ✅ | ✅ | ✅ | | Composite (nested) state | ✅ | ✅ | ✅ | ✅ | | Initial / final pseudo-state | ✅ | ✅ | ✅ | ✅ | | Choice pseudo-state | ✅ | — | ✅ | ✅ | | Fork / join | ✅ | ✅ | ✅ | ✅ | | Junction pseudo-state | ✅ | — | ❌ | ✅ | | History (shallow / deep) | ✅ | ✅ | ❌ | ✅ | | Terminate pseudo-state | ✅ | — | ❌ | ✅ | | `entry / exit / do` activities | ✅ | ✅ | ❌ | ✅ | | `trigger [guard] / action` labels | ✅ | ✅ | ❌ (label only) | ✅ | | Internal transitions | ✅ | ✅ | ❌ | ✅ | | Concurrent regions | ✅ | ✅ | ✅ | ✅ | | Notes on states | — | — | ✅ | ✅ | | Cross-composite transitions | ✅ | ✅ | ❌ | ✅ | | `[*]` initial/final alias | — | — | ✅ | ✅ | Schematex is a strict superset of Mermaid `stateDiagram-v2`. Mermaid examples paste in unchanged; the additional UML 2.5 elements (activities, history, junction, terminate, full transition labels) are accepted alongside. References: * OMG UML 2.5.1 — *Unified Modeling Language*: [https://www.omg.org/spec/UML/2.5.1/](https://www.omg.org/spec/UML/2.5.1/) * Harel (1987) — *Statecharts: A visual formalism for complex systems*, Science of Computer Programming 8(3) * Mermaid stateDiagram-v2 — [https://mermaid.js.org/syntax/stateDiagram.html](https://mermaid.js.org/syntax/stateDiagram.html) *** ## 13. Roadmap * **`BT` / `RL` directions** — currently normalized to `TB` / `LR` at parse time; visual flip pending * **Per-region direction override** — `direction LR` inside a composite block (currently silently ignored) * **Submachine references** — `state Foo : Submachine` stereotype rendering * **Internal transition compartment** — explicit visual divider for `tick [g] / a` lines inside a state body *** ## Related examples Ready-to-use scenarios from the examples gallery: [Browse related examples](https://schematex.js.org/examples) ## Interactive editing Double-click authored state or transition labels to edit their exact source ranges. Drag states only across the primary layout axis; transition progression remains semantic and connectors follow live. --- # Threat Model (STRIDE DFD) > Security data-flow diagrams where the engine maps STRIDE threats per element and flags every trust-boundary crossing. Canonical URL: https://schematex.js.org/docs/threatmodel Markdown URL: https://schematex.js.org/docs/threatmodel.md ## About threat models A **threat model** is a security **data-flow diagram** (DFD) annotated with trust boundaries: **external entities** (users, third parties), **processes** (services), **data stores** (databases, logs), and the **flows** between them. Microsoft's **STRIDE** framework — Spoofing, Tampering, Repudiation, Information disclosure, Denial of service, Elevation of privilege — classifies the threats each element faces. The reference is Shostack, *Threat Modeling: Designing for Security* (2014). Schematex's edge is that the engine **does the STRIDE-per-element analysis**, not just the boxes. It applies the canonical element→threat mapping, conditionally adds Repudiation to log/audit stores, and — most usefully — **flags every flow that crosses a trust boundary**, because that is where spoofing, tampering, and information-disclosure concentrate. ```schematex threatmodel "Web App" external: User process 1.1: Web Server datastore D1: User DB datastore D2: Audit Log User -> 1.1 : "HTTPS Request" 1.1 -> D1 : Lookup 1.1 -> D2 : Auth Event boundary "Internet" { User } boundary "DMZ" { 1.1 } boundary "Internal" { D1, D2 } ``` *** ## 1. Your first threat model Start with the `threatmodel` keyword (alias `stride`), an optional title, then **declare elements** and **wire flows**: ``` threatmodel "Login flow" external: User process 1.1: Web Server datastore D1: User DB User -> 1.1 : "Login request" 1.1 -> D1 : Lookup ``` Each element is `kind: ID: Label` (or `kind: Label`, where the id is slugged from the label — `external: Mobile App` becomes id `Mobile_App`). A flow is `SOURCE -> TARGET : label`, and the **label is mandatory** (it names the data crossing). *** ## 2. Element kinds and flows ``` external: User # external entity (the attacker's side) process 1.1: Web Server # a process / service datastore D1: User DB # a data store datastore D2: Audit log # a log/audit store (gets conditional Repudiation) User -> 1.1 : "HTTPS Request" # directed flow, quoted or bare label 1.1 <-> D1 : Read/Write # <-> expands into two directed flows ``` * Process ids are often dotted DFD numbers (`1.1`, `2.3`); external/store ids are usually short slugs (`User`, `D1`). * A `<->` flow expands into **two** directed flows. * A store whose name/id matches `log|audit|journal` (or carries an explicit hint) is treated as a log store. **Flow rules** the engine enforces: no store→store flows (data stores are passive), no external→external flows, and every endpoint must be a declared element. *** ## 3. Trust boundaries ``` boundary "Internet" { User } boundary "DMZ" { 1.1 } boundary "Internal" { D1, D2 } ``` `boundary "name" { id, id, … }` groups elements into a trust zone. An element may belong to **at most one** boundary; members must be declared. Elements in no boundary share an implicit untrusted zone. *** ## 4. Computed STRIDE analysis This is the differentiator. The engine applies the **STRIDE-per-element** mapping: | DFD element | Threats applied | | --------------- | -------------------- | | External entity | S, R | | Process | S, T, R, I, D, E | | Data store | T, I, D (+ R if log) | | Data flow | T, I, D | * The data-store **Repudiation** is conditional — added for log / audit / journal stores (the Shostack green "?"). * **Boundary crossing**: a flow whose endpoints sit in different trust zones is flagged, because that is where Spoofing / Tampering / Information-disclosure concentrate. Two elements in the same (or implicit) zone do not cross. Each element and flow carries its applicable STRIDE categories in `data-*` so the analysis is inspectable. *** ## 5. Common mistakes ``` # WRONG — flow with no label User -> 1.1 # WRONG — store to store (data stores are passive) D1 -> D2 : x # WRONG — external to external A -> B : x # WRONG — unknown flow endpoint P -> Ghost : x # WRONG — an element in two boundaries boundary "A" { P } boundary "B" { P } ``` Every flow needs a label; stores and externals cannot be flow partners with their own kind; endpoints must be declared; an element belongs to at most one trust boundary. Duplicate ids are rejected. *** ## 6. Standard compliance The element vocabulary, DFD conventions, and the per-element STRIDE table follow Shostack (2014) and the Microsoft Threat Modeling Tool. The conditional log-store Repudiation and trust-boundary-crossing emphasis match the published STRIDE-per-element chart. ## 7. Roadmap Deferred: DREAD/risk scoring, threat-mitigation linkage, multi-process decomposition (DFD levels), and attack-tree drill-down. --- # Timeline diagram Canonical URL: https://schematex.js.org/docs/timeline Markdown URL: https://schematex.js.org/docs/timeline.md ## About timelines A **timeline diagram** arranges events along a shared time axis so that chronological relationships are immediately visible. Project managers use them to show sprint schedules and milestones; historians use them to place discoveries in context; journalists use them to reconstruct event sequences; clinical researchers use them to map treatment courses. The visual form has no single governing standard, but the conventions are universal: time runs left to right, events are marked at their dates, and spans show duration. Schematex renders timelines in three visual styles — **swimlane** (events spread above and below an axis), **gantt** (horizontal duration bars, useful for project scheduling), and **lollipop** (dot-on-stick markers, useful for historical retrospectives). All three share the same DSL; the `style` config key switches between them. ```schematex timeline "History of the Web" config: style = lollipop 1991: "World Wide Web — Berners-Lee" 1993: milestone "Mosaic browser" [side: above, color: #1565C0] 1995: "JavaScript — Brendan Eich" [side: below] 1998: milestone "Google founded" [side: above, color: #E53935] 2004: "Facebook launches" [side: below] 2007: milestone "iPhone — mobile web era" [color: #2E7D32, side: above] 2009: "Node.js — server-side JavaScript" [side: below] 2015: milestone "ES6 / React mainstream" [side: above, color: #6A1B9A] 2022: "ChatGPT — AI era begins" [side: below] ``` *** ## 1. Your first timeline The smallest useful timeline: a title, two ordinary events, and a milestone. ```schematex timeline "Product Launch" 2024-06-01: "Development complete" 2024-08-15: "Closed beta" 2024-09-01: milestone "Public launch" ``` Four rules cover 80% of usage: 1. Start with the keyword `timeline`, optionally followed by a quoted title. 2. Each event is `DATE: "Label"` — a date, a colon, then a quoted label. A `milestone` keyword before the label marks the event as a milestone. 3. Date ranges use `DATE - DATE:` or `DATE .. DATE:` (both are equivalent). 4. Add `[key: value]` properties after the quoted label to set color, side placement, shape, or category. > Comments must start with `#` or `//` on their own line. *** ## 2. Events An event line places a labeled marker at a point in time (or a bar across a span). ### 2.1 Point events ``` 2024-09-15: "Conference keynote" ``` ### 2.2 Range events ``` 2024-06-01 - 2024-08-31: "Q3 development sprint" 2024-06-01 .. 2024-08-31: "Q3 development sprint" ``` Both separators (`-` surrounded by spaces, or `..`) are equivalent. ### 2.3 Milestones ``` 2024-09-01: milestone "Public launch" ``` The `milestone` keyword before the quoted label switches the marker to a diamond shape. ### 2.4 Event properties Properties go in `[key: value, …]` after the quoted label, before the newline. | Property | Values | Meaning | | ----------- | ----------------------------------------------------- | ------------------------------------------------------------- | | `color:` | hex string | Custom color for this marker or bar | | `side:` | `above` \| `below` | Force placement above or below the axis (swimlane / lollipop) | | `icon:` | any text (e.g. emoji) | Icon displayed with the event | | `shape:` | `circle` \| `square` \| `diamond` \| `star` \| `flag` | Point marker shape | | `category:` | quoted string | Group label — drives color in gantt legend | ``` 2024-09-01: milestone "Launch day" [color: #E53935, side: above] 2024-07-15: "Beta ships" [icon: 🚀, category: "product"] ``` ```schematex timeline "Engineering Milestones" config: style = lollipop 2024-01-08: "Sprint planning complete" 2024-02-14: milestone "Design system shipped" [side: above, color: #1565C0] 2024-03-22: "Incident — 4h outage" [icon: ⚠️] 2024-04-01 - 2024-06-30: "API v2 build" 2024-07-01: milestone "API v2 GA" [side: above, color: #2E7D32] ``` *** ## 3. Date formats All date formats can appear anywhere a date is expected — in events, eras, and ranges. | Format | Example | Notes | | ------------------ | ----------------------- | -------------------------------------- | | Full date | `2024-09-15` | Day-precision; `YYYY-MM-DD` | | Year-month | `2024-09` | Month-precision | | Year | `2024` | Year-precision | | Quarter | `2024-Q3` | Maps to start of that quarter | | BC year (negative) | `-753` | 753 BC | | BC year (suffix) | `753BC` or `753BCE` | Same as `-753` | | Geological | `65Ma`, `4.6Ga`, `12ka` | Million / billion / thousand years ago | ```schematex timeline "Age of Earth" config: style = lollipop config: scale = log 4600Ma: "Earth forms" 540Ma: "Cambrian explosion" 252Ma: milestone "Permian extinction — 96% of species lost" 66Ma: milestone "K-Pg extinction — end of non-avian dinosaurs" 3Ma: "Earliest stone tools (Lomekwi)" 0: "Present day" ``` *** ## 4. Eras (background spans) An `era` line draws a shaded background band across the time axis. It always requires a date range. ``` era 2020 - 2022: "Foundation Phase" era 2022 .. 2025: "Growth Phase" [color: #E8F5E9] ``` The `[color: …]` property is optional. Overlapping eras stack into separate bands automatically. ```schematex timeline "Company History" config: style = swimlane era 2019..2021: "Early Stage" [color: #FFF8E1] era 2021..2024: "Series A & B" [color: #E8F5E9] 2019-03-01: "Founded" 2019-11-15: "First paying customer" 2020-05-01: milestone "Product-market fit" [side: above] 2021-02-10: "Series A — $8M" 2022-09-01: "Series B — $30M" 2023-06-15: milestone "Profitability" [color: #1B5E20] ``` *** ## 5. Tracks (swimlane grouping) A `track` block groups related events into a named swimlane. Indented event lines (2 spaces) belong to the track. ``` track "Engineering": 2024-01-15 - 2024-03-31: "API design" 2024-04-01 - 2024-07-31: "Implementation" track "Marketing": 2024-06-01: "Campaign kick-off" 2024-09-01: milestone "Launch campaign" [color: #1B5E20] ``` Tracks are most useful in `gantt` style, where each track becomes its own labeled row. ```schematex timeline "Q3 Project Plan" config: style = gantt track "Backend": 2024-07-01 - 2024-08-15: "Database migration" 2024-08-16 - 2024-09-30: "API hardening" track "Frontend": 2024-07-01 - 2024-08-31: "New design system" 2024-09-01 - 2024-09-30: "Integration & QA" track "DevOps": 2024-07-15 - 2024-08-01: "Staging environment" 2024-09-30: milestone "Go-live" [color: #2E7D32] ``` *** ## 6. Notes A `note:` line indented under an event attaches a tooltip annotation to it. ``` 2024-06-01: "Beta launch" note: "First external users; NPS target 40+" ``` Notes work both for flat events and for events inside tracks. *** ## 7. Configuration `config:` lines tune the layout and visual style. Each is its own line in the form `config: key = value`. | Key | Values | Default | Effect | | ------------- | ---------------------------------------- | -------------- | ----------------------------- | | `style` | `swimlane` \| `gantt` \| `lollipop` | `swimlane` | Visual rendering mode | | `orientation` | `horizontal` \| `vertical` | `horizontal` | Axis direction | | `scale` | `proportional` \| `equidistant` \| `log` | `proportional` | How time maps to screen space | | `axis` | `bottom` \| `center` | `bottom` | Where the time axis is drawn | **Style notes:** * `swimlane` — events alternate above and below a horizontal axis; `side:` controls per-event placement. Eras add colored background bands. Best for roadmaps and biographies. * `gantt` — each named track becomes a horizontal bar lane; milestones become pins above the bars. `gantt-project` is an alias. Best for project scheduling. * `lollipop` — a center axis with labeled cards on alternating stems. Best for historical retrospectives with sparse, memorable events. **Swimlane** — roadmaps, product timelines, biographies. Eras add color bands; events alternate above and below. ```schematex timeline "SaaS Platform Roadmap" config: style = swimlane era 2024-Q1..2024-Q2: "Foundation" [color: #E3F2FD] era 2024-Q3..2024-Q4: "Growth" [color: #E8F5E9] 2024-01-15: "Kick-off & team onboarding" 2024-02-01: milestone "Architecture sign-off" [side: above] 2024-03-01 - 2024-05-31: "API v2 build" 2024-04-10: "Security audit" [color: #F9A825] 2024-06-30: milestone "Beta launch" [side: above] 2024-07-01 - 2024-09-30: "Performance & scaling" 2024-10-15: milestone "General availability" [color: #2E7D32] ``` **Gantt** — parallel tracks with horizontal duration bars and milestone pins. Best for project scheduling. ```schematex timeline "Product Launch — Q4" config: style = gantt track "Engineering": 2024-10-01 - 2024-11-15: "Feature freeze & hardening" 2024-11-16 - 2024-11-30: "Load testing" track "Design": 2024-10-01 - 2024-10-31: "Final UI polish" 2024-11-01 - 2024-11-14: "Asset delivery" track "Marketing": 2024-10-15 - 2024-11-14: "Campaign prep" 2024-11-15 - 2024-11-30: "Launch campaign" 2024-12-01: milestone "Launch day" [color: #2E7D32] ``` **Lollipop** — sparse milestones on a centered axis with alternating cards. Best for historical retrospectives and brand stories. ```schematex timeline "History of Computing" config: style = lollipop config: scale = equidistant 1936: "Turing — On Computable Numbers" 1945: "ENIAC — first general-purpose computer" 1969: "ARPANET" 1971: milestone "Intel 4004 microprocessor" [side: above] 1976: "Apple I" 1991: "World Wide Web (Berners-Lee)" 2007: milestone "iPhone" [color: #1565C0, side: above] ``` *** ## 8. Labels & comments * **Title:** `timeline "My Title"` — first line only. * **Event label:** quoted string after the colon: `DATE: "Label"`. * **Milestone label:** `DATE: milestone "Label"`. * **Era label:** `era DATE - DATE: "Label"`. * **Track name:** `track "Name":`. * **Note:** `note: "text"` indented under an event. * **Comments:** `#` or `//` at the start of a line (after leading whitespace). *** ## 9. Reserved words & escaping **Reserved at line start:** `timeline` (header), `config:`, `era`, `track`, `note:`. **Date range separators:** `-` (space-hyphen-space) and `..` — avoid these sequences inside label text that appears before the colon. **BC years as negative integers:** `-753` is the year 753 BC. The parser distinguishes the negative sign from a range separator by checking for surrounding whitespace — `-` (with spaces) is a range; `-753` (no leading space after a colon) is a BC year. **Property blocks:** `[key: value]` must appear *after* the quoted label on the same line. The closing `]` must be present; unclosed brackets produce a parse error. *** ## 10. Common mistakes | You wrote | Parser says | Fix | | ---------------------------------------------------- | --------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------- | | `2024-06-01: Launch day` (unquoted label) | Line not recognized as an event — `TimelineParseError` | Quote the label: `2024-06-01: "Launch day"` | | `2024-06 - 2024-09: "Q3"` (year-month range) | Parsed correctly | This works — all date formats are valid in ranges | | `era 2024: "Whole year"` (no range) | `TimelineParseError: era requires a date range` | Use a range: `era 2024 - 2024: "Whole year"` | | `track "Backend"` (no colon) | `TimelineParseError: Expected ':' after track name` | Add the colon: `track "Backend":` | | `2024-01-01: "Event" [side: left]` | `side: left` silently ignored; only `above` and `below` are valid | Use `side: above` or `side: below` | | `config: style = Gantt` (capital G) | `TimelineParseError: Invalid style: Gantt` | Use lowercase: `config: style = gantt` | | `2024-01-01-2024-03-31: "Q1"` (no spaces around `-`) | Parser reads `2024-01-01-2024` as a date — fails | Use spaces: `2024-01-01 - 2024-03-31:` or `..`: `2024-01-01..2024-03-31:` | | Indented event without a track | Indented lines under the timeline header that aren't inside a `track` block — parsed as flat events | Only indent events that are inside a `track "Name":` block | *** ## 11. Grammar (EBNF) ```text document = header ( blank | comment | config | era | track | event )* header = "timeline" ( WS quoted-string )? NEWLINE quoted-string = '"' any-char-but-quote* '"' config = "config:" WS key WS "=" WS value NEWLINE key = "style" | "orientation" | "scale" | "axis" era = "era" WS date-range ":" WS quoted-string ( WS props )? NEWLINE track = "track" WS quoted-string ":" NEWLINE ( INDENT≥2 event | INDENT≥2 note )* event = date-spec ":" WS event-body ( WS props )? NEWLINE ( INDENT note )? event-body = ( "milestone" WS )? quoted-string date-spec = date ( ( " - " | ".." ) date )? note = "note:" WS quoted-string NEWLINE props = "[" prop-list "]" prop-list = prop ( "," prop )* prop = key ":" WS value | key ":" WS quoted-string date = iso-date | year-month | year | quarter | bc-year | geological iso-date = digit{4} "-" digit{2} "-" digit{2} year-month = digit{4} "-" digit{2} year = "-"? digit{1,5} quarter = digit{4} "-"? "Q" [1-4] bc-year = digit+ ( "BC" | "BCE" ) geological = number ( "Ma" | "Ga" | "ka" ) comment = ( "#" | "//" ) any NEWLINE ``` Authoritative source: `src/diagrams/timeline/parser.ts`. If this diverges from the parser, the parser wins — please open an issue. *** ## 12. Standard compliance Timeline diagrams have no single governing standard. Schematex follows established visualization conventions: * **Swimlane style** — adapted from the horizontal timeline convention used in academic historiography and project management (PMI PMBOK). * **Gantt style** — follows the Gantt chart form introduced by Henry Gantt (1910–1915); bar-per-track layout matches the ISO 21500 project scheduling convention. * **Lollipop style** — follows the dot plot / stem plot convention common in data journalism and infographics. What is implemented today: * ✅ Three visual styles: swimlane, gantt, lollipop * ✅ Horizontal and vertical orientation * ✅ Three scale modes: proportional, equidistant, log * ✅ Point events, range events (bars), and milestone markers * ✅ Eras (background shading bands) * ✅ Named tracks (swimlane rows) * ✅ Event notes * ✅ Six date formats: full date, year-month, year, quarter, BC year, geological (Ma/Ga/ka) * ✅ Event properties: color, side, icon, shape, category * ⏳ Vertical orientation rendering (parsed, layout not yet implemented) * ⏳ `log` scale rendering (parsed, layout treats as proportional today) * ⏳ Axis tick labels with custom format strings * ⏳ Event connectors (arrows between related events) References: * Gantt, H.L. (1910). *Work, Wages, and Profits.* Engineering Magazine. * Tufte, E.R. (1983). *The Visual Display of Quantitative Information.* — Timeline as small multiples. *** ## 13. Roadmap **Planned — not yet parseable.** Do not use these in generated DSL today; the parser will reject or ignore them. * **Event connectors** — `connect ev1 -> ev2 [label: "causes"]` arrows linking two events across time. * **Repeat / recurrence** — `every 2024-Q1 .. 2024-Q4 monthly: "Sprint review"` for regular events. * **Axis tick format** — `config: tickFormat = "%b %Y"` for custom date display on the axis. * **Vertical orientation** — `config: orientation = vertical` with events left/right of a vertical axis (parsed; rendering deferred). * **Log scale** — `config: scale = log` for geological or deep-history spans that need compressed recent years (parsed; layout deferred). Track in the GitHub issues if you need any of these sooner. *** ## Related examples Ready-to-use scenarios from the examples gallery: [Browse related examples](https://schematex.js.org/examples) ## Interactive editing The title is editable and movable. Native point and range-edge handles move horizontally and rewrite authored ISO dates; they never persist decorative canvas coordinates. --- # Timing diagram Canonical URL: https://schematex.js.org/docs/timing Markdown URL: https://schematex.js.org/docs/timing.md ## About timing diagrams A **timing diagram** shows how digital signals change over time — clock pulses, bus transitions, data values, and high-impedance states — drawn as a set of horizontal waveform bands with a shared time axis. Hardware engineers use them to specify protocol behavior, verify setup and hold constraints, and document chip interfaces. They appear in datasheets, HDL simulation reports, and digital systems textbooks. Schematex uses a **[WaveDrom-compatible](https://wavedrom.com/tutorial.html)** signal notation — the same wave characters (`0`, `1`, `x`, `z`, `p`, `=`, …) and data label syntax that WaveDrom pioneered — so existing WaveDrom DSL transfers directly. This page documents what the parser accepts today. ```schematex timing "SPI Transaction" CLK: pppppppp CS_N: 10000001 MOSI: x======= data: ["0xAB","0xCD","0xEF","0x01","0x02","0x03","0x04","0x05"] MISO: zzzz==== data: ["","","","","0xFF","0x12","0x34","0x56"] ``` *** ## 1. Your first timing diagram The smallest useful timing diagram: a clock and one data signal. The friendliest way to write it avoids counting characters entirely: ```schematex timing CLK: clock 8 RST: rle 1*2 0*6 DATA: 0011==00 data: ["A","B"] ``` Three rules cover 80% of usage: 1. Start with the keyword `timing`, optionally followed by a quoted title and `[hscale: N]`. 2. Each signal is one line: `NAME: <wave>` — name, colon, then the waveform. The waveform can be: * **`clock N`** — a clock generator with `N` periods (add `neg` for a negedge clock). No character-counting. * **`rle <state>*<count> …`** — run-length segments, e.g. `rle 1*2 0*6` = `11000000`. Auto-aligns length. * **a raw WaveDrom wave string** — a contiguous run of state characters (no internal spaces) for fine control. 3. Add `data: ["val1", "val2"]` after a raw wave string to label bus segments. > **Tip for alignment:** the #1 cause of a broken timing diagram is signals of unequal length. `clock N` and `rle` make every signal's cell count explicit, so they line up. Use raw wave strings only when you need per-cell control. > Comments must start with `#` on their own line. *** ## 2. Wave characters The wave string is a sequence of characters, one per time period. The parser accepts these: | Character | State | Meaning | | --------- | ---------------------------- | ------------------------------------------------------------------ | | `0` | Logic low | Signal at GND / VSS | | `1` | Logic high | Signal at VDD | | `x` | Unknown | Don't-care, undefined, or uninitialized | | `z` | High-Z | Tri-state / high-impedance | | `p` | Clock pulse (positive) | Rising-edge-active clock; one `p` = one full period (low→high→low) | | `P` | Clock pulse (positive, tall) | Same as `p`, visually taller | | `n` | Clock pulse (negative) | Falling-edge-active; one `n` = one full period (high→low→high) | | `N` | Clock pulse (negative, tall) | Same as `n`, visually taller | | `=` | Bus data | Parallel-bus segment; add labels via `data: […]` | | `2`–`9` | Named bus segment | Same as `=`, indexed into `data: […]` by position | | `.` | Hold / continue | Extend the previous state for one more period | | `h` / `H` | Hold high | Force-high for this period | | `l` / `L` | Hold low | Force-low for this period | | `u` | Rising edge | Diagonal from low to high (transition only) | | `d` / `D` | Falling edge | Diagonal from high to low (transition only) | ```schematex timing "Wave character reference" clk: pppppppppp high: 1111111111 low: 0000000000 unkn: xxxxxxxxxx hiz: zzzzzzzzzz bus: x========x data: ["ADDR","DATA","","","","","",""] hold: 0..1..0..1 rise: 0u1 fall: 1d0 ``` *** ## 3. Data labels When a signal carries a bus value, tag the wave with `data: ["label1", "label2", …]`. Each non-empty quoted string is placed inside the corresponding `=` (or `2`–`9`) segment. ``` MOSI: x======= data: ["0xAB","0xCD","0xEF","0x01","0x02","0x03","0x04","0x05"] ``` Empty strings `""` leave a segment unlabeled (useful for segments that extend a previous value). ``` MISO: zzzz==== data: ["","","","","0xFF","0x12","0x34","0x56"] # first four z-periods have no label; four = segments get labels starting at 0xFF ``` ```schematex timing "I2C read burst" SCL: ppppppppppp SDA: x1=======1x data: ["ADDR+R","ACK","D0","D1","D2","D3","D4","D5","NACK"] ``` *** ## 4. Grouping signals Wrap related signals in a `[GroupName]` block. A `---` line closes the group and also acts as a visual separator between groups. ``` [Control] CLK: pppppppp CS_N: 10000001 --- [Data] MOSI: x======= data: ["0xAB","0xCD","0xEF","0x01","0x02","0x03","0x04","0x05"] MISO: zzzz==== data: ["","","","","0xFF","0x12","0x34","0x56"] ``` Alternative `group "name" { … }` syntax is also accepted (closing `}` closes the group). ```schematex timing "UART frame" [Clock & control] CLK: pppppppppppp TX_EN: 0111111110 --- [Data lines] TX: 1========== data: ["START","D0","D1","D2","D3","D4","D5","D6","D7","STOP"] RX: zz1=======1 data: ["","","D0","D1","D2","D3","D4","D5","D6","D7"] ``` *** ## 5. Title and hscale **Title:** `timing "SPI Transaction"` — appears at the top of the diagram. **hscale:** `timing "title" [hscale: 2]` — scales the width of each time period. Default is 1. Use 2 for wider periods when data labels need more room. ``` timing "Wide bus" [hscale: 2] CLK: pppp DATA: ==== data: ["long label here","another","third","fourth"] ``` *** ## 6. Labels & comments * **Signal name:** anything before the first `:` on a signal line. Names with spaces are fine — the colon is the delimiter. * **Data labels:** `data: ["a", "b"]` after the wave string. * **Title:** first token after `timing` keyword, quoted. * **Comments:** `#` at the start of a line (after leading whitespace). ``` timing "Demo" # this is a comment CLK: pppp # ← inline trailing comment is NOT supported ``` *** ## 7. Common mistakes | You wrote | Parser says | Fix | | ------------------------------- | ------------------------------------------------------------------ | ------------------------------------------- | | `CLK: p p p p` (spaces in wave) | Wave string parsed as `p` only; the rest is treated as data clause | Remove spaces: `CLK: pppp` | | `DATA: =====` with no `data:` | Segments render as unlabeled bus cells | Add `data: ["A","B","C","D","E"]` | | Wave character `s` or `r` | `TimingParseError: Invalid wave string` | Only the characters listed in §2 are valid | | `CLK pppp` (no colon) | Line does not match signal pattern; silently skipped | The colon after the signal name is required | | `data: [A, B, C]` (unquoted) | Values not recognized — parser looks for `"…"` | Quote each value: `data: ["A","B","C"]` | | `[Group Name with spaces]` | Group label is `Group Name with spaces` — parsed fine | Supported | | `hscale: 2` on its own line | Not recognized (hscale goes on the header line) | `timing "title" [hscale: 2]` | *** ## 8. Grammar (EBNF) ```text document = header (blank | comment | group-open | group-close | separator | signal)* header = "timing" ( WS quoted-string )? ( WS "[" "hscale:" number "]" )? NEWLINE quoted-string = '"' any-char-but-quote* '"' group-open = "[" label "]" NEWLINE | "group" WS quoted-string WS "{"? NEWLINE group-close = "}" NEWLINE separator = "---" NEWLINE signal = name ":" WS wave-string ( WS data-clause )? NEWLINE name = any text before the first ":" wave-string = wave-char+ wave-char = "0"|"1"|"x"|"z" | "p"|"P"|"n"|"N" | "h"|"H"|"l"|"L" | "u"|"d"|"D" | "="|"."|"2"|"3"|"4"|"5"|"6"|"7"|"8"|"9" data-clause = "data:" WS ( "[" quoted-string ("," quoted-string)* "]" | quoted-string+ ) comment = "#" any NEWLINE ``` Authoritative source: `src/diagrams/timing/parser.ts`. If this diverges from the parser, the parser wins — please open an issue. *** ## 9. Standard compliance Schematex timing diagrams follow the **[WaveDrom WaveJSON](https://wavedrom.com/tutorial.html)** signal notation for wave characters and data labels — the same set used by WaveDrom's online editor, making Schematex DSL largely interchangeable with WaveDrom input. The `hscale` option, group syntax, and data label format are all compatible. What is implemented today: * ✅ All core wave characters: `0 1 x z p P n N h H l L u d D = . 2–9` * ✅ Data labels via `data: ["…"]` * ✅ Group blocks: `[Name]` and `group "name" { }` syntax * ✅ `---` separator / group-close * ✅ `hscale` period-width multiplier * ⏳ Timing annotations (arrows between signal transitions, `t_su`, `t_pd` labels) * ⏳ `phase:` offset per signal (fractional cycle shift) * ⏳ WaveDrom `node:` / `edge:` annotation blocks * ⏳ Skin / theme (`default`, `narrow`, `lowkey`) References: * WaveDrom — [https://wavedrom.com](https://wavedrom.com) (WaveJSON specification) * IEEE Std 1364 (Verilog HDL) — digital timing simulation concepts * IEEE Std 1497 (Standard Delay Format) — timing annotation conventions *** ## 10. Related examples [Browse related examples](https://schematex.js.org/examples) *** ## 11. Roadmap **Planned — not yet parseable.** Do not use these in generated DSL today; the parser will ignore them. * **Timing annotation arrows** — `annotate:` block with `A -> B [label: "t_su = 5ns"]` syntax to draw setup/hold and propagation-delay spans between signal transitions. * **`phase:` per signal** — fractional-cycle offset so signals can start part-way through a period. * **WaveDrom `node:` / `edge:`** — full WaveDrom annotation block compatibility. * **Skin options** — `narrow` (compact) and `lowkey` (muted palette) rendering themes. * **Time axis** — optional numeric time axis at the bottom (ns, µs, ps units). Track in the GitHub issues if you need any of these sooner. ## Interactive editing The title is editable and movable. Native waveform-boundary handles resize adjacent states on the time axis and rewrite the literal wave token while signal-row order stays fixed. --- # UML Class Diagram Canonical URL: https://schematex.js.org/docs/umlclass Markdown URL: https://schematex.js.org/docs/umlclass.md ## About class diagrams A **class diagram** is the backbone of object-oriented design: it shows the *types* in a system — classes, interfaces, enumerations — their attributes and operations, and the structural relationships between them (inheritance, composition, aggregation, dependency). It is **[UML](https://www.omg.org/spec/UML/2.5.1/) 2.5.1 §9–§11** and the single most-used UML diagram in software documentation. Schematex implements the UML 2.5.1 visual subset with a single-word `umlclass` keyword and a text DSL built for LLM generation — PlantUML-flavoured, additionally accepting Mermaid `classDiagram` glyph aliases for one-line migration. Standard-correct adornments (a hollow triangle points to the more general classifier; a filled diamond marks the composite end), a **generalization-driven layered layout** (interfaces float to the top, leaf classes sink to the bottom), and zero-dependency embeddable SVG. Distinct from **[erd](/docs/erd)** (models *data* rows, not types) and **[c4](/docs/flowchart)** (architecture, not code-level types). ```schematex umlclass title: "Shapes" «interface» Shape { + area() : double } abstract class AbstractShape { # name : String + area() : double {abstract} } class Circle { + radius : double + area() : double } class Square { + side : double + area() : double } Shape <|.. AbstractShape AbstractShape <|-- Circle AbstractShape <|-- Square ``` *** ## 1. Your first diagram Every document starts with the `umlclass` keyword (the Mermaid `classDiagram` header is also accepted), then declarations and relationships: ``` umlclass class Account { + id : String - balance : Money + deposit(amount : Money) : void } class Customer { + name : String } Customer "1" o-- "*" Account : owns ``` A `class X { … }` block declares a classifier with a name compartment, an attributes compartment, and an operations compartment. Members on their own lines inside the braces; you can also write the body on a single line (`class Account { + id : String + deposit() }`). The header accepts: * `title: "…"` — a heading drawn above the diagram. * `direction: tb | bt | lr | rl` — rank direction, default `tb` (parents on top). * `theme: …` — a theme override. *** ## 2. Classifiers ``` class Order «interface» Repository «enumeration» Status abstract class Shape datatype Money primitive int ``` The five classifier kinds are `class`, `interface`, `enum` (alias `enumeration`), `datatype`, and `primitive`. A `«stereotype»` (or ASCII `<<stereotype>>`) renders above the name; `abstract class` (or the `{abstract}` annotation) renders the name in italics. Use `as` to give a display name that differs from the reference id: `class "Order Service" as OrderSvc`. *** ## 3. Members — attributes & operations ``` class Account { + id : String - balance : Money = 0 / available : Money # owner : Customer ~ region : String + count : int {static} + deposit(amount : Money) : void + transfer(to : Account, amount : Money) : boolean {query} } ``` * **Visibility** glyphs: `+` public, `-` private, `#` protected, `~` package. * `: Type` gives the attribute type or operation return type; `= value` a default; `[0..*]` a multiplicity; `/` a derived attribute. * `{…}` annotations: `{static}` (renders underlined), `{abstract}` (italic operation), `{readOnly}`, `{query}`, `{ordered}`, … * `name : Type`, `name: Type`, and Java-order `Type name` are all accepted and normalised to `name : Type`. **Enum literals** are bare names inside an `enum` body: ``` «enumeration» Status { ACTIVE SUSPENDED CLOSED } ``` *** ## 4. Relationships ``` Vehicle <|-- Car generalization (hollow triangle → parent) Shape <|.. Circle realization (dashed + hollow triangle → interface) Order *-- LineItem composition (filled diamond at the whole) Customer o-- Address aggregation (hollow diamond at the whole) Service --> Repository directed association (open arrow → target) Service ..> Logger dependency (dashed + open arrow → supplier) A -- B plain association (no head) ``` Reversed forms are accepted and normalised (`Car --|> Vehicle` ≡ `Vehicle <|-- Car`). Whitespace around a connector is optional. When two or more children share one parent via generalization/realization, the heads are **tree-merged**: one trunk, one shared triangle, per-child legs. *** ## 5. Labels & multiplicity ``` Customer "1" o-- "0..*" Order : places ``` A trailing `: label` names the association (drawn at the line midpoint). Quoted ends are multiplicities or role names: `"1"`, `"0..1"`, `"*"`, `"1..*"`. The source end is the left of the connector, the target end the right (after reversed-form normalisation). *** ## 6. Namespaces / packages Group classifiers into a labelled containment frame: ``` umlclass namespace Platform { namespace Auth { class UserService { + login() } } namespace Data { class Repository { + find() } } } class Gateway { + route() } Gateway --> UserService : delegates Gateway --> Repository : delegates ``` * Blocks **nest** syntactically. Dot-notation auto-creates parents: `namespace Company.Engineering.Backend { … }` creates `Company` and `Company.Engineering` too. * An explicit label: `namespace plat["Platform Layer"] { … }`. * Each package renders as a frame = the union of its members (and nested sub-frames) + padding + a top label. Namespace bodies must use newlines (one declaration per line). *** ## 7. Mermaid-compatibility forms For one-line migration from Mermaid `classDiagram`: ``` classDiagram class Repository~T~ { + findAll() List~T~ + cache : Map~String,List~int~~ + count$ + flush()* } Repository : <<service>> Repository : + save(e : T) T ``` * **Tilde-generics** `List~T~` → `List<T>` (nesting supported: `Map~String,List~int~~` → `Map<String,List<int>>`); also on class names (`class Box~T~`). * **Single-line member**: `ClassName : +member` appends a member; `ClassName : <<interface>>` sets the kind/stereotype. * **Member classifiers**: trailing `*` = abstract (italic), trailing `$` = static (underlined). * **Space-return-type**: `getId() String` needs no colon. A lone leading `~` is still the package-visibility glyph; tilde-generics only convert balanced `~…~` pairs inside a type, so the two never collide. *** ## 8. Theming & accessibility All strokes and fills come from theme tokens (no inline styles); CSS classes are prefixed `sx-umlclass-*`. Visibility renders as text glyphs (`+ - # ~`), never coloured icons — standard-faithful and monochrome-safe. Every diagram carries `<title>`/`<desc>` and `data-*` attributes (`data-id`, `data-kind`, `data-from`/`data-to`/`data-kind` on relationships, `data-package-id` on frames) for interactivity. ## Interactive editing The title, classifier display aliases, member names, and member types expose independent exact source fields. Classifiers move only across relationship ranks so presentation changes cannot silently alter UML semantics. --- # UML Use Case Diagram Canonical URL: https://schematex.js.org/docs/usecase Markdown URL: https://schematex.js.org/docs/usecase.md ## About use case diagrams A **use case diagram** answers one question every requirements engagement starts with: *what does this system do, and for whom?* Introduced by Ivar Jacobson in 1992 and folded into **[UML](https://www.omg.org/spec/UML/2.5.1/) 2.5.1 §18**, it's the most-taught UML diagram and the lingua franca between product, engineering, and QA. The notation is small — actor stick figures, use-case ellipses, a system boundary, and four line types — but it pins down system scope better than any boxes-and-arrows flowchart. Schematex implements the UML 2.5.1 visual subset with a single-word `usecase` keyword and a text DSL built for LLM generation — the PlantUML developer experience without the JVM and without the bracket gymnastics. Distinct from **[state](/docs/state)** (intra-object behavior, not system scope), **[flowchart](/docs/flowchart)** (no actor / subject / include-extend semantics), and **[bpmn](/docs/bpmn)** (how a process executes, not what a system offers). ```schematex usecase title: "ATM" system: "ATM System" actor: Customer actor: Bank (external) usecase: "Withdraw Cash" as Withdraw usecase: "Deposit Funds" as Deposit usecase: "Check Balance" as Check Customer -- Withdraw Customer -- Deposit Customer -- Check Withdraw -- Bank Deposit -- Bank Check -- Bank ``` *** ## 1. Your first diagram Every document starts with the `usecase` keyword, then a header, then declarations and relationships: ``` usecase system: "Library" actor: Member usecase: "Borrow Book" as Borrow Member -- Borrow ``` `actor:` declares an actor (a stick figure), `usecase:` declares a use case (an ellipse), and `Member -- Borrow` draws a plain association between them. `system:` wraps the use cases in a labelled **subject** rectangle. The primary actor is placed on the left; supporting actors on the right. The header accepts: * `title: "…"` — a heading drawn above the diagram. * `system: "…"` — the subject (system boundary) name. Omit it to let the use cases float free (Schematex warns if you omit it with ≥3 use cases). * `direction: LR | TB` — default `LR` (actors flank the subject horizontally). * `generalization: tree | individual` — whether to merge sibling generalization arrows (default `tree`). *** ## 2. Actors ``` actor: Customer actor: "Payment Gateway" as PG (external) actor: "Warehouse Staff" as WH actor: Admin (left) ``` * A bare or `"quoted"` name becomes the label; `as ID` gives it a short identifier for relationships. * `(external)` (or `(system)`) renders the actor as a **rectangle with an `«actor»` stereotype** instead of a stick figure — use it for other software systems (payment gateways, third-party APIs). * `(business)` adds the Bittner & Spence diagonal slash across the stick figure. * `(left)` / `(right)` pins the actor to a side. By default the first actor goes left and the rest go right. Custom stereotypes go in guillemets after the declaration: `actor: "Audit Service" as Audit (external) «system»`. The parser also accepts ASCII `<<system>>` and normalises it to `«system»`. *** ## 3. Use cases ``` usecase: "Checkout" as Checkout { extension point: payment failed extension point: stock depleted } ``` A use case is an ellipse sized to fit its text. The optional `{ … }` block lists **extension points** in a compartment below the name, separated by a divider line. Stereotypes work here too: `usecase: "Validate Card" as ValidateCard «secured»`. *** ## 4. Relationships Four line types connect actors and use cases: | DSL | Meaning | Rendering | | ----------- | ------------------------------ | ------------------------------------------------------- | | `A -- B` | association | solid line, no arrow | | `A --> B` | directed association | solid line, open arrow at B | | `A ..> B` | `A` **includes** `B` | dashed line, open arrow → B, `«include»` pill | | `A <.. B` | `A` **extends** `B` | dashed line, open arrow → B (the base), `«extend»` pill | | `A --\|> B` | `A` is a specialisation of `B` | solid line, hollow triangle → parent B | ``` Customer -- Checkout Checkout ..> Pay : «include» Pay ..> ValidateCard : «include» Cancel <.. Checkout : «extend» [payment failed] (extension point: payment failed) ``` * **`«include»`** points *toward the included use case* — the reusable behavior `A` always runs. Source includes target. * **`«extend»`** points *toward the base* — `A` is the optional behavior, `B` is the base it extends. You can attach a `[condition]` and reference one of the base's `(extension point: …)` entries. (The arrowhead is always drawn toward the base regardless of how you order the endpoints.) * An `«extend»` line is drawn in the theme **accent color** so the rarer, more surprising relationship stands out. The parser rejects the high-confidence mistakes humans and LLMs both make, with the offending line number: association between two actors or two use cases, `include`/`extend` touching an actor, generalization across metaclasses (actor → use case), reused identifiers, and extension-point references that don't exist on the base. *** ## 5. Generalization `--|>` works between two actors **or** between two use cases (never across the two — that's a hard error): ``` actor: User as U actor: "Premium User" as PU PU --|> U usecase: "Pay by Card" as PayCard usecase: "Pay by PayPal" as PayPaypal usecase: "Pay" as Pay PayCard --|> Pay PayPaypal --|> Pay ``` The arrow carries a **hollow triangle** pointing at the parent. When three or more siblings share one parent, the arrows merge into a single shared head (UML 2.5 Figure 18.5 convention); set `generalization: individual` in the header to keep them separate. Actor hierarchies route as a clean bus on the outer edge of the actor stack. *** ## 6. Multiplicity Quote a multiplicity string immediately beside the endpoint it belongs to: ``` Customer "1" -- "*" Checkout Cashier "1..*" -- "1" Register ``` *** ## 7. PlantUML-style inline form Coming from PlantUML? The inline declaration form works and mixes freely with the declarative form: ``` usecase :Customer: as C (Browse Catalog) as Browse (Add to Cart) as AddCart C -- Browse C -- AddCart Browse ..> AddCart : «include» ``` `:Name:` declares an actor, `(Name)` declares a use case, and `as ID` aliases either. Schematex is *inspired by* PlantUML, not a 1:1 transpiler — multiplicity and relationship syntax differ slightly. *** ## 8. Grammar (EBNF) ```text document = "usecase" NEWLINE header_prop* statement* header_prop = ("title:" | "system:") quoted_string | "direction:" ("LR" | "TB") | "generalization:" ("tree" | "individual") statement = actor_decl | usecase_decl | plantuml_inline | relation | note actor_decl = "actor" ":" name ("as" IDENT)? actor_kind? stereotype? actor_kind = "(" ("external" | "system" | "business" | "left" | "right") ")" usecase_decl = "usecase" ":" quoted ("as" IDENT)? stereotype? extpoints? extpoints = "{" ("extension point:" TEXT)+ "}" plantuml_inline = ":" name ":" ("as" IDENT)? ; actor | "(" name ")" ("as" IDENT)? ; use case relation = endpoint relop endpoint label_clause? endpoint = (IDENT | quoted) multiplicity? | multiplicity? (IDENT | quoted) relop = "--" | "-->" | "..>" | "<.." | "--|>" label_clause = ":" stereotype? condition? extpoint_ref? stereotype = "«" TEXT "»" | "<<" TEXT ">>" condition = "[" TEXT "]" extpoint_ref = "(extension point:" TEXT ")" multiplicity = quoted_string ; "1", "*", "0..1", "1..*" ``` *** ## 9. Standard compliance Schematex follows the OMG **UML 2.5.1 §18 (UseCases)** visual subset and the Bittner & Spence style guide: guillemets (never ASCII `<<>>`) in the rendered SVG, `«include»` toward the included use case, `«extend»` toward the base, hollow-triangle generalization heads, and the `«actor»` stereotype on external-system rectangles. Textual use-case descriptions (Cockburn fully-dressed form), scenario tables, and XMI export are out of scope for a *diagram* renderer. See `docs/reference/29-USECASE-STANDARD.md` for the full specification. *** ## Related examples Ready-to-use scenarios from the examples gallery: [Browse related examples](https://schematex.js.org/examples) --- # Venn / Euler diagram Canonical URL: https://schematex.js.org/docs/venn Markdown URL: https://schematex.js.org/docs/venn.md ## About Venn and Euler diagrams A **Venn diagram** uses overlapping circles to show every possible logical relationship among a collection of sets — whether or not each intersection actually contains any elements. A **Euler diagram** is the more general form: circles are only drawn where relationships actually exist, so a set fully contained in another is shown as a nested circle, and two disjoint sets do not overlap at all. John Venn introduced the fixed-overlap form in 1880; Leonhard Euler described the general set-containment form in the 1760s. Educators use them to teach set theory; data analysts reach for them to show audience overlap between segments (email list ∩ mobile users ∩ paid subscribers); researchers in biology, linguistics, and medicine use Euler diagrams to map taxonomic or conceptual hierarchies. Schematex supports all four common usage patterns in one DSL — declarative counts, element enumeration, region labels, and Euler subset/disjoint/overlap relations — and can mix them in a single diagram. See the [Wikipedia article on Venn diagrams](https://en.wikipedia.org/wiki/Venn_diagram) and [Euler diagrams](https://en.wikipedia.org/wiki/Euler_diagram) for background. ```schematex venn "Customer Segments — Q3 2025" set email "Email subscribers" [color: "#1E88E5"] set paid "Paid users" [color: "#E53935"] set mobile "Mobile app users" [color: "#43A047"] email & paid : 1840 email & mobile : 920 paid & mobile : 2100 email & paid & mobile : 650 email only : 12400 paid only : 3200 mobile only : 8700 ``` *** ## 1. Your first Venn diagram The smallest useful diagram: two sets, one overlap, two exclusive regions. ```schematex venn "Support channels" set A "Email support" [color: "#1E88E5"] set B "Live chat" [color: "#E53935"] A & B : 320 A only : 1450 B only : 890 ``` Four rules cover 80% of usage: 1. Start with `venn`, optionally followed by a quoted title. 2. Declare each **set** with `set ID "Label"` — the id is used internally, the label appears in the diagram. 3. Assign values to **regions** using `A & B : value` for intersections and `A only : value` for exclusive regions. 4. Configure appearance with `config:` lines; the diagram mode (`venn` vs `euler`) can be set explicitly or left as `auto`. > Comments must start with `#` on their own line. *** ## 2. Sets A set declaration creates one circle in the diagram. ``` set ID "Label" [color: "#hex"] ``` | Part | Required | Notes | | ----------------- | -------- | ------------------------------------- | | `ID` | Yes | Must match `[A-Za-z][A-Za-z0-9_-]*` | | `"Label"` | Yes | Quoted string displayed on the circle | | `[color: "#hex"]` | No | Override fill color for this set | Sets must be declared before they are referenced in region or relation lines. ```schematex venn "Programming paradigms" set oop "Object-Oriented" [color: "#1E88E5"] set fp "Functional" [color: "#E53935"] set logic "Logic" [color: "#43A047"] oop & fp : 180 oop & logic : 45 fp & logic : 90 oop & fp & logic : 12 oop only : 620 fp only : 340 logic only : 95 ``` *** ## 3. Regions A region assigns a value to an intersection or exclusive area. The four DSL modes can be mixed in one diagram. ### 3.1 Declarative mode — counts and percentages Assign a number or percentage to a named region. The region key is either an `&`-separated list of set ids (for intersections) or `ID only` (for the part of that set not covered by any other set). ``` A & B : 320 # integer count A & B & C : 45 # three-way intersection A only : 1450 # A minus all other sets A & B : 18.5% # percentage value ``` ```schematex venn "Market research" set aware "Awareness" [color: "#7B1FA2"] set consider "Consideration" [color: "#0288D1"] set convert "Conversion" [color: "#388E3C"] aware & consider : 3400 consider & convert : 890 aware & convert : 210 aware & consider & convert : 150 aware only : 18200 consider only : 2100 convert only : 540 ``` ### 3.2 Region labels (text) Use the `region` keyword prefix and assign a quoted string instead of a number. The string is rendered inside the region. ``` region A & B : "Nurture" region B & C : "Convert" region A & B & C : "Loyal customer" ``` ```schematex venn "Go-to-market funnel" set A "Awareness" [color: "#7B1FA2"] set B "Consideration" [color: "#0288D1"] set C "Purchase" [color: "#388E3C"] region A & B : "Nurture" region B & C : "Convert" region A only : "Cold audience" region A & B & C : "Loyal" region C only : "Direct buyers" ``` ### 3.3 Enumeration mode — element lists List the actual elements of each set. Schematex computes all intersections automatically. ``` ID = { element1, element2, element3 } ``` Elements are comma-separated bare words or quoted strings. Enumeration sets do not need an explicit `set` declaration — the declaration is implied. ```schematex venn "Full-stack team skills" Frontend = { React, TypeScript, CSS, Jest, Webpack } Backend = { TypeScript, Node.js, PostgreSQL, Jest, Redis } DevOps = { Docker, Kubernetes, PostgreSQL, Terraform, Redis } ``` *** ## 4. Euler relations Euler relations express structural containment or separation — that one set is a subset of another, that two sets are completely disjoint, or that they merely overlap. They must reference set ids already declared with `set`. ``` from subset to # from is fully inside to (also: "in") from in to # alias for subset from disjoint to # from and to do not overlap from overlap to # from and to partially overlap (explicit — the default for unrelated sets) ``` ```schematex venn "Biology taxonomy" set animals "Animals" set vertebrates "Vertebrates" set mammals "Mammals" set birds "Birds" set fish "Fish" vertebrates subset animals mammals subset vertebrates birds subset vertebrates fish subset vertebrates mammals disjoint birds mammals disjoint fish birds disjoint fish ``` | Keyword | Alias | Meaning | | ---------- | ----- | -------------------------------------------------------- | | `subset` | `in` | `from` is fully contained within `to` | | `disjoint` | — | `from` and `to` do not intersect | | `overlap` | — | `from` and `to` intersect but neither contains the other | *** ## 5. Configuration `config:` lines tune diagram behavior. Each goes on its own line. | Config key | Values | Default | Effect | | -------------- | -------------------------------- | ---------- | ------------------------------------------------------------------------------------------------------------- | | `diagram` | `venn`, `euler`, `auto` | `auto` | Force Venn (all circles fixed) or Euler (subset nesting). `auto` infers from the presence of Euler relations. | | `proportional` | `true`, `false` | `false` | Scale circle area proportional to region count values | | `showCounts` | `true`, `false` | `auto` | Always / never show count labels. `auto` shows them when counts are provided. | | `showPercent` | `true`, `false` | `false` | Show each region value as a percentage of the grand total | | `palette` | `default`, `brand`, `monochrome` | `default` | Color palette for sets (overridden by per-set `color:`) | | `blendMode` | `multiply`, `screen`, `none` | `multiply` | How overlapping fill colors blend | Config can also be written inline on the header line: `venn "Title" [proportional: true, showPercent: true]`. ``` venn "Segment overlap" config: proportional = true config: showPercent = true config: blendMode = screen ``` **Layout selection:** * `layout venn` — alternative form of `config: diagram = venn` (parses identically). * `layout euler` — alternative form of `config: diagram = euler`. * `layout auto` — alternative form of `config: diagram = auto`. *** ## 6. Labels & comments * **Title:** `venn "My diagram"` — first line only. * **Set label:** `set A "Email subscribers"` — quoted string on the `set` line. * **Region value:** integer, percentage, quoted string, or element list assigned after the `:`. * **Comments:** `#` at the start of a line (after leading whitespace). *** ## 7. Reserved words & escaping **Reserved at line start:** `venn` (header), `set`, `config:`, `layout`, `region`. **Reserved operators in region keys:** `&` (intersection), `only` (exclusive region). **Euler relation keywords:** `subset`, `in`, `disjoint`, `overlap` — cannot be used as set ids. **ID rules:** must match `[A-Za-z][A-Za-z0-9_-]*`. Labels with spaces go in the quoted `"Label"` field. *** ## 8. Common mistakes | You wrote | Parser says | Fix | | ------------------------------------------------------- | ------------------------------------------------------------- | --------------------------------------------------------------- | | `A & B : 320` before `set A …` and `set B …` | `VennParseError: unknown set id "A" in region key` | Declare sets with `set` before referencing them in region lines | | `dogs subset mammals` before `set dogs …` | `VennParseError: unknown set "dogs" in relation` | Declare sets first, then write Euler relations | | `set A Email subscribers` (unquoted label with space) | Parser error — label is expected to be a quoted string | Quote it: `set A "Email subscribers"` | | `A & B = 320` (equals instead of colon) | Line doesn't match region pattern; parse error | Use colon: `A & B : 320` | | `Frontend = { React TypeScript }` (no commas) | `React TypeScript` treated as one element | Comma-separate: `Frontend = { React, TypeScript }` | | `config: mode = venn` | `mode` is not a recognized key (key is `diagram`) | Use `config: diagram = venn` | | Mixing enumeration sets with explicit `A & B :` regions | Enumeration auto-derive only runs when `regions.length === 0` | Use one style per diagram or add all regions explicitly | *** ## 9. Grammar (EBNF) ```text document = header (blank | comment | config | layout-stmt | set-decl | enum-decl | euler-rel | region)* header = "venn" ( ":"? WS quoted-string )? ( WS "[" config-props "]" )? NEWLINE quoted-string = '"' any-char-but-quote* '"' config = "config" WS ":" WS config-key WS "=" WS config-value NEWLINE config-key = "diagram" | "proportional" | "palette" | "blendMode" | "showCounts" | "showPercent" layout-stmt = "layout" WS ( "venn" | "euler" | "auto" ) NEWLINE set-decl = "set" WS id WS quoted-string ( WS "[" set-props "]" )? NEWLINE set-props = "color:" quoted-hex | "fill:" quoted-hex enum-decl = id WS "=" WS "{" element-list "}" NEWLINE element-list = element ( "," element )* element = quoted-string | bare-word euler-rel = id WS euler-op WS id NEWLINE euler-op = "subset" | "in" | "disjoint" | "overlap" region = "region"? WS region-key WS ":" WS region-value NEWLINE region-key = id WS "only" | id ( WS "&" WS id )+ region-value = integer | percent | quoted-string | "[" element-list "]" percent = number "%" id = [A-Za-z] [A-Za-z0-9_-]* comment = "#" any NEWLINE ``` Authoritative source: `src/diagrams/venn/parser.ts`. If this diverges from the parser, the parser wins — please open an issue. *** ## 10. Roadmap **Planned — not yet parseable.** Do not use these in generated DSL today; the parser will reject or ignore them. * **Three-way and N-way Euler nesting** — the renderer currently supports up to 3 sets in Venn mode; larger Euler diagrams with complex containment are not yet fully laid out. * **`fill:` color alias** — the `fill:` property on `set` is accepted by the parser (as an alias for `color:`) but the renderer currently only uses `color:`. * **`label:` on regions** — `region A & B [label: "Core"]` syntax for separate label vs. value. * **Area-proportional Euler** — `proportional: true` currently only affects Venn mode; Euler containment layout ignores the flag. * **`and` keyword** — natural-language `A and B : 120` as alias for `A & B : 120`. Track in the GitHub issues if you need any of these sooner. *** ## Related examples Ready-to-use scenarios from the examples gallery: [Browse related examples](https://schematex.js.org/examples) --- # Welding symbol diagram Canonical URL: https://schematex.js.org/docs/welding Markdown URL: https://schematex.js.org/docs/welding.md ## About welding symbols A **welding symbol** tells a fabricator exactly how to weld a joint: what kind of weld, on which side, how big, how long, and how to finish it. The notation is a **reference-line skeleton** — a horizontal line, a leader arrow pointing at the joint, and a small weld-symbol glyph snapped above or below the line — standardised by **AWS A2.4** (US) and **ISO 2553** (international). Schematex draws the skeleton correct-by-construction: you describe the weld, the engine places every glyph, dimension, and supplementary symbol. This is a *fixed-skeleton* notation, not a graph — there is no layout to solve, so it renders deterministically every time. ```schematex welding "Bracket welds" joint "bracket to plate" { arrow: fillet size=8 len=50 pitch=150 other: fillet size=6 around tail: "GMAW" } ``` *** ## 1. Your first weld The smallest useful callout: a header, one joint, one arrow-side weld. ``` welding "Bracket" joint "bracket to plate" { arrow: fillet size=8 } ``` Three rules cover most usage: 1. Start with `welding`, optionally `standard: aws | iso-a | iso-b` (default `aws`) and a quoted title. 2. Each joint is a `joint "label" { … }` block. Put a weld on `arrow:` (arrow side) and/or `other:` (other side). 3. A weld spec is a type followed by `key=value` dimensions — `fillet size=8`, `vgroove angle=60 root=3`. *** ## 2. Sides — arrow, other, both A joint is welded on the arrow side, the other side, or both. ``` joint "double fillet" { both: fillet size=6 # same weld on both sides } joint "asymmetric" { arrow: fillet size=8 # arrow side only other: vgroove angle=60 # different weld on the other side } ``` * **AWS** (default): the arrow-side glyph draws **below** the reference line, the other-side glyph **above**. * **ISO-A** (`standard: iso-a`): a **dashed companion line** appears; the arrow-side weld attaches to the solid line, the other-side weld to the dashed line. A symmetric `both:` weld suppresses the dashed line. *** ## 3. Weld types | Type | Glyph | Type | Glyph | | ----------------------- | ------------------ | ------------------ | ------------------ | | `fillet` | triangle | `plug` / `slot` | rectangle | | `square` | parallel verticals | `spot` | circle on the line | | `vgroove` | V | `seam` | circle + line | | `bevel` | half-V | `back` / `backing` | semicircle | | `ugroove` | U | `surfacing` | build-up bumps | | `jgroove` | half-U | `edge` | tall verticals | | `flarev` / `flarebevel` | curved groove | | | Aliases: `v`→`vgroove`, `u`→`ugroove`, `j`→`jgroove`, `flare-v`→`flarev`. *** ## 4. Dimensions Dimensions read along the reference line in fixed slots. ``` joint "groove" { arrow: vgroove angle=60 root=3 throat=12 len=50 pitch=150 } ``` | Key | Slot | Meaning | | ---------- | ----------------------- | ------------------------------------------------- | | `size=` | left of symbol | fillet leg / groove depth / plug diameter | | `throat=` | left, in parentheses | effective throat `(E)` | | `len=` | right of symbol | weld length | | `pitch=` | right (with `len`) | intermittent centre-to-centre pitch → `len-pitch` | | `count=` | right (ISO) | number of increments → `count×len (pitch)` | | `angle=` | at the opening | groove included angle (groove types only) | | `root=` | between symbol and line | root opening / gap | | `contour=` | above the symbol | `flush` (bar), `convex`, `concave` (arc) | | `finish=` | above the contour | finish method letter `G`/`M`/`C`/`R`/`H`/`U` | *** ## 5. Supplementary symbols ``` joint "post base" { arrow: fillet size=10 around # weld-all-around — open circle at the junction field # field / site weld — filled flag pointing to the tail tail: "GTAW; WPS-12" # process / spec / NDE method } ``` ```schematex welding joint "butt weld" { arrow: vgroove angle=60 root=3 throat=12 other: backing tail: "SMAW; E7018" } ``` *** ## 6. Standard compliance Schematex welding diagrams implement **AWS A2.4:2020** (single reference line) and **ISO 2553:2019** Systems A (dual solid+dashed line) and B. * ✅ Full glyph catalog — fillet, square / V / bevel / U / J / flare-V / flare-bevel grooves, plug, slot, spot, seam, back, backing, surfacing, edge * ✅ Dimension slots — size, throat `(E)`, length, length-pitch, count×length, groove angle, root opening * ✅ Supplementary — weld-all-around circle, field-weld flag, tail process/spec/NDE, contour + finish * ✅ Arrow / other / both sides with per-standard convention * ✅ AI-readable validation of illegal type/side/dimension combinations * ⏳ Combined weld + NDE symbols (NDE is tail text today) * ⏳ Arrow-break for the prepared member; staggered intermittent offset; melt-through / consumable-insert glyphs References: * American Welding Society (2020). *AWS A2.4: Standard Symbols for Welding, Brazing, and Nondestructive Examination.* * ISO 2553:2019. *Welding and allied processes — Symbolic representation on drawings — Welded joints.* *** ## 7. Grammar (EBNF) ```text document = "welding" ( "standard:" std )? title? NEWLINE joint* std = "aws" | "iso-a" | "iso-b" joint = "joint" title? "{" directive* "}" directive = ( "arrow:" | "other:" | "both:" ) weldspec | "around" | "field" | "tail:" quoted-string weldspec = type ( WS key "=" value )* type = "fillet"|"square"|"vgroove"|"bevel"|"ugroove"|"jgroove" | "flarev"|"flarebevel"|"plug"|"slot"|"spot"|"seam" | "back"|"backing"|"surfacing"|"edge" key = "size"|"len"|"pitch"|"count"|"angle"|"root"|"throat"|"contour"|"finish" ``` Authoritative source: `src/diagrams/welding/parser.ts`. *** ## Related examples [Browse related examples](https://schematex.js.org/examples)