@transitrix/diagrams 1.0.1 → 1.2.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 (137) hide show
  1. package/dist/activities/__tests__/validate.test.js +32 -1
  2. package/dist/activities/cpm.js +11 -2
  3. package/dist/activities/gantt.js +6 -3
  4. package/dist/activities/types.d.ts +1 -0
  5. package/dist/activities/validate.js +24 -8
  6. package/dist/activity-card/__tests__/resolver.test.js +22 -1
  7. package/dist/activity-card/__tests__/validate.test.js +23 -1
  8. package/dist/activity-card/resolver.js +28 -20
  9. package/dist/activity-card/validate.js +22 -11
  10. package/dist/capability-map/index.d.ts +4 -0
  11. package/dist/capability-map/index.js +2 -0
  12. package/dist/capability-map/layout-tree.d.ts +42 -0
  13. package/dist/capability-map/layout-tree.js +65 -0
  14. package/dist/capability-map/render-capability-tree.d.ts +15 -0
  15. package/dist/capability-map/render-capability-tree.js +108 -0
  16. package/dist/capability-map/types.d.ts +2 -0
  17. package/dist/compliance/__tests__/impact.test.js +198 -0
  18. package/dist/compliance/html.js +1 -7
  19. package/dist/compliance/impact.d.ts +24 -1
  20. package/dist/compliance/impact.js +97 -21
  21. package/dist/factor/__tests__/validate.test.js +3 -3
  22. package/dist/factor/types.d.ts +1 -1
  23. package/dist/factor/validate.js +2 -2
  24. package/dist/fgca/__tests__/chain-table.test.js +2 -2
  25. package/dist/fgca/__tests__/layout.test.js +11 -11
  26. package/dist/fgca/__tests__/preview-layout.test.js +7 -7
  27. package/dist/fgca/__tests__/resolver.test.js +2 -2
  28. package/dist/fgca/__tests__/validate.test.js +2 -2
  29. package/dist/fgca/chain-table.d.ts +1 -1
  30. package/dist/fgca/chain-table.js +4 -4
  31. package/dist/fgca/index.d.ts +2 -2
  32. package/dist/fgca/index.js +1 -1
  33. package/dist/fgca/layout.d.ts +2 -2
  34. package/dist/fgca/layout.js +9 -9
  35. package/dist/fgca/nodeTypes.d.ts +1 -1
  36. package/dist/fgca/nodeTypes.js +2 -2
  37. package/dist/fgca/nodes/FGCAFactorNode.d.ts +3 -3
  38. package/dist/fgca/nodes/FGCAFactorNode.js +5 -5
  39. package/dist/fgca/parse-canonical.js +24 -13
  40. package/dist/fgca/preview-layout.d.ts +1 -1
  41. package/dist/fgca/preview-layout.js +4 -4
  42. package/dist/fgca/resolver.js +3 -1
  43. package/dist/fgca/types.d.ts +13 -11
  44. package/dist/fgca/types.js +2 -2
  45. package/dist/fgca/validate.d.ts +2 -2
  46. package/dist/fgca/validate.js +34 -23
  47. package/dist/process-blueprint/index.d.ts +1 -1
  48. package/dist/process-blueprint/layout.js +64 -37
  49. package/dist/process-blueprint/types.d.ts +14 -0
  50. package/dist/scenarios/index.d.ts +1 -1
  51. package/dist/scenarios/types.d.ts +4 -4
  52. package/dist/theme/index.d.ts +1 -1
  53. package/dist/theme/index.js +1 -1
  54. package/dist/theme/themes.js +28 -3
  55. package/dist/theme/tokens.d.ts +47 -1
  56. package/dist/theme/tokens.js +31 -1
  57. package/dist/webview/__tests__/entry-notations.test.js +2 -0
  58. package/dist/webview/__tests__/render-process.test.d.ts +1 -0
  59. package/dist/webview/__tests__/render-process.test.js +463 -0
  60. package/dist/webview/__tests__/render-util.test.d.ts +1 -0
  61. package/dist/webview/__tests__/render-util.test.js +54 -0
  62. package/dist/webview/entry.d.ts +1 -1
  63. package/dist/webview/entry.js +9 -5
  64. package/dist/webview/render-activities.d.ts +21 -1
  65. package/dist/webview/render-activities.js +91 -56
  66. package/dist/webview/render-activity-card.d.ts +11 -1
  67. package/dist/webview/render-activity-card.js +11 -2
  68. package/dist/webview/render-blocks.d.ts +24 -1
  69. package/dist/webview/render-blocks.js +58 -24
  70. package/dist/webview/render-fgca.d.ts +27 -0
  71. package/dist/webview/render-fgca.js +30 -16
  72. package/dist/webview/render-goals.d.ts +26 -1
  73. package/dist/webview/render-goals.js +30 -20
  74. package/dist/webview/render-process-blueprint.d.ts +11 -1
  75. package/dist/webview/render-process-blueprint.js +118 -53
  76. package/dist/webview/render-process.d.ts +95 -0
  77. package/dist/webview/render-process.js +392 -0
  78. package/dist/webview/render-scenarios.js +2 -2
  79. package/dist/webview/render-util.d.ts +19 -6
  80. package/dist/webview/render-util.js +23 -15
  81. package/package.json +15 -1
  82. package/src/activities/__tests__/validate.test.ts +35 -1
  83. package/src/activities/cpm.ts +14 -2
  84. package/src/activities/gantt.ts +6 -5
  85. package/src/activities/types.ts +1 -0
  86. package/src/activities/validate.ts +24 -8
  87. package/src/activity-card/__tests__/resolver.test.ts +24 -1
  88. package/src/activity-card/__tests__/validate.test.ts +25 -1
  89. package/src/activity-card/resolver.ts +29 -20
  90. package/src/activity-card/validate.ts +20 -11
  91. package/src/capability-map/index.ts +11 -0
  92. package/src/capability-map/layout-tree.ts +114 -0
  93. package/src/capability-map/render-capability-tree.ts +131 -0
  94. package/src/capability-map/types.ts +2 -0
  95. package/src/compliance/__tests__/impact.test.ts +219 -0
  96. package/src/compliance/html.ts +1 -8
  97. package/src/compliance/impact.ts +141 -23
  98. package/src/factor/__tests__/validate.test.ts +3 -3
  99. package/src/factor/types.ts +1 -1
  100. package/src/factor/validate.ts +2 -2
  101. package/src/fgca/__tests__/chain-table.test.ts +2 -2
  102. package/src/fgca/__tests__/layout.test.ts +13 -13
  103. package/src/fgca/__tests__/preview-layout.test.ts +7 -7
  104. package/src/fgca/__tests__/resolver.test.ts +2 -2
  105. package/src/fgca/__tests__/validate.test.ts +2 -2
  106. package/src/fgca/chain-table.ts +5 -5
  107. package/src/fgca/index.ts +3 -3
  108. package/src/fgca/layout.ts +11 -11
  109. package/src/fgca/nodeTypes.ts +3 -3
  110. package/src/fgca/nodes/FGCAFactorNode.tsx +6 -6
  111. package/src/fgca/parse-canonical.ts +26 -15
  112. package/src/fgca/preview-layout.ts +5 -5
  113. package/src/fgca/resolver.ts +3 -1
  114. package/src/fgca/types.ts +15 -13
  115. package/src/fgca/validate.ts +32 -21
  116. package/src/process-blueprint/index.ts +1 -0
  117. package/src/process-blueprint/layout.ts +71 -42
  118. package/src/process-blueprint/types.ts +15 -0
  119. package/src/scenarios/index.ts +2 -2
  120. package/src/scenarios/types.ts +4 -4
  121. package/src/scenarios/validate.ts +3 -3
  122. package/src/theme/index.ts +1 -1
  123. package/src/theme/themes.ts +29 -2
  124. package/src/theme/tokens.ts +33 -1
  125. package/src/webview/__tests__/entry-notations.test.ts +2 -0
  126. package/src/webview/__tests__/render-process.test.ts +538 -0
  127. package/src/webview/__tests__/render-util.test.ts +66 -0
  128. package/src/webview/entry.ts +11 -5
  129. package/src/webview/render-activities.ts +107 -67
  130. package/src/webview/render-activity-card.ts +11 -2
  131. package/src/webview/render-blocks.ts +78 -28
  132. package/src/webview/render-fgca.ts +53 -29
  133. package/src/webview/render-goals.ts +45 -23
  134. package/src/webview/render-process-blueprint.ts +156 -82
  135. package/src/webview/render-process.ts +507 -0
  136. package/src/webview/render-scenarios.ts +3 -3
  137. package/src/webview/render-util.ts +27 -14
@@ -13,6 +13,7 @@ import type {
13
13
  LegendCell,
14
14
  ProcessBlueprintFile,
15
15
  ProcessBlueprintLayout,
16
+ RowId,
16
17
  Stage,
17
18
  StageHeaderCell,
18
19
  StageTextCell,
@@ -31,7 +32,7 @@ const ASPECT_LABEL: Record<AspectCategory, string> = {
31
32
  // (absent = feature disabled) and must stay optional in the resolved type.
32
33
  type SizingOptionKeys = Exclude<
33
34
  keyof ProcessBlueprintLayoutOptions,
34
- 'complianceLane' | 'complianceInput' | 'visibleAspects'
35
+ 'complianceLane' | 'complianceInput' | 'visibleAspects' | 'visibleRows' | 'visibleStages'
35
36
  >;
36
37
 
37
38
  /**
@@ -39,7 +40,7 @@ type SizingOptionKeys = Exclude<
39
40
  * the opt-in feature fields stay optional (undefined = feature disabled).
40
41
  */
41
42
  type ResolvedLayoutOptions = Required<Pick<ProcessBlueprintLayoutOptions, SizingOptionKeys>> &
42
- Pick<ProcessBlueprintLayoutOptions, 'complianceLane' | 'complianceInput' | 'visibleAspects'>;
43
+ Pick<ProcessBlueprintLayoutOptions, 'complianceLane' | 'complianceInput' | 'visibleAspects' | 'visibleRows' | 'visibleStages'>;
43
44
 
44
45
  const DEFAULTS = {
45
46
  legendColumnWidth: 140,
@@ -48,14 +49,19 @@ const DEFAULTS = {
48
49
  goalRowHeight: 56,
49
50
  resultRowHeight: 56,
50
51
  aspectRowMinHeight: 60,
51
- pillHeight: 28,
52
+ pillHeight: 40,
52
53
  pillGap: 4,
53
54
  cellPadding: 8,
54
- textLineHeight: 15,
55
- textCharWidth: 6.2,
55
+ // 17px gives comfortable breathing room between wrapped lines at typical
56
+ // webview font sizes (VS Code uses ~13–14px body, SVG text-secondary ≈ same).
57
+ textLineHeight: 17,
58
+ // 7.5px per char is a conservative estimate that causes wrap to trigger
59
+ // before text visually overflows the cell border.
60
+ textCharWidth: 7.5,
56
61
  cellTextPadX: 10,
57
62
  cellTextPadY: 10,
58
- maxTextLines: 6,
63
+ // No hard truncation: rows grow to fit all wrapped lines.
64
+ maxTextLines: 100,
59
65
  } satisfies Required<Pick<ProcessBlueprintLayoutOptions, SizingOptionKeys>>;
60
66
 
61
67
  interface RawPill {
@@ -369,9 +375,25 @@ export function layoutProcessBlueprint(
369
375
  const opts = resolveOptions(options);
370
376
 
371
377
  const pb = file.process_blueprint;
372
- const stages = Array.isArray(pb?.stages) ? pb.stages : [];
378
+
379
+ // Filter stages by visibleStages if set
380
+ const allStages = Array.isArray(pb?.stages) ? pb.stages : [];
381
+ const stages = opts.visibleStages
382
+ ? allStages.filter(s => s?.id != null && opts.visibleStages!.includes(s.id))
383
+ : allStages;
373
384
  const stageIndexById = buildStageIndex(stages);
374
385
 
386
+ // Row visibility from visibleRows
387
+ const visRows = opts.visibleRows;
388
+ const showGoal = !visRows || (visRows as string[]).includes('goal');
389
+ const showResult = !visRows || (visRows as string[]).includes('result');
390
+ const effectiveVisibleAspects: AspectCategory[] | undefined = visRows
391
+ ? (ASPECT_CATEGORIES.filter(c => (visRows as string[]).includes(c)) as AspectCategory[])
392
+ : opts.visibleAspects;
393
+ const effectiveComplianceEnabled =
394
+ opts.complianceLane?.enabled === true &&
395
+ (!visRows || (visRows as string[]).includes('compliance'));
396
+
375
397
  // Stage headers
376
398
  const stageHeaders: StageHeaderCell[] = stages.map((s, i) => ({
377
399
  stageIndex: i,
@@ -398,38 +420,39 @@ export function layoutProcessBlueprint(
398
420
  const goalLines = stages.map(s => wrap(s?.goal ?? ''));
399
421
  const resultLines = stages.map(s => wrap(s?.result ?? ''));
400
422
 
401
- const goalRowY = opts.stageHeaderHeight;
402
- const goalRowHeight = rowHeightFor(goalLines.map(l => l.length), opts.goalRowHeight);
403
- const resultRowY = goalRowY + goalRowHeight;
404
- const resultRowHeight = rowHeightFor(resultLines.map(l => l.length), opts.resultRowHeight);
405
-
406
- const goalCells: StageTextCell[] = stages.map((s, i) => ({
407
- stageIndex: i,
408
- text: s?.goal ?? '',
409
- lines: goalLines[i],
410
- x: opts.legendColumnWidth + i * opts.stageColumnWidth,
411
- y: goalRowY,
412
- width: opts.stageColumnWidth,
413
- height: goalRowHeight,
414
- }));
415
-
416
- const resultCells: StageTextCell[] = stages.map((s, i) => ({
417
- stageIndex: i,
418
- text: s?.result ?? '',
419
- lines: resultLines[i],
420
- x: opts.legendColumnWidth + i * opts.stageColumnWidth,
421
- y: resultRowY,
422
- width: opts.stageColumnWidth,
423
- height: resultRowHeight,
424
- }));
425
-
426
- const aspectsStartY = resultRowY + resultRowHeight;
423
+ let cursorY = opts.stageHeaderHeight;
424
+ let goalRowY = 0, goalRowHeight = 0;
425
+ let resultRowY = 0, resultRowHeight = 0;
426
+ const goalCells: StageTextCell[] = [];
427
+ const resultCells: StageTextCell[] = [];
428
+
429
+ if (showGoal) {
430
+ goalRowY = cursorY;
431
+ goalRowHeight = rowHeightFor(goalLines.map(l => l.length), opts.goalRowHeight);
432
+ goalCells.push(...stages.map((s, i) => ({
433
+ stageIndex: i, text: s?.goal ?? '', lines: goalLines[i],
434
+ x: opts.legendColumnWidth + i * opts.stageColumnWidth, y: goalRowY,
435
+ width: opts.stageColumnWidth, height: goalRowHeight,
436
+ })));
437
+ cursorY += goalRowHeight;
438
+ }
439
+ if (showResult) {
440
+ resultRowY = cursorY;
441
+ resultRowHeight = rowHeightFor(resultLines.map(l => l.length), opts.resultRowHeight);
442
+ resultCells.push(...stages.map((s, i) => ({
443
+ stageIndex: i, text: s?.result ?? '', lines: resultLines[i],
444
+ x: opts.legendColumnWidth + i * opts.stageColumnWidth, y: resultRowY,
445
+ width: opts.stageColumnWidth, height: resultRowHeight,
446
+ })));
447
+ cursorY += resultRowHeight;
448
+ }
449
+ const aspectsStartY = cursorY;
427
450
 
428
451
  // Aspect rows: only categories with at least one entry, in fixed order.
429
- // When visibleAspects is set, skip categories not in the list.
452
+ // When effectiveVisibleAspects is set, skip categories not in the list.
430
453
  const aspectCategoriesToRender =
431
- opts.visibleAspects !== undefined
432
- ? ASPECT_CATEGORIES.filter(c => opts.visibleAspects!.includes(c))
454
+ effectiveVisibleAspects !== undefined
455
+ ? ASPECT_CATEGORIES.filter(c => effectiveVisibleAspects.includes(c))
433
456
  : ASPECT_CATEGORIES;
434
457
 
435
458
  const aspectRows: AspectRow[] = [];
@@ -461,6 +484,13 @@ export function layoutProcessBlueprint(
461
484
  const width =
462
485
  (p.endStageIndex - p.startStageIndex + 1) * opts.stageColumnWidth - 2 * opts.cellPadding;
463
486
  const y = aspectCursorY + opts.cellPadding + slot[i] * (opts.pillHeight + opts.pillGap);
487
+ const pillMaxChars = Math.max(4, Math.floor((width - 2 * opts.cellPadding) / opts.textCharWidth));
488
+ const nameWrapped = wrapText(p.name, pillMaxChars, 2);
489
+ // Show id as second line only when the name fits on one line; otherwise
490
+ // use both lines for the name so it isn't truncated.
491
+ const lines: string[] = p.id && nameWrapped.length <= 1
492
+ ? [...nameWrapped, p.id].slice(0, 2)
493
+ : nameWrapped;
464
494
  return {
465
495
  category: p.category,
466
496
  entryIndex: p.entryIndex,
@@ -472,6 +502,7 @@ export function layoutProcessBlueprint(
472
502
  y,
473
503
  width,
474
504
  height: opts.pillHeight,
505
+ lines,
475
506
  };
476
507
  });
477
508
 
@@ -487,7 +518,7 @@ export function layoutProcessBlueprint(
487
518
 
488
519
  // Compliance lane — derived from assertions/requirements, rendered after aspect rows.
489
520
  let complianceRow: import('./types.js').ComplianceRow | undefined;
490
- if (opts.complianceLane?.enabled && opts.complianceInput) {
521
+ if (effectiveComplianceEnabled && opts.complianceInput) {
491
522
  complianceRow = deriveComplianceRow(
492
523
  stages,
493
524
  stageIndexById,
@@ -502,8 +533,8 @@ export function layoutProcessBlueprint(
502
533
 
503
534
  // Legend (left column labels): one entry per row.
504
535
  const legend: LegendCell[] = [
505
- { kind: 'goal', label: 'Goal', y: goalRowY, height: goalRowHeight },
506
- { kind: 'result', label: 'Result', y: resultRowY, height: resultRowHeight },
536
+ ...(showGoal ? [{ kind: 'goal' as const, label: 'Goal', y: goalRowY, height: goalRowHeight }] : []),
537
+ ...(showResult ? [{ kind: 'result' as const, label: 'Result', y: resultRowY, height: resultRowHeight }] : []),
507
538
  ...aspectRows.map<LegendCell>(row => ({
508
539
  kind: 'aspect',
509
540
  category: row.category,
@@ -511,9 +542,7 @@ export function layoutProcessBlueprint(
511
542
  y: row.y,
512
543
  height: row.height,
513
544
  })),
514
- ...(complianceRow
515
- ? [{ kind: 'compliance' as const, label: 'Compliance', y: complianceRow.y, height: complianceRow.height }]
516
- : []),
545
+ ...(complianceRow ? [{ kind: 'compliance' as const, label: 'Compliance', y: complianceRow.y, height: complianceRow.height }] : []),
517
546
  ];
518
547
 
519
548
  const totalWidth = opts.legendColumnWidth + stages.length * opts.stageColumnWidth;
@@ -1,5 +1,7 @@
1
1
  export type AspectCategory = 'systems' | 'actors' | 'equipment' | 'information_entities';
2
2
 
3
+ export type RowId = 'goal' | 'result' | 'systems' | 'actors' | 'equipment' | 'information_entities' | 'compliance';
4
+
3
5
  // ── Compliance lane types ────────────────────────────────────────────────────
4
6
 
5
7
  /**
@@ -191,6 +193,17 @@ export interface ProcessBlueprintLayoutOptions {
191
193
  * Pass an empty array to suppress all aspect rows.
192
194
  */
193
195
  visibleAspects?: AspectCategory[];
196
+ /**
197
+ * Restrict which rows are rendered. When absent, all rows with data are shown.
198
+ * When set, takes precedence over `visibleAspects` for aspect categories and
199
+ * overrides `complianceLane.enabled` for the compliance row.
200
+ */
201
+ visibleRows?: RowId[];
202
+ /**
203
+ * Restrict which stages (columns) are rendered by their stage ID.
204
+ * When absent, all stages are shown.
205
+ */
206
+ visibleStages?: string[];
194
207
  }
195
208
 
196
209
  import type { LayoutBounds } from '../geometry.js';
@@ -236,6 +249,8 @@ export interface AspectPill {
236
249
  y: number;
237
250
  width: number;
238
251
  height: number;
252
+ /** Word-wrapped lines of `name`, pre-fitted to the pill width by the layout. */
253
+ lines: string[];
239
254
  }
240
255
 
241
256
  export interface AspectRow {
@@ -1,7 +1,7 @@
1
1
  export type {
2
2
  ScenarioStatus,
3
- FactorRelevance,
4
- FactorView,
3
+ DriverRelevance,
4
+ DriverView,
5
5
  GoalRef,
6
6
  CapabilityRef,
7
7
  ActivityRef,
@@ -1,9 +1,9 @@
1
1
  export type ScenarioStatus = 'Draft' | 'Active' | 'Archived';
2
- export type FactorRelevance = 'High' | 'Medium' | 'Low';
2
+ export type DriverRelevance = 'High' | 'Medium' | 'Low';
3
3
 
4
- export interface FactorView {
4
+ export interface DriverView {
5
5
  factor_id: string;
6
- relevance?: FactorRelevance;
6
+ relevance?: DriverRelevance;
7
7
  impact?: string;
8
8
  }
9
9
 
@@ -21,7 +21,7 @@ export interface ScenarioHeader {
21
21
  status: ScenarioStatus;
22
22
  created_at?: string;
23
23
  vision?: string;
24
- factors_view?: FactorView[];
24
+ factors_view?: DriverView[];
25
25
  goals?: GoalRef[];
26
26
  capabilities?: CapabilityRef[];
27
27
  activities?: ActivityRef[];
@@ -1,10 +1,10 @@
1
- import type { ScenarioStatus, FactorRelevance } from './types.js';
1
+ import type { ScenarioStatus, DriverRelevance } from './types.js';
2
2
  import type { ValidationError, ValidationWarning, ValidationResult } from '../validation-types.js';
3
3
 
4
4
  export type { ValidationError, ValidationWarning, ValidationResult };
5
5
 
6
6
  const VALID_STATUSES = new Set<ScenarioStatus>(['Draft', 'Active', 'Archived']);
7
- const VALID_RELEVANCE = new Set<FactorRelevance>(['High', 'Medium', 'Low']);
7
+ const VALID_RELEVANCE = new Set<DriverRelevance>(['High', 'Medium', 'Low']);
8
8
  const DATE_RE = /^\d{4}-\d{2}-\d{2}$/;
9
9
 
10
10
  interface RefSpec {
@@ -84,7 +84,7 @@ export function validateScenario(input: unknown): ValidationResult {
84
84
  if (seen.has(fid)) errors.push({ code: 'SCN-013', message: `Duplicate factor_id: "${fid}"` });
85
85
  seen.add(fid);
86
86
  }
87
- if (f['relevance'] !== undefined && !VALID_RELEVANCE.has(f['relevance'] as FactorRelevance)) {
87
+ if (f['relevance'] !== undefined && !VALID_RELEVANCE.has(f['relevance'] as DriverRelevance)) {
88
88
  errors.push({ code: 'SCN-006', message: `${idx}: relevance "${f['relevance']}" must be one of: High, Medium, Low` });
89
89
  }
90
90
  }
@@ -1,4 +1,4 @@
1
1
  export type { AdaptiveTokens } from './tokens.js';
2
- export { BRAND, FUNCTIONAL, LAYER_COLORS, LEVEL_COLORS, MATURITY_COLORS, STRUCTURAL, TYPOGRAPHY, CSS_VAR, getBaseResetCss } from './tokens.js';
2
+ export { BRAND, FUNCTIONAL, LAYER_COLORS, LEVEL_COLORS, MATURITY_COLORS, TREE_LEVEL_COLORS, TREE_MATURITY_COLORS, STRUCTURAL, TYPOGRAPHY, CSS_VAR, getBaseResetCss } from './tokens.js';
3
3
  export type { ThemeId } from './themes.js';
4
4
  export { generateWebviewCss, generateSvgEmbedCss } from './themes.js';
@@ -2,6 +2,8 @@ import {
2
2
  LAYER_COLORS,
3
3
  LEVEL_COLORS,
4
4
  MATURITY_COLORS,
5
+ TREE_LEVEL_COLORS,
6
+ TREE_MATURITY_COLORS,
5
7
  STRUCTURAL,
6
8
  TYPOGRAPHY,
7
9
  CSS_VAR,
@@ -75,8 +77,21 @@ function diagramVars(variant: 'light' | 'dark' | 'hc'): string {
75
77
  const sc = STRUCTURAL;
76
78
  const lv = LEVEL_COLORS[variant];
77
79
  const mat = MATURITY_COLORS[variant];
80
+ const treeLevel = TREE_LEVEL_COLORS;
81
+ const treeMat = TREE_MATURITY_COLORS[variant];
78
82
  const levelVars = lv.map((c, i) => `--ts-level-${i}:${c}`).join(';') + ';';
83
+ const treeVars = [
84
+ `${CSS_VAR.treeLevel0}:${treeLevel.band0[variant]}`,
85
+ `${CSS_VAR.treeLevel1}:${treeLevel.band1[variant]}`,
86
+ `${CSS_VAR.treeLevel2}:${treeLevel.band2[variant]}`,
87
+ `${CSS_VAR.treeMaturity1}:${treeMat[0]}`,
88
+ `${CSS_VAR.treeMaturity2}:${treeMat[1]}`,
89
+ `${CSS_VAR.treeMaturity3}:${treeMat[2]}`,
90
+ `${CSS_VAR.treeMaturity4}:${treeMat[3]}`,
91
+ `${CSS_VAR.treeMaturity5}:${treeMat[4]}`,
92
+ ].join(';') + ';';
79
93
  return [
94
+ `${CSS_VAR.layerDriver}:${lc.driver[variant]}`,
80
95
  `${CSS_VAR.layerFactor}:${lc.factor[variant]}`,
81
96
  `${CSS_VAR.layerGoal}:${lc.goal[variant]}`,
82
97
  `${CSS_VAR.layerChange}:${lc.change[variant]}`,
@@ -91,7 +106,7 @@ function diagramVars(variant: 'light' | 'dark' | 'hc'): string {
91
106
  `${CSS_VAR.maturity3}:${mat[2]}`,
92
107
  `${CSS_VAR.maturity4}:${mat[3]}`,
93
108
  `${CSS_VAR.maturity5}:${mat[4]}`,
94
- ].join(';') + ';' + levelVars;
109
+ ].join(';') + ';' + levelVars + treeVars;
95
110
  }
96
111
 
97
112
  /** CSS for the webview shell (toolbar, canvas). */
@@ -131,6 +146,7 @@ function diagramClassCss(): string {
131
146
  // contract every notation preview honours so the catalogue reads as one
132
147
  // visual family.
133
148
  return `.diagram-node{stroke:var(${cv.nodeStroke});stroke-width:1;}
149
+ .layer-driver{fill:var(${cv.layerDriver});}
134
150
  .layer-factor{fill:var(${cv.layerFactor});}
135
151
  .layer-goal{fill:var(${cv.layerGoal});}
136
152
  .layer-change{fill:var(${cv.layerChange});}
@@ -153,7 +169,18 @@ ${levelRules}
153
169
  .compliance-deadline{fill:var(${cv.statusErrorBg});stroke:var(${cv.statusErrorFg});}
154
170
  .compliance-deadline text{fill:var(${cv.statusErrorFg});}
155
171
  .compliance-badge{fill:var(${cv.statusErrorFg});}
156
- .compliance-badge-text{fill:var(${cv.textInverse});font-family:${t.fontFamily};font-size:8px;font-weight:700;}`;
172
+ .compliance-badge-text{fill:var(${cv.textInverse});font-family:${t.fontFamily};font-size:8px;font-weight:700;}
173
+ .tree-level-0{fill:var(${cv.treeLevel0});}
174
+ .tree-level-1{fill:var(${cv.treeLevel1});}
175
+ .tree-level-2{fill:var(${cv.treeLevel2});}
176
+ .tree-maturity-1{fill:var(${cv.treeMaturity1});}
177
+ .tree-maturity-2{fill:var(${cv.treeMaturity2});}
178
+ .tree-maturity-3{fill:var(${cv.treeMaturity3});}
179
+ .tree-maturity-4{fill:var(${cv.treeMaturity4});}
180
+ .tree-maturity-5{fill:var(${cv.treeMaturity5});}
181
+ .tree-collapse-btn{cursor:pointer;fill:white;stroke:var(${cv.nodeStroke});stroke-width:1.5;}
182
+ .tree-collapse-btn:hover{fill:var(${cv.bgElevated});}
183
+ .tree-collapse-lbl{fill:var(${cv.textSecondary});font-family:${t.fontFamily};font-size:12px;font-weight:700;dominant-baseline:central;pointer-events:none;}`;
157
184
  }
158
185
 
159
186
  /**
@@ -13,8 +13,9 @@ export const FUNCTIONAL = {
13
13
  info: '#1d6fa8',
14
14
  } as const;
15
15
 
16
- /** Fill colors for FGCA notation columns. */
16
+ /** Fill colors for FGCA/DGCA notation columns. */
17
17
  export const LAYER_COLORS = {
18
+ driver: { light: '#fef3c7', dark: '#292108', hc: '#f59e0b' },
18
19
  factor: { light: '#fef3c7', dark: '#292108', hc: '#f59e0b' },
19
20
  goal: { light: '#e0e7ff', dark: '#1e1b4b', hc: '#818cf8' },
20
21
  change: { light: '#dbeafe', dark: '#0c2042', hc: '#60a5fa' },
@@ -47,6 +48,28 @@ export const MATURITY_COLORS = {
47
48
  hc: ['#f87171', '#fb923c', '#facc15', '#a3e635', '#4ade80'],
48
49
  } as const;
49
50
 
51
+ /**
52
+ * DSM-matching capability tree node fill colors, grouped by depth band.
53
+ * band0: depth 0–2 (pink)
54
+ * band1: depth 3–4 (yellow)
55
+ * band2: depth 5+ (blue)
56
+ */
57
+ export const TREE_LEVEL_COLORS = {
58
+ band0: { light: '#fee0e0', dark: '#3d0f0f', hc: '#ff9999' },
59
+ band1: { light: '#ffffcb', dark: '#3a3500', hc: '#ffff88' },
60
+ band2: { light: '#c2f0ff', dark: '#003d4d', hc: '#66ccff' },
61
+ } as const;
62
+
63
+ /**
64
+ * DSM-matching maturity badge fills for the capability tree (L1–L5).
65
+ * Distinct from MATURITY_COLORS which drives the cards / compliance views.
66
+ */
67
+ export const TREE_MATURITY_COLORS = {
68
+ light: ['#c1c1c1', '#ff3d67', '#ff9a59', '#fdef59', '#83cca3'],
69
+ dark: ['#555555', '#cc1040', '#d06020', '#c8c000', '#4d9970'],
70
+ hc: ['#cccccc', '#ff6699', '#ffbb77', '#ffff44', '#88ddaa'],
71
+ } as const;
72
+
50
73
  /**
51
74
  * Typography hierarchy. All previews resolve text styling from these roles
52
75
  * — never from inline font-* attributes on individual <text> elements.
@@ -112,6 +135,7 @@ export const CSS_VAR = {
112
135
  statusInfoFg: '--ts-status-info-fg',
113
136
  statusErrorBg: '--ts-status-error-bg',
114
137
  statusErrorFg: '--ts-status-error-fg',
138
+ layerDriver: '--ts-layer-driver',
115
139
  layerFactor: '--ts-layer-factor',
116
140
  layerGoal: '--ts-layer-goal',
117
141
  layerChange: '--ts-layer-change',
@@ -126,6 +150,14 @@ export const CSS_VAR = {
126
150
  maturity3: '--ts-maturity-3',
127
151
  maturity4: '--ts-maturity-4',
128
152
  maturity5: '--ts-maturity-5',
153
+ treeLevel0: '--ts-tree-level-0',
154
+ treeLevel1: '--ts-tree-level-1',
155
+ treeLevel2: '--ts-tree-level-2',
156
+ treeMaturity1: '--ts-tree-maturity-1',
157
+ treeMaturity2: '--ts-tree-maturity-2',
158
+ treeMaturity3: '--ts-tree-maturity-3',
159
+ treeMaturity4: '--ts-tree-maturity-4',
160
+ treeMaturity5: '--ts-tree-maturity-5',
129
161
  } as const;
130
162
 
131
163
  export function getBaseResetCss(): string {
@@ -26,6 +26,8 @@ interface Fixture {
26
26
  // example per notation is enough to prove the dispatch + renderer wiring.
27
27
  const FIXTURES: Fixture[] = [
28
28
  { kind: 'goals', file: 'goals/strategy-2026.goals.transitrix.yaml', markup: 'svg' },
29
+ { kind: 'dgca', file: 'dgca/strategy-2026.dgca.transitrix.yaml', markup: 'svg' },
30
+ { kind: 'dga', file: 'dga/strategy-2026.dga.transitrix.yaml', markup: 'svg' },
29
31
  { kind: 'fgca', file: 'fgca/strategy-2026.fgca.transitrix.yaml', markup: 'svg' },
30
32
  { kind: 'fga', file: 'fga/strategy-2026.fga.transitrix.yaml', markup: 'svg' },
31
33
  { kind: 'activities', file: 'activities/platform-launch.activities.transitrix.yaml', markup: 'svg' },