@diagrammo/dgmo 0.15.1 → 0.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (109) hide show
  1. package/README.md +9 -9
  2. package/dist/advanced.cjs +479 -454
  3. package/dist/advanced.d.cts +34 -35
  4. package/dist/advanced.d.ts +34 -35
  5. package/dist/advanced.js +479 -453
  6. package/dist/auto.cjs +374 -352
  7. package/dist/auto.js +103 -103
  8. package/dist/auto.mjs +374 -352
  9. package/dist/cli.cjs +140 -140
  10. package/dist/editor.cjs +8 -9
  11. package/dist/editor.js +8 -9
  12. package/dist/highlight.cjs +8 -9
  13. package/dist/highlight.js +8 -9
  14. package/dist/index.cjs +365 -342
  15. package/dist/index.js +365 -342
  16. package/dist/internal.cjs +479 -454
  17. package/dist/internal.d.cts +34 -35
  18. package/dist/internal.d.ts +34 -35
  19. package/dist/internal.js +479 -453
  20. package/dist/pert.d.cts +2 -2
  21. package/dist/pert.d.ts +2 -2
  22. package/docs/language-reference.md +83 -66
  23. package/gallery/fixtures/area.dgmo +3 -3
  24. package/gallery/fixtures/bar-stacked.dgmo +5 -5
  25. package/gallery/fixtures/boxes-and-lines.dgmo +2 -2
  26. package/gallery/fixtures/c4-full.dgmo +8 -8
  27. package/gallery/fixtures/class-full.dgmo +2 -2
  28. package/gallery/fixtures/doughnut.dgmo +6 -6
  29. package/gallery/fixtures/flowchart-colors.dgmo +3 -3
  30. package/gallery/fixtures/function.dgmo +3 -3
  31. package/gallery/fixtures/gantt-full.dgmo +9 -9
  32. package/gallery/fixtures/gantt.dgmo +7 -7
  33. package/gallery/fixtures/infra-full.dgmo +6 -6
  34. package/gallery/fixtures/infra.dgmo +2 -2
  35. package/gallery/fixtures/kanban.dgmo +9 -9
  36. package/gallery/fixtures/line.dgmo +2 -2
  37. package/gallery/fixtures/multi-line.dgmo +3 -3
  38. package/gallery/fixtures/org-full.dgmo +6 -6
  39. package/gallery/fixtures/quadrant.dgmo +2 -2
  40. package/gallery/fixtures/sankey.dgmo +9 -9
  41. package/gallery/fixtures/scatter.dgmo +3 -3
  42. package/gallery/fixtures/sequence-tags-protocols.dgmo +8 -8
  43. package/gallery/fixtures/sequence-tags.dgmo +7 -7
  44. package/gallery/fixtures/sitemap-full.dgmo +7 -7
  45. package/gallery/fixtures/slope.dgmo +5 -5
  46. package/gallery/fixtures/spr-eras.dgmo +9 -9
  47. package/gallery/fixtures/timeline.dgmo +3 -3
  48. package/gallery/fixtures/venn.dgmo +3 -3
  49. package/package.json +1 -1
  50. package/src/advanced.ts +0 -1
  51. package/src/boxes-and-lines/renderer.ts +5 -1
  52. package/src/c4/parser.ts +1 -1
  53. package/src/c4/renderer.ts +15 -8
  54. package/src/chart.ts +18 -9
  55. package/src/class/parser.ts +7 -6
  56. package/src/class/renderer.ts +17 -6
  57. package/src/cli.ts +6 -6
  58. package/src/completion.ts +13 -3
  59. package/src/cycle/parser.ts +14 -0
  60. package/src/cycle/renderer.ts +6 -3
  61. package/src/d3.ts +86 -46
  62. package/src/echarts.ts +26 -9
  63. package/src/editor/dgmo.grammar +1 -3
  64. package/src/editor/dgmo.grammar.js +8 -8
  65. package/src/editor/dgmo.grammar.terms.js +11 -12
  66. package/src/editor/highlight-api.ts +0 -1
  67. package/src/editor/highlight.ts +0 -1
  68. package/src/er/parser.ts +18 -11
  69. package/src/er/renderer.ts +19 -7
  70. package/src/gantt/parser.ts +1 -1
  71. package/src/gantt/renderer.ts +7 -4
  72. package/src/graph/flowchart-parser.ts +18 -84
  73. package/src/graph/flowchart-renderer.ts +3 -8
  74. package/src/graph/layout.ts +0 -2
  75. package/src/graph/state-parser.ts +17 -62
  76. package/src/graph/state-renderer.ts +3 -8
  77. package/src/infra/parser.ts +21 -11
  78. package/src/infra/renderer.ts +7 -4
  79. package/src/journey-map/parser.ts +10 -3
  80. package/src/journey-map/renderer.ts +3 -1
  81. package/src/kanban/parser.ts +10 -6
  82. package/src/kanban/renderer.ts +3 -1
  83. package/src/mindmap/parser.ts +2 -2
  84. package/src/mindmap/renderer.ts +2 -1
  85. package/src/org/parser.ts +2 -2
  86. package/src/org/renderer.ts +4 -3
  87. package/src/pert/parser.ts +7 -7
  88. package/src/pert/renderer.ts +7 -2
  89. package/src/pert/types.ts +1 -1
  90. package/src/pyramid/parser.ts +12 -0
  91. package/src/raci/parser.ts +40 -10
  92. package/src/raci/renderer.ts +2 -1
  93. package/src/raci/types.ts +4 -3
  94. package/src/ring/parser.ts +12 -0
  95. package/src/sequence/parser.ts +15 -9
  96. package/src/sequence/renderer.ts +1 -1
  97. package/src/sitemap/layout.ts +0 -2
  98. package/src/sitemap/parser.ts +11 -37
  99. package/src/sitemap/renderer.ts +13 -13
  100. package/src/sitemap/types.ts +0 -1
  101. package/src/tech-radar/renderer.ts +5 -3
  102. package/src/tech-radar/types.ts +2 -0
  103. package/src/utils/arrows.ts +3 -28
  104. package/src/utils/legend-d3.ts +12 -6
  105. package/src/utils/legend-layout.ts +1 -1
  106. package/src/utils/legend-types.ts +1 -1
  107. package/src/utils/parsing.ts +64 -35
  108. package/src/utils/tag-groups.ts +98 -18
  109. package/src/wireframe/parser.ts +2 -2
@@ -1,27 +1,27 @@
1
1
  kanban Plunder Sprint 7
2
2
 
3
3
  tag Priority
4
- Low(green)
5
- Urgent(red)
6
- High(orange)
4
+ Low green
5
+ Urgent red
6
+ High orange
7
7
 
8
8
  tag Crew as c
9
- Blackbeard(red)
10
- Anne Bonny(purple)
11
- Calico Jack(teal)
9
+ Blackbeard red
10
+ Anne Bonny purple
11
+ Calico Jack teal
12
12
 
13
- [Awaiting Orders](red)
13
+ [Awaiting Orders] red
14
14
  Recruit gunners at Tortuga | priority: High, c: Calico Jack
15
15
  Chart new trade route | priority: Urgent, c: Anne Bonny
16
16
  Scout the Windward Passage
17
17
  Avoid Royal Navy patrols
18
18
  Resupply rum and powder | priority: Low, c: Calico Jack
19
19
 
20
- [Underway](orange) | wip: 2
20
+ [Underway] orange | wip: 2
21
21
  Forge letters of marque | priority: High, c: Anne Bonny
22
22
  Raid merchant convoy | priority: Urgent, c: Blackbeard
23
23
  Three ships spotted off Nassau
24
24
 
25
- [Done](green)
25
+ [Done] green
26
26
  Bribe the harbour master | priority: High, c: Anne Bonny
27
27
  Repair hull damage | priority: Low, c: Blackbeard
@@ -2,8 +2,8 @@ line Daily Active Users (Q1 2025)
2
2
  x-label Week
3
3
  y-label Users (thousands)
4
4
 
5
- era Week 1 -> Week 6 Phase 1 (blue)
6
- era Week 6 -> Week 12 Phase 2 (green)
5
+ era Week 1 -> Week 6 Phase 1 blue
6
+ era Week 6 -> Week 12 Phase 2 green
7
7
 
8
8
  Week 1 12.4
9
9
  Week 2 13.1
@@ -3,9 +3,9 @@ x-label Quarter
3
3
  y-label Amount ($M)
4
4
 
5
5
  series
6
- Revenue (blue)
7
- Operating Cost (red)
8
- Net Profit (green)
6
+ Revenue blue
7
+ Operating Cost red
8
+ Net Profit green
9
9
 
10
10
  Q1 2023 4.2 3.1 1.1
11
11
  Q2 2023 4.8 3.3 1.5
@@ -1,14 +1,14 @@
1
1
  org Acme Corp
2
2
 
3
3
  tag Location
4
- NY(blue)
5
- LA(yellow)
6
- CO(green)
7
- Remote(purple)
4
+ NY blue
5
+ LA yellow
6
+ CO green
7
+ Remote purple
8
8
 
9
9
  tag Status
10
- FTE(green)
11
- Contractor(orange)
10
+ FTE green
11
+ Contractor orange
12
12
 
13
13
  Jane Smith
14
14
  role: CEO
@@ -8,8 +8,8 @@ top-right Major Projects
8
8
  bottom-left Fill-ins
9
9
  bottom-right Avoid
10
10
 
11
- Dark Mode (blue) 0.25 0.85
12
- API v2 (red) 0.8 0.9
11
+ Dark Mode blue 0.25 0.85
12
+ API v2 red 0.8 0.9
13
13
  Fix Typos 0.1 0.15
14
14
  SSO Integration 0.75 0.7
15
15
  Export CSV 0.3 0.6
@@ -1,22 +1,22 @@
1
1
  sankey Website Traffic Flow
2
2
 
3
3
  // Source channels — colored by type
4
- Organic Search (green)
4
+ Organic Search green
5
5
  Landing Page 450
6
- Paid Ads (orange)
6
+ Paid Ads orange
7
7
  Landing Page 280
8
- Social Media (blue)
8
+ Social Media blue
9
9
  Landing Page 180
10
- Email (purple)
10
+ Email purple
11
11
  Landing Page 120
12
- Direct (teal)
12
+ Direct teal
13
13
  Landing Page 90
14
14
 
15
15
  // Engagement & Conversion
16
16
  Landing Page
17
17
  Sign Up 340
18
18
  Browse Products 520
19
- Bounce 260 (red)
19
+ Bounce 260 red
20
20
 
21
21
  Sign Up
22
22
  Free Trial 240
@@ -24,8 +24,8 @@ Sign Up
24
24
 
25
25
  Browse Products
26
26
  Add to Cart 310
27
- Exit 210 (red)
27
+ Exit 210 red
28
28
 
29
29
  Add to Cart
30
- Purchase (green) 220
31
- Abandon 90 (red)
30
+ Purchase green 220
31
+ Abandon 90 red
@@ -2,20 +2,20 @@ scatter Startup Funding vs Revenue
2
2
  x-label Funding ($M)
3
3
  y-label Annual Revenue ($M)
4
4
 
5
- [SaaS](blue)
5
+ [SaaS] blue
6
6
  Acme Cloud 12 8.5
7
7
  DataSync 5.2 3.1
8
8
  CloudOps 25 18.4
9
9
  PlatformX 8 5.7
10
10
  NexGen 3.5 1.2
11
11
 
12
- [Fintech](green)
12
+ [Fintech] green
13
13
  PayFlow 45 32
14
14
  LendTech 18 12.5
15
15
  CoinBase+ 60 41
16
16
  QuickPay 9 6.8
17
17
 
18
- [HealthTech](red)
18
+ [HealthTech] red
19
19
  MediScan 15 7.2
20
20
  HealthAI 22 14.1
21
21
  CareLink 7 3.8
@@ -2,16 +2,16 @@ sequence Order Fulfillment — Protocols & Ownership
2
2
  active-tag Protocol
3
3
 
4
4
  tag Protocol as p
5
- REST(blue)
6
- gRPC(green)
7
- Async(orange)
8
- SQL(purple)
5
+ REST blue
6
+ gRPC green
7
+ Async orange
8
+ SQL purple
9
9
 
10
10
  tag Owner as o
11
- Checkout(teal)
12
- Fulfillment(orange)
13
- Payments(red)
14
- Data(blue)
11
+ Checkout teal
12
+ Fulfillment orange
13
+ Payments red
14
+ Data blue
15
15
 
16
16
  Buyer is an actor
17
17
  CheckoutSvc is a service | o: Checkout
@@ -2,15 +2,15 @@ sequence API Gateway — Infrastructure Concerns
2
2
  active-tag Concern
3
3
 
4
4
  tag Concern as c
5
- Caching(blue)
6
- Auth(green)
7
- RateLimiting(orange)
8
- BusinessLogic(purple) default
5
+ Caching blue
6
+ Auth green
7
+ RateLimiting orange
8
+ BusinessLogic purple default
9
9
 
10
10
  tag Team as t
11
- Platform(teal)
12
- Product(orange)
13
- Security(red)
11
+ Platform teal
12
+ Product orange
13
+ Security red
14
14
 
15
15
  Mobile is an actor
16
16
  Gateway is a gateway | t: Platform
@@ -2,15 +2,15 @@ sitemap Grand Slam Tickets
2
2
  direction-tb
3
3
 
4
4
  tag Auth
5
- Public(green)
6
- Required(blue)
7
- Admin(red)
5
+ Public green
6
+ Required blue
7
+ Admin red
8
8
 
9
9
  tag Type
10
- Landing(purple)
11
- Form(orange)
12
- Content(cyan)
13
- Transactional(yellow)
10
+ Landing purple
11
+ Form orange
12
+ Content cyan
13
+ Transactional yellow
14
14
 
15
15
  Home
16
16
  Auth: Public
@@ -2,8 +2,8 @@ slope Programming Language Popularity
2
2
 
3
3
  period 2020 2022 2025
4
4
 
5
- Python (blue) 3 1 1
6
- JavaScript (yellow) 1 2 2
7
- TypeScript (cyan) 7 4 3
8
- Rust (orange) 18 12 5
9
- Go (green) 10 8 7
5
+ Python blue 3 1 1
6
+ JavaScript yellow 1 2 2
7
+ TypeScript cyan 7 4 3
8
+ Rust orange 18 12 5
9
+ Go green 10 8 7
@@ -1,15 +1,15 @@
1
1
  line U.S. Strategic Petroleum Reserve
2
2
  y-label Million Barrels
3
3
 
4
- era '77 -> '81 Carter (blue)
5
- era '81 -> '89 Reagan (red)
6
- era '89 -> '93 Bush (red)
7
- era '93 -> '01 Clinton (blue)
8
- era '01 -> '09 Bush (red)
9
- era '09 -> '17 Obama (blue)
10
- era '17 -> '21 Trump (red)
11
- era '21 -> '25 Biden (blue)
12
- era '25 -> '25 Trump (red)
4
+ era '77 -> '81 Carter blue
5
+ era '81 -> '89 Reagan red
6
+ era '89 -> '93 Bush red
7
+ era '93 -> '01 Clinton blue
8
+ era '01 -> '09 Bush red
9
+ era '09 -> '17 Obama blue
10
+ era '17 -> '21 Trump red
11
+ era '21 -> '25 Biden blue
12
+ era '25 -> '25 Trump red
13
13
 
14
14
  '77 7
15
15
  '78 67
@@ -2,9 +2,9 @@ timeline Product Roadmap 2024-2025
2
2
  sort tag:Team
3
3
 
4
4
  tag Team as t
5
- Engineering(blue)
6
- Design(purple)
7
- Product(green)
5
+ Engineering blue
6
+ Design purple
7
+ Product green
8
8
 
9
9
  era 2024-01 -> 2024-06 Phase 1 - Foundation
10
10
  era 2024-07 -> 2024-12 Phase 2 - Growth
@@ -1,8 +1,8 @@
1
1
  venn Full-Stack Developer Skills
2
2
 
3
- Frontend(blue) as fe
4
- Backend(green) as be
5
- DevOps(orange) as de
3
+ Frontend blue as fe
4
+ Backend green as be
5
+ DevOps orange as de
6
6
 
7
7
  fe + be Web Systems
8
8
  be + de Platform Ops
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@diagrammo/dgmo",
3
- "version": "0.15.1",
3
+ "version": "0.16.0",
4
4
  "description": "DGMO diagram markup language — parser, renderer, and color system",
5
5
  "license": "MIT",
6
6
  "repository": {
package/src/advanced.ts CHANGED
@@ -28,7 +28,6 @@ export type { DgmoError, DgmoSeverity } from './diagnostics';
28
28
  export {
29
29
  parseInArrowLabel,
30
30
  validateLabelCharacters,
31
- matchColorParens,
32
31
  ARROW_DIAGNOSTIC_CODES,
33
32
  } from './utils/arrows';
34
33
  export type { ParseInArrowLabelResult } from './utils/arrows';
@@ -309,6 +309,7 @@ interface BLRenderOptions {
309
309
  controlsExpanded?: boolean;
310
310
  onToggleDescriptions?: (active: boolean) => void;
311
311
  onToggleControlsExpand?: () => void;
312
+ exportMode?: boolean;
312
313
  }
313
314
 
314
315
  export function renderBoxesAndLines(
@@ -328,6 +329,7 @@ export function renderBoxesAndLines(
328
329
  controlsExpanded,
329
330
  onToggleDescriptions,
330
331
  onToggleControlsExpand,
332
+ exportMode = false,
331
333
  } = options ?? {};
332
334
  d3Selection.select(container).selectAll(':not([data-d3-tooltip])').remove();
333
335
 
@@ -974,7 +976,7 @@ export function renderBoxesAndLines(
974
976
  const legendConfig: LegendConfig = {
975
977
  groups: parsed.tagGroups,
976
978
  position: { placement: 'top-center', titleRelation: 'below-title' },
977
- mode: 'fixed',
979
+ mode: exportMode ? 'export' : 'preview',
978
980
  controlsGroup,
979
981
  };
980
982
  const legendState: LegendState = {
@@ -1017,11 +1019,13 @@ export function renderBoxesAndLinesForExport(
1017
1019
  exportDims?: { width: number; height: number };
1018
1020
  activeTagGroup?: string | null;
1019
1021
  hiddenTagValues?: Map<string, Set<string>>;
1022
+ exportMode?: boolean;
1020
1023
  }
1021
1024
  ): void {
1022
1025
  renderBoxesAndLines(container, parsed, layout, palette, isDark, {
1023
1026
  exportDims: options?.exportDims,
1024
1027
  activeTagGroup: options?.activeTagGroup,
1025
1028
  hiddenTagValues: options?.hiddenTagValues,
1029
+ exportMode: options?.exportMode,
1026
1030
  });
1027
1031
  }
package/src/c4/parser.ts CHANGED
@@ -350,7 +350,7 @@ export function parseC4(content: string, palette?: PaletteColors): ParsedC4 {
350
350
  if (!color) {
351
351
  pushError(
352
352
  lineNumber,
353
- `Expected 'Value(color)' in tag group '${currentTagGroup.name}'`
353
+ `Expected 'Value color' in tag group '${currentTagGroup.name}'`
354
354
  );
355
355
  continue;
356
356
  }
@@ -232,7 +232,8 @@ export function renderC4Context(
232
232
  isDark: boolean,
233
233
  onClickItem?: (lineNumber: number) => void,
234
234
  exportDims?: { width?: number; height?: number },
235
- activeTagGroup?: string | null
235
+ activeTagGroup?: string | null,
236
+ exportMode?: boolean
236
237
  ): void {
237
238
  d3Selection.select(container).selectAll(':not([data-d3-tooltip])').remove();
238
239
 
@@ -636,7 +637,8 @@ export function renderC4Context(
636
637
  palette,
637
638
  isDark,
638
639
  activeTagGroup,
639
- fixedLegend ? width : null
640
+ fixedLegend ? width : null,
641
+ exportMode
640
642
  );
641
643
  }
642
644
  }
@@ -1259,7 +1261,8 @@ function renderLegend(
1259
1261
  palette: PaletteColors,
1260
1262
  isDark: boolean,
1261
1263
  activeTagGroup?: string | null,
1262
- fixedWidth?: number | null
1264
+ fixedWidth?: number | null,
1265
+ exportMode?: boolean
1263
1266
  ): void {
1264
1267
  const groups = layout.legend.map((g) => ({
1265
1268
  name: g.name,
@@ -1268,7 +1271,7 @@ function renderLegend(
1268
1271
  const legendConfig: LegendConfig = {
1269
1272
  groups,
1270
1273
  position: { placement: 'top-center', titleRelation: 'below-title' },
1271
- mode: 'fixed',
1274
+ mode: exportMode ? 'export' : 'preview',
1272
1275
  };
1273
1276
  const legendState: LegendState = { activeGroup: activeTagGroup ?? null };
1274
1277
  const containerWidth = fixedWidth ?? layout.width;
@@ -1300,7 +1303,8 @@ export function renderC4Containers(
1300
1303
  isDark: boolean,
1301
1304
  onClickItem?: (lineNumber: number) => void,
1302
1305
  exportDims?: { width?: number; height?: number },
1303
- activeTagGroup?: string | null
1306
+ activeTagGroup?: string | null,
1307
+ exportMode?: boolean
1304
1308
  ): void {
1305
1309
  d3Selection.select(container).selectAll(':not([data-d3-tooltip])').remove();
1306
1310
 
@@ -1805,7 +1809,8 @@ export function renderC4Containers(
1805
1809
  palette,
1806
1810
  isDark,
1807
1811
  activeTagGroup,
1808
- fixedLegend ? width : null
1812
+ fixedLegend ? width : null,
1813
+ exportMode
1809
1814
  );
1810
1815
  }
1811
1816
  }
@@ -1933,7 +1938,8 @@ export function renderC4Deployment(
1933
1938
  isDark: boolean,
1934
1939
  onClickItem?: (lineNumber: number) => void,
1935
1940
  exportDims?: { width?: number; height?: number },
1936
- activeTagGroup?: string | null
1941
+ activeTagGroup?: string | null,
1942
+ exportMode?: boolean
1937
1943
  ): void {
1938
1944
  renderC4Containers(
1939
1945
  container,
@@ -1943,7 +1949,8 @@ export function renderC4Deployment(
1943
1949
  isDark,
1944
1950
  onClickItem,
1945
1951
  exportDims,
1946
- activeTagGroup
1952
+ activeTagGroup,
1953
+ exportMode
1947
1954
  );
1948
1955
  }
1949
1956
 
package/src/chart.ts CHANGED
@@ -63,7 +63,7 @@ export interface ParsedChart {
63
63
  // Colors
64
64
  // ============================================================
65
65
 
66
- import { resolveColorWithDiagnostic } from './colors';
66
+ import { resolveColorWithDiagnostic, RECOGNIZED_COLOR_NAMES } from './colors';
67
67
  import type { PaletteColors } from './palettes';
68
68
  import { makeDgmoError, formatDgmoError, suggest } from './diagnostics';
69
69
  import {
@@ -212,21 +212,30 @@ export function parseChart(
212
212
  // Fall through — first line might be a data row or option
213
213
  }
214
214
 
215
- // Era line: era Day 1 -> Day 3 Rough Seas (blue) — colon-free
216
- const eraMatch = trimmed.match(
217
- /^era\s+(.+?)\s*->\s*(.+?)(?:\s*\(([^)]+)\))?\s*$/
218
- );
215
+ // Era line 1.5 trailing-token):
216
+ // `era Day 1 -> Day 3 Rough Seas` (no color)
217
+ // `era Day 1 -> Day 3 Rough Seas blue` (trailing color word)
218
+ // Color (if any) is the last whitespace-delimited token of the label.
219
+ const eraMatch = trimmed.match(/^era\s+(.+?)\s*->\s*(.+?)\s*$/);
219
220
  if (eraMatch) {
220
- // Store start and raw afterArrow — resolved against data labels after parsing
221
221
  const afterArrow = eraMatch[2].trim();
222
222
  const spaceIdx = afterArrow.indexOf(' ');
223
223
  if (spaceIdx >= 0) {
224
+ // Peel trailing-token color off the after-arrow label region.
225
+ const lastSpaceIdx = afterArrow.lastIndexOf(' ');
226
+ const trailing = afterArrow.substring(lastSpaceIdx + 1);
227
+ const hasColor = RECOGNIZED_COLOR_NAMES.includes(
228
+ trailing as (typeof RECOGNIZED_COLOR_NAMES)[number]
229
+ );
230
+ const labelPart = hasColor
231
+ ? afterArrow.substring(0, lastSpaceIdx).trimEnd()
232
+ : afterArrow;
224
233
  rawEras.push({
225
234
  start: eraMatch[1].trim(),
226
- afterArrow,
227
- color: eraMatch[3]
235
+ afterArrow: labelPart,
236
+ color: hasColor
228
237
  ? (resolveColorWithDiagnostic(
229
- eraMatch[3].trim(),
238
+ trailing,
230
239
  lineNumber,
231
240
  result.diagnostics,
232
241
  palette
@@ -35,10 +35,11 @@ function classId(name: string): string {
35
35
  // Regex patterns
36
36
  // ============================================================
37
37
 
38
- // Class declaration: [modifier] ClassName [extends Parent] [implements Interface] (color)
39
- // Multi-word names allowed (`Customer Service`); quote with `"name"` if name
40
- // contains reserved chars. ClassName must start with uppercase to keep the
41
- // convention. Captures (positional):
38
+ // Class declaration: [modifier] ClassName [extends Parent] [implements Interface] [color] [as alias]
39
+ // Color is the universal §1.5 trailing-token form (a bare lowercase palette
40
+ // color word after structural slots). Multi-word names allowed
41
+ // (`Customer Service`); quote with `"name"` if name contains reserved chars.
42
+ // ClassName must start with uppercase. Captures (positional):
42
43
  // 1: modifier (abstract|interface|enum) | undefined
43
44
  // 2: quotedClassName | undefined
44
45
  // 3: bareClassName | undefined
@@ -47,10 +48,10 @@ function classId(name: string): string {
47
48
  // 6: quotedImplements | undefined
48
49
  // 7: bareImplements | undefined
49
50
  // 8: legacy bracket modifier | undefined
50
- // 9: color | undefined
51
+ // 9: color (trailing token; recognized palette word) | undefined
51
52
  // 10: alias literal (TD-18) | undefined
52
53
  const CLASS_DECL_RE =
53
- /^(?:(abstract|interface|enum)\s+)?(?:"([^"]+)"|([A-Z][^":]*?))(?:\s+extends\s+(?:"([^"]+)"|([A-Z][^":]*?)))?(?:\s+implements\s+(?:"([^"]+)"|([A-Z][^":]*?)))?(?:\s+\[(abstract|interface|enum)\])?(?:\s+\(([^)]+)\))?(?:\s+as\s+([A-Za-z][A-Za-z0-9_]{0,11}))?\s*$/;
54
+ /^(?:(abstract|interface|enum)\s+)?(?:"([^"]+)"|([A-Z][^":]*?))(?:\s+extends\s+(?:"([^"]+)"|([A-Z][^":]*?)))?(?:\s+implements\s+(?:"([^"]+)"|([A-Z][^":]*?)))?(?:\s+\[(abstract|interface|enum)\])?(?:\s+(red|orange|yellow|green|blue|purple|teal|cyan|gray|black|white))?(?:\s+as\s+([A-Za-z][A-Za-z0-9_]{0,11}))?\s*$/;
54
55
 
55
56
  // Relationship — arrow syntax (indented under source class).
56
57
  // Arrows: --|> ..|> *-- o-- ..> ->
@@ -192,7 +192,8 @@ export function renderClassDiagram(
192
192
  isDark: boolean,
193
193
  onClickItem?: (lineNumber: number) => void,
194
194
  exportDims?: { width?: number; height?: number },
195
- legendActive?: boolean | null
195
+ legendActive?: boolean | null,
196
+ exportMode?: boolean
196
197
  ): void {
197
198
  d3Selection.select(container).selectAll(':not([data-d3-tooltip])').remove();
198
199
 
@@ -376,7 +377,7 @@ export function renderClassDiagram(
376
377
  const legendConfig: LegendConfig = {
377
378
  groups: legendGroups,
378
379
  position: { placement: 'top-center', titleRelation: 'below-title' },
379
- mode: 'fixed',
380
+ mode: exportMode ? 'export' : 'preview',
380
381
  };
381
382
  const legendState: LegendState = {
382
383
  activeGroup: isLegendExpanded ? LEGEND_GROUP_NAME : null,
@@ -697,10 +698,20 @@ export function renderClassDiagramForExport(
697
698
  legendReserve;
698
699
 
699
700
  return runInExportContainer(exportWidth, exportHeight, (container) => {
700
- renderClassDiagram(container, parsed, layout, palette, isDark, undefined, {
701
- width: exportWidth,
702
- height: exportHeight,
703
- });
701
+ renderClassDiagram(
702
+ container,
703
+ parsed,
704
+ layout,
705
+ palette,
706
+ isDark,
707
+ undefined,
708
+ {
709
+ width: exportWidth,
710
+ height: exportHeight,
711
+ },
712
+ true, // legendActive for export
713
+ true // exportMode
714
+ );
704
715
  return extractExportSvg(container, theme);
705
716
  });
706
717
  }
package/src/cli.ts CHANGED
@@ -179,8 +179,8 @@ palette: catppuccin // override palette
179
179
  // This is a comment (only // syntax — not #)
180
180
  \`\`\`
181
181
 
182
- Inline colors on most elements: append \`(colorname)\` — e.g. \`North(red): 850\`, \`[Process(blue)]\`.
183
- Named colors: \`red\`, \`orange\`, \`yellow\`, \`green\`, \`blue\`, \`purple\`, \`teal\`, \`cyan\`, \`gray\`.
182
+ Inline colors on most elements: append the color name as the trailing token — e.g. \`North red 850\`, \`[Process] blue\`. To use a color word as a literal label, capitalize it (\`Red\` stays as the word Red).
183
+ Named colors: \`red\`, \`orange\`, \`yellow\`, \`green\`, \`blue\`, \`purple\`, \`teal\`, \`cyan\`, \`gray\`, \`black\`, \`white\`.
184
184
 
185
185
  ### sequence (most commonly used)
186
186
 
@@ -234,7 +234,7 @@ North: 850
234
234
  South: 620
235
235
 
236
236
  // line (multi-series)
237
- series: Sales(red), Costs(blue)
237
+ series: Sales red, Costs blue
238
238
  Q1: 100, 50
239
239
  Q2: 120, 55
240
240
 
@@ -295,7 +295,7 @@ API
295
295
  async A -> B: msg ❌ use A ~msg~> B
296
296
  A <- B ❌ left-pointing arrows removed — use B -> A
297
297
  parallel else ❌ not supported — use separate parallel blocks
298
- == Foo(#ff0000) == ❌ hex colors not supported — use named colors: == Foo(red) ==
298
+ == Foo #ff0000 == ❌ hex colors not supported — use named colors: == Foo red ==
299
299
  A -routes to /api-> B ❌ -> inside a label is ambiguous — rephrase the label
300
300
  end ❌ not needed — indentation closes blocks in sequence diagrams
301
301
  \`\`\`
@@ -427,8 +427,8 @@ bar, line, multi-line, area, pie, doughnut, radar, polar-area, bar-stacked, scat
427
427
 
428
428
  - First line: chart type keyword (e.g. \`sequence\`, \`flowchart\`, \`bar\`), optionally followed by a title (\`bar Revenue\`)
429
429
  - \`// comment\` — only \`//\` comments (not \`#\`)
430
- - \`(colorname)\` — inline colors on data series, tag values, kanban columns: \`Label(red) 100\`
431
- - \`series A(red), B(blue)\` — multi-series with colors
430
+ - Trailing color name — inline colors on data series, tag values, kanban columns: \`Label red 100\`
431
+ - \`series A red, B blue\` — multi-series with colors
432
432
 
433
433
  ## Rendering via CLI
434
434
 
package/src/completion.ts CHANGED
@@ -17,6 +17,11 @@ import { extractSymbols as extractInfraSymbols } from './infra/parser';
17
17
  import { extractSymbols as extractClassSymbols } from './class/parser';
18
18
  import { extractPertSymbols } from './pert/parser';
19
19
  import { parseFirstLine, ALL_CHART_TYPES } from './utils/parsing';
20
+ import { RECOGNIZED_COLOR_NAMES } from './colors';
21
+
22
+ const RECOGNIZED_COLOR_SET: ReadonlySet<string> = new Set(
23
+ RECOGNIZED_COLOR_NAMES
24
+ );
20
25
  // Read chart-type descriptions directly from the source-of-truth data
21
26
  // module instead of via dgmo-router.ts. dgmo-router imports every
22
27
  // parser, and the parsers (Class/ER/Infra/Pert/Flowchart) type-only
@@ -968,10 +973,15 @@ export function extractTagDeclarations(docText: string): Map<string, string[]> {
968
973
  (raw[0] === ' ' || raw[0] === '\t')
969
974
  ) {
970
975
  if (trimmed && !trimmed.startsWith('//')) {
971
- // Strip color annotation: Frontend(blue) → Frontend
972
- const colorIdx = trimmed.indexOf('(');
976
+ // Strip trailing-token color (§1.5): `Frontend blue``Frontend`.
977
+ // Whitespace-split; if the last token is a recognized color word,
978
+ // drop it; otherwise the whole trimmed string is the value.
979
+ const lastSpaceIdx = trimmed.lastIndexOf(' ');
973
980
  const value =
974
- colorIdx > 0 ? trimmed.substring(0, colorIdx).trim() : trimmed;
981
+ lastSpaceIdx > 0 &&
982
+ RECOGNIZED_COLOR_SET.has(trimmed.substring(lastSpaceIdx + 1))
983
+ ? trimmed.substring(0, lastSpaceIdx).trim()
984
+ : trimmed;
975
985
  if (value) currentValues.push(value);
976
986
  }
977
987
  continue;