@transitrix/diagrams 1.0.0 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -21
- package/README.md +47 -47
- package/dist/__tests__/edge-path.test.js +8 -0
- package/dist/activities/cpm.js +11 -2
- package/dist/activities/gantt.js +6 -3
- package/dist/activities/types.d.ts +1 -0
- package/dist/activities/validate.js +3 -3
- 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 +161 -7
- 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 +115 -28
- 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 +509 -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 +78 -81
- package/dist/compliance/impact.d.ts +100 -16
- package/dist/compliance/impact.js +272 -28
- 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 +75 -46
- package/dist/factor/index.d.ts +3 -2
- package/dist/factor/index.js +1 -1
- package/dist/factor/types.d.ts +5 -8
- 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__/chain-table.test.js +2 -2
- package/dist/fgca/__tests__/layout.test.js +11 -11
- package/dist/fgca/__tests__/preview-layout.test.js +7 -7
- package/dist/fgca/__tests__/resolver.test.js +185 -0
- package/dist/fgca/chain-table.d.ts +1 -1
- package/dist/fgca/chain-table.js +4 -4
- package/dist/fgca/index.d.ts +4 -2
- package/dist/fgca/index.js +2 -1
- package/dist/fgca/layout.d.ts +2 -2
- package/dist/fgca/layout.js +9 -9
- package/dist/fgca/nodeTypes.d.ts +1 -1
- package/dist/fgca/nodeTypes.js +2 -2
- package/dist/fgca/nodes/FGCAFactorNode.d.ts +3 -3
- package/dist/fgca/nodes/FGCAFactorNode.js +5 -5
- package/dist/fgca/parse-canonical.js +15 -10
- package/dist/fgca/preview-layout.d.ts +1 -1
- package/dist/fgca/preview-layout.js +4 -4
- package/dist/fgca/resolver.d.ts +49 -0
- package/dist/fgca/resolver.js +126 -0
- package/dist/fgca/types.d.ts +11 -11
- package/dist/fgca/types.js +2 -2
- package/dist/fgca/validate.d.ts +2 -2
- package/dist/fgca/validate.js +17 -14
- 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/index.d.ts +1 -1
- package/dist/process-blueprint/layout.js +66 -35
- package/dist/process-blueprint/types.d.ts +36 -1
- package/dist/scenarios/index.d.ts +1 -1
- package/dist/scenarios/types.d.ts +4 -4
- 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 +71 -69
- package/dist/theme/tokens.d.ts +7 -1
- package/dist/theme/tokens.js +3 -1
- package/dist/webview/__tests__/entry-notations.test.js +2 -0
- package/dist/webview/__tests__/entry.test.js +11 -11
- package/dist/webview/__tests__/render-process.test.js +463 -0
- package/dist/webview/__tests__/render-util.test.d.ts +1 -0
- package/dist/webview/__tests__/render-util.test.js +54 -0
- package/dist/webview/entry.d.ts +1 -1
- package/dist/webview/entry.js +9 -5
- package/dist/webview/render-activities.d.ts +24 -2
- package/dist/webview/render-activities.js +92 -57
- package/dist/webview/render-activity-card.d.ts +11 -1
- package/dist/webview/render-activity-card.js +115 -79
- package/dist/webview/render-blocks.d.ts +24 -1
- package/dist/webview/render-blocks.js +58 -24
- package/dist/webview/render-fgca.d.ts +31 -0
- package/dist/webview/render-fgca.js +30 -19
- package/dist/webview/render-goals.d.ts +27 -1
- package/dist/webview/render-goals.js +42 -32
- package/dist/webview/render-process-blueprint.d.ts +11 -1
- package/dist/webview/render-process-blueprint.js +118 -53
- package/dist/webview/render-process.d.ts +95 -0
- package/dist/webview/render-process.js +392 -0
- package/dist/webview/render-scenarios.js +2 -2
- package/dist/webview/render-util.d.ts +19 -6
- package/dist/webview/render-util.js +23 -15
- package/package.json +54 -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 +107 -95
- package/src/activities/gantt.ts +277 -276
- package/src/activities/index.ts +28 -28
- package/src/activities/layout.ts +112 -112
- package/src/activities/types.ts +149 -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 +330 -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 +422 -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 +1059 -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 +291 -295
- package/src/compliance/impact.ts +979 -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 +81 -46
- package/src/factor/index.ts +3 -7
- package/src/factor/types.ts +15 -19
- 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 +405 -400
- package/src/fgca/preview-layout.ts +217 -217
- package/src/fgca/resolver.ts +159 -0
- package/src/fgca/types.ts +50 -50
- package/src/fgca/validate.ts +129 -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 +32 -31
- package/src/process-blueprint/layout.ts +564 -531
- package/src/process-blueprint/types.ts +278 -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 +239 -237
- package/src/theme/tokens.ts +135 -133
- package/src/typed-id.ts +56 -56
- package/src/validation-types.ts +36 -36
- package/src/webview/__tests__/entry-notations.test.ts +79 -77
- package/src/webview/__tests__/entry.test.ts +64 -64
- package/src/webview/__tests__/render-goals.test.ts +72 -72
- package/src/webview/__tests__/render-process.test.ts +538 -0
- package/src/webview/__tests__/render-util.test.ts +66 -0
- package/src/webview/entry.ts +298 -292
- package/src/webview/render-activities.ts +111 -69
- package/src/webview/render-activity-card.ts +275 -234
- package/src/webview/render-blocks.ts +78 -28
- package/src/webview/render-fgca.ts +53 -28
- package/src/webview/render-goals.ts +133 -110
- package/src/webview/render-process-blueprint.ts +281 -207
- package/src/webview/render-process.ts +507 -0
- package/src/webview/render-scenarios.ts +3 -3
- package/src/webview/render-util.ts +27 -14
- 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.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
- /package/dist/{issues/__tests__/validate.test.d.ts → webview/__tests__/render-process.test.d.ts} +0 -0
|
@@ -11,6 +11,10 @@
|
|
|
11
11
|
* Only the DEFAULT network view (Project Schedule Network Diagram) is ported.
|
|
12
12
|
* The Gantt view, the CSS-only tab switcher and the interactive spacing /
|
|
13
13
|
* curvature controls stay in the VS Code preview.
|
|
14
|
+
*
|
|
15
|
+
* Single-emitter unification (review C): the canonical network body lives here
|
|
16
|
+
* in `renderActivitiesNetworkBody` and is shared verbatim with the VS Code
|
|
17
|
+
* preview's `networkSvg`.
|
|
14
18
|
*/
|
|
15
19
|
import { layoutActivities } from '../activities/layout.js';
|
|
16
20
|
import { computeCpm } from '../activities/cpm.js';
|
|
@@ -27,29 +31,119 @@ const N_NODE_W = 200;
|
|
|
27
31
|
const N_NODE_H = 80;
|
|
28
32
|
const N_PAD = 24;
|
|
29
33
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
34
|
+
/**
|
|
35
|
+
* Network-view diagram CSS (critical path, milestones, edge colours) — the
|
|
36
|
+
* canonical rules shared with the VS Code preview's `ACTIVITIES_DIAGRAM_CSS`
|
|
37
|
+
* network subset. Embedded alongside the shared theme CSS so the SVG is
|
|
38
|
+
* self-contained for the JCEF host.
|
|
39
|
+
*/
|
|
40
|
+
export const ACTIVITIES_NETWORK_CSS = `
|
|
41
|
+
.act-node { fill: var(--ts-layer-activity, #d4edda); stroke: var(--ts-node-stroke, #94a3b8); stroke-width: 1; }
|
|
36
42
|
.critical-node { fill: #fff7ed; stroke: var(--ts-brand-orange, #ff4d00); stroke-width: 2.5; }
|
|
37
43
|
.milestone-node { fill: #ecfeff; stroke: var(--ts-text-muted, #64748b); stroke-dasharray: 4 2; }
|
|
38
44
|
.critical-edge { stroke: var(--ts-brand-orange, #ff4d00); stroke-width: 2; }
|
|
39
45
|
.arrow-fill-critical { fill: var(--ts-brand-orange, #ff4d00); }
|
|
40
46
|
`;
|
|
41
47
|
|
|
48
|
+
/** Arrowhead marker defs shared by both hosts for the network view. */
|
|
49
|
+
export const ACTIVITIES_NETWORK_DEFS = `<defs>
|
|
50
|
+
<marker id="arrow" markerWidth="8" markerHeight="8" refX="8" refY="3" orient="auto">
|
|
51
|
+
<path d="M0,0 L0,6 L8,3 z" class="arrow-fill"/>
|
|
52
|
+
</marker>
|
|
53
|
+
<marker id="arrow-crit" markerWidth="8" markerHeight="8" refX="8" refY="3" orient="auto">
|
|
54
|
+
<path d="M0,0 L0,6 L8,3 z" class="arrow-fill-critical"/>
|
|
55
|
+
</marker>
|
|
56
|
+
</defs>`;
|
|
57
|
+
|
|
42
58
|
function truncate(s: string, maxLen: number): string {
|
|
43
59
|
return s.length > maxLen ? s.slice(0, maxLen - 1) + '…' : s;
|
|
44
60
|
}
|
|
45
61
|
|
|
62
|
+
/**
|
|
63
|
+
* Canonical Network (PSND) body — the node rects and edge paths that go inside
|
|
64
|
+
* the `<svg>`, shared verbatim by the VS Code preview and the host-neutral
|
|
65
|
+
* wrapper below. Excludes the host-specific title block and the (identical)
|
|
66
|
+
* marker defs (`ACTIVITIES_NETWORK_DEFS`).
|
|
67
|
+
*
|
|
68
|
+
* `ox`/`oy` are the canvas offsets (the caller folds in padding + any title
|
|
69
|
+
* height); `curvature`/`entryCurvature` scale the exit/entry edge handles.
|
|
70
|
+
*/
|
|
71
|
+
export function renderActivitiesNetworkBody(
|
|
72
|
+
layout: ActivitiesLayout,
|
|
73
|
+
cpm: ReturnType<typeof computeCpm>,
|
|
74
|
+
ox: number,
|
|
75
|
+
oy: number,
|
|
76
|
+
curvature: number,
|
|
77
|
+
entryCurvature: number | undefined,
|
|
78
|
+
): string {
|
|
79
|
+
const nodeMap = new Map(layout.nodes.map((n) => [n.id, n]));
|
|
80
|
+
|
|
81
|
+
// SVG paints later siblings over earlier ones. Sort non-critical first so the
|
|
82
|
+
// bright orange critical-path edges land on top — a gray edge crossing an
|
|
83
|
+
// orange one shouldn't bury the critical signal.
|
|
84
|
+
const orderedEdges = [...layout.edges].sort(
|
|
85
|
+
(a, b) => Number(Boolean(a.isCritical)) - Number(Boolean(b.isCritical)),
|
|
86
|
+
);
|
|
87
|
+
|
|
88
|
+
const edgeSvg = orderedEdges
|
|
89
|
+
.map((e) => {
|
|
90
|
+
const s = nodeMap.get(e.sourceId);
|
|
91
|
+
const t = nodeMap.get(e.targetId);
|
|
92
|
+
if (!s || !t) return '';
|
|
93
|
+
const sx = s.x + ox + s.width;
|
|
94
|
+
const sy = s.y + oy + s.height / 2;
|
|
95
|
+
const tx = t.x + ox;
|
|
96
|
+
const ty = t.y + oy + t.height / 2;
|
|
97
|
+
const cls = e.isCritical ? 'diagram-edge critical-edge' : 'diagram-edge';
|
|
98
|
+
const marker = `url(#${e.isCritical ? 'arrow-crit' : 'arrow'})`;
|
|
99
|
+
return `<path d="${horizontalCubicEdgePath(sx, sy, tx, ty, curvature, entryCurvature)}" class="${cls}" marker-end="${marker}"/>`;
|
|
100
|
+
})
|
|
101
|
+
.join('\n');
|
|
102
|
+
|
|
103
|
+
const nodeSvg = layout.nodes
|
|
104
|
+
.map((n) => {
|
|
105
|
+
const x = n.x + ox;
|
|
106
|
+
const y = n.y + oy;
|
|
107
|
+
const isCritical = cpm.get(n.id)?.isCritical ?? false;
|
|
108
|
+
const durVal = n.data.duration;
|
|
109
|
+
const isMilestone = (durVal ?? -1) === 0;
|
|
110
|
+
const cls = `diagram-node act-node ${isCritical ? 'critical-node' : ''} ${isMilestone ? 'milestone-node' : ''}`.trim();
|
|
111
|
+
const idLabel = escXml(n.id);
|
|
112
|
+
const words = n.data.name.split(' ');
|
|
113
|
+
let line1 = '';
|
|
114
|
+
let line2 = '';
|
|
115
|
+
for (const w of words) {
|
|
116
|
+
if ((line1 + ' ' + w).trim().length <= 24) line1 = (line1 + ' ' + w).trim();
|
|
117
|
+
else if ((line2 + ' ' + w).trim().length <= 24) line2 = (line2 + ' ' + w).trim();
|
|
118
|
+
else if (!line2) { line2 = w.slice(0, 22) + '…'; break; }
|
|
119
|
+
}
|
|
120
|
+
const twoLines = line2.length > 0;
|
|
121
|
+
const nameY1 = twoLines ? y + 18 : y + 28;
|
|
122
|
+
const nameY2 = y + 34;
|
|
123
|
+
const idY = twoLines ? y + 54 : y + 50;
|
|
124
|
+
const durLabel = (durVal !== undefined && durVal > 0) ? `${durVal}d` : '';
|
|
125
|
+
return [
|
|
126
|
+
`<rect class="${cls}" x="${x}" y="${y}" width="${N_NODE_W}" height="${N_NODE_H}" rx="8"/>`,
|
|
127
|
+
`<text class="text-primary" x="${x + N_NODE_W / 2}" y="${nameY1}" text-anchor="middle" dominant-baseline="central">${escXml(line1)}</text>`,
|
|
128
|
+
twoLines ? `<text class="text-primary" x="${x + N_NODE_W / 2}" y="${nameY2}" text-anchor="middle" dominant-baseline="central">${escXml(line2)}</text>` : '',
|
|
129
|
+
`<text class="text-id" x="${x + N_NODE_W / 2}" y="${idY}" text-anchor="middle" dominant-baseline="central">${idLabel}</text>`,
|
|
130
|
+
durLabel ? `<text class="text-secondary" x="${x + N_NODE_W - 8}" y="${y + N_NODE_H - 8}" text-anchor="end">${durLabel}</text>` : '',
|
|
131
|
+
].filter(Boolean).join('\n');
|
|
132
|
+
})
|
|
133
|
+
.join('\n');
|
|
134
|
+
|
|
135
|
+
return `${nodeSvg}\n${edgeSvg}`;
|
|
136
|
+
}
|
|
137
|
+
|
|
46
138
|
export interface RenderActivitiesOptions {
|
|
47
139
|
/** Optional heading rendered as a `text-header` above the diagram. */
|
|
48
140
|
title?: string;
|
|
49
141
|
/** Network column / row gaps. Defaults match `layoutActivities`. */
|
|
50
142
|
gaps?: ActivitiesLayoutOptions;
|
|
51
|
-
/**
|
|
143
|
+
/** Exit edge curvature; 1 = default, 0 = straight, higher = stronger arc. */
|
|
52
144
|
curvature?: number;
|
|
145
|
+
/** Entry curvature at the target node; defaults to `curvature` when omitted. */
|
|
146
|
+
entryCurvature?: number;
|
|
53
147
|
}
|
|
54
148
|
|
|
55
149
|
/**
|
|
@@ -67,7 +161,7 @@ export interface RenderActivitiesOptions {
|
|
|
67
161
|
* than short-circuiting.
|
|
68
162
|
*/
|
|
69
163
|
export function renderActivitiesSvg(doc: ActivityDoc, options: RenderActivitiesOptions = {}): string {
|
|
70
|
-
const { title = '', gaps = {}, curvature = DEFAULT_EDGE_CURVATURE } = options;
|
|
164
|
+
const { title = '', gaps = {}, curvature = DEFAULT_EDGE_CURVATURE, entryCurvature } = options;
|
|
71
165
|
|
|
72
166
|
const layout: ActivitiesLayout = layoutActivities(doc, gaps);
|
|
73
167
|
|
|
@@ -82,75 +176,23 @@ export function renderActivitiesSvg(doc: ActivityDoc, options: RenderActivitiesO
|
|
|
82
176
|
const ox = -layout.bounds.x + N_PAD;
|
|
83
177
|
const oy = -layout.bounds.y + N_PAD + titleH;
|
|
84
178
|
|
|
85
|
-
const
|
|
86
|
-
|
|
87
|
-
// SVG paints later siblings over earlier ones. Sort non-critical first so the
|
|
88
|
-
// bright orange critical-path edges land on top — a gray edge crossing an
|
|
89
|
-
// orange one shouldn't bury the critical signal.
|
|
90
|
-
const orderedEdges = [...layout.edges].sort(
|
|
91
|
-
(a, b) => Number(Boolean(a.isCritical)) - Number(Boolean(b.isCritical)),
|
|
92
|
-
);
|
|
93
|
-
|
|
94
|
-
// Edge path = a single cubic Bézier with horizontal control handles (shared
|
|
95
|
-
// geometry in `edge-path.js`), so the curve meets the vertical node edge at a
|
|
96
|
-
// right angle and the marker-end arrow reads as perpendicular.
|
|
97
|
-
const edgeSvg = orderedEdges
|
|
98
|
-
.map((e) => {
|
|
99
|
-
const s = nodeMap.get(e.sourceId);
|
|
100
|
-
const t = nodeMap.get(e.targetId);
|
|
101
|
-
if (!s || !t) return '';
|
|
102
|
-
const sx = s.x + ox + s.width;
|
|
103
|
-
const sy = s.y + oy + s.height / 2;
|
|
104
|
-
const tx = t.x + ox;
|
|
105
|
-
const ty = t.y + oy + t.height / 2;
|
|
106
|
-
const cls = e.isCritical ? 'diagram-edge critical-edge' : 'diagram-edge';
|
|
107
|
-
const marker = `url(#${e.isCritical ? 'arrow-crit' : 'arrow'})`;
|
|
108
|
-
return `<path d="${horizontalCubicEdgePath(sx, sy, tx, ty, curvature)}" class="${cls}" marker-end="${marker}"/>`;
|
|
109
|
-
})
|
|
110
|
-
.join('\n');
|
|
111
|
-
|
|
112
|
-
const nodeSvg = layout.nodes
|
|
113
|
-
.map((n) => {
|
|
114
|
-
const x = n.x + ox;
|
|
115
|
-
const y = n.y + oy;
|
|
116
|
-
const isCritical = cpm.get(n.id)?.isCritical ?? false;
|
|
117
|
-
const isMilestone = (n.data.duration ?? -1) === 0;
|
|
118
|
-
const cls = `diagram-node act-node ${isCritical ? 'critical-node' : ''} ${isMilestone ? 'milestone-node' : ''}`.trim();
|
|
119
|
-
const idLabel = escXml(n.id);
|
|
120
|
-
const nameLabel = escXml(truncate(n.data.name, 24));
|
|
121
|
-
const durLabel = n.data.duration !== undefined ? `${n.data.duration}d` : '—';
|
|
122
|
-
return [
|
|
123
|
-
`<rect class="${cls}" x="${x}" y="${y}" width="${N_NODE_W}" height="${N_NODE_H}" rx="6"/>`,
|
|
124
|
-
`<text class="text-id" x="${x + 8}" y="${y + 18}">${idLabel}</text>`,
|
|
125
|
-
`<text class="text-primary" x="${x + N_NODE_W / 2}" y="${y + N_NODE_H / 2}" text-anchor="middle" dominant-baseline="central">${nameLabel}</text>`,
|
|
126
|
-
`<text class="text-secondary" x="${x + N_NODE_W - 8}" y="${y + N_NODE_H - 10}" text-anchor="end">${escXml(durLabel)}</text>`,
|
|
127
|
-
].join('\n');
|
|
128
|
-
})
|
|
129
|
-
.join('\n');
|
|
179
|
+
const body = renderActivitiesNetworkBody(layout, cpm, ox, oy, curvature, entryCurvature);
|
|
130
180
|
|
|
131
181
|
const titleSvg = title
|
|
132
182
|
? `<text class="text-header" x="${N_PAD}" y="${N_PAD - 6}">${escXml(title)}</text>`
|
|
133
183
|
: '';
|
|
134
184
|
|
|
135
|
-
// Embed the shared theme CSS plus the
|
|
136
|
-
// the
|
|
137
|
-
//
|
|
138
|
-
//
|
|
139
|
-
//
|
|
140
|
-
const embedCss = generateSvgEmbedCss('transitrix') +
|
|
185
|
+
// Embed the shared theme CSS plus the network diagram CSS inside the SVG so
|
|
186
|
+
// the rendered output is self-contained — the JCEF host page only needs to
|
|
187
|
+
// drop the SVG into the DOM and styling resolves without any cooperation from
|
|
188
|
+
// the host stylesheet. Matches what the VS Code path produces via
|
|
189
|
+
// `prepareSvgForExport`.
|
|
190
|
+
const embedCss = generateSvgEmbedCss('transitrix') + ACTIVITIES_NETWORK_CSS;
|
|
141
191
|
|
|
142
192
|
return `<svg xmlns="http://www.w3.org/2000/svg" width="${w}" height="${h}" viewBox="0 0 ${w} ${h}">
|
|
143
193
|
<style>${embedCss}</style>
|
|
144
|
-
|
|
145
|
-
<marker id="arrow" markerWidth="8" markerHeight="8" refX="8" refY="3" orient="auto">
|
|
146
|
-
<path d="M0,0 L0,6 L8,3 z" class="arrow-fill"/>
|
|
147
|
-
</marker>
|
|
148
|
-
<marker id="arrow-crit" markerWidth="8" markerHeight="8" refX="8" refY="3" orient="auto">
|
|
149
|
-
<path d="M0,0 L0,6 L8,3 z" class="arrow-fill-critical"/>
|
|
150
|
-
</marker>
|
|
151
|
-
</defs>
|
|
194
|
+
${ACTIVITIES_NETWORK_DEFS}
|
|
152
195
|
${titleSvg}
|
|
153
|
-
${
|
|
154
|
-
${edgeSvg}
|
|
196
|
+
${body}
|
|
155
197
|
</svg>`;
|
|
156
198
|
}
|