@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
@@ -9,27 +9,38 @@ 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))
19
22
  errors.push({ code: 'SCHEMA_INVALID', message: 'goals must be an array', path: 'goals' });
20
23
  if (!Array.isArray(raw.changes))
21
24
  errors.push({ code: 'SCHEMA_INVALID', message: 'changes must be an array', path: 'changes' });
22
- if (!Array.isArray(raw.activities))
23
- errors.push({ code: 'SCHEMA_INVALID', message: 'activities must be an array', path: 'activities' });
25
+ // Accept canonical `actions:` or deprecated `activities:`; warn on the latter.
26
+ const hasActions = Array.isArray(raw.actions);
27
+ const hasActivities = Array.isArray(raw.activities);
28
+ if (!hasActions && !hasActivities) {
29
+ errors.push({ code: 'SCHEMA_INVALID', message: 'actions must be an array', path: 'actions' });
30
+ }
31
+ else if ('activities' in raw) {
32
+ warnings.push({ code: 'DEPRECATED_NOTATION', message: '"activities" key is deprecated — rename to "actions"' });
33
+ }
24
34
  if (errors.length > 0)
25
35
  return { valid: false, errors, warnings };
36
+ const actionsArr = (hasActions ? raw.actions : raw.activities);
26
37
  const doc = raw;
27
38
  const collectIds = (arr) => {
28
39
  const out = new Set();
29
40
  for (const el of arr) {
30
41
  if (el && typeof el === 'object') {
31
42
  const id = el.id;
32
- if (typeof id === 'number')
43
+ if (typeof id === 'number' || typeof id === 'string')
33
44
  out.add(id);
34
45
  }
35
46
  }
@@ -37,7 +48,7 @@ export function validateFGCADoc(input) {
37
48
  };
38
49
  const factorIds = collectIds(doc.factors);
39
50
  const goalIds = collectIds(doc.goals);
40
- const activityIds = collectIds(doc.activities);
51
+ const activityIds = collectIds(actionsArr);
41
52
  for (let i = 0; i < doc.factors.length; i++) {
42
53
  const f = doc.factors[i];
43
54
  if (!f || typeof f !== 'object') {
@@ -45,8 +56,8 @@ export function validateFGCADoc(input) {
45
56
  continue;
46
57
  }
47
58
  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}]` });
59
+ if (typeof factor.id !== 'number' && typeof factor.id !== 'string')
60
+ errors.push({ code: 'SCHEMA_INVALID', message: 'factor id must be a number or string', path: `factors[${i}]` });
50
61
  if (typeof factor.name !== 'string' || !factor.name.trim())
51
62
  errors.push({ code: 'EMPTY_NAME', message: `Factor ${String(factor.id)} has empty name`, path: `factors[${i}]` });
52
63
  }
@@ -57,8 +68,8 @@ export function validateFGCADoc(input) {
57
68
  continue;
58
69
  }
59
70
  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}]` });
71
+ if (typeof goal.id !== 'number' && typeof goal.id !== 'string')
72
+ errors.push({ code: 'SCHEMA_INVALID', message: 'goal id must be a number or string', path: `goals[${i}]` });
62
73
  if (typeof goal.name !== 'string' || !goal.name.trim())
63
74
  errors.push({ code: 'EMPTY_NAME', message: `Goal ${String(goal.id)} has empty name`, path: `goals[${i}]` });
64
75
  const factorRefs = Array.isArray(goal.factor) ? goal.factor : [];
@@ -66,7 +77,7 @@ export function validateFGCADoc(input) {
66
77
  if (!fr || typeof fr !== 'object')
67
78
  continue;
68
79
  const fid = fr.id;
69
- if (typeof fid === 'number' && !factorIds.has(fid)) {
80
+ if ((typeof fid === 'number' || typeof fid === 'string') && !factorIds.has(fid)) {
70
81
  warnings.push({ code: 'BROKEN_REF', message: `Goal ${String(goal.id)} references missing factor ${fid}`, path: `goals[${i}].factor` });
71
82
  }
72
83
  }
@@ -78,31 +89,31 @@ export function validateFGCADoc(input) {
78
89
  continue;
79
90
  }
80
91
  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}]` });
92
+ if (typeof change.id !== 'number' && typeof change.id !== 'string')
93
+ errors.push({ code: 'SCHEMA_INVALID', message: 'change id must be a number or string', path: `changes[${i}]` });
83
94
  if (typeof change.name !== 'string' || !change.name.trim())
84
95
  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)) {
96
+ if ((typeof change.goal_id === 'number' || typeof change.goal_id === 'string') && !goalIds.has(change.goal_id)) {
86
97
  warnings.push({ code: 'BROKEN_REF', message: `Change ${String(change.id)} references missing goal ${change.goal_id}`, path: `changes[${i}].goal_id` });
87
98
  }
88
99
  const aids = Array.isArray(change.activity_ids) ? change.activity_ids : [];
89
100
  for (const aid of aids) {
90
- if (typeof aid === 'number' && !activityIds.has(aid)) {
101
+ if ((typeof aid === 'number' || typeof aid === 'string') && !activityIds.has(aid)) {
91
102
  warnings.push({ code: 'BROKEN_REF', message: `Change ${String(change.id)} references missing activity ${aid}`, path: `changes[${i}].activity_ids` });
92
103
  }
93
104
  }
94
105
  }
95
- for (let i = 0; i < doc.activities.length; i++) {
96
- const a = doc.activities[i];
106
+ for (let i = 0; i < actionsArr.length; i++) {
107
+ const a = actionsArr[i];
97
108
  if (!a || typeof a !== 'object') {
98
- errors.push({ code: 'SCHEMA_INVALID', message: 'activity entry must be an object', path: `activities[${i}]` });
109
+ errors.push({ code: 'SCHEMA_INVALID', message: 'action entry must be an object', path: `actions[${i}]` });
99
110
  continue;
100
111
  }
101
- 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}]` });
104
- if (typeof activity.name !== 'string' || !activity.name.trim())
105
- errors.push({ code: 'EMPTY_NAME', message: `Activity ${String(activity.id)} has empty name`, path: `activities[${i}]` });
112
+ const action = a;
113
+ if (typeof action.id !== 'number' && typeof action.id !== 'string')
114
+ errors.push({ code: 'SCHEMA_INVALID', message: 'action id must be a number or string', path: `actions[${i}]` });
115
+ if (typeof action.name !== 'string' || !action.name.trim())
116
+ errors.push({ code: 'EMPTY_NAME', message: `Action ${String(action.id)} has empty name`, path: `actions[${i}]` });
106
117
  }
107
118
  return { valid: errors.length === 0, errors, warnings };
108
119
  }
@@ -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[];
@@ -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';
@@ -1,2 +1,2 @@
1
- export { BRAND, FUNCTIONAL, LAYER_COLORS, LEVEL_COLORS, MATURITY_COLORS, STRUCTURAL, TYPOGRAPHY, CSS_VAR, getBaseResetCss } from './tokens.js';
1
+ export { BRAND, FUNCTIONAL, LAYER_COLORS, LEVEL_COLORS, MATURITY_COLORS, TREE_LEVEL_COLORS, TREE_MATURITY_COLORS, STRUCTURAL, TYPOGRAPHY, CSS_VAR, getBaseResetCss } from './tokens.js';
2
2
  export { generateWebviewCss, generateSvgEmbedCss } from './themes.js';
@@ -1,4 +1,4 @@
1
- import { LAYER_COLORS, LEVEL_COLORS, MATURITY_COLORS, STRUCTURAL, TYPOGRAPHY, CSS_VAR, getBaseResetCss, } from './tokens.js';
1
+ import { LAYER_COLORS, LEVEL_COLORS, MATURITY_COLORS, TREE_LEVEL_COLORS, TREE_MATURITY_COLORS, STRUCTURAL, TYPOGRAPHY, CSS_VAR, getBaseResetCss, } from './tokens.js';
2
2
  const TRANSITRIX_LIGHT = {
3
3
  bg: '#ffffff',
4
4
  bgSurface: '#f8fafc',
@@ -60,8 +60,21 @@ function diagramVars(variant) {
60
60
  const sc = STRUCTURAL;
61
61
  const lv = LEVEL_COLORS[variant];
62
62
  const mat = MATURITY_COLORS[variant];
63
+ const treeLevel = TREE_LEVEL_COLORS;
64
+ const treeMat = TREE_MATURITY_COLORS[variant];
63
65
  const levelVars = lv.map((c, i) => `--ts-level-${i}:${c}`).join(';') + ';';
66
+ const treeVars = [
67
+ `${CSS_VAR.treeLevel0}:${treeLevel.band0[variant]}`,
68
+ `${CSS_VAR.treeLevel1}:${treeLevel.band1[variant]}`,
69
+ `${CSS_VAR.treeLevel2}:${treeLevel.band2[variant]}`,
70
+ `${CSS_VAR.treeMaturity1}:${treeMat[0]}`,
71
+ `${CSS_VAR.treeMaturity2}:${treeMat[1]}`,
72
+ `${CSS_VAR.treeMaturity3}:${treeMat[2]}`,
73
+ `${CSS_VAR.treeMaturity4}:${treeMat[3]}`,
74
+ `${CSS_VAR.treeMaturity5}:${treeMat[4]}`,
75
+ ].join(';') + ';';
64
76
  return [
77
+ `${CSS_VAR.layerDriver}:${lc.driver[variant]}`,
65
78
  `${CSS_VAR.layerFactor}:${lc.factor[variant]}`,
66
79
  `${CSS_VAR.layerGoal}:${lc.goal[variant]}`,
67
80
  `${CSS_VAR.layerChange}:${lc.change[variant]}`,
@@ -76,7 +89,7 @@ function diagramVars(variant) {
76
89
  `${CSS_VAR.maturity3}:${mat[2]}`,
77
90
  `${CSS_VAR.maturity4}:${mat[3]}`,
78
91
  `${CSS_VAR.maturity5}:${mat[4]}`,
79
- ].join(';') + ';' + levelVars;
92
+ ].join(';') + ';' + levelVars + treeVars;
80
93
  }
81
94
  /** CSS for the webview shell (toolbar, canvas). */
82
95
  function shellCss() {
@@ -112,6 +125,7 @@ function diagramClassCss() {
112
125
  // contract every notation preview honours so the catalogue reads as one
113
126
  // visual family.
114
127
  return `.diagram-node{stroke:var(${cv.nodeStroke});stroke-width:1;}
128
+ .layer-driver{fill:var(${cv.layerDriver});}
115
129
  .layer-factor{fill:var(${cv.layerFactor});}
116
130
  .layer-goal{fill:var(${cv.layerGoal});}
117
131
  .layer-change{fill:var(${cv.layerChange});}
@@ -134,7 +148,18 @@ ${levelRules}
134
148
  .compliance-deadline{fill:var(${cv.statusErrorBg});stroke:var(${cv.statusErrorFg});}
135
149
  .compliance-deadline text{fill:var(${cv.statusErrorFg});}
136
150
  .compliance-badge{fill:var(${cv.statusErrorFg});}
137
- .compliance-badge-text{fill:var(${cv.textInverse});font-family:${t.fontFamily};font-size:8px;font-weight:700;}`;
151
+ .compliance-badge-text{fill:var(${cv.textInverse});font-family:${t.fontFamily};font-size:8px;font-weight:700;}
152
+ .tree-level-0{fill:var(${cv.treeLevel0});}
153
+ .tree-level-1{fill:var(${cv.treeLevel1});}
154
+ .tree-level-2{fill:var(${cv.treeLevel2});}
155
+ .tree-maturity-1{fill:var(${cv.treeMaturity1});}
156
+ .tree-maturity-2{fill:var(${cv.treeMaturity2});}
157
+ .tree-maturity-3{fill:var(${cv.treeMaturity3});}
158
+ .tree-maturity-4{fill:var(${cv.treeMaturity4});}
159
+ .tree-maturity-5{fill:var(${cv.treeMaturity5});}
160
+ .tree-collapse-btn{cursor:pointer;fill:white;stroke:var(${cv.nodeStroke});stroke-width:1.5;}
161
+ .tree-collapse-btn:hover{fill:var(${cv.bgElevated});}
162
+ .tree-collapse-lbl{fill:var(${cv.textSecondary});font-family:${t.fontFamily};font-size:12px;font-weight:700;dominant-baseline:central;pointer-events:none;}`;
138
163
  }
139
164
  /**
140
165
  * Generates a self-contained CSS block for embedding inside a standalone SVG file.
@@ -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";
@@ -77,6 +82,38 @@ export declare const MATURITY_COLORS: {
77
82
  readonly dark: readonly ["#7f1d1d", "#92400e", "#854d0e", "#3f6212", "#14532d"];
78
83
  readonly hc: readonly ["#f87171", "#fb923c", "#facc15", "#a3e635", "#4ade80"];
79
84
  };
85
+ /**
86
+ * DSM-matching capability tree node fill colors, grouped by depth band.
87
+ * band0: depth 0–2 (pink)
88
+ * band1: depth 3–4 (yellow)
89
+ * band2: depth 5+ (blue)
90
+ */
91
+ export declare const TREE_LEVEL_COLORS: {
92
+ readonly band0: {
93
+ readonly light: "#fee0e0";
94
+ readonly dark: "#3d0f0f";
95
+ readonly hc: "#ff9999";
96
+ };
97
+ readonly band1: {
98
+ readonly light: "#ffffcb";
99
+ readonly dark: "#3a3500";
100
+ readonly hc: "#ffff88";
101
+ };
102
+ readonly band2: {
103
+ readonly light: "#c2f0ff";
104
+ readonly dark: "#003d4d";
105
+ readonly hc: "#66ccff";
106
+ };
107
+ };
108
+ /**
109
+ * DSM-matching maturity badge fills for the capability tree (L1–L5).
110
+ * Distinct from MATURITY_COLORS which drives the cards / compliance views.
111
+ */
112
+ export declare const TREE_MATURITY_COLORS: {
113
+ readonly light: readonly ["#c1c1c1", "#ff3d67", "#ff9a59", "#fdef59", "#83cca3"];
114
+ readonly dark: readonly ["#555555", "#cc1040", "#d06020", "#c8c000", "#4d9970"];
115
+ readonly hc: readonly ["#cccccc", "#ff6699", "#ffbb77", "#ffff44", "#88ddaa"];
116
+ };
80
117
  /**
81
118
  * Typography hierarchy. All previews resolve text styling from these roles
82
119
  * — never from inline font-* attributes on individual <text> elements.
@@ -140,6 +177,7 @@ export declare const CSS_VAR: {
140
177
  readonly statusInfoFg: "--ts-status-info-fg";
141
178
  readonly statusErrorBg: "--ts-status-error-bg";
142
179
  readonly statusErrorFg: "--ts-status-error-fg";
180
+ readonly layerDriver: "--ts-layer-driver";
143
181
  readonly layerFactor: "--ts-layer-factor";
144
182
  readonly layerGoal: "--ts-layer-goal";
145
183
  readonly layerChange: "--ts-layer-change";
@@ -154,5 +192,13 @@ export declare const CSS_VAR: {
154
192
  readonly maturity3: "--ts-maturity-3";
155
193
  readonly maturity4: "--ts-maturity-4";
156
194
  readonly maturity5: "--ts-maturity-5";
195
+ readonly treeLevel0: "--ts-tree-level-0";
196
+ readonly treeLevel1: "--ts-tree-level-1";
197
+ readonly treeLevel2: "--ts-tree-level-2";
198
+ readonly treeMaturity1: "--ts-tree-maturity-1";
199
+ readonly treeMaturity2: "--ts-tree-maturity-2";
200
+ readonly treeMaturity3: "--ts-tree-maturity-3";
201
+ readonly treeMaturity4: "--ts-tree-maturity-4";
202
+ readonly treeMaturity5: "--ts-tree-maturity-5";
157
203
  };
158
204
  export declare function getBaseResetCss(): string;
@@ -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' },
@@ -41,6 +42,26 @@ export const MATURITY_COLORS = {
41
42
  dark: ['#7f1d1d', '#92400e', '#854d0e', '#3f6212', '#14532d'],
42
43
  hc: ['#f87171', '#fb923c', '#facc15', '#a3e635', '#4ade80'],
43
44
  };
45
+ /**
46
+ * DSM-matching capability tree node fill colors, grouped by depth band.
47
+ * band0: depth 0–2 (pink)
48
+ * band1: depth 3–4 (yellow)
49
+ * band2: depth 5+ (blue)
50
+ */
51
+ export const TREE_LEVEL_COLORS = {
52
+ band0: { light: '#fee0e0', dark: '#3d0f0f', hc: '#ff9999' },
53
+ band1: { light: '#ffffcb', dark: '#3a3500', hc: '#ffff88' },
54
+ band2: { light: '#c2f0ff', dark: '#003d4d', hc: '#66ccff' },
55
+ };
56
+ /**
57
+ * DSM-matching maturity badge fills for the capability tree (L1–L5).
58
+ * Distinct from MATURITY_COLORS which drives the cards / compliance views.
59
+ */
60
+ export const TREE_MATURITY_COLORS = {
61
+ light: ['#c1c1c1', '#ff3d67', '#ff9a59', '#fdef59', '#83cca3'],
62
+ dark: ['#555555', '#cc1040', '#d06020', '#c8c000', '#4d9970'],
63
+ hc: ['#cccccc', '#ff6699', '#ffbb77', '#ffff44', '#88ddaa'],
64
+ };
44
65
  /**
45
66
  * Typography hierarchy. All previews resolve text styling from these roles
46
67
  * — never from inline font-* attributes on individual <text> elements.
@@ -85,6 +106,7 @@ export const CSS_VAR = {
85
106
  statusInfoFg: '--ts-status-info-fg',
86
107
  statusErrorBg: '--ts-status-error-bg',
87
108
  statusErrorFg: '--ts-status-error-fg',
109
+ layerDriver: '--ts-layer-driver',
88
110
  layerFactor: '--ts-layer-factor',
89
111
  layerGoal: '--ts-layer-goal',
90
112
  layerChange: '--ts-layer-change',
@@ -99,6 +121,14 @@ export const CSS_VAR = {
99
121
  maturity3: '--ts-maturity-3',
100
122
  maturity4: '--ts-maturity-4',
101
123
  maturity5: '--ts-maturity-5',
124
+ treeLevel0: '--ts-tree-level-0',
125
+ treeLevel1: '--ts-tree-level-1',
126
+ treeLevel2: '--ts-tree-level-2',
127
+ treeMaturity1: '--ts-tree-maturity-1',
128
+ treeMaturity2: '--ts-tree-maturity-2',
129
+ treeMaturity3: '--ts-tree-maturity-3',
130
+ treeMaturity4: '--ts-tree-maturity-4',
131
+ treeMaturity5: '--ts-tree-maturity-5',
102
132
  };
103
133
  export function getBaseResetCss() {
104
134
  return `*,*:before,*:after{box-sizing:border-box;}html,body{margin:0;padding:0;}`;
@@ -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 {};