@transitrix/diagrams 1.7.6 → 1.8.2
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/dist/__tests__/node-size-presets.test.d.ts +1 -0
- package/dist/__tests__/node-size-presets.test.js +30 -0
- package/dist/activities/layout.js +7 -7
- package/dist/activities/types.d.ts +4 -0
- package/dist/blocks/__tests__/layout.test.js +2 -2
- package/dist/blocks/layout.js +3 -2
- package/dist/capability-map/dsm-layout.js +1 -1
- package/dist/capability-map/dsm-schema.d.ts +1 -1
- package/dist/capability-map/dsm-schema.js +1 -1
- package/dist/capability-map/layout-tree.d.ts +6 -2
- package/dist/capability-map/layout-tree.js +12 -10
- package/dist/capability-map/render-capability-tree.d.ts +2 -9
- package/dist/capability-map/render-capability-tree.js +16 -28
- package/dist/fgca/preview-layout.d.ts +10 -6
- package/dist/fgca/preview-layout.js +16 -15
- package/dist/node-size-presets.d.ts +63 -0
- package/dist/node-size-presets.js +117 -0
- package/dist/process-blueprint/layout.js +4 -47
- package/dist/webview/__tests__/entity-text-layout.test.d.ts +1 -0
- package/dist/webview/__tests__/entity-text-layout.test.js +60 -0
- package/dist/webview/__tests__/entry.test.js +3 -3
- package/dist/webview/__tests__/render-blocks.test.js +10 -13
- package/dist/webview/__tests__/render-goals.test.js +41 -0
- package/dist/webview/entity-text-layout.d.ts +95 -0
- package/dist/webview/entity-text-layout.js +277 -0
- package/dist/webview/entry.js +7 -3
- package/dist/webview/notation-style.d.ts +1 -1
- package/dist/webview/notation-style.js +1 -1
- package/dist/webview/render-activities.d.ts +3 -1
- package/dist/webview/render-activities.js +29 -32
- package/dist/webview/render-blocks.d.ts +4 -1
- package/dist/webview/render-blocks.js +27 -108
- package/dist/webview/render-fgca.d.ts +15 -8
- package/dist/webview/render-fgca.js +41 -44
- package/dist/webview/render-goals.d.ts +2 -0
- package/dist/webview/render-goals.js +21 -36
- package/dist/webview/render-process-blueprint.d.ts +4 -10
- package/dist/webview/render-process-blueprint.js +18 -64
- package/package.json +1 -1
- package/src/__tests__/node-size-presets.test.ts +50 -0
- package/src/activities/layout.ts +12 -7
- package/src/activities/types.ts +4 -0
- package/src/blocks/__tests__/layout.test.ts +2 -2
- package/src/blocks/layout.ts +3 -2
- package/src/capability-map/dsm-layout.ts +1 -1
- package/src/capability-map/dsm-schema.ts +1 -1
- package/src/capability-map/index.ts +1 -1
- package/src/capability-map/layout-tree.ts +17 -9
- package/src/capability-map/render-capability-tree.ts +18 -30
- package/src/fgca/preview-layout.ts +24 -17
- package/src/node-size-presets.ts +180 -0
- package/src/process-blueprint/layout.ts +4 -48
- package/src/webview/__tests__/entity-text-layout.test.ts +73 -0
- package/src/webview/__tests__/entry.test.ts +3 -3
- package/src/webview/__tests__/render-blocks.test.ts +11 -14
- package/src/webview/__tests__/render-goals.test.ts +42 -0
- package/src/webview/entity-text-layout.ts +355 -0
- package/src/webview/entry.ts +8 -4
- package/src/webview/notation-style.ts +1 -1
- package/src/webview/render-activities.ts +38 -27
- package/src/webview/render-blocks.ts +35 -118
- package/src/webview/render-fgca.ts +59 -44
- package/src/webview/render-goals.ts +22 -34
- package/src/webview/render-process-blueprint.ts +30 -65
|
@@ -14,30 +14,28 @@ import type {
|
|
|
14
14
|
ComplianceChip,
|
|
15
15
|
ProcessBlueprintFile,
|
|
16
16
|
ProcessBlueprintLayout,
|
|
17
|
+
ProcessBlueprintLayoutOptions,
|
|
17
18
|
} from '../process-blueprint/types.js';
|
|
19
|
+
import {
|
|
20
|
+
parseNodeSizePreset,
|
|
21
|
+
resolveProcessBlueprintSize,
|
|
22
|
+
type NodeSizePreset,
|
|
23
|
+
} from '../node-size-presets.js';
|
|
18
24
|
import { generateSvgEmbedCss } from '../theme/index.js';
|
|
25
|
+
import {
|
|
26
|
+
layoutCenteredPillText,
|
|
27
|
+
layoutLeftCellLines,
|
|
28
|
+
maxCharsForInnerWidth,
|
|
29
|
+
truncateLine,
|
|
30
|
+
} from './entity-text-layout.js';
|
|
19
31
|
import { escXml } from './render-util.js';
|
|
20
32
|
|
|
21
33
|
const PAD = 24;
|
|
22
34
|
|
|
23
|
-
// Blueprint-specific rules not carried by the shared theme CSS. The outer
|
|
24
|
-
// border is drawn last on top of the clipped inner content; the row
|
|
25
|
-
// backgrounds are transparent (pills/chips carry the colour). Mirrors
|
|
26
|
-
// `BLUEPRINT_CSS` from the VS Code preview so the self-contained JCEF output
|
|
27
|
-
// matches the editor.
|
|
28
35
|
const BP_EMBED_CSS = `
|
|
29
36
|
.bp-row-bg { fill: none; }
|
|
30
37
|
.bp-border { fill: none; stroke: var(--ts-border, #cbd5e1); stroke-width: 1; }`;
|
|
31
38
|
|
|
32
|
-
function truncate(text: string, maxChars: number): string {
|
|
33
|
-
if (text.length <= maxChars) return text;
|
|
34
|
-
return text.slice(0, Math.max(0, maxChars - 1)) + '…';
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* Render a left-anchored, vertically centred multi-line text cell. `lines` are
|
|
39
|
-
* pre-wrapped by the layout; the block is centred within the cell height.
|
|
40
|
-
*/
|
|
41
39
|
function textCellSvg(
|
|
42
40
|
lines: string[],
|
|
43
41
|
cls: string,
|
|
@@ -46,25 +44,16 @@ function textCellSvg(
|
|
|
46
44
|
cellHeight: number,
|
|
47
45
|
lineHeight: number,
|
|
48
46
|
): string {
|
|
47
|
+
const specs = layoutLeftCellLines(lines, x, cellTop, cellHeight, lineHeight, cls);
|
|
49
48
|
const ls = lines.length > 0 ? lines : [''];
|
|
50
49
|
const first = cellTop + cellHeight / 2 - ((ls.length - 1) / 2) * lineHeight;
|
|
51
50
|
const tspans = ls
|
|
52
51
|
.map((ln, i) => `<tspan x="${x}" y="${first + i * lineHeight}">${escXml(ln)}</tspan>`)
|
|
53
52
|
.join('');
|
|
53
|
+
void specs;
|
|
54
54
|
return `<text class="${cls}" dominant-baseline="central">${tspans}</text>`;
|
|
55
55
|
}
|
|
56
56
|
|
|
57
|
-
/**
|
|
58
|
-
* Render a single compliance law chip wrapped in a `<g data-chip-law…>` so the
|
|
59
|
-
* VS Code host can wire click-to-drill-down. Other hosts (JCEF) simply ignore
|
|
60
|
-
* the data attributes.
|
|
61
|
-
*
|
|
62
|
-
* Three orthogonal decorations are expressed as SVG overrides:
|
|
63
|
-
* - `new` — dashed stroke border (`stroke-dasharray="4 2"`)
|
|
64
|
-
* - `gap` — warning-level fill (`class="…compliance-gap"`)
|
|
65
|
-
* - `deadline` — urgent fill override (`class="…compliance-deadline"`) + a small
|
|
66
|
-
* badge circle in the top-right corner
|
|
67
|
-
*/
|
|
68
57
|
function complianceChipSvg(
|
|
69
58
|
chip: ComplianceChip,
|
|
70
59
|
ox: number,
|
|
@@ -81,12 +70,13 @@ function complianceChipSvg(
|
|
|
81
70
|
if (hasDeadline) rectClass += ' compliance-deadline';
|
|
82
71
|
else if (hasGap) rectClass += ' compliance-gap';
|
|
83
72
|
const strokeDash = hasNew ? ' stroke-dasharray="4 2"' : '';
|
|
73
|
+
const maxChars = maxCharsForInnerWidth(Math.max(0, width - 8), 8);
|
|
84
74
|
const parts: string[] = [];
|
|
85
75
|
parts.push(
|
|
86
76
|
`<rect class="${rectClass}" x="${ax}" y="${ay}" width="${width}" height="${height}" rx="6"${strokeDash}/>`,
|
|
87
77
|
);
|
|
88
78
|
parts.push(
|
|
89
|
-
`<text class="text-pill" x="${ax + width / 2}" y="${ay + height / 2}" text-anchor="middle" dominant-baseline="central">${escXml(
|
|
79
|
+
`<text class="text-pill" x="${ax + width / 2}" y="${ay + height / 2}" text-anchor="middle" dominant-baseline="central">${escXml(truncateLine(lawId, maxChars))}</text>`,
|
|
90
80
|
);
|
|
91
81
|
if (hasDeadline) {
|
|
92
82
|
const br = 5;
|
|
@@ -100,15 +90,6 @@ function complianceChipSvg(
|
|
|
100
90
|
return `<g data-chip-law="${escXml(lawId)}" data-chip-stage="${escXml(stageId)}">\n${parts.join('\n')}\n</g>`;
|
|
101
91
|
}
|
|
102
92
|
|
|
103
|
-
/**
|
|
104
|
-
* Canonical Process Blueprint body — everything inside the `<svg>` except the
|
|
105
|
-
* host-specific title block. Shared verbatim by the VS Code preview and the
|
|
106
|
-
* host-neutral wrapper below so both surfaces stay pixel-identical.
|
|
107
|
-
*
|
|
108
|
-
* Layering: a clipped group holds the row fills and grid lines; pills and
|
|
109
|
-
* compliance chips render on top (so grid lines sit under pill content); the
|
|
110
|
-
* outer rounded border is drawn last so corner cells never overflow it.
|
|
111
|
-
*/
|
|
112
93
|
export function renderProcessBlueprintBody(
|
|
113
94
|
layout: ProcessBlueprintLayout,
|
|
114
95
|
ox: number,
|
|
@@ -119,31 +100,26 @@ export function renderProcessBlueprintBody(
|
|
|
119
100
|
const th = layout.bounds.height;
|
|
120
101
|
|
|
121
102
|
const parts: string[] = [];
|
|
122
|
-
// Pills and chips collected separately so they render after (on top of) grid lines.
|
|
123
103
|
const topParts: string[] = [];
|
|
124
104
|
|
|
125
|
-
// Outer background (fill only; border drawn last on top).
|
|
126
105
|
parts.push(
|
|
127
106
|
`<rect class="diagram-node level-0 bp-row-bg" x="${ox}" y="${oy}" width="${tw}" height="${th}" rx="6" stroke="none"/>`,
|
|
128
107
|
);
|
|
129
108
|
|
|
130
|
-
// ClipPath — clips all inner content to the outer rounded rect so corner cells
|
|
131
|
-
// don't visually overflow the rx=6 boundary.
|
|
132
109
|
parts.push(`<defs><clipPath id="${clipId}"><rect x="${ox}" y="${oy}" width="${tw}" height="${th}" rx="6"/></clipPath></defs>`);
|
|
133
110
|
|
|
134
111
|
const inner: string[] = [];
|
|
112
|
+
const headerMaxChars = Math.max(8, Math.floor((layout.stageColumnWidth - 16) / 7));
|
|
135
113
|
|
|
136
|
-
// Stage headers (top row).
|
|
137
114
|
for (const s of layout.stageHeaders) {
|
|
138
115
|
inner.push(
|
|
139
116
|
`<rect class="diagram-node level-1" x="${s.x + ox}" y="${s.y + oy}" width="${s.width}" height="${s.height}" stroke="none"/>`,
|
|
140
117
|
);
|
|
141
118
|
inner.push(
|
|
142
|
-
`<text class="text-header" x="${s.x + ox + s.width / 2}" y="${s.y + oy + s.height / 2}" text-anchor="middle" dominant-baseline="central">${escXml(
|
|
119
|
+
`<text class="text-header" x="${s.x + ox + s.width / 2}" y="${s.y + oy + s.height / 2}" text-anchor="middle" dominant-baseline="central">${escXml(truncateLine(s.name, headerMaxChars))}</text>`,
|
|
143
120
|
);
|
|
144
121
|
}
|
|
145
122
|
|
|
146
|
-
// Legend column (row labels).
|
|
147
123
|
for (const l of layout.legend) {
|
|
148
124
|
inner.push(
|
|
149
125
|
`<rect class="diagram-node level-2 bp-row-bg" x="${ox}" y="${l.y + oy}" width="${layout.legendColumnWidth}" height="${l.height}" stroke="none"/>`,
|
|
@@ -153,7 +129,6 @@ export function renderProcessBlueprintBody(
|
|
|
153
129
|
);
|
|
154
130
|
}
|
|
155
131
|
|
|
156
|
-
// Goal and result cells.
|
|
157
132
|
const textX = layout.cellTextPadX;
|
|
158
133
|
for (const c of layout.goalCells) {
|
|
159
134
|
inner.push(
|
|
@@ -168,7 +143,6 @@ export function renderProcessBlueprintBody(
|
|
|
168
143
|
inner.push(textCellSvg(c.lines, 'text-secondary', c.x + ox + textX, c.y + oy, c.height, layout.textLineHeight));
|
|
169
144
|
}
|
|
170
145
|
|
|
171
|
-
// Aspect rows — transparent background; pills carry the colour.
|
|
172
146
|
for (let r = 0; r < layout.aspectRows.length; r++) {
|
|
173
147
|
const row = layout.aspectRows[r];
|
|
174
148
|
const level = 5 + (r % 3);
|
|
@@ -176,31 +150,22 @@ export function renderProcessBlueprintBody(
|
|
|
176
150
|
`<rect class="diagram-node level-${level} bp-row-bg" x="${layout.legendColumnWidth + ox}" y="${row.y + oy}" width="${tw - layout.legendColumnWidth}" height="${row.height}" stroke="none"/>`,
|
|
177
151
|
);
|
|
178
152
|
for (const p of row.pills) {
|
|
179
|
-
const cx = p.x + ox + p.width / 2;
|
|
180
|
-
const pillLH = 15;
|
|
181
|
-
// p.lines: name lines (possibly wrapped), optionally with id as last line.
|
|
182
153
|
const hasIdLine = !!(p.id && p.lines.length > 0 && p.lines[p.lines.length - 1] === p.id);
|
|
183
154
|
const nameLines = hasIdLine ? p.lines.slice(0, -1) : p.lines;
|
|
184
155
|
const idLine = hasIdLine ? p.id : undefined;
|
|
185
|
-
const
|
|
156
|
+
const pillSpecs = layoutCenteredPillText(p.x + ox, p.y + oy, p.width, p.height, nameLines, idLine);
|
|
186
157
|
topParts.push(
|
|
187
158
|
`<rect class="diagram-node level-${level}" x="${p.x + ox}" y="${p.y + oy}" width="${p.width}" height="${p.height}" rx="6"/>`,
|
|
188
159
|
);
|
|
189
|
-
|
|
190
|
-
const
|
|
191
|
-
.map((ln, i) => `<tspan x="${cx}" y="${pillFirstY + i * pillLH}">${escXml(ln)}</tspan>`)
|
|
192
|
-
.join('');
|
|
193
|
-
topParts.push(`<text class="text-pill" text-anchor="middle" dominant-baseline="central">${tspans}</text>`);
|
|
194
|
-
}
|
|
195
|
-
if (idLine) {
|
|
160
|
+
for (const spec of pillSpecs) {
|
|
161
|
+
const anchor = spec.cls === 'text-pill' || spec.cls === 'text-secondary' ? ' text-anchor="middle"' : '';
|
|
196
162
|
topParts.push(
|
|
197
|
-
`<text class="
|
|
163
|
+
`<text class="${spec.cls}" x="${spec.x}" y="${spec.y}"${anchor} dominant-baseline="central">${escXml(spec.text)}</text>`,
|
|
198
164
|
);
|
|
199
165
|
}
|
|
200
166
|
}
|
|
201
167
|
}
|
|
202
168
|
|
|
203
|
-
// Compliance row (optional).
|
|
204
169
|
if (layout.complianceRow) {
|
|
205
170
|
const row = layout.complianceRow;
|
|
206
171
|
inner.push(
|
|
@@ -212,7 +177,6 @@ export function renderProcessBlueprintBody(
|
|
|
212
177
|
}
|
|
213
178
|
}
|
|
214
179
|
|
|
215
|
-
// Grid lines — drawn before pills so they appear under pill content.
|
|
216
180
|
const gridX1 = ox;
|
|
217
181
|
const gridX2 = ox + tw;
|
|
218
182
|
const gridY1 = oy;
|
|
@@ -232,10 +196,7 @@ export function renderProcessBlueprintBody(
|
|
|
232
196
|
inner.push(`<line class="diagram-edge" x1="${stageLineX}" y1="${gridY1}" x2="${stageLineX}" y2="${gridY2}"/>`);
|
|
233
197
|
}
|
|
234
198
|
|
|
235
|
-
// Inner content (fills + grid lines) clipped to rounded outer rect.
|
|
236
199
|
parts.push(`<g clip-path="url(#${clipId})">${inner.join('\n')}${topParts.join('\n')}</g>`);
|
|
237
|
-
|
|
238
|
-
// Outer border on top — fill="none" as attribute so PNG export (no external CSS) never shows black.
|
|
239
200
|
parts.push(`<rect class="bp-border" x="${ox}" y="${oy}" width="${tw}" height="${th}" rx="6" fill="none"/>`);
|
|
240
201
|
|
|
241
202
|
return parts.join('\n');
|
|
@@ -243,15 +204,21 @@ export function renderProcessBlueprintBody(
|
|
|
243
204
|
|
|
244
205
|
export interface RenderProcessBlueprintOptions {
|
|
245
206
|
title?: string;
|
|
207
|
+
nodeSizePreset?: NodeSizePreset;
|
|
208
|
+
layoutOptions?: ProcessBlueprintLayoutOptions;
|
|
246
209
|
}
|
|
247
210
|
|
|
248
211
|
export function renderProcessBlueprintSvg(
|
|
249
212
|
doc: ProcessBlueprintFile,
|
|
250
213
|
options: RenderProcessBlueprintOptions = {},
|
|
251
214
|
): string {
|
|
252
|
-
const { title = '' } = options;
|
|
215
|
+
const { title = '', nodeSizePreset = 'normal', layoutOptions } = options;
|
|
216
|
+
const sizing = resolveProcessBlueprintSize(parseNodeSizePreset(nodeSizePreset));
|
|
253
217
|
|
|
254
|
-
const layout: ProcessBlueprintLayout = layoutProcessBlueprint(doc
|
|
218
|
+
const layout: ProcessBlueprintLayout = layoutProcessBlueprint(doc, {
|
|
219
|
+
...sizing,
|
|
220
|
+
...layoutOptions,
|
|
221
|
+
});
|
|
255
222
|
|
|
256
223
|
if (layout.bounds.width === 0 || layout.bounds.height === 0) {
|
|
257
224
|
return `<svg xmlns="http://www.w3.org/2000/svg" width="0" height="0" viewBox="0 0 0 0"></svg>`;
|
|
@@ -269,8 +236,6 @@ export function renderProcessBlueprintSvg(
|
|
|
269
236
|
? `<text class="text-header" x="${PAD}" y="${PAD - 6}">${escXml(title)}</text>`
|
|
270
237
|
: '';
|
|
271
238
|
|
|
272
|
-
// Embed the shared theme CSS plus the blueprint-specific rules inside the SVG
|
|
273
|
-
// so the rendered output is self-contained for the JCEF host.
|
|
274
239
|
const embedCss = generateSvgEmbedCss('transitrix') + BP_EMBED_CSS;
|
|
275
240
|
|
|
276
241
|
return `<svg xmlns="http://www.w3.org/2000/svg" width="${w}" height="${h}" viewBox="0 0 ${w} ${h}">
|