@transitrix/diagrams 1.0.1 → 1.1.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 (115) hide show
  1. package/dist/activities/cpm.js +11 -2
  2. package/dist/activities/gantt.js +6 -3
  3. package/dist/activities/types.d.ts +1 -0
  4. package/dist/activities/validate.js +3 -3
  5. package/dist/activity-card/__tests__/resolver.test.js +22 -1
  6. package/dist/activity-card/resolver.js +11 -5
  7. package/dist/compliance/__tests__/impact.test.js +198 -0
  8. package/dist/compliance/html.js +1 -7
  9. package/dist/compliance/impact.d.ts +24 -1
  10. package/dist/compliance/impact.js +97 -21
  11. package/dist/factor/__tests__/validate.test.js +3 -3
  12. package/dist/factor/types.d.ts +1 -1
  13. package/dist/factor/validate.js +2 -2
  14. package/dist/fgca/__tests__/chain-table.test.js +2 -2
  15. package/dist/fgca/__tests__/layout.test.js +11 -11
  16. package/dist/fgca/__tests__/preview-layout.test.js +7 -7
  17. package/dist/fgca/__tests__/resolver.test.js +2 -2
  18. package/dist/fgca/chain-table.d.ts +1 -1
  19. package/dist/fgca/chain-table.js +4 -4
  20. package/dist/fgca/index.d.ts +2 -2
  21. package/dist/fgca/index.js +1 -1
  22. package/dist/fgca/layout.d.ts +2 -2
  23. package/dist/fgca/layout.js +9 -9
  24. package/dist/fgca/nodeTypes.d.ts +1 -1
  25. package/dist/fgca/nodeTypes.js +2 -2
  26. package/dist/fgca/nodes/FGCAFactorNode.d.ts +3 -3
  27. package/dist/fgca/nodes/FGCAFactorNode.js +5 -5
  28. package/dist/fgca/parse-canonical.js +15 -10
  29. package/dist/fgca/preview-layout.d.ts +1 -1
  30. package/dist/fgca/preview-layout.js +4 -4
  31. package/dist/fgca/resolver.js +3 -1
  32. package/dist/fgca/types.d.ts +11 -11
  33. package/dist/fgca/types.js +2 -2
  34. package/dist/fgca/validate.d.ts +2 -2
  35. package/dist/fgca/validate.js +17 -14
  36. package/dist/process-blueprint/index.d.ts +1 -1
  37. package/dist/process-blueprint/layout.js +64 -37
  38. package/dist/process-blueprint/types.d.ts +14 -0
  39. package/dist/scenarios/index.d.ts +1 -1
  40. package/dist/scenarios/types.d.ts +4 -4
  41. package/dist/theme/themes.js +2 -0
  42. package/dist/theme/tokens.d.ts +7 -1
  43. package/dist/theme/tokens.js +3 -1
  44. package/dist/webview/__tests__/entry-notations.test.js +2 -0
  45. package/dist/webview/__tests__/render-process.test.d.ts +1 -0
  46. package/dist/webview/__tests__/render-process.test.js +463 -0
  47. package/dist/webview/__tests__/render-util.test.d.ts +1 -0
  48. package/dist/webview/__tests__/render-util.test.js +54 -0
  49. package/dist/webview/entry.d.ts +1 -1
  50. package/dist/webview/entry.js +9 -5
  51. package/dist/webview/render-activities.d.ts +21 -1
  52. package/dist/webview/render-activities.js +91 -56
  53. package/dist/webview/render-activity-card.d.ts +11 -1
  54. package/dist/webview/render-activity-card.js +11 -2
  55. package/dist/webview/render-blocks.d.ts +24 -1
  56. package/dist/webview/render-blocks.js +58 -24
  57. package/dist/webview/render-fgca.d.ts +27 -0
  58. package/dist/webview/render-fgca.js +29 -15
  59. package/dist/webview/render-goals.d.ts +26 -1
  60. package/dist/webview/render-goals.js +30 -20
  61. package/dist/webview/render-process-blueprint.d.ts +11 -1
  62. package/dist/webview/render-process-blueprint.js +118 -53
  63. package/dist/webview/render-process.d.ts +95 -0
  64. package/dist/webview/render-process.js +392 -0
  65. package/dist/webview/render-scenarios.js +2 -2
  66. package/dist/webview/render-util.d.ts +19 -6
  67. package/dist/webview/render-util.js +23 -15
  68. package/package.json +15 -1
  69. package/src/activities/cpm.ts +14 -2
  70. package/src/activities/gantt.ts +6 -5
  71. package/src/activities/types.ts +1 -0
  72. package/src/activities/validate.ts +3 -3
  73. package/src/activity-card/__tests__/resolver.test.ts +24 -1
  74. package/src/activity-card/resolver.ts +12 -5
  75. package/src/compliance/__tests__/impact.test.ts +219 -0
  76. package/src/compliance/html.ts +1 -8
  77. package/src/compliance/impact.ts +141 -23
  78. package/src/factor/__tests__/validate.test.ts +3 -3
  79. package/src/factor/types.ts +1 -1
  80. package/src/factor/validate.ts +2 -2
  81. package/src/fgca/__tests__/chain-table.test.ts +2 -2
  82. package/src/fgca/__tests__/layout.test.ts +13 -13
  83. package/src/fgca/__tests__/preview-layout.test.ts +7 -7
  84. package/src/fgca/__tests__/resolver.test.ts +2 -2
  85. package/src/fgca/chain-table.ts +5 -5
  86. package/src/fgca/index.ts +3 -3
  87. package/src/fgca/layout.ts +11 -11
  88. package/src/fgca/nodeTypes.ts +3 -3
  89. package/src/fgca/nodes/FGCAFactorNode.tsx +6 -6
  90. package/src/fgca/parse-canonical.ts +17 -12
  91. package/src/fgca/preview-layout.ts +5 -5
  92. package/src/fgca/resolver.ts +3 -1
  93. package/src/fgca/types.ts +13 -13
  94. package/src/fgca/validate.ts +17 -14
  95. package/src/process-blueprint/index.ts +1 -0
  96. package/src/process-blueprint/layout.ts +71 -42
  97. package/src/process-blueprint/types.ts +15 -0
  98. package/src/scenarios/index.ts +2 -2
  99. package/src/scenarios/types.ts +4 -4
  100. package/src/scenarios/validate.ts +3 -3
  101. package/src/theme/themes.ts +2 -0
  102. package/src/theme/tokens.ts +3 -1
  103. package/src/webview/__tests__/entry-notations.test.ts +2 -0
  104. package/src/webview/__tests__/render-process.test.ts +538 -0
  105. package/src/webview/__tests__/render-util.test.ts +66 -0
  106. package/src/webview/entry.ts +11 -5
  107. package/src/webview/render-activities.ts +107 -67
  108. package/src/webview/render-activity-card.ts +11 -2
  109. package/src/webview/render-blocks.ts +78 -28
  110. package/src/webview/render-fgca.ts +52 -28
  111. package/src/webview/render-goals.ts +45 -23
  112. package/src/webview/render-process-blueprint.ts +156 -82
  113. package/src/webview/render-process.ts +507 -0
  114. package/src/webview/render-scenarios.ts +3 -3
  115. package/src/webview/render-util.ts +27 -14
@@ -9,10 +9,13 @@ export function validateFGCADoc(input) {
9
9
  errors.push({ code: 'MISSING_NOTATION', message: 'notation field is required' });
10
10
  return { valid: false, errors, warnings };
11
11
  }
12
- if (raw.notation !== 'fgca') {
13
- errors.push({ code: 'WRONG_NOTATION', message: `notation must be "fgca", got "${String(raw.notation)}"` });
12
+ if (raw.notation !== 'fgca' && raw.notation !== 'dgca') {
13
+ errors.push({ code: 'WRONG_NOTATION', message: `notation must be "dgca" (or legacy "fgca"), got "${String(raw.notation)}"` });
14
14
  return { valid: false, errors, warnings };
15
15
  }
16
+ if (raw.notation === 'fgca') {
17
+ warnings.push({ code: 'DEPRECATED_NOTATION', message: 'notation "fgca" is deprecated — rename to "dgca"' });
18
+ }
16
19
  if (!Array.isArray(raw.factors))
17
20
  errors.push({ code: 'SCHEMA_INVALID', message: 'factors must be an array', path: 'factors' });
18
21
  if (!Array.isArray(raw.goals))
@@ -29,7 +32,7 @@ export function validateFGCADoc(input) {
29
32
  for (const el of arr) {
30
33
  if (el && typeof el === 'object') {
31
34
  const id = el.id;
32
- if (typeof id === 'number')
35
+ if (typeof id === 'number' || typeof id === 'string')
33
36
  out.add(id);
34
37
  }
35
38
  }
@@ -45,8 +48,8 @@ export function validateFGCADoc(input) {
45
48
  continue;
46
49
  }
47
50
  const factor = f;
48
- if (typeof factor.id !== 'number')
49
- errors.push({ code: 'SCHEMA_INVALID', message: 'factor id must be a number', path: `factors[${i}]` });
51
+ if (typeof factor.id !== 'number' && typeof factor.id !== 'string')
52
+ errors.push({ code: 'SCHEMA_INVALID', message: 'factor id must be a number or string', path: `factors[${i}]` });
50
53
  if (typeof factor.name !== 'string' || !factor.name.trim())
51
54
  errors.push({ code: 'EMPTY_NAME', message: `Factor ${String(factor.id)} has empty name`, path: `factors[${i}]` });
52
55
  }
@@ -57,8 +60,8 @@ export function validateFGCADoc(input) {
57
60
  continue;
58
61
  }
59
62
  const goal = g;
60
- if (typeof goal.id !== 'number')
61
- errors.push({ code: 'SCHEMA_INVALID', message: 'goal id must be a number', path: `goals[${i}]` });
63
+ if (typeof goal.id !== 'number' && typeof goal.id !== 'string')
64
+ errors.push({ code: 'SCHEMA_INVALID', message: 'goal id must be a number or string', path: `goals[${i}]` });
62
65
  if (typeof goal.name !== 'string' || !goal.name.trim())
63
66
  errors.push({ code: 'EMPTY_NAME', message: `Goal ${String(goal.id)} has empty name`, path: `goals[${i}]` });
64
67
  const factorRefs = Array.isArray(goal.factor) ? goal.factor : [];
@@ -66,7 +69,7 @@ export function validateFGCADoc(input) {
66
69
  if (!fr || typeof fr !== 'object')
67
70
  continue;
68
71
  const fid = fr.id;
69
- if (typeof fid === 'number' && !factorIds.has(fid)) {
72
+ if ((typeof fid === 'number' || typeof fid === 'string') && !factorIds.has(fid)) {
70
73
  warnings.push({ code: 'BROKEN_REF', message: `Goal ${String(goal.id)} references missing factor ${fid}`, path: `goals[${i}].factor` });
71
74
  }
72
75
  }
@@ -78,16 +81,16 @@ export function validateFGCADoc(input) {
78
81
  continue;
79
82
  }
80
83
  const change = c;
81
- if (typeof change.id !== 'number')
82
- errors.push({ code: 'SCHEMA_INVALID', message: 'change id must be a number', path: `changes[${i}]` });
84
+ if (typeof change.id !== 'number' && typeof change.id !== 'string')
85
+ errors.push({ code: 'SCHEMA_INVALID', message: 'change id must be a number or string', path: `changes[${i}]` });
83
86
  if (typeof change.name !== 'string' || !change.name.trim())
84
87
  errors.push({ code: 'EMPTY_NAME', message: `Change ${String(change.id)} has empty name`, path: `changes[${i}]` });
85
- if (typeof change.goal_id === 'number' && !goalIds.has(change.goal_id)) {
88
+ if ((typeof change.goal_id === 'number' || typeof change.goal_id === 'string') && !goalIds.has(change.goal_id)) {
86
89
  warnings.push({ code: 'BROKEN_REF', message: `Change ${String(change.id)} references missing goal ${change.goal_id}`, path: `changes[${i}].goal_id` });
87
90
  }
88
91
  const aids = Array.isArray(change.activity_ids) ? change.activity_ids : [];
89
92
  for (const aid of aids) {
90
- if (typeof aid === 'number' && !activityIds.has(aid)) {
93
+ if ((typeof aid === 'number' || typeof aid === 'string') && !activityIds.has(aid)) {
91
94
  warnings.push({ code: 'BROKEN_REF', message: `Change ${String(change.id)} references missing activity ${aid}`, path: `changes[${i}].activity_ids` });
92
95
  }
93
96
  }
@@ -99,8 +102,8 @@ export function validateFGCADoc(input) {
99
102
  continue;
100
103
  }
101
104
  const activity = a;
102
- if (typeof activity.id !== 'number')
103
- errors.push({ code: 'SCHEMA_INVALID', message: 'activity id must be a number', path: `activities[${i}]` });
105
+ if (typeof activity.id !== 'number' && typeof activity.id !== 'string')
106
+ errors.push({ code: 'SCHEMA_INVALID', message: 'activity id must be a number or string', path: `activities[${i}]` });
104
107
  if (typeof activity.name !== 'string' || !activity.name.trim())
105
108
  errors.push({ code: 'EMPTY_NAME', message: `Activity ${String(activity.id)} has empty name`, path: `activities[${i}]` });
106
109
  }
@@ -1,4 +1,4 @@
1
- export type { AspectCategory, Stage, AspectEntry, LaneConfig, ProcessBlueprintHeader, ProcessBlueprintFile, ProcessBlueprintLayoutOptions, LegendCell, StageHeaderCell, StageTextCell, AspectPill, AspectRow, ProcessBlueprintLayout, ComplianceDecoration, ComplianceChip, ComplianceRow, ComplianceLaneConfig, ComplianceLaneAssertion, ComplianceLaneRequirement, ComplianceLaneInput, } from './types.js';
1
+ export type { AspectCategory, RowId, Stage, AspectEntry, LaneConfig, ProcessBlueprintHeader, ProcessBlueprintFile, ProcessBlueprintLayoutOptions, LegendCell, StageHeaderCell, StageTextCell, AspectPill, AspectRow, ProcessBlueprintLayout, ComplianceDecoration, ComplianceChip, ComplianceRow, ComplianceLaneConfig, ComplianceLaneAssertion, ComplianceLaneRequirement, ComplianceLaneInput, } from './types.js';
2
2
  export { validateProcessBlueprint } from './validate.js';
3
3
  export type { ValidationError as ProcessBlueprintValidationError, ValidationWarning as ProcessBlueprintValidationWarning, ValidationResult as ProcessBlueprintValidationResult, } from './validate.js';
4
4
  export { layoutProcessBlueprint } from './layout.js';
@@ -12,14 +12,19 @@ const DEFAULTS = {
12
12
  goalRowHeight: 56,
13
13
  resultRowHeight: 56,
14
14
  aspectRowMinHeight: 60,
15
- pillHeight: 28,
15
+ pillHeight: 40,
16
16
  pillGap: 4,
17
17
  cellPadding: 8,
18
- textLineHeight: 15,
19
- textCharWidth: 6.2,
18
+ // 17px gives comfortable breathing room between wrapped lines at typical
19
+ // webview font sizes (VS Code uses ~13–14px body, SVG text-secondary ≈ same).
20
+ textLineHeight: 17,
21
+ // 7.5px per char is a conservative estimate that causes wrap to trigger
22
+ // before text visually overflows the cell border.
23
+ textCharWidth: 7.5,
20
24
  cellTextPadX: 10,
21
25
  cellTextPadY: 10,
22
- maxTextLines: 6,
26
+ // No hard truncation: rows grow to fit all wrapped lines.
27
+ maxTextLines: 100,
23
28
  };
24
29
  function resolveOptions(options) {
25
30
  return { ...DEFAULTS, ...(options ?? {}) };
@@ -285,8 +290,21 @@ function deriveComplianceRow(stages, stageIndexById, input, opts, yStart) {
285
290
  export function layoutProcessBlueprint(file, options) {
286
291
  const opts = resolveOptions(options);
287
292
  const pb = file.process_blueprint;
288
- const stages = Array.isArray(pb?.stages) ? pb.stages : [];
293
+ // Filter stages by visibleStages if set
294
+ const allStages = Array.isArray(pb?.stages) ? pb.stages : [];
295
+ const stages = opts.visibleStages
296
+ ? allStages.filter(s => s?.id != null && opts.visibleStages.includes(s.id))
297
+ : allStages;
289
298
  const stageIndexById = buildStageIndex(stages);
299
+ // Row visibility from visibleRows
300
+ const visRows = opts.visibleRows;
301
+ const showGoal = !visRows || visRows.includes('goal');
302
+ const showResult = !visRows || visRows.includes('result');
303
+ const effectiveVisibleAspects = visRows
304
+ ? ASPECT_CATEGORIES.filter(c => visRows.includes(c))
305
+ : opts.visibleAspects;
306
+ const effectiveComplianceEnabled = opts.complianceLane?.enabled === true &&
307
+ (!visRows || visRows.includes('compliance'));
290
308
  // Stage headers
291
309
  const stageHeaders = stages.map((s, i) => ({
292
310
  stageIndex: i,
@@ -307,33 +325,36 @@ export function layoutProcessBlueprint(file, options) {
307
325
  };
308
326
  const goalLines = stages.map(s => wrap(s?.goal ?? ''));
309
327
  const resultLines = stages.map(s => wrap(s?.result ?? ''));
310
- const goalRowY = opts.stageHeaderHeight;
311
- const goalRowHeight = rowHeightFor(goalLines.map(l => l.length), opts.goalRowHeight);
312
- const resultRowY = goalRowY + goalRowHeight;
313
- const resultRowHeight = rowHeightFor(resultLines.map(l => l.length), opts.resultRowHeight);
314
- const goalCells = stages.map((s, i) => ({
315
- stageIndex: i,
316
- text: s?.goal ?? '',
317
- lines: goalLines[i],
318
- x: opts.legendColumnWidth + i * opts.stageColumnWidth,
319
- y: goalRowY,
320
- width: opts.stageColumnWidth,
321
- height: goalRowHeight,
322
- }));
323
- const resultCells = stages.map((s, i) => ({
324
- stageIndex: i,
325
- text: s?.result ?? '',
326
- lines: resultLines[i],
327
- x: opts.legendColumnWidth + i * opts.stageColumnWidth,
328
- y: resultRowY,
329
- width: opts.stageColumnWidth,
330
- height: resultRowHeight,
331
- }));
332
- const aspectsStartY = resultRowY + resultRowHeight;
328
+ let cursorY = opts.stageHeaderHeight;
329
+ let goalRowY = 0, goalRowHeight = 0;
330
+ let resultRowY = 0, resultRowHeight = 0;
331
+ const goalCells = [];
332
+ const resultCells = [];
333
+ if (showGoal) {
334
+ goalRowY = cursorY;
335
+ goalRowHeight = rowHeightFor(goalLines.map(l => l.length), opts.goalRowHeight);
336
+ goalCells.push(...stages.map((s, i) => ({
337
+ stageIndex: i, text: s?.goal ?? '', lines: goalLines[i],
338
+ x: opts.legendColumnWidth + i * opts.stageColumnWidth, y: goalRowY,
339
+ width: opts.stageColumnWidth, height: goalRowHeight,
340
+ })));
341
+ cursorY += goalRowHeight;
342
+ }
343
+ if (showResult) {
344
+ resultRowY = cursorY;
345
+ resultRowHeight = rowHeightFor(resultLines.map(l => l.length), opts.resultRowHeight);
346
+ resultCells.push(...stages.map((s, i) => ({
347
+ stageIndex: i, text: s?.result ?? '', lines: resultLines[i],
348
+ x: opts.legendColumnWidth + i * opts.stageColumnWidth, y: resultRowY,
349
+ width: opts.stageColumnWidth, height: resultRowHeight,
350
+ })));
351
+ cursorY += resultRowHeight;
352
+ }
353
+ const aspectsStartY = cursorY;
333
354
  // Aspect rows: only categories with at least one entry, in fixed order.
334
- // When visibleAspects is set, skip categories not in the list.
335
- const aspectCategoriesToRender = opts.visibleAspects !== undefined
336
- ? ASPECT_CATEGORIES.filter(c => opts.visibleAspects.includes(c))
355
+ // When effectiveVisibleAspects is set, skip categories not in the list.
356
+ const aspectCategoriesToRender = effectiveVisibleAspects !== undefined
357
+ ? ASPECT_CATEGORIES.filter(c => effectiveVisibleAspects.includes(c))
337
358
  : ASPECT_CATEGORIES;
338
359
  const aspectRows = [];
339
360
  let aspectCursorY = aspectsStartY;
@@ -358,6 +379,13 @@ export function layoutProcessBlueprint(file, options) {
358
379
  const x = opts.legendColumnWidth + p.startStageIndex * opts.stageColumnWidth + opts.cellPadding;
359
380
  const width = (p.endStageIndex - p.startStageIndex + 1) * opts.stageColumnWidth - 2 * opts.cellPadding;
360
381
  const y = aspectCursorY + opts.cellPadding + slot[i] * (opts.pillHeight + opts.pillGap);
382
+ const pillMaxChars = Math.max(4, Math.floor((width - 2 * opts.cellPadding) / opts.textCharWidth));
383
+ const nameWrapped = wrapText(p.name, pillMaxChars, 2);
384
+ // Show id as second line only when the name fits on one line; otherwise
385
+ // use both lines for the name so it isn't truncated.
386
+ const lines = p.id && nameWrapped.length <= 1
387
+ ? [...nameWrapped, p.id].slice(0, 2)
388
+ : nameWrapped;
361
389
  return {
362
390
  category: p.category,
363
391
  entryIndex: p.entryIndex,
@@ -369,6 +397,7 @@ export function layoutProcessBlueprint(file, options) {
369
397
  y,
370
398
  width,
371
399
  height: opts.pillHeight,
400
+ lines,
372
401
  };
373
402
  });
374
403
  aspectRows.push({
@@ -381,7 +410,7 @@ export function layoutProcessBlueprint(file, options) {
381
410
  }
382
411
  // Compliance lane — derived from assertions/requirements, rendered after aspect rows.
383
412
  let complianceRow;
384
- if (opts.complianceLane?.enabled && opts.complianceInput) {
413
+ if (effectiveComplianceEnabled && opts.complianceInput) {
385
414
  complianceRow = deriveComplianceRow(stages, stageIndexById, opts.complianceInput, opts, aspectCursorY);
386
415
  if (complianceRow) {
387
416
  aspectCursorY += complianceRow.height;
@@ -389,8 +418,8 @@ export function layoutProcessBlueprint(file, options) {
389
418
  }
390
419
  // Legend (left column labels): one entry per row.
391
420
  const legend = [
392
- { kind: 'goal', label: 'Goal', y: goalRowY, height: goalRowHeight },
393
- { kind: 'result', label: 'Result', y: resultRowY, height: resultRowHeight },
421
+ ...(showGoal ? [{ kind: 'goal', label: 'Goal', y: goalRowY, height: goalRowHeight }] : []),
422
+ ...(showResult ? [{ kind: 'result', label: 'Result', y: resultRowY, height: resultRowHeight }] : []),
394
423
  ...aspectRows.map(row => ({
395
424
  kind: 'aspect',
396
425
  category: row.category,
@@ -398,9 +427,7 @@ export function layoutProcessBlueprint(file, options) {
398
427
  y: row.y,
399
428
  height: row.height,
400
429
  })),
401
- ...(complianceRow
402
- ? [{ kind: 'compliance', label: 'Compliance', y: complianceRow.y, height: complianceRow.height }]
403
- : []),
430
+ ...(complianceRow ? [{ kind: 'compliance', label: 'Compliance', y: complianceRow.y, height: complianceRow.height }] : []),
404
431
  ];
405
432
  const totalWidth = opts.legendColumnWidth + stages.length * opts.stageColumnWidth;
406
433
  const totalHeight = aspectCursorY;
@@ -1,4 +1,5 @@
1
1
  export type AspectCategory = 'systems' | 'actors' | 'equipment' | 'information_entities';
2
+ export type RowId = 'goal' | 'result' | 'systems' | 'actors' | 'equipment' | 'information_entities' | 'compliance';
2
3
  /**
3
4
  * One of the three orthogonal decoration signals that can appear on a compliance
4
5
  * law chip (ADR 2026-06-09-compliance-impact-as-blueprint-lane §3):
@@ -176,6 +177,17 @@ export interface ProcessBlueprintLayoutOptions {
176
177
  * Pass an empty array to suppress all aspect rows.
177
178
  */
178
179
  visibleAspects?: AspectCategory[];
180
+ /**
181
+ * Restrict which rows are rendered. When absent, all rows with data are shown.
182
+ * When set, takes precedence over `visibleAspects` for aspect categories and
183
+ * overrides `complianceLane.enabled` for the compliance row.
184
+ */
185
+ visibleRows?: RowId[];
186
+ /**
187
+ * Restrict which stages (columns) are rendered by their stage ID.
188
+ * When absent, all stages are shown.
189
+ */
190
+ visibleStages?: string[];
179
191
  }
180
192
  import type { LayoutBounds } from '../geometry.js';
181
193
  export type { LayoutBounds };
@@ -216,6 +228,8 @@ export interface AspectPill {
216
228
  y: number;
217
229
  width: number;
218
230
  height: number;
231
+ /** Word-wrapped lines of `name`, pre-fitted to the pill width by the layout. */
232
+ lines: string[];
219
233
  }
220
234
  export interface AspectRow {
221
235
  category: AspectCategory;
@@ -1,2 +1,2 @@
1
- export type { ScenarioStatus, FactorRelevance, FactorView, GoalRef, CapabilityRef, ActivityRef, ProductRef, ProcessRef, ApplicationRef, ScenarioHeader, ScenarioFile, } from './types.js';
1
+ export type { ScenarioStatus, DriverRelevance, DriverView, GoalRef, CapabilityRef, ActivityRef, ProductRef, ProcessRef, ApplicationRef, ScenarioHeader, ScenarioFile, } from './types.js';
2
2
  export { validateScenario } from './validate.js';
@@ -1,8 +1,8 @@
1
1
  export type ScenarioStatus = 'Draft' | 'Active' | 'Archived';
2
- export type FactorRelevance = 'High' | 'Medium' | 'Low';
3
- export interface FactorView {
2
+ export type DriverRelevance = 'High' | 'Medium' | 'Low';
3
+ export interface DriverView {
4
4
  factor_id: string;
5
- relevance?: FactorRelevance;
5
+ relevance?: DriverRelevance;
6
6
  impact?: string;
7
7
  }
8
8
  export interface GoalRef {
@@ -30,7 +30,7 @@ export interface ScenarioHeader {
30
30
  status: ScenarioStatus;
31
31
  created_at?: string;
32
32
  vision?: string;
33
- factors_view?: FactorView[];
33
+ factors_view?: DriverView[];
34
34
  goals?: GoalRef[];
35
35
  capabilities?: CapabilityRef[];
36
36
  activities?: ActivityRef[];
@@ -62,6 +62,7 @@ function diagramVars(variant) {
62
62
  const mat = MATURITY_COLORS[variant];
63
63
  const levelVars = lv.map((c, i) => `--ts-level-${i}:${c}`).join(';') + ';';
64
64
  return [
65
+ `${CSS_VAR.layerDriver}:${lc.driver[variant]}`,
65
66
  `${CSS_VAR.layerFactor}:${lc.factor[variant]}`,
66
67
  `${CSS_VAR.layerGoal}:${lc.goal[variant]}`,
67
68
  `${CSS_VAR.layerChange}:${lc.change[variant]}`,
@@ -112,6 +113,7 @@ function diagramClassCss() {
112
113
  // contract every notation preview honours so the catalogue reads as one
113
114
  // visual family.
114
115
  return `.diagram-node{stroke:var(${cv.nodeStroke});stroke-width:1;}
116
+ .layer-driver{fill:var(${cv.layerDriver});}
115
117
  .layer-factor{fill:var(${cv.layerFactor});}
116
118
  .layer-goal{fill:var(${cv.layerGoal});}
117
119
  .layer-change{fill:var(${cv.layerChange});}
@@ -11,8 +11,13 @@ export declare const FUNCTIONAL: {
11
11
  readonly error: "#b91c1c";
12
12
  readonly info: "#1d6fa8";
13
13
  };
14
- /** Fill colors for FGCA notation columns. */
14
+ /** Fill colors for FGCA/DGCA notation columns. */
15
15
  export declare const LAYER_COLORS: {
16
+ readonly driver: {
17
+ readonly light: "#fef3c7";
18
+ readonly dark: "#292108";
19
+ readonly hc: "#f59e0b";
20
+ };
16
21
  readonly factor: {
17
22
  readonly light: "#fef3c7";
18
23
  readonly dark: "#292108";
@@ -140,6 +145,7 @@ export declare const CSS_VAR: {
140
145
  readonly statusInfoFg: "--ts-status-info-fg";
141
146
  readonly statusErrorBg: "--ts-status-error-bg";
142
147
  readonly statusErrorFg: "--ts-status-error-fg";
148
+ readonly layerDriver: "--ts-layer-driver";
143
149
  readonly layerFactor: "--ts-layer-factor";
144
150
  readonly layerGoal: "--ts-layer-goal";
145
151
  readonly layerChange: "--ts-layer-change";
@@ -11,8 +11,9 @@ export const FUNCTIONAL = {
11
11
  error: '#b91c1c',
12
12
  info: '#1d6fa8',
13
13
  };
14
- /** Fill colors for FGCA notation columns. */
14
+ /** Fill colors for FGCA/DGCA notation columns. */
15
15
  export const LAYER_COLORS = {
16
+ driver: { light: '#fef3c7', dark: '#292108', hc: '#f59e0b' },
16
17
  factor: { light: '#fef3c7', dark: '#292108', hc: '#f59e0b' },
17
18
  goal: { light: '#e0e7ff', dark: '#1e1b4b', hc: '#818cf8' },
18
19
  change: { light: '#dbeafe', dark: '#0c2042', hc: '#60a5fa' },
@@ -85,6 +86,7 @@ export const CSS_VAR = {
85
86
  statusInfoFg: '--ts-status-info-fg',
86
87
  statusErrorBg: '--ts-status-error-bg',
87
88
  statusErrorFg: '--ts-status-error-fg',
89
+ layerDriver: '--ts-layer-driver',
88
90
  layerFactor: '--ts-layer-factor',
89
91
  layerGoal: '--ts-layer-goal',
90
92
  layerChange: '--ts-layer-change',
@@ -15,6 +15,8 @@ const EXAMPLES = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../
15
15
  // example per notation is enough to prove the dispatch + renderer wiring.
16
16
  const FIXTURES = [
17
17
  { kind: 'goals', file: 'goals/strategy-2026.goals.transitrix.yaml', markup: 'svg' },
18
+ { kind: 'dgca', file: 'dgca/strategy-2026.dgca.transitrix.yaml', markup: 'svg' },
19
+ { kind: 'dga', file: 'dga/strategy-2026.dga.transitrix.yaml', markup: 'svg' },
18
20
  { kind: 'fgca', file: 'fgca/strategy-2026.fgca.transitrix.yaml', markup: 'svg' },
19
21
  { kind: 'fga', file: 'fga/strategy-2026.fga.transitrix.yaml', markup: 'svg' },
20
22
  { kind: 'activities', file: 'activities/platform-launch.activities.transitrix.yaml', markup: 'svg' },
@@ -0,0 +1 @@
1
+ export {};