@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.
Files changed (62) hide show
  1. package/README.md +63 -1
  2. package/dist/FrameGraphInspector.d.ts +2 -5
  3. package/dist/FrameGraphInspector.d.ts.map +1 -1
  4. package/dist/FrameGraphInspector.js +16 -38
  5. package/dist/FrameGraphInspector.js.map +1 -1
  6. package/dist/debugCaptureModel.d.ts +9 -4
  7. package/dist/debugCaptureModel.d.ts.map +1 -1
  8. package/dist/debugCaptureModel.js +36 -14
  9. package/dist/debugCaptureModel.js.map +1 -1
  10. package/dist/panelCytoscapeGraphRenderer.d.ts +3 -1
  11. package/dist/panelCytoscapeGraphRenderer.d.ts.map +1 -1
  12. package/dist/panelCytoscapeGraphRenderer.js +39 -19
  13. package/dist/panelCytoscapeGraphRenderer.js.map +1 -1
  14. package/dist/panelDiagnosticsView.js +2 -2
  15. package/dist/panelDiagnosticsView.js.map +1 -1
  16. package/dist/panelGraphLayout.d.ts.map +1 -1
  17. package/dist/panelGraphLayout.js +15 -1
  18. package/dist/panelGraphLayout.js.map +1 -1
  19. package/dist/panelGraphScene.d.ts +14 -26
  20. package/dist/panelGraphScene.d.ts.map +1 -1
  21. package/dist/panelGraphScene.js +141 -195
  22. package/dist/panelGraphScene.js.map +1 -1
  23. package/dist/panelGraphView.d.ts.map +1 -1
  24. package/dist/panelGraphView.js +31 -9
  25. package/dist/panelGraphView.js.map +1 -1
  26. package/dist/panelGraphVisuals.d.ts +7 -3
  27. package/dist/panelGraphVisuals.d.ts.map +1 -1
  28. package/dist/panelGraphVisuals.js +45 -80
  29. package/dist/panelGraphVisuals.js.map +1 -1
  30. package/dist/panelInspectorView.d.ts +5 -0
  31. package/dist/panelInspectorView.d.ts.map +1 -1
  32. package/dist/panelInspectorView.js +99 -13
  33. package/dist/panelInspectorView.js.map +1 -1
  34. package/dist/panelSelection.js +2 -2
  35. package/dist/panelSelection.js.map +1 -1
  36. package/dist/panelTypes.d.ts +8 -3
  37. package/dist/panelTypes.d.ts.map +1 -1
  38. package/dist/panelTypes.js.map +1 -1
  39. package/dist/panelVisualTheme.d.ts +21 -13
  40. package/dist/panelVisualTheme.d.ts.map +1 -1
  41. package/dist/panelVisualTheme.js +15 -7
  42. package/dist/panelVisualTheme.js.map +1 -1
  43. package/dist/panelWorkbenchHelpers.js +1 -1
  44. package/dist/panelWorkbenchHelpers.js.map +1 -1
  45. package/dist/styles.d.ts.map +1 -1
  46. package/dist/styles.js +16 -12
  47. package/dist/styles.js.map +1 -1
  48. package/package.json +2 -2
  49. package/src/FrameGraphInspector.ts +17 -39
  50. package/src/debugCaptureModel.ts +44 -19
  51. package/src/panelCytoscapeGraphRenderer.ts +38 -18
  52. package/src/panelDiagnosticsView.ts +3 -3
  53. package/src/panelGraphLayout.ts +15 -1
  54. package/src/panelGraphScene.ts +153 -279
  55. package/src/panelGraphView.ts +31 -9
  56. package/src/panelGraphVisuals.ts +46 -82
  57. package/src/panelInspectorView.ts +104 -16
  58. package/src/panelSelection.ts +2 -2
  59. package/src/panelTypes.ts +9 -3
  60. package/src/panelVisualTheme.ts +18 -7
  61. package/src/panelWorkbenchHelpers.ts +2 -2
  62. package/src/styles.ts +16 -12
@@ -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.2",
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.2",
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
- this.passesGraphModeButton.classList.add('zenfg-inspector-mode-button');
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(modeControls, graphLegend, actionControls);
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.updateGraphModeButtonState();
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.0 value.
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.updateGraphModeButtonState();
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 setGraphMode(mode: GraphViewState['graphMode']): void {
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.passesGraphModeButton.classList.toggle('active', passesActive);
497
- this.resourcesGraphModeButton.classList.toggle('active', !passesActive);
498
- this.passesGraphModeButton.setAttribute('aria-pressed', passesActive ? 'true' : 'false');
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 = !passesActive || !hasGroups || !this.graphView.groupsEnabled || !hasExpanded;
479
+ this.collapseGroupsButton.disabled = !hasGroups || !this.graphView.groupsEnabled || !hasExpanded;
505
480
  }
506
481
 
507
482
  private toggleGroups(): void {
508
- if (this.graphView.graphMode !== 'passes' || !this.viewModel?.debugGroups.length) return;
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.updateGraphModeButtonState();
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.updateGraphModeButtonState();
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.updateGraphModeButtonState();
505
+ this.updateGraphControls();
528
506
  this.workbench.refreshGraphStructure();
529
507
  }
530
508
 
@@ -6,7 +6,7 @@ import type {
6
6
  FrameGraphSnapshotNodeKind,
7
7
  FrameGraphSnapshotResourceKind,
8
8
  FrameGraphSnapshotResourceOrigin,
9
- FrameGraphSnapshotRootReason,
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 reason: FrameGraphSnapshotRootReason;
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
- reason: root.reason,
293
- nodeId: root.nodeId,
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 input.accessEdges) {
530
- if (!retainedIds.has(access.nodeId)) continue;
531
- const resource = resourcesById.get(access.resource.id);
532
- const hasRetainedValueProducer = input.edges.some((edge) => (
533
- edge.toNodeId === access.nodeId
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 (input.accessEdges.some((access) => retainedIds.has(access.nodeId)
544
- && access.resource.id === root.resource!.id && access.mode === 'write')) {
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 lastTap: { readonly id: string; readonly at: number } | undefined;
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
- if (selection) request.onSelect(selection);
323
- const now = performance.now();
324
- if (this.lastTap?.id === id && now - this.lastTap.at <= 350 && selection?.kind === 'group') {
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
- this.lastTap = { id, at: now };
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.lastTap = undefined;
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.relatedElementIdsBySelection.get(selectionKey(request.hovered)) ?? []) {
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
- this.lastTap = undefined;
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' || node.kind === 'culled-pass' ? node.passKind : undefined,
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, index) => {
75
- const selection: Selection = { kind: 'root', index };
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
  }