@diagrammo/dgmo 0.4.2 → 0.4.3

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 (59) hide show
  1. package/.claude/skills/dgmo-chart/SKILL.md +28 -0
  2. package/.claude/skills/dgmo-generate/SKILL.md +1 -0
  3. package/.claude/skills/dgmo-sequence/SKILL.md +24 -1
  4. package/.cursorrules +27 -2
  5. package/.github/copilot-instructions.md +36 -3
  6. package/.windsurfrules +27 -2
  7. package/README.md +12 -3
  8. package/dist/cli.cjs +197 -154
  9. package/dist/index.cjs +8371 -3200
  10. package/dist/index.cjs.map +1 -1
  11. package/dist/index.d.cts +502 -58
  12. package/dist/index.d.ts +502 -58
  13. package/dist/index.js +8594 -3444
  14. package/dist/index.js.map +1 -1
  15. package/docs/ai-integration.md +1 -1
  16. package/docs/language-reference.md +336 -17
  17. package/docs/migration-sequence-color-to-tags.md +98 -0
  18. package/package.json +1 -1
  19. package/src/c4/renderer.ts +1 -20
  20. package/src/class/renderer.ts +1 -11
  21. package/src/cli.ts +40 -0
  22. package/src/d3.ts +92 -2
  23. package/src/dgmo-router.ts +11 -0
  24. package/src/echarts.ts +74 -8
  25. package/src/er/parser.ts +29 -3
  26. package/src/er/renderer.ts +1 -15
  27. package/src/graph/flowchart-parser.ts +7 -30
  28. package/src/graph/flowchart-renderer.ts +62 -69
  29. package/src/graph/layout.ts +5 -0
  30. package/src/graph/state-parser.ts +388 -0
  31. package/src/graph/state-renderer.ts +496 -0
  32. package/src/graph/types.ts +4 -2
  33. package/src/index.ts +42 -1
  34. package/src/infra/compute.ts +1113 -0
  35. package/src/infra/layout.ts +575 -0
  36. package/src/infra/parser.ts +559 -0
  37. package/src/infra/renderer.ts +1509 -0
  38. package/src/infra/roles.ts +60 -0
  39. package/src/infra/serialize.ts +67 -0
  40. package/src/infra/types.ts +221 -0
  41. package/src/infra/validation.ts +192 -0
  42. package/src/initiative-status/layout.ts +56 -61
  43. package/src/initiative-status/renderer.ts +13 -13
  44. package/src/kanban/renderer.ts +1 -24
  45. package/src/org/layout.ts +28 -37
  46. package/src/org/parser.ts +16 -1
  47. package/src/org/renderer.ts +159 -121
  48. package/src/org/resolver.ts +90 -23
  49. package/src/palettes/color-utils.ts +30 -0
  50. package/src/render.ts +2 -0
  51. package/src/sequence/parser.ts +202 -42
  52. package/src/sequence/renderer.ts +576 -113
  53. package/src/sequence/tag-resolution.ts +163 -0
  54. package/src/sitemap/collapse.ts +187 -0
  55. package/src/sitemap/layout.ts +738 -0
  56. package/src/sitemap/parser.ts +489 -0
  57. package/src/sitemap/renderer.ts +774 -0
  58. package/src/sitemap/types.ts +42 -0
  59. package/src/utils/tag-groups.ts +119 -0
@@ -122,4 +122,4 @@ dgmo --chart-types --json # List all chart types
122
122
 
123
123
  Run `dgmo --chart-types` for the full list, or see `docs/language-reference.md`.
124
124
 
125
- 29 types: bar, line, area, pie, doughnut, radar, polar-area, bar-stacked, scatter, sankey, chord, function, heatmap, funnel, slope, wordcloud, arc, timeline, venn, quadrant, sequence, flowchart, class, er, org, kanban, c4, initiative-status.
125
+ 32 types: bar, line, area, pie, doughnut, radar, polar-area, bar-stacked, scatter, sankey, chord, function, heatmap, funnel, slope, wordcloud, arc, timeline, venn, quadrant, sequence, flowchart, state, class, er, org, kanban, c4, initiative-status, sitemap, infra.
@@ -203,6 +203,8 @@ Options: `columns` (required).
203
203
 
204
204
  ### sankey
205
205
 
206
+ **Arrow syntax:**
207
+
206
208
  ```
207
209
  chart: sankey
208
210
  title: Resource Flow
@@ -213,7 +215,43 @@ Processing -> Output X: 350
213
215
  Processing -> Output Y: 150
214
216
  ```
215
217
 
216
- Data: `Source -> Target: value`
218
+ **Indentation syntax:**
219
+
220
+ ```
221
+ chart: sankey
222
+ title: Resource Flow
223
+
224
+ Source A
225
+ Processing: 300
226
+ Source B
227
+ Processing: 200
228
+ Processing
229
+ Output X: 350
230
+ Output Y: 150
231
+ ```
232
+
233
+ Both syntaxes can be mixed in the same diagram.
234
+
235
+ **Node colors** — `(color)` after a node name:
236
+
237
+ ```
238
+ Revenue (green)
239
+ Costs (red): 600
240
+ Profit (blue): 400
241
+
242
+ // or with arrows
243
+ Revenue (green) -> Costs (red): 600
244
+ ```
245
+
246
+ **Link colors** — `(color)` after the value:
247
+
248
+ ```
249
+ Revenue
250
+ Costs: 600 (orange)
251
+
252
+ // or with arrows
253
+ Revenue -> Costs: 600 (orange)
254
+ ```
217
255
 
218
256
  ### chord
219
257
 
@@ -425,11 +463,47 @@ API -200 OK-> User
425
463
  - `note on Participant: text` — anchored
426
464
  - Multi-line: indent continuation lines under `note:`
427
465
 
428
- **Sections**: `== Section Title ==` or `== Section Title(color) ==`
466
+ **Sections**: `== Section Title ==`
467
+
468
+ **Groups**: `[Group Name]` or `[Group Name | key: value]` — visual grouping box around participants
469
+
470
+ **Options**: `activations: off`, `collapse-notes: no`, `active-tag: GroupName`
471
+
472
+ **Tag groups** — define color-coded metadata dimensions for interactive recoloring:
473
+
474
+ ```
475
+ tag: Concern alias c
476
+ Caching(blue)
477
+ Auth(green)
478
+ BusinessLogic(purple) default
479
+ ```
429
480
 
430
- **Groups**: `## Group Name` visual grouping box around messages
481
+ - `tag: Name` declares a tag group; `alias x` adds a shorthand
482
+ - Each entry: `Value(color)` — named color for that value
483
+ - `default` on an entry applies it to untagged elements when the group is active
431
484
 
432
- **Options**: `activations: off`, `collapse-notes: no`
485
+ **Pipe metadata** attach tag values to participants, messages, and groups:
486
+
487
+ ```
488
+ API is a service | concern: Caching, team: Platform
489
+ User -login-> API | concern: Auth
490
+ [Backend | concern: BusinessLogic]
491
+ OrderAPI
492
+ DB
493
+ ```
494
+
495
+ - `| key: value` after participant declarations, message lines, or group headers
496
+ - Multiple tags: `| key1: val1, key2: val2`
497
+ - Aliases work: `| c: Caching` (if `alias c` was declared)
498
+
499
+ **Tag resolution priority** (when a tag group is active):
500
+ 1. Explicit metadata on the participant
501
+ 2. Group propagation (participant inherits from its group)
502
+ 3. Receiver inheritance (all incoming tagged messages agree on same value)
503
+ 4. `default` entry value
504
+ 5. Neutral (no color)
505
+
506
+ **Legend** — rendered automatically above participants when tag groups exist. The active group expands to show colored entry dots. Click a group pill to activate it (in the desktop app).
433
507
 
434
508
  ### flowchart
435
509
 
@@ -463,6 +537,55 @@ title: Decision Process
463
537
 
464
538
  Colors on nodes: `[Process(blue)]`
465
539
 
540
+ ### state
541
+
542
+ Minimal example:
543
+
544
+ ```
545
+ [*] -> Idle -> Active -> [*]
546
+ ```
547
+
548
+ Full example:
549
+
550
+ ```
551
+ chart: state
552
+ title: Order Lifecycle
553
+ direction: LR
554
+
555
+ ## Processing(blue)
556
+ Validating -valid-> Approved
557
+ Validating -invalid-> Rejected(red)
558
+
559
+ [*] -> Pending -submit-> Validating
560
+ Approved -ship-> Shipped -> [*]
561
+ Rejected -> [*]
562
+ Shipped -return-> Pending
563
+ ```
564
+
565
+ **States**: bare text — `Idle`, `Active`, `Processing`. Optional color suffix: `Active(green)`.
566
+
567
+ **Pseudostates**: `[*]` — rendered as a filled circle. Use for start and end points.
568
+
569
+ **Transitions**: `->`, `-label->`, `-(color)->`, `-label(color)->`.
570
+
571
+ **Chains**: `A -> B -> C` on a single line creates two transitions.
572
+
573
+ **Indentation**: indented lines use the parent as implicit source:
574
+
575
+ ```
576
+ Idle
577
+ -start-> Running
578
+ -configure-> Configuring
579
+ ```
580
+
581
+ is equivalent to `Idle -start-> Running` and `Idle -configure-> Configuring`.
582
+
583
+ **Groups**: `## GroupName` or `## GroupName(color)` — groups subsequent indented states visually.
584
+
585
+ **Self-loops**: `Running -retry-> Running` — a state transitioning to itself.
586
+
587
+ **Options**: `direction` (`TB` or `LR`), `title`, `color: off` (monochrome mode).
588
+
466
589
  ### class
467
590
 
468
591
  Minimal example:
@@ -528,12 +651,11 @@ Minimal example:
528
651
  users
529
652
  id: int [pk]
530
653
  name: varchar
654
+ 1-* posts
531
655
 
532
656
  posts
533
657
  id: int [pk]
534
658
  user_id: int [fk]
535
-
536
- users 1--* posts : writes
537
659
  ```
538
660
 
539
661
  Full example:
@@ -546,6 +668,8 @@ users
546
668
  id: int [pk]
547
669
  email: varchar [unique]
548
670
  name: varchar
671
+ 1-writes-* posts
672
+ ?-moderates-* categories
549
673
 
550
674
  posts
551
675
  id: int [pk]
@@ -557,20 +681,23 @@ posts
557
681
  categories
558
682
  id: int [pk]
559
683
  name: varchar [unique]
560
-
561
- users 1--* posts : writes
562
- categories 1--* posts : contains
563
- users ?--* categories : moderates
684
+ 1-* posts
564
685
  ```
565
686
 
566
687
  **Columns**: `name: type [constraints]`. Constraints: `pk`, `fk`, `unique`, `nullable`. Multiple: `[fk, nullable]`.
567
688
 
568
- **Relationships** (symbolic cardinality):
569
- - `1--1` — one-to-one
570
- - `1--*` — one-to-many
571
- - `?--1` — zero-or-one to one
572
- - `?--*` — zero-or-more
573
- - Optional label: `table1 1--* table2 : description`
689
+ **Relationships** — indented under the source table (preferred):
690
+ - `1-* target` — one-to-many
691
+ - `1-1 target` — one-to-one
692
+ - `?-1 target` — zero-or-one to one
693
+ - `?-* target` — zero-or-more
694
+ - Labeled: `1-writes-* target` (label between dashes)
695
+
696
+ Columns and relationships can be mixed under the same table. The parser distinguishes them by the leading cardinality character (`1`, `*`, `?`).
697
+
698
+ **Flat relationships** — at indent 0 (also supported):
699
+ - `table1 1--* table2` — one-to-many
700
+ - `table1 1--* table2 : label` — with label
574
701
 
575
702
  ### c4
576
703
 
@@ -766,11 +893,203 @@ DBLayer | done
766
893
 
767
894
  **Groups**: `[Group Name]` for visual grouping.
768
895
 
896
+ ### sitemap
897
+
898
+ Minimal example:
899
+
900
+ ```
901
+ chart: sitemap
902
+
903
+ Home
904
+ -about-> About
905
+ -blog-> Blog
906
+
907
+ [Content]
908
+ About
909
+ Blog
910
+ -read-> Post
911
+ Post
912
+ ```
913
+
914
+ Full example:
915
+
916
+ ```
917
+ chart: sitemap
918
+ title: SaaS Platform
919
+ direction: TB
920
+
921
+ tag: Auth
922
+ Public(green)
923
+ Required(blue)
924
+ Admin(red)
925
+
926
+ tag: Type
927
+ Landing(purple)
928
+ Form(orange)
929
+ Content(cyan)
930
+
931
+ Home
932
+ Auth: Public
933
+ Type: Landing
934
+ -pricing-> Pricing
935
+ -login-> Login
936
+ -docs-> Docs
937
+
938
+ [Marketing]
939
+ Pricing
940
+ Auth: Public
941
+ Type: Content
942
+ -sign up-> Register
943
+
944
+ Docs
945
+ Auth: Public
946
+ Type: Content
947
+
948
+ [Auth]
949
+ Login
950
+ Auth: Public
951
+ Type: Form
952
+ -success-> Dashboard
953
+ -forgot-> Reset Password
954
+
955
+ Register
956
+ Auth: Public
957
+ Type: Form
958
+ -success-> Dashboard
959
+
960
+ Reset Password
961
+ Auth: Public
962
+ Type: Form
963
+ -submitted-> Login
964
+
965
+ [App]
966
+ Dashboard
967
+ Auth: Required
968
+ Type: Landing
969
+ -projects-> Projects
970
+ -settings-> Settings
971
+
972
+ Projects
973
+ Auth: Required
974
+ Type: Content
975
+
976
+ Settings
977
+ Auth: Required
978
+ Type: Form
979
+ -saved-> Dashboard
980
+ ```
981
+
982
+ **Pages**: Plain labels at any indent level become page nodes.
983
+
984
+ **Groups**: `[Group Name]` wraps indented children in a container.
985
+
986
+ **Arrows**: `-label-> Target`, `-(color)-> Target`, `-label(color)-> Target` — cross-link between any pages.
987
+
988
+ **Metadata**: `Key: Value` lines attach to the parent page (displayed as card rows).
989
+
990
+ **Tag groups**: `tag: Name` with colored entries — same syntax as org charts.
991
+
992
+ **Direction**: `direction: TB` (top-to-bottom, default) or `direction: LR` (left-to-right).
993
+
994
+ **Collapsible groups**: Groups can be collapsed/expanded in the app — arrows to hidden pages re-terminate at the group boundary.
995
+
996
+ ### infra
997
+
998
+ Minimal example:
999
+
1000
+ ```
1001
+ chart: infra
1002
+
1003
+ edge
1004
+ rps: 1000
1005
+ -> CDN
1006
+
1007
+ CDN
1008
+ cache-hit: 80%
1009
+ -> API
1010
+
1011
+ API
1012
+ instances: 2
1013
+ max-rps: 400
1014
+ latency-ms: 30
1015
+ ```
1016
+
1017
+ Full example:
1018
+
1019
+ ```
1020
+ chart: infra
1021
+ title: Production Traffic Flow
1022
+ direction: LR
1023
+
1024
+ tag: Team alias t
1025
+ Backend(blue)
1026
+ Platform(teal)
1027
+
1028
+ edge
1029
+ rps: 10000
1030
+ -> CloudFront
1031
+
1032
+ CloudFront | t: Platform
1033
+ cache-hit: 80%
1034
+ -> CloudArmor
1035
+
1036
+ CloudArmor | t: Platform
1037
+ firewall-block: 5%
1038
+ -> ALB
1039
+
1040
+ ALB | t: Platform
1041
+ -/api-> [API Pods] | split: 60%
1042
+ -/purchase-> [Commerce Pods] | split: 30%
1043
+ -/static-> StaticServer | split: 10%
1044
+
1045
+ [API Pods]
1046
+ APIServer | t: Backend
1047
+ instances: 3
1048
+ max-rps: 500
1049
+ latency-ms: 45
1050
+ cb-error-threshold: 50%
1051
+
1052
+ [Commerce Pods]
1053
+ PurchaseMS
1054
+ instances: 1-8
1055
+ max-rps: 300
1056
+ latency-ms: 120
1057
+
1058
+ StaticServer | t: Platform
1059
+ latency-ms: 5
1060
+ ```
1061
+
1062
+ **Entry point**: The `edge` block declares the external traffic source with `rps:` (requests per second). All downstream rps are computed automatically.
1063
+
1064
+ **Components**: Bare labels at indent 0 define infrastructure components. Properties are indented below:
1065
+ - `cache-hit: N%` — percentage of traffic served from cache (reduces downstream flow)
1066
+ - `firewall-block: N%` — percentage of traffic blocked (reduces downstream flow)
1067
+ - `ratelimit-rps: N` — maximum rps allowed through (excess dropped)
1068
+ - `max-rps: N` — maximum rps capacity per instance
1069
+ - `instances: N` or `instances: N-M` — fixed or auto-scaling instance count
1070
+ - `latency-ms: N` — per-request latency in milliseconds
1071
+ - `cb-error-threshold: N%` — circuit breaker opens when overload exceeds this ratio
1072
+ - `cb-latency-threshold-ms: N` — circuit breaker opens when cumulative latency exceeds this
1073
+
1074
+ **Connections**: `-> Target` (unlabeled), `-label-> Target` (labeled). Pipe metadata for splits: `-> Target | split: N%`.
1075
+
1076
+ **Branching**: Multiple outbound connections with `split: N%` metadata. Splits must sum to 100%. Undeclared splits are evenly distributed from the remaining percentage.
1077
+
1078
+ **Groups**: `[Group Name]` with indented children — rendered as dashed-border containers. Edges targeting a group route to all children.
1079
+
1080
+ **Roles**: Inferred automatically from behavior properties — no type declarations needed. Components with `cache-hit` get a Cache role, `firewall-block` gets Firewall, etc. Roles appear as colored dots on nodes and in the legend.
1081
+
1082
+ **Overload**: When computed rps exceeds `max-rps × instances`, the node turns red. Dynamic scaling (`instances: 1-8`) auto-scales within the range before overloading.
1083
+
1084
+ **Direction**: `direction: LR` (left-to-right, default) or `direction: TB` (top-to-bottom).
1085
+
1086
+ **Tag groups**: Same syntax as org/kanban/sitemap — `tag: Name alias x` with colored entries.
1087
+
769
1088
  ---
770
1089
 
771
1090
  ## Tag Groups
772
1091
 
773
- Define reusable metadata categories for org charts, kanban boards, and C4 diagrams:
1092
+ Define reusable metadata categories for org charts, kanban boards, C4 diagrams, sitemaps, and infra charts:
774
1093
 
775
1094
  ```
776
1095
  tag: Priority
@@ -0,0 +1,98 @@
1
+ # Migration Guide: Sequence Diagram Colors → Tags
2
+
3
+ This guide covers migrating from inline `(color)` syntax to the tag system for sequence diagrams.
4
+
5
+ ## Why migrate?
6
+
7
+ Inline `(color)` on sequence elements is static — every viewer sees the same fixed colors. Tags provide:
8
+
9
+ - **Interactive recoloring** — click legend pills to switch between different color dimensions (e.g., "by team" vs "by concern")
10
+ - **Multiple dimensions** — one diagram can carry team ownership, protocol type, and concern metadata simultaneously
11
+ - **Receiver inheritance** — participants auto-inherit color from incoming tagged messages
12
+ - **Legend** — auto-generated legend bar with clickable pills and colored entry dots
13
+
14
+ ## Before / After
15
+
16
+ ### Participants
17
+
18
+ Before (static color):
19
+ ```
20
+ API(blue)
21
+ DB(green)
22
+ ```
23
+
24
+ After (tag-driven):
25
+ ```
26
+ tag: Role
27
+ Gateway(blue)
28
+ Storage(green)
29
+
30
+ API is a service | role: Gateway
31
+ DB is a database | role: Storage
32
+ ```
33
+
34
+ ### Messages
35
+
36
+ Before (no color mechanism existed for messages):
37
+ ```
38
+ User -login-> API
39
+ ```
40
+
41
+ After (tag metadata on messages):
42
+ ```
43
+ tag: Concern
44
+ Auth(green)
45
+
46
+ User -login-> API | concern: Auth
47
+ ```
48
+
49
+ ### Groups
50
+
51
+ Before (static color):
52
+ ```
53
+ [Backend(teal)]
54
+ API
55
+ DB
56
+ ```
57
+
58
+ After (tag-driven):
59
+ ```
60
+ tag: Team
61
+ Product(teal)
62
+
63
+ [Backend | team: Product]
64
+ API
65
+ DB
66
+ ```
67
+
68
+ ### Sections
69
+
70
+ Sections (`== Title ==`) do not carry tag metadata. Continue using inline colors if needed:
71
+ ```
72
+ == Authentication(green) ==
73
+ ```
74
+
75
+ ## Quick reference
76
+
77
+ | Syntax | Purpose |
78
+ |--------|---------|
79
+ | `tag: Name alias x` | Declare a tag group with optional alias |
80
+ | `Value(color)` | Tag entry with named color |
81
+ | `Value(color) default` | Default entry for untagged elements |
82
+ | `\| key: value` | Pipe metadata on participants, messages, groups |
83
+ | `\| k1: v1, k2: v2` | Multiple tag values |
84
+ | `active-tag: Name` | Activate a group for CLI/export rendering |
85
+
86
+ ## Activation
87
+
88
+ In the desktop app, click legend pills to switch active tag groups interactively.
89
+
90
+ For CLI or static export, add `active-tag: GroupName` to the diagram header:
91
+ ```
92
+ chart: sequence
93
+ active-tag: Concern
94
+
95
+ tag: Concern alias c
96
+ Caching(blue)
97
+ Auth(green)
98
+ ```
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@diagrammo/dgmo",
3
- "version": "0.4.2",
3
+ "version": "0.4.3",
4
4
  "description": "DGMO diagram markup language — parser, renderer, and color system",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -6,6 +6,7 @@ import * as d3Selection from 'd3-selection';
6
6
  import * as d3Shape from 'd3-shape';
7
7
  import { FONT_FAMILY } from '../fonts';
8
8
  import type { PaletteColors } from '../palettes';
9
+ import { mix } from '../palettes/color-utils';
9
10
  import { renderInlineText } from '../utils/inline-markdown';
10
11
  import type { ParsedC4 } from './types';
11
12
  import type { C4Shape } from './types';
@@ -73,26 +74,6 @@ const LEGEND_CAPSULE_PAD = 4;
73
74
  // Color helpers
74
75
  // ============================================================
75
76
 
76
- function mix(a: string, b: string, pct: number): string {
77
- const parse = (h: string) => {
78
- const r = h.replace('#', '');
79
- const f = r.length === 3 ? r[0] + r[0] + r[1] + r[1] + r[2] + r[2] : r;
80
- return [
81
- parseInt(f.substring(0, 2), 16),
82
- parseInt(f.substring(2, 4), 16),
83
- parseInt(f.substring(4, 6), 16),
84
- ];
85
- };
86
- const [ar, ag, ab] = parse(a),
87
- [br, bg, bb] = parse(b),
88
- t = pct / 100;
89
- const c = (x: number, y: number) =>
90
- Math.round(x * t + y * (1 - t))
91
- .toString(16)
92
- .padStart(2, '0');
93
- return `#${c(ar, br)}${c(ag, bg)}${c(ab, bb)}`;
94
- }
95
-
96
77
  function typeColor(
97
78
  type: 'person' | 'system' | 'container' | 'component',
98
79
  palette: PaletteColors,
@@ -6,6 +6,7 @@ import * as d3Selection from 'd3-selection';
6
6
  import * as d3Shape from 'd3-shape';
7
7
  import { FONT_FAMILY } from '../fonts';
8
8
  import type { PaletteColors } from '../palettes';
9
+ import { mix } from '../palettes/color-utils';
9
10
  import type { ParsedClassDiagram, ClassModifier, RelationshipType } from './types';
10
11
  import type { ClassLayoutResult, ClassLayoutNode, ClassLayoutEdge } from './layout';
11
12
  import { parseClassDiagram } from './parser';
@@ -30,17 +31,6 @@ const MEMBER_PADDING_X = 10;
30
31
  // Color helpers
31
32
  // ============================================================
32
33
 
33
- function mix(a: string, b: string, pct: number): string {
34
- const parse = (h: string) => {
35
- const r = h.replace('#', '');
36
- const f = r.length === 3 ? r[0]+r[0]+r[1]+r[1]+r[2]+r[2] : r;
37
- return [parseInt(f.substring(0,2),16), parseInt(f.substring(2,4),16), parseInt(f.substring(4,6),16)];
38
- };
39
- const [ar,ag,ab] = parse(a), [br,bg,bb] = parse(b), t = pct/100;
40
- const c = (x: number, y: number) => Math.round(x*t + y*(1-t)).toString(16).padStart(2,'0');
41
- return `#${c(ar,br)}${c(ag,bg)}${c(ab,bb)}`;
42
- }
43
-
44
34
  function modifierColor(modifier: ClassModifier | undefined, palette: PaletteColors, colorOff?: boolean): string {
45
35
  if (colorOff) return palette.textMuted;
46
36
  switch (modifier) {
package/src/cli.ts CHANGED
@@ -54,6 +54,7 @@ const CHART_TYPE_DESCRIPTIONS: Record<string, string> = {
54
54
  kanban: 'Kanban board — task/workflow columns',
55
55
  c4: 'C4 diagram — system architecture (context, container, component, deployment)',
56
56
  'initiative-status': 'Initiative status — project roadmap with dependency tracking',
57
+ infra: 'Infra chart — infrastructure traffic flow with rps computation',
57
58
  };
58
59
 
59
60
  function printHelp(): void {
@@ -101,6 +102,8 @@ function parseArgs(argv: string[]): {
101
102
  c4Level: 'context' | 'containers' | 'components' | 'deployment';
102
103
  c4System: string | undefined;
103
104
  c4Container: string | undefined;
105
+ scenario: string | undefined;
106
+ listScenarios: boolean;
104
107
  } {
105
108
  const result = {
106
109
  input: undefined as string | undefined,
@@ -116,6 +119,8 @@ function parseArgs(argv: string[]): {
116
119
  c4Level: 'context' as 'context' | 'containers' | 'components' | 'deployment',
117
120
  c4System: undefined as string | undefined,
118
121
  c4Container: undefined as string | undefined,
122
+ scenario: undefined as string | undefined,
123
+ listScenarios: false,
119
124
  };
120
125
 
121
126
  const args = argv.slice(2); // skip node + script
@@ -169,6 +174,12 @@ function parseArgs(argv: string[]): {
169
174
  } else if (arg === '--c4-container') {
170
175
  result.c4Container = args[++i];
171
176
  i++;
177
+ } else if (arg === '--scenario') {
178
+ result.scenario = args[++i];
179
+ i++;
180
+ } else if (arg === '--list-scenarios') {
181
+ result.listScenarios = true;
182
+ i++;
172
183
  } else if (arg === '--no-branding') {
173
184
  result.noBranding = true;
174
185
  i++;
@@ -429,6 +440,34 @@ async function main(): Promise<void> {
429
440
  }
430
441
  }
431
442
 
443
+ // List scenarios (infra diagrams)
444
+ if (opts.listScenarios) {
445
+ const { parseInfra } = await import('./infra/parser');
446
+ const infraParsed = parseInfra(content);
447
+ if (infraParsed.scenarios.length === 0) {
448
+ console.log('(no scenarios defined)');
449
+ } else {
450
+ for (const s of infraParsed.scenarios) {
451
+ console.log(s.name);
452
+ }
453
+ }
454
+ return;
455
+ }
456
+
457
+ // Validate --scenario name against defined scenarios
458
+ if (opts.scenario) {
459
+ const { parseInfra } = await import('./infra/parser');
460
+ const infraParsed = parseInfra(content);
461
+ const match = infraParsed.scenarios.find((s) => s.name.toLowerCase() === opts.scenario!.toLowerCase());
462
+ if (!match) {
463
+ const available = infraParsed.scenarios.map((s) => s.name);
464
+ const msg = available.length > 0
465
+ ? `Error: Unknown scenario "${opts.scenario}". Available: ${available.join(', ')}`
466
+ : `Error: Unknown scenario "${opts.scenario}". No scenarios defined in this file.`;
467
+ exitWithJsonError(msg);
468
+ }
469
+ }
470
+
432
471
  // Validate C4 options
433
472
  if (opts.c4Level === 'containers' && !opts.c4System) {
434
473
  exitWithJsonError('Error: --c4-system is required when --c4-level is containers');
@@ -449,6 +488,7 @@ async function main(): Promise<void> {
449
488
  c4Level: opts.c4Level,
450
489
  c4System: opts.c4System,
451
490
  c4Container: opts.c4Container,
491
+ scenario: opts.scenario,
452
492
  });
453
493
 
454
494
  if (!svg) {