@webpieces/nx-webpieces-rules 0.4.870 → 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.
Files changed (61) hide show
  1. package/README.md +36 -0
  2. package/package.json +7 -7
  3. package/src/executors/generate/executor.d.ts +3 -1
  4. package/src/executors/generate/executor.js +9 -1
  5. package/src/executors/generate/executor.js.map +1 -1
  6. package/src/index.d.ts +1 -0
  7. package/src/index.js +5 -1
  8. package/src/index.js.map +1 -1
  9. package/src/lib/di-graph/runtime-client-bindings.d.ts +5 -1
  10. package/src/lib/di-graph/runtime-client-bindings.js +21 -5
  11. package/src/lib/di-graph/runtime-client-bindings.js.map +1 -1
  12. package/src/lib/dot-syntax.d.ts +9 -0
  13. package/src/lib/dot-syntax.js +17 -0
  14. package/src/lib/dot-syntax.js.map +1 -1
  15. package/src/lib/graph-color-modes.d.ts +136 -0
  16. package/src/lib/graph-color-modes.js +299 -0
  17. package/src/lib/graph-color-modes.js.map +1 -0
  18. package/src/lib/graph-filter.client.d.ts +11 -1
  19. package/src/lib/graph-filter.client.js +25 -4
  20. package/src/lib/graph-filter.client.js.map +1 -1
  21. package/src/lib/graph-impact.d.ts +119 -0
  22. package/src/lib/graph-impact.js +295 -0
  23. package/src/lib/graph-impact.js.map +1 -0
  24. package/src/lib/graph-legend.d.ts +34 -0
  25. package/src/lib/graph-legend.js +116 -0
  26. package/src/lib/graph-legend.js.map +1 -0
  27. package/src/lib/graph-names.d.ts +2 -2
  28. package/src/lib/graph-names.js +2 -2
  29. package/src/lib/graph-names.js.map +1 -1
  30. package/src/lib/graph-navigation.d.ts +10 -0
  31. package/src/lib/graph-navigation.js +47 -12
  32. package/src/lib/graph-navigation.js.map +1 -1
  33. package/src/lib/graph-node-menu.d.ts +4 -3
  34. package/src/lib/graph-node-menu.js +137 -20
  35. package/src/lib/graph-node-menu.js.map +1 -1
  36. package/src/lib/graph-page-shell.d.ts +34 -0
  37. package/src/lib/graph-page-shell.js +121 -0
  38. package/src/lib/graph-page-shell.js.map +1 -0
  39. package/src/lib/graph-page-styles.d.ts +20 -0
  40. package/src/lib/graph-page-styles.js +144 -0
  41. package/src/lib/graph-page-styles.js.map +1 -0
  42. package/src/lib/graph-render-model.d.ts +14 -2
  43. package/src/lib/graph-render-model.js +19 -3
  44. package/src/lib/graph-render-model.js.map +1 -1
  45. package/src/lib/graph-responsibilities.js +1 -1
  46. package/src/lib/graph-responsibilities.js.map +1 -1
  47. package/src/lib/graph-visualizer.client.d.ts +65 -0
  48. package/src/lib/graph-visualizer.client.js +311 -7
  49. package/src/lib/graph-visualizer.client.js.map +1 -1
  50. package/src/lib/graph-visualizer.d.ts +19 -31
  51. package/src/lib/graph-visualizer.js +50 -286
  52. package/src/lib/graph-visualizer.js.map +1 -1
  53. package/src/lib/runtime-html-page.js +2 -2
  54. package/src/lib/runtime-html-page.js.map +1 -1
  55. package/src/lib/runtime-wiring/source-extractor.d.ts +2 -1
  56. package/src/lib/runtime-wiring/source-extractor.js +3 -2
  57. package/src/lib/runtime-wiring/source-extractor.js.map +1 -1
  58. package/src/lib/runtime-wiring/wiring-format.js +2 -2
  59. package/src/lib/runtime-wiring/wiring-format.js.map +1 -1
  60. package/src/rule-help.js +1 -1
  61. package/src/rule-help.js.map +1 -1
@@ -0,0 +1,136 @@
1
+ /**
2
+ * Graph color modes (#1155)
3
+ *
4
+ * The architecture graph answers ONE question at a time instead of asking every box to encode two
5
+ * (fill = framework AND border = role). Each box is emitted once per mode, so the page can switch
6
+ * modes and re-render without regenerating anything:
7
+ *
8
+ * - RUNTIME "where the code can run". Base runtimes are equal vertical stripes in the fixed
9
+ * order browser | node | react-native; a specialization (angular/react inside
10
+ * browser, express inside node) is an inset inside its base stripe. The dependency
11
+ * rule becomes visual: a box may depend on a box carrying every color it has. The
12
+ * name and meta sit on a light label PLATE across the middle, so they read on every
13
+ * stripe combination while the stripes and insets stay visible above and below it.
14
+ * - ARCHITECTURE "servers · clients · APIs". A solid fill by ROLE; framework color is not shown.
15
+ * - IMPACT "what this branch touches". Projects owning a changed file are solid amber
16
+ * ("touched"); the rest of nx's affected set is light amber ("affected": their ci
17
+ * re-runs); every transitive dependency of the affected set that is not itself
18
+ * affected is light slate with a dashed border ("build input": compiled or restored
19
+ * from cache, unchanged); everything else is grey. Which box is which is only known
20
+ * per branch, so every box carries all four variants and the page picks one from the
21
+ * sidecar (graph-impact.ts).
22
+ */
23
+ import { KnownFramework } from '@webpieces/rules-sdk';
24
+ export declare enum GraphMode {
25
+ RUNTIME = "runtime",
26
+ ARCHITECTURE = "architecture",
27
+ IMPACT = "impact"
28
+ }
29
+ /** One selectable mode as the drawer and the node menu show it. */
30
+ export declare class ModeInfo {
31
+ readonly mode: GraphMode;
32
+ readonly name: string;
33
+ readonly subtitle: string;
34
+ constructor(mode: GraphMode, name: string, subtitle: string);
35
+ }
36
+ /** In key order: `1` selects the first, `2` the second, `3` the third. */
37
+ export declare const GRAPH_MODES: readonly ModeInfo[];
38
+ /** A framework value's color and the BASE runtime it is drawn inside (itself, for a base). */
39
+ export declare class FrameworkStyle {
40
+ readonly framework: KnownFramework;
41
+ readonly base: KnownFramework;
42
+ readonly color: string;
43
+ readonly description: string;
44
+ constructor(framework: KnownFramework, base: KnownFramework, color: string, description: string);
45
+ isBase(): boolean;
46
+ }
47
+ /**
48
+ * Keyed by the shared vocabulary, so a framework added to KNOWN_FRAMEWORKS without a color here is a
49
+ * COMPILE error — not a box silently painted grey and missing from the legend, which is exactly how
50
+ * react-native shipped (#1155).
51
+ */
52
+ export declare const FRAMEWORK_STYLES: Record<KnownFramework, FrameworkStyle>;
53
+ /** Stripe order, left to right. Fixed, so the same set always reads the same way. */
54
+ export declare const BASE_ORDER: readonly KnownFramework[];
55
+ /** A box with no known framework tag. */
56
+ export declare const NO_RUNTIME_COLOR = "#e4e6ec";
57
+ /** A role's solid fill and the text color that stays readable on it. */
58
+ export declare class RoleStyle {
59
+ readonly role: string;
60
+ readonly fill: string;
61
+ readonly fontColor: string;
62
+ readonly description: string;
63
+ constructor(role: string, fill: string, fontColor: string, description: string);
64
+ }
65
+ /** Every role role-resolver knows, in legend order (runnable things first). */
66
+ export declare const ROLE_STYLES: readonly RoleStyle[];
67
+ /** The four impact shades (graph-impact.ts decides which box gets which). */
68
+ export declare const IMPACT_TOUCHED = "#f5a524";
69
+ export declare const IMPACT_AFFECTED = "#fde3b0";
70
+ export declare const IMPACT_BUILD_INPUT = "#e3e9f2";
71
+ export declare const IMPACT_BUILD_INPUT_BORDER = "#7c8aa3";
72
+ export declare const IMPACT_UNTOUCHED = "#eef0f4";
73
+ /** The light plate the name and meta sit on, across every stripe of a multi-color box. */
74
+ export declare const LABEL_PLATE = "#fbfbfd";
75
+ /** What a box's per-mode rendering is computed from. */
76
+ export declare class NodeFacts {
77
+ readonly nodeId: string;
78
+ readonly shortName: string;
79
+ readonly level: number;
80
+ readonly role: string;
81
+ readonly frameworks: string[];
82
+ constructor(nodeId: string, shortName: string, level: number, role: string, frameworks: string[]);
83
+ }
84
+ /** One box's DOT statement for every mode (and for each impact state). */
85
+ export declare class NodeModeDots {
86
+ readonly runtime: string;
87
+ readonly architecture: string;
88
+ readonly touched: string;
89
+ readonly affected: string;
90
+ readonly buildInput: string;
91
+ readonly untouched: string;
92
+ constructor(runtime: string, architecture: string, touched: string, affected: string, buildInput: string, untouched: string);
93
+ }
94
+ /** Which colors and roles the drawn projects actually use, so the legend lists exactly those. */
95
+ export declare class LegendFacts {
96
+ frameworks: string[];
97
+ roles: string[];
98
+ multiRuntime: boolean;
99
+ noRuntime: boolean;
100
+ }
101
+ export declare class NodeModeStyler {
102
+ styleOf(framework: string): FrameworkStyle | undefined;
103
+ roleStyle(role: string): RoleStyle;
104
+ /** Every role the renderer styles; a spec asserts this covers role-resolver's KNOWN_ROLES. */
105
+ styledRoles(): string[];
106
+ /** The base runtimes of a framework set, in stripe order (a specialization implies its base). */
107
+ bases(frameworks: string[]): KnownFramework[];
108
+ /** The specialization drawn inside `base`, if the set carries one. */
109
+ specialization(base: KnownFramework, frameworks: string[]): FrameworkStyle | undefined;
110
+ dots(facts: NodeFacts): NodeModeDots;
111
+ /** Fold one drawn box into the legend facts. */
112
+ record(legend: LegendFacts, facts: NodeFacts): void;
113
+ private statement;
114
+ private plainLabel;
115
+ private runtimeMeta;
116
+ private envMeta;
117
+ private architectureAttrs;
118
+ /**
119
+ * Runtime mode. A box in ONE base runtime with no specialization is a plain fill — its dark text
120
+ * reads on any of the pastel base colors. Every other box (several runtimes, or a specialization)
121
+ * is an HTML-like table of three rows: a band of equal stripes, a light PLATE carrying the name and
122
+ * meta across all of them, and the same stripes again. The stripes and insets stay visible above
123
+ * and below the text, and the text never straddles two colors.
124
+ */
125
+ private runtime;
126
+ /** `filled` for one runtime; equal `striped` weights for several (the last takes the remainder). */
127
+ stripeAttrs(bases: KnownFramework[]): string;
128
+ /**
129
+ * Stripe band / plate / stripe band. Every stripe cell asks for the same minimum width (an
130
+ * estimate of the text's width split evenly), so the stripes come out equal and the plate spans
131
+ * exactly their total.
132
+ */
133
+ private plateTable;
134
+ /** One stripe of a band: the base color, with its specialization inset inside a margin of it. */
135
+ private stripeCell;
136
+ }
@@ -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
- render(retained: Set<string>): string;
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
- render(retained) {
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.dot).join('') +
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();