@opensip-cli/dashboard 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +202 -0
- package/NOTICE +8 -0
- package/README.md +31 -0
- package/dist/__tests__/catalog-provenance.test.d.ts +11 -0
- package/dist/__tests__/catalog-provenance.test.d.ts.map +1 -0
- package/dist/__tests__/catalog-provenance.test.js +130 -0
- package/dist/__tests__/catalog-provenance.test.js.map +1 -0
- package/dist/__tests__/coupling-attribution.test.d.ts +9 -0
- package/dist/__tests__/coupling-attribution.test.d.ts.map +1 -0
- package/dist/__tests__/coupling-attribution.test.js +99 -0
- package/dist/__tests__/coupling-attribution.test.js.map +1 -0
- package/dist/__tests__/dashboard-bundle-weight.test.d.ts +16 -0
- package/dist/__tests__/dashboard-bundle-weight.test.d.ts.map +1 -0
- package/dist/__tests__/dashboard-bundle-weight.test.js +73 -0
- package/dist/__tests__/dashboard-bundle-weight.test.js.map +1 -0
- package/dist/__tests__/dashboard-cell-containment.test.d.ts +2 -0
- package/dist/__tests__/dashboard-cell-containment.test.d.ts.map +1 -0
- package/dist/__tests__/dashboard-cell-containment.test.js +49 -0
- package/dist/__tests__/dashboard-cell-containment.test.js.map +1 -0
- package/dist/__tests__/dashboard-cytoscape-vendor.test.d.ts +10 -0
- package/dist/__tests__/dashboard-cytoscape-vendor.test.d.ts.map +1 -0
- package/dist/__tests__/dashboard-cytoscape-vendor.test.js +40 -0
- package/dist/__tests__/dashboard-cytoscape-vendor.test.js.map +1 -0
- package/dist/__tests__/dashboard-editor-link.test.d.ts +5 -0
- package/dist/__tests__/dashboard-editor-link.test.d.ts.map +1 -0
- package/dist/__tests__/dashboard-editor-link.test.js +36 -0
- package/dist/__tests__/dashboard-editor-link.test.js.map +1 -0
- package/dist/__tests__/dashboard-filters.test.d.ts +9 -0
- package/dist/__tests__/dashboard-filters.test.d.ts.map +1 -0
- package/dist/__tests__/dashboard-filters.test.js +95 -0
- package/dist/__tests__/dashboard-filters.test.js.map +1 -0
- package/dist/__tests__/dashboard-function-card-singleton.test.d.ts +9 -0
- package/dist/__tests__/dashboard-function-card-singleton.test.d.ts.map +1 -0
- package/dist/__tests__/dashboard-function-card-singleton.test.js +87 -0
- package/dist/__tests__/dashboard-function-card-singleton.test.js.map +1 -0
- package/dist/__tests__/dashboard-function-card.test.d.ts +10 -0
- package/dist/__tests__/dashboard-function-card.test.d.ts.map +1 -0
- package/dist/__tests__/dashboard-function-card.test.js +403 -0
- package/dist/__tests__/dashboard-function-card.test.js.map +1 -0
- package/dist/__tests__/dashboard-function-row.test.d.ts +11 -0
- package/dist/__tests__/dashboard-function-row.test.d.ts.map +1 -0
- package/dist/__tests__/dashboard-function-row.test.js +101 -0
- package/dist/__tests__/dashboard-function-row.test.js.map +1 -0
- package/dist/__tests__/dashboard-generator-graph-catalog.test.d.ts +9 -0
- package/dist/__tests__/dashboard-generator-graph-catalog.test.d.ts.map +1 -0
- package/dist/__tests__/dashboard-generator-graph-catalog.test.js +75 -0
- package/dist/__tests__/dashboard-generator-graph-catalog.test.js.map +1 -0
- package/dist/__tests__/dashboard-graph-offline.integration.test.d.ts +13 -0
- package/dist/__tests__/dashboard-graph-offline.integration.test.d.ts.map +1 -0
- package/dist/__tests__/dashboard-graph-offline.integration.test.js +55 -0
- package/dist/__tests__/dashboard-graph-offline.integration.test.js.map +1 -0
- package/dist/__tests__/dashboard-graph-scc.test.d.ts +17 -0
- package/dist/__tests__/dashboard-graph-scc.test.d.ts.map +1 -0
- package/dist/__tests__/dashboard-graph-scc.test.js +89 -0
- package/dist/__tests__/dashboard-graph-scc.test.js.map +1 -0
- package/dist/__tests__/dashboard-graph-view-model.test.d.ts +12 -0
- package/dist/__tests__/dashboard-graph-view-model.test.d.ts.map +1 -0
- package/dist/__tests__/dashboard-graph-view-model.test.js +331 -0
- package/dist/__tests__/dashboard-graph-view-model.test.js.map +1 -0
- package/dist/__tests__/dashboard-help-drawer.test.d.ts +9 -0
- package/dist/__tests__/dashboard-help-drawer.test.d.ts.map +1 -0
- package/dist/__tests__/dashboard-help-drawer.test.js +101 -0
- package/dist/__tests__/dashboard-help-drawer.test.js.map +1 -0
- package/dist/__tests__/dashboard-html.test.d.ts +2 -0
- package/dist/__tests__/dashboard-html.test.d.ts.map +1 -0
- package/dist/__tests__/dashboard-html.test.js +169 -0
- package/dist/__tests__/dashboard-html.test.js.map +1 -0
- package/dist/__tests__/dashboard-indexes.test.d.ts +9 -0
- package/dist/__tests__/dashboard-indexes.test.d.ts.map +1 -0
- package/dist/__tests__/dashboard-indexes.test.js +211 -0
- package/dist/__tests__/dashboard-indexes.test.js.map +1 -0
- package/dist/__tests__/dashboard-path-utils.test.d.ts +6 -0
- package/dist/__tests__/dashboard-path-utils.test.d.ts.map +1 -0
- package/dist/__tests__/dashboard-path-utils.test.js +54 -0
- package/dist/__tests__/dashboard-path-utils.test.js.map +1 -0
- package/dist/__tests__/dashboard-search.test.d.ts +5 -0
- package/dist/__tests__/dashboard-search.test.d.ts.map +1 -0
- package/dist/__tests__/dashboard-search.test.js +46 -0
- package/dist/__tests__/dashboard-search.test.js.map +1 -0
- package/dist/__tests__/dashboard-sessions.test.d.ts +14 -0
- package/dist/__tests__/dashboard-sessions.test.d.ts.map +1 -0
- package/dist/__tests__/dashboard-sessions.test.js +330 -0
- package/dist/__tests__/dashboard-sessions.test.js.map +1 -0
- package/dist/__tests__/dashboard-trace.test.d.ts +5 -0
- package/dist/__tests__/dashboard-trace.test.d.ts.map +1 -0
- package/dist/__tests__/dashboard-trace.test.js +147 -0
- package/dist/__tests__/dashboard-trace.test.js.map +1 -0
- package/dist/__tests__/dashboard-validation.integration.test.d.ts +14 -0
- package/dist/__tests__/dashboard-validation.integration.test.d.ts.map +1 -0
- package/dist/__tests__/dashboard-validation.integration.test.js +221 -0
- package/dist/__tests__/dashboard-validation.integration.test.js.map +1 -0
- package/dist/__tests__/dashboard-view-conformance.test.d.ts +12 -0
- package/dist/__tests__/dashboard-view-conformance.test.d.ts.map +1 -0
- package/dist/__tests__/dashboard-view-conformance.test.js +58 -0
- package/dist/__tests__/dashboard-view-conformance.test.js.map +1 -0
- package/dist/__tests__/dashboard-view-coupling.test.d.ts +7 -0
- package/dist/__tests__/dashboard-view-coupling.test.d.ts.map +1 -0
- package/dist/__tests__/dashboard-view-coupling.test.js +381 -0
- package/dist/__tests__/dashboard-view-coupling.test.js.map +1 -0
- package/dist/__tests__/dashboard-view-distribution.test.d.ts +10 -0
- package/dist/__tests__/dashboard-view-distribution.test.d.ts.map +1 -0
- package/dist/__tests__/dashboard-view-distribution.test.js +272 -0
- package/dist/__tests__/dashboard-view-distribution.test.js.map +1 -0
- package/dist/__tests__/dashboard-view-graph.test.d.ts +13 -0
- package/dist/__tests__/dashboard-view-graph.test.d.ts.map +1 -0
- package/dist/__tests__/dashboard-view-graph.test.js +332 -0
- package/dist/__tests__/dashboard-view-graph.test.js.map +1 -0
- package/dist/__tests__/dashboard-view-template.test.d.ts +21 -0
- package/dist/__tests__/dashboard-view-template.test.d.ts.map +1 -0
- package/dist/__tests__/dashboard-view-template.test.js +153 -0
- package/dist/__tests__/dashboard-view-template.test.js.map +1 -0
- package/dist/__tests__/dashboard.test.d.ts +2 -0
- package/dist/__tests__/dashboard.test.d.ts.map +1 -0
- package/dist/__tests__/dashboard.test.js +129 -0
- package/dist/__tests__/dashboard.test.js.map +1 -0
- package/dist/__tests__/graph-tab.test.d.ts +10 -0
- package/dist/__tests__/graph-tab.test.d.ts.map +1 -0
- package/dist/__tests__/graph-tab.test.js +76 -0
- package/dist/__tests__/graph-tab.test.js.map +1 -0
- package/dist/checks.d.ts +7 -0
- package/dist/checks.d.ts.map +1 -0
- package/dist/checks.js +283 -0
- package/dist/checks.js.map +1 -0
- package/dist/code-paths/__tests__/views-registry.test.d.ts +13 -0
- package/dist/code-paths/__tests__/views-registry.test.d.ts.map +1 -0
- package/dist/code-paths/__tests__/views-registry.test.js +39 -0
- package/dist/code-paths/__tests__/views-registry.test.js.map +1 -0
- package/dist/code-paths/catalog-provenance.d.ts +22 -0
- package/dist/code-paths/catalog-provenance.d.ts.map +1 -0
- package/dist/code-paths/catalog-provenance.js +108 -0
- package/dist/code-paths/catalog-provenance.js.map +1 -0
- package/dist/code-paths/catalog-recipes-tables.d.ts +11 -0
- package/dist/code-paths/catalog-recipes-tables.d.ts.map +1 -0
- package/dist/code-paths/catalog-recipes-tables.js +86 -0
- package/dist/code-paths/catalog-recipes-tables.js.map +1 -0
- package/dist/code-paths/cytoscape-vendor.d.ts +27 -0
- package/dist/code-paths/cytoscape-vendor.d.ts.map +1 -0
- package/dist/code-paths/cytoscape-vendor.js +68 -0
- package/dist/code-paths/cytoscape-vendor.js.map +1 -0
- package/dist/code-paths/editor-link.d.ts +10 -0
- package/dist/code-paths/editor-link.d.ts.map +1 -0
- package/dist/code-paths/editor-link.js +20 -0
- package/dist/code-paths/editor-link.js.map +1 -0
- package/dist/code-paths/filters.d.ts +19 -0
- package/dist/code-paths/filters.d.ts.map +1 -0
- package/dist/code-paths/filters.js +47 -0
- package/dist/code-paths/filters.js.map +1 -0
- package/dist/code-paths/function-card.d.ts +15 -0
- package/dist/code-paths/function-card.d.ts.map +1 -0
- package/dist/code-paths/function-card.js +169 -0
- package/dist/code-paths/function-card.js.map +1 -0
- package/dist/code-paths/function-row.d.ts +17 -0
- package/dist/code-paths/function-row.d.ts.map +1 -0
- package/dist/code-paths/function-row.js +77 -0
- package/dist/code-paths/function-row.js.map +1 -0
- package/dist/code-paths/graph-controls.d.ts +27 -0
- package/dist/code-paths/graph-controls.d.ts.map +1 -0
- package/dist/code-paths/graph-controls.js +257 -0
- package/dist/code-paths/graph-controls.js.map +1 -0
- package/dist/code-paths/graph-scc.d.ts +32 -0
- package/dist/code-paths/graph-scc.d.ts.map +1 -0
- package/dist/code-paths/graph-scc.js +136 -0
- package/dist/code-paths/graph-scc.js.map +1 -0
- package/dist/code-paths/graph-stylesheet.d.ts +22 -0
- package/dist/code-paths/graph-stylesheet.d.ts.map +1 -0
- package/dist/code-paths/graph-stylesheet.js +121 -0
- package/dist/code-paths/graph-stylesheet.js.map +1 -0
- package/dist/code-paths/graph-view-model.d.ts +120 -0
- package/dist/code-paths/graph-view-model.d.ts.map +1 -0
- package/dist/code-paths/graph-view-model.js +199 -0
- package/dist/code-paths/graph-view-model.js.map +1 -0
- package/dist/code-paths/help-drawer.d.ts +18 -0
- package/dist/code-paths/help-drawer.d.ts.map +1 -0
- package/dist/code-paths/help-drawer.js +54 -0
- package/dist/code-paths/help-drawer.js.map +1 -0
- package/dist/code-paths/indexes.d.ts +28 -0
- package/dist/code-paths/indexes.d.ts.map +1 -0
- package/dist/code-paths/indexes.js +97 -0
- package/dist/code-paths/indexes.js.map +1 -0
- package/dist/code-paths/path-utils.d.ts +15 -0
- package/dist/code-paths/path-utils.d.ts.map +1 -0
- package/dist/code-paths/path-utils.js +47 -0
- package/dist/code-paths/path-utils.js.map +1 -0
- package/dist/code-paths/search.d.ts +14 -0
- package/dist/code-paths/search.d.ts.map +1 -0
- package/dist/code-paths/search.js +54 -0
- package/dist/code-paths/search.js.map +1 -0
- package/dist/code-paths/trace.d.ts +11 -0
- package/dist/code-paths/trace.d.ts.map +1 -0
- package/dist/code-paths/trace.js +60 -0
- package/dist/code-paths/trace.js.map +1 -0
- package/dist/code-paths/view-coupling.d.ts +22 -0
- package/dist/code-paths/view-coupling.d.ts.map +1 -0
- package/dist/code-paths/view-coupling.js +218 -0
- package/dist/code-paths/view-coupling.js.map +1 -0
- package/dist/code-paths/view-distribution.d.ts +20 -0
- package/dist/code-paths/view-distribution.d.ts.map +1 -0
- package/dist/code-paths/view-distribution.js +82 -0
- package/dist/code-paths/view-distribution.js.map +1 -0
- package/dist/code-paths/view-graph.d.ts +35 -0
- package/dist/code-paths/view-graph.d.ts.map +1 -0
- package/dist/code-paths/view-graph.js +379 -0
- package/dist/code-paths/view-graph.js.map +1 -0
- package/dist/code-paths/view-template.d.ts +154 -0
- package/dist/code-paths/view-template.d.ts.map +1 -0
- package/dist/code-paths/view-template.js +218 -0
- package/dist/code-paths/view-template.js.map +1 -0
- package/dist/code-paths/views-registry.d.ts +13 -0
- package/dist/code-paths/views-registry.d.ts.map +1 -0
- package/dist/code-paths/views-registry.js +53 -0
- package/dist/code-paths/views-registry.js.map +1 -0
- package/dist/code-paths.d.ts +69 -0
- package/dist/code-paths.d.ts.map +1 -0
- package/dist/code-paths.js +356 -0
- package/dist/code-paths.js.map +1 -0
- package/dist/css/cards.d.ts +9 -0
- package/dist/css/cards.d.ts.map +1 -0
- package/dist/css/cards.js +36 -0
- package/dist/css/cards.js.map +1 -0
- package/dist/css/code-paths.d.ts +9 -0
- package/dist/css/code-paths.d.ts.map +1 -0
- package/dist/css/code-paths.js +111 -0
- package/dist/css/code-paths.js.map +1 -0
- package/dist/css/data-table.d.ts +12 -0
- package/dist/css/data-table.d.ts.map +1 -0
- package/dist/css/data-table.js +103 -0
- package/dist/css/data-table.js.map +1 -0
- package/dist/css/function-card.d.ts +6 -0
- package/dist/css/function-card.d.ts.map +1 -0
- package/dist/css/function-card.js +26 -0
- package/dist/css/function-card.js.map +1 -0
- package/dist/css/help-drawer.d.ts +6 -0
- package/dist/css/help-drawer.d.ts.map +1 -0
- package/dist/css/help-drawer.js +22 -0
- package/dist/css/help-drawer.js.map +1 -0
- package/dist/css/tabs.d.ts +9 -0
- package/dist/css/tabs.d.ts.map +1 -0
- package/dist/css/tabs.js +28 -0
- package/dist/css/tabs.js.map +1 -0
- package/dist/css/theme.d.ts +8 -0
- package/dist/css/theme.d.ts.map +1 -0
- package/dist/css/theme.js +34 -0
- package/dist/css/theme.js.map +1 -0
- package/dist/css.d.ts +13 -0
- package/dist/css.d.ts.map +1 -0
- package/dist/css.js +30 -0
- package/dist/css.js.map +1 -0
- package/dist/generator.d.ts +33 -0
- package/dist/generator.d.ts.map +1 -0
- package/dist/generator.js +161 -0
- package/dist/generator.js.map +1 -0
- package/dist/index.d.ts +21 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +20 -0
- package/dist/index.js.map +1 -0
- package/dist/overview.d.ts +13 -0
- package/dist/overview.d.ts.map +1 -0
- package/dist/overview.js +91 -0
- package/dist/overview.js.map +1 -0
- package/dist/recipes.d.ts +6 -0
- package/dist/recipes.d.ts.map +1 -0
- package/dist/recipes.js +68 -0
- package/dist/recipes.js.map +1 -0
- package/dist/sessions.d.ts +6 -0
- package/dist/sessions.d.ts.map +1 -0
- package/dist/sessions.js +288 -0
- package/dist/sessions.js.map +1 -0
- package/dist/shared/el.d.ts +13 -0
- package/dist/shared/el.d.ts.map +1 -0
- package/dist/shared/el.js +27 -0
- package/dist/shared/el.js.map +1 -0
- package/dist/shared/pagination.d.ts +15 -0
- package/dist/shared/pagination.d.ts.map +1 -0
- package/dist/shared/pagination.js +113 -0
- package/dist/shared/pagination.js.map +1 -0
- package/dist/shared/sortable.d.ts +14 -0
- package/dist/shared/sortable.d.ts.map +1 -0
- package/dist/shared/sortable.js +101 -0
- package/dist/shared/sortable.js.map +1 -0
- package/dist/shared/tab-activators.d.ts +16 -0
- package/dist/shared/tab-activators.d.ts.map +1 -0
- package/dist/shared/tab-activators.js +33 -0
- package/dist/shared/tab-activators.js.map +1 -0
- package/dist/shared/tab-bar.d.ts +8 -0
- package/dist/shared/tab-bar.d.ts.map +1 -0
- package/dist/shared/tab-bar.js +20 -0
- package/dist/shared/tab-bar.js.map +1 -0
- package/dist/shared.d.ts +26 -0
- package/dist/shared.d.ts.map +1 -0
- package/dist/shared.js +39 -0
- package/dist/shared.js.map +1 -0
- package/dist/subtab-bar.d.ts +23 -0
- package/dist/subtab-bar.d.ts.map +1 -0
- package/dist/subtab-bar.js +77 -0
- package/dist/subtab-bar.js.map +1 -0
- package/dist/tool-tab-registry.d.ts +76 -0
- package/dist/tool-tab-registry.d.ts.map +1 -0
- package/dist/tool-tab-registry.js +44 -0
- package/dist/tool-tab-registry.js.map +1 -0
- package/dist/tool-tabs-registrations.d.ts +19 -0
- package/dist/tool-tabs-registrations.d.ts.map +1 -0
- package/dist/tool-tabs-registrations.js +46 -0
- package/dist/tool-tabs-registrations.js.map +1 -0
- package/dist/tool-tabs.d.ts +12 -0
- package/dist/tool-tabs.d.ts.map +1 -0
- package/dist/tool-tabs.js +80 -0
- package/dist/tool-tabs.js.map +1 -0
- package/dist/vendor/cytoscape-bundle.js +600 -0
- package/package.json +52 -0
|
@@ -0,0 +1,379 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* View 8 — "Visualization" (node-link topology, Cytoscape.js + dagre).
|
|
3
|
+
*
|
|
4
|
+
* The non-tabular Code Graph view. A self-contained **Level** control switches
|
|
5
|
+
* what the graph shows:
|
|
6
|
+
*
|
|
7
|
+
* - PACKAGE level (default) — one node per package, one edge per directed
|
|
8
|
+
* package→package coupling. Function granularity across the whole repo
|
|
9
|
+
* produced thousands of nodes, unusable in a node-link layout; the package
|
|
10
|
+
* rollup is the same data the Coupling matrix shows, drawn as a graph.
|
|
11
|
+
* Source: the pre-projected `graph-view-model` JSON blob embedded by
|
|
12
|
+
* `generator.ts` (projector in `graph-view-model.ts`, run at
|
|
13
|
+
* report-generation time) — NOT the raw catalog. Absent blob → empty state.
|
|
14
|
+
* - FUNCTION level — the functions of ONE selected package and the calls
|
|
15
|
+
* among them, projected client-side from the embedded catalog indexes by
|
|
16
|
+
* `gvBuildFunctionElements`. Scoping to a single package keeps the node
|
|
17
|
+
* count bounded. The "Edges" toggle chooses intra-package only (default) or
|
|
18
|
+
* "+ cross-package" (also draw calls leaving the package, to faded external
|
|
19
|
+
* nodes). Honors the Scope (test inclusion) and Kind (multi-select) filters.
|
|
20
|
+
*
|
|
21
|
+
* The view owns its controls (Level / Scope / Package / Kind / Edges) in
|
|
22
|
+
* `gvRenderControls`; the shared Explore filter chips govern the Functions
|
|
23
|
+
* table, NOT this view. Package + Kind only apply at function level, so they
|
|
24
|
+
* are disabled at package level. Renderer is the vendored `cytoscape` global +
|
|
25
|
+
* the `cytoscapeDagre` layout extension, both inlined by
|
|
26
|
+
* `dashboardCytoscapeVendorJs()` ahead of this emitter in `code-paths.ts`.
|
|
27
|
+
*
|
|
28
|
+
* Features: pan/zoom, layout selector (dagre/cose/breadthfirst), name search,
|
|
29
|
+
* node-click impact highlight (direct caller/callee neighbors), and
|
|
30
|
+
* cross-package cycle highlighting (package level). Visual encoding
|
|
31
|
+
* (totalCoupling→size, weight→edge thickness, sccId→accent, external→faded) is
|
|
32
|
+
* applied in `gvStylesheet` / `gvBuildElements` / `gvBuildFunctionElements`.
|
|
33
|
+
*/
|
|
34
|
+
import { dashboardGraphControlsJs } from './graph-controls.js';
|
|
35
|
+
import { dashboardViewGraphStylesheetJs } from './graph-stylesheet.js';
|
|
36
|
+
// @graph-ignore-next-line graph:large-function -- emits one cohesive browser-JS bundle as a String.raw template; bodyLines counts the embedded JS-as-string, not splittable logic (ADR-0014)
|
|
37
|
+
export function dashboardViewGraphJs() {
|
|
38
|
+
return String.raw `
|
|
39
|
+
// Register the dagre layout extension once (the vendored globals are
|
|
40
|
+
// declared earlier in the bundle). Guarded so a double-load is harmless.
|
|
41
|
+
(function registerGraphLayouts() {
|
|
42
|
+
try {
|
|
43
|
+
if (typeof cytoscape === 'function' && typeof cytoscapeDagre !== 'undefined' && !cytoscape.__gvDagreRegistered) {
|
|
44
|
+
cytoscape.use(cytoscapeDagre);
|
|
45
|
+
cytoscape.__gvDagreRegistered = true;
|
|
46
|
+
}
|
|
47
|
+
} catch (e) { /* extension already registered or unavailable */ }
|
|
48
|
+
})();
|
|
49
|
+
|
|
50
|
+
// Available layouts. dagre is the default for mostly-DAG package graphs; cose
|
|
51
|
+
// (built-in force-directed) reads better when cycles dominate;
|
|
52
|
+
// breadthfirst is a cheap hierarchical fallback. No fcose — it needs a
|
|
53
|
+
// fourth vendored extension and the bundle budget is tight.
|
|
54
|
+
var GV_LAYOUTS = [
|
|
55
|
+
{ id: 'dagre', label: 'Dagre (layered)' },
|
|
56
|
+
{ id: 'cose', label: 'Cose (force)' },
|
|
57
|
+
{ id: 'breadthfirst', label: 'Breadthfirst' },
|
|
58
|
+
];
|
|
59
|
+
var gvCurrentLayout = 'dagre';
|
|
60
|
+
var gvCy = null;
|
|
61
|
+
|
|
62
|
+
// ---- Visualization control state (self-contained; NOT the shared Explore
|
|
63
|
+
// filter bar). These live in module vars so they survive the in-place
|
|
64
|
+
// re-render each control change triggers (gvRenderGraph). ----
|
|
65
|
+
// gvLevel 'package' (default) → package→package rollup blob.
|
|
66
|
+
// 'function' → the selected package's functions.
|
|
67
|
+
// gvIncludeTests Scope dropdown: false = production only (default).
|
|
68
|
+
// gvSelectedPackage single package chosen at function level (null = none).
|
|
69
|
+
// gvKinds multi-selected function kinds ([] = all).
|
|
70
|
+
// gvCrossPackage function-level "Edges" toggle: false = intra-package
|
|
71
|
+
// (default), true = also draw edges leaving the package
|
|
72
|
+
// to faded external function nodes.
|
|
73
|
+
var gvLevel = 'package';
|
|
74
|
+
var gvIncludeTests = false;
|
|
75
|
+
var gvSelectedPackage = null;
|
|
76
|
+
var gvKinds = [];
|
|
77
|
+
var gvCrossPackage = false;
|
|
78
|
+
// The active Escape handler, tracked so each re-render replaces (not stacks)
|
|
79
|
+
// its document-level keydown listener.
|
|
80
|
+
var gvEscHandler = null;
|
|
81
|
+
|
|
82
|
+
function gvLoadViewModel() {
|
|
83
|
+
var blob = document.getElementById('graph-view-model');
|
|
84
|
+
if (!blob || !blob.textContent) return null;
|
|
85
|
+
try { return JSON.parse(blob.textContent); } catch (e) { return null; }
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// The init loop renders EVERY Code Graph panel, not just the active one — the
|
|
89
|
+
// row-table views re-render in O(rows), but a full Cytoscape mount + dagre
|
|
90
|
+
// layout is far from free. Defer that work until this panel is actually
|
|
91
|
+
// visible: the panel orchestrator toggles an 'active' class on the live
|
|
92
|
+
// '.code-paths-view' panel. A container that is a panel but not active is
|
|
93
|
+
// hidden → skip the mount (it runs on activation). A container that is NOT a
|
|
94
|
+
// panel (e.g. a unit-test harness div) is treated as visible so direct
|
|
95
|
+
// render() calls mount.
|
|
96
|
+
function gvPanelHidden(container) {
|
|
97
|
+
return !!(container && container.classList &&
|
|
98
|
+
container.classList.contains('code-paths-view') &&
|
|
99
|
+
!container.classList.contains('active'));
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Map a sccId to a stable hue so cross-package cyclic clusters are grouped.
|
|
103
|
+
function gvSccColor(sccId) {
|
|
104
|
+
if (!sccId) return null;
|
|
105
|
+
var h = 0;
|
|
106
|
+
for (var i = 0; i < sccId.length; i++) { h = (h * 31 + sccId.charCodeAt(i)) % 360; }
|
|
107
|
+
return 'hsl(' + h + ', 70%, 55%)';
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function gvBuildElements(vm) {
|
|
111
|
+
var elements = [];
|
|
112
|
+
for (var i = 0; i < vm.nodes.length; i++) {
|
|
113
|
+
var n = vm.nodes[i];
|
|
114
|
+
elements.push({
|
|
115
|
+
group: 'nodes',
|
|
116
|
+
data: {
|
|
117
|
+
id: n.id,
|
|
118
|
+
label: n.label,
|
|
119
|
+
totalCoupling: n.totalCoupling || 0,
|
|
120
|
+
sccId: n.sccId || null,
|
|
121
|
+
sccColor: gvSccColor(n.sccId),
|
|
122
|
+
},
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
for (var j = 0; j < vm.edges.length; j++) {
|
|
126
|
+
var e = vm.edges[j];
|
|
127
|
+
elements.push({
|
|
128
|
+
group: 'edges',
|
|
129
|
+
data: {
|
|
130
|
+
id: 'e' + j,
|
|
131
|
+
source: e.source,
|
|
132
|
+
target: e.target,
|
|
133
|
+
weight: e.weight || 1,
|
|
134
|
+
isCycleEdge: !!e.isCycleEdge,
|
|
135
|
+
},
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
return elements;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
${dashboardViewGraphStylesheetJs()}
|
|
142
|
+
|
|
143
|
+
function gvLayoutOptions(layoutId) {
|
|
144
|
+
if (layoutId === 'dagre') {
|
|
145
|
+
return { name: 'dagre', rankDir: 'LR', nodeSep: 24, rankSep: 64, fit: true, padding: 24 };
|
|
146
|
+
}
|
|
147
|
+
if (layoutId === 'breadthfirst') {
|
|
148
|
+
return { name: 'breadthfirst', directed: true, spacingFactor: 1.2, fit: true, padding: 24 };
|
|
149
|
+
}
|
|
150
|
+
return { name: 'cose', animate: false, fit: true, padding: 24, nodeRepulsion: 6000 };
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function gvRunLayout(layoutId) {
|
|
154
|
+
if (!gvCy) return;
|
|
155
|
+
gvCurrentLayout = layoutId;
|
|
156
|
+
var layout = gvCy.layout(gvLayoutOptions(layoutId));
|
|
157
|
+
layout.run();
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// The Layout selector and the "Highlight cycles" toggle now live in the single
|
|
161
|
+
// control toolbar built by gvRenderControls (graph-controls.ts) — Layout as a
|
|
162
|
+
// dropdown matching the other controls. gvRunLayout / gvSccHighlight /
|
|
163
|
+
// gvApplySccHighlight (below) are the shared handlers those controls call.
|
|
164
|
+
|
|
165
|
+
// Emphasize cross-package cyclic clusters on the live graph. A node is "in a
|
|
166
|
+
// cycle" when it carries an sccId; an edge when isCycleEdge is set. Toggling
|
|
167
|
+
// off clears the emphasis.
|
|
168
|
+
var gvSccHighlight = false;
|
|
169
|
+
|
|
170
|
+
function gvApplySccHighlight() {
|
|
171
|
+
if (!gvCy) return;
|
|
172
|
+
gvCy.batch(function() {
|
|
173
|
+
gvCy.elements().removeClass('gv-scc-member gv-scc-edge gv-scc-dimmed');
|
|
174
|
+
if (!gvSccHighlight) return;
|
|
175
|
+
gvCy.nodes().forEach(function(n) {
|
|
176
|
+
if (n.data('sccId')) n.addClass('gv-scc-member');
|
|
177
|
+
else n.addClass('gv-scc-dimmed');
|
|
178
|
+
});
|
|
179
|
+
gvCy.edges().forEach(function(ed) {
|
|
180
|
+
if (ed.data('isCycleEdge')) ed.addClass('gv-scc-edge');
|
|
181
|
+
else ed.addClass('gv-scc-dimmed');
|
|
182
|
+
});
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
function gvClearImpact() {
|
|
187
|
+
if (!gvCy) return;
|
|
188
|
+
gvCy.elements().removeClass('gv-selected gv-upstream gv-downstream gv-dimmed');
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// Impact highlight at package granularity: light the clicked package, its
|
|
192
|
+
// direct caller packages (incomers) and direct callee packages (outgoers).
|
|
193
|
+
// Built straight off the live Cytoscape adjacency rather than a function-level
|
|
194
|
+
// index, since the nodes ARE packages here.
|
|
195
|
+
function gvApplyImpact(seedId) {
|
|
196
|
+
if (!gvCy) return;
|
|
197
|
+
var seed = gvCy.getElementById(seedId);
|
|
198
|
+
if (!seed || seed.length === 0) return;
|
|
199
|
+
var upstream = {}; // packages that call the seed
|
|
200
|
+
var downstream = {}; // packages the seed calls
|
|
201
|
+
seed.incomers('node').forEach(function(n) { if (n.id() !== seedId) upstream[n.id()] = true; });
|
|
202
|
+
seed.outgoers('node').forEach(function(n) { if (n.id() !== seedId) downstream[n.id()] = true; });
|
|
203
|
+
gvCy.batch(function() {
|
|
204
|
+
gvCy.elements().removeClass('gv-selected gv-upstream gv-downstream');
|
|
205
|
+
gvCy.elements().addClass('gv-dimmed');
|
|
206
|
+
gvCy.nodes().forEach(function(n) {
|
|
207
|
+
var id = n.id();
|
|
208
|
+
if (id === seedId) { n.removeClass('gv-dimmed').addClass('gv-selected'); }
|
|
209
|
+
else if (upstream[id]) { n.removeClass('gv-dimmed').addClass('gv-upstream'); }
|
|
210
|
+
else if (downstream[id]) { n.removeClass('gv-dimmed').addClass('gv-downstream'); }
|
|
211
|
+
});
|
|
212
|
+
// Un-dim edges incident to the seed so the lit neighborhood reads as
|
|
213
|
+
// connected coupling, not isolated nodes.
|
|
214
|
+
gvCy.edges().forEach(function(ed) {
|
|
215
|
+
var s = ed.source().id();
|
|
216
|
+
var t = ed.target().id();
|
|
217
|
+
if (s === seedId || t === seedId) ed.removeClass('gv-dimmed');
|
|
218
|
+
});
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// Search box (DOM, above the canvas). Filters by PACKAGE NAME. Reuses the same
|
|
223
|
+
// fuzzy index the table views use (fuzzyMatch over the node labels = package
|
|
224
|
+
// names). A hit centers + fits the matched node and flags it; non-matches
|
|
225
|
+
// fade. Clearing restores opacity.
|
|
226
|
+
function gvRenderSearchBox(host) {
|
|
227
|
+
var input = el('input', {
|
|
228
|
+
type: 'search',
|
|
229
|
+
class: 'search-input code-paths-graph-search',
|
|
230
|
+
id: 'code-paths-graph-search-input',
|
|
231
|
+
// Labels are package names at package level, function names at function
|
|
232
|
+
// level; the search matches whatever the live node labels are.
|
|
233
|
+
placeholder: gvLevel === 'function' ? 'Find a function by name…' : 'Find a package by name…',
|
|
234
|
+
});
|
|
235
|
+
input.addEventListener('input', function(e) {
|
|
236
|
+
gvApplySearch((e.target && e.target.value) || '');
|
|
237
|
+
});
|
|
238
|
+
host.appendChild(input);
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
function gvApplySearch(query) {
|
|
242
|
+
if (!gvCy) return;
|
|
243
|
+
var q = (query || '').trim();
|
|
244
|
+
gvCy.nodes().removeClass('gv-search-hit gv-search-fade');
|
|
245
|
+
if (q.length === 0) return;
|
|
246
|
+
// The view-model label is the package name; match against it directly
|
|
247
|
+
// (the fuzzy scorer lives in search.js, emitted earlier in the bundle).
|
|
248
|
+
var labels = gvCy.nodes().map(function(n) { return n.data('label') || ''; });
|
|
249
|
+
var matches = (typeof fuzzyMatch === 'function') ? fuzzyMatch(q, labels) : [];
|
|
250
|
+
var hitLabels = {};
|
|
251
|
+
for (var i = 0; i < matches.length; i++) hitLabels[matches[i].name] = true;
|
|
252
|
+
var hitCollection = gvCy.collection();
|
|
253
|
+
gvCy.nodes().forEach(function(n) {
|
|
254
|
+
if (hitLabels[n.data('label')]) { n.addClass('gv-search-hit'); hitCollection = hitCollection.union(n); }
|
|
255
|
+
else { n.addClass('gv-search-fade'); }
|
|
256
|
+
});
|
|
257
|
+
if (hitCollection.length > 0) {
|
|
258
|
+
try { gvCy.center(hitCollection); gvCy.fit(hitCollection, 120); } catch (e) { /* ignore */ }
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
${dashboardGraphControlsJs()}
|
|
263
|
+
|
|
264
|
+
// The actual render driver — called by the view's render() AND by every
|
|
265
|
+
// control-change handler (so a control change re-renders in place). Mirrors
|
|
266
|
+
// the old render() but branches on gvLevel for the element source. Package
|
|
267
|
+
// level keeps the historical empty-state ORDER (view-model check before the
|
|
268
|
+
// cytoscape check) so the structural view tests stay valid.
|
|
269
|
+
function gvRenderGraph(container, catalog, indexes) {
|
|
270
|
+
while (container.firstChild) container.removeChild(container.firstChild);
|
|
271
|
+
// Defer the expensive mount while this panel is hidden (see gvPanelHidden).
|
|
272
|
+
// activateView() re-renders the panel once it becomes visible.
|
|
273
|
+
if (gvPanelHidden(container)) return;
|
|
274
|
+
|
|
275
|
+
// Section heading + ⓘ help button, consistent with the Coupling and
|
|
276
|
+
// Functions views (makeSectionHeading wires the button to openHelpDrawer for
|
|
277
|
+
// this view's id, which surfaces the 'graph' view's help sections).
|
|
278
|
+
container.appendChild(makeSectionHeading('Visualization', 'graph'));
|
|
279
|
+
// The control grid includes the search box and the Highlight-cycles toggle
|
|
280
|
+
// (row 1, cols 3-4), so there's nothing else to render here.
|
|
281
|
+
gvRenderControls(container, catalog, indexes);
|
|
282
|
+
|
|
283
|
+
var elements;
|
|
284
|
+
if (gvLevel === 'function') {
|
|
285
|
+
if (typeof cytoscape !== 'function') {
|
|
286
|
+
container.appendChild(el('div', { class: 'empty', text: 'Graph renderer unavailable.' }));
|
|
287
|
+
return;
|
|
288
|
+
}
|
|
289
|
+
if (!gvSelectedPackage) {
|
|
290
|
+
container.appendChild(el('div', { class: 'empty', text: 'Select a package to view its functions.' }));
|
|
291
|
+
return;
|
|
292
|
+
}
|
|
293
|
+
elements = gvBuildFunctionElements(indexes, gvSelectedPackage, gvIncludeTests, gvKinds, gvCrossPackage);
|
|
294
|
+
if (!elements || elements.length === 0) {
|
|
295
|
+
container.appendChild(el('div', { class: 'empty', text: 'No functions in this package.' }));
|
|
296
|
+
return;
|
|
297
|
+
}
|
|
298
|
+
} else {
|
|
299
|
+
var vm = gvLoadViewModel();
|
|
300
|
+
if (!vm || !vm.nodes || vm.nodes.length === 0) {
|
|
301
|
+
container.appendChild(el('div', { class: 'empty', text: 'No graph to display.' }));
|
|
302
|
+
return;
|
|
303
|
+
}
|
|
304
|
+
if (typeof cytoscape !== 'function') {
|
|
305
|
+
container.appendChild(el('div', { class: 'empty', text: 'Graph renderer unavailable.' }));
|
|
306
|
+
return;
|
|
307
|
+
}
|
|
308
|
+
elements = gvBuildElements(vm);
|
|
309
|
+
if (elements.length === 0) {
|
|
310
|
+
container.appendChild(el('div', { class: 'empty', text: 'No packages to display.' }));
|
|
311
|
+
return;
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
var canvas = el('div', { class: 'code-paths-graph-canvas', id: 'code-paths-graph-canvas' });
|
|
316
|
+
container.appendChild(canvas);
|
|
317
|
+
|
|
318
|
+
// Mounting can throw in environments without a real 2D canvas (e.g. headless
|
|
319
|
+
// test runners). Fail soft — the rest of the page stays usable.
|
|
320
|
+
try {
|
|
321
|
+
gvCy = cytoscape({
|
|
322
|
+
container: canvas,
|
|
323
|
+
elements: elements,
|
|
324
|
+
style: gvStylesheet(),
|
|
325
|
+
layout: gvLayoutOptions(gvCurrentLayout),
|
|
326
|
+
wheelSensitivity: 0.2,
|
|
327
|
+
minZoom: 0.05,
|
|
328
|
+
maxZoom: 4,
|
|
329
|
+
});
|
|
330
|
+
} catch (e) {
|
|
331
|
+
gvCy = null;
|
|
332
|
+
canvas.appendChild(el('div', { class: 'empty', text: 'Graph renderer could not initialize in this environment.' }));
|
|
333
|
+
return;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
// Click a node → highlight its direct caller/callee neighbors. Background
|
|
337
|
+
// click or Esc clears. Replace (don't stack) the document keydown listener.
|
|
338
|
+
gvCy.on('tap', 'node', function(evt) { gvApplyImpact(evt.target.id()); });
|
|
339
|
+
gvCy.on('tap', function(evt) { if (evt.target === gvCy) gvClearImpact(); });
|
|
340
|
+
if (gvEscHandler) { try { document.removeEventListener('keydown', gvEscHandler); } catch (e) { /* ignore */ } }
|
|
341
|
+
gvEscHandler = function(e) { if (e.key === 'Escape') gvClearImpact(); };
|
|
342
|
+
document.addEventListener('keydown', gvEscHandler);
|
|
343
|
+
|
|
344
|
+
// Re-apply the cycle emphasis if the toggle was left on across a re-render.
|
|
345
|
+
gvApplySccHighlight();
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
views.push({
|
|
349
|
+
id: 'graph',
|
|
350
|
+
label: 'Visualization',
|
|
351
|
+
help: {
|
|
352
|
+
title: 'Visualization',
|
|
353
|
+
sections: [
|
|
354
|
+
{ heading: 'What this is', body: 'A node-link visualization of the call graph, rendered with Cytoscape.js. At Package level each node is a package and each edge is the directed coupling from one package into another; node size reflects total coupling (calls in + calls out) and edge thickness reflects the number of call edges. At Function level it shows the functions of one selected package and the calls among them.' },
|
|
355
|
+
{ heading: 'Levels', body: 'Use the Level control to switch between Package (the whole-repo package rollup, the same data as the Coupling matrix) and Function (one package at a time). Function level enables the Package picker (which package to show) and the Kind multi-select, plus an Edges toggle: "Intra-package" shows only calls inside the package; "+ cross-package" also draws calls leaving the package to faded external nodes. The Scope control (production only vs include tests) applies at both levels.' },
|
|
356
|
+
{ heading: 'Why you care', body: 'The table views project the graph into rankings and lists, and the Coupling matrix shows the same package data as a grid. This view shows that topology directly — hub packages, tightly-coupled clusters, and circular package dependencies at package level; the internal call structure of a single package at function level.' },
|
|
357
|
+
{ heading: 'How to read it', body: 'Bigger nodes are more coupled; thicker edges carry more calls. Use the layout selector to switch between layered (dagre), force (cose), and hierarchical (breadthfirst). The matrix on the Coupling tab is the package-level data in tabular form.' },
|
|
358
|
+
{ heading: 'What to do', body: 'Pan and zoom to explore. Type in the search box to center and highlight a node by name; non-matches fade. Click a node to trace its direct callers (upstream) and callees (downstream).' },
|
|
359
|
+
{ heading: 'Cross-package cycles', body: 'Strongly-connected components are groups of packages that can all reach each other through call edges (found via Tarjan’s algorithm). Click "Highlight cycles" in the toolbar to emphasize cycle members and cycle edges while dimming the acyclic remainder. A cycle between packages is usually a layering smell. Break it by extracting the shared protocol into a third package both sides depend on, or by inverting one call into a callback/event.' },
|
|
360
|
+
],
|
|
361
|
+
},
|
|
362
|
+
render(container, catalog, indexes, filterState) {
|
|
363
|
+
// The shared Explore filterState is intentionally NOT consulted here — this
|
|
364
|
+
// view owns its own controls (gvRenderControls). All rendering lives in
|
|
365
|
+
// gvRenderGraph so control-change handlers can re-render in place.
|
|
366
|
+
gvRenderGraph(container, catalog, indexes);
|
|
367
|
+
},
|
|
368
|
+
onActivate() {
|
|
369
|
+
// The canvas needs a measured size before fit() — defer one frame so
|
|
370
|
+
// the container has finished switching from display:none to block.
|
|
371
|
+
if (!gvCy) return;
|
|
372
|
+
setTimeout(function() {
|
|
373
|
+
try { gvCy.resize(); gvCy.fit(undefined, 24); } catch (e) { /* ignore */ }
|
|
374
|
+
}, 0);
|
|
375
|
+
},
|
|
376
|
+
});
|
|
377
|
+
`;
|
|
378
|
+
}
|
|
379
|
+
//# sourceMappingURL=view-graph.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"view-graph.js","sourceRoot":"","sources":["../../src/code-paths/view-graph.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AAEH,OAAO,EAAE,wBAAwB,EAAE,MAAM,qBAAqB,CAAC;AAC/D,OAAO,EAAE,8BAA8B,EAAE,MAAM,uBAAuB,CAAC;AAEvE,6LAA6L;AAC7L,MAAM,UAAU,oBAAoB;IAClC,OAAO,MAAM,CAAC,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAuGjB,8BAA8B,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAyHhC,wBAAwB,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAmH3B,CAAC;AACF,CAAC"}
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `defineRankedView` — JS-string emitter for ranked-list views.
|
|
3
|
+
*
|
|
4
|
+
* Hot, Big, Wide, and Untested all share the same skeleton:
|
|
5
|
+
*
|
|
6
|
+
* 1. Bail out with an empty-state message if the catalog is missing.
|
|
7
|
+
* 2. Walk `indexes.byBodyHash.values()`, applying the active filter
|
|
8
|
+
* chips (`passesFilter`) and an optional view-specific predicate.
|
|
9
|
+
* 3. Compute a per-occurrence numeric metric.
|
|
10
|
+
* 4. Sort descending by that metric.
|
|
11
|
+
* 5. Render via `renderFunctionRows` with the supplied column
|
|
12
|
+
* definitions, or show an empty-state if everything was filtered
|
|
13
|
+
* out.
|
|
14
|
+
*
|
|
15
|
+
* This helper accepts a declarative config and emits the JS source for
|
|
16
|
+
* one view. Each `view-*.ts` that fits the shape collapses to ~15 lines
|
|
17
|
+
* of config; bespoke views (coupling, sccs, search) keep their own
|
|
18
|
+
* emitters.
|
|
19
|
+
*
|
|
20
|
+
* The `metric` and `predicate` (and column `value`) fields are JS
|
|
21
|
+
* source strings that close over the in-page locals `occ`, `o`,
|
|
22
|
+
* `indexes`, `filterState`, etc. The helper splices them directly into
|
|
23
|
+
* the emitted view; that is how the existing per-view emitters work
|
|
24
|
+
* too. The skeleton is the only place that knows the rank-and-render
|
|
25
|
+
* shape — adding a new ranked view means writing a config, not a new
|
|
26
|
+
* `view-*.ts` file from scratch.
|
|
27
|
+
*/
|
|
28
|
+
interface RankedViewColumn {
|
|
29
|
+
/** Header label (e.g. "Function", "Callers"). */
|
|
30
|
+
label: string;
|
|
31
|
+
/**
|
|
32
|
+
* JS source for the cell value, spliced VERBATIM into the emitted
|
|
33
|
+
* view body — there is no TS type-checking on this expression.
|
|
34
|
+
*
|
|
35
|
+
* Conventionally a single-arg arrow function `o => …`, where `o`
|
|
36
|
+
* is the augmented occurrence: the original `occ` (graph function
|
|
37
|
+
* descriptor) plus a `__metric` field carrying the ranking value
|
|
38
|
+
* plus any extras the view's `rowExtras` returned (e.g. `__thumb`
|
|
39
|
+
* in Wide). The expression closes over the in-page locals
|
|
40
|
+
* `displayName` and `packageOfPath` (declared by `dashboardPathUtilsJs`)
|
|
41
|
+
* and any helper the view's `preamble` declared.
|
|
42
|
+
*
|
|
43
|
+
* A typo here (`o => o.callerz`) compiles, lints, and ships — it
|
|
44
|
+
* will fail at runtime as an undefined property read, not a build
|
|
45
|
+
* error. Keep these expressions minimal and prefer pulling logic
|
|
46
|
+
* into `preamble`-declared helpers when complexity grows.
|
|
47
|
+
*
|
|
48
|
+
* Example: `o => displayName(o.simpleName)`.
|
|
49
|
+
*/
|
|
50
|
+
value: string;
|
|
51
|
+
}
|
|
52
|
+
interface RankedViewHelpSection {
|
|
53
|
+
heading: string;
|
|
54
|
+
body: string;
|
|
55
|
+
}
|
|
56
|
+
interface RankedViewHelp {
|
|
57
|
+
title: string;
|
|
58
|
+
sections: RankedViewHelpSection[];
|
|
59
|
+
}
|
|
60
|
+
export interface RankedViewConfig {
|
|
61
|
+
/** Stable id (e.g. `'hot'`). Must match the existing tab id. */
|
|
62
|
+
id: string;
|
|
63
|
+
/** Tab label. */
|
|
64
|
+
label: string;
|
|
65
|
+
/** Help-drawer copy. */
|
|
66
|
+
help: RankedViewHelp;
|
|
67
|
+
/**
|
|
68
|
+
* JS source for the metric expression, spliced VERBATIM into the
|
|
69
|
+
* emitted view body — there is no TS type-checking on this
|
|
70
|
+
* expression.
|
|
71
|
+
*
|
|
72
|
+
* Closes over `occ` (the graph function occurrence) and `indexes`
|
|
73
|
+
* (the in-page indexes built by `buildIndexes`). The expression is
|
|
74
|
+
* normally a non-negative number — `ranked.sort` uses `b.metric -
|
|
75
|
+
* a.metric` so larger values rank higher.
|
|
76
|
+
*
|
|
77
|
+
* Sentinel: returning `false` skips the row entirely (used by Hot
|
|
78
|
+
* and Wide to drop functions with zero callers / zero parameters
|
|
79
|
+
* rather than rank them at zero). Returning any other falsy value
|
|
80
|
+
* (`0`, `null`, `undefined`, `NaN`) does NOT skip — only literal
|
|
81
|
+
* `false`. New views that want a "drop if predicate doesn't match"
|
|
82
|
+
* filter should put it in `predicate` instead; the sentinel exists
|
|
83
|
+
* because Hot and Wide want different drop conditions per call,
|
|
84
|
+
* not a fixed predicate.
|
|
85
|
+
*
|
|
86
|
+
* Example: `(indexes.callers.get(occ.bodyHash) || []).length`.
|
|
87
|
+
*/
|
|
88
|
+
metric: string;
|
|
89
|
+
/**
|
|
90
|
+
* Optional JS source for a predicate expression, spliced VERBATIM
|
|
91
|
+
* into the emitted view body — there is no TS type-checking on
|
|
92
|
+
* this expression.
|
|
93
|
+
*
|
|
94
|
+
* Closes over `occ` and `filterState`. Truthy = keep; falsy = skip.
|
|
95
|
+
* Defaults to `passesFilter(occ, filterState)`. When a predicate
|
|
96
|
+
* is supplied it REPLACES the default `passesFilter` call entirely
|
|
97
|
+
* — include it in the predicate (e.g. `passesFilter(occ,
|
|
98
|
+
* filterState) && occ.calls.length === 0`) when you still want
|
|
99
|
+
* chip filtering.
|
|
100
|
+
*/
|
|
101
|
+
predicate?: string;
|
|
102
|
+
/**
|
|
103
|
+
* Optional JS source emitting extra fields to splice into the row
|
|
104
|
+
* via `Object.assign`, spliced VERBATIM into the emitted view body.
|
|
105
|
+
* Closes over `occ` and `metric`. Defaults to `{}`. Used by Wide
|
|
106
|
+
* to splice in a `__thumb` parameter list.
|
|
107
|
+
*/
|
|
108
|
+
rowExtras?: string;
|
|
109
|
+
/**
|
|
110
|
+
* Optional JS source for additional helper declarations to emit
|
|
111
|
+
* inside the `render` body, before the metric loop. Spliced
|
|
112
|
+
* VERBATIM. Used by Wide for the `paramThumb` helper. Helpers
|
|
113
|
+
* declared here are visible to `metric`, `predicate`, `rowExtras`,
|
|
114
|
+
* and the column `value` expressions.
|
|
115
|
+
*/
|
|
116
|
+
preamble?: string;
|
|
117
|
+
/** Columns rendered by `renderFunctionRows`. */
|
|
118
|
+
columns: RankedViewColumn[];
|
|
119
|
+
/** Section heading text (e.g. "Big functions"). */
|
|
120
|
+
headingText: string;
|
|
121
|
+
/** Empty-state message when the filter strips everything. */
|
|
122
|
+
emptyMessage: string;
|
|
123
|
+
/**
|
|
124
|
+
* When true, render a search input above the table that filters the
|
|
125
|
+
* ranked rows by function simple-name (case-insensitive substring).
|
|
126
|
+
* Typing re-filters the table in place; the search box auto-focuses
|
|
127
|
+
* when the view activates. Used by the Functions view to absorb the
|
|
128
|
+
* former standalone Search subtab.
|
|
129
|
+
*/
|
|
130
|
+
searchByName?: boolean;
|
|
131
|
+
/**
|
|
132
|
+
* When true, render a Kind single-select and a Package single-select
|
|
133
|
+
* dropdown alongside the search box (in one controls row above the table)
|
|
134
|
+
* that further narrow the ranked rows. Both default to "all"; selecting a
|
|
135
|
+
* value re-filters the table in place. Combines with `searchByName` (Kind,
|
|
136
|
+
* Package, then the search box, in that order). Used by the Functions view.
|
|
137
|
+
*/
|
|
138
|
+
filterByKindPackage?: boolean;
|
|
139
|
+
/**
|
|
140
|
+
* When set, render a checkbox toggle AFTER the search box that narrows the
|
|
141
|
+
* table to rows matching `predicate` when checked (off by default).
|
|
142
|
+
*
|
|
143
|
+
* `predicate` is JS source spliced VERBATIM into the row filter — a boolean
|
|
144
|
+
* expression closing over `occ` (and any `preamble`-declared helper). Used by
|
|
145
|
+
* the Functions view for a "Test-only" toggle (`distTestOnly(occ)`).
|
|
146
|
+
*/
|
|
147
|
+
filterToggle?: {
|
|
148
|
+
label: string;
|
|
149
|
+
predicate: string;
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
export declare function defineRankedView(config: RankedViewConfig): string;
|
|
153
|
+
export {};
|
|
154
|
+
//# sourceMappingURL=view-template.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"view-template.d.ts","sourceRoot":"","sources":["../../src/code-paths/view-template.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,UAAU,gBAAgB;IACxB,iDAAiD;IACjD,KAAK,EAAE,MAAM,CAAC;IACd;;;;;;;;;;;;;;;;;;OAkBG;IACH,KAAK,EAAE,MAAM,CAAC;CACf;AAED,UAAU,qBAAqB;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,UAAU,cAAc;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,qBAAqB,EAAE,CAAC;CACnC;AAED,MAAM,WAAW,gBAAgB;IAC/B,gEAAgE;IAChE,EAAE,EAAE,MAAM,CAAC;IACX,iBAAiB;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,wBAAwB;IACxB,IAAI,EAAE,cAAc,CAAC;IACrB;;;;;;;;;;;;;;;;;;;;OAoBG;IACH,MAAM,EAAE,MAAM,CAAC;IACf;;;;;;;;;;;OAWG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;;;OAKG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;;;;OAMG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,gDAAgD;IAChD,OAAO,EAAE,gBAAgB,EAAE,CAAC;IAC5B,mDAAmD;IACnD,WAAW,EAAE,MAAM,CAAC;IACpB,6DAA6D;IAC7D,YAAY,EAAE,MAAM,CAAC;IACrB;;;;;;OAMG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB;;;;;;OAMG;IACH,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B;;;;;;;OAOG;IACH,YAAY,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC;CACrD;AA8BD,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,gBAAgB,GAAG,MAAM,CAmKjE"}
|