[backport core/1.49] feat(first-run-tour): run the tour from a picked template - #14637
Conversation
> **Stack — 4 of 6.** Rebuilt from scratch after the review on #13725, split into short PRs. > Based on #13290 (coachmark engine); each PR stacks on the one before it. > > 1. #14140 — engine: canvas targets for the coachmark tour > 2. #14141 — Getting Started screen + entry gating > 3. #14142 — role pins + live drift guard > 4. #14143 — the tour itself + e2e walk ← this PR > 5. #14144 — post-tour nudge > 6. #14145 — unpinned workflows (share / template links) ## Summary The tour a new user actually walks: upload, prompt, run, result. Steps come from the resolved roles, targets are canvas nodes registered with the engine, and the engine renders and advances them. The parallel overlay stack from the previous attempt is conspicuously absent. ## Changes - `TOURS.firstRun` builds real `CoachStep`s with `coachId`s and registered canvas targets, so the engine's spotlight, positioning, copy resolution and telemetry all apply. - Camera framing is a glide then a fit against a known duration. No settle detection, no watchdog — `animateToBounds` is wall-clock, so the end time is already known. Reduced motion frames instantly rather than skipping. - The Run step gates on the paywall, consuming the click so the underlying handler never queues a refused run. - Run outcomes are matched to the tour's own workflow through the execution store, so a job queued elsewhere can't speak for the tour. - The tour turns Nodes 2.0 on when it starts: every step names a `[data-node-id]`, and a new user has no `Comfy.InstalledVersion`, so the versioned default never applies and the setting reads off. If no tour then starts, the switch is undone. - A Playwright walk: fresh profile → Getting Started → pick a template → steps → Run → result. ### From the review round (`c504c6a`, `48358e6`) - A rejecting tour resolver used to escape `begin()` and leave the engine stuck at `resolving`, refusing every later start. It now resolves to no steps. - `beginTour` consulted the engine only after persisting the renderer switch, mutating the tour registry and waiting out the preview. It bails first, and undoes only a switch it made itself. - The Run-button interceptor keyed on `step.name === 'run'`, so renaming a translation key changed control flow. It reads `selfAdvancing`. - A registered tour can now carry the condition it runs under, so the first-run tour is torn down when the layout its coachmarks need goes away — same path as a trigger's, without the seen flag. - The Result step's spinner is gone. Its predicate was the same expression as the copy beside it, and it kept spinning when a run ended without a recorded status. ## Review focus No feature-layer RAF, no second overlay, no second i18n namespace, no inline telemetry — the engine owns all of it. Worth knowing: onboarding is one-shot by design. A run that fails still ends the tour; the user sees a "didn't finish" card and moves on. --------- Co-authored-by: pythongosssss <125205205+pythongosssss@users.noreply.github.com> Co-authored-by: GitHub Action <action@github.com> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Co-authored-by: AustinMroz <austin@comfy.org> Co-authored-by: bymyself <cbyrne@comfy.org>
📄 Knowledge reviewDosu skipped reviewing this PR because your organization has used its |
🎨 Storybook: ✅ Built — View Storybook🎭 Playwright: ✅ 1787 passed, 0 failed📊 Browser Reports
📦 Bundle Size
⚡ Performance Report
Show regressions
All metrics
Historical variance (last 15 runs)
Trend (last 15 commits on main)
Raw data{
"timestamp": "2026-08-04T03:57:16.583Z",
"gitSha": "da9b95afcaa429c40597d517190a5205e9c6de79",
"branch": "backport-14143-to-core-1.49",
"measurements": [
{
"name": "canvas-idle",
"durationMs": 2037.2679999999832,
"styleRecalcs": 10,
"styleRecalcDurationMs": 6.370000000000001,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 380.73,
"heapDeltaBytes": 5156364,
"heapUsedBytes": 73179756,
"domNodes": 20,
"jsHeapTotalBytes": 20566016,
"scriptDurationMs": 14.663,
"eventListeners": 6,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "canvas-idle",
"durationMs": 2013.7920000000236,
"styleRecalcs": 11,
"styleRecalcDurationMs": 7.325,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 356.128,
"heapDeltaBytes": 5083428,
"heapUsedBytes": 73284560,
"domNodes": 22,
"jsHeapTotalBytes": 20041728,
"scriptDurationMs": 11.944999999999999,
"eventListeners": 6,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "canvas-mouse-sweep",
"durationMs": 1825.0620000000026,
"styleRecalcs": 73,
"styleRecalcDurationMs": 34.182,
"layouts": 12,
"layoutDurationMs": 3.2180000000000004,
"taskDurationMs": 832.2420000000001,
"heapDeltaBytes": -18260088,
"heapUsedBytes": 49675400,
"domNodes": -280,
"jsHeapTotalBytes": 21479424,
"scriptDurationMs": 104.467,
"eventListeners": -153,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.670000000000012,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "canvas-mouse-sweep",
"durationMs": 1708.9749999999526,
"styleRecalcs": 71,
"styleRecalcDurationMs": 29.751000000000005,
"layouts": 12,
"layoutDurationMs": 3.382,
"taskDurationMs": 696.5160000000001,
"heapDeltaBytes": -18463828,
"heapUsedBytes": 49676792,
"domNodes": -282,
"jsHeapTotalBytes": 20430848,
"scriptDurationMs": 93.66799999999999,
"eventListeners": -149,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "canvas-zoom-sweep",
"durationMs": 1705.885999999964,
"styleRecalcs": 30,
"styleRecalcDurationMs": 14.135,
"layouts": 6,
"layoutDurationMs": 0.629,
"taskDurationMs": 292.76599999999996,
"heapDeltaBytes": 7786312,
"heapUsedBytes": 75877564,
"domNodes": 77,
"jsHeapTotalBytes": 20303872,
"scriptDurationMs": 15.202000000000002,
"eventListeners": 19,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "canvas-zoom-sweep",
"durationMs": 1707.0589999999584,
"styleRecalcs": 31,
"styleRecalcDurationMs": 13.708,
"layouts": 6,
"layoutDurationMs": 0.5720000000000002,
"taskDurationMs": 296.80899999999997,
"heapDeltaBytes": 7858672,
"heapUsedBytes": 75955640,
"domNodes": 78,
"jsHeapTotalBytes": 20566016,
"scriptDurationMs": 15.115,
"eventListeners": 19,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "dom-widget-clipping",
"durationMs": 524.6650000000272,
"styleRecalcs": 11,
"styleRecalcDurationMs": 6.361999999999999,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 303.146,
"heapDeltaBytes": -12447784,
"heapUsedBytes": 55834680,
"domNodes": 18,
"jsHeapTotalBytes": 21614592,
"scriptDurationMs": 47.227,
"eventListeners": 2,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000273
},
{
"name": "dom-widget-clipping",
"durationMs": 512.7930000000447,
"styleRecalcs": 12,
"styleRecalcDurationMs": 6.939000000000003,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 295.238,
"heapDeltaBytes": -12604088,
"heapUsedBytes": 55488700,
"domNodes": 20,
"jsHeapTotalBytes": 21614592,
"scriptDurationMs": 45.18,
"eventListeners": 2,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000273
},
{
"name": "large-graph-idle",
"durationMs": 2028.6419999999907,
"styleRecalcs": 9,
"styleRecalcDurationMs": 6.126999999999999,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 527.824,
"heapDeltaBytes": 8498440,
"heapUsedBytes": 67174888,
"domNodes": -282,
"jsHeapTotalBytes": 5300224,
"scriptDurationMs": 73.416,
"eventListeners": -149,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.670000000000012,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "large-graph-idle",
"durationMs": 2015.641999999957,
"styleRecalcs": 8,
"styleRecalcDurationMs": 4.732,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 520.531,
"heapDeltaBytes": 4749192,
"heapUsedBytes": 66520416,
"domNodes": -283,
"jsHeapTotalBytes": 4775936,
"scriptDurationMs": 70.33399999999999,
"eventListeners": -149,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "large-graph-pan",
"durationMs": 2077.3320000000126,
"styleRecalcs": 67,
"styleRecalcDurationMs": 12.730000000000002,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 977.3309999999999,
"heapDeltaBytes": 4709600,
"heapUsedBytes": 65019380,
"domNodes": -285,
"jsHeapTotalBytes": 4739072,
"scriptDurationMs": 323.09,
"eventListeners": -149,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "large-graph-pan",
"durationMs": 2136.08899999997,
"styleRecalcs": 70,
"styleRecalcDurationMs": 14.678000000000003,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 985.4079999999999,
"heapDeltaBytes": 25896824,
"heapUsedBytes": 85540596,
"domNodes": -283,
"jsHeapTotalBytes": 5787648,
"scriptDurationMs": 324.535,
"eventListeners": -177,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "large-graph-zoom",
"durationMs": 3107.30099999995,
"styleRecalcs": 66,
"styleRecalcDurationMs": 13.736999999999998,
"layouts": 60,
"layoutDurationMs": 7.046,
"taskDurationMs": 1214.5410000000002,
"heapDeltaBytes": -7448936,
"heapUsedBytes": 54381396,
"domNodes": -289,
"jsHeapTotalBytes": 7135232,
"scriptDurationMs": 422.619,
"eventListeners": -153,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "large-graph-zoom",
"durationMs": 3122.6009999999746,
"styleRecalcs": 65,
"styleRecalcDurationMs": 13.077000000000002,
"layouts": 60,
"layoutDurationMs": 6.925,
"taskDurationMs": 1203.402,
"heapDeltaBytes": -5014536,
"heapUsedBytes": 56576140,
"domNodes": -288,
"jsHeapTotalBytes": 7659520,
"scriptDurationMs": 416.236,
"eventListeners": -153,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "minimap-idle",
"durationMs": 2013.1129999999757,
"styleRecalcs": 7,
"styleRecalcDurationMs": 4.849999999999999,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 525.474,
"heapDeltaBytes": 7687896,
"heapUsedBytes": 68080940,
"domNodes": -285,
"jsHeapTotalBytes": 4775936,
"scriptDurationMs": 72.687,
"eventListeners": -147,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "minimap-idle",
"durationMs": 2001.4529999999695,
"styleRecalcs": 10,
"styleRecalcDurationMs": 6.862000000000002,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 520.761,
"heapDeltaBytes": 7688992,
"heapUsedBytes": 68055820,
"domNodes": -281,
"jsHeapTotalBytes": 5038080,
"scriptDurationMs": 71.79299999999999,
"eventListeners": -149,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "subgraph-dom-widget-clipping",
"durationMs": 504.71899999996594,
"styleRecalcs": 48,
"styleRecalcDurationMs": 9.573,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 312.31,
"heapDeltaBytes": -12302608,
"heapUsedBytes": 55871020,
"domNodes": 22,
"jsHeapTotalBytes": 22925312,
"scriptDurationMs": 93.265,
"eventListeners": 8,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.663333333333338,
"p95FrameDurationMs": 16.700000000000273
},
{
"name": "subgraph-dom-widget-clipping",
"durationMs": 535.8580000000757,
"styleRecalcs": 47,
"styleRecalcDurationMs": 9.120999999999999,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 335.57200000000006,
"heapDeltaBytes": -11963892,
"heapUsedBytes": 56239740,
"domNodes": 20,
"jsHeapTotalBytes": 22138880,
"scriptDurationMs": 98.08600000000001,
"eventListeners": 8,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66666666666665,
"p95FrameDurationMs": 16.700000000000273
},
{
"name": "subgraph-idle",
"durationMs": 2003.409999999974,
"styleRecalcs": 11,
"styleRecalcDurationMs": 7.840999999999999,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 368.74899999999997,
"heapDeltaBytes": 4541328,
"heapUsedBytes": 72734104,
"domNodes": 22,
"jsHeapTotalBytes": 20303872,
"scriptDurationMs": 11.226999999999999,
"eventListeners": 6,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333335,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "subgraph-idle",
"durationMs": 1997.9089999999928,
"styleRecalcs": 10,
"styleRecalcDurationMs": 6.792,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 354.11,
"heapDeltaBytes": 4842780,
"heapUsedBytes": 73016164,
"domNodes": 20,
"jsHeapTotalBytes": 20566016,
"scriptDurationMs": 10.498999999999999,
"eventListeners": 6,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "subgraph-mouse-sweep",
"durationMs": 1705.502000000024,
"styleRecalcs": 75,
"styleRecalcDurationMs": 26.837999999999997,
"layouts": 16,
"layoutDurationMs": 3.136,
"taskDurationMs": 622.846,
"heapDeltaBytes": -4793636,
"heapUsedBytes": 63445708,
"domNodes": 63,
"jsHeapTotalBytes": 21876736,
"scriptDurationMs": 70.769,
"eventListeners": 4,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.670000000000012,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "subgraph-mouse-sweep",
"durationMs": 1726.03399999997,
"styleRecalcs": 77,
"styleRecalcDurationMs": 32.111000000000004,
"layouts": 16,
"layoutDurationMs": 3.9430000000000005,
"taskDurationMs": 626.4380000000001,
"heapDeltaBytes": -4764112,
"heapUsedBytes": 63517356,
"domNodes": 66,
"jsHeapTotalBytes": 20566016,
"scriptDurationMs": 75.319,
"eventListeners": 6,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "subgraph-transition-enter",
"durationMs": 900.3060000000005,
"styleRecalcs": 19,
"styleRecalcDurationMs": 23.362000000000002,
"layouts": 15,
"layoutDurationMs": 9.078000000000001,
"taskDurationMs": 704.1310000000001,
"heapDeltaBytes": 30903796,
"heapUsedBytes": 98988544,
"domNodes": 13673,
"jsHeapTotalBytes": 15728640,
"scriptDurationMs": 20.214000000000002,
"eventListeners": 2369,
"totalBlockingTimeMs": 82,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "viewport-pan-sweep",
"durationMs": 8111.304000000018,
"styleRecalcs": 248,
"styleRecalcDurationMs": 35.75300000000001,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 3419.061,
"heapDeltaBytes": 28034340,
"heapUsedBytes": 87777072,
"domNodes": -284,
"jsHeapTotalBytes": 6311936,
"scriptDurationMs": 1044.817,
"eventListeners": -163,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "viewport-pan-sweep",
"durationMs": 8095.236,
"styleRecalcs": 249,
"styleRecalcDurationMs": 37.188,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 3397.177,
"heapDeltaBytes": 9697292,
"heapUsedBytes": 68368764,
"domNodes": -283,
"jsHeapTotalBytes": 6836224,
"scriptDurationMs": 1045.389,
"eventListeners": -131,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.660000000000036,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "vue-large-graph-idle",
"durationMs": 14264.139,
"styleRecalcs": 0,
"styleRecalcDurationMs": 0,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 14255.644999999997,
"heapDeltaBytes": -41464516,
"heapUsedBytes": 167498424,
"domNodes": -8312,
"jsHeapTotalBytes": -10424320,
"scriptDurationMs": 414.86699999999996,
"eventListeners": -16389,
"totalBlockingTimeMs": 0,
"frameDurationMs": 17.780000000000047,
"p95FrameDurationMs": 16.80000000000291
},
{
"name": "vue-large-graph-idle",
"durationMs": 14272.905000000037,
"styleRecalcs": 0,
"styleRecalcDurationMs": 0,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 14260.220999999998,
"heapDeltaBytes": -42689768,
"heapUsedBytes": 167845960,
"domNodes": -8312,
"jsHeapTotalBytes": -11214848,
"scriptDurationMs": 426.36000000000007,
"eventListeners": -16393,
"totalBlockingTimeMs": 0,
"frameDurationMs": 17.776666666666642,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "vue-large-graph-pan",
"durationMs": 16855.624000000033,
"styleRecalcs": 114,
"styleRecalcDurationMs": 13.47100000000001,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 16836.943,
"heapDeltaBytes": -39764680,
"heapUsedBytes": 177240584,
"domNodes": -8312,
"jsHeapTotalBytes": -17051648,
"scriptDurationMs": 653.685,
"eventListeners": -16385,
"totalBlockingTimeMs": 10,
"frameDurationMs": 17.223333333333358,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "vue-large-graph-pan",
"durationMs": 17073.576,
"styleRecalcs": 115,
"styleRecalcDurationMs": 14.124999999999998,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 17053.354,
"heapDeltaBytes": -49857100,
"heapUsedBytes": 177025572,
"domNodes": -8312,
"jsHeapTotalBytes": -9183232,
"scriptDurationMs": 705.549,
"eventListeners": -16387,
"totalBlockingTimeMs": 94,
"frameDurationMs": 17.223333333333358,
"p95FrameDurationMs": 16.80000000000291
},
{
"name": "workflow-execution",
"durationMs": 430.2240000000097,
"styleRecalcs": 17,
"styleRecalcDurationMs": 20.497,
"layouts": 4,
"layoutDurationMs": 1.252,
"taskDurationMs": 111.38300000000001,
"heapDeltaBytes": -15438724,
"heapUsedBytes": 51819684,
"domNodes": 136,
"jsHeapTotalBytes": 7458816,
"scriptDurationMs": 8.443,
"eventListeners": 97,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "workflow-execution",
"durationMs": 441.983999999934,
"styleRecalcs": 15,
"styleRecalcDurationMs": 16.38,
"layouts": 2,
"layoutDurationMs": 0.42899999999999994,
"taskDurationMs": 94.582,
"heapDeltaBytes": -15829332,
"heapUsedBytes": 51277924,
"domNodes": 127,
"jsHeapTotalBytes": 7983104,
"scriptDurationMs": 8.104,
"eventListeners": 99,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.669999999999998,
"p95FrameDurationMs": 16.800000000000182
}
]
} |
Codecov Report❌ Patch coverage is
@@ Coverage Diff @@
## core/1.49 #14637 +/- ##
=============================================
+ Coverage 78.47% 78.52% +0.05%
=============================================
Files 1781 1784 +3
Lines 103859 104084 +225
Branches 31868 31926 +58
=============================================
+ Hits 81502 81734 +232
+ Misses 21926 21919 -7
Partials 431 431
Flags with carried forward coverage won't be shown. Click here to find out more.
🚀 New features to boost your workflow:
|
christian-byrne
left a comment
There was a problem hiding this comment.
Approving — clean backport of #14143, which I reviewed and approved on main.
This PR was failing 8 checks earlier (setup, and lint/scan/fonts/perf cascading from it). That was not a defect in the backport. The auto-backport bot cherry-picks each PR onto the release branch independently, so this branch carried #14143's code with none of #14142's underneath it — it merged textually and then could not build. Now that #14634/#14635 have landed, I merged the release branch in and CI is green: 49 success, 8 skipped, zero failures.
Same story applies to the #14144 and #14145 backports, whose auto-attempts failed for exactly this reason. I'll re-trigger those once this pair lands.
For confidence rather than hope: a dry run cherry-picking all four onto core/1.49 and cloud/1.49 in stack order produced zero conflicts, the resulting tour code matches main byte-for-byte, and the locale merge loses no keys while correctly not dragging in main-only strings from unrelated PRs.
Backport of #14143 to
core/1.49Automatically created by backport workflow.