fix: separate surfaced outputs from inspectable 3D media#10698
fix: separate surfaced outputs from inspectable 3D media#10698benceruleanlu wants to merge 4 commits intomainfrom
Conversation
📝 WalkthroughWalkthroughA centralized 3D media-extensions module and a filename/extension-based preview/inspection API were added; codebase updated to use extension helpers and "inspectable" output semantics across utilities, components, stores, services, and tests. Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant UI as Asset UI / Menu
participant Store as Queue Store
participant Cache as JobOutputCache
participant Gallery
User->>UI: click "Inspect" / view asset
UI->>Store: request taskRef.inspectableOutput
Store-->>UI: returns inspectableOutput (or undefined)
alt inspectableOutput present
UI->>Cache: getInspectableOutputsForTask(task)
Cache-->>UI: returns inspectable outputs
UI->>Gallery: open gallery with inspectable output URL
Gallery-->>User: shows 3D viewer
else missing inspectableOutput
UI-->>User: short-circuit (no gallery)
end
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~30 minutes Poem
Caution Pre-merge checks failedPlease resolve all errors before merging. Addressing warnings is optional.
❌ Failed checks (1 error, 1 warning)
✅ Passed checks (3 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
🎨 Storybook: ✅ Built — View Storybook |
🎭 Playwright: ✅ 980 passed, 0 failed · 6 flaky📊 Browser Reports
|
|
No dependency changes detected. Learn more about Socket for GitHub. 👍 No dependency changes detected in pull request |
📦 Bundle: 5.12 MB gzip 🔴 +707 BDetailsSummary
Category Glance App Entry Points — 22.3 kB (baseline 22.3 kB) • ⚪ 0 BMain entry bundles and manifests
Status: 1 added / 1 removed Graph Workspace — 1.2 MB (baseline 1.2 MB) • 🔴 +49 BGraph editor runtime, canvas, workflow orchestration
Status: 1 added / 1 removed Views & Navigation — 76.6 kB (baseline 76.6 kB) • ⚪ 0 BTop-level views, pages, and routed surfaces
Status: 9 added / 9 removed / 2 unchanged Panels & Settings — 484 kB (baseline 484 kB) • ⚪ 0 BConfiguration panels, inspectors, and settings screens
Status: 10 added / 10 removed / 12 unchanged User & Accounts — 17.1 kB (baseline 17.1 kB) • ⚪ 0 BAuthentication, profile, and account management bundles
Status: 5 added / 5 removed / 2 unchanged Editors & Dialogs — 109 kB (baseline 109 kB) • ⚪ 0 BModals, dialogs, drawers, and in-app editors
Status: 2 added / 2 removed UI Components — 60.3 kB (baseline 60.3 kB) • ⚪ 0 BReusable component library chunks
Status: 5 added / 5 removed / 8 unchanged Data & Services — 2.98 MB (baseline 2.97 MB) • 🔴 +636 BStores, services, APIs, and repositories
Status: 13 added / 13 removed / 4 unchanged Utilities & Hooks — 340 kB (baseline 338 kB) • 🔴 +1.52 kBHelpers, composables, and utility bundles
Status: 14 added / 14 removed / 12 unchanged Vendor & Third-Party — 9.8 MB (baseline 9.8 MB) • ⚪ 0 BExternal libraries and shared vendor chunks Status: 16 unchanged Other — 8.53 MB (baseline 8.53 MB) • 🔴 +92 BBundles that do not match a named category
Status: 59 added / 59 removed / 75 unchanged ⚡ Performance Report
No regressions detected. All metrics
Historical variance (last 15 runs)
Trend (last 15 commits on main)
Raw data{
"timestamp": "2026-04-07T22:47:55.750Z",
"gitSha": "5fe6bb1ec9ce530b2c373b44701dbb389a4c1588",
"branch": "bl/fix-ply-asset-previewability",
"measurements": [
{
"name": "canvas-idle",
"durationMs": 2039.705999999967,
"styleRecalcs": 9,
"styleRecalcDurationMs": 8.528,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 362.28299999999996,
"heapDeltaBytes": 20054724,
"heapUsedBytes": 62721164,
"domNodes": 17,
"jsHeapTotalBytes": 22806528,
"scriptDurationMs": 16.349999999999998,
"eventListeners": 6,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.699999999999818
},
{
"name": "canvas-idle",
"durationMs": 2021.9000000000165,
"styleRecalcs": 11,
"styleRecalcDurationMs": 8.806000000000001,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 347.66300000000007,
"heapDeltaBytes": 21035976,
"heapUsedBytes": 63822172,
"domNodes": 22,
"jsHeapTotalBytes": 22806528,
"scriptDurationMs": 20.369999999999997,
"eventListeners": 6,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "canvas-idle",
"durationMs": 2044.8579999999765,
"styleRecalcs": 10,
"styleRecalcDurationMs": 8.39,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 355.59200000000004,
"heapDeltaBytes": 20345796,
"heapUsedBytes": 63140112,
"domNodes": 20,
"jsHeapTotalBytes": 22806528,
"scriptDurationMs": 21.87,
"eventListeners": 6,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "canvas-mouse-sweep",
"durationMs": 2045.803000000035,
"styleRecalcs": 81,
"styleRecalcDurationMs": 40.923,
"layouts": 12,
"layoutDurationMs": 3.4420000000000006,
"taskDurationMs": 921.3299999999999,
"heapDeltaBytes": 15917432,
"heapUsedBytes": 59001796,
"domNodes": 66,
"jsHeapTotalBytes": 23068672,
"scriptDurationMs": 119.80799999999999,
"eventListeners": 6,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66999999999998,
"p95FrameDurationMs": 16.699999999999818
},
{
"name": "canvas-mouse-sweep",
"durationMs": 1998.6599999999726,
"styleRecalcs": 80,
"styleRecalcDurationMs": 40.38399999999999,
"layouts": 12,
"layoutDurationMs": 3.797,
"taskDurationMs": 900.257,
"heapDeltaBytes": 16275564,
"heapUsedBytes": 59148952,
"domNodes": 65,
"jsHeapTotalBytes": 23068672,
"scriptDurationMs": 121.33800000000001,
"eventListeners": 6,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "canvas-mouse-sweep",
"durationMs": 2063.1480000000693,
"styleRecalcs": 84,
"styleRecalcDurationMs": 47.017,
"layouts": 12,
"layoutDurationMs": 4.167000000000001,
"taskDurationMs": 977.873,
"heapDeltaBytes": 15987636,
"heapUsedBytes": 59139204,
"domNodes": 64,
"jsHeapTotalBytes": 23330816,
"scriptDurationMs": 125.70399999999998,
"eventListeners": 6,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.670000000000012,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "canvas-zoom-sweep",
"durationMs": 1739.2239999999788,
"styleRecalcs": 31,
"styleRecalcDurationMs": 16.924000000000003,
"layouts": 6,
"layoutDurationMs": 0.709,
"taskDurationMs": 304.02699999999993,
"heapDeltaBytes": 15431568,
"heapUsedBytes": 67341080,
"domNodes": 79,
"jsHeapTotalBytes": 23068672,
"scriptDurationMs": 25.273999999999997,
"eventListeners": 19,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "canvas-zoom-sweep",
"durationMs": 1751.2879999999882,
"styleRecalcs": 31,
"styleRecalcDurationMs": 17.247000000000003,
"layouts": 6,
"layoutDurationMs": 0.5840000000000001,
"taskDurationMs": 303.427,
"heapDeltaBytes": 24723288,
"heapUsedBytes": 67522548,
"domNodes": 80,
"jsHeapTotalBytes": 20709376,
"scriptDurationMs": 23.003000000000004,
"eventListeners": 19,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "canvas-zoom-sweep",
"durationMs": 1735.7509999999365,
"styleRecalcs": 31,
"styleRecalcDurationMs": 16.604,
"layouts": 6,
"layoutDurationMs": 0.5740000000000001,
"taskDurationMs": 300.315,
"heapDeltaBytes": 24543240,
"heapUsedBytes": 67520336,
"domNodes": 79,
"jsHeapTotalBytes": 20709376,
"scriptDurationMs": 23.592000000000002,
"eventListeners": 19,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66999999999998,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "dom-widget-clipping",
"durationMs": 564.0419999999722,
"styleRecalcs": 12,
"styleRecalcDurationMs": 9.468,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 334.53,
"heapDeltaBytes": 6181840,
"heapUsedBytes": 49040544,
"domNodes": 19,
"jsHeapTotalBytes": 13369344,
"scriptDurationMs": 72.019,
"eventListeners": 2,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.65999999999999,
"p95FrameDurationMs": 16.700000000000273
},
{
"name": "dom-widget-clipping",
"durationMs": 569.0950000000043,
"styleRecalcs": 15,
"styleRecalcDurationMs": 10.870999999999999,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 334.644,
"heapDeltaBytes": 6792844,
"heapUsedBytes": 49442076,
"domNodes": 24,
"jsHeapTotalBytes": 13107200,
"scriptDurationMs": 64.165,
"eventListeners": 2,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.669999999999998,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "dom-widget-clipping",
"durationMs": 541.5269999999737,
"styleRecalcs": 13,
"styleRecalcDurationMs": 9.023,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 323.724,
"heapDeltaBytes": 6234876,
"heapUsedBytes": 49038316,
"domNodes": 22,
"jsHeapTotalBytes": 13893632,
"scriptDurationMs": 60.85099999999999,
"eventListeners": 2,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "large-graph-idle",
"durationMs": 2023.4700000000316,
"styleRecalcs": 10,
"styleRecalcDurationMs": 8.263,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 525.5179999999999,
"heapDeltaBytes": 18368828,
"heapUsedBytes": 70317976,
"domNodes": -257,
"jsHeapTotalBytes": 15454208,
"scriptDurationMs": 86.729,
"eventListeners": -127,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.699999999999818
},
{
"name": "large-graph-idle",
"durationMs": 2072.862999999984,
"styleRecalcs": 11,
"styleRecalcDurationMs": 11.678999999999998,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 534.398,
"heapDeltaBytes": 4689180,
"heapUsedBytes": 55600788,
"domNodes": -258,
"jsHeapTotalBytes": 16969728,
"scriptDurationMs": 86.738,
"eventListeners": -125,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.699999999999818
},
{
"name": "large-graph-idle",
"durationMs": 2040.345000000002,
"styleRecalcs": 11,
"styleRecalcDurationMs": 9.106,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 524.8539999999999,
"heapDeltaBytes": 4546048,
"heapUsedBytes": 54998152,
"domNodes": -258,
"jsHeapTotalBytes": 16707584,
"scriptDurationMs": 85.482,
"eventListeners": -125,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "large-graph-pan",
"durationMs": 2145.7960000000185,
"styleRecalcs": 71,
"styleRecalcDurationMs": 18.972999999999995,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 1041.241,
"heapDeltaBytes": 20633452,
"heapUsedBytes": 72438652,
"domNodes": -256,
"jsHeapTotalBytes": 16912384,
"scriptDurationMs": 371.719,
"eventListeners": -125,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.670000000000012,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "large-graph-pan",
"durationMs": 2134.730999999988,
"styleRecalcs": 70,
"styleRecalcDurationMs": 17.858000000000004,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 1002.5999999999999,
"heapDeltaBytes": 12920412,
"heapUsedBytes": 66004440,
"domNodes": -260,
"jsHeapTotalBytes": 18485248,
"scriptDurationMs": 356.15799999999996,
"eventListeners": -125,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66999999999998,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "large-graph-pan",
"durationMs": 2120.0309999999263,
"styleRecalcs": 69,
"styleRecalcDurationMs": 16.349000000000004,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 1071.4700000000003,
"heapDeltaBytes": 19795100,
"heapUsedBytes": 71929532,
"domNodes": -260,
"jsHeapTotalBytes": 18223104,
"scriptDurationMs": 416.571,
"eventListeners": -125,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "large-graph-zoom",
"durationMs": 3163.5180000000105,
"styleRecalcs": 66,
"styleRecalcDurationMs": 18.641000000000005,
"layouts": 60,
"layoutDurationMs": 8.325,
"taskDurationMs": 1308.184,
"heapDeltaBytes": 8440312,
"heapUsedBytes": 64075032,
"domNodes": -264,
"jsHeapTotalBytes": 17813504,
"scriptDurationMs": 484.01300000000003,
"eventListeners": -127,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "large-graph-zoom",
"durationMs": 3118.0229999999938,
"styleRecalcs": 65,
"styleRecalcDurationMs": 16.279000000000003,
"layouts": 60,
"layoutDurationMs": 7.866999999999999,
"taskDurationMs": 1261.4650000000001,
"heapDeltaBytes": 1308392,
"heapUsedBytes": 55850268,
"domNodes": -265,
"jsHeapTotalBytes": 18280448,
"scriptDurationMs": 460.48,
"eventListeners": -123,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.670000000000012,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "large-graph-zoom",
"durationMs": 3169.583999999986,
"styleRecalcs": 66,
"styleRecalcDurationMs": 16.427999999999997,
"layouts": 60,
"layoutDurationMs": 7.917000000000001,
"taskDurationMs": 1282.504,
"heapDeltaBytes": 7322476,
"heapUsedBytes": 61950356,
"domNodes": -265,
"jsHeapTotalBytes": 17231872,
"scriptDurationMs": 476.62699999999995,
"eventListeners": -123,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "minimap-idle",
"durationMs": 2020.5030000000193,
"styleRecalcs": 9,
"styleRecalcDurationMs": 7.298000000000003,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 522.9019999999999,
"heapDeltaBytes": 2737124,
"heapUsedBytes": 56844604,
"domNodes": -260,
"jsHeapTotalBytes": 16707584,
"scriptDurationMs": 80.59899999999999,
"eventListeners": -125,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.670000000000012,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "minimap-idle",
"durationMs": 2017.973999999981,
"styleRecalcs": 10,
"styleRecalcDurationMs": 8.021999999999998,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 511.5109999999999,
"heapDeltaBytes": 1782488,
"heapUsedBytes": 55025992,
"domNodes": -259,
"jsHeapTotalBytes": 15134720,
"scriptDurationMs": 81.49300000000001,
"eventListeners": -125,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333335,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "minimap-idle",
"durationMs": 2008.181000000036,
"styleRecalcs": 9,
"styleRecalcDurationMs": 7.486,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 521.4909999999999,
"heapDeltaBytes": 4296100,
"heapUsedBytes": 56520840,
"domNodes": -260,
"jsHeapTotalBytes": 16445440,
"scriptDurationMs": 82.05600000000001,
"eventListeners": -125,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "subgraph-dom-widget-clipping",
"durationMs": 556.4379999999574,
"styleRecalcs": 49,
"styleRecalcDurationMs": 13.096000000000002,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 368.757,
"heapDeltaBytes": 6534036,
"heapUsedBytes": 49612736,
"domNodes": 23,
"jsHeapTotalBytes": 12845056,
"scriptDurationMs": 130.302,
"eventListeners": 8,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.799999999999727
},
{
"name": "subgraph-dom-widget-clipping",
"durationMs": 563.2699999999886,
"styleRecalcs": 48,
"styleRecalcDurationMs": 11.297,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 354.514,
"heapDeltaBytes": 6487996,
"heapUsedBytes": 49585468,
"domNodes": 21,
"jsHeapTotalBytes": 13369344,
"scriptDurationMs": 124.891,
"eventListeners": 8,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.663333333333338,
"p95FrameDurationMs": 16.700000000000273
},
{
"name": "subgraph-dom-widget-clipping",
"durationMs": 537.4089999999114,
"styleRecalcs": 49,
"styleRecalcDurationMs": 12.541000000000002,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 362.759,
"heapDeltaBytes": 7069820,
"heapUsedBytes": 49873364,
"domNodes": 24,
"jsHeapTotalBytes": 13369344,
"scriptDurationMs": 132.468,
"eventListeners": 8,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.663333333333338,
"p95FrameDurationMs": 16.700000000000273
},
{
"name": "subgraph-idle",
"durationMs": 1998.221000000001,
"styleRecalcs": 11,
"styleRecalcDurationMs": 9.026000000000002,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 334.306,
"heapDeltaBytes": 19902056,
"heapUsedBytes": 63043552,
"domNodes": 22,
"jsHeapTotalBytes": 22806528,
"scriptDurationMs": 16.538999999999998,
"eventListeners": 6,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "subgraph-idle",
"durationMs": 2025.6840000000125,
"styleRecalcs": 11,
"styleRecalcDurationMs": 9.155999999999999,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 338.12600000000003,
"heapDeltaBytes": 19922640,
"heapUsedBytes": 63127208,
"domNodes": 22,
"jsHeapTotalBytes": 23068672,
"scriptDurationMs": 18.86,
"eventListeners": 6,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "subgraph-idle",
"durationMs": 1998.7199999999348,
"styleRecalcs": 10,
"styleRecalcDurationMs": 8.476,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 332.241,
"heapDeltaBytes": 19954492,
"heapUsedBytes": 63036736,
"domNodes": 20,
"jsHeapTotalBytes": 22806528,
"scriptDurationMs": 18.472,
"eventListeners": 6,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "subgraph-mouse-sweep",
"durationMs": 1756.2179999999898,
"styleRecalcs": 76,
"styleRecalcDurationMs": 43.067,
"layouts": 16,
"layoutDurationMs": 4.997,
"taskDurationMs": 693.797,
"heapDeltaBytes": 11576844,
"heapUsedBytes": 54636580,
"domNodes": 63,
"jsHeapTotalBytes": 22806528,
"scriptDurationMs": 99.429,
"eventListeners": 4,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "subgraph-mouse-sweep",
"durationMs": 1984.9179999999933,
"styleRecalcs": 85,
"styleRecalcDurationMs": 43.907000000000004,
"layouts": 16,
"layoutDurationMs": 4.785,
"taskDurationMs": 875.319,
"heapDeltaBytes": 11828984,
"heapUsedBytes": 55116872,
"domNodes": 75,
"jsHeapTotalBytes": 23068672,
"scriptDurationMs": 95.54300000000002,
"eventListeners": 6,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "subgraph-mouse-sweep",
"durationMs": 1962.4529999999822,
"styleRecalcs": 83,
"styleRecalcDurationMs": 42.219,
"layouts": 16,
"layoutDurationMs": 4.5729999999999995,
"taskDurationMs": 856.644,
"heapDeltaBytes": 11953916,
"heapUsedBytes": 55017432,
"domNodes": 72,
"jsHeapTotalBytes": 22544384,
"scriptDurationMs": 95.37899999999999,
"eventListeners": 6,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333335,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "viewport-pan-sweep",
"durationMs": 8185.084000000017,
"styleRecalcs": 252,
"styleRecalcDurationMs": 47.794000000000004,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 3530.763,
"heapDeltaBytes": 18240412,
"heapUsedBytes": 68939716,
"domNodes": -257,
"jsHeapTotalBytes": 20320256,
"scriptDurationMs": 1170.4460000000001,
"eventListeners": -109,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.80000000000109
},
{
"name": "viewport-pan-sweep",
"durationMs": 8168.885999999986,
"styleRecalcs": 250,
"styleRecalcDurationMs": 46.486,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 3389.513,
"heapDeltaBytes": 24230148,
"heapUsedBytes": 75305580,
"domNodes": -257,
"jsHeapTotalBytes": 18485248,
"scriptDurationMs": 1139.4959999999999,
"eventListeners": -111,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.670000000000012,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "viewport-pan-sweep",
"durationMs": 8180.964000000017,
"styleRecalcs": 250,
"styleRecalcDurationMs": 45.615,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 3436.773,
"heapDeltaBytes": 24629580,
"heapUsedBytes": 76068968,
"domNodes": -258,
"jsHeapTotalBytes": 20058112,
"scriptDurationMs": 1158.112,
"eventListeners": -111,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "vue-large-graph-idle",
"durationMs": 12812.846999999976,
"styleRecalcs": 0,
"styleRecalcDurationMs": 0,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 12799.349,
"heapDeltaBytes": -35730084,
"heapUsedBytes": 166173212,
"domNodes": -8331,
"jsHeapTotalBytes": 27353088,
"scriptDurationMs": 598.833,
"eventListeners": -16462,
"totalBlockingTimeMs": 0,
"frameDurationMs": 17.220000000000073,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "vue-large-graph-idle",
"durationMs": 12702.46499999996,
"styleRecalcs": 0,
"styleRecalcDurationMs": 0,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 12688.461,
"heapDeltaBytes": -30524216,
"heapUsedBytes": 165287476,
"domNodes": -8331,
"jsHeapTotalBytes": 26566656,
"scriptDurationMs": 538.952,
"eventListeners": -16464,
"totalBlockingTimeMs": 0,
"frameDurationMs": 17.219999999999953,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "vue-large-graph-idle",
"durationMs": 12630.691999999954,
"styleRecalcs": 0,
"styleRecalcDurationMs": 0,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 12617.874,
"heapDeltaBytes": -32105100,
"heapUsedBytes": 166513728,
"domNodes": -8331,
"jsHeapTotalBytes": 27615232,
"scriptDurationMs": 558.702,
"eventListeners": -16468,
"totalBlockingTimeMs": 0,
"frameDurationMs": 17.223333333333358,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "vue-large-graph-pan",
"durationMs": 14629.512000000033,
"styleRecalcs": 67,
"styleRecalcDurationMs": 14.275999999999984,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 14607.264000000001,
"heapDeltaBytes": -7675592,
"heapUsedBytes": 186268896,
"domNodes": -8331,
"jsHeapTotalBytes": 25694208,
"scriptDurationMs": 808.04,
"eventListeners": -16460,
"totalBlockingTimeMs": 0,
"frameDurationMs": 17.77333333333336,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "vue-large-graph-pan",
"durationMs": 14703.540999999972,
"styleRecalcs": 68,
"styleRecalcDurationMs": 14.771000000000006,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 14680.226,
"heapDeltaBytes": -32096808,
"heapUsedBytes": 174024576,
"domNodes": -8331,
"jsHeapTotalBytes": 25956352,
"scriptDurationMs": 824.896,
"eventListeners": -16458,
"totalBlockingTimeMs": 0,
"frameDurationMs": 17.220000000000073,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "vue-large-graph-pan",
"durationMs": 14708.50900000005,
"styleRecalcs": 66,
"styleRecalcDurationMs": 14.023999999999981,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 14688.142,
"heapDeltaBytes": -19380736,
"heapUsedBytes": 174476692,
"domNodes": -8331,
"jsHeapTotalBytes": 24907776,
"scriptDurationMs": 834.43,
"eventListeners": -16458,
"totalBlockingTimeMs": 0,
"frameDurationMs": 18.333333333333332,
"p95FrameDurationMs": 33.29999999999927
},
{
"name": "workflow-execution",
"durationMs": 442.63999999998305,
"styleRecalcs": 17,
"styleRecalcDurationMs": 23.575,
"layouts": 5,
"layoutDurationMs": 1.5,
"taskDurationMs": 123.05799999999998,
"heapDeltaBytes": 4741400,
"heapUsedBytes": 49359576,
"domNodes": 168,
"jsHeapTotalBytes": 0,
"scriptDurationMs": 27.550000000000004,
"eventListeners": 71,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000273
},
{
"name": "workflow-execution",
"durationMs": 445.1569999999947,
"styleRecalcs": 18,
"styleRecalcDurationMs": 23.477999999999998,
"layouts": 4,
"layoutDurationMs": 1.1260000000000001,
"taskDurationMs": 112.10699999999999,
"heapDeltaBytes": 4385192,
"heapUsedBytes": 49091252,
"domNodes": 158,
"jsHeapTotalBytes": 0,
"scriptDurationMs": 23.798,
"eventListeners": 71,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.700000000000273
},
{
"name": "workflow-execution",
"durationMs": 438.7530000000197,
"styleRecalcs": 20,
"styleRecalcDurationMs": 25.119000000000003,
"layouts": 6,
"layoutDurationMs": 1.469,
"taskDurationMs": 118.226,
"heapDeltaBytes": 4455796,
"heapUsedBytes": 48419340,
"domNodes": 157,
"jsHeapTotalBytes": 0,
"scriptDurationMs": 24.07,
"eventListeners": 71,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.669999999999998,
"p95FrameDurationMs": 16.700000000000273
}
]
} |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
packages/shared-frontend-utils/src/formatUtil.test.ts (1)
179-183: Consider adding an explicitnullcase forgetFileExtensionparity.You already assert
undefinedand empty input; addingnullwould fully mirror the “nullable input” contract tested elsewhere in this suite.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/shared-frontend-utils/src/formatUtil.test.ts` around lines 179 - 183, Add a test case asserting getFileExtension(null) returns null to mirror existing checks for undefined and empty string; update the test block in formatUtil.test.ts (the it(...) containing getFileExtension('README'), getFileExtension(''), getFileExtension(undefined)) to include expect(getFileExtension(null)).toBe(null) so null input is explicitly covered for the getFileExtension function.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@packages/shared-frontend-utils/src/formatUtil.ts`:
- Around line 629-637: The getFileExtension function incorrectly only splits on
'/' so Windows paths and URLs with query/hash can return wrong extensions;
update the extraction of fullFilename in getFileExtension to split on both '/'
and backslash (e.g., using a regex like /[\/\\]/), then strip any query or
fragment by removing anything after '?' or '#' (e.g., split on /[?#]/ and take
[0]) before computing dotIndex; keep the existing dotIndex check (dotIndex <= 0)
and the final slice + toLowerCase logic to return the extension.
---
Nitpick comments:
In `@packages/shared-frontend-utils/src/formatUtil.test.ts`:
- Around line 179-183: Add a test case asserting getFileExtension(null) returns
null to mirror existing checks for undefined and empty string; update the test
block in formatUtil.test.ts (the it(...) containing getFileExtension('README'),
getFileExtension(''), getFileExtension(undefined)) to include
expect(getFileExtension(null)).toBe(null) so null input is explicitly covered
for the getFileExtension function.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 3874b422-b71d-42c8-a5c9-e6d32b4074a2
📒 Files selected for processing (12)
packages/shared-frontend-utils/package.jsonpackages/shared-frontend-utils/src/formatUtil.test.tspackages/shared-frontend-utils/src/formatUtil.tspackages/shared-frontend-utils/src/mediaExtensions.tssrc/components/queue/job/JobAssetsList.test.tssrc/components/sidebar/tabs/AssetsSidebarTab.vuesrc/extensions/core/load3d/constants.tssrc/platform/assets/components/MediaAssetCard.vuesrc/platform/assets/components/MediaAssetContextMenu.test.tssrc/platform/assets/components/MediaAssetContextMenu.vuesrc/stores/queueStore.test.tssrc/stores/queueStore.ts
| export function getFileExtension( | ||
| filename: string | null | undefined | ||
| ): string | null { | ||
| if (!filename) return null | ||
| const fullFilename = filename.split('/').pop() ?? filename | ||
| const dotIndex = fullFilename.lastIndexOf('.') | ||
| if (dotIndex <= 0) return null | ||
| return fullFilename.slice(dotIndex + 1).toLowerCase() | ||
| } |
There was a problem hiding this comment.
Harden extension parsing for backslash/query/hash inputs.
On Line 633, only / is treated as a separator. Inputs like Windows-style paths or URL-like strings can produce incorrect extensions and false “not previewable” results.
🔧 Proposed fix
export function getFileExtension(
filename: string | null | undefined
): string | null {
if (!filename) return null
- const fullFilename = filename.split('/').pop() ?? filename
- const dotIndex = fullFilename.lastIndexOf('.')
+ const fullFilename = filename.split(/[\\/]/).pop() ?? filename
+ const cleanFilename = fullFilename.split(/[?#]/)[0]
+ const dotIndex = cleanFilename.lastIndexOf('.')
if (dotIndex <= 0) return null
- return fullFilename.slice(dotIndex + 1).toLowerCase()
+ return cleanFilename.slice(dotIndex + 1).toLowerCase()
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| export function getFileExtension( | |
| filename: string | null | undefined | |
| ): string | null { | |
| if (!filename) return null | |
| const fullFilename = filename.split('/').pop() ?? filename | |
| const dotIndex = fullFilename.lastIndexOf('.') | |
| if (dotIndex <= 0) return null | |
| return fullFilename.slice(dotIndex + 1).toLowerCase() | |
| } | |
| export function getFileExtension( | |
| filename: string | null | undefined | |
| ): string | null { | |
| if (!filename) return null | |
| const fullFilename = filename.split(/[\\/]/).pop() ?? filename | |
| const cleanFilename = fullFilename.split(/[?#]/)[0] | |
| const dotIndex = cleanFilename.lastIndexOf('.') | |
| if (dotIndex <= 0) return null | |
| return cleanFilename.slice(dotIndex + 1).toLowerCase() | |
| } |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/shared-frontend-utils/src/formatUtil.ts` around lines 629 - 637, The
getFileExtension function incorrectly only splits on '/' so Windows paths and
URLs with query/hash can return wrong extensions; update the extraction of
fullFilename in getFileExtension to split on both '/' and backslash (e.g., using
a regex like /[\/\\]/), then strip any query or fragment by removing anything
after '?' or '#' (e.g., split on /[?#]/ and take [0]) before computing dotIndex;
keep the existing dotIndex check (dotIndex <= 0) and the final slice +
toLowerCase logic to return the extension.
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/components/queue/job/JobAssetsList.test.ts`:
- Around line 290-293: The test uses an undefined helper createResultItem and
the renderJobAssetsList call uses incorrect positional args; replace
createResultItem with createPreviewOutput when calling
createTaskRef(createPreviewOutput('job-1.ply', 'model')) and change the
renderJobAssetsList invocation to use the object form (e.g.
renderJobAssetsList({ jobs: [job], onViewItem })) so the helper names and call
signature match the test helpers.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 2bdf213f-eef7-4bb1-9c73-f69d9234f6ba
📒 Files selected for processing (2)
src/components/queue/job/JobAssetsList.test.tssrc/components/sidebar/tabs/AssetsSidebarTab.vue
✅ Files skipped from review due to trivial changes (1)
- src/components/sidebar/tabs/AssetsSidebarTab.vue
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
src/components/queue/job/JobAssetsList.test.ts (1)
299-355:⚠️ Potential issue | 🟠 MajorPlease add a browser regression for this fix.
These component tests help, but the PR metadata still describes a user-visible
fix:across queue/history/sidebar flows without anybrowser_tests/coverage or a concrete note explaining why Playwright coverage is not practical. Please add one browser-level regression, or document the constraint in the PR description.Based on learnings, "End-to-end regression coverage for fixes: Use only PR metadata ... Fail otherwise and request author to add Playwright regression test under
browser_tests/or explain why test is not practical."🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/components/queue/job/JobAssetsList.test.ts` around lines 299 - 355, Add a browser-level regression: create a Playwright test under browser_tests that exercises the JobAssetsList UI (use the same scenarios covered in JobAssetsList.test.ts such as clicking the icon for a completed PLY job without a preview tile and verifying the "view" action is triggered, or hovering a completed job without inspectable output and asserting the View button is not shown) by rendering the component/page and interacting with the icon/row to assert the expected behavior; if adding a browser test is impractical, update the PR metadata to include a concrete note explaining why Playwright coverage cannot be added for this fix and reference the unit tests (e.g., renderJobAssetsList, onViewItem behavior) so reviewers can accept the exception.src/services/jobOutputCache.test.ts (1)
33-55:⚠️ Potential issue | 🟡 MinorUse the real filename-based inspection logic in this regression case.
createResultItem()now hard-codessupportsInspection, so the.usdzpath here still passes even ifResultItemImpl.supportsInspectionorisPreviewableMediaFilename()regresses. Please add at least one case that derives inspectability from the real filename + mediaType path instead of injecting the boolean.Based on learnings, "Aim for behavioral coverage of critical and new features in unit/component tests".
Also applies to: 127-139
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/services/jobOutputCache.test.ts` around lines 33 - 55, The test helper createResultItem currently forces supportsInspection via Object.defineProperty which masks regressions in ResultItemImpl.supportsInspection and isPreviewableMediaFilename; change the helper so when supportsInspection is not explicitly provided it is not overridden (i.e., remove or skip the Object.defineProperty for 'supportsInspection' unless the caller passes a value) and add at least one test case that creates an item with a .usdz filename (or other inspectable extension) without passing supportsInspection so the code path uses the real filename+mediaType logic in ResultItemImpl and isPreviewableMediaFilename to determine inspectability.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/stores/queueStore.ts`:
- Around line 233-240: The supportsInspection getter currently checks
isImage/isVideo/isAudio before the 3D filename gate, causing stale mediaType to
incorrectly enable inspection for non-previewable 3D files; change the
evaluation order in the supportsInspection getter to short-circuit the 3D case
first by returning (this.is3D && isPreviewableMediaFilename(this.filename))
before checking this.isImage, this.isVideo, or this.isAudio so non-previewable
3D files cannot fall through to the image/video/audio branches (update the
supportsInspection getter that references is3D, isImage, isVideo, isAudio,
isPreviewableMediaFilename, and filename).
---
Outside diff comments:
In `@src/components/queue/job/JobAssetsList.test.ts`:
- Around line 299-355: Add a browser-level regression: create a Playwright test
under browser_tests that exercises the JobAssetsList UI (use the same scenarios
covered in JobAssetsList.test.ts such as clicking the icon for a completed PLY
job without a preview tile and verifying the "view" action is triggered, or
hovering a completed job without inspectable output and asserting the View
button is not shown) by rendering the component/page and interacting with the
icon/row to assert the expected behavior; if adding a browser test is
impractical, update the PR metadata to include a concrete note explaining why
Playwright coverage cannot be added for this fix and reference the unit tests
(e.g., renderJobAssetsList, onViewItem behavior) so reviewers can accept the
exception.
In `@src/services/jobOutputCache.test.ts`:
- Around line 33-55: The test helper createResultItem currently forces
supportsInspection via Object.defineProperty which masks regressions in
ResultItemImpl.supportsInspection and isPreviewableMediaFilename; change the
helper so when supportsInspection is not explicitly provided it is not
overridden (i.e., remove or skip the Object.defineProperty for
'supportsInspection' unless the caller passes a value) and add at least one test
case that creates an item with a .usdz filename (or other inspectable extension)
without passing supportsInspection so the code path uses the real
filename+mediaType logic in ResultItemImpl and isPreviewableMediaFilename to
determine inspectability.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 7025a030-98c1-4759-8e6f-8813af3dd78c
📒 Files selected for processing (12)
src/components/queue/QueueProgressOverlay.vuesrc/components/queue/job/JobAssetsList.test.tssrc/components/queue/job/JobAssetsList.vuesrc/components/sidebar/tabs/JobHistorySidebarTab.vuesrc/composables/queue/useJobMenu.test.tssrc/composables/queue/useJobMenu.tssrc/composables/queue/useResultGallery.test.tssrc/composables/queue/useResultGallery.tssrc/services/jobOutputCache.test.tssrc/services/jobOutputCache.tssrc/stores/queueStore.test.tssrc/stores/queueStore.ts
🚧 Files skipped from review as they are similar to previous changes (1)
- src/stores/queueStore.test.ts
| get supportsInspection(): boolean { | ||
| return ( | ||
| this.isImage || | ||
| this.isVideo || | ||
| this.isAudio || | ||
| (this.is3D && isPreviewableMediaFilename(this.filename)) | ||
| ) | ||
| } |
There was a problem hiding this comment.
Short-circuit 3D files before the media-type fallbacks.
A non-loadable 3D file can still return true here when the upstream mediaType is stale ('images', 'video', etc.), because isImage / isVideo / isAudio are checked before the 3D filename gate. That would bring back View affordances for formats like USDZ.
Suggested fix
get supportsInspection(): boolean {
- return (
- this.isImage ||
- this.isVideo ||
- this.isAudio ||
- (this.is3D && isPreviewableMediaFilename(this.filename))
- )
+ if (this.is3D) {
+ return isPreviewableMediaFilename(this.filename)
+ }
+
+ return this.isImage || this.isVideo || this.isAudio
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| get supportsInspection(): boolean { | |
| return ( | |
| this.isImage || | |
| this.isVideo || | |
| this.isAudio || | |
| (this.is3D && isPreviewableMediaFilename(this.filename)) | |
| ) | |
| } | |
| get supportsInspection(): boolean { | |
| if (this.is3D) { | |
| return isPreviewableMediaFilename(this.filename) | |
| } | |
| return this.isImage || this.isVideo || this.isAudio | |
| } |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/stores/queueStore.ts` around lines 233 - 240, The supportsInspection
getter currently checks isImage/isVideo/isAudio before the 3D filename gate,
causing stale mediaType to incorrectly enable inspection for non-previewable 3D
files; change the evaluation order in the supportsInspection getter to
short-circuit the 3D case first by returning (this.is3D &&
isPreviewableMediaFilename(this.filename)) before checking this.isImage,
this.isVideo, or this.isAudio so non-previewable 3D files cannot fall through to
the image/video/audio branches (update the supportsInspection getter that
references is3D, isImage, isVideo, isAudio, isPreviewableMediaFilename, and
filename).
There was a problem hiding this comment.
♻️ Duplicate comments (1)
packages/shared-frontend-utils/src/formatUtil.ts (1)
629-637:⚠️ Potential issue | 🟡 MinorStrip query/fragment suffixes before parsing the extension.
Line 633 handles Windows separators now, but
model.glb?download=1andmodel.glb#viewstill produceglb?download=1/glb#view. That makes bothgetMediaTypeFromFilename()andisPreviewableMediaFilename()misclassify URL-like inputs as non-media.🔧 Proposed fix
export function getFileExtension( filename: string | null | undefined ): string | null { if (!filename) return null const fullFilename = filename.split(/[/\\]/).pop() ?? filename - const dotIndex = fullFilename.lastIndexOf('.') + const cleanFilename = fullFilename.split(/[?#]/)[0] + const dotIndex = cleanFilename.lastIndexOf('.') if (dotIndex <= 0) return null - return fullFilename.slice(dotIndex + 1).toLowerCase() + return cleanFilename.slice(dotIndex + 1).toLowerCase() }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/shared-frontend-utils/src/formatUtil.ts` around lines 629 - 637, getFileExtension currently extracts the last path segment but doesn't remove URL query or fragment suffixes, so inputs like "model.glb?download=1" return "glb?download=1"; update getFileExtension to strip any query ("?") and fragment ("#") parts from the final segment before finding the last dot. Locate the function getFileExtension and after computing fullFilename, trim anything after the first '?' or '#' (e.g., split on /[?#]/ and take the first part) then continue with lastIndexOf('.') and returning the lower-cased extension; this ensures getMediaTypeFromFilename and isPreviewableMediaFilename receive a clean filename.
🧹 Nitpick comments (1)
packages/shared-frontend-utils/src/formatUtil.ts (1)
639-654: Consider renaming this toisInspectableMediaFilename.The implementation is stricter than the PR's retained
previewable/surfaced concept because non-loadable 3D files returnfalsehere. Keeping that behavior under apreviewableexport blurs the new previewable-vs-inspectable split and makes future misuse easy.As per coding guidelines, "Code must conform to style guides and use clear names for everything."
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/shared-frontend-utils/src/formatUtil.ts` around lines 639 - 654, The function isPreviewableMediaFilename has a misleading name because it excludes non-loadable 3D files; rename the exported symbol to isInspectableMediaFilename and update its JSDoc to reflect "inspectable" semantics (strictly loadable/inspectable assets), then update all internal references/imports (calls, tests, and re-exports) to use isInspectableMediaFilename instead of isPreviewableMediaFilename while preserving the existing implementation (getFileExtension, IMAGE_EXTENSIONS, VIDEO_EXTENSIONS, AUDIO_EXTENSIONS, isThreeDLoadableExtension) and export shape.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Duplicate comments:
In `@packages/shared-frontend-utils/src/formatUtil.ts`:
- Around line 629-637: getFileExtension currently extracts the last path segment
but doesn't remove URL query or fragment suffixes, so inputs like
"model.glb?download=1" return "glb?download=1"; update getFileExtension to strip
any query ("?") and fragment ("#") parts from the final segment before finding
the last dot. Locate the function getFileExtension and after computing
fullFilename, trim anything after the first '?' or '#' (e.g., split on /[?#]/
and take the first part) then continue with lastIndexOf('.') and returning the
lower-cased extension; this ensures getMediaTypeFromFilename and
isPreviewableMediaFilename receive a clean filename.
---
Nitpick comments:
In `@packages/shared-frontend-utils/src/formatUtil.ts`:
- Around line 639-654: The function isPreviewableMediaFilename has a misleading
name because it excludes non-loadable 3D files; rename the exported symbol to
isInspectableMediaFilename and update its JSDoc to reflect "inspectable"
semantics (strictly loadable/inspectable assets), then update all internal
references/imports (calls, tests, and re-exports) to use
isInspectableMediaFilename instead of isPreviewableMediaFilename while
preserving the existing implementation (getFileExtension, IMAGE_EXTENSIONS,
VIDEO_EXTENSIONS, AUDIO_EXTENSIONS, isThreeDLoadableExtension) and export shape.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 0ae1e2da-fbc7-4ea5-9c84-59c65da1ceb1
📒 Files selected for processing (2)
packages/shared-frontend-utils/src/formatUtil.test.tspackages/shared-frontend-utils/src/formatUtil.ts
🚧 Files skipped from review as they are similar to previous changes (1)
- packages/shared-frontend-utils/src/formatUtil.test.ts
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 759032ced2
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| if (!inspectableOutput) { | ||
| return | ||
| } |
There was a problem hiding this comment.
Defer inspectability check until after lazy output loading
This early return prevents inspection for completed jobs whose preview_output is non-loadable (for example .usdz) but whose full outputs include inspectable media. In that case taskRef.inspectableOutput is initially empty, yet openResultGallery can still discover inspectable outputs via getInspectableOutputsForTask lazy loading when outputs_count > 1; returning here makes that path unreachable and leaves valid inspectable results inaccessible.
Useful? React with 👍 / 👎.
Summary
Split 3D output handling so shared media classification stays broad while UI inspectability only includes formats the browser viewer can actually load.
Changes
@comfyorg/shared-frontend-utils/mediaExtensions, keptpreviewOutput/previewableOutputsas surfaced outputs, and addedinspectableOutput/inspectableOutputsfor viewer and lightbox entry points.Review Focus
Validation
pnpm exec vitest run packages/shared-frontend-utils/src/formatUtil.test.ts src/stores/queueStore.test.ts src/components/queue/job/JobAssetsList.test.ts src/composables/queue/useResultGallery.test.ts src/composables/queue/useJobMenu.test.ts src/platform/assets/components/MediaAssetContextMenu.test.tspnpm typecheckpnpm lint┆Issue is synchronized with this Notion page by Unito