@zenfg/inspector 0.1.0-beta.2 → 0.1.0-beta.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +63 -1
- package/dist/FrameGraphInspector.d.ts +2 -5
- package/dist/FrameGraphInspector.d.ts.map +1 -1
- package/dist/FrameGraphInspector.js +16 -38
- package/dist/FrameGraphInspector.js.map +1 -1
- package/dist/debugCaptureModel.d.ts +9 -4
- package/dist/debugCaptureModel.d.ts.map +1 -1
- package/dist/debugCaptureModel.js +36 -14
- package/dist/debugCaptureModel.js.map +1 -1
- package/dist/panelCytoscapeGraphRenderer.d.ts +3 -1
- package/dist/panelCytoscapeGraphRenderer.d.ts.map +1 -1
- package/dist/panelCytoscapeGraphRenderer.js +39 -19
- package/dist/panelCytoscapeGraphRenderer.js.map +1 -1
- package/dist/panelDiagnosticsView.js +2 -2
- package/dist/panelDiagnosticsView.js.map +1 -1
- package/dist/panelGraphLayout.d.ts.map +1 -1
- package/dist/panelGraphLayout.js +15 -1
- package/dist/panelGraphLayout.js.map +1 -1
- package/dist/panelGraphScene.d.ts +14 -26
- package/dist/panelGraphScene.d.ts.map +1 -1
- package/dist/panelGraphScene.js +141 -195
- package/dist/panelGraphScene.js.map +1 -1
- package/dist/panelGraphView.d.ts.map +1 -1
- package/dist/panelGraphView.js +31 -9
- package/dist/panelGraphView.js.map +1 -1
- package/dist/panelGraphVisuals.d.ts +7 -3
- package/dist/panelGraphVisuals.d.ts.map +1 -1
- package/dist/panelGraphVisuals.js +45 -80
- package/dist/panelGraphVisuals.js.map +1 -1
- package/dist/panelInspectorView.d.ts +5 -0
- package/dist/panelInspectorView.d.ts.map +1 -1
- package/dist/panelInspectorView.js +99 -13
- package/dist/panelInspectorView.js.map +1 -1
- package/dist/panelSelection.js +2 -2
- package/dist/panelSelection.js.map +1 -1
- package/dist/panelTypes.d.ts +8 -3
- package/dist/panelTypes.d.ts.map +1 -1
- package/dist/panelTypes.js.map +1 -1
- package/dist/panelVisualTheme.d.ts +21 -13
- package/dist/panelVisualTheme.d.ts.map +1 -1
- package/dist/panelVisualTheme.js +15 -7
- package/dist/panelVisualTheme.js.map +1 -1
- package/dist/panelWorkbenchHelpers.js +1 -1
- package/dist/panelWorkbenchHelpers.js.map +1 -1
- package/dist/styles.d.ts.map +1 -1
- package/dist/styles.js +16 -12
- package/dist/styles.js.map +1 -1
- package/package.json +2 -2
- package/src/FrameGraphInspector.ts +17 -39
- package/src/debugCaptureModel.ts +44 -19
- package/src/panelCytoscapeGraphRenderer.ts +38 -18
- package/src/panelDiagnosticsView.ts +3 -3
- package/src/panelGraphLayout.ts +15 -1
- package/src/panelGraphScene.ts +153 -279
- package/src/panelGraphView.ts +31 -9
- package/src/panelGraphVisuals.ts +46 -82
- package/src/panelInspectorView.ts +104 -16
- package/src/panelSelection.ts +2 -2
- package/src/panelTypes.ts +9 -3
- package/src/panelVisualTheme.ts +18 -7
- package/src/panelWorkbenchHelpers.ts +2 -2
- package/src/styles.ts +16 -12
package/dist/styles.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"styles.js","sourceRoot":"","sources":["../src/styles.ts"],"names":[],"mappings":"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","sourcesContent":["import { FRAME_GRAPH_DEBUG_VISUAL_THEME, GRAPH_VISUAL_THEME } from './panelVisualTheme.ts';\n\nconst STYLE_ELEMENT_ID = 'zenfg-inspector-panel-styles';\nconst theme = FRAME_GRAPH_DEBUG_VISUAL_THEME;\n\nconst FRAME_GRAPH_DEBUG_PANEL_CSS = `\n/* Tokens and workbench root */\n.zenfg-inspector {\n\t--fgd-canvas: ${theme.canvas};\n\t--fgd-panel: var(--zenfg-inspector-background, ${theme.panel});\n\t--fgd-surface: var(--zenfg-inspector-surface, ${theme.surface});\n\t--fgd-surface-raised: var(--zenfg-inspector-surface-raised, ${theme.surfaceRaised});\n\t--fgd-surface-hover: var(--zenfg-inspector-surface-hover, ${theme.surfaceHover});\n\t--fgd-border: var(--zenfg-inspector-border, ${theme.border});\n\t--fgd-border-subtle: var(--zenfg-inspector-border-subtle, rgba(180, 190, 202, 0.11));\n\t--fgd-border-strong: var(--zenfg-inspector-border-strong, rgba(180, 190, 202, 0.28));\n\t--fgd-text: var(--zenfg-inspector-text, ${theme.text});\n\t--fgd-text-secondary: var(--zenfg-inspector-text-secondary, ${theme.textSecondary});\n\t--fgd-muted: var(--zenfg-inspector-muted, ${theme.muted});\n\t--fgd-accent: var(--zenfg-inspector-accent, ${theme.accent});\n\t--fgd-accent-soft: var(--zenfg-inspector-accent-soft, rgba(56, 189, 248, 0.12));\n\t--fgd-accent-hover: var(--zenfg-inspector-accent-hover, rgba(56, 189, 248, 0.18));\n\t--fgd-success: var(--zenfg-inspector-success, ${theme.success});\n\t--fgd-warning: var(--zenfg-inspector-warning, ${theme.warning});\n\t--fgd-danger: var(--zenfg-inspector-danger, ${theme.danger});\n\t--fgd-font-ui: var(--zenfg-inspector-font-ui, ${theme.fontUi});\n\t--fgd-font-mono: var(--zenfg-inspector-font-mono, ${theme.fontMono});\n\tposition: relative;\n\tdisplay: block;\n\twidth: 100%;\n\theight: 100%;\n\tmin-width: 0;\n\tmin-height: 0;\n\toverflow: hidden;\n\tcolor: var(--fgd-text);\n\tbackground: var(--fgd-canvas);\n\tfont-family: var(--fgd-font-ui);\n\tfont-size: 12px;\n\tline-height: 1.45;\n\tcolor-scheme: dark;\n\tcontainer-name: zenfg-inspector;\n\tcontainer-type: inline-size;\n}\n\n.zenfg-inspector,\n.zenfg-inspector * { box-sizing: border-box; }\n\n.zenfg-inspector-body {\n\twidth: 100%;\n\theight: 100%;\n\tmin-height: 0;\n\tpadding: 10px;\n\toverflow: hidden;\n\tborder: 0;\n\tborder-radius: 0;\n\tbackground: var(--zenfg-inspector-background, var(--fgd-panel));\n}\n\n.zenfg-inspector-content {\n\tmin-width: 0;\n\tmin-height: 0;\n\theight: 100%;\n\toverflow: hidden;\n}\n\n.zenfg-inspector-drop-overlay {\n\tposition: absolute;\n\tz-index: 40;\n\tinset: 10px;\n\tdisplay: grid;\n\tplace-content: center;\n\tgap: 6px;\n\tborder: 2px dashed var(--fgd-accent);\n\tborder-radius: var(--zenfg-inspector-radius-md, 6px);\n\tcolor: #bae6fd;\n\tbackground: color-mix(in srgb, var(--fgd-canvas) 90%, transparent);\n\tfont: 600 13px/1.4 var(--fgd-font-ui);\n\ttext-align: center;\n\tpointer-events: none;\n}\n.zenfg-inspector-drop-overlay[hidden] { display: none; }\n.zenfg-inspector-drop-overlay > span { color: var(--fgd-muted); font-size: 11px; }\n\n.zenfg-inspector-capture-summary[hidden],\n.zenfg-inspector-command-status[hidden],\n.zenfg-inspector-capture-action[hidden],\n.zenfg-inspector-workbench-empty[hidden],\n.zenfg-inspector-open-inspector[hidden],\n.zenfg-inspector-inspector.unavailable,\n.zenfg-inspector-view[hidden],\n.zenfg-inspector-graph-status[hidden] { display: none !important; }\n\n.zenfg-inspector-muted {\n\tmargin: 0;\n\tcolor: var(--fgd-muted);\n\tfont: 11px/1.5 var(--fgd-font-ui);\n}\n\n.zenfg-inspector-control-icon {\n\tdisplay: block;\n\tflex: 0 0 auto;\n\twidth: 14px;\n\theight: 14px;\n}\n\n.zenfg-inspector-table-scroller,\n.zenfg-inspector-memory-scroller,\n.zenfg-inspector-diagnostics-scroller,\n.zenfg-inspector-inspector-content,\n.zenfg-inspector-workbench-tabs,\n.zenfg-inspector-graph-legend {\n\tscrollbar-width: thin;\n\tscrollbar-color: rgba(180, 190, 202, 0.25) transparent;\n}\n\n/* Workbench and summary */\n.zenfg-inspector-workbench {\n\tdisplay: grid;\n\tgrid-template-rows: auto minmax(0, 1fr);\n\tgap: 8px;\n\theight: 100%;\n\tmin-width: 0;\n\tmin-height: 0;\n\tfont-family: var(--fgd-font-ui);\n}\n\n.zenfg-inspector-capture-summary {\n\tdisplay: grid;\n\tgrid-template-columns: repeat(auto-fit, minmax(min(230px, 100%), 1fr));\n\talign-content: start;\n\tgap: 8px;\n\tmin-width: 0;\n\tbackground: transparent;\n}\n\n.zenfg-inspector-capture-summary > section {\n\tmin-width: 0;\n\tpadding: 12px;\n\tborder: 1px solid var(--fgd-border-subtle);\n\tborder-radius: 6px;\n\tbackground: var(--fgd-surface);\n}\n\n.zenfg-inspector-capture-summary h2 {\n\tmargin: 0 0 5px;\n\tcolor: var(--fgd-text-secondary);\n\tfont-size: 10px;\n\tfont-weight: 700;\n\tletter-spacing: 0.075em;\n\tline-height: 1.2;\n\ttext-transform: uppercase;\n}\n\n.zenfg-inspector-capture-summary section > div {\n\tdisplay: grid;\n\tgrid-template-columns: auto minmax(0, 1fr);\n\tgap: 8px;\n\tmin-width: 0;\n\tfont-size: 11px;\n\tline-height: 1.5;\n}\n\n.zenfg-inspector-capture-summary span { color: var(--fgd-muted); white-space: nowrap; }\n.zenfg-inspector-capture-summary strong {\n\tmin-width: 0;\n\toverflow: hidden;\n\tcolor: var(--fgd-text);\n\tfont: 600 11px/1.5 var(--fgd-font-mono);\n\ttext-align: right;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n}\n\n/* Command bar, tabs, and controls */\n.zenfg-inspector-workbench-command-bar {\n\tposition: relative;\n\tdisplay: grid;\n\tgrid-template-columns: auto minmax(0, 1fr) auto;\n\talign-items: center;\n\tmin-width: 0;\n\tborder-bottom: 1px solid var(--fgd-border-subtle);\n}\n.zenfg-inspector-workbench-command-bar.branding-hidden {\n\tgrid-template-columns: minmax(0, 1fr) auto;\n}\n\n.zenfg-inspector-brand {\n\tposition: relative;\n\tdisplay: flex;\n\talign-items: center;\n\tmin-width: 0;\n\tmin-height: 32px;\n\tpadding: 0 12px 0 4px;\n\toverflow: hidden;\n\tcolor: var(--fgd-text);\n\tfont: 700 12px/1 var(--fgd-font-ui);\n\tletter-spacing: 0.015em;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n}\n.zenfg-inspector-brand::after {\n\tposition: absolute;\n\ttop: 50%;\n\tright: 0;\n\twidth: 1px;\n\theight: 16px;\n\tbackground: var(--fgd-border);\n\tcontent: '';\n\ttransform: translateY(-50%);\n}\n\n.zenfg-inspector-workbench-tabs,\n.zenfg-inspector-subtabs,\n.zenfg-inspector-inspector-tabs {\n\tdisplay: flex;\n\talign-items: end;\n\tmin-width: 0;\n\tborder-bottom: 1px solid var(--fgd-border-subtle);\n}\n\n.zenfg-inspector-workbench-tabs {\n\toverflow-x: auto;\n\tborder-bottom: 0;\n}\n\n.zenfg-inspector-workbench-tabs > button,\n.zenfg-inspector-subtabs > button,\n.zenfg-inspector-inspector-tabs > button {\n\tmin-height: 32px;\n\tpadding: 6px 10px;\n\tborder: 0;\n\tborder-bottom: 2px solid transparent;\n\tcolor: var(--fgd-muted);\n\tbackground: transparent;\n\tfont: 600 12px/1 var(--fgd-font-ui);\n\twhite-space: nowrap;\n\tcursor: pointer;\n\ttransition: color 120ms ease, background-color 120ms ease, border-color 120ms ease;\n}\n\n.zenfg-inspector-workbench-tabs > button:hover:not(:disabled),\n.zenfg-inspector-subtabs > button:hover:not(:disabled),\n.zenfg-inspector-inspector-tabs > button:hover:not(:disabled) {\n\tcolor: var(--fgd-text-secondary);\n\tbackground: rgba(180, 190, 202, 0.055);\n}\n\n.zenfg-inspector-workbench-tabs > button.active,\n.zenfg-inspector-subtabs > button.active,\n.zenfg-inspector-inspector-tabs > button.active {\n\tborder-bottom-color: var(--fgd-accent);\n\tcolor: var(--fgd-text);\n\tbackground: linear-gradient(to top, var(--fgd-accent-soft), transparent 55%);\n}\n\n.zenfg-inspector-workbench-tabs > button:disabled,\n.zenfg-inspector-subtabs > button:disabled,\n.zenfg-inspector-inspector-tabs > button:disabled { cursor: default; opacity: 0.42; }\n\n.zenfg-inspector-workbench-actions {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 6px;\n\tpadding-left: 8px;\n\toverflow-x: auto;\n\tscrollbar-width: thin;\n}\n\n.zenfg-inspector-export-menu {\n\tposition: absolute;\n\tz-index: 30;\n\ttop: calc(100% + 4px);\n\tright: 0;\n\tdisplay: grid;\n\tmin-width: 142px;\n\tpadding: 4px;\n\tborder: 1px solid var(--fgd-border-strong);\n\tborder-radius: 6px;\n\tbackground: var(--fgd-surface-raised);\n\tbox-shadow: 0 10px 24px rgba(0, 0, 0, 0.34);\n}\n.zenfg-inspector-export-menu[hidden] { display: none; }\n.zenfg-inspector-export-menu > button {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 7px;\n\tpadding: 7px 9px;\n\tborder: 0;\n\tborder-radius: 4px;\n\tcolor: var(--fgd-text-secondary);\n\tbackground: transparent;\n\tfont: 600 11px/1.2 var(--fgd-font-ui);\n\ttext-align: left;\n\twhite-space: nowrap;\n\tcursor: pointer;\n}\n.zenfg-inspector-export-menu > button:hover:not(:disabled) {\n\tcolor: var(--fgd-text);\n\tbackground: var(--fgd-surface-hover);\n}\n.zenfg-inspector-export-menu > button:disabled { cursor: default; opacity: 0.46; }\n\n.zenfg-inspector-workbench-actions > button,\n.zenfg-inspector-graph-toolbar button,\n.zenfg-inspector-group-toggle,\n.zenfg-inspector-inspector-close {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tjustify-content: center;\n\tgap: 6px;\n\theight: 28px;\n\tmin-height: 28px;\n\tpadding: 0 9px;\n\tborder: 1px solid var(--fgd-border);\n\tborder-radius: 5px;\n\tcolor: var(--fgd-text-secondary);\n\tbackground: var(--fgd-surface);\n\tfont: 600 11px/1 var(--fgd-font-ui);\n\twhite-space: nowrap;\n\tcursor: pointer;\n\ttransition: color 120ms ease, background-color 120ms ease, border-color 120ms ease;\n}\n\n.zenfg-inspector-workbench-actions > button:hover:not(:disabled),\n.zenfg-inspector-graph-toolbar button:hover:not(:disabled),\n.zenfg-inspector-group-toggle:hover:not(:disabled),\n.zenfg-inspector-inspector-close:hover:not(:disabled) {\n\tborder-color: var(--fgd-border-strong);\n\tcolor: var(--fgd-text);\n\tbackground: var(--fgd-surface-hover);\n}\n\n.zenfg-inspector-workbench-actions > button[data-tone='accent'] {\n\tborder-color: rgba(56, 189, 248, 0.5);\n\tcolor: #bae6fd;\n\tbackground: var(--fgd-accent-soft);\n}\n.zenfg-inspector-workbench-actions > button[data-tone='accent']:hover:not(:disabled) {\n\tborder-color: var(--fgd-accent);\n\tbackground: var(--fgd-accent-hover);\n}\n.zenfg-inspector-workbench-actions > button[data-tone='pending'] {\n\tborder-color: rgba(56, 189, 248, 0.42);\n\tcolor: var(--fgd-text-secondary);\n\tbackground: var(--fgd-accent-soft);\n}\n.zenfg-inspector-workbench-actions > button[data-tone='success'] {\n\tborder-color: rgba(52, 211, 153, 0.46);\n\tcolor: var(--fgd-success);\n\tbackground: rgba(52, 211, 153, 0.1);\n}\n.zenfg-inspector-workbench-actions > button:disabled { cursor: default; opacity: 0.46; }\n.zenfg-inspector-workbench-actions > button[aria-busy='true']:disabled { opacity: 0.72; }\n\n.zenfg-inspector-command-status {\n\tmax-width: 220px;\n\toverflow: hidden;\n\tcolor: var(--fgd-muted);\n\tfont: 11px/1.35 var(--fgd-font-ui);\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n}\n.zenfg-inspector-command-status[data-tone='error'] { color: var(--fgd-danger); }\n\n.zenfg-inspector-control-icon[data-icon='spinner'],\nbutton[aria-busy='true'] > .zenfg-inspector-control-icon {\n\tanimation: zenfg-inspector-spin 800ms linear infinite;\n}\n@keyframes zenfg-inspector-spin { to { transform: rotate(360deg); } }\n\n.zenfg-inspector-workbench-tabs > button:focus-visible,\n.zenfg-inspector-subtabs > button:focus-visible,\n.zenfg-inspector-inspector-tabs > button:focus-visible,\n.zenfg-inspector-workbench-actions > button:focus-visible,\n.zenfg-inspector-graph-toolbar button:focus-visible,\n.zenfg-inspector-group-toggle:focus-visible,\n.zenfg-inspector-inspector-close:focus-visible,\n.zenfg-inspector-relation-button:focus-visible,\n.zenfg-inspector-view-toolbar input:focus-visible,\n.zenfg-inspector-view-toolbar select:focus-visible {\n\toutline: 2px solid var(--fgd-accent);\n\toutline-offset: 1px;\n}\n\n/* Workspace and empty states */\n.zenfg-inspector-overview-view {\n\tpadding: 12px;\n\toverflow: auto;\n\tbackground: var(--fgd-canvas);\n}\n\n.zenfg-inspector-workspace {\n\tposition: relative;\n\tdisplay: grid;\n\tgrid-template-columns: minmax(0, 1fr);\n\tgap: 8px;\n\tmin-width: 0;\n\tmin-height: 0;\n\toverflow: hidden;\n}\n.zenfg-inspector-workspace.inspector-open {\n\tgrid-template-columns: minmax(0, 1fr) minmax(360px, 28vw);\n}\n\n.zenfg-inspector-main {\n\tdisplay: grid;\n\tgrid-template: minmax(0, 1fr) / minmax(0, 1fr);\n\tmin-width: 0;\n\tmin-height: 0;\n\toverflow: hidden;\n}\n\n.zenfg-inspector-workbench-empty {\n\tgrid-area: 1 / 1;\n\tdisplay: flex;\n\tflex-direction: column;\n\talign-items: center;\n\tjustify-content: center;\n\tgap: 8px;\n\tmin-width: 0;\n\tmin-height: 0;\n\tpadding: 24px;\n\tcolor: var(--fgd-muted);\n\tfont: 12px/1.5 var(--fgd-font-ui);\n\ttext-align: center;\n}\n.zenfg-inspector-workbench-empty > .zenfg-inspector-control-icon {\n\twidth: 22px;\n\theight: 22px;\n\tcolor: var(--fgd-text-secondary);\n}\n.zenfg-inspector-workbench-empty[data-state='error'] > .zenfg-inspector-control-icon { color: var(--fgd-danger); }\n.zenfg-inspector-workbench-empty[data-state='capturing'] > .zenfg-inspector-control-icon { color: var(--fgd-accent); }\n.zenfg-inspector-empty-message { max-width: 520px; }\n.zenfg-inspector-empty-detail { color: color-mix(in srgb, var(--fgd-muted) 78%, transparent); font-size: 11px; }\n\n.zenfg-inspector-view {\n\tgrid-area: 1 / 1;\n\tmin-width: 0;\n\tmin-height: 0;\n\toverflow: hidden;\n\tborder: 1px solid var(--fgd-border-subtle);\n\tborder-radius: 6px;\n\tbackground: var(--fgd-canvas);\n}\n\n.zenfg-inspector-view-toolbar {\n\tdisplay: flex;\n\tflex: 0 0 auto;\n\tflex-wrap: wrap;\n\talign-items: center;\n\tgap: 6px;\n\tpadding: 6px 8px;\n\tborder-bottom: 1px solid var(--fgd-border-subtle);\n\tbackground: var(--fgd-surface);\n}\n\n.zenfg-inspector-view-toolbar input,\n.zenfg-inspector-view-toolbar select {\n\tmin-width: 0;\n\theight: 28px;\n\tpadding: 4px 8px;\n\tborder: 1px solid var(--fgd-border);\n\tborder-radius: 5px;\n\tcolor: var(--fgd-text);\n\tbackground: var(--fgd-panel);\n\tfont: 11px/1 var(--fgd-font-ui);\n}\n.zenfg-inspector-view-toolbar input { flex: 1 1 210px; }\n.zenfg-inspector-view-toolbar input::placeholder { color: var(--fgd-muted); opacity: 0.86; }\n.zenfg-inspector-view-toolbar select { flex: 0 1 auto; color-scheme: dark; }\n\n.zenfg-inspector-passes-view,\n.zenfg-inspector-resources-view { display: flex; flex-direction: column; }\n.zenfg-inspector-subtabs,\n.zenfg-inspector-inspector-tabs {\n\tflex: 0 0 auto;\n\tpadding: 0 4px;\n\tbackground: var(--fgd-panel);\n}\n.zenfg-inspector-subview { flex: 1 1 auto; min-height: 0; overflow: hidden; }\n\n/* Tables and relations */\n.zenfg-inspector-table-scroller {\n\twidth: 100%;\n\theight: 100%;\n\tmin-height: 0;\n\toverflow: auto;\n}\n.zenfg-inspector-workbench-table {\n\twidth: 100%;\n\tmin-width: 720px;\n\tborder-collapse: collapse;\n\tfont: 11px/1.45 var(--fgd-font-mono);\n}\n.zenfg-inspector-workbench-table th,\n.zenfg-inspector-workbench-table td {\n\theight: 30px;\n\tmax-width: 360px;\n\tpadding: 6px 10px;\n\tborder-bottom: 1px solid var(--fgd-border-subtle);\n\toverflow: hidden;\n\ttext-align: left;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n}\n.zenfg-inspector-workbench-table th {\n\tposition: sticky;\n\tz-index: 2;\n\ttop: 0;\n\tcolor: var(--fgd-muted);\n\tbackground: var(--fgd-panel);\n\tfont: 650 10px/1.3 var(--fgd-font-ui);\n\tletter-spacing: 0.025em;\n}\n.zenfg-inspector-workbench-table [data-column='numeric'] {\n\tfont-variant-numeric: tabular-nums;\n\ttext-align: right;\n}\n.zenfg-inspector-workbench-table [data-column='code'] { font-family: var(--fgd-font-mono); }\n.zenfg-inspector-workbench-table tbody tr:hover td { background: rgba(180, 190, 202, 0.045); }\n\n.zenfg-inspector-workbench-table tr.selected td,\n.zenfg-inspector-diagnostic-list .selected,\n.zenfg-inspector-memory-allocation.selected,\n.zenfg-inspector-memory-resource.selected { background: var(--fgd-accent-soft); }\n.zenfg-inspector-workbench-table tr.selected td:first-child { box-shadow: inset 2px 0 var(--fgd-accent); }\n\n.zenfg-inspector-workbench-table td small {\n\tdisplay: block;\n\tmargin-top: 2px;\n\toverflow: hidden;\n\tcolor: var(--fgd-muted);\n\tfont: 10px/1.4 var(--fgd-font-ui);\n\ttext-overflow: ellipsis;\n}\n\n.zenfg-inspector-kind-label {\n\t--fgd-kind-color: var(--fgd-muted);\n\tdisplay: inline-flex;\n\talign-items: center;\n\tgap: 6px;\n\tmax-width: 100%;\n\toverflow: hidden;\n\tcolor: var(--fgd-text-secondary);\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n}\n.zenfg-inspector-kind-label::before {\n\twidth: 6px;\n\theight: 6px;\n\tflex: 0 0 auto;\n\tborder: 1px solid color-mix(in srgb, var(--fgd-kind-color) 78%, white);\n\tborder-radius: 50%;\n\tbackground: color-mix(in srgb, var(--fgd-kind-color) 72%, transparent);\n\tcontent: '';\n}\n.zenfg-inspector-kind-label[data-kind='render'] { --fgd-kind-color: ${GRAPH_VISUAL_THEME.render.stroke}; }\n.zenfg-inspector-kind-label[data-kind='compute'] { --fgd-kind-color: ${GRAPH_VISUAL_THEME.compute.stroke}; }\n.zenfg-inspector-kind-label[data-kind='copy'] { --fgd-kind-color: ${GRAPH_VISUAL_THEME.copy.stroke}; }\n.zenfg-inspector-kind-label[data-kind='clear-buffer'] { --fgd-kind-color: ${GRAPH_VISUAL_THEME.clear.stroke}; }\n.zenfg-inspector-kind-label[data-kind='command'] { --fgd-kind-color: ${GRAPH_VISUAL_THEME.command.stroke}; }\n.zenfg-inspector-kind-label[data-kind='external-submission'] { --fgd-kind-color: ${GRAPH_VISUAL_THEME.external.stroke}; }\n.zenfg-inspector-kind-label[data-kind='opaque'] { --fgd-kind-color: ${GRAPH_VISUAL_THEME.external.stroke}; }\n.zenfg-inspector-kind-label[data-kind='texture'] { --fgd-kind-color: ${GRAPH_VISUAL_THEME.texture.stroke}; }\n.zenfg-inspector-kind-label[data-kind='buffer'] { --fgd-kind-color: ${GRAPH_VISUAL_THEME.buffer.stroke}; }\n.zenfg-inspector-kind-label[data-kind='frame-graph'] { --fgd-kind-color: var(--fgd-accent); }\n.zenfg-inspector-kind-label[data-kind='timed'] { --fgd-kind-color: var(--fgd-success); }\n.zenfg-inspector-kind-label[data-kind='not-timed'] { --fgd-kind-color: var(--fgd-muted); }\n\n.zenfg-inspector-relation-button {\n\tmax-width: 100%;\n\tpadding: 0;\n\tborder: 0;\n\tcolor: #a5def6;\n\tbackground: transparent;\n\tfont: inherit;\n\ttext-align: left;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n\tcursor: pointer;\n}\n.zenfg-inspector-relation-button:hover {\n\tcolor: #d5f3ff;\n\ttext-decoration: underline;\n\ttext-underline-offset: 2px;\n}\n.zenfg-inspector-group-toggle { width: 28px; padding: 0; color: var(--fgd-text-secondary); }\n.zenfg-inspector-empty-row {\n\theight: 60px !important;\n\tcolor: var(--fgd-muted);\n\tfont-family: var(--fgd-font-ui);\n\ttext-align: center !important;\n}\n\n/* Graph */\n.zenfg-inspector-graph-view { display: flex; flex-direction: column; }\n.zenfg-inspector-graph-toolbar {\n\tdisplay: grid;\n\tgrid-template-columns: auto minmax(0, 1fr) auto;\n\talign-items: center;\n\tgap: 10px;\n\tmin-width: 0;\n\tmin-height: 38px;\n\tpadding: 5px 6px;\n\tborder-bottom: 1px solid var(--fgd-border-subtle);\n\tbackground: var(--fgd-surface);\n}\n.zenfg-inspector-graph-mode-controls,\n.zenfg-inspector-graph-action-controls { display: flex; align-items: center; min-width: 0; }\n.zenfg-inspector-graph-action-controls { gap: 4px; }\n.zenfg-inspector-graph-toolbar .zenfg-inspector-mode-button {\n\tmargin-right: -1px;\n\tborder-radius: 0;\n}\n.zenfg-inspector-graph-toolbar .zenfg-inspector-mode-button:first-child { border-radius: 5px 0 0 5px; }\n.zenfg-inspector-graph-toolbar .zenfg-inspector-mode-button:last-child {\n\tmargin-right: 0;\n\tborder-radius: 0 5px 5px 0;\n}\n.zenfg-inspector-graph-toolbar button.active,\n.zenfg-inspector-graph-toolbar button[aria-pressed='true'] {\n\tz-index: 1;\n\tborder-color: rgba(56, 189, 248, 0.6);\n\tcolor: var(--fgd-accent);\n\tbackground: var(--fgd-accent-soft);\n}\n.zenfg-inspector-graph-toolbar button:disabled { cursor: default; opacity: 0.42; }\n.zenfg-inspector-graph-toolbar .zenfg-inspector-icon-button { width: 28px; padding: 0; }\n\n.zenfg-inspector-graph-legend {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 10px;\n\tmin-width: 0;\n\tpadding: 2px 0;\n\toverflow-x: auto;\n\tcolor: var(--fgd-muted);\n\tfont: 10px/1 var(--fgd-font-ui);\n\twhite-space: nowrap;\n}\n.zenfg-inspector-legend-item { display: inline-flex; align-items: center; gap: 4px; flex: 0 0 auto; }\n.zenfg-inspector-legend-swatch {\n\tposition: relative;\n\tdisplay: inline-block;\n\twidth: 10px;\n\theight: 8px;\n\tborder: 1px solid var(--zenfg-inspector-legend-color);\n\tborder-radius: 2px;\n\tbackground: color-mix(in srgb, var(--zenfg-inspector-legend-color) 20%, var(--fgd-canvas));\n}\n.zenfg-inspector-legend-swatch[data-shape='ellipse'] { border-radius: 50%; }\n.zenfg-inspector-legend-swatch[data-shape='group'] { border-style: double; background: transparent; }\n.zenfg-inspector-legend-swatch[data-shape='line'] {\n\twidth: 16px;\n\theight: 0;\n\tborder: 0;\n\tborder-top: 1px solid var(--zenfg-inspector-legend-color);\n\tborder-radius: 0;\n\tbackground: transparent;\n}\n.zenfg-inspector-legend-swatch[data-shape='line'][data-line-style='dotted'] { border-top-style: dotted; }\n.zenfg-inspector-legend-swatch[data-shape='line'][data-line-style='dashed'] { border-top-style: dashed; }\n.zenfg-inspector-legend-swatch:not([data-shape='line'])[data-line-style='dashed'] { border-style: dashed; }\n.zenfg-inspector-legend-swatch[data-shape='line']::after {\n\tposition: absolute;\n\ttop: -2px;\n\tright: -1px;\n\twidth: 5px;\n\theight: 4px;\n\tbackground: var(--zenfg-inspector-legend-color);\n\tclip-path: polygon(0 0, 100% 50%, 0 100%);\n\tcontent: '';\n}\n.zenfg-inspector-legend-swatch[data-shape='line'][data-hollow-arrow='true']::after {\n\ttop: -3px;\n\twidth: 4px;\n\theight: 4px;\n\tborder-top: 1px solid var(--zenfg-inspector-legend-color);\n\tborder-right: 1px solid var(--zenfg-inspector-legend-color);\n\tbackground: transparent;\n\tclip-path: none;\n\ttransform: rotate(45deg);\n}\n\n.zenfg-inspector-graph-view .zenfg-inspector-graph {\n\tflex: 1 1 auto;\n\twidth: 100%;\n\theight: auto;\n\tmin-height: 0;\n\tborder: 0;\n}\n.zenfg-inspector-graph {\n\tposition: relative;\n\twidth: 100%;\n\theight: 260px;\n\tmin-height: 220px;\n\toverflow: hidden;\n\tborder: 1px solid var(--fgd-border-subtle);\n\tbackground: var(--fgd-canvas);\n\tcolor: var(--fgd-muted);\n\tfont-size: 11px;\n}\n.zenfg-inspector-graph-canvas {\n\twidth: 100%;\n\theight: 100%;\n\tmin-width: 0;\n\tmin-height: 0;\n\toutline: none;\n}\n.zenfg-inspector-graph-status {\n\tposition: absolute;\n\tinset: 0;\n\tz-index: 2;\n\tdisplay: flex;\n\talign-items: center;\n\tjustify-content: center;\n\tgap: 8px;\n\tpadding: 16px;\n\tcolor: var(--fgd-muted);\n\tbackground: rgba(11, 15, 20, 0.82);\n\tfont: 12px/1.45 var(--fgd-font-ui);\n\ttext-align: center;\n\tpointer-events: none;\n}\n.zenfg-inspector-graph-status[data-state='layout'] { background: rgba(11, 15, 20, 0.5); }\n.zenfg-inspector-graph-status[data-state='error'] {\n\tcolor: var(--fgd-text);\n\tbackground: rgba(11, 15, 20, 0.94);\n}\n.zenfg-inspector-graph-status > span { max-width: min(560px, 80%); }\n.zenfg-inspector-graph-status button {\n\theight: 28px;\n\tpadding: 0 9px;\n\tborder: 1px solid rgba(56, 189, 248, 0.55);\n\tborder-radius: 5px;\n\tcolor: var(--fgd-accent);\n\tbackground: var(--fgd-accent-soft);\n\tfont: 600 11px/1 var(--fgd-font-ui);\n\tcursor: pointer;\n\tpointer-events: auto;\n}\n.zenfg-inspector-graph-tooltip {\n\tposition: absolute;\n\tz-index: 3;\n\tmax-width: min(380px, calc(100% - 16px));\n\tpadding: 8px 10px;\n\tborder: 1px solid var(--fgd-border-strong);\n\tborder-radius: 6px;\n\tcolor: var(--fgd-text);\n\tbackground: var(--fgd-surface-raised);\n\tbox-shadow: 0 8px 24px rgba(0, 0, 0, 0.42);\n\tfont: 11px/1.5 var(--fgd-font-mono);\n\twhite-space: pre-wrap;\n\tpointer-events: none;\n}\n\n/* Inspector */\n.zenfg-inspector-inspector {\n\tdisplay: flex;\n\tflex-direction: column;\n\tmin-width: 0;\n\tmin-height: 0;\n\toverflow: hidden;\n\tborder: 1px solid var(--fgd-border);\n\tborder-radius: 6px;\n\tbackground: var(--fgd-surface-raised);\n\tbox-shadow: -8px 0 28px rgba(0, 0, 0, 0.2);\n}\n.zenfg-inspector-inspector[hidden] { display: none; }\n.zenfg-inspector-inspector > header {\n\tdisplay: flex;\n\tflex: 0 0 auto;\n\talign-items: center;\n\tjustify-content: space-between;\n\tmin-height: 36px;\n\tpadding: 0 8px 0 10px;\n\tborder-bottom: 1px solid var(--fgd-border-subtle);\n\tbackground: var(--fgd-surface);\n}\n.zenfg-inspector-inspector > header strong {\n\tmin-width: 0;\n\toverflow: hidden;\n\tfont: 600 11px/1.3 var(--fgd-font-mono);\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n}\n.zenfg-inspector-inspector-close {\n\twidth: 28px;\n\tpadding: 0;\n\tborder-color: transparent;\n\tbackground: transparent;\n}\n.zenfg-inspector-inspector-content {\n\tflex: 1 1 auto;\n\tmin-height: 0;\n\tpadding: 10px;\n\toverflow: auto;\n}\n.zenfg-inspector-inspector-summary {\n\tdisplay: grid;\n\tgrid-template-columns: minmax(96px, auto) minmax(0, 1fr);\n\tgap: 3px 12px;\n\tmargin: 0;\n\tfont-size: 11px;\n\tline-height: 1.6;\n}\n.zenfg-inspector-inspector-summary dt { color: var(--fgd-muted); font-family: var(--fgd-font-ui); }\n.zenfg-inspector-inspector-summary dd {\n\tmin-width: 0;\n\tmargin: 0;\n\tcolor: var(--fgd-text);\n\tfont-family: var(--fgd-font-mono);\n\toverflow-wrap: anywhere;\n}\n.zenfg-inspector-inspector-relations { display: grid; gap: 8px; }\n.zenfg-inspector-inspector-relations section {\n\tdisplay: grid;\n\tgap: 6px;\n\tpadding: 8px;\n\tborder: 1px solid var(--fgd-border-subtle);\n\tborder-radius: 5px;\n\tbackground: var(--fgd-surface);\n}\n.zenfg-inspector-inspector-relations h3 {\n\tmargin: 0 0 2px;\n\tcolor: var(--fgd-muted);\n\tfont: 700 10px/1.3 var(--fgd-font-ui);\n\tletter-spacing: 0.055em;\n\ttext-transform: uppercase;\n}\n.zenfg-inspector-inspector-relations .zenfg-inspector-relation-button {\n\tfont: 11px/1.5 var(--fgd-font-mono);\n\toverflow-wrap: anywhere;\n\twhite-space: normal;\n}\n.zenfg-inspector-inspector .zenfg-inspector-raw-detail {\n\tmin-width: 100%;\n\tmargin: 0;\n\toverflow: auto;\n\tcolor: var(--fgd-text-secondary);\n\tfont: 11px/1.55 var(--fgd-font-mono);\n\twhite-space: pre;\n}\n\n/* Memory */\n.zenfg-inspector-memory-view {\n\tdisplay: grid;\n\tgrid-template-rows: auto minmax(0, 1fr);\n}\n.zenfg-inspector-memory-summary {\n\tdisplay: grid;\n\tgrid-template-columns: repeat(4, minmax(0, 1fr));\n\tgap: 6px;\n\tpadding: 6px;\n\tborder-bottom: 1px solid var(--fgd-border-subtle);\n\tbackground: var(--fgd-panel);\n}\n.zenfg-inspector-memory-summary > div {\n\tdisplay: grid;\n\tgap: 3px;\n\tmin-width: 0;\n\tpadding: 6px 8px;\n\tborder: 1px solid var(--fgd-border-subtle);\n\tborder-radius: 5px;\n\tbackground: var(--fgd-surface);\n}\n.zenfg-inspector-memory-summary span { color: var(--fgd-muted); font: 10px/1.3 var(--fgd-font-ui); }\n.zenfg-inspector-memory-summary strong {\n\toverflow: hidden;\n\tfont: 600 11px/1.35 var(--fgd-font-mono);\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n}\n.zenfg-inspector-memory-scroller { min-height: 0; overflow: auto; }\n.zenfg-inspector-memory-timeline {\n\tdisplay: grid;\n\tmin-width: 760px;\n\tpadding: 8px;\n\tfont: 11px/1.4 var(--fgd-font-mono);\n}\n.zenfg-inspector-memory-axis,\n.zenfg-inspector-memory-resource {\n\tdisplay: grid;\n\tgrid-template-columns: minmax(150px, 0.9fr) 72px minmax(320px, 2.4fr) 82px;\n\tgap: 10px;\n\talign-items: center;\n\tmin-height: 30px;\n}\n.zenfg-inspector-memory-axis {\n\tposition: sticky;\n\tz-index: 2;\n\ttop: 0;\n\tpadding: 0 8px 6px;\n\tcolor: var(--fgd-muted);\n\tbackground: var(--fgd-canvas);\n\tfont: 10px/1.3 var(--fgd-font-ui);\n}\n.zenfg-inspector-memory-axis-track,\n.zenfg-inspector-memory-track { position: relative; height: 16px; }\n.zenfg-inspector-memory-axis-track { border-bottom: 1px solid var(--fgd-border-strong); }\n.zenfg-inspector-memory-axis-track > span {\n\tposition: absolute;\n\tbottom: -1px;\n\tpadding-bottom: 5px;\n\tborder-left: 1px solid var(--fgd-border-strong);\n\tfont: 10px/1 var(--fgd-font-mono);\n\ttransform: translateX(-1px);\n}\n.zenfg-inspector-memory-allocation {\n\tdisplay: flex;\n\talign-items: center;\n\tjustify-content: space-between;\n\tgap: 12px;\n\tmargin-top: 6px;\n\tpadding: 7px 8px;\n\tborder-top: 1px solid var(--fgd-border-subtle);\n\tborder-bottom: 1px solid var(--fgd-border-subtle);\n\tbackground: var(--fgd-surface);\n}\n.zenfg-inspector-memory-allocation > span { color: var(--fgd-muted); font: 10px/1.35 var(--fgd-font-mono); }\n.zenfg-inspector-memory-allocation.muted { color: var(--fgd-muted); font-family: var(--fgd-font-ui); }\n.zenfg-inspector-memory-resource { padding: 0 8px; border-bottom: 1px solid var(--fgd-border-subtle); }\n.zenfg-inspector-memory-resource.selected,\n.zenfg-inspector-memory-allocation.selected { box-shadow: inset 2px 0 var(--fgd-accent); }\n.zenfg-inspector-memory-bar {\n\tposition: absolute;\n\ttop: 4px;\n\theight: 8px;\n\tmin-width: 2px;\n\tborder-radius: 4px;\n\tbackground: color-mix(in srgb, ${GRAPH_VISUAL_THEME.texture.stroke} 76%, transparent);\n\tbox-shadow: 0 0 0 1px color-mix(in srgb, ${GRAPH_VISUAL_THEME.texture.stroke} 36%, transparent);\n}\n.zenfg-inspector-memory-bar.buffer {\n\tbackground: color-mix(in srgb, ${GRAPH_VISUAL_THEME.buffer.stroke} 72%, transparent);\n\tbox-shadow: 0 0 0 1px color-mix(in srgb, ${GRAPH_VISUAL_THEME.buffer.stroke} 34%, transparent);\n}\n.zenfg-inspector-memory-bar.empty { display: none; }\n\n/* Diagnostics */\n.zenfg-inspector-diagnostics-scroller { width: 100%; height: 100%; overflow: auto; }\n.zenfg-inspector-diagnostics-grid {\n\tdisplay: grid;\n\tgrid-template-columns: repeat(2, minmax(0, 1fr));\n\tgap: 8px;\n\tpadding: 8px;\n}\n.zenfg-inspector-diagnostic-card {\n\tdisplay: flex;\n\tflex-direction: column;\n\tmin-width: 0;\n\tmin-height: 164px;\n\toverflow: hidden;\n\tborder: 1px solid var(--fgd-border-subtle);\n\tborder-radius: 6px;\n\tbackground: var(--fgd-surface);\n}\n.zenfg-inspector-diagnostic-card > h2 {\n\tmargin: 0;\n\tpadding: 9px 10px 3px;\n\tcolor: var(--fgd-text-secondary);\n\tfont: 650 12px/1.35 var(--fgd-font-ui);\n}\n.zenfg-inspector-diagnostic-card > p {\n\tmargin: 0;\n\tpadding: 0 10px 8px;\n\tcolor: var(--fgd-muted);\n\tfont: 11px/1.5 var(--fgd-font-ui);\n}\n.zenfg-inspector-diagnostic-card .zenfg-inspector-table-scroller { flex: 1 1 auto; max-height: 280px; }\n.zenfg-inspector-diagnostic-card .zenfg-inspector-workbench-table { min-width: 100%; table-layout: fixed; }\n.zenfg-inspector-diagnostic-card .zenfg-inspector-workbench-table td {\n\toverflow-wrap: anywhere;\n\twhite-space: normal;\n}\n.zenfg-inspector-diagnostic-list { display: grid; gap: 2px; padding: 6px; overflow: auto; }\n.zenfg-inspector-diagnostic-list .zenfg-inspector-relation-button {\n\tpadding: 6px 8px;\n\tborder-radius: 4px;\n\tcolor: var(--fgd-text-secondary);\n\tfont: 11px/1.45 var(--fgd-font-mono);\n}\n.zenfg-inspector-diagnostic-list .zenfg-inspector-relation-button:hover {\n\tbackground: rgba(180, 190, 202, 0.05);\n\ttext-decoration: none;\n}\n.zenfg-inspector-diagnostic-list .selected { box-shadow: inset 2px 0 var(--fgd-accent); }\n\n/* Responsive */\n@container zenfg-inspector (max-width: 960px) {\n\t.zenfg-inspector-workspace.inspector-open { grid-template-columns: minmax(0, 1fr); }\n\t.zenfg-inspector-workspace.inspector-open::after {\n\t\tposition: absolute;\n\t\tz-index: 10;\n\t\tinset: 0;\n\t\tbackground: rgba(3, 7, 12, 0.38);\n\t\tcontent: '';\n\t\tpointer-events: none;\n\t}\n\t.zenfg-inspector-inspector {\n\t\tposition: absolute;\n\t\tz-index: 20;\n\t\ttop: 0;\n\t\tright: 0;\n\t\tbottom: 0;\n\t\twidth: min(380px, calc(100% - 24px));\n\t\tbox-shadow: -18px 0 42px rgba(0, 0, 0, 0.42);\n\t}\n\t.zenfg-inspector-graph-toolbar {\n\t\tgrid-template-columns: minmax(0, 1fr) auto;\n\t\tgap: 6px 8px;\n\t}\n\t.zenfg-inspector-graph-legend {\n\t\tgrid-column: 1 / -1;\n\t\tgrid-row: 2;\n\t\tpadding-top: 6px;\n\t\tborder-top: 1px solid var(--fgd-border-subtle);\n\t}\n\t.zenfg-inspector-graph-action-controls { justify-self: end; }\n}\n\n@container zenfg-inspector (max-width: 840px) {\n\t.zenfg-inspector-workbench-command-bar {\n\t\tgrid-template-columns: minmax(0, 1fr) auto;\n\t}\n\t.zenfg-inspector-brand { grid-column: 1; grid-row: 1; min-height: 36px; display: flex; align-items: center; }\n\t.zenfg-inspector-brand::after { display: none; }\n\t.zenfg-inspector-workbench-actions { grid-column: 2; grid-row: 1; }\n\t.zenfg-inspector-workbench-tabs { grid-column: 1 / -1; grid-row: 2; }\n\t.zenfg-inspector-workbench-command-bar.branding-hidden .zenfg-inspector-workbench-tabs {\n\t\tgrid-column: 1;\n\t\tgrid-row: 1;\n\t}\n\t.zenfg-inspector-diagnostics-grid { grid-template-columns: minmax(0, 1fr); }\n}\n\n@container zenfg-inspector (max-width: 720px) {\n\t.zenfg-inspector-body { padding: 8px; }\n\t.zenfg-inspector-overview-view { padding: 8px; }\n\t.zenfg-inspector-capture-summary > section { padding: 10px; }\n\t.zenfg-inspector-workbench-actions { padding-left: 6px; }\n\t.zenfg-inspector-command-status { max-width: 104px; }\n\t.zenfg-inspector-workbench-tabs > button { flex: 0 0 auto; padding-inline: 8px; }\n\t.zenfg-inspector-memory-summary { grid-template-columns: repeat(2, minmax(0, 1fr)); }\n\t.zenfg-inspector-view-toolbar input { flex-basis: 100%; }\n\t.zenfg-inspector-graph-toolbar { display: flex; flex-wrap: wrap; }\n\t.zenfg-inspector-graph-legend { order: 3; flex-basis: 100%; }\n\t.zenfg-inspector-graph-action-controls { margin-left: auto; }\n\t.zenfg-inspector-graph-view .zenfg-inspector-graph { height: auto; }\n}\n\n@media (prefers-reduced-motion: reduce) {\n\t.zenfg-inspector *,\n\t.zenfg-inspector *::before,\n\t.zenfg-inspector *::after {\n\t\tscroll-behavior: auto !important;\n\t\ttransition-duration: 0.01ms !important;\n\t\tanimation-duration: 0.01ms !important;\n\t\tanimation-iteration-count: 1 !important;\n\t}\n}\n`;\n\n/**\n * Installs the inspector's shared stylesheet in the current document once.\n *\n * @remarks This is safe to call repeatedly and is a no-op during server-side\n * rendering. `FrameGraphInspector` calls it automatically.\n */\nexport function ensureFrameGraphInspectorStyles(): void {\n\tif (typeof document === 'undefined' || document.getElementById(STYLE_ELEMENT_ID)) {\n\t\treturn;\n\t}\n\n\tconst style = document.createElement('style');\n\tstyle.id = STYLE_ELEMENT_ID;\n\tstyle.textContent = FRAME_GRAPH_DEBUG_PANEL_CSS;\n\tdocument.head.appendChild(style);\n}\n"]}
|
|
1
|
+
{"version":3,"file":"styles.js","sourceRoot":"","sources":["../src/styles.ts"],"names":[],"mappings":"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","sourcesContent":["import { FRAME_GRAPH_DEBUG_VISUAL_THEME, GRAPH_VISUAL_THEME } from './panelVisualTheme.ts';\n\nconst STYLE_ELEMENT_ID = 'zenfg-inspector-panel-styles';\nconst theme = FRAME_GRAPH_DEBUG_VISUAL_THEME;\n\nconst FRAME_GRAPH_DEBUG_PANEL_CSS = `\n/* Tokens and workbench root */\n.zenfg-inspector {\n\t--fgd-canvas: ${theme.canvas};\n\t--fgd-panel: var(--zenfg-inspector-background, ${theme.panel});\n\t--fgd-surface: var(--zenfg-inspector-surface, ${theme.surface});\n\t--fgd-surface-raised: var(--zenfg-inspector-surface-raised, ${theme.surfaceRaised});\n\t--fgd-surface-hover: var(--zenfg-inspector-surface-hover, ${theme.surfaceHover});\n\t--fgd-border: var(--zenfg-inspector-border, ${theme.border});\n\t--fgd-border-subtle: var(--zenfg-inspector-border-subtle, rgba(180, 190, 202, 0.11));\n\t--fgd-border-strong: var(--zenfg-inspector-border-strong, rgba(180, 190, 202, 0.28));\n\t--fgd-text: var(--zenfg-inspector-text, ${theme.text});\n\t--fgd-text-secondary: var(--zenfg-inspector-text-secondary, ${theme.textSecondary});\n\t--fgd-muted: var(--zenfg-inspector-muted, ${theme.muted});\n\t--fgd-accent: var(--zenfg-inspector-accent, ${theme.accent});\n\t--fgd-accent-soft: var(--zenfg-inspector-accent-soft, rgba(56, 189, 248, 0.12));\n\t--fgd-accent-hover: var(--zenfg-inspector-accent-hover, rgba(56, 189, 248, 0.18));\n\t--fgd-success: var(--zenfg-inspector-success, ${theme.success});\n\t--fgd-warning: var(--zenfg-inspector-warning, ${theme.warning});\n\t--fgd-danger: var(--zenfg-inspector-danger, ${theme.danger});\n\t--fgd-font-ui: var(--zenfg-inspector-font-ui, ${theme.fontUi});\n\t--fgd-font-mono: var(--zenfg-inspector-font-mono, ${theme.fontMono});\n\tposition: relative;\n\tdisplay: block;\n\twidth: 100%;\n\theight: 100%;\n\tmin-width: 0;\n\tmin-height: 0;\n\toverflow: hidden;\n\tcolor: var(--fgd-text);\n\tbackground: var(--fgd-canvas);\n\tfont-family: var(--fgd-font-ui);\n\tfont-size: 12px;\n\tline-height: 1.45;\n\tcolor-scheme: dark;\n\tcontainer-name: zenfg-inspector;\n\tcontainer-type: inline-size;\n}\n\n.zenfg-inspector,\n.zenfg-inspector * { box-sizing: border-box; }\n\n.zenfg-inspector-body {\n\twidth: 100%;\n\theight: 100%;\n\tmin-height: 0;\n\tpadding: 10px;\n\toverflow: hidden;\n\tborder: 0;\n\tborder-radius: 0;\n\tbackground: var(--zenfg-inspector-background, var(--fgd-panel));\n}\n\n.zenfg-inspector-content {\n\tmin-width: 0;\n\tmin-height: 0;\n\theight: 100%;\n\toverflow: hidden;\n}\n\n.zenfg-inspector-drop-overlay {\n\tposition: absolute;\n\tz-index: 40;\n\tinset: 10px;\n\tdisplay: grid;\n\tplace-content: center;\n\tgap: 6px;\n\tborder: 2px dashed var(--fgd-accent);\n\tborder-radius: var(--zenfg-inspector-radius-md, 6px);\n\tcolor: #bae6fd;\n\tbackground: color-mix(in srgb, var(--fgd-canvas) 90%, transparent);\n\tfont: 600 13px/1.4 var(--fgd-font-ui);\n\ttext-align: center;\n\tpointer-events: none;\n}\n.zenfg-inspector-drop-overlay[hidden] { display: none; }\n.zenfg-inspector-drop-overlay > span { color: var(--fgd-muted); font-size: 11px; }\n\n.zenfg-inspector-capture-summary[hidden],\n.zenfg-inspector-command-status[hidden],\n.zenfg-inspector-capture-action[hidden],\n.zenfg-inspector-workbench-empty[hidden],\n.zenfg-inspector-open-inspector[hidden],\n.zenfg-inspector-inspector.unavailable,\n.zenfg-inspector-view[hidden],\n.zenfg-inspector-graph-status[hidden] { display: none !important; }\n\n.zenfg-inspector-muted {\n\tmargin: 0;\n\tcolor: var(--fgd-muted);\n\tfont: 11px/1.5 var(--fgd-font-ui);\n}\n\n.zenfg-inspector-control-icon {\n\tdisplay: block;\n\tflex: 0 0 auto;\n\twidth: 14px;\n\theight: 14px;\n}\n\n.zenfg-inspector-table-scroller,\n.zenfg-inspector-memory-scroller,\n.zenfg-inspector-diagnostics-scroller,\n.zenfg-inspector-inspector-content,\n.zenfg-inspector-workbench-tabs,\n.zenfg-inspector-graph-legend {\n\tscrollbar-width: thin;\n\tscrollbar-color: rgba(180, 190, 202, 0.25) transparent;\n}\n\n/* Workbench and summary */\n.zenfg-inspector-workbench {\n\tdisplay: grid;\n\tgrid-template-rows: auto minmax(0, 1fr);\n\tgap: 8px;\n\theight: 100%;\n\tmin-width: 0;\n\tmin-height: 0;\n\tfont-family: var(--fgd-font-ui);\n}\n\n.zenfg-inspector-capture-summary {\n\tdisplay: grid;\n\tgrid-template-columns: repeat(auto-fit, minmax(min(230px, 100%), 1fr));\n\talign-content: start;\n\tgap: 8px;\n\tmin-width: 0;\n\tbackground: transparent;\n}\n\n.zenfg-inspector-capture-summary > section {\n\tmin-width: 0;\n\tpadding: 12px;\n\tborder: 1px solid var(--fgd-border-subtle);\n\tborder-radius: 6px;\n\tbackground: var(--fgd-surface);\n}\n\n.zenfg-inspector-capture-summary h2 {\n\tmargin: 0 0 5px;\n\tcolor: var(--fgd-text-secondary);\n\tfont-size: 10px;\n\tfont-weight: 700;\n\tletter-spacing: 0.075em;\n\tline-height: 1.2;\n\ttext-transform: uppercase;\n}\n\n.zenfg-inspector-capture-summary section > div {\n\tdisplay: grid;\n\tgrid-template-columns: auto minmax(0, 1fr);\n\tgap: 8px;\n\tmin-width: 0;\n\tfont-size: 11px;\n\tline-height: 1.5;\n}\n\n.zenfg-inspector-capture-summary span { color: var(--fgd-muted); white-space: nowrap; }\n.zenfg-inspector-capture-summary strong {\n\tmin-width: 0;\n\toverflow: hidden;\n\tcolor: var(--fgd-text);\n\tfont: 600 11px/1.5 var(--fgd-font-mono);\n\ttext-align: right;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n}\n\n/* Command bar, tabs, and controls */\n.zenfg-inspector-workbench-command-bar {\n\tposition: relative;\n\tdisplay: grid;\n\tgrid-template-columns: auto minmax(0, 1fr) auto;\n\talign-items: center;\n\tmin-width: 0;\n\tborder-bottom: 1px solid var(--fgd-border-subtle);\n}\n.zenfg-inspector-workbench-command-bar.branding-hidden {\n\tgrid-template-columns: minmax(0, 1fr) auto;\n}\n\n.zenfg-inspector-brand {\n\tposition: relative;\n\tdisplay: flex;\n\talign-items: center;\n\tmin-width: 0;\n\tmin-height: 32px;\n\tpadding: 0 12px 0 4px;\n\toverflow: hidden;\n\tcolor: var(--fgd-text);\n\tfont: 700 12px/1 var(--fgd-font-ui);\n\tletter-spacing: 0.015em;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n}\n.zenfg-inspector-brand::after {\n\tposition: absolute;\n\ttop: 50%;\n\tright: 0;\n\twidth: 1px;\n\theight: 16px;\n\tbackground: var(--fgd-border);\n\tcontent: '';\n\ttransform: translateY(-50%);\n}\n\n.zenfg-inspector-workbench-tabs,\n.zenfg-inspector-subtabs,\n.zenfg-inspector-inspector-tabs {\n\tdisplay: flex;\n\talign-items: end;\n\tmin-width: 0;\n\tborder-bottom: 1px solid var(--fgd-border-subtle);\n}\n\n.zenfg-inspector-workbench-tabs {\n\toverflow-x: auto;\n\tborder-bottom: 0;\n}\n\n.zenfg-inspector-workbench-tabs > button,\n.zenfg-inspector-subtabs > button,\n.zenfg-inspector-inspector-tabs > button {\n\tmin-height: 32px;\n\tpadding: 6px 10px;\n\tborder: 0;\n\tborder-bottom: 2px solid transparent;\n\tcolor: var(--fgd-muted);\n\tbackground: transparent;\n\tfont: 600 12px/1 var(--fgd-font-ui);\n\twhite-space: nowrap;\n\tcursor: pointer;\n\ttransition: color 120ms ease, background-color 120ms ease, border-color 120ms ease;\n}\n\n.zenfg-inspector-workbench-tabs > button:hover:not(:disabled),\n.zenfg-inspector-subtabs > button:hover:not(:disabled),\n.zenfg-inspector-inspector-tabs > button:hover:not(:disabled) {\n\tcolor: var(--fgd-text-secondary);\n\tbackground: rgba(180, 190, 202, 0.055);\n}\n\n.zenfg-inspector-workbench-tabs > button.active,\n.zenfg-inspector-subtabs > button.active,\n.zenfg-inspector-inspector-tabs > button.active {\n\tborder-bottom-color: var(--fgd-accent);\n\tcolor: var(--fgd-text);\n\tbackground: linear-gradient(to top, var(--fgd-accent-soft), transparent 55%);\n}\n\n.zenfg-inspector-workbench-tabs > button:disabled,\n.zenfg-inspector-subtabs > button:disabled,\n.zenfg-inspector-inspector-tabs > button:disabled { cursor: default; opacity: 0.42; }\n\n.zenfg-inspector-workbench-actions {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 6px;\n\tpadding-left: 8px;\n\toverflow-x: auto;\n\tscrollbar-width: thin;\n}\n\n.zenfg-inspector-export-menu {\n\tposition: absolute;\n\tz-index: 30;\n\ttop: calc(100% + 4px);\n\tright: 0;\n\tdisplay: grid;\n\tmin-width: 142px;\n\tpadding: 4px;\n\tborder: 1px solid var(--fgd-border-strong);\n\tborder-radius: 6px;\n\tbackground: var(--fgd-surface-raised);\n\tbox-shadow: 0 10px 24px rgba(0, 0, 0, 0.34);\n}\n.zenfg-inspector-export-menu[hidden] { display: none; }\n.zenfg-inspector-export-menu > button {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 7px;\n\tpadding: 7px 9px;\n\tborder: 0;\n\tborder-radius: 4px;\n\tcolor: var(--fgd-text-secondary);\n\tbackground: transparent;\n\tfont: 600 11px/1.2 var(--fgd-font-ui);\n\ttext-align: left;\n\twhite-space: nowrap;\n\tcursor: pointer;\n}\n.zenfg-inspector-export-menu > button:hover:not(:disabled) {\n\tcolor: var(--fgd-text);\n\tbackground: var(--fgd-surface-hover);\n}\n.zenfg-inspector-export-menu > button:disabled { cursor: default; opacity: 0.46; }\n\n.zenfg-inspector-workbench-actions > button,\n.zenfg-inspector-graph-toolbar button,\n.zenfg-inspector-group-toggle,\n.zenfg-inspector-inspector-close {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tjustify-content: center;\n\tgap: 6px;\n\theight: 28px;\n\tmin-height: 28px;\n\tpadding: 0 9px;\n\tborder: 1px solid var(--fgd-border);\n\tborder-radius: 5px;\n\tcolor: var(--fgd-text-secondary);\n\tbackground: var(--fgd-surface);\n\tfont: 600 11px/1 var(--fgd-font-ui);\n\twhite-space: nowrap;\n\tcursor: pointer;\n\ttransition: color 120ms ease, background-color 120ms ease, border-color 120ms ease;\n}\n\n.zenfg-inspector-workbench-actions > button:hover:not(:disabled),\n.zenfg-inspector-graph-toolbar button:hover:not(:disabled),\n.zenfg-inspector-group-toggle:hover:not(:disabled),\n.zenfg-inspector-inspector-close:hover:not(:disabled) {\n\tborder-color: var(--fgd-border-strong);\n\tcolor: var(--fgd-text);\n\tbackground: var(--fgd-surface-hover);\n}\n\n.zenfg-inspector-workbench-actions > button[data-tone='accent'] {\n\tborder-color: rgba(56, 189, 248, 0.5);\n\tcolor: #bae6fd;\n\tbackground: var(--fgd-accent-soft);\n}\n.zenfg-inspector-workbench-actions > button[data-tone='accent']:hover:not(:disabled) {\n\tborder-color: var(--fgd-accent);\n\tbackground: var(--fgd-accent-hover);\n}\n.zenfg-inspector-workbench-actions > button[data-tone='pending'] {\n\tborder-color: rgba(56, 189, 248, 0.42);\n\tcolor: var(--fgd-text-secondary);\n\tbackground: var(--fgd-accent-soft);\n}\n.zenfg-inspector-workbench-actions > button[data-tone='success'] {\n\tborder-color: rgba(52, 211, 153, 0.46);\n\tcolor: var(--fgd-success);\n\tbackground: rgba(52, 211, 153, 0.1);\n}\n.zenfg-inspector-workbench-actions > button:disabled { cursor: default; opacity: 0.46; }\n.zenfg-inspector-workbench-actions > button[aria-busy='true']:disabled { opacity: 0.72; }\n\n.zenfg-inspector-command-status {\n\tmax-width: 220px;\n\toverflow: hidden;\n\tcolor: var(--fgd-muted);\n\tfont: 11px/1.35 var(--fgd-font-ui);\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n}\n.zenfg-inspector-command-status[data-tone='error'] { color: var(--fgd-danger); }\n\n.zenfg-inspector-control-icon[data-icon='spinner'],\nbutton[aria-busy='true'] > .zenfg-inspector-control-icon {\n\tanimation: zenfg-inspector-spin 800ms linear infinite;\n}\n@keyframes zenfg-inspector-spin { to { transform: rotate(360deg); } }\n\n.zenfg-inspector-workbench-tabs > button:focus-visible,\n.zenfg-inspector-subtabs > button:focus-visible,\n.zenfg-inspector-inspector-tabs > button:focus-visible,\n.zenfg-inspector-workbench-actions > button:focus-visible,\n.zenfg-inspector-graph-toolbar button:focus-visible,\n.zenfg-inspector-group-toggle:focus-visible,\n.zenfg-inspector-inspector-close:focus-visible,\n.zenfg-inspector-relation-button:focus-visible,\n.zenfg-inspector-view-toolbar input:focus-visible,\n.zenfg-inspector-view-toolbar select:focus-visible {\n\toutline: 2px solid var(--fgd-accent);\n\toutline-offset: 1px;\n}\n\n/* Workspace and empty states */\n.zenfg-inspector-overview-view {\n\tpadding: 12px;\n\toverflow: auto;\n\tbackground: var(--fgd-canvas);\n}\n\n.zenfg-inspector-workspace {\n\tposition: relative;\n\tdisplay: grid;\n\tgrid-template-columns: minmax(0, 1fr);\n\tgap: 8px;\n\tmin-width: 0;\n\tmin-height: 0;\n\toverflow: hidden;\n}\n.zenfg-inspector-workspace.inspector-open {\n\tgrid-template-columns: minmax(0, 1fr) minmax(360px, 28vw);\n}\n\n.zenfg-inspector-main {\n\tdisplay: grid;\n\tgrid-template: minmax(0, 1fr) / minmax(0, 1fr);\n\tmin-width: 0;\n\tmin-height: 0;\n\toverflow: hidden;\n}\n\n.zenfg-inspector-workbench-empty {\n\tgrid-area: 1 / 1;\n\tdisplay: flex;\n\tflex-direction: column;\n\talign-items: center;\n\tjustify-content: center;\n\tgap: 8px;\n\tmin-width: 0;\n\tmin-height: 0;\n\tpadding: 24px;\n\tcolor: var(--fgd-muted);\n\tfont: 12px/1.5 var(--fgd-font-ui);\n\ttext-align: center;\n}\n.zenfg-inspector-workbench-empty > .zenfg-inspector-control-icon {\n\twidth: 22px;\n\theight: 22px;\n\tcolor: var(--fgd-text-secondary);\n}\n.zenfg-inspector-workbench-empty[data-state='error'] > .zenfg-inspector-control-icon { color: var(--fgd-danger); }\n.zenfg-inspector-workbench-empty[data-state='capturing'] > .zenfg-inspector-control-icon { color: var(--fgd-accent); }\n.zenfg-inspector-empty-message { max-width: 520px; }\n.zenfg-inspector-empty-detail { color: color-mix(in srgb, var(--fgd-muted) 78%, transparent); font-size: 11px; }\n\n.zenfg-inspector-view {\n\tgrid-area: 1 / 1;\n\tmin-width: 0;\n\tmin-height: 0;\n\toverflow: hidden;\n\tborder: 1px solid var(--fgd-border-subtle);\n\tborder-radius: 6px;\n\tbackground: var(--fgd-canvas);\n}\n\n.zenfg-inspector-view-toolbar {\n\tdisplay: flex;\n\tflex: 0 0 auto;\n\tflex-wrap: wrap;\n\talign-items: center;\n\tgap: 6px;\n\tpadding: 6px 8px;\n\tborder-bottom: 1px solid var(--fgd-border-subtle);\n\tbackground: var(--fgd-surface);\n}\n\n.zenfg-inspector-view-toolbar input,\n.zenfg-inspector-view-toolbar select {\n\tmin-width: 0;\n\theight: 28px;\n\tpadding: 4px 8px;\n\tborder: 1px solid var(--fgd-border);\n\tborder-radius: 5px;\n\tcolor: var(--fgd-text);\n\tbackground: var(--fgd-panel);\n\tfont: 11px/1 var(--fgd-font-ui);\n}\n.zenfg-inspector-view-toolbar input { flex: 1 1 210px; }\n.zenfg-inspector-view-toolbar input::placeholder { color: var(--fgd-muted); opacity: 0.86; }\n.zenfg-inspector-view-toolbar select { flex: 0 1 auto; color-scheme: dark; }\n\n.zenfg-inspector-passes-view,\n.zenfg-inspector-resources-view { display: flex; flex-direction: column; }\n.zenfg-inspector-subtabs,\n.zenfg-inspector-inspector-tabs {\n\tflex: 0 0 auto;\n\tpadding: 0 4px;\n\tbackground: var(--fgd-panel);\n}\n.zenfg-inspector-subview { flex: 1 1 auto; min-height: 0; overflow: hidden; }\n\n/* Tables and relations */\n.zenfg-inspector-table-scroller {\n\twidth: 100%;\n\theight: 100%;\n\tmin-height: 0;\n\toverflow: auto;\n}\n.zenfg-inspector-workbench-table {\n\twidth: 100%;\n\tmin-width: 720px;\n\tborder-collapse: collapse;\n\tfont: 11px/1.45 var(--fgd-font-mono);\n}\n.zenfg-inspector-workbench-table th,\n.zenfg-inspector-workbench-table td {\n\theight: 30px;\n\tmax-width: 360px;\n\tpadding: 6px 10px;\n\tborder-bottom: 1px solid var(--fgd-border-subtle);\n\toverflow: hidden;\n\ttext-align: left;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n}\n.zenfg-inspector-workbench-table th {\n\tposition: sticky;\n\tz-index: 2;\n\ttop: 0;\n\tcolor: var(--fgd-muted);\n\tbackground: var(--fgd-panel);\n\tfont: 650 10px/1.3 var(--fgd-font-ui);\n\tletter-spacing: 0.025em;\n}\n.zenfg-inspector-workbench-table [data-column='numeric'] {\n\tfont-variant-numeric: tabular-nums;\n\ttext-align: right;\n}\n.zenfg-inspector-workbench-table [data-column='code'] { font-family: var(--fgd-font-mono); }\n.zenfg-inspector-workbench-table tbody tr:hover td { background: rgba(180, 190, 202, 0.045); }\n\n.zenfg-inspector-workbench-table tr.selected td,\n.zenfg-inspector-diagnostic-list .selected,\n.zenfg-inspector-memory-allocation.selected,\n.zenfg-inspector-memory-resource.selected { background: var(--fgd-accent-soft); }\n.zenfg-inspector-workbench-table tr.selected td:first-child { box-shadow: inset 2px 0 var(--fgd-accent); }\n\n.zenfg-inspector-workbench-table td small {\n\tdisplay: block;\n\tmargin-top: 2px;\n\toverflow: hidden;\n\tcolor: var(--fgd-muted);\n\tfont: 10px/1.4 var(--fgd-font-ui);\n\ttext-overflow: ellipsis;\n}\n\n.zenfg-inspector-kind-label {\n\t--fgd-kind-color: var(--fgd-muted);\n\tdisplay: inline-flex;\n\talign-items: center;\n\tgap: 6px;\n\tmax-width: 100%;\n\toverflow: hidden;\n\tcolor: var(--fgd-text-secondary);\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n}\n.zenfg-inspector-kind-label::before {\n\twidth: 6px;\n\theight: 6px;\n\tflex: 0 0 auto;\n\tborder: 1px solid color-mix(in srgb, var(--fgd-kind-color) 78%, white);\n\tborder-radius: 50%;\n\tbackground: color-mix(in srgb, var(--fgd-kind-color) 72%, transparent);\n\tcontent: '';\n}\n.zenfg-inspector-kind-label[data-kind='render'] { --fgd-kind-color: ${GRAPH_VISUAL_THEME.render.stroke}; }\n.zenfg-inspector-kind-label[data-kind='compute'] { --fgd-kind-color: ${GRAPH_VISUAL_THEME.compute.stroke}; }\n.zenfg-inspector-kind-label[data-kind='copy'] { --fgd-kind-color: ${GRAPH_VISUAL_THEME.copy.stroke}; }\n.zenfg-inspector-kind-label[data-kind='clear-buffer'] { --fgd-kind-color: ${GRAPH_VISUAL_THEME.clear.stroke}; }\n.zenfg-inspector-kind-label[data-kind='command'] { --fgd-kind-color: ${GRAPH_VISUAL_THEME.command.stroke}; }\n.zenfg-inspector-kind-label[data-kind='external-submission'] { --fgd-kind-color: ${GRAPH_VISUAL_THEME.external.stroke}; }\n.zenfg-inspector-kind-label[data-kind='opaque'] { --fgd-kind-color: ${GRAPH_VISUAL_THEME.external.stroke}; }\n.zenfg-inspector-kind-label[data-kind='texture'] { --fgd-kind-color: ${GRAPH_VISUAL_THEME.texture.stroke}; }\n.zenfg-inspector-kind-label[data-kind='buffer'] { --fgd-kind-color: ${GRAPH_VISUAL_THEME.buffer.stroke}; }\n.zenfg-inspector-kind-label[data-kind='frame-graph'] { --fgd-kind-color: var(--fgd-accent); }\n.zenfg-inspector-kind-label[data-kind='timed'] { --fgd-kind-color: var(--fgd-success); }\n.zenfg-inspector-kind-label[data-kind='not-timed'] { --fgd-kind-color: var(--fgd-muted); }\n\n.zenfg-inspector-relation-button {\n\tmax-width: 100%;\n\tpadding: 0;\n\tborder: 0;\n\tcolor: #a5def6;\n\tbackground: transparent;\n\tfont: inherit;\n\ttext-align: left;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n\tcursor: pointer;\n}\n.zenfg-inspector-relation-button:hover {\n\tcolor: #d5f3ff;\n\ttext-decoration: underline;\n\ttext-underline-offset: 2px;\n}\n.zenfg-inspector-group-toggle { width: 28px; padding: 0; color: var(--fgd-text-secondary); }\n.zenfg-inspector-empty-row {\n\theight: 60px !important;\n\tcolor: var(--fgd-muted);\n\tfont-family: var(--fgd-font-ui);\n\ttext-align: center !important;\n}\n\n/* Graph */\n.zenfg-inspector-graph-view { display: flex; flex-direction: column; }\n.zenfg-inspector-graph-toolbar {\n\tdisplay: grid;\n\tgrid-template-columns: minmax(0, 1fr) auto;\n\talign-items: center;\n\tgap: 10px;\n\tmin-width: 0;\n\tmin-height: 38px;\n\tpadding: 5px 6px;\n\tborder-bottom: 1px solid var(--fgd-border-subtle);\n\tbackground: var(--fgd-surface);\n}\n.zenfg-inspector-graph-action-controls { display: flex; align-items: center; min-width: 0; }\n.zenfg-inspector-graph-action-controls { gap: 4px; }\n.zenfg-inspector-graph-toolbar button.active,\n.zenfg-inspector-graph-toolbar button[aria-pressed='true'] {\n\tz-index: 1;\n\tborder-color: rgba(56, 189, 248, 0.6);\n\tcolor: var(--fgd-accent);\n\tbackground: var(--fgd-accent-soft);\n}\n.zenfg-inspector-graph-toolbar button:disabled { cursor: default; opacity: 0.42; }\n.zenfg-inspector-graph-toolbar .zenfg-inspector-icon-button { width: 28px; padding: 0; }\n\n.zenfg-inspector-graph-legend {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 10px;\n\tmin-width: 0;\n\tpadding: 2px 0;\n\toverflow-x: auto;\n\tcolor: var(--fgd-muted);\n\tfont: 10px/1 var(--fgd-font-ui);\n\twhite-space: nowrap;\n}\n.zenfg-inspector-legend-item { display: inline-flex; align-items: center; gap: 4px; flex: 0 0 auto; }\n.zenfg-inspector-legend-group { display: inline-flex; align-items: center; gap: 10px; flex: 0 0 auto; }\n.zenfg-inspector-legend-group + .zenfg-inspector-legend-group { border-left: 1px solid var(--fgd-border-subtle); padding-left: 10px; }\n.zenfg-inspector-legend-heading { color: var(--fgd-text-secondary); font-weight: 600; }\n.zenfg-inspector-legend-swatch {\n\tposition: relative;\n\tdisplay: inline-block;\n\twidth: 10px;\n\theight: 8px;\n\tborder: 1px solid var(--zenfg-inspector-legend-color);\n\tborder-radius: 2px;\n\tbackground: color-mix(in srgb, var(--zenfg-inspector-legend-color) 20%, var(--fgd-canvas));\n}\n.zenfg-inspector-legend-swatch[data-shape='ellipse'] { border-radius: 50%; }\n.zenfg-inspector-legend-swatch[data-shape='group'] { background: transparent; }\n.zenfg-inspector-legend-swatch[data-shape='cut-rectangle'],\n.zenfg-inspector-legend-swatch[data-shape='tag'] { width: 14px; height: 10px; border: 0; background: transparent; }\n.zenfg-inspector-legend-swatch > svg { display: block; width: 100%; height: 100%; overflow: visible; }\n.zenfg-inspector-legend-swatch > svg polygon {\n\tfill: color-mix(in srgb, var(--zenfg-inspector-legend-color) 20%, var(--fgd-canvas));\n\tstroke: var(--zenfg-inspector-legend-color);\n\tstroke-width: 1.4;\n}\n.zenfg-inspector-legend-swatch[data-shape='line'] {\n\twidth: 16px;\n\theight: 0;\n\tborder: 0;\n\tborder-top: 1px solid var(--zenfg-inspector-legend-color);\n\tborder-radius: 0;\n\tbackground: transparent;\n}\n.zenfg-inspector-legend-swatch[data-shape='line'][data-line-style='dotted'] { border-top-style: dotted; }\n.zenfg-inspector-legend-swatch[data-shape='line'][data-line-style='dashed'] { border-top-style: dashed; }\n.zenfg-inspector-legend-swatch:not([data-shape='line'])[data-line-style='dashed'] { border-style: dashed; }\n.zenfg-inspector-legend-swatch[data-shape='line']::after {\n\tposition: absolute;\n\ttop: -2px;\n\tright: -1px;\n\twidth: 5px;\n\theight: 4px;\n\tbackground: var(--zenfg-inspector-legend-color);\n\tclip-path: polygon(0 0, 100% 50%, 0 100%);\n\tcontent: '';\n}\n.zenfg-inspector-legend-swatch[data-shape='line'][data-hollow-arrow='true']::after {\n\ttop: -3px;\n\twidth: 4px;\n\theight: 4px;\n\tborder-top: 1px solid var(--zenfg-inspector-legend-color);\n\tborder-right: 1px solid var(--zenfg-inspector-legend-color);\n\tbackground: transparent;\n\tclip-path: none;\n\ttransform: rotate(45deg);\n}\n\n.zenfg-inspector-graph-view .zenfg-inspector-graph {\n\tflex: 1 1 auto;\n\twidth: 100%;\n\theight: auto;\n\tmin-height: 0;\n\tborder: 0;\n}\n.zenfg-inspector-graph {\n\tposition: relative;\n\twidth: 100%;\n\theight: 260px;\n\tmin-height: 220px;\n\toverflow: hidden;\n\tborder: 1px solid var(--fgd-border-subtle);\n\tbackground: var(--fgd-canvas);\n\tcolor: var(--fgd-muted);\n\tfont-size: 11px;\n}\n.zenfg-inspector-graph-canvas {\n\twidth: 100%;\n\theight: 100%;\n\tmin-width: 0;\n\tmin-height: 0;\n\toutline: none;\n}\n.zenfg-inspector-graph-status {\n\tposition: absolute;\n\tinset: 0;\n\tz-index: 2;\n\tdisplay: flex;\n\talign-items: center;\n\tjustify-content: center;\n\tgap: 8px;\n\tpadding: 16px;\n\tcolor: var(--fgd-muted);\n\tbackground: rgba(11, 15, 20, 0.82);\n\tfont: 12px/1.45 var(--fgd-font-ui);\n\ttext-align: center;\n\tpointer-events: none;\n}\n.zenfg-inspector-graph-status[data-state='layout'] { background: rgba(11, 15, 20, 0.5); }\n.zenfg-inspector-graph-status[data-state='error'] {\n\tcolor: var(--fgd-text);\n\tbackground: rgba(11, 15, 20, 0.94);\n}\n.zenfg-inspector-graph-status > span { max-width: min(560px, 80%); }\n.zenfg-inspector-graph-status button {\n\theight: 28px;\n\tpadding: 0 9px;\n\tborder: 1px solid rgba(56, 189, 248, 0.55);\n\tborder-radius: 5px;\n\tcolor: var(--fgd-accent);\n\tbackground: var(--fgd-accent-soft);\n\tfont: 600 11px/1 var(--fgd-font-ui);\n\tcursor: pointer;\n\tpointer-events: auto;\n}\n.zenfg-inspector-graph-tooltip {\n\tposition: absolute;\n\tz-index: 3;\n\twidth: max-content;\n\tmax-width: min(380px, calc(100% - 16px));\n\tpadding: 8px 10px;\n\tborder: 1px solid var(--fgd-border-strong);\n\tborder-radius: 6px;\n\tcolor: var(--fgd-text);\n\tbackground: var(--fgd-surface-raised);\n\tbox-shadow: 0 8px 24px rgba(0, 0, 0, 0.42);\n\tfont: 11px/1.5 var(--fgd-font-mono);\n\twhite-space: pre-wrap;\n\tpointer-events: none;\n}\n\n/* Inspector */\n.zenfg-inspector-inspector {\n\tdisplay: flex;\n\tflex-direction: column;\n\tmin-width: 0;\n\tmin-height: 0;\n\toverflow: hidden;\n\tborder: 1px solid var(--fgd-border);\n\tborder-radius: 6px;\n\tbackground: var(--fgd-surface-raised);\n\tbox-shadow: -8px 0 28px rgba(0, 0, 0, 0.2);\n}\n.zenfg-inspector-inspector[hidden] { display: none; }\n.zenfg-inspector-inspector > header {\n\tdisplay: flex;\n\tflex: 0 0 auto;\n\talign-items: center;\n\tjustify-content: space-between;\n\tmin-height: 36px;\n\tpadding: 0 8px 0 10px;\n\tborder-bottom: 1px solid var(--fgd-border-subtle);\n\tbackground: var(--fgd-surface);\n}\n.zenfg-inspector-inspector > header strong {\n\tmin-width: 0;\n\toverflow: hidden;\n\tfont: 600 11px/1.3 var(--fgd-font-mono);\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n}\n.zenfg-inspector-inspector-close {\n\twidth: 28px;\n\tpadding: 0;\n\tborder-color: transparent;\n\tbackground: transparent;\n}\n.zenfg-inspector-inspector-content {\n\tflex: 1 1 auto;\n\tmin-height: 0;\n\tpadding: 10px;\n\toverflow: auto;\n}\n.zenfg-inspector-inspector-summary {\n\tdisplay: grid;\n\tgrid-template-columns: minmax(96px, auto) minmax(0, 1fr);\n\tgap: 3px 12px;\n\tmargin: 0;\n\tfont-size: 11px;\n\tline-height: 1.6;\n}\n.zenfg-inspector-inspector-summary dt { color: var(--fgd-muted); font-family: var(--fgd-font-ui); }\n.zenfg-inspector-inspector-summary dd {\n\tmin-width: 0;\n\tmargin: 0;\n\tcolor: var(--fgd-text);\n\tfont-family: var(--fgd-font-mono);\n\toverflow-wrap: anywhere;\n}\n.zenfg-inspector-inspector-relations { display: grid; gap: 8px; }\n.zenfg-inspector-inspector-relations section {\n\tdisplay: grid;\n\tgap: 6px;\n\tpadding: 8px;\n\tborder: 1px solid var(--fgd-border-subtle);\n\tborder-radius: 5px;\n\tbackground: var(--fgd-surface);\n}\n.zenfg-inspector-inspector-relations h3 {\n\tmargin: 0 0 2px;\n\tcolor: var(--fgd-muted);\n\tfont: 700 10px/1.3 var(--fgd-font-ui);\n\tletter-spacing: 0.055em;\n\ttext-transform: uppercase;\n}\n.zenfg-inspector-inspector-relations .zenfg-inspector-relation-button {\n\tfont: 11px/1.5 var(--fgd-font-mono);\n\toverflow-wrap: anywhere;\n\twhite-space: normal;\n}\n.zenfg-inspector-relation-entry { display: grid; justify-items: start; gap: 2px; }\n.zenfg-inspector-relation-entry > span { font: 11px/1.5 var(--fgd-font-mono); overflow-wrap: anywhere; }\n.zenfg-inspector-inspector .zenfg-inspector-raw-detail {\n\tmin-width: 100%;\n\tmargin: 0;\n\toverflow: auto;\n\tcolor: var(--fgd-text-secondary);\n\tfont: 11px/1.55 var(--fgd-font-mono);\n\twhite-space: pre;\n}\n\n/* Memory */\n.zenfg-inspector-memory-view {\n\tdisplay: grid;\n\tgrid-template-rows: auto minmax(0, 1fr);\n}\n.zenfg-inspector-memory-summary {\n\tdisplay: grid;\n\tgrid-template-columns: repeat(4, minmax(0, 1fr));\n\tgap: 6px;\n\tpadding: 6px;\n\tborder-bottom: 1px solid var(--fgd-border-subtle);\n\tbackground: var(--fgd-panel);\n}\n.zenfg-inspector-memory-summary > div {\n\tdisplay: grid;\n\tgap: 3px;\n\tmin-width: 0;\n\tpadding: 6px 8px;\n\tborder: 1px solid var(--fgd-border-subtle);\n\tborder-radius: 5px;\n\tbackground: var(--fgd-surface);\n}\n.zenfg-inspector-memory-summary span { color: var(--fgd-muted); font: 10px/1.3 var(--fgd-font-ui); }\n.zenfg-inspector-memory-summary strong {\n\toverflow: hidden;\n\tfont: 600 11px/1.35 var(--fgd-font-mono);\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n}\n.zenfg-inspector-memory-scroller { min-height: 0; overflow: auto; }\n.zenfg-inspector-memory-timeline {\n\tdisplay: grid;\n\tmin-width: 760px;\n\tpadding: 8px;\n\tfont: 11px/1.4 var(--fgd-font-mono);\n}\n.zenfg-inspector-memory-axis,\n.zenfg-inspector-memory-resource {\n\tdisplay: grid;\n\tgrid-template-columns: minmax(150px, 0.9fr) 72px minmax(320px, 2.4fr) 82px;\n\tgap: 10px;\n\talign-items: center;\n\tmin-height: 30px;\n}\n.zenfg-inspector-memory-axis {\n\tposition: sticky;\n\tz-index: 2;\n\ttop: 0;\n\tpadding: 0 8px 6px;\n\tcolor: var(--fgd-muted);\n\tbackground: var(--fgd-canvas);\n\tfont: 10px/1.3 var(--fgd-font-ui);\n}\n.zenfg-inspector-memory-axis-track,\n.zenfg-inspector-memory-track { position: relative; height: 16px; }\n.zenfg-inspector-memory-axis-track { border-bottom: 1px solid var(--fgd-border-strong); }\n.zenfg-inspector-memory-axis-track > span {\n\tposition: absolute;\n\tbottom: -1px;\n\tpadding-bottom: 5px;\n\tborder-left: 1px solid var(--fgd-border-strong);\n\tfont: 10px/1 var(--fgd-font-mono);\n\ttransform: translateX(-1px);\n}\n.zenfg-inspector-memory-allocation {\n\tdisplay: flex;\n\talign-items: center;\n\tjustify-content: space-between;\n\tgap: 12px;\n\tmargin-top: 6px;\n\tpadding: 7px 8px;\n\tborder-top: 1px solid var(--fgd-border-subtle);\n\tborder-bottom: 1px solid var(--fgd-border-subtle);\n\tbackground: var(--fgd-surface);\n}\n.zenfg-inspector-memory-allocation > span { color: var(--fgd-muted); font: 10px/1.35 var(--fgd-font-mono); }\n.zenfg-inspector-memory-allocation.muted { color: var(--fgd-muted); font-family: var(--fgd-font-ui); }\n.zenfg-inspector-memory-resource { padding: 0 8px; border-bottom: 1px solid var(--fgd-border-subtle); }\n.zenfg-inspector-memory-resource.selected,\n.zenfg-inspector-memory-allocation.selected { box-shadow: inset 2px 0 var(--fgd-accent); }\n.zenfg-inspector-memory-bar {\n\tposition: absolute;\n\ttop: 4px;\n\theight: 8px;\n\tmin-width: 2px;\n\tborder-radius: 4px;\n\tbackground: color-mix(in srgb, ${GRAPH_VISUAL_THEME.texture.stroke} 76%, transparent);\n\tbox-shadow: 0 0 0 1px color-mix(in srgb, ${GRAPH_VISUAL_THEME.texture.stroke} 36%, transparent);\n}\n.zenfg-inspector-memory-bar.buffer {\n\tbackground: color-mix(in srgb, ${GRAPH_VISUAL_THEME.buffer.stroke} 72%, transparent);\n\tbox-shadow: 0 0 0 1px color-mix(in srgb, ${GRAPH_VISUAL_THEME.buffer.stroke} 34%, transparent);\n}\n.zenfg-inspector-memory-bar.empty { display: none; }\n\n/* Diagnostics */\n.zenfg-inspector-diagnostics-scroller { width: 100%; height: 100%; overflow: auto; }\n.zenfg-inspector-diagnostics-grid {\n\tdisplay: grid;\n\tgrid-template-columns: repeat(2, minmax(0, 1fr));\n\tgap: 8px;\n\tpadding: 8px;\n}\n.zenfg-inspector-diagnostic-card {\n\tdisplay: flex;\n\tflex-direction: column;\n\tmin-width: 0;\n\tmin-height: 164px;\n\toverflow: hidden;\n\tborder: 1px solid var(--fgd-border-subtle);\n\tborder-radius: 6px;\n\tbackground: var(--fgd-surface);\n}\n.zenfg-inspector-diagnostic-card > h2 {\n\tmargin: 0;\n\tpadding: 9px 10px 3px;\n\tcolor: var(--fgd-text-secondary);\n\tfont: 650 12px/1.35 var(--fgd-font-ui);\n}\n.zenfg-inspector-diagnostic-card > p {\n\tmargin: 0;\n\tpadding: 0 10px 8px;\n\tcolor: var(--fgd-muted);\n\tfont: 11px/1.5 var(--fgd-font-ui);\n}\n.zenfg-inspector-diagnostic-card .zenfg-inspector-table-scroller { flex: 1 1 auto; max-height: 280px; }\n.zenfg-inspector-diagnostic-card .zenfg-inspector-workbench-table { min-width: 100%; table-layout: fixed; }\n.zenfg-inspector-diagnostic-card .zenfg-inspector-workbench-table td {\n\toverflow-wrap: anywhere;\n\twhite-space: normal;\n}\n.zenfg-inspector-diagnostic-list { display: grid; gap: 2px; padding: 6px; overflow: auto; }\n.zenfg-inspector-diagnostic-list .zenfg-inspector-relation-button {\n\tpadding: 6px 8px;\n\tborder-radius: 4px;\n\tcolor: var(--fgd-text-secondary);\n\tfont: 11px/1.45 var(--fgd-font-mono);\n}\n.zenfg-inspector-diagnostic-list .zenfg-inspector-relation-button:hover {\n\tbackground: rgba(180, 190, 202, 0.05);\n\ttext-decoration: none;\n}\n.zenfg-inspector-diagnostic-list .selected { box-shadow: inset 2px 0 var(--fgd-accent); }\n\n/* Responsive */\n@container zenfg-inspector (max-width: 960px) {\n\t.zenfg-inspector-workspace.inspector-open { grid-template-columns: minmax(0, 1fr); }\n\t.zenfg-inspector-workspace.inspector-open::after {\n\t\tposition: absolute;\n\t\tz-index: 10;\n\t\tinset: 0;\n\t\tbackground: rgba(3, 7, 12, 0.38);\n\t\tcontent: '';\n\t\tpointer-events: none;\n\t}\n\t.zenfg-inspector-inspector {\n\t\tposition: absolute;\n\t\tz-index: 20;\n\t\ttop: 0;\n\t\tright: 0;\n\t\tbottom: 0;\n\t\twidth: min(380px, calc(100% - 24px));\n\t\tbox-shadow: -18px 0 42px rgba(0, 0, 0, 0.42);\n\t}\n\t.zenfg-inspector-graph-toolbar {\n\t\tgrid-template-columns: minmax(0, 1fr) auto;\n\t\tgap: 6px 8px;\n\t}\n\t.zenfg-inspector-graph-legend {\n\t\tgrid-column: 1 / -1;\n\t\tgrid-row: 2;\n\t\tpadding-top: 6px;\n\t\tborder-top: 1px solid var(--fgd-border-subtle);\n\t}\n\t.zenfg-inspector-graph-action-controls { justify-self: end; }\n}\n\n@container zenfg-inspector (max-width: 840px) {\n\t.zenfg-inspector-workbench-command-bar {\n\t\tgrid-template-columns: minmax(0, 1fr) auto;\n\t}\n\t.zenfg-inspector-brand { grid-column: 1; grid-row: 1; min-height: 36px; display: flex; align-items: center; }\n\t.zenfg-inspector-brand::after { display: none; }\n\t.zenfg-inspector-workbench-actions { grid-column: 2; grid-row: 1; }\n\t.zenfg-inspector-workbench-tabs { grid-column: 1 / -1; grid-row: 2; }\n\t.zenfg-inspector-workbench-command-bar.branding-hidden .zenfg-inspector-workbench-tabs {\n\t\tgrid-column: 1;\n\t\tgrid-row: 1;\n\t}\n\t.zenfg-inspector-diagnostics-grid { grid-template-columns: minmax(0, 1fr); }\n}\n\n@container zenfg-inspector (max-width: 720px) {\n\t.zenfg-inspector-body { padding: 8px; }\n\t.zenfg-inspector-overview-view { padding: 8px; }\n\t.zenfg-inspector-capture-summary > section { padding: 10px; }\n\t.zenfg-inspector-workbench-actions { padding-left: 6px; }\n\t.zenfg-inspector-command-status { max-width: 104px; }\n\t.zenfg-inspector-workbench-tabs > button { flex: 0 0 auto; padding-inline: 8px; }\n\t.zenfg-inspector-memory-summary { grid-template-columns: repeat(2, minmax(0, 1fr)); }\n\t.zenfg-inspector-view-toolbar input { flex-basis: 100%; }\n\t.zenfg-inspector-graph-toolbar { display: flex; flex-wrap: wrap; }\n\t.zenfg-inspector-graph-legend { order: 3; flex-basis: 100%; }\n\t.zenfg-inspector-graph-action-controls { margin-left: auto; }\n\t.zenfg-inspector-graph-view .zenfg-inspector-graph { height: auto; }\n}\n\n@media (prefers-reduced-motion: reduce) {\n\t.zenfg-inspector *,\n\t.zenfg-inspector *::before,\n\t.zenfg-inspector *::after {\n\t\tscroll-behavior: auto !important;\n\t\ttransition-duration: 0.01ms !important;\n\t\tanimation-duration: 0.01ms !important;\n\t\tanimation-iteration-count: 1 !important;\n\t}\n}\n`;\n\n/**\n * Installs the inspector's shared stylesheet in the current document once.\n *\n * @remarks This is safe to call repeatedly and is a no-op during server-side\n * rendering. `FrameGraphInspector` calls it automatically.\n */\nexport function ensureFrameGraphInspectorStyles(): void {\n\tif (typeof document === 'undefined' || document.getElementById(STYLE_ELEMENT_ID)) {\n\t\treturn;\n\t}\n\n\tconst style = document.createElement('style');\n\tstyle.id = STYLE_ELEMENT_ID;\n\tstyle.textContent = FRAME_GRAPH_DEBUG_PANEL_CSS;\n\tdocument.head.appendChild(style);\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zenfg/inspector",
|
|
3
|
-
"version": "0.1.0-beta.
|
|
3
|
+
"version": "0.1.0-beta.3",
|
|
4
4
|
"description": "Embeddable Inspector for ZenFG Snapshot files and live captures",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
"types": "./dist/index.d.ts",
|
|
9
9
|
"files": ["dist", "src", "README.md", "LICENSE"],
|
|
10
10
|
"dependencies": {
|
|
11
|
-
"@zenfg/snapshot": "0.1.0-beta.
|
|
11
|
+
"@zenfg/snapshot": "0.1.0-beta.3",
|
|
12
12
|
"cytoscape": "^3.34.1",
|
|
13
13
|
"elkjs": "^0.12.0"
|
|
14
14
|
},
|
|
@@ -78,8 +78,6 @@ export class FrameGraphInspector {
|
|
|
78
78
|
private readonly body = document.createElement('div');
|
|
79
79
|
private readonly content = document.createElement('div');
|
|
80
80
|
private readonly dropOverlay = document.createElement('div');
|
|
81
|
-
private readonly passesGraphModeButton: HTMLButtonElement;
|
|
82
|
-
private readonly resourcesGraphModeButton: HTMLButtonElement;
|
|
83
81
|
private readonly groupsButton: HTMLButtonElement;
|
|
84
82
|
private readonly collapseGroupsButton: HTMLButtonElement;
|
|
85
83
|
private readonly graphView: GraphViewState;
|
|
@@ -161,7 +159,6 @@ export class FrameGraphInspector {
|
|
|
161
159
|
toolbar: document.createElement('div'),
|
|
162
160
|
legend: graphLegend,
|
|
163
161
|
layoutElementBudget: normalizeLimit(options.maxGraphElements, DEFAULT_MAX_GRAPH_ELEMENTS, 'maxGraphElements'),
|
|
164
|
-
graphMode: 'passes',
|
|
165
162
|
groupsEnabled: true,
|
|
166
163
|
expandedGroupPaths: new Set(),
|
|
167
164
|
fitOnNextRender: true,
|
|
@@ -171,18 +168,9 @@ export class FrameGraphInspector {
|
|
|
171
168
|
this.graphView.toolbar.setAttribute('role', 'toolbar');
|
|
172
169
|
this.graphView.toolbar.setAttribute('aria-label', 'Frame graph view controls');
|
|
173
170
|
|
|
174
|
-
this.passesGraphModeButton = createToolbarButton('Pass dependency', 'Show pass dependency graph', () => this.setGraphMode('passes'));
|
|
175
|
-
this.resourcesGraphModeButton = createToolbarButton('Resource access', 'Show resource access graph', () => this.setGraphMode('resources'));
|
|
176
171
|
this.groupsButton = createToolbarButton('Groups', 'Toggle diagnostic group projection', () => this.toggleGroups());
|
|
177
172
|
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);
|
|
173
|
+
|
|
186
174
|
const actionControls = document.createElement('div');
|
|
187
175
|
actionControls.className = 'zenfg-inspector-graph-action-controls';
|
|
188
176
|
actionControls.append(
|
|
@@ -190,7 +178,7 @@ export class FrameGraphInspector {
|
|
|
190
178
|
this.collapseGroupsButton,
|
|
191
179
|
createGraphIconButton('fit', 'Fit graph to view', () => fitGraph(this.graphView)),
|
|
192
180
|
);
|
|
193
|
-
this.graphView.toolbar.append(
|
|
181
|
+
this.graphView.toolbar.append(graphLegend, actionControls);
|
|
194
182
|
|
|
195
183
|
const callbacks: WorkbenchCallbacks = {
|
|
196
184
|
onSelect: (selection) => this.handleSelect(selection),
|
|
@@ -216,7 +204,7 @@ export class FrameGraphInspector {
|
|
|
216
204
|
this.dom.addEventListener('dragend', this.handleDragEnd);
|
|
217
205
|
this.dom.addEventListener('drop', this.handleDrop);
|
|
218
206
|
this.updateCaptureActions();
|
|
219
|
-
this.
|
|
207
|
+
this.updateGraphControls();
|
|
220
208
|
this.showEmptyState();
|
|
221
209
|
queueMicrotask(() => this.maybeAutoCapture());
|
|
222
210
|
}
|
|
@@ -282,7 +270,7 @@ export class FrameGraphInspector {
|
|
|
282
270
|
}
|
|
283
271
|
|
|
284
272
|
/**
|
|
285
|
-
* Validates and synchronously displays a programmatic Snapshot 1.
|
|
273
|
+
* Validates and synchronously displays a programmatic Snapshot 1.1 value.
|
|
286
274
|
*
|
|
287
275
|
* @throws {@link FrameGraphSnapshotValidationError} if `snapshot` is invalid.
|
|
288
276
|
*/
|
|
@@ -424,7 +412,7 @@ export class FrameGraphInspector {
|
|
|
424
412
|
}
|
|
425
413
|
this.workbench.setSnapshot(viewModel, this.selected);
|
|
426
414
|
this.updateCaptureActions();
|
|
427
|
-
this.
|
|
415
|
+
this.updateGraphControls();
|
|
428
416
|
}
|
|
429
417
|
|
|
430
418
|
/**
|
|
@@ -482,49 +470,39 @@ export class FrameGraphInspector {
|
|
|
482
470
|
this.workbench.setHovered(selection);
|
|
483
471
|
}
|
|
484
472
|
|
|
485
|
-
private
|
|
486
|
-
if (this.graphView.graphMode === mode) return;
|
|
487
|
-
this.graphView.graphMode = mode;
|
|
488
|
-
this.graphView.fitOnNextRender = true;
|
|
489
|
-
this.updateGraphModeButtonState();
|
|
490
|
-
this.workbench.refreshGraphStructure();
|
|
491
|
-
}
|
|
492
|
-
|
|
493
|
-
private updateGraphModeButtonState(): void {
|
|
494
|
-
const passesActive = this.graphView.graphMode === 'passes';
|
|
473
|
+
private updateGraphControls(): void {
|
|
495
474
|
const hasGroups = (this.viewModel?.debugGroups.length ?? 0) > 0;
|
|
496
|
-
this.
|
|
497
|
-
this.
|
|
498
|
-
this.
|
|
499
|
-
this.resourcesGraphModeButton.setAttribute('aria-pressed', passesActive ? 'false' : 'true');
|
|
500
|
-
this.groupsButton.disabled = !passesActive || !hasGroups;
|
|
501
|
-
this.groupsButton.classList.toggle('active', passesActive && hasGroups && this.graphView.groupsEnabled);
|
|
502
|
-
this.groupsButton.setAttribute('aria-pressed', passesActive && hasGroups && this.graphView.groupsEnabled ? 'true' : 'false');
|
|
475
|
+
this.groupsButton.disabled = !hasGroups;
|
|
476
|
+
this.groupsButton.classList.toggle('active', hasGroups && this.graphView.groupsEnabled);
|
|
477
|
+
this.groupsButton.setAttribute('aria-pressed', hasGroups && this.graphView.groupsEnabled ? 'true' : 'false');
|
|
503
478
|
const hasExpanded = this.viewModel?.debugGroups.some((group) => this.graphView.expandedGroupPaths.has(group.pathKey)) ?? false;
|
|
504
|
-
this.collapseGroupsButton.disabled = !
|
|
479
|
+
this.collapseGroupsButton.disabled = !hasGroups || !this.graphView.groupsEnabled || !hasExpanded;
|
|
505
480
|
}
|
|
506
481
|
|
|
507
482
|
private toggleGroups(): void {
|
|
508
|
-
if (
|
|
483
|
+
if (!this.viewModel?.debugGroups.length) return;
|
|
484
|
+
this.handleHover(undefined);
|
|
509
485
|
this.graphView.groupsEnabled = !this.graphView.groupsEnabled;
|
|
510
486
|
this.graphView.fitOnNextRender = true;
|
|
511
|
-
this.
|
|
487
|
+
this.updateGraphControls();
|
|
512
488
|
this.workbench.refreshGraphStructure();
|
|
513
489
|
}
|
|
514
490
|
|
|
515
491
|
private toggleGroup(pathKey: string): void {
|
|
492
|
+
this.handleHover(undefined);
|
|
516
493
|
if (this.graphView.expandedGroupPaths.has(pathKey)) this.graphView.expandedGroupPaths.delete(pathKey);
|
|
517
494
|
else this.graphView.expandedGroupPaths.add(pathKey);
|
|
518
495
|
this.graphView.anchorElementIdOnNextRender = graphGroupElementId(pathKey);
|
|
519
|
-
this.
|
|
496
|
+
this.updateGraphControls();
|
|
520
497
|
this.workbench.refreshGraphStructure();
|
|
521
498
|
}
|
|
522
499
|
|
|
523
500
|
private collapseAllGroups(): void {
|
|
524
501
|
if (this.graphView.expandedGroupPaths.size === 0) return;
|
|
502
|
+
this.handleHover(undefined);
|
|
525
503
|
this.graphView.expandedGroupPaths.clear();
|
|
526
504
|
this.graphView.fitOnNextRender = true;
|
|
527
|
-
this.
|
|
505
|
+
this.updateGraphControls();
|
|
528
506
|
this.workbench.refreshGraphStructure();
|
|
529
507
|
}
|
|
530
508
|
|
package/src/debugCaptureModel.ts
CHANGED
|
@@ -6,7 +6,7 @@ import type {
|
|
|
6
6
|
FrameGraphSnapshotNodeKind,
|
|
7
7
|
FrameGraphSnapshotResourceKind,
|
|
8
8
|
FrameGraphSnapshotResourceOrigin,
|
|
9
|
-
|
|
9
|
+
FrameGraphSnapshotRoot,
|
|
10
10
|
FrameGraphSnapshotSegment,
|
|
11
11
|
FrameGraphSnapshotTextureRegion,
|
|
12
12
|
FrameGraphSnapshotWriteContents,
|
|
@@ -65,6 +65,7 @@ export type FrameGraphDebugNode = {
|
|
|
65
65
|
|
|
66
66
|
export type FrameGraphDebugResource = FrameGraphDebugResourceRef & {
|
|
67
67
|
readonly origin: FrameGraphSnapshotResourceOrigin;
|
|
68
|
+
readonly initialContents?: 'defined' | 'undefined';
|
|
68
69
|
readonly debugGroupId?: string;
|
|
69
70
|
readonly usageFlags: readonly string[];
|
|
70
71
|
readonly lifetime?: { readonly firstUse: number; readonly lastUse: number };
|
|
@@ -80,12 +81,19 @@ export type FrameGraphDebugEdge = {
|
|
|
80
81
|
readonly kind: 'value' | 'ordering';
|
|
81
82
|
};
|
|
82
83
|
|
|
83
|
-
export type FrameGraphDebugRoot = {
|
|
84
|
-
readonly
|
|
85
|
-
readonly nodeId?: string;
|
|
84
|
+
export type FrameGraphDebugRoot = FrameGraphSnapshotRoot & {
|
|
85
|
+
readonly key: string;
|
|
86
86
|
readonly resource?: FrameGraphDebugResourceRef;
|
|
87
87
|
};
|
|
88
88
|
|
|
89
|
+
/** Semantic identity within one recording, independent of root array order. */
|
|
90
|
+
export function rootKey(root: FrameGraphSnapshotRoot): string {
|
|
91
|
+
return root.reason === 'side-effect' ? JSON.stringify([root.reason, root.nodeId])
|
|
92
|
+
: JSON.stringify([root.reason, root.resourceId, root.range?.kind === 'buffer'
|
|
93
|
+
? ['buffer', root.range.offset, root.range.size]
|
|
94
|
+
: root.range?.regions.map((r) => [r.baseMipLevel, r.mipLevelCount, r.aspect, r.baseArrayLayer, r.arrayLayerCount, r.baseDepthSlice, r.depthSliceCount]) ?? null]);
|
|
95
|
+
}
|
|
96
|
+
|
|
89
97
|
export type FrameGraphDebugCulledNode = {
|
|
90
98
|
readonly node: Omit<FrameGraphDebugNode, 'order'>;
|
|
91
99
|
readonly reason: string;
|
|
@@ -105,6 +113,7 @@ export type FrameGraphDebugGroupSummary = {
|
|
|
105
113
|
readonly nodeKindCounts: Readonly<Partial<Record<FrameGraphSnapshotNodeKind, number>>>;
|
|
106
114
|
readonly inputResources: readonly FrameGraphDebugResourceRef[];
|
|
107
115
|
readonly outputResources: readonly FrameGraphDebugResourceRef[];
|
|
116
|
+
readonly outputRoots: readonly FrameGraphDebugRoot[];
|
|
108
117
|
readonly registeredTransientResourceCount: number;
|
|
109
118
|
readonly accessedTransientResourceCount: number;
|
|
110
119
|
readonly physicalAllocationCount: number;
|
|
@@ -277,6 +286,7 @@ export function createDebugViewModel(
|
|
|
277
286
|
kind: resource.kind,
|
|
278
287
|
label: resource.label,
|
|
279
288
|
origin: resource.origin,
|
|
289
|
+
initialContents: resource.initialContents,
|
|
280
290
|
debugGroupId: resource.groupId,
|
|
281
291
|
usageFlags: resource.usageFlags,
|
|
282
292
|
lifetime: resource.lifetime,
|
|
@@ -289,8 +299,8 @@ export function createDebugViewModel(
|
|
|
289
299
|
resource: resourceRef(dependency.resourceId),
|
|
290
300
|
}));
|
|
291
301
|
const roots: FrameGraphDebugRoot[] = compilation.roots.map((root) => ({
|
|
292
|
-
|
|
293
|
-
|
|
302
|
+
...root,
|
|
303
|
+
key: rootKey(root),
|
|
294
304
|
resource: root.resourceId === undefined ? undefined : resourceRef(root.resourceId),
|
|
295
305
|
}));
|
|
296
306
|
const culledNodes: FrameGraphDebugCulledNode[] = compilation.nodes
|
|
@@ -492,6 +502,23 @@ function validateDebugGroupReferences(
|
|
|
492
502
|
}
|
|
493
503
|
}
|
|
494
504
|
|
|
505
|
+
/** Declaration boundaries are topological, not per-range initial-value provenance. */
|
|
506
|
+
export function declarationEntrances(
|
|
507
|
+
nodes: readonly FrameGraphDebugNode[],
|
|
508
|
+
accesses: readonly FrameGraphDebugAccessEdge[],
|
|
509
|
+
edges: readonly FrameGraphDebugEdge[],
|
|
510
|
+
): readonly FrameGraphDebugAccessEdge[] {
|
|
511
|
+
const retained = new Set(nodes.map((node) => node.id));
|
|
512
|
+
const incoming = new Set(edges.map((edge) => JSON.stringify([edge.resource.id, edge.toNodeId])));
|
|
513
|
+
const seen = new Set<string>();
|
|
514
|
+
return accesses.filter((access) => {
|
|
515
|
+
const key = JSON.stringify([access.resource.id, access.nodeId]);
|
|
516
|
+
if (!retained.has(access.nodeId) || incoming.has(key) || seen.has(key)) return false;
|
|
517
|
+
seen.add(key);
|
|
518
|
+
return true;
|
|
519
|
+
});
|
|
520
|
+
}
|
|
521
|
+
|
|
495
522
|
function buildDebugGroupSummaries(input: {
|
|
496
523
|
readonly groups: readonly NormalizedDebugGroup[];
|
|
497
524
|
readonly nodes: readonly FrameGraphDebugNode[];
|
|
@@ -504,6 +531,7 @@ function buildDebugGroupSummaries(input: {
|
|
|
504
531
|
}): FrameGraphDebugGroup[] {
|
|
505
532
|
const groupsById = new Map(input.groups.map((group) => [group.id, group]));
|
|
506
533
|
const resourcesById = new Map(input.resources.map((resource) => [resource.id, resource]));
|
|
534
|
+
const entrances = declarationEntrances(input.nodes, input.accessEdges, input.edges);
|
|
507
535
|
const isInGroup = (candidateId: string | undefined, groupId: string) => (
|
|
508
536
|
candidateId !== undefined && groupsById.get(candidateId)?.ancestorIds.includes(groupId) === true
|
|
509
537
|
);
|
|
@@ -526,23 +554,19 @@ function buildDebugGroupSummaries(input: {
|
|
|
526
554
|
if (!fromInside && toInside) inputIds.add(edge.resource.id);
|
|
527
555
|
if (fromInside && !toInside) outputIds.add(edge.resource.id);
|
|
528
556
|
}
|
|
529
|
-
for (const access of
|
|
530
|
-
|
|
531
|
-
const
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
&& edge.resource.id === access.resource.id
|
|
535
|
-
&& edge.kind === 'value'
|
|
536
|
-
));
|
|
537
|
-
if (access.mode === 'read' && resource && resource.origin !== 'transient' && !hasRetainedValueProducer) {
|
|
538
|
-
inputIds.add(resource.id);
|
|
539
|
-
}
|
|
557
|
+
for (const access of entrances) {
|
|
558
|
+
const fromInside = isInGroup(resourcesById.get(access.resource.id)?.debugGroupId, group.id);
|
|
559
|
+
const toInside = retainedIds.has(access.nodeId);
|
|
560
|
+
if (!fromInside && toInside) inputIds.add(access.resource.id);
|
|
561
|
+
if (fromInside && !toInside) outputIds.add(access.resource.id);
|
|
540
562
|
}
|
|
563
|
+
const outputRoots: FrameGraphDebugRoot[] = [];
|
|
541
564
|
for (const root of input.roots) {
|
|
542
565
|
if (!root.resource) continue;
|
|
543
|
-
if (
|
|
544
|
-
|
|
566
|
+
if (root.resolution?.producerNodeIds.some((id) => retainedIds.has(id))
|
|
567
|
+
|| root.resolution?.usesInitialContents && isInGroup(resourcesById.get(root.resource.id)?.debugGroupId, group.id)) {
|
|
545
568
|
outputIds.add(root.resource.id);
|
|
569
|
+
outputRoots.push(root);
|
|
546
570
|
}
|
|
547
571
|
}
|
|
548
572
|
|
|
@@ -569,6 +593,7 @@ function buildDebugGroupSummaries(input: {
|
|
|
569
593
|
nodeKindCounts,
|
|
570
594
|
inputResources: [...inputIds].map((id) => resourcesById.get(id)!).filter(Boolean),
|
|
571
595
|
outputResources: [...outputIds].map((id) => resourcesById.get(id)!).filter(Boolean),
|
|
596
|
+
outputRoots,
|
|
572
597
|
registeredTransientResourceCount: registeredTransient.length,
|
|
573
598
|
accessedTransientResourceCount: accessedTransientIds.size,
|
|
574
599
|
physicalAllocationCount: allocationIds.size,
|
|
@@ -64,7 +64,7 @@ export class CytoscapeGraphRenderer implements GraphRenderer {
|
|
|
64
64
|
private anchorTargetContentKey: string | undefined;
|
|
65
65
|
private failedSceneContentKey: string | undefined;
|
|
66
66
|
private overview = false;
|
|
67
|
-
private
|
|
67
|
+
private pendingGroupTap: { readonly id: string; readonly timer: ReturnType<typeof setTimeout> } | undefined;
|
|
68
68
|
|
|
69
69
|
constructor(
|
|
70
70
|
private readonly host: HTMLElement,
|
|
@@ -75,6 +75,8 @@ export class CytoscapeGraphRenderer implements GraphRenderer {
|
|
|
75
75
|
this.tooltip = environment.createElement('div');
|
|
76
76
|
this.tooltip.className = 'zenfg-inspector-graph-tooltip';
|
|
77
77
|
this.tooltip.hidden = true;
|
|
78
|
+
// Cytoscape's node mouseout is not guaranteed when leaving its canvas directly.
|
|
79
|
+
this.canvasHost.addEventListener('pointerleave', () => this.clearHover());
|
|
78
80
|
this.status = environment.createElement('div');
|
|
79
81
|
this.status.className = 'zenfg-inspector-graph-status';
|
|
80
82
|
this.status.hidden = true;
|
|
@@ -93,6 +95,14 @@ export class CytoscapeGraphRenderer implements GraphRenderer {
|
|
|
93
95
|
|
|
94
96
|
render(request: GraphRenderRequest): void {
|
|
95
97
|
if (this.destroyed) return;
|
|
98
|
+
if (this.latestRequest?.scene !== request.scene
|
|
99
|
+
|| (this.latestRequest?.selected && selectionKey(this.latestRequest.selected)) !== (request.selected && selectionKey(request.selected))) {
|
|
100
|
+
this.cancelGroupTap();
|
|
101
|
+
}
|
|
102
|
+
if ((!request.hovered && !this.tooltip.hidden) || this.latestRequest?.scene.contentKey !== request.scene.contentKey) {
|
|
103
|
+
this.hideTooltip();
|
|
104
|
+
this.core?.elements().removeClass('semantic-hover');
|
|
105
|
+
}
|
|
96
106
|
this.latestRequest = request;
|
|
97
107
|
if (request.fit) {
|
|
98
108
|
this.fitTargetContentKey = request.scene.contentKey;
|
|
@@ -144,6 +154,7 @@ export class CytoscapeGraphRenderer implements GraphRenderer {
|
|
|
144
154
|
|
|
145
155
|
relayout(): void {
|
|
146
156
|
if (!this.latestRequest || this.destroyed) return;
|
|
157
|
+
this.cancelGroupTap();
|
|
147
158
|
this.failedSceneContentKey = undefined;
|
|
148
159
|
this.forceRelayout = true;
|
|
149
160
|
this.fitTargetContentKey = this.latestRequest.scene.contentKey;
|
|
@@ -157,6 +168,7 @@ export class CytoscapeGraphRenderer implements GraphRenderer {
|
|
|
157
168
|
destroy(): void {
|
|
158
169
|
if (this.destroyed) return;
|
|
159
170
|
this.destroyed = true;
|
|
171
|
+
this.cancelGroupTap();
|
|
160
172
|
this.requestVersion++;
|
|
161
173
|
this.resizeSubscription?.disconnect();
|
|
162
174
|
this.hideTooltip();
|
|
@@ -319,19 +331,24 @@ export class CytoscapeGraphRenderer implements GraphRenderer {
|
|
|
319
331
|
if (!request) return;
|
|
320
332
|
const id = event.target.id() as GraphSceneElementId;
|
|
321
333
|
const selection = request.scene.interaction.selectionByElementId.get(id);
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
if (
|
|
334
|
+
const doubleTap = this.pendingGroupTap?.id === id;
|
|
335
|
+
this.cancelGroupTap();
|
|
336
|
+
if (selection?.kind !== 'group') {
|
|
337
|
+
if (selection) request.onSelect(selection);
|
|
338
|
+
} else if (doubleTap) {
|
|
325
339
|
request.onToggleGroup(selection.pathKey);
|
|
326
|
-
this.lastTap = undefined;
|
|
327
340
|
} else {
|
|
328
|
-
|
|
341
|
+
// Defer group selection so expansion does not replace the inspected resource.
|
|
342
|
+
this.pendingGroupTap = { id, timer: setTimeout(() => {
|
|
343
|
+
this.pendingGroupTap = undefined;
|
|
344
|
+
this.interactiveRequest()?.onSelect(selection);
|
|
345
|
+
}, 350) };
|
|
329
346
|
}
|
|
330
347
|
};
|
|
331
348
|
core.on('tap', 'node', selectElement);
|
|
332
349
|
core.on('tap', 'edge', selectElement);
|
|
333
350
|
core.on('tap', (event) => {
|
|
334
|
-
if (event.target === core) this.
|
|
351
|
+
if (event.target === core) this.cancelGroupTap();
|
|
335
352
|
});
|
|
336
353
|
const hoverElement = (event: cytoscape.EventObject) => {
|
|
337
354
|
const request = this.interactiveRequest();
|
|
@@ -351,10 +368,7 @@ export class CytoscapeGraphRenderer implements GraphRenderer {
|
|
|
351
368
|
const moveTooltip = (event: cytoscape.EventObject) => this.positionTooltip(event.renderedPosition);
|
|
352
369
|
core.on('mousemove', 'node', moveTooltip);
|
|
353
370
|
core.on('mousemove', 'edge', moveTooltip);
|
|
354
|
-
const clearHover = () =>
|
|
355
|
-
this.hideTooltip();
|
|
356
|
-
this.latestRequest?.onHover(undefined);
|
|
357
|
-
};
|
|
371
|
+
const clearHover = () => this.clearHover();
|
|
358
372
|
core.on('mouseout', 'node', clearHover);
|
|
359
373
|
core.on('mouseout', 'edge', clearHover);
|
|
360
374
|
core.on('zoom', () => this.updateSemanticZoom());
|
|
@@ -371,7 +385,7 @@ export class CytoscapeGraphRenderer implements GraphRenderer {
|
|
|
371
385
|
core.batch(() => {
|
|
372
386
|
core.elements().removeClass('semantic-selected semantic-hover');
|
|
373
387
|
if (request.hovered) {
|
|
374
|
-
for (const id of request.scene.interaction.
|
|
388
|
+
for (const id of request.scene.interaction.hoverElementIdsBySelection.get(selectionKey(request.hovered)) ?? []) {
|
|
375
389
|
core.getElementById(id).addClass('semantic-hover');
|
|
376
390
|
}
|
|
377
391
|
}
|
|
@@ -434,7 +448,16 @@ export class CytoscapeGraphRenderer implements GraphRenderer {
|
|
|
434
448
|
private hideTooltip(): void {
|
|
435
449
|
this.tooltip.hidden = true;
|
|
436
450
|
this.tooltip.textContent = '';
|
|
437
|
-
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
private cancelGroupTap(): void {
|
|
454
|
+
if (this.pendingGroupTap) clearTimeout(this.pendingGroupTap.timer);
|
|
455
|
+
this.pendingGroupTap = undefined;
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
private clearHover(): void {
|
|
459
|
+
this.hideTooltip();
|
|
460
|
+
this.latestRequest?.onHover(undefined);
|
|
438
461
|
}
|
|
439
462
|
|
|
440
463
|
private showStatus(kind: 'loading' | 'layout' | 'empty' | 'error', message: string, retry = false): void {
|
|
@@ -544,8 +567,8 @@ function nodeRenderableData(node: GraphSceneNode): Record<string, unknown> {
|
|
|
544
567
|
: 160;
|
|
545
568
|
return {
|
|
546
569
|
kind: node.kind,
|
|
547
|
-
passKind: node.kind === 'pass'
|
|
548
|
-
resourceKind: node.kind === 'resource' ? node.resourceKind : undefined,
|
|
570
|
+
passKind: node.kind === 'pass' ? node.passKind : undefined,
|
|
571
|
+
resourceKind: (node.kind === 'resource' || node.kind === 'root') ? node.resourceKind : undefined,
|
|
549
572
|
collapsed: node.kind === 'group' && node.collapsed ? 1 : 0,
|
|
550
573
|
hasCulled: node.kind === 'group' && node.culledNodeCount > 0 ? 1 : 0,
|
|
551
574
|
depthBand: node.kind === 'group' ? node.depthBand : undefined,
|
|
@@ -575,9 +598,6 @@ function edgeRenderableData(edge: GraphSceneEdge): Record<string, unknown> {
|
|
|
575
598
|
const detailLabel = graphEdgeDisplayLabel(edge);
|
|
576
599
|
return {
|
|
577
600
|
kind: edge.kind,
|
|
578
|
-
dependencyKind: edge.kind === 'dependency' ? edge.dependencyKind : undefined,
|
|
579
|
-
accessMode: edge.kind === 'access' ? edge.accessMode : undefined,
|
|
580
|
-
dashed: edge.kind === 'access' && edge.dashed ? 1 : 0,
|
|
581
601
|
detailLabel,
|
|
582
602
|
displayLabel: detailLabel,
|
|
583
603
|
tooltip: edge.title,
|
|
@@ -71,8 +71,8 @@ export class DiagnosticsView {
|
|
|
71
71
|
const section = this.createSection('Retention roots', 'Roots explain why graph work survived dead-node elimination.');
|
|
72
72
|
const list = document.createElement('div');
|
|
73
73
|
list.className = 'zenfg-inspector-diagnostic-list';
|
|
74
|
-
snapshot.roots.forEach((root
|
|
75
|
-
const selection: Selection = { kind: 'root',
|
|
74
|
+
snapshot.roots.forEach((root) => {
|
|
75
|
+
const selection: Selection = { kind: 'root', key: root.key };
|
|
76
76
|
const button = createRelationButton(
|
|
77
77
|
`${root.reason} · ${root.resource ? labelResource(root.resource) : root.nodeId === undefined ? '-' : `node-${root.nodeId}`}`,
|
|
78
78
|
selection,
|
|
@@ -149,7 +149,7 @@ export class DiagnosticsView {
|
|
|
149
149
|
row.append(
|
|
150
150
|
createSelectionCell(labelNode(node), selection, this.callbacks),
|
|
151
151
|
createKindCell(node.gpuDurationMicros === undefined ? 'not-timed' : 'timed', node.gpuDurationMicros === undefined ? 'Not timed' : 'Timed'),
|
|
152
|
-
createCell(node.gpuDurationMicros === undefined ? '-' : (node.gpuDurationMicros / 1000).toFixed(3), { column: 'numeric' }),
|
|
152
|
+
createCell(node.gpuDurationMicros === undefined ? '-' : (node.gpuDurationMicros / 1000).toFixed(3), { column: 'numeric' }),
|
|
153
153
|
);
|
|
154
154
|
table.body.appendChild(row);
|
|
155
155
|
}
|