@transitrix/diagrams 1.0.0 → 1.0.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/LICENSE +21 -21
- package/README.md +47 -47
- package/dist/__tests__/edge-path.test.js +8 -0
- package/dist/activity-card/__tests__/example.test.js +20 -1
- package/dist/activity-card/__tests__/layout.test.js +102 -11
- package/dist/activity-card/__tests__/resolver.test.js +139 -6
- package/dist/activity-card/index.d.ts +1 -1
- package/dist/activity-card/layout.d.ts +1 -1
- package/dist/activity-card/layout.js +220 -103
- package/dist/activity-card/resolver.js +107 -26
- package/dist/activity-card/types.d.ts +150 -27
- package/dist/activity-card/validate.js +3 -0
- package/dist/actor/__tests__/validate.test.js +63 -31
- package/dist/actor/index.d.ts +1 -1
- package/dist/actor/index.js +1 -1
- package/dist/actor/types.d.ts +7 -3
- package/dist/actor/types.js +17 -3
- package/dist/actor/validate.js +55 -26
- package/dist/assertion/types.d.ts +3 -1
- package/dist/assertion/types.js +1 -1
- package/dist/change/__tests__/validate.test.js +40 -34
- package/dist/change/index.d.ts +1 -0
- package/dist/change/types.d.ts +1 -1
- package/dist/change/types.js +2 -2
- package/dist/change/validate.d.ts +6 -1
- package/dist/change/validate.js +68 -41
- package/dist/compliance/__tests__/classify.test.js +34 -0
- package/dist/compliance/__tests__/coverage-metric.test.js +146 -46
- package/dist/compliance/__tests__/html.test.js +2 -2
- package/dist/compliance/__tests__/impact.test.js +311 -6
- package/dist/compliance/classify.d.ts +6 -0
- package/dist/compliance/classify.js +9 -1
- package/dist/compliance/coverage-metric.d.ts +31 -11
- package/dist/compliance/coverage-metric.js +149 -39
- package/dist/compliance/html.js +77 -74
- package/dist/compliance/impact.d.ts +77 -16
- package/dist/compliance/impact.js +193 -25
- package/dist/compliance/index.d.ts +2 -2
- package/dist/compliance/index.js +1 -1
- package/dist/compliance/markdown.js +1 -0
- package/dist/compliance/types.d.ts +25 -6
- package/dist/edge-path.d.ts +8 -4
- package/dist/edge-path.js +11 -6
- package/dist/factor/__tests__/validate.test.js +74 -45
- package/dist/factor/index.d.ts +3 -2
- package/dist/factor/index.js +1 -1
- package/dist/factor/types.d.ts +4 -7
- package/dist/factor/types.js +10 -5
- package/dist/factor/validate.d.ts +6 -1
- package/dist/factor/validate.js +68 -50
- package/dist/fgca/__tests__/resolver.test.js +185 -0
- package/dist/fgca/index.d.ts +2 -0
- package/dist/fgca/index.js +1 -0
- package/dist/fgca/resolver.d.ts +49 -0
- package/dist/fgca/resolver.js +124 -0
- package/dist/goals/index.d.ts +1 -1
- package/dist/index.d.ts +5 -0
- package/dist/index.js +5 -0
- package/dist/process-blueprint/__tests__/layout.test.js +37 -0
- package/dist/process-blueprint/layout.js +5 -1
- package/dist/process-blueprint/types.d.ts +22 -1
- package/dist/stakeholder/__tests__/validate.test.js +49 -47
- package/dist/stakeholder/index.d.ts +1 -0
- package/dist/stakeholder/types.d.ts +3 -4
- package/dist/stakeholder/types.js +2 -3
- package/dist/stakeholder/validate.d.ts +6 -1
- package/dist/stakeholder/validate.js +71 -50
- package/dist/target-state/__tests__/validate.test.js +43 -27
- package/dist/target-state/index.d.ts +2 -0
- package/dist/target-state/index.js +1 -0
- package/dist/target-state/types.d.ts +6 -1
- package/dist/target-state/types.js +8 -7
- package/dist/target-state/validate.d.ts +6 -1
- package/dist/target-state/validate.js +69 -45
- package/dist/theme/themes.js +69 -69
- package/dist/webview/__tests__/entry.test.js +11 -11
- package/dist/webview/render-activities.d.ts +3 -1
- package/dist/webview/render-activities.js +2 -2
- package/dist/webview/render-activity-card.js +104 -77
- package/dist/webview/render-fgca.d.ts +4 -0
- package/dist/webview/render-fgca.js +2 -5
- package/dist/webview/render-goals.d.ts +1 -0
- package/dist/webview/render-goals.js +15 -15
- package/dist/webview/render-process-blueprint.js +10 -10
- package/package.json +40 -40
- package/src/__tests__/edge-path.test.ts +58 -46
- package/src/__tests__/scope.test.ts +27 -27
- package/src/__tests__/yaml-normalize.test.ts +158 -158
- package/src/activities/__tests__/cpm.test.ts +164 -164
- package/src/activities/__tests__/gantt.test.ts +245 -245
- package/src/activities/__tests__/layout.test.ts +112 -112
- package/src/activities/__tests__/validate.test.ts +409 -409
- package/src/activities/cpm.ts +95 -95
- package/src/activities/gantt.ts +276 -276
- package/src/activities/index.ts +28 -28
- package/src/activities/layout.ts +112 -112
- package/src/activities/types.ts +148 -148
- package/src/activities/validate.ts +357 -357
- package/src/activity-card/__tests__/example.test.ts +85 -66
- package/src/activity-card/__tests__/layout.test.ts +169 -63
- package/src/activity-card/__tests__/resolver.test.ts +307 -161
- package/src/activity-card/__tests__/validate.test.ts +131 -131
- package/src/activity-card/index.ts +38 -32
- package/src/activity-card/layout.ts +324 -194
- package/src/activity-card/resolver.ts +415 -337
- package/src/activity-card/types.ts +376 -237
- package/src/activity-card/validate.ts +162 -158
- package/src/actor/__tests__/validate.test.ts +70 -31
- package/src/actor/index.ts +1 -1
- package/src/actor/types.ts +21 -7
- package/src/actor/validate.ts +59 -27
- package/src/applications/__tests__/validate.test.ts +256 -256
- package/src/applications/index.ts +11 -11
- package/src/applications/types.ts +43 -43
- package/src/applications/validate.ts +131 -131
- package/src/assertion/__tests__/example.test.ts +37 -37
- package/src/assertion/__tests__/validate.test.ts +142 -142
- package/src/assertion/index.ts +12 -12
- package/src/assertion/types.ts +60 -58
- package/src/assertion/validate.ts +145 -145
- package/src/blocks/__tests__/layout.test.ts +169 -169
- package/src/blocks/__tests__/validate.test.ts +260 -260
- package/src/blocks/index.ts +17 -17
- package/src/blocks/layout.ts +186 -186
- package/src/blocks/types.ts +62 -62
- package/src/blocks/validate.ts +206 -206
- package/src/capability-map/__tests__/validate.test.ts +238 -238
- package/src/capability-map/index.ts +8 -8
- package/src/capability-map/types.ts +29 -29
- package/src/capability-map/validate.ts +127 -127
- package/src/change/__tests__/validate.test.ts +43 -34
- package/src/change/index.ts +1 -0
- package/src/change/types.ts +5 -3
- package/src/change/validate.ts +75 -43
- package/src/compliance/__tests__/classify.test.ts +64 -27
- package/src/compliance/__tests__/confidence.test.ts +98 -98
- package/src/compliance/__tests__/coverage-metric.test.ts +300 -188
- package/src/compliance/__tests__/demo-scenario.test.ts +273 -273
- package/src/compliance/__tests__/example.test.ts +57 -57
- package/src/compliance/__tests__/gap-report.test.ts +128 -128
- package/src/compliance/__tests__/html.test.ts +141 -141
- package/src/compliance/__tests__/impact.test.ts +840 -481
- package/src/compliance/__tests__/markdown.test.ts +94 -94
- package/src/compliance/__tests__/views.test.ts +64 -64
- package/src/compliance/classify.ts +100 -84
- package/src/compliance/confidence.ts +69 -69
- package/src/compliance/coverage-metric.ts +404 -263
- package/src/compliance/gap-report.ts +94 -94
- package/src/compliance/html.ts +298 -295
- package/src/compliance/impact.ts +861 -677
- package/src/compliance/index.ts +57 -56
- package/src/compliance/markdown.ts +136 -135
- package/src/compliance/reverse-index.ts +33 -33
- package/src/compliance/types.ts +136 -120
- package/src/compliance/views.ts +46 -46
- package/src/compliance-matrix/__tests__/build.test.ts +200 -200
- package/src/compliance-matrix/__tests__/example.test.ts +57 -57
- package/src/compliance-matrix/index.ts +11 -11
- package/src/compliance-matrix/types.ts +102 -102
- package/src/edge-path.ts +50 -44
- package/src/factor/__tests__/validate.test.ts +80 -45
- package/src/factor/index.ts +3 -7
- package/src/factor/types.ts +14 -18
- package/src/factor/validate.ts +77 -51
- package/src/fgca/__tests__/chain-table.test.ts +172 -172
- package/src/fgca/__tests__/layout.test.ts +179 -179
- package/src/fgca/__tests__/parse-canonical.test.ts +248 -248
- package/src/fgca/__tests__/preview-layout.test.ts +124 -124
- package/src/fgca/__tests__/resolver.test.ts +209 -0
- package/src/fgca/__tests__/validate.test.ts +142 -142
- package/src/fgca/chain-table.ts +225 -225
- package/src/fgca/index.ts +47 -45
- package/src/fgca/layout.ts +204 -204
- package/src/fgca/nodeTypes.ts +11 -11
- package/src/fgca/nodes/FGCAActivityNode.tsx +65 -65
- package/src/fgca/nodes/FGCAChangeNode.tsx +64 -64
- package/src/fgca/nodes/FGCAFactorNode.tsx +64 -64
- package/src/fgca/nodes/FGCAGoalNode.tsx +72 -72
- package/src/fgca/parse-canonical.ts +400 -400
- package/src/fgca/preview-layout.ts +217 -217
- package/src/fgca/resolver.ts +157 -0
- package/src/fgca/types.ts +50 -50
- package/src/fgca/validate.ts +126 -126
- package/src/geometry.ts +9 -9
- package/src/goals/__tests__/layout.test.ts +175 -175
- package/src/goals/__tests__/mutations.test.ts +111 -111
- package/src/goals/__tests__/parse-canonical.test.ts +145 -145
- package/src/goals/__tests__/validate.test.ts +202 -202
- package/src/goals/index.ts +20 -21
- package/src/goals/layout.ts +172 -172
- package/src/goals/mutations.ts +123 -123
- package/src/goals/parse-canonical.ts +286 -286
- package/src/goals/types.ts +85 -85
- package/src/goals/validate.ts +136 -136
- package/src/index.ts +28 -23
- package/src/process-blueprint/__tests__/compliance-lane.test.ts +320 -320
- package/src/process-blueprint/__tests__/layout.test.ts +240 -195
- package/src/process-blueprint/__tests__/validate.test.ts +310 -310
- package/src/process-blueprint/index.ts +31 -31
- package/src/process-blueprint/layout.ts +535 -531
- package/src/process-blueprint/types.ts +263 -242
- package/src/process-blueprint/validate.ts +181 -181
- package/src/process-map/__tests__/validate.test.ts +223 -223
- package/src/process-map/index.ts +10 -10
- package/src/process-map/types.ts +36 -36
- package/src/process-map/validate.ts +135 -135
- package/src/products/__tests__/validate.test.ts +239 -239
- package/src/products/index.ts +9 -9
- package/src/products/types.ts +31 -31
- package/src/products/validate.ts +112 -112
- package/src/repo-validate/__tests__/validate-repo.test.ts +149 -149
- package/src/repo-validate/index.ts +2 -2
- package/src/repo-validate/types.ts +50 -50
- package/src/repo-validate/validate-repo.ts +196 -196
- package/src/requirement/__tests__/example.test.ts +24 -24
- package/src/requirement/__tests__/validate.test.ts +96 -96
- package/src/requirement/index.ts +4 -4
- package/src/requirement/types.ts +35 -35
- package/src/requirement/validate.ts +92 -92
- package/src/scenarios/__tests__/validate.test.ts +209 -209
- package/src/scenarios/index.ts +15 -15
- package/src/scenarios/types.ts +37 -37
- package/src/scenarios/validate.ts +122 -122
- package/src/schema-version.ts +16 -16
- package/src/scope.ts +46 -46
- package/src/stakeholder/__tests__/validate.test.ts +51 -47
- package/src/stakeholder/index.ts +1 -0
- package/src/stakeholder/types.ts +7 -7
- package/src/stakeholder/validate.ts +79 -52
- package/src/target-state/__tests__/validate.test.ts +48 -27
- package/src/target-state/index.ts +2 -0
- package/src/target-state/types.ts +11 -7
- package/src/target-state/validate.ts +76 -46
- package/src/theme/index.ts +4 -4
- package/src/theme/themes.ts +237 -237
- package/src/theme/tokens.ts +133 -133
- package/src/typed-id.ts +56 -56
- package/src/validation-types.ts +36 -36
- package/src/webview/__tests__/entry-notations.test.ts +77 -77
- package/src/webview/__tests__/entry.test.ts +64 -64
- package/src/webview/__tests__/render-goals.test.ts +72 -72
- package/src/webview/entry.ts +292 -292
- package/src/webview/render-activities.ts +5 -3
- package/src/webview/render-activity-card.ts +266 -234
- package/src/webview/render-fgca.ts +6 -5
- package/src/webview/render-goals.ts +111 -110
- package/src/webview/render-process-blueprint.ts +207 -207
- package/src/webview/styles.css +322 -323
- package/src/yaml-normalize.ts +56 -56
- package/dist/element-envelope.d.ts +0 -15
- package/dist/element-envelope.js +0 -55
- package/dist/issues/__tests__/layout.test.js +0 -62
- package/dist/issues/__tests__/validate.test.d.ts +0 -1
- package/dist/issues/__tests__/validate.test.js +0 -82
- package/dist/issues/index.d.ts +0 -4
- package/dist/issues/index.js +0 -2
- package/dist/issues/layout.d.ts +0 -10
- package/dist/issues/layout.js +0 -82
- package/dist/issues/types.d.ts +0 -61
- package/dist/issues/types.js +0 -6
- package/dist/issues/validate.d.ts +0 -3
- package/dist/issues/validate.js +0 -143
- package/dist/webview/transitrix-render.css +0 -323
- package/dist/webview/transitrix-render.js +0 -6452
- package/dist/webview/transitrix-render.js.map +0 -7
- package/src/element-envelope.ts +0 -76
- /package/dist/{issues/__tests__/layout.test.d.ts → fgca/__tests__/resolver.test.d.ts} +0 -0
|
@@ -1,59 +1,83 @@
|
|
|
1
|
-
// TARGET_STATE validator — methodology notations/ELEMENT_PRIMITIVES.md §7.
|
|
1
|
+
// TARGET_STATE validator — methodology notations/ELEMENT_PRIMITIVES.md §7.18.
|
|
2
2
|
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
3
|
+
// Codes:
|
|
4
|
+
// TSTATE-001 — shape / id grammar / required envelope fields.
|
|
5
|
+
// TSTATE-002 — composition list entry malformed or wrong TYPE.
|
|
6
|
+
// TSTATE-003 — forbidden inline `goals` field — TARGET_STATE → GOAL
|
|
7
|
+
// satisfaction is a `target_state_satisfies_goal` REL
|
|
8
|
+
// (elements/17-relations.md §3), never inline here.
|
|
9
|
+
//
|
|
10
|
+
// CAPABILITY ids use the V/H sub-grammar (IDS_AND_REFERENCES.md §2): the
|
|
11
|
+
// integer-terminal isCanonicalId would reject them. So the composition entry
|
|
12
|
+
// check accepts any id whose TYPE prefix matches; existence is enforced only
|
|
13
|
+
// when a `CanonCatalog` is supplied.
|
|
14
|
+
import { typeOfId, isCanonicalIdOfType } from '../typed-id.js';
|
|
15
|
+
import { TARGET_STATE_COMPOSITION_FIELDS } from './types.js';
|
|
16
|
+
const REQUIRED_STRING_FIELDS = [
|
|
17
|
+
'name', 'admitted_at', 'admitted_by', 'valid_from',
|
|
18
|
+
];
|
|
19
|
+
export function validateTargetState(input, options = {}) {
|
|
15
20
|
const errors = [];
|
|
16
21
|
const warnings = [];
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
idType: 'TARGET_STATE',
|
|
20
|
-
code: 'TARGET_STATE-001',
|
|
21
|
-
});
|
|
22
|
-
errors.push(...envErrors);
|
|
23
|
-
if (!record)
|
|
22
|
+
if (input === null || typeof input !== 'object' || Array.isArray(input)) {
|
|
23
|
+
errors.push({ code: 'TSTATE-001', message: 'TargetState must be a YAML mapping.' });
|
|
24
24
|
return { valid: false, errors, warnings };
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
25
|
+
}
|
|
26
|
+
const t = input;
|
|
27
|
+
// TSTATE-001 — id grammar + notation tag + envelope.
|
|
28
|
+
if (!isCanonicalIdOfType(t.id, 'TARGET_STATE')) {
|
|
29
|
+
errors.push({ code: 'TSTATE-001', message: `id "${String(t.id)}" must match TARGET_STATE-[<middle>-]<INTEGER>.`, path: 'id' });
|
|
30
|
+
}
|
|
31
|
+
if (t.notation !== 'target-state') {
|
|
32
|
+
errors.push({ code: 'TSTATE-001', message: 'notation must be the fixed value "target-state".', path: 'notation' });
|
|
33
|
+
}
|
|
34
|
+
if (t.zone !== undefined && t.zone !== 'canon') {
|
|
35
|
+
errors.push({ code: 'TSTATE-001', message: 'zone must be "canon" for a TARGET_STATE.', path: 'zone' });
|
|
36
|
+
}
|
|
37
|
+
else if (t.zone === undefined) {
|
|
38
|
+
errors.push({ code: 'TSTATE-001', message: 'zone is required.', path: 'zone' });
|
|
39
|
+
}
|
|
40
|
+
for (const field of REQUIRED_STRING_FIELDS) {
|
|
41
|
+
const v = t[field];
|
|
42
|
+
if (typeof v !== 'string' || v.trim() === '') {
|
|
43
|
+
errors.push({ code: 'TSTATE-001', message: `${field} is required.`, path: field });
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
if (t.gate_checks === null || typeof t.gate_checks !== 'object') {
|
|
47
|
+
errors.push({ code: 'TSTATE-001', message: 'gate_checks is required and must be a mapping.', path: 'gate_checks' });
|
|
48
|
+
}
|
|
49
|
+
if (!('valid_to' in t) || !(typeof t.valid_to === 'string' || t.valid_to === null)) {
|
|
50
|
+
errors.push({ code: 'TSTATE-001', message: 'valid_to is required (an ISO date string or null).', path: 'valid_to' });
|
|
51
|
+
}
|
|
52
|
+
// TSTATE-002 — composition list TYPE checks.
|
|
53
|
+
for (const [field, expectedType] of Object.entries(TARGET_STATE_COMPOSITION_FIELDS)) {
|
|
54
|
+
const list = t[field];
|
|
55
|
+
if (list === undefined)
|
|
29
56
|
continue;
|
|
30
|
-
if (!Array.isArray(
|
|
31
|
-
errors.push({
|
|
32
|
-
code: 'TARGET_STATE-002',
|
|
33
|
-
message: `${field} must be a list of ${expectedType} typed IDs.`,
|
|
34
|
-
path: field,
|
|
35
|
-
});
|
|
57
|
+
if (!Array.isArray(list)) {
|
|
58
|
+
errors.push({ code: 'TSTATE-001', message: `${field} must be a list of typed IDs.`, path: field });
|
|
36
59
|
continue;
|
|
37
60
|
}
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
61
|
+
list.forEach((ref, i) => {
|
|
62
|
+
const refType = typeOfId(ref);
|
|
63
|
+
if (!refType) {
|
|
64
|
+
errors.push({ code: 'TSTATE-002', message: `${field}[${i}] "${String(ref)}" is not a resolvable typed ID.`, path: `${field}[${i}]` });
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
if (refType !== expectedType) {
|
|
68
|
+
errors.push({ code: 'TSTATE-002', message: `${field}[${i}] TYPE "${refType}" must be ${expectedType}.`, path: `${field}[${i}]` });
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
if (options.catalog && options.catalog.typeOf(ref) === undefined) {
|
|
72
|
+
errors.push({ code: 'TSTATE-002', message: `${field}[${i}] "${String(ref)}" does not resolve to an admitted ${expectedType}.`, path: `${field}[${i}]` });
|
|
48
73
|
}
|
|
49
74
|
});
|
|
50
75
|
}
|
|
51
|
-
//
|
|
52
|
-
|
|
53
|
-
if ('goals' in record) {
|
|
76
|
+
// TSTATE-003 — `goals` is not an inline field on TARGET_STATE.
|
|
77
|
+
if ('goals' in t) {
|
|
54
78
|
errors.push({
|
|
55
|
-
code: '
|
|
56
|
-
message: 'goals
|
|
79
|
+
code: 'TSTATE-003',
|
|
80
|
+
message: 'goals must not be inline on a TARGET_STATE — TARGET_STATE → GOAL satisfaction is a target_state_satisfies_goal REL.',
|
|
57
81
|
path: 'goals',
|
|
58
82
|
});
|
|
59
83
|
}
|
package/dist/theme/themes.js
CHANGED
|
@@ -95,10 +95,10 @@ function shellCss() {
|
|
|
95
95
|
// body[data-theme] selector, so html itself can't read it. Without that
|
|
96
96
|
// explicit html bg, the iframe's underlying VS Code editor background
|
|
97
97
|
// showed through any uncovered area below short diagrams.
|
|
98
|
-
return `html,body{height:100%;}
|
|
99
|
-
body{background:var(${cv.bg});color:var(${cv.text});display:flex;flex-direction:column;}
|
|
100
|
-
#toolbar{position:sticky;top:0;z-index:10;padding:6px 12px;border-bottom:1px solid var(${cv.border});font-family:${t.fontFamily};font-size:${t.sizes.secondary}px;color:var(${cv.textMuted});background:var(${cv.bg});flex-shrink:0;}
|
|
101
|
-
#canvas{flex:1;min-height:0;padding:16px;overflow:auto;}
|
|
98
|
+
return `html,body{height:100%;}
|
|
99
|
+
body{background:var(${cv.bg});color:var(${cv.text});display:flex;flex-direction:column;}
|
|
100
|
+
#toolbar{position:sticky;top:0;z-index:10;padding:6px 12px;border-bottom:1px solid var(${cv.border});font-family:${t.fontFamily};font-size:${t.sizes.secondary}px;color:var(${cv.textMuted});background:var(${cv.bg});flex-shrink:0;}
|
|
101
|
+
#canvas{flex:1;min-height:0;padding:16px;overflow:auto;}
|
|
102
102
|
svg{display:block;}`;
|
|
103
103
|
}
|
|
104
104
|
/** CSS for SVG diagram classes — all consume --ts-* variables. */
|
|
@@ -111,29 +111,29 @@ function diagramClassCss() {
|
|
|
111
111
|
// weight from these classes instead of inline attributes. That is the
|
|
112
112
|
// contract every notation preview honours so the catalogue reads as one
|
|
113
113
|
// visual family.
|
|
114
|
-
return `.diagram-node{stroke:var(${cv.nodeStroke});stroke-width:1;}
|
|
115
|
-
.layer-factor{fill:var(${cv.layerFactor});}
|
|
116
|
-
.layer-goal{fill:var(${cv.layerGoal});}
|
|
117
|
-
.layer-change{fill:var(${cv.layerChange});}
|
|
118
|
-
.layer-activity{fill:var(${cv.layerActivity});}
|
|
119
|
-
${levelRules}
|
|
120
|
-
.diagram-edge{stroke:var(${cv.edgeStroke});stroke-width:1.5;fill:none;}
|
|
121
|
-
.arrow-fill{fill:var(${cv.edgeStroke});}
|
|
122
|
-
.text-header{fill:var(${cv.headerText});font-family:${t.fontFamily};font-size:${t.sizes.header}px;font-weight:${t.weights.header};}
|
|
123
|
-
.text-primary{fill:var(${cv.textPrimary});font-family:${t.fontFamily};font-size:${t.sizes.primary}px;font-weight:${t.weights.primary};}
|
|
124
|
-
.text-secondary{fill:var(${cv.textSecondary});font-family:${t.fontFamily};font-size:${t.sizes.secondary}px;font-weight:${t.weights.secondary};}
|
|
125
|
-
.text-id{fill:var(${cv.textSecondary});font-family:${t.fontFamily};font-size:${t.sizes.id}px;font-weight:${t.weights.id};}
|
|
126
|
-
.text-pill{fill:var(${cv.textPrimary});font-family:${t.fontFamily};font-size:${t.sizes.pill}px;font-weight:${t.weights.pill};}
|
|
127
|
-
.maturity-1{fill:var(${cv.maturity1});}
|
|
128
|
-
.maturity-2{fill:var(${cv.maturity2});}
|
|
129
|
-
.maturity-3{fill:var(${cv.maturity3});}
|
|
130
|
-
.maturity-4{fill:var(${cv.maturity4});}
|
|
131
|
-
.maturity-5{fill:var(${cv.maturity5});}
|
|
132
|
-
.compliance-gap{fill:var(${cv.statusWarningBg});stroke:var(${cv.statusWarningFg});}
|
|
133
|
-
.compliance-gap text{fill:var(${cv.statusWarningFg});}
|
|
134
|
-
.compliance-deadline{fill:var(${cv.statusErrorBg});stroke:var(${cv.statusErrorFg});}
|
|
135
|
-
.compliance-deadline text{fill:var(${cv.statusErrorFg});}
|
|
136
|
-
.compliance-badge{fill:var(${cv.statusErrorFg});}
|
|
114
|
+
return `.diagram-node{stroke:var(${cv.nodeStroke});stroke-width:1;}
|
|
115
|
+
.layer-factor{fill:var(${cv.layerFactor});}
|
|
116
|
+
.layer-goal{fill:var(${cv.layerGoal});}
|
|
117
|
+
.layer-change{fill:var(${cv.layerChange});}
|
|
118
|
+
.layer-activity{fill:var(${cv.layerActivity});}
|
|
119
|
+
${levelRules}
|
|
120
|
+
.diagram-edge{stroke:var(${cv.edgeStroke});stroke-width:1.5;fill:none;}
|
|
121
|
+
.arrow-fill{fill:var(${cv.edgeStroke});}
|
|
122
|
+
.text-header{fill:var(${cv.headerText});font-family:${t.fontFamily};font-size:${t.sizes.header}px;font-weight:${t.weights.header};dominant-baseline:central;}
|
|
123
|
+
.text-primary{fill:var(${cv.textPrimary});font-family:${t.fontFamily};font-size:${t.sizes.primary}px;font-weight:${t.weights.primary};dominant-baseline:central;}
|
|
124
|
+
.text-secondary{fill:var(${cv.textSecondary});font-family:${t.fontFamily};font-size:${t.sizes.secondary}px;font-weight:${t.weights.secondary};dominant-baseline:central;}
|
|
125
|
+
.text-id{fill:var(${cv.textSecondary});font-family:${t.fontFamily};font-size:${t.sizes.id}px;font-weight:${t.weights.id};dominant-baseline:central;}
|
|
126
|
+
.text-pill{fill:var(${cv.textPrimary});font-family:${t.fontFamily};font-size:${t.sizes.pill}px;font-weight:${t.weights.pill};dominant-baseline:central;}
|
|
127
|
+
.maturity-1{fill:var(${cv.maturity1});}
|
|
128
|
+
.maturity-2{fill:var(${cv.maturity2});}
|
|
129
|
+
.maturity-3{fill:var(${cv.maturity3});}
|
|
130
|
+
.maturity-4{fill:var(${cv.maturity4});}
|
|
131
|
+
.maturity-5{fill:var(${cv.maturity5});}
|
|
132
|
+
.compliance-gap{fill:var(${cv.statusWarningBg});stroke:var(${cv.statusWarningFg});}
|
|
133
|
+
.compliance-gap text{fill:var(${cv.statusWarningFg});}
|
|
134
|
+
.compliance-deadline{fill:var(${cv.statusErrorBg});stroke:var(${cv.statusErrorFg});}
|
|
135
|
+
.compliance-deadline text{fill:var(${cv.statusErrorFg});}
|
|
136
|
+
.compliance-badge{fill:var(${cv.statusErrorFg});}
|
|
137
137
|
.compliance-badge-text{fill:var(${cv.textInverse});font-family:${t.fontFamily};font-size:8px;font-weight:700;}`;
|
|
138
138
|
}
|
|
139
139
|
/**
|
|
@@ -159,55 +159,55 @@ export function generateWebviewCss(themeId) {
|
|
|
159
159
|
const shell = shellCss();
|
|
160
160
|
const classes = diagramClassCss();
|
|
161
161
|
if (themeId === 'transitrix') {
|
|
162
|
-
return `${base}
|
|
163
|
-
html{background:${TRANSITRIX_LIGHT.bg};min-height:100vh;}
|
|
164
|
-
body[data-theme="transitrix"]{${adaptiveVars(TRANSITRIX_LIGHT)}${diagramVars('light')}}
|
|
165
|
-
${shell}
|
|
162
|
+
return `${base}
|
|
163
|
+
html{background:${TRANSITRIX_LIGHT.bg};min-height:100vh;}
|
|
164
|
+
body[data-theme="transitrix"]{${adaptiveVars(TRANSITRIX_LIGHT)}${diagramVars('light')}}
|
|
165
|
+
${shell}
|
|
166
166
|
${classes}`;
|
|
167
167
|
}
|
|
168
168
|
if (themeId === 'transitrix-dark') {
|
|
169
|
-
return `${base}
|
|
170
|
-
html{background:${TRANSITRIX_DARK.bg};min-height:100vh;}
|
|
171
|
-
body[data-theme="transitrix-dark"]{${adaptiveVars(TRANSITRIX_DARK)}${diagramVars('dark')}}
|
|
172
|
-
${shell}
|
|
169
|
+
return `${base}
|
|
170
|
+
html{background:${TRANSITRIX_DARK.bg};min-height:100vh;}
|
|
171
|
+
body[data-theme="transitrix-dark"]{${adaptiveVars(TRANSITRIX_DARK)}${diagramVars('dark')}}
|
|
172
|
+
${shell}
|
|
173
173
|
${classes}`;
|
|
174
174
|
}
|
|
175
175
|
// vscode-adaptive: map --ts-shell vars to --vscode-* CSS variables;
|
|
176
176
|
// layer/level/structural colors follow the VS Code theme body class.
|
|
177
177
|
const cv = CSS_VAR;
|
|
178
|
-
return `${base}
|
|
179
|
-
html{background:var(--vscode-editor-background,${TRANSITRIX_LIGHT.bg});min-height:100vh;}
|
|
180
|
-
body[data-theme="vscode-adaptive"]{
|
|
181
|
-
${cv.bg}:var(--vscode-editor-background);
|
|
182
|
-
${cv.bgSurface}:var(--vscode-sideBar-background,var(--vscode-editor-background));
|
|
183
|
-
${cv.bgElevated}:var(--vscode-editorHoverWidget-background,var(--vscode-editor-background));
|
|
184
|
-
${cv.text}:var(--vscode-foreground);
|
|
185
|
-
${cv.textMuted}:var(--vscode-descriptionForeground);
|
|
186
|
-
${cv.textInverse}:var(--vscode-editor-background);
|
|
187
|
-
${cv.border}:var(--vscode-panel-border);
|
|
188
|
-
${cv.divider}:var(--vscode-panel-border);
|
|
189
|
-
${cv.statusSuccessBg}:${TRANSITRIX_LIGHT.statusSuccessBg};
|
|
190
|
-
${cv.statusSuccessFg}:${TRANSITRIX_LIGHT.statusSuccessFg};
|
|
191
|
-
${cv.statusWarningBg}:${TRANSITRIX_LIGHT.statusWarningBg};
|
|
192
|
-
${cv.statusWarningFg}:${TRANSITRIX_LIGHT.statusWarningFg};
|
|
193
|
-
${cv.statusInfoBg}:${TRANSITRIX_LIGHT.statusInfoBg};
|
|
194
|
-
${cv.statusInfoFg}:${TRANSITRIX_LIGHT.statusInfoFg};
|
|
195
|
-
${cv.statusErrorBg}:${TRANSITRIX_LIGHT.statusErrorBg};
|
|
196
|
-
${cv.statusErrorFg}:${TRANSITRIX_LIGHT.statusErrorFg};
|
|
197
|
-
${diagramVars('light')}
|
|
198
|
-
}
|
|
199
|
-
body[data-theme="vscode-adaptive"].vscode-dark{
|
|
200
|
-
${cv.statusSuccessBg}:${TRANSITRIX_DARK.statusSuccessBg};
|
|
201
|
-
${cv.statusSuccessFg}:${TRANSITRIX_DARK.statusSuccessFg};
|
|
202
|
-
${cv.statusWarningBg}:${TRANSITRIX_DARK.statusWarningBg};
|
|
203
|
-
${cv.statusWarningFg}:${TRANSITRIX_DARK.statusWarningFg};
|
|
204
|
-
${cv.statusInfoBg}:${TRANSITRIX_DARK.statusInfoBg};
|
|
205
|
-
${cv.statusInfoFg}:${TRANSITRIX_DARK.statusInfoFg};
|
|
206
|
-
${cv.statusErrorBg}:${TRANSITRIX_DARK.statusErrorBg};
|
|
207
|
-
${cv.statusErrorFg}:${TRANSITRIX_DARK.statusErrorFg};
|
|
208
|
-
${diagramVars('dark')}
|
|
209
|
-
}
|
|
210
|
-
body[data-theme="vscode-adaptive"].vscode-high-contrast{${diagramVars('hc')}}
|
|
211
|
-
${shell}
|
|
178
|
+
return `${base}
|
|
179
|
+
html{background:var(--vscode-editor-background,${TRANSITRIX_LIGHT.bg});min-height:100vh;}
|
|
180
|
+
body[data-theme="vscode-adaptive"]{
|
|
181
|
+
${cv.bg}:var(--vscode-editor-background);
|
|
182
|
+
${cv.bgSurface}:var(--vscode-sideBar-background,var(--vscode-editor-background));
|
|
183
|
+
${cv.bgElevated}:var(--vscode-editorHoverWidget-background,var(--vscode-editor-background));
|
|
184
|
+
${cv.text}:var(--vscode-foreground);
|
|
185
|
+
${cv.textMuted}:var(--vscode-descriptionForeground);
|
|
186
|
+
${cv.textInverse}:var(--vscode-editor-background);
|
|
187
|
+
${cv.border}:var(--vscode-panel-border);
|
|
188
|
+
${cv.divider}:var(--vscode-panel-border);
|
|
189
|
+
${cv.statusSuccessBg}:${TRANSITRIX_LIGHT.statusSuccessBg};
|
|
190
|
+
${cv.statusSuccessFg}:${TRANSITRIX_LIGHT.statusSuccessFg};
|
|
191
|
+
${cv.statusWarningBg}:${TRANSITRIX_LIGHT.statusWarningBg};
|
|
192
|
+
${cv.statusWarningFg}:${TRANSITRIX_LIGHT.statusWarningFg};
|
|
193
|
+
${cv.statusInfoBg}:${TRANSITRIX_LIGHT.statusInfoBg};
|
|
194
|
+
${cv.statusInfoFg}:${TRANSITRIX_LIGHT.statusInfoFg};
|
|
195
|
+
${cv.statusErrorBg}:${TRANSITRIX_LIGHT.statusErrorBg};
|
|
196
|
+
${cv.statusErrorFg}:${TRANSITRIX_LIGHT.statusErrorFg};
|
|
197
|
+
${diagramVars('light')}
|
|
198
|
+
}
|
|
199
|
+
body[data-theme="vscode-adaptive"].vscode-dark{
|
|
200
|
+
${cv.statusSuccessBg}:${TRANSITRIX_DARK.statusSuccessBg};
|
|
201
|
+
${cv.statusSuccessFg}:${TRANSITRIX_DARK.statusSuccessFg};
|
|
202
|
+
${cv.statusWarningBg}:${TRANSITRIX_DARK.statusWarningBg};
|
|
203
|
+
${cv.statusWarningFg}:${TRANSITRIX_DARK.statusWarningFg};
|
|
204
|
+
${cv.statusInfoBg}:${TRANSITRIX_DARK.statusInfoBg};
|
|
205
|
+
${cv.statusInfoFg}:${TRANSITRIX_DARK.statusInfoFg};
|
|
206
|
+
${cv.statusErrorBg}:${TRANSITRIX_DARK.statusErrorBg};
|
|
207
|
+
${cv.statusErrorFg}:${TRANSITRIX_DARK.statusErrorFg};
|
|
208
|
+
${diagramVars('dark')}
|
|
209
|
+
}
|
|
210
|
+
body[data-theme="vscode-adaptive"].vscode-high-contrast{${diagramVars('hc')}}
|
|
211
|
+
${shell}
|
|
212
212
|
${classes}`;
|
|
213
213
|
}
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
import { describe, expect, it } from 'vitest';
|
|
2
2
|
import { api, render } from '../entry.js';
|
|
3
|
-
const VALID_GOALS_DOC = `
|
|
4
|
-
notation: goals
|
|
5
|
-
id: GOALS-WEBVIEW-1
|
|
6
|
-
name: "Webview entry smoke test"
|
|
7
|
-
goal_types:
|
|
8
|
-
- { name: "Strategy", level: 0 }
|
|
9
|
-
goals:
|
|
10
|
-
- id: GOAL-MOON-1
|
|
11
|
-
name: "Reach the moon"
|
|
12
|
-
type: "Strategy"
|
|
13
|
-
level: 0
|
|
3
|
+
const VALID_GOALS_DOC = `
|
|
4
|
+
notation: goals
|
|
5
|
+
id: GOALS-WEBVIEW-1
|
|
6
|
+
name: "Webview entry smoke test"
|
|
7
|
+
goal_types:
|
|
8
|
+
- { name: "Strategy", level: 0 }
|
|
9
|
+
goals:
|
|
10
|
+
- id: GOAL-MOON-1
|
|
11
|
+
name: "Reach the moon"
|
|
12
|
+
type: "Strategy"
|
|
13
|
+
level: 0
|
|
14
14
|
`;
|
|
15
15
|
describe('webview/entry — Step 2 host API', () => {
|
|
16
16
|
it('exposes a version + supportedKinds + render() trio', () => {
|
|
@@ -4,8 +4,10 @@ export interface RenderActivitiesOptions {
|
|
|
4
4
|
title?: string;
|
|
5
5
|
/** Network column / row gaps. Defaults match `layoutActivities`. */
|
|
6
6
|
gaps?: ActivitiesLayoutOptions;
|
|
7
|
-
/**
|
|
7
|
+
/** Exit edge curvature; 1 = default, 0 = straight, higher = stronger arc. */
|
|
8
8
|
curvature?: number;
|
|
9
|
+
/** Entry curvature at the target node; defaults to `curvature` when omitted. */
|
|
10
|
+
entryCurvature?: number;
|
|
9
11
|
}
|
|
10
12
|
/**
|
|
11
13
|
* Render the network (PSND) view of an already-validated ActivityDoc to a
|
|
@@ -49,7 +49,7 @@ function truncate(s, maxLen) {
|
|
|
49
49
|
* than short-circuiting.
|
|
50
50
|
*/
|
|
51
51
|
export function renderActivitiesSvg(doc, options = {}) {
|
|
52
|
-
const { title = '', gaps = {}, curvature = DEFAULT_EDGE_CURVATURE } = options;
|
|
52
|
+
const { title = '', gaps = {}, curvature = DEFAULT_EDGE_CURVATURE, entryCurvature } = options;
|
|
53
53
|
const layout = layoutActivities(doc, gaps);
|
|
54
54
|
if (layout.nodes.length === 0) {
|
|
55
55
|
return `<svg xmlns="http://www.w3.org/2000/svg" width="0" height="0" viewBox="0 0 0 0"></svg>`;
|
|
@@ -80,7 +80,7 @@ export function renderActivitiesSvg(doc, options = {}) {
|
|
|
80
80
|
const ty = t.y + oy + t.height / 2;
|
|
81
81
|
const cls = e.isCritical ? 'diagram-edge critical-edge' : 'diagram-edge';
|
|
82
82
|
const marker = `url(#${e.isCritical ? 'arrow-crit' : 'arrow'})`;
|
|
83
|
-
return `<path d="${horizontalCubicEdgePath(sx, sy, tx, ty, curvature)}" class="${cls}" marker-end="${marker}"/>`;
|
|
83
|
+
return `<path d="${horizontalCubicEdgePath(sx, sy, tx, ty, curvature, entryCurvature)}" class="${cls}" marker-end="${marker}"/>`;
|
|
84
84
|
})
|
|
85
85
|
.join('\n');
|
|
86
86
|
const nodeSvg = layout.nodes
|
|
@@ -12,18 +12,17 @@
|
|
|
12
12
|
* CROSS-DOCUMENT RESOLUTION CAVEAT
|
|
13
13
|
* The Activity Card is the only MULTI-DOCUMENT Studio notation: the card YAML
|
|
14
14
|
* names a project Activity, and the project's name, dates, motivation chain
|
|
15
|
-
* (
|
|
16
|
-
* the canonical element + relation store (`canon/elements/**`,
|
|
15
|
+
* (Drivers → Assessments → Goals → Changes) and child activities are pulled BY
|
|
16
|
+
* REFERENCE from the canonical element + relation store (`canon/elements/**`,
|
|
17
17
|
* `canon/relations/**`; see `../activity-card/resolver.ts`). That resolution
|
|
18
18
|
* requires reading those files from disk, which is NOT available inside the
|
|
19
19
|
* JCEF host. We therefore resolve from the single in-memory card document
|
|
20
|
-
* ONLY: we synthesise
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
* still renders its title, dates band and milestone timeline.
|
|
20
|
+
* ONLY: we synthesise a minimal `ResolvedActivityCard` whose project carries
|
|
21
|
+
* just the referenced project id (no resolved name/dates), whose milestones
|
|
22
|
+
* come straight from the card's own `milestones[]`, and whose chain sections
|
|
23
|
+
* and child activities are empty. The SVG layout is shared with the VS Code
|
|
24
|
+
* path via `layoutActivityCard`, so a single-document card still renders its
|
|
25
|
+
* title, dates band and milestone section.
|
|
27
26
|
*/
|
|
28
27
|
import { layoutActivityCard, ARCHIMATE_CLASS } from '../activity-card/layout.js';
|
|
29
28
|
import { generateSvgEmbedCss } from '../theme/index.js';
|
|
@@ -35,13 +34,6 @@ function truncate(text, maxChars) {
|
|
|
35
34
|
return text;
|
|
36
35
|
return text.slice(0, Math.max(0, maxChars - 1)) + '…';
|
|
37
36
|
}
|
|
38
|
-
/**
|
|
39
|
-
* Build a single-document resolution of the card: everything that can be read
|
|
40
|
-
* straight off the card YAML, with the cross-document fields left empty. This
|
|
41
|
-
* mirrors what `resolveActivityCard` produces, minus the parts that need the
|
|
42
|
-
* canon element + relation store (project name/dates, motivation chain, child
|
|
43
|
-
* activities).
|
|
44
|
-
*/
|
|
45
37
|
function resolveSingleDoc(doc) {
|
|
46
38
|
const card = doc.activity_card;
|
|
47
39
|
const rawMilestones = Array.isArray(card?.milestones) ? card.milestones : [];
|
|
@@ -62,74 +54,113 @@ function resolveSingleDoc(doc) {
|
|
|
62
54
|
// only. The layout falls back to '—' for the unknown date fields.
|
|
63
55
|
project: { id: card?.project ?? '', name: card?.project ?? '' },
|
|
64
56
|
milestones,
|
|
65
|
-
motivation: {
|
|
57
|
+
motivation: { drivers: [], goals: [], changes: [] },
|
|
58
|
+
assessments: [],
|
|
66
59
|
childActivities: [],
|
|
60
|
+
notes: card?.notes,
|
|
67
61
|
};
|
|
68
62
|
}
|
|
69
63
|
function buildBody(layout, ox, oy) {
|
|
70
64
|
const parts = [];
|
|
71
65
|
// Outer card.
|
|
72
66
|
parts.push(`<rect class="diagram-node level-0" x="${ox}" y="${oy}" width="${layout.bounds.width}" height="${layout.bounds.height}" rx="8"/>`);
|
|
73
|
-
// Title
|
|
74
|
-
parts.push(`<text class="text-header" x="${layout.
|
|
75
|
-
//
|
|
67
|
+
// Title row.
|
|
68
|
+
parts.push(`<text class="text-header" x="${layout.titleRow.x + ox}" y="${layout.titleRow.y + oy}" style="dominant-baseline:central" font-size="18">${escXml(truncate(layout.titleRow.name, 60))}</text>`);
|
|
69
|
+
// Activity type badge.
|
|
70
|
+
if (layout.activityTypeBadge) {
|
|
71
|
+
const b = layout.activityTypeBadge;
|
|
72
|
+
parts.push(`<rect class="diagram-node level-2" x="${b.x + ox}" y="${b.y + oy}" width="${b.width}" height="${b.height}" rx="4"/>`);
|
|
73
|
+
parts.push(`<text class="text-secondary" style="dominant-baseline:central;text-anchor:middle" x="${b.x + ox + b.width / 2}" y="${b.y + oy + b.height / 2}">${escXml(b.label)}</text>`);
|
|
74
|
+
}
|
|
75
|
+
// Status badge.
|
|
76
|
+
if (layout.statusBadge) {
|
|
77
|
+
const b = layout.statusBadge;
|
|
78
|
+
parts.push(`<rect class="diagram-node level-3" x="${b.x + ox}" y="${b.y + oy}" width="${b.width}" height="${b.height}" rx="4"/>`);
|
|
79
|
+
parts.push(`<text class="text-secondary" style="dominant-baseline:central;text-anchor:middle" x="${b.x + ox + b.width / 2}" y="${b.y + oy + b.height / 2}">${escXml(b.label)}</text>`);
|
|
80
|
+
}
|
|
81
|
+
// Date fields.
|
|
76
82
|
for (const d of layout.dateFields) {
|
|
77
83
|
parts.push(`<rect class="diagram-node level-2" x="${d.x + ox}" y="${d.y + oy}" width="${d.width}" height="${d.height}" rx="6"/>`);
|
|
78
|
-
parts.push(`<text class="text-secondary" x="${d.x + ox + 12}" y="${d.y + oy + 18}" dominant-baseline
|
|
79
|
-
parts.push(`<text class="text-primary" x="${d.x + ox + 12}" y="${d.y + oy + 40}" dominant-baseline
|
|
84
|
+
parts.push(`<text class="text-secondary" x="${d.x + ox + 12}" y="${d.y + oy + 18}" style="dominant-baseline:central">${escXml(d.label)}</text>`);
|
|
85
|
+
parts.push(`<text class="text-primary" x="${d.x + ox + 12}" y="${d.y + oy + 40}" style="dominant-baseline:central">${escXml(d.value)}</text>`);
|
|
80
86
|
}
|
|
81
|
-
//
|
|
82
|
-
for (const s of layout.
|
|
83
|
-
parts.push(`<
|
|
87
|
+
// Stakeholder role slots (2-column grid).
|
|
88
|
+
for (const s of layout.stakeholderRoleSlots) {
|
|
89
|
+
parts.push(`<rect class="diagram-node level-2" x="${s.x + ox}" y="${s.y + oy}" width="${s.width}" height="${s.height}" rx="6"/>`);
|
|
90
|
+
parts.push(`<text class="text-secondary" x="${s.x + ox + 10}" y="${s.y + oy + 16}" style="dominant-baseline:central">${escXml(s.role)}</text>`);
|
|
91
|
+
parts.push(`<text class="text-primary" x="${s.x + ox + 10}" y="${s.y + oy + 36}" style="dominant-baseline:central">${escXml(truncate(s.name, 40))}</text>`);
|
|
92
|
+
}
|
|
93
|
+
// Description row.
|
|
94
|
+
if (layout.descriptionRow) {
|
|
95
|
+
const r = layout.descriptionRow;
|
|
96
|
+
parts.push(`<rect class="diagram-node level-2" x="${r.x + ox}" y="${r.y + oy}" width="${r.width}" height="${r.height}" rx="6"/>`);
|
|
97
|
+
parts.push(`<text class="text-secondary" x="${r.x + ox + 12}" y="${r.y + oy + 22}" style="dominant-baseline:central">${escXml(r.label)}</text>`);
|
|
98
|
+
r.valueLines.forEach((line, i) => {
|
|
99
|
+
parts.push(`<text class="text-primary" x="${r.x + ox + 12}" y="${r.y + oy + 44 + i * 18}" style="dominant-baseline:central">${escXml(line)}</text>`);
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
// Chain sections (Drivers → Assessments → Goals → Changes).
|
|
103
|
+
const SECTION_LEVEL = {
|
|
104
|
+
drivers: 4,
|
|
105
|
+
assessments: 5,
|
|
106
|
+
goals: 5,
|
|
107
|
+
changes: 6,
|
|
108
|
+
};
|
|
109
|
+
for (let si = 0; si < layout.chainSections.length; si++) {
|
|
110
|
+
const section = layout.chainSections[si];
|
|
111
|
+
const level = SECTION_LEVEL[section.type] ?? 5;
|
|
112
|
+
// Section outer box.
|
|
113
|
+
parts.push(`<rect class="diagram-node level-1" x="${section.x + ox}" y="${section.y + oy}" width="${section.width}" height="${section.height}" rx="6"/>`);
|
|
114
|
+
// Section header label + subtitle.
|
|
115
|
+
parts.push(`<text class="text-header" x="${section.x + ox + 12}" y="${section.y + oy + 14}" style="dominant-baseline:central">${escXml(section.label)}<tspan class="text-secondary" font-size="11"> (${escXml(section.subtitle)})</tspan></text>`);
|
|
116
|
+
if (section.isEmpty) {
|
|
117
|
+
// Gap indicator.
|
|
118
|
+
parts.push(`<text class="text-secondary" x="${section.x + ox + 12}" y="${section.y + oy + 24 + 8 + 16}" style="dominant-baseline:central">— not on file</text>`);
|
|
119
|
+
}
|
|
120
|
+
else {
|
|
121
|
+
for (const n of section.nodes) {
|
|
122
|
+
parts.push(`<rect class="diagram-node level-${level}" x="${n.x + ox}" y="${n.y + oy}" width="${n.width}" height="${n.height}" rx="4"/>`);
|
|
123
|
+
parts.push(`<text class="text-primary" x="${n.x + ox + 10}" y="${n.y + oy + (n.meta ? 16 : n.height / 2)}" style="dominant-baseline:central">${escXml(truncate(n.name, 80))}</text>`);
|
|
124
|
+
if (n.meta) {
|
|
125
|
+
parts.push(`<text class="text-secondary" x="${n.x + ox + 10}" y="${n.y + oy + 30}" style="dominant-baseline:central">${escXml(n.meta)}</text>`);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
// Connector arrow to next section.
|
|
130
|
+
if (si < layout.chainSections.length - 1) {
|
|
131
|
+
const nextSection = layout.chainSections[si + 1];
|
|
132
|
+
const arrowX = section.x + ox + section.width / 2;
|
|
133
|
+
const arrowTopY = section.y + oy + section.height;
|
|
134
|
+
const arrowBotY = nextSection.y + oy;
|
|
135
|
+
const midY = (arrowTopY + arrowBotY) / 2;
|
|
136
|
+
parts.push(`<path class="diagram-edge" d="M${arrowX},${arrowTopY} L${arrowX},${arrowBotY}" fill="none" marker-end="url(#ac-arrow)"/>`);
|
|
137
|
+
void midY; // midpoint available for future label placement
|
|
138
|
+
}
|
|
84
139
|
}
|
|
85
140
|
// Milestones.
|
|
86
141
|
for (const m of layout.milestones) {
|
|
87
142
|
parts.push(`<rect class="diagram-node level-3" x="${m.x + ox}" y="${m.y + oy}" width="${m.width}" height="${m.height}" rx="6"/>`);
|
|
88
|
-
parts.push(`<text class="text-secondary" x="${m.x + ox + 10}" y="${m.y + oy + 16}" dominant-baseline
|
|
89
|
-
parts.push(`<text class="text-primary" x="${m.x + ox + 10}" y="${m.y + oy + 36}" dominant-baseline
|
|
90
|
-
parts.push(`<text class="text-secondary" x="${m.x + ox + 10}" y="${m.y + oy + 52}" dominant-baseline
|
|
91
|
-
}
|
|
92
|
-
// Motivation chain — edges first (under nodes).
|
|
93
|
-
const nodeById = new Map();
|
|
94
|
-
for (const col of [
|
|
95
|
-
layout.chainColumns.factors,
|
|
96
|
-
layout.chainColumns.goals,
|
|
97
|
-
layout.chainColumns.changes,
|
|
98
|
-
]) {
|
|
99
|
-
for (const n of col)
|
|
100
|
-
nodeById.set(n.id, n);
|
|
101
|
-
}
|
|
102
|
-
for (const e of layout.chainEdges) {
|
|
103
|
-
const s = nodeById.get(e.sourceId);
|
|
104
|
-
const t = nodeById.get(e.targetId);
|
|
105
|
-
if (!s || !t)
|
|
106
|
-
continue;
|
|
107
|
-
const x1 = s.x + s.width + ox;
|
|
108
|
-
const y1 = s.y + s.height / 2 + oy;
|
|
109
|
-
const x2 = t.x + ox;
|
|
110
|
-
const y2 = t.y + t.height / 2 + oy;
|
|
111
|
-
const mx = (x1 + x2) / 2;
|
|
112
|
-
parts.push(`<path class="diagram-edge" d="M${x1},${y1} C${mx},${y1} ${mx},${y2} ${x2},${y2}" fill="none" marker-end="url(#ac-arrow)"/>`);
|
|
113
|
-
}
|
|
114
|
-
const chainLevels = [
|
|
115
|
-
[layout.chainColumns.factors, 4],
|
|
116
|
-
[layout.chainColumns.goals, 5],
|
|
117
|
-
[layout.chainColumns.changes, 6],
|
|
118
|
-
];
|
|
119
|
-
for (const [col, level] of chainLevels) {
|
|
120
|
-
for (const n of col) {
|
|
121
|
-
parts.push(`<rect class="diagram-node level-${level}" x="${n.x + ox}" y="${n.y + oy}" width="${n.width}" height="${n.height}" rx="6"/>`);
|
|
122
|
-
parts.push(`<text class="text-pill" x="${n.x + ox + n.width / 2}" y="${n.y + oy + n.height / 2}" text-anchor="middle" dominant-baseline="central">${escXml(truncate(n.name, Math.floor(n.width / 7)))}</text>`);
|
|
123
|
-
}
|
|
143
|
+
parts.push(`<text class="text-secondary" x="${m.x + ox + 10}" y="${m.y + oy + 16}" style="dominant-baseline:central">${escXml(m.date)}</text>`);
|
|
144
|
+
parts.push(`<text class="text-primary" x="${m.x + ox + 10}" y="${m.y + oy + 36}" style="dominant-baseline:central">${escXml(truncate(m.name, 22))}</text>`);
|
|
145
|
+
parts.push(`<text class="text-secondary" x="${m.x + ox + 10}" y="${m.y + oy + 52}" style="dominant-baseline:central">(${escXml(m.archimateClass)})</text>`);
|
|
124
146
|
}
|
|
125
147
|
// Child activities.
|
|
126
148
|
for (const a of layout.childActivities) {
|
|
127
149
|
parts.push(`<rect class="diagram-node level-1" x="${a.x + ox}" y="${a.y + oy}" width="${a.width}" height="${a.height}" rx="6"/>`);
|
|
128
|
-
parts.push(`<text class="text-primary" x="${a.x + ox + 12}" y="${a.y + oy + a.height / 2}" dominant-baseline
|
|
150
|
+
parts.push(`<text class="text-primary" x="${a.x + ox + 12}" y="${a.y + oy + a.height / 2}" style="dominant-baseline:central">${escXml(truncate(a.name, 48))} <tspan class="text-secondary">(${escXml(a.archimateClass)})</tspan></text>`);
|
|
129
151
|
if (a.meta) {
|
|
130
|
-
parts.push(`<text class="text-secondary" x="${a.x + a.width + ox - 12}" y="${a.y + oy + a.height / 2}"
|
|
152
|
+
parts.push(`<text class="text-secondary" x="${a.x + a.width + ox - 12}" y="${a.y + oy + a.height / 2}" style="dominant-baseline:central;text-anchor:end">${escXml(truncate(a.meta, 40))}</text>`);
|
|
131
153
|
}
|
|
132
154
|
}
|
|
155
|
+
// Footer — notes.
|
|
156
|
+
if (layout.footerRow) {
|
|
157
|
+
const r = layout.footerRow;
|
|
158
|
+
parts.push(`<rect class="diagram-node level-2" x="${r.x + ox}" y="${r.y + oy}" width="${r.width}" height="${r.height}" rx="6"/>`);
|
|
159
|
+
parts.push(`<text class="text-secondary" x="${r.x + ox + 12}" y="${r.y + oy + 22}" style="dominant-baseline:central">${escXml(r.label)}</text>`);
|
|
160
|
+
r.valueLines.forEach((line, i) => {
|
|
161
|
+
parts.push(`<text class="text-primary" x="${r.x + ox + 12}" y="${r.y + oy + 44 + i * 18}" style="dominant-baseline:central">${escXml(line)}</text>`);
|
|
162
|
+
});
|
|
163
|
+
}
|
|
133
164
|
return parts.join('\n');
|
|
134
165
|
}
|
|
135
166
|
export function renderActivityCardSvg(doc, options = {}) {
|
|
@@ -139,9 +170,8 @@ export function renderActivityCardSvg(doc, options = {}) {
|
|
|
139
170
|
const { title = '' } = options;
|
|
140
171
|
const resolved = resolveSingleDoc(doc);
|
|
141
172
|
const layout = layoutActivityCard(resolved);
|
|
142
|
-
if (layout.bounds.width <= 0 || layout.bounds.height <= 0)
|
|
173
|
+
if (layout.bounds.width <= 0 || layout.bounds.height <= 0)
|
|
143
174
|
return EMPTY_SVG;
|
|
144
|
-
}
|
|
145
175
|
const titleH = title ? 28 : 0;
|
|
146
176
|
const w = layout.bounds.width + PAD * 2;
|
|
147
177
|
const h = layout.bounds.height + PAD * 2 + titleH;
|
|
@@ -151,18 +181,15 @@ export function renderActivityCardSvg(doc, options = {}) {
|
|
|
151
181
|
const titleSvg = title
|
|
152
182
|
? `<text class="text-header" x="${PAD}" y="${PAD + 14}">${escXml(`Activity Card — ${title}`)}</text>`
|
|
153
183
|
: '';
|
|
154
|
-
// Embed the shared theme CSS inside the SVG so the rendered output is
|
|
155
|
-
// self-contained — the JCEF host page only needs to drop the SVG into the
|
|
156
|
-
// DOM and styling resolves without any cooperation from the host stylesheet.
|
|
157
184
|
const embedCss = generateSvgEmbedCss('transitrix');
|
|
158
|
-
return `<svg xmlns="http://www.w3.org/2000/svg" width="${w}" height="${h}" viewBox="0 0 ${w} ${h}">
|
|
159
|
-
<style>${embedCss}</style>
|
|
160
|
-
<defs>
|
|
161
|
-
<marker id="ac-arrow" markerWidth="8" markerHeight="8" refX="8" refY="3" orient="auto">
|
|
162
|
-
<path d="M0,0 L0,6 L8,3 z" class="arrow-fill"/>
|
|
163
|
-
</marker>
|
|
164
|
-
</defs>
|
|
165
|
-
${titleSvg}
|
|
166
|
-
${body}
|
|
185
|
+
return `<svg xmlns="http://www.w3.org/2000/svg" width="${w}" height="${h}" viewBox="0 0 ${w} ${h}">
|
|
186
|
+
<style>${embedCss}</style>
|
|
187
|
+
<defs>
|
|
188
|
+
<marker id="ac-arrow" markerWidth="8" markerHeight="8" refX="8" refY="3" orient="auto">
|
|
189
|
+
<path d="M0,0 L0,6 L8,3 z" class="arrow-fill"/>
|
|
190
|
+
</marker>
|
|
191
|
+
</defs>
|
|
192
|
+
${titleSvg}
|
|
193
|
+
${body}
|
|
167
194
|
</svg>`;
|
|
168
195
|
}
|
|
@@ -4,5 +4,9 @@ export interface RenderFgcaOptions {
|
|
|
4
4
|
variant?: 'fgca' | 'fga';
|
|
5
5
|
/** Optional heading rendered as a left-anchored `text-header` line. */
|
|
6
6
|
title?: string;
|
|
7
|
+
/** Exit edge curvature; 1 = default, 0 = straight, higher = stronger arc. */
|
|
8
|
+
curvature?: number;
|
|
9
|
+
/** Entry curvature at the target node; defaults to `curvature` when omitted. */
|
|
10
|
+
entryCurvature?: number;
|
|
7
11
|
}
|
|
8
12
|
export declare function renderFgcaSvg(doc: FGCADoc, options?: RenderFgcaOptions): string;
|