@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,234 +1,266 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Host-neutral SVG renderer for the Activity Card notation.
|
|
3
|
-
*
|
|
4
|
-
* Step 4 of the IntelliJ epic (ADR 0001): the webview bundle must turn a
|
|
5
|
-
* validated Activity Card document into renderable SVG so JCEF can drop it into
|
|
6
|
-
* the preview panel. The VS Code path lives in
|
|
7
|
-
* `extension/src/activity-card-preview.ts` and pulls in VS Code-specific
|
|
8
|
-
* concerns (themes, title block, save dialogs, sibling-document discovery on
|
|
9
|
-
* the filesystem); this module is the host-neutral subset — pure layout → SVG
|
|
10
|
-
* with no VS Code APIs, no `node:fs`, no `node:path`.
|
|
11
|
-
*
|
|
12
|
-
* CROSS-DOCUMENT RESOLUTION CAVEAT
|
|
13
|
-
* The Activity Card is the only MULTI-DOCUMENT Studio notation: the card YAML
|
|
14
|
-
* names a project Activity, and the project's name, dates, motivation chain
|
|
15
|
-
* (
|
|
16
|
-
* the canonical element + relation store (`canon/elements/**`,
|
|
17
|
-
* `canon/relations/**`; see `../activity-card/resolver.ts`). That resolution
|
|
18
|
-
* requires reading those files from disk, which is NOT available inside the
|
|
19
|
-
* JCEF host. We therefore resolve from the single in-memory card document
|
|
20
|
-
* ONLY: we synthesise
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
import {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
} from '../
|
|
35
|
-
import {
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
const
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
`<text class="text-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
//
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
);
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
);
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
);
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
);
|
|
123
|
-
parts.push(
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
for (
|
|
137
|
-
layout.
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
const
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Host-neutral SVG renderer for the Activity Card notation.
|
|
3
|
+
*
|
|
4
|
+
* Step 4 of the IntelliJ epic (ADR 0001): the webview bundle must turn a
|
|
5
|
+
* validated Activity Card document into renderable SVG so JCEF can drop it into
|
|
6
|
+
* the preview panel. The VS Code path lives in
|
|
7
|
+
* `extension/src/activity-card-preview.ts` and pulls in VS Code-specific
|
|
8
|
+
* concerns (themes, title block, save dialogs, sibling-document discovery on
|
|
9
|
+
* the filesystem); this module is the host-neutral subset — pure layout → SVG
|
|
10
|
+
* with no VS Code APIs, no `node:fs`, no `node:path`.
|
|
11
|
+
*
|
|
12
|
+
* CROSS-DOCUMENT RESOLUTION CAVEAT
|
|
13
|
+
* The Activity Card is the only MULTI-DOCUMENT Studio notation: the card YAML
|
|
14
|
+
* names a project Activity, and the project's name, dates, motivation chain
|
|
15
|
+
* (Drivers → Assessments → Goals → Changes) and child activities are pulled BY
|
|
16
|
+
* REFERENCE from the canonical element + relation store (`canon/elements/**`,
|
|
17
|
+
* `canon/relations/**`; see `../activity-card/resolver.ts`). That resolution
|
|
18
|
+
* requires reading those files from disk, which is NOT available inside the
|
|
19
|
+
* JCEF host. We therefore resolve from the single in-memory card document
|
|
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.
|
|
26
|
+
*/
|
|
27
|
+
import { layoutActivityCard, ARCHIMATE_CLASS } from '../activity-card/layout.js';
|
|
28
|
+
import type {
|
|
29
|
+
ActivityCardDoc,
|
|
30
|
+
ActivityCardLayout,
|
|
31
|
+
ResolvedActivityCard,
|
|
32
|
+
ResolvedMilestone,
|
|
33
|
+
} from '../activity-card/types.js';
|
|
34
|
+
import { generateSvgEmbedCss } from '../theme/index.js';
|
|
35
|
+
import { escXml } from './render-util.js';
|
|
36
|
+
|
|
37
|
+
const PAD = 24;
|
|
38
|
+
const EMPTY_SVG = `<svg xmlns="http://www.w3.org/2000/svg" width="0" height="0" viewBox="0 0 0 0"></svg>`;
|
|
39
|
+
|
|
40
|
+
export interface RenderActivityCardOptions {
|
|
41
|
+
title?: string;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function truncate(text: string, maxChars: number): string {
|
|
45
|
+
if (text.length <= maxChars) return text;
|
|
46
|
+
return text.slice(0, Math.max(0, maxChars - 1)) + '…';
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function resolveSingleDoc(doc: ActivityCardDoc): ResolvedActivityCard {
|
|
50
|
+
const card = doc.activity_card;
|
|
51
|
+
const rawMilestones = Array.isArray(card?.milestones) ? card.milestones : [];
|
|
52
|
+
|
|
53
|
+
const milestones: ResolvedMilestone[] = rawMilestones
|
|
54
|
+
.filter((m): m is NonNullable<typeof m> => !!m && typeof m === 'object')
|
|
55
|
+
.map((m) => ({
|
|
56
|
+
id: m.id,
|
|
57
|
+
name: m.name,
|
|
58
|
+
date: m.date,
|
|
59
|
+
description: m.description,
|
|
60
|
+
deliversChanges: Array.isArray(m.delivers_changes) ? m.delivers_changes : [],
|
|
61
|
+
}))
|
|
62
|
+
.sort((a, b) => (a.date < b.date ? -1 : a.date > b.date ? 1 : 0));
|
|
63
|
+
|
|
64
|
+
return {
|
|
65
|
+
cardId: card?.id ?? '',
|
|
66
|
+
cardDescription: card?.description,
|
|
67
|
+
// No sibling docs in the host-neutral path: the project resolves to its id
|
|
68
|
+
// only. The layout falls back to '—' for the unknown date fields.
|
|
69
|
+
project: { id: card?.project ?? '', name: card?.project ?? '' },
|
|
70
|
+
milestones,
|
|
71
|
+
motivation: { drivers: [], goals: [], changes: [] },
|
|
72
|
+
assessments: [],
|
|
73
|
+
childActivities: [],
|
|
74
|
+
notes: card?.notes,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function buildBody(layout: ActivityCardLayout, ox: number, oy: number): string {
|
|
79
|
+
const parts: string[] = [];
|
|
80
|
+
|
|
81
|
+
// Outer card.
|
|
82
|
+
parts.push(
|
|
83
|
+
`<rect class="diagram-node level-0" x="${ox}" y="${oy}" width="${layout.bounds.width}" height="${layout.bounds.height}" rx="8"/>`,
|
|
84
|
+
);
|
|
85
|
+
|
|
86
|
+
// Title row.
|
|
87
|
+
parts.push(
|
|
88
|
+
`<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>`,
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
// Activity type badge.
|
|
92
|
+
if (layout.activityTypeBadge) {
|
|
93
|
+
const b = layout.activityTypeBadge;
|
|
94
|
+
parts.push(`<rect class="diagram-node level-2" x="${b.x + ox}" y="${b.y + oy}" width="${b.width}" height="${b.height}" rx="4"/>`);
|
|
95
|
+
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>`);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Status badge.
|
|
99
|
+
if (layout.statusBadge) {
|
|
100
|
+
const b = layout.statusBadge;
|
|
101
|
+
parts.push(`<rect class="diagram-node level-3" x="${b.x + ox}" y="${b.y + oy}" width="${b.width}" height="${b.height}" rx="4"/>`);
|
|
102
|
+
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>`);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Date fields.
|
|
106
|
+
for (const d of layout.dateFields) {
|
|
107
|
+
parts.push(`<rect class="diagram-node level-2" x="${d.x + ox}" y="${d.y + oy}" width="${d.width}" height="${d.height}" rx="6"/>`);
|
|
108
|
+
parts.push(`<text class="text-secondary" x="${d.x + ox + 12}" y="${d.y + oy + 18}" style="dominant-baseline:central">${escXml(d.label)}</text>`);
|
|
109
|
+
parts.push(`<text class="text-primary" x="${d.x + ox + 12}" y="${d.y + oy + 40}" style="dominant-baseline:central">${escXml(d.value)}</text>`);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Stakeholder role slots (2-column grid).
|
|
113
|
+
for (const s of layout.stakeholderRoleSlots) {
|
|
114
|
+
parts.push(`<rect class="diagram-node level-2" x="${s.x + ox}" y="${s.y + oy}" width="${s.width}" height="${s.height}" rx="6"/>`);
|
|
115
|
+
parts.push(`<text class="text-secondary" x="${s.x + ox + 10}" y="${s.y + oy + 16}" style="dominant-baseline:central">${escXml(s.role)}</text>`);
|
|
116
|
+
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>`);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Description row.
|
|
120
|
+
if (layout.descriptionRow) {
|
|
121
|
+
const r = layout.descriptionRow;
|
|
122
|
+
parts.push(`<rect class="diagram-node level-2" x="${r.x + ox}" y="${r.y + oy}" width="${r.width}" height="${r.height}" rx="6"/>`);
|
|
123
|
+
parts.push(`<text class="text-secondary" x="${r.x + ox + 12}" y="${r.y + oy + 22}" style="dominant-baseline:central">${escXml(r.label)}</text>`);
|
|
124
|
+
r.valueLines.forEach((line, i) => {
|
|
125
|
+
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>`);
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// Chain sections (Drivers → Assessments → Goals → Changes).
|
|
130
|
+
const SECTION_LEVEL: Record<string, number> = {
|
|
131
|
+
drivers: 4,
|
|
132
|
+
assessments: 5,
|
|
133
|
+
goals: 5,
|
|
134
|
+
changes: 6,
|
|
135
|
+
};
|
|
136
|
+
for (let si = 0; si < layout.chainSections.length; si++) {
|
|
137
|
+
const section = layout.chainSections[si];
|
|
138
|
+
const level = SECTION_LEVEL[section.type] ?? 5;
|
|
139
|
+
|
|
140
|
+
// Section outer box.
|
|
141
|
+
parts.push(
|
|
142
|
+
`<rect class="diagram-node level-1" x="${section.x + ox}" y="${section.y + oy}" width="${section.width}" height="${section.height}" rx="6"/>`,
|
|
143
|
+
);
|
|
144
|
+
// Section header label + subtitle.
|
|
145
|
+
parts.push(
|
|
146
|
+
`<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>`,
|
|
147
|
+
);
|
|
148
|
+
|
|
149
|
+
if (section.isEmpty) {
|
|
150
|
+
// Gap indicator.
|
|
151
|
+
parts.push(
|
|
152
|
+
`<text class="text-secondary" x="${section.x + ox + 12}" y="${section.y + oy + 24 + 8 + 16}" style="dominant-baseline:central">— not on file</text>`,
|
|
153
|
+
);
|
|
154
|
+
} else {
|
|
155
|
+
for (const n of section.nodes) {
|
|
156
|
+
parts.push(
|
|
157
|
+
`<rect class="diagram-node level-${level}" x="${n.x + ox}" y="${n.y + oy}" width="${n.width}" height="${n.height}" rx="4"/>`,
|
|
158
|
+
);
|
|
159
|
+
parts.push(
|
|
160
|
+
`<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>`,
|
|
161
|
+
);
|
|
162
|
+
if (n.meta) {
|
|
163
|
+
parts.push(
|
|
164
|
+
`<text class="text-secondary" x="${n.x + ox + 10}" y="${n.y + oy + 30}" style="dominant-baseline:central">${escXml(n.meta)}</text>`,
|
|
165
|
+
);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// Connector arrow to next section.
|
|
171
|
+
if (si < layout.chainSections.length - 1) {
|
|
172
|
+
const nextSection = layout.chainSections[si + 1];
|
|
173
|
+
const arrowX = section.x + ox + section.width / 2;
|
|
174
|
+
const arrowTopY = section.y + oy + section.height;
|
|
175
|
+
const arrowBotY = nextSection.y + oy;
|
|
176
|
+
const midY = (arrowTopY + arrowBotY) / 2;
|
|
177
|
+
parts.push(
|
|
178
|
+
`<path class="diagram-edge" d="M${arrowX},${arrowTopY} L${arrowX},${arrowBotY}" fill="none" marker-end="url(#ac-arrow)"/>`,
|
|
179
|
+
);
|
|
180
|
+
void midY; // midpoint available for future label placement
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// Milestones.
|
|
185
|
+
for (const m of layout.milestones) {
|
|
186
|
+
parts.push(
|
|
187
|
+
`<rect class="diagram-node level-3" x="${m.x + ox}" y="${m.y + oy}" width="${m.width}" height="${m.height}" rx="6"/>`,
|
|
188
|
+
);
|
|
189
|
+
parts.push(
|
|
190
|
+
`<text class="text-secondary" x="${m.x + ox + 10}" y="${m.y + oy + 16}" style="dominant-baseline:central">${escXml(m.date)}</text>`,
|
|
191
|
+
);
|
|
192
|
+
parts.push(
|
|
193
|
+
`<text class="text-primary" x="${m.x + ox + 10}" y="${m.y + oy + 36}" style="dominant-baseline:central">${escXml(truncate(m.name, 22))}</text>`,
|
|
194
|
+
);
|
|
195
|
+
parts.push(
|
|
196
|
+
`<text class="text-secondary" x="${m.x + ox + 10}" y="${m.y + oy + 52}" style="dominant-baseline:central">(${escXml(m.archimateClass)})</text>`,
|
|
197
|
+
);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// Child activities.
|
|
201
|
+
for (const a of layout.childActivities) {
|
|
202
|
+
parts.push(
|
|
203
|
+
`<rect class="diagram-node level-1" x="${a.x + ox}" y="${a.y + oy}" width="${a.width}" height="${a.height}" rx="6"/>`,
|
|
204
|
+
);
|
|
205
|
+
parts.push(
|
|
206
|
+
`<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>`,
|
|
207
|
+
);
|
|
208
|
+
if (a.meta) {
|
|
209
|
+
parts.push(
|
|
210
|
+
`<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>`,
|
|
211
|
+
);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// Footer — notes.
|
|
216
|
+
if (layout.footerRow) {
|
|
217
|
+
const r = layout.footerRow;
|
|
218
|
+
parts.push(`<rect class="diagram-node level-2" x="${r.x + ox}" y="${r.y + oy}" width="${r.width}" height="${r.height}" rx="6"/>`);
|
|
219
|
+
parts.push(`<text class="text-secondary" x="${r.x + ox + 12}" y="${r.y + oy + 22}" style="dominant-baseline:central">${escXml(r.label)}</text>`);
|
|
220
|
+
r.valueLines.forEach((line, i) => {
|
|
221
|
+
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>`);
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
return parts.join('\n');
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
export function renderActivityCardSvg(
|
|
229
|
+
doc: ActivityCardDoc,
|
|
230
|
+
options: RenderActivityCardOptions = {},
|
|
231
|
+
): string {
|
|
232
|
+
// `ARCHIMATE_CLASS` is referenced so the host-neutral module pins the same
|
|
233
|
+
// §5.1 class convention the layout applies; keeps the import meaningful.
|
|
234
|
+
void ARCHIMATE_CLASS;
|
|
235
|
+
|
|
236
|
+
const { title = '' } = options;
|
|
237
|
+
const resolved = resolveSingleDoc(doc);
|
|
238
|
+
const layout = layoutActivityCard(resolved);
|
|
239
|
+
|
|
240
|
+
if (layout.bounds.width <= 0 || layout.bounds.height <= 0) return EMPTY_SVG;
|
|
241
|
+
|
|
242
|
+
const titleH = title ? 28 : 0;
|
|
243
|
+
const w = layout.bounds.width + PAD * 2;
|
|
244
|
+
const h = layout.bounds.height + PAD * 2 + titleH;
|
|
245
|
+
const ox = PAD;
|
|
246
|
+
const oy = PAD + titleH;
|
|
247
|
+
|
|
248
|
+
const body = buildBody(layout, ox, oy);
|
|
249
|
+
|
|
250
|
+
const titleSvg = title
|
|
251
|
+
? `<text class="text-header" x="${PAD}" y="${PAD + 14}">${escXml(`Activity Card — ${title}`)}</text>`
|
|
252
|
+
: '';
|
|
253
|
+
|
|
254
|
+
const embedCss = generateSvgEmbedCss('transitrix');
|
|
255
|
+
|
|
256
|
+
return `<svg xmlns="http://www.w3.org/2000/svg" width="${w}" height="${h}" viewBox="0 0 ${w} ${h}">
|
|
257
|
+
<style>${embedCss}</style>
|
|
258
|
+
<defs>
|
|
259
|
+
<marker id="ac-arrow" markerWidth="8" markerHeight="8" refX="8" refY="3" orient="auto">
|
|
260
|
+
<path d="M0,0 L0,6 L8,3 z" class="arrow-fill"/>
|
|
261
|
+
</marker>
|
|
262
|
+
</defs>
|
|
263
|
+
${titleSvg}
|
|
264
|
+
${body}
|
|
265
|
+
</svg>`;
|
|
266
|
+
}
|
|
@@ -37,10 +37,14 @@ export interface RenderFgcaOptions {
|
|
|
37
37
|
variant?: 'fgca' | 'fga';
|
|
38
38
|
/** Optional heading rendered as a left-anchored `text-header` line. */
|
|
39
39
|
title?: string;
|
|
40
|
+
/** Exit edge curvature; 1 = default, 0 = straight, higher = stronger arc. */
|
|
41
|
+
curvature?: number;
|
|
42
|
+
/** Entry curvature at the target node; defaults to `curvature` when omitted. */
|
|
43
|
+
entryCurvature?: number;
|
|
40
44
|
}
|
|
41
45
|
|
|
42
46
|
export function renderFgcaSvg(doc: FGCADoc, options: RenderFgcaOptions = {}): string {
|
|
43
|
-
const { variant = 'fgca', title = '' } = options;
|
|
47
|
+
const { variant = 'fgca', title = '', curvature = DEFAULT_EDGE_CURVATURE, entryCurvature } = options;
|
|
44
48
|
const hideChanges = variant === 'fga';
|
|
45
49
|
|
|
46
50
|
const { nodes, edges, columns, width, height } = layoutFGCAPreview(doc, { hideChanges });
|
|
@@ -58,13 +62,10 @@ export function renderFgcaSvg(doc: FGCADoc, options: RenderFgcaOptions = {}): st
|
|
|
58
62
|
)
|
|
59
63
|
.join('\n');
|
|
60
64
|
|
|
61
|
-
// Cubic bezier with horizontal control handles (shared geometry in
|
|
62
|
-
// @transitrix/diagrams). Uses the package default curvature — the
|
|
63
|
-
// interactive curvature control is a VS Code-only concern.
|
|
64
65
|
const edgeSvg = edges
|
|
65
66
|
.map(
|
|
66
67
|
(e) =>
|
|
67
|
-
`<path d="${horizontalCubicEdgePath(e.sx, e.sy, e.tx, e.ty,
|
|
68
|
+
`<path d="${horizontalCubicEdgePath(e.sx, e.sy, e.tx, e.ty, curvature, entryCurvature)}" class="diagram-edge" marker-end="url(#arrow)"/>`,
|
|
68
69
|
)
|
|
69
70
|
.join('\n');
|
|
70
71
|
|