@webpieces/nx-webpieces-rules 0.4.920 → 0.4.922
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/package.json +7 -7
- package/src/executors/generate/executor.d.ts +7 -0
- package/src/executors/generate/executor.js +13 -5
- package/src/executors/generate/executor.js.map +1 -1
- package/src/executors/validate-architecture-unchanged/executor.d.ts +1 -20
- package/src/executors/validate-architecture-unchanged/executor.js +2 -45
- package/src/executors/validate-architecture-unchanged/executor.js.map +1 -1
- package/src/executors/visualize/executor.d.ts +8 -1
- package/src/executors/visualize/executor.js +11 -3
- package/src/executors/visualize/executor.js.map +1 -1
- package/src/executors/visualize-runtime/executor.js +2 -1
- package/src/executors/visualize-runtime/executor.js.map +1 -1
- package/src/executors/visualize-runtime-goal/executor.js +2 -1
- package/src/executors/visualize-runtime-goal/executor.js.map +1 -1
- package/src/lib/current-architecture.d.ts +26 -0
- package/src/lib/current-architecture.js +56 -0
- package/src/lib/current-architecture.js.map +1 -0
- package/src/lib/graph-edge-diff.d.ts +115 -0
- package/src/lib/graph-edge-diff.js +263 -0
- package/src/lib/graph-edge-diff.js.map +1 -0
- package/src/lib/graph-impact.d.ts +21 -4
- package/src/lib/graph-impact.js +33 -6
- package/src/lib/graph-impact.js.map +1 -1
- package/src/lib/graph-legend.d.ts +4 -0
- package/src/lib/graph-legend.js +14 -1
- package/src/lib/graph-legend.js.map +1 -1
- package/src/lib/graph-mode-state.client.d.ts +64 -0
- package/src/lib/graph-mode-state.client.js +209 -3
- package/src/lib/graph-mode-state.client.js.map +1 -1
- package/src/lib/graph-navigation.js +4 -2
- package/src/lib/graph-navigation.js.map +1 -1
- package/src/lib/graph-page-shell.d.ts +3 -1
- package/src/lib/graph-page-shell.js +3 -0
- package/src/lib/graph-page-shell.js.map +1 -1
- package/src/lib/graph-visualizer.client.d.ts +3 -2
- package/src/lib/graph-visualizer.client.js +26 -13
- package/src/lib/graph-visualizer.client.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/native-actions.js +1 -1
- package/src/native-actions.js.map +1 -1
|
@@ -142,6 +142,38 @@ class WpModeState {
|
|
|
142
142
|
return [];
|
|
143
143
|
return [...impact.touched, ...impact.dependents, ...impact.implicit, ...impact.rootFile, ...impact.unexplained];
|
|
144
144
|
}
|
|
145
|
+
/** The edges gained and lost since the fork point that THIS page draws: build deps or runtime calls. */
|
|
146
|
+
edgeChanges() {
|
|
147
|
+
const edges = window.__WP_IMPACT__?.edges;
|
|
148
|
+
if (edges === undefined)
|
|
149
|
+
return [];
|
|
150
|
+
return this.viewer === 'runtime' ? edges.runtime : edges.dependency;
|
|
151
|
+
}
|
|
152
|
+
/** '' when this page's edge changes were computed (even if none), else why the page shows none. */
|
|
153
|
+
edgeReason() {
|
|
154
|
+
const edges = window.__WP_IMPACT__?.edges;
|
|
155
|
+
if (edges === undefined)
|
|
156
|
+
return 'No edge changes here. Run pnpm arch:visualize to compute them.';
|
|
157
|
+
return this.viewer === 'runtime' ? edges.runtimeReason : edges.dependencyReason;
|
|
158
|
+
}
|
|
159
|
+
/** Every box at either end of a changed edge: the Filter's "Edge changes only". */
|
|
160
|
+
edgeEnds() {
|
|
161
|
+
const ends = new Set();
|
|
162
|
+
for (const change of this.edgeChanges()) {
|
|
163
|
+
ends.add(change.from);
|
|
164
|
+
ends.add(change.to);
|
|
165
|
+
}
|
|
166
|
+
return ends;
|
|
167
|
+
}
|
|
168
|
+
/** "Edges since abc1234: 1 gained · 1 lost." — or why there are none to show. */
|
|
169
|
+
edgeNote() {
|
|
170
|
+
const reason = this.edgeReason();
|
|
171
|
+
if (reason !== '')
|
|
172
|
+
return `${reason}.`;
|
|
173
|
+
const changes = this.edgeChanges();
|
|
174
|
+
const gained = changes.filter((change) => change.change === 'gained').length;
|
|
175
|
+
return `Edges since ${window.__WP_IMPACT__?.edges.base ?? ''}: ${gained} gained · ${changes.length - gained} lost.`;
|
|
176
|
+
}
|
|
145
177
|
/** Why `id` is in the active scan's build; null when it is not affected. */
|
|
146
178
|
reason(id) {
|
|
147
179
|
return this.impact?.reasons.find((reason) => reason.project === id) ?? null;
|
|
@@ -237,7 +269,7 @@ class ImpactWhyPanel {
|
|
|
237
269
|
text = new WpImpactWhy();
|
|
238
270
|
anchor = null;
|
|
239
271
|
constructor() {
|
|
240
|
-
this.panel.className = 'wp-
|
|
272
|
+
this.panel.className = 'wp-impact-why';
|
|
241
273
|
this.panel.hidden = true;
|
|
242
274
|
this.panel.setAttribute('role', 'dialog');
|
|
243
275
|
this.panel.setAttribute('aria-label', 'Why this project is in the build');
|
|
@@ -255,11 +287,15 @@ class ImpactWhyPanel {
|
|
|
255
287
|
});
|
|
256
288
|
}
|
|
257
289
|
open(node, reason) {
|
|
290
|
+
this.show(node, this.text.title(reason), this.text.lines(reason));
|
|
291
|
+
}
|
|
292
|
+
/** Any heading and lines, anchored under `node` (a box, or a changed edge). */
|
|
293
|
+
show(node, title, lines) {
|
|
258
294
|
this.anchor = node;
|
|
259
295
|
const heading = document.createElement('strong');
|
|
260
|
-
heading.textContent =
|
|
296
|
+
heading.textContent = title;
|
|
261
297
|
const list = document.createElement('ul');
|
|
262
|
-
for (const line of
|
|
298
|
+
for (const line of lines) {
|
|
263
299
|
const item = document.createElement('li');
|
|
264
300
|
item.textContent = line;
|
|
265
301
|
list.append(item);
|
|
@@ -285,4 +321,174 @@ class ImpactWhyPanel {
|
|
|
285
321
|
anchor.focus({ preventScroll: true });
|
|
286
322
|
}
|
|
287
323
|
}
|
|
324
|
+
/** A changed edge the saved page does not draw: added to the Impact overlay. */
|
|
325
|
+
class WpOverlayEdge {
|
|
326
|
+
id;
|
|
327
|
+
from;
|
|
328
|
+
to;
|
|
329
|
+
dot;
|
|
330
|
+
productDot = null;
|
|
331
|
+
constructor(id, from, to, dot) {
|
|
332
|
+
this.id = id;
|
|
333
|
+
this.from = from;
|
|
334
|
+
this.to = to;
|
|
335
|
+
this.dot = dot;
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
/** The page's own model with the overlay's edges: same boxes, levels and frame. */
|
|
339
|
+
class WpOverlayModel {
|
|
340
|
+
edges;
|
|
341
|
+
viewer;
|
|
342
|
+
nodes;
|
|
343
|
+
bands;
|
|
344
|
+
fullDot;
|
|
345
|
+
header;
|
|
346
|
+
footer;
|
|
347
|
+
comparison;
|
|
348
|
+
constructor(base, edges) {
|
|
349
|
+
this.edges = edges;
|
|
350
|
+
this.viewer = base.viewer;
|
|
351
|
+
this.nodes = base.nodes;
|
|
352
|
+
this.bands = base.bands;
|
|
353
|
+
this.fullDot = base.fullDot;
|
|
354
|
+
this.header = base.header;
|
|
355
|
+
this.footer = base.footer;
|
|
356
|
+
this.comparison = base.comparison ?? null;
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
/**
|
|
360
|
+
* The edges this branch gained or lost since its fork point, drawn over the page in Impact mode
|
|
361
|
+
* (#1264). A changed edge the page already draws is restyled; one it does not (the page is the saved
|
|
362
|
+
* snapshot, the diff is against the LIVE graph) is added: a gained edge solid red and bold, a lost one
|
|
363
|
+
* a red dashed ghost. Every changed edge is clickable, and a box whose only change is an edge gets a
|
|
364
|
+
* red badge.
|
|
365
|
+
*/
|
|
366
|
+
class WpEdgeOverlay {
|
|
367
|
+
modes;
|
|
368
|
+
static RED = '#d32f2f';
|
|
369
|
+
static PREFIX = 'wp-edge-change-';
|
|
370
|
+
/** pair key → that pair's changes; null until first asked. */
|
|
371
|
+
pairs = null;
|
|
372
|
+
/** Rendered edge id → the changes it stands for. */
|
|
373
|
+
byEdgeId = new Map();
|
|
374
|
+
overlay = null;
|
|
375
|
+
constructor(modes) {
|
|
376
|
+
this.modes = modes;
|
|
377
|
+
}
|
|
378
|
+
key(from, to) {
|
|
379
|
+
return `${from}\u0000${to}`;
|
|
380
|
+
}
|
|
381
|
+
grouped() {
|
|
382
|
+
if (this.pairs !== null)
|
|
383
|
+
return this.pairs;
|
|
384
|
+
this.pairs = new Map();
|
|
385
|
+
for (const change of this.modes.edgeChanges()) {
|
|
386
|
+
const key = this.key(change.from, change.to);
|
|
387
|
+
this.pairs.set(key, [...(this.pairs.get(key) ?? []), change]);
|
|
388
|
+
}
|
|
389
|
+
return this.pairs;
|
|
390
|
+
}
|
|
391
|
+
any() {
|
|
392
|
+
return this.modes.edgeChanges().length > 0;
|
|
393
|
+
}
|
|
394
|
+
/** `base` plus a ghost statement for every changed edge it does not draw (both ends must be boxes). */
|
|
395
|
+
model(base) {
|
|
396
|
+
if (this.overlay !== null)
|
|
397
|
+
return this.overlay;
|
|
398
|
+
const ids = new Set(base.nodes.map((node) => node.id));
|
|
399
|
+
const drawn = new Set(base.edges.map((edge) => this.key(edge.from, edge.to)));
|
|
400
|
+
const extra = [];
|
|
401
|
+
for (const key of this.grouped().keys()) {
|
|
402
|
+
const changes = this.grouped().get(key);
|
|
403
|
+
const from = changes[0].from;
|
|
404
|
+
const to = changes[0].to;
|
|
405
|
+
if (drawn.has(key) || !ids.has(from) || !ids.has(to))
|
|
406
|
+
continue;
|
|
407
|
+
const id = `${WpEdgeOverlay.PREFIX}${extra.length}`;
|
|
408
|
+
const dot = ` "${this.esc(from)}" -> "${this.esc(to)}" [id="${id}", constraint=false${this.attrs(changes)}];\n`;
|
|
409
|
+
this.byEdgeId.set(id, changes);
|
|
410
|
+
extra.push(new WpOverlayEdge(id, from, to, dot));
|
|
411
|
+
}
|
|
412
|
+
this.overlay = new WpOverlayModel(base, [...base.edges, ...extra]);
|
|
413
|
+
return this.overlay;
|
|
414
|
+
}
|
|
415
|
+
/** A drawn edge's statement restyled when it changed; null when it did not. */
|
|
416
|
+
edgeDot(edge) {
|
|
417
|
+
if (edge.id.startsWith(WpEdgeOverlay.PREFIX))
|
|
418
|
+
return edge.dot;
|
|
419
|
+
const changes = this.grouped().get(this.key(edge.from, edge.to));
|
|
420
|
+
if (changes === undefined)
|
|
421
|
+
return null;
|
|
422
|
+
this.byEdgeId.set(edge.id, changes);
|
|
423
|
+
return `${edge.dot.slice(0, -2)} [${this.attrs(changes).slice(2)}];\n`;
|
|
424
|
+
}
|
|
425
|
+
/** Gained (any API new) is solid, bold red; otherwise a lost edge is a red dashed ghost. */
|
|
426
|
+
attrs(changes) {
|
|
427
|
+
const gained = changes.some((change) => change.change === 'gained');
|
|
428
|
+
const tooltip = this.esc(changes.map((change) => change.text).join('; '));
|
|
429
|
+
return `, color="${WpEdgeOverlay.RED}", fontcolor="${WpEdgeOverlay.RED}", penwidth=${gained ? '3' : '2'}, style="${gained ? 'bold' : 'dashed'}", tooltip="${tooltip}"`;
|
|
430
|
+
}
|
|
431
|
+
esc(text) {
|
|
432
|
+
return text.replace(/\\/g, '\\\\').replace(/"/g, '\\"');
|
|
433
|
+
}
|
|
434
|
+
/** The lines for one box's edge changes ("new on this branch: …", "… no longer calls …"). */
|
|
435
|
+
nodeLines(id) {
|
|
436
|
+
return this.modes
|
|
437
|
+
.edgeChanges()
|
|
438
|
+
.filter((change) => change.from === id || change.to === id)
|
|
439
|
+
.map((change) => change.text);
|
|
440
|
+
}
|
|
441
|
+
/** Every changed edge clickable; a red badge on each box whose ONLY change is an edge. */
|
|
442
|
+
decorate(svg, panel) {
|
|
443
|
+
for (const id of this.byEdgeId.keys()) {
|
|
444
|
+
const edge = svg.querySelector(`g.edge[id="${id}"]`);
|
|
445
|
+
const changes = this.byEdgeId.get(id);
|
|
446
|
+
if (edge === null)
|
|
447
|
+
continue;
|
|
448
|
+
edge.classList.add('wp-edge-change');
|
|
449
|
+
edge.setAttribute('tabindex', '0');
|
|
450
|
+
edge.setAttribute('role', 'button');
|
|
451
|
+
const title = changes.some((change) => change.change === 'gained') ? 'Gained edge' : 'Lost edge';
|
|
452
|
+
const show = (ev) => {
|
|
453
|
+
ev.stopPropagation();
|
|
454
|
+
panel.show(edge, title, changes.map((change) => change.text));
|
|
455
|
+
};
|
|
456
|
+
edge.addEventListener('click', show);
|
|
457
|
+
edge.addEventListener('keydown', (ev) => {
|
|
458
|
+
if (ev.key === 'Enter' || ev.key === ' ')
|
|
459
|
+
show(ev);
|
|
460
|
+
});
|
|
461
|
+
}
|
|
462
|
+
const affected = new Set(this.modes.affected());
|
|
463
|
+
const ends = this.modes.edgeEnds();
|
|
464
|
+
svg.querySelectorAll('g.node').forEach((node) => {
|
|
465
|
+
const id = node.querySelector('title')?.textContent ?? '';
|
|
466
|
+
if (!ends.has(id) || affected.has(id))
|
|
467
|
+
return;
|
|
468
|
+
node.append(this.badge(node));
|
|
469
|
+
});
|
|
470
|
+
}
|
|
471
|
+
badge(node) {
|
|
472
|
+
const ns = 'http://www.w3.org/2000/svg';
|
|
473
|
+
const box = node.getBBox();
|
|
474
|
+
const group = document.createElementNS(ns, 'g');
|
|
475
|
+
group.setAttribute('class', 'wp-edge-badge');
|
|
476
|
+
const circle = document.createElementNS(ns, 'circle');
|
|
477
|
+
circle.setAttribute('cx', String(box.x + box.width - 2));
|
|
478
|
+
circle.setAttribute('cy', String(box.y + 2));
|
|
479
|
+
circle.setAttribute('r', '8');
|
|
480
|
+
circle.setAttribute('fill', WpEdgeOverlay.RED);
|
|
481
|
+
const text = document.createElementNS(ns, 'text');
|
|
482
|
+
text.setAttribute('x', String(box.x + box.width - 2));
|
|
483
|
+
text.setAttribute('y', String(box.y + 6));
|
|
484
|
+
text.setAttribute('text-anchor', 'middle');
|
|
485
|
+
text.setAttribute('font-size', '12');
|
|
486
|
+
text.setAttribute('fill', '#ffffff');
|
|
487
|
+
text.textContent = '±';
|
|
488
|
+
const hint = document.createElementNS(ns, 'title');
|
|
489
|
+
hint.textContent = 'Only its edges changed on this branch';
|
|
490
|
+
group.append(circle, text, hint);
|
|
491
|
+
return group;
|
|
492
|
+
}
|
|
493
|
+
}
|
|
288
494
|
//# sourceMappingURL=graph-mode-state.client.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"graph-mode-state.client.js","sourceRoot":"","sources":["../../../../../../packages/tooling/nx-webpieces-rules/src/lib/graph-mode-state.client.ts"],"names":[],"mappings":"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","sourcesContent":["class WpModeState {\n /** `goal` (Goal vs Current, #1239) exists on runtime pages only; the shell renders it nowhere else. */\n static readonly MODES = ['runtime', 'architecture', 'impact', 'product', 'goal'];\n static readonly NAMES = ['Runtime', 'Architecture', 'Impact', 'Product', 'Goal vs Current'];\n static readonly SUBTITLES = [\n 'where the code can run',\n 'servers · clients · APIs',\n 'what changed',\n 'which products it belongs to',\n 'Current → Goal proposal',\n ];\n readonly scans: ImpactJson[] = window.__WP_IMPACT__?.scans ?? [];\n kind = '';\n mode = 'runtime';\n\n /**\n * @param hasProducts some box belongs to a product, so the Product mode has something to show\n * @param goalUnavailable '' when Goal vs Current can be shown, else why not (#1239)\n */\n constructor(\n private readonly hasProducts: boolean,\n private readonly viewer: string,\n private readonly goalUnavailable: string,\n ) {\n this.kind = this.initialKind();\n this.mode = this.initial();\n }\n\n private storageKey(kind: string): string {\n return `wp-${this.viewer}-graph-${kind}`;\n }\n\n /** '' when Product can be shown, else why not. */\n productReason(): string {\n return this.hasProducts\n ? ''\n : 'No products here: tag each project product:<name> in its project.json, then regenerate.';\n }\n\n /** '' when Goal vs Current can be shown, else why not (a missing or unusable input, never \"no differences\"). */\n goalReason(): string {\n return this.goalUnavailable;\n }\n\n /** The active scan; null when the page has no Impact data. */\n get impact(): ImpactJson | null {\n return this.scans.find((scan: ImpactJson): boolean => scan.kind === this.kind) ?? null;\n }\n\n /** '' when Impact can be shown, else the one line the drawer and the Filter popover say instead. */\n impactReason(): string {\n const impact = this.impact;\n if (\n impact === null &&\n this.viewer === 'runtime' &&\n document.body.dataset['wpProjectFacts'] === 'false'\n )\n return 'No saved project facts for Impact. Run pnpm nx run architecture:generate, then pnpm arch:visualize-runtime.';\n if (impact === null)\n return `No impact data here. Run pnpm arch:visualize${this.viewer === 'runtime' ? '-runtime' : ''} to compute it — no full regenerate needed.`;\n return impact.available ? '' : `Impact unavailable (${impact.label}): ${impact.reason}.`;\n }\n\n /** Switch the active scan; false (and nothing changes) for a missing or unavailable one. */\n setKind(kind: string): boolean {\n if (!this.kindAvailable(kind)) return false;\n this.kind = kind;\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions -- storage is a per-viewer convenience; a blocked store must not break the page\n try {\n localStorage.setItem(this.storageKey('impact-kind'), kind);\n // webpieces-disable no-any-unknown -- browsers may throw any value from blocked storage\n } catch (err: unknown) {\n //const error = toError(err);\n void err;\n }\n return true;\n }\n\n kindAvailable(kind: string): boolean {\n return this.scans.some((scan: ImpactJson): boolean => scan.kind === kind && scan.available);\n }\n\n private initialKind(): string {\n const stored = this.read(this.storageKey('impact-kind'));\n if (stored !== null && this.kindAvailable(stored)) return stored;\n const preferred = window.__WP_IMPACT__?.defaultKind ?? '';\n if (this.scans.some((scan: ImpactJson): boolean => scan.kind === preferred))\n return preferred;\n return this.scans[0]?.kind ?? '';\n }\n\n available(mode: string): boolean {\n if (!WpModeState.MODES.includes(mode)) return false;\n if (mode === 'impact') return this.impactReason() === '';\n if (mode === 'product') return this.productReason() === '';\n if (mode === 'goal') return this.goalReason() === '';\n return true;\n }\n\n name(mode: string): string {\n return WpModeState.NAMES[WpModeState.MODES.indexOf(mode)] ?? mode;\n }\n\n subtitle(mode: string): string {\n return WpModeState.SUBTITLES[WpModeState.MODES.indexOf(mode)] ?? '';\n }\n\n /** False (and nothing changes) for an unknown or unavailable mode. */\n set(mode: string): boolean {\n if (!this.available(mode)) return false;\n this.mode = mode;\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions -- storage is a per-viewer convenience; a blocked store must not break the page\n try {\n localStorage.setItem(this.storageKey('mode'), mode);\n if (location.hash !== `#${mode}`) history.replaceState(null, '', `#${mode}`);\n // webpieces-disable no-any-unknown -- browsers may throw any value from blocked storage\n } catch (err: unknown) {\n //const error = toError(err);\n void err;\n }\n return true;\n }\n\n /** Which impact shade a box gets, from the ACTIVE scan: WHY nx included it (#1264). */\n status(id: string): 'touched' | 'dependent' | 'implicit' | 'rootFile' | 'unexplained' | 'buildInput' | 'untouched' {\n const impact = this.impact;\n if (impact?.touched.includes(id)) return 'touched';\n if (impact?.dependents.includes(id)) return 'dependent';\n if (impact?.implicit.includes(id)) return 'implicit';\n if (impact?.rootFile.includes(id)) return 'rootFile';\n if (impact?.unexplained.includes(id)) return 'unexplained';\n if (impact?.buildInputs.includes(id)) return 'buildInput';\n return 'untouched';\n }\n\n /** Every affected node id of the active scan, whatever its reason. */\n affected(): string[] {\n const impact = this.impact;\n if (impact === null) return [];\n return [...impact.touched, ...impact.dependents, ...impact.implicit, ...impact.rootFile, ...impact.unexplained];\n }\n\n /** Why `id` is in the active scan's build; null when it is not affected. */\n reason(id: string): AffectedReasonJson | null {\n return this.impact?.reasons.find((reason: AffectedReasonJson): boolean => reason.project === id) ?? null;\n }\n\n private initial(): string {\n const hashed = location.hash.slice(1);\n if (this.available(hashed)) return hashed;\n const stored = this.read(this.storageKey('mode'));\n if (stored !== null && this.available(stored)) return stored;\n return 'runtime';\n }\n\n private read(key: string): string | null {\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions -- storage is a per-viewer convenience; a blocked store reads as \"nothing stored\"\n try {\n return localStorage.getItem(key);\n // webpieces-disable no-any-unknown -- browsers may throw any value from blocked storage\n } catch (err: unknown) {\n //const error = toError(err);\n void err;\n return null;\n }\n }\n}\n\n/**\n * What the \"why\" panel says about one affected project (#1264): never a reason the graph cannot show.\n * Pure text, so a spec reads it without a browser; GraphPage puts it in a panel.\n */\nclass WpImpactWhy {\n static readonly TITLES: Record<string, string> = {\n touched: 'Changed',\n dependent: 'Dependent',\n implicit: 'Implicitly touched',\n rootFile: 'Root-file touched',\n unexplained: 'Unexplained',\n };\n\n /**\n * `8 changed · 3 dependents · 22 implicitly touched (runtime-wiring-check inputs) · 47 dependencies`\n * — the same split ImpactCounts (graph-impact.ts) prints on the console.\n */\n counts(impact: ImpactJson): string {\n const parts = [`${impact.touched.length} changed`, `${impact.dependents.length} dependents`];\n if (impact.implicit.length > 0) {\n const targets = [\n ...new Set(\n impact.reasons\n .filter((reason: AffectedReasonJson): boolean => reason.kind === 'implicit' && reason.target !== '')\n .map((reason: AffectedReasonJson): string => reason.target),\n ),\n ].sort();\n parts.push(`${impact.implicit.length} implicitly touched${targets.length === 0 ? '' : ` (${targets.join(', ')} inputs)`}`);\n }\n if (impact.rootFile.length > 0) parts.push(`${impact.rootFile.length} root-file touched`);\n if (impact.unexplained.length > 0) parts.push(`${impact.unexplained.length} unexplained`);\n parts.push(`${impact.buildInputs.length} dependencies`);\n return parts.join(' · ');\n }\n\n title(reason: AffectedReasonJson): string {\n return `${WpImpactWhy.TITLES[reason.kind] ?? reason.kind}: ${reason.project}`;\n }\n\n lines(reason: AffectedReasonJson): string[] {\n if (reason.kind === 'touched') {\n const owns = reason.files.length === 1 ? 'this changed file' : `these ${reason.files.length} changed files`;\n return [`It owns ${owns}:`, ...reason.files];\n }\n if (reason.kind === 'dependent')\n return [\n 'It uses a changed or touched project through these dependency edges, so its tests and build re-run:',\n reason.path.join(' -> '),\n ];\n if (reason.kind === 'implicit')\n return [\n reason.target === '' ? reason.detail : `${reason.target} input ${reason.input} matched ${reason.files.join(', ')}`,\n 'nx marks every project whose target input glob matches a changed file. It uses no changed project.',\n ];\n if (reason.kind === 'rootFile')\n return [\n reason.detail,\n ...(reason.path.length > 0 ? [reason.path.join(' -> ')] : []),\n 'nx marked it for a workspace root file; it uses no changed project.',\n ];\n return [reason.detail];\n }\n}\n\n/**\n * The \"why is this in the build?\" panel (#1264): opened from an affected box's menu in Impact mode.\n * Text nodes only, so no project name is parsed as HTML.\n */\nclass ImpactWhyPanel {\n private readonly panel = document.createElement('div');\n private readonly text = new WpImpactWhy();\n private anchor: SVGGElement | null = null;\n\n constructor() {\n this.panel.className = 'wp-graph-details wp-impact-why';\n this.panel.hidden = true;\n this.panel.setAttribute('role', 'dialog');\n this.panel.setAttribute('aria-label', 'Why this project is in the build');\n document.body.append(this.panel);\n document.addEventListener('keydown', (ev: KeyboardEvent) => {\n if (ev.key !== 'Escape' || this.panel.hidden) return;\n ev.stopPropagation();\n this.close();\n }, true);\n document.addEventListener('click', (ev: MouseEvent) => {\n const target = ev.target;\n if (target instanceof Node && !this.panel.contains(target) && !this.anchor?.contains(target)) this.close();\n });\n }\n\n open(node: SVGGElement, reason: AffectedReasonJson): void {\n this.anchor = node;\n const heading = document.createElement('strong');\n heading.textContent = this.text.title(reason);\n const list = document.createElement('ul');\n for (const line of this.text.lines(reason)) {\n const item = document.createElement('li');\n item.textContent = line;\n list.append(item);\n }\n const close = document.createElement('button');\n close.type = 'button';\n close.textContent = 'Close';\n close.addEventListener('click', () => this.close());\n this.panel.replaceChildren(heading, list, close);\n this.panel.hidden = false;\n const box = node.getBoundingClientRect();\n this.panel.style.left = `${Math.max(12, Math.min(box.left, innerWidth - this.panel.offsetWidth - 12))}px`;\n this.panel.style.top = `${Math.max(12, Math.min(box.bottom + 6, innerHeight - this.panel.offsetHeight - 12))}px`;\n close.focus({ preventScroll: true });\n }\n\n close(): void {\n if (this.panel.hidden) return;\n this.panel.hidden = true;\n const anchor = this.anchor;\n this.anchor = null;\n if (anchor?.isConnected) anchor.focus({ preventScroll: true });\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"graph-mode-state.client.js","sourceRoot":"","sources":["../../../../../../packages/tooling/nx-webpieces-rules/src/lib/graph-mode-state.client.ts"],"names":[],"mappings":"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","sourcesContent":["class WpModeState {\n /** `goal` (Goal vs Current, #1239) exists on runtime pages only; the shell renders it nowhere else. */\n static readonly MODES = ['runtime', 'architecture', 'impact', 'product', 'goal'];\n static readonly NAMES = ['Runtime', 'Architecture', 'Impact', 'Product', 'Goal vs Current'];\n static readonly SUBTITLES = [\n 'where the code can run',\n 'servers · clients · APIs',\n 'what changed',\n 'which products it belongs to',\n 'Current → Goal proposal',\n ];\n readonly scans: ImpactJson[] = window.__WP_IMPACT__?.scans ?? [];\n kind = '';\n mode = 'runtime';\n\n /**\n * @param hasProducts some box belongs to a product, so the Product mode has something to show\n * @param goalUnavailable '' when Goal vs Current can be shown, else why not (#1239)\n */\n constructor(\n private readonly hasProducts: boolean,\n private readonly viewer: string,\n private readonly goalUnavailable: string,\n ) {\n this.kind = this.initialKind();\n this.mode = this.initial();\n }\n\n private storageKey(kind: string): string {\n return `wp-${this.viewer}-graph-${kind}`;\n }\n\n /** '' when Product can be shown, else why not. */\n productReason(): string {\n return this.hasProducts\n ? ''\n : 'No products here: tag each project product:<name> in its project.json, then regenerate.';\n }\n\n /** '' when Goal vs Current can be shown, else why not (a missing or unusable input, never \"no differences\"). */\n goalReason(): string {\n return this.goalUnavailable;\n }\n\n /** The active scan; null when the page has no Impact data. */\n get impact(): ImpactJson | null {\n return this.scans.find((scan: ImpactJson): boolean => scan.kind === this.kind) ?? null;\n }\n\n /** '' when Impact can be shown, else the one line the drawer and the Filter popover say instead. */\n impactReason(): string {\n const impact = this.impact;\n if (\n impact === null &&\n this.viewer === 'runtime' &&\n document.body.dataset['wpProjectFacts'] === 'false'\n )\n return 'No saved project facts for Impact. Run pnpm nx run architecture:generate, then pnpm arch:visualize-runtime.';\n if (impact === null)\n return `No impact data here. Run pnpm arch:visualize${this.viewer === 'runtime' ? '-runtime' : ''} to compute it — no full regenerate needed.`;\n return impact.available ? '' : `Impact unavailable (${impact.label}): ${impact.reason}.`;\n }\n\n /** Switch the active scan; false (and nothing changes) for a missing or unavailable one. */\n setKind(kind: string): boolean {\n if (!this.kindAvailable(kind)) return false;\n this.kind = kind;\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions -- storage is a per-viewer convenience; a blocked store must not break the page\n try {\n localStorage.setItem(this.storageKey('impact-kind'), kind);\n // webpieces-disable no-any-unknown -- browsers may throw any value from blocked storage\n } catch (err: unknown) {\n //const error = toError(err);\n void err;\n }\n return true;\n }\n\n kindAvailable(kind: string): boolean {\n return this.scans.some((scan: ImpactJson): boolean => scan.kind === kind && scan.available);\n }\n\n private initialKind(): string {\n const stored = this.read(this.storageKey('impact-kind'));\n if (stored !== null && this.kindAvailable(stored)) return stored;\n const preferred = window.__WP_IMPACT__?.defaultKind ?? '';\n if (this.scans.some((scan: ImpactJson): boolean => scan.kind === preferred))\n return preferred;\n return this.scans[0]?.kind ?? '';\n }\n\n available(mode: string): boolean {\n if (!WpModeState.MODES.includes(mode)) return false;\n if (mode === 'impact') return this.impactReason() === '';\n if (mode === 'product') return this.productReason() === '';\n if (mode === 'goal') return this.goalReason() === '';\n return true;\n }\n\n name(mode: string): string {\n return WpModeState.NAMES[WpModeState.MODES.indexOf(mode)] ?? mode;\n }\n\n subtitle(mode: string): string {\n return WpModeState.SUBTITLES[WpModeState.MODES.indexOf(mode)] ?? '';\n }\n\n /** False (and nothing changes) for an unknown or unavailable mode. */\n set(mode: string): boolean {\n if (!this.available(mode)) return false;\n this.mode = mode;\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions -- storage is a per-viewer convenience; a blocked store must not break the page\n try {\n localStorage.setItem(this.storageKey('mode'), mode);\n if (location.hash !== `#${mode}`) history.replaceState(null, '', `#${mode}`);\n // webpieces-disable no-any-unknown -- browsers may throw any value from blocked storage\n } catch (err: unknown) {\n //const error = toError(err);\n void err;\n }\n return true;\n }\n\n /** Which impact shade a box gets, from the ACTIVE scan: WHY nx included it (#1264). */\n status(id: string): 'touched' | 'dependent' | 'implicit' | 'rootFile' | 'unexplained' | 'buildInput' | 'untouched' {\n const impact = this.impact;\n if (impact?.touched.includes(id)) return 'touched';\n if (impact?.dependents.includes(id)) return 'dependent';\n if (impact?.implicit.includes(id)) return 'implicit';\n if (impact?.rootFile.includes(id)) return 'rootFile';\n if (impact?.unexplained.includes(id)) return 'unexplained';\n if (impact?.buildInputs.includes(id)) return 'buildInput';\n return 'untouched';\n }\n\n /** Every affected node id of the active scan, whatever its reason. */\n affected(): string[] {\n const impact = this.impact;\n if (impact === null) return [];\n return [...impact.touched, ...impact.dependents, ...impact.implicit, ...impact.rootFile, ...impact.unexplained];\n }\n\n /** The edges gained and lost since the fork point that THIS page draws: build deps or runtime calls. */\n edgeChanges(): EdgeChangeJson[] {\n const edges = window.__WP_IMPACT__?.edges;\n if (edges === undefined) return [];\n return this.viewer === 'runtime' ? edges.runtime : edges.dependency;\n }\n\n /** '' when this page's edge changes were computed (even if none), else why the page shows none. */\n edgeReason(): string {\n const edges = window.__WP_IMPACT__?.edges;\n if (edges === undefined) return 'No edge changes here. Run pnpm arch:visualize to compute them.';\n return this.viewer === 'runtime' ? edges.runtimeReason : edges.dependencyReason;\n }\n\n /** Every box at either end of a changed edge: the Filter's \"Edge changes only\". */\n edgeEnds(): Set<string> {\n const ends = new Set<string>();\n for (const change of this.edgeChanges()) {\n ends.add(change.from);\n ends.add(change.to);\n }\n return ends;\n }\n\n /** \"Edges since abc1234: 1 gained · 1 lost.\" — or why there are none to show. */\n edgeNote(): string {\n const reason = this.edgeReason();\n if (reason !== '') return `${reason}.`;\n const changes = this.edgeChanges();\n const gained = changes.filter((change: EdgeChangeJson): boolean => change.change === 'gained').length;\n return `Edges since ${window.__WP_IMPACT__?.edges.base ?? ''}: ${gained} gained · ${changes.length - gained} lost.`;\n }\n\n /** Why `id` is in the active scan's build; null when it is not affected. */\n reason(id: string): AffectedReasonJson | null {\n return this.impact?.reasons.find((reason: AffectedReasonJson): boolean => reason.project === id) ?? null;\n }\n\n private initial(): string {\n const hashed = location.hash.slice(1);\n if (this.available(hashed)) return hashed;\n const stored = this.read(this.storageKey('mode'));\n if (stored !== null && this.available(stored)) return stored;\n return 'runtime';\n }\n\n private read(key: string): string | null {\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions -- storage is a per-viewer convenience; a blocked store reads as \"nothing stored\"\n try {\n return localStorage.getItem(key);\n // webpieces-disable no-any-unknown -- browsers may throw any value from blocked storage\n } catch (err: unknown) {\n //const error = toError(err);\n void err;\n return null;\n }\n }\n}\n\n/**\n * What the \"why\" panel says about one affected project (#1264): never a reason the graph cannot show.\n * Pure text, so a spec reads it without a browser; GraphPage puts it in a panel.\n */\nclass WpImpactWhy {\n static readonly TITLES: Record<string, string> = {\n touched: 'Changed',\n dependent: 'Dependent',\n implicit: 'Implicitly touched',\n rootFile: 'Root-file touched',\n unexplained: 'Unexplained',\n };\n\n /**\n * `8 changed · 3 dependents · 22 implicitly touched (runtime-wiring-check inputs) · 47 dependencies`\n * — the same split ImpactCounts (graph-impact.ts) prints on the console.\n */\n counts(impact: ImpactJson): string {\n const parts = [`${impact.touched.length} changed`, `${impact.dependents.length} dependents`];\n if (impact.implicit.length > 0) {\n const targets = [\n ...new Set(\n impact.reasons\n .filter((reason: AffectedReasonJson): boolean => reason.kind === 'implicit' && reason.target !== '')\n .map((reason: AffectedReasonJson): string => reason.target),\n ),\n ].sort();\n parts.push(`${impact.implicit.length} implicitly touched${targets.length === 0 ? '' : ` (${targets.join(', ')} inputs)`}`);\n }\n if (impact.rootFile.length > 0) parts.push(`${impact.rootFile.length} root-file touched`);\n if (impact.unexplained.length > 0) parts.push(`${impact.unexplained.length} unexplained`);\n parts.push(`${impact.buildInputs.length} dependencies`);\n return parts.join(' · ');\n }\n\n title(reason: AffectedReasonJson): string {\n return `${WpImpactWhy.TITLES[reason.kind] ?? reason.kind}: ${reason.project}`;\n }\n\n lines(reason: AffectedReasonJson): string[] {\n if (reason.kind === 'touched') {\n const owns = reason.files.length === 1 ? 'this changed file' : `these ${reason.files.length} changed files`;\n return [`It owns ${owns}:`, ...reason.files];\n }\n if (reason.kind === 'dependent')\n return [\n 'It uses a changed or touched project through these dependency edges, so its tests and build re-run:',\n reason.path.join(' -> '),\n ];\n if (reason.kind === 'implicit')\n return [\n reason.target === '' ? reason.detail : `${reason.target} input ${reason.input} matched ${reason.files.join(', ')}`,\n 'nx marks every project whose target input glob matches a changed file. It uses no changed project.',\n ];\n if (reason.kind === 'rootFile')\n return [\n reason.detail,\n ...(reason.path.length > 0 ? [reason.path.join(' -> ')] : []),\n 'nx marked it for a workspace root file; it uses no changed project.',\n ];\n return [reason.detail];\n }\n}\n\n/**\n * The \"why is this in the build?\" panel (#1264): opened from an affected box's menu in Impact mode.\n * Text nodes only, so no project name is parsed as HTML.\n */\nclass ImpactWhyPanel {\n private readonly panel = document.createElement('div');\n private readonly text = new WpImpactWhy();\n private anchor: SVGElement | null = null;\n\n constructor() {\n this.panel.className = 'wp-impact-why';\n this.panel.hidden = true;\n this.panel.setAttribute('role', 'dialog');\n this.panel.setAttribute('aria-label', 'Why this project is in the build');\n document.body.append(this.panel);\n document.addEventListener('keydown', (ev: KeyboardEvent) => {\n if (ev.key !== 'Escape' || this.panel.hidden) return;\n ev.stopPropagation();\n this.close();\n }, true);\n document.addEventListener('click', (ev: MouseEvent) => {\n const target = ev.target;\n if (target instanceof Node && !this.panel.contains(target) && !this.anchor?.contains(target)) this.close();\n });\n }\n\n open(node: SVGGElement, reason: AffectedReasonJson): void {\n this.show(node, this.text.title(reason), this.text.lines(reason));\n }\n\n /** Any heading and lines, anchored under `node` (a box, or a changed edge). */\n show(node: SVGElement, title: string, lines: string[]): void {\n this.anchor = node;\n const heading = document.createElement('strong');\n heading.textContent = title;\n const list = document.createElement('ul');\n for (const line of lines) {\n const item = document.createElement('li');\n item.textContent = line;\n list.append(item);\n }\n const close = document.createElement('button');\n close.type = 'button';\n close.textContent = 'Close';\n close.addEventListener('click', () => this.close());\n this.panel.replaceChildren(heading, list, close);\n this.panel.hidden = false;\n const box = node.getBoundingClientRect();\n this.panel.style.left = `${Math.max(12, Math.min(box.left, innerWidth - this.panel.offsetWidth - 12))}px`;\n this.panel.style.top = `${Math.max(12, Math.min(box.bottom + 6, innerHeight - this.panel.offsetHeight - 12))}px`;\n close.focus({ preventScroll: true });\n }\n\n close(): void {\n if (this.panel.hidden) return;\n this.panel.hidden = true;\n const anchor = this.anchor;\n this.anchor = null;\n if (anchor?.isConnected) anchor.focus({ preventScroll: true });\n }\n}\n\n/** A changed edge the saved page does not draw: added to the Impact overlay. */\nclass WpOverlayEdge implements RenderEdgeJson {\n readonly productDot: string | null = null;\n\n constructor(\n readonly id: string,\n readonly from: string,\n readonly to: string,\n readonly dot: string,\n ) {}\n}\n\n/** The page's own model with the overlay's edges: same boxes, levels and frame. */\nclass WpOverlayModel implements RenderModelJson {\n readonly viewer: 'architecture' | 'runtime';\n readonly nodes: RenderNodeJson[];\n readonly bands: RenderBandJson[];\n readonly fullDot: string;\n readonly header: string;\n readonly footer: string;\n readonly comparison: RuntimeComparisonJson | null;\n\n constructor(\n base: RenderModelJson,\n readonly edges: RenderEdgeJson[],\n ) {\n this.viewer = base.viewer;\n this.nodes = base.nodes;\n this.bands = base.bands;\n this.fullDot = base.fullDot;\n this.header = base.header;\n this.footer = base.footer;\n this.comparison = base.comparison ?? null;\n }\n}\n\n/**\n * The edges this branch gained or lost since its fork point, drawn over the page in Impact mode\n * (#1264). A changed edge the page already draws is restyled; one it does not (the page is the saved\n * snapshot, the diff is against the LIVE graph) is added: a gained edge solid red and bold, a lost one\n * a red dashed ghost. Every changed edge is clickable, and a box whose only change is an edge gets a\n * red badge.\n */\nclass WpEdgeOverlay {\n static readonly RED = '#d32f2f';\n static readonly PREFIX = 'wp-edge-change-';\n /** pair key → that pair's changes; null until first asked. */\n private pairs: Map<string, EdgeChangeJson[]> | null = null;\n /** Rendered edge id → the changes it stands for. */\n private readonly byEdgeId = new Map<string, EdgeChangeJson[]>();\n private overlay: RenderModelJson | null = null;\n\n constructor(private readonly modes: WpModeState) {}\n\n private key(from: string, to: string): string {\n return `${from}\\u0000${to}`;\n }\n\n private grouped(): Map<string, EdgeChangeJson[]> {\n if (this.pairs !== null) return this.pairs;\n this.pairs = new Map<string, EdgeChangeJson[]>();\n for (const change of this.modes.edgeChanges()) {\n const key = this.key(change.from, change.to);\n this.pairs.set(key, [...(this.pairs.get(key) ?? []), change]);\n }\n return this.pairs;\n }\n\n any(): boolean {\n return this.modes.edgeChanges().length > 0;\n }\n\n /** `base` plus a ghost statement for every changed edge it does not draw (both ends must be boxes). */\n model(base: RenderModelJson): RenderModelJson {\n if (this.overlay !== null) return this.overlay;\n const ids = new Set(base.nodes.map((node: RenderNodeJson): string => node.id));\n const drawn = new Set(base.edges.map((edge: RenderEdgeJson): string => this.key(edge.from, edge.to)));\n const extra: RenderEdgeJson[] = [];\n for (const key of this.grouped().keys()) {\n const changes = this.grouped().get(key) as EdgeChangeJson[];\n const from = changes[0].from;\n const to = changes[0].to;\n if (drawn.has(key) || !ids.has(from) || !ids.has(to)) continue;\n const id = `${WpEdgeOverlay.PREFIX}${extra.length}`;\n const dot = ` \"${this.esc(from)}\" -> \"${this.esc(to)}\" [id=\"${id}\", constraint=false${this.attrs(changes)}];\\n`;\n this.byEdgeId.set(id, changes);\n extra.push(new WpOverlayEdge(id, from, to, dot));\n }\n this.overlay = new WpOverlayModel(base, [...base.edges, ...extra]);\n return this.overlay;\n }\n\n /** A drawn edge's statement restyled when it changed; null when it did not. */\n edgeDot(edge: RenderEdgeJson): string | null {\n if (edge.id.startsWith(WpEdgeOverlay.PREFIX)) return edge.dot;\n const changes = this.grouped().get(this.key(edge.from, edge.to));\n if (changes === undefined) return null;\n this.byEdgeId.set(edge.id, changes);\n return `${edge.dot.slice(0, -2)} [${this.attrs(changes).slice(2)}];\\n`;\n }\n\n /** Gained (any API new) is solid, bold red; otherwise a lost edge is a red dashed ghost. */\n private attrs(changes: EdgeChangeJson[]): string {\n const gained = changes.some((change: EdgeChangeJson): boolean => change.change === 'gained');\n const tooltip = this.esc(changes.map((change: EdgeChangeJson): string => change.text).join('; '));\n return `, color=\"${WpEdgeOverlay.RED}\", fontcolor=\"${WpEdgeOverlay.RED}\", penwidth=${gained ? '3' : '2'}, style=\"${gained ? 'bold' : 'dashed'}\", tooltip=\"${tooltip}\"`;\n }\n\n private esc(text: string): string {\n return text.replace(/\\\\/g, '\\\\\\\\').replace(/\"/g, '\\\\\"');\n }\n\n /** The lines for one box's edge changes (\"new on this branch: …\", \"… no longer calls …\"). */\n nodeLines(id: string): string[] {\n return this.modes\n .edgeChanges()\n .filter((change: EdgeChangeJson): boolean => change.from === id || change.to === id)\n .map((change: EdgeChangeJson): string => change.text);\n }\n\n /** Every changed edge clickable; a red badge on each box whose ONLY change is an edge. */\n decorate(svg: SVGSVGElement, panel: ImpactWhyPanel): void {\n for (const id of this.byEdgeId.keys()) {\n const edge = svg.querySelector<SVGGElement>(`g.edge[id=\"${id}\"]`);\n const changes = this.byEdgeId.get(id) as EdgeChangeJson[];\n if (edge === null) continue;\n edge.classList.add('wp-edge-change');\n edge.setAttribute('tabindex', '0');\n edge.setAttribute('role', 'button');\n const title = changes.some((change: EdgeChangeJson): boolean => change.change === 'gained') ? 'Gained edge' : 'Lost edge';\n const show = (ev: Event): void => {\n ev.stopPropagation();\n panel.show(edge, title, changes.map((change: EdgeChangeJson): string => change.text));\n };\n edge.addEventListener('click', show);\n edge.addEventListener('keydown', (ev: KeyboardEvent) => {\n if (ev.key === 'Enter' || ev.key === ' ') show(ev);\n });\n }\n const affected = new Set(this.modes.affected());\n const ends = this.modes.edgeEnds();\n svg.querySelectorAll<SVGGElement>('g.node').forEach((node: SVGGElement) => {\n const id = node.querySelector('title')?.textContent ?? '';\n if (!ends.has(id) || affected.has(id)) return;\n node.append(this.badge(node));\n });\n }\n\n private badge(node: SVGGElement): SVGGElement {\n const ns = 'http://www.w3.org/2000/svg';\n const box = node.getBBox();\n const group = document.createElementNS(ns, 'g');\n group.setAttribute('class', 'wp-edge-badge');\n const circle = document.createElementNS(ns, 'circle');\n circle.setAttribute('cx', String(box.x + box.width - 2));\n circle.setAttribute('cy', String(box.y + 2));\n circle.setAttribute('r', '8');\n circle.setAttribute('fill', WpEdgeOverlay.RED);\n const text = document.createElementNS(ns, 'text');\n text.setAttribute('x', String(box.x + box.width - 2));\n text.setAttribute('y', String(box.y + 6));\n text.setAttribute('text-anchor', 'middle');\n text.setAttribute('font-size', '12');\n text.setAttribute('fill', '#ffffff');\n text.textContent = '±';\n const hint = document.createElementNS(ns, 'title');\n hint.textContent = 'Only its edges changed on this branch';\n group.append(circle, text, hint);\n return group;\n }\n}\n"]}
|
|
@@ -26,9 +26,11 @@ class GraphNavigation {
|
|
|
26
26
|
.wp-navigation.wp-zoom button { min-width: 34px; height: 34px; padding: 0 8px; border-radius: 8px; border: 1px solid #d9dce5; background: #fff; color: #1b1e27; font-size: 15px; }
|
|
27
27
|
.wp-navigation.wp-zoom button:hover { border-color: #3559d6; }
|
|
28
28
|
.wp-navigation.wp-zoom output { min-width: 42px; padding: 2px 6px; border-radius: 6px; background: #ffffffd9; color: #5d6475; font-size: 12px; text-align: center; }
|
|
29
|
-
.wp-graph-details { position: fixed; z-index: 1100; box-sizing: border-box; max-width: min(520px, calc(100vw - 24px)); max-height: min(480px, calc(100vh - 24px)); overflow: auto; background: white; padding: 12px; border: 1px solid #aaa; border-radius: 6px; box-shadow: 0 4px 16px #0003; font: 14px/1.5 Arial, sans-serif; overflow-wrap: anywhere; }
|
|
30
|
-
.wp-graph-details li { margin-bottom: 8px; }
|
|
29
|
+
.wp-graph-details, .wp-impact-why { position: fixed; z-index: 1100; box-sizing: border-box; max-width: min(520px, calc(100vw - 24px)); max-height: min(480px, calc(100vh - 24px)); overflow: auto; background: white; padding: 12px; border: 1px solid #aaa; border-radius: 6px; box-shadow: 0 4px 16px #0003; font: 14px/1.5 Arial, sans-serif; overflow-wrap: anywhere; }
|
|
30
|
+
.wp-graph-details li, .wp-impact-why li { margin-bottom: 8px; }
|
|
31
31
|
.wp-api-detail { cursor: pointer; text-decoration: underline; }
|
|
32
|
+
#graph g.edge.wp-edge-change { cursor: pointer; }
|
|
33
|
+
#graph g.edge.wp-edge-change:focus { outline: 2px dashed #d32f2f; }
|
|
32
34
|
.wp-api-detail:focus { outline: 2px solid #1565c0; }
|
|
33
35
|
`;
|
|
34
36
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"graph-navigation.js","sourceRoot":"","sources":["../../../../../../packages/tooling/nx-webpieces-rules/src/lib/graph-navigation.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Where the zoom controls sit. The runtime page keeps its toolbar ABOVE the graph; the architecture\n * page gives the graph the whole viewport and floats a compact `− 1:1 ⤢ +` cluster bottom-right.\n */\nexport enum NavigationLayout {\n TOOLBAR = 'toolbar',\n FLOATING = 'floating',\n}\n\n/** Shared local-file navigation. Reset restores readable scale; Fit is an explicit overview. */\nexport class GraphNavigation {\n constructor(private readonly layout: NavigationLayout) {}\n\n styles(): string {\n return `\n #graph { overflow: auto; max-height: 75vh; text-align: left; touch-action: pan-x pan-y; }\n #graph svg { max-width: none; max-height: none; display: block; }\n .wp-navigation { display: flex; gap: 8px; align-items: center; margin: 10px 0; }\n .wp-navigation button { padding: 6px 12px; cursor: pointer; }\n .wp-navigation.wp-zoom { position: absolute; right: 14px; bottom: 14px; margin: 0; gap: 6px; z-index: 20; }\n .wp-navigation.wp-zoom button { min-width: 34px; height: 34px; padding: 0 8px; border-radius: 8px; border: 1px solid #d9dce5; background: #fff; color: #1b1e27; font-size: 15px; }\n .wp-navigation.wp-zoom button:hover { border-color: #3559d6; }\n .wp-navigation.wp-zoom output { min-width: 42px; padding: 2px 6px; border-radius: 6px; background: #ffffffd9; color: #5d6475; font-size: 12px; text-align: center; }\n .wp-graph-details { position: fixed; z-index: 1100; box-sizing: border-box; max-width: min(520px, calc(100vw - 24px)); max-height: min(480px, calc(100vh - 24px)); overflow: auto; background: white; padding: 12px; border: 1px solid #aaa; border-radius: 6px; box-shadow: 0 4px 16px #0003; font: 14px/1.5 Arial, sans-serif; overflow-wrap: anywhere; }\n .wp-graph-details li { margin-bottom: 8px; }\n .wp-api-detail { cursor: pointer; text-decoration: underline; }\n .wp-api-detail:focus { outline: 2px solid #1565c0; }\n `;\n }\n\n script(): string {\n const floating = this.layout === NavigationLayout.FLOATING;\n return `new (${GraphNavigationController.toString()})(${JSON.stringify(floating)});`;\n }\n}\n\n/** Serialized browser controller; dependencies are native DOM APIs only. */\nclass GraphNavigationController {\n private readonly host = document.getElementById('graph');\n private readonly toolbar = document.createElement('nav');\n private readonly status = document.createElement('output');\n private svg: SVGSVGElement | null = null;\n private scale = 1;\n\n constructor(floating: boolean) {\n if (this.host === null) return;\n this.toolbar.className = floating ? 'wp-navigation wp-zoom' : 'wp-navigation';\n this.toolbar.setAttribute('aria-label', 'Graph navigation');\n if (floating) this.floatingButtons();\n else this.toolbarButtons();\n new MutationObserver(() => this.attach()).observe(this.host, { childList: true });\n this.attach();\n window.addEventListener('resize', () => this.changed());\n this.host.addEventListener('scroll', () => this.changed());\n }\n\n private toolbarButtons(): void {\n this.host?.before(this.toolbar);\n this.button('Zoom in', 'Zoom in', () => this.zoom(1.25));\n this.button('Zoom out', 'Zoom out', () => this.zoom(0.8));\n this.button('Readable reset', 'Readable reset', () => this.reset());\n this.button('Fit overview', 'Fit overview', () => this.fit());\n this.toolbar.append(this.status);\n }\n\n /** Placed AFTER the graph in the DOM, so it never sits ahead of the boxes in the tab order. */\n private floatingButtons(): void {\n this.host?.after(this.toolbar);\n this.toolbar.append(this.status);\n this.button('−', 'Zoom out', () => this.zoom(0.8));\n this.button('1:1', 'Readable reset', () => this.reset());\n this.button('⤢', 'Fit overview', () => this.fit());\n this.button('+', 'Zoom in', () => this.zoom(1.25));\n }\n\n private button(text: string, label: string, action: () => void): void {\n const button = document.createElement('button');\n button.type = 'button';\n button.textContent = text;\n button.title = label;\n button.setAttribute('aria-label', label);\n button.addEventListener('click', action);\n this.toolbar.append(button);\n }\n\n private changed(): void {\n document.dispatchEvent(new Event('wp-graph-transform'));\n }\n\n private resize(): void {\n if (this.svg === null) return;\n const box = this.svg.viewBox.baseVal;\n this.svg.style.width = `${box.width * this.scale}px`;\n this.svg.style.height = `${box.height * this.scale}px`;\n this.status.textContent = `${Math.round(this.scale * 100)}%`;\n this.changed();\n }\n\n private zoom(factor: number): void {\n if (this.host === null) return;\n const before = this.scale;\n this.scale = Math.max(0.1, Math.min(4, this.scale * factor));\n const centerX = this.host.scrollLeft + this.host.clientWidth / 2;\n const centerY = this.host.scrollTop + this.host.clientHeight / 2;\n this.resize();\n this.host.scrollLeft = (centerX * this.scale) / before - this.host.clientWidth / 2;\n this.host.scrollTop = (centerY * this.scale) / before - this.host.clientHeight / 2;\n }\n\n private reset(): void {\n this.scale = 1;\n this.resize();\n this.scrollOrigin();\n }\n\n private scrollOrigin(): void {\n if (this.host === null) return;\n this.host.scrollLeft = 0;\n this.host.scrollTop = 0;\n }\n\n private fit(): void {\n if (this.svg === null || this.host === null) return;\n this.scale = Math.min(\n 1,\n this.host.clientWidth / this.svg.viewBox.baseVal.width,\n this.host.clientHeight / this.svg.viewBox.baseVal.height,\n );\n this.resize();\n this.scrollOrigin();\n }\n\n private attach(): void {\n const next = this.host?.querySelector('svg');\n if (next === null || next === undefined || next === this.svg) return;\n this.svg = next;\n this.reset();\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"graph-navigation.js","sourceRoot":"","sources":["../../../../../../packages/tooling/nx-webpieces-rules/src/lib/graph-navigation.ts"],"names":[],"mappings":";;;AAAA;;;GAGG;AACH,IAAY,gBAGX;AAHD,WAAY,gBAAgB;IACxB,uCAAmB,CAAA;IACnB,yCAAqB,CAAA;AACzB,CAAC,EAHW,gBAAgB,gCAAhB,gBAAgB,QAG3B;AAED,gGAAgG;AAChG,MAAa,eAAe;IACK;IAA7B,YAA6B,MAAwB;QAAxB,WAAM,GAAN,MAAM,CAAkB;IAAG,CAAC;IAEzD,MAAM;QACF,OAAO;;;;;;;;;;;;;;;SAeN,CAAC;IACN,CAAC;IAED,MAAM;QACF,MAAM,QAAQ,GAAG,IAAI,CAAC,MAAM,KAAK,gBAAgB,CAAC,QAAQ,CAAC;QAC3D,OAAO,QAAQ,yBAAyB,CAAC,QAAQ,EAAE,KAAK,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,IAAI,CAAC;IACzF,CAAC;CACJ;AA1BD,0CA0BC;AAED,4EAA4E;AAC5E,MAAM,yBAAyB;IACV,IAAI,GAAG,QAAQ,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC;IACxC,OAAO,GAAG,QAAQ,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;IACxC,MAAM,GAAG,QAAQ,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC;IACnD,GAAG,GAAyB,IAAI,CAAC;IACjC,KAAK,GAAG,CAAC,CAAC;IAElB,YAAY,QAAiB;QACzB,IAAI,IAAI,CAAC,IAAI,KAAK,IAAI;YAAE,OAAO;QAC/B,IAAI,CAAC,OAAO,CAAC,SAAS,GAAG,QAAQ,CAAC,CAAC,CAAC,uBAAuB,CAAC,CAAC,CAAC,eAAe,CAAC;QAC9E,IAAI,CAAC,OAAO,CAAC,YAAY,CAAC,YAAY,EAAE,kBAAkB,CAAC,CAAC;QAC5D,IAAI,QAAQ;YAAE,IAAI,CAAC,eAAe,EAAE,CAAC;;YAChC,IAAI,CAAC,cAAc,EAAE,CAAC;QAC3B,IAAI,gBAAgB,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAClF,IAAI,CAAC,MAAM,EAAE,CAAC;QACd,MAAM,CAAC,gBAAgB,CAAC,QAAQ,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC;QACxD,IAAI,CAAC,IAAI,CAAC,gBAAgB,CAAC,QAAQ,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC;IAC/D,CAAC;IAEO,cAAc;QAClB,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAChC,IAAI,CAAC,MAAM,CAAC,SAAS,EAAE,SAAS,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;QACzD,IAAI,CAAC,MAAM,CAAC,UAAU,EAAE,UAAU,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;QAC1D,IAAI,CAAC,MAAM,CAAC,gBAAgB,EAAE,gBAAgB,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC;QACpE,IAAI,CAAC,MAAM,CAAC,cAAc,EAAE,cAAc,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC;QAC9D,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACrC,CAAC;IAED,+FAA+F;IACvF,eAAe;QACnB,IAAI,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAC/B,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACjC,IAAI,CAAC,MAAM,CAAC,GAAG,EAAE,UAAU,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;QACnD,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,gBAAgB,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC;QACzD,IAAI,CAAC,MAAM,CAAC,GAAG,EAAE,cAAc,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC;QACnD,IAAI,CAAC,MAAM,CAAC,GAAG,EAAE,SAAS,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;IACvD,CAAC;IAEO,MAAM,CAAC,IAAY,EAAE,KAAa,EAAE,MAAkB;QAC1D,MAAM,MAAM,GAAG,QAAQ,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC;QAChD,MAAM,CAAC,IAAI,GAAG,QAAQ,CAAC;QACvB,MAAM,CAAC,WAAW,GAAG,IAAI,CAAC;QAC1B,MAAM,CAAC,KAAK,GAAG,KAAK,CAAC;QACrB,MAAM,CAAC,YAAY,CAAC,YAAY,EAAE,KAAK,CAAC,CAAC;QACzC,MAAM,CAAC,gBAAgB,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;QACzC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IAChC,CAAC;IAEO,OAAO;QACX,QAAQ,CAAC,aAAa,CAAC,IAAI,KAAK,CAAC,oBAAoB,CAAC,CAAC,CAAC;IAC5D,CAAC;IAEO,MAAM;QACV,IAAI,IAAI,CAAC,GAAG,KAAK,IAAI;YAAE,OAAO;QAC9B,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,OAAO,CAAC;QACrC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,GAAG,GAAG,GAAG,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,IAAI,CAAC;QACrD,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,GAAG,GAAG,GAAG,CAAC,MAAM,GAAG,IAAI,CAAC,KAAK,IAAI,CAAC;QACvD,IAAI,CAAC,MAAM,CAAC,WAAW,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,GAAG,GAAG,CAAC,GAAG,CAAC;QAC7D,IAAI,CAAC,OAAO,EAAE,CAAC;IACnB,CAAC;IAEO,IAAI,CAAC,MAAc;QACvB,IAAI,IAAI,CAAC,IAAI,KAAK,IAAI;YAAE,OAAO;QAC/B,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC;QAC1B,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,GAAG,MAAM,CAAC,CAAC,CAAC;QAC7D,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,WAAW,GAAG,CAAC,CAAC;QACjE,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,YAAY,GAAG,CAAC,CAAC;QACjE,IAAI,CAAC,MAAM,EAAE,CAAC;QACd,IAAI,CAAC,IAAI,CAAC,UAAU,GAAG,CAAC,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,WAAW,GAAG,CAAC,CAAC;QACnF,IAAI,CAAC,IAAI,CAAC,SAAS,GAAG,CAAC,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,YAAY,GAAG,CAAC,CAAC;IACvF,CAAC;IAEO,KAAK;QACT,IAAI,CAAC,KAAK,GAAG,CAAC,CAAC;QACf,IAAI,CAAC,MAAM,EAAE,CAAC;QACd,IAAI,CAAC,YAAY,EAAE,CAAC;IACxB,CAAC;IAEO,YAAY;QAChB,IAAI,IAAI,CAAC,IAAI,KAAK,IAAI;YAAE,OAAO;QAC/B,IAAI,CAAC,IAAI,CAAC,UAAU,GAAG,CAAC,CAAC;QACzB,IAAI,CAAC,IAAI,CAAC,SAAS,GAAG,CAAC,CAAC;IAC5B,CAAC;IAEO,GAAG;QACP,IAAI,IAAI,CAAC,GAAG,KAAK,IAAI,IAAI,IAAI,CAAC,IAAI,KAAK,IAAI;YAAE,OAAO;QACpD,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,GAAG,CACjB,CAAC,EACD,IAAI,CAAC,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,OAAO,CAAC,KAAK,EACtD,IAAI,CAAC,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,OAAO,CAAC,MAAM,CAC3D,CAAC;QACF,IAAI,CAAC,MAAM,EAAE,CAAC;QACd,IAAI,CAAC,YAAY,EAAE,CAAC;IACxB,CAAC;IAEO,MAAM;QACV,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,EAAE,aAAa,CAAC,KAAK,CAAC,CAAC;QAC7C,IAAI,IAAI,KAAK,IAAI,IAAI,IAAI,KAAK,SAAS,IAAI,IAAI,KAAK,IAAI,CAAC,GAAG;YAAE,OAAO;QACrE,IAAI,CAAC,GAAG,GAAG,IAAI,CAAC;QAChB,IAAI,CAAC,KAAK,EAAE,CAAC;IACjB,CAAC;CACJ","sourcesContent":["/**\n * Where the zoom controls sit. The runtime page keeps its toolbar ABOVE the graph; the architecture\n * page gives the graph the whole viewport and floats a compact `− 1:1 ⤢ +` cluster bottom-right.\n */\nexport enum NavigationLayout {\n TOOLBAR = 'toolbar',\n FLOATING = 'floating',\n}\n\n/** Shared local-file navigation. Reset restores readable scale; Fit is an explicit overview. */\nexport class GraphNavigation {\n constructor(private readonly layout: NavigationLayout) {}\n\n styles(): string {\n return `\n #graph { overflow: auto; max-height: 75vh; text-align: left; touch-action: pan-x pan-y; }\n #graph svg { max-width: none; max-height: none; display: block; }\n .wp-navigation { display: flex; gap: 8px; align-items: center; margin: 10px 0; }\n .wp-navigation button { padding: 6px 12px; cursor: pointer; }\n .wp-navigation.wp-zoom { position: absolute; right: 14px; bottom: 14px; margin: 0; gap: 6px; z-index: 20; }\n .wp-navigation.wp-zoom button { min-width: 34px; height: 34px; padding: 0 8px; border-radius: 8px; border: 1px solid #d9dce5; background: #fff; color: #1b1e27; font-size: 15px; }\n .wp-navigation.wp-zoom button:hover { border-color: #3559d6; }\n .wp-navigation.wp-zoom output { min-width: 42px; padding: 2px 6px; border-radius: 6px; background: #ffffffd9; color: #5d6475; font-size: 12px; text-align: center; }\n .wp-graph-details, .wp-impact-why { position: fixed; z-index: 1100; box-sizing: border-box; max-width: min(520px, calc(100vw - 24px)); max-height: min(480px, calc(100vh - 24px)); overflow: auto; background: white; padding: 12px; border: 1px solid #aaa; border-radius: 6px; box-shadow: 0 4px 16px #0003; font: 14px/1.5 Arial, sans-serif; overflow-wrap: anywhere; }\n .wp-graph-details li, .wp-impact-why li { margin-bottom: 8px; }\n .wp-api-detail { cursor: pointer; text-decoration: underline; }\n #graph g.edge.wp-edge-change { cursor: pointer; }\n #graph g.edge.wp-edge-change:focus { outline: 2px dashed #d32f2f; }\n .wp-api-detail:focus { outline: 2px solid #1565c0; }\n `;\n }\n\n script(): string {\n const floating = this.layout === NavigationLayout.FLOATING;\n return `new (${GraphNavigationController.toString()})(${JSON.stringify(floating)});`;\n }\n}\n\n/** Serialized browser controller; dependencies are native DOM APIs only. */\nclass GraphNavigationController {\n private readonly host = document.getElementById('graph');\n private readonly toolbar = document.createElement('nav');\n private readonly status = document.createElement('output');\n private svg: SVGSVGElement | null = null;\n private scale = 1;\n\n constructor(floating: boolean) {\n if (this.host === null) return;\n this.toolbar.className = floating ? 'wp-navigation wp-zoom' : 'wp-navigation';\n this.toolbar.setAttribute('aria-label', 'Graph navigation');\n if (floating) this.floatingButtons();\n else this.toolbarButtons();\n new MutationObserver(() => this.attach()).observe(this.host, { childList: true });\n this.attach();\n window.addEventListener('resize', () => this.changed());\n this.host.addEventListener('scroll', () => this.changed());\n }\n\n private toolbarButtons(): void {\n this.host?.before(this.toolbar);\n this.button('Zoom in', 'Zoom in', () => this.zoom(1.25));\n this.button('Zoom out', 'Zoom out', () => this.zoom(0.8));\n this.button('Readable reset', 'Readable reset', () => this.reset());\n this.button('Fit overview', 'Fit overview', () => this.fit());\n this.toolbar.append(this.status);\n }\n\n /** Placed AFTER the graph in the DOM, so it never sits ahead of the boxes in the tab order. */\n private floatingButtons(): void {\n this.host?.after(this.toolbar);\n this.toolbar.append(this.status);\n this.button('−', 'Zoom out', () => this.zoom(0.8));\n this.button('1:1', 'Readable reset', () => this.reset());\n this.button('⤢', 'Fit overview', () => this.fit());\n this.button('+', 'Zoom in', () => this.zoom(1.25));\n }\n\n private button(text: string, label: string, action: () => void): void {\n const button = document.createElement('button');\n button.type = 'button';\n button.textContent = text;\n button.title = label;\n button.setAttribute('aria-label', label);\n button.addEventListener('click', action);\n this.toolbar.append(button);\n }\n\n private changed(): void {\n document.dispatchEvent(new Event('wp-graph-transform'));\n }\n\n private resize(): void {\n if (this.svg === null) return;\n const box = this.svg.viewBox.baseVal;\n this.svg.style.width = `${box.width * this.scale}px`;\n this.svg.style.height = `${box.height * this.scale}px`;\n this.status.textContent = `${Math.round(this.scale * 100)}%`;\n this.changed();\n }\n\n private zoom(factor: number): void {\n if (this.host === null) return;\n const before = this.scale;\n this.scale = Math.max(0.1, Math.min(4, this.scale * factor));\n const centerX = this.host.scrollLeft + this.host.clientWidth / 2;\n const centerY = this.host.scrollTop + this.host.clientHeight / 2;\n this.resize();\n this.host.scrollLeft = (centerX * this.scale) / before - this.host.clientWidth / 2;\n this.host.scrollTop = (centerY * this.scale) / before - this.host.clientHeight / 2;\n }\n\n private reset(): void {\n this.scale = 1;\n this.resize();\n this.scrollOrigin();\n }\n\n private scrollOrigin(): void {\n if (this.host === null) return;\n this.host.scrollLeft = 0;\n this.host.scrollTop = 0;\n }\n\n private fit(): void {\n if (this.svg === null || this.host === null) return;\n this.scale = Math.min(\n 1,\n this.host.clientWidth / this.svg.viewBox.baseVal.width,\n this.host.clientHeight / this.svg.viewBox.baseVal.height,\n );\n this.resize();\n this.scrollOrigin();\n }\n\n private attach(): void {\n const next = this.host?.querySelector('svg');\n if (next === null || next === undefined || next === this.svg) return;\n this.svg = next;\n this.reset();\n }\n}\n"]}
|
|
@@ -26,7 +26,9 @@ export declare enum ChangeScope {
|
|
|
26
26
|
CHANGED = "changed",
|
|
27
27
|
DEPENDENTS = "dependents",
|
|
28
28
|
DEPENDENCIES = "dependencies",
|
|
29
|
-
BUILD = "build"
|
|
29
|
+
BUILD = "build",
|
|
30
|
+
/** #1264: the boxes at either end of an edge gained or lost since the fork point. */
|
|
31
|
+
EDGES = "edges"
|
|
30
32
|
}
|
|
31
33
|
/**
|
|
32
34
|
* One change scope: its name and the grey second line under it, like the Color-by menu. These are the
|
|
@@ -31,6 +31,8 @@ var ChangeScope;
|
|
|
31
31
|
ChangeScope["DEPENDENTS"] = "dependents";
|
|
32
32
|
ChangeScope["DEPENDENCIES"] = "dependencies";
|
|
33
33
|
ChangeScope["BUILD"] = "build";
|
|
34
|
+
/** #1264: the boxes at either end of an edge gained or lost since the fork point. */
|
|
35
|
+
ChangeScope["EDGES"] = "edges";
|
|
34
36
|
})(ChangeScope || (exports.ChangeScope = ChangeScope = {}));
|
|
35
37
|
/**
|
|
36
38
|
* One change scope: its name and the grey second line under it, like the Color-by menu. These are the
|
|
@@ -54,6 +56,7 @@ exports.SCOPE_OPTIONS = [
|
|
|
54
56
|
new ScopeOption(ChangeScope.DEPENDENTS, 'Changed + dependents', '+ everything else nx affected: dependents, implicitly and root-file touched (their tests re-run)'),
|
|
55
57
|
new ScopeOption(ChangeScope.DEPENDENCIES, 'Changed + dependencies', '+ everything they use'),
|
|
56
58
|
new ScopeOption(ChangeScope.BUILD, 'Changed + dependents + dependencies', 'everything CI builds for this branch'),
|
|
59
|
+
new ScopeOption(ChangeScope.EDGES, 'Edge changes only', 'projects that gained or lost a dependency or call since the fork point'),
|
|
57
60
|
];
|
|
58
61
|
/** Everything the shell frames, already rendered by its owner. */
|
|
59
62
|
class ShellParts {
|