refactor: migrate main app toggles to shared switch#13778
Conversation
🎭 Playwright: ❌ 1701 passed, 6 failed · 2 flaky❌ Failed Tests📊 Browser Reports
🎨 Storybook: ✅ Built — View Storybook📦 Bundle: 7.91 MB gzip 🔴 +887 BDetailsSummary
Category Glance App Entry Points — 47.4 kB (baseline 47.3 kB) • 🔴 +26 BMain entry bundles and manifests
Status: 1 added / 1 removed Graph Workspace — 1.25 MB (baseline 1.25 MB) • 🟢 -59 BGraph editor runtime, canvas, workflow orchestration
Status: 1 added / 1 removed Views & Navigation — 109 kB (baseline 109 kB) • 🟢 -1 BTop-level views, pages, and routed surfaces
Status: 12 added / 12 removed / 2 unchanged Panels & Settings — 551 kB (baseline 550 kB) • 🔴 +231 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: 8 added / 8 removed / 2 unchanged Editors & Dialogs — 121 kB (baseline 121 kB) • ⚪ 0 BModals, dialogs, drawers, and in-app editors
Status: 6 added / 6 removed UI Components — 56.8 kB (baseline 56.8 kB) • ⚪ 0 BReusable component library chunks
Status: 12 added / 12 removed / 1 unchanged Data & Services — 270 kB (baseline 270 kB) • 🔴 +52 BStores, services, APIs, and repositories
Status: 13 added / 13 removed / 3 unchanged Utilities & Hooks — 3.4 MB (baseline 3.4 MB) • 🔴 +115 BHelpers, composables, and utility bundles
Status: 20 added / 20 removed / 16 unchanged Vendor & Third-Party — 15.7 MB (baseline 15.7 MB) • 🟢 -4.99 kBExternal libraries and shared vendor chunks
Status: 2 added / 2 removed / 14 unchanged Other — 11.9 MB (baseline 11.9 MB) • 🔴 +2.46 kBBundles that do not match a named category
Status: 93 added / 92 removed / 71 unchanged ⚡ Performance Report
✅ No regressions detected. All metrics
Historical variance (last 15 runs)
Trend (last 15 commits on main)
Raw data{
"timestamp": "2026-07-18T00:35:00.770Z",
"gitSha": "24be535eca1144b925efd70b0328708476543b63",
"branch": "jaewon/migrate-primevue-toggle-switch",
"measurements": [
{
"name": "canvas-idle",
"durationMs": 2147.10199999999,
"styleRecalcs": 8,
"styleRecalcDurationMs": 5.8050000000000015,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 461.857,
"heapDeltaBytes": 3813844,
"heapUsedBytes": 72596932,
"domNodes": 16,
"jsHeapTotalBytes": 19935232,
"scriptDurationMs": 20.747,
"eventListeners": 6,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "canvas-idle",
"durationMs": 2021.7870000000175,
"styleRecalcs": 9,
"styleRecalcDurationMs": 6.542000000000001,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 394.254,
"heapDeltaBytes": -20881660,
"heapUsedBytes": 47920856,
"domNodes": -265,
"jsHeapTotalBytes": 19275776,
"scriptDurationMs": 15.110000000000001,
"eventListeners": -133,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.670000000000012,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "canvas-mouse-sweep",
"durationMs": 1827.0430000000033,
"styleRecalcs": 74,
"styleRecalcDurationMs": 34.604000000000006,
"layouts": 12,
"layoutDurationMs": 3.4330000000000003,
"taskDurationMs": 789.2399999999999,
"heapDeltaBytes": -13210556,
"heapUsedBytes": 55501092,
"domNodes": -265,
"jsHeapTotalBytes": 20848640,
"scriptDurationMs": 119.63399999999999,
"eventListeners": -133,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "canvas-mouse-sweep",
"durationMs": 1761.6130000000112,
"styleRecalcs": 72,
"styleRecalcDurationMs": 32.062999999999995,
"layouts": 12,
"layoutDurationMs": 3.216,
"taskDurationMs": 776.4689999999999,
"heapDeltaBytes": -17702520,
"heapUsedBytes": 51263428,
"domNodes": -264,
"jsHeapTotalBytes": 19800064,
"scriptDurationMs": 104.96699999999998,
"eventListeners": -133,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "canvas-zoom-sweep",
"durationMs": 1740.3790000000186,
"styleRecalcs": 31,
"styleRecalcDurationMs": 15.691000000000004,
"layouts": 6,
"layoutDurationMs": 0.642,
"taskDurationMs": 297.77600000000007,
"heapDeltaBytes": 7671936,
"heapUsedBytes": 76488464,
"domNodes": 78,
"jsHeapTotalBytes": 19148800,
"scriptDurationMs": 16.933999999999997,
"eventListeners": 19,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "canvas-zoom-sweep",
"durationMs": 1723.5210000000052,
"styleRecalcs": 32,
"styleRecalcDurationMs": 15.216999999999999,
"layouts": 6,
"layoutDurationMs": 0.6839999999999999,
"taskDurationMs": 293.447,
"heapDeltaBytes": 7522612,
"heapUsedBytes": 76457488,
"domNodes": 78,
"jsHeapTotalBytes": 19148800,
"scriptDurationMs": 17.264000000000003,
"eventListeners": 19,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "dom-widget-clipping",
"durationMs": 562.6689999999996,
"styleRecalcs": 13,
"styleRecalcDurationMs": 8.753,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 333.125,
"heapDeltaBytes": -11751976,
"heapUsedBytes": 57104364,
"domNodes": 22,
"jsHeapTotalBytes": 19935232,
"scriptDurationMs": 52.931000000000004,
"eventListeners": 0,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "dom-widget-clipping",
"durationMs": 560.1929999999697,
"styleRecalcs": 12,
"styleRecalcDurationMs": 12.149000000000001,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 338.18000000000006,
"heapDeltaBytes": -11608772,
"heapUsedBytes": 57150388,
"domNodes": 20,
"jsHeapTotalBytes": 19410944,
"scriptDurationMs": 60.405,
"eventListeners": 0,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "large-graph-idle",
"durationMs": 2037.3299999999972,
"styleRecalcs": 11,
"styleRecalcDurationMs": 8.901,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 519.2260000000001,
"heapDeltaBytes": 5372048,
"heapUsedBytes": 66543268,
"domNodes": -264,
"jsHeapTotalBytes": 3878912,
"scriptDurationMs": 78.44800000000001,
"eventListeners": -129,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "large-graph-idle",
"durationMs": 2036.9910000000004,
"styleRecalcs": 9,
"styleRecalcDurationMs": 6.780000000000001,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 522.7239999999999,
"heapDeltaBytes": 5397324,
"heapUsedBytes": 64762884,
"domNodes": -267,
"jsHeapTotalBytes": 4927488,
"scriptDurationMs": 77.401,
"eventListeners": -129,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333335,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "large-graph-pan",
"durationMs": 2171.3379999999916,
"styleRecalcs": 71,
"styleRecalcDurationMs": 16.474999999999998,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 1033.445,
"heapDeltaBytes": 1684236,
"heapUsedBytes": 63967812,
"domNodes": -266,
"jsHeapTotalBytes": 5394432,
"scriptDurationMs": 360.452,
"eventListeners": -127,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "large-graph-pan",
"durationMs": 2158.52799999999,
"styleRecalcs": 70,
"styleRecalcDurationMs": 16.496000000000002,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 1038.585,
"heapDeltaBytes": -1051924,
"heapUsedBytes": 61313168,
"domNodes": -268,
"jsHeapTotalBytes": 5132288,
"scriptDurationMs": 363.765,
"eventListeners": -129,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "large-graph-zoom",
"durationMs": 3146.8039999999746,
"styleRecalcs": 66,
"styleRecalcDurationMs": 15.795000000000003,
"layouts": 60,
"layoutDurationMs": 8.365,
"taskDurationMs": 1320.505,
"heapDeltaBytes": -5790212,
"heapUsedBytes": 55996344,
"domNodes": -269,
"jsHeapTotalBytes": 7811072,
"scriptDurationMs": 473.232,
"eventListeners": -133,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "large-graph-zoom",
"durationMs": 3134.6960000000195,
"styleRecalcs": 66,
"styleRecalcDurationMs": 17.055,
"layouts": 60,
"layoutDurationMs": 8.106,
"taskDurationMs": 1291.1650000000002,
"heapDeltaBytes": -5538228,
"heapUsedBytes": 56372872,
"domNodes": -273,
"jsHeapTotalBytes": 7811072,
"scriptDurationMs": 471.17599999999993,
"eventListeners": -133,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "minimap-idle",
"durationMs": 2030.017999999984,
"styleRecalcs": 8,
"styleRecalcDurationMs": 6.620000000000001,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 530.0129999999999,
"heapDeltaBytes": 4922692,
"heapUsedBytes": 65957232,
"domNodes": -270,
"jsHeapTotalBytes": 5451776,
"scriptDurationMs": 78.747,
"eventListeners": -127,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "minimap-idle",
"durationMs": 2010.7550000000174,
"styleRecalcs": 8,
"styleRecalcDurationMs": 5.5569999999999995,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 512.4979999999999,
"heapDeltaBytes": 5137912,
"heapUsedBytes": 66237672,
"domNodes": -268,
"jsHeapTotalBytes": 5451776,
"scriptDurationMs": 76.769,
"eventListeners": -129,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "subgraph-dom-widget-clipping",
"durationMs": 550.4030000000171,
"styleRecalcs": 47,
"styleRecalcDurationMs": 10.083,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 351.63100000000003,
"heapDeltaBytes": -11467956,
"heapUsedBytes": 57609628,
"domNodes": 20,
"jsHeapTotalBytes": 20983808,
"scriptDurationMs": 120.45000000000002,
"eventListeners": 6,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "subgraph-dom-widget-clipping",
"durationMs": 577.8760000000602,
"styleRecalcs": 47,
"styleRecalcDurationMs": 10.729999999999999,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 366.81399999999996,
"heapDeltaBytes": -11371800,
"heapUsedBytes": 57512804,
"domNodes": 20,
"jsHeapTotalBytes": 20721664,
"scriptDurationMs": 123.654,
"eventListeners": 6,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "subgraph-idle",
"durationMs": 2056.137000000007,
"styleRecalcs": 11,
"styleRecalcDurationMs": 14.573999999999998,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 404.212,
"heapDeltaBytes": -21275088,
"heapUsedBytes": 47503092,
"domNodes": -264,
"jsHeapTotalBytes": 20848640,
"scriptDurationMs": 12.782000000000002,
"eventListeners": -133,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "subgraph-idle",
"durationMs": 2041.7120000000182,
"styleRecalcs": 11,
"styleRecalcDurationMs": 8.091999999999999,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 346.96099999999996,
"heapDeltaBytes": 3796032,
"heapUsedBytes": 72792928,
"domNodes": 22,
"jsHeapTotalBytes": 19673088,
"scriptDurationMs": 13.142,
"eventListeners": 4,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "subgraph-mouse-sweep",
"durationMs": 1724.6189999999615,
"styleRecalcs": 75,
"styleRecalcDurationMs": 33.961999999999996,
"layouts": 16,
"layoutDurationMs": 4.184,
"taskDurationMs": 706.0079999999999,
"heapDeltaBytes": -19076168,
"heapUsedBytes": 49970460,
"domNodes": -265,
"jsHeapTotalBytes": 19275776,
"scriptDurationMs": 91.227,
"eventListeners": -133,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "subgraph-mouse-sweep",
"durationMs": 1724.486000000013,
"styleRecalcs": 76,
"styleRecalcDurationMs": 34.741,
"layouts": 16,
"layoutDurationMs": 4.375,
"taskDurationMs": 683.9259999999999,
"heapDeltaBytes": -19394952,
"heapUsedBytes": 49495120,
"domNodes": -265,
"jsHeapTotalBytes": 20848640,
"scriptDurationMs": 84.325,
"eventListeners": -133,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "subgraph-transition-enter",
"durationMs": 965.411999999958,
"styleRecalcs": 19,
"styleRecalcDurationMs": 29.715,
"layouts": 15,
"layoutDurationMs": 13.409,
"taskDurationMs": 730.227,
"heapDeltaBytes": 1184716,
"heapUsedBytes": 84825020,
"domNodes": 13673,
"jsHeapTotalBytes": 16252928,
"scriptDurationMs": 25.828000000000003,
"eventListeners": 2371,
"totalBlockingTimeMs": 120,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "viewport-pan-sweep",
"durationMs": 8143.024999999966,
"styleRecalcs": 249,
"styleRecalcDurationMs": 40.46,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 3430.35,
"heapDeltaBytes": 15987716,
"heapUsedBytes": 75014756,
"domNodes": -266,
"jsHeapTotalBytes": 7229440,
"scriptDurationMs": 1106.157,
"eventListeners": -113,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "viewport-pan-sweep",
"durationMs": 8249.192999999992,
"styleRecalcs": 250,
"styleRecalcDurationMs": 40.435,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 3481.493,
"heapDeltaBytes": 15137964,
"heapUsedBytes": 74273412,
"domNodes": -267,
"jsHeapTotalBytes": 6443008,
"scriptDurationMs": 1128.63,
"eventListeners": -113,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "vue-large-graph-idle",
"durationMs": 12914.032000000021,
"styleRecalcs": 0,
"styleRecalcDurationMs": 0,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 12900.831999999999,
"heapDeltaBytes": -46914680,
"heapUsedBytes": 166088980,
"domNodes": -8311,
"jsHeapTotalBytes": -14356480,
"scriptDurationMs": 538.229,
"eventListeners": -16386,
"totalBlockingTimeMs": 0,
"frameDurationMs": 17.223333333333358,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "vue-large-graph-idle",
"durationMs": 12818.320999999969,
"styleRecalcs": 0,
"styleRecalcDurationMs": 0,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 12806.524999999998,
"heapDeltaBytes": -48895292,
"heapUsedBytes": 170981624,
"domNodes": -8311,
"jsHeapTotalBytes": -7540736,
"scriptDurationMs": 495.40700000000004,
"eventListeners": -16384,
"totalBlockingTimeMs": 0,
"frameDurationMs": 17.223333333333358,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "vue-large-graph-pan",
"durationMs": 16122.314000000017,
"styleRecalcs": 83,
"styleRecalcDurationMs": 13.52399999999998,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 16100.473999999998,
"heapDeltaBytes": -51356596,
"heapUsedBytes": 177197036,
"domNodes": -8311,
"jsHeapTotalBytes": -12607488,
"scriptDurationMs": 817.331,
"eventListeners": -16380,
"totalBlockingTimeMs": 38,
"frameDurationMs": 17.22666666666664,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "vue-large-graph-pan",
"durationMs": 16148.210000000063,
"styleRecalcs": 84,
"styleRecalcDurationMs": 14.065999999999995,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 16126.345999999998,
"heapDeltaBytes": -50190288,
"heapUsedBytes": 173289928,
"domNodes": -8311,
"jsHeapTotalBytes": -11034624,
"scriptDurationMs": 771.3729999999999,
"eventListeners": -16380,
"totalBlockingTimeMs": 47,
"frameDurationMs": 17.219999999999953,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "workflow-execution",
"durationMs": 455.02399999998033,
"styleRecalcs": 20,
"styleRecalcDurationMs": 21.549000000000003,
"layouts": 3,
"layoutDurationMs": 1.451,
"taskDurationMs": 119.90699999999998,
"heapDeltaBytes": -15796160,
"heapUsedBytes": 52259236,
"domNodes": 156,
"jsHeapTotalBytes": 6565888,
"scriptDurationMs": 9.379,
"eventListeners": 67,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66999999999998,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "workflow-execution",
"durationMs": 460.3940000000648,
"styleRecalcs": 16,
"styleRecalcDurationMs": 21.425,
"layouts": 3,
"layoutDurationMs": 0.72,
"taskDurationMs": 118.086,
"heapDeltaBytes": -16489988,
"heapUsedBytes": 51586216,
"domNodes": 123,
"jsHeapTotalBytes": 7614464,
"scriptDurationMs": 8.812000000000001,
"eventListeners": 65,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333335,
"p95FrameDurationMs": 16.800000000000182
}
]
} |
📝 WalkthroughWalkthroughThe PR adds a shared accessible ChangesSwitch component migration
Estimated code review effort: 3 (Moderate) | ~25 minutes Suggested labels: Suggested reviewers: Important Pre-merge checks failedPlease resolve all errors before merging. Addressing warnings is optional. ❌ Failed checks (1 inconclusive)
✅ Passed checks (5 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: 3
🤖 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 `@browser_tests/fixtures/components/SettingDialog.ts`:
- Around line 41-43: Update the component chain used by toggleBooleanSetting so
the provided id is forwarded from FieldSwitch.vue through Switch.vue to the
rendered SwitchRoot button. Ensure the id attribute is applied to the actual
switch element targeted by button[role="switch"][id="${id}"], while preserving
existing switch behavior.
In `@src/components/sidebar/ComfyMenuButton.vue`:
- Around line 79-85: Update the Switch binding in the sidebar menu to use one
state-update path: replace v-model with :model-value while retaining
`@update`:model-value="onNodes2ToggleChange", so the computed nodes2Enabled setter
is not invoked alongside settingStore.set. Keep the existing toggle behavior and
displayed value unchanged.
In `@src/components/ui/switch/Switch.vue`:
- Around line 8-25: Replace the separate modelValue prop and update:modelValue
emit in Switch.vue with a defineModel<boolean> binding, and update
updateModelValue to respect readonly while writing through that model. Remove
the now-unused defineEmits call and SwitchRootEmits import, while preserving the
existing disabled, class, and readonly props.
🪄 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: 96ad9fe7-7e9f-41dc-b483-9b1872777b65
📒 Files selected for processing (15)
browser_tests/fixtures/components/SettingDialog.tsbrowser_tests/tests/dialogs/settingsDialog.spec.tsbrowser_tests/tests/extensionAPI.spec.tssrc/components/common/FormItem.vuesrc/components/rightSidePanel/settings/FieldSwitch.test.tssrc/components/rightSidePanel/settings/FieldSwitch.vuesrc/components/sidebar/ComfyMenuButton.vuesrc/components/ui/switch/Switch.stories.tssrc/components/ui/switch/Switch.test.tssrc/components/ui/switch/Switch.vuesrc/platform/settings/components/ExtensionPanel.vuesrc/renderer/extensions/vueNodes/widgets/components/WidgetToggleSwitch.test.tssrc/renderer/extensions/vueNodes/widgets/components/WidgetToggleSwitch.vuesrc/workbench/extensions/manager/components/manager/button/PackEnableToggle.test.tssrc/workbench/extensions/manager/components/manager/button/PackEnableToggle.vue
| async toggleBooleanSetting(id: string) { | ||
| const settingInputDiv = this.root.locator(`div[id="${id}"]`) | ||
| await settingInputDiv.locator('input').click() | ||
| await this.root.locator(`button[role="switch"][id="${id}"]`).click() | ||
| } |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Description: Check where the `id` prop is applied in FieldSwitch.vue
cat src/components/rightSidePanel/settings/FieldSwitch.vueRepository: Comfy-Org/ComfyUI_frontend
Length of output: 622
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '\n## Switch.vue\n'
cat src/components/ui/switch/Switch.vue
printf '\n## LayoutField.vue\n'
cat src/components/rightSidePanel/settings/LayoutField.vueRepository: Comfy-Org/ComfyUI_frontend
Length of output: 2631
🏁 Script executed:
#!/bin/bash
set -euo pipefail
rg -n "FieldSwitch|<FieldSwitch|id=" src/components src -g '*.vue' -g '*.ts'Repository: Comfy-Org/ComfyUI_frontend
Length of output: 50383
Forward the id to the switch button. FieldSwitch.vue doesn’t pass id to Switch, and Switch.vue only renders SwitchRoot, so button[role="switch"][id="${id}"] won’t match unless the attribute reaches the button itself.
🤖 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 `@browser_tests/fixtures/components/SettingDialog.ts` around lines 41 - 43,
Update the component chain used by toggleBooleanSetting so the provided id is
forwarded from FieldSwitch.vue through Switch.vue to the rendered SwitchRoot
button. Ensure the id attribute is applied to the actual switch element targeted
by button[role="switch"][id="${id}"], while preserving existing switch behavior.
| <Switch | ||
| v-model="nodes2Enabled" | ||
| class="ml-4" | ||
| :aria-label="item.label" | ||
| :pt="{ | ||
| root: { | ||
| style: { | ||
| width: '38px', | ||
| height: '20px' | ||
| } | ||
| }, | ||
| handle: { | ||
| style: { | ||
| width: '16px', | ||
| height: '16px' | ||
| } | ||
| } | ||
| }" | ||
| @click.stop | ||
| @update:model-value="onNodes2ToggleChange" | ||
| /> |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value
Avoid duplicate state updates.
Using v-model="nodes2Enabled" generates an implicit update listener that triggers the nodes2Enabled computed setter. Since the explicitly bound @update:model-value="onNodes2ToggleChange" also calls settingStore.set, the async update operation is redundantly executed twice per toggle.
Consider either changing v-model to :model-value to rely exclusively on the event handler for updates, or removing the redundant settingStore.set from onNodes2ToggleChange.
♻️ Proposed refactor to avoid duplicate async saves
Change v-model to :model-value:
- <Switch
- v-model="nodes2Enabled"
- class="ml-4"
- :aria-label="item.label"
- `@click.stop`
- `@update`:model-value="onNodes2ToggleChange"
- />
+ <Switch
+ :model-value="nodes2Enabled"
+ class="ml-4"
+ :aria-label="item.label"
+ `@click.stop`
+ `@update`:model-value="onNodes2ToggleChange"
+ />📝 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.
| <Switch | |
| v-model="nodes2Enabled" | |
| class="ml-4" | |
| :aria-label="item.label" | |
| :pt="{ | |
| root: { | |
| style: { | |
| width: '38px', | |
| height: '20px' | |
| } | |
| }, | |
| handle: { | |
| style: { | |
| width: '16px', | |
| height: '16px' | |
| } | |
| } | |
| }" | |
| @click.stop | |
| @update:model-value="onNodes2ToggleChange" | |
| /> | |
| <Switch | |
| :model-value="nodes2Enabled" | |
| class="ml-4" | |
| :aria-label="item.label" | |
| `@click.stop` | |
| `@update`:model-value="onNodes2ToggleChange" | |
| /> |
🤖 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/sidebar/ComfyMenuButton.vue` around lines 79 - 85, Update the
Switch binding in the sidebar menu to use one state-update path: replace v-model
with :model-value while retaining `@update`:model-value="onNodes2ToggleChange", so
the computed nodes2Enabled setter is not invoked alongside settingStore.set.
Keep the existing toggle behavior and displayed value unchanged.
| const { | ||
| class: customClass = '', | ||
| disabled = false, | ||
| modelValue = false, | ||
| readonly = false | ||
| } = defineProps<{ | ||
| class?: HTMLAttributes['class'] | ||
| disabled?: boolean | ||
| modelValue?: boolean | ||
| readonly?: boolean | ||
| }>() | ||
|
|
||
| const emit = defineEmits<SwitchRootEmits>() | ||
|
|
||
| function updateModelValue(value: boolean) { | ||
| if (readonly) return | ||
| emit('update:modelValue', value) | ||
| } |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win
Prefer defineModel for v-model bindings.
As per coding guidelines, use defineModel for v-model bindings instead of defining separate props and emits when possible. This simplifies the component API and removes the need for defineEmits.
♻️ Proposed refactor
-const {
- class: customClass = '',
- disabled = false,
- modelValue = false,
- readonly = false
-} = defineProps<{
- class?: HTMLAttributes['class']
- disabled?: boolean
- modelValue?: boolean
- readonly?: boolean
-}>()
-
-const emit = defineEmits<SwitchRootEmits>()
+const {
+ class: customClass = '',
+ disabled = false,
+ readonly = false
+} = defineProps<{
+ class?: HTMLAttributes['class']
+ disabled?: boolean
+ readonly?: boolean
+}>()
+
+const modelValue = defineModel<boolean>({ default: false })
function updateModelValue(value: boolean) {
if (readonly) return
- emit('update:modelValue', value)
+ modelValue.value = value
}Also, you can remove SwitchRootEmits from the Reka UI imports at the top of the file since it will no longer be used.
📝 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.
| const { | |
| class: customClass = '', | |
| disabled = false, | |
| modelValue = false, | |
| readonly = false | |
| } = defineProps<{ | |
| class?: HTMLAttributes['class'] | |
| disabled?: boolean | |
| modelValue?: boolean | |
| readonly?: boolean | |
| }>() | |
| const emit = defineEmits<SwitchRootEmits>() | |
| function updateModelValue(value: boolean) { | |
| if (readonly) return | |
| emit('update:modelValue', value) | |
| } | |
| const { | |
| class: customClass = '', | |
| disabled = false, | |
| readonly = false | |
| } = defineProps<{ | |
| class?: HTMLAttributes['class'] | |
| disabled?: boolean | |
| readonly?: boolean | |
| }>() | |
| const modelValue = defineModel<boolean>({ default: false }) | |
| function updateModelValue(value: boolean) { | |
| if (readonly) return | |
| modelValue.value = value | |
| } |
🤖 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/ui/switch/Switch.vue` around lines 8 - 25, Replace the
separate modelValue prop and update:modelValue emit in Switch.vue with a
defineModel<boolean> binding, and update updateModelValue to respect readonly
while writing through that model. Remove the now-unused defineEmits call and
SwitchRootEmits import, while preserving the existing disabled, class, and
readonly props.
Source: Coding guidelines
Codecov Report❌ Patch coverage is
@@ Coverage Diff @@
## main #13778 +/- ##
==========================================
+ Coverage 78.33% 78.35% +0.01%
==========================================
Files 1683 1684 +1
Lines 105867 105876 +9
Branches 37435 38102 +667
==========================================
+ Hits 82934 82957 +23
+ Misses 22342 22320 -22
- Partials 591 599 +8
Flags with carried forward coverage won't be shown. Click here to find out more.
... and 3 files with indirect coverage changes 🚀 New features to boost your workflow:
|
Summary
Replaces PrimeVue
ToggleSwitchin the main application with a shared Reka-basedSwitch, extracted from #13762.Changes
apps/desktop-uiout of scope because it is a separately built package and cannot consume the main app's local UI component directly.Review Focus
aria-readonly, but pointer and keyboard input cannot change their value.ptorchange.Validation
pnpm typecheckpnpm typecheck:browserpnpm lint— passes with pre-existing warnings onlypnpm format:checkpnpm knip