@webpieces/nx-webpieces-rules 0.4.871 → 0.4.872
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/README.md +36 -0
- package/package.json +7 -7
- package/src/executors/generate/executor.d.ts +3 -1
- package/src/executors/generate/executor.js +9 -1
- package/src/executors/generate/executor.js.map +1 -1
- package/src/index.d.ts +1 -0
- package/src/index.js +5 -1
- package/src/index.js.map +1 -1
- package/src/lib/dot-syntax.d.ts +9 -0
- package/src/lib/dot-syntax.js +17 -0
- package/src/lib/dot-syntax.js.map +1 -1
- package/src/lib/graph-color-modes.d.ts +136 -0
- package/src/lib/graph-color-modes.js +299 -0
- package/src/lib/graph-color-modes.js.map +1 -0
- package/src/lib/graph-filter.client.d.ts +11 -1
- package/src/lib/graph-filter.client.js +25 -4
- package/src/lib/graph-filter.client.js.map +1 -1
- package/src/lib/graph-impact.d.ts +119 -0
- package/src/lib/graph-impact.js +295 -0
- package/src/lib/graph-impact.js.map +1 -0
- package/src/lib/graph-legend.d.ts +34 -0
- package/src/lib/graph-legend.js +116 -0
- package/src/lib/graph-legend.js.map +1 -0
- package/src/lib/graph-names.d.ts +2 -2
- package/src/lib/graph-names.js +2 -2
- package/src/lib/graph-names.js.map +1 -1
- package/src/lib/graph-navigation.d.ts +10 -0
- package/src/lib/graph-navigation.js +47 -12
- package/src/lib/graph-navigation.js.map +1 -1
- package/src/lib/graph-node-menu.d.ts +4 -3
- package/src/lib/graph-node-menu.js +137 -20
- package/src/lib/graph-node-menu.js.map +1 -1
- package/src/lib/graph-page-shell.d.ts +34 -0
- package/src/lib/graph-page-shell.js +121 -0
- package/src/lib/graph-page-shell.js.map +1 -0
- package/src/lib/graph-page-styles.d.ts +20 -0
- package/src/lib/graph-page-styles.js +144 -0
- package/src/lib/graph-page-styles.js.map +1 -0
- package/src/lib/graph-render-model.d.ts +14 -2
- package/src/lib/graph-render-model.js +19 -3
- package/src/lib/graph-render-model.js.map +1 -1
- package/src/lib/graph-responsibilities.js +1 -1
- package/src/lib/graph-responsibilities.js.map +1 -1
- package/src/lib/graph-visualizer.client.d.ts +65 -0
- package/src/lib/graph-visualizer.client.js +311 -7
- package/src/lib/graph-visualizer.client.js.map +1 -1
- package/src/lib/graph-visualizer.d.ts +19 -31
- package/src/lib/graph-visualizer.js +50 -286
- package/src/lib/graph-visualizer.js.map +1 -1
- package/src/lib/runtime-html-page.js +2 -2
- package/src/lib/runtime-html-page.js.map +1 -1
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Graph color modes (#1155)
|
|
4
|
+
*
|
|
5
|
+
* The architecture graph answers ONE question at a time instead of asking every box to encode two
|
|
6
|
+
* (fill = framework AND border = role). Each box is emitted once per mode, so the page can switch
|
|
7
|
+
* modes and re-render without regenerating anything:
|
|
8
|
+
*
|
|
9
|
+
* - RUNTIME "where the code can run". Base runtimes are equal vertical stripes in the fixed
|
|
10
|
+
* order browser | node | react-native; a specialization (angular/react inside
|
|
11
|
+
* browser, express inside node) is an inset inside its base stripe. The dependency
|
|
12
|
+
* rule becomes visual: a box may depend on a box carrying every color it has. The
|
|
13
|
+
* name and meta sit on a light label PLATE across the middle, so they read on every
|
|
14
|
+
* stripe combination while the stripes and insets stay visible above and below it.
|
|
15
|
+
* - ARCHITECTURE "servers · clients · APIs". A solid fill by ROLE; framework color is not shown.
|
|
16
|
+
* - IMPACT "what this branch touches". Projects owning a changed file are solid amber
|
|
17
|
+
* ("touched"); the rest of nx's affected set is light amber ("affected": their ci
|
|
18
|
+
* re-runs); every transitive dependency of the affected set that is not itself
|
|
19
|
+
* affected is light slate with a dashed border ("build input": compiled or restored
|
|
20
|
+
* from cache, unchanged); everything else is grey. Which box is which is only known
|
|
21
|
+
* per branch, so every box carries all four variants and the page picks one from the
|
|
22
|
+
* sidecar (graph-impact.ts).
|
|
23
|
+
*/
|
|
24
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
25
|
+
exports.NodeModeStyler = exports.LegendFacts = exports.NodeModeDots = exports.NodeFacts = exports.LABEL_PLATE = exports.IMPACT_UNTOUCHED = exports.IMPACT_BUILD_INPUT_BORDER = exports.IMPACT_BUILD_INPUT = exports.IMPACT_AFFECTED = exports.IMPACT_TOUCHED = exports.ROLE_STYLES = exports.RoleStyle = exports.NO_RUNTIME_COLOR = exports.BASE_ORDER = exports.FRAMEWORK_STYLES = exports.FrameworkStyle = exports.GRAPH_MODES = exports.ModeInfo = exports.GraphMode = void 0;
|
|
26
|
+
const rules_sdk_1 = require("@webpieces/rules-sdk");
|
|
27
|
+
const dot_syntax_1 = require("./dot-syntax");
|
|
28
|
+
var GraphMode;
|
|
29
|
+
(function (GraphMode) {
|
|
30
|
+
GraphMode["RUNTIME"] = "runtime";
|
|
31
|
+
GraphMode["ARCHITECTURE"] = "architecture";
|
|
32
|
+
GraphMode["IMPACT"] = "impact";
|
|
33
|
+
})(GraphMode || (exports.GraphMode = GraphMode = {}));
|
|
34
|
+
/** One selectable mode as the drawer and the node menu show it. */
|
|
35
|
+
class ModeInfo {
|
|
36
|
+
mode;
|
|
37
|
+
name;
|
|
38
|
+
subtitle;
|
|
39
|
+
constructor(mode, name, subtitle) {
|
|
40
|
+
this.mode = mode;
|
|
41
|
+
this.name = name;
|
|
42
|
+
this.subtitle = subtitle;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
exports.ModeInfo = ModeInfo;
|
|
46
|
+
/** In key order: `1` selects the first, `2` the second, `3` the third. */
|
|
47
|
+
exports.GRAPH_MODES = [
|
|
48
|
+
new ModeInfo(GraphMode.RUNTIME, 'Runtime', 'where the code can run'),
|
|
49
|
+
new ModeInfo(GraphMode.ARCHITECTURE, 'Architecture', 'servers · clients · APIs'),
|
|
50
|
+
new ModeInfo(GraphMode.IMPACT, 'Impact', 'what this branch touches'),
|
|
51
|
+
];
|
|
52
|
+
/** A framework value's color and the BASE runtime it is drawn inside (itself, for a base). */
|
|
53
|
+
class FrameworkStyle {
|
|
54
|
+
framework;
|
|
55
|
+
base;
|
|
56
|
+
color;
|
|
57
|
+
description;
|
|
58
|
+
constructor(framework, base, color, description) {
|
|
59
|
+
this.framework = framework;
|
|
60
|
+
this.base = base;
|
|
61
|
+
this.color = color;
|
|
62
|
+
this.description = description;
|
|
63
|
+
}
|
|
64
|
+
isBase() {
|
|
65
|
+
return this.framework === this.base;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
exports.FrameworkStyle = FrameworkStyle;
|
|
69
|
+
/**
|
|
70
|
+
* Keyed by the shared vocabulary, so a framework added to KNOWN_FRAMEWORKS without a color here is a
|
|
71
|
+
* COMPILE error — not a box silently painted grey and missing from the legend, which is exactly how
|
|
72
|
+
* react-native shipped (#1155).
|
|
73
|
+
*/
|
|
74
|
+
exports.FRAMEWORK_STYLES = {
|
|
75
|
+
browser: new FrameworkStyle('browser', 'browser', '#b9a7e6', 'browser base runtime'),
|
|
76
|
+
node: new FrameworkStyle('node', 'node', '#ffe07a', 'node base runtime'),
|
|
77
|
+
'react-native': new FrameworkStyle('react-native', 'react-native', '#7fd1c4', 'its own runtime, not a browser'),
|
|
78
|
+
angular: new FrameworkStyle('angular', 'browser', '#f59ab9', 'inside browser'),
|
|
79
|
+
react: new FrameworkStyle('react', 'browser', '#8fd3f7', 'inside browser'),
|
|
80
|
+
express: new FrameworkStyle('express', 'node', '#9fd8a3', 'inside node'),
|
|
81
|
+
};
|
|
82
|
+
/** Stripe order, left to right. Fixed, so the same set always reads the same way. */
|
|
83
|
+
exports.BASE_ORDER = ['browser', 'node', 'react-native'];
|
|
84
|
+
/** A box with no known framework tag. */
|
|
85
|
+
exports.NO_RUNTIME_COLOR = '#e4e6ec';
|
|
86
|
+
/** A role's solid fill and the text color that stays readable on it. */
|
|
87
|
+
class RoleStyle {
|
|
88
|
+
role;
|
|
89
|
+
fill;
|
|
90
|
+
fontColor;
|
|
91
|
+
description;
|
|
92
|
+
constructor(role, fill, fontColor, description) {
|
|
93
|
+
this.role = role;
|
|
94
|
+
this.fill = fill;
|
|
95
|
+
this.fontColor = fontColor;
|
|
96
|
+
this.description = description;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
exports.RoleStyle = RoleStyle;
|
|
100
|
+
/** Every role role-resolver knows, in legend order (runnable things first). */
|
|
101
|
+
exports.ROLE_STYLES = [
|
|
102
|
+
new RoleStyle('server', '#4caf50', '#ffffff', 'runnable server'),
|
|
103
|
+
new RoleStyle('client', '#e5533d', '#ffffff', 'browser or mobile client app'),
|
|
104
|
+
new RoleStyle('app', '#1e88e5', '#ffffff', 'runnable non-HTTP app'),
|
|
105
|
+
new RoleStyle('bundle', '#7b3fa6', '#ffffff', 'bundles several apps'),
|
|
106
|
+
new RoleStyle('api-lib', '#f08a24', '#1a1c22', 'contract + DTOs'),
|
|
107
|
+
new RoleStyle('api-client', '#1a9aa6', '#ffffff', 'contract + SDK implementation'),
|
|
108
|
+
new RoleStyle('designed-lib', '#7d8597', '#ffffff', 'library with a generated design'),
|
|
109
|
+
new RoleStyle('lib', '#d7dae2', '#1a1c22', 'plain library'),
|
|
110
|
+
];
|
|
111
|
+
/** The four impact shades (graph-impact.ts decides which box gets which). */
|
|
112
|
+
exports.IMPACT_TOUCHED = '#f5a524';
|
|
113
|
+
exports.IMPACT_AFFECTED = '#fde3b0';
|
|
114
|
+
exports.IMPACT_BUILD_INPUT = '#e3e9f2';
|
|
115
|
+
exports.IMPACT_BUILD_INPUT_BORDER = '#7c8aa3';
|
|
116
|
+
exports.IMPACT_UNTOUCHED = '#eef0f4';
|
|
117
|
+
/** The light plate the name and meta sit on, across every stripe of a multi-color box. */
|
|
118
|
+
exports.LABEL_PLATE = '#fbfbfd';
|
|
119
|
+
/** Height of each stripe band above and below the plate. */
|
|
120
|
+
const STRIPE_HEIGHT = 16;
|
|
121
|
+
/** Margin of base color around a specialization's inset (inside one stripe band). */
|
|
122
|
+
const INSET_PAD = 4;
|
|
123
|
+
/** Padding around the text on the plate. */
|
|
124
|
+
const PLATE_PAD = 4;
|
|
125
|
+
/** No stripe narrower than this, however short the name. */
|
|
126
|
+
const MIN_STRIPE_WIDTH = 28;
|
|
127
|
+
/** Rough Arial 14 advance, used only to size the stripes so they span the text evenly. */
|
|
128
|
+
const CHAR_WIDTH = 7.5;
|
|
129
|
+
/** What a box's per-mode rendering is computed from. */
|
|
130
|
+
class NodeFacts {
|
|
131
|
+
nodeId;
|
|
132
|
+
shortName;
|
|
133
|
+
level;
|
|
134
|
+
role;
|
|
135
|
+
frameworks;
|
|
136
|
+
constructor(nodeId, shortName, level, role, frameworks) {
|
|
137
|
+
this.nodeId = nodeId;
|
|
138
|
+
this.shortName = shortName;
|
|
139
|
+
this.level = level;
|
|
140
|
+
this.role = role;
|
|
141
|
+
this.frameworks = frameworks;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
exports.NodeFacts = NodeFacts;
|
|
145
|
+
/** One box's DOT statement for every mode (and for each impact state). */
|
|
146
|
+
class NodeModeDots {
|
|
147
|
+
runtime;
|
|
148
|
+
architecture;
|
|
149
|
+
touched;
|
|
150
|
+
affected;
|
|
151
|
+
buildInput;
|
|
152
|
+
untouched;
|
|
153
|
+
constructor(runtime, architecture, touched, affected, buildInput, untouched) {
|
|
154
|
+
this.runtime = runtime;
|
|
155
|
+
this.architecture = architecture;
|
|
156
|
+
this.touched = touched;
|
|
157
|
+
this.affected = affected;
|
|
158
|
+
this.buildInput = buildInput;
|
|
159
|
+
this.untouched = untouched;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
exports.NodeModeDots = NodeModeDots;
|
|
163
|
+
/** Which colors and roles the drawn projects actually use, so the legend lists exactly those. */
|
|
164
|
+
class LegendFacts {
|
|
165
|
+
frameworks = [];
|
|
166
|
+
roles = [];
|
|
167
|
+
multiRuntime = false;
|
|
168
|
+
noRuntime = false;
|
|
169
|
+
}
|
|
170
|
+
exports.LegendFacts = LegendFacts;
|
|
171
|
+
class NodeModeStyler {
|
|
172
|
+
styleOf(framework) {
|
|
173
|
+
if (!rules_sdk_1.KNOWN_FRAMEWORKS.includes(framework))
|
|
174
|
+
return undefined;
|
|
175
|
+
return exports.FRAMEWORK_STYLES[framework];
|
|
176
|
+
}
|
|
177
|
+
roleStyle(role) {
|
|
178
|
+
const style = exports.ROLE_STYLES.find((candidate) => candidate.role === role);
|
|
179
|
+
return style ?? exports.ROLE_STYLES.find((s) => s.role === 'lib');
|
|
180
|
+
}
|
|
181
|
+
/** Every role the renderer styles; a spec asserts this covers role-resolver's KNOWN_ROLES. */
|
|
182
|
+
styledRoles() {
|
|
183
|
+
return exports.ROLE_STYLES.map((style) => style.role);
|
|
184
|
+
}
|
|
185
|
+
/** The base runtimes of a framework set, in stripe order (a specialization implies its base). */
|
|
186
|
+
bases(frameworks) {
|
|
187
|
+
const present = new Set();
|
|
188
|
+
for (const framework of frameworks) {
|
|
189
|
+
const style = this.styleOf(framework);
|
|
190
|
+
if (style !== undefined)
|
|
191
|
+
present.add(style.base);
|
|
192
|
+
}
|
|
193
|
+
return exports.BASE_ORDER.filter((base) => present.has(base));
|
|
194
|
+
}
|
|
195
|
+
/** The specialization drawn inside `base`, if the set carries one. */
|
|
196
|
+
specialization(base, frameworks) {
|
|
197
|
+
for (const framework of rules_sdk_1.KNOWN_FRAMEWORKS) {
|
|
198
|
+
const style = exports.FRAMEWORK_STYLES[framework];
|
|
199
|
+
if (style.isBase() || style.base !== base)
|
|
200
|
+
continue;
|
|
201
|
+
if (frameworks.includes(framework))
|
|
202
|
+
return style;
|
|
203
|
+
}
|
|
204
|
+
return undefined;
|
|
205
|
+
}
|
|
206
|
+
dots(facts) {
|
|
207
|
+
return new NodeModeDots(this.runtime(facts), this.statement(facts, this.architectureAttrs(facts), this.plainLabel(facts, this.envMeta(facts))), this.statement(facts, `style="filled", fillcolor="${exports.IMPACT_TOUCHED}"`, this.plainLabel(facts, 'touched')), this.statement(facts, `style="filled", fillcolor="${exports.IMPACT_AFFECTED}", color="${exports.IMPACT_TOUCHED}", penwidth=2`, this.plainLabel(facts, 'affected')), this.statement(facts, `style="filled,dashed", fillcolor="${exports.IMPACT_BUILD_INPUT}", color="${exports.IMPACT_BUILD_INPUT_BORDER}", penwidth=1.5`, this.plainLabel(facts, 'build input')), this.statement(facts, `style="filled", fillcolor="${exports.IMPACT_UNTOUCHED}", color="#b4b9c4", fontcolor="#6b7180"`, this.plainLabel(facts, `L${facts.level}`)));
|
|
208
|
+
}
|
|
209
|
+
/** Fold one drawn box into the legend facts. */
|
|
210
|
+
record(legend, facts) {
|
|
211
|
+
for (const framework of facts.frameworks) {
|
|
212
|
+
if (this.styleOf(framework) !== undefined && !legend.frameworks.includes(framework))
|
|
213
|
+
legend.frameworks.push(framework);
|
|
214
|
+
}
|
|
215
|
+
const role = this.roleStyle(facts.role).role;
|
|
216
|
+
if (!legend.roles.includes(role))
|
|
217
|
+
legend.roles.push(role);
|
|
218
|
+
const bases = this.bases(facts.frameworks);
|
|
219
|
+
if (bases.length > 1)
|
|
220
|
+
legend.multiRuntime = true;
|
|
221
|
+
if (bases.length === 0)
|
|
222
|
+
legend.noRuntime = true;
|
|
223
|
+
}
|
|
224
|
+
statement(facts, attrs, label) {
|
|
225
|
+
return ` "${(0, dot_syntax_1.dotValue)(facts.nodeId)}" [${attrs}, ${label}];\n`;
|
|
226
|
+
}
|
|
227
|
+
plainLabel(facts, meta) {
|
|
228
|
+
return `label="${(0, dot_syntax_1.dotValue)(facts.shortName)}\\n${(0, dot_syntax_1.dotValue)(meta)}"`;
|
|
229
|
+
}
|
|
230
|
+
runtimeMeta(facts) {
|
|
231
|
+
return `L${facts.level} · ${facts.role}`;
|
|
232
|
+
}
|
|
233
|
+
envMeta(facts) {
|
|
234
|
+
const envs = facts.frameworks.length === 0 ? 'no framework tag' : facts.frameworks.join('+');
|
|
235
|
+
return `L${facts.level} · ${envs}`;
|
|
236
|
+
}
|
|
237
|
+
architectureAttrs(facts) {
|
|
238
|
+
const style = this.roleStyle(facts.role);
|
|
239
|
+
return `style="filled", fillcolor="${style.fill}", fontcolor="${style.fontColor}"`;
|
|
240
|
+
}
|
|
241
|
+
/**
|
|
242
|
+
* Runtime mode. A box in ONE base runtime with no specialization is a plain fill — its dark text
|
|
243
|
+
* reads on any of the pastel base colors. Every other box (several runtimes, or a specialization)
|
|
244
|
+
* is an HTML-like table of three rows: a band of equal stripes, a light PLATE carrying the name and
|
|
245
|
+
* meta across all of them, and the same stripes again. The stripes and insets stay visible above
|
|
246
|
+
* and below the text, and the text never straddles two colors.
|
|
247
|
+
*/
|
|
248
|
+
runtime(facts) {
|
|
249
|
+
const bases = this.bases(facts.frameworks);
|
|
250
|
+
const meta = this.runtimeMeta(facts);
|
|
251
|
+
if (bases.length === 0)
|
|
252
|
+
return this.statement(facts, `style="filled", fillcolor="${exports.NO_RUNTIME_COLOR}"`, this.plainLabel(facts, meta));
|
|
253
|
+
const insets = bases.map((base) => this.specialization(base, facts.frameworks));
|
|
254
|
+
const plain = insets.every((inset) => inset === undefined);
|
|
255
|
+
if (bases.length === 1 && plain)
|
|
256
|
+
return this.statement(facts, this.stripeAttrs(bases), this.plainLabel(facts, meta));
|
|
257
|
+
return this.statement(facts, `style="filled", fillcolor="${exports.LABEL_PLATE}", margin=0`, `label=<${this.plateTable(facts, bases, insets, meta)}>`);
|
|
258
|
+
}
|
|
259
|
+
/** `filled` for one runtime; equal `striped` weights for several (the last takes the remainder). */
|
|
260
|
+
stripeAttrs(bases) {
|
|
261
|
+
const colors = bases.map((base) => exports.FRAMEWORK_STYLES[base].color);
|
|
262
|
+
if (colors.length === 1)
|
|
263
|
+
return `style="filled", fillcolor="${colors[0]}"`;
|
|
264
|
+
const share = (1 / colors.length).toFixed(3);
|
|
265
|
+
const weighted = colors.map((color, index) => index === colors.length - 1 ? color : `${color};${share}`);
|
|
266
|
+
return `style="striped", fillcolor="${weighted.join(':')}"`;
|
|
267
|
+
}
|
|
268
|
+
/**
|
|
269
|
+
* Stripe band / plate / stripe band. Every stripe cell asks for the same minimum width (an
|
|
270
|
+
* estimate of the text's width split evenly), so the stripes come out equal and the plate spans
|
|
271
|
+
* exactly their total.
|
|
272
|
+
*/
|
|
273
|
+
plateTable(facts, bases, insets, meta) {
|
|
274
|
+
const chars = Math.max(facts.shortName.length, meta.length);
|
|
275
|
+
const textWidth = Math.ceil(chars * CHAR_WIDTH) + 2 * PLATE_PAD;
|
|
276
|
+
const stripeWidth = Math.max(MIN_STRIPE_WIDTH, Math.ceil(textWidth / bases.length));
|
|
277
|
+
const band = bases
|
|
278
|
+
.map((base, index) => this.stripeCell(exports.FRAMEWORK_STYLES[base].color, insets[index], stripeWidth))
|
|
279
|
+
.join('');
|
|
280
|
+
const text = `${(0, dot_syntax_1.htmlLabelText)(facts.shortName)}<BR/>${(0, dot_syntax_1.htmlLabelText)(meta)}`;
|
|
281
|
+
return ('<TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" CELLPADDING="0">' +
|
|
282
|
+
`<TR>${band}</TR>` +
|
|
283
|
+
`<TR><TD COLSPAN="${bases.length}" BGCOLOR="${exports.LABEL_PLATE}" CELLPADDING="${PLATE_PAD}">${text}</TD></TR>` +
|
|
284
|
+
`<TR>${band}</TR>` +
|
|
285
|
+
'</TABLE>');
|
|
286
|
+
}
|
|
287
|
+
/** One stripe of a band: the base color, with its specialization inset inside a margin of it. */
|
|
288
|
+
stripeCell(base, inset, width) {
|
|
289
|
+
if (inset === undefined)
|
|
290
|
+
return `<TD BGCOLOR="${base}" WIDTH="${width}" HEIGHT="${STRIPE_HEIGHT}"></TD>`;
|
|
291
|
+
const innerWidth = width - 2 * INSET_PAD;
|
|
292
|
+
const innerHeight = STRIPE_HEIGHT - 2 * INSET_PAD;
|
|
293
|
+
return (`<TD BGCOLOR="${base}" CELLPADDING="${INSET_PAD}">` +
|
|
294
|
+
`<TABLE BORDER="0" CELLSPACING="0" CELLPADDING="0" BGCOLOR="${inset.color}" STYLE="rounded">` +
|
|
295
|
+
`<TR><TD WIDTH="${innerWidth}" HEIGHT="${innerHeight}"></TD></TR></TABLE></TD>`);
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
exports.NodeModeStyler = NodeModeStyler;
|
|
299
|
+
//# sourceMappingURL=graph-color-modes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"graph-color-modes.js","sourceRoot":"","sources":["../../../../../../packages/tooling/nx-webpieces-rules/src/lib/graph-color-modes.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Graph color modes (#1155)\n *\n * The architecture graph answers ONE question at a time instead of asking every box to encode two\n * (fill = framework AND border = role). Each box is emitted once per mode, so the page can switch\n * modes and re-render without regenerating anything:\n *\n * - RUNTIME \"where the code can run\". Base runtimes are equal vertical stripes in the fixed\n * order browser | node | react-native; a specialization (angular/react inside\n * browser, express inside node) is an inset inside its base stripe. The dependency\n * rule becomes visual: a box may depend on a box carrying every color it has. The\n * name and meta sit on a light label PLATE across the middle, so they read on every\n * stripe combination while the stripes and insets stay visible above and below it.\n * - ARCHITECTURE \"servers · clients · APIs\". A solid fill by ROLE; framework color is not shown.\n * - IMPACT \"what this branch touches\". Projects owning a changed file are solid amber\n * (\"touched\"); the rest of nx's affected set is light amber (\"affected\": their ci\n * re-runs); every transitive dependency of the affected set that is not itself\n * affected is light slate with a dashed border (\"build input\": compiled or restored\n * from cache, unchanged); everything else is grey. Which box is which is only known\n * per branch, so every box carries all four variants and the page picks one from the\n * sidecar (graph-impact.ts).\n */\n\nimport { KNOWN_FRAMEWORKS, KnownFramework } from '@webpieces/rules-sdk';\nimport { dotValue, htmlLabelText } from './dot-syntax';\n\nexport enum GraphMode {\n RUNTIME = 'runtime',\n ARCHITECTURE = 'architecture',\n IMPACT = 'impact',\n}\n\n/** One selectable mode as the drawer and the node menu show it. */\nexport class ModeInfo {\n constructor(\n public readonly mode: GraphMode,\n public readonly name: string,\n public readonly subtitle: string,\n ) {}\n}\n\n/** In key order: `1` selects the first, `2` the second, `3` the third. */\nexport const GRAPH_MODES: readonly ModeInfo[] = [\n new ModeInfo(GraphMode.RUNTIME, 'Runtime', 'where the code can run'),\n new ModeInfo(GraphMode.ARCHITECTURE, 'Architecture', 'servers · clients · APIs'),\n new ModeInfo(GraphMode.IMPACT, 'Impact', 'what this branch touches'),\n];\n\n/** A framework value's color and the BASE runtime it is drawn inside (itself, for a base). */\nexport class FrameworkStyle {\n constructor(\n public readonly framework: KnownFramework,\n public readonly base: KnownFramework,\n public readonly color: string,\n public readonly description: string,\n ) {}\n\n isBase(): boolean {\n return this.framework === this.base;\n }\n}\n\n/**\n * Keyed by the shared vocabulary, so a framework added to KNOWN_FRAMEWORKS without a color here is a\n * COMPILE error — not a box silently painted grey and missing from the legend, which is exactly how\n * react-native shipped (#1155).\n */\nexport const FRAMEWORK_STYLES: Record<KnownFramework, FrameworkStyle> = {\n browser: new FrameworkStyle('browser', 'browser', '#b9a7e6', 'browser base runtime'),\n node: new FrameworkStyle('node', 'node', '#ffe07a', 'node base runtime'),\n 'react-native': new FrameworkStyle(\n 'react-native',\n 'react-native',\n '#7fd1c4',\n 'its own runtime, not a browser',\n ),\n angular: new FrameworkStyle('angular', 'browser', '#f59ab9', 'inside browser'),\n react: new FrameworkStyle('react', 'browser', '#8fd3f7', 'inside browser'),\n express: new FrameworkStyle('express', 'node', '#9fd8a3', 'inside node'),\n};\n\n/** Stripe order, left to right. Fixed, so the same set always reads the same way. */\nexport const BASE_ORDER: readonly KnownFramework[] = ['browser', 'node', 'react-native'];\n\n/** A box with no known framework tag. */\nexport const NO_RUNTIME_COLOR = '#e4e6ec';\n\n/** A role's solid fill and the text color that stays readable on it. */\nexport class RoleStyle {\n constructor(\n public readonly role: string,\n public readonly fill: string,\n public readonly fontColor: string,\n public readonly description: string,\n ) {}\n}\n\n/** Every role role-resolver knows, in legend order (runnable things first). */\nexport const ROLE_STYLES: readonly RoleStyle[] = [\n new RoleStyle('server', '#4caf50', '#ffffff', 'runnable server'),\n new RoleStyle('client', '#e5533d', '#ffffff', 'browser or mobile client app'),\n new RoleStyle('app', '#1e88e5', '#ffffff', 'runnable non-HTTP app'),\n new RoleStyle('bundle', '#7b3fa6', '#ffffff', 'bundles several apps'),\n new RoleStyle('api-lib', '#f08a24', '#1a1c22', 'contract + DTOs'),\n new RoleStyle('api-client', '#1a9aa6', '#ffffff', 'contract + SDK implementation'),\n new RoleStyle('designed-lib', '#7d8597', '#ffffff', 'library with a generated design'),\n new RoleStyle('lib', '#d7dae2', '#1a1c22', 'plain library'),\n];\n\n/** The four impact shades (graph-impact.ts decides which box gets which). */\nexport const IMPACT_TOUCHED = '#f5a524';\nexport const IMPACT_AFFECTED = '#fde3b0';\nexport const IMPACT_BUILD_INPUT = '#e3e9f2';\nexport const IMPACT_BUILD_INPUT_BORDER = '#7c8aa3';\nexport const IMPACT_UNTOUCHED = '#eef0f4';\n\n/** The light plate the name and meta sit on, across every stripe of a multi-color box. */\nexport const LABEL_PLATE = '#fbfbfd';\n/** Height of each stripe band above and below the plate. */\nconst STRIPE_HEIGHT = 16;\n/** Margin of base color around a specialization's inset (inside one stripe band). */\nconst INSET_PAD = 4;\n/** Padding around the text on the plate. */\nconst PLATE_PAD = 4;\n/** No stripe narrower than this, however short the name. */\nconst MIN_STRIPE_WIDTH = 28;\n/** Rough Arial 14 advance, used only to size the stripes so they span the text evenly. */\nconst CHAR_WIDTH = 7.5;\n\n/** What a box's per-mode rendering is computed from. */\nexport class NodeFacts {\n constructor(\n public readonly nodeId: string,\n public readonly shortName: string,\n public readonly level: number,\n public readonly role: string,\n public readonly frameworks: string[],\n ) {}\n}\n\n/** One box's DOT statement for every mode (and for each impact state). */\nexport class NodeModeDots {\n constructor(\n public readonly runtime: string,\n public readonly architecture: string,\n public readonly touched: string,\n public readonly affected: string,\n public readonly buildInput: string,\n public readonly untouched: string,\n ) {}\n}\n\n/** Which colors and roles the drawn projects actually use, so the legend lists exactly those. */\nexport class LegendFacts {\n frameworks: string[] = [];\n roles: string[] = [];\n multiRuntime = false;\n noRuntime = false;\n}\n\nexport class NodeModeStyler {\n styleOf(framework: string): FrameworkStyle | undefined {\n if (!(KNOWN_FRAMEWORKS as readonly string[]).includes(framework)) return undefined;\n return FRAMEWORK_STYLES[framework as KnownFramework];\n }\n\n roleStyle(role: string): RoleStyle {\n const style = ROLE_STYLES.find((candidate: RoleStyle): boolean => candidate.role === role);\n return style ?? (ROLE_STYLES.find((s: RoleStyle): boolean => s.role === 'lib') as RoleStyle);\n }\n\n /** Every role the renderer styles; a spec asserts this covers role-resolver's KNOWN_ROLES. */\n styledRoles(): string[] {\n return ROLE_STYLES.map((style: RoleStyle): string => style.role);\n }\n\n /** The base runtimes of a framework set, in stripe order (a specialization implies its base). */\n bases(frameworks: string[]): KnownFramework[] {\n const present = new Set<KnownFramework>();\n for (const framework of frameworks) {\n const style = this.styleOf(framework);\n if (style !== undefined) present.add(style.base);\n }\n return BASE_ORDER.filter((base: KnownFramework): boolean => present.has(base));\n }\n\n /** The specialization drawn inside `base`, if the set carries one. */\n specialization(base: KnownFramework, frameworks: string[]): FrameworkStyle | undefined {\n for (const framework of KNOWN_FRAMEWORKS) {\n const style = FRAMEWORK_STYLES[framework];\n if (style.isBase() || style.base !== base) continue;\n if (frameworks.includes(framework)) return style;\n }\n return undefined;\n }\n\n dots(facts: NodeFacts): NodeModeDots {\n return new NodeModeDots(\n this.runtime(facts),\n this.statement(facts, this.architectureAttrs(facts), this.plainLabel(facts, this.envMeta(facts))),\n this.statement(facts, `style=\"filled\", fillcolor=\"${IMPACT_TOUCHED}\"`, this.plainLabel(facts, 'touched')),\n this.statement(\n facts,\n `style=\"filled\", fillcolor=\"${IMPACT_AFFECTED}\", color=\"${IMPACT_TOUCHED}\", penwidth=2`,\n this.plainLabel(facts, 'affected'),\n ),\n this.statement(\n facts,\n `style=\"filled,dashed\", fillcolor=\"${IMPACT_BUILD_INPUT}\", color=\"${IMPACT_BUILD_INPUT_BORDER}\", penwidth=1.5`,\n this.plainLabel(facts, 'build input'),\n ),\n this.statement(\n facts,\n `style=\"filled\", fillcolor=\"${IMPACT_UNTOUCHED}\", color=\"#b4b9c4\", fontcolor=\"#6b7180\"`,\n this.plainLabel(facts, `L${facts.level}`),\n ),\n );\n }\n\n /** Fold one drawn box into the legend facts. */\n record(legend: LegendFacts, facts: NodeFacts): void {\n for (const framework of facts.frameworks) {\n if (this.styleOf(framework) !== undefined && !legend.frameworks.includes(framework))\n legend.frameworks.push(framework);\n }\n const role = this.roleStyle(facts.role).role;\n if (!legend.roles.includes(role)) legend.roles.push(role);\n const bases = this.bases(facts.frameworks);\n if (bases.length > 1) legend.multiRuntime = true;\n if (bases.length === 0) legend.noRuntime = true;\n }\n\n private statement(facts: NodeFacts, attrs: string, label: string): string {\n return ` \"${dotValue(facts.nodeId)}\" [${attrs}, ${label}];\\n`;\n }\n\n private plainLabel(facts: NodeFacts, meta: string): string {\n return `label=\"${dotValue(facts.shortName)}\\\\n${dotValue(meta)}\"`;\n }\n\n private runtimeMeta(facts: NodeFacts): string {\n return `L${facts.level} · ${facts.role}`;\n }\n\n private envMeta(facts: NodeFacts): string {\n const envs = facts.frameworks.length === 0 ? 'no framework tag' : facts.frameworks.join('+');\n return `L${facts.level} · ${envs}`;\n }\n\n private architectureAttrs(facts: NodeFacts): string {\n const style = this.roleStyle(facts.role);\n return `style=\"filled\", fillcolor=\"${style.fill}\", fontcolor=\"${style.fontColor}\"`;\n }\n\n /**\n * Runtime mode. A box in ONE base runtime with no specialization is a plain fill — its dark text\n * reads on any of the pastel base colors. Every other box (several runtimes, or a specialization)\n * is an HTML-like table of three rows: a band of equal stripes, a light PLATE carrying the name and\n * meta across all of them, and the same stripes again. The stripes and insets stay visible above\n * and below the text, and the text never straddles two colors.\n */\n private runtime(facts: NodeFacts): string {\n const bases = this.bases(facts.frameworks);\n const meta = this.runtimeMeta(facts);\n if (bases.length === 0)\n return this.statement(facts, `style=\"filled\", fillcolor=\"${NO_RUNTIME_COLOR}\"`, this.plainLabel(facts, meta));\n const insets = bases.map((base: KnownFramework) => this.specialization(base, facts.frameworks));\n const plain = insets.every((inset: FrameworkStyle | undefined): boolean => inset === undefined);\n if (bases.length === 1 && plain)\n return this.statement(facts, this.stripeAttrs(bases), this.plainLabel(facts, meta));\n return this.statement(\n facts,\n `style=\"filled\", fillcolor=\"${LABEL_PLATE}\", margin=0`,\n `label=<${this.plateTable(facts, bases, insets, meta)}>`,\n );\n }\n\n /** `filled` for one runtime; equal `striped` weights for several (the last takes the remainder). */\n stripeAttrs(bases: KnownFramework[]): string {\n const colors = bases.map((base: KnownFramework): string => FRAMEWORK_STYLES[base].color);\n if (colors.length === 1) return `style=\"filled\", fillcolor=\"${colors[0]}\"`;\n const share = (1 / colors.length).toFixed(3);\n const weighted = colors.map((color: string, index: number): string =>\n index === colors.length - 1 ? color : `${color};${share}`,\n );\n return `style=\"striped\", fillcolor=\"${weighted.join(':')}\"`;\n }\n\n /**\n * Stripe band / plate / stripe band. Every stripe cell asks for the same minimum width (an\n * estimate of the text's width split evenly), so the stripes come out equal and the plate spans\n * exactly their total.\n */\n private plateTable(\n facts: NodeFacts,\n bases: KnownFramework[],\n insets: (FrameworkStyle | undefined)[],\n meta: string,\n ): string {\n const chars = Math.max(facts.shortName.length, meta.length);\n const textWidth = Math.ceil(chars * CHAR_WIDTH) + 2 * PLATE_PAD;\n const stripeWidth = Math.max(MIN_STRIPE_WIDTH, Math.ceil(textWidth / bases.length));\n const band = bases\n .map((base: KnownFramework, index: number): string =>\n this.stripeCell(FRAMEWORK_STYLES[base].color, insets[index], stripeWidth),\n )\n .join('');\n const text = `${htmlLabelText(facts.shortName)}<BR/>${htmlLabelText(meta)}`;\n return (\n '<TABLE BORDER=\"0\" CELLBORDER=\"0\" CELLSPACING=\"0\" CELLPADDING=\"0\">' +\n `<TR>${band}</TR>` +\n `<TR><TD COLSPAN=\"${bases.length}\" BGCOLOR=\"${LABEL_PLATE}\" CELLPADDING=\"${PLATE_PAD}\">${text}</TD></TR>` +\n `<TR>${band}</TR>` +\n '</TABLE>'\n );\n }\n\n /** One stripe of a band: the base color, with its specialization inset inside a margin of it. */\n private stripeCell(base: string, inset: FrameworkStyle | undefined, width: number): string {\n if (inset === undefined) return `<TD BGCOLOR=\"${base}\" WIDTH=\"${width}\" HEIGHT=\"${STRIPE_HEIGHT}\"></TD>`;\n const innerWidth = width - 2 * INSET_PAD;\n const innerHeight = STRIPE_HEIGHT - 2 * INSET_PAD;\n return (\n `<TD BGCOLOR=\"${base}\" CELLPADDING=\"${INSET_PAD}\">` +\n `<TABLE BORDER=\"0\" CELLSPACING=\"0\" CELLPADDING=\"0\" BGCOLOR=\"${inset.color}\" STYLE=\"rounded\">` +\n `<TR><TD WIDTH=\"${innerWidth}\" HEIGHT=\"${innerHeight}\"></TD></TR></TABLE></TD>`\n );\n }\n}\n"]}
|
|
@@ -16,7 +16,8 @@ declare class WpRenderBand {
|
|
|
16
16
|
declare class WpFilteredDot {
|
|
17
17
|
private readonly model;
|
|
18
18
|
constructor(model: RenderModelJson);
|
|
19
|
-
|
|
19
|
+
/** `nodeDot` picks each box's statement — the architecture page passes its current color mode's. */
|
|
20
|
+
render(retained: Set<string>, nodeDot?: (node: RenderNodeJson) => string): string;
|
|
20
21
|
private bandDot;
|
|
21
22
|
}
|
|
22
23
|
/** Page-owned state survives replacements; rendering failures leave the usable SVG intact. */
|
|
@@ -33,6 +34,15 @@ declare abstract class WpFilterPage {
|
|
|
33
34
|
protected wireControls(): void;
|
|
34
35
|
protected abstract wireSvg(svg: SVGSVGElement): void;
|
|
35
36
|
protected prepareSvg(_svg: SVGSVGElement): void;
|
|
37
|
+
/** The statement drawn for one box. The architecture page answers with its color mode's. */
|
|
38
|
+
protected nodeDot(node: RenderNodeJson): string;
|
|
39
|
+
/** Whether an unfiltered draw may use the model's precomputed `fullDot` verbatim. */
|
|
40
|
+
protected usesFullDot(): boolean;
|
|
41
|
+
/**
|
|
42
|
+
* Draw again with the current filter — after a color-mode switch. A control outside the graph
|
|
43
|
+
* (a drawer button) keeps keyboard focus; the redraw would otherwise move it into the graph.
|
|
44
|
+
*/
|
|
45
|
+
redraw(): void;
|
|
36
46
|
filterItem(name: string): WpNodeMenuItem;
|
|
37
47
|
filter(anchor: string | null): void;
|
|
38
48
|
private indicator;
|
|
@@ -54,14 +54,15 @@ class WpFilteredDot {
|
|
|
54
54
|
constructor(model) {
|
|
55
55
|
this.model = model;
|
|
56
56
|
}
|
|
57
|
-
|
|
57
|
+
/** `nodeDot` picks each box's statement — the architecture page passes its current color mode's. */
|
|
58
|
+
render(retained, nodeDot = (node) => node.dot) {
|
|
58
59
|
const nodes = this.model.nodes.filter((node) => retained.has(node.id));
|
|
59
60
|
const edges = this.model.edges.filter((edge) => retained.has(edge.from) && retained.has(edge.to));
|
|
60
61
|
const bands = this.model.bands
|
|
61
62
|
.map((band) => new WpRenderBand(band.level, band.nodeNames.filter((id) => retained.has(id))))
|
|
62
63
|
.filter((band) => band.nodeNames.length > 0);
|
|
63
64
|
return (this.model.header +
|
|
64
|
-
nodes.map((node) => node
|
|
65
|
+
nodes.map((node) => nodeDot(node)).join('') +
|
|
65
66
|
this.bandDot(bands) +
|
|
66
67
|
edges.map((edge) => edge.dot).join('') +
|
|
67
68
|
this.model.footer);
|
|
@@ -122,6 +123,26 @@ class WpFilterPage {
|
|
|
122
123
|
}
|
|
123
124
|
wireControls() { }
|
|
124
125
|
prepareSvg(_svg) { }
|
|
126
|
+
/** The statement drawn for one box. The architecture page answers with its color mode's. */
|
|
127
|
+
nodeDot(node) {
|
|
128
|
+
return node.dot;
|
|
129
|
+
}
|
|
130
|
+
/** Whether an unfiltered draw may use the model's precomputed `fullDot` verbatim. */
|
|
131
|
+
usesFullDot() {
|
|
132
|
+
return true;
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Draw again with the current filter — after a color-mode switch. A control outside the graph
|
|
136
|
+
* (a drawer button) keeps keyboard focus; the redraw would otherwise move it into the graph.
|
|
137
|
+
*/
|
|
138
|
+
redraw() {
|
|
139
|
+
const active = document.activeElement;
|
|
140
|
+
const host = document.getElementById('graph');
|
|
141
|
+
const outside = active instanceof HTMLElement && host !== null && !host.contains(active);
|
|
142
|
+
this.filter(this.anchor);
|
|
143
|
+
if (outside && active.isConnected)
|
|
144
|
+
active.focus({ preventScroll: true });
|
|
145
|
+
}
|
|
125
146
|
filterItem(name) {
|
|
126
147
|
return new WpNodeMenuItem(this.anchor === null ? 'Filter Unconnected' : 'Turn off Filter', () => this.filter(this.anchor === null ? name : null));
|
|
127
148
|
}
|
|
@@ -141,9 +162,9 @@ class WpFilterPage {
|
|
|
141
162
|
const rollback = this.captureBinding(previousSvg);
|
|
142
163
|
// eslint-disable-next-line @webpieces/no-unmanaged-exceptions -- browser transaction restores the previous usable graph on failure
|
|
143
164
|
try {
|
|
144
|
-
const dot = anchor === null
|
|
165
|
+
const dot = anchor === null && this.usesFullDot()
|
|
145
166
|
? this.model.fullDot
|
|
146
|
-
: new WpFilteredDot(this.model).render(retained);
|
|
167
|
+
: new WpFilteredDot(this.model).render(retained, (node) => this.nodeDot(node));
|
|
147
168
|
const svg = this.viz.renderSVGElement(dot);
|
|
148
169
|
this.prepareSvg(svg);
|
|
149
170
|
WpNodeMenu.close();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"graph-filter.client.js","sourceRoot":"","sources":["../../../../../../packages/tooling/nx-webpieces-rules/src/lib/graph-filter.client.ts"],"names":[],"mappings":"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","sourcesContent":["/** Plain browser helpers shared by architecture and runtime; no CommonJS imports. */\nclass WpGraphChain {\n readonly inNodes = new Map<string, Set<string>>();\n readonly outNodes = new Map<string, Set<string>>();\n\n constructor(private readonly model: RenderModelJson) {\n const ids = new Set(model.nodes.map((node) => node.id));\n for (const edge of model.edges) {\n if (!ids.has(edge.from) || !ids.has(edge.to)) continue;\n this.add(this.outNodes, edge.from, edge.to);\n this.add(this.inNodes, edge.to, edge.from);\n }\n }\n\n private add(map: Map<string, Set<string>>, from: string, to: string): void {\n if (!map.has(from)) map.set(from, new Set());\n map.get(from)?.add(to);\n }\n\n nodes(anchor: string): Set<string> {\n const retained = new Set<string>();\n if (!this.model.nodes.some((node) => node.id === anchor)) return retained;\n for (const direction of [this.inNodes, this.outNodes]) {\n const visited = new Set([anchor]);\n const stack = [anchor];\n while (stack.length > 0) {\n const id = stack.pop() as string;\n retained.add(id);\n for (const next of direction.get(id) ?? []) {\n if (visited.has(next)) continue;\n visited.add(next);\n stack.push(next);\n }\n }\n }\n return retained;\n }\n}\n\nclass WpRenderBand {\n constructor(\n readonly level: number,\n readonly nodeNames: string[],\n ) {}\n}\n\n/** Original records plus fresh ranks: removed nodes cannot reappear via a rank/edge. */\nclass WpFilteredDot {\n constructor(private readonly model: RenderModelJson) {}\n\n render(retained: Set<string>): string {\n const nodes = this.model.nodes.filter((node) => retained.has(node.id));\n const edges = this.model.edges.filter(\n (edge) => retained.has(edge.from) && retained.has(edge.to),\n );\n const bands = this.model.bands\n .map(\n (band) =>\n new WpRenderBand(\n band.level,\n band.nodeNames.filter((id) => retained.has(id)),\n ),\n )\n .filter((band) => band.nodeNames.length > 0);\n return (\n this.model.header +\n nodes.map((node) => node.dot).join('') +\n this.bandDot(bands) +\n edges.map((edge) => edge.dot).join('') +\n this.model.footer\n );\n }\n\n private bandDot(bands: WpRenderBand[]): string {\n const nodeAttrs =\n '[style=invis, shape=point, width=0.01, height=0.01, label=\"\", class=\"wp-layout\"]';\n const edgeAttrs = '[style=invis, class=\"wp-layout\"]';\n let dot = '';\n for (const band of bands) {\n const anchor = `__wp_layout_L${band.level}`;\n dot += ` \"${anchor}\" ${nodeAttrs};\\n { rank=same; \"${anchor}\"; `;\n dot += band.nodeNames.map((id) => `${JSON.stringify(id)}; `).join('') + '}\\n';\n }\n for (let i = 0; i + 1 < bands.length; i++) {\n const upper = bands[i];\n const lower = bands[i + 1];\n const from = `__wp_layout_L${upper.level}`;\n const to = `__wp_layout_L${lower.level}`;\n if (upper.nodeNames.length <= 10 && lower.nodeNames.length <= 10) {\n dot += ` \"${from}\" -> \"${to}\" ${edgeAttrs};\\n`;\n continue;\n }\n const spacer = `__wp_layout_spacer_L${upper.level}_L${lower.level}`;\n dot += ` \"${spacer}\" ${nodeAttrs};\\n { rank=same; \"${spacer}\"; }\\n`;\n dot += ` \"${from}\" -> \"${spacer}\" ${edgeAttrs};\\n \"${spacer}\" -> \"${to}\" ${edgeAttrs};\\n`;\n }\n return dot;\n }\n}\n\n/** Page-owned state survives replacements; rendering failures leave the usable SVG intact. */\nabstract class WpFilterPage {\n protected readonly chain: WpGraphChain;\n protected locked: string | null = null;\n protected anchor: string | null = null;\n protected retained: Set<string>;\n private viz: VizInstance | null = null;\n\n constructor(protected readonly model: RenderModelJson) {\n this.chain = new WpGraphChain(model);\n this.retained = new Set(model.nodes.map((node) => node.id));\n }\n\n render(): void {\n document\n .getElementById('wp-filter-off')\n ?.addEventListener('click', () => this.filter(null));\n this.wireControls();\n Viz.instance()\n .then((viz) => {\n this.viz = viz;\n this.filter(null);\n })\n // webpieces-disable no-any-unknown -- promise rejections may carry any JavaScript value\n .catch((err: unknown): void =>\n this.error(err instanceof Error ? err : new Error(String(err))),\n );\n }\n\n protected captureBinding(_svg: SVGSVGElement | null): () => void {\n return (): void => {};\n }\n protected wireControls(): void {}\n protected abstract wireSvg(svg: SVGSVGElement): void;\n protected prepareSvg(_svg: SVGSVGElement): void {}\n\n filterItem(name: string): WpNodeMenuItem {\n return new WpNodeMenuItem(\n this.anchor === null ? 'Filter Unconnected' : 'Turn off Filter',\n () => this.filter(this.anchor === null ? name : null),\n );\n }\n\n filter(anchor: string | null): void {\n if (this.viz === null) return;\n const host = document.getElementById('graph');\n if (host === null) return;\n const retained =\n anchor === null\n ? new Set(this.model.nodes.map((node) => node.id))\n : this.chain.nodes(anchor);\n const previousAnchor = this.anchor;\n const previousRetained = this.retained;\n const previousSvg = host.querySelector('svg');\n const focusName = WpNodeMenu.focusedName(previousSvg);\n const rollback = this.captureBinding(previousSvg);\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions -- browser transaction restores the previous usable graph on failure\n try {\n const dot =\n anchor === null\n ? this.model.fullDot\n : new WpFilteredDot(this.model).render(retained);\n const svg = this.viz.renderSVGElement(dot);\n this.prepareSvg(svg);\n WpNodeMenu.close();\n this.anchor = anchor;\n this.retained = retained;\n host.replaceChildren(svg);\n this.wireSvg(svg);\n this.indicator();\n WpNodeMenu.restoreFocus(svg, focusName);\n this.error(null);\n // webpieces-disable no-any-unknown -- JavaScript may throw any value at this browser boundary\n } catch (err: unknown) {\n //const error = toError(err);\n this.anchor = previousAnchor;\n this.retained = previousRetained;\n if (previousSvg !== null) host.replaceChildren(previousSvg);\n else host.replaceChildren();\n rollback();\n if (previousSvg !== null) WpNodeMenu.restoreFocus(previousSvg, focusName);\n this.error(err instanceof Error ? err : new Error(String(err)));\n }\n }\n\n private indicator(): void {\n const status = document.getElementById('wp-filter-status');\n const label = document.getElementById('wp-filter-anchor');\n if (status !== null) status.hidden = this.anchor === null;\n if (label !== null) label.textContent = this.anchor;\n if (this.anchor !== null) document.getElementById('wp-filter-off')?.focus();\n else document.querySelector<SVGGElement>('#graph g.wp-node-clickable')?.focus();\n }\n\n private error(err: Error | null): void {\n const el = document.getElementById('wp-graph-error');\n if (el === null) return;\n el.hidden = err === null;\n el.textContent =\n err === null\n ? ''\n : `Graph rendering failed: ${err.message}. Try Turn off Filter or reload the page.`;\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"graph-filter.client.js","sourceRoot":"","sources":["../../../../../../packages/tooling/nx-webpieces-rules/src/lib/graph-filter.client.ts"],"names":[],"mappings":";AAAA,qFAAqF;AACrF,MAAM,YAAY;IAIe;IAHpB,OAAO,GAAG,IAAI,GAAG,EAAuB,CAAC;IACzC,QAAQ,GAAG,IAAI,GAAG,EAAuB,CAAC;IAEnD,YAA6B,KAAsB;QAAtB,UAAK,GAAL,KAAK,CAAiB;QAC/C,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC;QACxD,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,KAAK,EAAE,CAAC;YAC7B,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;gBAAE,SAAS;YACvD,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,EAAE,CAAC,CAAC;YAC5C,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,EAAE,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC;QAC/C,CAAC;IACL,CAAC;IAEO,GAAG,CAAC,GAA6B,EAAE,IAAY,EAAE,EAAU;QAC/D,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC;YAAE,GAAG,CAAC,GAAG,CAAC,IAAI,EAAE,IAAI,GAAG,EAAE,CAAC,CAAC;QAC7C,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC;IAC3B,CAAC;IAED,KAAK,CAAC,MAAc;QAChB,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAU,CAAC;QACnC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,EAAE,KAAK,MAAM,CAAC;YAAE,OAAO,QAAQ,CAAC;QAC1E,KAAK,MAAM,SAAS,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;YACpD,MAAM,OAAO,GAAG,IAAI,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC;YAClC,MAAM,KAAK,GAAG,CAAC,MAAM,CAAC,CAAC;YACvB,OAAO,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBACtB,MAAM,EAAE,GAAG,KAAK,CAAC,GAAG,EAAY,CAAC;gBACjC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;gBACjB,KAAK,MAAM,IAAI,IAAI,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC;oBACzC,IAAI,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC;wBAAE,SAAS;oBAChC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;oBAClB,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;gBACrB,CAAC;YACL,CAAC;QACL,CAAC;QACD,OAAO,QAAQ,CAAC;IACpB,CAAC;CACJ;AAED,MAAM,YAAY;IAED;IACA;IAFb,YACa,KAAa,EACb,SAAmB;QADnB,UAAK,GAAL,KAAK,CAAQ;QACb,cAAS,GAAT,SAAS,CAAU;IAC7B,CAAC;CACP;AAED,wFAAwF;AACxF,MAAM,aAAa;IACc;IAA7B,YAA6B,KAAsB;QAAtB,UAAK,GAAL,KAAK,CAAiB;IAAG,CAAC;IAEvD,oGAAoG;IACpG,MAAM,CACF,QAAqB,EACrB,UAA4C,CAAC,IAAoB,EAAU,EAAE,CAAC,IAAI,CAAC,GAAG;QAEtF,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,IAAoB,EAAW,EAAE,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC;QAChG,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,CACjC,CAAC,IAAI,EAAE,EAAE,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAC7D,CAAC;QACF,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK;aACzB,GAAG,CACA,CAAC,IAAI,EAAE,EAAE,CACL,IAAI,YAAY,CACZ,IAAI,CAAC,KAAK,EACV,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAClD,CACR;aACA,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;QACjD,OAAO,CACH,IAAI,CAAC,KAAK,CAAC,MAAM;YACjB,KAAK,CAAC,GAAG,CAAC,CAAC,IAAoB,EAAU,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC;YACnE,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC;YACnB,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC;YACtC,IAAI,CAAC,KAAK,CAAC,MAAM,CACpB,CAAC;IACN,CAAC;IAEO,OAAO,CAAC,KAAqB;QACjC,MAAM,SAAS,GACX,kFAAkF,CAAC;QACvF,MAAM,SAAS,GAAG,kCAAkC,CAAC;QACrD,IAAI,GAAG,GAAG,EAAE,CAAC;QACb,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACvB,MAAM,MAAM,GAAG,gBAAgB,IAAI,CAAC,KAAK,EAAE,CAAC;YAC5C,GAAG,IAAI,MAAM,MAAM,KAAK,SAAS,sBAAsB,MAAM,KAAK,CAAC;YACnE,GAAG,IAAI,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC;QAClF,CAAC;QACD,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YACxC,MAAM,KAAK,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;YACvB,MAAM,KAAK,GAAG,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;YAC3B,MAAM,IAAI,GAAG,gBAAgB,KAAK,CAAC,KAAK,EAAE,CAAC;YAC3C,MAAM,EAAE,GAAG,gBAAgB,KAAK,CAAC,KAAK,EAAE,CAAC;YACzC,IAAI,KAAK,CAAC,SAAS,CAAC,MAAM,IAAI,EAAE,IAAI,KAAK,CAAC,SAAS,CAAC,MAAM,IAAI,EAAE,EAAE,CAAC;gBAC/D,GAAG,IAAI,MAAM,IAAI,SAAS,EAAE,KAAK,SAAS,KAAK,CAAC;gBAChD,SAAS;YACb,CAAC;YACD,MAAM,MAAM,GAAG,uBAAuB,KAAK,CAAC,KAAK,KAAK,KAAK,CAAC,KAAK,EAAE,CAAC;YACpE,GAAG,IAAI,MAAM,MAAM,KAAK,SAAS,sBAAsB,MAAM,QAAQ,CAAC;YACtE,GAAG,IAAI,MAAM,IAAI,SAAS,MAAM,KAAK,SAAS,SAAS,MAAM,SAAS,EAAE,KAAK,SAAS,KAAK,CAAC;QAChG,CAAC;QACD,OAAO,GAAG,CAAC;IACf,CAAC;CACJ;AAED,8FAA8F;AAC9F,MAAe,YAAY;IAOQ;IANZ,KAAK,CAAe;IAC7B,MAAM,GAAkB,IAAI,CAAC;IAC7B,MAAM,GAAkB,IAAI,CAAC;IAC7B,QAAQ,CAAc;IACxB,GAAG,GAAuB,IAAI,CAAC;IAEvC,YAA+B,KAAsB;QAAtB,UAAK,GAAL,KAAK,CAAiB;QACjD,IAAI,CAAC,KAAK,GAAG,IAAI,YAAY,CAAC,KAAK,CAAC,CAAC;QACrC,IAAI,CAAC,QAAQ,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC;IAChE,CAAC;IAED,MAAM;QACF,QAAQ;aACH,cAAc,CAAC,eAAe,CAAC;YAChC,EAAE,gBAAgB,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC;QACzD,IAAI,CAAC,YAAY,EAAE,CAAC;QACpB,GAAG,CAAC,QAAQ,EAAE;aACT,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE;YACV,IAAI,CAAC,GAAG,GAAG,GAAG,CAAC;YACf,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QACtB,CAAC,CAAC;YACF,wFAAwF;aACvF,KAAK,CAAC,CAAC,GAAY,EAAQ,EAAE,CAC1B,IAAI,CAAC,KAAK,CAAC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAClE,CAAC;IACV,CAAC;IAES,cAAc,CAAC,IAA0B;QAC/C,OAAO,GAAS,EAAE,GAAE,CAAC,CAAC;IAC1B,CAAC;IACS,YAAY,KAAU,CAAC;IAEvB,UAAU,CAAC,IAAmB,IAAS,CAAC;IAClD,4FAA4F;IAClF,OAAO,CAAC,IAAoB;QAClC,OAAO,IAAI,CAAC,GAAG,CAAC;IACpB,CAAC;IACD,qFAAqF;IAC3E,WAAW;QACjB,OAAO,IAAI,CAAC;IAChB,CAAC;IAED;;;OAGG;IACH,MAAM;QACF,MAAM,MAAM,GAAG,QAAQ,CAAC,aAAa,CAAC;QACtC,MAAM,IAAI,GAAG,QAAQ,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC;QAC9C,MAAM,OAAO,GAAG,MAAM,YAAY,WAAW,IAAI,IAAI,KAAK,IAAI,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;QACzF,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACzB,IAAI,OAAO,IAAI,MAAM,CAAC,WAAW;YAAE,MAAM,CAAC,KAAK,CAAC,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;IAC7E,CAAC;IAED,UAAU,CAAC,IAAY;QACnB,OAAO,IAAI,cAAc,CACrB,IAAI,CAAC,MAAM,KAAK,IAAI,CAAC,CAAC,CAAC,oBAAoB,CAAC,CAAC,CAAC,iBAAiB,EAC/D,GAAG,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CACxD,CAAC;IACN,CAAC;IAED,MAAM,CAAC,MAAqB;QACxB,IAAI,IAAI,CAAC,GAAG,KAAK,IAAI;YAAE,OAAO;QAC9B,MAAM,IAAI,GAAG,QAAQ,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC;QAC9C,IAAI,IAAI,KAAK,IAAI;YAAE,OAAO;QAC1B,MAAM,QAAQ,GACV,MAAM,KAAK,IAAI;YACX,CAAC,CAAC,IAAI,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YAClD,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;QACnC,MAAM,cAAc,GAAG,IAAI,CAAC,MAAM,CAAC;QACnC,MAAM,gBAAgB,GAAG,IAAI,CAAC,QAAQ,CAAC;QACvC,MAAM,WAAW,GAAG,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;QAC9C,MAAM,SAAS,GAAG,UAAU,CAAC,WAAW,CAAC,WAAW,CAAC,CAAC;QACtD,MAAM,QAAQ,GAAG,IAAI,CAAC,cAAc,CAAC,WAAW,CAAC,CAAC;QAClD,mIAAmI;QACnI,IAAI,CAAC;YACD,MAAM,GAAG,GACL,MAAM,KAAK,IAAI,IAAI,IAAI,CAAC,WAAW,EAAE;gBACjC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO;gBACpB,CAAC,CAAC,IAAI,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,QAAQ,EAAE,CAAC,IAAoB,EAAU,EAAE,CAC5E,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CACrB,CAAC;YACZ,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,gBAAgB,CAAC,GAAG,CAAC,CAAC;YAC3C,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC;YACrB,UAAU,CAAC,KAAK,EAAE,CAAC;YACnB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;YACrB,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;YACzB,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,CAAC;YAC1B,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;YAClB,IAAI,CAAC,SAAS,EAAE,CAAC;YACjB,UAAU,CAAC,YAAY,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC;YACxC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;YACjB,8FAA8F;QAClG,CAAC;QAAC,OAAO,GAAY,EAAE,CAAC;YACpB,6BAA6B;YAC7B,IAAI,CAAC,MAAM,GAAG,cAAc,CAAC;YAC7B,IAAI,CAAC,QAAQ,GAAG,gBAAgB,CAAC;YACjC,IAAI,WAAW,KAAK,IAAI;gBAAE,IAAI,CAAC,eAAe,CAAC,WAAW,CAAC,CAAC;;gBACvD,IAAI,CAAC,eAAe,EAAE,CAAC;YAC5B,QAAQ,EAAE,CAAC;YACX,IAAI,WAAW,KAAK,IAAI;gBAAE,UAAU,CAAC,YAAY,CAAC,WAAW,EAAE,SAAS,CAAC,CAAC;YAC1E,IAAI,CAAC,KAAK,CAAC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;QACpE,CAAC;IACL,CAAC;IAEO,SAAS;QACb,MAAM,MAAM,GAAG,QAAQ,CAAC,cAAc,CAAC,kBAAkB,CAAC,CAAC;QAC3D,MAAM,KAAK,GAAG,QAAQ,CAAC,cAAc,CAAC,kBAAkB,CAAC,CAAC;QAC1D,IAAI,MAAM,KAAK,IAAI;YAAE,MAAM,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,KAAK,IAAI,CAAC;QAC1D,IAAI,KAAK,KAAK,IAAI;YAAE,KAAK,CAAC,WAAW,GAAG,IAAI,CAAC,MAAM,CAAC;QACpD,IAAI,IAAI,CAAC,MAAM,KAAK,IAAI;YAAE,QAAQ,CAAC,cAAc,CAAC,eAAe,CAAC,EAAE,KAAK,EAAE,CAAC;;YACvE,QAAQ,CAAC,aAAa,CAAc,4BAA4B,CAAC,EAAE,KAAK,EAAE,CAAC;IACpF,CAAC;IAEO,KAAK,CAAC,GAAiB;QAC3B,MAAM,EAAE,GAAG,QAAQ,CAAC,cAAc,CAAC,gBAAgB,CAAC,CAAC;QACrD,IAAI,EAAE,KAAK,IAAI;YAAE,OAAO;QACxB,EAAE,CAAC,MAAM,GAAG,GAAG,KAAK,IAAI,CAAC;QACzB,EAAE,CAAC,WAAW;YACV,GAAG,KAAK,IAAI;gBACR,CAAC,CAAC,EAAE;gBACJ,CAAC,CAAC,2BAA2B,GAAG,CAAC,OAAO,2CAA2C,CAAC;IAChG,CAAC;CACJ","sourcesContent":["/** Plain browser helpers shared by architecture and runtime; no CommonJS imports. */\nclass WpGraphChain {\n readonly inNodes = new Map<string, Set<string>>();\n readonly outNodes = new Map<string, Set<string>>();\n\n constructor(private readonly model: RenderModelJson) {\n const ids = new Set(model.nodes.map((node) => node.id));\n for (const edge of model.edges) {\n if (!ids.has(edge.from) || !ids.has(edge.to)) continue;\n this.add(this.outNodes, edge.from, edge.to);\n this.add(this.inNodes, edge.to, edge.from);\n }\n }\n\n private add(map: Map<string, Set<string>>, from: string, to: string): void {\n if (!map.has(from)) map.set(from, new Set());\n map.get(from)?.add(to);\n }\n\n nodes(anchor: string): Set<string> {\n const retained = new Set<string>();\n if (!this.model.nodes.some((node) => node.id === anchor)) return retained;\n for (const direction of [this.inNodes, this.outNodes]) {\n const visited = new Set([anchor]);\n const stack = [anchor];\n while (stack.length > 0) {\n const id = stack.pop() as string;\n retained.add(id);\n for (const next of direction.get(id) ?? []) {\n if (visited.has(next)) continue;\n visited.add(next);\n stack.push(next);\n }\n }\n }\n return retained;\n }\n}\n\nclass WpRenderBand {\n constructor(\n readonly level: number,\n readonly nodeNames: string[],\n ) {}\n}\n\n/** Original records plus fresh ranks: removed nodes cannot reappear via a rank/edge. */\nclass WpFilteredDot {\n constructor(private readonly model: RenderModelJson) {}\n\n /** `nodeDot` picks each box's statement — the architecture page passes its current color mode's. */\n render(\n retained: Set<string>,\n nodeDot: (node: RenderNodeJson) => string = (node: RenderNodeJson): string => node.dot,\n ): string {\n const nodes = this.model.nodes.filter((node: RenderNodeJson): boolean => retained.has(node.id));\n const edges = this.model.edges.filter(\n (edge) => retained.has(edge.from) && retained.has(edge.to),\n );\n const bands = this.model.bands\n .map(\n (band) =>\n new WpRenderBand(\n band.level,\n band.nodeNames.filter((id) => retained.has(id)),\n ),\n )\n .filter((band) => band.nodeNames.length > 0);\n return (\n this.model.header +\n nodes.map((node: RenderNodeJson): string => nodeDot(node)).join('') +\n this.bandDot(bands) +\n edges.map((edge) => edge.dot).join('') +\n this.model.footer\n );\n }\n\n private bandDot(bands: WpRenderBand[]): string {\n const nodeAttrs =\n '[style=invis, shape=point, width=0.01, height=0.01, label=\"\", class=\"wp-layout\"]';\n const edgeAttrs = '[style=invis, class=\"wp-layout\"]';\n let dot = '';\n for (const band of bands) {\n const anchor = `__wp_layout_L${band.level}`;\n dot += ` \"${anchor}\" ${nodeAttrs};\\n { rank=same; \"${anchor}\"; `;\n dot += band.nodeNames.map((id) => `${JSON.stringify(id)}; `).join('') + '}\\n';\n }\n for (let i = 0; i + 1 < bands.length; i++) {\n const upper = bands[i];\n const lower = bands[i + 1];\n const from = `__wp_layout_L${upper.level}`;\n const to = `__wp_layout_L${lower.level}`;\n if (upper.nodeNames.length <= 10 && lower.nodeNames.length <= 10) {\n dot += ` \"${from}\" -> \"${to}\" ${edgeAttrs};\\n`;\n continue;\n }\n const spacer = `__wp_layout_spacer_L${upper.level}_L${lower.level}`;\n dot += ` \"${spacer}\" ${nodeAttrs};\\n { rank=same; \"${spacer}\"; }\\n`;\n dot += ` \"${from}\" -> \"${spacer}\" ${edgeAttrs};\\n \"${spacer}\" -> \"${to}\" ${edgeAttrs};\\n`;\n }\n return dot;\n }\n}\n\n/** Page-owned state survives replacements; rendering failures leave the usable SVG intact. */\nabstract class WpFilterPage {\n protected readonly chain: WpGraphChain;\n protected locked: string | null = null;\n protected anchor: string | null = null;\n protected retained: Set<string>;\n private viz: VizInstance | null = null;\n\n constructor(protected readonly model: RenderModelJson) {\n this.chain = new WpGraphChain(model);\n this.retained = new Set(model.nodes.map((node) => node.id));\n }\n\n render(): void {\n document\n .getElementById('wp-filter-off')\n ?.addEventListener('click', () => this.filter(null));\n this.wireControls();\n Viz.instance()\n .then((viz) => {\n this.viz = viz;\n this.filter(null);\n })\n // webpieces-disable no-any-unknown -- promise rejections may carry any JavaScript value\n .catch((err: unknown): void =>\n this.error(err instanceof Error ? err : new Error(String(err))),\n );\n }\n\n protected captureBinding(_svg: SVGSVGElement | null): () => void {\n return (): void => {};\n }\n protected wireControls(): void {}\n protected abstract wireSvg(svg: SVGSVGElement): void;\n protected prepareSvg(_svg: SVGSVGElement): void {}\n /** The statement drawn for one box. The architecture page answers with its color mode's. */\n protected nodeDot(node: RenderNodeJson): string {\n return node.dot;\n }\n /** Whether an unfiltered draw may use the model's precomputed `fullDot` verbatim. */\n protected usesFullDot(): boolean {\n return true;\n }\n\n /**\n * Draw again with the current filter — after a color-mode switch. A control outside the graph\n * (a drawer button) keeps keyboard focus; the redraw would otherwise move it into the graph.\n */\n redraw(): void {\n const active = document.activeElement;\n const host = document.getElementById('graph');\n const outside = active instanceof HTMLElement && host !== null && !host.contains(active);\n this.filter(this.anchor);\n if (outside && active.isConnected) active.focus({ preventScroll: true });\n }\n\n filterItem(name: string): WpNodeMenuItem {\n return new WpNodeMenuItem(\n this.anchor === null ? 'Filter Unconnected' : 'Turn off Filter',\n () => this.filter(this.anchor === null ? name : null),\n );\n }\n\n filter(anchor: string | null): void {\n if (this.viz === null) return;\n const host = document.getElementById('graph');\n if (host === null) return;\n const retained =\n anchor === null\n ? new Set(this.model.nodes.map((node) => node.id))\n : this.chain.nodes(anchor);\n const previousAnchor = this.anchor;\n const previousRetained = this.retained;\n const previousSvg = host.querySelector('svg');\n const focusName = WpNodeMenu.focusedName(previousSvg);\n const rollback = this.captureBinding(previousSvg);\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions -- browser transaction restores the previous usable graph on failure\n try {\n const dot =\n anchor === null && this.usesFullDot()\n ? this.model.fullDot\n : new WpFilteredDot(this.model).render(retained, (node: RenderNodeJson): string =>\n this.nodeDot(node),\n );\n const svg = this.viz.renderSVGElement(dot);\n this.prepareSvg(svg);\n WpNodeMenu.close();\n this.anchor = anchor;\n this.retained = retained;\n host.replaceChildren(svg);\n this.wireSvg(svg);\n this.indicator();\n WpNodeMenu.restoreFocus(svg, focusName);\n this.error(null);\n // webpieces-disable no-any-unknown -- JavaScript may throw any value at this browser boundary\n } catch (err: unknown) {\n //const error = toError(err);\n this.anchor = previousAnchor;\n this.retained = previousRetained;\n if (previousSvg !== null) host.replaceChildren(previousSvg);\n else host.replaceChildren();\n rollback();\n if (previousSvg !== null) WpNodeMenu.restoreFocus(previousSvg, focusName);\n this.error(err instanceof Error ? err : new Error(String(err)));\n }\n }\n\n private indicator(): void {\n const status = document.getElementById('wp-filter-status');\n const label = document.getElementById('wp-filter-anchor');\n if (status !== null) status.hidden = this.anchor === null;\n if (label !== null) label.textContent = this.anchor;\n if (this.anchor !== null) document.getElementById('wp-filter-off')?.focus();\n else document.querySelector<SVGGElement>('#graph g.wp-node-clickable')?.focus();\n }\n\n private error(err: Error | null): void {\n const el = document.getElementById('wp-graph-error');\n if (el === null) return;\n el.hidden = err === null;\n el.textContent =\n err === null\n ? ''\n : `Graph rendering failed: ${err.message}. Try Turn off Filter or reload the page.`;\n }\n}\n"]}
|