schematex 0.6.10 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/dist/ai/ai-sdk.cjs +8 -8
  2. package/dist/ai/ai-sdk.d.cts +1 -1
  3. package/dist/ai/ai-sdk.d.ts +1 -1
  4. package/dist/ai/ai-sdk.js +3 -3
  5. package/dist/ai/index.cjs +14 -14
  6. package/dist/ai/index.js +3 -3
  7. package/dist/browser.cjs +9 -9
  8. package/dist/browser.js +3 -3
  9. package/dist/{chunk-DZGA25O5.cjs → chunk-CTMJ3XP2.cjs} +156 -9
  10. package/dist/chunk-CTMJ3XP2.cjs.map +1 -0
  11. package/dist/{chunk-HL5PS6MG.js → chunk-EENA7KNU.js} +324 -27
  12. package/dist/chunk-EENA7KNU.js.map +1 -0
  13. package/dist/{chunk-3YRYBTLG.cjs → chunk-HFATQXFN.cjs} +1401 -148
  14. package/dist/chunk-HFATQXFN.cjs.map +1 -0
  15. package/dist/{chunk-6AWASOFO.js → chunk-IFNNV54X.js} +1400 -147
  16. package/dist/chunk-IFNNV54X.js.map +1 -0
  17. package/dist/{chunk-X4P4HKHP.js → chunk-IFXHIYZE.js} +154 -7
  18. package/dist/chunk-IFXHIYZE.js.map +1 -0
  19. package/dist/{chunk-BL57NQKN.cjs → chunk-JAYJ2G4R.cjs} +324 -27
  20. package/dist/chunk-JAYJ2G4R.cjs.map +1 -0
  21. package/dist/diagrams/phylo/index.cjs +6 -6
  22. package/dist/diagrams/phylo/index.d.cts +21 -0
  23. package/dist/diagrams/phylo/index.d.ts +21 -0
  24. package/dist/diagrams/phylo/index.js +1 -1
  25. package/dist/index.cjs +25 -25
  26. package/dist/index.js +3 -3
  27. package/dist/react.cjs +3 -3
  28. package/dist/react.js +2 -2
  29. package/package.json +1 -1
  30. package/dist/chunk-3YRYBTLG.cjs.map +0 -1
  31. package/dist/chunk-6AWASOFO.js.map +0 -1
  32. package/dist/chunk-BL57NQKN.cjs.map +0 -1
  33. package/dist/chunk-DZGA25O5.cjs.map +0 -1
  34. package/dist/chunk-HL5PS6MG.js.map +0 -1
  35. package/dist/chunk-X4P4HKHP.js.map +0 -1
@@ -1,4 +1,4 @@
1
- import { parseResult, renderResult } from './chunk-6AWASOFO.js';
1
+ import { parseResult, renderResult } from './chunk-IFNNV54X.js';
2
2
 
3
3
  // src/ai/registry.ts
4
4
  var DIAGRAM_REGISTRY = [
@@ -743,6 +743,44 @@ If the LED doesn't light up, three things to check, in order: LED polarity (the
743
743
  "dsl": 'circuit "Pull-up + push button" netlist\nV1 vcc 0 5V\nR1 vcc sig 10k dir=down\nSW1 sig 0 type=switch\nC1 sig 0 100n',
744
744
  "notes": "## Scenario\n\nAn embedded engineer documents a classic active-low input: a pull-up resistor\nholds the signal high, a push button pulls it to ground, and a small capacitor\ndebounces it. The connectivity is written as a SPICE-style netlist \u2014 the engine\nplaces everything from the node names \u2014 and one optional hint refines the look.\n\n## Annotation key\n\n- **netlist line** \u2014 `id node-A node-B value`; components that share a node name\n are wired together. `0` is ground.\n- **`dir=down`** \u2014 the optional orientation hint. `R1` connects `vcc` to `sig`;\n by default the engine would lay it horizontally, but a pull-up reads best drawn\n vertically from the supply rail down to the signal node, so `dir=down` rotates\n just that symbol. Connectivity is unchanged \u2014 `dir=` only rotates the glyph.\n- **`type=switch`** \u2014 the `SW1` id prefix is ambiguous, so the component type is\n made explicit.\n\n## How to read\n\n`R1` ties `sig` up to `vcc` (drawn vertically thanks to `dir=down`). `SW1` and the\ndebounce cap `C1` both go from `sig` to ground, so the engine recognises them as\nshunt legs and drops them beneath the node. Pressing the button shorts `sig` to\nground, pulling the input low."
745
745
  },
746
+ {
747
+ "slug": "decisiontree-influence-market-entry",
748
+ "diagram": "decisiontree",
749
+ "title": "Market entry (influence diagram)",
750
+ "description": "A go/no-go market-entry decision as a compact influence diagram \u2014 demand observed before entering, a competitor response, and the profit objective \u2014 using the inline mode directive form.",
751
+ "standard": "Howard & Matheson (1981) influence diagram",
752
+ "tags": [
753
+ "decisiontree",
754
+ "influence",
755
+ "decision-analysis",
756
+ "dag",
757
+ "strategy",
758
+ "go-no-go"
759
+ ],
760
+ "complexity": 2,
761
+ "featured": false,
762
+ "dsl": 'decisiontree "Market Entry"\n mode: influence\n decision Enter "Enter market?"\n chance Demand "Market demand"\n chance Competition "Competitor response"\n value V "Profit" utility=120\n Demand -> Enter\n Demand -> V\n Competition -> V\n Enter -> V',
763
+ "notes": "## What this shows\n\nA classic go/no-go framed as an **influence diagram**, written with the inline `mode: influence` directive (the alternative to the `decisiontree:influence` header). The decision-maker controls one **rectangle** \u2014 whether to enter \u2014 against two uncertain **ovals**, market demand and the competitor's response, all feeding a single profit **octagon**.\n\nTwo arcs land on the decision and the value node from demand. `Demand -> Enter` is a **dashed informational arc**: demand is read before committing to entry, so the choice can react to it. `Demand -> V` and `Competition -> V` are functional arcs \u2014 both directly shape profit \u2014 while `Enter -> V` ties the chosen action into the payoff. The graph stays acyclic and carries exactly one value node, the two structural rules the engine enforces; it captures the shape of the bet without unrolling every demand-by-competitor branch into a tree."
764
+ },
765
+ {
766
+ "slug": "decisiontree-influence-oil-wildcatter",
767
+ "diagram": "decisiontree",
768
+ "title": "Oil wildcatter (influence diagram)",
769
+ "description": "The canonical decision-analysis teaching problem as a compact influence diagram \u2014 a drill decision informed by a seismic test, the uncertain oil state, and the profit objective, wired as a clean acyclic graph instead of an unrolled tree.",
770
+ "standard": "Howard & Matheson (1981) influence diagram",
771
+ "tags": [
772
+ "decisiontree",
773
+ "influence",
774
+ "decision-analysis",
775
+ "dag",
776
+ "oil-and-gas",
777
+ "howard-matheson"
778
+ ],
779
+ "complexity": 2,
780
+ "featured": false,
781
+ "dsl": 'decisiontree:influence "Oil Wildcatter"\n decision Drill "Drill?"\n chance Oil "Oil present"\n chance Seismic "Seismic test"\n value Profit "Net profit" utility=42\n Seismic -> Oil\n Seismic -> Drill\n Oil -> Profit\n Drill -> Profit',
782
+ "notes": "## What this shows\n\nThe textbook oil-wildcatter problem drawn the way a decision analyst frames it first \u2014 as an **influence diagram** rather than a fully unrolled decision tree. One **rectangle** for the drill decision, two **ovals** for the uncertainties (whether oil is present, what the seismic test reads), and one **octagon** for the profit objective. The whole problem is four nodes and four arcs, no matter how many states each variable could take.\n\nThe arcs carry the structure. `Seismic -> Drill` is a **dashed informational arc**: the seismic result is observed *before* the drill choice is made \u2014 that's the entire reason the test is worth running. `Seismic -> Oil` is a relevance arc (the reading is conditioned on the true oil state), and `Oil -> Profit` plus `Drill -> Profit` are functional arcs feeding the payoff. This mode is deliberately structural \u2014 it shows what informs what, without solving for expected value; reach for decision-analysis mode when you want the numbers folded back."
783
+ },
746
784
  {
747
785
  "slug": "decisiontree-investment-analysis",
748
786
  "diagram": "decisiontree",
@@ -1514,6 +1552,78 @@ If the LED doesn't light up, three things to check, in order: LED polarity (the
1514
1552
  "dsl": 'matrix johari "Self vs. Team \u2014 Q2 Reflection"\nstyle: table\nQ2: "Strong technical instincts"\nQ2: "Direct in code review"\nQ2: "Patient with juniors"\nQ1: "Interrupts in meetings"\nQ1: "Hard to read when stressed"\nQ3: "Imposter syndrome about leadership"\nQ3: "Anxiety about cross-team politics"\nQ4: "Capacity for difficult conversations under pressure"',
1515
1553
  "notes": "## Scenario\n\nA newly-promoted engineering manager runs a Johari exercise with her team during a 1:1 retro. She populates the **Open** cell (things both she and the team see); the team adds to **Blind** (things they see that she doesn't); she fills **Hidden** privately; **Unknown** is the open hypothesis space \u2014 capabilities and limitations that haven't surfaced yet.\n\nThe table form is the canonical Johari output. Coaches print it on a single page and walk through it with the coachee \u2014 a scatter plot of dots would defeat the entire purpose.\n\n## Annotation key\n\n- `matrix johari` \u2014 preset axes (Known to Self \xD7 Known to Others) with the four window panes\n- `style: table` \u2014 flips off axes/grid, places each pane title as a cell header, lists items as bullets\n- `Q1` = Blind (top-right: not known to self, known to others)\n- `Q2` = Open / Arena (top-left: known to self + others)\n- `Q3` = Hidden / Fa\xE7ade (bottom-left: known to self, not to others)\n- `Q4` = Unknown (bottom-right: not known to either \u2014 the growth hypothesis space)\n\n## How to read\n\nThe classic Johari coaching prompt: **how do you move items from Hidden \u2192 Open** (vulnerability work) **and from Blind \u2192 Open** (feedback-acceptance work)? An overstuffed Hidden pane signals psychological-safety debt; an empty Blind pane usually means the team hasn't been asked."
1516
1554
  },
1555
+ {
1556
+ "slug": "matrix-qfd-coffee-maker",
1557
+ "diagram": "matrix",
1558
+ "title": "Coffee maker House of Quality",
1559
+ "description": "A QFD House of Quality where the engine computes the technical-importance row (45 / 39 / 51) \u2014 the ranked answer to which engineering characteristic moves the most customer value \u2014 plus the diamond-cell roof that flags trade-offs between characteristics.",
1560
+ "standard": "Akao Quality Function Deployment",
1561
+ "tags": [
1562
+ "matrix",
1563
+ "qfd",
1564
+ "house-of-quality",
1565
+ "akao",
1566
+ "voice-of-customer"
1567
+ ],
1568
+ "complexity": 3,
1569
+ "featured": false,
1570
+ "dsl": 'matrix qfd "Coffee maker"\nwhat: "Quiet operation" weight: 5\nwhat: "Brews fast" weight: 3\nwhat: "Energy efficient" weight: 4\nhow: "Fan RPM" dir: down\nhow: "Heater watts" dir: up\nhow: "Insulation" dir: up\nrel (0,0): 9\nrel (0,2): 3\nrel (1,1): 9\nrel (2,1): 3\nrel (2,2): 9\nroof (0,1): --\nroof (1,2): +',
1571
+ "notes": "## What this shows\n\nThe **House of Quality** \u2014 the core matrix of Akao's Quality Function Deployment \u2014 translates what customers want into the engineering characteristics that deliver it. Customer requirements (**WHATs**) are the rows, each with an importance weight; engineering characteristics (**HOWs**) are the columns; the body cells record how strongly each HOW serves each WHAT on the 9 / 3 / 1 strong-medium-weak scale.\n\nThe differentiator is the computed row at the foot of the house: each column's **technical importance** is the sum of `weight \xD7 strength` down that column, here **45 / 39 / 51** \u2014 so Insulation (51) is the highest-leverage characteristic to invest in and Heater watts (39) the lowest. (Add `normalize: true` to read these as 33% / 29% / 38% instead.) Above the columns, the **roof** is a half-matrix of diamond cells recording HOW-to-HOW correlations: `roof (0,1): --` flags that lowering Fan RPM while raising Heater watts is a trade-off, while `roof (1,2): +` flags that Heater watts and Insulation reinforce each other."
1572
+ },
1573
+ {
1574
+ "slug": "matrix-sipoc-order-fulfilment",
1575
+ "diagram": "matrix",
1576
+ "title": "Order fulfilment SIPOC",
1577
+ "description": "The one-page scoping table that opens a Six Sigma DMAIC project \u2014 Suppliers, Inputs, Process, Outputs, Customers in five fixed columns, pinning down exactly where the process starts and ends before anyone improves it.",
1578
+ "standard": "Six Sigma DMAIC",
1579
+ "tags": [
1580
+ "matrix",
1581
+ "sipoc",
1582
+ "six-sigma",
1583
+ "dmaic",
1584
+ "process-scoping"
1585
+ ],
1586
+ "complexity": 2,
1587
+ "featured": false,
1588
+ "dsl": 'matrix sipoc "Order fulfilment"\nsuppliers: "Vendor", "Warehouse"\ninputs: "PO", "Stock levels"\nprocess: "Receive order", "Pick", "Pack", "Ship"\noutputs: "Shipped package", "Invoice"\ncustomers: "End customer", "Finance"',
1589
+ "notes": "## What this shows\n\nA **SIPOC** is the first artifact a Six Sigma team builds in the *Define* phase of DMAIC. It names \u2014 in five columns read left to right \u2014 everyone and everything the process touches: **S**uppliers hand in **I**nputs, the **P**rocess turns them into **O**utputs, and **C**ustomers receive them. Here the order-fulfilment process runs `Receive order \u2192 Pick \u2192 Pack \u2192 Ship`, fed by purchase orders and stock levels from the vendor and warehouse, and producing a shipped package for the end customer and an invoice for finance.\n\nThe point of a SIPOC is boundary-setting before measurement: it forces the team to agree where the process starts, where it ends, and who hands work in and out of it. The five columns always render in canonical S-I-P-O-C order, so the diagram reads correctly even when the blocks are authored out of sequence."
1590
+ },
1591
+ {
1592
+ "slug": "mindmap-driver-readmissions",
1593
+ "diagram": "mindmap",
1594
+ "title": "Driver diagram \u2014 reduce 30-day readmissions",
1595
+ "description": "An IHI driver diagram that traces a quality-improvement aim left to right through its primary drivers and the concrete change ideas meant to move them.",
1596
+ "standard": "Driver Diagram: IHI improvement model",
1597
+ "tags": [
1598
+ "mindmap",
1599
+ "driver",
1600
+ "quality-improvement",
1601
+ "healthcare",
1602
+ "aim-driver-change"
1603
+ ],
1604
+ "complexity": 2,
1605
+ "featured": false,
1606
+ "dsl": "%% style: driver\n# Reduce 30-day readmissions\n## Reliable discharge process\n- Teach-back at bedside\n- Med reconciliation\n## Timely follow-up\n- Appointment within 7 days\n- Post-discharge phone call",
1607
+ "notes": '## What this shows\n\nA **driver diagram** \u2014 the planning artifact from the IHI (Institute for Healthcare Improvement) model for improvement. It reads left to right as a tidy tree: the **aim** ("reduce 30-day readmissions") on the far left, the **primary drivers** that move it ("reliable discharge process", "timely follow-up") in the next column, and the concrete **change ideas** \u2014 teach-back at the bedside, medication reconciliation, an appointment within 7 days \u2014 branching out to the right.\n\nThe value is the line of sight it forces. Every change idea on the right traces back through a driver to the aim, so a team can defend *why* each intervention is on the board and spot which drivers still have no ideas attached. It is the ordinary `#` / `##` / `-` mindmap input, rendered as an aim \u2192 drivers \u2192 change-ideas tree by adding `%% style: driver`.'
1608
+ },
1609
+ {
1610
+ "slug": "mindmap-futureswheel-remote-work",
1611
+ "diagram": "mindmap",
1612
+ "title": "Futures wheel \u2014 remote work becomes default",
1613
+ "description": "A structured-brainstorming futures wheel that ripples a single trend outward into first- and second-order consequences across concentric, color-coded rings.",
1614
+ "standard": "Futures Wheel: Glenn (1972)",
1615
+ "tags": [
1616
+ "mindmap",
1617
+ "futureswheel",
1618
+ "foresight",
1619
+ "consequence-mapping",
1620
+ "scenario-planning"
1621
+ ],
1622
+ "complexity": 2,
1623
+ "featured": false,
1624
+ "dsl": "%% style: futureswheel\n# Remote work becomes default\n## Less commuting\n- Lower carbon emissions\n- Cheaper city living\n## Distributed teams\n- Async communication norms\n- Global hiring pools\n## Empty offices\n- Commercial real estate slump\n- Repurposed to housing",
1625
+ "notes": "## What this shows\n\nA **futures wheel** \u2014 Jerome Glenn's 1971/72 technique for thinking past a trend's obvious effects. A single event sits at the hub (\"remote work becomes default\"), its first-order consequences land on the inner ring, and each of those fans out to its own second-order consequences on the next ring. Reading outward is reading forward in causal time: less commuting *leads to* lower emissions and cheaper city living; empty offices *lead to* a commercial-real-estate slump and housing conversions.\n\nThe layout does the discipline for you. Every child is kept inside its parent's angular sector, so a branch never tangles with its neighbors, and each ring is color-coded by order \u2014 the visual cue that tells a workshop room how many steps removed a given consequence is from the original trend. It is the same `#` / `##` / `-` mindmap input you already write, switched on with `%% style: futureswheel`."
1626
+ },
1517
1627
  {
1518
1628
  "slug": "mindmap-product-launch",
1519
1629
  "diagram": "mindmap",
@@ -2013,6 +2123,43 @@ If the LED doesn't light up, three things to check, in order: LED polarity (the
2013
2123
  "dsl": 'phylo "Bacterial Diversity"\n newick: "((((Ecoli:0.1,Salmonella:0.12):0.05[&&NHX:B=98],Vibrio:0.2):0.08[&&NHX:B=85],((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[&&NHX:B=78]):0.2);"\n clade Gamma = (Ecoli, Salmonella, Vibrio) [color: "#1E88E5", label: "\u03B3-Proteobacteria"]\n clade Firmi = (Bacillus, Staph, Listeria, Strepto, Lactobacillus) [color: "#E53935", label: "Firmicutes"]\n clade Actino = (Myco_tb, Myco_leprae) [color: "#43A047", label: "Actinobacteria"]\n scale "substitutions/site"',
2014
2124
  "notes": '## Scenario\n\nA microbiologist or bioinformatician pastes a Newick tree string exported from RAxML, IQ-TREE, or MEGA and immediately gets a publication-ready SVG with clade highlights and a branch-length scale bar \u2014 no manual layout required.\n\n## Annotation key\n\n- `newick: "..."` \u2014 standard Newick format tree string; branch lengths follow `:` after each taxon name\n- `[&&NHX:B=98]` \u2014 NHX annotation; `B=` is the bootstrap support value (0\u2013100), rendered on internal nodes\n- `clade id = (taxon, ...)` \u2014 defines a named clade by listing its leaf members\n- `[color: "#hex", label: "..."]` \u2014 colors the clade\'s subtree and adds a labeled arc\n- `scale "..."` \u2014 draws a calibrated scale bar with the given unit label\n\n## How to read\n\nThe tree shows three major bacterial clades. Blue (\u03B3-Proteobacteria): *E. coli*, *Salmonella*, and *Vibrio* cluster with 98% bootstrap support. Red (Firmicutes): *Bacillus*, *Staph*, *Listeria*, *Streptococcus*, and *Lactobacillus*. Green (Actinobacteria): the two *Mycobacterium* species form a highly supported clade (bootstrap 100). Branch lengths represent substitutions per site \u2014 longer branches indicate faster evolutionary rates.'
2015
2125
  },
2126
+ {
2127
+ "slug": "phylo-dendrogram-gene-expression",
2128
+ "diagram": "phylo",
2129
+ "title": "Gene expression clusters (cut into flat groups)",
2130
+ "description": "A hierarchical-clustering dendrogram of gene-expression samples, sliced at a chosen height so the continuous tree collapses into distinctly colored flat clusters with a dashed threshold line.",
2131
+ "standard": "Hierarchical clustering (cophenetic height)",
2132
+ "tags": [
2133
+ "phylo",
2134
+ "dendrogram",
2135
+ "clustering",
2136
+ "cut",
2137
+ "cophenetic",
2138
+ "fcluster"
2139
+ ],
2140
+ "complexity": 2,
2141
+ "featured": false,
2142
+ "dsl": 'phylo "Gene expression clusters" [mode: dendrogram]\n newick: "(((A:1,B:1):2,C:3):2,(D:2,E:2):3);"\n cut 4\n scale "cluster distance"',
2143
+ "notes": "## What this shows\n\nThe same tree the `phylo` engine draws for evolution, read instead as the output of **hierarchical agglomerative clustering** (Sokal & Michener 1958 \u2014 the form `scipy.cluster.hierarchy.dendrogram` produces). Each internal node sits at its **merge height**: the cophenetic distance at which its two child clusters fuse. Leaves align on a common baseline, branches are rectangular elbows, and a height axis lets you read off exactly how far apart any two leaves are before they first share a cluster \u2014 `(A, B)` join low and tight, while `(D, E)` only merge with the rest near the top.\n\nThe `cut 4` line is what makes this actionable. It slices the tree at height 4: every subtree that fuses below that line becomes one **flat cluster**, each painted a distinct color, with a dashed threshold line drawn across the chart. This is the dendrogram counterpart of choosing `k` clusters in `fcluster` \u2014 turning a continuous similarity tree into the discrete groups you actually act on."
2144
+ },
2145
+ {
2146
+ "slug": "phylo-dendrogram-sample-clustering",
2147
+ "diagram": "phylo",
2148
+ "title": "Sample clustering dendrogram (no cut)",
2149
+ "description": "A bare hierarchical-clustering dendrogram with internal nodes placed at their merge height and a cluster-distance axis \u2014 the plain similarity tree before any flat-cluster threshold is applied.",
2150
+ "standard": "Hierarchical clustering (cophenetic height)",
2151
+ "tags": [
2152
+ "phylo",
2153
+ "dendrogram",
2154
+ "clustering",
2155
+ "cophenetic",
2156
+ "similarity"
2157
+ ],
2158
+ "complexity": 1,
2159
+ "featured": false,
2160
+ "dsl": 'phylo "Sample clustering" [mode: dendrogram]\n newick: "(((A:1,B:1):2,C:3):2,(D:2,E:2):3);"\n scale "cluster distance"',
2161
+ "notes": "## What this shows\n\nThe plainest reading of a **hierarchical-clustering dendrogram**: five samples joined bottom-up, with each internal node placed at its **merge height** \u2014 the cophenetic distance at which its two child clusters fuse. Leaves align on a shared baseline and a cluster-distance axis runs down the side, so the vertical position of every join tells you how similar the samples beneath it are.\n\nUnlike a cladogram (topology only) or a phylogram (branch length = evolutionary distance), here the height of the merge *is* the message: samples that fuse low are alike, samples that only fuse near the top are distant. This example omits the `cut` directive on purpose \u2014 it is the raw similarity tree, before you commit to a threshold that would carve it into flat clusters."
2162
+ },
2016
2163
  {
2017
2164
  "slug": "phylo-sars-cov-2-chronogram",
2018
2165
  "diagram": "phylo",
@@ -2860,7 +3007,7 @@ var SYNTAX = {
2860
3007
  },
2861
3008
  "phylo": {
2862
3009
  "title": "Phylogenetic tree",
2863
- "content": '## 1. Your first phylogenetic tree\n\nThe smallest useful tree: four taxa, two clades.\n\n```\nphylo "Vertebrates"\n newick: "((Human:0.1,Chimp:0.08):0.03,(Dog:0.35,Cat:0.30):0.2);"\n```\n\nThree rules cover 80% of usage:\n\n1. Start with `phylo`, optionally followed by a quoted title and bracket props.\n2. Provide the tree topology in `newick:` format \u2014 the standard Newick string, quoted, on one line. The trailing `;` is optional.\n3. Optionally define **clade** highlight groups and a **scale** label below the newick line.\n\n> Comments must start with `#` on their own line. Inline trailing comments are not supported.\n\n---\n\n## 2. Input formats\n\n### 2.1 Newick format\n\nNewick is the primary input. The full grammar is:\n\n```\n(A,B,(C,D)); # topology only\n(A:0.1,B:0.2,(C:0.3,D:0.4):0.5); # with branch lengths\n((A:0.1,B:0.2):0.05[&&NHX:B=98],(C,D):0.08); # NHX bootstrap\n(\'Homo sapiens\':0.1,\'Mus musculus\':0.2); # quoted names with spaces\n```\n\nBranch lengths follow the node name after a colon. Internal node support values can appear as plain brackets `[95]` or as NHX `[&&NHX:B=95]`.\n\n```\nphylo "Newick examples"\n newick: "((A:0.1,B:0.2):0.05[&&NHX:B=98],(C:0.3,D:0.4):0.08[&&NHX:B=87]);"\n```\n\n**Newick rules the parser accepts:**\n\n| Feature | Syntax | Notes |\n|---|---|---|\n| Leaf name | `A`, `Homo_sapiens` | No spaces \u2014 use `_` or quote |\n| Quoted leaf name | `\'Homo sapiens\'` | Single quotes; `\'\'` is a literal quote inside |\n| Branch length | `:0.035` after name | Float; optional |\n| Internal node name | `(A,B)ancestor` | After closing `)` |\n| Bootstrap (plain) | `(A,B)[95]` | Integer or float in brackets |\n| Bootstrap (NHX) | `(A,B)[&&NHX:B=95]` | `B=` field; other NHX fields stored but not rendered |\n| Semicolon | `;` at end | Optional \u2014 parser strips it |\n| Polytomy | `(A,B,C)` | More than 2 children |\n\n### 2.2 Indent DSL\n\nFor hand-written or small trees, Schematex offers an indentation-based alternative that is easier to read and edit than raw Newick:\n\n```\nphylo "Vertebrates (indent DSL)" [mode: phylogram]\nroot:\n :0.03\n Human: 0.1\n Chimp: 0.08\n :0.2\n Dog: 0.35\n Cat: 0.30\nscale "substitutions/site"\n```\n\n**Indent DSL rules:**\n\n| Syntax | Meaning |\n|---|---|\n| `Name: length` | Leaf node with branch length |\n| `: length` | Unnamed internal node with branch length |\n| `Name` | Leaf node, no branch length (cladogram) |\n| `Name [N]` | Node with support value N |\n| Deeper indent | Child of the node above at a shallower indent |\n| `#` line | Comment, ignored |\n\nThe 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.\n\n---\n\n## 3. Layout\n\nSet the layout in the header brackets: `phylo "Title" [layout: rectangular]`.\n\n| Layout | Value | Description |\n|---|---|---|\n| Rectangular | `rectangular` | Default. L-shaped branches; root on left, tips on right |\n| Slanted | `slanted` | Diagonal lines from parent to child; more compact |\n| Circular | `circular` | Root at center, tips around the circumference |\n| Unrooted | `unrooted` | Equal-angle radial; emphasizes distance, not ancestry |\n\n`[unrooted]` as a bare flag is equivalent to `[layout: unrooted]`.\n\n**Circular** \u2014 root at center, tips fanning outward. Most visually striking for many-taxa trees with clade highlights.\n\n```\nphylo "Vertebrates \u2014 circular" [layout: circular]\n 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);"\n clade Primates = (Human, Chimp, Gorilla) [color: "#1E88E5", label: "Primates", highlight: both]\n clade Carnivora = (Dog, Cat, Wolf) [color: "#E53935", label: "Carnivora", highlight: both]\n```\n\n**Rectangular** \u2014 L-shaped branches; root on the left, tips on the right. The classic phylogram form for published figures.\n\n```\nphylo "Bacterial Diversity" [layout: rectangular, mode: phylogram]\n 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);"\n clade Gamma = (Ecoli, Salmonella, Vibrio) [color: "#1E88E5", label: "\u03B3-Proteobacteria"]\n clade Firmi = (Bacillus, Staph, Listeria) [color: "#E53935", label: "Firmicutes"]\n scale "substitutions/site"\n```\n\n**Slanted** \u2014 diagonal lines from parent to child; more compact than rectangular, same left-to-right reading direction.\n\n```\nphylo "Vertebrates \u2014 slanted" [layout: slanted]\n 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);"\n clade Primates = (Human, Chimp, Gorilla) [color: "#1E88E5", label: "Primates"]\n clade Carnivora = (Dog, Cat, Wolf) [color: "#E53935", label: "Carnivora"]\n scale "substitutions/site"\n```\n\n**Unrooted** \u2014 equal-angle radial layout; de-emphasizes the root, emphasizes pairwise distance between all taxa.\n\n```\nphylo "Vertebrates \u2014 unrooted" [unrooted]\n 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);"\n clade Primates = (Human, Chimp, Gorilla) [color: "#1E88E5", label: "Primates"]\n clade Carnivora = (Dog, Cat, Wolf) [color: "#E53935", label: "Carnivora"]\n```\n\n---\n\n## 4. Mode\n\nSet with `[mode: \u2026]` in the header (or in a `style [mode: \u2026]` line).\n\n| Mode | Value | Branch length meaning |\n|---|---|---|\n| Phylogram | `phylogram` | Default. Proportional to evolutionary distance (substitutions/site) |\n| Cladogram | `cladogram` | Ignored \u2014 tips align; only topology matters |\n| Chronogram | `chronogram` | Proportional to divergence time; all tips align to "present" |\n\nChronogram 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.\n\n```\nphylo "SARS-CoV-2 variants" [mode: chronogram, mrsd: "2023"]\n newick: "((Alpha:0.5,Delta:0.4):0.3,Omicron:0.8);"\n scale "years"\n```\n\n---\n\n## 5. Clade highlighting\n\nA `clade` line marks a monophyletic group with a color, an optional label, and an optional highlight mode.\n\n```\nclade ID = (member1, member2, ...) [color: "#hex", label: "text", highlight: mode]\n```\n\n| Prop | Values | Effect |\n|---|---|---|\n| `color:` | hex string e.g. `"#1E88E5"` | Branch and/or background color |\n| `label:` | quoted string | Clade label shown at right margin |\n| `highlight:` | `branch`, `background`, `both` | `branch` colors lines; `background` shades the region; `both` does both |\n\nMembers are tip (leaf) IDs from the Newick string. The renderer computes the MRCA of the listed tips and highlights the entire subtree rooted there.\n\n```\nphylo "Mammal clades" [layout: rectangular]\n 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);"\n clade Primates = (Human, Chimp, Gorilla) [color: "#1E88E5", label: "Primates", highlight: both]\n clade Carnivora = (Dog, Cat, Tiger) [color: "#E53935", label: "Carnivora", highlight: branch]\n```\n\n---\n\n## 6. Scale bar and outgroup\n\n**Scale bar:** `scale "label"` \u2014 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.\n\n**Outgroup:** `outgroup: taxonId` \u2014 records the outgroup for documentation; the renderer may use it to visually mark the outgroup taxon.\n\n```\nphylo "Vertebrates"\n newick: "((Human:0.1,Chimp:0.08):0.03,Lamprey:0.8);"\n outgroup: Lamprey\n scale "substitutions/site"\n```\n\n---\n\n## 7. Header props reference\n\nAll options go inside `[\u2026]` on the `phylo` header line, or in a `style [\u2026]` line anywhere in the body.\n\n| Prop | Values | Default | Effect |\n|---|---|---|---|\n| `layout:` | `rectangular`, `slanted`, `circular`, `unrooted` | `rectangular` | Tree layout |\n| `mode:` | `phylogram`, `cladogram`, `chronogram` | `phylogram` | Branch length semantics |\n| `unrooted` | (flag) | \u2014 | Equivalent to `layout: unrooted` |\n| `branch-width:` | number | `1.5` | Stroke width of branches |\n| `openAngle:` | number (degrees) | `0` | Fan gap for circular layout (0 = full 360\xB0) |\n| `mrsd:` | quoted year string | \u2014 | Most-recent sampling date for chronograms |\n\n---\n\n## 8. Labels & comments\n\n- **Title:** `phylo "Tree of Life"` \u2014 first line only.\n- **Scale label:** `scale "substitutions/site"` \u2014 one per document.\n- **Clade label:** `[label: "Primates"]` inside a `clade` line.\n- **Comments:** `#` at the start of a line (after leading whitespace). Inline trailing comments are not supported.\n\n---\n\n## 9. Common mistakes\n\n| You wrote | Parser says | Fix |\n|---|---|---|\n| `newick: (A,B,C);` (unquoted) | `PhyloParseError: Phylo document must start with \'phylo\'` | Quote the Newick string: `newick: "(A,B,C);"` |\n| Tip name with a space: `Homo sapiens:0.1` | Parsed as `Homo` \u2014 space terminates an unquoted name | Use underscore (`Homo_sapiens`) or single-quote (`\'Homo sapiens\'`) |\n| 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 |\n| `clade X = (A, B)` with no `newick:` or indent tree | `PhyloParseError: No tree definition found` | Add a `newick:` line or an indent tree block |\n| `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 |\n| `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:` |\n| Newick with internal node names: `(A,B)ancestor:0.5` | Parses fine \u2014 `ancestor` is the internal node label | Supported; internal names appear on internal nodes |\n\n---\n\n## 10. Grammar (EBNF)\n\n```text\ndocument = header (blank | comment | newick-line | scale-line\n | outgroup-line | clade-line | style-line | indent-line)*\n\nheader = "phylo" ( WS quoted-string )? ( WS "[" props "]" )? NEWLINE\nquoted-string = \'"\' any-char-but-quote* \'"\'\n\nnewick-line = "newick:" WS quoted-newick NEWLINE\nscale-line = "scale" ( WS quoted-string )? NEWLINE\noutgroup-line = "outgroup:" WS id NEWLINE\nclade-line = "clade" WS id WS "=" WS "(" id ("," id)* ")"\n ( WS "[" clade-props "]" )? NEWLINE\nstyle-line = "style" WS "[" props "]" NEWLINE\n\n// Indent tree \u2014 triggered by a line ending in ":" with no spaces\nindent-tree = root-line indent-node*\nroot-line = id ":" NEWLINE\nindent-node = INDENT ( id ":" length | ":" length | id ) ( WS "[" number "]" )? NEWLINE\n\nprops = prop ("," prop)*\nprop = "layout:" layout-value\n | "mode:" mode-value\n | "unrooted"\n | "branch-width:" number\n | "openAngle:" number\n | "mrsd:" quoted-string\n\nclade-props = clade-prop ("," clade-prop)*\nclade-prop = "color:" quoted-string\n | "label:" quoted-string\n | "highlight:" ( "branch" | "background" | "both" )\n\nlayout-value = "rectangular" | "slanted" | "circular" | "unrooted"\nmode-value = "phylogram" | "cladogram" | "chronogram"\n\n// Newick grammar (embedded, parsed separately)\nnewick = subtree ";"?\nsubtree = leaf | internal\ninternal = "(" subtree ("," subtree)* ")" name? nhx? length?\nleaf = name nhx? length?\nname = unquoted-name | "\'" single-quoted "\'")\nlength = ":" number\nnhx = "[" number "]" // plain bootstrap\n | "[&&NHX:" nhx-pair (":" nhx-pair)* "]"\nnhx-pair = key "=" value\n\nid = [a-zA-Z] [a-zA-Z0-9_-]*\nnumber = /[+-]?[0-9]+(\\.[0-9]+)?([eE][+-]?[0-9]+)?/\ncomment = INDENT "#" any NEWLINE\n```\n\nAuthoritative source: `src/diagrams/phylo/parser.ts`. If this diverges from the parser, the parser wins \u2014 please open an issue.\n\n---'
3010
+ "content": '## 1. Your first phylogenetic tree\n\nThe smallest useful tree: four taxa, two clades.\n\n```\nphylo "Vertebrates"\n newick: "((Human:0.1,Chimp:0.08):0.03,(Dog:0.35,Cat:0.30):0.2);"\n```\n\nThree rules cover 80% of usage:\n\n1. Start with `phylo`, optionally followed by a quoted title and bracket props.\n2. Provide the tree topology in `newick:` format \u2014 the standard Newick string, quoted, on one line. The trailing `;` is optional.\n3. Optionally define **clade** highlight groups and a **scale** label below the newick line.\n\n> Comments must start with `#` on their own line. Inline trailing comments are not supported.\n\n---\n\n## 2. Input formats\n\n### 2.1 Newick format\n\nNewick is the primary input. The full grammar is:\n\n```\n(A,B,(C,D)); # topology only\n(A:0.1,B:0.2,(C:0.3,D:0.4):0.5); # with branch lengths\n((A:0.1,B:0.2):0.05[&&NHX:B=98],(C,D):0.08); # NHX bootstrap\n(\'Homo sapiens\':0.1,\'Mus musculus\':0.2); # quoted names with spaces\n```\n\nBranch lengths follow the node name after a colon. Internal node support values can appear as plain brackets `[95]` or as NHX `[&&NHX:B=95]`.\n\n```\nphylo "Newick examples"\n newick: "((A:0.1,B:0.2):0.05[&&NHX:B=98],(C:0.3,D:0.4):0.08[&&NHX:B=87]);"\n```\n\n**Newick rules the parser accepts:**\n\n| Feature | Syntax | Notes |\n|---|---|---|\n| Leaf name | `A`, `Homo_sapiens` | No spaces \u2014 use `_` or quote |\n| Quoted leaf name | `\'Homo sapiens\'` | Single quotes; `\'\'` is a literal quote inside |\n| Branch length | `:0.035` after name | Float; optional |\n| Internal node name | `(A,B)ancestor` | After closing `)` |\n| Bootstrap (plain) | `(A,B)[95]` | Integer or float in brackets |\n| Bootstrap (NHX) | `(A,B)[&&NHX:B=95]` | `B=` field; other NHX fields stored but not rendered |\n| Semicolon | `;` at end | Optional \u2014 parser strips it |\n| Polytomy | `(A,B,C)` | More than 2 children |\n\n### 2.2 Indent DSL\n\nFor hand-written or small trees, Schematex offers an indentation-based alternative that is easier to read and edit than raw Newick:\n\n```\nphylo "Vertebrates (indent DSL)" [mode: phylogram]\nroot:\n :0.03\n Human: 0.1\n Chimp: 0.08\n :0.2\n Dog: 0.35\n Cat: 0.30\nscale "substitutions/site"\n```\n\n**Indent DSL rules:**\n\n| Syntax | Meaning |\n|---|---|\n| `Name: length` | Leaf node with branch length |\n| `: length` | Unnamed internal node with branch length |\n| `Name` | Leaf node, no branch length (cladogram) |\n| `Name [N]` | Node with support value N |\n| Deeper indent | Child of the node above at a shallower indent |\n| `#` line | Comment, ignored |\n\nThe 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.\n\n---\n\n## 3. Layout\n\nSet the layout in the header brackets: `phylo "Title" [layout: rectangular]`.\n\n| Layout | Value | Description |\n|---|---|---|\n| Rectangular | `rectangular` | Default. L-shaped branches; root on left, tips on right |\n| Slanted | `slanted` | Diagonal lines from parent to child; more compact |\n| Circular | `circular` | Root at center, tips around the circumference |\n| Unrooted | `unrooted` | Equal-angle radial; emphasizes distance, not ancestry |\n\n`[unrooted]` as a bare flag is equivalent to `[layout: unrooted]`.\n\n**Circular** \u2014 root at center, tips fanning outward. Most visually striking for many-taxa trees with clade highlights.\n\n```\nphylo "Vertebrates \u2014 circular" [layout: circular]\n 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);"\n clade Primates = (Human, Chimp, Gorilla) [color: "#1E88E5", label: "Primates", highlight: both]\n clade Carnivora = (Dog, Cat, Wolf) [color: "#E53935", label: "Carnivora", highlight: both]\n```\n\n**Rectangular** \u2014 L-shaped branches; root on the left, tips on the right. The classic phylogram form for published figures.\n\n```\nphylo "Bacterial Diversity" [layout: rectangular, mode: phylogram]\n 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);"\n clade Gamma = (Ecoli, Salmonella, Vibrio) [color: "#1E88E5", label: "\u03B3-Proteobacteria"]\n clade Firmi = (Bacillus, Staph, Listeria) [color: "#E53935", label: "Firmicutes"]\n scale "substitutions/site"\n```\n\n**Slanted** \u2014 diagonal lines from parent to child; more compact than rectangular, same left-to-right reading direction.\n\n```\nphylo "Vertebrates \u2014 slanted" [layout: slanted]\n 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);"\n clade Primates = (Human, Chimp, Gorilla) [color: "#1E88E5", label: "Primates"]\n clade Carnivora = (Dog, Cat, Wolf) [color: "#E53935", label: "Carnivora"]\n scale "substitutions/site"\n```\n\n**Unrooted** \u2014 equal-angle radial layout; de-emphasizes the root, emphasizes pairwise distance between all taxa.\n\n```\nphylo "Vertebrates \u2014 unrooted" [unrooted]\n 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);"\n clade Primates = (Human, Chimp, Gorilla) [color: "#1E88E5", label: "Primates"]\n clade Carnivora = (Dog, Cat, Wolf) [color: "#E53935", label: "Carnivora"]\n```\n\n---\n\n## 4. Mode\n\nSet with `[mode: \u2026]` in the header (or in a `style [mode: \u2026]` line).\n\n| Mode | Value | Branch length meaning |\n|---|---|---|\n| Phylogram | `phylogram` | Default. Proportional to evolutionary distance (substitutions/site) |\n| Cladogram | `cladogram` | Ignored \u2014 tips align; only topology matters |\n| Chronogram | `chronogram` | Proportional to divergence time; all tips align to "present" |\n| Dendrogram | `dendrogram` | Branch length is **merge height** \u2014 the distance at which two clusters join |\n\nChronogram 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.\n\n```\nphylo "SARS-CoV-2 variants" [mode: chronogram, mrsd: "2023"]\n newick: "((Alpha:0.5,Delta:0.4):0.3,Omicron:0.8);"\n scale "years"\n```\n\n**Dendrogram** \u2014 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 \u2014 gene-expression clusters, sample similarity, survey-response groups \u2014 rather than a phylogeny.\n\nAdd 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 \u2014 turning a continuous tree into a discrete set of groups.\n\n```\nphylo "Gene expression clusters" [mode: dendrogram]\n newick: "(((A:1,B:1):2,C:3):2,(D:2,E:2):3);"\n cut 4\n scale "cluster distance"\n```\n\nOmit `cut` to show the bare dendrogram with no flat-cluster coloring:\n\n```\nphylo "Sample clustering" [mode: dendrogram]\n newick: "(((A:1,B:1):2,C:3):2,(D:2,E:2):3);"\n scale "cluster distance"\n```\n\n---\n\n## 5. Clade highlighting\n\nA `clade` line marks a monophyletic group with a color, an optional label, and an optional highlight mode.\n\n```\nclade ID = (member1, member2, ...) [color: "#hex", label: "text", highlight: mode]\n```\n\n| Prop | Values | Effect |\n|---|---|---|\n| `color:` | hex string e.g. `"#1E88E5"` | Branch and/or background color |\n| `label:` | quoted string | Clade label shown at right margin |\n| `highlight:` | `branch`, `background`, `both` | `branch` colors lines; `background` shades the region; `both` does both |\n\nMembers are tip (leaf) IDs from the Newick string. The renderer computes the MRCA of the listed tips and highlights the entire subtree rooted there.\n\n```\nphylo "Mammal clades" [layout: rectangular]\n 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);"\n clade Primates = (Human, Chimp, Gorilla) [color: "#1E88E5", label: "Primates", highlight: both]\n clade Carnivora = (Dog, Cat, Tiger) [color: "#E53935", label: "Carnivora", highlight: branch]\n```\n\n---\n\n## 6. Scale bar and outgroup\n\n**Scale bar:** `scale "label"` \u2014 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.\n\n**Outgroup:** `outgroup: taxonId` \u2014 records the outgroup for documentation; the renderer may use it to visually mark the outgroup taxon.\n\n```\nphylo "Vertebrates"\n newick: "((Human:0.1,Chimp:0.08):0.03,Lamprey:0.8);"\n outgroup: Lamprey\n scale "substitutions/site"\n```\n\n---\n\n## 7. Header props reference\n\nAll options go inside `[\u2026]` on the `phylo` header line, or in a `style [\u2026]` line anywhere in the body.\n\n| Prop | Values | Default | Effect |\n|---|---|---|---|\n| `layout:` | `rectangular`, `slanted`, `circular`, `unrooted` | `rectangular` | Tree layout |\n| `mode:` | `phylogram`, `cladogram`, `chronogram`, `dendrogram` | `phylogram` | Branch length semantics |\n| `unrooted` | (flag) | \u2014 | Equivalent to `layout: unrooted` |\n| `branch-width:` | number | `1.5` | Stroke width of branches |\n| `openAngle:` | number (degrees) | `0` | Fan gap for circular layout (0 = full 360\xB0) |\n| `mrsd:` | quoted year string | \u2014 | Most-recent sampling date for chronograms |\n\n---\n\n## 8. Labels & comments\n\n- **Title:** `phylo "Tree of Life"` \u2014 first line only.\n- **Scale label:** `scale "substitutions/site"` \u2014 one per document.\n- **Clade label:** `[label: "Primates"]` inside a `clade` line.\n- **Comments:** `#` at the start of a line (after leading whitespace). Inline trailing comments are not supported.\n\n---\n\n## 9. Common mistakes\n\n| You wrote | Parser says | Fix |\n|---|---|---|\n| `newick: (A,B,C);` (unquoted) | `PhyloParseError: Phylo document must start with \'phylo\'` | Quote the Newick string: `newick: "(A,B,C);"` |\n| Tip name with a space: `Homo sapiens:0.1` | Parsed as `Homo` \u2014 space terminates an unquoted name | Use underscore (`Homo_sapiens`) or single-quote (`\'Homo sapiens\'`) |\n| 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 |\n| `clade X = (A, B)` with no `newick:` or indent tree | `PhyloParseError: No tree definition found` | Add a `newick:` line or an indent tree block |\n| `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 |\n| `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:` |\n| Newick with internal node names: `(A,B)ancestor:0.5` | Parses fine \u2014 `ancestor` is the internal node label | Supported; internal names appear on internal nodes |\n\n---\n\n## 10. Grammar (EBNF)\n\n```text\ndocument = header (blank | comment | newick-line | scale-line\n | outgroup-line | clade-line | style-line | cut-line | indent-line)*\n\nheader = "phylo" ( WS quoted-string )? ( WS "[" props "]" )? NEWLINE\nquoted-string = \'"\' any-char-but-quote* \'"\'\n\nnewick-line = "newick:" WS quoted-newick NEWLINE\nscale-line = "scale" ( WS quoted-string )? NEWLINE\noutgroup-line = "outgroup:" WS id NEWLINE\ncut-line = "cut" WS number NEWLINE // dendrogram mode: flat-cluster threshold height\nclade-line = "clade" WS id WS "=" WS "(" id ("," id)* ")"\n ( WS "[" clade-props "]" )? NEWLINE\nstyle-line = "style" WS "[" props "]" NEWLINE\n\n// Indent tree \u2014 triggered by a line ending in ":" with no spaces\nindent-tree = root-line indent-node*\nroot-line = id ":" NEWLINE\nindent-node = INDENT ( id ":" length | ":" length | id ) ( WS "[" number "]" )? NEWLINE\n\nprops = prop ("," prop)*\nprop = "layout:" layout-value\n | "mode:" mode-value\n | "unrooted"\n | "branch-width:" number\n | "openAngle:" number\n | "mrsd:" quoted-string\n\nclade-props = clade-prop ("," clade-prop)*\nclade-prop = "color:" quoted-string\n | "label:" quoted-string\n | "highlight:" ( "branch" | "background" | "both" )\n\nlayout-value = "rectangular" | "slanted" | "circular" | "unrooted"\nmode-value = "phylogram" | "cladogram" | "chronogram" | "dendrogram"\n\n// Newick grammar (embedded, parsed separately)\nnewick = subtree ";"?\nsubtree = leaf | internal\ninternal = "(" subtree ("," subtree)* ")" name? nhx? length?\nleaf = name nhx? length?\nname = unquoted-name | "\'" single-quoted "\'")\nlength = ":" number\nnhx = "[" number "]" // plain bootstrap\n | "[&&NHX:" nhx-pair (":" nhx-pair)* "]"\nnhx-pair = key "=" value\n\nid = [a-zA-Z] [a-zA-Z0-9_-]*\nnumber = /[+-]?[0-9]+(\\.[0-9]+)?([eE][+-]?[0-9]+)?/\ncomment = INDENT "#" any NEWLINE\n```\n\nAuthoritative source: `src/diagrams/phylo/parser.ts`. If this diverges from the parser, the parser wins \u2014 please open an issue.\n\n---'
2864
3011
  },
2865
3012
  "sociogram": {
2866
3013
  "title": "Sociogram",
@@ -2904,7 +3051,7 @@ var SYNTAX = {
2904
3051
  },
2905
3052
  "decisiontree": {
2906
3053
  "title": "Decision tree diagram",
2907
- "content": '## 1. Your first decision tree\n\nThe smallest useful decision tree: a root question with two branches.\n\n```\n\n decisiontree "Laptop troubleshoot"\n\n question "Does it power on?"\n yes: answer "Check display \u2014 connect external monitor"\n no: question "Is the charger light on?"\n yes: answer "Hold power button 10 s \u2014 try again"\n no: answer "Check outlet and charging cable"\n```\n\nFour rules cover 80% of usage:\n\n1. Start with `decisiontree`, optionally with `:mode` and a quoted title.\n2. Each question node uses `question "text"` (or shorthand `q "text"`).\n3. Each answer/leaf uses `answer "text"` (or `a "text"` or `leaf "text"`).\n4. Branch labels \u2014 `yes:`, `no:`, or a custom `label "X":` \u2014 prefix the child node on the same line.\n\nIndentation controls nesting: each level adds 2 spaces. The parser computes parent-child relationships from indent depth.\n\n> Comments must start with `#` or `//` on their own line.\n\n---\n\n## 2. Modes\n\nThe mode is set in the header line:\n\n| Header | Mode | Used for |\n|---|---|---|\n| `decisiontree` | taxonomy | Yes/no question flows, troubleshooting guides, clinical decision support |\n| `decisiontree:decision` (or `decisiontree:da`) | decision analysis | Investment decisions, risk analysis, expected value calculation |\n| `decisiontree:ml` | machine learning | Visualizing trained CART classifiers (scikit-learn, XGBoost, etc.) |\n\nDefault direction is `top-down` for taxonomy and ML, `left-right` for decision analysis.\n\n---\n\n## 3. Taxonomy mode\n\nBest for: troubleshooting guides, FAQs, clinical protocols, product recommendation flows.\n\n### Node keywords\n\n| Keyword | Aliases | Meaning |\n|---|---|---|\n| `question "\u2026"` | `q "\u2026"` | Internal node \u2014 a question with children |\n| `answer "\u2026"` | `a "\u2026"`, `leaf "\u2026"` | Leaf node \u2014 a terminal outcome |\n\n### Branch labels\n\n| Syntax | Meaning |\n|---|---|\n| `yes: question "\u2026"` | Branch labeled "yes" |\n| `no: answer "\u2026"` | Branch labeled "no" |\n| `label "Custom text": answer "\u2026"` | Branch with any custom label |\n\nCustom labels let you go beyond yes/no for multi-way decisions from one question.\n\n```\n\n decisiontree "Triage \u2014 chest pain onset"\n\n q "Onset sudden?"\n yes: q "ECG changes present?"\n yes: a "ACS protocol \u2014 cardiology consult"\n no: q "D-dimer elevated?"\n yes: a "PE workup \u2014 CT pulmonary angiography"\n no: a "Aortic dissection \u2014 CT angiography"\n no: q "Pain reproducible on palpation?"\n yes: a "Musculoskeletal \u2014 NSAIDs, follow-up PCP"\n no: a "GI / anxiety \u2014 further history"\n```\n\n```\n\n decisiontree "Pain level triage"\n\n question "Reported pain level?"\n label "Severe (8-10)": answer "Emergency \u2014 send to ER immediately"\n label "Moderate (4-7)": answer "Urgent care \u2014 within 2 hours"\n label "Mild (1-3)": answer "Schedule next available \u2014 OTC care"\n label "None": answer "Monitor \u2014 patient may be post-medication"\n```\n\n---\n\n## 4. Decision analysis mode\n\nBest for: investment decisions, build-vs-buy analysis, risk-weighted strategy evaluation.\n\n### Node keywords\n\n| Keyword | Aliases | Meaning |\n|---|---|---|\n| `decision "\u2026"` | \u2014 | Decision node \u2014 the actor chooses a branch |\n| `chance "\u2026"` | \u2014 | Chance node \u2014 an uncertain outcome |\n| `end "\u2026"` | `outcome "\u2026"` | Terminal node \u2014 final payoff |\n\n### Branch keywords\n\n| Keyword | Meaning |\n|---|---|\n| `choice "label"` | Names the incoming branch from a decision node |\n| `prob N` | Sets the probability (0\u20131) on the incoming branch from a chance node |\n\n### Payoff attribute\n\n`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.\n\n**Constraint:** probabilities on all direct children of a `chance` node must sum to 1.0 (\xB10.01). The parser throws a `DTreeParseError` if they do not.\n\n```\n\n decisiontree:decision "Cloud vendor selection"\n\n decision "Which vendor?"\n choice "Build in-house"\n chance "Project outcome"\n prob 0.6 end "On-time delivery" payoff=900000\n prob 0.4 end "Over budget / delayed" payoff=150000\n choice "Managed SaaS vendor"\n end "Predictable cost" payoff=500000\n choice "Hybrid approach"\n chance "Integration complexity"\n prob 0.5 end "Smooth integration" payoff=700000\n prob 0.5 end "Integration rework" payoff=300000\n```\n\n---\n\n## 5. Machine learning mode\n\nBest for: explaining trained CART classifiers, model transparency reports, feature importance analysis.\n\n### Node keywords\n\n| Keyword | Meaning |\n|---|---|\n| `split "\u2026"` | Internal split node \u2014 contains a feature test |\n| `leaf "\u2026"` | Leaf node \u2014 class or regression value |\n\n### Branch prefixes\n\n`true` and `false` prefix child nodes to mark which branch each child represents.\n\n### Properties (key=value, no colon, no quotes around values)\n\n| Property | Applies to | Meaning |\n|---|---|---|\n| `feature=name` | split | Feature name used at the split |\n| `op="<="` | split | Comparison operator (quote if contains special chars) |\n| `threshold=5.9` | split | Split threshold value |\n| `samples=150` | split, leaf | Sample count at this node |\n| `gini=0.5` | split, leaf | Gini impurity |\n| `entropy=0.5` | split, leaf | Entropy impurity |\n| `mse=0.3` | split, leaf | Mean squared error (regression) |\n| `gain=0.2` | split, leaf | Information gain |\n| `class=name` | leaf | Predicted class name |\n| `value=50` | leaf | Sample count; use `value=[50,30,20]` for class distribution |\n\n```\n\n decisiontree:ml "Iris classification (CART)"\n direction: top-down\n impurity: gini\n\n split "Petal length \u2264 2.45" feature=petal_length op="<=" threshold=2.45 samples=150 gini=0.667\n true leaf "Setosa" class=Iris-setosa value=50 gini=0.0\n false split "Petal width \u2264 1.75" feature=petal_width op="<=" threshold=1.75 samples=100 gini=0.5\n true leaf "Versicolor" class=Iris-versicolor value=50 gini=0.0\n false leaf "Virginica" class=Iris-virginica value=50 gini=0.0\n```\n\n---\n\n## 6. Config options\n\nConfig lines appear between the header and the first node. Each is `key: value` (colon, no `config` keyword).\n\n### Shared config (all modes)\n\n| Key | Values | Default | Effect |\n|---|---|---|---|\n| `direction:` | `top-down`, `left-right` | `top-down` (taxonomy/ML), `left-right` (decision) | Layout direction |\n| `edgeStyle:` (or `edge-style:`) | `diagonal`, `orthogonal`, `bracket` | mode-dependent | Edge drawing style |\n\n### Taxonomy config\n\n| Key | Values | Default | Effect |\n|---|---|---|---|\n| `branchLabels:` (or `branch-labels:`) | `boolean`, `relation` | `boolean` | Branch label style |\n\n### Decision analysis config\n\n| Key | Values | Default | Effect |\n|---|---|---|---|\n| `branchLength:` (or `branch-length:`) | `probability` | off | Scale branch length proportional to probability |\n\n### ML config\n\n| Key | Values | Default | Effect |\n|---|---|---|---|\n| `impurity:` | `gini`, `entropy`, `mse`, `gain` | `gini` | Impurity metric shown on nodes |\n| `classes:` | comma-separated list | \u2014 | Class label names for display |\n\n```\ndecisiontree:ml "Loan classifier"\ndirection: top-down\nimpurity: gini\nclasses: Approved, Denied, Review\n```\n\n---\n\n## 7. Labels & comments\n\n- **Diagram title:** `decisiontree "Title"` \u2014 the quoted string after the header keyword.\n- **Node label:** the quoted string immediately after the node keyword \u2014 `question "Is the fee waived?"`.\n- **Branch label:** `yes:`, `no:`, or `label "Custom":` before the child node \u2014 on the same line as the child.\n- **Payoff:** `payoff=250000` at the end of a decision/end node line.\n- **ML properties:** `key=value` tokens after the node\'s label string (no `[\u2026]` brackets, no colons).\n- **Comments:** `#` or `//` at the start of a line (after optional leading whitespace). Only full-line comments are supported \u2014 inline trailing comments are not.\n\n---\n\n## 8. Reserved words & escaping\n\n**Reserved node keywords:** `decision`, `chance`, `end`, `outcome`, `choice`, `prob`, `split`, `leaf`, `question`, `q`, `answer`, `a`.\n\n**Reserved branch prefixes:** `yes:`, `no:`, `true`, `false`, `label`.\n\n**Reserved header forms:** `decisiontree`, `decisiontree:decision`, `decisiontree:da`, `decisiontree:ml`.\n\n**Strings with spaces** must be double-quoted: `question "Annual revenue > $1M?"`. Node labels, branch labels from `label "\u2026":` syntax, and the diagram title all require double quotes.\n\n| Reserved token | Context | Notes |\n|---|---|---|\n| `yes:` / `no:` | Line start in taxonomy | Cannot be used as a label \u2014 use `label "yes":` if you need literal text "yes" |\n| `true` / `false` | Line start in ML mode | Cannot be a node label |\n| `choice` | Line start in decision mode | Acts as branch wrapper, not a node |\n| `prob` | Line start in decision mode | Must be followed by a number |\n\n---\n\n## 9. Common mistakes\n\n| You wrote | Parser says | Fix |\n|---|---|---|\n| `yes: "Approve"` (no node keyword) | `DTreeParseError: Missing taxonomy node kind` | `yes: answer "Approve"` |\n| 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 (\xB10.01) |\n| `question "text"` with a child at the same indent level | Child not parsed as a child \u2014 becomes a sibling | Indent children by 2 more spaces than the parent |\n| `config direction = top-down` (using `config` keyword) | `config` is a fishbone keyword \u2014 not recognized here | Use `direction: top-down` (no `config` prefix) |\n| `feature = petal_length` (spaces around `=`) | Parsed as separate tokens; property not recognized | No spaces: `feature=petal_length` |\n| `[payoff: 500000]` bracket syntax | Not recognized \u2014 parser ignores brackets for payoff | Use `payoff=500000` (no brackets, no spaces around `=`) |\n| `decisiontree:taxonomy` | `DTreeParseError: Invalid header` | Use `decisiontree` (no mode suffix for taxonomy) |\n\n---\n\n## 10. Grammar (EBNF)\n\n```text\ndocument = header ( config-line )* node\n\nheader = "decisiontree" ( ":" mode )? ( WS quoted-string )? NEWLINE\nmode = "decision" | "da" | "ml"\n // omitted \u2192 taxonomy\n\nconfig-line = config-key ":" WS config-value NEWLINE\nconfig-key = "direction" | "edgeStyle" | "edge-style"\n | "branchLabels" | "branch-labels"\n | "branchLength" | "branch-length"\n | "impurity" | "classes"\n\n// \u2500\u2500 Taxonomy mode \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nnode = ( branch-prefix WS )? tax-node ( WS "[" tax-attrs "]" )? NEWLINE\n INDENT child-node*\ntax-node = ( "question" | "q" ) WS quoted-string\n | ( "answer" | "a" | "leaf" ) WS quoted-string\nbranch-prefix = "yes:" | "no:" | "label" WS quoted-string ":"\n\n// \u2500\u2500 Decision-analysis mode \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nda-node = "decision" WS quoted-string NEWLINE INDENT da-child+\n | "chance" WS quoted-string NEWLINE INDENT da-prob-child+\n | ( "end" | "outcome" ) WS quoted-string ( WS "payoff=" number )? NEWLINE\nda-child = "choice" WS quoted-string NEWLINE INDENT da-node\nda-prob-child = "prob" WS number WS da-node // prob, value, and child all on one line\n\n// \u2500\u2500 ML mode \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nml-node = ( "true" | "false" )? ml-kind WS quoted-string ml-prop* NEWLINE\n INDENT ml-child*\n // "true"/"false" and ml-kind must be on the same line\nml-kind = "split" | "leaf"\nml-prop = WS key "=" value // no spaces around "="\n\ncomment = ( "#" | "//" ) any NEWLINE\nquoted-string = \'"\' any-char-but-quote* \'"\'\n```\n\nAuthoritative source: `src/diagrams/decisiontree/parser.ts`. If this diverges from the parser, the parser wins \u2014 please open an issue.\n\n---'
3054
+ "content": '## 1. Your first decision tree\n\nThe smallest useful decision tree: a root question with two branches.\n\n```\n\n decisiontree "Laptop troubleshoot"\n\n question "Does it power on?"\n yes: answer "Check display \u2014 connect external monitor"\n no: question "Is the charger light on?"\n yes: answer "Hold power button 10 s \u2014 try again"\n no: answer "Check outlet and charging cable"\n```\n\nFour rules cover 80% of usage:\n\n1. Start with `decisiontree`, optionally with `:mode` and a quoted title.\n2. Each question node uses `question "text"` (or shorthand `q "text"`).\n3. Each answer/leaf uses `answer "text"` (or `a "text"` or `leaf "text"`).\n4. Branch labels \u2014 `yes:`, `no:`, or a custom `label "X":` \u2014 prefix the child node on the same line.\n\nIndentation controls nesting: each level adds 2 spaces. The parser computes parent-child relationships from indent depth.\n\n> Comments must start with `#` or `//` on their own line.\n\n---\n\n## 2. Modes\n\nThe mode is set in the header line:\n\n| Header | Mode | Used for |\n|---|---|---|\n| `decisiontree` | taxonomy | Yes/no question flows, troubleshooting guides, clinical decision support |\n| `decisiontree:decision` (or `decisiontree:da`) | decision analysis | Investment decisions, risk analysis, expected value calculation |\n| `decisiontree:influence` (or `mode: influence`) | influence diagram | Compact DAG view of a decision problem \u2014 structure before unrolling to a tree |\n| `decisiontree:ml` | machine learning | Visualizing trained CART classifiers (scikit-learn, XGBoost, etc.) |\n\nDefault direction is `top-down` for taxonomy and ML, `left-right` for decision analysis.\n\n---\n\n## 3. Taxonomy mode\n\nBest for: troubleshooting guides, FAQs, clinical protocols, product recommendation flows.\n\n### Node keywords\n\n| Keyword | Aliases | Meaning |\n|---|---|---|\n| `question "\u2026"` | `q "\u2026"` | Internal node \u2014 a question with children |\n| `answer "\u2026"` | `a "\u2026"`, `leaf "\u2026"` | Leaf node \u2014 a terminal outcome |\n\n### Branch labels\n\n| Syntax | Meaning |\n|---|---|\n| `yes: question "\u2026"` | Branch labeled "yes" |\n| `no: answer "\u2026"` | Branch labeled "no" |\n| `label "Custom text": answer "\u2026"` | Branch with any custom label |\n\nCustom labels let you go beyond yes/no for multi-way decisions from one question.\n\n```\n\n decisiontree "Triage \u2014 chest pain onset"\n\n q "Onset sudden?"\n yes: q "ECG changes present?"\n yes: a "ACS protocol \u2014 cardiology consult"\n no: q "D-dimer elevated?"\n yes: a "PE workup \u2014 CT pulmonary angiography"\n no: a "Aortic dissection \u2014 CT angiography"\n no: q "Pain reproducible on palpation?"\n yes: a "Musculoskeletal \u2014 NSAIDs, follow-up PCP"\n no: a "GI / anxiety \u2014 further history"\n```\n\n```\n\n decisiontree "Pain level triage"\n\n question "Reported pain level?"\n label "Severe (8-10)": answer "Emergency \u2014 send to ER immediately"\n label "Moderate (4-7)": answer "Urgent care \u2014 within 2 hours"\n label "Mild (1-3)": answer "Schedule next available \u2014 OTC care"\n label "None": answer "Monitor \u2014 patient may be post-medication"\n```\n\n---\n\n## 4. Decision analysis mode\n\nBest for: investment decisions, build-vs-buy analysis, risk-weighted strategy evaluation.\n\n### Node keywords\n\n| Keyword | Aliases | Meaning |\n|---|---|---|\n| `decision "\u2026"` | \u2014 | Decision node \u2014 the actor chooses a branch |\n| `chance "\u2026"` | \u2014 | Chance node \u2014 an uncertain outcome |\n| `end "\u2026"` | `outcome "\u2026"` | Terminal node \u2014 final payoff |\n\n### Branch keywords\n\n| Keyword | Meaning |\n|---|---|\n| `choice "label"` | Names the incoming branch from a decision node |\n| `prob N` | Sets the probability (0\u20131) on the incoming branch from a chance node |\n\n### Payoff attribute\n\n`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.\n\n**Constraint:** probabilities on all direct children of a `chance` node must sum to 1.0 (\xB10.01). The parser throws a `DTreeParseError` if they do not.\n\n```\n\n decisiontree:decision "Cloud vendor selection"\n\n decision "Which vendor?"\n choice "Build in-house"\n chance "Project outcome"\n prob 0.6 end "On-time delivery" payoff=900000\n prob 0.4 end "Over budget / delayed" payoff=150000\n choice "Managed SaaS vendor"\n end "Predictable cost" payoff=500000\n choice "Hybrid approach"\n chance "Integration complexity"\n prob 0.5 end "Smooth integration" payoff=700000\n prob 0.5 end "Integration rework" payoff=300000\n```\n\n---\n\n## 5. Influence diagram mode\n\nBest 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 \u2014 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.\n\nThis mode is **structural, not computational.** Unlike decision-analysis mode, it does not solve for expected value \u2014 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.\n\n### Header forms\n\nTwo equivalent ways to select the mode:\n\n```\ndecisiontree:influence "Oil Wildcatter"\n```\n\nor, as a directive on its own line after the header:\n\n```\ndecisiontree "Market Entry"\n mode: influence\n```\n\n### Node keywords\n\nEach node is declared as `kind Id "label"` \u2014 an id (used to wire arcs) followed by a quoted display label.\n\n| Keyword | Shape | Meaning |\n|---|---|---|\n| `decision Id "\u2026"` | **rectangle** | A choice the decision-maker controls |\n| `chance Id "\u2026"` | **oval** | An uncertain variable (a state of the world) |\n| `value Id "\u2026"` | **octagon** | The objective / payoff being optimized |\n\nThe 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`).\n\n### Arcs and their semantics\n\nArcs 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:\n\n| Arc into a\u2026 | Meaning | Drawn as |\n|---|---|---|\n| `decision` | **Informational** \u2014 this is known *before* the decision is made | dashed line |\n| `chance` | **Relevance / conditioning** \u2014 the source conditions this uncertainty | solid line |\n| `value` | **Functional** \u2014 the source is an argument of the payoff function | solid line |\n\nThe 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.\n\n### Validation rules\n\n- The graph must be **acyclic** \u2014 a cycle (e.g. `A -> B` and `B -> A`) is rejected.\n- At least **one `value` node** is required; an influence diagram with no objective is not a decision problem.\n- Arcs reference node ids that must be declared.\n\n### Examples\n\nThe Oil Wildcatter \u2014 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`).\n\n```\n\n decisiontree:influence "Oil Wildcatter"\n decision Drill "Drill?"\n chance Oil "Oil present"\n chance Seismic "Seismic test"\n value Profit "Net profit" utility=42\n Seismic -> Oil\n Seismic -> Drill\n Oil -> Profit\n Drill -> Profit\n```\n\nA 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.\n\n```\n\n decisiontree "Market Entry"\n mode: influence\n decision Enter "Enter market?"\n chance Demand "Market demand"\n chance Competition "Competitor response"\n value V "Profit" utility=120\n Demand -> Enter\n Demand -> V\n Competition -> V\n Enter -> V\n```\n\n---\n\n## 6. Machine learning mode\n\nBest for: explaining trained CART classifiers, model transparency reports, feature importance analysis.\n\n### Node keywords\n\n| Keyword | Meaning |\n|---|---|\n| `split "\u2026"` | Internal split node \u2014 contains a feature test |\n| `leaf "\u2026"` | Leaf node \u2014 class or regression value |\n\n### Branch prefixes\n\n`true` and `false` prefix child nodes to mark which branch each child represents.\n\n### Properties (key=value, no colon, no quotes around values)\n\n| Property | Applies to | Meaning |\n|---|---|---|\n| `feature=name` | split | Feature name used at the split |\n| `op="<="` | split | Comparison operator (quote if contains special chars) |\n| `threshold=5.9` | split | Split threshold value |\n| `samples=150` | split, leaf | Sample count at this node |\n| `gini=0.5` | split, leaf | Gini impurity |\n| `entropy=0.5` | split, leaf | Entropy impurity |\n| `mse=0.3` | split, leaf | Mean squared error (regression) |\n| `gain=0.2` | split, leaf | Information gain |\n| `class=name` | leaf | Predicted class name |\n| `value=50` | leaf | Sample count; use `value=[50,30,20]` for class distribution |\n\n```\n\n decisiontree:ml "Iris classification (CART)"\n direction: top-down\n impurity: gini\n\n split "Petal length \u2264 2.45" feature=petal_length op="<=" threshold=2.45 samples=150 gini=0.667\n true leaf "Setosa" class=Iris-setosa value=50 gini=0.0\n false split "Petal width \u2264 1.75" feature=petal_width op="<=" threshold=1.75 samples=100 gini=0.5\n true leaf "Versicolor" class=Iris-versicolor value=50 gini=0.0\n false leaf "Virginica" class=Iris-virginica value=50 gini=0.0\n```\n\n---\n\n## 7. Config options\n\nConfig lines appear between the header and the first node. Each is `key: value` (colon, no `config` keyword).\n\n### Shared config (all modes)\n\n| Key | Values | Default | Effect |\n|---|---|---|---|\n| `direction:` | `top-down`, `left-right` | `top-down` (taxonomy/ML), `left-right` (decision) | Layout direction |\n| `edgeStyle:` (or `edge-style:`) | `diagonal`, `orthogonal`, `bracket` | mode-dependent | Edge drawing style |\n\n### Taxonomy config\n\n| Key | Values | Default | Effect |\n|---|---|---|---|\n| `branchLabels:` (or `branch-labels:`) | `boolean`, `relation` | `boolean` | Branch label style |\n\n### Decision analysis config\n\n| Key | Values | Default | Effect |\n|---|---|---|---|\n| `branchLength:` (or `branch-length:`) | `probability` | off | Scale branch length proportional to probability |\n\n### ML config\n\n| Key | Values | Default | Effect |\n|---|---|---|---|\n| `impurity:` | `gini`, `entropy`, `mse`, `gain` | `gini` | Impurity metric shown on nodes |\n| `classes:` | comma-separated list | \u2014 | Class label names for display |\n\n```\ndecisiontree:ml "Loan classifier"\ndirection: top-down\nimpurity: gini\nclasses: Approved, Denied, Review\n```\n\n---\n\n## 8. Labels & comments\n\n- **Diagram title:** `decisiontree "Title"` \u2014 the quoted string after the header keyword.\n- **Node label:** the quoted string immediately after the node keyword \u2014 `question "Is the fee waived?"`.\n- **Branch label:** `yes:`, `no:`, or `label "Custom":` before the child node \u2014 on the same line as the child.\n- **Payoff:** `payoff=250000` at the end of a decision/end node line.\n- **ML properties:** `key=value` tokens after the node\'s label string (no `[\u2026]` brackets, no colons).\n- **Comments:** `#` or `//` at the start of a line (after optional leading whitespace). Only full-line comments are supported \u2014 inline trailing comments are not.\n\n---\n\n## 9. Reserved words & escaping\n\n**Reserved node keywords:** `decision`, `chance`, `end`, `outcome`, `choice`, `prob`, `split`, `leaf`, `question`, `q`, `answer`, `a`.\n\n**Reserved branch prefixes:** `yes:`, `no:`, `true`, `false`, `label`.\n\n**Reserved header forms:** `decisiontree`, `decisiontree:decision`, `decisiontree:da`, `decisiontree:ml`.\n\n**Strings with spaces** must be double-quoted: `question "Annual revenue > $1M?"`. Node labels, branch labels from `label "\u2026":` syntax, and the diagram title all require double quotes.\n\n| Reserved token | Context | Notes |\n|---|---|---|\n| `yes:` / `no:` | Line start in taxonomy | Cannot be used as a label \u2014 use `label "yes":` if you need literal text "yes" |\n| `true` / `false` | Line start in ML mode | Cannot be a node label |\n| `choice` | Line start in decision mode | Acts as branch wrapper, not a node |\n| `prob` | Line start in decision mode | Must be followed by a number |\n\n---\n\n## 10. Common mistakes\n\n| You wrote | Parser says | Fix |\n|---|---|---|\n| `yes: "Approve"` (no node keyword) | `DTreeParseError: Missing taxonomy node kind` | `yes: answer "Approve"` |\n| 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 (\xB10.01) |\n| `question "text"` with a child at the same indent level | Child not parsed as a child \u2014 becomes a sibling | Indent children by 2 more spaces than the parent |\n| `config direction = top-down` (using `config` keyword) | `config` is a fishbone keyword \u2014 not recognized here | Use `direction: top-down` (no `config` prefix) |\n| `feature = petal_length` (spaces around `=`) | Parsed as separate tokens; property not recognized | No spaces: `feature=petal_length` |\n| `[payoff: 500000]` bracket syntax | Not recognized \u2014 parser ignores brackets for payoff | Use `payoff=500000` (no brackets, no spaces around `=`) |\n| `decisiontree:taxonomy` | `DTreeParseError: Invalid header` | Use `decisiontree` (no mode suffix for taxonomy) |\n\n---\n\n## 11. Grammar (EBNF)\n\n```text\ndocument = header ( config-line )* node\n\nheader = "decisiontree" ( ":" mode )? ( WS quoted-string )? NEWLINE\nmode = "decision" | "da" | "ml"\n // omitted \u2192 taxonomy\n\nconfig-line = config-key ":" WS config-value NEWLINE\nconfig-key = "direction" | "edgeStyle" | "edge-style"\n | "branchLabels" | "branch-labels"\n | "branchLength" | "branch-length"\n | "impurity" | "classes"\n\n// \u2500\u2500 Taxonomy mode \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nnode = ( branch-prefix WS )? tax-node ( WS "[" tax-attrs "]" )? NEWLINE\n INDENT child-node*\ntax-node = ( "question" | "q" ) WS quoted-string\n | ( "answer" | "a" | "leaf" ) WS quoted-string\nbranch-prefix = "yes:" | "no:" | "label" WS quoted-string ":"\n\n// \u2500\u2500 Decision-analysis mode \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nda-node = "decision" WS quoted-string NEWLINE INDENT da-child+\n | "chance" WS quoted-string NEWLINE INDENT da-prob-child+\n | ( "end" | "outcome" ) WS quoted-string ( WS "payoff=" number )? NEWLINE\nda-child = "choice" WS quoted-string NEWLINE INDENT da-node\nda-prob-child = "prob" WS number WS da-node // prob, value, and child all on one line\n\n// \u2500\u2500 ML mode \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nml-node = ( "true" | "false" )? ml-kind WS quoted-string ml-prop* NEWLINE\n INDENT ml-child*\n // "true"/"false" and ml-kind must be on the same line\nml-kind = "split" | "leaf"\nml-prop = WS key "=" value // no spaces around "="\n\ncomment = ( "#" | "//" ) any NEWLINE\nquoted-string = \'"\' any-char-but-quote* \'"\'\n```\n\nAuthoritative source: `src/diagrams/decisiontree/parser.ts`. If this diverges from the parser, the parser wins \u2014 please open an issue.\n\n---'
2908
3055
  },
2909
3056
  "flowchart": {
2910
3057
  "title": "Flowchart",
@@ -2912,7 +3059,7 @@ var SYNTAX = {
2912
3059
  },
2913
3060
  "matrix": {
2914
3061
  "title": "Matrix / Quadrant diagram",
2915
- "content": '## 1. Your first matrix\n\nThe smallest useful matrix: a custom 2\xD72 with two labeled axes and three points.\n\n```\nmatrix "Feature Prioritization"\nx-axis: Low Effort \u2192 High Effort\ny-axis: Low Value \u2192 High Value\n\n"Add search" at (0.3, 0.8)\n"Rebuild pipeline" at (0.85, 0.7)\n"Update footer" at (0.2, 0.2)\n```\n\nFour rules cover 80% of usage:\n\n1. Start with the keyword `matrix`, optionally followed by a template name or a quoted title.\n2. Set the axes with `x-axis:` and `y-axis:` \u2014 or use a built-in template and skip this step entirely.\n3. 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).\n4. Add optional properties \u2014 `size:`, `category:`, `color:`, `shape:`, `highlight:` \u2014 after the coordinates.\n\n> Comments must start with `#` anywhere on a line (outside quoted strings).\n\n---\n\n## 2. Built-in templates\n\nA template pre-configures axes, quadrant labels, and grid size. Just use the template name as the second token on the header line.\n\n| Template | Grid | Use case |\n|---|---|---|\n| `eisenhower` | 2\xD72 | Urgency / Importance task prioritization |\n| `impact-effort` | 2\xD72 | Feature prioritization by impact vs. effort |\n| `rice` | 2\xD72 | RICE scoring \u2014 Reach \xD7 Impact vs. Effort |\n| `bcg` | 2\xD72 | Portfolio \u2014 Market Share vs. Growth rate |\n| `ansoff` | 2\xD72 | Product/market growth strategy |\n| `johari` | 2\xD72 | Self-awareness \u2014 known-to-self vs. known-to-others |\n| `9-box` | 3\xD73 | HR talent review \u2014 Performance vs. Potential |\n| `risk-matrix` | 5\xD75 | Risk assessment \u2014 Likelihood vs. Severity (heatmap) |\n\n```\nmatrix eisenhower "This Week"\n"Ship hotfix" at (0.1, 0.9) size: 5 highlight: true\n"Team 1:1s" at (0.1, 0.7) size: 3\n"Write Q3 OKRs" at (0.8, 0.85) size: 4\n"Inbox zero" at (0.1, 0.3) size: 2\n"Refactor auth" at (0.75, 0.4) size: 3\n```\n\nAxes and quadrant labels from a template can be overridden with explicit `x-axis:` / `y-axis:` / `quadrant` directives.\n\n---\n\n## 3. Axes\n\nAxis lines declare the semantic poles of each dimension.\n\n```\nx-axis: Low Effort \u2192 High Effort\ny-axis: Low Value \u2192 High Value\n```\n\nThe arrow separates the low label (left / bottom) from the high label (right / top). All of these separators are equivalent:\n\n| Separator | Example |\n|---|---|\n| `\u2192` (Unicode) | `x-axis: Rare \u2192 Certain` |\n| `->` (ASCII) | `x-axis: Rare -> Certain` |\n| `\u2191` | `y-axis: Cheap \u2191 Expensive` |\n| `\u2190` / `<-` / `<` | Reversed axis \u2014 high label is on the left |\n\nA **reversed axis** is for conventions where the "high" value sits at the left or bottom:\n\n```\nx-axis: High Market Share \u2190 Low Market Share\n```\n\n```\nmatrix "Product Portfolio"\nx-axis: High Market Share \u2190 Low Market Share\ny-axis: Low Growth \u2192 High Growth\n\nquadrant Q1 "Question Marks"\nquadrant Q2 "Stars"\nquadrant Q3 "Cash Cows"\nquadrant Q4 "Dogs"\n\n"Analytics Suite" at (0.25, 0.35) size: 5\n"ChatBot Pro" at (0.2, 0.8) size: 4 highlight: true\n"Legacy CRM" at (0.75, 0.25) size: 6\n"Mobile App" at (0.65, 0.75) size: 3\n```\n\n---\n\n## 4. Points\n\nEach point is a bubble positioned by a normalized (x, y) coordinate pair.\n\n```\n"Label" at (x, y)\n"Label" at (x, y) size: 4 category: design color: #7B1FA2 highlight: true note: "clarify spec"\n```\n\n| Property | Values | Meaning |\n|---|---|---|\n| `size:` | positive number | Bubble area weight (default: 3) |\n| `category:` | bareword | Color group; drives the legend |\n| `color:` | hex string | Override bubble color for this point |\n| `shape:` | `circle` \\| `square` \\| `triangle` \\| `diamond` | Bubble shape (default: `circle`) |\n| `highlight:` | `true` | Draws an emphasis ring around the bubble |\n| `note:` | quoted string | Tooltip annotation |\n| `label:` | quoted string | Replaces the display label (different from the ID) |\n\nCoordinates outside `[0, 1]` are clamped to the chart boundary and flagged with a badge \u2014 the original value is stored for tooltip display.\n\n```\nmatrix "Risk Register"\nx-axis: Low Impact \u2192 High Impact\ny-axis: Rare \u2192 Certain\n\n"Vendor delay" at (0.45, 0.7) size: 4 category: schedule highlight: true\n"Security breach" at (0.9, 0.3) size: 5 category: security shape: diamond\n"Budget overrun" at (0.5, 0.65) size: 3 category: finance\n"Key hire falls through" at (0.6, 0.55) size: 3 category: people\n"Scope creep" at (0.4, 0.8) size: 4 category: schedule\n```\n\n---\n\n## 5. Quadrant labels\n\nLabel each quadrant with a name and an optional subtitle.\n\n```\nquadrant Q1 "Do First"\nquadrant Q2 "Schedule"\nquadrant Q3 "Delete"\nquadrant Q4 "Delegate"\n\n# With an optional subtitle:\nquadrant Q1 "Do First" description: "High urgency, high importance"\n```\n\nQuadrant numbering follows the standard mathematical convention: **Q1 = top-right, Q2 = top-left, Q3 = bottom-left, Q4 = bottom-right**. The `Q` prefix is optional \u2014 `quadrant 1 "Label"` is equally valid.\n\n---\n\n## 6. Heatmap mode\n\nHeatmap mode fills N\xD7M cells with color intensity instead of plotting bubble positions.\n\n```\nmatrix heatmap 4x3 "Skill Matrix"\nrows: [Strategy, Execution, Communication, Technical]\ncols: [Junior, Mid, Senior]\n\ncell (0,0) level: weak\ncell (1,0) level: medium\ncell (2,0) level: strong\ncell (0,1) value: 7\ncell (1,2) label: "Top 10%"\n```\n\n- `matrix heatmap COLxROW` \u2014 header sets the grid dimensions.\n- `rows:` and `cols:` \u2014 comma-separated or bracket-list of axis labels.\n- `cell (col, row)` \u2014 zero-indexed, column first, row second (row 0 = bottom).\n- `level:` \u2014 `strong` (3), `medium` (2), or `weak` (1) \u2014 shorthand for heat intensity.\n- `value:` \u2014 explicit numeric value (overrides `level:`).\n- `label:` \u2014 quoted text placed inside the cell.\n\n```\nmatrix heatmap 4x4 "Competency Heat Map"\nrows: [Leadership, Execution, Communication, Technical]\ncols: [Junior, Mid, Senior, Staff]\n\ncell (0,0) level: weak\ncell (1,0) level: medium\ncell (2,0) level: strong\ncell (3,0) level: strong\ncell (0,1) level: medium\ncell (1,1) level: medium\ncell (2,1) level: strong\ncell (3,1) level: strong\ncell (0,2) level: weak\ncell (1,2) level: medium\ncell (2,2) level: medium\ncell (3,2) level: strong\ncell (0,3) level: weak\ncell (1,3) level: weak\ncell (2,3) level: medium\ncell (3,3) level: strong\n```\n\n---\n\n## 7. Correlation mode\n\nCorrelation mode renders an N\xD7M dot matrix where intensity represents the relationship strength between row and column variables.\n\n```\nmatrix correlation 4x4 "Product Metrics"\nrows: [DAU, Retention, Revenue, NPS]\ncols: [DAU, Retention, Revenue, NPS]\n\ncell (0,0) value: 1\ncell (1,0) value: 0.82\ncell (2,0) value: 0.54\ncell (3,0) value: 0.71\n```\n\nThe same `cell` syntax applies. `level: strong | medium | weak` is also accepted in correlation mode.\n\n---\n\n## 8. Config options\n\nA `config:` block tunes visual rendering. Each option goes on its own indented line below the `config:` header.\n\n```\nconfig:\n quadrantBg: true\n gridLines: true\n axisArrows: true\n bubbleScale: area\n legendPosition: bottom-right\n```\n\n| Key | Values | Default | Effect |\n|---|---|---|---|\n| `quadrantBg` | `true` \\| `false` | `true` | Colored quadrant background fills |\n| `gridLines` | `true` \\| `false` | `true` | Grid lines overlay |\n| `axisArrows` | `true` \\| `false` | `true` | Arrows at axis ends |\n| `bubbleScale` | `area` \\| `radius` | `area` | Whether `size:` scales bubble area or radius |\n| `quadrantAnnotations` | `true` \\| `false` | `true` | Show quadrant label text in corners |\n| `legendPosition` | `bottom-right` \\| `right` \\| `bottom-center` \\| `none` | `bottom-right` | Category legend placement |\n| `labelCollision` | `auto` \\| `offset-only` \\| `leader-only` \\| `off` | `auto` | Overlap avoidance strategy for point labels |\n| `offChartPolicy` | `clamp-badge` \\| `drop` | `clamp-badge` | What to do with points outside [0,1] |\n\nTwo shorthand directives also work at the top level (not inside the `config:` block):\n\n```\naxis: off # off | on | auto \u2014 show or hide the axis lines\nmargins: true # true | false \u2014 show Score + Rank margins (correlation mode)\n```\n\n---\n\n## 9. Labels & comments\n\n- **Title:** `matrix "My Title"` or `title: My Title` as a standalone line.\n- **Point label:** the quoted string before `at (\u2026)`.\n- **Axis labels:** `x-axis:` and `y-axis:` directives.\n- **Quadrant labels:** `quadrant Q1 "Name"` directive.\n- **Comments:** `#` anywhere on a line, outside quoted strings.\n\n```\nmatrix "Prioritization"\n# This is a comment\nx-axis: Low Cost \u2192 High Cost # inline comment after a directive\n"Fix bug" at (0.1, 0.9) size: 3 # comment after a point\n```\n\n---\n\n## 10. Table mode (`style: table`)\n\nThe default matrix rendering is a **scatter / bubble chart** \u2014 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.\n\n```\nmatrix eisenhower "This Week"\nstyle: table\nQ2: "Ship hotfix"\nQ2: "Customer demo prep"\nQ1: "Write Q3 OKRs"\nQ1: "Refactor auth layer"\nQ4: "LinkedIn updates"\nQ3: "Reorganize Slack channels"\n```\n\n`style: table` applies these changes automatically:\n\n| Effect | Detail |\n|---|---|\n| Axes and arrows hidden | No axis lines, labels, or arrowheads |\n| Grid lines hidden | Only the outer border and cell dividers remain |\n| Quadrant titles move inside cells | Each title becomes a cell header instead of a corner overlay |\n| Items stack as a bullet list | Multiple entries for the same quadrant stack top-down |\n\n### `Q1` \u2026 `Q4` shorthand (2\xD72 only)\n\nFor 2\xD72 templates, use `Qn: "item"` instead of the longer `cell (col, row) label: "item"` form. Mapping:\n\n| Shorthand | Cell | Eisenhower | Johari |\n|---|---|---|---|\n| `Q1:` | top-right | Schedule | Blind |\n| `Q2:` | top-left | Do First | Open / Arena |\n| `Q3:` | bottom-left | Delete | Hidden / Fa\xE7ade |\n| `Q4:` | bottom-right | Delegate | Unknown |\n\nRepeat a shorthand key to add multiple items to the same cell:\n\n```\nQ2: "Ship hotfix"\nQ2: "Customer demo prep"\n```\n\nFor 3\xD73 grids (9-box), use `cell (col, row) label: "\u2026"` directly \u2014 the `Q` shorthand is 2\xD72 only.\n\n### When to use table vs scatter\n\n| Use `style: table` for | Use scatter (default) for |\n|---|---|\n| Eisenhower with task lists | Eisenhower with `size:` effort weights |\n| Johari window coaching | Impact-Effort with bubble = revenue |\n| Backlog grouping (no numeric third dimension) | RICE / BCG portfolio (third dimension IS the bubble size) |\n| 9-box talent review | Risk heatmap (5\xD75 with numeric severity) |\n\n```\nmatrix eisenhower "This Week"\nstyle: table\nQ2: "Ship hotfix"\nQ2: "Customer demo prep"\nQ1: "Write Q3 OKRs"\nQ1: "Refactor auth layer"\nQ4: "LinkedIn updates"\nQ3: "Reorganize Slack channels"\n```\n\n---\n\n## 11. Reserved words & escaping\n\n**Reserved at line start:** `matrix` (header), `x-axis:`, `y-axis:`, `quadrant`, `config:`, `title:`, `rows:`, `cols:`, `grid:`, `axis:`, `margins:`, `cell`.\n\n**Point lines must start with a quote character** (`"` or `\'`). A line that does not start with a quote is not treated as a point.\n\n**Strings with spaces** in axis labels do not need quoting \u2014 the text after the colon (and after the arrow) is taken verbatim. In `note:` and `label:` point properties, use double quotes.\n\n---\n\n## 12. Common mistakes\n\n| You wrote | Parser says | Fix |\n|---|---|---|\n| `"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 |\n| `quadrant 1 "Quick Wins"` (no Q prefix) | Accepted \u2014 `Q` prefix is optional | Both `quadrant 1` and `quadrant Q1` work |\n| `config: gridLines: false` (on same line) | Only `config:` keyword recognized; `gridLines: false` silently ignored | Put options on their own indented lines below `config:` |\n| `x-axis: "Low" \u2192 "High"` (quoted labels) | Arrow not found inside quotes \u2014 treated as plain text | Remove quotes: `x-axis: Low \u2192 High` |\n| `matrix heatmap` without dimensions | Defaults to 2\xD72; rows/cols directives set actual size | Specify dimensions on the header: `matrix heatmap 4x4` |\n| `cell (0, 0) level: Strong` (capital S) | `level` match is case-insensitive \u2014 accepted | Both `strong` and `Strong` work |\n| `shape: oval` | Unknown shape value \u2014 silently ignored | Use `circle`, `square`, `triangle`, or `diamond` |\n| `"Fix bug" at (0.1, 0.9)` on an Eisenhower with a task list | Valid scatter point \u2014 but you probably wanted a list in a cell | Add `style: table` and use `Q2: "Fix bug"` instead |\n| `Q1: "item"` on a 3\xD73 template | `Q` shorthand is parsed as a point line \u2014 silently dropped | Use `cell (col, row) label: "item"` for 3\xD73 grids |\n\n---\n\n## 13. Grammar (EBNF)\n\n```text\ndocument = header directive*\n\nheader = "matrix" ( template-name | mode-header | title )? NEWLINE\ntemplate-name = "eisenhower"|"impact-effort"|"rice"|"bcg"|"ansoff"|"johari"|"9-box"|"risk-matrix"\nmode-header = ( "heatmap" | "correlation" ) ( number "x" number )? title?\ntitle = quoted-string | bare-text\n\ndirective = x-axis | y-axis | quadrant-dir | config-block\n | point | cell | q-short | rows-dir | cols-dir | grid-dir\n | style-dir | title-dir | axis-dir | margins-dir | comment | blank\n\nx-axis = "x-axis:" WS axis-spec NEWLINE\ny-axis = "y-axis:" WS axis-spec NEWLINE\naxis-spec = text arrow text | text # plain text \u2192 high label only\narrow = "\u2192" | "->" | "\u2191" | "\u2190" | "<-" | "<" | "\u2193"\n\nquadrant-dir = "quadrant" WS "Q"? digit WS quoted-string ( WS "description:" quoted-string )? NEWLINE\n\nconfig-block = "config:" NEWLINE ( INDENT key ":" WS value NEWLINE )*\n\npoint = quoted-string WS "at" WS "(" number "," number ")" ( WS point-prop )* NEWLINE\npoint-prop = "size:" number\n | "category:" bareword\n | "color:" hex-color\n | "shape:" ( "circle"|"square"|"triangle"|"diamond" )\n | "highlight:" "true"\n | "note:" quoted-string\n | "label:" quoted-string\n\ncell = "cell" WS "(" digit "," digit ")" ( WS cell-prop )* NEWLINE\ncell-prop = "value:" number\n | "label:" quoted-string\n | "level:" ( "strong" | "medium" | "weak" )\n\nstyle-dir = "style:" WS "table" NEWLINE\nq-short = "Q" ( "1" | "2" | "3" | "4" ) ":" WS quoted-string NEWLINE # 2\xD72 only\n\nrows-dir = "rows:" WS label-list NEWLINE\ncols-dir = "cols:" WS label-list NEWLINE\ngrid-dir = "grid:" WS number "x" number NEWLINE\naxis-dir = "axis:" WS ( "off" | "on" | "auto" ) NEWLINE\nmargins-dir = "margins:" WS ( "true" | "false" | "on" | "1" ) NEWLINE\n\nlabel-list = "[" text ("," text)* "]" | text ("," text)*\nquoted-string = \'"\' any-char-but-quote* \'"\' | "\'" any-char-but-quote* "\'"\ncomment = "#" any NEWLINE\n```\n\nAuthoritative source: `src/diagrams/matrix/parser.ts`. If this diverges from the parser, the parser wins \u2014 please open an issue.\n\n---'
3062
+ "content": '## 1. Your first matrix\n\nThe smallest useful matrix: a custom 2\xD72 with two labeled axes and three points.\n\n```\nmatrix "Feature Prioritization"\nx-axis: Low Effort \u2192 High Effort\ny-axis: Low Value \u2192 High Value\n\n"Add search" at (0.3, 0.8)\n"Rebuild pipeline" at (0.85, 0.7)\n"Update footer" at (0.2, 0.2)\n```\n\nFour rules cover 80% of usage:\n\n1. Start with the keyword `matrix`, optionally followed by a template name or a quoted title.\n2. Set the axes with `x-axis:` and `y-axis:` \u2014 or use a built-in template and skip this step entirely.\n3. 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).\n4. Add optional properties \u2014 `size:`, `category:`, `color:`, `shape:`, `highlight:` \u2014 after the coordinates.\n\n> Comments must start with `#` anywhere on a line (outside quoted strings).\n\n---\n\n## 2. Built-in templates\n\nA template pre-configures axes, quadrant labels, and grid size. Just use the template name as the second token on the header line.\n\n| Template | Grid | Use case |\n|---|---|---|\n| `eisenhower` | 2\xD72 | Urgency / Importance task prioritization |\n| `impact-effort` | 2\xD72 | Feature prioritization by impact vs. effort |\n| `rice` | 2\xD72 | RICE scoring \u2014 Reach \xD7 Impact vs. Effort |\n| `bcg` | 2\xD72 | Portfolio \u2014 Market Share vs. Growth rate |\n| `ansoff` | 2\xD72 | Product/market growth strategy |\n| `johari` | 2\xD72 | Self-awareness \u2014 known-to-self vs. known-to-others |\n| `9-box` | 3\xD73 | HR talent review \u2014 Performance vs. Potential |\n| `risk-matrix` | 5\xD75 | Risk assessment \u2014 Likelihood vs. Severity (heatmap) |\n\n```\nmatrix eisenhower "This Week"\n"Ship hotfix" at (0.1, 0.9) size: 5 highlight: true\n"Team 1:1s" at (0.1, 0.7) size: 3\n"Write Q3 OKRs" at (0.8, 0.85) size: 4\n"Inbox zero" at (0.1, 0.3) size: 2\n"Refactor auth" at (0.75, 0.4) size: 3\n```\n\nAxes and quadrant labels from a template can be overridden with explicit `x-axis:` / `y-axis:` / `quadrant` directives.\n\n---\n\n## 3. Axes\n\nAxis lines declare the semantic poles of each dimension.\n\n```\nx-axis: Low Effort \u2192 High Effort\ny-axis: Low Value \u2192 High Value\n```\n\nThe arrow separates the low label (left / bottom) from the high label (right / top). All of these separators are equivalent:\n\n| Separator | Example |\n|---|---|\n| `\u2192` (Unicode) | `x-axis: Rare \u2192 Certain` |\n| `->` (ASCII) | `x-axis: Rare -> Certain` |\n| `\u2191` | `y-axis: Cheap \u2191 Expensive` |\n| `\u2190` / `<-` / `<` | Reversed axis \u2014 high label is on the left |\n\nA **reversed axis** is for conventions where the "high" value sits at the left or bottom:\n\n```\nx-axis: High Market Share \u2190 Low Market Share\n```\n\n```\nmatrix "Product Portfolio"\nx-axis: High Market Share \u2190 Low Market Share\ny-axis: Low Growth \u2192 High Growth\n\nquadrant Q1 "Question Marks"\nquadrant Q2 "Stars"\nquadrant Q3 "Cash Cows"\nquadrant Q4 "Dogs"\n\n"Analytics Suite" at (0.25, 0.35) size: 5\n"ChatBot Pro" at (0.2, 0.8) size: 4 highlight: true\n"Legacy CRM" at (0.75, 0.25) size: 6\n"Mobile App" at (0.65, 0.75) size: 3\n```\n\n---\n\n## 4. Points\n\nEach point is a bubble positioned by a normalized (x, y) coordinate pair.\n\n```\n"Label" at (x, y)\n"Label" at (x, y) size: 4 category: design color: #7B1FA2 highlight: true note: "clarify spec"\n```\n\n| Property | Values | Meaning |\n|---|---|---|\n| `size:` | positive number | Bubble area weight (default: 3) |\n| `category:` | bareword | Color group; drives the legend |\n| `color:` | hex string | Override bubble color for this point |\n| `shape:` | `circle` \\| `square` \\| `triangle` \\| `diamond` | Bubble shape (default: `circle`) |\n| `highlight:` | `true` | Draws an emphasis ring around the bubble |\n| `note:` | quoted string | Tooltip annotation |\n| `label:` | quoted string | Replaces the display label (different from the ID) |\n\nCoordinates outside `[0, 1]` are clamped to the chart boundary and flagged with a badge \u2014 the original value is stored for tooltip display.\n\n```\nmatrix "Risk Register"\nx-axis: Low Impact \u2192 High Impact\ny-axis: Rare \u2192 Certain\n\n"Vendor delay" at (0.45, 0.7) size: 4 category: schedule highlight: true\n"Security breach" at (0.9, 0.3) size: 5 category: security shape: diamond\n"Budget overrun" at (0.5, 0.65) size: 3 category: finance\n"Key hire falls through" at (0.6, 0.55) size: 3 category: people\n"Scope creep" at (0.4, 0.8) size: 4 category: schedule\n```\n\n---\n\n## 5. Quadrant labels\n\nLabel each quadrant with a name and an optional subtitle.\n\n```\nquadrant Q1 "Do First"\nquadrant Q2 "Schedule"\nquadrant Q3 "Delete"\nquadrant Q4 "Delegate"\n\n# With an optional subtitle:\nquadrant Q1 "Do First" description: "High urgency, high importance"\n```\n\nQuadrant numbering follows the standard mathematical convention: **Q1 = top-right, Q2 = top-left, Q3 = bottom-left, Q4 = bottom-right**. The `Q` prefix is optional \u2014 `quadrant 1 "Label"` is equally valid.\n\n---\n\n## 6. Heatmap mode\n\nHeatmap mode fills N\xD7M cells with color intensity instead of plotting bubble positions.\n\n```\nmatrix heatmap 4x3 "Skill Matrix"\nrows: [Strategy, Execution, Communication, Technical]\ncols: [Junior, Mid, Senior]\n\ncell (0,0) level: weak\ncell (1,0) level: medium\ncell (2,0) level: strong\ncell (0,1) value: 7\ncell (1,2) label: "Top 10%"\n```\n\n- `matrix heatmap COLxROW` \u2014 header sets the grid dimensions.\n- `rows:` and `cols:` \u2014 comma-separated or bracket-list of axis labels.\n- `cell (col, row)` \u2014 zero-indexed, column first, row second (row 0 = bottom).\n- `level:` \u2014 `strong` (3), `medium` (2), or `weak` (1) \u2014 shorthand for heat intensity.\n- `value:` \u2014 explicit numeric value (overrides `level:`).\n- `label:` \u2014 quoted text placed inside the cell.\n\n```\nmatrix heatmap 4x4 "Competency Heat Map"\nrows: [Leadership, Execution, Communication, Technical]\ncols: [Junior, Mid, Senior, Staff]\n\ncell (0,0) level: weak\ncell (1,0) level: medium\ncell (2,0) level: strong\ncell (3,0) level: strong\ncell (0,1) level: medium\ncell (1,1) level: medium\ncell (2,1) level: strong\ncell (3,1) level: strong\ncell (0,2) level: weak\ncell (1,2) level: medium\ncell (2,2) level: medium\ncell (3,2) level: strong\ncell (0,3) level: weak\ncell (1,3) level: weak\ncell (2,3) level: medium\ncell (3,3) level: strong\n```\n\n---\n\n## 7. Correlation mode\n\nCorrelation mode renders an N\xD7M dot matrix where intensity represents the relationship strength between row and column variables.\n\n```\nmatrix correlation 4x4 "Product Metrics"\nrows: [DAU, Retention, Revenue, NPS]\ncols: [DAU, Retention, Revenue, NPS]\n\ncell (0,0) value: 1\ncell (1,0) value: 0.82\ncell (2,0) value: 0.54\ncell (3,0) value: 0.71\n```\n\nThe same `cell` syntax applies. `level: strong | medium | weak` is also accepted in correlation mode.\n\n---\n\n## 8. SIPOC mode\n\nA **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 \xB7 **I**nputs \xB7 **P**rocess \xB7 **O**utputs \xB7 **C**ustomers. Before a team measures or improves anything, SIPOC pins down the boundary \u2014 "where does this process start, where does it end, and who hands work in and out of it."\n\n```\nmatrix sipoc "Order fulfilment"\nsuppliers: "Vendor", "Warehouse"\ninputs: "PO", "Stock levels"\nprocess: "Receive order", "Pick", "Pack", "Ship"\noutputs: "Shipped package", "Invoice"\ncustomers: "End customer", "Finance"\n```\n\n- Start with `matrix sipoc`, optionally followed by a quoted title.\n- Each of the five columns is its own directive: `suppliers:`, `inputs:`, `process:`, `outputs:`, `customers:`.\n- 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.\n- The `process:` column is the high-level step sequence (typically 4\u20137 steps) \u2014 keep it to the major stages, not a detailed flowchart.\n\nThe 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.\n\n```\nmatrix sipoc "Order fulfilment"\nsuppliers: "Vendor", "Warehouse"\ninputs: "PO", "Stock levels"\nprocess: "Receive order", "Pick", "Pack", "Ship"\noutputs: "Shipped package", "Invoice"\ncustomers: "End customer", "Finance"\n```\n\n---\n\n## 9. QFD mode (House of Quality)\n\n**Quality Function Deployment (QFD)** \u2014 the *House of Quality*, introduced by Yoji Akao \u2014 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.\n\nThe differentiator: the engine **computes** the bottom row for you. Each HOW\'s *technical importance* is the sum down its column of `weight \xD7 relationship strength` \u2014 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 \u2014 a half-matrix of diamond cells above the columns \u2014 records whether two HOWs help or fight each other.\n\n```\nmatrix qfd "Coffee maker"\nwhat: "Quiet operation" weight: 5\nwhat: "Brews fast" weight: 3\nwhat: "Energy efficient" weight: 4\nhow: "Fan RPM" dir: down\nhow: "Heater watts" dir: up\nhow: "Insulation" dir: up\nrel (0,0): 9\nrel (0,2): 3\nrel (1,1): 9\nrel (2,1): 3\nrel (2,2): 9\nroof (0,1): --\nroof (1,2): +\n```\n\n### WHATs and HOWs\n\n| Directive | Form | Meaning |\n|---|---|---|\n| `what:` | `what: "Label" weight: N` | A customer requirement (one row). `weight:` is its importance, conventionally 1\u20135. Declaration order is the row order, indexed from 0. |\n| `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 \u2014 `up` = more is better, `down` = less is better. |\n\n### Relationship cells\n\n`rel (i, j): strength` records how strongly column-`j` HOW serves row-`i` WHAT. The index is **(row, column)**, both zero-based.\n\n| Strength | Meaning |\n|---|---|\n| `9` | Strong relationship |\n| `3` | Medium relationship |\n| `1` | Weak relationship |\n| *(omitted)* | No relationship \u2014 leave the cell out |\n\nThis 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.\n\n### Computed technical-importance row\n\nThe engine sums each column to produce the technical-importance row at the foot of the house:\n\n```\nimportance(j) = \u03A3 over rows i ( weight(i) \xD7 strength(i, j) )\n```\n\nFor the coffee-maker example above the row computes to **45 / 39 / 51** \u2014 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.\n\nAdd `normalize: true` (its own line, anywhere in the block) to show each column as a **percentage of the total** instead of a raw sum \u2014 for this example, **33% / 29% / 38%**. Percentages make the relative priorities easier to read across very different weight scales.\n\n### The roof \u2014 HOW \xD7 HOW correlations\n\nThe **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 \u2014 the synergies and trade-offs a team must reconcile.\n\n| Glyph | Correlation |\n|---|---|\n| `++` | Strong positive \u2014 improving one strongly helps the other |\n| `+` | Positive |\n| `-` | Negative |\n| `--` | Strong negative \u2014 improving one hurts the other (a trade-off) |\n| *(omitted)* | No correlation \u2014 leave the cell out |\n\nEach 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.\n\n```\nmatrix qfd "Coffee maker"\nwhat: "Quiet operation" weight: 5\nwhat: "Brews fast" weight: 3\nwhat: "Energy efficient" weight: 4\nhow: "Fan RPM" dir: down\nhow: "Heater watts" dir: up\nhow: "Insulation" dir: up\nrel (0,0): 9\nrel (0,2): 3\nrel (1,1): 9\nrel (2,1): 3\nrel (2,2): 9\nroof (0,1): --\nroof (1,2): +\n```\n\n---\n\n## 10. Config options\n\nA `config:` block tunes visual rendering. Each option goes on its own indented line below the `config:` header.\n\n```\nconfig:\n quadrantBg: true\n gridLines: true\n axisArrows: true\n bubbleScale: area\n legendPosition: bottom-right\n```\n\n| Key | Values | Default | Effect |\n|---|---|---|---|\n| `quadrantBg` | `true` \\| `false` | `true` | Colored quadrant background fills |\n| `gridLines` | `true` \\| `false` | `true` | Grid lines overlay |\n| `axisArrows` | `true` \\| `false` | `true` | Arrows at axis ends |\n| `bubbleScale` | `area` \\| `radius` | `area` | Whether `size:` scales bubble area or radius |\n| `quadrantAnnotations` | `true` \\| `false` | `true` | Show quadrant label text in corners |\n| `legendPosition` | `bottom-right` \\| `right` \\| `bottom-center` \\| `none` | `bottom-right` | Category legend placement |\n| `labelCollision` | `auto` \\| `offset-only` \\| `leader-only` \\| `off` | `auto` | Overlap avoidance strategy for point labels |\n| `offChartPolicy` | `clamp-badge` \\| `drop` | `clamp-badge` | What to do with points outside [0,1] |\n\nTwo shorthand directives also work at the top level (not inside the `config:` block):\n\n```\naxis: off # off | on | auto \u2014 show or hide the axis lines\nmargins: true # true | false \u2014 show Score + Rank margins (correlation mode)\n```\n\n---\n\n## 11. Labels & comments\n\n- **Title:** `matrix "My Title"` or `title: My Title` as a standalone line.\n- **Point label:** the quoted string before `at (\u2026)`.\n- **Axis labels:** `x-axis:` and `y-axis:` directives.\n- **Quadrant labels:** `quadrant Q1 "Name"` directive.\n- **Comments:** `#` anywhere on a line, outside quoted strings.\n\n```\nmatrix "Prioritization"\n# This is a comment\nx-axis: Low Cost \u2192 High Cost # inline comment after a directive\n"Fix bug" at (0.1, 0.9) size: 3 # comment after a point\n```\n\n---\n\n## 12. Table mode (`style: table`)\n\nThe default matrix rendering is a **scatter / bubble chart** \u2014 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.\n\n```\nmatrix eisenhower "This Week"\nstyle: table\nQ2: "Ship hotfix"\nQ2: "Customer demo prep"\nQ1: "Write Q3 OKRs"\nQ1: "Refactor auth layer"\nQ4: "LinkedIn updates"\nQ3: "Reorganize Slack channels"\n```\n\n`style: table` applies these changes automatically:\n\n| Effect | Detail |\n|---|---|\n| Axes and arrows hidden | No axis lines, labels, or arrowheads |\n| Grid lines hidden | Only the outer border and cell dividers remain |\n| Quadrant titles move inside cells | Each title becomes a cell header instead of a corner overlay |\n| Items stack as a bullet list | Multiple entries for the same quadrant stack top-down |\n\n### `Q1` \u2026 `Q4` shorthand (2\xD72 only)\n\nFor 2\xD72 templates, use `Qn: "item"` instead of the longer `cell (col, row) label: "item"` form. Mapping:\n\n| Shorthand | Cell | Eisenhower | Johari |\n|---|---|---|---|\n| `Q1:` | top-right | Schedule | Blind |\n| `Q2:` | top-left | Do First | Open / Arena |\n| `Q3:` | bottom-left | Delete | Hidden / Fa\xE7ade |\n| `Q4:` | bottom-right | Delegate | Unknown |\n\nRepeat a shorthand key to add multiple items to the same cell:\n\n```\nQ2: "Ship hotfix"\nQ2: "Customer demo prep"\n```\n\nFor 3\xD73 grids (9-box), use `cell (col, row) label: "\u2026"` directly \u2014 the `Q` shorthand is 2\xD72 only.\n\n### When to use table vs scatter\n\n| Use `style: table` for | Use scatter (default) for |\n|---|---|\n| Eisenhower with task lists | Eisenhower with `size:` effort weights |\n| Johari window coaching | Impact-Effort with bubble = revenue |\n| Backlog grouping (no numeric third dimension) | RICE / BCG portfolio (third dimension IS the bubble size) |\n| 9-box talent review | Risk heatmap (5\xD75 with numeric severity) |\n\n```\nmatrix eisenhower "This Week"\nstyle: table\nQ2: "Ship hotfix"\nQ2: "Customer demo prep"\nQ1: "Write Q3 OKRs"\nQ1: "Refactor auth layer"\nQ4: "LinkedIn updates"\nQ3: "Reorganize Slack channels"\n```\n\n---\n\n## 13. Reserved words & escaping\n\n**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:`.\n\n**Point lines must start with a quote character** (`"` or `\'`). A line that does not start with a quote is not treated as a point.\n\n**Strings with spaces** in axis labels do not need quoting \u2014 the text after the colon (and after the arrow) is taken verbatim. In `note:` and `label:` point properties, use double quotes.\n\n---\n\n## 14. Common mistakes\n\n| You wrote | Parser says | Fix |\n|---|---|---|\n| `"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 |\n| `quadrant 1 "Quick Wins"` (no Q prefix) | Accepted \u2014 `Q` prefix is optional | Both `quadrant 1` and `quadrant Q1` work |\n| `config: gridLines: false` (on same line) | Only `config:` keyword recognized; `gridLines: false` silently ignored | Put options on their own indented lines below `config:` |\n| `x-axis: "Low" \u2192 "High"` (quoted labels) | Arrow not found inside quotes \u2014 treated as plain text | Remove quotes: `x-axis: Low \u2192 High` |\n| `matrix heatmap` without dimensions | Defaults to 2\xD72; rows/cols directives set actual size | Specify dimensions on the header: `matrix heatmap 4x4` |\n| `cell (0, 0) level: Strong` (capital S) | `level` match is case-insensitive \u2014 accepted | Both `strong` and `Strong` work |\n| `shape: oval` | Unknown shape value \u2014 silently ignored | Use `circle`, `square`, `triangle`, or `diamond` |\n| `"Fix bug" at (0.1, 0.9)` on an Eisenhower with a task list | Valid scatter point \u2014 but you probably wanted a list in a cell | Add `style: table` and use `Q2: "Fix bug"` instead |\n| `Q1: "item"` on a 3\xD73 template | `Q` shorthand is parsed as a point line \u2014 silently dropped | Use `cell (col, row) label: "item"` for 3\xD73 grids |\n\n---\n\n## 15. Grammar (EBNF)\n\n```text\ndocument = header directive*\n\nheader = "matrix" ( template-name | mode-header | title )? NEWLINE\ntemplate-name = "eisenhower"|"impact-effort"|"rice"|"bcg"|"ansoff"|"johari"|"9-box"|"risk-matrix"\nmode-header = ( "heatmap" | "correlation" ) ( number "x" number )? title?\n | ( "sipoc" | "qfd" ) title?\ntitle = quoted-string | bare-text\n\ndirective = x-axis | y-axis | quadrant-dir | config-block\n | point | cell | q-short | rows-dir | cols-dir | grid-dir\n | style-dir | title-dir | axis-dir | margins-dir\n | sipoc-col | qfd-what | qfd-how | qfd-rel | qfd-roof | normalize-dir\n | comment | blank\n\n# SIPOC mode\nsipoc-col = ( "suppliers:" | "inputs:" | "process:" | "outputs:" | "customers:" )\n WS quoted-string ( "," quoted-string )* NEWLINE\n\n# QFD / House of Quality mode\nqfd-what = "what:" WS quoted-string WS "weight:" number NEWLINE\nqfd-how = "how:" WS quoted-string ( WS "dir:" ( "up" | "down" ) )? NEWLINE\nqfd-rel = "rel" WS "(" number "," number ")" ":" WS ( "9" | "3" | "1" ) NEWLINE # (row, col)\nqfd-roof = "roof" WS "(" number "," number ")" ":" WS ( "++" | "+" | "-" | "--" ) NEWLINE # (how, how)\nnormalize-dir = "normalize:" WS "true" NEWLINE\n\nx-axis = "x-axis:" WS axis-spec NEWLINE\ny-axis = "y-axis:" WS axis-spec NEWLINE\naxis-spec = text arrow text | text # plain text \u2192 high label only\narrow = "\u2192" | "->" | "\u2191" | "\u2190" | "<-" | "<" | "\u2193"\n\nquadrant-dir = "quadrant" WS "Q"? digit WS quoted-string ( WS "description:" quoted-string )? NEWLINE\n\nconfig-block = "config:" NEWLINE ( INDENT key ":" WS value NEWLINE )*\n\npoint = quoted-string WS "at" WS "(" number "," number ")" ( WS point-prop )* NEWLINE\npoint-prop = "size:" number\n | "category:" bareword\n | "color:" hex-color\n | "shape:" ( "circle"|"square"|"triangle"|"diamond" )\n | "highlight:" "true"\n | "note:" quoted-string\n | "label:" quoted-string\n\ncell = "cell" WS "(" digit "," digit ")" ( WS cell-prop )* NEWLINE\ncell-prop = "value:" number\n | "label:" quoted-string\n | "level:" ( "strong" | "medium" | "weak" )\n\nstyle-dir = "style:" WS "table" NEWLINE\nq-short = "Q" ( "1" | "2" | "3" | "4" ) ":" WS quoted-string NEWLINE # 2\xD72 only\n\nrows-dir = "rows:" WS label-list NEWLINE\ncols-dir = "cols:" WS label-list NEWLINE\ngrid-dir = "grid:" WS number "x" number NEWLINE\naxis-dir = "axis:" WS ( "off" | "on" | "auto" ) NEWLINE\nmargins-dir = "margins:" WS ( "true" | "false" | "on" | "1" ) NEWLINE\n\nlabel-list = "[" text ("," text)* "]" | text ("," text)*\nquoted-string = \'"\' any-char-but-quote* \'"\' | "\'" any-char-but-quote* "\'"\ncomment = "#" any NEWLINE\n```\n\nAuthoritative source: `src/diagrams/matrix/parser.ts`. If this diverges from the parser, the parser wins \u2014 please open an issue.\n\n---'
2916
3063
  },
2917
3064
  "orgchart": {
2918
3065
  "title": "Org chart",
@@ -2920,7 +3067,7 @@ var SYNTAX = {
2920
3067
  },
2921
3068
  "mindmap": {
2922
3069
  "title": "Mind map",
2923
- "content": '## 1. Your first mind map\n\nThe smallest useful mind map: a central topic with two branches, one with a sub-item.\n\n```\nmindmap\n\n# Team retrospective\n\n## What went well\n- Clear sprint goals\n- Good test coverage\n\n## What to improve\n- Slower PR reviews\n - Add a review SLA\n```\n\nFour rules cover 80% of usage:\n\n1. Start with an optional `mindmap` keyword on its own line, then a blank line.\n2. The root is the single `#` heading \u2014 exactly one is allowed.\n3. Use `##`, `###`, and deeper headings to set branch depth. Heading level equals tree depth.\n4. Use `-`, `*`, or `+` bullets to add sub-items under any heading. Each 2-space indent adds one more depth level.\n\n> Comments are not supported. Use `%%` directives (before the `#` root) for configuration only.\n\n---\n\n## 2. Headings and depth\n\nHeading 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).\n\n```\nmindmap\n\n# Root\n## Branch A \u2190 depth 1\n### Sub-branch \u2190 depth 2\n#### Leaf \u2190 depth 3\n## Branch B\n```\n\nHeadings can jump levels \u2014 `####` after `##` is valid and produces a node at depth 3. The tree depth is relative to the root, not to the previous heading.\n\n---\n\n## 3. Bullets\n\nBullets 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.\n\n```\n## Risks\n- Technical complexity \u2190 depth 2 (one level under ## Risks)\n - Legacy integrations \u2190 depth 3 (2 spaces indent)\n - Auth service \u2190 depth 4 (4 spaces indent)\n- Team availability \u2190 depth 2 again\n```\n\n```\nmindmap\n\n# Book outline\n\n## Chapter 1 \u2014 Introduction\n- Why this matters\n - Historical context\n - Current state\n- What you will learn\n\n## Chapter 2 \u2014 Core concepts\n- Concept A\n - Definition\n - Examples\n- Concept B\n - Definition\n - Worked example\n - Step-by-step walkthrough\n```\n\n---\n\n## 4. Inline formatting\n\nNode labels support a subset of Markdown inline formatting. The parser tokenizes labels at parse time; the renderer uses the tokens to emit styled text.\n\n| Syntax | Effect | Example |\n|---|---|---|\n| `**text**` | Bold | `**Critical path**` |\n| `*text*` | Italic | `*optional*` |\n| `` `code` `` | Monospace code | `` `npm install` `` |\n| `[text](url)` | Link | `[RFC 7519](https://tools.ietf.org/html/rfc7519)` |\n| `[ ] item` | Unchecked task | `[ ] Write tests` |\n| `[x] item` | Checked task | `[x] Design review` |\n\nThe checkbox must be at the very start of the label (before any other text). Inline formatting can be nested: `**[bold link](url)**`.\n\n```\nmindmap\n\n# Sprint 24 review\n\n## Completed\n- [x] **Auth redesign** \u2014 JWT + refresh tokens\n- [x] API rate limiting \\`per-user\\`\n- [x] [Error budget dashboard](https://metrics.example.com)\n\n## In progress\n- [ ] *Mobile push notifications*\n - [ ] iOS APNs integration\n - [ ] Android FCM setup\n\n## Blocked\n- [ ] **Payment webhook** \u2014 waiting on Stripe team\n - *Escalated to account manager*\n```\n\n---\n\n## 5. Layout styles\n\nThe `%% style:` directive selects the layout algorithm. Place it before the `#` root heading.\n\n| Style | Layout | Best for |\n|---|---|---|\n| `map` (default) | Radial \u2014 branches spread in all directions from the center | Brainstorming, concept maps, free-form exploration |\n| `logic-right` | Horizontal tree \u2014 all branches extend to the right | Structured outlines, hierarchies, sequential breakdowns |\n\n```\n%% style: map\n%% style: logic-right\n```\n\n**`map`** (default) \u2014 radial layout, branches spread in all directions from the center. Best for brainstorming and concept maps.\n\n```\nmindmap\n\n# Machine learning\n\n## Supervised\n### Classification\n- Decision tree\n- SVM\n- Neural net\n### Regression\n- Linear\n- Gradient boosting\n\n## Unsupervised\n### Clustering\n- K-means\n- DBSCAN\n### Reduction\n- PCA\n- t-SNE\n\n## Reinforcement\n- Q-learning\n- Policy gradient\n```\n\n**`logic-right`** \u2014 horizontal tree, all branches extend to the right. Best for structured outlines and sequential hierarchies.\n\n```\nmindmap\n%% style: logic-right\n\n# Machine learning\n\n## Supervised\n### Classification\n- Decision tree\n- SVM\n- Neural net\n### Regression\n- Linear\n- Gradient boosting\n\n## Unsupervised\n### Clustering\n- K-means\n- DBSCAN\n### Reduction\n- PCA\n- t-SNE\n\n## Reinforcement\n- Q-learning\n- Policy gradient\n```\n\n---\n\n## 6. Directives\n\nDirectives are `%%` lines placed **before** the `#` root heading. They configure the diagram globally.\n\n| Directive | Values | Default | Effect |\n|---|---|---|---|\n| `%% style: \u2026` | `map`, `logic-right` | `map` | Layout algorithm |\n| `%% theme: \u2026` | any string | (none) | Theme override passed to renderer |\n| `%% maxLabelWidth: \u2026` | integer 80\u20131000 | `240` | Max pixel width before label wraps |\n\n```\nmindmap\n%% style: logic-right\n%% maxLabelWidth: 320\n\n# Wide label root\n```\n\n```\nmindmap\n%% style: logic-right\n%% maxLabelWidth: 200\n\n# Schematex features\n\n## DSL-first design\n- One keyword per diagram\n- AI-friendly syntax\n- CJK support\n\n## Zero dependencies\n- Hand-written parser\n- No D3, no dagre\n- ~KB-level bundle\n\n## Standards-compliant\n- IEEE for logic gates\n- IEC for circuits\n- McGoldrick for genograms\n```\n\n---\n\n## 7. Labels & comments\n\n- **Root title:** the text after `#` on the root heading line.\n- **Branch labels:** the text after `##`, `###`, etc.\n- **Bullet labels:** the text after the `- ` / `* ` / `+ ` marker.\n- **Inline formatting:** `**bold**`, `*italic*`, `` `code` ``, `[text](url)`, `[ ]` / `[x]`.\n- **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).\n\n---\n\n## 8. Reserved words & escaping\n\n**Reserved at document start:** `mindmap` (optional keyword) and `%%` (directive prefix).\n\n**Reserved as root:** exactly one `#` heading; a second `#` heading throws a parse error.\n\n**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.\n\n**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 `\\[` \u2014 the backslash escapes the bracket.\n\n---\n\n## 9. Common mistakes\n\n| You wrote | Parser says | Fix |\n|---|---|---|\n| Two `#` headings | `Error: multiple # center nodes not allowed` | Use exactly one `#` heading as the root |\n| `##Branch` (no space after `##`) | Line is not recognized as a heading; silently skipped | Always put a space: `## Branch` |\n| Bullet indented 3 spaces | Depth = `lastHeadingDepth + 1 + floor(3/2) = lastHeadingDepth + 2` \u2014 may create an unexpected level | Use multiples of 2 spaces: 0, 2, 4, 6\u2026 |\n| `%% style: radial` | Unknown value silently ignored; layout stays `map` | Use `map` or `logic-right` |\n| `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 |\n| `[ ]text` (no space after bracket) | Checkbox not recognized; rendered as literal `[ ]text` | `[ ] text` \u2014 space required after the closing bracket |\n\n---\n\n## 10. Grammar (EBNF)\n\n```text\ndocument = ("mindmap" NEWLINE)? (blank | directive)* node*\n\ndirective = "%%" WS key ":" WS value NEWLINE\nkey = "style" | "theme" | "maxlabelwidth"\n\nnode = heading | bullet\nheading = INDENT? "#"+ SPACE label NEWLINE\nbullet = SPACE* bullet-marker SPACE label NEWLINE\nbullet-marker = "-" | "*" | "+"\n\nlabel = inline-token*\ninline-token = checkbox\n | "**" inline-token* "**"\n | "*" inline-token* "*"\n | "`" code-text "`"\n | "[" inline-token* "]" "(" url ")"\n | plain-text\n\ncheckbox = "[ ]" SPACE | "[x]" SPACE | "[X]" SPACE\n\nINDENT = WS* %% headings may have leading whitespace (ignored)\nSPACE = " " | "\\t"\n```\n\n**Depth rules:**\n- Heading `#` \u2192 depth 0 (root)\n- Heading `##` \u2192 depth 1, `###` \u2192 depth 2, etc.\n- Bullet at `n` leading spaces \u2192 depth = `lastHeadingDepth + 1 + floor(n / 2)`\n\nAuthoritative source: `src/diagrams/mindmap/parser.ts`. If this diverges from the parser, the parser wins \u2014 please open an issue.\n\n---'
3070
+ "content": '## 1. Your first mind map\n\nThe smallest useful mind map: a central topic with two branches, one with a sub-item.\n\n```\nmindmap\n\n# Team retrospective\n\n## What went well\n- Clear sprint goals\n- Good test coverage\n\n## What to improve\n- Slower PR reviews\n - Add a review SLA\n```\n\nFour rules cover 80% of usage:\n\n1. Start with an optional `mindmap` keyword on its own line, then a blank line.\n2. The root is the single `#` heading \u2014 exactly one is allowed.\n3. Use `##`, `###`, and deeper headings to set branch depth. Heading level equals tree depth.\n4. Use `-`, `*`, or `+` bullets to add sub-items under any heading. Each 2-space indent adds one more depth level.\n\n> Comments are not supported. Use `%%` directives (before the `#` root) for configuration only.\n\n---\n\n## 2. Headings and depth\n\nHeading 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).\n\n```\nmindmap\n\n# Root\n## Branch A \u2190 depth 1\n### Sub-branch \u2190 depth 2\n#### Leaf \u2190 depth 3\n## Branch B\n```\n\nHeadings can jump levels \u2014 `####` after `##` is valid and produces a node at depth 3. The tree depth is relative to the root, not to the previous heading.\n\n---\n\n## 3. Bullets\n\nBullets 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.\n\n```\n## Risks\n- Technical complexity \u2190 depth 2 (one level under ## Risks)\n - Legacy integrations \u2190 depth 3 (2 spaces indent)\n - Auth service \u2190 depth 4 (4 spaces indent)\n- Team availability \u2190 depth 2 again\n```\n\n```\nmindmap\n\n# Book outline\n\n## Chapter 1 \u2014 Introduction\n- Why this matters\n - Historical context\n - Current state\n- What you will learn\n\n## Chapter 2 \u2014 Core concepts\n- Concept A\n - Definition\n - Examples\n- Concept B\n - Definition\n - Worked example\n - Step-by-step walkthrough\n```\n\n---\n\n## 4. Inline formatting\n\nNode labels support a subset of Markdown inline formatting. The parser tokenizes labels at parse time; the renderer uses the tokens to emit styled text.\n\n| Syntax | Effect | Example |\n|---|---|---|\n| `**text**` | Bold | `**Critical path**` |\n| `*text*` | Italic | `*optional*` |\n| `` `code` `` | Monospace code | `` `npm install` `` |\n| `[text](url)` | Link | `[RFC 7519](https://tools.ietf.org/html/rfc7519)` |\n| `[ ] item` | Unchecked task | `[ ] Write tests` |\n| `[x] item` | Checked task | `[x] Design review` |\n\nThe checkbox must be at the very start of the label (before any other text). Inline formatting can be nested: `**[bold link](url)**`.\n\n```\nmindmap\n\n# Sprint 24 review\n\n## Completed\n- [x] **Auth redesign** \u2014 JWT + refresh tokens\n- [x] API rate limiting \\`per-user\\`\n- [x] [Error budget dashboard](https://metrics.example.com)\n\n## In progress\n- [ ] *Mobile push notifications*\n - [ ] iOS APNs integration\n - [ ] Android FCM setup\n\n## Blocked\n- [ ] **Payment webhook** \u2014 waiting on Stripe team\n - *Escalated to account manager*\n```\n\n---\n\n## 5. Layout styles\n\nThe `%% style:` directive selects the layout algorithm. Place it before the `#` root heading.\n\n| Style | Layout | Best for |\n|---|---|---|\n| `map` (default) | Radial \u2014 branches spread in all directions from the center | Brainstorming, concept maps, free-form exploration |\n| `logic-right` | Horizontal tree \u2014 all branches extend to the right | Structured outlines, hierarchies, sequential breakdowns |\n| `futureswheel` | Concentric rings \u2014 the root at the hub, each heading level on its own ring | Foresight, consequence mapping, structured brainstorming |\n| `driver` | Horizontal tree \u2014 aim on the left flowing right through drivers to change ideas | Improvement programs, aim \u2192 driver \u2192 action breakdowns |\n\n```\n%% style: map\n%% style: logic-right\n%% style: futureswheel\n%% style: driver\n```\n\n**`map`** (default) \u2014 radial layout, branches spread in all directions from the center. Best for brainstorming and concept maps.\n\n```\nmindmap\n\n# Machine learning\n\n## Supervised\n### Classification\n- Decision tree\n- SVM\n- Neural net\n### Regression\n- Linear\n- Gradient boosting\n\n## Unsupervised\n### Clustering\n- K-means\n- DBSCAN\n### Reduction\n- PCA\n- t-SNE\n\n## Reinforcement\n- Q-learning\n- Policy gradient\n```\n\n**`logic-right`** \u2014 horizontal tree, all branches extend to the right. Best for structured outlines and sequential hierarchies.\n\n```\nmindmap\n%% style: logic-right\n\n# Machine learning\n\n## Supervised\n### Classification\n- Decision tree\n- SVM\n- Neural net\n### Regression\n- Linear\n- Gradient boosting\n\n## Unsupervised\n### Clustering\n- K-means\n- DBSCAN\n### Reduction\n- PCA\n- t-SNE\n\n## Reinforcement\n- Q-learning\n- Policy gradient\n```\n\n**`futureswheel`** \u2014 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.\n\n```\nmindmap\n%% style: futureswheel\n\n# Remote work becomes default\n\n## Less commuting\n- Lower carbon emissions\n- Cheaper city living\n\n## Distributed teams\n- Async communication norms\n- Global hiring pools\n\n## Empty offices\n- Commercial real estate slump\n- Repurposed to housing\n```\n\n**`driver`** \u2014 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.\n\n```\nmindmap\n%% style: driver\n\n# Reduce 30-day readmissions\n\n## Reliable discharge process\n- Teach-back at bedside\n- Med reconciliation\n\n## Timely follow-up\n- Appointment within 7 days\n- Post-discharge phone call\n```\n\n---\n\n## 6. Directives\n\nDirectives are `%%` lines placed **before** the `#` root heading. They configure the diagram globally.\n\n| Directive | Values | Default | Effect |\n|---|---|---|---|\n| `%% style: \u2026` | `map`, `logic-right`, `futureswheel`, `driver` | `map` | Layout algorithm |\n| `%% theme: \u2026` | any string | (none) | Theme override passed to renderer |\n| `%% maxLabelWidth: \u2026` | integer 80\u20131000 | `240` | Max pixel width before label wraps |\n\n```\nmindmap\n%% style: logic-right\n%% maxLabelWidth: 320\n\n# Wide label root\n```\n\n```\nmindmap\n%% style: logic-right\n%% maxLabelWidth: 200\n\n# Schematex features\n\n## DSL-first design\n- One keyword per diagram\n- AI-friendly syntax\n- CJK support\n\n## Zero dependencies\n- Hand-written parser\n- No D3, no dagre\n- ~KB-level bundle\n\n## Standards-compliant\n- IEEE for logic gates\n- IEC for circuits\n- McGoldrick for genograms\n```\n\n---\n\n## 7. Labels & comments\n\n- **Root title:** the text after `#` on the root heading line.\n- **Branch labels:** the text after `##`, `###`, etc.\n- **Bullet labels:** the text after the `- ` / `* ` / `+ ` marker.\n- **Inline formatting:** `**bold**`, `*italic*`, `` `code` ``, `[text](url)`, `[ ]` / `[x]`.\n- **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).\n\n---\n\n## 8. Reserved words & escaping\n\n**Reserved at document start:** `mindmap` (optional keyword) and `%%` (directive prefix).\n\n**Reserved as root:** exactly one `#` heading; a second `#` heading throws a parse error.\n\n**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.\n\n**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 `\\[` \u2014 the backslash escapes the bracket.\n\n---\n\n## 9. Common mistakes\n\n| You wrote | Parser says | Fix |\n|---|---|---|\n| Two `#` headings | `Error: multiple # center nodes not allowed` | Use exactly one `#` heading as the root |\n| `##Branch` (no space after `##`) | Line is not recognized as a heading; silently skipped | Always put a space: `## Branch` |\n| Bullet indented 3 spaces | Depth = `lastHeadingDepth + 1 + floor(3/2) = lastHeadingDepth + 2` \u2014 may create an unexpected level | Use multiples of 2 spaces: 0, 2, 4, 6\u2026 |\n| `%% style: radial` | Unknown value silently ignored; layout stays `map` | Use `map`, `logic-right`, `futureswheel`, or `driver` |\n| `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 |\n| `[ ]text` (no space after bracket) | Checkbox not recognized; rendered as literal `[ ]text` | `[ ] text` \u2014 space required after the closing bracket |\n\n---\n\n## 10. Grammar (EBNF)\n\n```text\ndocument = ("mindmap" NEWLINE)? (blank | directive)* node*\n\ndirective = "%%" WS key ":" WS value NEWLINE\nkey = "style" | "theme" | "maxlabelwidth"\n\nnode = heading | bullet\nheading = INDENT? "#"+ SPACE label NEWLINE\nbullet = SPACE* bullet-marker SPACE label NEWLINE\nbullet-marker = "-" | "*" | "+"\n\nlabel = inline-token*\ninline-token = checkbox\n | "**" inline-token* "**"\n | "*" inline-token* "*"\n | "`" code-text "`"\n | "[" inline-token* "]" "(" url ")"\n | plain-text\n\ncheckbox = "[ ]" SPACE | "[x]" SPACE | "[X]" SPACE\n\nINDENT = WS* %% headings may have leading whitespace (ignored)\nSPACE = " " | "\\t"\n```\n\n**Depth rules:**\n- Heading `#` \u2192 depth 0 (root)\n- Heading `##` \u2192 depth 1, `###` \u2192 depth 2, etc.\n- Bullet at `n` leading spaces \u2192 depth = `lastHeadingDepth + 1 + floor(n / 2)`\n\nAuthoritative source: `src/diagrams/mindmap/parser.ts`. If this diverges from the parser, the parser wins \u2014 please open an issue.\n\n---'
2924
3071
  },
2925
3072
  "timeline": {
2926
3073
  "title": "Timeline diagram",
@@ -3706,5 +3853,5 @@ function repairHint(type) {
3706
3853
  }
3707
3854
 
3708
3855
  export { DIAGRAM_REGISTRY, DIAGRAM_SINCE, getAllDiagramTypes, getDiagramMeta, getDiagramSince, getExamples, getSyntax, listDiagrams, renderDsl, resolveDiagramType, validateDsl };
3709
- //# sourceMappingURL=chunk-X4P4HKHP.js.map
3710
- //# sourceMappingURL=chunk-X4P4HKHP.js.map
3856
+ //# sourceMappingURL=chunk-IFXHIYZE.js.map
3857
+ //# sourceMappingURL=chunk-IFXHIYZE.js.map