schematex 0.5.2 → 0.6.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 (174) hide show
  1. package/README.md +75 -1
  2. package/dist/ai/ai-sdk.cjs +22 -22
  3. package/dist/ai/ai-sdk.d.cts +3 -3
  4. package/dist/ai/ai-sdk.d.ts +3 -3
  5. package/dist/ai/ai-sdk.js +17 -17
  6. package/dist/ai/index.cjs +34 -26
  7. package/dist/ai/index.d.cts +4 -4
  8. package/dist/ai/index.d.ts +4 -4
  9. package/dist/ai/index.js +17 -17
  10. package/dist/{api-Cr_MxttI.d.ts → api-XWHHAhQI.d.ts} +3 -40
  11. package/dist/{api-C5SECOxZ.d.cts → api-qVDutqXH.d.cts} +3 -40
  12. package/dist/browser.cjs +23 -23
  13. package/dist/browser.d.cts +3 -3
  14. package/dist/browser.d.ts +3 -3
  15. package/dist/browser.js +17 -17
  16. package/dist/{chunk-4QPDZJAL.cjs → chunk-25ZON47K.cjs} +11484 -9030
  17. package/dist/chunk-25ZON47K.cjs.map +1 -0
  18. package/dist/{chunk-N7W5KZK7.cjs → chunk-2L4YXZAZ.cjs} +4 -4
  19. package/dist/{chunk-N7W5KZK7.cjs.map → chunk-2L4YXZAZ.cjs.map} +1 -1
  20. package/dist/{chunk-ZYRCPSBU.js → chunk-3IE7KZY4.js} +4 -4
  21. package/dist/{chunk-ZYRCPSBU.js.map → chunk-3IE7KZY4.js.map} +1 -1
  22. package/dist/{chunk-ZX74KJPM.js → chunk-3VB5AT4R.js} +3 -3
  23. package/dist/{chunk-ZX74KJPM.js.map → chunk-3VB5AT4R.js.map} +1 -1
  24. package/dist/{chunk-S2KJRHDZ.cjs → chunk-4UFR2LB6.cjs} +13 -13
  25. package/dist/{chunk-S2KJRHDZ.cjs.map → chunk-4UFR2LB6.cjs.map} +1 -1
  26. package/dist/{chunk-QTNPMIO2.cjs → chunk-6JI6FWLZ.cjs} +341 -35
  27. package/dist/chunk-6JI6FWLZ.cjs.map +1 -0
  28. package/dist/{chunk-OK5ZS3LU.js → chunk-7AFW2J6J.js} +11445 -8998
  29. package/dist/chunk-7AFW2J6J.js.map +1 -0
  30. package/dist/{chunk-UFTYX73U.js → chunk-7F76AWOI.js} +339 -35
  31. package/dist/chunk-7F76AWOI.js.map +1 -0
  32. package/dist/{chunk-BW4KGTV7.cjs → chunk-C5C5EF3W.cjs} +4 -4
  33. package/dist/{chunk-BW4KGTV7.cjs.map → chunk-C5C5EF3W.cjs.map} +1 -1
  34. package/dist/{chunk-VJGMEGMR.js → chunk-DOK7LKLO.js} +125 -13
  35. package/dist/chunk-DOK7LKLO.js.map +1 -0
  36. package/dist/{chunk-3M6WB62Y.cjs → chunk-ECD5XHBM.cjs} +139 -7
  37. package/dist/chunk-ECD5XHBM.cjs.map +1 -0
  38. package/dist/{chunk-L3CTXXVZ.js → chunk-FBS3PACU.js} +3 -3
  39. package/dist/{chunk-L3CTXXVZ.js.map → chunk-FBS3PACU.js.map} +1 -1
  40. package/dist/{chunk-QUKVGHN4.cjs → chunk-FKJBXGWP.cjs} +4 -4
  41. package/dist/{chunk-QUKVGHN4.cjs.map → chunk-FKJBXGWP.cjs.map} +1 -1
  42. package/dist/{chunk-SZK376QB.js → chunk-H4MT5TJP.js} +3 -3
  43. package/dist/{chunk-SZK376QB.js.map → chunk-H4MT5TJP.js.map} +1 -1
  44. package/dist/{chunk-3M6T7KB4.js → chunk-HAZALB7U.js} +3 -3
  45. package/dist/{chunk-3M6T7KB4.js.map → chunk-HAZALB7U.js.map} +1 -1
  46. package/dist/{chunk-VFZOPRQP.cjs → chunk-HWVBHU3O.cjs} +5 -5
  47. package/dist/{chunk-VFZOPRQP.cjs.map → chunk-HWVBHU3O.cjs.map} +1 -1
  48. package/dist/{chunk-D7EHZFK4.cjs → chunk-L7POWM5B.cjs} +138 -2
  49. package/dist/chunk-L7POWM5B.cjs.map +1 -0
  50. package/dist/{chunk-6OSUNBZY.js → chunk-LGABFD3L.js} +135 -7
  51. package/dist/chunk-LGABFD3L.js.map +1 -0
  52. package/dist/{chunk-2VNMKOUO.js → chunk-LRI4RH2N.js} +135 -3
  53. package/dist/chunk-LRI4RH2N.js.map +1 -0
  54. package/dist/{chunk-VDSYMSUY.js → chunk-MVIEIKOI.js} +3 -3
  55. package/dist/{chunk-VDSYMSUY.js.map → chunk-MVIEIKOI.js.map} +1 -1
  56. package/dist/{chunk-ZL5RB4UV.js → chunk-N5B242WY.js} +3 -3
  57. package/dist/{chunk-ZL5RB4UV.js.map → chunk-N5B242WY.js.map} +1 -1
  58. package/dist/{chunk-VKPCR7BG.js → chunk-P63S7P6N.js} +770 -12
  59. package/dist/chunk-P63S7P6N.js.map +1 -0
  60. package/dist/{chunk-YLEVMOK2.cjs → chunk-R66QG3XT.cjs} +5 -4
  61. package/dist/{chunk-YLEVMOK2.cjs.map → chunk-R66QG3XT.cjs.map} +1 -1
  62. package/dist/{chunk-I55HO32M.js → chunk-RJMCWT7Z.js} +3 -3
  63. package/dist/{chunk-I55HO32M.js.map → chunk-RJMCWT7Z.js.map} +1 -1
  64. package/dist/{chunk-TZTCIAYW.cjs → chunk-S3RMAXH5.cjs} +137 -25
  65. package/dist/chunk-S3RMAXH5.cjs.map +1 -0
  66. package/dist/{chunk-IBRW3UOA.js → chunk-TWLKXV2O.js} +3 -3
  67. package/dist/{chunk-IBRW3UOA.js.map → chunk-TWLKXV2O.js.map} +1 -1
  68. package/dist/{chunk-VCH7RI5H.js → chunk-UWA5MWCI.js} +4 -4
  69. package/dist/chunk-UWA5MWCI.js.map +1 -0
  70. package/dist/{chunk-RYVV5UVI.cjs → chunk-V4GILQR6.cjs} +4 -4
  71. package/dist/{chunk-RYVV5UVI.cjs.map → chunk-V4GILQR6.cjs.map} +1 -1
  72. package/dist/{chunk-QMTWG6JL.cjs → chunk-V4RO5KYY.cjs} +6 -4
  73. package/dist/chunk-V4RO5KYY.cjs.map +1 -0
  74. package/dist/{chunk-VZ5LDNHK.cjs → chunk-VTSH4YPT.cjs} +12 -12
  75. package/dist/{chunk-VZ5LDNHK.cjs.map → chunk-VTSH4YPT.cjs.map} +1 -1
  76. package/dist/{chunk-UUBNQV2T.cjs → chunk-WQDIZH2Z.cjs} +12 -12
  77. package/dist/{chunk-UUBNQV2T.cjs.map → chunk-WQDIZH2Z.cjs.map} +1 -1
  78. package/dist/{chunk-EGSUMHCS.cjs → chunk-YB4XJY5L.cjs} +12 -12
  79. package/dist/{chunk-EGSUMHCS.cjs.map → chunk-YB4XJY5L.cjs.map} +1 -1
  80. package/dist/{chunk-JHDR56XO.js → chunk-YS6CGUNH.js} +3 -3
  81. package/dist/{chunk-JHDR56XO.js.map → chunk-YS6CGUNH.js.map} +1 -1
  82. package/dist/{chunk-HK56GQQP.cjs → chunk-YVDUEUFV.cjs} +773 -13
  83. package/dist/chunk-YVDUEUFV.cjs.map +1 -0
  84. package/dist/{types-WTr9W5Ud.d.ts → diagnostics-DRxhodP6.d.cts} +74 -2
  85. package/dist/{types-WTr9W5Ud.d.cts → diagnostics-DRxhodP6.d.ts} +74 -2
  86. package/dist/diagrams/blockdiagram/index.d.cts +1 -1
  87. package/dist/diagrams/blockdiagram/index.d.ts +1 -1
  88. package/dist/diagrams/circuit/index.cjs +8 -8
  89. package/dist/diagrams/circuit/index.d.cts +1 -1
  90. package/dist/diagrams/circuit/index.d.ts +1 -1
  91. package/dist/diagrams/circuit/index.js +2 -2
  92. package/dist/diagrams/ecomap/index.cjs +7 -7
  93. package/dist/diagrams/ecomap/index.d.cts +1 -1
  94. package/dist/diagrams/ecomap/index.d.ts +1 -1
  95. package/dist/diagrams/ecomap/index.js +2 -2
  96. package/dist/diagrams/entity/index.cjs +6 -6
  97. package/dist/diagrams/entity/index.d.cts +1 -1
  98. package/dist/diagrams/entity/index.d.ts +1 -1
  99. package/dist/diagrams/entity/index.js +2 -2
  100. package/dist/diagrams/fishbone/index.cjs +8 -8
  101. package/dist/diagrams/fishbone/index.d.cts +1 -1
  102. package/dist/diagrams/fishbone/index.d.ts +1 -1
  103. package/dist/diagrams/fishbone/index.js +2 -2
  104. package/dist/diagrams/flowchart/index.cjs +8 -8
  105. package/dist/diagrams/flowchart/index.d.cts +2 -2
  106. package/dist/diagrams/flowchart/index.d.ts +2 -2
  107. package/dist/diagrams/flowchart/index.js +2 -2
  108. package/dist/diagrams/genogram/index.cjs +9 -9
  109. package/dist/diagrams/genogram/index.d.cts +1 -1
  110. package/dist/diagrams/genogram/index.d.ts +1 -1
  111. package/dist/diagrams/genogram/index.js +2 -2
  112. package/dist/diagrams/ladder/index.cjs +6 -6
  113. package/dist/diagrams/ladder/index.d.cts +1 -1
  114. package/dist/diagrams/ladder/index.d.ts +1 -1
  115. package/dist/diagrams/ladder/index.js +2 -2
  116. package/dist/diagrams/logic/index.cjs +6 -6
  117. package/dist/diagrams/logic/index.d.cts +1 -1
  118. package/dist/diagrams/logic/index.d.ts +1 -1
  119. package/dist/diagrams/logic/index.js +2 -2
  120. package/dist/diagrams/orgchart/index.cjs +7 -7
  121. package/dist/diagrams/orgchart/index.d.cts +1 -1
  122. package/dist/diagrams/orgchart/index.d.ts +1 -1
  123. package/dist/diagrams/orgchart/index.js +2 -2
  124. package/dist/diagrams/pedigree/index.cjs +7 -7
  125. package/dist/diagrams/pedigree/index.d.cts +1 -1
  126. package/dist/diagrams/pedigree/index.d.ts +1 -1
  127. package/dist/diagrams/pedigree/index.js +2 -2
  128. package/dist/diagrams/phylo/index.cjs +7 -7
  129. package/dist/diagrams/phylo/index.d.cts +1 -1
  130. package/dist/diagrams/phylo/index.d.ts +1 -1
  131. package/dist/diagrams/phylo/index.js +2 -2
  132. package/dist/diagrams/sld/index.cjs +14 -6
  133. package/dist/diagrams/sld/index.d.cts +14 -2
  134. package/dist/diagrams/sld/index.d.ts +14 -2
  135. package/dist/diagrams/sld/index.js +2 -2
  136. package/dist/diagrams/sociogram/index.cjs +6 -6
  137. package/dist/diagrams/sociogram/index.d.cts +1 -1
  138. package/dist/diagrams/sociogram/index.d.ts +1 -1
  139. package/dist/diagrams/sociogram/index.js +2 -2
  140. package/dist/diagrams/timing/index.d.cts +1 -1
  141. package/dist/diagrams/timing/index.d.ts +1 -1
  142. package/dist/diagrams/venn/index.cjs +9 -9
  143. package/dist/diagrams/venn/index.d.cts +1 -1
  144. package/dist/diagrams/venn/index.d.ts +1 -1
  145. package/dist/diagrams/venn/index.js +2 -2
  146. package/dist/{index-C30zQWZI.d.ts → index-BRIkOPnd.d.cts} +60 -2
  147. package/dist/{index-BD2yDfQM.d.cts → index-C7SN-FB3.d.ts} +60 -2
  148. package/dist/index.cjs +342 -54
  149. package/dist/index.cjs.map +1 -1
  150. package/dist/index.d.cts +38 -4
  151. package/dist/index.d.ts +38 -4
  152. package/dist/index.js +283 -16
  153. package/dist/index.js.map +1 -1
  154. package/dist/react.cjs +17 -17
  155. package/dist/react.d.cts +2 -2
  156. package/dist/react.d.ts +2 -2
  157. package/dist/react.js +16 -16
  158. package/dist/{tools-BHWaJPOl.d.ts → tools-BVeUNdsU.d.ts} +12 -4
  159. package/dist/{tools-DlpuE76u.d.cts → tools-DdhP1kWY.d.cts} +12 -4
  160. package/package.json +1 -1
  161. package/dist/chunk-2VNMKOUO.js.map +0 -1
  162. package/dist/chunk-3M6WB62Y.cjs.map +0 -1
  163. package/dist/chunk-4QPDZJAL.cjs.map +0 -1
  164. package/dist/chunk-6OSUNBZY.js.map +0 -1
  165. package/dist/chunk-D7EHZFK4.cjs.map +0 -1
  166. package/dist/chunk-HK56GQQP.cjs.map +0 -1
  167. package/dist/chunk-OK5ZS3LU.js.map +0 -1
  168. package/dist/chunk-QMTWG6JL.cjs.map +0 -1
  169. package/dist/chunk-QTNPMIO2.cjs.map +0 -1
  170. package/dist/chunk-TZTCIAYW.cjs.map +0 -1
  171. package/dist/chunk-UFTYX73U.js.map +0 -1
  172. package/dist/chunk-VCH7RI5H.js.map +0 -1
  173. package/dist/chunk-VJGMEGMR.js.map +0 -1
  174. package/dist/chunk-VKPCR7BG.js.map +0 -1
@@ -1,4 +1,4 @@
1
- import { parseResult, renderResult } from './chunk-OK5ZS3LU.js';
1
+ import { parseResult, renderResult } from './chunk-7AFW2J6J.js';
2
2
 
3
3
  // src/ai/registry.ts
4
4
  var DIAGRAM_REGISTRY = [
@@ -209,7 +209,7 @@ var DIAGRAM_REGISTRY = [
209
209
  {
210
210
  type: "usecase",
211
211
  name: "UML use case diagram",
212
- tagline: "UML 2.5 use case \u2014 actors, use cases, subject boundary, include/extend/generalization.",
212
+ tagline: "UML 2.5.1 use case diagram \u2014 captures what a system does and for whom: actors, use cases, a subject boundary, and include/extend/generalization.",
213
213
  useWhen: "Use for software-engineering requirements and scope diagrams \u2014 'what does this system do, and for whom'. Actors (stick figures, or `(external)` rectangles for other systems) sit outside a subject boundary; use cases are ellipses inside it. `--` association, `..>` \xABinclude\xBB (source includes target), `<..` \xABextend\xBB (left extends right, with optional `[condition]` and extension points), `--|>` generalization (hollow triangle to parent, between actors or between use cases). Accepts a PlantUML-style inline form (`:Actor:`, `(Use case)`, `as ID`). Distinct from `state` (intra-object behavior, not system scope), `flowchart` (no actor/subject/include-extend semantics), and `bpmn` (how a process executes, not what a system offers).",
214
214
  cluster: "behavior-modeling",
215
215
  standard: "OMG UML 2.5.1 \xA718 visual subset; see 29-USECASE-STANDARD.md",
@@ -218,12 +218,31 @@ var DIAGRAM_REGISTRY = [
218
218
  {
219
219
  type: "sequence",
220
220
  name: "UML sequence diagram",
221
- tagline: "UML 2.5.1 \xA717 interaction \u2014 lifelines, messages over time, activations, and combined fragments.",
221
+ tagline: "UML 2.5.1 \xA717 interaction diagram \u2014 shows how participants exchange messages over time (who calls whom, in what order): lifelines, activations, and all twelve combined fragments.",
222
222
  useWhen: "Use for time-ordered interactions between participants \u2014 API call flows, auth handshakes, distributed protocols, object collaborations, 'who calls whom in what order'. Lifelines run top\u2192bottom; messages run left\u2192right: `->` synchronous (filled head), `->>` asynchronous (open head), `-->` reply (dashed), `-x` lost, `o->` found. `+`/`-` suffixes open/close activation bars; `*Target` creates a participant and `destroy` ends one. All twelve UML combined fragments \u2014 `alt`/`opt`/`loop`/`par`/`break`/`critical`/`seq`/`strict`/`neg`/`ignore`/`consider`/`assert` \u2014 plus `ref` interaction-use frames. Participant kinds `actor`/`boundary`/`control`/`entity`/`database` render their UML/Jacobson symbols; `\xABstereotype\xBB` overrides the label. Distinct from `usecase` (system scope, not message order), `state` (one object's modes, not inter-object messages), `bpmn` (organisational process), and `flowchart` (no lifelines/time axis).",
223
223
  cluster: "behavior-modeling",
224
224
  standard: "OMG UML 2.5.1 \xA717 (Interactions); see 33-SEQUENCE-STANDARD.md",
225
225
  syntaxKey: "sequence"
226
226
  },
227
+ {
228
+ type: "petri",
229
+ name: "Petri net",
230
+ tagline: "Place/transition net that computes the dynamics \u2014 enabled transitions and token firing, not just shapes.",
231
+ useWhen: "Use whenever the user mentions 'Petri net', 'place/transition net', 'token', 'marking', 'concurrency model', 'mutual exclusion', 'producer/consumer', or wants to model concurrent resource flow / synchronisation. Declare `place <id> *<tokens>` (circles holding tokens), `transition <id>` (bars \u2014 add `timed rate: <\u03BB>` for a GSPN timed transition), and bipartite arcs: `->` standard, `-o` inhibitor (enabled only while the place is empty), `--` read/test, `=>` reset. Arc weight via `weight: n` or `*n`; place limit via `capacity: n`. The engine validates the bipartite structure, applies a `fire: T1, T2` sequence to the initial marking, and highlights which transitions are *enabled* in the result. `layout: lr|tb`. Distinct from `state` (one active state, not a token distribution), `sfc` (a restricted PLC Petri net), `bpmn` (organisational process), and `flowchart` (single thread, no concurrency or marking).",
232
+ cluster: "concurrency",
233
+ standard: "Murata 1989 + ISO/IEC 15909-1 (place/transition net); see 34-PETRINET-STANDARD.md",
234
+ syntaxKey: "petri"
235
+ },
236
+ // ── Network / infrastructure ─────────────────────────────────
237
+ {
238
+ type: "network",
239
+ name: "Network topology",
240
+ tagline: "IT / CCTV network topology with Cisco-convention device icons, typed links, subnets/VLANs, and topology-correct layout.",
241
+ useWhen: "Use whenever the user mentions 'network diagram', 'network topology', 'infrastructure diagram', a 'c\xE1maras / CCTV / camera network', a LAN/WAN/data-center diagram, or wants to lay out routers, switches, firewalls, access points, servers, IP cameras, NVRs, etc. Declare typed devices `<kind> <id> \"label\"` (router, switch, l3switch, firewall, loadbalancer, ap, wlc, gateway, modem, ids, proxy, vpngw, server, serverfarm, pc, laptop, mobile, ipphone, printer, storage, camera (with `type: fixed|bullet|dome|ptz|turret`), nvr, dvr, poeswitch, encoder, monitor, internet, wan, pstn, cloud, lan) and connect with `a -- b` (undirected), `a -> b` (directed), or `a == b` (LAG). After `:` add a link spec: a link type (fiber/wireless/serial/poe/vpn/lag), `trunk`/`access` mode, `vlan: 10,20`, a speed like `1G`/`10G`, and `port: Gi0/1>eth0`. Group devices in nested boundaries: `site`/`rack` (physical) and `subnet`/`vlan`/`zone`/`dmz` (logical) blocks `{ \u2026 }`. Choose `layout: tiered` (default; band by `tier: edge|core|distribution|access`), `tree`, `star`, `ring`, `bus`, `mesh`, `spine-leaf` (declare `spines:`/`leaves:` and the mesh is auto-generated), or `manual`. The engine never drops a device/port/link, and validates VLAN range 1\u20134094 plus device IP-in-subnet-CIDR. Distinct from `flowchart` (no device icons/topology), `c4` (software containers, not physical devices), and `sld` (electrical single-line, not data network).",
242
+ cluster: "network-infrastructure",
243
+ standard: "Cisco-convention topology icons + hierarchical/spine-leaf models + ANSI/TIA-606 + ONVIF; see 35-NETWORK-STANDARD.md",
244
+ syntaxKey: "network"
245
+ },
227
246
  // ── Research / evidence synthesis ────────────────────────────
228
247
  {
229
248
  type: "prisma",
@@ -293,6 +312,54 @@ var DIAGRAM_REGISTRY = [
293
312
  syntaxKey: "timeline"
294
313
  }
295
314
  ];
315
+ var DIAGRAM_SINCE = {
316
+ // 0.1.0 — initial release (2026-03-15)
317
+ genogram: "0.1.0",
318
+ ecomap: "0.1.0",
319
+ pedigree: "0.1.0",
320
+ phylo: "0.1.0",
321
+ sociogram: "0.1.0",
322
+ logic: "0.1.0",
323
+ circuit: "0.1.0",
324
+ timing: "0.1.0",
325
+ blockdiagram: "0.1.0",
326
+ ladder: "0.1.0",
327
+ sld: "0.1.0",
328
+ entity: "0.1.0",
329
+ fishbone: "0.1.0",
330
+ // 0.1.1 (2026-04-18)
331
+ flowchart: "0.1.1",
332
+ venn: "0.1.1",
333
+ matrix: "0.1.1",
334
+ mindmap: "0.1.1",
335
+ orgchart: "0.1.1",
336
+ // 0.2.0 (2026-04-20)
337
+ timeline: "0.2.0",
338
+ decisiontree: "0.2.0",
339
+ // 0.3.0 (2026-04-29)
340
+ state: "0.3.0",
341
+ pid: "0.3.0",
342
+ // 0.4.0 (2026-05-05)
343
+ erd: "0.4.0",
344
+ breadboard: "0.4.0",
345
+ bpmn: "0.4.0",
346
+ fbd: "0.4.0",
347
+ sfc: "0.4.0",
348
+ // 0.4.3 (2026-05-16) — usecase
349
+ usecase: "0.4.3",
350
+ // 0.5.0 (2026-05-19)
351
+ prisma: "0.5.0",
352
+ // 0.5.1
353
+ sequence: "0.5.1",
354
+ // 0.6.0 — upcoming (committed post-0.5.2, not yet released)
355
+ pert: "0.6.0",
356
+ petri: "0.6.0",
357
+ network: "0.6.0"
358
+ };
359
+ function getDiagramSince(type) {
360
+ const resolved = resolveDiagramType(type);
361
+ return resolved ? DIAGRAM_SINCE[resolved] : void 0;
362
+ }
296
363
  var TYPE_ALIASES = {
297
364
  block: "blockdiagram",
298
365
  "entity-structure": "entity",
@@ -315,6 +382,25 @@ function getAllDiagramTypes() {
315
382
 
316
383
  // src/ai/_generated.ts
317
384
  var EXAMPLES = [
385
+ {
386
+ "slug": "block-nested-feedback",
387
+ "diagram": "block",
388
+ "title": "Nested feedback loops",
389
+ "description": "A multi-loop control block diagram \u2014 an inner loop closes G2\xB7G3 around sensor H1, an outer loop closes the whole forward path around sensor H2 routed over the top, and two summing junctions combine the reference with each feedback signal. The kind of cascaded architecture a generic flowchart can't lay out.",
390
+ "standard": "Ogata control systems",
391
+ "tags": [
392
+ "nested-loops",
393
+ "cascade",
394
+ "feedback",
395
+ "summing-junction",
396
+ "sensor",
397
+ "signal-flow"
398
+ ],
399
+ "complexity": 3,
400
+ "featured": false,
401
+ "dsl": 'blockdiagram "Nested Feedback Loops"\nG1 = block("G1(s)") [role: plant]\nG2 = block("G2(s)") [role: plant]\nG3 = block("G3(s)") [role: plant]\nH1 = block("H1(s)") [role: sensor]\nH2 = block("H2(s)") [role: sensor, route: above]\ns1 = sum(+R, -h2)\ns2 = sum(+a, -h1)\nin -> s1 ["R(s)"]\ns1 -> G1 -> s2\ns2 -> G2 -> G3\nG3 -> out ["Y(s)"]\nG2 -> H1\nH1 -> s2\nG3 -> H2\nH2 -> s1',
402
+ "notes": '## Scenario\n\nCascaded and nested control loops are everywhere in real plants \u2014 an inner velocity loop inside an outer position loop, an inner current loop inside a speed loop. Textbooks (Ogata, Franklin) draw them as block diagrams with multiple summing junctions and feedback paths that cross over the forward chain. Schematex routes those crossings automatically from a signal-flow description.\n\n## Annotation key\n\n- `block("label") [role: plant|sensor]` \u2014 transfer-function block; `role` drives the visual styling so plants and sensors read differently\n- `sum(+R, -h2)` \u2014 a summing junction with explicit signs: adds reference `R`, subtracts the outer feedback `h2`\n- `route: above` on `H2` \u2014 forces the outer feedback sensor to route over the top of the diagram, keeping the long return path clear of the forward chain\n- `s1 -> G1 -> s2` \u2014 chained connections; signal flows left to right through the forward path\n\n## How to read\n\nThe inner loop is `G2 \u2192 G3 \u2192 H1 \u2192 s2`: sensor H1 feeds the plant output back to the second summing junction, closing a loop around `G2\xB7G3`. The outer loop is wider \u2014 `G3 \u2192 H2 \u2192 s1` \u2014 where H2 (routed above) returns the final output to the first summing junction. The reference `R(s)` enters at `s1`, is corrected by both feedback signals in turn, and the cascaded plants drive `Y(s)`. Two nested loops, one signal-flow spec.'
403
+ },
318
404
  {
319
405
  "slug": "block-pid-loop",
320
406
  "diagram": "block",
@@ -455,6 +541,24 @@ If the LED doesn't light up, three things to check, in order: LED polarity (the
455
541
  "dsl": 'breadboard\nboard: half\ntitle: "HC-SR04 distance sensor + Arduino Uno"\n\nparts\n uno: mcu uno @beside-left\n s1: sensor hcsr04 @8a\n\nwires\n s1:VCC --red-- @+t1\n s1:GND --black-- @-t1\n s1:TRIG --yellow-- @9c\n s1:ECHO --green-- @10c\n uno:5V --red-- @+t1\n uno:GND --black-- @-t1\n uno:D9 --yellow-- @9c\n uno:D10 --green-- @10c',
456
542
  "notes": "The HC-SR04 is the most common ultrasonic distance sensor in beginner Arduino kits. Four pins, four wires, no driver IC needed. The sensor module is rendered as a blue PCB tile with its four pin labels (VCC / TRIG / ECHO / GND) sitting above the breadboard rows where they plug in.\n\nThe TRIG line is a digital output from the Arduino \u2014 pulse it high for 10 \xB5s and the sensor fires an ultrasonic chirp. The ECHO line is a digital input \u2014 its high-time, in microseconds, is twice the round-trip distance divided by the speed of sound. Conventional wiring uses yellow for TRIG and green for ECHO so the two signals are visually distinguishable, though the colors carry no electrical meaning."
457
543
  },
544
+ {
545
+ "slug": "circuit-bridge-rectifier-supply",
546
+ "diagram": "circuit",
547
+ "title": "Bridge rectifier power supply",
548
+ "description": "Four-diode full-wave bridge rectifier with a smoothing capacitor and load resistor, written as a compact electrical netlist.",
549
+ "standard": "IEEE 315",
550
+ "tags": [
551
+ "rectifier",
552
+ "diode",
553
+ "power-supply",
554
+ "capacitor",
555
+ "netlist"
556
+ ],
557
+ "complexity": 2,
558
+ "featured": false,
559
+ "dsl": 'circuit "Bridge Rectifier Supply" netlist\nV1 ac1 ac2 12Vac\nD1 ac1 vout 1N4007\nD2 ac2 vout 1N4007\nD3 0 ac1 1N4007\nD4 0 ac2 1N4007\nC1 vout 0 470u\nRload vout 0 1k',
560
+ "notes": "## Scenario\n\nA technician needs a quick schematic for the textbook AC-to-DC rectifier: a transformer secondary, four rectifier diodes, a reservoir capacitor, and a load. The netlist form keeps the topology precise while the renderer chooses the schematic placement.\n\n## Annotation key\n\n- `D1` to `D4` are ordinary diode components inferred from the `D` prefix.\n- `0` is the canonical ground net.\n- `C1` smooths the rectified output across `vout` and ground.\n\n## How to read\n\nDuring each half-cycle, two diodes conduct and steer current into `vout` with the same polarity. The capacitor charges near the peak and supplies the load between peaks, reducing ripple at `Rload`."
561
+ },
458
562
  {
459
563
  "slug": "circuit-ce-amplifier",
460
564
  "diagram": "circuit",
@@ -473,6 +577,24 @@ If the LED doesn't light up, three things to check, in order: LED polarity (the
473
577
  "dsl": 'circuit "CE Amp (netlist)" netlist\nV1 vcc 0 9V\nRc vcc c 2.2k\nRb vcc b 100k\nQ1 c b e npn\nRe e 0 1k',
474
578
  "notes": '## Scenario\n\nThe NPN common-emitter amplifier is the first transistor circuit every electronics student builds. Schematex renders it from a five-line SPICE netlist \u2014 the same format used in LTspice, ngspice, and Cadence \u2014 with automatic component placement and rail routing, so the diagram matches the hand-drawn textbook version without any manual layout.\n\n## Annotation key\n\n- `circuit "..." netlist` \u2014 enables netlist parsing mode (SPICE syntax)\n- `V1 vcc 0 9V` \u2014 voltage source named V1, positive terminal at node `vcc`, negative at `0` (ground), value 9V\n- `Rc vcc c 2.2k` \u2014 resistor Rc between nodes `vcc` and `c` with value 2.2 k\u03A9\n- `Rb vcc b 100k` \u2014 base bias resistor between `vcc` and node `b`\n- `Q1 c b e npn` \u2014 NPN BJT transistor: collector=c, base=b, emitter=e\n- `Re e 0 1k` \u2014 emitter degeneration resistor between node `e` and ground\n\n## How to read\n\nThe supply rail (Vcc = 9 V) connects to the top of both resistors. Rc is the collector load; the output signal is taken across it. Rb biases the base into the active region. Re provides emitter degeneration for stability. Q1 amplifies a small base current into a large collector-to-emitter current flow. The auto-layout positions Vcc at top, ground at bottom, and Q1 in the center.'
475
579
  },
580
+ {
581
+ "slug": "circuit-opamp-inverting-amplifier",
582
+ "diagram": "circuit",
583
+ "title": "Inverting op-amp amplifier",
584
+ "description": "A practical inverting op-amp stage rendered from a SPICE-style netlist with feedback and input resistors, split supply rails, and a labelled output node.",
585
+ "standard": "IEEE 315",
586
+ "tags": [
587
+ "op-amp",
588
+ "amplifier",
589
+ "feedback",
590
+ "netlist",
591
+ "analog"
592
+ ],
593
+ "complexity": 2,
594
+ "featured": false,
595
+ "dsl": 'circuit "Inverting Op-Amp (netlist)" netlist\nVin vin 0 AC1V\nRin vin inv 10k\nRf out inv 100k\nU1 noninv inv out type=opamp label="U1 TL072"\nRbias noninv 0 10k\nVp vcc 0 +12V\nVn vee 0 -12V',
596
+ "notes": "## Scenario\n\nAn analog engineer documents a standard inverting gain stage before moving to PCB layout. The two resistors make the gain visible in the schematic: `Rf / Rin = 10`, so the output is an inverted, amplified version of the input.\n\n## Annotation key\n\n- `type=opamp` selects the three-pin op-amp symbol in netlist mode.\n- `Rin` and `Rf` are ordinary SPICE-style resistor lines.\n- `noninv`, `inv`, and `out` are named nets, not pixel coordinates.\n\n## How to read\n\nThe input signal enters through `Rin` into the inverting input. `Rf` feeds the output back to the same node, closing the negative feedback loop. The non-inverting input is tied to ground through `Rbias`, giving the stage a stable reference."
597
+ },
476
598
  {
477
599
  "slug": "decisiontree-investment-analysis",
478
600
  "diagram": "decisiontree",
@@ -491,6 +613,24 @@ If the LED doesn't light up, three things to check, in order: LED polarity (the
491
613
  "dsl": 'decisiontree:decision "Platform Vendor Choice"\n\ndecision "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',
492
614
  "notes": '## Scenario\n\nA CTO is deciding how to stand up a new data-platform layer: build internally, buy managed SaaS, or take a hybrid. Each path has outcomes with probabilities and net-value estimates. The decision-analysis tree rolls the expected value back to the decision node so the optimal branch is identified automatically.\n\n## Annotation key\n\n- `decision "\u2026"` \u2014 actor chooses a branch\n- `chance "\u2026"` \u2014 nature chooses; `prob N` on each child (must sum to 1)\n- `end "\u2026" payoff=N` \u2014 terminal payoff\n- `choice "label"` \u2014 label on an outgoing decision branch\n- EV rollback: chance = probability-weighted sum; decision = max child EV\n\n## How to read\n\nEvaluate each branch\'s expected value. Build in-house: 0.6 \xD7 900k + 0.4 \xD7 150k = **600k**. Managed SaaS: flat **500k**. Hybrid: 0.5 \xD7 700k + 0.5 \xD7 300k = **500k**. Under these estimates the optimal branch is *Build in-house* \u2014 the parser flags it on render. The chart\'s real value is in forcing stakeholders to state probabilities explicitly; sensitivity to them is where the interesting argument happens.'
493
615
  },
616
+ {
617
+ "slug": "decisiontree-iris-cart-ml",
618
+ "diagram": "decisiontree",
619
+ "title": "Iris CART decision tree",
620
+ "description": "Machine-learning decision tree for the classic Iris classifier with split thresholds, sample counts, Gini impurity, and class leaves.",
621
+ "standard": "CART / scikit-learn plot_tree convention",
622
+ "tags": [
623
+ "decisiontree",
624
+ "ml",
625
+ "cart",
626
+ "iris",
627
+ "gini"
628
+ ],
629
+ "complexity": 3,
630
+ "featured": false,
631
+ "dsl": 'decisiontree:ml "Iris CART Classifier"\ndirection: top-down\nimpurity: gini\nclasses: setosa, versicolor, virginica\n\nsplit "Petal length <= 2.45" feature=petal_length op="<=" threshold=2.45 samples=150 gini=0.667\n true leaf "Setosa" class=setosa value=50 gini=0.0 samples=50\n false split "Petal width <= 1.75" feature=petal_width op="<=" threshold=1.75 samples=100 gini=0.5\n true split "Petal length <= 4.95" feature=petal_length op="<=" threshold=4.95 samples=54 gini=0.168\n true leaf "Versicolor" class=versicolor value=49 gini=0.04 samples=49\n false leaf "Virginica" class=virginica value=5 gini=0.32 samples=5\n false leaf "Virginica" class=virginica value=45 gini=0.0 samples=45',
632
+ "notes": "## Scenario\n\nModel explainability often starts with a small tree. This Iris example mirrors the structure exported by CART tools: feature thresholds inside split nodes, impurity metrics, samples, and class-labelled leaves.\n\n## Annotation key\n\n- `decisiontree:ml` selects ML mode.\n- `split` nodes carry `feature`, `threshold`, `samples`, and `gini`.\n- `true` and `false` prefixes define the branch semantics."
633
+ },
494
634
  {
495
635
  "slug": "decisiontree-support-triage",
496
636
  "diagram": "decisiontree",
@@ -561,6 +701,24 @@ If the LED doesn't light up, three things to check, in order: LED polarity (the
561
701
  "dsl": 'ecomap "Marcus, age 15"\n center: client [label: "Marcus"]\n mom [label: "Mother", category: family]\n dad [label: "Father (divorced)", category: family]\n school [label: "East High School", category: education]\n coach [label: "Soccer Coach", category: community]\n peers [label: "Soccer team", category: community]\n therapist [label: "Ms. Chen", category: mental-health]\n mom === client [label: "primary caregiver"]\n dad --- client [label: "EOW weekends"]\n school === client\n coach --> client [label: "mentor"]\n peers === client\n therapist <-> client [label: "weekly"]',
562
702
  "notes": "## Scenario\n\nA school-based counselor draws this ecomap during an initial intake session with Marcus, a 15-year-old referred for behavioral issues. The diagram takes under five minutes and immediately shows where Marcus's support is concentrated (peers, soccer team) and where it's fragile (divorced father, every-other-weekend contact).\n\n## Annotation key\n\n- `center: client` \u2014 the identified young person\n- `===` \u2014 strong connection; `---` \u2014 tenuous connection\n- `-->` \u2014 mentor/support flows toward client\n- `<->` \u2014 reciprocal therapeutic alliance\n- `EOW weekends` \u2014 edge label capturing the visitation schedule\n\n## How to read\n\nMarcus's strongest systems are his mother (primary caregiver), school, and soccer peers \u2014 all strong ties (===). His father is a tenuous connection (---). The therapist and soccer coach have supportive directional relationships pointing toward Marcus. The soccer team appears to be the central protective factor in this adolescent's life."
563
703
  },
704
+ {
705
+ "slug": "entity-family-office-trust",
706
+ "diagram": "entity",
707
+ "title": "Family office trust structure",
708
+ "description": "Estate-planning entity structure with a grantor, irrevocable trust, trustee LLC, holding company, investment LLCs, and beneficiary distributions.",
709
+ "standard": "Trust and estate planning conventions",
710
+ "tags": [
711
+ "entity",
712
+ "trust",
713
+ "family-office",
714
+ "estate-planning",
715
+ "distributions"
716
+ ],
717
+ "complexity": 3,
718
+ "featured": false,
719
+ "dsl": 'entity-structure "Family Office Trust"\njurisdiction SD "South Dakota" [color: "#2563eb"]\njurisdiction DE "Delaware" [color: "#059669"]\n\nentity grantor "Grantor" individual [role: "Settlor"]\nentity trust1 "Irrevocable Dynasty Trust" trust@SD [est: "2021-04-01", note: "GST exempt"]\nentity trustee "Trustee Services LLC" llc@SD [role: "Directed trustee"]\nentity holdco "Family Holdings, Inc." corp@DE\nentity realestate "Real Estate LLC" llc@DE\nentity markets "Marketable Securities LLC" llc@DE\nentity beneficiary "Children Trust Share" trust@SD\n\ngrantor -> trust1 : contribution\ntrustee ==> trust1 [label: "administrative control"]\ntrust1 -> holdco : 100%\nholdco -> realestate : 100%\nholdco -> markets : 100%\ntrust1 --> beneficiary [label: "distributions"]',
720
+ "notes": "## Scenario\n\nEstate attorneys often need to explain control, economics, and fiduciary roles on one page. This structure separates voting/control relationships from ownership and beneficiary distributions.\n\n## Annotation key\n\n- `trust` renders as an ellipse, distinct from corporate entities.\n- `==>` marks voting or control without economic ownership.\n- `-->` marks distributions to beneficiaries."
721
+ },
564
722
  {
565
723
  "slug": "entity-holding-company",
566
724
  "diagram": "entity",
@@ -634,6 +792,24 @@ If the LED doesn't light up, three things to check, in order: LED polarity (the
634
792
  "dsl": 'erd\ntitle: "E-commerce Schema"\ndirection: LR\n\ntable Customer {\n customer_id int PK\n email varchar UK\n name varchar\n created_at timestamp\n}\n\ntable Address {\n address_id int PK\n customer_id int FK -> Customer.customer_id\n line1 varchar\n city varchar\n zip varchar\n}\n\ntable Category {\n category_id int PK\n name varchar\n}\n\ntable Product {\n product_id int PK\n category_id int FK -> Category.category_id\n name varchar\n price decimal\n}\n\ntable Order {\n order_id int PK\n customer_id int FK -> Customer.customer_id\n placed_at timestamp\n status varchar\n}\n\ntable OrderLine {\n order_id int PK FK -> Order.order_id\n line_no int PK\n product_id int FK -> Product.product_id\n qty int\n price decimal\n}\n\nref Address.customer_id many-mandatory -- one-mandatory Customer.customer_id\nref Order.customer_id many-mandatory -- one-mandatory Customer.customer_id : "places"\nref Product.category_id many-mandatory -- one-mandatory Category.category_id\nref OrderLine.order_id many-mandatory -- one-mandatory Order.order_id\nref OrderLine.product_id many-mandatory -- one-mandatory Product.product_id',
635
793
  "notes": 'A six-table schema spanning four FK-depth layers: Category and Customer at the root; Product and Address at depth one; Order at depth two; OrderLine at depth three with a composite PK `(order_id, line_no)`. Demonstrates layered LR routing and labelled relationships ("places").'
636
794
  },
795
+ {
796
+ "slug": "erd-saas-rbac-multitenant",
797
+ "diagram": "erd",
798
+ "title": "Multi-tenant SaaS RBAC schema",
799
+ "description": "Crow's-foot ERD for tenants, users, memberships, roles, permissions, invitations, and audit events in a SaaS workspace product.",
800
+ "standard": "Crow's-foot ERD",
801
+ "tags": [
802
+ "erd",
803
+ "saas",
804
+ "multitenant",
805
+ "rbac",
806
+ "schema"
807
+ ],
808
+ "complexity": 3,
809
+ "featured": false,
810
+ "dsl": 'erd\ntitle: "SaaS RBAC"\ndirection: LR\n\ntable Tenant {\n tenant_id uuid PK\n name varchar NN\n plan varchar\n}\ntable User {\n user_id uuid PK\n email varchar UK\n name varchar\n}\ntable Membership {\n tenant_id uuid PK FK -> Tenant.tenant_id\n user_id uuid PK FK -> User.user_id\n role_id uuid FK -> Role.role_id\n status varchar NN\n}\ntable Role {\n role_id uuid PK\n tenant_id uuid FK -> Tenant.tenant_id\n name varchar NN\n}\ntable Permission {\n permission_id uuid PK\n key varchar UK\n}\ntable RolePermission {\n role_id uuid PK FK -> Role.role_id\n permission_id uuid PK FK -> Permission.permission_id\n}\ntable Invitation {\n invitation_id uuid PK\n tenant_id uuid FK -> Tenant.tenant_id\n email varchar NN\n}\ntable AuditEvent {\n event_id uuid PK\n tenant_id uuid FK -> Tenant.tenant_id\n actor_user_id uuid FK -> User.user_id\n}\n\nref Membership.tenant_id many-mandatory -- one-mandatory Tenant.tenant_id : "belongs to"\nref Membership.user_id many-mandatory -- one-mandatory User.user_id : "joins"\nref Membership.role_id many-optional -- one-mandatory Role.role_id : "has role"\nref Role.tenant_id many-mandatory -- one-mandatory Tenant.tenant_id : "scoped to"\nref RolePermission.role_id many-mandatory -- one-mandatory Role.role_id\nref RolePermission.permission_id many-mandatory -- one-mandatory Permission.permission_id\nref Invitation.tenant_id many-optional .. one-mandatory Tenant.tenant_id\nref AuditEvent.tenant_id many-mandatory -- one-mandatory Tenant.tenant_id\nref AuditEvent.actor_user_id many-optional .. one-optional User.user_id',
811
+ "notes": "## Scenario\n\nRBAC schemas are where generic ERD examples stop being useful. A SaaS product needs tenant scoping, composite keys on join tables, optional actor references for system events, and non-identifying edges for nullable audit metadata.\n\n## Annotation key\n\n- `Membership` and `RolePermission` use composite primary keys.\n- `..` marks a non-identifying relationship.\n- FK targets are declared inline and repeated as explicit `ref` lines for crow's-foot cardinality."
812
+ },
637
813
  {
638
814
  "slug": "erd-university-schema",
639
815
  "diagram": "erd",
@@ -711,6 +887,24 @@ If the LED doesn't light up, three things to check, in order: LED polarity (the
711
887
  "dsl": 'fbd "Tank Level Setpoint Limiter"\n\nvar DesiredSetpoint: real\nvar SafeSetpoint: real\nvar Alarm: bool\n\nnetwork 0 "Clamp setpoint to safe range":\n SafeSetpoint = LIMIT(MN: 0.0, IN: DesiredSetpoint, MX: 95.0)\n\nnetwork 1 "Alarm on out-of-range request":\n OutOfRange = OR(LT(DesiredSetpoint, 0.0), GT(DesiredSetpoint, 95.0))\n Alarm = MOVE(OutOfRange.OUT)',
712
888
  "notes": "When an operator types a tank-level setpoint into the HMI, you can't just trust the number. Maybe they meant to type 75 and hit 750. Maybe they typed -5 because they were copying from a spec sheet that used a different reference. Maybe the HMI's input field doesn't have validation and the value is whatever bit-pattern the OPC UA bridge happened to land on. The PLC always validates, and the canonical pattern is: clamp to a safe range, then raise an alarm if the requested value was out of range so a human knows to check.\n\n**Network 0 \u2014 LIMIT.** `SafeSetpoint = LIMIT(MN: 0.0, IN: DesiredSetpoint, MX: 95.0)` is the clamp. LIMIT takes three REAL inputs and returns a REAL: the value of `IN` if it's in [MN, MX], otherwise MN or MX. Both bounds are inline constants \u2014 they render as yellow boxed text at their ports, no wire needed. The downstream control loop reads `SafeSetpoint`, never `DesiredSetpoint` directly. Even if the operator's input is corrupted, the tank can never be commanded outside the physically safe range.\n\n**Network 1 \u2014 out-of-range alarm.** `OutOfRange = OR(LT(DesiredSetpoint, 0.0), GT(DesiredSetpoint, 95.0))` is an inline expression that nests two comparison blocks inside an OR. LT (less-than) returns BOOL when its first input is less than the second; GT (greater-than) is the opposite. The OR fires when *either* fires \u2014 the operator's value was either too low or too high. The `Alarm = MOVE(OutOfRange.OUT)` then drives whatever HMI alarm channel \u2014 a red banner, a Slack notification, a SCADA event log entry.\n\n**Two data types in one diagram.** Look at the wire colors in the rendered SVG: the wire from `DesiredSetpoint` into LIMIT.IN is REAL (orange \u2014 IEEE 754 floating-point); the wires from LT.OUT and GT.OUT into OR are BOOL (black). The LIMIT.OUT is also REAL (orange). One of the FBD engine's small but pleasant features: the renderer infers each wire's type from the source port and colors it accordingly, following the TIA Portal convention. If you accidentally wired an INT to a REAL port the colors would mismatch at the junction and you'd notice immediately.\n\n**Why not ladder?** Ladder logic excels at boolean signal routing \u2014 contacts in series and parallel feeding into output coils. It has zero affordance for REAL arithmetic and comparison; you'd write the LIMIT expression as a structured-text \"function block\" call inside a ladder rung, which kills the visual semantic. FBD makes the math first-class. For per-scan combinational logic involving any non-BOOL signal, FBD is what the IEC 61131-3 standard expects you to use."
713
889
  },
890
+ {
891
+ "slug": "fishbone-manufacturing-defect-6m",
892
+ "diagram": "fishbone",
893
+ "title": "Manufacturing defect 6M fishbone",
894
+ "description": "Ishikawa 6M root-cause analysis for a solder joint defect spike on an electronics assembly line.",
895
+ "standard": "Ishikawa 1968 / ISO 9001 CAPA",
896
+ "tags": [
897
+ "fishbone",
898
+ "6m",
899
+ "manufacturing",
900
+ "defect",
901
+ "capa"
902
+ ],
903
+ "complexity": 3,
904
+ "featured": false,
905
+ "dsl": 'fishbone "Solder Joint Defect Spike"\nconfig density = spacious\neffect "3.2% solder joint rejects"\n\ncategory man "Man"\ncategory machine "Machine"\ncategory material "Material"\ncategory method "Method"\ncategory measurement "Measurement"\ncategory environment "Environment"\n\nman : "New operators on night shift"\n - "Training checklist not signed"\nman : "Handover notes incomplete"\n\nmachine : "Reflow oven zone 4 drift"\n - "Thermocouple calibration overdue"\nmachine : "Stencil printer squeegee worn"\n\nmaterial : "Solder paste past floor life"\nmaterial : "PCB pads oxidized after storage"\n\nmethod : "Stencil aperture undersized"\nmethod : "Pick-and-place speed raised"\n\nmeasurement : "AOI false accept rate rising"\nmeasurement : "X-ray sampling reduced"\n\nenvironment : "Humidity above process window"\nenvironment : "ESD straps failing audit"',
906
+ "notes": "## Scenario\n\nAn electronics manufacturer opens a CAPA after solder rejects spike above the control limit. The 6M fishbone forces the team to inspect people, machines, material, method, measurement, and environment before committing to corrective action.\n\n## Annotation key\n\n- `config density = spacious` gives the dense six-category diagram breathing room.\n- Indented `-` lines are sub-causes under the preceding cause.\n- The 6M category names match the manufacturing convention quality teams expect."
907
+ },
714
908
  {
715
909
  "slug": "fishbone-website-traffic",
716
910
  "diagram": "fishbone",
@@ -853,6 +1047,24 @@ If the LED doesn't light up, three things to check, in order: LED polarity (the
853
1047
  "dsl": 'genogram "The Potter Family"\n fleamont [male, 1909, 1979, deceased]\n euphemia [female, 1920, 1979, deceased]\n fleamont -- euphemia\n james [male, 1960, 1981, deceased]\n mr_evans [male, 1925, deceased]\n mrs_evans [female, 1928, deceased]\n mr_evans -- mrs_evans\n lily [female, 1960, 1981, deceased]\n petunia [female, 1958]\n james -- lily "m. 1978"\n harry [male, 1980, index]\n petunia -- vernon [male, 1951]\n dudley [male, 1980]\n harry -cutoff- petunia\n harry -hostile- dudley\n harry -close- lily',
854
1048
  "notes": '## Scenario\n\nA teaching example for social work students learning genogram notation. The Potter family is fictional but emotionally rich \u2014 death years, a marriage date, cross-family emotional relationships, and three distinct relational patterns (cutoff, hostile, close) all in one diagram.\n\n## Annotation key\n\n- `[male/female, birth_year, death_year, deceased]` \u2014 person with death marker\n- `"m. 1978"` \u2014 marriage date label on the union line\n- `index` \u2014 marks Harry as the identified patient (proband)\n- `-cutoff-` \u2014 estrangement; drawn as two parallel bars across the relationship line\n- `-hostile-` \u2014 conflict; drawn as zigzag line\n- `-close-` \u2014 enmeshment/closeness; drawn as double parallel line\n\n## How to read\n\nRead each indented block as a family unit. James and Lily (index generation) both died in 1981. Harry\'s emotional world is defined by three relational lines: cutoff from Aunt Petunia, hostility toward cousin Dudley, and closeness to his deceased mother.'
855
1049
  },
1050
+ {
1051
+ "slug": "ladder-conveyor-interlock",
1052
+ "diagram": "ladder",
1053
+ "title": "Conveyor interlock chain",
1054
+ "description": "Three-rung ladder logic for a downstream-first conveyor start sequence with jam permissives and an alarm latch.",
1055
+ "standard": "IEC 61131-3",
1056
+ "tags": [
1057
+ "ladder",
1058
+ "conveyor",
1059
+ "interlock",
1060
+ "permissive",
1061
+ "alarm"
1062
+ ],
1063
+ "complexity": 3,
1064
+ "featured": false,
1065
+ "dsl": 'ladder "Conveyor Interlock Chain"\n\nrung 1 "Run downstream conveyor first":\n XIC(AUTO_MODE, "BIT 1.0", name="Auto mode")\n XIO(ESTOP_OK, "BIT 1.1", name="E-stop not tripped")\n XIO(CV2_JAM, "IN 2.2", name="Downstream jam clear")\n OTE(CV2_RUN, "OUT 4.1", name="Conveyor 2 run")\n\nrung 2 "Start upstream only when downstream is running":\n XIC(AUTO_MODE, "BIT 1.0", name="Auto mode")\n XIC(CV2_RUN, "OUT 4.1", name="CV2 running")\n XIO(CV1_JAM, "IN 2.1", name="Upstream jam clear")\n OTE(CV1_RUN, "OUT 4.0", name="Conveyor 1 run")\n\nrung 3 "Latch jam alarm":\n parallel:\n branch:\n XIC(CV1_JAM, "IN 2.1", name="CV1 jam")\n branch:\n XIC(CV2_JAM, "IN 2.2", name="CV2 jam")\n OTL(JAM_ALARM, "BIT 3.0", name="Jam alarm")',
1066
+ "notes": "## Scenario\n\nConveyors are started downstream-first so a running upstream belt never feeds material into a stopped downstream belt. This example shows the interlock directly in the rungs: conveyor 1 cannot run until conveyor 2 is already commanded on.\n\n## Annotation key\n\n- `XIC(CV2_RUN)` is the downstream permissive on the upstream conveyor rung.\n- `XIO(CV1_JAM)` and `XIO(CV2_JAM)` block the run command when a jam bit is true.\n- `OTL(JAM_ALARM)` latches the alarm so it remains visible after the jam sensor clears."
1067
+ },
856
1068
  {
857
1069
  "slug": "ladder-mode-selection",
858
1070
  "diagram": "ladder",
@@ -891,6 +1103,42 @@ If the LED doesn't light up, three things to check, in order: LED polarity (the
891
1103
  "dsl": 'ladder "Motor Start/Stop"\nrung 1 "Seal-in circuit":\n parallel:\n branch:\n XIC(START_PB, "IN 1.0", name="Start Button")\n branch:\n XIC(MOTOR_AUX, "BIT 3.0", name="Aux Contact")\n XIO(STOP_PB, "IN 1.1", name="Stop Button")\n OTE(MOTOR_CMD, "OUT 2.0", name="Motor Command")',
892
1104
  "notes": "## Scenario\n\nEvery controls engineer learns the three-wire motor start/stop circuit before writing their first PLC program. It appears verbatim in IEC 61131-3 training materials, Allen-Bradley certification exams, and factory acceptance tests worldwide. The seal-in contact latches the motor ON after the momentary start pushbutton is released \u2014 the fundamental pattern for any maintained-output logic.\n\n## Annotation key\n\n- `XIC(tag, address, name=...)` \u2014 Examine If Closed: contact passes power when the referenced bit is `1` (true)\n- `XIO(tag, address, name=...)` \u2014 Examine If Open: contact passes power when the referenced bit is `0` (false); normal for stop buttons wired N.C.\n- `OTE(tag, address, name=...)` \u2014 Output Energize: coil energizes the referenced bit when rung has power\n- `parallel: branch:` \u2014 models a parallel contact branch (logical OR)\n- The `MOTOR_AUX` contact in the parallel branch is the seal-in: once the motor output energizes, the aux contact closes and holds the rung true even after the START_PB releases\n\n## How to read\n\nThe rung reads left to right. Power flows if *either* the start button (XIC START_PB) *or* the aux contact (XIC MOTOR_AUX) is closed, *and* the stop button (XIO STOP_PB) is not pressed. When the output coil (OTE MOTOR_CMD) energizes the motor, it also drives the aux contact bit \u2014 latching the rung high. Pressing STOP breaks the series path and de-energizes the rung, dropping the motor and the seal-in simultaneously."
893
1105
  },
1106
+ {
1107
+ "slug": "ladder-tank-level-pump-control",
1108
+ "diagram": "ladder",
1109
+ "title": "Tank level pump control",
1110
+ "description": "Pump control ladder with low-level start, high-level stop, seal-in logic, and a run-delay timer.",
1111
+ "standard": "IEC 61131-3",
1112
+ "tags": [
1113
+ "ladder",
1114
+ "pump",
1115
+ "tank",
1116
+ "timer",
1117
+ "seal-in"
1118
+ ],
1119
+ "complexity": 3,
1120
+ "featured": false,
1121
+ "dsl": 'ladder "Tank Level Pump Control"\n\nrung 1 "Fill pump seal-in":\n parallel:\n branch:\n XIC(LOW_LEVEL, "IN 1.0", name="Low level")\n branch:\n XIC(PUMP_RUN, "OUT 2.0", name="Pump running")\n XIO(HIGH_LEVEL, "IN 1.1", name="High level")\n XIO(FAULT, "BIT 3.0", name="Fault clear")\n OTE(PUMP_RUN, "OUT 2.0", name="Fill pump")\n\nrung 2 "Run delay proves flow":\n XIC(PUMP_RUN, "OUT 2.0", name="Pump running")\n TON(FLOW_DELAY, IN=PUMP_RUN, PT=5000, name="Flow prove delay")\n\nrung 3 "No-flow alarm after delay":\n XIC(FLOW_DELAY, "TMR 1.0", name="Delay done")\n XIO(FLOW_OK, "IN 1.2", name="Flow switch")\n OTL(FLOW_ALARM, "BIT 3.1", name="No flow alarm")',
1122
+ "notes": "## Scenario\n\nA fill pump starts when the tank reaches low level, stays on through a seal-in path, and stops at the high-level switch. A timer allows a few seconds for the flow switch to prove flow before latching an alarm.\n\n## Annotation key\n\n- The `parallel:` block is the seal-in path.\n- `TON` is the on-delay timer used as a permissive before declaring no-flow.\n- `OTL` latches the alarm so an operator must acknowledge the failure."
1123
+ },
1124
+ {
1125
+ "slug": "logic-ansi-vs-iec-gate-gallery",
1126
+ "diagram": "logic",
1127
+ "title": "IEC logic gate gallery",
1128
+ "description": "Logic gate gallery using IEC 60617 rectangular symbols for AND, OR, XOR, NAND, NOR, NOT, and special-output buffers.",
1129
+ "standard": "IEC 60617-12",
1130
+ "tags": [
1131
+ "logic",
1132
+ "iec",
1133
+ "gate-gallery",
1134
+ "ansi-alternative",
1135
+ "symbols"
1136
+ ],
1137
+ "complexity": 2,
1138
+ "featured": false,
1139
+ "dsl": 'logic "IEC Gate Gallery" style: iec\ninput A, B, C, EN\noutput Y_and, Y_or, Y_xor, Y_nand, Y_nor, Y_not, Y_tri\nY_and = AND(A, B)\nY_or = OR(A, B)\nY_xor = XOR(A, B)\nY_nand = NAND(A, B)\nY_nor = NOR(A, B)\nY_not = NOT(C)\nY_tri = TRISTATE_BUF(A, EN)',
1140
+ "notes": "## Scenario\n\nANSI curved gates are common in US education, but IEC rectangular logic symbols are standard in many international and industrial documents. This example shows the same functional DSL rendered with the IEC style.\n\n## Annotation key\n\n- `style: iec` switches the symbol family for the whole diagram.\n- Gate definitions remain ordinary functional assignments.\n- The gallery includes both basic gates and a special-output buffer."
1141
+ },
894
1142
  {
895
1143
  "slug": "logic-full-adder",
896
1144
  "diagram": "logic",
@@ -1040,6 +1288,124 @@ If the LED doesn't light up, three things to check, in order: LED polarity (the
1040
1288
  "dsl": "mindmap\n\n# Q4 Company OKRs\n\n## Grow ARR 30%\n### Expand enterprise pipeline\n- 10 new qualified logos\n- Win rate \u2265 25%\n### Increase expansion\n- Net revenue retention \u2265 120%\n- Seat adoption +40%\n\n## Ship Platform v2\n### Core migration\n- 100% API coverage\n- Zero-downtime cutover\n### Developer experience\n- Sub-5-min quickstart\n- 95% doc satisfaction\n\n## Strengthen team\n### Hiring\n- 8 senior engineers\n- 2 staff PMs\n### Retention\n- Voluntary attrition < 5%\n- eNPS \u2265 40",
1041
1289
  "notes": "## Scenario\n\nThe chief of staff projects this during the Q4 all-hands. Three objectives radiate from the centre; every key result is a leaf with a specific number. The mindmap format reads fast \u2014 every person in the company can find their team's objective within three seconds \u2014 and it tolerates mid-quarter edits without disturbing other branches.\n\n## Annotation key\n\n- `#` \u2014 root; company-level frame\n- `##` \u2014 objective (qualitative direction)\n- `###` \u2014 key-result cluster\n- `-` bullets \u2014 specific measurable KRs\n\n## How to read\n\nThe root names the quarter. The three `##` branches are the objectives \u2014 the things that will be judged at the end of the quarter. Each `###` groups key results by theme; the bullets are the actual measurable targets. If a KR can't be reduced to a number, it probably belongs in a planning doc rather than on this mindmap."
1042
1290
  },
1291
+ {
1292
+ "slug": "network-boundaries",
1293
+ "diagram": "network",
1294
+ "title": "Physical and logical boundaries in one diagram",
1295
+ "description": "A branch-office topology nesting physical containers (a site holding an MDF rack) and logical overlays (a DMZ security zone and a CIDR subnet) \u2014 solid borders for physical, dashed tinted borders for logical \u2014 so the same devices read correctly in both the cabling and the addressing views.",
1296
+ "standard": "Cisco-convention topology icons + hierarchical campus model",
1297
+ "tags": [
1298
+ "network",
1299
+ "boundaries",
1300
+ "subnet",
1301
+ "vlan",
1302
+ "dmz",
1303
+ "rack",
1304
+ "security-zone"
1305
+ ],
1306
+ "complexity": 3,
1307
+ "featured": false,
1308
+ "dsl": 'network "Branch Office"\n layout: tiered\n internet net\n site hq "HQ Building" {\n rack mdf "MDF Rack" {\n firewall fw1 tier: edge\n l3switch core1 tier: core\n }\n }\n zone dmz "DMZ" {\n server web "Web Server"\n }\n subnet lan "10.0.10.0/24" {\n switch a1 tier: access\n pc u1 "User PC" ip: 10.0.10.50\n }\n net -- fw1 : wan\n fw1 -- web\n fw1 -- core1 : 10G\n core1 -- a1 : trunk vlan: 10\n a1 -- u1',
1309
+ "notes": "Real network documentation has to answer two different questions at once: *where is this box physically* (which building, which rack) and *what address space / security zone is it in*. Schematex draws both kinds of grouping and distinguishes them visually.\n\n**Physical containers are solid.** `site` and `rack` blocks get solid borders \u2014 the HQ building holds an MDF rack holding the firewall and core switch.\n\n**Logical overlays are dashed and tinted.** `zone` (the DMZ) and `subnet` (the `10.0.10.0/24` LAN) get dashed, tinted borders \u2014 they group by policy and addressing, not by location. The engine validates that `u1`'s `ip: 10.0.10.50` actually falls inside the subnet's CIDR.\n\n**Tiers still band the layout.** `tier: edge|core|access` keeps the hierarchical top-to-bottom ordering even inside the boundaries, so the diagram reads as a proper three-tier design."
1310
+ },
1311
+ {
1312
+ "slug": "network-cctv-camera-system",
1313
+ "diagram": "network",
1314
+ "title": "CCTV camera network topology",
1315
+ "description": "IP-video surveillance topology \u2014 Internet \u2192 perimeter firewall \u2192 core switch \u2192 PoE switches \u2192 dome/PTZ/bullet cameras, with the cameras isolated on their own 192.168.20.0/24 subnet and an NVR for recording.",
1316
+ "standard": "Cisco topology icons + IP/CCTV (ONVIF) conventions",
1317
+ "tags": [
1318
+ "cctv",
1319
+ "surveillance",
1320
+ "ip-camera",
1321
+ "nvr",
1322
+ "poe",
1323
+ "vlan",
1324
+ "subnet"
1325
+ ],
1326
+ "complexity": 3,
1327
+ "featured": true,
1328
+ "dsl": 'network "Acme HQ \u2014 CCTV"\n layout: tiered\n internet net "Internet"\n firewall fw1 "Perimeter FW" tier: edge\n l3switch core1 "Core SW" tier: core\n poeswitch poe1 "PoE Switch A" tier: access\n poeswitch poe2 "PoE Switch B" tier: access\n nvr nvr1 "Video Recorder"\n monitor wall1 "Guard Station"\n subnet cams "192.168.20.0/24" {\n camera cam1 "Lobby Dome" type: dome ip: 192.168.20.11\n camera cam2 "Gate PTZ" type: ptz ip: 192.168.20.12\n camera cam3 "Dock Bullet" type: bullet ip: 192.168.20.13\n poe1\n poe2\n }\n net -- fw1 : wan "ISP 1Gbps"\n fw1 -- core1 : fiber 10G\n core1 -- poe1 : trunk vlan: 20 1G\n core1 -- poe2 : trunk vlan: 20 1G\n core1 -- nvr1 : 1G\n core1 -- wall1\n poe1 -- cam1 : poe\n poe1 -- cam2 : poe\n poe2 -- cam3 : poe',
1329
+ "notes": "## Scenario\n\nA security integrator is documenting an IP-CCTV install for hand-off. The cameras need their own isolated subnet (so they can't route to the corporate LAN), Power-over-Ethernet from the access switches, and a single NVR recording everything. The diagram has to be **editable and printable** so it can be reused for the next building.\n\n## What the diagram shows\n\n- **The camera subnet** `192.168.20.0/24` is drawn as a dashed boundary enclosing the three cameras and both PoE switches \u2014 the cameras' IPs are validated against the CIDR, so a typo'd address is caught, not silently rendered.\n- **Camera body styles** are real silhouettes: `type: dome`, `type: ptz`, and `type: bullet` each render differently, the way an integrator's drawings distinguish them.\n- **PoE links** (`poe`) are drawn in green with a power tag \u2014 these carry both data and power to the cameras.\n- **The uplinks** are a trunk carrying VLAN 20 at 1G; the fiber backbone to the core is orange at 10G; the ISP hand-off is a serial/WAN circuit.\n\n## Annotation key\n\n| Element | Meaning |\n|---|---|\n| Dashed blue box | Logical subnet (label = CIDR) |\n| Green link + PoE | Power-over-Ethernet to a camera |\n| Orange link | Fiber uplink |\n| `Trunk \xB7 VLAN 20 \xB7 1G` | 802.1Q trunk carrying VLAN 20 at 1 Gbps |\n\nSwap `layout: tiered` for `layout: tree` to get a pure hierarchy, or add more `subnet` / `vlan` boundaries as the install grows."
1330
+ },
1331
+ {
1332
+ "slug": "network-enterprise-campus",
1333
+ "diagram": "network",
1334
+ "title": "Three-tier enterprise campus network",
1335
+ "description": "Classic Cisco hierarchical campus \u2014 Internet/WAN edge, redundant core switches with a LAG uplink, a distribution layer, and an access layer feeding a server farm. The canonical core/distribution/access model.",
1336
+ "standard": "Cisco hierarchical internetworking model (core/distribution/access)",
1337
+ "tags": [
1338
+ "campus",
1339
+ "three-tier",
1340
+ "core",
1341
+ "distribution",
1342
+ "access",
1343
+ "lag",
1344
+ "enterprise"
1345
+ ],
1346
+ "complexity": 3,
1347
+ "featured": false,
1348
+ "dsl": 'network "Driscoll Campus"\n layout: tiered\n internet inet\n cloud wan "WAN"\n firewall fw1 "Core Firewall" tier: edge\n router er1 "Edge Rtr 1" tier: edge\n l3switch cs1 "Core SW 1" tier: core\n l3switch cs2 "Core SW 2" tier: core\n switch d1 "Dist A" tier: distribution\n switch d2 "Dist B" tier: distribution\n serverfarm farm "Server Farm" count: 4\n a1 a2 a3 : switch tier: access\n inet -- fw1\n wan -- er1 : serial\n fw1 -- cs1 : 10G\n er1 -- cs2\n cs1 == cs2 : lag 40G\n cs1 -- d1\n cs2 -- d2\n cs1 -- farm : trunk vlan: 100\n d1 -- a1\n d2 -- a2\n d2 -- a3',
1349
+ "notes": '## Scenario\n\nA network engineer is drawing the as-built for a campus that follows Cisco\'s three-tier hierarchical model. The `tier:` attribute on each device drives the banding, so the diagram lands in the canonical **edge \u2192 core \u2192 distribution \u2192 access** rows automatically \u2014 no manual placement.\n\n## What the diagram shows\n\n- **`tier:` banding** \u2014 `edge`, `core`, `distribution`, `access` place devices in fixed rows top-to-bottom. Endpoints (the server farm) hang below their switch.\n- **The redundant core** \u2014 `cs1 == cs2 : lag 40G` is the EtherChannel uplink, drawn as a double line with a 40G label (`==` is shorthand for a LAG link).\n- **Shorthand** \u2014 `a1 a2 a3 : switch tier: access` declares three identical access switches on one line.\n- **The server farm** \u2014 `serverfarm ... count: 4` draws a stacked icon labelled \xD74.\n\n## Annotation key\n\n| Element | Meaning |\n|---|---|\n| Double line + `40G` | Aggregated link (LAG / EtherChannel) |\n| `tier: core` etc. | Hierarchical band assignment |\n| `Trunk \xB7 VLAN 100` | Server-farm uplink trunk |\n\nFor a two-tier "collapsed core" just drop the distribution devices; for a data-center fabric use `layout: spine-leaf` instead.'
1350
+ },
1351
+ {
1352
+ "slug": "network-layout-modes-star-ring-bus-mesh",
1353
+ "diagram": "network",
1354
+ "title": "Network layout modes",
1355
+ "description": "Four small network topology sketches showing star, ring, bus, and mesh layouts as separate diagrams in the gallery corpus.",
1356
+ "standard": "Cisco topology conventions",
1357
+ "tags": [
1358
+ "network",
1359
+ "layout",
1360
+ "star",
1361
+ "ring",
1362
+ "bus",
1363
+ "mesh"
1364
+ ],
1365
+ "complexity": 2,
1366
+ "featured": false,
1367
+ "dsl": 'network "Branch LAN - Star Layout"\n layout: star\n router gw "Gateway"\n switch sw1 "Access Switch"\n pc pc1 "Admin PC"\n laptop lt1 "Laptop"\n printer prn "Printer"\n ap ap1 "Wi-Fi AP"\n gw -- sw1 : 1G\n sw1 -- pc1 : access vlan: 10 1G\n sw1 -- lt1 : access vlan: 10 1G\n sw1 -- prn : access vlan: 20 1G\n sw1 -- ap1 : poe vlan: 30 1G',
1368
+ "notes": "## Scenario\n\nNetwork examples should prove that layout is semantic, not incidental. This one uses `layout: star`, where the gateway and access switch form the center and endpoints fan around them.\n\n## Annotation key\n\n- `layout: star` selects the hub-and-spoke placer.\n- Link annotations carry VLANs, speed, and PoE.\n- The same device/link model can be rendered with other layouts in companion examples."
1369
+ },
1370
+ {
1371
+ "slug": "network-link-types",
1372
+ "diagram": "network",
1373
+ "title": "Every link type on one fabric",
1374
+ "description": "A compact topology that exercises the full link vocabulary \u2014 fiber with speed and port tags, a LAG bundle, a wireless association, an 802.1Q trunk carrying VLANs, a PoE drop to a camera, and a site-to-site VPN tunnel \u2014 each rendered with its own line style.",
1375
+ "standard": "Cisco-convention topology icons + ANSI/TIA-606 labelling",
1376
+ "tags": [
1377
+ "network",
1378
+ "links",
1379
+ "fiber",
1380
+ "lag",
1381
+ "vlan",
1382
+ "poe",
1383
+ "vpn"
1384
+ ],
1385
+ "complexity": 2,
1386
+ "featured": false,
1387
+ "dsl": 'network "Link types"\n layout: tree\n router core "Core"\n switch a "Sw A"\n switch b "Sw B"\n ap ap1 "AP"\n server s1\n camera c1 type: turret\n vpngw vpn1 "VPN GW"\n core -- a : fiber 10G port: Gi0/1>Gi1/0/1\n core -- b : lag 20G\n a -- ap1 : wireless\n a -- s1 : trunk vlan: 10,20 1G\n b -- c1 : poe\n core -- vpn1 : vpn "site-to-site"',
1388
+ "notes": "A network diagram is only as useful as the *link* annotations \u2014 \"are these two switches on fiber or copper, trunk or access, what VLANs, what speed.\" Schematex encodes all of it after the `:` on a connection, and renders each link type distinctly.\n\n**One line, many semantics.** `fiber 10G port: Gi0/1>Gi1/0/1` draws the orange ticked fiber style and prints both the speed and the two interface names. `lag 20G` thickens the line for a bundled uplink. `wireless` and `vpn` go dashed; `poe` carries power to the camera; `trunk vlan: 10,20` tags the 802.1Q VLAN list.\n\n**Validation comes free.** VLAN ids are range-checked (1\u20134094) and the engine never silently drops a port, link, or device \u2014 if you wrote it, it's on the diagram or it's a reported error."
1389
+ },
1390
+ {
1391
+ "slug": "network-spine-leaf-fabric",
1392
+ "diagram": "network",
1393
+ "title": "Spine-leaf data-center fabric",
1394
+ "description": "A folded-Clos data-center fabric \u2014 two spine switches, four leaf switches fully meshed to the spines automatically, and servers attached to their leaves at 25G.",
1395
+ "standard": "Clos (1953) folded-Clos / spine-leaf fabric",
1396
+ "tags": [
1397
+ "spine-leaf",
1398
+ "clos",
1399
+ "fabric",
1400
+ "datacenter",
1401
+ "leaf",
1402
+ "spine"
1403
+ ],
1404
+ "complexity": 2,
1405
+ "featured": false,
1406
+ "dsl": 'network "DC Fabric"\n layout: spine-leaf\n spines: sp1 sp2\n leaves: lf1 lf2 lf3 lf4\n server h1\n server h2\n server h3\n lf1 -- h1 : 25G\n lf2 -- h2 : 25G\n lf4 -- h3 : 25G',
1407
+ "notes": "## Scenario\n\nA data-center architect wants the fabric drawn without hand-typing every spine-to-leaf cable. In `layout: spine-leaf` mode you declare the spines and leaves, and the engine **auto-meshes every leaf to every spine** \u2014 here that's 2 \xD7 4 = 8 generated links \u2014 leaving you to add only the host attachments.\n\n## What the diagram shows\n\n- **`spines:` / `leaves:`** declare the two fabric rows. The devices don't need a separate `kind` line \u2014 spines become L3 switches, leaves become switches.\n- **Auto-mesh** \u2014 every spine\u2194leaf link is generated; you never type them. Add or remove a leaf and the mesh updates.\n- **Host attachments** \u2014 `lf1 -- h1 : 25G` hangs a server below its leaf at 25 Gbps.\n\n## Annotation key\n\n| Element | Meaning |\n|---|---|\n| Top row | Spine switches |\n| Middle row | Leaf switches (fully meshed to spines) |\n| Bottom row | Hosts, under their leaf |\n\nThis is the modern east-west fabric; for a traditional north-south campus use `layout: tiered` with `tier:` bands instead."
1408
+ },
1043
1409
  {
1044
1410
  "slug": "orgchart-matrix-reporting",
1045
1411
  "diagram": "orgchart",
@@ -1076,6 +1442,25 @@ If the LED doesn't light up, three things to check, in order: LED polarity (the
1076
1442
  "dsl": 'orgchart "Acme \u2014 Series A Team"\nceo: "Jamie Torres" | CEO [role: ceo]\n cto: "Raj Patel" | CTO [role: cto]\n lead_fe: "Priya Nair" | Eng Lead | Frontend [role: engineer]\n eng1: "Alex Kim" | Senior Engineer [role: engineer]\n eng2: "Jordan Lee" | Engineer [role: engineer, status: new]\n open1: open "TBH" | Frontend Engineer [role: engineer]\n lead_be: "Omar Hassan" | Eng Lead | Backend [role: engineer]\n eng3: "Yuki Tanaka" | Staff Engineer [role: engineer]\n draft1: draft "TBH" | Senior Engineer [role: engineer]\n cpo: "Ellen Wu" | CPO [role: cpo]\n pm1: "Tyler Brooks" | Product Lead | Core [role: product]\n pm2: "Suki Ito" | Product Lead | Growth [role: product]\n coo: "Maria Santos" | COO [role: ops]\n fin1: "Nour Ahmed" | Finance Manager [role: ops]\nadvisor adv1: "Dr. Alan Ford" | Board Advisor [role: advisor]',
1077
1443
  "notes": '## Scenario\n\nThe founder is preparing a hiring plan for the next two quarters and uses this chart in a board update. It shows the current team, the one confirmed open req (Frontend Engineer), one planned-but-not-recruiting slot (Staff Engineer backend), and the board advisor relationship. Indentation communicates reporting lines without drawing edges.\n\n## Annotation key\n\n- `id: "Name" | "Title" | "Department"` \u2014 a person node\n- Indentation (2 spaces) \u2014 reporting hierarchy\n- `open \u2026` / `draft \u2026` \u2014 unfilled / planned roles\n- `advisor \u2026` \u2014 external board or advisor relationship\n- `[role: \u2026]` \u2014 colour-coded by function\n\n## How to read\n\nThe single root is the CEO. Each two-space indent step moves one level down the reporting tree. Two kinds of "ghost" slots appear: `open` nodes (Frontend Engineer) are reqs you are actively hiring for; `draft` nodes (Staff Backend) are next-quarter plans. The advisor sits outside the tree \u2014 not in the reporting chain but formally associated with the org.'
1078
1444
  },
1445
+ {
1446
+ "slug": "pedigree-assisted-reproduction-ivf-donor-surrogate",
1447
+ "diagram": "pedigree",
1448
+ "title": "Assisted reproduction pedigree",
1449
+ "description": "Clinical pedigree showing IVF donor and surrogate context with a proband child and clearly separated biological and gestational participants.",
1450
+ "standard": "Bennett 2022",
1451
+ "tags": [
1452
+ "pedigree",
1453
+ "assisted-reproduction",
1454
+ "ivf",
1455
+ "donor",
1456
+ "surrogate",
1457
+ "proband"
1458
+ ],
1459
+ "complexity": 3,
1460
+ "featured": false,
1461
+ "dsl": 'pedigree "Assisted Reproduction Family"\n intended_father [male, unaffected]\n intended_mother [female, unaffected]\n egg_donor [female, evaluated]\n surrogate [female, evaluated]\n\n intended_father -- egg_donor\n embryo [unknown, pregnancy]\n\n intended_father -- intended_mother\n child [female, unaffected, proband]\n\n surrogate -- intended_father\n child [female, unaffected, proband]',
1462
+ "notes": "## Scenario\n\nAssisted reproduction cases need more than a simple parent-child line. This example keeps the clinical pedigree compact while labeling the relevant participants: intended parents, donor, surrogate, embryo, and proband.\n\n## Annotation key\n\n- `pregnancy` marks the embryo node.\n- `evaluated` indicates participants evaluated in the genetics intake.\n- Re-declaring `child` preserves the proband while documenting alternate family context."
1463
+ },
1079
1464
  {
1080
1465
  "slug": "pedigree-brca1",
1081
1466
  "diagram": "pedigree",
@@ -1220,6 +1605,114 @@ If the LED doesn't light up, three things to check, in order: LED polarity (the
1220
1605
  "dsl": 'pert\ntitle: "Three-point project"\nunit: days\ncritical-tolerance: 0.01\n\ntask A "Spec" duration: 2/3/5\ntask B "Build" duration: 5/8/14 after: A\ntask C "Test" duration: 3/4/6 after: B\ntask D "Deploy" duration: 1/2/3 after: C',
1221
1606
  "notes": "PERT's original 1959 contribution wasn't the box \u2014 it was handling *uncertainty* in durations. Write a duration as `O/M/P` (optimistic / most-likely / pessimistic) and the engine treats it as a beta-PERT estimate.\n\n**Expected duration.** Each three-point activity collapses to its beta-distribution mean **te = (O + 4M + P) / 6**, which is what the Duration field shows. `Build` at `5/8/14` becomes `te = 8.5` \u2014 pulled above the most-likely 8 because the pessimistic tail (14) is fatter than the optimistic one (5). The whole forward/backward pass then runs on these `te` values, so the project duration here is \u2248 17.83 days, not an integer.\n\n**Variance and project risk.** Each activity also carries `\u03C3\xB2 = ((P \u2212 O) / 6)\xB2`, surfaced as a small `\u03C3=\u2026` annotation under the name and on a `data-pert-variance` attribute. Summed over the critical-path activities (under the classical independence assumption), the project standard deviation is \u2248 1.69 days \u2014 a one-line risk figure you can quote: \"\u224868% chance of finishing within \xB11.69 days of 17.83.\"\n\n**Why `critical-tolerance: 0.01`.** Once durations are fractional, accumulated floating-point error can give a truly-critical activity a slack of `0.0000001`. The tolerance treats anything within 0.01 of zero as critical, so the red path stays stable across runs. For all-integer projects you can leave it at the default of `0`."
1222
1607
  },
1608
+ {
1609
+ "slug": "petri-arc-types-inhibitor-read-reset",
1610
+ "diagram": "petri",
1611
+ "title": "Petri net arc types",
1612
+ "description": "Petri net showing standard, inhibitor, read, reset, weighted arcs, capacity, and enabled-transition highlighting in one compact control example.",
1613
+ "standard": "Murata 1989 / ISO-IEC 15909",
1614
+ "tags": [
1615
+ "petri",
1616
+ "inhibitor",
1617
+ "read-arc",
1618
+ "reset-arc",
1619
+ "capacity"
1620
+ ],
1621
+ "complexity": 3,
1622
+ "featured": false,
1623
+ "dsl": 'petri "Arc Types"\nlayout: lr\ntokens: auto\nplace Idle *1\nplace Busy\nplace Fault\nplace Permit *1\nplace Buffer capacity: 3 tokens: 2\ntransition Start\ntransition Complete\ntransition Reset\nIdle -> Start\nPermit -- Start\nFault -o Start\nBuffer -> Start weight: 2\nStart -> Busy\nBusy -> Complete\nComplete -> Idle\nFault => Reset\nBusy -> Reset\nReset -> Idle',
1624
+ "notes": "## Scenario\n\nPetri nets become much more expressive once arc types carry semantics. This example shows a start transition that requires permission, is inhibited by a fault, consumes two buffer tokens, and can be reset from a fault state.\n\n## Annotation key\n\n- `--` is a read arc: test without consuming.\n- `-o` is an inhibitor arc: enabled only when the place is empty.\n- `=>` is a reset arc: clear the source place when the transition fires."
1625
+ },
1626
+ {
1627
+ "slug": "petri-classic-net",
1628
+ "diagram": "petri",
1629
+ "title": "The classic Petri net \u2014 concurrency with feedback",
1630
+ "description": "Murata's canonical place/transition net \u2014 one transition forks a token into two concurrent branches that a second transition joins, with a feedback place closing the loop. The engine marks which transitions are enabled in the current marking.",
1631
+ "standard": "Murata 1989 + ISO/IEC 15909-1 (place/transition net)",
1632
+ "tags": [
1633
+ "petri",
1634
+ "concurrency",
1635
+ "fork-join",
1636
+ "feedback",
1637
+ "enabled-transitions"
1638
+ ],
1639
+ "complexity": 2,
1640
+ "featured": true,
1641
+ "dsl": 'petri "Classic"\n place P1 *1\n place P2\n place P3 *2\n place P4 *1\n transition T1\n transition T2\n P1 -> T1\n T1 -> P2\n T1 -> P3\n P2 -> T2\n P3 -> T2\n T2 -> P4\n P4 -> T1',
1642
+ "notes": "This is the net you find on the first page of every Petri-net text (Murata's 1989 survey, Wikipedia's figure 1): the smallest diagram that shows concurrency, synchronisation, and feedback at once.\n\n**One fork, one join.** Firing `T1` puts a token into both `P2` and `P3` \u2014 two branches now run concurrently. `T2` cannot fire until *both* branches deposit their tokens, so it is a synchronisation point (an AND-join).\n\n**The feedback place closes the loop.** `P4 -> T1` routes the result back as a back-edge, drawn around the layout so the cycle reads cleanly. In the current marking only `T1` is *enabled* (highlighted) \u2014 `T2` is dead because `P2` is empty until `T1` fires.\n\n**The render is downstream of the semantics.** Schematex isn't drawing shapes you positioned; it validated the bipartite structure, checked each transition's input places against the marking, and coloured the enabled one."
1643
+ },
1644
+ {
1645
+ "slug": "petri-fire-sequence",
1646
+ "diagram": "petri",
1647
+ "title": "Firing a transition advances the marking",
1648
+ "description": "The same three-place net before and after firing \u2014 the `fire:` directive replays a transition, so the rendered marking is the state *after* the token moved from P1 to P2, and the engine now highlights T2 as the newly enabled transition.",
1649
+ "standard": "Murata 1989 (place/transition net firing rule)",
1650
+ "tags": [
1651
+ "petri",
1652
+ "firing-rule",
1653
+ "marking",
1654
+ "dynamics",
1655
+ "enabled-transitions"
1656
+ ],
1657
+ "complexity": 1,
1658
+ "featured": false,
1659
+ "dsl": 'petri "After fire: T1"\n place P1 *1\n transition T1\n place P2\n transition T2\n place P3\n P1 -> T1\n T1 -> P2\n P2 -> T2\n T2 -> P3\n fire: T1',
1660
+ "notes": "Most text-to-diagram tools draw a Petri net as static shapes. Schematex *runs* it.\n\n**The firing rule, applied.** The net is declared with one token in `P1`. The `fire: T1` directive tells the engine to apply T1's firing \u2014 consume one token from each input place (`P1`), produce one in each output place (`P2`). What you see rendered is the marking **after** that step: the token now sits in `P2`.\n\n**Enablement is recomputed.** With the token in `P2`, `T1` is now dead (its input `P1` is empty) and `T2` is the *enabled* transition (highlighted). Chain more steps \u2014 `fire: T1, T2` \u2014 to walk the net forward and watch the token travel P1 \u2192 P2 \u2192 P3.\n\nThis is the difference between a drawing and a model: the diagram is a *function of* the computed marking, not something you hand-positioned."
1661
+ },
1662
+ {
1663
+ "slug": "petri-mutual-exclusion",
1664
+ "diagram": "petri",
1665
+ "title": "Mutual exclusion with a shared resource",
1666
+ "description": "The canonical concurrency pattern as a Petri net \u2014 two processes competing for a single Mutex token. Either process may enter its critical section, but the shared token guarantees they never do so at the same time. Both entry transitions show as enabled until one fires and consumes the resource.",
1667
+ "standard": "Murata 1989 / ISO-IEC 15909-1 (place/transition net)",
1668
+ "tags": [
1669
+ "petri",
1670
+ "concurrency",
1671
+ "mutual-exclusion",
1672
+ "synchronization",
1673
+ "formal-methods"
1674
+ ],
1675
+ "complexity": 2,
1676
+ "featured": true,
1677
+ "dsl": 'petri "Mutual Exclusion \u2014 two processes, one resource"\n place idleA *1 "A idle"\n place idleB *1 "B idle"\n place mutex *1 "resource"\n place critA "A critical"\n place critB "B critical"\n transition enterA\n transition exitA\n transition enterB\n transition exitB\n idleA -> enterA\n mutex -> enterA\n enterA -> critA\n critA -> exitA\n exitA -> idleA\n exitA -> mutex\n idleB -> enterB\n mutex -> enterB\n enterB -> critB\n critB -> exitB\n exitB -> idleB\n exitB -> mutex',
1678
+ "notes": 'Mutual exclusion is the "hello world" of concurrency theory, and a Petri net states it more honestly than a flowchart can: the safety property is a structural fact about a single token, not a comment in the margin.\n\n**One token, two claimants.** The `mutex` place holds exactly one token. Both `enterA` and `enterB` are enabled \u2014 each has its process idle *and* the resource available \u2014 so the engine rings both green. The moment one fires, it consumes the mutex token, and the other entry transition is no longer enabled. There is no marking in which both `critA` and `critB` hold a token; that invariant is what "mutual exclusion" means.\n\n**Exit returns the resource.** `exitA` / `exitB` put the token back into `mutex` and the process back to idle, so the net cycles forever without ever violating the invariant \u2014 a *live* and *safe* net in Petri terminology.\n\n**Why the formalism earns its keep.** Drawn as two side-by-side flowcharts, the shared-resource constraint is invisible. Drawn as a Petri net, it is the single most important node in the diagram. That is the whole pitch: the model carries the property, and the renderer shows you which moves are currently legal.'
1679
+ },
1680
+ {
1681
+ "slug": "petri-producer-consumer",
1682
+ "diagram": "petri",
1683
+ "title": "Producer / consumer with a bounded buffer",
1684
+ "description": "The bounded-buffer producer/consumer pattern as a Petri net \u2014 complementary free/used slot places enforce the buffer size, a timed withdraw transition models the consumer's rate, and place capacity caps the buffer at three slots. Tokens flowing through free\u2192used\u2192free show backpressure as a structural property.",
1685
+ "standard": "Murata 1989 + Marsan 1995 (GSPN timed transitions)",
1686
+ "tags": [
1687
+ "petri",
1688
+ "concurrency",
1689
+ "producer-consumer",
1690
+ "bounded-buffer",
1691
+ "queueing"
1692
+ ],
1693
+ "complexity": 3,
1694
+ "featured": false,
1695
+ "dsl": 'petri "Producer / Consumer (bounded buffer)"\n place pReady *1 "producer ready"\n place free *3 "free slots"\n place used capacity: 3 "used slots"\n place cReady *1 "consumer ready"\n transition produce "deposit"\n transition consume timed rate: 0.8 "withdraw"\n pReady -> produce\n free -> produce\n produce -> used\n produce -> pReady\n used -> consume\n cReady -> consume\n consume -> free\n consume -> cReady',
1696
+ "notes": "A bounded buffer is where queueing theory meets concurrency, and the Petri net version makes the backpressure mechanism explicit instead of hiding it in a counter variable.\n\n**Free and used slots are complementary places.** The `free` place starts with three tokens (three empty slots); `used` starts empty and is capped at `capacity: 3` (the dashed border). Every `produce` firing moves one token free\u2192used; every `consume` firing moves one back used\u2192free. Their sum is invariant \u2014 the buffer can never hold more than three items, and that bound is a structural property of the net, not a runtime check.\n\n**A timed consumer.** `consume` is a `timed` transition with a rate (\u03BB = 0.8), rendered as a hollow box \u2014 the [GSPN](https://en.wikipedia.org/wiki/Stochastic_Petri_net) convention for a transition that fires after a stochastic delay. That is how you model a consumer that drains the buffer at a finite rate, the seed of any performance analysis.\n\n**Backpressure for free.** When `free` is empty (buffer full), `produce` loses its input token and is no longer enabled \u2014 the producer blocks until the consumer frees a slot. No `if buffer.full` branch; the net's enabling rule *is* the backpressure."
1697
+ },
1698
+ {
1699
+ "slug": "petri-workflow-net",
1700
+ "diagram": "petri",
1701
+ "title": "Order-fulfilment workflow net (WF-net)",
1702
+ "description": "A van der Aalst workflow net \u2014 a single source place and single sink place bracket an AND-split/join that runs picking and invoicing concurrently before shipping. The engine detects the WF-net structure and reports it in the SVG description.",
1703
+ "standard": "van der Aalst 1998 workflow nets (a Murata place/transition net)",
1704
+ "tags": [
1705
+ "petri",
1706
+ "workflow",
1707
+ "wf-net",
1708
+ "and-split",
1709
+ "business-process"
1710
+ ],
1711
+ "complexity": 2,
1712
+ "featured": false,
1713
+ "dsl": 'petri "Order workflow"\n place in *1 "received"\n transition split\n place pick\n place invoice\n transition pack\n transition bill\n place packed\n place billed\n transition ship\n place out "shipped"\n in -> split\n split -> pick\n split -> invoice\n pick -> pack\n invoice -> bill\n pack -> packed\n bill -> billed\n packed -> ship\n billed -> ship\n ship -> out',
1714
+ "notes": "A *workflow net* is the Petri-net dialect business-process people actually use: exactly one **source** place (no incoming arcs \u2014 where a case enters) and one **sink** place (no outgoing arcs \u2014 where it leaves). Schematex detects this structure and notes it in the diagram's `<desc>`.\n\n**Concurrency by construction.** `split` forks the order into a `pick` branch and an `invoice` branch \u2014 warehouse and finance work in parallel. `ship` is the AND-join: it needs both `packed` and `billed`, so an order can't ship before it's been both packed and billed. No status flags, no polling \u2014 the net enforces the ordering.\n\n**Why not a flowchart?** A flowchart would draw the same boxes but couldn't express \"these two run concurrently and rejoin\" without ambiguity. The Petri net's token flow makes the AND-split/join unambiguous and analysable. This is the same reduction BPMN uses internally."
1715
+ },
1223
1716
  {
1224
1717
  "slug": "phylo-bacterial-diversity",
1225
1718
  "diagram": "phylo",
@@ -1238,6 +1731,43 @@ If the LED doesn't light up, three things to check, in order: LED polarity (the
1238
1731
  "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"',
1239
1732
  "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.'
1240
1733
  },
1734
+ {
1735
+ "slug": "phylo-sars-cov-2-chronogram",
1736
+ "diagram": "phylo",
1737
+ "title": "SARS-CoV-2 variant chronogram",
1738
+ "description": "Chronogram-style phylogenetic tree for major SARS-CoV-2 variants with clade highlights and a year scale.",
1739
+ "standard": "Newick / phylogram conventions",
1740
+ "tags": [
1741
+ "phylo",
1742
+ "chronogram",
1743
+ "newick",
1744
+ "variants",
1745
+ "clades"
1746
+ ],
1747
+ "complexity": 3,
1748
+ "featured": false,
1749
+ "dsl": 'phylo "SARS-CoV-2 Variants" [mode: chronogram, layout: rectangular, mrsd: "2023"]\n newick: "((Alpha:0.45,(Beta:0.35,Gamma:0.38):0.12):0.25,(Delta:0.42,(BA1:0.28,(BA2:0.18,XBB:0.12):0.1):0.22):0.3);"\n clade VOC = (Alpha, Beta, Gamma, Delta) [color: "#1E88E5", label: "Early VOCs", highlight: branch]\n clade Omicron = (BA1, BA2, XBB) [color: "#E53935", label: "Omicron lineages", highlight: both]\n scale "years before 2023"',
1750
+ "notes": "## Scenario\n\nChronograms use branch lengths as time. For variant surveillance, that means the horizontal distance conveys approximate divergence time rather than only genetic distance.\n\n## Annotation key\n\n- `mode: chronogram` and `mrsd` tell the renderer how to interpret branch lengths.\n- `clade` highlights named groups from the Newick leaves.\n- `scale` labels the time axis."
1751
+ },
1752
+ {
1753
+ "slug": "pid-distillation-column-reboiler-condenser",
1754
+ "diagram": "pid",
1755
+ "title": "Distillation column with reboiler and condenser",
1756
+ "description": "P&ID for a distillation overhead loop with condenser, reflux drum, reflux pump, reboiler, and ISA instrument bubbles.",
1757
+ "standard": "ANSI/ISA-5.1 + ISO 10628",
1758
+ "tags": [
1759
+ "pid",
1760
+ "distillation",
1761
+ "column",
1762
+ "condenser",
1763
+ "reboiler",
1764
+ "instrumentation"
1765
+ ],
1766
+ "complexity": 3,
1767
+ "featured": false,
1768
+ "dsl": 'pid "Distillation Column T-201"\n\nequip T-201 : column_tray [tag: "T-201"]\nequip E-201 : condenser [tag: "Overhead Condenser"]\nequip D-201 : vessel_h [tag: "Reflux Drum"]\nequip P-201 : pump_centrifugal [tag: "Reflux Pump"]\nequip E-202 : reboiler [tag: "Reboiler"]\nequip PSV-201 : valve_psv [tag: "PSV-201", set_pressure: "150 psig"]\n\nline L1 from T-201.top to E-201.shell_in [size: "8in", service: "overhead vapor", type: "process"]\nline L2 from E-201.shell_out to D-201.in [size: "8in", service: "condensate", type: "process"]\nline L3 from D-201.bottom to P-201.in [size: "3in", service: "reflux", type: "process"]\nline L4 from P-201.out to T-201.reflux [size: "3in", service: "reflux", type: "process"]\nline L5 from T-201.bottom to E-202.in [size: "6in", service: "bottoms", type: "process"]\nline L6 from T-201.top to PSV-201.in [size: "2in", service: "relief", type: "process_minor"]\n\ninst PT-201 : field_discrete\n measures T-201\ninst LIC-201 : cr_shared\n measures D-201\ninst TIC-201 : cr_shared\n measures T-201',
1769
+ "notes": "## Scenario\n\nDistillation columns are a canonical P&ID use case because the physical process and the control loops must be legible together. This example shows the main process path and three instrument bubbles without pretending to be a full plant drawing.\n\n## Annotation key\n\n- `equip` lines select ISO process equipment symbols.\n- `line ... from ... to ...` connects equipment ports and carries line tags.\n- `inst` declarations create ISA-5.1 instrument bubbles tied to measured equipment."
1770
+ },
1241
1771
  {
1242
1772
  "slug": "pid-pump-flow-control",
1243
1773
  "diagram": "pid",
@@ -1326,6 +1856,44 @@ If the LED doesn't light up, three things to check, in order: LED polarity (the
1326
1856
  "dsl": "prisma\nmode: 2020-single\ntitle: Effect of exercise on chronic low-back pain \u2014 SR\n\nidentification:\n databases:\n n: 1418\n sources: PubMed=600, Embase=450, Cochrane=184, Web of Science=184\n duplicates-removed: 318\n\nscreening:\n records-screened: 1100\n excluded:\n n: 870\n reasons: irrelevant title=750, non-English=120\n\neligibility:\n full-text-assessed: 230\n excluded:\n n: 195\n reasons: wrong population=80, wrong intervention=60, wrong outcome=55\n\nincluded:\n studies: 35\n reports: 38",
1327
1857
  "notes": '## Scenario\n\nA research librarian produces the PRISMA 2020 flow diagram for a Cochrane review submission. The journal **requires** the diagram in the exact four-row structure \u2014 Identification \u2192 Screening \u2192 Eligibility \u2192 Included \u2014 with the count `(n = \u2026)` shown in every box and the excluded boxes itemizing reasons. With the dedicated `prisma` engine the librarian writes only counts and reasons; the rigid layout, the "Records removed before screening" side-box, and the exclusion side-boxes are produced automatically and are correct by construction.\n\n## Annotation key\n\n- **`mode: 2020-single`** \u2014 one Identification column (databases & registers). Use `mode: 2020-dual` to add the "other methods" column.\n- **`identification.databases`** \u2014 `n:` is the mandatory total; `sources:` renders the per-database breakdown; `duplicates-removed:` splits out into the right-column "Records removed before screening" box automatically.\n- **`screening` / `eligibility` `excluded:` blocks** \u2014 each has its own `n:` total and an optional `reasons:` breakdown rendered in a side-box to the right, connected by a horizontal arrow.\n- **`included.studies` / `reports`** \u2014 one study can yield several reports, so both counts render.\n\n## How to read\n\nTop to bottom mirrors the PRISMA 2020 template exactly. The left capsule bands label the three canonical stages; the orange bar spans the Identification column group. Counts reconcile across stages (1418 \u2212 318 = 1100 screened; 1100 \u2212 870 = 230 assessed; 230 \u2212 195 = 35 included) \u2014 the engine warns if they don\'t.\n\n## Standard reference\n\nPage MJ, McKenzie JE, Bossuyt PM, et al. *The PRISMA 2020 statement: an updated guideline for reporting systematic reviews.* BMJ 2021;372:n71. Template: [prisma-statement.org/prisma-2020-flow-diagram](https://www.prisma-statement.org/prisma-2020-flow-diagram).'
1328
1858
  },
1859
+ {
1860
+ "slug": "sequence-combined-fragments-gallery",
1861
+ "diagram": "sequence",
1862
+ "title": "Combined fragments gallery",
1863
+ "description": "UML sequence diagram demonstrating alt, opt, loop, par, critical, and ref frames in one realistic API interaction.",
1864
+ "standard": "OMG UML 2.5.1",
1865
+ "tags": [
1866
+ "sequence",
1867
+ "uml",
1868
+ "combined-fragments",
1869
+ "alt",
1870
+ "loop",
1871
+ "par"
1872
+ ],
1873
+ "complexity": 3,
1874
+ "featured": false,
1875
+ "dsl": 'sequence "API request with combined fragments"\n autonumber 1 1\n actor Client\n boundary API as "API Gateway"\n control Auth\n control Orders\n database DB\n queue Events\n\n Client ->+ API : POST /orders\n ref over API, Auth : Validate token\n alt [token valid]\n API -> Auth : checkScopes()\n Auth --> API : scopes\n opt [idempotency key present]\n API -> DB : lookupRequest(key)\n DB --> API : previous result?\n end\n loop (1,3)\n API -> Orders : reserveInventory()\n Orders --> API : reservation result\n end\n par\n API ->> Events : OrderRequested\n and\n API -> DB : persistOrder()\n end\n critical\n DB --> API : commit ok\n API -->- Client : 201 Created\n end\n else [token invalid]\n API -->- Client : 401 Unauthorized\n end',
1876
+ "notes": "## Scenario\n\nThis is a compact tour of UML sequence constructs that matter in real service diagrams. The request validates auth, checks idempotency, retries a reservation, publishes asynchronously, persists state, and returns a response.\n\n## Annotation key\n\n- `alt`, `opt`, `loop`, `par`, and `critical` are first-class combined fragments.\n- `ref over API, Auth` references a reusable interaction instead of inlining it.\n- `->>` distinguishes asynchronous event publication from blocking calls."
1877
+ },
1878
+ {
1879
+ "slug": "sequence-login-flow",
1880
+ "diagram": "sequence",
1881
+ "title": "Login flow with an alt fragment",
1882
+ "description": "An authentication handshake across an actor, a Jacobson control object, and a database lifeline \u2014 synchronous calls open activation bars, a dashed reply returns the row, and an alt combined fragment splits the valid/invalid branches. A note records the session-cookie side effect.",
1883
+ "standard": "OMG UML 2.5.1 \xA717 (Interactions)",
1884
+ "tags": [
1885
+ "sequence",
1886
+ "uml",
1887
+ "authentication",
1888
+ "alt-fragment",
1889
+ "activation",
1890
+ "lifeline-kinds"
1891
+ ],
1892
+ "complexity": 2,
1893
+ "featured": true,
1894
+ "dsl": 'sequence "Login flow"\n actor User\n participant Web as "Web App"\n control Auth\n database DB\n User -> Web : submit(credentials)\n activate Web\n Web ->+ Auth : verify(credentials)\n Auth ->+ DB : SELECT user\n DB --> Auth : row\n deactivate DB\n alt [credentials valid]\n Auth --> Web : token\n Web --> User : 200 OK\n else [invalid]\n Auth --> Web : 401\n Web --> User : error\n end\n deactivate Auth\n deactivate Web\n note over User, Web : session cookie set',
1895
+ "notes": 'The canonical "who calls whom, in what order" diagram \u2014 and a good tour of the features that separate a UML sequence diagram from a generic flow.\n\n**Lifeline kinds carry meaning.** `actor`, `control`, and `database` render their distinct UML/Jacobson heads (stick figure, circled arrow, cylinder), so the diagram tells you *what kind of thing* each participant is, not just its name.\n\n**Activation and replies.** `->+` opens an activation bar on the callee; `--` draws the dashed reply that returns control (and the `row`). Bars nest, so you can see `Auth` is active while it waits on `DB`.\n\n**Branching is a real fragment.** The `alt` block with its `[credentials valid]` / `[invalid]` guards is a UML *combined fragment*, drawn as a labelled box spanning the lifelines \u2014 not two separate arrows you have to mentally group.'
1896
+ },
1329
1897
  {
1330
1898
  "slug": "sequence-microservices-saga",
1331
1899
  "diagram": "sequence",
@@ -1363,6 +1931,26 @@ If the LED doesn't light up, three things to check, in order: LED polarity (the
1363
1931
  "dsl": 'sequence "OAuth 2.0 Authorization Code"\n actor User\n boundary Browser\n control App as "Web App"\n participant Auth as "Auth Server"\n database DB as "User Store"\n\n User -> Browser : click "Sign in"\n Browser ->+ App : GET /login\n App --> Browser : 302 \u2192 Auth Server\n Browser ->+ Auth : GET /authorize\n Auth --> User : consent screen\n User -> Auth : approve\n Auth -->- Browser : 302 + auth code\n\n Browser ->+ App : GET /callback?code\n App ->+ Auth : POST /token (code)\n Auth ->+ DB : load user\n DB -->- Auth : profile\n Auth -->- App : access + refresh token\n alt [token exchange ok]\n App --> Browser : Set-Cookie: session\n Browser --> User : signed in\n else [exchange failed]\n App --> Browser : 401 Unauthorized\n Browser --> User : retry\n end\n deactivate App',
1364
1932
  "notes": "Almost every product ships this flow, and almost every whiteboard drawing of it is subtly wrong about *who is active when*. A sequence diagram makes the call order and the active spans explicit.\n\n**Lifeline kinds carry meaning.** The `boundary` Browser, the `control` App, and the `entity`/`database` user store render as their UML symbols, so a reviewer reads the architecture at a glance: the browser is the UI edge, the app orchestrates, the auth server and store are collaborators.\n\n**Activation bars track the redirect dance.** OAuth bounces the user between the app and the auth server twice. The `+`/`-` suffixes open and close execution-specification bars exactly where each party becomes busy and hands control back \u2014 the `Auth -->- Browser` reply both returns the auth code *and* closes the auth server's first activation.\n\n**The branch is a real fragment, not two diagrams.** The `alt` frame captures the success and failure paths in one place: a valid token exchange sets the session cookie; a failed one returns `401`. That is the single most common place sequence diagrams earn their keep \u2014 showing the unhappy path next to the happy one instead of hiding it."
1365
1933
  },
1934
+ {
1935
+ "slug": "sequence-object-lifecycle",
1936
+ "diagram": "sequence",
1937
+ "title": "Object lifecycle \u2014 create, found, lost, destroy",
1938
+ "description": "The four UML interaction lifecycle markers in one short diagram \u2014 a create message draws its arrow to the new participant's box, a found message starts from a filled circle, a lost message ends at one, and destroy terminates a lifeline with an \u2715.",
1939
+ "standard": "OMG UML 2.5.1 \xA717 (Interactions)",
1940
+ "tags": [
1941
+ "sequence",
1942
+ "uml",
1943
+ "create",
1944
+ "destroy",
1945
+ "found-message",
1946
+ "lost-message",
1947
+ "lifecycle"
1948
+ ],
1949
+ "complexity": 1,
1950
+ "featured": false,
1951
+ "dsl": "sequence\n participant Factory\n Factory -> *Worker : \xABcreate\xBB\n o-> Worker : external trigger\n Worker -x : fire-and-forget\n destroy Worker",
1952
+ "notes": 'UML interactions model not just messages but the *birth and death* of participants \u2014 the part most text-to-diagram tools skip. This tiny diagram shows all four markers.\n\n**Create.** `Factory -> *Worker` \u2014 the `*` prefix creates `Worker`; its arrow lands on the *side of the new participant\'s box*, which appears partway down rather than at the top, the UML convention for "instantiated here."\n\n**Found and lost.** `o-> Worker` is a *found* message \u2014 it starts from a filled circle, meaning "from outside the modelled scope" (an external trigger). `Worker -x` is a *lost* message \u2014 it ends at a circle, a fire-and-forget whose receiver isn\'t shown.\n\n**Destroy.** `destroy Worker` terminates the lifeline with the \u2715 marker, so the diagram shows exactly when the object ceases to exist.'
1953
+ },
1366
1954
  {
1367
1955
  "slug": "sfc-bake-cool-concurrent",
1368
1956
  "diagram": "sfc",
@@ -1419,6 +2007,43 @@ If the LED doesn't light up, three things to check, in order: LED polarity (the
1419
2007
  "dsl": 'sfc "Order routing"\n\nvar ProductOrdered: bool\nvar IsExpressShipping: bool\nvar IsStandardShipping: bool\nvar Shipped: bool\n\nstep S0 [initial]\n\nstep S_Pick\n N PickFromBin\n\nalt from: S_Pick:\n branch [priority: 1]:\n transition: IsExpressShipping\n step S_Express\n N PrepExpressBox\n transition: TRUE\n branch [priority: 2]:\n transition: IsStandardShipping\n step S_Standard\n N PrepStandardBox\n transition: TRUE\nmerge_to: S_Ship\n\nstep S_Ship\n N CarrierPickup\n\ntransition from: S0 to: S_Pick: ProductOrdered\ntransition from: S_Ship to: S0: Shipped',
1420
2008
  "notes": "A modern fulfillment center handles both express (next-day) and standard (3\u20135 day) shipping on the same physical line, with the same picking robots and the same carrier-pickup conveyor. The only divergence is the boxing step: express boxes are different sizes, use rigid corrugate, and get extra tracking labels. SFC's alternative branch is the right shape for this: one step (picking) splits into two paths (express boxing OR standard boxing) and they converge back to a common end step (carrier pickup).\n\n**Single horizontal bar = alternative.** IEC 61131-3 \xA76.5.4 uses a **single horizontal line** for alternative (OR-semantic) divergence and convergence. Visually it's the simpler cousin of the double-bar simultaneous fork; semantically it's the opposite \u2014 only *one* branch fires per scan, picked by which entry transition's condition evaluates true first under the priority order.\n\n**Priority annotation matters.** Both branches have `[priority: N]` markers \u2014 express is priority 1, standard is priority 2. If both `IsExpressShipping` and `IsStandardShipping` were somehow true at the same time (a bug, but a common one in early integration), the priority forces the express path. The renderer puts a small red number near each branch entry to make the priority visible at review time.\n\n**Per-branch entry and exit transitions.** Inside each branch, there are two `transition: ...` lines \u2014 the first is the **entry transition** (fires when control reaches the divergence bar; renders between the bar and the first step in that branch), and the second is the **exit transition** (fires when the branch's last step completes; renders between that step and the convergence bar). Most exit transitions are `TRUE` (unconditional) because the *step* itself is what's gating the work \u2014 once the step completes, you want to leave the branch. The entry transitions are where the actual decision logic lives.\n\n**Why not flowchart?** A flowchart of this would render the routing as a diamond with two branches and labels on each branch. You'd lose the explicit step semantics \u2014 in flowchart, the diamond *is* the decision point and there's no notion of \"the picking step is currently active and the next step depends on shipping type.\" That distinction matters for PLC code: while the picking step is active, the picking robot is physically holding the product, and you need to know exactly when the routing decision happens (at step exit, not step entry) to coordinate the conveyor handoff. SFC's \"step active \u2192 transition \u2192 next step active\" semantics are what real PLC scan engines do; flowchart's diamond/box semantics are not.\n\n**The shared S_Ship destination.** Both branches converge to S_Ship, where `CarrierPickup` runs. This is one of the strengths of alternative-branch SFC: the post-routing step is shared infrastructure, drawn exactly once. If you moved to multiple destinations (e.g. express has its own carrier door, standard has another), you'd skip the convergence and let each branch end at its own terminal step \u2014 also a valid SFC pattern."
1421
2009
  },
2010
+ {
2011
+ "slug": "sld-commercial-solar-pv",
2012
+ "diagram": "sld",
2013
+ "title": "Commercial solar PV interconnection",
2014
+ "description": "Single-line diagram for a commercial PV array feeding a combiner, inverter, AC disconnect, production meter, and main switchboard.",
2015
+ "standard": "IEEE 315 / NEC 690",
2016
+ "tags": [
2017
+ "sld",
2018
+ "solar",
2019
+ "pv",
2020
+ "inverter",
2021
+ "interconnection"
2022
+ ],
2023
+ "complexity": 2,
2024
+ "featured": false,
2025
+ "dsl": 'sld "Commercial PV Interconnection"\nPV1 = solar [rating: "250 kWdc", label: "PV Array"]\nCMB = hub [rating: "600 Vdc", label: "DC Combiner"]\nDISC_DC = switch_load [rating: "600 Vdc", label: "DC Disconnect"]\nINV = load [rating: "200 kWac", label: "Grid-tie Inverter"]\nDISC_AC = switch_load [rating: "480V 400A", label: "AC Disconnect"]\nMTR = watthour_meter [label: "Production Meter"]\nMSB = bus [voltage: "480V", label: "Main Switchboard"]\nUTIL = utility [voltage: "480V", label: "Utility"]\n\nPV1 -> CMB [cable: "PV string conductors"]\nCMB -> DISC_DC\nDISC_DC -> INV\nINV -> DISC_AC\nDISC_AC -> MTR\nMTR -> MSB\nUTIL -> MSB',
2026
+ "notes": "## Scenario\n\nCommercial PV one-lines have a recognizable chain: DC array, combiner, inverter, AC disconnect, meter, and point of interconnection. This example uses standard SLD equipment while preserving PV-specific ratings in labels.\n\n## Annotation key\n\n- `solar` marks the generation source.\n- `hub` stands in for the combiner point.\n- `watthour_meter` captures the production metering requirement."
2027
+ },
2028
+ {
2029
+ "slug": "sld-data-center-2n-ups",
2030
+ "diagram": "sld",
2031
+ "title": "Data center 2N UPS single-line",
2032
+ "description": "Single-line diagram for a dual-path data center power train with utility feeds, UPS A/B, static transfer switch, and critical load panels.",
2033
+ "standard": "IEEE 315 / Uptime Institute topology convention",
2034
+ "tags": [
2035
+ "sld",
2036
+ "data-center",
2037
+ "ups",
2038
+ "2n",
2039
+ "sts",
2040
+ "critical-power"
2041
+ ],
2042
+ "complexity": 3,
2043
+ "featured": false,
2044
+ "dsl": 'sld "Data Center 2N UPS"\nUTIL_A = utility [voltage: "13.8kV", label: "Utility A"]\nUTIL_B = utility [voltage: "13.8kV", label: "Utility B"]\nXFMR_A = transformer [rating: "2500 kVA", voltage: "13.8kV/480V", label: "TX-A"]\nXFMR_B = transformer [rating: "2500 kVA", voltage: "13.8kV/480V", label: "TX-B"]\nSWGR_A = bus [voltage: "480V", label: "Switchgear A"]\nSWGR_B = bus [voltage: "480V", label: "Switchgear B"]\nUPS_A = ups [rating: "1 MW", label: "UPS A"]\nUPS_B = ups [rating: "1 MW", label: "UPS B"]\nSTS = ats [rating: "1600A", label: "Static Transfer Switch"]\nPDU_A = load [label: "PDU A"]\nPDU_B = load [label: "PDU B"]\nCRIT = load [label: "Critical IT Load"]\n\nUTIL_A -> XFMR_A\nUTIL_B -> XFMR_B\nXFMR_A -> SWGR_A\nXFMR_B -> SWGR_B\nSWGR_A -> UPS_A\nSWGR_B -> UPS_B\nUPS_A -> STS\nUPS_B -> STS\nSTS -> PDU_A\nSTS -> PDU_B\nPDU_A -> CRIT\nPDU_B -> CRIT',
2045
+ "notes": "## Scenario\n\nData centers are sold on redundancy. A 2N power path has two independent sources and UPS chains so either side can carry the critical load. A one-line diagram makes that redundancy auditable.\n\n## Annotation key\n\n- Two utility and transformer paths stay separate until the transfer point.\n- `ups` nodes show the energy-storage conversion stage.\n- The transfer switch feeds two PDUs before the critical IT load."
2046
+ },
1422
2047
  {
1423
2048
  "slug": "sld-generator-ats",
1424
2049
  "diagram": "sld",
@@ -1506,6 +2131,24 @@ If the LED doesn't light up, three things to check, in order: LED polarity (the
1506
2131
  "dsl": 'sld "13.8 kV Substation"\nutility = utility [label: "Grid 138 kV"]\nxfmr1 = transformer [kva: 15000, primary: 138, secondary: 13.8]\nbus_hv = bus [voltage: 138]\nbus_mv = bus [voltage: 13.8]\nbrk1 = breaker [amps: 1200]\nbrk2 = breaker [amps: 1200]\nbrk3 = breaker [amps: 1200]\nfeeder1 = load [label: "Feeder 1"]\nfeeder2 = load [label: "Feeder 2"]\nfeeder3 = load [label: "Feeder 3"]\nutility -> bus_hv\nbus_hv -> xfmr1\nxfmr1 -> bus_mv\nbus_mv -> brk1\nbrk1 -> feeder1\nbus_mv -> brk2\nbrk2 -> feeder2\nbus_mv -> brk3\nbrk3 -> feeder3',
1507
2132
  "notes": '## Scenario\n\nA power systems engineer documents a distribution substation design for a utility interconnection application or a facility\'s electrical permit drawings. The single-line diagram is the first deliverable in any power system project \u2014 required by IEEE, NFPA 70E, and utility interconnection standards before detailed engineering begins.\n\n## Annotation key\n\n- `utility = utility [label: "..."]` \u2014 utility supply source (three-phase symbol)\n- `[type: transformer, kva:..., primary:..., secondary:...]` \u2014 step-down transformer with rated kVA and voltage levels\n- `[type: bus, voltage:...]` \u2014 horizontal bus bar at the specified voltage level\n- `[type: breaker, amps:...]` \u2014 rated circuit breaker\n- `[type: load, label:...]` \u2014 load or feeder destination\n- `->` \u2014 directed power path from source to load\n\n## How to read\n\nThe 138 kV grid source feeds the high-voltage bus, which connects to the primary of the 15 MVA step-down transformer. The transformer secondary feeds the 13.8 kV medium-voltage bus. Three 1200 A circuit breakers fan out from the MV bus to three distribution feeders \u2014 each breaker isolates its feeder independently.'
1508
2133
  },
2134
+ {
2135
+ "slug": "sociogram-criminal-network",
2136
+ "diagram": "sociogram",
2137
+ "title": "Criminal communication network",
2138
+ "description": "Investigative sociogram of a covert organization \u2014 weighted command ties between the principal and lieutenants, directed courier and associate links to external contacts, and a weak tie that hints at an unconfirmed connection. The force-directed layout surfaces the command hierarchy and the bridging couriers.",
2139
+ "standard": "Moreno 1934",
2140
+ "tags": [
2141
+ "force-directed",
2142
+ "weighted-ties",
2143
+ "directed",
2144
+ "bridging",
2145
+ "command-hierarchy"
2146
+ ],
2147
+ "complexity": 2,
2148
+ "featured": false,
2149
+ "dsl": 'sociogram "Operation Sunset - Communication Network"\n config: layout = force-directed\n boss [label: "Subject Alpha"]\n lt1 [label: "Lieutenant 1"]\n lt2 [label: "Lieutenant 2"]\n courier1 [label: "Courier A"]\n courier2 [label: "Courier B"]\n contact1 [label: "External Contact 1"]\n contact2 [label: "External Contact 2"]\n associate1 [label: "Associate 1"]\n associate2 [label: "Associate 2"]\n boss <-> lt1 [weight: 4]\n boss <-> lt2 [weight: 4]\n lt1 -> courier1\n lt1 -> courier2\n lt2 -> associate1\n lt2 -> associate2\n courier1 -> contact1 [label: "supplier"]\n courier2 -> contact2 [label: "distributor"]\n lt1 <-> lt2 [weight: 2]\n associate1 -.- courier1',
2150
+ "notes": "## Scenario\n\nA link-analysis unit reconstructs a covert organization from intercept metadata. The sociogram is the standard deliverable: who talks to whom, how strongly, and in which direction \u2014 so the analyst can identify the principal, the lieutenants who insulate him, and the couriers who bridge the organization to outside suppliers.\n\n## Annotation key\n\n- `<-> [weight: N]` \u2014 a reciprocal, weighted tie; the heavier `weight: 4` command links between `boss` and the two lieutenants render thicker than the `weight: 2` peer link\n- `->` \u2014 a directed contact (one party initiates), used for the courier and associate tasking lines\n- `-> [label: ...]` \u2014 a directed tie carrying a role annotation (`supplier`, `distributor`)\n- `-.-` \u2014 a weak/unconfirmed tie, drawn dashed: `associate1 -.- courier1` flags a suspected but unverified connection\n\n## How to read\n\nSubject Alpha sits at the center, tied strongly to both lieutenants but never directly to the couriers or external contacts \u2014 the classic insulation pattern. Lieutenant 1 controls both couriers, each of whom reaches a single external contact, so the couriers are the bridging nodes whose removal would sever the outside supply. The dashed `associate1 -.- courier1` tie is the lead worth developing: an unconfirmed cross-link between the two lieutenants' sub-networks."
2151
+ },
1509
2152
  {
1510
2153
  "slug": "sociogram-playground-dynamics",
1511
2154
  "diagram": "sociogram",
@@ -1542,6 +2185,24 @@ If the LED doesn't light up, three things to check, in order: LED polarity (the
1542
2185
  "dsl": 'sociogram "Engineering team \u2014 informal influence"\n config: layout = force-directed\n group leads [label: "Tech leads", color: "#1976D2"]\n alex; sam\n group sr [label: "Senior ICs", color: "#66BB6A"]\n priya; jordan; kim; tao\n group jr [label: "Junior", color: "#FFA726"]\n lee; ravi; nina; dev\n alex <-> sam\n alex -> priya\n sam -> jordan\n priya <-> kim\n jordan <-> tao\n kim -> lee\n priya -> ravi\n tao -> nina\n dev -.- lee\n nina -.- priya',
1543
2186
  "notes": "## Scenario\n\nAn engineering manager runs an informal network analysis survey (\"Who do you go to when you're stuck?\") and maps the results to identify knowledge hubs, bridging individuals between seniority tiers, and team members who are drifting toward isolation before performance reviews surface the issue.\n\n## Annotation key\n\n- `group id [label:..., color:...]` \u2014 assigns individuals to organizational tiers, color-coded\n- `<->` \u2014 mutual influence; both nominated each other\n- `->` \u2014 one-way influence nomination\n- `-.-` \u2014 weak tie; neither party nominated the other in the survey\n- The force-directed layout clusters mutual-nomination groups and separates isolates\n\n## How to read\n\nAlex and Sam (tech leads) are mutually influential. Alex bridges down to Priya, Sam to Jordan \u2014 healthy knowledge flow across tiers. Priya and Kim form a strong senior IC hub. Dev and Nina have only weak ties (--. to the network), suggesting integration risk. Dev's only connection is a weak tie to Lee \u2014 a coaching opportunity before the next performance cycle."
1544
2187
  },
2188
+ {
2189
+ "slug": "state-checkout-composite-history",
2190
+ "diagram": "state",
2191
+ "title": "Checkout session with composite state and history",
2192
+ "description": "UML statechart for a checkout session with a composite Payment state, entry and exit activities, guards, and retry transitions.",
2193
+ "standard": "OMG UML 2.5.1",
2194
+ "tags": [
2195
+ "statechart",
2196
+ "uml",
2197
+ "composite",
2198
+ "guard",
2199
+ "checkout"
2200
+ ],
2201
+ "complexity": 3,
2202
+ "featured": false,
2203
+ "dsl": "stateDiagram-v2 [direction: LR]\n\n[*] --> Cart\nCart --> Checkout : begin_checkout\nCheckout --> Payment : submit_shipping [addressValid] / createIntent()\nCheckout --> Cart : edit_cart\nPayment --> Confirmed : captured / sendReceipt()\nPayment --> Checkout : failed [retries < 3] / showError()\nPayment --> Cancelled : failed [retries >= 3]\nConfirmed --> [*]\nCancelled --> [*]\n\nstate Payment {\n entry / startPaymentTimer()\n exit / stopPaymentTimer()\n [*] --> Authorizing\n Authorizing --> Capturing : authorized\n Authorizing --> Failed : declined\n Capturing --> Captured : capture_ok\n Capturing --> Failed : capture_error\n}\n\nnote right of Payment : Composite state hides gateway detail",
2204
+ "notes": "## Scenario\n\nCheckout is a lifecycle, not a process list: the session can move forward, retry, or terminate depending on events and guards. A composite state keeps the external lifecycle readable while still showing the internal payment substates.\n\n## Annotation key\n\n- `state Payment { ... }` creates the composite state.\n- `entry /` and `exit /` activities document side effects on state entry and exit.\n- Transition labels use the UML `trigger [guard] / action` format."
2205
+ },
1545
2206
  {
1546
2207
  "slug": "state-ecommerce-order",
1547
2208
  "diagram": "state",
@@ -1600,6 +2261,24 @@ If the LED doesn't light up, three things to check, in order: LED polarity (the
1600
2261
  "dsl": 'timeline "Acme \u2014 First Five Years"\nconfig: style = lollipop\n\n2020-06: "Founders meet at Y Combinator" [side: below]\n2020-11: milestone "Incorporation + pre-seed $1M" [side: above, color: #1565C0]\n2021-04: "First engineer hired" [side: below]\n2021-09: milestone "Product beta \u2014 50 design partners" [side: above, color: #2E7D32]\n2022-03: milestone "Seed round \u2014 $6M" [side: above, color: #1565C0]\n2022-11: "Team reaches 20 people" [side: below]\n2023-05: milestone "Platform v1 GA" [side: above, color: #6A1B9A]\n2023-10: milestone "Series A \u2014 $22M" [side: above, color: #1565C0]\n2024-06: "100th enterprise customer" [side: below]\n2025-01: milestone "Platform v2 launched" [side: above, color: #6A1B9A]',
1601
2262
  "notes": '## Scenario\n\nThe founder drops this into the first page of the fundraising deck. Funding rounds, product GAs, and growth markers alternate above/below the axis, which makes the parallel story \u2014 "we raised capital and shipped on time" \u2014 visible in one glance. Reviewers who only read the top of the page still get the two-line story.\n\n## Annotation key\n\n- `style = lollipop` \u2014 dot-on-stick markers alternating above/below axis\n- `milestone` \u2014 diamond marker for headline events\n- `[side: above|below]` \u2014 explicit placement\n- `[color: #hex]` \u2014 colour-code category (fundraising / product / team)\n\n## How to read\n\nTime runs left to right. Each marker is a single dated event; *milestone* markers are the diamond-shaped headline items (fundraising, GAs). Colour carries category: blue = fundraising, purple = product, green = early commercial traction. Events below the axis are supporting context (people, growth stats); events above are the announceable headlines.'
1602
2263
  },
2264
+ {
2265
+ "slug": "timeline-litigation-case",
2266
+ "diagram": "timeline",
2267
+ "title": "Litigation case timeline",
2268
+ "description": "Lollipop litigation timeline with pleadings, discovery, motions, settlement conference, and trial milestones.",
2269
+ "standard": "Legal case chronology convention",
2270
+ "tags": [
2271
+ "timeline",
2272
+ "litigation",
2273
+ "lollipop",
2274
+ "legal",
2275
+ "milestones"
2276
+ ],
2277
+ "complexity": 2,
2278
+ "featured": false,
2279
+ "dsl": 'timeline "Smith v. Acme - Case Timeline"\nconfig: style = lollipop\n\n2025-01-10: milestone "Complaint filed" [side: above, color: #1565C0]\n2025-02-07: "Answer due" [side: below]\n2025-03-01 - 2025-06-30: "Fact discovery" [category: "discovery"]\n2025-04-15: "Plaintiff deposition" [side: above]\n2025-05-22: "Expert reports exchanged" [side: below]\n2025-07-15: milestone "Summary judgment motion" [side: above, color: #C62828]\n2025-08-20: "Mediation" [side: below]\n2025-10-06: milestone "Trial setting" [side: above, color: #2E7D32]',
2280
+ "notes": "## Scenario\n\nLitigation timelines mix hard deadlines, long phases, and high-stakes milestones. The lollipop style keeps single events readable while still allowing discovery to appear as a range.\n\n## Annotation key\n\n- `config: style = lollipop` selects the narrative timeline style.\n- Date ranges render as spans.\n- `milestone` emphasizes critical procedural events."
2281
+ },
1603
2282
  {
1604
2283
  "slug": "timeline-product-launch",
1605
2284
  "diagram": "timeline",
@@ -1618,6 +2297,24 @@ If the LED doesn't light up, three things to check, in order: LED polarity (the
1618
2297
  "dsl": 'timeline "Platform v2 Launch"\nconfig: style = gantt\n\n2025-07-01 - 2025-08-15: "Engineering build" [category: "engineering"]\n2025-07-15 - 2025-08-31: "Design polish" [category: "design"]\n2025-08-01 - 2025-09-10: "Marketing collateral" [category: "marketing"]\n2025-08-20: milestone "Feature freeze" [color: #E53935]\n2025-08-20 - 2025-09-05: "QA hardening" [category: "engineering"]\n2025-09-01 - 2025-09-12: "Press embargo outreach" [category: "marketing"]\n2025-09-15: milestone "Public launch" [color: #2E7D32]',
1619
2298
  "notes": '## Scenario\n\nThe launch PM shares this in weekly exec status. Overlapping bars show where workstreams parallelize (design polishing while engineering still builds) and the feature-freeze diamond makes the handoff between build and QA unmissable. The second milestone (public launch) anchors the entire timeline and is the reason every other bar exists.\n\n## Annotation key\n\n- `DATE - DATE: "Label"` \u2014 range (bar) event\n- `DATE: milestone "Label"` \u2014 point milestone (diamond)\n- `[category: \u2026]` \u2014 group colour in the gantt legend\n- `[color: #hex]` \u2014 explicit marker colour\n\n## How to read\n\nTime flows left to right. Horizontal bars are continuous work; diamonds are instantaneous events. Overlapping bars mean two teams are working simultaneously \u2014 fine, so long as they coordinate. The red *Feature freeze* marks the transition from net-new work to hardening; any engineering bar extending past it needs an exception. The green *Public launch* is the terminal milestone every other bar is serving.'
1620
2299
  },
2300
+ {
2301
+ "slug": "timing-i2c-read-burst",
2302
+ "diagram": "timing",
2303
+ "title": "I2C read burst timing",
2304
+ "description": "WaveDrom-compatible timing diagram for an I2C register read with address, repeated start, data bytes, ACK, and NACK phases.",
2305
+ "standard": "WaveDrom signal notation",
2306
+ "tags": [
2307
+ "timing",
2308
+ "i2c",
2309
+ "waveform",
2310
+ "bus",
2311
+ "embedded"
2312
+ ],
2313
+ "complexity": 2,
2314
+ "featured": false,
2315
+ "dsl": 'timing "I2C Read Burst" [hscale: 1.2]\n[Control]\nSCL: pppppppppppp\nSTART: 100000100001\n---\n[Bus]\nSDA: x==========x data: ["ADDR+W","ACK","REG","ACK","REP START","ADDR+R","ACK","DATA0","ACK","DATA1","NACK"]\n---\n[Status]\nBUSY: 111111111110',
2316
+ "notes": "## Scenario\n\nI2C reads are visually clearer as timing diagrams than as prose. The repeated start and ACK/NACK phases are where firmware and hardware teams most often talk past each other.\n\n## Annotation key\n\n- `[Control]`, `[Bus]`, and `[Status]` group related signals.\n- `p` renders clock pulses.\n- `=` segments carry bus labels from the `data:` list."
2317
+ },
1621
2318
  {
1622
2319
  "slug": "timing-spi-transaction",
1623
2320
  "diagram": "timing",
@@ -1771,7 +2468,7 @@ var SYNTAX = {
1771
2468
  },
1772
2469
  "decisiontree": {
1773
2470
  "title": "Decision tree diagram",
1774
- "content": '## 1. Your first decision tree\n\nThe smallest useful decision tree: a root question with two branches.\n\n```\ndecisiontree "Laptop troubleshoot"\n\nquestion "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```\ndecisiontree "Triage \u2014 chest pain onset"\n\nq "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```\ndecisiontree "Pain level triage"\n\nquestion "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```\ndecisiontree:decision "Cloud vendor selection"\n\ndecision "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```\ndecisiontree:ml "Iris classification (CART)"\ndirection: top-down\nimpurity: gini\n\nsplit "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---'
2471
+ "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---'
1775
2472
  },
1776
2473
  "flowchart": {
1777
2474
  "title": "Flowchart",
@@ -1803,23 +2500,23 @@ var SYNTAX = {
1803
2500
  },
1804
2501
  "erd": {
1805
2502
  "title": "ERD (Entity-Relationship Diagram)",
1806
- "content": '## 1. Your first ERD\n\nThe smallest useful ERD: a parent table referenced by a child via foreign key.\n\n```\nerd\ntable Customer {\n customer_id int PK\n email varchar UK\n}\ntable Order {\n order_id int PK\n customer_id int FK -> Customer.customer_id\n}\nref Order.customer_id many-mandatory -- one-mandatory Customer.customer_id : "places"\n```\n\nFour rules cover 80 % of usage:\n\n1. Start with `erd`. Optional headers `title:`, `direction: LR | TB`.\n2. Each table is a block: `table Name { col type marker }`. Markers are `PK`, `FK`, `UK`, `NN` (or `*`) for NOT NULL.\n3. Inline foreign-key target: append `FK -> Other.column` to a column. The renderer adds an `FK` pill automatically.\n4. Connect tables with a `ref` line: `ref Source <left-card> -- <right-card> Target [: "label"]`. Cardinality is one of `one-mandatory`, `one-optional`, `many-mandatory`, `many-optional` (named form), `1..1` / `0..1` / `1..N` / `0..N` (Min-Max), or Mermaid glyphs (`}o--||`, etc.) as alias.\n\n> Comments use `//` or `#`. Block forms can be either multi-line (one column per line) or inline (`table A { id int PK; name varchar }`).\n\n---\n\n## 2. Cardinality glyphs\n\nCrow\'s foot encodes the cardinality at each end of the relationship line:\n\n| Glyph | Reading | Min..Max | Named token |\n|---|---|---|---|\n| `\u2500\u2503` (perpendicular bar) | Exactly one (mandatory one) | 1..1 | `one-mandatory` |\n| `\u2500\u25CB` (open circle) | Zero or one (optional one) | 0..1 | `one-optional` |\n| `\u2500\u2503<` (bar + crow\'s foot) | One or more (mandatory many) | 1..N | `many-mandatory` |\n| `\u2500\u25CB<` (circle + crow\'s foot) | Zero or more (optional many) | 0..N | `many-optional` |\n\nEach end of a line is annotated independently. A typical 1:N relationship reads "exactly one CUSTOMER places zero-or-more ORDERs":\n\n```\nref Order.customer_id many-mandatory -- one-mandatory Customer.customer_id : "places"\n```\n\nReading right-to-left: the **right end** of the line attaches to Customer with a single bar (one-mandatory); the **left end** attaches to Order with a bar + crow\'s foot (many-mandatory).\n\n---\n\n## 3. Mermaid alias\n\nIf you already use Mermaid `erDiagram`, the same glyphs work as input:\n\n```\nerd\ntable Customer { customer_id int PK; email varchar UK }\ntable Order { order_id int PK; customer_id int FK -> Customer.customer_id }\ntable Product { product_id int PK; name varchar }\n\nref Order }o--|| Customer : "places"\nref Order }o--o{ Product : "contains"\n```\n\n| Mermaid token | Schematex meaning |\n|---|---|\n| `\\|o` left / `o\\|` right | 0..1 |\n| `\\|\\|` | 1..1 |\n| `}o` left / `o{` right | 0..N |\n| `}\\|` left / `\\|{` right | 1..N |\n| `--` | identifying / solid line |\n| `..` | non-identifying / dashed line |\n\n---\n\n## 4. Identifying vs non-identifying\n\nUse `--` for identifying relationships (solid line) and `..` for non-identifying (dashed):\n\n```\nerd\ntable User { id int PK; email varchar }\ntable SessionLog { id int PK; user_id int FK -> User.id; created_at timestamp }\n\nref SessionLog.user_id many-optional .. one-optional User.id : "logs (optional FK)"\n```\n\nThe dashed line follows the Barker / IDEF1X non-identifying convention.\n\n---\n\n## 5. Composite primary keys (associative tables)\n\nAssociative ("junction") tables resolve M:N relationships. Mark each PK + FK column with both `PK` and `FK`:\n\n```\nerd\ntable Student { student_id int PK; name varchar }\ntable Course { course_id int PK; title varchar }\ntable Enrollment {\n student_id int PK FK -> Student.student_id\n course_id int PK FK -> Course.course_id\n grade char\n}\nref Enrollment.student_id many-mandatory -- one-mandatory Student.student_id\nref Enrollment.course_id many-mandatory -- one-mandatory Course.course_id\n```\n\nSchematex renders both pills (PK + FK) on the same row. The PK underline applies to the column name when any `PK` marker is present.\n\n---\n\n## 6. Layout direction\n\n`direction: LR` (default) places parent tables left, child tables right. `direction: TB` flips to top-to-bottom \u2014 useful for narrow embeds:\n\n```\nerd\ntitle: "Library (top-down)"\ndirection: TB\n\ntable Member { id int PK; name varchar; email varchar UK }\ntable Book { id int PK; title varchar; author varchar }\ntable Loan { id int PK; member_id int FK -> Member.id; book_id int FK -> Book.id; due_date date }\n\nref Loan.member_id many-mandatory -- one-mandatory Member.id : "borrowed by"\nref Loan.book_id many-mandatory -- one-mandatory Book.id : "of"\n```\n\nLayered orthogonal Manhattan routing with single-bend edges. v0.1 uses appearance-order stacking inside layers; barycenter crossing-reduction is deferred to v0.2.\n\n---\n\n## 7. Notation modes\n\nThe DSL header can pin the notation:\n\n```\nerd\nnotation: crowsfoot // default \u2014 only mode supported in v0.1\n```\n\n`notation: chen` (rectangle-diamond-oval, weak entities, ternary, ISA) and `notation: barker` (per-half dashed) are documented in `27-ERD-STANDARD.md` but rejected by the parser today. Targets v0.2.\n\n---\n\n## 8. Limitations of v0.1\n\n- **Crow\'s foot only.** Chen and Barker rendering deferred.\n- **No edge-crossing minimization.** Layered placement uses declaration order within layers; large ERDs (10+ tables, dense FKs) will show some crossings. Reorder `table` blocks if a specific layout matters.\n- **No self-referential C-loops.** Recursive relationships (`Employee.manager_id -> Employee.id`) parse but route as straight orthogonal lines, not the canonical C-shape.\n- **No M:N auto-resolution.** Express the associative entity explicitly (this is the standard practice in production schemas anyway).\n\nFor the full standard reference, see `docs/reference/27-ERD-STANDARD.md`.'
2503
+ "content": '## 1. Your first ERD\n\nThe smallest useful ERD: a parent table referenced by a child via foreign key.\n\n```\nerd\ntable Customer {\n customer_id int PK\n email varchar UK\n}\ntable Order {\n order_id int PK\n customer_id int FK -> Customer.customer_id\n}\nref Order.customer_id many-mandatory -- one-mandatory Customer.customer_id : "places"\n```\n\nFour rules cover 80 % of usage:\n\n1. Start with `erd`. Optional headers `title:`, `direction: LR | TB`.\n2. Each table is a block: `table Name { col type marker }`. Markers are `PK`, `FK`, `UK`, `NN` (or `*`) for NOT NULL.\n3. Inline foreign-key target: append `FK -> Other.column` to a column. The renderer adds an `FK` pill automatically.\n4. Connect tables with a `ref` line: `ref Source <left-card> -- <right-card> Target [: "label"]`. Cardinality is one of `one-mandatory`, `one-optional`, `many-mandatory`, `many-optional` (named form), `1..1` / `0..1` / `1..N` / `0..N` (Min-Max), or Mermaid glyphs (`}o--||`, etc.) as alias.\n\n> Comments use `//` or `#`. Block forms can be either multi-line (one column per line) or inline (`table A { id int PK; name varchar }`).\n\n---\n\n## 2. Cardinality glyphs\n\nCrow\'s foot encodes the cardinality at each end of the relationship line:\n\n| Glyph | Reading | Min..Max | Named token |\n|---|---|---|---|\n| `\u2500\u2503` (perpendicular bar) | Exactly one (mandatory one) | 1..1 | `one-mandatory` |\n| `\u2500\u25CB` (open circle) | Zero or one (optional one) | 0..1 | `one-optional` |\n| `\u2500\u2503<` (bar + crow\'s foot) | One or more (mandatory many) | 1..N | `many-mandatory` |\n| `\u2500\u25CB<` (circle + crow\'s foot) | Zero or more (optional many) | 0..N | `many-optional` |\n\nEach end of a line is annotated independently. A typical 1:N relationship reads "exactly one CUSTOMER places zero-or-more ORDERs":\n\n```\nref Order.customer_id many-mandatory -- one-mandatory Customer.customer_id : "places"\n```\n\nReading right-to-left: the **right end** of the line attaches to Customer with a single bar (one-mandatory); the **left end** attaches to Order with a bar + crow\'s foot (many-mandatory).\n\n---\n\n## 3. Mermaid alias\n\nIf you already use Mermaid `erDiagram`, the same glyphs work as input:\n\n```\nerd\ntable Customer { customer_id int PK; email varchar UK }\ntable Order { order_id int PK; customer_id int FK -> Customer.customer_id }\ntable Product { product_id int PK; name varchar }\n\nref Order }o--|| Customer : "places"\nref Order }o--o{ Product : "contains"\n```\n\n| Mermaid token | Schematex meaning |\n|---|---|\n| `\\|o` left / `o\\|` right | 0..1 |\n| `\\|\\|` | 1..1 |\n| `}o` left / `o{` right | 0..N |\n| `}\\|` left / `\\|{` right | 1..N |\n| `--` | identifying / solid line |\n| `..` | non-identifying / dashed line |\n\n---\n\n## 4. Identifying vs non-identifying\n\nUse `--` for identifying relationships (solid line) and `..` for non-identifying (dashed):\n\n```\nerd\ntable User { id int PK; email varchar }\ntable SessionLog { id int PK; user_id int FK -> User.id; created_at timestamp }\n\nref SessionLog.user_id many-optional .. one-optional User.id : "logs (optional FK)"\n```\n\nThe dashed line follows the Barker / IDEF1X non-identifying convention.\n\n---\n\n## 5. Composite primary keys (associative tables)\n\nAssociative ("junction") tables resolve M:N relationships. Mark each PK + FK column with both `PK` and `FK`:\n\n```\nerd\ntable Student { student_id int PK; name varchar }\ntable Course { course_id int PK; title varchar }\ntable Enrollment {\n student_id int PK FK -> Student.student_id\n course_id int PK FK -> Course.course_id\n grade char\n}\nref Enrollment.student_id many-mandatory -- one-mandatory Student.student_id\nref Enrollment.course_id many-mandatory -- one-mandatory Course.course_id\n```\n\nSchematex renders both pills (PK + FK) on the same row. The PK underline applies to the column name when any `PK` marker is present.\n\n---\n\n## 6. Layout direction\n\n`direction: LR` (default) places parent tables left, child tables right. `direction: TB` flips to top-to-bottom \u2014 useful for narrow embeds:\n\n```\nerd\ntitle: "Library (top-down)"\ndirection: TB\n\ntable Member { id int PK; name varchar; email varchar UK }\ntable Book { id int PK; title varchar; author varchar }\ntable Loan { id int PK; member_id int FK -> Member.id; book_id int FK -> Book.id; due_date date }\n\nref Loan.member_id many-mandatory -- one-mandatory Member.id : "borrowed by"\nref Loan.book_id many-mandatory -- one-mandatory Book.id : "of"\n```\n\nLayered orthogonal Manhattan routing with single-bend edges. v0.1 uses appearance-order stacking inside layers; barycenter crossing-reduction is deferred to v0.2.\n\n---\n\n## 7. Notation modes\n\nThe DSL header can pin the notation:\n\n```\nerd\nnotation: crowsfoot // default \u2014 only mode supported in v0.1\n```\n\n`notation: chen` (rectangle-diamond-oval, weak entities, ternary, ISA) and `notation: barker` (per-half dashed) are documented in `27-ERD-STANDARD.md` but rejected by the parser today. Targets v0.2.\n\n---\n\n## 8. Limitations of v0.1\n\n- **Crow\'s foot only.** Chen and Barker rendering deferred.\n- **No edge-crossing minimization.** Layered placement uses declaration order within layers; large ERDs (10+ tables, dense FKs) will show some crossings. Reorder `table` blocks if a specific layout matters.\n- **No self-referential C-loops.** Recursive relationships (`Employee.manager_id -> Employee.id`) parse but route as straight orthogonal lines, not the canonical C-shape.\n- **No M:N auto-resolution.** Express the associative entity explicitly (this is the standard practice in production schemas anyway).\n\nFor the full standard reference, see `docs/reference/27-ERD-STANDARD.md`.\n\n---\n\n## Related examples\n\nReady-to-use scenarios from the examples gallery:'
1807
2504
  },
1808
2505
  "breadboard": {
1809
2506
  "title": "Breadboard / Physical Wiring",
1810
- "content": "## 1. Your first breadboard\n\nThree sections: a one-line `breadboard` header, a `parts` block, and a `wires` block. Optional `board:` and `title:` lines come right after the header.\n\n```\nbreadboard\nparts\n uno: mcu uno @beside-left\n r1: resistor 220 @5e..9e\n d1: led red @10e..10f\n\nwires\n uno:5V --red-- @+t1\n uno:GND --black-- @-t1\n uno:D13 --yellow-- @5a\n @10j --black-- @-t1\n```\n\nEvery part is `id: kind [args] @placement`. Every wire is `<endpoint> --color-- <endpoint>`. That's the whole grammar.\n\n---\n\n## 2. Coordinates\n\nBreadboards have a 2D address grid. Schematex coordinates always start with `@`.\n\n| Form | Meaning | Example |\n|---|---|---|\n| `@<col><row>` | Main grid hole. Rows `a\u2013e` (top half), `f\u2013j` (bottom half). | `@5e`, `@12g` |\n| `@+t<col>` | Top **positive** rail (red stripe). | `@+t8` |\n| `@-t<col>` | Top **negative / GND** rail (blue stripe). | `@-t8` |\n| `@+b<col>` | Bottom positive rail. | `@+b14` |\n| `@-b<col>` | Bottom negative rail. | `@-b14` |\n| `@<a>..<b>` | Span \u2014 used in part placement (resistor, diode, LED). | `@5e..9e` |\n| `@beside-left` | Off-board placement for MCU boards. | `mcu uno @beside-left` |\n\nMini boards (`board: mini`) have **no power rails** \u2014 `@+t\u2026` / `@-b\u2026` are rejected by the parser.\n\n---\n\n## 3. Board sizes\n\n```\nbreadboard\nboard: half // default \u2014 30 columns, 400 tie-points, rails (continuous)\n```\n\n| Form | Tie points | Columns | Power rails |\n|---|---|---|---|\n| `mini` | 170 | 17 | none |\n| `half` (default) | 400 | 30 | continuous |\n| `full` | 830 | 63 | break at column 30/31 |\n\n> **Pitfall** \u2014 on full-size boards the rails break at the middle. If your circuit uses both halves you must jumper the rails together explicitly.\n\n---\n\n## 4. Parts catalog\n\nEach part is `id: <kind> [args] @<placement>`. The catalog covers the most common Arduino / ESP32 maker components:\n\n**Discrete components** (sit on the breadboard):\n\n| DSL | Args | Example |\n|---|---|---|\n| `resistor` | `value` (\u03A9; supports `k`/`M`) | `r1: resistor 220 @5e..9e` |\n| `led` | `color` (red/green/blue/yellow/white/orange) | `d1: led red @10e..10f` |\n| `cap-elec` | \u2014 | `c1: cap-elec @4e..4f` |\n| `cap-ceramic` | \u2014 | `c2: cap-ceramic @6e..6f` |\n| `diode` | \u2014 | `d2: diode @5e..8e` |\n| `button` | \u2014 | `btn: button @8e` |\n| `dip` | `pins=N` | `ic: dip pins=8 @4e` |\n| `header` | `pins=N` | `h1: header pins=4 @20a` |\n\n**Microcontroller boards** (placed beside / above / below the substrate):\n\n| DSL | Pin labels |\n|---|---|\n| `mcu uno` | `5V`, `3V3`, `GND`, `VIN`, `RST`, `D2\u2026D13`, `A0\u2026A5`, `RX`, `TX` |\n| `mcu nano` | Subset of Uno labels |\n| `mcu esp32` | `3V3`, `GND`, `VIN`, `GPIO2`, `GPIO4`, `GPIO5`, `GPIO12\u2026GPIO33` |\n| `mcu pico` | Same generic GPIO labels |\n\n**Sensors / displays / actuators** (modules sit on the breadboard with pin row anchored at the supplied coordinate):\n\n| DSL | Pins |\n|---|---|\n| `sensor hcsr04` | `VCC`, `TRIG`, `ECHO`, `GND` |\n| `sensor dht11` / `sensor dht22` | `VCC`, `DATA`, `GND` |\n| `display oled-ssd1306` | `GND`, `VCC`, `SCL`, `SDA` |\n| `display lcd-1602-i2c` | `GND`, `VCC`, `SDA`, `SCL` |\n| `module rotary-ky040` | `CLK`, `DT`, `SW`, `VCC`, `GND` |\n| `actuator servo-sg90` | `GND`, `VCC`, `SIG` |\n\nResistor color bands are decorated automatically from `value` \u2014 `220` \u2192 red-red-brown-gold, `10000` \u2192 brown-black-orange-gold.\n\n---\n\n## 5. Wires\n\nEvery wire connects two endpoints. An endpoint is either a **part pin** (`partId:pinName`) or a **breadboard coordinate** (`@\u2026`).\n\n```\nwires\n uno:5V --red-- @+t1\n uno:GND --black-- @-t1\n uno:D9 --yellow-- @9c\n @9a --green-- @+t9\n```\n\n| Color | Conventional role |\n|---|---|\n| `red` | +V (5V, 3.3V, VCC) |\n| `black` / `blue` | GND |\n| `yellow` / `orange` / `green` / `white` / `purple` | Signal |\n| `brown` / `grey` | Arbitrary signal |\n\nColor is purely visual \u2014 the engine does not validate it against electrical role.\n\nFor visually crowded boards, `via @<coord>` lets you pin an intermediate hole that biases the B\xE9zier control points:\n\n```\nwires\n uno:D13 --yellow-- @9a via @8c\n```\n\nMost wires don't need `via` \u2014 the layout engine produces a natural arc on its own.\n\n---\n\n## 6. Sensor with pull-up resistor (DHT11 motif)\n\nThe iconic Arduino tutorial pattern: a 10 k\u03A9 pull-up between VCC and the sensor's data line.\n\n```\nbreadboard\nboard: half\ntitle: \"DHT11 + 10k\u03A9 pull-up\"\n\nparts\n uno: mcu uno @beside-left\n s1: sensor dht11 @6a\n r1: resistor 10000 @8e..14e\n\nwires\n s1:VCC --red-- @+t1\n s1:GND --black-- @-t1\n s1:DATA --yellow-- @8e\n @14e --red-- @+t14\n uno:5V --red-- @+t1\n uno:GND --black-- @-t1\n uno:D2 --yellow-- @8a\n```\n\n---\n\n## 7. ESP32 + I\xB2C OLED\n\nESP32 runs at 3.3 V (not 5 V). I\xB2C convention: green = SDA, white = SCL.\n\n```\nbreadboard\nboard: half\ntitle: \"ESP32 + SSD1306 OLED I\xB2C\"\n\nparts\n esp: mcu esp32 @beside-left\n oled: display oled-ssd1306 @8a\n\nwires\n oled:GND --black-- @-t1\n oled:VCC --red-- @+t1\n oled:SCL --white-- @10c\n oled:SDA --green-- @11c\n esp:3V3 --red-- @+t1\n esp:GND --black-- @-t1\n esp:GPIO22 --white-- @10c\n esp:GPIO21 --green-- @11c\n```\n\n---\n\n## 8. Limitations of v0.1\n\n- **No leader-line callouts** \u2014 reference designators (R1, C2) are drawn near the part body. Off-board callout boxes are deferred.\n- **No `.fzz` import** \u2014 Schematex consumes only its own DSL; Fritzing files are not parsed.\n- **No simulation** \u2014 this is a renderer, not a Wokwi-style simulator. Component-value validation (Ohm's law, current limits) is out of scope.\n- **No PCB / schematic round-trip** \u2014 `breadboard` and `circuit` are independent engines. Authoring two views of the same prototype currently means writing two DSLs.\n- **Fixed parts catalog** \u2014 user-defined part types are deferred. v0.1 ships the maker-tutorial 80% catalog (resistors, LEDs, caps, DIPs, headers, four MCU families, six sensor / display / actuator modules).\n- **Power-rail break visual** \u2014 full-size boards mark the 30/31 break with a hole-wide gap; the rail stripes still draw through the gap (cosmetic)."
2507
+ "content": "## 1. Your first breadboard\n\nThree sections: a one-line `breadboard` header, a `parts` block, and a `wires` block. Optional `board:` and `title:` lines come right after the header.\n\n```\nbreadboard\nparts\n uno: mcu uno @beside-left\n r1: resistor 220 @5e..9e\n d1: led red @10e..10f\n\nwires\n uno:5V --red-- @+t1\n uno:GND --black-- @-t1\n uno:D13 --yellow-- @5a\n @10j --black-- @-t1\n```\n\nEvery part is `id: kind [args] @placement`. Every wire is `<endpoint> --color-- <endpoint>`. That's the whole grammar.\n\n---\n\n## 2. Coordinates\n\nBreadboards have a 2D address grid. Schematex coordinates always start with `@`.\n\n| Form | Meaning | Example |\n|---|---|---|\n| `@<col><row>` | Main grid hole. Rows `a\u2013e` (top half), `f\u2013j` (bottom half). | `@5e`, `@12g` |\n| `@+t<col>` | Top **positive** rail (red stripe). | `@+t8` |\n| `@-t<col>` | Top **negative / GND** rail (blue stripe). | `@-t8` |\n| `@+b<col>` | Bottom positive rail. | `@+b14` |\n| `@-b<col>` | Bottom negative rail. | `@-b14` |\n| `@<a>..<b>` | Span \u2014 used in part placement (resistor, diode, LED). | `@5e..9e` |\n| `@beside-left` | Off-board placement for MCU boards. | `mcu uno @beside-left` |\n\nMini boards (`board: mini`) have **no power rails** \u2014 `@+t\u2026` / `@-b\u2026` are rejected by the parser.\n\n---\n\n## 3. Board sizes\n\n```\nbreadboard\nboard: half // default \u2014 30 columns, 400 tie-points, rails (continuous)\n```\n\n| Form | Tie points | Columns | Power rails |\n|---|---|---|---|\n| `mini` | 170 | 17 | none |\n| `half` (default) | 400 | 30 | continuous |\n| `full` | 830 | 63 | break at column 30/31 |\n\n> **Pitfall** \u2014 on full-size boards the rails break at the middle. If your circuit uses both halves you must jumper the rails together explicitly.\n\n---\n\n## 4. Parts catalog\n\nEach part is `id: <kind> [args] @<placement>`. The catalog covers the most common Arduino / ESP32 maker components:\n\n**Discrete components** (sit on the breadboard):\n\n| DSL | Args | Example |\n|---|---|---|\n| `resistor` | `value` (\u03A9; supports `k`/`M`) | `r1: resistor 220 @5e..9e` |\n| `led` | `color` (red/green/blue/yellow/white/orange) | `d1: led red @10e..10f` |\n| `cap-elec` | \u2014 | `c1: cap-elec @4e..4f` |\n| `cap-ceramic` | \u2014 | `c2: cap-ceramic @6e..6f` |\n| `diode` | \u2014 | `d2: diode @5e..8e` |\n| `button` | \u2014 | `btn: button @8e` |\n| `dip` | `pins=N` | `ic: dip pins=8 @4e` |\n| `header` | `pins=N` | `h1: header pins=4 @20a` |\n\n**Microcontroller boards** (placed beside / above / below the substrate):\n\n| DSL | Pin labels |\n|---|---|\n| `mcu uno` | `5V`, `3V3`, `GND`, `VIN`, `RST`, `D2\u2026D13`, `A0\u2026A5`, `RX`, `TX` |\n| `mcu nano` | Subset of Uno labels |\n| `mcu esp32` | `3V3`, `GND`, `VIN`, `GPIO2`, `GPIO4`, `GPIO5`, `GPIO12\u2026GPIO33` |\n| `mcu pico` | Same generic GPIO labels |\n\n**Sensors / displays / actuators** (modules sit on the breadboard with pin row anchored at the supplied coordinate):\n\n| DSL | Pins |\n|---|---|\n| `sensor hcsr04` | `VCC`, `TRIG`, `ECHO`, `GND` |\n| `sensor dht11` / `sensor dht22` | `VCC`, `DATA`, `GND` |\n| `display oled-ssd1306` | `GND`, `VCC`, `SCL`, `SDA` |\n| `display lcd-1602-i2c` | `GND`, `VCC`, `SDA`, `SCL` |\n| `module rotary-ky040` | `CLK`, `DT`, `SW`, `VCC`, `GND` |\n| `actuator servo-sg90` | `GND`, `VCC`, `SIG` |\n\nResistor color bands are decorated automatically from `value` \u2014 `220` \u2192 red-red-brown-gold, `10000` \u2192 brown-black-orange-gold.\n\n---\n\n## 5. Wires\n\nEvery wire connects two endpoints. An endpoint is either a **part pin** (`partId:pinName`) or a **breadboard coordinate** (`@\u2026`).\n\n```\nwires\n uno:5V --red-- @+t1\n uno:GND --black-- @-t1\n uno:D9 --yellow-- @9c\n @9a --green-- @+t9\n```\n\n| Color | Conventional role |\n|---|---|\n| `red` | +V (5V, 3.3V, VCC) |\n| `black` / `blue` | GND |\n| `yellow` / `orange` / `green` / `white` / `purple` | Signal |\n| `brown` / `grey` | Arbitrary signal |\n\nColor is purely visual \u2014 the engine does not validate it against electrical role.\n\nFor visually crowded boards, `via @<coord>` lets you pin an intermediate hole that biases the B\xE9zier control points:\n\n```\nwires\n uno:D13 --yellow-- @9a via @8c\n```\n\nMost wires don't need `via` \u2014 the layout engine produces a natural arc on its own.\n\n---\n\n## 6. Sensor with pull-up resistor (DHT11 motif)\n\nThe iconic Arduino tutorial pattern: a 10 k\u03A9 pull-up between VCC and the sensor's data line.\n\n```\nbreadboard\nboard: half\ntitle: \"DHT11 + 10k\u03A9 pull-up\"\n\nparts\n uno: mcu uno @beside-left\n s1: sensor dht11 @6a\n r1: resistor 10000 @8e..14e\n\nwires\n s1:VCC --red-- @+t1\n s1:GND --black-- @-t1\n s1:DATA --yellow-- @8e\n @14e --red-- @+t14\n uno:5V --red-- @+t1\n uno:GND --black-- @-t1\n uno:D2 --yellow-- @8a\n```\n\n---\n\n## 7. ESP32 + I\xB2C OLED\n\nESP32 runs at 3.3 V (not 5 V). I\xB2C convention: green = SDA, white = SCL.\n\n```\nbreadboard\nboard: half\ntitle: \"ESP32 + SSD1306 OLED I\xB2C\"\n\nparts\n esp: mcu esp32 @beside-left\n oled: display oled-ssd1306 @8a\n\nwires\n oled:GND --black-- @-t1\n oled:VCC --red-- @+t1\n oled:SCL --white-- @10c\n oled:SDA --green-- @11c\n esp:3V3 --red-- @+t1\n esp:GND --black-- @-t1\n esp:GPIO22 --white-- @10c\n esp:GPIO21 --green-- @11c\n```\n\n---\n\n## 8. Limitations of v0.1\n\n- **No leader-line callouts** \u2014 reference designators (R1, C2) are drawn near the part body. Off-board callout boxes are deferred.\n- **No `.fzz` import** \u2014 Schematex consumes only its own DSL; Fritzing files are not parsed.\n- **No simulation** \u2014 this is a renderer, not a Wokwi-style simulator. Component-value validation (Ohm's law, current limits) is out of scope.\n- **No PCB / schematic round-trip** \u2014 `breadboard` and `circuit` are independent engines. Authoring two views of the same prototype currently means writing two DSLs.\n- **Fixed parts catalog** \u2014 user-defined part types are deferred. v0.1 ships the maker-tutorial 80% catalog (resistors, LEDs, caps, DIPs, headers, four MCU families, six sensor / display / actuator modules).\n- **Power-rail break visual** \u2014 full-size boards mark the 30/31 break with a hole-wide gap; the rail stripes still draw through the gap (cosmetic).\n\n---\n\n## Related examples\n\nReady-to-use scenarios from the examples gallery:"
1811
2508
  },
1812
2509
  "bpmn": {
1813
2510
  "title": "BPMN / Business Process",
1814
- "content": '## 1. Your first BPMN diagram\n\nThree sections: a one-line `bpmn` header, one or more `pool { \u2026 }` blocks, and a `flows` block. Inside each pool you put `lane { \u2026 }` blocks, and inside each lane you list flow objects as `id: kind "label"`.\n\n```\nbpmn\npool "Service" {\n lane "Worker" {\n A: start "Request"\n B: task service "Process"\n F: end "Done"\n }\n}\n\nflows\nA --> B\nB --> F\n```\n\nEvery flow object starts with an **id**, a colon, the **kind**, an optional sub-keyword (trigger / marker / gateway type), and a quoted **label**. Ids are referenced by flow lines.\n\n---\n\n## 2. Pools and lanes\n\nA **pool** represents one participant \u2014 an organisation, a department, or a system. A **lane** subdivides a pool into roles. The pool label is rendered rotated 90\xB0 on the left edge of a horizontal pool.\n\n```\npool "Customer" blackbox // black-box pool \u2014 no internal flow\npool "Bank" {\n lane "Clerk" { \u2026 }\n lane "Underwriter" { \u2026 }\n}\n```\n\nA **black-box pool** is a participant whose internal process you don\'t model \u2014 typically an external customer or partner. Black-box pools must contain zero flow objects (Schematex enforces this in the parser).\n\nSequence flow (`-->`) is **not allowed** to cross pool boundaries. Use a message flow (`~~>`) for cross-pool communication.\n\n---\n\n## 3. Events\n\nEvents are circles. Stroke weight encodes lifecycle role:\n\n| Kind | Stroke | DSL |\n|---|---|---|\n| Start | thin (1px) | `start` |\n| Intermediate | thin double ring | `intermediate` |\n| End | thick (3px) | `end` |\n\nThe optional **trigger** keyword adds an inner glyph. v0.1 supports the three most common triggers \u2014 `none` (no glyph), `message` (envelope), and `timer` (clock face):\n\n```\nA: start // none-trigger\nA: start message "Inbound" // message-catch (unfilled envelope)\nT: intermediate timer "60 min" // timer (clock face)\nF: end "Done"\n```\n\nFilled glyph = **throw**, unfilled glyph = **catch**. Schematex picks the right fill automatically based on event kind.\n\n---\n\n## 4. Activities \u2014 tasks and subprocesses\n\nActivities are rounded rectangles. The two v0.1 forms are `task` and a **collapsed** subprocess (the `+` marker indicates expandable detail):\n\n```\nB: task "Generic abstract task"\nU: task user "User decides"\nS: task service "API call"\nSE: task send "Send email"\nRE: task receive "Wait for reply"\nM: task manual "Hand-stamp the form"\nSC: task script "Run rule engine"\n\nX: subprocess "Verify identity" collapsed\n```\n\nThe **task marker** (small icon top-left) communicates *who or what* performs the work \u2014 a person (`user`), a software service (`service`), an outbound message (`send`), an inbound wait (`receive`), an out-of-system manual step (`manual`), or an automated script (`script`). When in doubt, omit the marker \u2014 it defaults to abstract.\n\n---\n\n## 5. Gateways\n\nGateways are diamonds. The inner glyph encodes branching semantics:\n\n| Kind | Glyph | Meaning | DSL |\n|---|---|---|---|\n| Exclusive (XOR) | **X** | Take exactly one outgoing branch (data-based) | `gateway xor` |\n| Inclusive (OR) | **O** | Take one or more outgoing branches | `gateway or` |\n| Parallel (AND) | **+** | Take all outgoing branches concurrently | `gateway and` |\n| Event-based | pentagon-in-circle | Pick the branch whose event fires first | `gateway event` |\n\nSchematex uses Bruce Silver\'s **X glyph** for XOR by default \u2014 that\'s the convention real BPMN audits expect. Most diagrams use XOR (data branch) and AND (parallel split / join); OR is rare and event-based mostly appears in race-condition models.\n\n---\n\n## 6. Connectors\n\nFour connector types. Three of them stay inside a pool; only message flow is allowed to cross pool boundaries.\n\n```\nA --> B // sequence flow (default)\nG --? "yes" --> C // conditional sequence (label is a guard)\nG --* "default" --> D // default flow (one per gateway, max)\n"Customer" ~~> A : "Submit application" // message flow\nE ~~> "Customer" : "Notify approval" // message flow back\n```\n\n- `-->` is the workhorse \u2014 solid line + filled triangle arrowhead.\n- `--? "label" -->` adds a small unfilled diamond at the source. Use it when leaving an activity *directly* on a guarded outcome. (At a gateway, the conditional label suffices; the diamond glyph isn\'t drawn.)\n- `--* "label" -->` adds a slash mark at the source. **One default flow maximum per gateway** \u2014 Schematex enforces this.\n- `~~>` is dashed with an open arrowhead and a small unfilled circle at the source. Source or target may be a quoted **pool name** (for black-box participants) or an object id.\n\n---\n\n## 7. Validation\n\nThe parser refuses diagrams that violate BPMN semantics, with line-numbered errors:\n\n| Rule | Error |\n|---|---|\n| Sequence flow crosses pool | `sequence flow \'A --> B\' crosses pool boundary \u2014 use message flow (~~>)` |\n| Message flow inside one pool | `message flow \'A ~~> B\' must cross pool boundaries` |\n| Black-box pool has internals | `black-box pool "X" cannot contain lanes` |\n| Two default flows from same gateway | `gateway \'G\' has 2 default flows (max 1)` |\n| Duplicate id within a pool | `duplicate id \'A\'` |\n| Unknown source / target | `unknown source \'X\' in sequence flow` |\n\nThese checks fire during parse, so an LLM gets a usable signal before the layout pass.\n\n---\n\n## 8. Larger example \u2014 pizza order with black-box customer\n\nA canonical BPMN tutorial: an external customer (whose process we don\'t model) places an order with a pizzeria split into Clerk / Chef / Delivery lanes, with a rework loop on the chef\'s quality check.\n\n```\nbpmn\ndirection: LR\ntitle: "Pizza order"\n\npool "Customer" blackbox\n\npool "Pizzeria" {\n lane "Clerk" {\n A: start message "Order received"\n B: task user "Take order"\n }\n lane "Chef" {\n C: task manual "Make pizza"\n G1: gateway xor "Pizza ok?"\n D: task manual "Rework"\n }\n lane "Delivery" {\n E: task send "Deliver"\n F: end "Done"\n }\n}\n\nflows\nA --> B\nB --> C\nC --> G1\nG1 --? "yes" --> E\nG1 --* "no" --> D\nD --> C\nE --> F\n"Customer" ~~> A : "Place order"\nE ~~> "Customer" : "Pizza delivered"\n```\n\nThe rework loop (`D --> C`) creates a back-edge that the layout\'s cycle-break detects via DFS \u2014 the longest-path layering then proceeds on the forward DAG so columns stay sensible.\n\n---\n\n## 9. Limitations of v0.1\n\nThese are deferred to a later release. If your diagram needs one, file an issue with the use case:\n\n- **Boundary events** \u2014 events attached to an activity edge (timer, error, escalation, compensation). Currently you have to model the boundary as a free-floating intermediate event with manual flows.\n- **Expanded subprocesses** \u2014 collapsed `subprocess` works; expanded inline blocks (`subprocess "X" { \u2026 }`) are deferred.\n- **Rare event triggers** \u2014 error / escalation / cancel / compensation / signal / link / conditional / multiple / parallel-multiple. Use `none` or `message` as a placeholder.\n- **Transaction / call activities** \u2014 render as normal tasks for v0.1.\n- **Loop and multi-instance markers** \u2014 bottom-center activity glyphs; deferred.\n- **Artifacts** \u2014 data object / data store / group / text annotation. Deferred.\n- **BPMN 2.0 XML import / export** \u2014 out of scope. Schematex computes layout from DSL; no DI layer to round-trip.'
2511
+ "content": '## 1. Your first BPMN diagram\n\nThree sections: a one-line `bpmn` header, one or more `pool { \u2026 }` blocks, and a `flows` block. Inside each pool you put `lane { \u2026 }` blocks, and inside each lane you list flow objects as `id: kind "label"`.\n\n```\nbpmn\npool "Service" {\n lane "Worker" {\n A: start "Request"\n B: task service "Process"\n F: end "Done"\n }\n}\n\nflows\nA --> B\nB --> F\n```\n\nEvery flow object starts with an **id**, a colon, the **kind**, an optional sub-keyword (trigger / marker / gateway type), and a quoted **label**. Ids are referenced by flow lines.\n\n---\n\n## 2. Pools and lanes\n\nA **pool** represents one participant \u2014 an organisation, a department, or a system. A **lane** subdivides a pool into roles. The pool label is rendered rotated 90\xB0 on the left edge of a horizontal pool.\n\n```\npool "Customer" blackbox // black-box pool \u2014 no internal flow\npool "Bank" {\n lane "Clerk" { \u2026 }\n lane "Underwriter" { \u2026 }\n}\n```\n\nA **black-box pool** is a participant whose internal process you don\'t model \u2014 typically an external customer or partner. Black-box pools must contain zero flow objects (Schematex enforces this in the parser).\n\nSequence flow (`-->`) is **not allowed** to cross pool boundaries. Use a message flow (`~~>`) for cross-pool communication.\n\n---\n\n## 3. Events\n\nEvents are circles. Stroke weight encodes lifecycle role:\n\n| Kind | Stroke | DSL |\n|---|---|---|\n| Start | thin (1px) | `start` |\n| Intermediate | thin double ring | `intermediate` |\n| End | thick (3px) | `end` |\n\nThe optional **trigger** keyword adds an inner glyph. v0.1 supports the three most common triggers \u2014 `none` (no glyph), `message` (envelope), and `timer` (clock face):\n\n```\nA: start // none-trigger\nA: start message "Inbound" // message-catch (unfilled envelope)\nT: intermediate timer "60 min" // timer (clock face)\nF: end "Done"\n```\n\nFilled glyph = **throw**, unfilled glyph = **catch**. Schematex picks the right fill automatically based on event kind.\n\n---\n\n## 4. Activities \u2014 tasks and subprocesses\n\nActivities are rounded rectangles. The two v0.1 forms are `task` and a **collapsed** subprocess (the `+` marker indicates expandable detail):\n\n```\nB: task "Generic abstract task"\nU: task user "User decides"\nS: task service "API call"\nSE: task send "Send email"\nRE: task receive "Wait for reply"\nM: task manual "Hand-stamp the form"\nSC: task script "Run rule engine"\n\nX: subprocess "Verify identity" collapsed\n```\n\nThe **task marker** (small icon top-left) communicates *who or what* performs the work \u2014 a person (`user`), a software service (`service`), an outbound message (`send`), an inbound wait (`receive`), an out-of-system manual step (`manual`), or an automated script (`script`). When in doubt, omit the marker \u2014 it defaults to abstract.\n\n---\n\n## 5. Gateways\n\nGateways are diamonds. The inner glyph encodes branching semantics:\n\n| Kind | Glyph | Meaning | DSL |\n|---|---|---|---|\n| Exclusive (XOR) | **X** | Take exactly one outgoing branch (data-based) | `gateway xor` |\n| Inclusive (OR) | **O** | Take one or more outgoing branches | `gateway or` |\n| Parallel (AND) | **+** | Take all outgoing branches concurrently | `gateway and` |\n| Event-based | pentagon-in-circle | Pick the branch whose event fires first | `gateway event` |\n\nSchematex uses Bruce Silver\'s **X glyph** for XOR by default \u2014 that\'s the convention real BPMN audits expect. Most diagrams use XOR (data branch) and AND (parallel split / join); OR is rare and event-based mostly appears in race-condition models.\n\n---\n\n## 6. Connectors\n\nFour connector types. Three of them stay inside a pool; only message flow is allowed to cross pool boundaries.\n\n```\nA --> B // sequence flow (default)\nG --? "yes" --> C // conditional sequence (label is a guard)\nG --* "default" --> D // default flow (one per gateway, max)\n"Customer" ~~> A : "Submit application" // message flow\nE ~~> "Customer" : "Notify approval" // message flow back\n```\n\n- `-->` is the workhorse \u2014 solid line + filled triangle arrowhead.\n- `--? "label" -->` adds a small unfilled diamond at the source. Use it when leaving an activity *directly* on a guarded outcome. (At a gateway, the conditional label suffices; the diamond glyph isn\'t drawn.)\n- `--* "label" -->` adds a slash mark at the source. **One default flow maximum per gateway** \u2014 Schematex enforces this.\n- `~~>` is dashed with an open arrowhead and a small unfilled circle at the source. Source or target may be a quoted **pool name** (for black-box participants) or an object id.\n\n---\n\n## 7. Validation\n\nThe parser refuses diagrams that violate BPMN semantics, with line-numbered errors:\n\n| Rule | Error |\n|---|---|\n| Sequence flow crosses pool | `sequence flow \'A --> B\' crosses pool boundary \u2014 use message flow (~~>)` |\n| Message flow inside one pool | `message flow \'A ~~> B\' must cross pool boundaries` |\n| Black-box pool has internals | `black-box pool "X" cannot contain lanes` |\n| Two default flows from same gateway | `gateway \'G\' has 2 default flows (max 1)` |\n| Duplicate id within a pool | `duplicate id \'A\'` |\n| Unknown source / target | `unknown source \'X\' in sequence flow` |\n\nThese checks fire during parse, so an LLM gets a usable signal before the layout pass.\n\n---\n\n## 8. Larger example \u2014 pizza order with black-box customer\n\nA canonical BPMN tutorial: an external customer (whose process we don\'t model) places an order with a pizzeria split into Clerk / Chef / Delivery lanes, with a rework loop on the chef\'s quality check.\n\n```\nbpmn\ndirection: LR\ntitle: "Pizza order"\n\npool "Customer" blackbox\n\npool "Pizzeria" {\n lane "Clerk" {\n A: start message "Order received"\n B: task user "Take order"\n }\n lane "Chef" {\n C: task manual "Make pizza"\n G1: gateway xor "Pizza ok?"\n D: task manual "Rework"\n }\n lane "Delivery" {\n E: task send "Deliver"\n F: end "Done"\n }\n}\n\nflows\nA --> B\nB --> C\nC --> G1\nG1 --? "yes" --> E\nG1 --* "no" --> D\nD --> C\nE --> F\n"Customer" ~~> A : "Place order"\nE ~~> "Customer" : "Pizza delivered"\n```\n\nThe rework loop (`D --> C`) creates a back-edge that the layout\'s cycle-break detects via DFS \u2014 the longest-path layering then proceeds on the forward DAG so columns stay sensible.\n\n---\n\n## 9. Limitations of v0.1\n\nThese are deferred to a later release. If your diagram needs one, file an issue with the use case:\n\n- **Boundary events** \u2014 events attached to an activity edge (timer, error, escalation, compensation). Currently you have to model the boundary as a free-floating intermediate event with manual flows.\n- **Expanded subprocesses** \u2014 collapsed `subprocess` works; expanded inline blocks (`subprocess "X" { \u2026 }`) are deferred.\n- **Rare event triggers** \u2014 error / escalation / cancel / compensation / signal / link / conditional / multiple / parallel-multiple. Use `none` or `message` as a placeholder.\n- **Transaction / call activities** \u2014 render as normal tasks for v0.1.\n- **Loop and multi-instance markers** \u2014 bottom-center activity glyphs; deferred.\n- **Artifacts** \u2014 data object / data store / group / text annotation. Deferred.\n- **BPMN 2.0 XML import / export** \u2014 out of scope. Schematex computes layout from DSL; no DI layer to round-trip.\n\n---\n\n## Related examples\n\nReady-to-use scenarios from the examples gallery:'
1815
2512
  },
1816
2513
  "fbd": {
1817
2514
  "title": "Function Block Diagram (FBD)",
1818
- "content": '## 1. Your first FBD network\n\nThe smallest useful FBD network: one block, two inputs, one output.\n\n```\nfbd\nnetwork 0:\n Out = AND(A, B)\n```\n\n`A` and `B` are auto-declared as BOOL inputs (left-side terminals), and `Out` is auto-wired to the AND block\'s `OUT` port (right-side terminal). The block sits between them with port stubs and labels.\n\n---\n\n## 2. Variables\n\nDeclare variables before any networks. Each variable has a name, an IEC data type, and an optional initial value.\n\n```\nfbd "Tank Control"\n\nvar StartBtn: bool\nvar TankLevel: real\nvar SetPoint: real = 80.0\nvar DwellTimer: timer\nvar Pulse: counter\n```\n\nSupported types: `bool`, `int`, `dint`, `uint`, `udint`, `real`, `lreal`, `time`, `date`, `tod`, `string`, `wstring`, `byte`, `word`, `dword`, `timer`, `counter`. Any other identifier is treated as a user-defined function-block type.\n\nOptional scope prefixes (default = local): `var_input`, `var_output`, `var_in_out`, `var_global`, `var_external`.\n\n---\n\n## 3. Networks\n\nA **network** is one independent piece of data flow, evaluated left-to-right within itself; networks evaluate top-to-bottom across the program per scan.\n\n```\nnetwork 0 "Start latch":\n ...\n\nnetwork 1:\n ...\n```\n\nThe number is optional \u2014 networks are auto-numbered if absent. The title (in quotes) renders at the top-left of the network frame.\n\n---\n\n## 4. Block calls \u2014 inline expression notation\n\nThe clearest way to write a combinational network is as a single nested expression:\n\n```\nnetwork 0:\n Out = OR(A, AND(B, ~C))\n```\n\nThe parser builds the call tree: outer OR with `A` on input 1 and the AND result on input 2; the AND has `B` and the negated `C`. The renderer lays them out left-to-right (inputs at layer 0, AND at layer 1, OR at layer 2, output at layer 3).\n\n`~C` adds a **negation bubble** (small open circle) at the input port \u2014 equivalent to inserting a NOT block on that wire, but cleaner.\n\n---\n\n## 5. Block calls \u2014 instance-named notation\n\nWhen you need to reference a block\'s outputs from elsewhere, give it an instance tag:\n\n```\nnetwork 0:\n Pulse = R_TRIG(CLK: Sensor)\n Count = CTU(CU: Pulse.Q, R: Reset, PV: 100)\n Done = GE(IN1: Count.CV, IN2: 100)\n```\n\n`Pulse.Q`, `Count.CV` reference output ports of named instances. The instance tag renders italicized above the block header.\n\nArgument lists accept **named ports** (`CU: Pulse.Q`) or **positional** (`CTU(Pulse.Q, Reset, 100)`) \u2014 named is recommended for readability and required when you skip a port.\n\n---\n\n## 6. Inline constants\n\nInput ports can take literals directly \u2014 no wire needed:\n\n```\nnetwork 0:\n Dwell = TON(IN: BottleSensor, PT: T#50ms)\n Cap = LIMIT(MN: 0.0, IN: Setpoint, MX: 95.0)\n Mode = SEL(G: ManualSwitch, IN0: AutoMode, IN1: ManualMode)\n```\n\n`T#50ms`, `0.0`, `95.0` render as small yellow boxed text to the left of their port. Time literals follow IEC 61131-3: `T#10ms`, `T#5s`, `T#3m20s`, `T#1h`. Booleans are `true` / `false` (case-insensitive).\n\n---\n\n## 7. Standard block library\n\n| Category | Blocks |\n|---|---|\n| Boolean | `AND`, `OR`, `NOT`, `NAND`, `NOR`, `XOR`, `XNOR`, `BUF` |\n| Edge detect | `R_TRIG`, `F_TRIG` |\n| Bistable | `SR`, `RS` |\n| Timer | `TON`, `TOF`, `TP` |\n| Counter | `CTU`, `CTD` |\n| Math | `ADD`, `SUB`, `MUL`, `DIV`, `MOD`, `ABS`, `NEG`, `MOVE` |\n| Comparison | `EQ`, `NE`, `GT`, `GE`, `LT`, `LE` |\n| Selection | `SEL`, `MUX`, `MAX`, `MIN`, `LIMIT` |\n\nAND, OR, NAND, NOR, ADD, MUL, MAX, MIN accept any number of inputs (default 2). Pass extra positional args or use `[inputs: N]` to extend.\n\n```\nnetwork 0:\n All4 = AND(A, B, C, D)\n Sum = ADD(X, Y, Z)\n```\n\n---\n\n## 8. Larger example \u2014 bottle counter\n\n```\nfbd "Bottle Counter"\n\nvar ConveyorRunning: bool\nvar BottleSensor: bool\nvar BatchDone: bool\nvar BatchSize: counter\nvar DwellTimer: timer\n\nnetwork 0 "Debounce sensor with 50ms dwell":\n Dwell = TON(IN: BottleSensor, PT: T#50ms)\n\nnetwork 1 "Count one bottle on rising edge of debounced signal":\n Pulse = R_TRIG(CLK: Dwell.Q)\n BatchSize = CTU(CU: Pulse.Q, R: BatchDone, PV: 24)\n\nnetwork 2 "Batch done":\n BatchDone = MOVE(BatchSize.Q)\n```\n\nThree networks: debounce \u2192 edge-detect \u2192 count \u2192 flag. Each network is one DAG; the renderer routes wires through Manhattan paths.\n\n---\n\n## 9. v0.1 limitations\n\nThe current engine implements the standard-block subset most teams use day-to-day. The following are deferred and will be added in a follow-up:\n\n- **EN/ENO power-flow rails** (`[en]` block attribute, `[rail: on]` header) \u2014 adds a top-of-network enable rail, vendor convention from Studio 5000 / TIA Portal.\n- **User-defined function blocks** with `pins_in:` / `pins_out:` declarations \u2014 for custom motor controllers, PID instances, etc.\n- **Page connectors** (`connector_out` / `connector_in`) for wires that span multiple pages.\n- **Bit-string blocks** (SHL, SHR, ROL, ROR, AND_BIT, OR_BIT, etc.).\n- **Extended math** (SQRT, LN, LOG, EXP, SIN, COS, TAN, ASIN, ACOS, ATAN).\n- **ANSI distinctive shape mode** (`[shape: ansi]`) \u2014 the `logic` engine already provides this for pure-Boolean diagrams.\n- **CTUD** bidirectional counter, **TP** retentive timer (RTO).\n\nIf you need any of these now, open an issue or use the `ladder` engine for the full IEC 61131-3 LD subset.'
2515
+ "content": '## 1. Your first FBD network\n\nThe smallest useful FBD network: one block, two inputs, one output.\n\n```\nfbd\nnetwork 0:\n Out = AND(A, B)\n```\n\n`A` and `B` are auto-declared as BOOL inputs (left-side terminals), and `Out` is auto-wired to the AND block\'s `OUT` port (right-side terminal). The block sits between them with port stubs and labels.\n\n---\n\n## 2. Variables\n\nDeclare variables before any networks. Each variable has a name, an IEC data type, and an optional initial value.\n\n```\nfbd "Tank Control"\n\nvar StartBtn: bool\nvar TankLevel: real\nvar SetPoint: real = 80.0\nvar DwellTimer: timer\nvar Pulse: counter\n```\n\nSupported types: `bool`, `int`, `dint`, `uint`, `udint`, `real`, `lreal`, `time`, `date`, `tod`, `string`, `wstring`, `byte`, `word`, `dword`, `timer`, `counter`. Any other identifier is treated as a user-defined function-block type.\n\nOptional scope prefixes (default = local): `var_input`, `var_output`, `var_in_out`, `var_global`, `var_external`.\n\n---\n\n## 3. Networks\n\nA **network** is one independent piece of data flow, evaluated left-to-right within itself; networks evaluate top-to-bottom across the program per scan.\n\n```\nnetwork 0 "Start latch":\n ...\n\nnetwork 1:\n ...\n```\n\nThe number is optional \u2014 networks are auto-numbered if absent. The title (in quotes) renders at the top-left of the network frame.\n\n---\n\n## 4. Block calls \u2014 inline expression notation\n\nThe clearest way to write a combinational network is as a single nested expression:\n\n```\nnetwork 0:\n Out = OR(A, AND(B, ~C))\n```\n\nThe parser builds the call tree: outer OR with `A` on input 1 and the AND result on input 2; the AND has `B` and the negated `C`. The renderer lays them out left-to-right (inputs at layer 0, AND at layer 1, OR at layer 2, output at layer 3).\n\n`~C` adds a **negation bubble** (small open circle) at the input port \u2014 equivalent to inserting a NOT block on that wire, but cleaner.\n\n---\n\n## 5. Block calls \u2014 instance-named notation\n\nWhen you need to reference a block\'s outputs from elsewhere, give it an instance tag:\n\n```\nnetwork 0:\n Pulse = R_TRIG(CLK: Sensor)\n Count = CTU(CU: Pulse.Q, R: Reset, PV: 100)\n Done = GE(IN1: Count.CV, IN2: 100)\n```\n\n`Pulse.Q`, `Count.CV` reference output ports of named instances. The instance tag renders italicized above the block header.\n\nArgument lists accept **named ports** (`CU: Pulse.Q`) or **positional** (`CTU(Pulse.Q, Reset, 100)`) \u2014 named is recommended for readability and required when you skip a port.\n\n---\n\n## 6. Inline constants\n\nInput ports can take literals directly \u2014 no wire needed:\n\n```\nnetwork 0:\n Dwell = TON(IN: BottleSensor, PT: T#50ms)\n Cap = LIMIT(MN: 0.0, IN: Setpoint, MX: 95.0)\n Mode = SEL(G: ManualSwitch, IN0: AutoMode, IN1: ManualMode)\n```\n\n`T#50ms`, `0.0`, `95.0` render as small yellow boxed text to the left of their port. Time literals follow IEC 61131-3: `T#10ms`, `T#5s`, `T#3m20s`, `T#1h`. Booleans are `true` / `false` (case-insensitive).\n\n---\n\n## 7. Standard block library\n\n| Category | Blocks |\n|---|---|\n| Boolean | `AND`, `OR`, `NOT`, `NAND`, `NOR`, `XOR`, `XNOR`, `BUF` |\n| Edge detect | `R_TRIG`, `F_TRIG` |\n| Bistable | `SR`, `RS` |\n| Timer | `TON`, `TOF`, `TP` |\n| Counter | `CTU`, `CTD` |\n| Math | `ADD`, `SUB`, `MUL`, `DIV`, `MOD`, `ABS`, `NEG`, `MOVE` |\n| Comparison | `EQ`, `NE`, `GT`, `GE`, `LT`, `LE` |\n| Selection | `SEL`, `MUX`, `MAX`, `MIN`, `LIMIT` |\n\nAND, OR, NAND, NOR, ADD, MUL, MAX, MIN accept any number of inputs (default 2). Pass extra positional args or use `[inputs: N]` to extend.\n\n```\nnetwork 0:\n All4 = AND(A, B, C, D)\n Sum = ADD(X, Y, Z)\n```\n\n---\n\n## 8. Larger example \u2014 bottle counter\n\n```\nfbd "Bottle Counter"\n\nvar ConveyorRunning: bool\nvar BottleSensor: bool\nvar BatchDone: bool\nvar BatchSize: counter\nvar DwellTimer: timer\n\nnetwork 0 "Debounce sensor with 50ms dwell":\n Dwell = TON(IN: BottleSensor, PT: T#50ms)\n\nnetwork 1 "Count one bottle on rising edge of debounced signal":\n Pulse = R_TRIG(CLK: Dwell.Q)\n BatchSize = CTU(CU: Pulse.Q, R: BatchDone, PV: 24)\n\nnetwork 2 "Batch done":\n BatchDone = MOVE(BatchSize.Q)\n```\n\nThree networks: debounce \u2192 edge-detect \u2192 count \u2192 flag. Each network is one DAG; the renderer routes wires through Manhattan paths.\n\n---\n\n## 9. v0.1 limitations\n\nThe current engine implements the standard-block subset most teams use day-to-day. The following are deferred and will be added in a follow-up:\n\n- **EN/ENO power-flow rails** (`[en]` block attribute, `[rail: on]` header) \u2014 adds a top-of-network enable rail, vendor convention from Studio 5000 / TIA Portal.\n- **User-defined function blocks** with `pins_in:` / `pins_out:` declarations \u2014 for custom motor controllers, PID instances, etc.\n- **Page connectors** (`connector_out` / `connector_in`) for wires that span multiple pages.\n- **Bit-string blocks** (SHL, SHR, ROL, ROR, AND_BIT, OR_BIT, etc.).\n- **Extended math** (SQRT, LN, LOG, EXP, SIN, COS, TAN, ASIN, ACOS, ATAN).\n- **ANSI distinctive shape mode** (`[shape: ansi]`) \u2014 the `logic` engine already provides this for pure-Boolean diagrams.\n- **CTUD** bidirectional counter, **TP** retentive timer (RTO).\n\nIf you need any of these now, open an issue or use the `ladder` engine for the full IEC 61131-3 LD subset.\n\n---\n\n## Related examples\n\nReady-to-use scenarios from the examples gallery:'
1819
2516
  },
1820
2517
  "sfc": {
1821
2518
  "title": "Sequential Function Chart (SFC)",
1822
- "content": '## 1. Your first chart\n\nTwo steps, one transition, an initial marker:\n\n```\nsfc\nstep S0 [initial]\nstep S1\ntransition from: S0 to: S1: Trigger\n```\n\n`S0` renders as a **double-bordered rectangle** (the IEC initial-step convention); `S1` as a single-border rectangle. Between them is a horizontal **transition bar** with the condition text `Trigger` to its right.\n\nIf you forget `[initial]`, the first declared step is auto-promoted to initial.\n\n---\n\n## 2. Steps\n\n```\nstep S_Filling [label: "Filling tank"]\n N FillValve_Open\n D Mixer_Run T#30s\n P StartChime\n```\n\nA step has:\n\n- An **id** (unique across the chart) \u2014 used in transitions and jumps.\n- An optional `[label: "..."]` for display.\n- An optional `[initial]` (one allowed) or `[final]` (vendor stop step, three borders).\n- Zero or more **action blocks**, indented one level, each with a qualifier letter.\n\n---\n\n## 3. Transitions\n\nA **transition** declares a directed link between two steps with a boolean condition:\n\n```\ntransition from: S0 to: S1: StartBtn\ntransition from: S1 to: S2: TankLevel >= 80.0 AND NOT EmergencyStop\ntransition T_Reset from: S5 to: S0: ResetBtn\n```\n\nThe condition text is opaque \u2014 Schematex stores it verbatim and renders it next to the bar. Every transition must have a non-empty condition; use `TRUE` for unconditional links.\n\nTransitions whose `from` and `to` are linearly adjacent in the body render as inline bars between the steps. Transitions whose pair is *not* linearly adjacent (e.g. a jump back to an earlier step) render as **margin arrows** on the left or right side of the chart.\n\n---\n\n## 4. Action qualifiers\n\nActions attach to the right side of a step and run according to their qualifier letter:\n\n| Qualifier | Behavior |\n|---|---|\n| `N` | Active while step is active (most common) |\n| `S` | Stored \u2014 set true on entry, stays until matching `R` |\n| `R` | Reset \u2014 clears a previously-stored action |\n| `L` | Time-Limited \u2014 active up to T after step entry |\n| `D` | Time-Delayed \u2014 activates T after entry |\n| `P` | Pulse \u2014 true for one PLC scan only |\n| `P0` | Pulse on deactivate (Siemens) |\n| `P1` | Synonym for `P` (Siemens) |\n| `SD` | Stored & Delayed |\n| `DS` | Delayed & Stored |\n| `SL` | Stored & Time-Limited |\n\nTime-parameterized qualifiers (L, D, SD, DS, SL) take a duration literal:\n\n```\nstep S1\n L LimitedRun T#5s\n D DelayedRun T#2s\n```\n\n---\n\n## 5. Alternative branches (single bar \u2014 OR)\n\nOnly **one** branch fires per scan, picked by transition condition:\n\n```\nstep S0 [initial]\nstep S_Pick\n\nalt from: S_Pick:\n branch [priority: 1]:\n transition: IsExpressShipping\n step S_Express\n N PrepExpressBox\n transition: TRUE\n branch [priority: 2]:\n transition: IsStandardShipping\n step S_Standard\n N PrepStandardBox\n transition: TRUE\nmerge_to: S_Ship\n\nstep S_Ship\n\ntransition from: S0 to: S_Pick: ProductOrdered\ntransition from: S_Ship to: S0: Shipped\n```\n\nThe single horizontal lines above and below the branches are the divergence and convergence bars. Each branch starts with its **entry transition** (between div bar and first step) and ends with an **exit transition** (between last step and conv bar).\n\n---\n\n## 6. Simultaneous branches (double bar \u2014 AND)\n\n**All** branches run concurrently; the chart waits at the convergence until every branch finishes:\n\n```\nsim from: S_Heat: TRUE\n branch:\n step S_Bake\n D Oven_Run T#15m\n branch:\n step S_Cool\n L Cooler_On T#5m\nmerge_to: S_Done: Bake_Done AND Cool_Done\n```\n\nThe two parallel horizontal lines (gap 4px) above and below the branches are the simultaneous bars. The shared **transition above** (`TRUE` here) triggers the divergence; the shared **transition below** (`Bake_Done AND Cool_Done`) is checked before convergence fires.\n\n---\n\n## 7. Jumps (loops)\n\nA transition whose target is an earlier step renders as a margin arrow:\n\n```\nstep S0 [initial]\nstep S1\nstep S2\ntransition from: S0 to: S1: A\ntransition from: S1 to: S2: B\ntransition T_Reset from: S2 to: S0: ResetBtn\ntransition from: S2 to: S1: NOT ResetBtn\n```\n\nThe forward `S2 \u2192 S1` (back-edge) gets the margin arrow on the right; the `T_Reset` jump back to `S0` goes on the left. Each margin arrow shows its target id and condition.\n\n---\n\n## 8. Variables\n\nReused from `ladder` and `fbd`:\n\n```\nvar StartBtn: bool\nvar TankLevel: real\nvar BakeReady: bool\nvar Counter: counter\nvar T1: timer\n```\n\nVariables declared in conditions and actions are not validated \u2014 Schematex treats condition / action body text as opaque strings, matching how `state` handles guards and actions.\n\n---\n\n## 9. v0.1 limitations\n\n- **Nested branches** (alt-in-sim, sim-in-alt) parse but layout collapse heuristics are basic; deep nests may overlap.\n- **S/R action-pair dashed connectors** (visually link an `S` action with its matching `R` elsewhere) are deferred.\n- **Active-step runtime indicator** (yellow fill on the currently active step) is deferred \u2014 useful for debugging integrations that surface PLC runtime state.\n- **GRAFCET forcing orders** (out-of-scope per IEC 60848-only feature).\n- **Final step** parses with `[final]` but renders with the same double border as initial; the IEC triple-border convention is deferred.'
2519
+ "content": '## 1. Your first chart\n\nTwo steps, one transition, an initial marker:\n\n```\nsfc\nstep S0 [initial]\nstep S1\ntransition from: S0 to: S1: Trigger\n```\n\n`S0` renders as a **double-bordered rectangle** (the IEC initial-step convention); `S1` as a single-border rectangle. Between them is a horizontal **transition bar** with the condition text `Trigger` to its right.\n\nIf you forget `[initial]`, the first declared step is auto-promoted to initial.\n\n---\n\n## 2. Steps\n\n```\nstep S_Filling [label: "Filling tank"]\n N FillValve_Open\n D Mixer_Run T#30s\n P StartChime\n```\n\nA step has:\n\n- An **id** (unique across the chart) \u2014 used in transitions and jumps.\n- An optional `[label: "..."]` for display.\n- An optional `[initial]` (one allowed) or `[final]` (vendor stop step, three borders).\n- Zero or more **action blocks**, indented one level, each with a qualifier letter.\n\n---\n\n## 3. Transitions\n\nA **transition** declares a directed link between two steps with a boolean condition:\n\n```\ntransition from: S0 to: S1: StartBtn\ntransition from: S1 to: S2: TankLevel >= 80.0 AND NOT EmergencyStop\ntransition T_Reset from: S5 to: S0: ResetBtn\n```\n\nThe condition text is opaque \u2014 Schematex stores it verbatim and renders it next to the bar. Every transition must have a non-empty condition; use `TRUE` for unconditional links.\n\nTransitions whose `from` and `to` are linearly adjacent in the body render as inline bars between the steps. Transitions whose pair is *not* linearly adjacent (e.g. a jump back to an earlier step) render as **margin arrows** on the left or right side of the chart.\n\n---\n\n## 4. Action qualifiers\n\nActions attach to the right side of a step and run according to their qualifier letter:\n\n| Qualifier | Behavior |\n|---|---|\n| `N` | Active while step is active (most common) |\n| `S` | Stored \u2014 set true on entry, stays until matching `R` |\n| `R` | Reset \u2014 clears a previously-stored action |\n| `L` | Time-Limited \u2014 active up to T after step entry |\n| `D` | Time-Delayed \u2014 activates T after entry |\n| `P` | Pulse \u2014 true for one PLC scan only |\n| `P0` | Pulse on deactivate (Siemens) |\n| `P1` | Synonym for `P` (Siemens) |\n| `SD` | Stored & Delayed |\n| `DS` | Delayed & Stored |\n| `SL` | Stored & Time-Limited |\n\nTime-parameterized qualifiers (L, D, SD, DS, SL) take a duration literal:\n\n```\nstep S1\n L LimitedRun T#5s\n D DelayedRun T#2s\n```\n\n---\n\n## 5. Alternative branches (single bar \u2014 OR)\n\nOnly **one** branch fires per scan, picked by transition condition:\n\n```\nstep S0 [initial]\nstep S_Pick\n\nalt from: S_Pick:\n branch [priority: 1]:\n transition: IsExpressShipping\n step S_Express\n N PrepExpressBox\n transition: TRUE\n branch [priority: 2]:\n transition: IsStandardShipping\n step S_Standard\n N PrepStandardBox\n transition: TRUE\nmerge_to: S_Ship\n\nstep S_Ship\n\ntransition from: S0 to: S_Pick: ProductOrdered\ntransition from: S_Ship to: S0: Shipped\n```\n\nThe single horizontal lines above and below the branches are the divergence and convergence bars. Each branch starts with its **entry transition** (between div bar and first step) and ends with an **exit transition** (between last step and conv bar).\n\n---\n\n## 6. Simultaneous branches (double bar \u2014 AND)\n\n**All** branches run concurrently; the chart waits at the convergence until every branch finishes:\n\n```\nsim from: S_Heat: TRUE\n branch:\n step S_Bake\n D Oven_Run T#15m\n branch:\n step S_Cool\n L Cooler_On T#5m\nmerge_to: S_Done: Bake_Done AND Cool_Done\n```\n\nThe two parallel horizontal lines (gap 4px) above and below the branches are the simultaneous bars. The shared **transition above** (`TRUE` here) triggers the divergence; the shared **transition below** (`Bake_Done AND Cool_Done`) is checked before convergence fires.\n\n---\n\n## 7. Jumps (loops)\n\nA transition whose target is an earlier step renders as a margin arrow:\n\n```\nstep S0 [initial]\nstep S1\nstep S2\ntransition from: S0 to: S1: A\ntransition from: S1 to: S2: B\ntransition T_Reset from: S2 to: S0: ResetBtn\ntransition from: S2 to: S1: NOT ResetBtn\n```\n\nThe forward `S2 \u2192 S1` (back-edge) gets the margin arrow on the right; the `T_Reset` jump back to `S0` goes on the left. Each margin arrow shows its target id and condition.\n\n---\n\n## 8. Variables\n\nReused from `ladder` and `fbd`:\n\n```\nvar StartBtn: bool\nvar TankLevel: real\nvar BakeReady: bool\nvar Counter: counter\nvar T1: timer\n```\n\nVariables declared in conditions and actions are not validated \u2014 Schematex treats condition / action body text as opaque strings, matching how `state` handles guards and actions.\n\n---\n\n## 9. v0.1 limitations\n\n- **Nested branches** (alt-in-sim, sim-in-alt) parse but layout collapse heuristics are basic; deep nests may overlap.\n- **S/R action-pair dashed connectors** (visually link an `S` action with its matching `R` elsewhere) are deferred.\n- **Active-step runtime indicator** (yellow fill on the currently active step) is deferred \u2014 useful for debugging integrations that surface PLC runtime state.\n- **GRAFCET forcing orders** (out-of-scope per IEC 60848-only feature).\n- **Final step** parses with `[final]` but renders with the same double border as initial; the IEC triple-border convention is deferred.\n\n---\n\n## Related examples\n\nReady-to-use scenarios from the examples gallery:'
1823
2520
  },
1824
2521
  "usecase": {
1825
2522
  "title": "UML Use Case Diagram",
@@ -1836,6 +2533,14 @@ var SYNTAX = {
1836
2533
  "pert": {
1837
2534
  "title": "PERT / CPM Network",
1838
2535
  "content": '## 1. Your first diagram\n\nEvery document starts with the `pert` keyword, an optional header, then one `task` line per activity:\n\n```\npert\nunit: days\n\ntask A "Market research" duration: 5\ntask B "Design mockups" duration: 8 after: A\ntask C "Backend API" duration: 15 after: A\ntask D "Frontend build" duration: 10 after: B, C\n```\n\nEach task carries an `<id>`, a quoted `<label>`, a `duration:`, and an optional `after:` list of predecessors. The engine adds an invisible Start and Finish, runs the schedule, and draws the six-field activity box for every task. You never type ES/EF/LS/LF \u2014 they are computed.\n\nThe header accepts:\n\n- `title: "\u2026"` \u2014 a heading drawn above the diagram.\n- `unit: days | weeks | hours | abstract` \u2014 the time unit (default `days`; purely a label, the engine is calendar-agnostic).\n- `direction: LR | TB` \u2014 left-to-right (default) or top-to-bottom.\n- `layout: network | timescaled` \u2014 the layered network (default) or a time-proportional view (\xA76).\n- `critical-tolerance: <n>` \u2014 slack \u2264 this counts as critical (default `0`; set `0.001` for three-point projects).\n- `show-sentinels: true` \u2014 draw the synthetic Start / Finish nodes (hidden by default).\n\n---\n\n## 2. The six-field activity box\n\nEvery task renders as the canonical 3\xD72 PERT/CPM rectangle:\n\n```\n\u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n\u2502 ES \u2502 Duration \u2502 EF \u2502\n\u251C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\n\u2502 Task Name (ID) \u2502\n\u251C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\n\u2502 LS \u2502 Slack \u2502 LF \u2502\n\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n```\n\n- **ES / EF** \u2014 Early Start / Early Finish, from the forward pass.\n- **LS / LF** \u2014 Late Start / Late Finish, from the backward pass.\n- **Slack** (total float) = LS \u2212 ES = LF \u2212 EF. Zero slack means the activity is on the critical path.\n\nCritical activities get a **red border** and a bold `0` slack; the project\'s critical chain reads as an unbroken red line. Every field is also mirrored onto `data-*` attributes (`data-es`, `data-slack`, `data-critical`, \u2026) so you can query the SVG without re-running the math.\n\n**On the two-colour palette.** A PERT chart is deliberately drawn in just two colours. Red for the critical path is a genuine industry convention \u2014 MS Project, Oracle Primavera P6, and PMBOK figures all use it, because "critical vs. not" is the one distinction the diagram exists to surface. Everything else stays a neutral house-blue. Adding more colours would imply categories that PERT\'s semantics don\'t have; if you need to group by team or phase, use [swimlanes](#7-swimlanes-tags-classes-and-comments) (`lane:`) instead of colour.\n\n---\n\n## 3. Dependencies (FS / SS / FF / SF + lag/lead)\n\n`after:` takes a comma-separated list of predecessor references. The default relationship is **Finish-to-Start (FS)** \u2014 a task starts once its predecessor finishes:\n\n```\ntask D "Frontend build" duration: 10 after: B, C\n```\n\nModern scheduling needs the other three Precedence-Diagramming relationships and **lag** (delay) or **lead** (negative lag):\n\n```\ntask B "Stakeholder interviews" duration: 6 after: A SS+1 # start 1d after A starts\ntask I "Documentation" duration: 4 after: D+3 # FS with a 3-day lag\ntask J "Translation" duration: 3 after: I SS-1 # start 1d before I starts (lead)\ntask K "Sign-off" duration: 1 after: I FF # finishes when I finishes\ntask L "Press release" duration: 2 after: G SF+1 # start-to-finish (rare)\n```\n\n| Form | Meaning |\n|------|---------|\n| `after: A` | Finish-to-Start, no lag (the common case) |\n| `after: A+2` or `after: A+2d` | FS with a 2-unit lag |\n| `after: A SS` / `A FF` / `A SF` | Start-to-Start / Finish-to-Finish / Start-to-Finish |\n| `after: A SS+2d` / `A FF-1d` | any type with lag (`+`) or lead (`-`) |\n\nA lag unit suffix (`d` / `w` / `h`) must match the diagram\'s `unit:` or be omitted; mixed units are rejected. FS with zero lag is unlabelled; SS/FF/SF always show their type on the edge.\n\n---\n\n## 4. Three-point (PERT) estimation\n\nWrite a duration as `O/M/P` (optimistic / most-likely / pessimistic) and the engine computes the beta-distribution expected duration **te = (O + 4M + P) / 6** and the variance **\u03C3\xB2 = ((P \u2212 O)/6)\xB2**:\n\n```\npert\ncritical-tolerance: 0.01\ntask A "Spec" duration: 2/3/5 # te = 3.17, \u03C3\xB2 = 0.25\ntask B "Build" duration: 5/8/14 after: A # te = 8.50, \u03C3\xB2 = 2.25\ntask C "Test" duration: 3/4/6 after: B\ntask D "Deploy" duration: 1/2/3 after: C\n```\n\nThe Duration field shows `te`; a small `\u03C3=\u2026` annotation appears under the name; and the project-level standard deviation (\u221A of the summed critical-path variances) is reported in the footer. Use `critical-tolerance: 0.01` so floating-point `te` values don\'t displace the visible critical path.\n\n---\n\n## 5. Milestones\n\nA milestone is a zero-duration checkpoint, drawn as a diamond. Use the `milestone` flag or `duration: 0`:\n\n```\ntask P "Cutover weekend" milestone after: O\ntask Q "Go-live" duration: 0 after: P\n```\n\n---\n\n## 6. Time-scaled layout\n\n`layout: timescaled` switches from the layered network to a network-Gantt hybrid: each activity\'s **x-position is proportional to its ES** and its **width is proportional to its duration**, with a unit time axis along the bottom. Activities are packed into lanes so nothing overlaps.\n\n```\npert\nlayout: timescaled\nunit: days\n\ntask A "Inventory" duration: 5\ntask B "Interviews" duration: 6 after: A SS+1\ntask C "Design" duration: 10 after: A, B\ntask D "Build" duration: 15 after: C\ntask E "Pilot" duration: 7 after: D\n```\n\nThis is a bridge to a Gantt view, not a replacement: there\'s no calendar, working-time mask, or resource swimlane.\n\n### Activity-on-arrow (`layout: aoa`)\n\n`layout: aoa` renders the older **activity-on-arrow** (ADM) notation you\'ll find in textbooks and on Investopedia: numbered **event** circles, **activities as labelled arrows**, and dotted **dummy activities** auto-inserted wherever an activity has two or more predecessors. You write the same activity-on-node DSL \u2014 Schematex builds the event graph and numbers the events for you.\n\n```\npert\nlayout: aoa\nunit: days\n\ntask A "create schedule" duration: 10\ntask B "buy hardware" duration: 5\ntask C "programming" duration: 20 after: A\ntask D "installation" duration: 5 after: B\ntask E "conversion" duration: 15 after: D\ntask F "test code" duration: 20 after: C, E\ntask G "write manual" duration: 15 after: E\n```\n\nAOA is a **legacy** notation (PMBOK 7 dropped it; AON is the modern standard) and it can only express **finish-to-start** logic \u2014 SS/FF/SF and lag/lead are flattened to FS with a warning. Use it for teaching, exam prep, or matching an existing textbook figure; use the default `network` (AON) layout for real scheduling.\n\n---\n\n## 7. Swimlanes, tags, classes, and comments\n\nAdd `lane: "\u2026"` to any task and the network re-groups into horizontal swimlanes \u2014 by responsible team, phase, or owner \u2014 while still computing the schedule exactly as before:\n\n```\ntask T1 "Support Account Deletion" duration: 3 lane: "Customer Account"\ntask T2 "Design a New Theme" duration: 8 lane: "Shopping Site"\ntask T3 "Apply New Theme" duration: 15 after: T2 lane: "Shopping Site"\n```\n\nLanes appear in first-declared order, each as a banded row with a label gutter on the left. Swimlanes are a presentation of the same AON network, not a different layout mode \u2014 they activate automatically when any task declares a lane.\n\n```\ntask X "External vendor work" duration: 10 after: A tags: vendor, external class: secondary\n# this is a comment\n```\n\n`tags:` emit `data-tag="\u2026"` on the node group and `class:` adds a CSS class for downstream theming. `#` and `//` start a comment to end of line.\n\n---\n\n## 8. Grammar (EBNF)\n\n```text\ndocument = "pert" NEWLINE header* task+\nheader = "title:" quoted\n | "unit:" ("days" | "weeks" | "hours" | "abstract")\n | "direction:" ("LR" | "TB")\n | "layout:" ("network" | "timescaled" | "aoa")\n | "critical-tolerance:" number\n | "show-sentinels:" boolean\n\ntask = "task" IDENT quoted "duration:" duration ("after:" reflist)? "milestone"? attrs?\n | "task" IDENT quoted "milestone" ("after:" reflist)? attrs?\nduration = number | number "/" number "/" number ; deterministic or O/M/P\nreflist = ref ("," ref)*\nref = IDENT (WS deptype)? laglead?\n | IDENT "+" number unit? ; attached FS lag sugar\ndeptype = "FS" | "SS" | "FF" | "SF"\nlaglead = ("+" | "-") number unit?\nunit = "d" | "w" | "h"\nattrs = ("tags:" idlist)? ("class:" IDENT)? ("lane:" quoted)?\n```\n\n---'
2536
+ },
2537
+ "petri": {
2538
+ "title": "Petri Net",
2539
+ "content": '## 1. Your first net\n\nEvery document starts with the `petri` keyword and an optional title, then declares **places** and **transitions** before connecting them with **arcs**:\n\n```\npetri "Minimal"\n place P1 *1\n transition T1\n place P2\n P1 -> T1\n T1 -> P2\n```\n\n- `place <id>` \u2014 a circle. `*1` sets its initial token count (the marking).\n- `transition <id>` \u2014 a bar (an event/action).\n- `<a> -> <b>` \u2014 a directed arc. Arcs are **bipartite**: every arc goes place\u2192transition or transition\u2192place, never place\u2192place or transition\u2192transition. If you write one wrong, the engine tells you which line.\n\nUnlike some Schematex diagrams, nodes are **not** auto-declared from arcs \u2014 because an undeclared id can\'t be safely typed as a place or a transition. An arc that references an unknown node is a readable error.\n\n---\n\n## 2. Marking & tokens\n\nThe **marking** is how many tokens each place holds. Three equivalent ways to set it:\n\n```\nplace P1 *3 # *n shorthand\nplace P2 tokens: 3 # explicit\nplace P3 \u2022\u2022\u2022 # literal dots (1\u20134)\n```\n\nOr set several at once with a `marking:` line:\n\n```\nmarking: P1=3, P3=2\n```\n\nTokens render as dots (up to 4, laid out in a grid) and as a numeral beyond that. Force one style with `tokens: dots | count | auto` (default `auto`).\n\n---\n\n## 3. Transitions: immediate vs timed\n\n```\ntransition fast # immediate \u2014 a solid bar (the default)\ntransition slow timed rate: 0.8 # timed \u2014 a hollow box with a rate label \u03BB\n```\n\nImmediate transitions fire in zero time and render as the classic filled bar; **timed** transitions (the [GSPN](https://en.wikipedia.org/wiki/Stochastic_Petri_net) convention) render as a hollow box and carry an optional `rate:` (\u03BB). A transition with a `rate:` is treated as timed automatically. `prio: n` sets a priority, and a `[guard]` is rendered as a label (not evaluated in v0.1).\n\n---\n\n## 4. Arc types\n\nFour arc heads cover the standard concurrency vocabulary:\n\n```\nP -> T # standard arc (filled arrowhead)\nP -> T weight: 2 # weight > 1 is labelled (weight: n or *n)\nP -o T # inhibitor \u2014 enabled only while the place is empty (hollow-circle head)\nP -- T # read / test \u2014 tests presence without consuming (no head)\nP => T # reset \u2014 empties the place when the transition fires (double head)\n```\n\nInhibitor and reset arcs are **place\u2192transition only** \u2014 the parser rejects the reverse direction.\n\n---\n\n## 5. Capacity\n\nA place can be capped. Firing that would overflow it is disabled, and the place is drawn with a dashed border and a `K=n` label:\n\n```\nplace Buffer capacity: 3\n```\n\n---\n\n## 6. The dynamics: enabled & fire\n\nThe engine computes the semantics on every render:\n\n- **Enabled** transitions (every input satisfied; inhibitor inputs empty; no output would overflow capacity) get a green ring.\n- **Dead** transitions \u2014 those that can never fire from the current marking \u2014 are muted.\n- A **`fire:`** line replays a firing sequence and renders the *resulting* marking:\n\n```\npetri\n place P1 *1\n transition T1\n place P2\n transition T2\n place P3\n P1 -> T1\n T1 -> P2\n P2 -> T2\n T2 -> P3\n fire: T1\n```\n\nAfter firing `T1`, the token has moved P1 \u2192 P2, and now **T2** is the enabled transition. The SVG `<desc>` records the marking, the enabled set, and any detected subclass (state machine / marked graph / workflow net).\n\n---\n\n## 7. Layout & themes\n\n```\nlayout: lr # left-to-right (default)\nlayout: tb # top-to-bottom\n```\n\nPlaces and transitions land on alternating layers automatically; cycles are detected and their feedback arcs routed as back-edge curves. Three themes:\n\n- **`default`** \u2014 house blue-grey, with green reserved for *enabled* and red for *inhibitor*.\n- **`monochrome`** \u2014 the faithful Murata-1989 textbook look; enabled shows as a doubled black ring (colour falls back to shape).\n- **`dark`** \u2014 Catppuccin Mocha.\n\nCJK labels and `\u300C\u2026\u300D` / `"\u2026"` quotes parse cleanly:\n\n```\npetri "\u751F\u4EA7\u6D41\u7A0B"\n place \u539F\u6599 *2 \u300C\u539F\u6750\u6599\u300D\n transition \u52A0\u5DE5\n place \u6210\u54C1\n \u539F\u6599 -> \u52A0\u5DE5 weight: 2\n \u52A0\u5DE5 -> \u6210\u54C1\n```\n\n---\n\nFull specification: [Petri Net Standard Reference](https://github.com/schematex/schematex/blob/main/docs/reference/34-PETRINET-STANDARD.md).\n\n---\n\n## Related examples\n\nReady-to-use scenarios from the examples gallery:'
2540
+ },
2541
+ "network": {
2542
+ "title": "Network Topology",
2543
+ "content": '## 1. Your first diagram\n\nEvery document starts with the `network` keyword and an optional title, then declares **devices** before connecting them with **links**:\n\n```\nnetwork "Home"\n layout: star\n router gw "Gateway"\n pc pc1\n laptop lt1\n gw -- pc1\n gw -- lt1 : wireless\n```\n\n- `<kind> <id> ["label"]` \u2014 a typed device. The kind picks the icon.\n- `<a> -- <b>` \u2014 an undirected link. `->` is directed; `==` is a LAG (aggregated) link.\n- Anything after `:` on a link is the **link spec** (type, mode, VLAN, speed, ports).\n\nDevices are **not** auto-declared from links \u2014 an undeclared id can\'t be safely typed, so a link to an unknown device is a readable error. Use `;` to put several statements on one line, and `a b c : kind` shorthand to declare several same-kind devices at once.\n\n---\n\n## 2. Device kinds\n\nPick the kind that matches the box; the icon follows the Cisco-convention silhouette.\n\n- **Infrastructure** \u2014 `router`, `switch`, `l3switch`, `firewall`, `loadbalancer`, `ap`, `wlc`, `gateway`, `modem`, `ids`, `proxy`, `vpngw`\n- **Endpoints** \u2014 `server`, `serverfarm` (`count: n`), `pc`, `laptop`, `mobile`, `ipphone`, `printer`, `storage`\n- **CCTV / security** \u2014 `camera` (with `type: fixed | bullet | dome | ptz | turret`), `nvr`, `dvr`, `poeswitch`, `encoder`, `monitor`\n- **Clouds** \u2014 `internet`, `wan`, `pstn`, `cloud`, plus `lan` (a bus bar)\n\nAliases are accepted: `multilayer`\u2192`l3switch`, `workstation`\u2192`pc`, `wifi`\u2192`ap`, `nas`/`san`\u2192`storage`, `voip`\u2192`ipphone`.\n\n```\ncamera cam1 type: dome ip: 192.168.20.11\nserverfarm farm "Server Farm" count: 4\nl3switch core1 tier: core model: "C9500"\n```\n\n---\n\n## 3. Links & annotations\n\nA link\'s appearance follows its type; everything after `:` is order-free.\n\n```\na -- b # copper / ethernet (default solid)\na -- b : fiber 10G # fiber \u2014 orange with slash ticks\na -- b : wireless # dashed\na -- b : serial # leased / WAN circuit\na -- b : poe # Power-over-Ethernet (green + tag)\na -- b : vpn "site-to-site" # dashed tunnel\na == b : lag 40G # aggregated / EtherChannel (double line)\na -- b : trunk vlan: 10,20 1G port: Gi0/1>Gi1/0/24\n```\n\n- `trunk` / `access` \u2014 port mode (a trunk should connect switch-class devices).\n- `vlan: 10` or `vlan: 10,20` \u2014 a single VLAN tints the link (skipping the reserved alarm-red).\n- `1G` / `10G` / `100M` / `40G` \u2014 speed, shown mid-link.\n- `port: near>far` \u2014 interface labels at each end.\n\n---\n\n## 4. Layout modes\n\n```\nlayout: tiered # default \u2014 band by tier: edge \u2192 core \u2192 distribution \u2192 access\nlayout: tree # hierarchical from the root\nlayout: star # hub at center, spokes on a ring\nlayout: ring # nodes on a circle\nlayout: bus # shared backbone\nlayout: mesh # full/partial mesh on a circle\nlayout: spine-leaf # two rows, every leaf auto-meshed to every spine\nlayout: manual # explicit at: x,y per device\ndirection: tb | lr # flow axis for tiered/tree\n```\n\nFor `tiered`, set `tier:` (`edge` / `core` / `distribution` / `access`) on infrastructure; untiered endpoints are placed below their switch. For `spine-leaf`, declare `spines:` and `leaves:` and the spine\u2194leaf links are generated for you.\n\n---\n\n## 5. Boundaries: sites, racks, subnets, VLANs\n\nA device can live inside nested boundary blocks. **Physical** containers (site/rack) draw a solid border; **logical** overlays (subnet/VLAN/zone/DMZ) draw a dashed tinted region.\n\n```\nnetwork "Branch"\n site hq "HQ Building" {\n rack mdf "MDF Rack" {\n firewall fw1 tier: edge\n l3switch core1 tier: core\n }\n }\n subnet lan "10.0.10.0/24" {\n switch a1 tier: access\n pc u1 "User PC" ip: 10.0.10.50\n }\n zone dmz "DMZ" {\n server web\n }\n fw1 -- core1 : 10G\n core1 -- a1 : trunk vlan: 10\n a1 -- u1\n```\n\nA device declared inside a `subnet` whose label is a CIDR has its `ip:` validated \u2014 an address outside the range is a readable error. A bare id on its own line inside a block adds an already-declared device to that group.\n\n---\n\n## 6. Validation & the no-drop guarantee\n\nThe engine guarantees every declared device and link renders \u2014 the dropped-device failure of generic tools is structurally impossible. It also checks:\n\n- **duplicate id** \u2192 error;\n- **unknown kind** \u2192 error with the nearest suggestion (`"swtich" \u2192 did you mean "switch"?`);\n- **link to an undeclared device** \u2192 error;\n- **VLAN id outside 1\u20134094** \u2192 warning (still renders);\n- **device IP outside its subnet CIDR** \u2192 error.\n\nThe SVG `<desc>` records device/link counts, the detected topology class (star / ring / bus / mesh / tree / hierarchical / spine-leaf), and any warnings.\n\n---\n\n## 7. Themes\n\n```\ntheme: default # house "network blue" Cisco-style bodies\ntheme: monochrome # clean line-art for print/audit (link meaning via line-style + tags)\ntheme: dark # Catppuccin Mocha\n```\n\nCJK labels and `\u300C\u2026\u300D` / `"\u2026"` quotes parse cleanly:\n\n```\nnetwork "\u529E\u516C\u5BA4"\n multilayer core1 \u300C\u6838\u5FC3\u4EA4\u6362\u673A\u300D\n poeswitch poe1\n camera cam1 type: dome\n core1 -- poe1 : trunk vlan: 10\n poe1 -- cam1 : poe\n```\n\n---'
1839
2544
  }
1840
2545
  };
1841
2546
 
@@ -2166,6 +2871,59 @@ var PROFILES = {
2166
2871
  prefer: ["Start with participants/messages, then add fragments only if control flow matters."],
2167
2872
  avoid: ["Avoid Mermaid `sequenceDiagram` header; this parser uses `sequence`."],
2168
2873
  repair: ["Unmatched `end`, `else`, or activation statements are validation failures."]
2874
+ },
2875
+ petri: {
2876
+ type: "petri",
2877
+ header: 'petri "Title"',
2878
+ mode: "declared places + transitions + arcs",
2879
+ forms: [
2880
+ "place P1 *1",
2881
+ "transition T1",
2882
+ "P1 -> T1",
2883
+ "T1 -> P2",
2884
+ "P3 -> T2 weight: 2"
2885
+ ],
2886
+ prefer: [
2887
+ "Declare every place and transition before any arc references it.",
2888
+ "Use `*n` or `tokens: n` for the initial marking and `weight: n` for arc multiplicity > 1.",
2889
+ "Keep arcs bipartite: every arc goes place\u2192transition or transition\u2192place."
2890
+ ],
2891
+ avoid: [
2892
+ "Avoid place\u2192place or transition\u2192transition arcs.",
2893
+ "Avoid `-o`/`=>` arcs from a transition; inhibitor and reset arcs are place\u2192transition only."
2894
+ ],
2895
+ repair: [
2896
+ "An 'unknown node' error means an arc references an undeclared place/transition \u2014 declare it first.",
2897
+ "Set the initial marking so the transitions you intend to be enabled actually have enough input tokens."
2898
+ ]
2899
+ },
2900
+ network: {
2901
+ type: "network",
2902
+ header: 'network "Title"',
2903
+ mode: "typed device declarations + links + optional boundaries",
2904
+ forms: [
2905
+ "layout: tiered",
2906
+ 'router r1 "Edge Router"',
2907
+ "switch core1 tier: core",
2908
+ "camera cam1 type: dome ip: 192.168.20.11",
2909
+ "core1 -- poe1 : trunk vlan: 20 1G",
2910
+ "poe1 -- cam1 : poe",
2911
+ 'subnet cams "192.168.20.0/24" { cam1 poe1 }'
2912
+ ],
2913
+ prefer: [
2914
+ "Declare every device with its kind before any link references it.",
2915
+ "Pick one `layout:` \u2014 tiered (default), tree, star, ring, bus, mesh, spine-leaf, or manual.",
2916
+ "Set `tier:` (edge/core/distribution/access) on infrastructure to drive the hierarchical bands.",
2917
+ "Annotate links after `:` with link-type (fiber/wireless/serial/poe/vpn/lag), mode (trunk/access), `vlan:`, `port:`, and a speed like 1G/10G."
2918
+ ],
2919
+ avoid: [
2920
+ "Avoid linking to an undeclared device id.",
2921
+ "Avoid VLAN ids outside 1\u20134094 and device IPs outside their subnet's CIDR."
2922
+ ],
2923
+ repair: [
2924
+ "An 'undeclared device' error means a link references an id with no `kind id` declaration \u2014 declare it first.",
2925
+ "A subnet-membership error means a device `ip:` falls outside the subnet label CIDR \u2014 fix the IP or the CIDR."
2926
+ ]
2169
2927
  }
2170
2928
  };
2171
2929
  function getGenerationProfile(type) {
@@ -2309,6 +3067,6 @@ function repairHint(type) {
2309
3067
  ].filter(Boolean).join(" ");
2310
3068
  }
2311
3069
 
2312
- export { DIAGRAM_REGISTRY, getAllDiagramTypes, getDiagramMeta, getExamples, getSyntax, listDiagrams, renderDsl, resolveDiagramType, validateDsl };
2313
- //# sourceMappingURL=chunk-VKPCR7BG.js.map
2314
- //# sourceMappingURL=chunk-VKPCR7BG.js.map
3070
+ export { DIAGRAM_REGISTRY, DIAGRAM_SINCE, getAllDiagramTypes, getDiagramMeta, getDiagramSince, getExamples, getSyntax, listDiagrams, renderDsl, resolveDiagramType, validateDsl };
3071
+ //# sourceMappingURL=chunk-P63S7P6N.js.map
3072
+ //# sourceMappingURL=chunk-P63S7P6N.js.map