@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.
- 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/di-graph/runtime-client-bindings.d.ts +5 -1
- package/src/lib/di-graph/runtime-client-bindings.js +21 -5
- package/src/lib/di-graph/runtime-client-bindings.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
- package/src/lib/runtime-wiring/source-extractor.d.ts +2 -1
- package/src/lib/runtime-wiring/source-extractor.js +3 -2
- package/src/lib/runtime-wiring/source-extractor.js.map +1 -1
- package/src/lib/runtime-wiring/wiring-format.js +2 -2
- package/src/lib/runtime-wiring/wiring-format.js.map +1 -1
- package/src/rule-help.js +1 -1
- package/src/rule-help.js.map +1 -1
|
@@ -43,6 +43,11 @@ export declare class GraphVisualizer {
|
|
|
43
43
|
private readonly cycles;
|
|
44
44
|
/** The ONE floating-node-menu implementation, shared with the per-project design pages. */
|
|
45
45
|
private readonly nodeMenu;
|
|
46
|
+
private readonly styler;
|
|
47
|
+
private readonly legend;
|
|
48
|
+
private readonly shell;
|
|
49
|
+
private readonly pageStyles;
|
|
50
|
+
private readonly navigation;
|
|
46
51
|
/**
|
|
47
52
|
* How to obtain the browser client's text. Injected so HTML generation does not depend on BUILD
|
|
48
53
|
* ORDER: the default reads the compiled sibling, which exists in the tarball and in dist but NOT in
|
|
@@ -52,27 +57,10 @@ export declare class GraphVisualizer {
|
|
|
52
57
|
constructor(clientJs?: () => string, filterJs?: () => string);
|
|
53
58
|
/**
|
|
54
59
|
* A project tagged `drawOnGraph:false` is hidden from the rendered graph —
|
|
55
|
-
* its node, its rank placement, its
|
|
60
|
+
* its node, its rank placement, its lock-search option, its responsibilities
|
|
56
61
|
* card, and every edge touching it are all omitted. It stays in the JSON.
|
|
57
62
|
*/
|
|
58
63
|
private isHidden;
|
|
59
|
-
/**
|
|
60
|
-
* Fill color for an env set — the color of the first env in the set that has
|
|
61
|
-
* a known color, else the default.
|
|
62
|
-
*/
|
|
63
|
-
private frameworkColor;
|
|
64
|
-
/**
|
|
65
|
-
* Role border styling — fill stays keyed on framework; the border shows a
|
|
66
|
-
* project's ROLE at a glance. Server and client are the top-level runnable
|
|
67
|
-
* nodes, so they get bold, colored borders to stand out:
|
|
68
|
-
* server → thick GREEN border (a runnable server app)
|
|
69
|
-
* app → thick BLUE border (a runnable non-HTTP app, e.g. a tooling app)
|
|
70
|
-
* bundle → thick PURPLE border (an aggregator that bundles several apps)
|
|
71
|
-
* client → thick RED border (a client app, e.g. angular)
|
|
72
|
-
* designed-lib → bold border (a library with a generated @DocumentDesign design)
|
|
73
|
-
* lib / other → plain thin border
|
|
74
|
-
*/
|
|
75
|
-
private roleBorderAttrs;
|
|
76
64
|
/**
|
|
77
65
|
* Edge styling by API-relation kind (why the edge exists):
|
|
78
66
|
* implements → BLACK dashed (a controller serves this api-lib's contract)
|
|
@@ -135,26 +123,26 @@ export declare class GraphVisualizer {
|
|
|
135
123
|
private dotNodes;
|
|
136
124
|
private dotEdges;
|
|
137
125
|
/**
|
|
138
|
-
* Generate interactive
|
|
126
|
+
* Generate the interactive page: the drawer shell around a viz.js-rendered graph.
|
|
127
|
+
*
|
|
128
|
+
* The Impact sidecar is a plain `<script src>` loaded BEFORE the page scripts, so the page knows
|
|
129
|
+
* the branch's impact when it first draws. It is per-branch and gitignored (graph-impact.ts):
|
|
130
|
+
* when it is absent the browser skips it and Impact is disabled with its reason.
|
|
139
131
|
*/
|
|
140
132
|
generateHTML(model: GraphRenderModel, links: DesignLink[], title?: string, lockControl?: string, responsibilitiesHtml?: string): string;
|
|
133
|
+
/** Shared menu/dim/filter/zoom rules first, then the page's own, so its `#graph` rules win. */
|
|
134
|
+
private styles;
|
|
141
135
|
/**
|
|
142
|
-
* The lock
|
|
143
|
-
* Picking a
|
|
144
|
-
* ancestor + descendant chain stays lit while everything else
|
|
145
|
-
*
|
|
146
|
-
* The first option, "All", is the default and clears the lock. Hover still
|
|
147
|
-
* works on top of a lock; leaving a box returns to the locked view.
|
|
136
|
+
* The drawer's type-to-search lock field, replacing a `<select>` that listed every project and
|
|
137
|
+
* was unusable at hundreds of options. Picking a project LOCKS the graph into that box's hover
|
|
138
|
+
* view — its full ancestor + descendant chain stays lit while everything else dims — and narrows
|
|
139
|
+
* the responsibilities panel to that chain. Clearing the field, or Esc, unlocks.
|
|
148
140
|
*
|
|
141
|
+
* Each option's VALUE is the node id the SVG is keyed on; its label is the level and short name.
|
|
149
142
|
* Options are ordered by level DESCENDING to match the responsibilities cards.
|
|
150
143
|
*/
|
|
151
144
|
lockControl(graph: EnhancedGraph): string;
|
|
152
|
-
private
|
|
153
|
-
private componentStyles;
|
|
154
|
-
private legend;
|
|
155
|
-
private fillItems;
|
|
156
|
-
private borderItems;
|
|
157
|
-
private edgeItems;
|
|
145
|
+
private escapeHtml;
|
|
158
146
|
/**
|
|
159
147
|
* The page script. The browser code lives in graph-visualizer.client.ts — real, linted TypeScript
|
|
160
148
|
* that tsc compiles in place, so what this inlines is its COMPILED .js sitting beside this file.
|
|
@@ -26,20 +26,12 @@ const graph_responsibilities_1 = require("./graph-responsibilities");
|
|
|
26
26
|
const graph_node_menu_1 = require("./graph-node-menu");
|
|
27
27
|
const dot_syntax_1 = require("./dot-syntax");
|
|
28
28
|
const graph_render_model_1 = require("./graph-render-model");
|
|
29
|
+
const graph_color_modes_1 = require("./graph-color-modes");
|
|
30
|
+
const graph_legend_1 = require("./graph-legend");
|
|
31
|
+
const graph_page_shell_1 = require("./graph-page-shell");
|
|
32
|
+
const graph_page_styles_1 = require("./graph-page-styles");
|
|
33
|
+
const graph_impact_1 = require("./graph-impact");
|
|
29
34
|
const toError_1 = require("../toError");
|
|
30
|
-
/**
|
|
31
|
-
* Framework (libType) colors for visualization — nodes are filled by the FIRST
|
|
32
|
-
* env in their set that has a color, so it is obvious at a glance which side a
|
|
33
|
-
* project targets. A project's full env set is shown in the label.
|
|
34
|
-
*/
|
|
35
|
-
const FRAMEWORK_COLORS = {
|
|
36
|
-
angular: '#FCE4EC', // pink - Angular front-end
|
|
37
|
-
react: '#E3F2FD', // blue - React front-end
|
|
38
|
-
browser: '#EDE7F6', // purple - browser (front-end base env)
|
|
39
|
-
express: '#E8F5E9', // green - Express / server side
|
|
40
|
-
node: '#FFF9C4', // yellow - node (server base env)
|
|
41
|
-
};
|
|
42
|
-
const DEFAULT_FRAMEWORK_COLOR = '#F5F5F5'; // grey - unknown/empty env set
|
|
43
35
|
/**
|
|
44
36
|
* Directory (repo-relative) that the committed architecture HTML lives in.
|
|
45
37
|
* Node click-through links are computed relative to this so they resolve when
|
|
@@ -106,6 +98,11 @@ class GraphVisualizer {
|
|
|
106
98
|
cycles = new graph_cycles_1.ProjectCycleDetector();
|
|
107
99
|
/** The ONE floating-node-menu implementation, shared with the per-project design pages. */
|
|
108
100
|
nodeMenu = new graph_node_menu_1.GraphNodeMenu();
|
|
101
|
+
styler = new graph_color_modes_1.NodeModeStyler();
|
|
102
|
+
legend = new graph_legend_1.GraphLegend();
|
|
103
|
+
shell = new graph_page_shell_1.GraphPageShell();
|
|
104
|
+
pageStyles = new graph_page_styles_1.GraphPageStyles();
|
|
105
|
+
navigation = new graph_navigation_1.GraphNavigation(graph_navigation_1.NavigationLayout.FLOATING);
|
|
109
106
|
/**
|
|
110
107
|
* How to obtain the browser client's text. Injected so HTML generation does not depend on BUILD
|
|
111
108
|
* ORDER: the default reads the compiled sibling, which exists in the tarball and in dist but NOT in
|
|
@@ -118,52 +115,12 @@ class GraphVisualizer {
|
|
|
118
115
|
}
|
|
119
116
|
/**
|
|
120
117
|
* A project tagged `drawOnGraph:false` is hidden from the rendered graph —
|
|
121
|
-
* its node, its rank placement, its
|
|
118
|
+
* its node, its rank placement, its lock-search option, its responsibilities
|
|
122
119
|
* card, and every edge touching it are all omitted. It stays in the JSON.
|
|
123
120
|
*/
|
|
124
121
|
isHidden(entry) {
|
|
125
122
|
return entry.drawOnGraph === false;
|
|
126
123
|
}
|
|
127
|
-
/**
|
|
128
|
-
* Fill color for an env set — the color of the first env in the set that has
|
|
129
|
-
* a known color, else the default.
|
|
130
|
-
*/
|
|
131
|
-
frameworkColor(frameworks) {
|
|
132
|
-
for (const env of frameworks) {
|
|
133
|
-
const color = FRAMEWORK_COLORS[env];
|
|
134
|
-
if (color !== undefined)
|
|
135
|
-
return color;
|
|
136
|
-
}
|
|
137
|
-
return DEFAULT_FRAMEWORK_COLOR;
|
|
138
|
-
}
|
|
139
|
-
/**
|
|
140
|
-
* Role border styling — fill stays keyed on framework; the border shows a
|
|
141
|
-
* project's ROLE at a glance. Server and client are the top-level runnable
|
|
142
|
-
* nodes, so they get bold, colored borders to stand out:
|
|
143
|
-
* server → thick GREEN border (a runnable server app)
|
|
144
|
-
* app → thick BLUE border (a runnable non-HTTP app, e.g. a tooling app)
|
|
145
|
-
* bundle → thick PURPLE border (an aggregator that bundles several apps)
|
|
146
|
-
* client → thick RED border (a client app, e.g. angular)
|
|
147
|
-
* designed-lib → bold border (a library with a generated @DocumentDesign design)
|
|
148
|
-
* lib / other → plain thin border
|
|
149
|
-
*/
|
|
150
|
-
roleBorderAttrs(role) {
|
|
151
|
-
if (role === 'server')
|
|
152
|
-
return ', color="green", penwidth=3';
|
|
153
|
-
if (role === 'app')
|
|
154
|
-
return ', color="#1976d2", penwidth=3';
|
|
155
|
-
if (role === 'bundle')
|
|
156
|
-
return ', color="#6A1B9A", penwidth=3';
|
|
157
|
-
if (role === 'client')
|
|
158
|
-
return ', color="red", penwidth=3';
|
|
159
|
-
if (role === 'api-lib')
|
|
160
|
-
return ', color="#EF6C00", penwidth=2';
|
|
161
|
-
if (role === 'api-client')
|
|
162
|
-
return ', color="#00838F", penwidth=2';
|
|
163
|
-
if (role === 'designed-lib')
|
|
164
|
-
return ', penwidth=2';
|
|
165
|
-
return '';
|
|
166
|
-
}
|
|
167
124
|
/**
|
|
168
125
|
* Edge styling by API-relation kind (why the edge exists):
|
|
169
126
|
* implements → BLACK dashed (a controller serves this api-lib's contract)
|
|
@@ -311,26 +268,20 @@ class GraphVisualizer {
|
|
|
311
268
|
const levels = [...byLevel.keys()].sort((a, b) => b - a);
|
|
312
269
|
return levels.map((level) => new graph_level_bands_1.LevelBand(level, [...byLevel.get(level)].sort()));
|
|
313
270
|
}
|
|
314
|
-
// Node lines:
|
|
315
|
-
//
|
|
316
|
-
// No node carries a URL: EVERY box is clickable and opens the floating node
|
|
317
|
-
//
|
|
271
|
+
// Node lines: one statement per color mode (graph-color-modes.ts) — the label is the short
|
|
272
|
+
// name plus a short meta line, because the fill now shows what the env list used to spell out.
|
|
273
|
+
// No node carries a URL: EVERY box is clickable and opens the floating node menu instead, which
|
|
274
|
+
// is where a design page is reached (see designLinks).
|
|
318
275
|
dotNodes(graph, model) {
|
|
319
276
|
let dot = '';
|
|
320
277
|
for (const project of Object.keys(graph)) {
|
|
321
278
|
const info = graph[project];
|
|
322
279
|
if (this.isHidden(info))
|
|
323
280
|
continue;
|
|
324
|
-
const nodeId = this.names.getNodeId(project);
|
|
325
|
-
const shortName = this.names.getShortName(project);
|
|
326
|
-
const frameworks = info.framework ?? [];
|
|
327
|
-
const role = info.role ?? 'lib';
|
|
328
|
-
const color = this.frameworkColor(frameworks);
|
|
329
|
-
const border = this.roleBorderAttrs(role);
|
|
330
|
-
const envSet = `[${frameworks.join(', ')}]`;
|
|
331
|
-
const labelMeta = `L${info.level} · ${envSet} · ${role}`;
|
|
332
281
|
// Identity is the project key; the LABEL is the pretty short name.
|
|
333
|
-
|
|
282
|
+
const facts = new graph_color_modes_1.NodeFacts(this.names.getNodeId(project), this.names.getShortName(project), info.level, info.role ?? 'lib', info.framework ?? []);
|
|
283
|
+
this.styler.record(model.legend, facts);
|
|
284
|
+
dot += model.styledNode(facts.nodeId, this.styler.dots(facts));
|
|
334
285
|
}
|
|
335
286
|
return dot;
|
|
336
287
|
}
|
|
@@ -355,45 +306,48 @@ class GraphVisualizer {
|
|
|
355
306
|
return dot;
|
|
356
307
|
}
|
|
357
308
|
/**
|
|
358
|
-
* Generate interactive
|
|
309
|
+
* Generate the interactive page: the drawer shell around a viz.js-rendered graph.
|
|
310
|
+
*
|
|
311
|
+
* The Impact sidecar is a plain `<script src>` loaded BEFORE the page scripts, so the page knows
|
|
312
|
+
* the branch's impact when it first draws. It is per-branch and gitignored (graph-impact.ts):
|
|
313
|
+
* when it is absent the browser skips it and Impact is disabled with its reason.
|
|
359
314
|
*/
|
|
360
315
|
generateHTML(model, links, title = 'Monorepo Dependency Architecture', lockControl = '', responsibilitiesHtml = '') {
|
|
361
|
-
const
|
|
362
|
-
const legend = this.legend();
|
|
363
|
-
const script = this.script(model, links);
|
|
316
|
+
const parts = new graph_page_shell_1.ShellParts(title, lockControl, this.legend.sections(model.legend), this.legend.edgeKey(), this.filterAssets.html(), this.snapshot.html(), responsibilitiesHtml);
|
|
364
317
|
return `<!DOCTYPE html>
|
|
365
318
|
<html>
|
|
366
319
|
<head>
|
|
367
320
|
<meta charset="utf-8">
|
|
321
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
368
322
|
<title>${title}</title>
|
|
369
323
|
<script src="https://cdn.jsdelivr.net/npm/@viz-js/viz@3.28.0/dist/viz-global.js"></script>
|
|
370
|
-
<
|
|
324
|
+
<script src="${graph_impact_1.IMPACT_SIDECAR_SRC}"></script>
|
|
325
|
+
<style>${this.styles()}</style>
|
|
371
326
|
</head>
|
|
372
327
|
<body>
|
|
373
|
-
|
|
374
|
-
${this.snapshot.html()}
|
|
375
|
-
<p class="hint">💡 <strong>Click any box</strong> for its menu — <strong>View Design</strong> (only where that project has a generated <strong>design.html</strong>, i.e. what the AI sees inside it) and <strong>Lock/Unlock</strong>, which is the same lock as the dropdown below. <strong>Filter Unconnected</strong> removes unrelated boxes and compacts the graph while preserving each L-number row.</p>
|
|
376
|
-
<p class="hint">🔦 <strong>Hover any box</strong> to trace its <em>entire</em> dependency chain — every ancestor above it (all the way up) <em>and</em> every dependency below it (all the way down), with all the boxes and lines between — while the rest of the graph dims so you can follow one box at a glance.</p>
|
|
377
|
-
${legend}
|
|
378
|
-
${lockControl}
|
|
379
|
-
${this.filterAssets.html()}
|
|
380
|
-
<div id="graph"></div>
|
|
381
|
-
${responsibilitiesHtml}
|
|
328
|
+
${this.shell.body(parts)}
|
|
382
329
|
<script>${this.nodeMenu.script()}</script>
|
|
383
330
|
<script>${this.filterJs()}</script>
|
|
384
|
-
<script>${
|
|
385
|
-
<script>${script}</script>
|
|
331
|
+
<script>${this.navigation.script()}</script>
|
|
332
|
+
<script>${this.script(model, links)}</script>
|
|
386
333
|
</body>
|
|
387
334
|
</html>`;
|
|
388
335
|
}
|
|
336
|
+
/** Shared menu/dim/filter/zoom rules first, then the page's own, so its `#graph` rules win. */
|
|
337
|
+
styles() {
|
|
338
|
+
return (this.nodeMenu.styles() +
|
|
339
|
+
this.nodeMenu.dimStyles('#graph') +
|
|
340
|
+
this.filterAssets.styles() +
|
|
341
|
+
this.navigation.styles() +
|
|
342
|
+
this.pageStyles.css());
|
|
343
|
+
}
|
|
389
344
|
/**
|
|
390
|
-
* The lock
|
|
391
|
-
* Picking a
|
|
392
|
-
* ancestor + descendant chain stays lit while everything else
|
|
393
|
-
*
|
|
394
|
-
* The first option, "All", is the default and clears the lock. Hover still
|
|
395
|
-
* works on top of a lock; leaving a box returns to the locked view.
|
|
345
|
+
* The drawer's type-to-search lock field, replacing a `<select>` that listed every project and
|
|
346
|
+
* was unusable at hundreds of options. Picking a project LOCKS the graph into that box's hover
|
|
347
|
+
* view — its full ancestor + descendant chain stays lit while everything else dims — and narrows
|
|
348
|
+
* the responsibilities panel to that chain. Clearing the field, or Esc, unlocks.
|
|
396
349
|
*
|
|
350
|
+
* Each option's VALUE is the node id the SVG is keyed on; its label is the level and short name.
|
|
397
351
|
* Options are ordered by level DESCENDING to match the responsibilities cards.
|
|
398
352
|
*/
|
|
399
353
|
lockControl(graph) {
|
|
@@ -408,206 +362,16 @@ class GraphVisualizer {
|
|
|
408
362
|
for (const project of projects) {
|
|
409
363
|
if (this.isHidden(graph[project]))
|
|
410
364
|
continue;
|
|
411
|
-
|
|
412
|
-
const
|
|
413
|
-
const shortName = this.names.getShortName(project);
|
|
365
|
+
const nodeId = this.escapeHtml(this.names.getNodeId(project));
|
|
366
|
+
const shortName = this.escapeHtml(this.names.getShortName(project));
|
|
414
367
|
options += `<option value="${nodeId}">L${graph[project].level} · ${shortName}</option>`;
|
|
415
368
|
}
|
|
416
|
-
return
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
</div>`;
|
|
420
|
-
}
|
|
421
|
-
styles() {
|
|
422
|
-
return `
|
|
423
|
-
body { margin: 0; padding: 20px; font-family: Arial, sans-serif; background: #f5f5f5; }
|
|
424
|
-
h1 { text-align: center; color: #333; }
|
|
425
|
-
.hint { text-align: center; color: #555; margin: 0 0 16px; }
|
|
426
|
-
/* Every box is clickable (the floating node menu replaced direct
|
|
427
|
-
* navigation), so the menu's own stylesheet carries the cursor + blue
|
|
428
|
-
* glow, and the dim/undim rules a hover or a lock toggles. Both are
|
|
429
|
-
* shared verbatim with the per-project design pages. */
|
|
430
|
-
${this.nodeMenu.styles()}
|
|
431
|
-
${this.nodeMenu.dimStyles('#graph')}
|
|
432
|
-
#graph g.node.wp-locked polygon,
|
|
433
|
-
#graph g.node.wp-locked ellipse,
|
|
434
|
-
#graph g.node.wp-locked path { stroke: #b26a00; stroke-width: 3; }
|
|
435
|
-
#graph {
|
|
436
|
-
text-align: center;
|
|
437
|
-
background: white;
|
|
438
|
-
padding: 20px;
|
|
439
|
-
border-radius: 8px;
|
|
440
|
-
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
|
441
|
-
}
|
|
442
|
-
.legend {
|
|
443
|
-
margin: 20px auto;
|
|
444
|
-
max-width: 1100px;
|
|
445
|
-
padding: 15px;
|
|
446
|
-
background: white;
|
|
447
|
-
border-radius: 8px;
|
|
448
|
-
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
|
449
|
-
}
|
|
450
|
-
.legend h2 { margin-top: 0; }
|
|
451
|
-
.legend-item { margin: 8px 0; }
|
|
452
|
-
.legend-box {
|
|
453
|
-
display: inline-block;
|
|
454
|
-
width: 20px;
|
|
455
|
-
height: 20px;
|
|
456
|
-
border: 1px solid #ccc;
|
|
457
|
-
margin-right: 10px;
|
|
458
|
-
vertical-align: middle;
|
|
459
|
-
}
|
|
460
|
-
${this.componentStyles()}
|
|
461
|
-
${this.filterAssets.styles()}
|
|
462
|
-
${new graph_navigation_1.GraphNavigation().styles()}`;
|
|
463
|
-
}
|
|
464
|
-
// Styles for the lock dropdown and the responsibilities card list below the
|
|
465
|
-
// graph. Split out of styles() to keep each method within the line limit.
|
|
466
|
-
componentStyles() {
|
|
467
|
-
return `
|
|
468
|
-
/* The architecture graph is very wide, so lay the legend out as three
|
|
469
|
-
* side-by-side columns (fill / border / edge) instead of one tall
|
|
470
|
-
* column — it keeps the legend short next to the wide graph, and
|
|
471
|
-
* collapses back to a single column on narrow viewports. */
|
|
472
|
-
.legend-columns {
|
|
473
|
-
display: grid;
|
|
474
|
-
grid-template-columns: repeat(3, 1fr);
|
|
475
|
-
gap: 28px;
|
|
476
|
-
align-items: start;
|
|
477
|
-
}
|
|
478
|
-
.legend-col h3 { margin: 0 0 8px; color: #333; font-size: 15px; }
|
|
479
|
-
@media (max-width: 800px) { .legend-columns { grid-template-columns: 1fr; } }
|
|
480
|
-
.wp-lock-control {
|
|
481
|
-
max-width: 600px;
|
|
482
|
-
margin: 0 auto 16px;
|
|
483
|
-
padding: 12px 15px;
|
|
484
|
-
background: white;
|
|
485
|
-
border-radius: 8px;
|
|
486
|
-
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
|
487
|
-
text-align: center;
|
|
488
|
-
}
|
|
489
|
-
.wp-lock-control label { font-weight: bold; color: #333; margin-right: 8px; }
|
|
490
|
-
.wp-lock-control select { font-size: 14px; padding: 4px 8px; }
|
|
491
|
-
#wp-responsibilities { max-width: 900px; margin: 24px auto 0; }
|
|
492
|
-
#wp-responsibilities h2 { color: #333; }
|
|
493
|
-
.wp-resp-card {
|
|
494
|
-
background: white;
|
|
495
|
-
border-radius: 8px;
|
|
496
|
-
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
|
497
|
-
margin: 10px 0;
|
|
498
|
-
padding: 10px 15px;
|
|
499
|
-
}
|
|
500
|
-
.wp-resp-card > summary { cursor: pointer; color: #333; }
|
|
501
|
-
.wp-resp-level {
|
|
502
|
-
display: inline-block;
|
|
503
|
-
min-width: 26px;
|
|
504
|
-
padding: 1px 6px;
|
|
505
|
-
margin-right: 6px;
|
|
506
|
-
border-radius: 4px;
|
|
507
|
-
background: #eef;
|
|
508
|
-
font-size: 12px;
|
|
509
|
-
font-weight: bold;
|
|
510
|
-
text-align: center;
|
|
511
|
-
}
|
|
512
|
-
.wp-resp-body { margin-top: 8px; color: #444; }
|
|
513
|
-
.wp-resp-body code {
|
|
514
|
-
background: #f2f2f2;
|
|
515
|
-
padding: 1px 4px;
|
|
516
|
-
border-radius: 3px;
|
|
517
|
-
font-family: monospace;
|
|
518
|
-
}
|
|
519
|
-
.wp-hidden { display: none; }`;
|
|
520
|
-
}
|
|
521
|
-
// The legend is laid out in three side-by-side columns (fill / border /
|
|
522
|
-
// edge) so it stays short next to the very wide architecture graph. Each
|
|
523
|
-
// column's rows come from a helper below to keep this method within the line
|
|
524
|
-
// limit; the footnote spans the full width beneath the columns.
|
|
525
|
-
legend() {
|
|
526
|
-
return `<div class="legend">
|
|
527
|
-
<h2>Legend</h2>
|
|
528
|
-
<div class="legend-columns">
|
|
529
|
-
<div class="legend-col">
|
|
530
|
-
<h3>Fill = framework (libType)</h3>
|
|
531
|
-
${this.fillItems()}
|
|
532
|
-
</div>
|
|
533
|
-
<div class="legend-col">
|
|
534
|
-
<h3>Border = role</h3>
|
|
535
|
-
${this.borderItems()}
|
|
536
|
-
</div>
|
|
537
|
-
<div class="legend-col">
|
|
538
|
-
<h3>Edge lines — <em>why</em> a project depends on an api-lib</h3>
|
|
539
|
-
${this.edgeItems()}
|
|
540
|
-
</div>
|
|
541
|
-
</div>
|
|
542
|
-
<div class="legend-item" style="margin-top: 15px;">
|
|
543
|
-
<em>Each node label shows its dependency level (L#), its framework env set (e.g. [browser, node]), and its role. Every row is one dependency level and nothing else: the HIGHEST level is the top row, levels descend as you read down, and L0 — the foundation libraries everything else is built on — is always the bottom row. Transitive dependencies are allowed but not shown.</em>
|
|
544
|
-
</div>
|
|
545
|
-
</div>`;
|
|
546
|
-
}
|
|
547
|
-
// Column 1 — fill color keyed on the project's framework (libType) env set.
|
|
548
|
-
fillItems() {
|
|
549
|
-
return `<div class="legend-item">
|
|
550
|
-
<span class="legend-box" style="background: #FCE4EC;"></span>
|
|
551
|
-
<strong>angular:</strong> Angular front-end
|
|
552
|
-
</div>
|
|
553
|
-
<div class="legend-item">
|
|
554
|
-
<span class="legend-box" style="background: #E3F2FD;"></span>
|
|
555
|
-
<strong>react:</strong> React front-end
|
|
556
|
-
</div>
|
|
557
|
-
<div class="legend-item">
|
|
558
|
-
<span class="legend-box" style="background: #EDE7F6;"></span>
|
|
559
|
-
<strong>browser:</strong> browser front-end base env
|
|
560
|
-
</div>
|
|
561
|
-
<div class="legend-item">
|
|
562
|
-
<span class="legend-box" style="background: #E8F5E9;"></span>
|
|
563
|
-
<strong>express:</strong> Express / server side
|
|
564
|
-
</div>
|
|
565
|
-
<div class="legend-item">
|
|
566
|
-
<span class="legend-box" style="background: #FFF9C4;"></span>
|
|
567
|
-
<strong>node:</strong> node server base env
|
|
568
|
-
</div>`;
|
|
569
|
-
}
|
|
570
|
-
// Column 2 — border style keyed on the project's role.
|
|
571
|
-
borderItems() {
|
|
572
|
-
return `<div class="legend-item">
|
|
573
|
-
<span class="legend-box" style="border: 3px solid green;"></span>
|
|
574
|
-
<strong>server:</strong> runnable server app (thick green border)
|
|
575
|
-
</div>
|
|
576
|
-
<div class="legend-item">
|
|
577
|
-
<span class="legend-box" style="border: 3px solid red;"></span>
|
|
578
|
-
<strong>client:</strong> client app, e.g. angular (thick red border)
|
|
579
|
-
</div>
|
|
580
|
-
<div class="legend-item">
|
|
581
|
-
<span class="legend-box" style="border: 2px solid #333;"></span>
|
|
582
|
-
<strong>designed-lib:</strong> library with a generated @DocumentDesign design (bold border)
|
|
583
|
-
</div>
|
|
584
|
-
<div class="legend-item">
|
|
585
|
-
<span class="legend-box" style="border: 1px solid #ccc;"></span>
|
|
586
|
-
<strong>lib:</strong> plain library, no generated design (thin border)
|
|
587
|
-
</div>
|
|
588
|
-
<div class="legend-item">
|
|
589
|
-
<span class="legend-box" style="border: 2px solid #EF6C00;"></span>
|
|
590
|
-
<strong>api-lib:</strong> API-contract library (defines <code>@ApiPath</code>/<code>@Rpc</code>/<code>@PubSub</code> <code>*Api</code> classes)
|
|
591
|
-
</div>
|
|
592
|
-
<div class="legend-item">
|
|
593
|
-
<span class="legend-box" style="border: 2px solid #00838F;"></span>
|
|
594
|
-
<strong>api-client:</strong> a contract plus its default implementation that talks to an outside system through its SDK
|
|
595
|
-
</div>`;
|
|
369
|
+
return ('<input type="search" id="wp-lock" list="wp-lock-options" placeholder="Lock a project… ( / )" ' +
|
|
370
|
+
'autocomplete="off" spellcheck="false">' +
|
|
371
|
+
`<datalist id="wp-lock-options">${options}</datalist>`);
|
|
596
372
|
}
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
return `<div class="legend-item">
|
|
600
|
-
<svg width="42" height="12" style="vertical-align: middle; margin-right: 10px;"><line x1="0" y1="6" x2="42" y2="6" stroke="#333" stroke-width="2"/></svg>
|
|
601
|
-
<strong>uses:</strong> calls the API (generates an rpc/pubsub client via <code>createRpcClient</code>/<code>createPubSubClient</code>) — also covers a plain library import, since a plain dependency is just a use.
|
|
602
|
-
</div>
|
|
603
|
-
<div class="legend-item">
|
|
604
|
-
<svg width="42" height="12" style="vertical-align: middle; margin-right: 10px;"><line x1="0" y1="6" x2="42" y2="6" stroke="#333" stroke-width="2" stroke-dasharray="5,3"/></svg>
|
|
605
|
-
<strong>implements:</strong> serves the API — labeled <code>implements: <contracts></code> so you can see WHICH contracts resolve to this server without opening the JSON. NOTE: this is a build-dependency diagram, so a UML <em>implements</em> arrow can't be used; we use a dashed line to signal a build dep, because this server implements the api and the api is built first, then this server after.
|
|
606
|
-
</div>
|
|
607
|
-
<div class="legend-item">
|
|
608
|
-
<svg width="42" height="12" style="vertical-align: middle; margin-right: 10px;"><line x1="0" y1="6" x2="42" y2="6" stroke="#1976d2" stroke-width="2" stroke-dasharray="5,3"/></svg>
|
|
609
|
-
<strong>uses/implements:</strong> both — implements some of the api-lib's contracts, uses others
|
|
610
|
-
</div>`;
|
|
373
|
+
escapeHtml(text) {
|
|
374
|
+
return text.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"');
|
|
611
375
|
}
|
|
612
376
|
/**
|
|
613
377
|
* The page script. The browser code lives in graph-visualizer.client.ts — real, linted TypeScript
|