@zenfg/inspector 0.1.0-beta.2 → 0.1.0-beta.4
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/GUIDE.md +182 -0
- package/README.md +60 -26
- package/THEMING.md +124 -0
- package/dist/FrameGraphInspector.d.ts +24 -8
- package/dist/FrameGraphInspector.d.ts.map +1 -1
- package/dist/FrameGraphInspector.js +151 -65
- package/dist/FrameGraphInspector.js.map +1 -1
- package/dist/capture.d.ts +5 -0
- package/dist/capture.d.ts.map +1 -0
- package/dist/capture.js +2 -0
- package/dist/capture.js.map +1 -0
- package/dist/debugCaptureModel.d.ts +28 -4
- package/dist/debugCaptureModel.d.ts.map +1 -1
- package/dist/debugCaptureModel.js +72 -16
- package/dist/debugCaptureModel.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/panelAliasAnalysis.d.ts +1 -0
- package/dist/panelAliasAnalysis.d.ts.map +1 -1
- package/dist/panelAliasAnalysis.js +23 -54
- package/dist/panelAliasAnalysis.js.map +1 -1
- package/dist/panelCytoscapeGraphRenderer.d.ts +18 -3
- package/dist/panelCytoscapeGraphRenderer.d.ts.map +1 -1
- package/dist/panelCytoscapeGraphRenderer.js +183 -41
- package/dist/panelCytoscapeGraphRenderer.js.map +1 -1
- package/dist/panelDetailLayout.d.ts +27 -0
- package/dist/panelDetailLayout.d.ts.map +1 -0
- package/dist/panelDetailLayout.js +174 -0
- package/dist/panelDetailLayout.js.map +1 -0
- package/dist/panelDetailStyles.d.ts +2 -0
- package/dist/panelDetailStyles.d.ts.map +1 -0
- package/dist/panelDetailStyles.js +57 -0
- package/dist/panelDetailStyles.js.map +1 -0
- package/dist/panelDiagnosticsView.d.ts +10 -1
- package/dist/panelDiagnosticsView.d.ts.map +1 -1
- package/dist/panelDiagnosticsView.js +167 -58
- package/dist/panelDiagnosticsView.js.map +1 -1
- package/dist/panelDomHelpers.d.ts.map +1 -1
- package/dist/panelDomHelpers.js +6 -5
- package/dist/panelDomHelpers.js.map +1 -1
- package/dist/panelGraphLayout.d.ts +3 -2
- package/dist/panelGraphLayout.d.ts.map +1 -1
- package/dist/panelGraphLayout.js +20 -5
- package/dist/panelGraphLayout.js.map +1 -1
- package/dist/panelGraphRenderer.d.ts +10 -0
- package/dist/panelGraphRenderer.d.ts.map +1 -1
- package/dist/panelGraphRenderer.js.map +1 -1
- package/dist/panelGraphScene.d.ts +15 -26
- package/dist/panelGraphScene.d.ts.map +1 -1
- package/dist/panelGraphScene.js +191 -208
- package/dist/panelGraphScene.js.map +1 -1
- package/dist/panelGraphSearch.d.ts +16 -0
- package/dist/panelGraphSearch.d.ts.map +1 -0
- package/dist/panelGraphSearch.js +93 -0
- package/dist/panelGraphSearch.js.map +1 -0
- package/dist/panelGraphView.d.ts +82 -0
- package/dist/panelGraphView.d.ts.map +1 -1
- package/dist/panelGraphView.js +42 -9
- package/dist/panelGraphView.js.map +1 -1
- package/dist/panelGraphVisuals.d.ts +18 -6
- package/dist/panelGraphVisuals.d.ts.map +1 -1
- package/dist/panelGraphVisuals.js +156 -116
- package/dist/panelGraphVisuals.js.map +1 -1
- package/dist/panelInspectorView.d.ts +13 -0
- package/dist/panelInspectorView.d.ts.map +1 -1
- package/dist/panelInspectorView.js +326 -46
- package/dist/panelInspectorView.js.map +1 -1
- package/dist/panelListStyles.d.ts +3 -0
- package/dist/panelListStyles.d.ts.map +1 -0
- package/dist/panelListStyles.js +155 -0
- package/dist/panelListStyles.js.map +1 -0
- package/dist/panelMemoryStyles.d.ts +3 -0
- package/dist/panelMemoryStyles.d.ts.map +1 -0
- package/dist/panelMemoryStyles.js +42 -0
- package/dist/panelMemoryStyles.js.map +1 -0
- package/dist/panelMemoryView.d.ts +13 -1
- package/dist/panelMemoryView.d.ts.map +1 -1
- package/dist/panelMemoryView.js +161 -41
- package/dist/panelMemoryView.js.map +1 -1
- package/dist/panelPassesView.d.ts +15 -0
- package/dist/panelPassesView.d.ts.map +1 -1
- package/dist/panelPassesView.js +202 -72
- package/dist/panelPassesView.js.map +1 -1
- package/dist/panelRawView.d.ts +15 -0
- package/dist/panelRawView.d.ts.map +1 -0
- package/dist/panelRawView.js +105 -0
- package/dist/panelRawView.js.map +1 -0
- package/dist/panelResourcesView.d.ts +8 -0
- package/dist/panelResourcesView.d.ts.map +1 -1
- package/dist/panelResourcesView.js +62 -31
- package/dist/panelResourcesView.js.map +1 -1
- package/dist/panelSelection.d.ts +9 -1
- package/dist/panelSelection.d.ts.map +1 -1
- package/dist/panelSelection.js +36 -6
- package/dist/panelSelection.js.map +1 -1
- package/dist/panelTheme.d.ts +13 -0
- package/dist/panelTheme.d.ts.map +1 -0
- package/dist/panelTheme.js +104 -0
- package/dist/panelTheme.js.map +1 -0
- package/dist/panelTypes.d.ts +18 -4
- package/dist/panelTypes.d.ts.map +1 -1
- package/dist/panelTypes.js.map +1 -1
- package/dist/panelVisualTheme.d.ts +176 -71
- package/dist/panelVisualTheme.d.ts.map +1 -1
- package/dist/panelVisualTheme.js +34 -36
- package/dist/panelVisualTheme.js.map +1 -1
- package/dist/panelWorkbenchHelpers.d.ts +12 -1
- package/dist/panelWorkbenchHelpers.d.ts.map +1 -1
- package/dist/panelWorkbenchHelpers.js +63 -2
- package/dist/panelWorkbenchHelpers.js.map +1 -1
- package/dist/panelWorkbenchStyles.d.ts +2 -0
- package/dist/panelWorkbenchStyles.d.ts.map +1 -0
- package/dist/panelWorkbenchStyles.js +101 -0
- package/dist/panelWorkbenchStyles.js.map +1 -0
- package/dist/panelWorkbenchView.d.ts +16 -0
- package/dist/panelWorkbenchView.d.ts.map +1 -1
- package/dist/panelWorkbenchView.js +228 -50
- package/dist/panelWorkbenchView.js.map +1 -1
- package/dist/query.js.map +1 -1
- package/dist/styles.d.ts.map +1 -1
- package/dist/styles.js +161 -205
- package/dist/styles.js.map +1 -1
- package/dist/theme.d.ts +22 -0
- package/dist/theme.d.ts.map +1 -0
- package/dist/theme.js +10 -0
- package/dist/theme.js.map +1 -0
- package/dist/themeDefinitions.d.ts +251 -0
- package/dist/themeDefinitions.d.ts.map +1 -0
- package/dist/themeDefinitions.js +96 -0
- package/dist/themeDefinitions.js.map +1 -0
- package/dist/themePalette.d.ts +66 -0
- package/dist/themePalette.d.ts.map +1 -0
- package/dist/themePalette.js +37 -0
- package/dist/themePalette.js.map +1 -0
- package/dist/themes.css +165 -0
- package/package.json +44 -10
- package/src/FrameGraphInspector.ts +145 -68
- package/src/capture.ts +4 -0
- package/src/debugCaptureModel.ts +97 -21
- package/src/index.ts +6 -2
- package/src/panelAliasAnalysis.ts +24 -70
- package/src/panelCytoscapeGraphRenderer.ts +177 -40
- package/src/panelDetailLayout.ts +155 -0
- package/src/panelDetailStyles.ts +56 -0
- package/src/panelDiagnosticsView.ts +167 -86
- package/src/panelDomHelpers.ts +4 -5
- package/src/panelGraphLayout.ts +20 -5
- package/src/panelGraphRenderer.ts +7 -0
- package/src/panelGraphScene.ts +204 -292
- package/src/panelGraphSearch.ts +96 -0
- package/src/panelGraphView.ts +41 -9
- package/src/panelGraphVisuals.ts +152 -118
- package/src/panelInspectorView.ts +315 -48
- package/src/panelListStyles.ts +154 -0
- package/src/panelMemoryStyles.ts +41 -0
- package/src/panelMemoryView.ts +178 -53
- package/src/panelPassesView.ts +190 -137
- package/src/panelRawView.ts +99 -0
- package/src/panelResourcesView.ts +69 -56
- package/src/panelSelection.ts +39 -7
- package/src/panelTheme.ts +85 -0
- package/src/panelTypes.ts +16 -4
- package/src/panelVisualTheme.ts +38 -38
- package/src/panelWorkbenchHelpers.ts +62 -4
- package/src/panelWorkbenchStyles.ts +100 -0
- package/src/panelWorkbenchView.ts +201 -52
- package/src/query.ts +11 -11
- package/src/styles.ts +162 -205
- package/src/theme.ts +24 -0
- package/src/themeDefinitions.ts +103 -0
- package/src/themePalette.ts +44 -0
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import type { FrameGraphCaptureRequest } from './capture.ts';
|
|
2
|
+
import type { InspectorTheme } from './theme.ts';
|
|
3
|
+
import { InspectorThemeController, resolveGraphTheme } from './panelTheme.ts';
|
|
1
4
|
import {
|
|
2
5
|
FrameGraphSnapshotValidationError,
|
|
3
6
|
decodeFrameGraphSnapshot,
|
|
@@ -11,20 +14,23 @@ import {
|
|
|
11
14
|
type FrameGraphDebugViewModel,
|
|
12
15
|
} from './debugCaptureModel.ts';
|
|
13
16
|
import { createToolbarButton } from './panelDomHelpers.ts';
|
|
14
|
-
import { createPanelIcon } from './panelIcons.ts';
|
|
15
17
|
import {
|
|
16
18
|
destroyGraph,
|
|
17
19
|
fitGraph,
|
|
20
|
+
resolveGraphScene,
|
|
21
|
+
renderGraphLegend,
|
|
18
22
|
} from './panelGraphView.ts';
|
|
19
|
-
import { graphGroupElementId } from './panelGraphScene.ts';
|
|
20
|
-
import { selectionExists } from './panelSelection.ts';
|
|
21
|
-
import type { GraphViewState, Selection } from './panelTypes.ts';
|
|
23
|
+
import { graphGroupElementId, selectionKey } from './panelGraphScene.ts';
|
|
24
|
+
import { resolveNodeSelection, selectionExists } from './panelSelection.ts';
|
|
25
|
+
import type { GraphViewState, Selection, WorkbenchTab } from './panelTypes.ts';
|
|
22
26
|
import { ensureFrameGraphInspectorStyles } from './styles.ts';
|
|
23
27
|
import { sameSelection, type WorkbenchCallbacks } from './panelWorkbenchHelpers.ts';
|
|
24
28
|
import { FrameGraphDebugWorkbench } from './panelWorkbenchView.ts';
|
|
25
29
|
|
|
26
30
|
/** Construction and safety limits for an embedded {@link FrameGraphInspector}. */
|
|
27
31
|
export type FrameGraphInspectorOptions = {
|
|
32
|
+
/** Optional CSS theme. Without one, host variables inherit over the Storm defaults. */
|
|
33
|
+
theme?: InspectorTheme;
|
|
28
34
|
/**
|
|
29
35
|
* Product label shown in the workbench command bar. Pass `false` to hide
|
|
30
36
|
* visible branding while retaining the inspector's accessible name.
|
|
@@ -36,7 +42,7 @@ export type FrameGraphInspectorOptions = {
|
|
|
36
42
|
* Produces a live snapshot when capture is requested. The inspector awaits
|
|
37
43
|
* promises and displays thrown or rejected errors in its status area.
|
|
38
44
|
*/
|
|
39
|
-
captureSnapshot?: () => FrameGraphSnapshot | undefined | Promise<FrameGraphSnapshot | undefined>;
|
|
45
|
+
captureSnapshot?: (request: FrameGraphCaptureRequest) => FrameGraphSnapshot | undefined | Promise<FrameGraphSnapshot | undefined>;
|
|
40
46
|
/**
|
|
41
47
|
* Maximum accepted import size in bytes.
|
|
42
48
|
*
|
|
@@ -74,13 +80,13 @@ const DEFAULT_BRANDING = 'ZenFG Inspector';
|
|
|
74
80
|
export class FrameGraphInspector {
|
|
75
81
|
/** Root element owned by this inspector instance. */
|
|
76
82
|
readonly dom = document.createElement('section');
|
|
83
|
+
private readonly themeController = new InspectorThemeController(this.dom);
|
|
77
84
|
|
|
78
85
|
private readonly body = document.createElement('div');
|
|
79
86
|
private readonly content = document.createElement('div');
|
|
80
87
|
private readonly dropOverlay = document.createElement('div');
|
|
81
|
-
private readonly passesGraphModeButton: HTMLButtonElement;
|
|
82
|
-
private readonly resourcesGraphModeButton: HTMLButtonElement;
|
|
83
88
|
private readonly groupsButton: HTMLButtonElement;
|
|
89
|
+
private readonly declarationsButton: HTMLButtonElement;
|
|
84
90
|
private readonly collapseGroupsButton: HTMLButtonElement;
|
|
85
91
|
private readonly graphView: GraphViewState;
|
|
86
92
|
private readonly workbench: FrameGraphDebugWorkbench;
|
|
@@ -101,6 +107,7 @@ export class FrameGraphInspector {
|
|
|
101
107
|
private copyFeedbackTimeout: number | undefined;
|
|
102
108
|
private readonly maxImportBytes: number;
|
|
103
109
|
private dragDepth = 0;
|
|
110
|
+
private revealRevision = 0;
|
|
104
111
|
private readonly handleDragEnter = (event: DragEvent): void => {
|
|
105
112
|
if (!isFileDrag(event)) return;
|
|
106
113
|
event.preventDefault();
|
|
@@ -160,9 +167,10 @@ export class FrameGraphInspector {
|
|
|
160
167
|
host: document.createElement('div'),
|
|
161
168
|
toolbar: document.createElement('div'),
|
|
162
169
|
legend: graphLegend,
|
|
170
|
+
refreshTheme: () => this.refreshTheme(),
|
|
163
171
|
layoutElementBudget: normalizeLimit(options.maxGraphElements, DEFAULT_MAX_GRAPH_ELEMENTS, 'maxGraphElements'),
|
|
164
|
-
graphMode: 'passes',
|
|
165
172
|
groupsEnabled: true,
|
|
173
|
+
showResourceDeclarations: true,
|
|
166
174
|
expandedGroupPaths: new Set(),
|
|
167
175
|
fitOnNextRender: true,
|
|
168
176
|
};
|
|
@@ -171,32 +179,27 @@ export class FrameGraphInspector {
|
|
|
171
179
|
this.graphView.toolbar.setAttribute('role', 'toolbar');
|
|
172
180
|
this.graphView.toolbar.setAttribute('aria-label', 'Frame graph view controls');
|
|
173
181
|
|
|
174
|
-
this.
|
|
175
|
-
this.resourcesGraphModeButton = createToolbarButton('Resource access', 'Show resource access graph', () => this.setGraphMode('resources'));
|
|
182
|
+
this.declarationsButton = createToolbarButton('Declarations', 'Show resource declaration nodes', () => this.toggleDeclarations());
|
|
176
183
|
this.groupsButton = createToolbarButton('Groups', 'Toggle diagnostic group projection', () => this.toggleGroups());
|
|
177
184
|
this.collapseGroupsButton = createToolbarButton('Collapse All', 'Collapse every diagnostic group', () => this.collapseAllGroups());
|
|
178
|
-
|
|
179
|
-
this.resourcesGraphModeButton.classList.add('zenfg-inspector-mode-button');
|
|
180
|
-
|
|
181
|
-
const modeControls = document.createElement('div');
|
|
182
|
-
modeControls.className = 'zenfg-inspector-graph-mode-controls';
|
|
183
|
-
modeControls.setAttribute('role', 'group');
|
|
184
|
-
modeControls.setAttribute('aria-label', 'Graph mode');
|
|
185
|
-
modeControls.append(this.passesGraphModeButton, this.resourcesGraphModeButton);
|
|
185
|
+
|
|
186
186
|
const actionControls = document.createElement('div');
|
|
187
187
|
actionControls.className = 'zenfg-inspector-graph-action-controls';
|
|
188
188
|
actionControls.append(
|
|
189
|
+
this.declarationsButton,
|
|
189
190
|
this.groupsButton,
|
|
190
191
|
this.collapseGroupsButton,
|
|
191
|
-
|
|
192
|
+
createToolbarButton('Fit', 'Fit graph to view', () => fitGraph(this.graphView)),
|
|
192
193
|
);
|
|
193
|
-
this.graphView.toolbar.append(
|
|
194
|
+
this.graphView.toolbar.append(actionControls);
|
|
194
195
|
|
|
195
196
|
const callbacks: WorkbenchCallbacks = {
|
|
196
197
|
onSelect: (selection) => this.handleSelect(selection),
|
|
197
198
|
onHover: (selection) => this.handleHover(selection),
|
|
198
199
|
onGroupToggle: (pathKey) => this.toggleGroup(pathKey),
|
|
199
200
|
isGroupExpanded: (pathKey) => this.graphView.expandedGroupPaths.has(pathKey),
|
|
201
|
+
onReveal: (selection, tab) => this.handleReveal(selection, tab),
|
|
202
|
+
onNavigate: (tab, filter) => this.workbench.navigate(tab, filter),
|
|
200
203
|
};
|
|
201
204
|
this.workbench = new FrameGraphDebugWorkbench(this.graphView, callbacks, {
|
|
202
205
|
onCapture: () => { void this.captureSnapshot(); },
|
|
@@ -216,22 +219,49 @@ export class FrameGraphInspector {
|
|
|
216
219
|
this.dom.addEventListener('dragend', this.handleDragEnd);
|
|
217
220
|
this.dom.addEventListener('drop', this.handleDrop);
|
|
218
221
|
this.updateCaptureActions();
|
|
219
|
-
this.
|
|
222
|
+
this.updateGraphControls();
|
|
220
223
|
this.showEmptyState();
|
|
221
|
-
|
|
224
|
+
if (options.theme) this.setTheme(options.theme);
|
|
225
|
+
queueMicrotask(() => { if (!this.destroyed) { this.refreshTheme(); this.maybeAutoCapture(); } });
|
|
222
226
|
}
|
|
223
227
|
|
|
228
|
+
/** Replace API-applied variables on this instance, then synchronize graph styles.
|
|
229
|
+
* Pass null to restore host CSS and defaults. Does not change capture or interaction state.
|
|
230
|
+
*/
|
|
231
|
+
setTheme(theme: InspectorTheme | null): void {
|
|
232
|
+
if (this.destroyed) return;
|
|
233
|
+
this.themeController.apply(theme);
|
|
234
|
+
this.refreshTheme();
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/** Re-read final CSS values after external CSS changes and refresh graph and legend.
|
|
238
|
+
* Does not write theme variables, recapture, fit, or rebuild the component.
|
|
239
|
+
*/
|
|
240
|
+
refreshTheme(): void {
|
|
241
|
+
if (this.destroyed) return;
|
|
242
|
+
const theme = resolveGraphTheme(this.dom);
|
|
243
|
+
this.graphView.theme = theme;
|
|
244
|
+
this.graphView.renderer?.setTheme?.(theme);
|
|
245
|
+
if (this.viewModel) renderGraphLegend(this.graphView.legend, this.viewModel, theme, this.graphView.showResourceDeclarations);
|
|
246
|
+
}
|
|
247
|
+
|
|
224
248
|
/**
|
|
225
249
|
* Replaces or removes the live-capture provider.
|
|
226
250
|
*
|
|
227
251
|
* @remarks Installing a provider may immediately begin an asynchronous
|
|
228
|
-
* capture when no snapshot is displayed.
|
|
252
|
+
* capture when no snapshot is displayed. Replacing or removing a provider
|
|
253
|
+
* invalidates its pending capture results without cancelling the provider
|
|
254
|
+
* promise. An already displayed snapshot and any pending import are preserved.
|
|
229
255
|
*/
|
|
230
256
|
setCaptureSnapshotProvider(provider: FrameGraphInspectorOptions['captureSnapshot']): void {
|
|
231
|
-
|
|
257
|
+
if (this.destroyed || this.captureSnapshotCallback === provider) return;
|
|
258
|
+
if (this.capturing) {
|
|
259
|
+
this.operationRevision += 1;
|
|
260
|
+
this.capturing = false;
|
|
261
|
+
}
|
|
232
262
|
this.captureSnapshotCallback = provider;
|
|
233
|
-
if (
|
|
234
|
-
if (!provider && !this.viewModel) this.showEmptyState();
|
|
263
|
+
if (!this.viewModel) this.initialAutoCaptureAttempted = false;
|
|
264
|
+
if (!provider && !this.viewModel && !this.importing) this.showEmptyState();
|
|
235
265
|
this.updateCaptureActions();
|
|
236
266
|
this.maybeAutoCapture();
|
|
237
267
|
}
|
|
@@ -257,7 +287,8 @@ export class FrameGraphInspector {
|
|
|
257
287
|
if (!this.viewModel) this.workbench.showEmptyState('capturing', 'Capturing the next rendered frame…');
|
|
258
288
|
this.updateCaptureActions();
|
|
259
289
|
try {
|
|
260
|
-
const
|
|
290
|
+
const request: FrameGraphCaptureRequest = { timing: 'both' };
|
|
291
|
+
const snapshot = await this.captureSnapshotCallback(request);
|
|
261
292
|
if (this.destroyed || revision !== this.operationRevision) return;
|
|
262
293
|
if (!snapshot) {
|
|
263
294
|
this.reportCaptureIssue('No snapshot was produced. Capture again when rendering is active.');
|
|
@@ -282,9 +313,9 @@ export class FrameGraphInspector {
|
|
|
282
313
|
}
|
|
283
314
|
|
|
284
315
|
/**
|
|
285
|
-
* Validates and synchronously displays a programmatic Snapshot 1.
|
|
316
|
+
* Validates and synchronously displays a programmatic Snapshot 1.2 value.
|
|
286
317
|
*
|
|
287
|
-
* @throws {@link FrameGraphSnapshotValidationError} if `snapshot` is invalid.
|
|
318
|
+
* @throws {@link @zenfg/snapshot!index.FrameGraphSnapshotValidationError | FrameGraphSnapshotValidationError} if `snapshot` is invalid.
|
|
288
319
|
*/
|
|
289
320
|
setSnapshot(snapshot: FrameGraphSnapshot): void {
|
|
290
321
|
if (this.destroyed) return;
|
|
@@ -334,10 +365,17 @@ export class FrameGraphInspector {
|
|
|
334
365
|
});
|
|
335
366
|
const sourceFormat = decoded.snapshot.capture.migration?.sourceFormat;
|
|
336
367
|
if (sourceFormat) {
|
|
337
|
-
|
|
368
|
+
// The current import format can differ from retained historical provenance.
|
|
369
|
+
const sourceLabel = {
|
|
370
|
+
'legacy-v0': 'Legacy V0',
|
|
371
|
+
'legacy-candidate-v1': 'Legacy Candidate V1',
|
|
372
|
+
'snapshot-v1.1': 'Snapshot 1.1',
|
|
373
|
+
}[decoded.migrated && decoded.source !== 'v1' ? decoded.source : sourceFormat];
|
|
374
|
+
const version = decoded.snapshot.version;
|
|
375
|
+
const targetLabel = `Snapshot ${version.major}.${version.minor}`;
|
|
338
376
|
this.statusMessage = decoded.migrated
|
|
339
|
-
? `Imported ${sourceLabel} and migrated it to ZenFG
|
|
340
|
-
: `Imported
|
|
377
|
+
? `Imported ${sourceLabel} and migrated it to ZenFG ${targetLabel}.`
|
|
378
|
+
: `Imported ${targetLabel} with ${sourceLabel} migration provenance.`;
|
|
341
379
|
this.statusTone = 'neutral';
|
|
342
380
|
}
|
|
343
381
|
}
|
|
@@ -413,18 +451,18 @@ export class FrameGraphInspector {
|
|
|
413
451
|
this.protocolSnapshot = snapshot;
|
|
414
452
|
this.viewModel = viewModel;
|
|
415
453
|
this.graphView.fitOnNextRender = true;
|
|
454
|
+
this.graphView.captureRevision = (this.graphView.captureRevision ?? 0) + 1;
|
|
455
|
+
this.graphView.revealOnNextRender = undefined;
|
|
456
|
+
this.graphView.renderer?.cancelReveal?.();
|
|
416
457
|
this.hovered = undefined;
|
|
417
458
|
this.initialAutoCaptureAttempted = true;
|
|
418
459
|
this.statusMessage = undefined;
|
|
419
460
|
this.statusTone = 'neutral';
|
|
420
|
-
if (
|
|
421
|
-
|
|
422
|
-
? { kind: 'node', id: viewModel.nodes[0].id }
|
|
423
|
-
: undefined;
|
|
424
|
-
}
|
|
461
|
+
if (this.selected?.kind === 'node' || this.selected?.kind === 'culled') this.selected = resolveNodeSelection(viewModel, this.selected.id);
|
|
462
|
+
else if (this.selected && !selectionExists(viewModel, this.selected)) this.selected = undefined;
|
|
425
463
|
this.workbench.setSnapshot(viewModel, this.selected);
|
|
426
464
|
this.updateCaptureActions();
|
|
427
|
-
this.
|
|
465
|
+
this.updateGraphControls();
|
|
428
466
|
}
|
|
429
467
|
|
|
430
468
|
/**
|
|
@@ -442,7 +480,9 @@ export class FrameGraphInspector {
|
|
|
442
480
|
this.dom.removeEventListener('dragleave', this.handleDragLeave);
|
|
443
481
|
this.dom.removeEventListener('dragend', this.handleDragEnd);
|
|
444
482
|
this.dom.removeEventListener('drop', this.handleDrop);
|
|
483
|
+
this.themeController.destroy();
|
|
445
484
|
destroyGraph(this.graphView);
|
|
485
|
+
this.workbench.destroy();
|
|
446
486
|
this.dom.remove();
|
|
447
487
|
}
|
|
448
488
|
|
|
@@ -472,59 +512,102 @@ export class FrameGraphInspector {
|
|
|
472
512
|
void this.captureSnapshot();
|
|
473
513
|
}
|
|
474
514
|
private handleSelect(selection: Selection): void {
|
|
515
|
+
if (!this.viewModel || !selectionExists(this.viewModel, selection)) return;
|
|
475
516
|
this.selected = selection;
|
|
476
517
|
this.workbench.setSelection(selection);
|
|
477
518
|
}
|
|
478
519
|
|
|
520
|
+
private handleReveal(selection: Selection, tab: WorkbenchTab): void {
|
|
521
|
+
const snapshot = this.viewModel;
|
|
522
|
+
if (!snapshot || !selectionExists(snapshot, selection)) return;
|
|
523
|
+
if (tab === 'graph') {
|
|
524
|
+
const expanded = new Set(this.graphView.expandedGroupPaths);
|
|
525
|
+
const groupsEnabled = this.graphView.groupsEnabled;
|
|
526
|
+
const showResourceDeclarations = this.graphView.showResourceDeclarations;
|
|
527
|
+
if (selection.kind === 'resource') this.graphView.showResourceDeclarations = true;
|
|
528
|
+
if (selection.kind === 'group') this.graphView.groupsEnabled = true;
|
|
529
|
+
let groupId: string | undefined;
|
|
530
|
+
if (selection.kind === 'node') groupId = snapshot.nodeById.get(selection.id)?.debugGroupId;
|
|
531
|
+
else if (selection.kind === 'resource') groupId = snapshot.resourceById.get(selection.id)?.debugGroupId;
|
|
532
|
+
else if (selection.kind === 'group') groupId = snapshot.groupByPathKey.get(selection.pathKey)?.parentId;
|
|
533
|
+
if (this.graphView.groupsEnabled && groupId) {
|
|
534
|
+
for (const id of snapshot.groupById.get(groupId)?.ancestorIds ?? []) {
|
|
535
|
+
const group = snapshot.groupById.get(id);
|
|
536
|
+
if (group) this.graphView.expandedGroupPaths.add(group.pathKey);
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
const scene = resolveGraphScene(this.graphView, snapshot);
|
|
540
|
+
const availableIds = new Set([...scene.nodes, ...scene.edges].map((entry) => entry.id));
|
|
541
|
+
if (!(scene.interaction.primaryElementIdsBySelection.get(selectionKey(selection))?.some((id) => availableIds.has(id)))) {
|
|
542
|
+
this.graphView.groupsEnabled = groupsEnabled;
|
|
543
|
+
this.graphView.showResourceDeclarations = showResourceDeclarations;
|
|
544
|
+
this.graphView.expandedGroupPaths.clear();
|
|
545
|
+
for (const path of expanded) this.graphView.expandedGroupPaths.add(path);
|
|
546
|
+
this.workbench.showNavigationIssue(selection.kind === 'culled' ? 'Culled passes are not part of Frame Flow.' : 'This object has no representation in Frame Flow.', selection);
|
|
547
|
+
return;
|
|
548
|
+
}
|
|
549
|
+
this.graphView.revealOnNextRender = { selection, revision: ++this.revealRevision };
|
|
550
|
+
this.updateGraphControls();
|
|
551
|
+
}
|
|
552
|
+
this.handleSelect(selection);
|
|
553
|
+
this.workbench.reveal(selection, tab);
|
|
554
|
+
}
|
|
555
|
+
|
|
479
556
|
private handleHover(selection: Selection | undefined): void {
|
|
480
557
|
if (!this.viewModel || sameSelection(this.hovered, selection)) return;
|
|
481
558
|
this.hovered = selection;
|
|
482
559
|
this.workbench.setHovered(selection);
|
|
483
560
|
}
|
|
484
561
|
|
|
485
|
-
private
|
|
486
|
-
|
|
487
|
-
this.
|
|
488
|
-
this.
|
|
489
|
-
this.updateGraphModeButtonState();
|
|
490
|
-
this.workbench.refreshGraphStructure();
|
|
491
|
-
}
|
|
492
|
-
|
|
493
|
-
private updateGraphModeButtonState(): void {
|
|
494
|
-
const passesActive = this.graphView.graphMode === 'passes';
|
|
562
|
+
private updateGraphControls(): void {
|
|
563
|
+
const showDeclarations = this.graphView.showResourceDeclarations ?? true;
|
|
564
|
+
this.declarationsButton.classList.toggle('active', showDeclarations);
|
|
565
|
+
this.declarationsButton.setAttribute('aria-pressed', String(showDeclarations));
|
|
495
566
|
const hasGroups = (this.viewModel?.debugGroups.length ?? 0) > 0;
|
|
496
|
-
this.
|
|
497
|
-
this.
|
|
498
|
-
this.
|
|
499
|
-
this.
|
|
500
|
-
this.groupsButton.
|
|
501
|
-
this.groupsButton.classList.toggle('active', passesActive && hasGroups && this.graphView.groupsEnabled);
|
|
502
|
-
this.groupsButton.setAttribute('aria-pressed', passesActive && hasGroups && this.graphView.groupsEnabled ? 'true' : 'false');
|
|
567
|
+
this.groupsButton.disabled = !hasGroups;
|
|
568
|
+
this.groupsButton.hidden = !hasGroups;
|
|
569
|
+
this.collapseGroupsButton.hidden = !hasGroups;
|
|
570
|
+
this.groupsButton.classList.toggle('active', hasGroups && this.graphView.groupsEnabled);
|
|
571
|
+
this.groupsButton.setAttribute('aria-pressed', hasGroups && this.graphView.groupsEnabled ? 'true' : 'false');
|
|
503
572
|
const hasExpanded = this.viewModel?.debugGroups.some((group) => this.graphView.expandedGroupPaths.has(group.pathKey)) ?? false;
|
|
504
|
-
this.collapseGroupsButton.disabled = !
|
|
573
|
+
this.collapseGroupsButton.disabled = !hasGroups || !this.graphView.groupsEnabled || !hasExpanded;
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
private toggleDeclarations(): void {
|
|
577
|
+
this.handleHover(undefined);
|
|
578
|
+
this.graphView.showResourceDeclarations = !(this.graphView.showResourceDeclarations ?? true);
|
|
579
|
+
this.graphView.renderer?.cancelReveal?.();
|
|
580
|
+
this.graphView.revealOnNextRender = undefined;
|
|
581
|
+
this.graphView.anchorElementIdOnNextRender = undefined;
|
|
582
|
+
this.graphView.fitOnNextRender = true;
|
|
583
|
+
this.updateGraphControls();
|
|
584
|
+
this.workbench.refreshGraphStructure();
|
|
505
585
|
}
|
|
506
586
|
|
|
507
587
|
private toggleGroups(): void {
|
|
508
|
-
if (
|
|
588
|
+
if (!this.viewModel?.debugGroups.length) return;
|
|
589
|
+
this.handleHover(undefined);
|
|
509
590
|
this.graphView.groupsEnabled = !this.graphView.groupsEnabled;
|
|
510
591
|
this.graphView.fitOnNextRender = true;
|
|
511
|
-
this.
|
|
592
|
+
this.updateGraphControls();
|
|
512
593
|
this.workbench.refreshGraphStructure();
|
|
513
594
|
}
|
|
514
595
|
|
|
515
596
|
private toggleGroup(pathKey: string): void {
|
|
597
|
+
this.handleHover(undefined);
|
|
516
598
|
if (this.graphView.expandedGroupPaths.has(pathKey)) this.graphView.expandedGroupPaths.delete(pathKey);
|
|
517
599
|
else this.graphView.expandedGroupPaths.add(pathKey);
|
|
518
600
|
this.graphView.anchorElementIdOnNextRender = graphGroupElementId(pathKey);
|
|
519
|
-
this.
|
|
601
|
+
this.updateGraphControls();
|
|
520
602
|
this.workbench.refreshGraphStructure();
|
|
521
603
|
}
|
|
522
604
|
|
|
523
605
|
private collapseAllGroups(): void {
|
|
524
606
|
if (this.graphView.expandedGroupPaths.size === 0) return;
|
|
607
|
+
this.handleHover(undefined);
|
|
525
608
|
this.graphView.expandedGroupPaths.clear();
|
|
526
609
|
this.graphView.fitOnNextRender = true;
|
|
527
|
-
this.
|
|
610
|
+
this.updateGraphControls();
|
|
528
611
|
this.workbench.refreshGraphStructure();
|
|
529
612
|
}
|
|
530
613
|
|
|
@@ -568,16 +651,10 @@ export class FrameGraphInspector {
|
|
|
568
651
|
export function mountFrameGraphInspector(host: HTMLElement, options?: FrameGraphInspectorOptions): FrameGraphInspector {
|
|
569
652
|
const inspector = new FrameGraphInspector(options);
|
|
570
653
|
host.appendChild(inspector.dom);
|
|
654
|
+
inspector.refreshTheme();
|
|
571
655
|
return inspector;
|
|
572
656
|
}
|
|
573
657
|
|
|
574
|
-
function createGraphIconButton(icon: 'fit', title: string, onClick: () => void): HTMLButtonElement {
|
|
575
|
-
const button = createToolbarButton('', title, onClick);
|
|
576
|
-
button.classList.add('zenfg-inspector-icon-button');
|
|
577
|
-
button.appendChild(createPanelIcon(icon));
|
|
578
|
-
return button;
|
|
579
|
-
}
|
|
580
|
-
|
|
581
658
|
async function writeTextToClipboard(text: string): Promise<void> {
|
|
582
659
|
if (navigator.clipboard?.writeText) {
|
|
583
660
|
await navigator.clipboard.writeText(text);
|
|
@@ -618,7 +695,7 @@ function formatImportLimit(bytes: number): string {
|
|
|
618
695
|
}
|
|
619
696
|
|
|
620
697
|
function formatIssues(issues: readonly { readonly path: string; readonly message: string }[]): string {
|
|
621
|
-
return issues.
|
|
698
|
+
return issues.map((issue) => `${issue.path || '/'}: ${issue.message}`).join('\n');
|
|
622
699
|
}
|
|
623
700
|
|
|
624
701
|
function isFileDrag(event: DragEvent): boolean {
|
package/src/capture.ts
ADDED