feat(video): VIDEO_EDIT rich widget with trim and crop editors - #14205
Conversation
🎭 Playwright: ✅ 1757 passed, 0 failed · 1 flaky📊 Browser Reports
🎨 Storybook: ✅ Built — View Storybook📦 Bundle: 8.16 MB gzip 🔴 +17 kBDetailsSummary
Category Glance App Entry Points — 3.63 kB (baseline 3.63 kB) • ⚪ 0 BMain entry bundles and manifests
Status: 1 added / 1 removed Graph Workspace — 1.28 MB (baseline 1.28 MB) • 🔴 +211 BGraph editor runtime, canvas, workflow orchestration
Status: 1 added / 1 removed / 1 unchanged Views & Navigation — 112 kB (baseline 112 kB) • ⚪ 0 BTop-level views, pages, and routed surfaces
Status: 12 added / 12 removed / 4 unchanged Panels & Settings — 551 kB (baseline 551 kB) • ⚪ 0 BConfiguration panels, inspectors, and settings screens
Status: 11 added / 11 removed / 15 unchanged User & Accounts — 29.1 kB (baseline 29.1 kB) • ⚪ 0 BAuthentication, profile, and account management bundles
Status: 7 added / 7 removed / 3 unchanged Editors & Dialogs — 124 kB (baseline 121 kB) • 🔴 +3.29 kBModals, dialogs, drawers, and in-app editors
Status: 6 added / 5 removed / 1 unchanged UI Components — 70.7 kB (baseline 64.8 kB) • 🔴 +5.89 kBReusable component library chunks
Status: 7 added / 6 removed / 8 unchanged Data & Services — 3.42 MB (baseline 3.42 MB) • 🔴 +2.19 kBStores, services, APIs, and repositories
Status: 14 added / 14 removed / 3 unchanged Utilities & Hooks — 384 kB (baseline 369 kB) • 🔴 +15.1 kBHelpers, composables, and utility bundles
Status: 19 added / 18 removed / 17 unchanged Vendor & Third-Party — 15.7 MB (baseline 15.7 MB) • ⚪ 0 BExternal libraries and shared vendor chunks Status: 16 unchanged Other — 12.5 MB (baseline 12.5 MB) • 🔴 +41.3 kBBundles that do not match a named category
Status: 79 added / 77 removed / 198 unchanged ⚡ Performance Report
Show regressions
All metrics
Historical variance (last 15 runs)
Trend (last 15 commits on main)
Raw data{
"timestamp": "2026-07-29T14:05:39.913Z",
"gitSha": "519bf1e8d75a6d6edeac7086ed702e62fae49042",
"branch": "feat/video-edit-widget",
"measurements": [
{
"name": "canvas-idle",
"durationMs": 2069.65299999996,
"styleRecalcs": 8,
"styleRecalcDurationMs": 7.667999999999999,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 617.419,
"heapDeltaBytes": 3668040,
"heapUsedBytes": 71544388,
"domNodes": 16,
"jsHeapTotalBytes": 20574208,
"scriptDurationMs": 26.600999999999996,
"eventListeners": 4,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66999999999998,
"p95FrameDurationMs": 16.699999999999818
},
{
"name": "canvas-idle",
"durationMs": 2049.482000000012,
"styleRecalcs": 9,
"styleRecalcDurationMs": 8.262000000000002,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 565.5779999999999,
"heapDeltaBytes": 3960272,
"heapUsedBytes": 72018304,
"domNodes": 18,
"jsHeapTotalBytes": 20312064,
"scriptDurationMs": 24.710000000000004,
"eventListeners": 4,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "canvas-mouse-sweep",
"durationMs": 1991.7090000000144,
"styleRecalcs": 77,
"styleRecalcDurationMs": 43.883,
"layouts": 12,
"layoutDurationMs": 4.101000000000001,
"taskDurationMs": 1028.863,
"heapDeltaBytes": -15421240,
"heapUsedBytes": 52342812,
"domNodes": -271,
"jsHeapTotalBytes": 19390464,
"scriptDurationMs": 134.24800000000002,
"eventListeners": -148,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333335,
"p95FrameDurationMs": 16.699999999999818
},
{
"name": "canvas-mouse-sweep",
"durationMs": 1904.0880000000016,
"styleRecalcs": 74,
"styleRecalcDurationMs": 42.341,
"layouts": 12,
"layoutDurationMs": 3.645,
"taskDurationMs": 1000.99,
"heapDeltaBytes": -15567540,
"heapUsedBytes": 52345212,
"domNodes": -277,
"jsHeapTotalBytes": 19914752,
"scriptDurationMs": 129.668,
"eventListeners": -148,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333335,
"p95FrameDurationMs": 16.699999999999818
},
{
"name": "canvas-zoom-sweep",
"durationMs": 1758.2340000000158,
"styleRecalcs": 31,
"styleRecalcDurationMs": 19.672,
"layouts": 6,
"layoutDurationMs": 0.7279999999999999,
"taskDurationMs": 474.665,
"heapDeltaBytes": 7114400,
"heapUsedBytes": 75169980,
"domNodes": 78,
"jsHeapTotalBytes": 20574208,
"scriptDurationMs": 29.531000000000002,
"eventListeners": 19,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "canvas-zoom-sweep",
"durationMs": 1752.1439999999302,
"styleRecalcs": 31,
"styleRecalcDurationMs": 19.063,
"layouts": 6,
"layoutDurationMs": 0.7300000000000002,
"taskDurationMs": 464.302,
"heapDeltaBytes": 7154760,
"heapUsedBytes": 75308708,
"domNodes": 75,
"jsHeapTotalBytes": 20574208,
"scriptDurationMs": 28.747000000000003,
"eventListeners": 19,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "dom-widget-clipping",
"durationMs": 612.2500000000173,
"styleRecalcs": 13,
"styleRecalcDurationMs": 10.340000000000002,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 424.76099999999997,
"heapDeltaBytes": -12351920,
"heapUsedBytes": 55527540,
"domNodes": 22,
"jsHeapTotalBytes": 21884928,
"scriptDurationMs": 61.784,
"eventListeners": 0,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "dom-widget-clipping",
"durationMs": 661.5360000000692,
"styleRecalcs": 11,
"styleRecalcDurationMs": 8.873000000000001,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 429.652,
"heapDeltaBytes": -12079540,
"heapUsedBytes": 55874724,
"domNodes": 18,
"jsHeapTotalBytes": 21360640,
"scriptDurationMs": 62.079,
"eventListeners": 2,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "large-graph-idle",
"durationMs": 2039.2819999999574,
"styleRecalcs": 8,
"styleRecalcDurationMs": 8.331000000000001,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 825.853,
"heapDeltaBytes": 6322636,
"heapUsedBytes": 65062288,
"domNodes": -275,
"jsHeapTotalBytes": 5042176,
"scriptDurationMs": 127.44999999999997,
"eventListeners": -142,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.699999999999818
},
{
"name": "large-graph-idle",
"durationMs": 2030.4370000000063,
"styleRecalcs": 8,
"styleRecalcDurationMs": 7.388999999999999,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 814.603,
"heapDeltaBytes": 5966124,
"heapUsedBytes": 65007268,
"domNodes": -276,
"jsHeapTotalBytes": 3993600,
"scriptDurationMs": 122.662,
"eventListeners": -142,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "large-graph-pan",
"durationMs": 2182.307000000037,
"styleRecalcs": 69,
"styleRecalcDurationMs": 16.522000000000002,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 1353.259,
"heapDeltaBytes": -3850824,
"heapUsedBytes": 55747216,
"domNodes": -276,
"jsHeapTotalBytes": 5828608,
"scriptDurationMs": 435.57500000000005,
"eventListeners": -142,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.699999999999818
},
{
"name": "large-graph-pan",
"durationMs": 2254.997000000003,
"styleRecalcs": 68,
"styleRecalcDurationMs": 15.518,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 1387.2549999999999,
"heapDeltaBytes": 2601668,
"heapUsedBytes": 62564616,
"domNodes": -277,
"jsHeapTotalBytes": 5246976,
"scriptDurationMs": 442.137,
"eventListeners": -146,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "large-graph-zoom",
"durationMs": 3296.2440000000015,
"styleRecalcs": 64,
"styleRecalcDurationMs": 15.442999999999998,
"layouts": 60,
"layoutDurationMs": 8.853,
"taskDurationMs": 1639.737,
"heapDeltaBytes": 2696572,
"heapUsedBytes": 64065208,
"domNodes": -283,
"jsHeapTotalBytes": 7401472,
"scriptDurationMs": 569.199,
"eventListeners": -148,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "large-graph-zoom",
"durationMs": 3326.4050000000225,
"styleRecalcs": 65,
"styleRecalcDurationMs": 17.345,
"layouts": 60,
"layoutDurationMs": 9.001,
"taskDurationMs": 1618.364,
"heapDeltaBytes": 23961000,
"heapUsedBytes": 85653732,
"domNodes": 12,
"jsHeapTotalBytes": 8126464,
"scriptDurationMs": 583.7439999999999,
"eventListeners": 8,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.670000000000012,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "minimap-idle",
"durationMs": 2045.6940000000259,
"styleRecalcs": 7,
"styleRecalcDurationMs": 6.789,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 850.1659999999999,
"heapDeltaBytes": 7112636,
"heapUsedBytes": 67845604,
"domNodes": -279,
"jsHeapTotalBytes": 3993600,
"scriptDurationMs": 129.274,
"eventListeners": -146,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "minimap-idle",
"durationMs": 2077.087000000006,
"styleRecalcs": 9,
"styleRecalcDurationMs": 9.731,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 863.567,
"heapDeltaBytes": 7165132,
"heapUsedBytes": 67799816,
"domNodes": -276,
"jsHeapTotalBytes": 3469312,
"scriptDurationMs": 141.845,
"eventListeners": -142,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "subgraph-dom-widget-clipping",
"durationMs": 641.2479999999618,
"styleRecalcs": 46,
"styleRecalcDurationMs": 11.31,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 438.457,
"heapDeltaBytes": -12003124,
"heapUsedBytes": 56172224,
"domNodes": 18,
"jsHeapTotalBytes": 21884928,
"scriptDurationMs": 127.017,
"eventListeners": 8,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "subgraph-dom-widget-clipping",
"durationMs": 661.5880000000516,
"styleRecalcs": 47,
"styleRecalcDurationMs": 11.879,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 442.484,
"heapDeltaBytes": -11787676,
"heapUsedBytes": 56153688,
"domNodes": 20,
"jsHeapTotalBytes": 21622784,
"scriptDurationMs": 130.6,
"eventListeners": 8,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "subgraph-idle",
"durationMs": 2042.1400000000176,
"styleRecalcs": 10,
"styleRecalcDurationMs": 9.082999999999998,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 555.523,
"heapDeltaBytes": 3590116,
"heapUsedBytes": 71704908,
"domNodes": 20,
"jsHeapTotalBytes": 20836352,
"scriptDurationMs": 20.794,
"eventListeners": 4,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "subgraph-idle",
"durationMs": 2003.4509999999273,
"styleRecalcs": 9,
"styleRecalcDurationMs": 8.516,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 595.972,
"heapDeltaBytes": 3436720,
"heapUsedBytes": 71394592,
"domNodes": 18,
"jsHeapTotalBytes": 21360640,
"scriptDurationMs": 22.479000000000003,
"eventListeners": 4,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "subgraph-mouse-sweep",
"durationMs": 1705.9070000000247,
"styleRecalcs": 76,
"styleRecalcDurationMs": 40.602,
"layouts": 16,
"layoutDurationMs": 5.313,
"taskDurationMs": 829.8810000000001,
"heapDeltaBytes": -5598360,
"heapUsedBytes": 62354300,
"domNodes": 62,
"jsHeapTotalBytes": 21622784,
"scriptDurationMs": 99.623,
"eventListeners": 4,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "subgraph-mouse-sweep",
"durationMs": 1759.6019999999726,
"styleRecalcs": 77,
"styleRecalcDurationMs": 42.281,
"layouts": 16,
"layoutDurationMs": 5.33,
"taskDurationMs": 912.2300000000001,
"heapDeltaBytes": -21832080,
"heapUsedBytes": 46188536,
"domNodes": 64,
"jsHeapTotalBytes": 21487616,
"scriptDurationMs": 102.03099999999999,
"eventListeners": -148,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "subgraph-transition-enter",
"durationMs": 1433.3430000000362,
"styleRecalcs": 17,
"styleRecalcDurationMs": 32.905,
"layouts": 13,
"layoutDurationMs": 12.825,
"taskDurationMs": 1075.251,
"heapDeltaBytes": 33243332,
"heapUsedBytes": 100289120,
"domNodes": 13673,
"jsHeapTotalBytes": 11272192,
"scriptDurationMs": 42.852000000000004,
"eventListeners": 2371,
"totalBlockingTimeMs": 138,
"frameDurationMs": 16.66333333333335,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "viewport-pan-sweep",
"durationMs": 8343.259000000045,
"styleRecalcs": 248,
"styleRecalcDurationMs": 43.351,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 4873.677000000001,
"heapDeltaBytes": 10908880,
"heapUsedBytes": 69999184,
"domNodes": -279,
"jsHeapTotalBytes": 6791168,
"scriptDurationMs": 1445.144,
"eventListeners": -128,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333338,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "viewport-pan-sweep",
"durationMs": 8600.922999999966,
"styleRecalcs": 250,
"styleRecalcDurationMs": 44.32600000000001,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 4898.594,
"heapDeltaBytes": 10006280,
"heapUsedBytes": 68819804,
"domNodes": -274,
"jsHeapTotalBytes": 6004736,
"scriptDurationMs": 1459.3149999999998,
"eventListeners": -126,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "vue-large-graph-idle",
"durationMs": 18645.606999999985,
"styleRecalcs": 0,
"styleRecalcDurationMs": 0,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 18597.988,
"heapDeltaBytes": -51623600,
"heapUsedBytes": 169584036,
"domNodes": -5028,
"jsHeapTotalBytes": -7016448,
"scriptDurationMs": 605.174,
"eventListeners": -16385,
"totalBlockingTimeMs": 0,
"frameDurationMs": 18.330000000000048,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "vue-large-graph-idle",
"durationMs": 18481.67799999999,
"styleRecalcs": 0,
"styleRecalcDurationMs": 0,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 18423.738,
"heapDeltaBytes": -38902620,
"heapUsedBytes": 172849020,
"domNodes": -8312,
"jsHeapTotalBytes": -11997184,
"scriptDurationMs": 592.1690000000001,
"eventListeners": -16387,
"totalBlockingTimeMs": 0,
"frameDurationMs": 17.776666666666642,
"p95FrameDurationMs": 16.80000000000291
},
{
"name": "vue-large-graph-pan",
"durationMs": 23533.161000000007,
"styleRecalcs": 179,
"styleRecalcDurationMs": 28.876000000000012,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 23485.839,
"heapDeltaBytes": -47128024,
"heapUsedBytes": 164253920,
"domNodes": -8312,
"jsHeapTotalBytes": -13045760,
"scriptDurationMs": 934.568,
"eventListeners": -16377,
"totalBlockingTimeMs": 853,
"frameDurationMs": 17.776666666666642,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "vue-large-graph-pan",
"durationMs": 23693.364999999972,
"styleRecalcs": 178,
"styleRecalcDurationMs": 29.299999999999994,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 23669.355,
"heapDeltaBytes": -67193872,
"heapUsedBytes": 154216812,
"domNodes": -8312,
"jsHeapTotalBytes": -14356480,
"scriptDurationMs": 998.2789999999999,
"eventListeners": -16381,
"totalBlockingTimeMs": 1115,
"frameDurationMs": 18.330000000000048,
"p95FrameDurationMs": 16.700000000004366
},
{
"name": "workflow-execution",
"durationMs": 110.6520000000728,
"styleRecalcs": 9,
"styleRecalcDurationMs": 16.253999999999998,
"layouts": 2,
"layoutDurationMs": 0.9830000000000001,
"taskDurationMs": 79.504,
"heapDeltaBytes": 2991564,
"heapUsedBytes": 70036776,
"domNodes": 132,
"jsHeapTotalBytes": 2883584,
"scriptDurationMs": 6.897,
"eventListeners": 17,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "workflow-execution",
"durationMs": 476.16099999993367,
"styleRecalcs": 12,
"styleRecalcDurationMs": 19.810999999999996,
"layouts": 3,
"layoutDurationMs": 0.6839999999999999,
"taskDurationMs": 118.065,
"heapDeltaBytes": -16078256,
"heapUsedBytes": 50892804,
"domNodes": 117,
"jsHeapTotalBytes": 7729152,
"scriptDurationMs": 10.629000000000001,
"eventListeners": 65,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
}
]
} |
🎨 Storybook: 🚧 Building... |
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughAdds a ChangesVideo-edit widget contracts and construction
Estimated code review effort: 4 (Complex) | ~60 minutes Possibly related PRs
Suggested reviewers: Important Pre-merge checks failedPlease resolve all errors before merging. Addressing warnings is optional. ❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (4 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 |
There was a problem hiding this comment.
Actionable comments posted: 4
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/components/videoEdit/VideoEditPanel.test.ts`:
- Around line 122-149: The VideoEditPanel tests lack coverage for the
panel-owned reset start/end frame controls and their disabled-state boundaries.
Extend the trim test flow around the existing trim toggle to locate buttons by
the accessible names “Reset start frame” and “Reset end frame”, verify their
enabled/disabled states for boundary and non-boundary frame values, and click
them to assert the corresponding frame values reset. Keep the test focused on
real panel behavior rather than stub interactions.
In `@src/components/videoEdit/VideoEditPanel.vue`:
- Around line 130-142: Associate the ratio label with the Select control in the
template around selectedRatio: give SelectTrigger a stable id and set the
label’s for attribute to that id, or otherwise provide an equivalent accessible
name. Preserve the existing Select behavior and styling.
In `@src/composables/video/useVideoEditModel.ts`:
- Around line 26-29: Update useVideoEditModel’s frameToTime and timeToFrame
calls to pass the model’s fps ref as the fallback FPS, matching
WidgetVideoEdit.vue’s helper usage. Ensure both toTime and toFrame use the same
fps option when duration or frameMax is unavailable so trim boundaries remain
consistent.
In `@src/schemas/nodeDef/nodeDefSchemaV2.ts`:
- Around line 192-214: The VIDEO_EDIT schema currently accepts negative or
non-finite edit values and defaults can specify disabled features. Update
zVideoEditValue and zVideoEditInputSpec so all trim/crop numeric fields are
finite and non-negative, and validate that each default section is permitted by
the corresponding features entry. Add coverage for negative/non-finite values
and defaults containing unsupported trim or crop sections.
🪄 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: ASSERTIVE
Plan: Pro Plus
Run ID: a5c98d19-d854-482c-840d-10a4aa98d969
📒 Files selected for processing (17)
src/components/videoEdit/VideoEditPanel.test.tssrc/components/videoEdit/VideoEditPanel.vuesrc/components/videoEdit/WidgetVideoEdit.test.tssrc/components/videoEdit/WidgetVideoEdit.vuesrc/composables/video/useVideoEditModel.test.tssrc/composables/video/useVideoEditModel.tssrc/lib/litegraph/src/types/widgets.tssrc/lib/litegraph/src/widgets/VideoEditWidget.tssrc/lib/litegraph/src/widgets/widgetMap.tssrc/locales/en/main.jsonsrc/renderer/extensions/vueNodes/components/LGraphNode.vuesrc/renderer/extensions/vueNodes/widgets/components/layout/index.tssrc/renderer/extensions/vueNodes/widgets/composables/useVideoEditWidget.test.tssrc/renderer/extensions/vueNodes/widgets/composables/useVideoEditWidget.tssrc/renderer/extensions/vueNodes/widgets/registry/widgetRegistry.tssrc/schemas/nodeDef/nodeDefSchemaV2.tssrc/scripts/widgets.ts
Codecov Report❌ Patch coverage is @@ Coverage Diff @@
## main #14205 +/- ##
==========================================
- Coverage 79.07% 78.62% -0.45%
==========================================
Files 1750 1758 +8
Lines 99329 101365 +2036
Branches 32936 32731 -205
==========================================
+ Hits 78540 79697 +1157
- Misses 20389 21238 +849
- Partials 400 430 +30
Flags with carried forward coverage won't be shown. Click here to find out more.
... and 45 files with indirect coverage changes 🚀 New features to boost your workflow:
|
1874e16 to
533a351
Compare
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/components/videoEdit/VideoEditPanel.test.ts`:
- Around line 180-184: The loading-state test only verifies the spinner and
lacks coverage that crop/trim overlays are suppressed. Extend the `shows a
loading overlay while the filmstrip loads` test using `renderPanel({ loading:
true })` to assert the `VideoCropOverlay` target is absent while preserving the
existing loading-overlay assertion.
🪄 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: ASSERTIVE
Plan: Pro Plus
Run ID: 1efe2423-c952-49de-8355-3d5cdee1b940
📒 Files selected for processing (17)
src/components/videoEdit/VideoEditPanel.test.tssrc/components/videoEdit/VideoEditPanel.vuesrc/components/videoEdit/WidgetVideoEdit.test.tssrc/components/videoEdit/WidgetVideoEdit.vuesrc/composables/video/useVideoEditModel.test.tssrc/composables/video/useVideoEditModel.tssrc/lib/litegraph/src/types/widgets.tssrc/lib/litegraph/src/widgets/VideoEditWidget.tssrc/lib/litegraph/src/widgets/widgetMap.tssrc/locales/en/main.jsonsrc/renderer/extensions/vueNodes/components/LGraphNode.vuesrc/renderer/extensions/vueNodes/widgets/components/layout/index.tssrc/renderer/extensions/vueNodes/widgets/composables/useVideoEditWidget.test.tssrc/renderer/extensions/vueNodes/widgets/composables/useVideoEditWidget.tssrc/renderer/extensions/vueNodes/widgets/registry/widgetRegistry.tssrc/schemas/nodeDef/nodeDefSchemaV2.tssrc/scripts/widgets.ts
| it('shows a loading overlay while the filmstrip loads', () => { | ||
| renderPanel({ loading: true }) | ||
|
|
||
| expect(screen.getByTestId('video-preview-loading')).toBeTruthy() | ||
| }) |
There was a problem hiding this comment.
🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win
Add coverage that loading suppresses the crop/trim overlays, not just that the spinner shows.
The template gates VideoCropOverlay on !loading (VideoEditPanel.vue Line 34), but this test only checks the loading overlay appears — it doesn't verify the crop overlay is hidden while loading.
✅ Suggested addition
it('shows a loading overlay while the filmstrip loads', () => {
- renderPanel({ loading: true })
+ renderPanel({ loading: true, features: ['crop'], cropEnabled: true })
expect(screen.getByTestId('video-preview-loading')).toBeTruthy()
+ expect(screen.queryByTestId('stub-crop-overlay')).toBeNull()
})As per path instructions, .agents/checks/test-quality.md calls out checking "missing test coverage for newly added behaviors (especially edge cases and error/null states)."
📝 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.
| it('shows a loading overlay while the filmstrip loads', () => { | |
| renderPanel({ loading: true }) | |
| expect(screen.getByTestId('video-preview-loading')).toBeTruthy() | |
| }) | |
| it('shows a loading overlay while the filmstrip loads', () => { | |
| renderPanel({ loading: true, features: ['crop'], cropEnabled: true }) | |
| expect(screen.getByTestId('video-preview-loading')).toBeTruthy() | |
| expect(screen.queryByTestId('stub-crop-overlay')).toBeNull() | |
| }) |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/components/videoEdit/VideoEditPanel.test.ts` around lines 180 - 184, The
loading-state test only verifies the spinner and lacks coverage that crop/trim
overlays are suppressed. Extend the `shows a loading overlay while the filmstrip
loads` test using `renderPanel({ loading: true })` to assert the
`VideoCropOverlay` target is absent while preserving the existing
loading-overlay assertion.
Source: Path instructions
533a351 to
dc1f5d9
Compare
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/components/videoEdit/VideoEditPanel.vue`:
- Around line 301-317: Update setStartFrameDisabled and setEndFrameDisabled to
include loading, and apply the same loading guard to the disabled options in
startFrameWidget and endFrameWidget. Keep the existing videoUrl and frame-bound
checks so all trim reset and number-input controls remain disabled until
reliable frame metadata is available.
🪄 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: ASSERTIVE
Plan: Pro Plus
Run ID: 703f646d-8123-427a-a316-dcd251d15495
📒 Files selected for processing (16)
src/components/videoEdit/VideoEditPanel.test.tssrc/components/videoEdit/VideoEditPanel.vuesrc/components/videoEdit/WidgetVideoEdit.test.tssrc/components/videoEdit/WidgetVideoEdit.vuesrc/composables/video/useVideoEditModel.test.tssrc/composables/video/useVideoEditModel.tssrc/lib/litegraph/src/types/widgets.tssrc/lib/litegraph/src/widgets/VideoEditWidget.tssrc/lib/litegraph/src/widgets/widgetMap.tssrc/locales/en/main.jsonsrc/renderer/extensions/vueNodes/components/LGraphNode.vuesrc/renderer/extensions/vueNodes/widgets/composables/useVideoEditWidget.test.tssrc/renderer/extensions/vueNodes/widgets/composables/useVideoEditWidget.tssrc/renderer/extensions/vueNodes/widgets/registry/widgetRegistry.tssrc/schemas/nodeDef/nodeDefSchemaV2.tssrc/scripts/widgets.ts
dante01yoon
left a comment
There was a problem hiding this comment.
Two blocking correctness issues remain in the video edit widget: trim handles serialize off-by-one frame boundaries, and promoted subgraph widgets resolve their source from the outer host node. Details are inline.
| get: () => clamp(toFrame(trimSection.value.start_time), 0, frameMax.value), | ||
| set: (frame) => { | ||
| const maxStart = Math.max(currentEndFrame() - 1, 0) | ||
| const startTime = roundSeconds(toTime(clamp(frame, 0, maxStart))) |
There was a problem hiding this comment.
issue: This maps a frame index against frameMax (totalFrames - 1) to the full media duration, so the serialized trim does not preserve the selected frames. For example, with 100 frames over 10s, selecting start frame 10 writes about 1.01s; VideoFromFile then skips the frame at 1.0s (pts < start_pts). The end handle has the complementary problem: it is inclusive in the UI, but serializing its own timestamp as the exclusive backend boundary omits that selected frame. Could start use the boundary at frame / totalFrames * duration and a non-final end use the boundary after the selected frame ((frame + 1) / totalFrames * duration), with equivalent FPS fallbacks and exact-frame coverage?
| (): VideoEditFeature[] => widget.options?.features ?? ['trim', 'crop'] | ||
| ) | ||
|
|
||
| const node = computed(() => app.canvas.graph?.getNodeById(nodeId)) |
There was a problem hiding this comment.
issue: nodeId is the rendered host node ID. For a promoted widget, NodeWidgets passes the outer subgraph node here while widget.nodeLocatorId identifies the inner node that actually owns this editor. Resolving the host makes useVideoSourceUrl inspect/traverse the outer node's video input, so the editor has no source (or the wrong one) unless that input also happens to be promoted onto a matching host slot. Could this resolve the owner through getNodeByLocatorId(app.rootGraph, widget.nodeLocatorId) and only fall back to the host when no locator is available, with a promoted-widget test?
e484ab9 to
db2e88c
Compare
There was a problem hiding this comment.
Actionable comments posted: 5
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/components/videoEdit/VideoEditPanel.vue`:
- Around line 275-291: Update the toTime and toFrame conversions in
VideoEditPanel to pass raw totalFrames instead of effectiveTotalFrames.value,
keeping them aligned with useVideoEditModel’s zero-frame behavior. Retain
effectiveTotalFrames only for display or timeline sizing, including frameMax as
currently used.
In `@src/components/videoEdit/WidgetVideoEdit.test.ts`:
- Around line 90-93: Update the stub button in the video edit test around the
emit-start-frame trigger to include visible, descriptive text and select it by
its accessible button role and name instead of the data-testid. Apply the same
change to the corresponding trigger at the additional occurrence near the later
test location, preserving its existing click behavior.
In `@src/composables/video/useVideoEditModel.ts`:
- Around line 128-157: The trimEnabledState and cropEnabledState watchers only
handle activation, so toggles remain enabled after external clearing. Update the
watchers for hasActiveTrim and hasActiveCrop to mirror both active and inactive
transitions, while preserving the computed setters’ existing reset behavior for
user-disabled toggles.
- Around line 98-116: Update the crop assignment in the setter to clamp the
rounded crop coordinates and dimensions so the crop remains within width.value
and height.value, including after rounding. Preserve the existing
coversFullFrame reset behavior, and ensure the crop’s right and bottom edges
cannot exceed the source frame.
In `@src/renderer/extensions/vueNodes/widgets/composables/useVideoEditWidget.ts`:
- Around line 27-32: Set the workflow-level serialize property on the concrete
videoedit widget instance, not only in the IWidgetVideoEditOptions object.
Update the widget construction flow around toClass() and VideoEditWidget so the
BaseWidget receives serialize: true for workflow-persistable trim/crop state,
including when the source POJO value is undefined.
🪄 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: ASSERTIVE
Plan: Pro Plus
Run ID: ce4e13c4-b694-4e4e-8cc8-546d5ef51e61
📒 Files selected for processing (17)
src/components/videoEdit/VideoEditPanel.test.tssrc/components/videoEdit/VideoEditPanel.vuesrc/components/videoEdit/WidgetVideoEdit.test.tssrc/components/videoEdit/WidgetVideoEdit.vuesrc/composables/video/useVideoEditModel.test.tssrc/composables/video/useVideoEditModel.tssrc/lib/litegraph/src/types/widgets.tssrc/lib/litegraph/src/widgets/VideoEditWidget.tssrc/lib/litegraph/src/widgets/widgetMap.tssrc/locales/en/main.jsonsrc/renderer/extensions/vueNodes/components/LGraphNode.vuesrc/renderer/extensions/vueNodes/widgets/composables/useVideoEditWidget.test.tssrc/renderer/extensions/vueNodes/widgets/composables/useVideoEditWidget.tssrc/renderer/extensions/vueNodes/widgets/registry/widgetRegistry.tssrc/schemas/nodeDef/nodeDefSchemaV2.tssrc/scripts/widgets.tssrc/utils/videoFrameUtil.ts
db2e88c to
6e6e0b2
Compare
📄 Knowledge reviewDosu skipped reviewing this PR because your organization has used its |
This is seperate last part for the big PR of video edit, crop, trim PR #14118
Summary
Wires the VIDEO_EDIT input type end to end and assembles the editor UI from the previously merged building blocks: