@diagrammo/dgmo 0.4.2 → 0.4.4
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.
- package/.claude/skills/dgmo-chart/SKILL.md +28 -0
- package/.claude/skills/dgmo-generate/SKILL.md +1 -0
- package/.claude/skills/dgmo-sequence/SKILL.md +24 -1
- package/.cursorrules +27 -2
- package/.github/copilot-instructions.md +36 -3
- package/.windsurfrules +27 -2
- package/README.md +12 -3
- package/dist/cli.cjs +197 -154
- package/dist/index.cjs +8647 -3447
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +503 -58
- package/dist/index.d.ts +503 -58
- package/dist/index.js +8379 -3200
- package/dist/index.js.map +1 -1
- package/docs/ai-integration.md +1 -1
- package/docs/language-reference.md +336 -17
- package/docs/migration-sequence-color-to-tags.md +98 -0
- package/package.json +1 -1
- package/src/c4/renderer.ts +1 -20
- package/src/class/renderer.ts +1 -11
- package/src/cli.ts +40 -0
- package/src/d3.ts +92 -2
- package/src/dgmo-router.ts +11 -0
- package/src/echarts.ts +74 -8
- package/src/er/parser.ts +29 -3
- package/src/er/renderer.ts +1 -15
- package/src/graph/flowchart-parser.ts +7 -30
- package/src/graph/flowchart-renderer.ts +62 -69
- package/src/graph/layout.ts +5 -0
- package/src/graph/state-parser.ts +388 -0
- package/src/graph/state-renderer.ts +496 -0
- package/src/graph/types.ts +4 -2
- package/src/index.ts +42 -1
- package/src/infra/compute.ts +1113 -0
- package/src/infra/layout.ts +578 -0
- package/src/infra/parser.ts +559 -0
- package/src/infra/renderer.ts +1553 -0
- package/src/infra/roles.ts +60 -0
- package/src/infra/serialize.ts +67 -0
- package/src/infra/types.ts +221 -0
- package/src/infra/validation.ts +192 -0
- package/src/initiative-status/layout.ts +56 -61
- package/src/initiative-status/renderer.ts +13 -13
- package/src/kanban/renderer.ts +1 -24
- package/src/org/layout.ts +28 -37
- package/src/org/parser.ts +16 -1
- package/src/org/renderer.ts +159 -121
- package/src/org/resolver.ts +90 -23
- package/src/palettes/color-utils.ts +30 -0
- package/src/render.ts +2 -0
- package/src/sequence/parser.ts +202 -42
- package/src/sequence/renderer.ts +576 -113
- package/src/sequence/tag-resolution.ts +163 -0
- package/src/sharing.ts +8 -0
- package/src/sitemap/collapse.ts +187 -0
- package/src/sitemap/layout.ts +738 -0
- package/src/sitemap/parser.ts +489 -0
- package/src/sitemap/renderer.ts +774 -0
- package/src/sitemap/types.ts +42 -0
- package/src/utils/tag-groups.ts +119 -0
package/docs/ai-integration.md
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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 ==`
|
|
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
|
-
|
|
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
|
-
**
|
|
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** (
|
|
569
|
-
- `1
|
|
570
|
-
- `1
|
|
571
|
-
-
|
|
572
|
-
-
|
|
573
|
-
-
|
|
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
|
|
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
package/src/c4/renderer.ts
CHANGED
|
@@ -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,
|
package/src/class/renderer.ts
CHANGED
|
@@ -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) {
|