schematex 0.4.3 → 0.5.2

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 (98) hide show
  1. package/README.md +36 -7
  2. package/dist/ai/ai-sdk.cjs +19 -13
  3. package/dist/ai/ai-sdk.cjs.map +1 -1
  4. package/dist/ai/ai-sdk.d.cts +4 -3
  5. package/dist/ai/ai-sdk.d.ts +4 -3
  6. package/dist/ai/ai-sdk.js +15 -9
  7. package/dist/ai/ai-sdk.js.map +1 -1
  8. package/dist/ai/index.cjs +16 -12
  9. package/dist/ai/index.d.cts +20 -153
  10. package/dist/ai/index.d.ts +20 -153
  11. package/dist/ai/index.js +4 -4
  12. package/dist/api-C5SECOxZ.d.cts +69 -0
  13. package/dist/api-Cr_MxttI.d.ts +69 -0
  14. package/dist/browser.cjs +24 -6
  15. package/dist/browser.cjs.map +1 -1
  16. package/dist/browser.d.cts +12 -3
  17. package/dist/browser.d.ts +12 -3
  18. package/dist/browser.js +14 -6
  19. package/dist/browser.js.map +1 -1
  20. package/dist/{chunk-SMXU3KYA.cjs → chunk-4QPDZJAL.cjs} +6337 -120
  21. package/dist/chunk-4QPDZJAL.cjs.map +1 -0
  22. package/dist/{chunk-N3SLYVNW.cjs → chunk-HK56GQQP.cjs} +751 -80
  23. package/dist/chunk-HK56GQQP.cjs.map +1 -0
  24. package/dist/{chunk-KWQTF6ZL.js → chunk-OK5ZS3LU.js} +6330 -119
  25. package/dist/chunk-OK5ZS3LU.js.map +1 -0
  26. package/dist/{chunk-2Z543TC3.cjs → chunk-QMTWG6JL.cjs} +1297 -1151
  27. package/dist/chunk-QMTWG6JL.cjs.map +1 -0
  28. package/dist/{chunk-VITE3MZQ.cjs → chunk-QTNPMIO2.cjs} +99 -5
  29. package/dist/chunk-QTNPMIO2.cjs.map +1 -0
  30. package/dist/{chunk-6IGSKU6D.js → chunk-UFTYX73U.js} +99 -5
  31. package/dist/chunk-UFTYX73U.js.map +1 -0
  32. package/dist/{chunk-J7JWMQD5.js → chunk-VCH7RI5H.js} +1297 -1151
  33. package/dist/chunk-VCH7RI5H.js.map +1 -0
  34. package/dist/{chunk-UBTKM2TB.js → chunk-VKPCR7BG.js} +751 -81
  35. package/dist/chunk-VKPCR7BG.js.map +1 -0
  36. package/dist/diagrams/blockdiagram/index.d.cts +1 -1
  37. package/dist/diagrams/blockdiagram/index.d.ts +1 -1
  38. package/dist/diagrams/circuit/index.cjs +7 -7
  39. package/dist/diagrams/circuit/index.d.cts +1 -1
  40. package/dist/diagrams/circuit/index.d.ts +1 -1
  41. package/dist/diagrams/circuit/index.js +1 -1
  42. package/dist/diagrams/ecomap/index.d.cts +1 -1
  43. package/dist/diagrams/ecomap/index.d.ts +1 -1
  44. package/dist/diagrams/entity/index.d.cts +1 -1
  45. package/dist/diagrams/entity/index.d.ts +1 -1
  46. package/dist/diagrams/fishbone/index.d.cts +1 -1
  47. package/dist/diagrams/fishbone/index.d.ts +1 -1
  48. package/dist/diagrams/flowchart/index.cjs +7 -7
  49. package/dist/diagrams/flowchart/index.d.cts +2 -2
  50. package/dist/diagrams/flowchart/index.d.ts +2 -2
  51. package/dist/diagrams/flowchart/index.js +1 -1
  52. package/dist/diagrams/genogram/index.d.cts +1 -1
  53. package/dist/diagrams/genogram/index.d.ts +1 -1
  54. package/dist/diagrams/ladder/index.d.cts +1 -1
  55. package/dist/diagrams/ladder/index.d.ts +1 -1
  56. package/dist/diagrams/logic/index.d.cts +1 -1
  57. package/dist/diagrams/logic/index.d.ts +1 -1
  58. package/dist/diagrams/orgchart/index.d.cts +1 -1
  59. package/dist/diagrams/orgchart/index.d.ts +1 -1
  60. package/dist/diagrams/pedigree/index.d.cts +1 -1
  61. package/dist/diagrams/pedigree/index.d.ts +1 -1
  62. package/dist/diagrams/phylo/index.d.cts +1 -1
  63. package/dist/diagrams/phylo/index.d.ts +1 -1
  64. package/dist/diagrams/sld/index.d.cts +1 -1
  65. package/dist/diagrams/sld/index.d.ts +1 -1
  66. package/dist/diagrams/sociogram/index.d.cts +1 -1
  67. package/dist/diagrams/sociogram/index.d.ts +1 -1
  68. package/dist/diagrams/timing/index.d.cts +1 -1
  69. package/dist/diagrams/timing/index.d.ts +1 -1
  70. package/dist/diagrams/venn/index.d.cts +1 -1
  71. package/dist/diagrams/venn/index.d.ts +1 -1
  72. package/dist/{index-DYpJXJcy.d.cts → index-BD2yDfQM.d.cts} +1 -1
  73. package/dist/{index-BeUVaQiD.d.ts → index-C30zQWZI.d.ts} +1 -1
  74. package/dist/index.cjs +35 -11
  75. package/dist/index.d.cts +10 -4
  76. package/dist/index.d.ts +10 -4
  77. package/dist/index.js +3 -3
  78. package/dist/react.cjs +8 -10
  79. package/dist/react.cjs.map +1 -1
  80. package/dist/react.d.cts +3 -2
  81. package/dist/react.d.ts +3 -2
  82. package/dist/react.js +8 -10
  83. package/dist/react.js.map +1 -1
  84. package/dist/tools-BHWaJPOl.d.ts +153 -0
  85. package/dist/tools-DlpuE76u.d.cts +153 -0
  86. package/dist/{types-DKdo-Ua_.d.cts → types-WTr9W5Ud.d.cts} +3 -3
  87. package/dist/{types-DKdo-Ua_.d.ts → types-WTr9W5Ud.d.ts} +3 -3
  88. package/package.json +2 -2
  89. package/dist/api-BIj9t4Oc.d.cts +0 -22
  90. package/dist/api-BIj9t4Oc.d.ts +0 -22
  91. package/dist/chunk-2Z543TC3.cjs.map +0 -1
  92. package/dist/chunk-6IGSKU6D.js.map +0 -1
  93. package/dist/chunk-J7JWMQD5.js.map +0 -1
  94. package/dist/chunk-KWQTF6ZL.js.map +0 -1
  95. package/dist/chunk-N3SLYVNW.cjs.map +0 -1
  96. package/dist/chunk-SMXU3KYA.cjs.map +0 -1
  97. package/dist/chunk-UBTKM2TB.js.map +0 -1
  98. package/dist/chunk-VITE3MZQ.cjs.map +0 -1
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var chunkSMXU3KYA_cjs = require('./chunk-SMXU3KYA.cjs');
3
+ var chunk4QPDZJAL_cjs = require('./chunk-4QPDZJAL.cjs');
4
4
 
5
5
  // src/ai/registry.ts
6
6
  var DIAGRAM_REGISTRY = [
@@ -208,6 +208,44 @@ var DIAGRAM_REGISTRY = [
208
208
  standard: "OMG BPMN 2.0.2 / ISO/IEC 19510:2013 visual subset; see 25-BPMN-STANDARD.md",
209
209
  syntaxKey: "bpmn"
210
210
  },
211
+ {
212
+ type: "usecase",
213
+ name: "UML use case diagram",
214
+ tagline: "UML 2.5 use case \u2014 actors, use cases, subject boundary, include/extend/generalization.",
215
+ 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).",
216
+ cluster: "behavior-modeling",
217
+ standard: "OMG UML 2.5.1 \xA718 visual subset; see 29-USECASE-STANDARD.md",
218
+ syntaxKey: "usecase"
219
+ },
220
+ {
221
+ type: "sequence",
222
+ name: "UML sequence diagram",
223
+ tagline: "UML 2.5.1 \xA717 interaction \u2014 lifelines, messages over time, activations, and combined fragments.",
224
+ 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).",
225
+ cluster: "behavior-modeling",
226
+ standard: "OMG UML 2.5.1 \xA717 (Interactions); see 33-SEQUENCE-STANDARD.md",
227
+ syntaxKey: "sequence"
228
+ },
229
+ // ── Research / evidence synthesis ────────────────────────────
230
+ {
231
+ type: "prisma",
232
+ name: "PRISMA 2020 flow diagram",
233
+ tagline: "PRISMA 2020 \u2014 the mandatory four-row flow diagram for systematic reviews and meta-analyses.",
234
+ useWhen: "Use whenever the user mentions 'PRISMA', 'systematic review', 'meta-analysis flow', 'scoping review', 'evidence screening', or 'Cochrane review' \u2014 this is the dedicated, standards-correct engine (prefer it over a generic flowchart). The author writes record counts and exclusion reasons; the rigid four-row layout (Identification \u2192 Screening \u2192 Eligibility \u2192 Included) is correct by construction, with mandatory `n = \u2026` counts, parallel exclusion side-boxes, and an optional second 'other methods' column (`mode: 2020-dual`). Vocabulary swaps for scoping reviews (`kind: scoping-review`) and IPD (`kind: ipd`). Count arithmetic is validated (`validate-counts: warn|strict|off`). Distinct from `flowchart` (no mandatory stages/counts/exclusion-box convention).",
235
+ cluster: "research",
236
+ standard: "PRISMA 2020 (Page MJ et al., BMJ 2021;372:n71); see 28-PRISMA-STANDARD.md",
237
+ syntaxKey: "prisma"
238
+ },
239
+ // ── Project management / scheduling ──────────────────────────
240
+ {
241
+ type: "pert",
242
+ name: "PERT / CPM network",
243
+ tagline: "Activity-on-node project schedule that computes ES/EF/LS/LF, slack, and the critical path.",
244
+ useWhen: "Use whenever the user mentions 'PERT', 'CPM', 'critical path', 'project network', 'precedence diagram', or wants a project schedule from tasks + durations + dependencies. Unlike a flowchart, this engine *computes* the schedule: write `task <id> \"label\" duration: <n> after: <preds>` and it runs the forward/backward pass and returns Early/Late Start & Finish, total slack, project duration, and highlights the critical path in red. Supports PDM dependency types (FS/SS/FF/SF) with lag/lead (`after: A SS+2d`), three-point estimation (`duration: 4/6/10` \u2192 te + variance), milestones (`milestone`), swimlanes (`lane: \"Team\"`), a `layout: timescaled` mode (x \u221D ES, width \u221D duration) for a network-Gantt hybrid, and a legacy `layout: aoa` mode (activity-on-arrow: numbered event circles + arrow activities + dummy activities, FS-only). Distinct from `flowchart` (no scheduling), `timeline`/Gantt (no critical-path computation), and `bpmn` (organisational process, not a one-off schedule).",
245
+ cluster: "project-management",
246
+ standard: "PMI PMBOK 7 + Moder 1983 (AON/PDM); see 32-PERT-STANDARD.md",
247
+ syntaxKey: "pert"
248
+ },
211
249
  // ── Generic process / flow ───────────────────────────────────
212
250
  {
213
251
  type: "flowchart",
@@ -257,46 +295,26 @@ var DIAGRAM_REGISTRY = [
257
295
  syntaxKey: "timeline"
258
296
  }
259
297
  ];
298
+ var TYPE_ALIASES = {
299
+ block: "blockdiagram",
300
+ "entity-structure": "entity",
301
+ graph: "flowchart",
302
+ statediagram: "state",
303
+ "statediagram-v2": "state",
304
+ sequencediagram: "sequence"
305
+ };
306
+ function resolveDiagramType(type) {
307
+ const normalized = type.trim().toLowerCase();
308
+ return DIAGRAM_REGISTRY.find((d) => d.type === normalized)?.type ?? TYPE_ALIASES[normalized];
309
+ }
260
310
  function getDiagramMeta(type) {
261
- return DIAGRAM_REGISTRY.find((d) => d.type === type);
311
+ const resolved = resolveDiagramType(type);
312
+ return DIAGRAM_REGISTRY.find((d) => d.type === resolved);
262
313
  }
263
314
  function getAllDiagramTypes() {
264
315
  return DIAGRAM_REGISTRY.map((d) => d.type);
265
316
  }
266
317
 
267
- // src/ai/errors.ts
268
- var ENGINE_BUG_NAMES = /* @__PURE__ */ new Set([
269
- "ReferenceError",
270
- "TypeError",
271
- "RangeError"
272
- ]);
273
- function extractError(err) {
274
- if (err instanceof Error) {
275
- const anyErr = err;
276
- const hasParseFields = typeof anyErr.line === "number";
277
- const isEngineBug = !hasParseFields && ENGINE_BUG_NAMES.has(err.name);
278
- const sourceHint = isEngineBug ? firstStackFrame(err.stack) : typeof anyErr.source === "string" ? anyErr.source : void 0;
279
- return {
280
- line: typeof anyErr.line === "number" ? anyErr.line : void 0,
281
- column: typeof anyErr.column === "number" ? anyErr.column : void 0,
282
- source: sourceHint,
283
- message: isEngineBug ? `[engine bug: ${err.name}] ${err.message}` : err.message,
284
- hint: typeof anyErr.hint === "string" ? anyErr.hint : isEngineBug ? "This looks like a Schematex internal error rather than a DSL syntax problem. Please file an issue with the failing DSL at https://github.com/SchemaTex/Schematex/issues." : void 0
285
- };
286
- }
287
- return { message: String(err) };
288
- }
289
- function firstStackFrame(stack) {
290
- if (!stack) return void 0;
291
- for (const line of stack.split("\n")) {
292
- const trimmed = line.trim();
293
- if (trimmed.startsWith("at ")) {
294
- return trimmed.replace(/\((?:.*\/)?([^/]+)\)/, "($1)");
295
- }
296
- }
297
- return void 0;
298
- }
299
-
300
318
  // src/ai/_generated.ts
301
319
  var EXAMPLES = [
302
320
  {
@@ -748,25 +766,6 @@ If the LED doesn't light up, three things to check, in order: LED polarity (the
748
766
  "dsl": "flowchart LR\n start([New order received])\n start --> validate{Inventory available?}\n validate -->|Yes| reserve[Reserve items]\n validate -->|No| notify[Notify customer]\n notify --> done([End])\n reserve --> payment{Payment authorized?}\n payment -->|Yes| ship[Ship order]\n payment -->|No| cancel[Cancel & release]\n ship --> confirm[Send confirmation email]\n confirm --> done\n cancel --> done",
749
767
  "notes": '## Scenario\n\nA product ops lead circulates this flowchart during an ops-review meeting to align engineering, customer support, and fulfillment on the single source of truth for what happens when a new order comes in. It surfaces the two decision gates (inventory, payment) and the three exception paths (out-of-stock notification, payment failure with released hold, successful ship with confirmation).\n\n## Annotation key\n\n- `([\u2026])` \u2014 stadium / terminal; used for Start and End\n- `{\u2026}` \u2014 diamond; decision node\n- `[\u2026]` \u2014 rectangle; process step\n- `-->|label|` \u2014 edge with a branch label (`Yes` / `No`)\n\n## How to read\n\nStart at the top-left terminal. Inventory check gates the first branch \u2014 a "No" routes straight to the End after notification. A "Yes" reserves stock then hits the payment gate. Payment failure releases the reservation and goes to End; success ships and emails the customer. Every path terminates at the same End node, so nothing dangles.'
750
768
  },
751
- {
752
- "slug": "flowchart-prisma-systematic-review",
753
- "diagram": "flowchart",
754
- "title": "PRISMA 2020 systematic review flow",
755
- "description": "Canonical PRISMA 2020 flow diagram for a systematic review \u2014 records identified across databases, screened, assessed for eligibility, and included in synthesis.",
756
- "standard": "PRISMA 2020 (Page et al., BMJ 2021)",
757
- "tags": [
758
- "flowchart",
759
- "prisma",
760
- "systematic-review",
761
- "meta-analysis",
762
- "research",
763
- "evidence-synthesis"
764
- ],
765
- "complexity": 3,
766
- "featured": true,
767
- "dsl": 'flowchart TD\n classDef excluded fill:#fef2f2,stroke:#b91c1c,stroke-width:1px,color:#7f1d1d\n\n subgraph ID ["Identification"]\n pubmed["PubMed (n = 482)"]\n embase["Embase (n = 314)"]\n cochrane["Cochrane (n = 91)"]\n registers["Trial registers (n = 38)"]\n end\n\n subgraph SCREEN ["Screening"]\n dedup["After duplicates removed (n = 712)"]\n titles["Title and abstract screened (n = 712)"]\n excl_titles["Records excluded (n = 598)"]\n fulltext["Full-text sought (n = 114)"]\n retrieved["Assessed for eligibility (n = 109)"]\n not_retrieved["Not retrieved (n = 5)"]\n end\n\n subgraph ELIG ["Eligibility"]\n assessed["Full-text articles assessed (n = 109)"]\n excl_reasons["Excluded with reasons (n = 60)"]\n r1["Wrong population (n = 18)"]\n r2["Wrong intervention (n = 22)"]\n r3["Wrong outcome (n = 11)"]\n r4["Conference abstract only (n = 9)"]\n end\n\n included(["Studies in qualitative synthesis (n = 49)"])\n meta(["Studies in meta-analysis (n = 31)"])\n\n pubmed --> dedup\n embase --> dedup\n cochrane --> dedup\n registers --> dedup\n dedup --> titles\n titles -->|excluded| excl_titles\n titles -->|kept| fulltext\n fulltext --> retrieved\n fulltext -->|paywall / no response| not_retrieved\n retrieved --> assessed\n assessed -->|excluded with reasons| excl_reasons\n excl_reasons --> r1\n excl_reasons --> r2\n excl_reasons --> r3\n excl_reasons --> r4\n assessed -->|met inclusion criteria| included\n included --> meta\n\n class excl_titles,not_retrieved,excl_reasons,r1,r2,r3,r4 excluded',
768
- "notes": '## Scenario\n\nA research librarian working with a clinical team produces the PRISMA 2020 flow diagram for an upcoming Cochrane review submission. The journal **requires** the diagram in this exact four-phase structure (Identification \u2192 Screening \u2192 Eligibility \u2192 Included), with the count `(n = \u2026)` shown explicitly in every box and the excluded-with-reasons box itemizing rejection reasons. Authoring this in a DSL (rather than redrawing in Word or BioRender) means the counts can be regenerated as the screening team updates the search.\n\n## Annotation key\n\n- **Subgraphs** name the four PRISMA phases: `subgraph ID ["Identification"]`, etc. \u2014 the rendered output groups boxes inside a labeled cluster border.\n- **`(n = N)` inline** \u2014 keep the count in the same label (PRISMA 2020 requires the count to be visible in every box; inline is the simplest layout that survives wide labels).\n- **`classDef excluded`** + **`class A,B excluded`** \u2014 applies the red-tinted class to the "records excluded" boxes so the eliminated stream is visually separated from the surviving stream.\n- **Edge labels** (`-->|excluded|`, `-->|met inclusion criteria|`) annotate the screening decision on each fork.\n- **Stadium nodes** (`(["\u2026"])`) mark the two terminal outcomes (qualitative synthesis count, meta-analysis count) so readers\' eyes land there.\n\n## How to read\n\nTop to bottom, the four phases mirror the PRISMA 2020 template exactly:\n\n1. **Identification** \u2014 every database and register the team searched, with raw record counts before deduplication.\n2. **Screening** \u2014 after deduplication, records pass through title/abstract screening (most are excluded here), then eligible records advance to full-text retrieval. Records lost at retrieval (paywall, no response from authors) are split into a separate "not retrieved" box per PRISMA 2020 reporting requirements.\n3. **Eligibility** \u2014 full-text articles are assessed against inclusion/exclusion criteria. Exclusions **must** be itemized with reasons \u2014 this is the box reviewers most often flag if missing.\n4. **Included** \u2014 final study count for the qualitative synthesis, with a sub-count for studies that contributed to the meta-analysis (not all included studies always do).\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. The flow-diagram template lives at [prisma-statement.org/prisma-2020-flow-diagram](https://www.prisma-statement.org/prisma-2020-flow-diagram).'
769
- },
770
769
  {
771
770
  "slug": "genogram-brca-cancer",
772
771
  "diagram": "genogram",
@@ -1132,6 +1131,97 @@ If the LED doesn't light up, three things to check, in order: LED polarity (the
1132
1131
  "dsl": 'pedigree "Hemophilia A"\n I-1 [male, unaffected]\n I-2 [female, carrier-x]\n I-1 -- I-2\n II-1 [male, affected]\n II-2 [female, carrier-x]\n II-3 [male, unaffected]\n II-4 [female, unaffected]\n II-2 -- II-5 [male, unaffected]\n III-1 [male, affected]\n III-2 [female, carrier-x]\n III-3 [male, unaffected]',
1133
1132
  "notes": "## Scenario\n\nA genetic counselor documents a three-generation hemophilia A pedigree during a prenatal consultation. The X-linked recessive pattern \u2014 carrier females who show no symptoms but pass the mutated allele \u2014 must be clearly distinguished from affected males. NSGC standard notation is required for clinical records and insurance pre-authorization.\n\n## Annotation key\n\n- `carrier-x` \u2014 female carrier of an X-linked recessive allele; rendered as a circle with a centre dot per NSGC convention\n- `affected` \u2014 fully filled symbol; individual expresses the condition\n- `unaffected` \u2014 open (unfilled) symbol; no clinical presentation\n- `proband` \u2014 the index case who prompted clinical referral (not used here, but add `proband` to any individual)\n- `I-1 -- I-2` followed by indented children \u2014 defines a mating pair and their offspring\n\n## How to read\n\nGeneration I: unaffected father, carrier mother. Generation II: one affected son (II-1), one carrier daughter (II-2), two unaffected children. Generation III: carrier daughter II-2 married an unaffected man; they produced another affected son (III-1) and another carrier daughter (III-2) \u2014 demonstrating the classic X-linked skip-generation pattern where the trait disappears in daughters only to re-emerge in grandsons."
1134
1133
  },
1134
+ {
1135
+ "slug": "pert-aoa-software-project",
1136
+ "diagram": "pert",
1137
+ "title": "Activity-on-arrow (AOA) network",
1138
+ "description": "The classic textbook PERT notation \u2014 numbered event circles, activities as labelled arrows, and dotted dummy activities auto-inserted at multi-predecessor merges. Written in the same activity-on-node DSL; Schematex builds and numbers the event graph.",
1139
+ "standard": "PMI PMBOK 7 + Moder 1983 \u2014 Activity-on-Arrow (ADM, legacy)",
1140
+ "tags": [
1141
+ "pert",
1142
+ "aoa",
1143
+ "activity-on-arrow",
1144
+ "critical-path",
1145
+ "education"
1146
+ ],
1147
+ "complexity": 2,
1148
+ "featured": false,
1149
+ "dsl": 'pert\ntitle: "Software project (AOA)"\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\ntask H "test system" duration: 10 after: F\ntask I "training" duration: 5 after: G\ntask J "user test" duration: 10 after: H, I',
1150
+ "notes": "Before activity-on-node won, project networks were drawn **activity-on-arrow** (AOA / ADM): the *arrows* are the work and the *nodes* are events \u2014 the instants when activities start and finish. It's the form on Investopedia and in older textbooks, and it's what `layout: aoa` produces.\n\n**You still write activity-on-node.** The DSL is identical to every other PERT example \u2014 tasks, durations, `after:` dependencies. Schematex converts it: each activity gets a head event, and the events are numbered so every arrow runs from a lower id to a higher one (the classic *i-j* rule).\n\n**Dummy activities.** AOA can't show two activities merging into one without help, so wherever an activity has multiple predecessors a **dummy** (dotted, zero-duration arrow) is inserted. Here `test code` depends on both `programming` and `conversion`, and `user test` on both `test system` and `training` \u2014 each merge gets dummies that carry the dependency without representing real work.\n\n**The critical path is still computed.** The forward/backward pass runs on the event graph and the critical activities are drawn as bold red arrows: `create schedule \u2192 programming \u2192 test code \u2192 test system \u2192 user test` (70 days).\n\n**One caveat.** AOA can only express finish-to-start logic \u2014 there's no way to draw a start-to-start or finish-to-finish relationship as an arrow-between-events. If your network uses SS/FF/SF or lag, those are flattened to FS with a warning. For real scheduling, prefer the default `network` (AON) layout; reach for `aoa` when you need to match a textbook figure."
1151
+ },
1152
+ {
1153
+ "slug": "pert-migration-timescaled",
1154
+ "diagram": "pert",
1155
+ "title": "Time-scaled migration plan (network-Gantt)",
1156
+ "description": "A data-centre migration in the time-scaled layout \u2014 x-position proportional to Early Start, width proportional to duration, with a unit time axis. Exercises start-to-start and finish-to-finish dependencies, lag/lead, a milestone, and lane packing.",
1157
+ "standard": "PMI PMBOK 7 + Moder 1983 (AON/PDM)",
1158
+ "tags": [
1159
+ "pert",
1160
+ "cpm",
1161
+ "timescaled",
1162
+ "gantt",
1163
+ "dependencies",
1164
+ "milestone"
1165
+ ],
1166
+ "complexity": 3,
1167
+ "featured": false,
1168
+ "dsl": 'pert\ntitle: "Data-centre migration"\nunit: days\nlayout: timescaled\n\ntask A "Inventory systems" duration: 5\ntask B "Stakeholder review" duration: 6 after: A SS+1\ntask C "Vendor selection" duration: 8 after: A, B\ntask D "Architecture" duration: 10 after: C\ntask E "Procurement" duration: 12 after: C+2\ntask F "Code refactor" duration: 15 after: D\ntask G "Pilot env" duration: 5 after: E, F FF\ntask H "Pilot run" duration: 7 after: G\ntask I "Cutover" milestone after: H\ntask J "Hypercare" duration: 5 after: I',
1169
+ "notes": "`layout: timescaled` is the bridge between the network view and a Gantt chart. Each activity's **x-position is proportional to its Early Start** and its **width is proportional to its duration**, with a unit time axis along the bottom \u2014 so distance reads as time.\n\n**Full PDM dependencies.** This network uses every relationship type beyond the default finish-to-start. `B after: A SS+1` is start-to-start with a 1-day lag (stakeholder review begins a day into the inventory). `E after: C+2` is FS with a 2-day lag (procurement waits two days after vendor selection finishes). `G after: ... F FF` is finish-to-finish (the pilot environment must be ready *when* the code refactor finishes, not after it). Each non-FS edge is labelled on the connector (`SS+1d`, `FS+2d`, `FF`).\n\n**Milestones and lane packing.** `Cutover` is declared with the `milestone` flag \u2014 a zero-duration checkpoint drawn as a diamond at its scheduled date. To avoid overlaps, activities are greedily packed into horizontal lanes by start time, and task names sit above their bars (Gantt convention) so long labels never overflow a short bar.\n\n**Still computed, still critical-aware.** Even in the time-scaled view the critical path is highlighted in red and slack is shown on each bar. `Procurement` (E) carries 6 days of slack and renders in blue, visibly off the critical chain `A \u2192 C \u2192 D \u2192 F \u2192 G \u2192 H \u2192 I \u2192 J`."
1170
+ },
1171
+ {
1172
+ "slug": "pert-product-launch",
1173
+ "diagram": "pert",
1174
+ "title": "Q3 product launch PERT/CPM network",
1175
+ "description": "A seven-task product-launch schedule where the engine computes every Early/Late Start & Finish, total slack, and the critical path (A \u2192 C \u2192 D \u2192 E \u2192 G) automatically \u2014 the canonical activity-on-node network with the six-field box.",
1176
+ "standard": "PMI PMBOK 7 + Moder 1983 (AON/PDM)",
1177
+ "tags": [
1178
+ "pert",
1179
+ "cpm",
1180
+ "critical-path",
1181
+ "schedule",
1182
+ "project-management"
1183
+ ],
1184
+ "complexity": 2,
1185
+ "featured": true,
1186
+ "dsl": 'pert\ntitle: "Q3 Product Launch"\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\ntask E "QA / testing" duration: 5 after: D\ntask F "Marketing collateral" duration: 7 after: B\ntask G "Launch event" duration: 2 after: E, F',
1187
+ "notes": 'This is what separates Schematex\'s `pert` engine from a drag-and-drop "PERT chart maker": you write only the tasks, durations, and dependencies \u2014 the schedule is *computed*.\n\n**The six-field box.** Each activity renders as the canonical 3\xD72 PERT/CPM rectangle: Early Start / Duration / Early Finish on top, the task name and id in the middle, and Late Start / Slack / Late Finish on the bottom. Those four schedule fields are never typed by hand \u2014 a forward pass walks the network to find ES/EF, a backward pass finds LS/LF, and total slack is `LS \u2212 ES`.\n\n**The critical path falls out of the math.** Any activity with zero slack is on the critical path and is drawn in red: `A \u2192 C \u2192 D \u2192 E \u2192 G`. Here `C` (Backend API, 15 days) is the long pole \u2014 it dominates `B` (Design mockups, 8 days) at the `D` merge, so the schedule pivots on the backend, not the design. Change `C` to 6 days and the critical path shifts to run through `B` instead; the render updates because the computation does.\n\n**Parallel work, one merge.** `B` and `C` both start once research finishes, run concurrently, and re-converge at `D` (Frontend build). `F` (Marketing collateral) branches off `B` and carries slack \u2014 it can slip several days without moving the launch. The project duration, 37 days, is the maximum Early Finish over the terminal tasks, not simply the last task you declared.'
1188
+ },
1189
+ {
1190
+ "slug": "pert-swimlane-online-shop",
1191
+ "diagram": "pert",
1192
+ "title": "PERT swimlanes grouped by team",
1193
+ "description": "The same computed activity-on-node schedule, re-grouped into horizontal swimlanes by responsible team (Customer Account, Shopping Site, Shopping Cart, Testing) \u2014 the way Visual Paradigm and PMO templates present a project network.",
1194
+ "standard": "PMI PMBOK 7 + Moder 1983 (AON/PDM)",
1195
+ "tags": [
1196
+ "pert",
1197
+ "cpm",
1198
+ "swimlane",
1199
+ "critical-path",
1200
+ "project-management"
1201
+ ],
1202
+ "complexity": 2,
1203
+ "featured": false,
1204
+ "dsl": 'pert\ntitle: "Online Shop Project"\nunit: days\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 to the Site" duration: 15 after: T2 lane: "Shopping Site"\ntask T4 "Improve Searching" duration: 7 after: T3 lane: "Shopping Site"\ntask T5 "Enhance Shopping Cart Functionality" duration: 8 after: T2 lane: "Shopping Cart"\ntask T6 "Enhance Shopping Cart Checkout" duration: 6 after: T5 lane: "Shopping Cart"\ntask T7 "Ready Testing Environment" duration: 2 after: T1, T4, T6 lane: "Testing"\ntask T8 "Test Online Shop" duration: 8 after: T7 lane: "Testing"',
1205
+ "notes": 'A flat network answers "what\'s the critical path?"; a swimlane network also answers "who owns what?". Add `lane: "\u2026"` to any task and the diagram re-groups into horizontal bands \u2014 by team, phase, or owner \u2014 without changing a single number in the schedule.\n\n**Same engine, different presentation.** This is still pure activity-on-node: the forward/backward pass runs exactly as it would without lanes, so the critical path `T2 \u2192 T3 \u2192 T4 \u2192 T7 \u2192 T8` and every slack value are identical to the ungrouped layout. Lanes are a *view*, not a computation \u2014 they activate automatically the moment any task declares one.\n\n**Reading the bands.** Lanes appear in first-declared order, each as a banded row with a label gutter on the left. A task sits in the column for its dependency depth but the row for its team, so you can trace both the time order (left to right) and the ownership (top to bottom) at a glance. Dependencies that cross teams \u2014 `T1` (Customer Account) and `T4` (Shopping Site) and `T6` (Shopping Cart) all feeding `T7` (Testing) \u2014 render as orthogonal connectors hopping between bands, making hand-off points obvious.\n\n**Where this helps.** This is the format PMO templates and tools like Visual Paradigm use for status reviews, because a stakeholder can find their team\'s row instantly and see what it\'s waiting on and what\'s waiting on it.'
1206
+ },
1207
+ {
1208
+ "slug": "pert-three-point-estimation",
1209
+ "diagram": "pert",
1210
+ "title": "Three-point (PERT) estimation with variance",
1211
+ "description": "Durations written as optimistic/most-likely/pessimistic (O/M/P). The engine computes the beta-distribution expected duration te = (O+4M+P)/6 per activity, the per-task variance, and the project-level standard deviation along the critical path.",
1212
+ "standard": "PMI PMBOK 7 \u2014 three-point (beta-PERT) estimation",
1213
+ "tags": [
1214
+ "pert",
1215
+ "three-point",
1216
+ "estimation",
1217
+ "variance",
1218
+ "critical-path"
1219
+ ],
1220
+ "complexity": 2,
1221
+ "featured": false,
1222
+ "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',
1223
+ "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`."
1224
+ },
1135
1225
  {
1136
1226
  "slug": "phylo-bacterial-diversity",
1137
1227
  "diagram": "phylo",
@@ -1150,6 +1240,131 @@ If the LED doesn't light up, three things to check, in order: LED polarity (the
1150
1240
  "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"',
1151
1241
  "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.'
1152
1242
  },
1243
+ {
1244
+ "slug": "pid-pump-flow-control",
1245
+ "diagram": "pid",
1246
+ "title": "Pump with flow control loop (P&ID)",
1247
+ "description": "Classic centrifugal pump pulling from an atmospheric tank, with a flow transmitter (FT-101), flow indicating controller (FIC-101), and a fail-closed pneumatic control valve \u2014 the minimum viable P&ID that every process engineer recognises at a glance.",
1248
+ "standard": "ANSI/ISA-5.1-2009 + ISO 10628-1:2014",
1249
+ "tags": [
1250
+ "pid",
1251
+ "isa-5.1",
1252
+ "flow-control",
1253
+ "centrifugal-pump",
1254
+ "control-valve",
1255
+ "instrument-loop"
1256
+ ],
1257
+ "complexity": 1,
1258
+ "featured": true,
1259
+ "dsl": 'pid "Water Pump Flow Control"\n\nequip T-101 : tank_atm [tag: "Feed Tank"]\nequip P-101 : pump_centrifugal [tag: "Feed Pump"]\nequip V-101 : valve_control [actuator: "diaphragm", fail: "FC"]\nequip V-100 : valve_gate [tag: "Isolation"]\n\nline L1 from T-101.bottom to P-101.in [size: "4\\"", service: "water", type: "process"]\nline L2 from P-101.out to V-101.in [size: "4\\"", service: "water", type: "process"]\nline L3 from V-101.out to dest [size: "4\\"", type: "process"]\n\ninst FT-101 : field_discrete\n measures L2\ninst FIC-101 : cr_shared\n controls V-101\n\nline s1 from FT-101 to FIC-101 [type: "electric"]\nline s2 from FIC-101 to V-101 [type: "pneumatic"]',
1260
+ "notes": 'The pump-with-flow-control loop is to P&ID what the "hello world" program is to software: every process engineer has drawn one, every piping textbook uses it as chapter one, and once you understand it you understand the grammar of every more complex diagram.\n\n**What the diagram shows.** T-101 is an atmospheric tank (dome-roof symbol, open to atmosphere). The centrifugal pump P-101 pulls fluid from the bottom nozzle and pushes it downstream through a 4-inch water service line. The flow transmitter FT-101 \u2014 a field-mounted discrete instrument, drawn as a plain circle per ISA-5.1 \xA74.1 \u2014 sits on the discharge line L2 and measures the volumetric flow rate. Its 4\u201320 mA electric signal travels to FIC-101, the flow indicating controller in the main control room (drawn as a circle with a horizontal line through it, indicating it is panel-mounted and has a display). FIC-101 computes the error between the setpoint and the measured flow and drives V-101, a diaphragm-actuated control valve, open or closed via a pneumatic signal. V-100 is a manual gate valve upstream \u2014 the isolation valve you close when you need to pull the pump for maintenance.\n\n**ISA-5.1 signal line types.** The two non-process lines in this diagram are drawn differently from the main pipe: the electric signal between FT-101 and FIC-101 is a dashed line (`stroke-dasharray: 6 4`), and the pneumatic signal between FIC-101 and V-101 is a solid line with small perpendicular slash marks at regular intervals. These are not stylistic choices \u2014 they are ISA-5.1 \xA75.2 mandatory line type codes. An instrument technician reading the diagram needs to know, at a glance, whether a run of wire failed or an air supply pressure dropped. The line type tells them which.\n\n**Fail-safe position.** V-101 is marked `fail: "FC"` \u2014 fail-closed. When the air supply to the diaphragm actuator is lost (instrument air header trips, tubing rupture), the spring return pushes the valve shut. This is the correct failure mode for a pump discharge valve: loss of control should stop flow, not open it fully. If this were a cooling-water valve on a reactor, you\'d want fail-open instead. The choice of fail position is one of the first decisions in a safety instrumented system review (HAZOP, SIL assessment) and it belongs on the P&ID from day one.\n\n**Why not draw.io.** Process engineers currently draw P&IDs in AutoCAD P&ID, SmartPlant P&ID, or draw.io with stencil libraries \u2014 all of which require manual symbol placement and wiring. The Schematex DSL lets you describe the topology and generate a standards-compliant SVG. This is particularly useful when a process is described in prose (from a process description document or a licensor\'s PFD) and needs to be turned into a P&ID for HAZOP review \u2014 the conversion becomes a text transformation task that LLMs can assist with, producing a reviewable first draft rather than a blank canvas.'
1261
+ },
1262
+ {
1263
+ "slug": "pid-reactor-feed-system",
1264
+ "diagram": "pid",
1265
+ "title": "Reactor feed with multi-loop control and pressure safety (P&ID)",
1266
+ "description": "CSTR reactor system with centrifugal pump, shell-and-tube pre-heater, flow control on the feed line, temperature control on the product outlet, and a PSHH pressure switch interlock \u2014 four instrument loops in one diagram, covering the core vocabulary of ISA-5.1 P&ID engineering.",
1267
+ "standard": "ANSI/ISA-5.1-2009 + ISO 10628-1:2014",
1268
+ "tags": [
1269
+ "pid",
1270
+ "isa-5.1",
1271
+ "reactor",
1272
+ "heat-exchanger",
1273
+ "multi-loop",
1274
+ "pshh",
1275
+ "interlock",
1276
+ "safety"
1277
+ ],
1278
+ "complexity": 3,
1279
+ "featured": false,
1280
+ "dsl": 'pid "High-Pressure Reactor Feed"\n\nequip T-201 : tank_atm [tag: "Raw Material Tank"]\nequip P-201 : pump_centrifugal [tag: "Feed Pump P-201A/B"]\nequip E-201 : hx_shell_tube [tag: "Feed Pre-heater"]\nequip R-201 : reactor_cstr [tag: "Reactor R-201"]\nequip V-201 : valve_control [actuator: "diaphragm", fail: "FC"]\nequip V-202 : valve_control [actuator: "diaphragm", fail: "FO"]\nequip V-203 : valve_psv [set_pressure: "150 psig"]\n\nline L1 from T-201.bottom to P-201.in [size: "6\\"", service: "feed", type: "process"]\nline L2 from P-201.out to E-201.shell_in [size: "6\\"", service: "feed", type: "process"]\nline L3 from E-201.shell_out to V-201.in [size: "6\\"", service: "feed", type: "process"]\nline L4 from V-201.out to R-201.in [size: "6\\"", service: "feed", type: "process"]\nline L5 from R-201.out to V-202.in [size: "4\\"", service: "product", type: "process"]\n\ninst FT-201 : field_discrete\n measures L2\ninst FIC-201 : cr_shared\n controls V-201\n\ninst TT-201 : field_discrete\n measures R-201\ninst TIC-201 : cr_shared\n controls V-202\n\ninst PT-201 : field_discrete\n measures R-201\ninst PSHH-201 : field_discrete\n measures R-201\n\nline s1 from FT-201 to FIC-201 [type: "electric"]\nline s2 from FIC-201 to V-201 [type: "pneumatic"]\nline s3 from TT-201 to TIC-201 [type: "electric"]\nline s4 from TIC-201 to V-202 [type: "pneumatic"]\nline s5 from PT-201 to PSHH-201 [type: "electric"]',
1281
+ "notes": `Real process plants don't have one control loop \u2014 they have a web of them. This diagram shows a CSTR reactor system with four instrument loops and a safety instrument, which is about the minimum complexity for a unit operation that would appear in a HAZOP study. Understanding how to read this diagram cold is a core skill for every process, instrumentation, and safety engineer on the project team.
1282
+
1283
+ **The process path.** Raw material is stored in T-201 (atmospheric tank). The centrifugal pump P-201A/B (the A/B suffix is conventional for spared pumps \u2014 one online, one standby) pulls from the bottom nozzle and pushes through 6-inch feed line L2 to the shell-and-tube heat exchanger E-201, which pre-heats the feed before it enters the reactor. From E-201, the line continues through the feed control valve V-201 into the reactor R-201. The reactor product exits through L5 and the product control valve V-202.
1284
+
1285
+ **Loop 201 \u2014 flow control.** FT-201 (flow transmitter, field-mounted) sits on the pump discharge line L2 and sends a 4\u201320 mA signal to FIC-201 (flow indicating controller, control-room mounted, DCS shared \u2014 note the horizontal line through the circle). FIC-201 closes or opens V-201 to maintain the feed flow setpoint. V-201 is fail-closed: if instrument air is lost, the feed to the reactor stops. Starving a reactor on air loss is usually safer than flooding it.
1286
+
1287
+ **Loop 201T \u2014 temperature control.** TT-201 (temperature transmitter) measures the reactor body temperature and signals TIC-201, which throttles V-202 \u2014 the product outlet valve \u2014 to regulate residence time and therefore heat generation in the reactor. V-202 is fail-open: losing air means the product continues to drain out, preventing dangerous temperature accumulation inside the vessel. The fail-safe position is always chosen by answering the question: "which state causes less harm if control is lost?"
1288
+
1289
+ **Loop 201P \u2014 pressure monitoring and safety.** PT-201 is a field-mounted pressure transmitter \u2014 it sends the continuous pressure reading to the DCS historian. PSHH-201 is a pressure switch, high-high: a discrete field-mounted device that trips at the maximum allowable working pressure (MAWP). It is wired to the safety interlock system (SIS), not the DCS. The distinction matters: DCS loops control the process; SIS loops protect equipment and people. OSHA PSM (29 CFR 1910.119) requires the two to be functionally independent. V-203 is the pressure safety valve \u2014 a spring-loaded valve that opens automatically at the set pressure of 150 psig regardless of any control signal, providing the last line of mechanical protection.
1290
+
1291
+ **Why P&ID, not PFD.** A process flow diagram (PFD) shows the same equipment but only the major process streams, mass balances, and operating conditions. A P&ID adds every instrument, every valve, every signal line, and every utility connection \u2014 it is the engineering document used by instrument engineers to write I/O lists, by safety engineers to perform HAZOP, and by construction teams to verify field installation. The DSL lets you build this level of detail from text, making it tractable for AI-assisted first drafts and version-controlled review cycles.`
1292
+ },
1293
+ {
1294
+ "slug": "prisma-dual-pipeline",
1295
+ "diagram": "prisma",
1296
+ "title": "PRISMA 2020 dual pipeline (databases + other methods)",
1297
+ "description": "PRISMA 2020 flow diagram in dual-pipeline mode \u2014 identification via databases and registers plus a second column for other methods (citation searching, hand searches, expert recommendations), merged into screening via a Y-junction.",
1298
+ "standard": "PRISMA 2020 (Page et al., BMJ 2021;372:n71)",
1299
+ "tags": [
1300
+ "prisma",
1301
+ "systematic-review",
1302
+ "dual-pipeline",
1303
+ "citation-search",
1304
+ "evidence-synthesis",
1305
+ "meta-analysis"
1306
+ ],
1307
+ "complexity": 3,
1308
+ "featured": false,
1309
+ "dsl": "prisma\nmode: 2020-dual\ntitle: Effect of yoga on chronic back pain \u2014 SR\n\nidentification:\n databases:\n n: 1234\n sources: PubMed=600, Embase=450, Cochrane=184\n duplicates-removed: 254\n other:\n n: 56\n sources: citation-search=30, hand-search=20, expert-recommendation=6\n\nscreening:\n records-screened: 1036\n excluded:\n n: 810\n reasons: duplicate=120, irrelevant title=560, non-English=130\n reports-sought: 226\n reports-not-retrieved: 12\n\neligibility:\n full-text-assessed: 214\n excluded:\n n: 191\n reasons: wrong population=80, wrong intervention=60, wrong outcome=51\n\nincluded:\n studies: 23\n reports: 25",
1310
+ "notes": '## Scenario\n\nThe 2020 update to PRISMA added a **second identification pathway** for studies found outside formal database searches \u2014 citation chasing, hand-searching reference lists, and expert recommendations. Reviewers must report these separately and show both streams merging into a single screening process. The `prisma` engine renders the two columns side by side with a Y-junction merge.\n\n## Annotation key\n\n- **`mode: 2020-dual`** \u2014 adds the right-hand "Identification via other methods" column. The engine auto-detects dual mode whenever an `other:` block is present, so the explicit `mode` line is optional here.\n- **`identification.other`** \u2014 its own `n:` total and `sources:` breakdown (citation search, hand search, expert recommendation).\n- **`reports-sought:` / `reports-not-retrieved:`** \u2014 optional Screening detail lines for the retrieval step.\n- **Y-junction merge** \u2014 the databases stream is the trunk; the other-methods stream drops and runs left to join it just above Screening.\n\n## Standard reference\n\nPage MJ, McKenzie JE, Bossuyt PM, et al. *The PRISMA 2020 statement.* BMJ 2021;372:n71, Figure 2 (dual-source variant).'
1311
+ },
1312
+ {
1313
+ "slug": "prisma-systematic-review",
1314
+ "diagram": "prisma",
1315
+ "title": "PRISMA 2020 systematic review (single pipeline)",
1316
+ "description": "Canonical PRISMA 2020 flow diagram for a systematic review using the dedicated prisma engine \u2014 records identified across four databases, deduplicated, screened, assessed for eligibility, and included, with exclusion side-boxes and mandatory n = counts.",
1317
+ "standard": "PRISMA 2020 (Page et al., BMJ 2021;372:n71)",
1318
+ "tags": [
1319
+ "prisma",
1320
+ "systematic-review",
1321
+ "meta-analysis",
1322
+ "research",
1323
+ "evidence-synthesis",
1324
+ "cochrane"
1325
+ ],
1326
+ "complexity": 2,
1327
+ "featured": true,
1328
+ "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",
1329
+ "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).'
1330
+ },
1331
+ {
1332
+ "slug": "sequence-microservices-saga",
1333
+ "diagram": "sequence",
1334
+ "title": "Microservices order saga",
1335
+ "description": "A distributed order-processing saga showing the parts of UML sequence notation that generic tools omit \u2014 a ref interaction-use frame, a par fragment for concurrent service calls, asynchronous event-bus messages, and an alt fragment with a compensating rollback on payment failure.",
1336
+ "standard": "OMG UML 2.5.1 \xA717 (Interactions)",
1337
+ "tags": [
1338
+ "sequence",
1339
+ "uml",
1340
+ "microservices",
1341
+ "saga",
1342
+ "async",
1343
+ "event-driven"
1344
+ ],
1345
+ "complexity": 3,
1346
+ "featured": false,
1347
+ "dsl": 'sequence "Order processing (saga)"\n actor Customer\n participant Gateway as "API Gateway"\n control Orders as "Order Service"\n participant Payment as "Payment Service"\n participant Inventory as "Inventory Service"\n queue Bus as "Event Bus"\n\n Customer -> Gateway : POST /orders\n Gateway ->+ Orders : createOrder()\n ref over Orders, Bus : Validate cart & price\n\n par\n Orders ->> Payment : charge(card)\n and\n Orders ->> Inventory : reserve(items)\n end\n\n alt [payment captured && stock reserved]\n Payment --> Orders : paid\n Inventory --> Orders : reserved\n Orders ->> Bus : OrderConfirmed\n Orders -->- Gateway : 201 Created\n Gateway --> Customer : confirmation\n else [payment failed]\n Orders ->> Inventory : release(items)\n Orders ->> Bus : OrderCancelled\n Orders -->- Gateway : 402 Payment Required\n Gateway --> Customer : declined\n end',
1348
+ "notes": "This is the case that separates a real UML engine from a flowchart with stick figures. A saga is concurrent, asynchronous, and has a compensating path \u2014 and Schematex has dedicated notation for each.\n\n**`ref` keeps the diagram composable.** \"Validate cart & price\" is its own interaction; inlining it here would bury the saga in detail. The `ref over Orders, Bus : \u2026` frame references it instead \u2014 notation most text-to-diagram tools simply don't have.\n\n**`par` shows true concurrency.** The order service charges the card and reserves stock *at the same time*. The `par` fragment's two operands say these run concurrently, not in sequence \u2014 a distinction a top-to-bottom message list cannot express.\n\n**Async vs. reply arrows are not cosmetic.** `->>` (open head) marks fire-and-forget calls and event-bus publishes; `-->` (dashed) marks the replies that come back. Reading the arrowheads tells you which steps block and which don't.\n\n**The compensation lives in the `alt`.** When payment fails, the second operand *releases* the previously reserved inventory and emits `OrderCancelled` \u2014 the saga's rollback, drawn right beside the happy path instead of in a separate diagram."
1349
+ },
1350
+ {
1351
+ "slug": "sequence-oauth-login",
1352
+ "diagram": "sequence",
1353
+ "title": "OAuth 2.0 authorization-code login",
1354
+ "description": "The canonical browser-based sign-in handshake as a UML sequence diagram \u2014 redirect to the auth server, user consent, code-for-token exchange, and an alt fragment for the success vs. failure branch, with activation bars tracking who is busy at each step.",
1355
+ "standard": "OMG UML 2.5.1 \xA717 (Interactions)",
1356
+ "tags": [
1357
+ "sequence",
1358
+ "uml",
1359
+ "oauth",
1360
+ "authentication",
1361
+ "api"
1362
+ ],
1363
+ "complexity": 2,
1364
+ "featured": true,
1365
+ "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',
1366
+ "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."
1367
+ },
1153
1368
  {
1154
1369
  "slug": "sfc-bake-cool-concurrent",
1155
1370
  "diagram": "sfc",
@@ -1329,6 +1544,46 @@ If the LED doesn't light up, three things to check, in order: LED polarity (the
1329
1544
  "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',
1330
1545
  "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."
1331
1546
  },
1547
+ {
1548
+ "slug": "state-ecommerce-order",
1549
+ "diagram": "state",
1550
+ "title": "E-commerce order lifecycle (state diagram)",
1551
+ "description": "Full order state machine \u2014 from Pending through payment routing (choice pseudo-state), composite Processing state with Picking/Packing/Shipped sub-states, delivery, refund, and cancellation paths. Demonstrates composite states, choice pseudo-states, guard conditions, entry actions, and UML notes.",
1552
+ "standard": "OMG UML 2.5.1 \xA714",
1553
+ "tags": [
1554
+ "state",
1555
+ "uml",
1556
+ "composite",
1557
+ "choice",
1558
+ "guard",
1559
+ "order-management",
1560
+ "e-commerce",
1561
+ "lifecycle"
1562
+ ],
1563
+ "complexity": 2,
1564
+ "featured": false,
1565
+ "dsl": 'state "E-Commerce Order Lifecycle"\n\ninitial i\ni -> Pending\n\nPending -> Confirmed : place_order [items_in_stock] / reserveInventory()\nPending -> Cancelled : cancel\n\nchoice PayRoute\nConfirmed -> PayRoute : pay\nPayRoute -> Processing : [method == "card"]\nPayRoute -> Processing : [method == "wallet"]\nPayRoute -> AwaitingTransfer : [method == "bank_transfer"]\n\nAwaitingTransfer -> Processing : transfer_received [amount_correct]\nAwaitingTransfer -> Cancelled : transfer_timeout\n\ncomposite Processing {\n initial pi\n final pf\n\n pi -> Picking\n Picking -> Packing : picked / updateWarehouse()\n Packing -> Shipped : label_printed\n Shipped -> pf : carrier_confirmed\n}\n\nProcessing -> Delivered : delivered / notifyCustomer()\nProcessing -> Failed : fulfillment_error\n\nDelivered -> Refunded : return_request [within_30_days] / initiateRefund()\nFailed -> Pending : retry [attempt < 3]\nFailed -> Cancelled : retry [attempt >= 3]\n\nfinal f\nDelivered -> f\nRefunded -> f\nCancelled -> f\n\nnote right_of AwaitingTransfer : SLA: 48 h before timeout.',
1566
+ "notes": 'Order state machines are one of the most common backend design artifacts, and one of the most commonly under-specified. Teams often start with a simple enum (`PENDING / PAID / SHIPPED / DELIVERED`) and then bolt on edge cases over time: partial shipments, payment holds, carrier errors, refund windows. Six months later the enum has twelve values, the transition logic is scattered across three services, and nobody can explain what sequence of events gets an order from `FAILED` to `CANCELLED` versus from `FAILED` back to `PENDING`. A UML state diagram catches all of this upfront.\n\n**Guard conditions on `place_order`.** The transition from `Pending` to `Confirmed` carries `[items_in_stock]` \u2014 a guard. Guards are boolean predicates that must be true for the transition to fire even when the trigger event (`place_order`) occurs. If the guard is false, the trigger is silently absorbed and the system stays in `Pending`. In practice this means inventory is checked synchronously during order placement, and the transition only proceeds if stock is available. The action `/ reserveInventory()` fires when the transition does go through, atomically.\n\n**Choice pseudo-state for payment routing.** `PayRoute` is a UML choice pseudo-state (drawn as a diamond). When the `pay` trigger fires from `Confirmed`, the machine immediately evaluates the guards on all outgoing transitions from `PayRoute`. If `method == "card"` or `method == "wallet"`, control goes directly to `Processing`. If `method == "bank_transfer"`, it goes to `AwaitingTransfer` \u2014 a waiting state with its own 48-hour SLA note. Choice pseudo-states do not dwell; they route. This is the correct model for "take different paths based on a value computed at runtime."\n\n**Composite state for fulfillment.** `Processing` is a composite state \u2014 it contains its own internal state machine with `Picking`, `Packing`, and `Shipped`. From the outside, the system is "in Processing" whether it\'s picking, packing, or confirming with the carrier. From the inside, the sub-machine tracks exactly where the warehouse is. The composite state has its own `initial` and `final` pseudo-states: the machine enters at `pi` (starts picking) and exits through `pf` (carrier confirmed), which fires the outer transition to `Delivered`. Cross-composite transitions to `Failed` are also valid \u2014 a fulfillment error at any sub-state terminates the Processing phase and moves to the outer `Failed` state.\n\n**Retry with bounded attempts.** `Failed` has two outgoing transitions back to `Pending` and `Cancelled`, both triggered by `retry` but guarded on `attempt`. This is the explicit model for "retry up to N times, then give up." Without the state diagram, this logic typically lives in a cron job or a dead-letter queue processor that nobody fully understands. Here it is the specification: anyone reading the diagram knows the retry policy without digging through queue configurations.\n\n**Final state convergence.** `Delivered`, `Refunded`, and `Cancelled` all transition to the same `final` pseudo-state. In implementation, "final" might mean the order record is archived, the event bus receives an `order.closed` event, and no further state transitions are accepted. The model is silent on what happens after final \u2014 that\'s intentional. The state machine is done; downstream processes (analytics, accounting, data retention) are separate concerns.'
1567
+ },
1568
+ {
1569
+ "slug": "state-traffic-light",
1570
+ "diagram": "state",
1571
+ "title": "Traffic light (state diagram)",
1572
+ "description": "A three-state finite state machine for a traffic signal \u2014 Red, Green, Yellow \u2014 with timer-driven transitions and a power_off exit to a final state. Introduces UML initial and final pseudo-states, transition labels, and the cyclic structure that makes state diagrams the right tool for reactive systems.",
1573
+ "standard": "OMG UML 2.5.1 \xA714",
1574
+ "tags": [
1575
+ "state",
1576
+ "uml",
1577
+ "fsm",
1578
+ "state-machine",
1579
+ "embedded",
1580
+ "reactive"
1581
+ ],
1582
+ "complexity": 1,
1583
+ "featured": true,
1584
+ "dsl": 'state "Traffic Light"\n\ninitial i\nfinal f\n\ni -> Red\nRed -> Green : timer\nGreen -> Yellow : timer\nYellow -> Red : timer\nRed -> f : power_off',
1585
+ "notes": "The traffic light is to state diagrams what the pump loop is to P&IDs: every textbook uses it, every engineer has drawn it, and if you understand it you understand the grammar of every more complex model.\n\n**Why a state diagram and not a flowchart.** A flowchart for a traffic light would show a loop: start \u2192 Red \u2192 (timer fires?) \u2192 Green \u2192 (timer fires?) \u2192 Yellow \u2192 back to Red. That works for describing an algorithm, but it misses the essential question: *what is the system doing right now?* A state machine makes the current state a first-class concept. The traffic light is not executing a loop \u2014 it *is* Red, or it *is* Green. Transitions are events that change what it is. This distinction matters the moment you add complexity: \"what happens if a pedestrian button is pressed while we're in Green?\" You answer that by looking at the transitions out of Green, not by tracing a flowchart path.\n\n**UML pseudo-states.** The filled black circle (`initial i`) and the bull's-eye circle (`final f`) are pseudo-states \u2014 they are not real states the system can dwell in, just notational entry and exit points. The initial pseudo-state shows where the machine starts; the arrow from `i` to Red tells you Red is the first real state. The final pseudo-state shows where the machine terminates. In the traffic light, termination is the `power_off` event from Red \u2014 the system shuts down from the Red state only (it wouldn't be safe to power off mid-Green).\n\n**Transition labels.** Each arrow is labeled with the trigger event that causes the transition. `timer` means \"the countdown for this phase has elapsed.\" In a real embedded implementation, this would be a hardware timer interrupt or a software watchdog expiry. Schematex doesn't execute the state machine \u2014 it renders the model. The labels are free text; you write exactly what your system calls the event.\n\n**Cyclic structure.** The three main states form a cycle: Red \u2192 Green \u2192 Yellow \u2192 Red. Most state diagrams describing continuous systems have cycles \u2014 the system runs until something external stops it. The `power_off` transition is the only way to reach the final state, and it is only modeled on Red because that is the safe state to stop in. If you wanted to model an emergency override (traffic officer stops the light mid-cycle), you would add `power_off` transitions from Green and Yellow too.\n\n**From model to code.** A UML state diagram maps directly to an enum + switch statement, a state table, or a state-machine framework (XState, Boost.MSM, Qt State Machine). The diagram is the specification; the implementation strategy is separate. Generating this diagram from a DSL means you can keep the spec in version control alongside the code, diff it in PRs, and regenerate it from an LLM prompt when requirements change."
1586
+ },
1332
1587
  {
1333
1588
  "slug": "timeline-company-milestones",
1334
1589
  "diagram": "timeline",
@@ -1385,6 +1640,43 @@ If the LED doesn't light up, three things to check, in order: LED polarity (the
1385
1640
  "dsl": 'timing "SPI Transaction"\nCLK: pppppppp\nCS_N: 10000001\nMOSI: x======= data: ["0xAB","0xCD","0xEF","0x01","0x02","0x03","0x04","0x05"]\nMISO: zzzz==== data: ["","","","","0xFF","0x12","0x34","0x56"]',
1386
1641
  "notes": "## Scenario\n\nA firmware engineer or hardware designer documents an 8-byte SPI master-to-slave transaction for a device driver review or datasheet. The WaveDrom-compatible syntax means the same DSL can be pasted directly into WaveDrom's online editor or embedded in documentation pipelines.\n\n## Annotation key\n\n- `p` \u2014 clock pulse (high period followed by low); each `p` is one clock cycle\n- `1` / `0` \u2014 logic high / logic low\n- `=` \u2014 data bus: stable data (value unchanged from previous cycle)\n- `x` \u2014 don't-care or undefined state (transition state)\n- `z` \u2014 high-impedance (floating / tri-state)\n- `data: [...]` \u2014 optional data labels for each stable segment, rendered inside the bus bar\n- `CS_N` \u2014 active-low chip select; `1` = deselected, `0` = selected\n\n## How to read\n\nThe clock runs for 8 cycles. CS_N goes low at cycle 1 and returns high at cycle 8, framing the transaction. MOSI (master out) sends 8 bytes starting at cycle 1. MISO (slave in) is high-Z for the first 4 cycles (slave preparing the response) then transitions to stable data bytes 5\u20138. The transaction completes when CS_N de-asserts."
1387
1642
  },
1643
+ {
1644
+ "slug": "usecase-atm",
1645
+ "diagram": "usecase",
1646
+ "title": "ATM use case diagram (UML)",
1647
+ "description": "The canonical software-engineering use case diagram \u2014 a Customer and an external Bank system around an ATM, with four withdraw/deposit/balance/transfer use cases. Exercises stick-figure actors, the external-system rectangle, subject sizing, and primary/supporting actor flanking.",
1648
+ "standard": "OMG UML 2.5.1 \xA718",
1649
+ "tags": [
1650
+ "usecase",
1651
+ "uml",
1652
+ "requirements",
1653
+ "actor",
1654
+ "subject"
1655
+ ],
1656
+ "complexity": 1,
1657
+ "featured": true,
1658
+ "dsl": 'usecase\ntitle: "ATM"\nsystem: "ATM System"\n\nactor: Customer\nactor: Bank (external)\n\nusecase: "Withdraw Cash" as Withdraw\nusecase: "Deposit Funds" as Deposit\nusecase: "Check Balance" as Check\nusecase: "Transfer Funds" as Transfer\n\nCustomer -- Withdraw\nCustomer -- Deposit\nCustomer -- Check\nCustomer -- Transfer\n\nWithdraw -- Bank\nDeposit -- Bank\nCheck -- Bank\nTransfer -- Bank',
1659
+ "notes": 'The ATM is the hello-world of UML use case diagrams \u2014 it appears in nearly every software-engineering textbook because it shows the whole notation with no clutter. The diagram answers "what can you do at an ATM, and who does the work behind the scenes?"\n\n**Two actors, two roles.** `Customer` is the **primary actor** \u2014 the human who initiates the interaction \u2014 so Schematex places the stick figure on the **left**. `Bank` is declared `(external)`, marking it as another software system rather than a person; it renders as a rectangle carrying the `\xABactor\xBB` stereotype and is placed on the **right** as a supporting actor. This left-primary / right-supporting convention comes from the Bittner & Spence style guide and matches what reviewers expect to see.\n\n**Four use cases, one subject.** Each `usecase:` becomes an ellipse, and the `system: "ATM System"` header wraps them in a rounded **subject** rectangle with the system name at the top. The subject is sized *after* the use cases are placed, so it always hugs its contents.\n\n**Plain associations.** Every `--` line is an undirected association \u2014 a solid line from the actor to the use-case ellipse perimeter. The Customer triggers all four operations; each operation in turn talks to the Bank to settle the transaction. There are no `\xABinclude\xBB` or `\xABextend\xBB` relationships here \u2014 that\'s deliberate. The ATM diagram\'s job is to establish *scope*, and at this level of detail plain associations are exactly right. Reach for include/extend only when use cases genuinely compose.'
1660
+ },
1661
+ {
1662
+ "slug": "usecase-online-bookstore",
1663
+ "diagram": "usecase",
1664
+ "title": "Online bookstore use case diagram (include + extend)",
1665
+ "description": "An e-commerce checkout use case diagram showing \xABinclude\xBB chains, an \xABextend\xBB relationship with a condition and extension point, and the extension-point compartment inside the base ellipse. A primary customer, an external payment gateway, and a supporting warehouse actor.",
1666
+ "standard": "OMG UML 2.5.1 \xA718",
1667
+ "tags": [
1668
+ "usecase",
1669
+ "uml",
1670
+ "include",
1671
+ "extend",
1672
+ "extension-point",
1673
+ "requirements"
1674
+ ],
1675
+ "complexity": 2,
1676
+ "featured": true,
1677
+ "dsl": 'usecase\ntitle: "Online Bookstore \u2014 Checkout"\nsystem: "Bookstore System"\n\nactor: Customer\nactor: "Payment Gateway" as PG (external)\nactor: "Warehouse Staff" as WH\n\nusecase: "Browse Catalog" as Browse\nusecase: "Add to Cart" as AddCart\nusecase: "Checkout" as Checkout {\n extension point: payment failed\n extension point: stock depleted\n}\nusecase: "Pay" as Pay\nusecase: "Validate Card" as ValidateCard\nusecase: "Cancel Order" as Cancel\nusecase: "Ship Order" as Ship\n\nCustomer -- Browse\nCustomer -- AddCart\nCustomer -- Checkout\nCheckout ..> Pay : \xABinclude\xBB\nPay ..> ValidateCard : \xABinclude\xBB\nPay -- PG\nCancel <.. Checkout : \xABextend\xBB [payment failed] (extension point: payment failed)\nShip -- WH\nCheckout ..> Ship : \xABinclude\xBB',
1678
+ "notes": "This is the diagram a business analyst hands to engineering when an online store is being scoped. It uses every relationship in the use case vocabulary, and each one means something precise.\n\n**Three actors on two sides.** `Customer` is the primary actor (left). `Payment Gateway` is `(external)` \u2014 a third-party system, drawn as an `\xABactor\xBB` rectangle on the right. `Warehouse Staff` is a supporting human actor, also on the right. Schematex flanks the subject automatically.\n\n**`\xABinclude\xBB` \u2014 mandatory composition.** `Checkout ..> Pay` reads \"Checkout *includes* Pay\": every checkout always runs the payment sub-flow, so the arrow points from the base toward the included use case. The chain continues \u2014 `Pay ..> ValidateCard` \u2014 so payment always validates the card. Include is the right tool when a step *always* happens and you want to factor it out for reuse. The dashed line, open arrowhead, and `\xABinclude\xBB` pill are all UML-standard.\n\n**`\xABextend\xBB` \u2014 optional, conditional behavior.** `Cancel <.. Checkout` reads \"Cancel Order *extends* Checkout\": cancellation is *optional* behavior that only kicks in under a condition. The `[payment failed]` clause is that condition, and `(extension point: payment failed)` ties it to the named extension point declared inside the Checkout ellipse. Note the arrow still points at the base (Checkout) even though Cancel is written first \u2014 that's the UML rule, and Schematex normalises it for you. Extend lines are drawn in the accent color because they're the rarer, more surprising relationship.\n\n**Extension points live in the ellipse.** Because `Checkout` declares `extension point: payment failed` and `stock depleted`, those appear in a compartment below the name, separated by a divider. They're the hooks an `\xABextend\xBB` relationship can attach to.\n\nThe result reads top-to-bottom as a flow: browse \u2192 add to cart \u2192 checkout, with checkout fanning out into pay (\u2192 validate card, \u2192 payment gateway), ship (\u2192 warehouse), and an optional cancel path. That's exactly the scope picture a stakeholder review needs."
1679
+ },
1388
1680
  {
1389
1681
  "slug": "venn-customer-segments",
1390
1682
  "diagram": "venn",
@@ -1530,6 +1822,22 @@ var SYNTAX = {
1530
1822
  "sfc": {
1531
1823
  "title": "Sequential Function Chart (SFC)",
1532
1824
  "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.'
1825
+ },
1826
+ "usecase": {
1827
+ "title": "UML Use Case Diagram",
1828
+ "content": '## 1. Your first diagram\n\nEvery document starts with the `usecase` keyword, then a header, then declarations and relationships:\n\n```\nusecase\nsystem: "Library"\n\nactor: Member\nusecase: "Borrow Book" as Borrow\n\nMember -- Borrow\n```\n\n`actor:` declares an actor (a stick figure), `usecase:` declares a use case (an ellipse), and `Member -- Borrow` draws a plain association between them. `system:` wraps the use cases in a labelled **subject** rectangle. The primary actor is placed on the left; supporting actors on the right.\n\nThe header accepts:\n\n- `title: "\u2026"` \u2014 a heading drawn above the diagram.\n- `system: "\u2026"` \u2014 the subject (system boundary) name. Omit it to let the use cases float free (Schematex warns if you omit it with \u22653 use cases).\n- `direction: LR | TB` \u2014 default `LR` (actors flank the subject horizontally).\n- `generalization: tree | individual` \u2014 whether to merge sibling generalization arrows (default `tree`).\n\n---\n\n## 2. Actors\n\n```\nactor: Customer\nactor: "Payment Gateway" as PG (external)\nactor: "Warehouse Staff" as WH\nactor: Admin (left)\n```\n\n- A bare or `"quoted"` name becomes the label; `as ID` gives it a short identifier for relationships.\n- `(external)` (or `(system)`) renders the actor as a **rectangle with an `\xABactor\xBB` stereotype** instead of a stick figure \u2014 use it for other software systems (payment gateways, third-party APIs).\n- `(business)` adds the Bittner & Spence diagonal slash across the stick figure.\n- `(left)` / `(right)` pins the actor to a side. By default the first actor goes left and the rest go right.\n\nCustom stereotypes go in guillemets after the declaration: `actor: "Audit Service" as Audit (external) \xABsystem\xBB`. The parser also accepts ASCII `<<system>>` and normalises it to `\xABsystem\xBB`.\n\n---\n\n## 3. Use cases\n\n```\nusecase: "Checkout" as Checkout {\n extension point: payment failed\n extension point: stock depleted\n}\n```\n\nA use case is an ellipse sized to fit its text. The optional `{ \u2026 }` block lists **extension points** in a compartment below the name, separated by a divider line. Stereotypes work here too: `usecase: "Validate Card" as ValidateCard \xABsecured\xBB`.\n\n---\n\n## 4. Relationships\n\nFour line types connect actors and use cases:\n\n| DSL | Meaning | Rendering |\n|-----|---------|-----------|\n| `A -- B` | association | solid line, no arrow |\n| `A --> B` | directed association | solid line, open arrow at B |\n| `A ..> B` | `A` **includes** `B` | dashed line, open arrow \u2192 B, `\xABinclude\xBB` pill |\n| `A <.. B` | `A` **extends** `B` | dashed line, open arrow \u2192 B (the base), `\xABextend\xBB` pill |\n| `A --\\|> B` | `A` is a specialisation of `B` | solid line, hollow triangle \u2192 parent B |\n\n```\nCustomer -- Checkout\nCheckout ..> Pay : \xABinclude\xBB\nPay ..> ValidateCard : \xABinclude\xBB\nCancel <.. Checkout : \xABextend\xBB [payment failed] (extension point: payment failed)\n```\n\n- **`\xABinclude\xBB`** points *toward the included use case* \u2014 the reusable behavior `A` always runs. Source includes target.\n- **`\xABextend\xBB`** points *toward the base* \u2014 `A` is the optional behavior, `B` is the base it extends. You can attach a `[condition]` and reference one of the base\'s `(extension point: \u2026)` entries. (The arrowhead is always drawn toward the base regardless of how you order the endpoints.)\n- An `\xABextend\xBB` line is drawn in the theme **accent color** so the rarer, more surprising relationship stands out.\n\nThe parser rejects the high-confidence mistakes humans and LLMs both make, with the offending line number: association between two actors or two use cases, `include`/`extend` touching an actor, generalization across metaclasses (actor \u2192 use case), reused identifiers, and extension-point references that don\'t exist on the base.\n\n---\n\n## 5. Generalization\n\n`--|>` works between two actors **or** between two use cases (never across the two \u2014 that\'s a hard error):\n\n```\nactor: User as U\nactor: "Premium User" as PU\nPU --|> U\n\nusecase: "Pay by Card" as PayCard\nusecase: "Pay by PayPal" as PayPaypal\nusecase: "Pay" as Pay\nPayCard --|> Pay\nPayPaypal --|> Pay\n```\n\nThe arrow carries a **hollow triangle** pointing at the parent. When three or more siblings share one parent, the arrows merge into a single shared head (UML 2.5 Figure 18.5 convention); set `generalization: individual` in the header to keep them separate. Actor hierarchies route as a clean bus on the outer edge of the actor stack.\n\n---\n\n## 6. Multiplicity\n\nQuote a multiplicity string immediately beside the endpoint it belongs to:\n\n```\nCustomer "1" -- "*" Checkout\nCashier "1..*" -- "1" Register\n```\n\n---\n\n## 7. PlantUML-style inline form\n\nComing from PlantUML? The inline declaration form works and mixes freely with the declarative form:\n\n```\nusecase\n:Customer: as C\n(Browse Catalog) as Browse\n(Add to Cart) as AddCart\n\nC -- Browse\nC -- AddCart\nBrowse ..> AddCart : \xABinclude\xBB\n```\n\n`:Name:` declares an actor, `(Name)` declares a use case, and `as ID` aliases either. Schematex is *inspired by* PlantUML, not a 1:1 transpiler \u2014 multiplicity and relationship syntax differ slightly.\n\n---\n\n## 8. Grammar (EBNF)\n\n```text\ndocument = "usecase" NEWLINE header_prop* statement*\nheader_prop = ("title:" | "system:") quoted_string\n | "direction:" ("LR" | "TB")\n | "generalization:" ("tree" | "individual")\n\nstatement = actor_decl | usecase_decl | plantuml_inline | relation | note\n\nactor_decl = "actor" ":" name ("as" IDENT)? actor_kind? stereotype?\nactor_kind = "(" ("external" | "system" | "business" | "left" | "right") ")"\nusecase_decl = "usecase" ":" quoted ("as" IDENT)? stereotype? extpoints?\nextpoints = "{" ("extension point:" TEXT)+ "}"\nplantuml_inline = ":" name ":" ("as" IDENT)? ; actor\n | "(" name ")" ("as" IDENT)? ; use case\n\nrelation = endpoint relop endpoint label_clause?\nendpoint = (IDENT | quoted) multiplicity? | multiplicity? (IDENT | quoted)\nrelop = "--" | "-->" | "..>" | "<.." | "--|>"\nlabel_clause = ":" stereotype? condition? extpoint_ref?\nstereotype = "\xAB" TEXT "\xBB" | "<<" TEXT ">>"\ncondition = "[" TEXT "]"\nextpoint_ref = "(extension point:" TEXT ")"\nmultiplicity = quoted_string ; "1", "*", "0..1", "1..*"\n```\n\n---'
1829
+ },
1830
+ "sequence": {
1831
+ "title": "UML Sequence Diagram",
1832
+ "content": '## 1. Your first diagram\n\nEvery document starts with the `sequence` keyword and an optional `"title"`. Participants don\'t need to be declared \u2014 the first time you mention one in a message, it becomes a lifeline:\n\n```\nsequence\n Alice -> Bob : Authentication Request\n Bob --> Alice : Authentication Response\n```\n\nLifelines appear left-to-right in first-use order. `->` is a synchronous call (solid line, filled arrowhead); `-->` is a reply (dashed line, open arrowhead). Text after `:` is the message label.\n\n---\n\n## 2. Participants\n\nDeclare a participant explicitly to set its **kind**, give it an **alias**, or fix its **order**:\n\n```\nsequence\n actor User\n participant Web as "Web App"\n boundary LoginUI\n control Auth\n entity Account\n database DB\n collections Sessions\n queue Events\n```\n\n| Kind | Rendered as |\n|------|-------------|\n| `participant` (default) | rounded classifier box |\n| `actor` | stick figure |\n| `boundary` / `control` / `entity` | the Jacobson robustness icons (\u22A2\u25EF / \u25EF with arrow / \u25EF with underline) |\n| `database` | cylinder |\n| `collections` / `queue` | box with the kind as a `\xABstereotype\xBB` |\n\n- `as "Label"` sets the display name (quotes optional; `\u300C\u2026\u300D` CJK quotes accepted).\n- A custom **stereotype** goes in guillemets or ASCII angle brackets after the declaration \u2014 it overrides the default label: `actor Printer \xABsystem\xBB`, `participant Bus as "Event Bus" <<service>>`.\n\n---\n\n## 3. Messages\n\nThe arrow token chooses the UML semantics:\n\n| DSL | Meaning | Rendering |\n|-----|---------|-----------|\n| `A -> B` | synchronous call | solid line, **filled** arrowhead |\n| `A ->> B` | asynchronous signal | solid line, **open** arrowhead |\n| `A --> B` | reply / return | **dashed** line, open arrowhead |\n| `A -x B` | lost message | line ending at a filled circle |\n| `o-> B` | found message | line starting from a filled circle |\n| `A -> A` | self message | bent loop back to the same lifeline |\n\nWhitespace around arrows is optional (`A->B` works), and labels are free text \u2014 including a return value, e.g. `aHotel -> aHotel : available(roomId, date): isRoom`.\n\n---\n\n## 4. Activations (execution specifications)\n\nThe thin bar on a lifeline shows it is active. Open and close it with explicit statements, or with `+` / `-` suffixes on the messages themselves:\n\n```\nsequence\n participant Client\n participant Server\n Client ->+ Server : request()\n Server ->> Server : validate()\n Server -->- Client : response\n```\n\n`+` after the arrow activates the **receiver** on arrival; `-` deactivates the **sender** after the message is sent. The explicit form is `activate X` / `deactivate X`. Overlapping bars on one lifeline nest with a horizontal offset.\n\n---\n\n## 5. Object creation & destruction\n\n```\nsequence\n participant Factory\n Factory -> *Worker : \xABcreate\xBB\n Factory -> Worker : work()\n destroy Worker\n```\n\nPrefix the receiver with `*` to make the message **instantiate** it \u2014 the new lifeline\'s head is drawn at the arrival row, not at the top. `destroy X` ends a lifeline with a \u2715 and stops its time axis.\n\n---\n\n## 6. Combined fragments\n\nA combined fragment is a labelled frame around a region. Schematex implements the full UML `InteractionOperatorKind` set:\n\n| Operator | Meaning | Operands |\n|----------|---------|----------|\n| `alt` | alternatives (if/else-if/else) | `else`, each guarded |\n| `opt` | runs iff the guard holds | one, guarded |\n| `loop` | repeat | one; guard may be `(min,max)` |\n| `par` | concurrent operands | `and` |\n| `break` | exceptional exit | one, guarded |\n| `critical` | atomic / no interleaving | one |\n| `seq` / `strict` | weak / strict sequencing | `and` |\n| `neg` | invalid traces (rendered tinted) | one |\n| `ignore` / `consider` | message-set filter `{m1, m2}` | one |\n| `assert` | the only valid continuation | one |\n\n```\nsequence\n actor User\n participant API\n User -> API : GET /resource\n alt [authorized]\n API --> User : 200 + body\n else [forbidden]\n API --> User : 403\n end\n```\n\nGuards go in `[brackets]` after the operator (and after `else`). Fragments nest, and inner frames inset automatically so they sit cleanly inside their parent.\n\n---\n\n## 7. Interaction use (`ref`)\n\nReference another interaction instead of inlining it \u2014 the way UML keeps large diagrams composable:\n\n```\nsequence\n participant A\n participant B\n ref over A, B : Establish session\n A -> B : poll()\n```\n\n`ref over <lifelines> : Name` draws a framed box across the named lifelines. Most tools omit this; it\'s how real systems decompose long flows.\n\n---\n\n## 8. Notes, dividers, invariants, numbering\n\n```\nsequence\n autonumber 1 1\n actor Shopper\n participant Cart\n == Phase 1: review ==\n Shopper -> Cart : view items\n note over Cart : cart persisted in Redis\n state Cart : ready\n```\n\n- `note over A` / `note over A, B` / `note left of A` / `note right of A` \u2014 folded-corner annotations.\n- `== text ==` \u2014 a full-width section divider.\n- `state X : text` \u2014 a state-invariant capsule on a lifeline.\n- `autonumber [start] [step]` \u2014 prefix every message with an incrementing number.\n\n---\n\n## 9. Grammar (EBNF)\n\n```text\ndiagram = "sequence" [ string ] NEWLINE statement*\nstatement = participant | message | activation | note\n | fragment | ref | divider | invariant | destroy | autonumber\n\nparticipant = kind IDENT ("as" label)? stereotype?\nkind = "participant" | "actor" | "boundary" | "control"\n | "entity" | "database" | "collections" | "queue"\nstereotype = "\xAB" TEXT "\xBB" | "<<" TEXT ">>"\n\nmessage = IDENT? act? arrow act? ("*")? IDENT? (":" TEXT)?\narrow = "->" | "->>" | "-->" | "-x" | "o->"\nact = "+" | "-"\nactivation = ("activate" | "deactivate") IDENT\n\nfragment = ("alt"|"opt"|"loop"|"par"|"break"|"critical"|"seq"\n |"strict"|"neg"|"ignore"|"consider"|"assert") guard? NEWLINE\n statement* (("else"|"and") guard? NEWLINE statement*)* "end"\nguard = "[" TEXT "]" | "(" NUMBER ("," NUMBER)? ")"\nref = "ref" "over" IDENT ("," IDENT)* ":" TEXT\nnote = "note" ("over"|"left of"|"right of") IDENT ("," IDENT)? ":" TEXT\ndivider = "==" TEXT "=="\ninvariant = "state" IDENT ":" TEXT\ndestroy = "destroy" IDENT\nautonumber = "autonumber" NUMBER? NUMBER?\n```\n\n---'
1833
+ },
1834
+ "prisma": {
1835
+ "title": "PRISMA 2020 flow diagram",
1836
+ "content": '## 1. Your first diagram\n\nThe minimum is the four stage blocks. Counts are mandatory; the parser refuses to lay out a diagram with a missing total.\n\n```\nprisma\n\nidentification:\n databases:\n n: 1000\n\nscreening:\n records-screened: 900\n excluded:\n n: 600\n\neligibility:\n full-text-assessed: 300\n excluded:\n n: 250\n\nincluded:\n studies: 50\n```\n\nIndentation is significant \u2014 **two spaces per level**, like genogram and SLD. The first non-blank line must be `prisma`. Comments use `#` or `//`.\n\n---\n\n## 2. Meta lines\n\nTop-level `key: value` lines, written before the stage blocks:\n\n```\nprisma\nmode: 2020-single\nkind: systematic-review\ntitle: My review\nvalidate-counts: warn\n```\n\n| Key | Values | Default | Meaning |\n|---|---|---|---|\n| `mode` | `2020-single` \xB7 `2020-dual` \xB7 `2009` | `2020-single` | Single column, or dual ("other methods") column. |\n| `kind` | `systematic-review` \xB7 `scoping-review` \xB7 `ipd` \xB7 `nma` | `systematic-review` | Swaps stage vocabulary (see \xA76). |\n| `title` | string | \u2014 | Rendered above the diagram. |\n| `validate-counts` | `warn` \xB7 `strict` \xB7 `off` | `warn` | Arithmetic checking (see \xA77). |\n| `direction` | `TB` / `TD` | `TB` | PRISMA is vertical by standard; horizontal is rejected. |\n\n---\n\n## 3. Identification\n\nThe `identification:` block holds a `databases:` sub-block (always) and an optional `other:` sub-block (dual mode).\n\n```\nidentification:\n databases:\n n: 1418\n sources: PubMed=600, Embase=450, Cochrane=184\n duplicates-removed: 318\n ineligible-automation: 0\n other-removed: 0\n```\n\n- `n:` \u2014 total records identified (**mandatory**).\n- `sources:` \u2014 `name=count` pairs, comma-separated. Rendered as an indented breakdown. Names with spaces or punctuation can be quoted: `"Web of Science"=184`.\n- `duplicates-removed:`, `ineligible-automation:`, `other-removed:` \u2014 optional removal counts. When any are present they render as a separate **"Records removed before screening"** box in the right column, connected by a horizontal arrow.\n\nLarge numbers may use commas: `n: 1,418` is the same as `n: 1418`.\n\n---\n\n## 4. Screening & Eligibility\n\nBoth stages carry a main count plus an `excluded:` block. The excluded block has its own `n:` and an optional `reasons:` breakdown.\n\n```\nscreening:\n records-screened: 1100\n excluded:\n n: 870\n reasons: irrelevant title=750, non-English=120\n reports-sought: 226 # optional\n reports-not-retrieved: 12 # optional\n\neligibility:\n full-text-assessed: 230\n excluded:\n n: 195\n reasons: wrong population=80, wrong intervention=60, wrong outcome=55\n```\n\n`reasons:` are `name=count` pairs. If you list more than 8, the renderer sorts them descending and aggregates the tail as `Other (n = \u2026)` so the side-box stays readable.\n\n---\n\n## 5. Included\n\n```\nincluded:\n studies: 35\n reports: 38 # one study may yield several reports\n participants: 28741 # PRISMA-IPD only\n```\n\n`studies:` is mandatory. `reports:` and `participants:` are optional extra count lines.\n\n---\n\n## 6. Dual pipeline & review kinds\n\n**Dual pipeline** \u2014 the PRISMA 2020 update added a second "Identification via other methods" column (citation searching, hand searches, expert recommendations). Add an `other:` block; the two columns merge into Screening via a Y-junction.\n\n```\nprisma\nmode: 2020-dual\n\nidentification:\n databases:\n n: 1234\n duplicates-removed: 254\n other:\n n: 56\n sources: citation-search=30, hand-search=20, expert-recommendation=6\n\nscreening:\n records-screened: 1036\n excluded:\n n: 810\n\neligibility:\n full-text-assessed: 226\n excluded:\n n: 195\n\nincluded:\n studies: 31\n```\n\n**Scoping review** \u2014 `kind: scoping-review` swaps "studies" \u2192 "sources of evidence" and re-labels the stages per Tricco et al. 2018, without changing geometry.\n\n**Updated review** \u2014 an optional `previous-studies:` block draws a dashed box on top that feeds into the identification section:\n\n```\nprevious-studies:\n n: 19\n sources: previous review=19\n```\n\n---\n\n## 7. Count arithmetic validation\n\nWith `validate-counts: warn` (default) the engine checks that the counts reconcile across stages \u2014 e.g. `databases.n + other.n \u2212 duplicates-removed = records-screened`, and that source/reason breakdowns sum to their totals. Mismatches render a small warning under the diagram (also surfaced in the SVG `<desc>` for screen readers).\n\n`validate-counts: strict` turns a mismatch into a parse error with an "off by N" message. `off` skips checking entirely.\n\n---\n\n## 8. Grammar (EBNF)\n\n```\nprisma-document = "prisma", { meta-line }, stage-block, { stage-block } ;\nmeta-line = ("mode:" | "kind:" | "title:" | "review-id:" | "validate-counts:" | "direction:") value ;\n\nstage-block = previous-block | identification-block | screening-block | eligibility-block | included-block ;\n\nprevious-block = "previous-studies:" , indent, "n:" int, [ "reports:" int ], { "sources:" pairs } ;\nidentification-block = "identification:" , indent,\n "databases:" , indent, "n:" int, { "sources:" pairs },\n [ "duplicates-removed:" int ], [ "ineligible-automation:" int ], [ "other-removed:" int ],\n [ "other:" , indent, "n:" int, { "sources:" pairs } ] ;\nscreening-block = "screening:" , indent, "records-screened:" int,\n "excluded:" , indent, "n:" int, { "reasons:" pairs },\n [ "reports-sought:" int ], [ "reports-not-retrieved:" int ] ;\neligibility-block = "eligibility:" , indent, "full-text-assessed:" int,\n "excluded:" , indent, "n:" int, { "reasons:" pairs } ;\nincluded-block = "included:" , indent, "studies:" int, [ "reports:" int ], [ "participants:" int ] ;\n\npairs = pair, { "," pair } ;\npair = (string | quoted) "=" int ;\nint = digit, { digit | "," } ; (* commas stripped: 1,234 == 1234 *)\n```\n\nIndentation is two spaces per level. Unknown keys inside a stage block are a parse error, keeping each stage well-defined.\n\n---'
1837
+ },
1838
+ "pert": {
1839
+ "title": "PERT / CPM Network",
1840
+ "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---'
1533
1841
  }
1534
1842
  };
1535
1843
 
@@ -1560,11 +1868,345 @@ function getExamplesForType(type, opts = {}) {
1560
1868
  return sorted.slice(0, limit);
1561
1869
  }
1562
1870
 
1871
+ // src/ai/profiles.ts
1872
+ var COMMON_GENERATION_RULES = [
1873
+ "Generate one diagram document with one selected diagram type.",
1874
+ "Use the canonical header and canonical forms from the generation profile first.",
1875
+ 'Use ASCII double quotes (") for generated labels and titles.',
1876
+ "Do not emit DSL comments unless the user explicitly asks for annotated source.",
1877
+ "Prefer explicit IDs and declarations when they make validation less ambiguous.",
1878
+ "Call validateDsl with the explicit selected type, fix reported errors, and validate again before returning DSL."
1879
+ ];
1880
+ var PROFILES = {
1881
+ genogram: {
1882
+ type: "genogram",
1883
+ header: 'genogram "Title"',
1884
+ mode: "family declarations + indented children",
1885
+ forms: [
1886
+ "personId [sex, birthYear, optionalAttrs]",
1887
+ 'parentA -- parentB "optional relationship label"',
1888
+ "indent children under the couple line"
1889
+ ],
1890
+ prefer: ["Declare people before emotional relationships.", 'Use `[label: "..."]` only where the syntax reference shows a label attribute.'],
1891
+ avoid: ["Avoid inline comments and speculative relationship operators."],
1892
+ repair: ["Unknown individuals usually need a declaration before the relationship line."]
1893
+ },
1894
+ ecomap: {
1895
+ type: "ecomap",
1896
+ header: 'ecomap "Title"',
1897
+ mode: "center + external systems",
1898
+ forms: [
1899
+ 'center: client [label: "Client"]',
1900
+ 'systemId [label: "System", category: family]',
1901
+ 'systemId === client [label: "support"]'
1902
+ ],
1903
+ prefer: ["Declare exactly one center.", "Declare outside systems before their connection lines."],
1904
+ avoid: ["Avoid borrowing genogram operators such as `--`."],
1905
+ repair: ["A valid render with a missing center is semantically wrong; add `center:` first."]
1906
+ },
1907
+ pedigree: {
1908
+ type: "pedigree",
1909
+ header: 'pedigree "Title"',
1910
+ mode: "clinical pedigree",
1911
+ forms: [
1912
+ "personId [sex, generationAttrs]",
1913
+ "parentA -- parentB",
1914
+ "indent offspring under the couple line"
1915
+ ],
1916
+ prefer: ["Use pedigree status/trait attributes from the syntax reference.", "Keep generation structure explicit."],
1917
+ avoid: ["Avoid genogram emotional-relationship lines in pedigree output."],
1918
+ repair: ["Declare every referenced individual before relationships."]
1919
+ },
1920
+ phylo: {
1921
+ type: "phylo",
1922
+ header: 'phylo "Title"',
1923
+ mode: "quoted Newick",
1924
+ forms: ['newick: "((A:0.1,B:0.2),C:0.3);"', 'clade Group = (A, B) [label: "Group"]'],
1925
+ prefer: ["Use Newick for first-shot generation.", "Quote the Newick string."],
1926
+ avoid: ["Avoid indent-tree mode unless the user wants a hand-authored tree."],
1927
+ repair: ["A phylo document needs exactly one tree definition: Newick or indent tree."]
1928
+ },
1929
+ sociogram: {
1930
+ type: "sociogram",
1931
+ header: 'sociogram "Title"',
1932
+ mode: "declared nodes + social ties",
1933
+ forms: ['nodeId [label: "Person"]', 'nodeA -> nodeB [label: "choice"]', "config: layout = circular"],
1934
+ prefer: ["Declare actors first.", "Use one supported layout/config value at a time."],
1935
+ avoid: ["Avoid unknown config values; some adapters keep defaults."],
1936
+ repair: ["Unknown edge endpoints need matching node declarations."]
1937
+ },
1938
+ timing: {
1939
+ type: "timing",
1940
+ header: 'timing "Title"',
1941
+ mode: "WaveDrom-compatible signals",
1942
+ forms: ["CLK: pppppppp", 'DATA: x======x data: ["A", "B"]'],
1943
+ prefer: ["Keep wave strings contiguous with no internal spaces.", "Use `data:` labels for bus segments."],
1944
+ avoid: ["Avoid unsupported WaveDrom annotation syntax."],
1945
+ repair: ["Invalid wave strings usually contain a character outside the timing wave table."]
1946
+ },
1947
+ logic: {
1948
+ type: "logic",
1949
+ header: 'logic "Title"',
1950
+ mode: "logic netlist",
1951
+ forms: ["INPUT A, B", "G1 = AND(A, B)", "OUTPUT Y = G1"],
1952
+ prefer: ["Use canonical gate names and explicit inputs/outputs."],
1953
+ avoid: ["Avoid circuit component names inside logic diagrams."],
1954
+ repair: ["Unknown gate kinds should be replaced with the closest listed logic primitive."]
1955
+ },
1956
+ circuit: {
1957
+ type: "circuit",
1958
+ header: 'circuit "Title" netlist',
1959
+ mode: "SPICE-style netlist",
1960
+ forms: ["V1 vcc 0 5V", "R1 vcc out 10k", "C1 out 0 100n"],
1961
+ prefer: ["Use netlist mode for generated schematics unless geometry is the task.", "Add explicit `type=` when an ID prefix is ambiguous."],
1962
+ avoid: ["Avoid positional cursor routing (`wire`, `at:`) for first-shot output."],
1963
+ repair: ["If a component type or pin count is ambiguous, make the symbol type explicit."]
1964
+ },
1965
+ blockdiagram: {
1966
+ type: "blockdiagram",
1967
+ header: 'blockdiagram "Title"',
1968
+ mode: "blocks, sums, signals",
1969
+ forms: ['ctrl = block("PID") [role: controller]', "err = sum(+r, -y)", "err -> ctrl -> plant"],
1970
+ prefer: ["Use named blocks and directed `->` chains."],
1971
+ avoid: ["Avoid unlabeled feedback intent; model the summing junction."],
1972
+ repair: ["Connections must point at declared blocks, sums, signals, or boundary IDs."]
1973
+ },
1974
+ ladder: {
1975
+ type: "ladder",
1976
+ header: 'ladder "Title"',
1977
+ mode: "rungs + IEC/Rockwell elements",
1978
+ forms: ['rung 1 "Run motor":', " XIC(START_PB)", " OTE(MOTOR_RUN)"],
1979
+ prefer: ["Use uppercase element names.", "Use `parallel:` + `branch:` only for OR branches."],
1980
+ avoid: ["Avoid variable declarations and ST syntax."],
1981
+ repair: ["Element typos are repairable from parser suggestions; keep the tag in parentheses."]
1982
+ },
1983
+ sld: {
1984
+ type: "sld",
1985
+ header: 'sld "Title"',
1986
+ mode: "equipment assignments + power-flow edges",
1987
+ forms: ['util = utility [label: "Grid"]', 'xfmr = transformer [rating: "500 kVA"]', "util -> xfmr -> load is not allowed; use one edge per line"],
1988
+ prefer: ["Declare equipment as `id = nodeType [attrs]`.", "Use one `from -> to` connection per line."],
1989
+ avoid: ["Avoid generic flowchart node syntax."],
1990
+ repair: ["Unknown equipment types often have a suggested canonical type or alias."]
1991
+ },
1992
+ entity: {
1993
+ type: "entity",
1994
+ header: 'entity-structure "Title"',
1995
+ mode: "legal entities + ownership edges",
1996
+ forms: ['entity holdco "HoldCo" corp@US', 'entity opco "OpCo" llc@DE', "holdco -> opco : 100%"],
1997
+ prefer: ["Use `entity` declarations before ownership edges.", "Keep legal form and jurisdiction explicit when known."],
1998
+ avoid: ["Avoid database schema terminology; use `erd` for tables and FKs."],
1999
+ repair: ["Unknown ownership endpoints need entity declarations."]
2000
+ },
2001
+ fishbone: {
2002
+ type: "fishbone",
2003
+ header: 'fishbone "Title"',
2004
+ mode: "effect + cause categories",
2005
+ forms: ['effect "Late Delivery"', 'category process "Process"', 'process: "Handoff delay"'],
2006
+ prefer: ["Use one effect and structured categories for generated DSL."],
2007
+ avoid: ["Avoid mixing compact alien syntax when a structured category form works."],
2008
+ repair: ["If a cause lands nowhere, add or reference its category explicitly."]
2009
+ },
2010
+ venn: {
2011
+ type: "venn",
2012
+ header: 'venn "Title"',
2013
+ mode: "declared-set region counts",
2014
+ forms: ['set A "Group A"', 'set B "Group B"', "A & B : 120", "A only : 80"],
2015
+ prefer: ["Use declared sets plus region counts for first-shot Venn output.", "Use Euler relations only when subset/disjoint structure is the request."],
2016
+ avoid: ["Avoid mixing enumeration mode with explicit region counts."],
2017
+ repair: ["Declare every set before region or Euler relation lines."]
2018
+ },
2019
+ flowchart: {
2020
+ type: "flowchart",
2021
+ header: 'flowchart TD "Title"',
2022
+ mode: "Mermaid-compatible nodes + edges",
2023
+ forms: ["start([Start]) --> check{Decision?}", "check -->|Yes| done([Done])"],
2024
+ prefer: ["Choose one direction in the header.", "Declare shapes explicitly when shape matters."],
2025
+ avoid: ["Avoid subgraph complexity unless grouping is part of the request."],
2026
+ repair: ["A bad header direction fails early; use TD, TB, BT, LR, or RL."]
2027
+ },
2028
+ mindmap: {
2029
+ type: "mindmap",
2030
+ header: "mindmap",
2031
+ mode: "Markdown headings + bullets",
2032
+ forms: ["# Root", "## Branch", "- Child item"],
2033
+ prefer: ["Use exactly one `#` root heading.", "Use bullets/headings instead of graph edges."],
2034
+ avoid: ["Avoid comments in the body."],
2035
+ repair: ["An orphan branch means the root `#` heading is missing."]
2036
+ },
2037
+ matrix: {
2038
+ type: "matrix",
2039
+ header: 'matrix "Title"',
2040
+ mode: "quadrant scatter",
2041
+ forms: ["x-axis: Low -> High", "y-axis: Low -> High", '"Item" at (0.25, 0.8)'],
2042
+ prefer: ["Use quadrant scatter for first-shot prioritization/portfolio requests.", "Use built-in templates when the framework is named."],
2043
+ avoid: ["Avoid heatmap, correlation, and table modes unless the user asks for that form."],
2044
+ repair: ["Point coordinates are normalized fractions; keep them in `[0,1]`."]
2045
+ },
2046
+ orgchart: {
2047
+ type: "orgchart",
2048
+ header: 'orgchart "Title"',
2049
+ mode: "indented hierarchy",
2050
+ forms: ['ceo: "Name" | CEO', ' cto: "Name" | CTO', ' eng: "Name" | Engineer'],
2051
+ prefer: ["Use indentation for the reporting tree.", "Use dotted explicit edges only for matrix reporting."],
2052
+ avoid: ["Avoid mixing explicit report edges with indentation unless needed."],
2053
+ repair: ["Duplicate IDs and unknown explicit edge endpoints are parse failures."]
2054
+ },
2055
+ decisiontree: {
2056
+ type: "decisiontree",
2057
+ header: 'decisiontree "Title"',
2058
+ mode: "taxonomy questions",
2059
+ forms: ['question "Question?"', ' yes: answer "Outcome"', ' no: answer "Other outcome"'],
2060
+ prefer: ["Use taxonomy mode for troubleshooting and triage.", "Select `decisiontree:decision` or `decisiontree:ml` only when expected value or ML is explicit."],
2061
+ avoid: ["Avoid mixing mode-specific keywords."],
2062
+ repair: ["Indent each child under its parent and use the branch prefix required by the selected mode."]
2063
+ },
2064
+ timeline: {
2065
+ type: "timeline",
2066
+ header: 'timeline "Title"',
2067
+ mode: "dated events",
2068
+ forms: ['2026-05-22: "Event"', '2026-06-01 - 2026-06-30: "Phase"', '2026-07-01: milestone "Launch"'],
2069
+ prefer: ["Use dated events/ranges and `milestone` for important points."],
2070
+ avoid: ["Avoid custom row keys when a date is known."],
2071
+ repair: ["Quote labels and keep the colon after the date/range."]
2072
+ },
2073
+ state: {
2074
+ type: "state",
2075
+ header: 'state "Title"',
2076
+ mode: "Schematex statechart core",
2077
+ forms: ["initial Start", "Start --> Running : event", "Running --> Done", "final Done"],
2078
+ prefer: ["Use `state` header for generated DSL.", "Use Mermaid `stateDiagram-v2` only when adapting Mermaid input."],
2079
+ avoid: ["Avoid composite/concurrent-state syntax until the request needs it."],
2080
+ repair: ["Use `-->` transitions and a named state between initial/final aliases."]
2081
+ },
2082
+ pid: {
2083
+ type: "pid",
2084
+ header: 'pid "Title"',
2085
+ mode: "equipment + process lines",
2086
+ forms: ["equip T-101 : tank_atm", "equip P-101 : pump_centrifugal", "line L1 from T-101.bottom to P-101.in"],
2087
+ prefer: ["Declare equipment first, then process/signal lines."],
2088
+ avoid: ["Avoid SLD electrical nodes in a P&ID."],
2089
+ repair: ["Unknown equipment and instrument categories must come from the catalog."]
2090
+ },
2091
+ erd: {
2092
+ type: "erd",
2093
+ header: "erd",
2094
+ mode: "table blocks + named cardinality refs",
2095
+ forms: ["table User { id int PK; email varchar }", "table Order { id int PK; user_id int FK -> User.id }", "ref Order.user_id many-mandatory -- one-mandatory User.id"],
2096
+ prefer: ["Use named cardinality tokens for generated refs.", "Keep FK targets explicit."],
2097
+ avoid: ["Avoid Mermaid cardinality glyphs unless converting Mermaid input."],
2098
+ repair: ["Unknown cardinality tokens and unterminated table blocks fail validation."]
2099
+ },
2100
+ breadboard: {
2101
+ type: "breadboard",
2102
+ header: "breadboard",
2103
+ mode: "parts + wires blocks",
2104
+ forms: ['title: "Prototype"', "parts", "wires"],
2105
+ prefer: ["Use physical parts and addressable tie points.", "Keep schematic tasks on `circuit` instead."],
2106
+ avoid: ["Avoid abstract netlist syntax in breadboard output."],
2107
+ repair: ["Missing parts or invalid breadboard connection endpoints need the breadboard syntax reference."]
2108
+ },
2109
+ bpmn: {
2110
+ type: "bpmn",
2111
+ header: "bpmn",
2112
+ mode: "pool/lane objects + flows",
2113
+ forms: ['pool "Service" {', ' lane "Worker" { A: start "Request" }', "flows", "A --> B"],
2114
+ prefer: ["Use pools, lanes, and a `flows` block.", "Use `~~>` only for cross-pool message flow."],
2115
+ avoid: ["Avoid generic flowchart syntax for BPMN semantics."],
2116
+ repair: ["Sequence flows cannot cross pools; switch to message flow when needed."]
2117
+ },
2118
+ fbd: {
2119
+ type: "fbd",
2120
+ header: 'fbd "Title"',
2121
+ mode: "networks + blocks",
2122
+ forms: ["var Start: bool", 'network "Logic":', "Motor = AND(Start, Safe)"],
2123
+ prefer: ["Use network/expression forms from the FBD syntax guide."],
2124
+ avoid: ["Avoid ladder rung syntax in FBD output."],
2125
+ repair: ["Invalid block calls need a supported block and valid arguments."]
2126
+ },
2127
+ sfc: {
2128
+ type: "sfc",
2129
+ header: 'sfc "Title"',
2130
+ mode: "steps + transitions",
2131
+ forms: ["step Idle [initial]", "transition Idle -> Run : Start", "step Run"],
2132
+ prefer: ["Use explicit steps and transitions before alternative/simultaneous branches."],
2133
+ avoid: ["Avoid UML state syntax in SFC output."],
2134
+ repair: ["Every transition step reference must resolve to a declared step."]
2135
+ },
2136
+ prisma: {
2137
+ type: "prisma",
2138
+ header: "prisma",
2139
+ mode: "PRISMA 2020 single pipeline",
2140
+ forms: ["identification:", " databases:", " n: 1000", "screening:", "included:"],
2141
+ prefer: ["Use the required four stages and mandatory counts.", "Use single-pipeline 2020 mode unless other-methods data is present."],
2142
+ avoid: ["Avoid generic flowchart boxes for PRISMA requests."],
2143
+ repair: ["Missing stage blocks or mandatory `n` fields are intentional parser errors."]
2144
+ },
2145
+ usecase: {
2146
+ type: "usecase",
2147
+ header: "usecase",
2148
+ mode: "declarative UML use cases",
2149
+ forms: ['system: "System"', "actor: Customer", 'usecase: "Checkout" as Checkout', "Customer -- Checkout"],
2150
+ prefer: ["Use declarative actor/usecase lines for generated DSL."],
2151
+ avoid: ["Avoid PlantUML inline form unless converting PlantUML-like input."],
2152
+ repair: ["Include/extend relations must connect use cases, not actors."]
2153
+ },
2154
+ pert: {
2155
+ type: "pert",
2156
+ header: "pert",
2157
+ mode: "AON tasks + dependencies",
2158
+ forms: ['task A "Spec" duration: 3', 'task B "Build" duration: 8 after: A'],
2159
+ prefer: ["Use AON network tasks first.", "Use time-scaled/AOA layouts only when requested."],
2160
+ avoid: ["Avoid writing computed ES/EF/LS/LF fields yourself."],
2161
+ repair: ["Task IDs must exist before they are used in `after:` lists."]
2162
+ },
2163
+ sequence: {
2164
+ type: "sequence",
2165
+ header: 'sequence "Title"',
2166
+ mode: "participants + messages",
2167
+ forms: ["actor User", 'participant API as "API"', "User -> API : request", "API --> User : response"],
2168
+ prefer: ["Start with participants/messages, then add fragments only if control flow matters."],
2169
+ avoid: ["Avoid Mermaid `sequenceDiagram` header; this parser uses `sequence`."],
2170
+ repair: ["Unmatched `end`, `else`, or activation statements are validation failures."]
2171
+ }
2172
+ };
2173
+ function getGenerationProfile(type) {
2174
+ return PROFILES[type];
2175
+ }
2176
+
1563
2177
  // src/ai/syntax.ts
1564
- function getSyntaxForType(syntaxKey) {
2178
+ function getSyntaxForType(syntaxKey, type, detail = "canonical") {
1565
2179
  const s = SYNTAX[syntaxKey];
1566
2180
  if (!s) return void 0;
1567
- return { key: syntaxKey, ...s };
2181
+ return {
2182
+ key: syntaxKey,
2183
+ title: s.title,
2184
+ detail,
2185
+ content: detail === "reference" ? s.content : buildCanonicalSyntax(getGenerationProfile(type))
2186
+ };
2187
+ }
2188
+ function buildCanonicalSyntax(profile) {
2189
+ return [
2190
+ "# Canonical generation syntax",
2191
+ "",
2192
+ 'Use this compact path for new DSL generation. Ask for `detail: "reference"` only when the request needs advanced forms or an imported adapter.',
2193
+ "",
2194
+ "## Generation profile",
2195
+ "",
2196
+ `- Canonical type: \`${profile.type}\``,
2197
+ `- Canonical header: \`${profile.header}\``,
2198
+ `- Preferred mode: ${profile.mode}`,
2199
+ bulletSection("Core forms", profile.forms),
2200
+ bulletSection("Prefer", profile.prefer),
2201
+ bulletSection("Avoid by default", profile.avoid),
2202
+ bulletSection("Repair checks", profile.repair),
2203
+ "## Shared generation rules",
2204
+ "",
2205
+ ...COMMON_GENERATION_RULES.map((rule) => `- ${rule}`)
2206
+ ].filter((part) => part !== "").join("\n");
2207
+ }
2208
+ function bulletSection(title, items) {
2209
+ return [`### ${title}`, "", ...items.map((item) => `- ${item}`), ""].join("\n");
1568
2210
  }
1569
2211
 
1570
2212
  // src/ai/tools.ts
@@ -1578,14 +2220,14 @@ function listDiagrams() {
1578
2220
  standard: d.standard
1579
2221
  }));
1580
2222
  }
1581
- function getSyntax(type) {
2223
+ function getSyntax(type, opts = {}) {
1582
2224
  const meta = getDiagramMeta(type);
1583
2225
  if (!meta) {
1584
2226
  throw new Error(
1585
2227
  `Unknown diagram type '${type}'. Call listDiagrams() for valid types.`
1586
2228
  );
1587
2229
  }
1588
- const syntax = getSyntaxForType(meta.syntaxKey);
2230
+ const syntax = getSyntaxForType(meta.syntaxKey, meta.type, opts.detail);
1589
2231
  if (!syntax) {
1590
2232
  throw new Error(`No syntax doc available for '${type}' (key: ${meta.syntaxKey}).`);
1591
2233
  }
@@ -1607,38 +2249,66 @@ function getExamples(type, opts = {}) {
1607
2249
  return { type: meta.type, count: examples.length, examples };
1608
2250
  }
1609
2251
  function validateDsl(type, dsl) {
1610
- const config = type ? { type } : void 0;
1611
- try {
1612
- chunkSMXU3KYA_cjs.parse(dsl, config);
1613
- return { ok: true, type: type ?? resolveTypeFromText(dsl) };
1614
- } catch (err) {
1615
- return {
1616
- ok: false,
1617
- type: type ?? resolveTypeFromText(dsl),
1618
- errors: [extractError(err)]
1619
- };
2252
+ const resolvedType = type ? resolveDiagramType(type) : void 0;
2253
+ const config = type ? { type: resolvedType ?? type } : void 0;
2254
+ const result = chunk4QPDZJAL_cjs.parseResult(dsl, config);
2255
+ if (result.ok) {
2256
+ return { ok: true, type: result.type };
1620
2257
  }
2258
+ return {
2259
+ ok: false,
2260
+ type: result.type ?? resolvedType ?? resolveTypeFromText(dsl),
2261
+ errors: result.diagnostics.map(
2262
+ (diagnostic) => toValidationError(diagnostic, result.type ?? resolvedType)
2263
+ )
2264
+ };
1621
2265
  }
1622
2266
  function renderDsl(type, dsl, options = {}) {
2267
+ const resolvedType = type ? resolveDiagramType(type) : void 0;
1623
2268
  const config = {
1624
2269
  ...options,
1625
- ...type ? { type } : {}
2270
+ ...type ? { type: resolvedType ?? type } : {}
1626
2271
  };
1627
- try {
1628
- const svg = chunkSMXU3KYA_cjs.render(dsl, config);
1629
- return { ok: true, type: type ?? resolveTypeFromText(dsl), svg };
1630
- } catch (err) {
2272
+ const result = chunk4QPDZJAL_cjs.renderResult(dsl, config);
2273
+ if (result.ok) {
1631
2274
  return {
1632
- ok: false,
1633
- type: type ?? resolveTypeFromText(dsl),
1634
- errors: [extractError(err)]
2275
+ ok: true,
2276
+ status: result.status,
2277
+ type: result.type,
2278
+ svg: result.svg
1635
2279
  };
1636
2280
  }
2281
+ return {
2282
+ ok: false,
2283
+ status: result.status,
2284
+ type: result.type ?? resolvedType ?? resolveTypeFromText(dsl),
2285
+ svg: result.svg,
2286
+ errors: result.diagnostics.map(
2287
+ (diagnostic) => toValidationError(diagnostic, result.type ?? resolvedType)
2288
+ )
2289
+ };
1637
2290
  }
1638
2291
  function resolveTypeFromText(text) {
1639
2292
  const first = text.trim().split(/\s+|\n/)[0]?.toLowerCase() ?? "";
1640
- const meta = DIAGRAM_REGISTRY.find((d) => d.type === first);
1641
- return meta?.type ?? null;
2293
+ return resolveDiagramType(first) ?? null;
2294
+ }
2295
+ function toValidationError(diagnostic, type) {
2296
+ return {
2297
+ line: diagnostic.line,
2298
+ column: diagnostic.column,
2299
+ source: diagnostic.source,
2300
+ message: diagnostic.message,
2301
+ hint: diagnostic.hint ?? repairHint(type)
2302
+ };
2303
+ }
2304
+ function repairHint(type) {
2305
+ const resolved = type ? resolveDiagramType(type) : void 0;
2306
+ const profile = resolved ? getGenerationProfile(resolved) : void 0;
2307
+ const typeHint = profile?.repair[0];
2308
+ return [
2309
+ typeHint,
2310
+ "Fix the reported DSL error, then call validateDsl again before rendering or returning DSL."
2311
+ ].filter(Boolean).join(" ");
1642
2312
  }
1643
2313
 
1644
2314
  exports.DIAGRAM_REGISTRY = DIAGRAM_REGISTRY;
@@ -1648,6 +2318,7 @@ exports.getExamples = getExamples;
1648
2318
  exports.getSyntax = getSyntax;
1649
2319
  exports.listDiagrams = listDiagrams;
1650
2320
  exports.renderDsl = renderDsl;
2321
+ exports.resolveDiagramType = resolveDiagramType;
1651
2322
  exports.validateDsl = validateDsl;
1652
- //# sourceMappingURL=chunk-N3SLYVNW.cjs.map
1653
- //# sourceMappingURL=chunk-N3SLYVNW.cjs.map
2323
+ //# sourceMappingURL=chunk-HK56GQQP.cjs.map
2324
+ //# sourceMappingURL=chunk-HK56GQQP.cjs.map