feat(cloud): redesign the cloud onboarding survey#13518
Conversation
Lead with an intent question that branches into a builder follow-up, trim familiarity to three levels, group the acquisition source with a social platform branch, and add free-text "other" on intent and source. Options carry an optional icon.
Replace the radio and checkbox rows with selectable cards, auto-advance on single-select, and render the survey on a dark surface so it reads on the onboarding background.
Widen intent to allow a single value and add the new field ids, keeping the legacy fields so existing analytics stay intact.
Assert card selection, single-click auto-advance, branch reveal and hiding, "other" free-text gating, and interaction-gated errors, plus the default schema's branching.
Force the flag on, prefer the frontend schema over the backend, and bypass the completed check so the redesign is visible during review. Revert before merge.
- Retheme cards, inputs, and buttons to comfy-canvas tokens; animate step height and cross-fade transitions; hide the hero on survey routes - Branch source into source_social and gate focus behind builder intents; surface field errors only after interaction
🎭 Playwright: ✅ 1690 passed, 0 failed · 3 flaky📊 Browser Reports
🎨 Storybook: ✅ Built — View Storybook📦 Bundle: 7.78 MB gzip 🔴 +1.29 kBDetailsSummary
Category Glance App Entry Points — 47.8 kB (baseline 47.8 kB) • ⚪ 0 BMain entry bundles and manifests
Status: 1 added / 1 removed Graph Workspace — 1.27 MB (baseline 1.27 MB) • ⚪ 0 BGraph editor runtime, canvas, workflow orchestration
Status: 1 added / 1 removed Views & Navigation — 104 kB (baseline 98 kB) • 🔴 +5.83 kBTop-level views, pages, and routed surfaces
Status: 9 added / 9 removed / 3 unchanged Panels & Settings — 546 kB (baseline 546 kB) • ⚪ 0 BConfiguration panels, inspectors, and settings screens
Status: 11 added / 11 removed / 16 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 — 117 kB (baseline 117 kB) • ⚪ 0 BModals, dialogs, drawers, and in-app editors
Status: 4 added / 4 removed / 1 unchanged UI Components — 57.5 kB (baseline 57.5 kB) • ⚪ 0 BReusable component library chunks
Status: 5 added / 5 removed / 8 unchanged Data & Services — 270 kB (baseline 270 kB) • ⚪ 0 BStores, services, APIs, and repositories
Status: 13 added / 13 removed / 3 unchanged Utilities & Hooks — 3.38 MB (baseline 3.38 MB) • ⚪ 0 BHelpers, composables, and utility bundles
Status: 19 added / 19 removed / 14 unchanged Vendor & Third-Party — 15.3 MB (baseline 15.3 MB) • ⚪ 0 BExternal libraries and shared vendor chunks Status: 16 unchanged Other — 11.7 MB (baseline 11.7 MB) • 🟢 -13 BBundles that do not match a named category
Status: 75 added / 75 removed / 90 unchanged ⚡ Performance Report
Show regressions
All metrics
Historical variance (last 15 runs)
Trend (last 15 commits on main)
Raw data{
"timestamp": "2026-07-08T18:57:44.394Z",
"gitSha": "11ee427dab356474d9916573ffa6658e93ec144b",
"branch": "feat/onboarding-survey-redesign",
"measurements": [
{
"name": "canvas-idle",
"durationMs": 2047.1029999999928,
"styleRecalcs": 8,
"styleRecalcDurationMs": 7.5809999999999995,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 557.2719999999999,
"heapDeltaBytes": -1032068,
"heapUsedBytes": 57050672,
"domNodes": 16,
"jsHeapTotalBytes": 25427968,
"scriptDurationMs": 29.237,
"eventListeners": 6,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333335,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "canvas-idle",
"durationMs": 2088.0279999999516,
"styleRecalcs": 10,
"styleRecalcDurationMs": 11.981000000000002,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 511.178,
"heapDeltaBytes": -7468288,
"heapUsedBytes": 62108952,
"domNodes": -278,
"jsHeapTotalBytes": 18759680,
"scriptDurationMs": 27.132,
"eventListeners": -199,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "canvas-mouse-sweep",
"durationMs": 1872.726,
"styleRecalcs": 75,
"styleRecalcDurationMs": 44.22,
"layouts": 12,
"layoutDurationMs": 3.8300000000000005,
"taskDurationMs": 915.749,
"heapDeltaBytes": -12715572,
"heapUsedBytes": 56802304,
"domNodes": -243,
"jsHeapTotalBytes": 20070400,
"scriptDurationMs": 132.759,
"eventListeners": -197,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "canvas-mouse-sweep",
"durationMs": 1842.2180000000026,
"styleRecalcs": 77,
"styleRecalcDurationMs": 40.63399999999999,
"layouts": 12,
"layoutDurationMs": 3.7939999999999996,
"taskDurationMs": 819.2900000000001,
"heapDeltaBytes": -7604520,
"heapUsedBytes": 55658912,
"domNodes": 60,
"jsHeapTotalBytes": 25448448,
"scriptDurationMs": 130.174,
"eventListeners": 4,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "canvas-zoom-sweep",
"durationMs": 1774.4000000000142,
"styleRecalcs": 32,
"styleRecalcDurationMs": 21.453,
"layouts": 6,
"layoutDurationMs": 0.9140000000000001,
"taskDurationMs": 443.06100000000004,
"heapDeltaBytes": 4537432,
"heapUsedBytes": 56514176,
"domNodes": -243,
"jsHeapTotalBytes": 1699840,
"scriptDurationMs": 28.584,
"eventListeners": -186,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.699999999999818
},
{
"name": "canvas-zoom-sweep",
"durationMs": 1724.6539999999868,
"styleRecalcs": 32,
"styleRecalcDurationMs": 18.673000000000002,
"layouts": 6,
"layoutDurationMs": 0.5690000000000001,
"taskDurationMs": 355.517,
"heapDeltaBytes": 5087428,
"heapUsedBytes": 62943896,
"domNodes": 78,
"jsHeapTotalBytes": 18874368,
"scriptDurationMs": 25.035,
"eventListeners": 19,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333335,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "dom-widget-clipping",
"durationMs": 639.6750000000111,
"styleRecalcs": 12,
"styleRecalcDurationMs": 8.664,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 413.76099999999997,
"heapDeltaBytes": -14003360,
"heapUsedBytes": 55630704,
"domNodes": 20,
"jsHeapTotalBytes": 20205568,
"scriptDurationMs": 68.853,
"eventListeners": 2,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.699999999999818
},
{
"name": "dom-widget-clipping",
"durationMs": 574.8229999999808,
"styleRecalcs": 12,
"styleRecalcDurationMs": 9.170000000000002,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 364.91200000000003,
"heapDeltaBytes": 7419924,
"heapUsedBytes": 66265444,
"domNodes": 20,
"jsHeapTotalBytes": 19660800,
"scriptDurationMs": 61.126,
"eventListeners": 2,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "large-graph-idle",
"durationMs": 2060.182999999995,
"styleRecalcs": 7,
"styleRecalcDurationMs": 7.2090000000000005,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 724.4649999999999,
"heapDeltaBytes": -665024,
"heapUsedBytes": 59453560,
"domNodes": -276,
"jsHeapTotalBytes": -1093632,
"scriptDurationMs": 123.843,
"eventListeners": -197,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "large-graph-idle",
"durationMs": 2057.6449999999795,
"styleRecalcs": 8,
"styleRecalcDurationMs": 7.943000000000002,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 664.79,
"heapDeltaBytes": -26119908,
"heapUsedBytes": 58310716,
"domNodes": -273,
"jsHeapTotalBytes": 2957312,
"scriptDurationMs": 117.31700000000001,
"eventListeners": -197,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "large-graph-pan",
"durationMs": 2133.262000000002,
"styleRecalcs": 68,
"styleRecalcDurationMs": 17.769,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 1366.305,
"heapDeltaBytes": 14559756,
"heapUsedBytes": 83733692,
"domNodes": -275,
"jsHeapTotalBytes": -856064,
"scriptDurationMs": 494.91999999999996,
"eventListeners": -197,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.699999999999818
},
{
"name": "large-graph-pan",
"durationMs": 2090.4620000000023,
"styleRecalcs": 67,
"styleRecalcDurationMs": 16.165,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 1106.487,
"heapDeltaBytes": 2520376,
"heapUsedBytes": 55291348,
"domNodes": -279,
"jsHeapTotalBytes": 0,
"scriptDurationMs": 394.75800000000004,
"eventListeners": -197,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333335,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "large-graph-zoom",
"durationMs": 3381.046000000026,
"styleRecalcs": 65,
"styleRecalcDurationMs": 21.849999999999998,
"layouts": 60,
"layoutDurationMs": 8.552999999999999,
"taskDurationMs": 1572.241,
"heapDeltaBytes": 17414676,
"heapUsedBytes": 72515968,
"domNodes": 12,
"jsHeapTotalBytes": 6815744,
"scriptDurationMs": 581.265,
"eventListeners": 8,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666636,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "large-graph-zoom",
"durationMs": 3133.5920000000215,
"styleRecalcs": 63,
"styleRecalcDurationMs": 17.753999999999998,
"layouts": 60,
"layoutDurationMs": 7.813000000000001,
"taskDurationMs": 1400.0529999999999,
"heapDeltaBytes": 13757484,
"heapUsedBytes": 67403328,
"domNodes": -285,
"jsHeapTotalBytes": 0,
"scriptDurationMs": 525.1289999999999,
"eventListeners": 8,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "minimap-idle",
"durationMs": 2036.9949999999903,
"styleRecalcs": 6,
"styleRecalcDurationMs": 5.8240000000000025,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 728.899,
"heapDeltaBytes": -2408740,
"heapUsedBytes": 59694076,
"domNodes": -276,
"jsHeapTotalBytes": -569344,
"scriptDurationMs": 126.34500000000001,
"eventListeners": -197,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "minimap-idle",
"durationMs": 2024.6949999999515,
"styleRecalcs": 7,
"styleRecalcDurationMs": 8.607,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 657.6560000000001,
"heapDeltaBytes": -10423044,
"heapUsedBytes": 59082200,
"domNodes": -275,
"jsHeapTotalBytes": -1323008,
"scriptDurationMs": 116.17999999999999,
"eventListeners": -197,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "subgraph-dom-widget-clipping",
"durationMs": 704.0959999999927,
"styleRecalcs": 45,
"styleRecalcDurationMs": 13.155000000000001,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 469.68199999999996,
"heapDeltaBytes": -22479684,
"heapUsedBytes": 47227092,
"domNodes": -286,
"jsHeapTotalBytes": 5652480,
"scriptDurationMs": 129.025,
"eventListeners": -195,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "subgraph-dom-widget-clipping",
"durationMs": 639.4559999999956,
"styleRecalcs": 48,
"styleRecalcDurationMs": 13.517,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 445.56100000000004,
"heapDeltaBytes": -23114372,
"heapUsedBytes": 46466764,
"domNodes": -277,
"jsHeapTotalBytes": 6438912,
"scriptDurationMs": 126.09300000000002,
"eventListeners": -195,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "subgraph-idle",
"durationMs": 2011.3870000000134,
"styleRecalcs": 8,
"styleRecalcDurationMs": 8.882,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 508.3310000000001,
"heapDeltaBytes": -3089696,
"heapUsedBytes": 49171056,
"domNodes": -292,
"jsHeapTotalBytes": 17690624,
"scriptDurationMs": 24.287000000000003,
"eventListeners": -199,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "subgraph-idle",
"durationMs": 2067.4949999999512,
"styleRecalcs": 9,
"styleRecalcDurationMs": 8.978000000000002,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 457.67300000000006,
"heapDeltaBytes": -8293016,
"heapUsedBytes": 61300184,
"domNodes": -279,
"jsHeapTotalBytes": 19546112,
"scriptDurationMs": 22.788,
"eventListeners": -199,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.699999999999818
},
{
"name": "subgraph-mouse-sweep",
"durationMs": 1753.0269999999746,
"styleRecalcs": 75,
"styleRecalcDurationMs": 42.273,
"layouts": 16,
"layoutDurationMs": 5.015,
"taskDurationMs": 830.6020000000001,
"heapDeltaBytes": -6781592,
"heapUsedBytes": 45386944,
"domNodes": -265,
"jsHeapTotalBytes": 16379904,
"scriptDurationMs": 106.21300000000001,
"eventListeners": -199,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "subgraph-mouse-sweep",
"durationMs": 1709.5920000000433,
"styleRecalcs": 76,
"styleRecalcDurationMs": 39.919999999999995,
"layouts": 16,
"layoutDurationMs": 5.199,
"taskDurationMs": 733.093,
"heapDeltaBytes": 18899640,
"heapUsedBytes": 76209504,
"domNodes": 62,
"jsHeapTotalBytes": 19660800,
"scriptDurationMs": 100.097,
"eventListeners": 4,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.699999999999818
},
{
"name": "subgraph-transition-enter",
"durationMs": 1416.718000000003,
"styleRecalcs": 19,
"styleRecalcDurationMs": 35.165,
"layouts": 15,
"layoutDurationMs": 16.823999999999998,
"taskDurationMs": 885.8699999999997,
"heapDeltaBytes": 30077732,
"heapUsedBytes": 102096064,
"domNodes": 13673,
"jsHeapTotalBytes": 16515072,
"scriptDurationMs": 43.17999999999999,
"eventListeners": 2533,
"totalBlockingTimeMs": 176,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "viewport-pan-sweep",
"durationMs": 8351.459000000033,
"styleRecalcs": 251,
"styleRecalcDurationMs": 61.669000000000004,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 4596.237,
"heapDeltaBytes": -547000,
"heapUsedBytes": 71501488,
"domNodes": 18,
"jsHeapTotalBytes": 16834560,
"scriptDurationMs": 1457.136,
"eventListeners": 22,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "viewport-pan-sweep",
"durationMs": 8252.208999999993,
"styleRecalcs": 250,
"styleRecalcDurationMs": 55.239999999999995,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 4225.042,
"heapDeltaBytes": 10272836,
"heapUsedBytes": 70410180,
"domNodes": -275,
"jsHeapTotalBytes": 3043328,
"scriptDurationMs": 1382.878,
"eventListeners": -183,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "vue-large-graph-idle",
"durationMs": 13174.312999999984,
"styleRecalcs": 0,
"styleRecalcDurationMs": 0,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 13148.891,
"heapDeltaBytes": -13608732,
"heapUsedBytes": 182831344,
"domNodes": -3300,
"jsHeapTotalBytes": 2359296,
"scriptDurationMs": 609.14,
"eventListeners": -16366,
"totalBlockingTimeMs": 0,
"frameDurationMs": 17.776666666666642,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "vue-large-graph-idle",
"durationMs": 12843.637000000057,
"styleRecalcs": 0,
"styleRecalcDurationMs": 0,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 12825.444,
"heapDeltaBytes": -55275596,
"heapUsedBytes": 167032532,
"domNodes": -3301,
"jsHeapTotalBytes": 2592768,
"scriptDurationMs": 582.225,
"eventListeners": -16381,
"totalBlockingTimeMs": 0,
"frameDurationMs": 18.333333333333332,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "vue-large-graph-pan",
"durationMs": 16364.684000000012,
"styleRecalcs": 93,
"styleRecalcDurationMs": 23.262999999999977,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 16333.34,
"heapDeltaBytes": -42326684,
"heapUsedBytes": 170574904,
"domNodes": -3301,
"jsHeapTotalBytes": 10981376,
"scriptDurationMs": 1025.836,
"eventListeners": -16375,
"totalBlockingTimeMs": 7,
"frameDurationMs": 17.223333333333358,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "vue-large-graph-pan",
"durationMs": 15359.20699999997,
"styleRecalcs": 77,
"styleRecalcDurationMs": 19.63100000000001,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 15332.635000000002,
"heapDeltaBytes": -41349984,
"heapUsedBytes": 155423924,
"domNodes": -3300,
"jsHeapTotalBytes": 12320768,
"scriptDurationMs": 865.1609999999998,
"eventListeners": -16364,
"totalBlockingTimeMs": 12,
"frameDurationMs": 17.223333333333237,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "workflow-execution",
"durationMs": 485.29800000000023,
"styleRecalcs": 17,
"styleRecalcDurationMs": 26.704,
"layouts": 5,
"layoutDurationMs": 1.563,
"taskDurationMs": 135.768,
"heapDeltaBytes": 5184664,
"heapUsedBytes": 63373168,
"domNodes": 166,
"jsHeapTotalBytes": 4980736,
"scriptDurationMs": 19.576,
"eventListeners": 69,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66999999999998,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "workflow-execution",
"durationMs": 540.6690000000935,
"styleRecalcs": 17,
"styleRecalcDurationMs": 29.987000000000002,
"layouts": 5,
"layoutDurationMs": 1.428,
"taskDurationMs": 184.98899999999998,
"heapDeltaBytes": -2503552,
"heapUsedBytes": 51126892,
"domNodes": -138,
"jsHeapTotalBytes": 126976,
"scriptDurationMs": 20.698,
"eventListeners": -132,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
}
]
} |
📝 WalkthroughWalkthroughUpdates the cloud onboarding survey with a new schema, field behavior, form validation flow, route-based hero hiding, refreshed layout styling, and revised English localization strings. ChangesCloud Onboarding Survey Redesign
Estimated code review effort: 4 (Complex) | ~60 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 |
Codecov Report❌ Patch coverage is @@ Coverage Diff @@
## main #13518 +/- ##
==========================================
+ Coverage 77.50% 77.96% +0.45%
==========================================
Files 1650 1650
Lines 92928 97419 +4491
Branches 32116 34096 +1980
==========================================
+ Hits 72021 75948 +3927
- Misses 20236 20760 +524
- Partials 671 711 +40
Flags with carried forward coverage won't be shown. Click here to find out more.
... and 133 files with indirect coverage changes 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
🔍 Cursor Review — Consolidated panel
Triggered by @MaanilVerma.
Found 6 finding(s).
| Severity | Count |
|---|---|
| 🟠 High | 1 |
| 🟡 Medium | 2 |
| 🟢 Low | 2 |
| ⚪ Nit | 1 |
Panel: 8/8 reviewers contributed findings.
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 (1)
src/platform/cloud/onboarding/survey/DynamicSurveyForm.vue (1)
212-224: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win"Other" required-field validation ignores multi-select values.
value === 'other'only matches scalar values. For amultifield withallowOther,valueis an array, so this check never triggers and the function falls through toreturn true, letting the user advance/submit while 'other' is selected but the required free-text is empty.DynamicSurveyField.vue'sisOtherSelectedalready handles this correctly by checkingArray.isArray(modelValue) ? modelValue.includes('other') : modelValue === 'other', confirming multi+other is a supported combination that this validity check misses.🐛 Proposed fix
- if (field.allowOther && field.otherFieldId && value === 'other') { + const selectedOther = Array.isArray(value) + ? value.includes('other') + : value === 'other' + if (field.allowOther && field.otherFieldId && selectedOther) { const other = values[field.otherFieldId] return typeof other === 'string' && other.trim().length > 0 }Also worth adding a
multi+allowOthertest case toDynamicSurveyForm.test.tsto cover this path going forward.🤖 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/platform/cloud/onboarding/survey/DynamicSurveyForm.vue` around lines 212 - 224, The validation in DynamicSurveyForm.vue’s isCurrentValid only checks value === 'other', so it misses the multi-select + allowOther case where the value is an array and the required free-text can be bypassed. Update the check to detect "other" for both scalar and array values, consistent with DynamicSurveyField.vue’s isOtherSelected logic, and then require the otherFieldId text when present. Add or update a DynamicSurveyForm.test.ts case covering multi plus allowOther to verify the free-text is enforced before advancing.
🤖 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/platform/cloud/onboarding/survey/DynamicSurveyForm.vue`:
- Around line 40-47: The inline event handlers in DynamicSurveyForm.vue are
calling the async onFieldChange function without handling its returned promise,
which violates the floating-promises guideline. Update the `@update`:model-value
and `@update`:other-value handlers to explicitly handle the promise, either by
awaiting the call in an async handler or by intentionally voiding it. Use the
onFieldChange helper and the currentField.id/currentField.otherFieldId wiring as
the place to fix this.
---
Outside diff comments:
In `@src/platform/cloud/onboarding/survey/DynamicSurveyForm.vue`:
- Around line 212-224: The validation in DynamicSurveyForm.vue’s isCurrentValid
only checks value === 'other', so it misses the multi-select + allowOther case
where the value is an array and the required free-text can be bypassed. Update
the check to detect "other" for both scalar and array values, consistent with
DynamicSurveyField.vue’s isOtherSelected logic, and then require the
otherFieldId text when present. Add or update a DynamicSurveyForm.test.ts case
covering multi plus allowOther to verify the free-text is enforced before
advancing.
🪄 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: a398a5ca-9968-49a5-864f-953d96c92405
📒 Files selected for processing (14)
src/locales/en/main.jsonsrc/platform/cloud/onboarding/CloudSurveyView.vuesrc/platform/cloud/onboarding/components/CloudTemplate.vuesrc/platform/cloud/onboarding/components/CloudTemplateFooter.vuesrc/platform/cloud/onboarding/onboardingCloudRoutes.tssrc/platform/cloud/onboarding/survey/DynamicSurveyField.test.tssrc/platform/cloud/onboarding/survey/DynamicSurveyField.vuesrc/platform/cloud/onboarding/survey/DynamicSurveyForm.test.tssrc/platform/cloud/onboarding/survey/DynamicSurveyForm.vuesrc/platform/cloud/onboarding/survey/defaultSurveySchema.tssrc/platform/cloud/onboarding/survey/surveySchema.test.tssrc/platform/cloud/onboarding/survey/surveySchema.tssrc/platform/remoteConfig/types.tssrc/platform/telemetry/types.ts
- Stop auto-advancing on a cleared single-select and re-clamp stepIndex so a backend showWhen that hides an earlier field can't strand an empty step - Detect "other" for multi-select too across validity, schema, and payload, and reject whitespace-only free-text
…Comfy-Org/ComfyUI_frontend into feat/onboarding-survey-redesign
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
src/locales/en/main.json (1)
3127-3127: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick winReplace or remove the dev placeholder string.
"placeholder": "Survey questions placeholder"is clearly a leftover development placeholder, not a production-ready user-facing string. If the key is unused, remove it; if it is consumed by a component, replace the value with real copy.🤖 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/locales/en/main.json` at line 3127, The locale entry for the Survey questions placeholder is a leftover dev string and should not ship as-is. Check where the placeholder key is used in the UI, and either remove the key from the en/main.json messages if it is unused or replace it with real user-facing copy if it is consumed by a component; use the existing locale key name to locate all references before updating.src/platform/cloud/onboarding/survey/DynamicSurveyField.vue (1)
138-142: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winReuse
isOtherValuefrom./surveySchemainstead of duplicating the logic.
isOtherSelectedreimplements the exact array/string"other"check thatisOtherValuealready exports from./surveySchema. Since the component already importsOTHER_TEXT_MAX_LENGTHfrom that module, importingisOtherValuetoo keeps the detection logic in a single place and prevents drift if the sentinel ever changes.♻️ Proposed refactor
import { OTHER_TEXT_MAX_LENGTH } from './surveySchema' +import { isOtherValue, OTHER_TEXT_MAX_LENGTH } from './surveySchema'const isOtherSelected = computed(() => - Array.isArray(modelValue) - ? modelValue.includes('other') - : modelValue === 'other' + isOtherValue(modelValue) )If
isOtherValue's type signature doesn't acceptundefined, add a guard:const isOtherSelected = computed(() => - Array.isArray(modelValue) - ? modelValue.includes('other') - : modelValue === 'other' + modelValue != null && isOtherValue(modelValue) )🤖 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/platform/cloud/onboarding/survey/DynamicSurveyField.vue` around lines 138 - 142, The `isOtherSelected` computed in `DynamicSurveyField.vue` duplicates the `"other"` detection logic already exported by `isOtherValue` in `./surveySchema`. Replace the inline array/string check with `isOtherValue` and import it alongside `OTHER_TEXT_MAX_LENGTH` so the component uses the shared helper; if `isOtherValue` does not accept `undefined`, add a small guard before calling it.
🤖 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.
Outside diff comments:
In `@src/locales/en/main.json`:
- Line 3127: The locale entry for the Survey questions placeholder is a leftover
dev string and should not ship as-is. Check where the placeholder key is used in
the UI, and either remove the key from the en/main.json messages if it is unused
or replace it with real user-facing copy if it is consumed by a component; use
the existing locale key name to locate all references before updating.
In `@src/platform/cloud/onboarding/survey/DynamicSurveyField.vue`:
- Around line 138-142: The `isOtherSelected` computed in
`DynamicSurveyField.vue` duplicates the `"other"` detection logic already
exported by `isOtherValue` in `./surveySchema`. Replace the inline array/string
check with `isOtherValue` and import it alongside `OTHER_TEXT_MAX_LENGTH` so the
component uses the shared helper; if `isOtherValue` does not accept `undefined`,
add a small guard before calling it.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: a45eddac-95a5-43f7-abdb-e51e6a39b1e4
📒 Files selected for processing (4)
src/locales/en/main.jsonsrc/platform/cloud/onboarding/survey/DynamicSurveyField.vuesrc/platform/cloud/onboarding/survey/surveySchema.test.tssrc/platform/cloud/onboarding/survey/surveySchema.ts
| "describeAnswer": "Please describe your answer.", | ||
| "answerTooLong": "Please keep your answer under {max} characters." | ||
| }, | ||
| "options": { |
There was a problem hiding this comment.
Is my read right: this removes the v2 survey label/option keys while OnboardingSurvey is still server-driven and backend-overridable? Could we keep the old usage / familiarity label and option translations, or require backend configs to ship literal labels, so a stale/rollback v2 schema does not render raw IDs like usage, personal, or familiarity through the fallback in DynamicSurveyField.vue?
Visual context: #13518 (comment)


Summary
New users get a cleaner, faster onboarding survey: one question per screen on tappable cards that advance the moment you pick an answer, with follow-up questions that appear only when they're relevant. The questions themselves were reworked to learn what people actually want to do with ComfyUI.
Changes
What
Breaking — None. The remote-config survey schema stays backend-overridable, hidden branch answers are zeroed in the submitted payload, and the telemetry field names line up 1:1 with the schema. The backend dynamic config already ships the matching version-3 schema.
Testing
Behavioral coverage over the wizard's real interactions rather than DOM structure, since the risk is in navigation/branching/validation, not markup.
vue-i18nis mounted for real with the actual locale file so tests assert on rendered copy.hasNonEmptyValuetruth table.Gates:
vue-tsctypecheck clean; eslint/oxfmt clean on touched files; survey suite green (68 tests across the three files). Manual: run the cloud onboarding flow, pick a workflow/apps intent to confirm the "building" follow-up, pick social to confirm the platform follow-up, and verify an empty "other" blocks advancing.Screen Recording
Screen.Recording.2026-07-08.at.9.51.19.PM.mov