@webpieces/nx-webpieces-rules 0.4.971 → 0.4.973

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/README.md +8 -8
  2. package/executors.json +1 -6
  3. package/package.json +7 -7
  4. package/src/executors/help/executor.js +1 -3
  5. package/src/executors/help/executor.js.map +1 -1
  6. package/src/executors/visualize-runtime/executor.d.ts +22 -3
  7. package/src/executors/visualize-runtime/executor.js +55 -8
  8. package/src/executors/visualize-runtime/executor.js.map +1 -1
  9. package/src/executors/visualize-runtime/schema.json +7 -2
  10. package/src/lib/graph-color-modes.d.ts +10 -0
  11. package/src/lib/graph-color-modes.js +16 -1
  12. package/src/lib/graph-color-modes.js.map +1 -1
  13. package/src/lib/graph-mode-state.client.d.ts +6 -1
  14. package/src/lib/graph-mode-state.client.js +9 -1
  15. package/src/lib/graph-mode-state.client.js.map +1 -1
  16. package/src/lib/graph-page-shell.d.ts +10 -2
  17. package/src/lib/graph-page-shell.js +20 -8
  18. package/src/lib/graph-page-shell.js.map +1 -1
  19. package/src/lib/graph-render-model.d.ts +10 -0
  20. package/src/lib/graph-render-model.js +7 -0
  21. package/src/lib/graph-render-model.js.map +1 -1
  22. package/src/lib/graph-visualizer.client.js +2 -2
  23. package/src/lib/graph-visualizer.client.js.map +1 -1
  24. package/src/lib/runtime-goal-comparison.js +1 -1
  25. package/src/lib/runtime-goal-comparison.js.map +1 -1
  26. package/src/lib/runtime-goal-file.d.ts +6 -11
  27. package/src/lib/runtime-goal-file.js +7 -23
  28. package/src/lib/runtime-goal-file.js.map +1 -1
  29. package/src/lib/runtime-goal-validator.d.ts +4 -4
  30. package/src/lib/runtime-goal-validator.js +4 -4
  31. package/src/lib/runtime-goal-validator.js.map +1 -1
  32. package/src/lib/runtime-goal-view.d.ts +18 -11
  33. package/src/lib/runtime-goal-view.js +27 -27
  34. package/src/lib/runtime-goal-view.js.map +1 -1
  35. package/src/lib/runtime-html-page.d.ts +2 -0
  36. package/src/lib/runtime-html-page.js +8 -2
  37. package/src/lib/runtime-html-page.js.map +1 -1
  38. package/src/lib/runtime-visualizer.d.ts +2 -3
  39. package/src/lib/runtime-visualizer.js +3 -4
  40. package/src/lib/runtime-visualizer.js.map +1 -1
  41. package/src/lib/runtime-viz-options.d.ts +3 -5
  42. package/src/lib/runtime-viz-options.js +3 -7
  43. package/src/lib/runtime-viz-options.js.map +1 -1
  44. package/src/plugin.js +0 -1
  45. package/src/plugin.js.map +1 -1
  46. package/src/runtime-targets.d.ts +0 -6
  47. package/src/runtime-targets.js +1 -18
  48. package/src/runtime-targets.js.map +1 -1
  49. package/src/executors/visualize-runtime-goal/executor.d.ts +0 -27
  50. package/src/executors/visualize-runtime-goal/executor.js +0 -99
  51. package/src/executors/visualize-runtime-goal/executor.js.map +0 -1
  52. package/src/executors/visualize-runtime-goal/schema.json +0 -13
@@ -3,6 +3,7 @@ class WpModeState {
3
3
  hasProducts;
4
4
  viewer;
5
5
  goalUnavailable;
6
+ goalNote;
6
7
  /** `goal` (Goal vs Current, #1239) exists on runtime pages only; the shell renders it nowhere else. */
7
8
  static MODES = ['runtime', 'architecture', 'impact', 'product', 'goal'];
8
9
  static NAMES = ['Runtime', 'Architecture', 'Impact', 'Product', 'Goal vs Current'];
@@ -19,11 +20,14 @@ class WpModeState {
19
20
  /**
20
21
  * @param hasProducts some box belongs to a product, so the Product mode has something to show
21
22
  * @param goalUnavailable '' when Goal vs Current can be shown, else why not (#1239)
23
+ * @param goalNote the line shown under the dimmed Goal vs Current item, e.g.
24
+ * "runtime-dependencies-goal.json does not exist" (#1373); '' when it can be shown
22
25
  */
23
- constructor(hasProducts, viewer, goalUnavailable) {
26
+ constructor(hasProducts, viewer, goalUnavailable, goalNote = goalUnavailable) {
24
27
  this.hasProducts = hasProducts;
25
28
  this.viewer = viewer;
26
29
  this.goalUnavailable = goalUnavailable;
30
+ this.goalNote = goalNote;
27
31
  this.kind = this.initialKind();
28
32
  this.mode = this.initial();
29
33
  }
@@ -40,6 +44,10 @@ class WpModeState {
40
44
  goalReason() {
41
45
  return this.goalUnavailable;
42
46
  }
47
+ /** The Goal vs Current item's second line: its subtitle, or the short reason it is dimmed (#1373). */
48
+ goalSubtitle() {
49
+ return this.goalUnavailable === '' ? this.subtitle('goal') : this.goalNote;
50
+ }
43
51
  /** The active scan; null when the page has no Impact data. */
44
52
  get impact() {
45
53
  return this.scans.find((scan) => scan.kind === this.kind) ?? null;
@@ -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 /** 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 crossesInto: string[] = [];\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 readonly legend: LegendJson;\n\n constructor(\n base: RenderModelJson,\n readonly edges: RenderEdgeJson[],\n ) {\n this.legend = base.legend;\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"]}
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 * @param goalNote the line shown under the dimmed Goal vs Current item, e.g.\n * \"runtime-dependencies-goal.json does not exist\" (#1373); '' when it can be shown\n */\n constructor(\n private readonly hasProducts: boolean,\n private readonly viewer: string,\n private readonly goalUnavailable: string,\n private readonly goalNote: string = goalUnavailable,\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 Goal vs Current item's second line: its subtitle, or the short reason it is dimmed (#1373). */\n goalSubtitle(): string {\n return this.goalUnavailable === '' ? this.subtitle('goal') : this.goalNote;\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 crossesInto: string[] = [];\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 readonly legend: LegendJson;\n\n constructor(\n base: RenderModelJson,\n readonly edges: RenderEdgeJson[],\n ) {\n this.legend = base.legend;\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"]}
@@ -10,6 +10,7 @@
10
10
  * Markup only. Behaviour is graph-visualizer.client.ts; styling is graph-page-styles.ts. No element
11
11
  * carries a `style=` attribute — every look is a class.
12
12
  */
13
+ import { ModeUnavailable } from './graph-color-modes';
13
14
  import { ProductColor } from './graph-products';
14
15
  /**
15
16
  * The Filter popover's "Changes on this branch" choices. The page computes each set from the Impact
@@ -60,12 +61,16 @@ export declare class ShellParts {
60
61
  readonly frameworks: readonly string[];
61
62
  readonly roles: readonly string[];
62
63
  readonly emptyState: string;
64
+ /** Modes rendered dimmed and unselectable from the first paint, each saying why (#1373). */
65
+ readonly unavailable: readonly ModeUnavailable[];
63
66
  constructor(title: string, lockControl: string, legend: string, edgeKey: string, filterStatus: string, snapshot: string, responsibilities: string,
64
67
  /**
65
68
  * One chip per product (#1179), sorted, each colored from the page's ONE palette (#1244);
66
69
  * empty hides the Filter's Product group.
67
70
  */
68
- products: ProductColor[], runtime?: boolean, frameworks?: readonly string[], roles?: readonly string[], emptyState?: string);
71
+ products: ProductColor[], runtime?: boolean, frameworks?: readonly string[], roles?: readonly string[], emptyState?: string,
72
+ /** Modes rendered dimmed and unselectable from the first paint, each saying why (#1373). */
73
+ unavailable?: readonly ModeUnavailable[]);
69
74
  }
70
75
  export declare class GraphPageShell {
71
76
  body(parts: ShellParts): string;
@@ -76,7 +81,10 @@ export declare class GraphPageShell {
76
81
  * switch.
77
82
  */
78
83
  private modePulldown;
79
- /** One menu option, two lines: the mode's name, then its grey subtitle. */
84
+ /**
85
+ * One menu option, two lines: the mode's name, then its grey subtitle. An unavailable mode is
86
+ * disabled (dimmed by `.wp-mode:disabled`) and its subtitle says why instead (#1373).
87
+ */
80
88
  private modeItem;
81
89
  /**
82
90
  * The Filter popover. Its four groups INTERSECT: a box is shown when it is in the chosen change
@@ -14,6 +14,7 @@
14
14
  Object.defineProperty(exports, "__esModule", { value: true });
15
15
  exports.GraphPageShell = exports.ShellParts = exports.SCOPE_OPTIONS = exports.ScopeOption = exports.ChangeScope = void 0;
16
16
  const graph_color_modes_1 = require("./graph-color-modes");
17
+ const dot_syntax_1 = require("./dot-syntax");
17
18
  /**
18
19
  * The Filter popover's "Changes on this branch" choices. The page computes each set from the Impact
19
20
  * sidecar (graph-impact.ts): CHANGED = touched; DEPENDENTS = every project nx affected, whatever its reason (their
@@ -72,12 +73,15 @@ class ShellParts {
72
73
  frameworks;
73
74
  roles;
74
75
  emptyState;
76
+ unavailable;
75
77
  constructor(title, lockControl, legend, edgeKey, filterStatus, snapshot, responsibilities,
76
78
  /**
77
79
  * One chip per product (#1179), sorted, each colored from the page's ONE palette (#1244);
78
80
  * empty hides the Filter's Product group.
79
81
  */
80
- products, runtime = false, frameworks = graph_color_modes_1.FRAMEWORK_ORDER, roles = graph_color_modes_1.ROLE_STYLES.map((style) => style.role), emptyState = '') {
82
+ products, runtime = false, frameworks = graph_color_modes_1.FRAMEWORK_ORDER, roles = graph_color_modes_1.ROLE_STYLES.map((style) => style.role), emptyState = '',
83
+ /** Modes rendered dimmed and unselectable from the first paint, each saying why (#1373). */
84
+ unavailable = []) {
81
85
  this.title = title;
82
86
  this.lockControl = lockControl;
83
87
  this.legend = legend;
@@ -90,6 +94,7 @@ class ShellParts {
90
94
  this.frameworks = frameworks;
91
95
  this.roles = roles;
92
96
  this.emptyState = emptyState;
97
+ this.unavailable = unavailable;
93
98
  }
94
99
  }
95
100
  exports.ShellParts = ShellParts;
@@ -109,7 +114,7 @@ class GraphPageShell {
109
114
  <div class="wp-brand">${this.brandIcon()}<span>${parts.title}</span></div>
110
115
  <div class="wp-group" role="group" aria-label="Color mode">
111
116
  <div class="wp-label">Color by</div>
112
- ${this.modePulldown(parts.runtime ? graph_color_modes_1.RUNTIME_GRAPH_MODES : graph_color_modes_1.GRAPH_MODES)}
117
+ ${this.modePulldown(parts.runtime ? graph_color_modes_1.RUNTIME_GRAPH_MODES : graph_color_modes_1.GRAPH_MODES, parts.unavailable)}
113
118
  <p class="wp-mode-reason" id="wp-impact-reason" hidden></p>
114
119
  ${this.impactKinds('drawer')}
115
120
  </div>
@@ -141,20 +146,27 @@ class GraphPageShell {
141
146
  * menu of the four. Keys 1/2/3/4 and the #runtime / #architecture / #impact / #product links still
142
147
  * switch.
143
148
  */
144
- modePulldown(modes) {
149
+ modePulldown(modes, unavailable) {
145
150
  const first = modes[0];
146
151
  return `<div class="wp-pulldown">
147
152
  <button type="button" class="wp-pill-trigger" id="wp-mode-trigger" aria-haspopup="menu" aria-controls="wp-mode-menu" aria-expanded="false">
148
153
  <span class="wp-pill-text"><span class="wp-mode-name" id="wp-mode-current">${first.name}</span><span class="wp-mode-sub" id="wp-mode-current-sub">${first.subtitle}</span></span><span class="wp-caret" aria-hidden="true">▾</span>
149
154
  </button>
150
- <div class="wp-menu" id="wp-mode-menu" role="menu" aria-label="Color by" hidden>${modes.map((info, index) => this.modeItem(info, index)).join('')}</div>
155
+ <div class="wp-menu" id="wp-mode-menu" role="menu" aria-label="Color by" hidden>${modes.map((info, index) => this.modeItem(info, index, unavailable)).join('')}</div>
151
156
  </div>`;
152
157
  }
153
- /** One menu option, two lines: the mode's name, then its grey subtitle. */
154
- modeItem(info, index) {
158
+ /**
159
+ * One menu option, two lines: the mode's name, then its grey subtitle. An unavailable mode is
160
+ * disabled (dimmed by `.wp-mode:disabled`) and its subtitle says why instead (#1373).
161
+ */
162
+ modeItem(info, index, unavailable) {
155
163
  const checked = info.mode === graph_color_modes_1.GraphMode.RUNTIME ? 'true' : 'false';
156
- return (`<button type="button" class="wp-mode" role="menuitemradio" data-wp-mode="${info.mode}" aria-checked="${checked}" title="Key ${index + 1}">` +
157
- `<span class="wp-mode-name">${info.name}</span><span class="wp-mode-sub">${info.subtitle}</span></button>`);
164
+ const gap = unavailable.find((entry) => entry.mode === info.mode);
165
+ const title = gap === undefined ? `Key ${index + 1}` : (0, dot_syntax_1.htmlLabelText)(gap.reason);
166
+ const subtitle = gap === undefined ? info.subtitle : (0, dot_syntax_1.htmlLabelText)(gap.note);
167
+ const disabled = gap === undefined ? '' : ' disabled aria-disabled="true"';
168
+ return (`<button type="button" class="wp-mode" role="menuitemradio" data-wp-mode="${info.mode}" aria-checked="${checked}" title="${title}"${disabled}>` +
169
+ `<span class="wp-mode-name">${info.name}</span><span class="wp-mode-sub">${subtitle}</span></button>`);
158
170
  }
159
171
  /**
160
172
  * The Filter popover. Its four groups INTERSECT: a box is shown when it is in the chosen change
@@ -1 +1 @@
1
- {"version":3,"file":"graph-page-shell.js","sourceRoot":"","sources":["../../../../../../packages/tooling/nx-webpieces-rules/src/lib/graph-page-shell.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The architecture page's shell (#1155): a collapsible dark drawer on the left holding every control,\n * and the graph filling the rest of the viewport.\n *\n * Before this, two hint paragraphs, a ~650px three-column legend, a lock dropdown and a zoom bar all\n * sat ABOVE the graph, so a laptop's first screen showed no graph at all. Now the hints are a \"?\"\n * popover, the legend lives in the drawer (and can pop out over the graph), zoom floats bottom-right\n * (graph-navigation.ts) and the responsibilities open in a right-hand panel.\n *\n * Markup only. Behaviour is graph-visualizer.client.ts; styling is graph-page-styles.ts. No element\n * carries a `style=` attribute — every look is a class.\n */\n\nimport { FRAMEWORK_ORDER, GRAPH_MODES, GraphMode, ModeInfo, ROLE_STYLES, RoleStyle, RUNTIME_GRAPH_MODES } from './graph-color-modes';\nimport { ProductColor } from './graph-products';\n\n/**\n * The Filter popover's \"Changes on this branch\" choices. The page computes each set from the Impact\n * sidecar (graph-impact.ts): CHANGED = touched; DEPENDENTS = every project nx affected, whatever its reason (their\n * tests re-run); DEPENDENCIES = touched + every transitive dependency of it; BUILD = touched +\n * every affected project + every build input — the dependencies of the DEPENDENTS too, which is why it is more than\n * the union of the two rows above it.\n *\n * \"Dependents\" are what USE a project and \"dependencies\" what it uses (#1179). \"Dependee\" would mean\n * the thing depended ON — the opposite of the dependents row — so it is never used.\n */\nexport enum ChangeScope {\n EVERYTHING = 'everything',\n CHANGED = 'changed',\n DEPENDENTS = 'dependents',\n DEPENDENCIES = 'dependencies',\n BUILD = 'build',\n /** #1264: the boxes at either end of an edge gained or lost since the fork point. */\n EDGES = 'edges',\n}\n\n/**\n * One change scope: its name and the grey second line under it, like the Color-by menu. These are the\n * ONE copy of the labels — the page script reads each name back from its `data-wp-scope-label` (the\n * top-bar pill), so the two cannot drift apart.\n */\nexport class ScopeOption {\n constructor(\n public readonly scope: ChangeScope,\n public readonly label: string,\n public readonly subtitle: string,\n ) {}\n}\n\nexport const SCOPE_OPTIONS: readonly ScopeOption[] = [\n new ScopeOption(ChangeScope.EVERYTHING, 'All', 'every project'),\n new ScopeOption(ChangeScope.CHANGED, 'Changed', 'owns a changed file'),\n new ScopeOption(ChangeScope.DEPENDENTS, 'Changed + dependents', '+ everything else nx affected: dependents, implicitly and root-file touched (their tests re-run)'),\n new ScopeOption(ChangeScope.DEPENDENCIES, 'Changed + dependencies', '+ everything they use'),\n new ScopeOption(ChangeScope.BUILD, 'Changed + dependents + dependencies', 'everything CI builds for this branch'),\n new ScopeOption(ChangeScope.EDGES, 'Edge changes only', 'projects that gained or lost a dependency or call since the fork point'),\n];\n\n/** Everything the shell frames, already rendered by its owner. */\nexport class ShellParts {\n constructor(\n public readonly title: string,\n public readonly lockControl: string,\n public readonly legend: string,\n public readonly edgeKey: string,\n public readonly filterStatus: string,\n public readonly snapshot: string,\n public readonly responsibilities: string,\n /**\n * One chip per product (#1179), sorted, each colored from the page's ONE palette (#1244);\n * empty hides the Filter's Product group.\n */\n public readonly products: ProductColor[],\n public readonly runtime: boolean = false,\n public readonly frameworks: readonly string[] = FRAMEWORK_ORDER,\n public readonly roles: readonly string[] = ROLE_STYLES.map((style: RoleStyle): string => style.role),\n public readonly emptyState: string = '',\n ) {}\n}\n\nexport class GraphPageShell {\n body(parts: ShellParts): string {\n return `<div class=\"wp-shell\" id=\"wp-shell\">\n ${this.drawer(parts)}\n <main class=\"wp-main\">\n ${this.topbar(parts)}\n <div id=\"graph\"></div>\n ${this.overlays(parts)}\n </main>\n</div>`;\n }\n\n private drawer(parts: ShellParts): string {\n return `<aside class=\"wp-side\" id=\"wp-side\" aria-label=\"Graph controls\">\n <div class=\"wp-brand\">${this.brandIcon()}<span>${parts.title}</span></div>\n <div class=\"wp-group\" role=\"group\" aria-label=\"Color mode\">\n <div class=\"wp-label\">Color by</div>\n ${this.modePulldown(parts.runtime ? RUNTIME_GRAPH_MODES : GRAPH_MODES)}\n <p class=\"wp-mode-reason\" id=\"wp-impact-reason\" hidden></p>\n ${this.impactKinds('drawer')}\n </div>\n <div class=\"wp-group\">\n <button type=\"button\" class=\"wp-filter-btn\" id=\"wp-filter-open\" aria-controls=\"wp-filter-pop\" aria-expanded=\"false\"><span>Filter</span><span class=\"wp-badge\" id=\"wp-filter-badge\" hidden>0</span></button>\n </div>\n <div class=\"wp-group\">\n <label class=\"wp-label\" for=\"wp-lock\">Focus</label>\n ${parts.lockControl}\n <button type=\"button\" class=\"wp-toggle\" id=\"wp-lock-toggle\" aria-pressed=\"false\">Lock <span class=\"wp-sw\" aria-hidden=\"true\"></span></button>\n <button type=\"button\" class=\"wp-toggle\" id=\"wp-filter-toggle\" aria-pressed=\"false\">Hide unconnected <span class=\"wp-sw\" aria-hidden=\"true\"></span></button>\n </div>\n <div class=\"wp-group\">\n <div class=\"wp-label wp-label-row\">Legend <button type=\"button\" class=\"wp-icon wp-icon-mini wp-icon-dark\" id=\"wp-legend-pop\" title=\"Pop the legend out over the graph\" aria-label=\"Pop the legend out over the graph\">⇱</button></div>\n ${parts.legend}\n </div>\n <div class=\"wp-group\">\n <div class=\"wp-label\">Edges</div>\n ${parts.edgeKey}\n </div>\n <div class=\"wp-foot\">\n <button type=\"button\" class=\"wp-link\" id=\"wp-resp-open\" aria-expanded=\"false\">Responsibilities</button>\n <button type=\"button\" class=\"wp-link\" id=\"wp-snapshot-open\" aria-expanded=\"false\">Saved snapshot</button>\n </div>\n </aside>`;\n }\n\n /**\n * \"Color by\": ONE pill-shaped trigger showing the current mode (name + subtitle) that opens a\n * menu of the four. Keys 1/2/3/4 and the #runtime / #architecture / #impact / #product links still\n * switch.\n */\n private modePulldown(modes: readonly ModeInfo[]): string {\n const first = modes[0];\n return `<div class=\"wp-pulldown\">\n <button type=\"button\" class=\"wp-pill-trigger\" id=\"wp-mode-trigger\" aria-haspopup=\"menu\" aria-controls=\"wp-mode-menu\" aria-expanded=\"false\">\n <span class=\"wp-pill-text\"><span class=\"wp-mode-name\" id=\"wp-mode-current\">${first.name}</span><span class=\"wp-mode-sub\" id=\"wp-mode-current-sub\">${first.subtitle}</span></span><span class=\"wp-caret\" aria-hidden=\"true\">▾</span>\n </button>\n <div class=\"wp-menu\" id=\"wp-mode-menu\" role=\"menu\" aria-label=\"Color by\" hidden>${modes.map((info: ModeInfo, index: number): string => this.modeItem(info, index)).join('')}</div>\n </div>`;\n }\n\n /** One menu option, two lines: the mode's name, then its grey subtitle. */\n private modeItem(info: ModeInfo, index: number): string {\n const checked = info.mode === GraphMode.RUNTIME ? 'true' : 'false';\n return (\n `<button type=\"button\" class=\"wp-mode\" role=\"menuitemradio\" data-wp-mode=\"${info.mode}\" aria-checked=\"${checked}\" title=\"Key ${index + 1}\">` +\n `<span class=\"wp-mode-name\">${info.name}</span><span class=\"wp-mode-sub\">${info.subtitle}</span></button>`\n );\n }\n\n /**\n * The Filter popover. Its four groups INTERSECT: a box is shown when it is in the chosen change\n * scope AND carries any selected runtime chip (when any is selected) AND any selected role chip\n * (when any is selected) AND belongs to any selected product (when any is selected). Nothing\n * applies until \"Show N projects\".\n */\n private filterPopover(parts: ShellParts): string {\n const scopes = SCOPE_OPTIONS.map(\n (option: ScopeOption): string =>\n `<label class=\"wp-scope\"><input type=\"radio\" name=\"wp-scope\" value=\"${option.scope}\"${option.scope === ChangeScope.EVERYTHING ? ' checked' : ''}>` +\n `<span class=\"wp-scope-text\"><span class=\"wp-scope-name\" data-wp-scope-label=\"${option.scope}\">${option.label}</span>` +\n `<span class=\"wp-scope-sub\">${parts.runtime && option.scope === ChangeScope.EVERYTHING ? 'every service' : option.subtitle}</span></span>` +\n `<span class=\"wp-count\" data-wp-scope-count=\"${option.scope}\"></span></label>`,\n ).join('');\n const chips = (group: string, values: readonly string[]): string =>\n values\n .map(\n (value: string): string =>\n `<button type=\"button\" class=\"wp-chip\" data-wp-chip-group=\"${group}\" data-wp-chip=\"${value}\" aria-pressed=\"false\">${value}</button>`,\n )\n .join('');\n return `<div class=\"wp-pop wp-filter-pop\" id=\"wp-filter-pop\" role=\"dialog\" aria-label=\"Filter ${parts.runtime ? 'services' : 'projects'}\" hidden>\n <header><span>Filter</span><button type=\"button\" class=\"wp-icon wp-icon-mini\" id=\"wp-filter-close\" aria-label=\"Close the filter\">✕</button></header>\n <fieldset class=\"wp-fgroup\" id=\"wp-scope-group\"><legend id=\"wp-scope-legend\">Changes on this branch</legend>${this.impactKinds('filter')}${scopes}<p class=\"wp-scope-reason\" id=\"wp-scope-reason\" hidden></p></fieldset>\n <fieldset class=\"wp-fgroup\"><legend>Runtime <small>any match</small></legend><div class=\"wp-chips\">${chips('runtime', parts.frameworks)}</div>${parts.runtime && parts.frameworks.length === 0 ? '<p>No declared framework metadata in the saved project graph. Refresh explicitly with architecture:generate.</p>' : ''}</fieldset>\n <fieldset class=\"wp-fgroup\"><legend>Role <small>any match</small></legend><div class=\"wp-chips\">${chips('role', parts.roles)}</div></fieldset>\n ${this.productGroup(parts.products)}\n <footer><button type=\"button\" class=\"wp-text-btn\" id=\"wp-filter-clear\">Clear all</button><button type=\"button\" class=\"wp-primary\" id=\"wp-filter-apply\">Show all ${parts.runtime ? 'services' : 'projects'}</button></footer>\n </div>`;\n }\n\n /**\n * The Product group (#1179): one chip per declared product, each with its palette swatch. Several\n * chips select the UNION of their members; the group intersects with the others. Omitted when the\n * workspace declares no product.\n */\n private productGroup(products: ProductColor[]): string {\n if (products.length === 0) return '';\n const chips = products\n .map(\n (color: ProductColor): string =>\n `<button type=\"button\" class=\"wp-chip\" data-wp-chip-group=\"product\" data-wp-chip=\"${color.product}\" aria-pressed=\"false\">` +\n `<svg class=\"wp-chip-swatch\" width=\"10\" height=\"10\" viewBox=\"0 0 10 10\" aria-hidden=\"true\"><circle cx=\"5\" cy=\"5\" r=\"4.5\" fill=\"${color.color}\"/></svg>` +\n `${color.product}</button>`,\n )\n .join('');\n return `<fieldset class=\"wp-fgroup\" id=\"wp-product-group\"><legend>Product <small>any match</small></legend><div class=\"wp-chips\">${chips}</div></fieldset>`;\n }\n\n /**\n * Which Impact scan the page shows (#1163): \"Changed on this branch\" (fork point → working tree)\n * or \"Last commit\" (HEAD^ → working tree). Hidden until the page finds TWO scans in the sidecar;\n * `where` tells the drawer's copy (shown only in Impact mode) from the Filter popover's.\n */\n private impactKinds(where: string): string {\n return (\n `<div class=\"wp-chips wp-impact-kinds\" data-wp-impact-kinds=\"${where}\" role=\"group\" aria-label=\"Compare\" hidden>` +\n '<button type=\"button\" class=\"wp-chip\" data-wp-impact-kind=\"branch\" aria-pressed=\"false\">Changed on this branch</button>' +\n '<button type=\"button\" class=\"wp-chip\" data-wp-impact-kind=\"commit\" aria-pressed=\"false\">Last commit</button></div>'\n );\n }\n\n private topbar(parts: ShellParts): string {\n return `<div class=\"wp-topbar\">\n <button type=\"button\" class=\"wp-icon\" id=\"wp-collapse\" aria-label=\"Toggle the controls drawer\" aria-controls=\"wp-side\" aria-expanded=\"true\">☰</button>\n <div class=\"wp-crumb\" id=\"wp-crumb\">Mode: <b>Runtime</b></div>\n <div class=\"wp-pills\" id=\"wp-filter-pills\" aria-label=\"Active filters\"></div>\n ${parts.filterStatus}\n <button type=\"button\" class=\"wp-icon\" id=\"wp-help-btn\" aria-label=\"How to read this graph\" aria-controls=\"wp-help\" aria-expanded=\"false\">?</button>\n </div>`;\n }\n\n private overlays(parts: ShellParts): string {\n return `${parts.emptyState}<div class=\"wp-pop wp-legend-popout\" id=\"wp-legend-popout\" hidden>\n <header><span id=\"wp-legend-pop-title\">Legend · Runtime</span><button type=\"button\" class=\"wp-icon wp-icon-mini\" id=\"wp-legend-close\" aria-label=\"Close the legend\">✕</button></header>\n ${parts.legend}\n </div>\n ${this.help(parts.runtime)}\n ${this.filterPopover(parts)}\n <div class=\"wp-pop wp-snapshot\" id=\"wp-snapshot\" hidden>${parts.snapshot}</div>\n <aside class=\"wp-panel\" id=\"wp-resp-panel\" aria-label=\"Responsibilities\" hidden>\n <header><span>Responsibilities</span><button type=\"button\" class=\"wp-icon wp-icon-mini\" id=\"wp-resp-close\" aria-label=\"Close responsibilities\">✕</button></header>\n ${parts.responsibilities}\n </aside>`;\n }\n\n /** The two hint paragraphs that used to sit above the graph, plus the keyboard shortcuts. */\n private help(runtime: boolean): string {\n if (runtime) return `<div class=\"wp-pop wp-help\" id=\"wp-help\" role=\"dialog\" aria-label=\"How to read this graph\" hidden>\n <header><span>Reading the runtime graph</span><button type=\"button\" class=\"wp-icon wp-icon-mini\" id=\"wp-help-close\" aria-label=\"Close help\">✕</button></header>\n <p>Rows preserve saved runtime call-depth levels. Boxes are services; horizontal cylinders are queues, upright cylinders datastores, and clocks scheduled triggers. Solid arrows are RPC calls, dashed arrows queued events; red arrows admit legacy cycles.</p>\n <p>Click a box for Lock, Filter Unconnected, or Implements. Implements in the box is a plain summary. Click Uses on a relationship line for its complete details. Hover traces chains without opening details.</p>\n <p>Lock pins separate incoming and outgoing chains; hover adds another chain temporarily. Hide unconnected removes unrelated boxes independently. Clear all clears facets, leaving Lock and chain filtering intact.</p>\n <p>Filters intersect declared runtime, role, product closure and Nx build scope. Counts describe services; attached contextual nodes are counted separately. Impact uses compile-project sets, never runtime reachability. Queues and vendors are context, never source owners or CI projects.</p>\n <p>Runtime uses declared framework colors, Architecture declared roles; contextual nodes keep their kind colors. Product colors come from each project's declared product: tags, painted for the products toggled ON in the legend (the first two on every load); contextual nodes inherit adjacent memberships. Unknown metadata is neutral. Missing saved metadata requires explicit architecture:generate.</p>\n <p><strong>Goal vs Current</strong> compares the saved goal (architecture/runtime-dependencies-goal.json) with the saved current graph, Current → Goal: green added, red dashed removed (still drawn), amber changed, grey unchanged. Click a box for its before/after details, or click an arrow.</p>\n <p><kbd>/</kbd> Focus search · <kbd>1</kbd>–<kbd>5</kbd> modes · <kbd>Esc</kbd> dismiss or unlock</p>\n </div>`;\n return `<div class=\"wp-pop wp-help\" id=\"wp-help\" role=\"dialog\" aria-label=\"How to read this graph\" hidden>\n <header><span>Reading the graph</span><button type=\"button\" class=\"wp-icon wp-icon-mini\" id=\"wp-help-close\" aria-label=\"Close help\">✕</button></header>\n <p>Every row is one dependency level: the HIGHEST level is the top row and L0, the foundation libraries, is always the bottom row. Servers, clients and apps always form the top row (a bundle sits above the apps it aggregates). Transitive dependencies are allowed but not drawn.</p>\n <p>💡 <strong>Click any box</strong> for its menu: <strong>View Design</strong> (only where the project has a generated <strong>design.html</strong>, i.e. what the AI sees inside it), <strong>Lock/Unlock</strong> (the same lock as the Focus field), <strong>Filter Unconnected</strong> (removes unrelated boxes, keeping each L-number row).</p>\n <p><strong>Filter</strong> narrows the graph by what changed on this branch or in the last commit, by runtime, by role and by product; the groups combine. A product keeps its servers, clients and apps plus everything they depend on, so <strong>Product</strong> + <strong>Changed + dependents + dependencies</strong> is exactly what this branch builds that can affect that product. Every box keeps its own L-row, and a level left empty shows as a thin labeled band.</p>\n <p>🔦 <strong>Hover any box</strong> to trace its <em>entire</em> dependency chain — every ancestor above it and every dependency below it, with the lines between — while the rest of the graph dims.</p>\n <p><strong>Runtime</strong>: a box may depend on a box carrying every color it has. <strong>Architecture</strong>: what each box is in the system. <strong>Impact</strong>: what changed on this branch or in the last commit (amber), its dependents (amber outline), and what nx marked for another reason: an input glob (violet hatch) or a root file (teal). Click an affected box for why; with both available, a toggle picks which. <strong>Product</strong>: which products each box belongs to. Toggle products in the legend (the first two are ON on every load): one ON product is its color, several ON are stripes, only OFF products are light grey, neutral means every product while all are ON, and a dashed white box belongs to none.</p>\n <p><kbd>/</kbd> focus the lock search · <kbd>1</kbd> <kbd>2</kbd> <kbd>3</kbd> <kbd>4</kbd> switch mode · <kbd>Esc</kbd> unlock</p>\n </div>`;\n }\n\n private brandIcon(): string {\n return (\n '<svg width=\"22\" height=\"22\" viewBox=\"0 0 22 22\" aria-hidden=\"true\">' +\n '<rect x=\"1\" y=\"1\" width=\"8\" height=\"6\" rx=\"1.5\" fill=\"none\" stroke=\"#a46bf7\" stroke-width=\"1.6\"/>' +\n '<rect x=\"13\" y=\"1\" width=\"8\" height=\"6\" rx=\"1.5\" fill=\"none\" stroke=\"#e9eaf0\" stroke-width=\"1.6\"/>' +\n '<rect x=\"7\" y=\"15\" width=\"8\" height=\"6\" rx=\"1.5\" fill=\"none\" stroke=\"#e9eaf0\" stroke-width=\"1.6\"/>' +\n '<path d=\"M5 7v4h12V7M11 11v4\" fill=\"none\" stroke=\"#e9eaf0\" stroke-width=\"1.6\"/></svg>'\n );\n }\n}\n"]}
1
+ {"version":3,"file":"graph-page-shell.js","sourceRoot":"","sources":["../../../../../../packages/tooling/nx-webpieces-rules/src/lib/graph-page-shell.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The architecture page's shell (#1155): a collapsible dark drawer on the left holding every control,\n * and the graph filling the rest of the viewport.\n *\n * Before this, two hint paragraphs, a ~650px three-column legend, a lock dropdown and a zoom bar all\n * sat ABOVE the graph, so a laptop's first screen showed no graph at all. Now the hints are a \"?\"\n * popover, the legend lives in the drawer (and can pop out over the graph), zoom floats bottom-right\n * (graph-navigation.ts) and the responsibilities open in a right-hand panel.\n *\n * Markup only. Behaviour is graph-visualizer.client.ts; styling is graph-page-styles.ts. No element\n * carries a `style=` attribute — every look is a class.\n */\n\nimport { FRAMEWORK_ORDER, GRAPH_MODES, GraphMode, ModeInfo, ModeUnavailable, ROLE_STYLES, RoleStyle, RUNTIME_GRAPH_MODES } from './graph-color-modes';\nimport { htmlLabelText } from './dot-syntax';\nimport { ProductColor } from './graph-products';\n\n/**\n * The Filter popover's \"Changes on this branch\" choices. The page computes each set from the Impact\n * sidecar (graph-impact.ts): CHANGED = touched; DEPENDENTS = every project nx affected, whatever its reason (their\n * tests re-run); DEPENDENCIES = touched + every transitive dependency of it; BUILD = touched +\n * every affected project + every build input — the dependencies of the DEPENDENTS too, which is why it is more than\n * the union of the two rows above it.\n *\n * \"Dependents\" are what USE a project and \"dependencies\" what it uses (#1179). \"Dependee\" would mean\n * the thing depended ON — the opposite of the dependents row — so it is never used.\n */\nexport enum ChangeScope {\n EVERYTHING = 'everything',\n CHANGED = 'changed',\n DEPENDENTS = 'dependents',\n DEPENDENCIES = 'dependencies',\n BUILD = 'build',\n /** #1264: the boxes at either end of an edge gained or lost since the fork point. */\n EDGES = 'edges',\n}\n\n/**\n * One change scope: its name and the grey second line under it, like the Color-by menu. These are the\n * ONE copy of the labels — the page script reads each name back from its `data-wp-scope-label` (the\n * top-bar pill), so the two cannot drift apart.\n */\nexport class ScopeOption {\n constructor(\n public readonly scope: ChangeScope,\n public readonly label: string,\n public readonly subtitle: string,\n ) {}\n}\n\nexport const SCOPE_OPTIONS: readonly ScopeOption[] = [\n new ScopeOption(ChangeScope.EVERYTHING, 'All', 'every project'),\n new ScopeOption(ChangeScope.CHANGED, 'Changed', 'owns a changed file'),\n new ScopeOption(ChangeScope.DEPENDENTS, 'Changed + dependents', '+ everything else nx affected: dependents, implicitly and root-file touched (their tests re-run)'),\n new ScopeOption(ChangeScope.DEPENDENCIES, 'Changed + dependencies', '+ everything they use'),\n new ScopeOption(ChangeScope.BUILD, 'Changed + dependents + dependencies', 'everything CI builds for this branch'),\n new ScopeOption(ChangeScope.EDGES, 'Edge changes only', 'projects that gained or lost a dependency or call since the fork point'),\n];\n\n/** Everything the shell frames, already rendered by its owner. */\nexport class ShellParts {\n constructor(\n public readonly title: string,\n public readonly lockControl: string,\n public readonly legend: string,\n public readonly edgeKey: string,\n public readonly filterStatus: string,\n public readonly snapshot: string,\n public readonly responsibilities: string,\n /**\n * One chip per product (#1179), sorted, each colored from the page's ONE palette (#1244);\n * empty hides the Filter's Product group.\n */\n public readonly products: ProductColor[],\n public readonly runtime: boolean = false,\n public readonly frameworks: readonly string[] = FRAMEWORK_ORDER,\n public readonly roles: readonly string[] = ROLE_STYLES.map((style: RoleStyle): string => style.role),\n public readonly emptyState: string = '',\n /** Modes rendered dimmed and unselectable from the first paint, each saying why (#1373). */\n public readonly unavailable: readonly ModeUnavailable[] = [],\n ) {}\n}\n\nexport class GraphPageShell {\n body(parts: ShellParts): string {\n return `<div class=\"wp-shell\" id=\"wp-shell\">\n ${this.drawer(parts)}\n <main class=\"wp-main\">\n ${this.topbar(parts)}\n <div id=\"graph\"></div>\n ${this.overlays(parts)}\n </main>\n</div>`;\n }\n\n private drawer(parts: ShellParts): string {\n return `<aside class=\"wp-side\" id=\"wp-side\" aria-label=\"Graph controls\">\n <div class=\"wp-brand\">${this.brandIcon()}<span>${parts.title}</span></div>\n <div class=\"wp-group\" role=\"group\" aria-label=\"Color mode\">\n <div class=\"wp-label\">Color by</div>\n ${this.modePulldown(parts.runtime ? RUNTIME_GRAPH_MODES : GRAPH_MODES, parts.unavailable)}\n <p class=\"wp-mode-reason\" id=\"wp-impact-reason\" hidden></p>\n ${this.impactKinds('drawer')}\n </div>\n <div class=\"wp-group\">\n <button type=\"button\" class=\"wp-filter-btn\" id=\"wp-filter-open\" aria-controls=\"wp-filter-pop\" aria-expanded=\"false\"><span>Filter</span><span class=\"wp-badge\" id=\"wp-filter-badge\" hidden>0</span></button>\n </div>\n <div class=\"wp-group\">\n <label class=\"wp-label\" for=\"wp-lock\">Focus</label>\n ${parts.lockControl}\n <button type=\"button\" class=\"wp-toggle\" id=\"wp-lock-toggle\" aria-pressed=\"false\">Lock <span class=\"wp-sw\" aria-hidden=\"true\"></span></button>\n <button type=\"button\" class=\"wp-toggle\" id=\"wp-filter-toggle\" aria-pressed=\"false\">Hide unconnected <span class=\"wp-sw\" aria-hidden=\"true\"></span></button>\n </div>\n <div class=\"wp-group\">\n <div class=\"wp-label wp-label-row\">Legend <button type=\"button\" class=\"wp-icon wp-icon-mini wp-icon-dark\" id=\"wp-legend-pop\" title=\"Pop the legend out over the graph\" aria-label=\"Pop the legend out over the graph\">⇱</button></div>\n ${parts.legend}\n </div>\n <div class=\"wp-group\">\n <div class=\"wp-label\">Edges</div>\n ${parts.edgeKey}\n </div>\n <div class=\"wp-foot\">\n <button type=\"button\" class=\"wp-link\" id=\"wp-resp-open\" aria-expanded=\"false\">Responsibilities</button>\n <button type=\"button\" class=\"wp-link\" id=\"wp-snapshot-open\" aria-expanded=\"false\">Saved snapshot</button>\n </div>\n </aside>`;\n }\n\n /**\n * \"Color by\": ONE pill-shaped trigger showing the current mode (name + subtitle) that opens a\n * menu of the four. Keys 1/2/3/4 and the #runtime / #architecture / #impact / #product links still\n * switch.\n */\n private modePulldown(modes: readonly ModeInfo[], unavailable: readonly ModeUnavailable[]): string {\n const first = modes[0];\n return `<div class=\"wp-pulldown\">\n <button type=\"button\" class=\"wp-pill-trigger\" id=\"wp-mode-trigger\" aria-haspopup=\"menu\" aria-controls=\"wp-mode-menu\" aria-expanded=\"false\">\n <span class=\"wp-pill-text\"><span class=\"wp-mode-name\" id=\"wp-mode-current\">${first.name}</span><span class=\"wp-mode-sub\" id=\"wp-mode-current-sub\">${first.subtitle}</span></span><span class=\"wp-caret\" aria-hidden=\"true\">▾</span>\n </button>\n <div class=\"wp-menu\" id=\"wp-mode-menu\" role=\"menu\" aria-label=\"Color by\" hidden>${modes.map((info: ModeInfo, index: number): string => this.modeItem(info, index, unavailable)).join('')}</div>\n </div>`;\n }\n\n /**\n * One menu option, two lines: the mode's name, then its grey subtitle. An unavailable mode is\n * disabled (dimmed by `.wp-mode:disabled`) and its subtitle says why instead (#1373).\n */\n private modeItem(info: ModeInfo, index: number, unavailable: readonly ModeUnavailable[]): string {\n const checked = info.mode === GraphMode.RUNTIME ? 'true' : 'false';\n const gap = unavailable.find((entry: ModeUnavailable): boolean => entry.mode === info.mode);\n const title = gap === undefined ? `Key ${index + 1}` : htmlLabelText(gap.reason);\n const subtitle = gap === undefined ? info.subtitle : htmlLabelText(gap.note);\n const disabled = gap === undefined ? '' : ' disabled aria-disabled=\"true\"';\n return (\n `<button type=\"button\" class=\"wp-mode\" role=\"menuitemradio\" data-wp-mode=\"${info.mode}\" aria-checked=\"${checked}\" title=\"${title}\"${disabled}>` +\n `<span class=\"wp-mode-name\">${info.name}</span><span class=\"wp-mode-sub\">${subtitle}</span></button>`\n );\n }\n\n /**\n * The Filter popover. Its four groups INTERSECT: a box is shown when it is in the chosen change\n * scope AND carries any selected runtime chip (when any is selected) AND any selected role chip\n * (when any is selected) AND belongs to any selected product (when any is selected). Nothing\n * applies until \"Show N projects\".\n */\n private filterPopover(parts: ShellParts): string {\n const scopes = SCOPE_OPTIONS.map(\n (option: ScopeOption): string =>\n `<label class=\"wp-scope\"><input type=\"radio\" name=\"wp-scope\" value=\"${option.scope}\"${option.scope === ChangeScope.EVERYTHING ? ' checked' : ''}>` +\n `<span class=\"wp-scope-text\"><span class=\"wp-scope-name\" data-wp-scope-label=\"${option.scope}\">${option.label}</span>` +\n `<span class=\"wp-scope-sub\">${parts.runtime && option.scope === ChangeScope.EVERYTHING ? 'every service' : option.subtitle}</span></span>` +\n `<span class=\"wp-count\" data-wp-scope-count=\"${option.scope}\"></span></label>`,\n ).join('');\n const chips = (group: string, values: readonly string[]): string =>\n values\n .map(\n (value: string): string =>\n `<button type=\"button\" class=\"wp-chip\" data-wp-chip-group=\"${group}\" data-wp-chip=\"${value}\" aria-pressed=\"false\">${value}</button>`,\n )\n .join('');\n return `<div class=\"wp-pop wp-filter-pop\" id=\"wp-filter-pop\" role=\"dialog\" aria-label=\"Filter ${parts.runtime ? 'services' : 'projects'}\" hidden>\n <header><span>Filter</span><button type=\"button\" class=\"wp-icon wp-icon-mini\" id=\"wp-filter-close\" aria-label=\"Close the filter\">✕</button></header>\n <fieldset class=\"wp-fgroup\" id=\"wp-scope-group\"><legend id=\"wp-scope-legend\">Changes on this branch</legend>${this.impactKinds('filter')}${scopes}<p class=\"wp-scope-reason\" id=\"wp-scope-reason\" hidden></p></fieldset>\n <fieldset class=\"wp-fgroup\"><legend>Runtime <small>any match</small></legend><div class=\"wp-chips\">${chips('runtime', parts.frameworks)}</div>${parts.runtime && parts.frameworks.length === 0 ? '<p>No declared framework metadata in the saved project graph. Refresh explicitly with architecture:generate.</p>' : ''}</fieldset>\n <fieldset class=\"wp-fgroup\"><legend>Role <small>any match</small></legend><div class=\"wp-chips\">${chips('role', parts.roles)}</div></fieldset>\n ${this.productGroup(parts.products)}\n <footer><button type=\"button\" class=\"wp-text-btn\" id=\"wp-filter-clear\">Clear all</button><button type=\"button\" class=\"wp-primary\" id=\"wp-filter-apply\">Show all ${parts.runtime ? 'services' : 'projects'}</button></footer>\n </div>`;\n }\n\n /**\n * The Product group (#1179): one chip per declared product, each with its palette swatch. Several\n * chips select the UNION of their members; the group intersects with the others. Omitted when the\n * workspace declares no product.\n */\n private productGroup(products: ProductColor[]): string {\n if (products.length === 0) return '';\n const chips = products\n .map(\n (color: ProductColor): string =>\n `<button type=\"button\" class=\"wp-chip\" data-wp-chip-group=\"product\" data-wp-chip=\"${color.product}\" aria-pressed=\"false\">` +\n `<svg class=\"wp-chip-swatch\" width=\"10\" height=\"10\" viewBox=\"0 0 10 10\" aria-hidden=\"true\"><circle cx=\"5\" cy=\"5\" r=\"4.5\" fill=\"${color.color}\"/></svg>` +\n `${color.product}</button>`,\n )\n .join('');\n return `<fieldset class=\"wp-fgroup\" id=\"wp-product-group\"><legend>Product <small>any match</small></legend><div class=\"wp-chips\">${chips}</div></fieldset>`;\n }\n\n /**\n * Which Impact scan the page shows (#1163): \"Changed on this branch\" (fork point → working tree)\n * or \"Last commit\" (HEAD^ → working tree). Hidden until the page finds TWO scans in the sidecar;\n * `where` tells the drawer's copy (shown only in Impact mode) from the Filter popover's.\n */\n private impactKinds(where: string): string {\n return (\n `<div class=\"wp-chips wp-impact-kinds\" data-wp-impact-kinds=\"${where}\" role=\"group\" aria-label=\"Compare\" hidden>` +\n '<button type=\"button\" class=\"wp-chip\" data-wp-impact-kind=\"branch\" aria-pressed=\"false\">Changed on this branch</button>' +\n '<button type=\"button\" class=\"wp-chip\" data-wp-impact-kind=\"commit\" aria-pressed=\"false\">Last commit</button></div>'\n );\n }\n\n private topbar(parts: ShellParts): string {\n return `<div class=\"wp-topbar\">\n <button type=\"button\" class=\"wp-icon\" id=\"wp-collapse\" aria-label=\"Toggle the controls drawer\" aria-controls=\"wp-side\" aria-expanded=\"true\">☰</button>\n <div class=\"wp-crumb\" id=\"wp-crumb\">Mode: <b>Runtime</b></div>\n <div class=\"wp-pills\" id=\"wp-filter-pills\" aria-label=\"Active filters\"></div>\n ${parts.filterStatus}\n <button type=\"button\" class=\"wp-icon\" id=\"wp-help-btn\" aria-label=\"How to read this graph\" aria-controls=\"wp-help\" aria-expanded=\"false\">?</button>\n </div>`;\n }\n\n private overlays(parts: ShellParts): string {\n return `${parts.emptyState}<div class=\"wp-pop wp-legend-popout\" id=\"wp-legend-popout\" hidden>\n <header><span id=\"wp-legend-pop-title\">Legend · Runtime</span><button type=\"button\" class=\"wp-icon wp-icon-mini\" id=\"wp-legend-close\" aria-label=\"Close the legend\">✕</button></header>\n ${parts.legend}\n </div>\n ${this.help(parts.runtime)}\n ${this.filterPopover(parts)}\n <div class=\"wp-pop wp-snapshot\" id=\"wp-snapshot\" hidden>${parts.snapshot}</div>\n <aside class=\"wp-panel\" id=\"wp-resp-panel\" aria-label=\"Responsibilities\" hidden>\n <header><span>Responsibilities</span><button type=\"button\" class=\"wp-icon wp-icon-mini\" id=\"wp-resp-close\" aria-label=\"Close responsibilities\">✕</button></header>\n ${parts.responsibilities}\n </aside>`;\n }\n\n /** The two hint paragraphs that used to sit above the graph, plus the keyboard shortcuts. */\n private help(runtime: boolean): string {\n if (runtime) return `<div class=\"wp-pop wp-help\" id=\"wp-help\" role=\"dialog\" aria-label=\"How to read this graph\" hidden>\n <header><span>Reading the runtime graph</span><button type=\"button\" class=\"wp-icon wp-icon-mini\" id=\"wp-help-close\" aria-label=\"Close help\">✕</button></header>\n <p>Rows preserve saved runtime call-depth levels. Boxes are services; horizontal cylinders are queues, upright cylinders datastores, and clocks scheduled triggers. Solid arrows are RPC calls, dashed arrows queued events; red arrows admit legacy cycles.</p>\n <p>Click a box for Lock, Filter Unconnected, or Implements. Implements in the box is a plain summary. Click Uses on a relationship line for its complete details. Hover traces chains without opening details.</p>\n <p>Lock pins separate incoming and outgoing chains; hover adds another chain temporarily. Hide unconnected removes unrelated boxes independently. Clear all clears facets, leaving Lock and chain filtering intact.</p>\n <p>Filters intersect declared runtime, role, product closure and Nx build scope. Counts describe services; attached contextual nodes are counted separately. Impact uses compile-project sets, never runtime reachability. Queues and vendors are context, never source owners or CI projects.</p>\n <p>Runtime uses declared framework colors, Architecture declared roles; contextual nodes keep their kind colors. Product colors come from each project's declared product: tags, painted for the products toggled ON in the legend (the first two on every load); contextual nodes inherit adjacent memberships. Unknown metadata is neutral. Missing saved metadata requires explicit architecture:generate.</p>\n <p><strong>Goal vs Current</strong> compares the saved goal (architecture/runtime-dependencies-goal.json) with the saved current graph, Current → Goal: green added, red dashed removed (still drawn), amber changed, grey unchanged. Click a box for its before/after details, or click an arrow.</p>\n <p><kbd>/</kbd> Focus search · <kbd>1</kbd>–<kbd>5</kbd> modes · <kbd>Esc</kbd> dismiss or unlock</p>\n </div>`;\n return `<div class=\"wp-pop wp-help\" id=\"wp-help\" role=\"dialog\" aria-label=\"How to read this graph\" hidden>\n <header><span>Reading the graph</span><button type=\"button\" class=\"wp-icon wp-icon-mini\" id=\"wp-help-close\" aria-label=\"Close help\">✕</button></header>\n <p>Every row is one dependency level: the HIGHEST level is the top row and L0, the foundation libraries, is always the bottom row. Servers, clients and apps always form the top row (a bundle sits above the apps it aggregates). Transitive dependencies are allowed but not drawn.</p>\n <p>💡 <strong>Click any box</strong> for its menu: <strong>View Design</strong> (only where the project has a generated <strong>design.html</strong>, i.e. what the AI sees inside it), <strong>Lock/Unlock</strong> (the same lock as the Focus field), <strong>Filter Unconnected</strong> (removes unrelated boxes, keeping each L-number row).</p>\n <p><strong>Filter</strong> narrows the graph by what changed on this branch or in the last commit, by runtime, by role and by product; the groups combine. A product keeps its servers, clients and apps plus everything they depend on, so <strong>Product</strong> + <strong>Changed + dependents + dependencies</strong> is exactly what this branch builds that can affect that product. Every box keeps its own L-row, and a level left empty shows as a thin labeled band.</p>\n <p>🔦 <strong>Hover any box</strong> to trace its <em>entire</em> dependency chain — every ancestor above it and every dependency below it, with the lines between — while the rest of the graph dims.</p>\n <p><strong>Runtime</strong>: a box may depend on a box carrying every color it has. <strong>Architecture</strong>: what each box is in the system. <strong>Impact</strong>: what changed on this branch or in the last commit (amber), its dependents (amber outline), and what nx marked for another reason: an input glob (violet hatch) or a root file (teal). Click an affected box for why; with both available, a toggle picks which. <strong>Product</strong>: which products each box belongs to. Toggle products in the legend (the first two are ON on every load): one ON product is its color, several ON are stripes, only OFF products are light grey, neutral means every product while all are ON, and a dashed white box belongs to none.</p>\n <p><kbd>/</kbd> focus the lock search · <kbd>1</kbd> <kbd>2</kbd> <kbd>3</kbd> <kbd>4</kbd> switch mode · <kbd>Esc</kbd> unlock</p>\n </div>`;\n }\n\n private brandIcon(): string {\n return (\n '<svg width=\"22\" height=\"22\" viewBox=\"0 0 22 22\" aria-hidden=\"true\">' +\n '<rect x=\"1\" y=\"1\" width=\"8\" height=\"6\" rx=\"1.5\" fill=\"none\" stroke=\"#a46bf7\" stroke-width=\"1.6\"/>' +\n '<rect x=\"13\" y=\"1\" width=\"8\" height=\"6\" rx=\"1.5\" fill=\"none\" stroke=\"#e9eaf0\" stroke-width=\"1.6\"/>' +\n '<rect x=\"7\" y=\"15\" width=\"8\" height=\"6\" rx=\"1.5\" fill=\"none\" stroke=\"#e9eaf0\" stroke-width=\"1.6\"/>' +\n '<path d=\"M5 7v4h12V7M11 11v4\" fill=\"none\" stroke=\"#e9eaf0\" stroke-width=\"1.6\"/></svg>'\n );\n }\n}\n"]}
@@ -87,6 +87,11 @@ export declare class RuntimeComparisonView {
87
87
  readonly available: boolean;
88
88
  /** Why the mode is unavailable; '' when available. */
89
89
  readonly reason: string;
90
+ /**
91
+ * The short line the Color-by menu shows under the DIMMED Goal vs Current item (#1373) — e.g.
92
+ * "runtime-dependencies-goal.json does not exist"; '' when available.
93
+ */
94
+ readonly menuNote: string;
90
95
  /** Always "Current → Goal": the direction every added/removed word is read in. */
91
96
  readonly direction: string;
92
97
  /** One line: counts per status, or the explicit no-differences result. */
@@ -102,6 +107,11 @@ export declare class RuntimeComparisonView {
102
107
  constructor(available: boolean,
103
108
  /** Why the mode is unavailable; '' when available. */
104
109
  reason: string,
110
+ /**
111
+ * The short line the Color-by menu shows under the DIMMED Goal vs Current item (#1373) — e.g.
112
+ * "runtime-dependencies-goal.json does not exist"; '' when available.
113
+ */
114
+ menuNote: string,
105
115
  /** Always "Current → Goal": the direction every added/removed word is read in. */
106
116
  direction: string,
107
117
  /** One line: counts per status, or the explicit no-differences result. */
@@ -146,6 +146,7 @@ exports.GraphRenderModel = GraphRenderModel;
146
146
  class RuntimeComparisonView {
147
147
  available;
148
148
  reason;
149
+ menuNote;
149
150
  direction;
150
151
  summary;
151
152
  model;
@@ -155,6 +156,11 @@ class RuntimeComparisonView {
155
156
  constructor(available,
156
157
  /** Why the mode is unavailable; '' when available. */
157
158
  reason,
159
+ /**
160
+ * The short line the Color-by menu shows under the DIMMED Goal vs Current item (#1373) — e.g.
161
+ * "runtime-dependencies-goal.json does not exist"; '' when available.
162
+ */
163
+ menuNote,
158
164
  /** Always "Current → Goal": the direction every added/removed word is read in. */
159
165
  direction,
160
166
  /** One line: counts per status, or the explicit no-differences result. */
@@ -169,6 +175,7 @@ class RuntimeComparisonView {
169
175
  differences) {
170
176
  this.available = available;
171
177
  this.reason = reason;
178
+ this.menuNote = menuNote;
172
179
  this.direction = direction;
173
180
  this.summary = summary;
174
181
  this.model = model;