@diagrammo/dgmo 0.6.0 → 0.6.1

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@diagrammo/dgmo",
3
- "version": "0.6.0",
3
+ "version": "0.6.1",
4
4
  "description": "DGMO diagram markup language — parser, renderer, and color system",
5
5
  "license": "MIT",
6
6
  "type": "module",
package/src/c4/layout.ts CHANGED
@@ -603,19 +603,12 @@ export function computeC4NodeDimensions(
603
603
  // Legend Helpers
604
604
  // ============================================================
605
605
 
606
- function computeLegendGroups(
607
- tagGroups: OrgTagGroup[],
608
- usedValuesByGroup?: Map<string, Set<string>>
609
- ): C4LegendGroup[] {
606
+ function computeLegendGroups(tagGroups: OrgTagGroup[]): C4LegendGroup[] {
610
607
  const result: C4LegendGroup[] = [];
611
608
 
612
609
  for (const group of tagGroups) {
613
610
  const entries: C4LegendEntry[] = [];
614
611
  for (const entry of group.entries) {
615
- if (usedValuesByGroup) {
616
- const used = usedValuesByGroup.get(group.name.toLowerCase());
617
- if (!used?.has(entry.value.toLowerCase())) continue;
618
- }
619
612
  entries.push({ value: entry.value, color: entry.color });
620
613
  }
621
614
  if (entries.length === 0) continue;
@@ -813,20 +806,9 @@ export function layoutC4Context(
813
806
  let totalWidth = nodes.length > 0 ? maxX - minX + MARGIN * 2 : 0;
814
807
  let totalHeight = nodes.length > 0 ? maxY - minY + MARGIN * 2 : 0;
815
808
 
816
- // Legend
817
- const usedValuesByGroup = new Map<string, Set<string>>();
818
- for (const el of contextElements) {
819
- for (const group of parsed.tagGroups) {
820
- const key = group.name.toLowerCase();
821
- const val = el.metadata[key];
822
- if (val) {
823
- if (!usedValuesByGroup.has(key)) usedValuesByGroup.set(key, new Set());
824
- usedValuesByGroup.get(key)!.add(val.toLowerCase());
825
- }
826
- }
827
- }
828
-
829
- const legendGroups = computeLegendGroups(parsed.tagGroups, usedValuesByGroup);
809
+ // Legend: show all defined tag groups and entries so users see the full
810
+ // tag vocabulary regardless of which elements are visible at this view level.
811
+ const legendGroups = computeLegendGroups(parsed.tagGroups);
830
812
 
831
813
  // Position legend below diagram
832
814
  if (legendGroups.length > 0) {
@@ -1236,20 +1218,7 @@ export function layoutC4Containers(
1236
1218
  let totalWidth = maxX - minX + MARGIN * 2;
1237
1219
  let totalHeight = maxY - minY + MARGIN * 2;
1238
1220
 
1239
- // Legend
1240
- const usedValuesByGroup = new Map<string, Set<string>>();
1241
- for (const el of [...containers, ...externals]) {
1242
- for (const group of parsed.tagGroups) {
1243
- const key = group.name.toLowerCase();
1244
- const val = el.metadata[key];
1245
- if (val) {
1246
- if (!usedValuesByGroup.has(key)) usedValuesByGroup.set(key, new Set());
1247
- usedValuesByGroup.get(key)!.add(val.toLowerCase());
1248
- }
1249
- }
1250
- }
1251
-
1252
- const legendGroups = computeLegendGroups(parsed.tagGroups, usedValuesByGroup);
1221
+ const legendGroups = computeLegendGroups(parsed.tagGroups);
1253
1222
 
1254
1223
  // Position legend below diagram
1255
1224
  if (legendGroups.length > 0) {
@@ -1722,24 +1691,8 @@ export function layoutC4Components(
1722
1691
  let totalWidth = maxX - minX + MARGIN * 2;
1723
1692
  let totalHeight = maxY - minY + MARGIN * 2;
1724
1693
 
1725
- // Legend
1726
- const usedValuesByGroup = new Map<string, Set<string>>();
1727
- for (const el of [...components, ...externals]) {
1728
- for (const group of parsed.tagGroups) {
1729
- const key = group.name.toLowerCase();
1730
- // Check element + ancestors for inherited values
1731
- let val = el.metadata[key];
1732
- if (!val && components.includes(el)) {
1733
- val = targetContainer.metadata[key] ?? system.metadata[key];
1734
- }
1735
- if (val) {
1736
- if (!usedValuesByGroup.has(key)) usedValuesByGroup.set(key, new Set());
1737
- usedValuesByGroup.get(key)!.add(val.toLowerCase());
1738
- }
1739
- }
1740
- }
1741
1694
 
1742
- const legendGroups = computeLegendGroups(parsed.tagGroups, usedValuesByGroup);
1695
+ const legendGroups = computeLegendGroups(parsed.tagGroups);
1743
1696
 
1744
1697
  // Position legend below diagram
1745
1698
  if (legendGroups.length > 0) {
@@ -2104,20 +2057,7 @@ export function layoutC4Deployment(
2104
2057
  let totalWidth = maxX - minX + MARGIN * 2;
2105
2058
  let totalHeight = maxY - minY + MARGIN * 2;
2106
2059
 
2107
- // Legend
2108
- const usedValuesByGroup = new Map<string, Set<string>>();
2109
- for (const r of refEntries) {
2110
- for (const group of parsed.tagGroups) {
2111
- const key = group.name.toLowerCase();
2112
- const val = r.element.metadata[key];
2113
- if (val) {
2114
- if (!usedValuesByGroup.has(key)) usedValuesByGroup.set(key, new Set());
2115
- usedValuesByGroup.get(key)!.add(val.toLowerCase());
2116
- }
2117
- }
2118
- }
2119
-
2120
- const legendGroups = computeLegendGroups(parsed.tagGroups, usedValuesByGroup);
2060
+ const legendGroups = computeLegendGroups(parsed.tagGroups);
2121
2061
 
2122
2062
  if (legendGroups.length > 0) {
2123
2063
  const legendY = totalHeight + MARGIN;
@@ -10,9 +10,22 @@ import { mix } from '../palettes/color-utils';
10
10
  import { renderInlineText } from '../utils/inline-markdown';
11
11
  import type { ParsedC4 } from './types';
12
12
  import type { C4Shape } from './types';
13
- import type { C4LayoutResult, C4LayoutNode, C4LayoutEdge, C4LayoutBoundary } from './layout';
13
+ import type { C4LayoutResult, C4LayoutNode, C4LayoutEdge, C4LayoutBoundary, C4LegendGroup } from './layout';
14
14
  import { parseC4 } from './parser';
15
15
  import { layoutC4Context, layoutC4Containers, layoutC4Components, layoutC4Deployment, collectCardMetadata } from './layout';
16
+ import {
17
+ LEGEND_HEIGHT,
18
+ LEGEND_PILL_FONT_SIZE,
19
+ LEGEND_PILL_FONT_W,
20
+ LEGEND_PILL_PAD,
21
+ LEGEND_DOT_R,
22
+ LEGEND_ENTRY_FONT_SIZE,
23
+ LEGEND_ENTRY_FONT_W,
24
+ LEGEND_ENTRY_DOT_GAP,
25
+ LEGEND_ENTRY_TRAIL,
26
+ LEGEND_CAPSULE_PAD,
27
+ LEGEND_GROUP_GAP,
28
+ } from '../utils/legend-constants';
16
29
 
17
30
  // ============================================================
18
31
  // Constants
@@ -59,16 +72,6 @@ const PERSON_ICON_W = PERSON_ARM_SPAN * 2; // total width including arms
59
72
  const PERSON_SW = 1.5;
60
73
 
61
74
  // Legend constants (match org)
62
- const LEGEND_HEIGHT = 28;
63
- const LEGEND_PILL_FONT_SIZE = 11;
64
- const LEGEND_PILL_FONT_W = LEGEND_PILL_FONT_SIZE * 0.6;
65
- const LEGEND_PILL_PAD = 16;
66
- const LEGEND_DOT_R = 4;
67
- const LEGEND_ENTRY_FONT_SIZE = 10;
68
- const LEGEND_ENTRY_FONT_W = LEGEND_ENTRY_FONT_SIZE * 0.6;
69
- const LEGEND_ENTRY_DOT_GAP = 4;
70
- const LEGEND_ENTRY_TRAIL = 8;
71
- const LEGEND_CAPSULE_PAD = 4;
72
75
 
73
76
  // ============================================================
74
77
  // Color helpers
@@ -239,8 +242,16 @@ export function renderC4Context(
239
242
 
240
243
  const titleHeight = parsed.title ? TITLE_HEIGHT + 10 : 0;
241
244
  const diagramW = layout.width;
242
- const diagramH = layout.height;
243
- const availH = height - titleHeight;
245
+ const hasLegend = layout.legend.length > 0;
246
+ // In app mode, legend is a fixed overlay outside the scaled group.
247
+ // C4 layout adds MARGIN(40) + LEGEND_HEIGHT below content — remove that from diagramH.
248
+ const C4_LAYOUT_MARGIN = 40;
249
+ const LEGEND_FIXED_GAP = 8;
250
+ const fixedLegend = !exportDims && hasLegend;
251
+ const legendLayoutSpace = C4_LAYOUT_MARGIN + LEGEND_HEIGHT;
252
+ const legendReserveH = fixedLegend ? LEGEND_HEIGHT + LEGEND_FIXED_GAP : 0;
253
+ const diagramH = fixedLegend ? layout.height - legendLayoutSpace : layout.height;
254
+ const availH = height - titleHeight - legendReserveH;
244
255
  const scaleX = (width - DIAGRAM_PADDING * 2) / diagramW;
245
256
  const scaleY = (availH - DIAGRAM_PADDING * 2) / diagramH;
246
257
  const scale = Math.min(MAX_SCALE, scaleX, scaleY);
@@ -428,6 +439,23 @@ export function renderC4Context(
428
439
  .attr('data-line-number', String(node.lineNumber))
429
440
  .attr('data-node-id', node.id);
430
441
 
442
+ if (activeTagGroup) {
443
+ const tagKey = activeTagGroup.toLowerCase();
444
+ const tagValue = node.metadata[tagKey];
445
+ if (tagValue) {
446
+ nodeG.attr(`data-tag-${tagKey}`, tagValue.toLowerCase());
447
+ } else {
448
+ // Fall back to the group's defaultValue so hover-dimming works for
449
+ // nodes that inherit the default (e.g. sc: Internal default).
450
+ const tagGroup = parsed.tagGroups.find(
451
+ (g) => g.name.toLowerCase() === tagKey || g.alias?.toLowerCase() === tagKey
452
+ );
453
+ if (tagGroup?.defaultValue) {
454
+ nodeG.attr(`data-tag-${tagKey}`, tagGroup.defaultValue.toLowerCase());
455
+ }
456
+ }
457
+ }
458
+
431
459
  if (node.importPath) {
432
460
  nodeG.attr('data-import-path', node.importPath);
433
461
  }
@@ -566,101 +594,18 @@ export function renderC4Context(
566
594
  }
567
595
 
568
596
  // ── Legend ──
569
- if (!exportDims) {
570
- for (const group of layout.legend) {
571
- const isActive =
572
- activeTagGroup != null &&
573
- group.name.toLowerCase() === (activeTagGroup ?? '').toLowerCase();
574
-
575
- if (activeTagGroup != null && !isActive) continue;
576
-
577
- const groupBg = isDark
578
- ? mix(palette.surface, palette.bg, 50)
579
- : mix(palette.surface, palette.bg, 30);
580
-
581
- const pillLabel = group.name;
582
- const pillWidth = pillLabel.length * LEGEND_PILL_FONT_W + LEGEND_PILL_PAD;
583
-
584
- const gEl = contentG
585
- .append('g')
586
- .attr('transform', `translate(${group.x}, ${group.y})`)
587
- .attr('class', 'c4-legend-group')
588
- .attr('data-legend-group', group.name.toLowerCase())
589
- .style('cursor', 'pointer');
590
-
591
- if (isActive) {
592
- gEl
593
- .append('rect')
594
- .attr('width', group.width)
595
- .attr('height', LEGEND_HEIGHT)
596
- .attr('rx', LEGEND_HEIGHT / 2)
597
- .attr('fill', groupBg);
598
- }
599
-
600
- const pillX = isActive ? LEGEND_CAPSULE_PAD : 0;
601
- const pillY = isActive ? LEGEND_CAPSULE_PAD : 0;
602
- const pillH = LEGEND_HEIGHT - (isActive ? LEGEND_CAPSULE_PAD * 2 : 0);
603
-
604
- gEl
605
- .append('rect')
606
- .attr('x', pillX)
607
- .attr('y', pillY)
608
- .attr('width', pillWidth)
609
- .attr('height', pillH)
610
- .attr('rx', pillH / 2)
611
- .attr('fill', isActive ? palette.bg : groupBg);
612
-
613
- if (isActive) {
614
- gEl
615
- .append('rect')
616
- .attr('x', pillX)
617
- .attr('y', pillY)
618
- .attr('width', pillWidth)
619
- .attr('height', pillH)
620
- .attr('rx', pillH / 2)
621
- .attr('fill', 'none')
622
- .attr('stroke', mix(palette.textMuted, palette.bg, 50))
623
- .attr('stroke-width', 0.75);
624
- }
625
-
626
- gEl
627
- .append('text')
628
- .attr('x', pillX + pillWidth / 2)
629
- .attr('y', LEGEND_HEIGHT / 2 + LEGEND_PILL_FONT_SIZE / 2 - 2)
630
- .attr('font-size', LEGEND_PILL_FONT_SIZE)
631
- .attr('font-weight', '500')
632
- .attr('fill', isActive ? palette.text : palette.textMuted)
633
- .attr('text-anchor', 'middle')
634
- .text(pillLabel);
635
-
636
- if (isActive) {
637
- let entryX = pillX + pillWidth + 4;
638
- for (const entry of group.entries) {
639
- const entryG = gEl
640
- .append('g')
641
- .attr('data-legend-entry', entry.value.toLowerCase())
642
- .style('cursor', 'pointer');
643
-
644
- entryG
645
- .append('circle')
646
- .attr('cx', entryX + LEGEND_DOT_R)
647
- .attr('cy', LEGEND_HEIGHT / 2)
648
- .attr('r', LEGEND_DOT_R)
649
- .attr('fill', entry.color);
650
-
651
- const textX = entryX + LEGEND_DOT_R * 2 + LEGEND_ENTRY_DOT_GAP;
652
- entryG
653
- .append('text')
654
- .attr('x', textX)
655
- .attr('y', LEGEND_HEIGHT / 2 + LEGEND_ENTRY_FONT_SIZE / 2 - 1)
656
- .attr('font-size', LEGEND_ENTRY_FONT_SIZE)
657
- .attr('fill', palette.textMuted)
658
- .text(entry.value);
659
-
660
- entryX = textX + entry.value.length * LEGEND_ENTRY_FONT_W + LEGEND_ENTRY_TRAIL;
661
- }
662
- }
597
+ if (hasLegend) {
598
+ // App mode: fixed overlay at SVG bottom so it's always readable regardless of scale.
599
+ // Export mode: render inside scaled contentG at layout coordinates.
600
+ const legendParent = fixedLegend
601
+ ? svg.append('g')
602
+ .attr('class', 'c4-legend-fixed')
603
+ .attr('transform', `translate(0, ${height - DIAGRAM_PADDING - LEGEND_HEIGHT})`)
604
+ : contentG.append('g').attr('class', 'c4-legend');
605
+ if (activeTagGroup) {
606
+ legendParent.attr('data-legend-active', activeTagGroup.toLowerCase());
663
607
  }
608
+ renderLegend(legendParent as GSelection, layout, palette, isDark, activeTagGroup, fixedLegend ? width : null);
664
609
  }
665
610
  }
666
611
 
@@ -1189,29 +1134,52 @@ function placeEdgeLabels(
1189
1134
  }
1190
1135
 
1191
1136
  function renderLegend(
1192
- contentG: GSelection,
1137
+ parent: GSelection,
1193
1138
  layout: C4LayoutResult,
1194
1139
  palette: PaletteColors,
1195
1140
  isDark: boolean,
1196
- activeTagGroup?: string | null
1141
+ activeTagGroup?: string | null,
1142
+ /** When set, center groups horizontally across this width (fixed overlay mode). */
1143
+ fixedWidth?: number | null
1197
1144
  ): void {
1198
- for (const group of layout.legend) {
1145
+ const visibleGroups = activeTagGroup != null
1146
+ ? layout.legend.filter((g) => g.name.toLowerCase() === (activeTagGroup ?? '').toLowerCase())
1147
+ : layout.legend;
1148
+
1149
+ const pillWidthOf = (g: C4LegendGroup) => g.name.length * LEGEND_PILL_FONT_W + LEGEND_PILL_PAD;
1150
+ const effectiveW = (g: C4LegendGroup) => activeTagGroup != null ? g.width : pillWidthOf(g);
1151
+
1152
+ // In fixed mode, compute centered x-positions
1153
+ let fixedPositions: Map<string, number> | null = null;
1154
+ if (fixedWidth != null && visibleGroups.length > 0) {
1155
+ fixedPositions = new Map();
1156
+ const totalW = visibleGroups.reduce((s, g) => s + effectiveW(g), 0)
1157
+ + (visibleGroups.length - 1) * LEGEND_GROUP_GAP;
1158
+ let cx = Math.max(DIAGRAM_PADDING, (fixedWidth - totalW) / 2);
1159
+ for (const g of visibleGroups) {
1160
+ fixedPositions.set(g.name, cx);
1161
+ cx += effectiveW(g) + LEGEND_GROUP_GAP;
1162
+ }
1163
+ }
1164
+
1165
+ for (const group of visibleGroups) {
1199
1166
  const isActive =
1200
1167
  activeTagGroup != null &&
1201
1168
  group.name.toLowerCase() === (activeTagGroup ?? '').toLowerCase();
1202
1169
 
1203
- if (activeTagGroup != null && !isActive) continue;
1204
-
1205
1170
  const groupBg = isDark
1206
1171
  ? mix(palette.surface, palette.bg, 50)
1207
1172
  : mix(palette.surface, palette.bg, 30);
1208
1173
 
1209
1174
  const pillLabel = group.name;
1210
- const pillWidth = pillLabel.length * LEGEND_PILL_FONT_W + LEGEND_PILL_PAD;
1175
+ const pillWidth = pillWidthOf(group);
1176
+
1177
+ const gX = fixedPositions?.get(group.name) ?? group.x;
1178
+ const gY = fixedPositions != null ? 0 : group.y;
1211
1179
 
1212
- const gEl = contentG
1180
+ const gEl = parent
1213
1181
  .append('g')
1214
- .attr('transform', `translate(${group.x}, ${group.y})`)
1182
+ .attr('transform', `translate(${gX}, ${gY})`)
1215
1183
  .attr('class', 'c4-legend-group')
1216
1184
  .attr('data-legend-group', group.name.toLowerCase())
1217
1185
  .style('cursor', 'pointer');
@@ -1317,8 +1285,16 @@ export function renderC4Containers(
1317
1285
 
1318
1286
  const titleHeight = parsed.title ? TITLE_HEIGHT + 10 : 0;
1319
1287
  const diagramW = layout.width;
1320
- const diagramH = layout.height;
1321
- const availH = height - titleHeight;
1288
+ const hasLegend = layout.legend.length > 0;
1289
+ // In app mode, legend is a fixed overlay outside the scaled group.
1290
+ // C4 layout adds MARGIN(40) + LEGEND_HEIGHT below content — remove that from diagramH.
1291
+ const C4_LAYOUT_MARGIN = 40;
1292
+ const LEGEND_FIXED_GAP = 8;
1293
+ const fixedLegend = !exportDims && hasLegend;
1294
+ const legendLayoutSpace = C4_LAYOUT_MARGIN + LEGEND_HEIGHT;
1295
+ const legendReserveH = fixedLegend ? LEGEND_HEIGHT + LEGEND_FIXED_GAP : 0;
1296
+ const diagramH = fixedLegend ? layout.height - legendLayoutSpace : layout.height;
1297
+ const availH = height - titleHeight - legendReserveH;
1322
1298
  const scaleX = (width - DIAGRAM_PADDING * 2) / diagramW;
1323
1299
  const scaleY = (availH - DIAGRAM_PADDING * 2) / diagramH;
1324
1300
  const scale = Math.min(MAX_SCALE, scaleX, scaleY);
@@ -1508,6 +1484,23 @@ export function renderC4Containers(
1508
1484
  .attr('data-line-number', String(node.lineNumber))
1509
1485
  .attr('data-node-id', node.id);
1510
1486
 
1487
+ if (activeTagGroup) {
1488
+ const tagKey = activeTagGroup.toLowerCase();
1489
+ const tagValue = node.metadata[tagKey];
1490
+ if (tagValue) {
1491
+ nodeG.attr(`data-tag-${tagKey}`, tagValue.toLowerCase());
1492
+ } else {
1493
+ // Fall back to the group's defaultValue so hover-dimming works for
1494
+ // nodes that inherit the default (e.g. sc: Internal default).
1495
+ const tagGroup = parsed.tagGroups.find(
1496
+ (g) => g.name.toLowerCase() === tagKey || g.alias?.toLowerCase() === tagKey
1497
+ );
1498
+ if (tagGroup?.defaultValue) {
1499
+ nodeG.attr(`data-tag-${tagKey}`, tagGroup.defaultValue.toLowerCase());
1500
+ }
1501
+ }
1502
+ }
1503
+
1511
1504
  if (node.shape) {
1512
1505
  nodeG.attr('data-shape', node.shape);
1513
1506
  }
@@ -1717,8 +1710,18 @@ export function renderC4Containers(
1717
1710
  }
1718
1711
 
1719
1712
  // ── Legend ──
1720
- if (!exportDims) {
1721
- renderLegend(contentG as GSelection, layout, palette, isDark, activeTagGroup);
1713
+ if (hasLegend) {
1714
+ // App mode: fixed overlay at SVG bottom so it's always readable regardless of scale.
1715
+ // Export mode: render inside scaled contentG at layout coordinates.
1716
+ const legendParent = fixedLegend
1717
+ ? svg.append('g')
1718
+ .attr('class', 'c4-legend-fixed')
1719
+ .attr('transform', `translate(0, ${height - DIAGRAM_PADDING - LEGEND_HEIGHT})`)
1720
+ : contentG.append('g').attr('class', 'c4-legend');
1721
+ if (activeTagGroup) {
1722
+ legendParent.attr('data-legend-active', activeTagGroup.toLowerCase());
1723
+ }
1724
+ renderLegend(legendParent as GSelection, layout, palette, isDark, activeTagGroup, fixedLegend ? width : null);
1722
1725
  }
1723
1726
  }
1724
1727
 
package/src/cli.ts CHANGED
@@ -73,6 +73,7 @@ Options:
73
73
  --c4-level <level> C4 render level: context (default), containers, components, deployment
74
74
  --c4-system <name> System to drill into (with --c4-level containers or components)
75
75
  --c4-container <name> Container to drill into (with --c4-level components)
76
+ --tag-group <name> Pre-select a tag group for static export coloring
76
77
  --no-branding Omit diagrammo.app branding from exports
77
78
  --copy Copy URL to clipboard (only with -o url)
78
79
  --json Output structured JSON to stdout
@@ -102,8 +103,7 @@ function parseArgs(argv: string[]): {
102
103
  c4Level: 'context' | 'containers' | 'components' | 'deployment';
103
104
  c4System: string | undefined;
104
105
  c4Container: string | undefined;
105
- scenario: string | undefined;
106
- listScenarios: boolean;
106
+ tagGroup: string | undefined;
107
107
  } {
108
108
  const result = {
109
109
  input: undefined as string | undefined,
@@ -119,8 +119,7 @@ function parseArgs(argv: string[]): {
119
119
  c4Level: 'context' as 'context' | 'containers' | 'components' | 'deployment',
120
120
  c4System: undefined as string | undefined,
121
121
  c4Container: undefined as string | undefined,
122
- scenario: undefined as string | undefined,
123
- listScenarios: false,
122
+ tagGroup: undefined as string | undefined,
124
123
  };
125
124
 
126
125
  const args = argv.slice(2); // skip node + script
@@ -174,11 +173,8 @@ function parseArgs(argv: string[]): {
174
173
  } else if (arg === '--c4-container') {
175
174
  result.c4Container = args[++i];
176
175
  i++;
177
- } else if (arg === '--scenario') {
178
- result.scenario = args[++i];
179
- i++;
180
- } else if (arg === '--list-scenarios') {
181
- result.listScenarios = true;
176
+ } else if (arg === '--tag-group') {
177
+ result.tagGroup = args[++i];
182
178
  i++;
183
179
  } else if (arg === '--no-branding') {
184
180
  result.noBranding = true;
@@ -440,34 +436,6 @@ async function main(): Promise<void> {
440
436
  }
441
437
  }
442
438
 
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
-
471
439
  // Validate C4 options
472
440
  if (opts.c4Level === 'containers' && !opts.c4System) {
473
441
  exitWithJsonError('Error: --c4-system is required when --c4-level is containers');
@@ -488,7 +456,7 @@ async function main(): Promise<void> {
488
456
  c4Level: opts.c4Level,
489
457
  c4System: opts.c4System,
490
458
  c4Container: opts.c4Container,
491
- scenario: opts.scenario,
459
+ tagGroup: opts.tagGroup,
492
460
  });
493
461
 
494
462
  if (!svg) {