## Summary
Fix cloud login page showing only the splash loader (wave SVG) instead
of the login form when the user is not authenticated.
## Changes
- **What**: Remove splash loader on `CloudLayoutView` mount. Cloud
onboarding pages do not use workspace initialization, so the
`workspaceStore.spinner` transition (`true→false`) that normally removes
the splash never occurs — leaving it visible indefinitely.
Co-authored-by: Amp <amp@ampcode.com>
<img width="842" height="488" alt="스크린샷 2026-03-07 오후 9 39 20"
src="https://github.com/user-attachments/assets/9ac8bfcd-c882-4661-851f-b08838d4fed1"
/>
## Summary
- Add Storybook stories for WidgetInputText, WidgetTextarea, and
ScrubableNumberInput
- Reorganize story titles under `Components/Input/` to align with Figma
design system
- Fix PrimeIcons not rendering in Storybook (caused by
`[&_*]:!font-inter` override)
- Fix knip unused export warnings (dead code removal + workspace config)
## Test plan
- [ ] Run `pnpm storybook` and verify Components/Input/InputText stories
render
- [ ] Verify Components/Input/TextArea stories render with label and
copy button
- [ ] Verify Components/Input/Number stories render with -/+ icons
- [ ] Toggle Storybook theme between Light/Dark and confirm Number story
adapts
- [ ] Verify existing Button stories still render correctly
🤖 Generated with [Claude Code](https://claude.com/claude-code)
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-9527-feat-add-text-widget-stories-and-Number-input-stories-31c6d73d3650817ba351cdef26a356c8)
by [Unito](https://www.unito.io)
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
## Summary
Replace legacy PrimeVue `AutoCompletePlus` with a new
`SearchAutocomplete` component built on Reka UI, matching the
`SearchInput` design system.
## Changes
- **What**: Add `SearchAutocomplete` component extending `SearchInput`
with dropdown suggestions, IME composition handling, and generic typed
`optionLabel` support. Replace `AutoCompletePlus` usage in
`ManagerDialog`.
- **Dependencies**: None (uses existing Reka UI Combobox primitives)
## Review Focus
- `SearchAutocomplete` feature parity with the replaced
`AutoCompletePlus` (suggestions, option selection, IME handling)
- Dropdown styling and positioning via Reka UI `ComboboxContent`
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-9645-feat-replace-PrimeVue-AutoComplete-with-SearchAutocomplete-in-ManagerDialog-31e6d73d36508117ba0bef3d30dd0863)
by [Unito](https://www.unito.io)
---------
Co-authored-by: Amp <amp@ampcode.com>
## Summary
Add support for deploying full ephemeral preview environments from
frontend PRs. This is the frontend-side half — it sends `pr_number` and
`variant` (cpu/gpu) in the dispatch payload, and adds a cleanup dispatch
on PR close/unlabel.
### Changes
- **`cloud-dispatch-build.yaml`** — Add `pr_number` and `variant` to the
`frontend-asset-build` dispatch payload. Variant is derived from which
preview label triggered the event (`preview-cpu` → cpu, else gpu).
- **`cloud-dispatch-cleanup.yaml`** (new) — Fire-and-forget dispatch of
`frontend-preview-cleanup` to the cloud repo when a frontend PR is
closed or has its preview label removed. Enables synchronized teardown.
### Companion PR
Cloud-side: Comfy-Org/cloud (creates the `deploy-frontend-preview` job,
extends the reconciler)
### How it works
1. Label a frontend PR with `preview`, `preview-cpu`, or `preview-gpu`
2. Assets build and upload to GCS (existing flow)
3. Cloud deploys a full ephemeral env at `fe-pr-{N}.testenvs.comfy.org`
using all `:main` service tags
4. Subsequent pushes update the frontend SHA via AppSet upsert
5. On close/unlabel, cleanup dispatch triggers immediate teardown
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-9715-feat-dispatch-frontend-PR-preview-environments-to-cloud-31f6d73d3650819da1b5ca5ce419e06e)
by [Unito](https://www.unito.io)
- Buttons are marked as `touch-manipulation` so double-tapping on them
doesn't initiate a zoom.
- Move scrubable inputs to usePointerSwipe
- Strangely, swipe direction was inverted on mobile. This solves the
issue and simplifies code
- Moves event handlers into the scrubbable input component
- Make the slightly bigger buttons only apply when on mobile.
- Updates the workflows dropdown to have a check by the activeWorkflow
and truncate workflow names
- Displays dropzones (for the image preview) on mobile, but disables the
prompt to drag and drop an image if none is selected.
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-9686-Mobile-input-tweaks-31f6d73d3650811d9025d0cd1ac58534)
by [Unito](https://www.unito.io)
## Summary
Fix HoneyToast footer content (text + buttons) being squished when
collapsed due to `sm:w-min` on the outer container.
## Changes
- **What**: Replace `sm:w-min sm:min-w-0` with state-aware sizing
(`sm:w-fit` when collapsed, `sm:w-[max(400px,40vw)]` when expanded) on
the HoneyToast container. Add `gap-4` between footer text and buttons in
ManagerProgressToast, `min-w-0` on text area, and `shrink-0` on button
area to prevent overlap.
## Review Focus
- HoneyToast is used in 3 places: ManagerProgressToast,
ModelImportProgressDialog, AssetExportProgressDialog. The change moves
width control from the inner content div to the outer container, which
should have no negative impact on the other two consumers.
## As-is
<img width="481" height="146" alt="스크린샷 2026-03-10 오전 10 40 52"
src="https://github.com/user-attachments/assets/b5e12e20-23ea-4f11-9778-ad4e6c10a425"
/>
## To-be
<img width="506" height="62" alt="스크린샷 2026-03-10 오후 1 46 18"
src="https://github.com/user-attachments/assets/f2b7963d-eedb-4885-bc57-f8b377962e92"
/>
<img width="630" height="83" alt="스크린샷 2026-03-10 오후 1 46 10"
src="https://github.com/user-attachments/assets/e9c35f1c-3441-4fb2-8fa4-f66b7d53b3e5"
/>
<img width="683" height="103" alt="스크린샷 2026-03-10 오후 1 46 02"
src="https://github.com/user-attachments/assets/afb94a16-cfba-4da9-8676-35f4d3133b57"
/>
## Summary
`loadApiJson` early-returns when missing node types are detected,
preventing the entire API-format workflow from loading onto the canvas.
## Changes
- **What**: Remove early `return` in `loadApiJson` so missing nodes are
skipped while the rest of the workflow loads normally, consistent with
how `loadGraphData` handles missing nodes in standard workflow format.
## Review Focus
The existing code already handles missing nodes gracefully:
- `LiteGraph.createNode()` returns `null` for unregistered types
- `if (!node) continue` skips missing nodes during graph construction
- `if (!fromNode) continue` skips connections to missing nodes
- `if (!node) return` skips input processing for missing nodes
The early `return` was unnecessarily preventing the entire load. The
warning modal is still shown via `showMissingNodesError`.
## Test workflow & screen recording
[04wan2.2smoothmix图生视频
(3).json](https://github.com/user-attachments/files/25858354/04wan2.2smoothmix.3.json)
[screen-capture.webm](https://github.com/user-attachments/assets/9c396f80-fff1-4d17-882c-35ada86542c1)
<img width="373" height="535" alt="스크린샷 2026-03-09 오후 2 48 10"
src="https://github.com/user-attachments/assets/7fea3fd4-0d90-4022-ad78-c53e3d5be887"
/>
## Summary
- Reorganize Select-related stories under `Components/Select/` hierarchy
(SingleSelect, MultiSelect, Select)
- Add `size` prop (`lg`/`md`) to SingleSelect, MultiSelect,
SelectTrigger for Figma Large (40px) / Medium (32px) variants
- Add `invalid` prop (red border) to SingleSelect and SelectTrigger
- Add `loading` prop (spinner) to SingleSelect
- Add `hover:bg-secondary-background-hover` to all select triggers
- Align disabled opacity to 30% per Figma spec
- Add new stories: Disabled, Invalid, Loading, MediumSize, AllStates
## Test plan
- [ ] Verify Storybook renders all stories under `Components/Select/`
- [ ] Check hover state visually on all select triggers
- [ ] Verify Medium size (32px) renders correctly
- [ ] Verify Invalid state shows red border
- [ ] Verify Loading state shows spinner
- [ ] Verify Disabled state has 30% opacity and no hover effect
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-9639-refactor-reorganize-Select-stories-and-add-size-state-variants-31e6d73d36508142b835f04ab6bdaefe)
by [Unito](https://www.unito.io)
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
## Summary
When a logged-in user navigates to `/cloud/signup`, they are now
redirected to `cloud-user-check` (which handles survey or main page
routing).
This mirrors the existing `beforeEnter` guard on the `cloud-login`
route. The `switchAccount` query param bypass is preserved for
consistency.
## Changes
- Added `beforeEnter` guard to the `cloud-signup` route in
`onboardingCloudRoutes.ts`
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-9691-Redirect-authenticated-users-from-signup-page-to-cloud-31f6d73d365081e08cb5c3360a862a37)
by [Unito](https://www.unito.io)
## Summary
Replace legacy `SearchBox` (PrimeVue) and `SearchBoxV2` with the unified
`SearchInput` (reka-ui) component across all consumers.
## Changes
- **What**: Remove `SearchBox.vue`, `SearchBoxV2.vue`, their tests and
stories. Migrate all 14 consumers to `SearchInput`. Move layout classes
to `ComboboxRoot` for proper flex sizing. Extract filter button/chips in
`NodeLibrarySidebarTab`. Standardize modal search width to `flex-1
max-w-lg`.
- **Dependencies**: None new — `SearchInput` already existed using
reka-ui
## Review Focus
- `NodeLibrarySidebarTab.vue`: filter button and `SearchFilterChip`
rendering moved outside the search component
- `SearchInput.vue`: `className` now applied to `ComboboxRoot` instead
of `ComboboxAnchor` for correct flex layout
- Modal dialogs (`WorkflowTemplateSelectorDialog`, `AssetBrowserModal`,
`SampleModelSelector`) unified to `flex-1 max-w-lg`
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-9644-feat-unify-search-components-by-replacing-SearchBox-SearchBoxV2-with-SearchInput-31e6d73d365081ebac55cb265f33b631)
by [Unito](https://www.unito.io)
---------
Co-authored-by: Amp <amp@ampcode.com>
Co-authored-by: github-actions <github-actions@github.com>
## Summary
Fix virtual scroll pagination not triggering in media asset panel list
view.
## Changes
**What**: `VirtualGrid` in `AssetsSidebarListView` was missing
`maxColumns=1` and had an incorrect default item height (200px vs actual
~48px). Without `maxColumns`, `cols` was calculated as
`floor(containerWidth / 200)` (e.g. 2), causing the row count to be
halved and `isNearEnd` to never fire correctly. Added `:max-columns="1"`
and `:default-item-height="48"` to fix pagination. Added regression
tests to `VirtualGrid.test.ts`.
## Review Focus
The root cause: `VirtualGrid.cols` computed as `floor(width/200)`
instead of `1` for single-column list layout, breaking spacer heights
and `approach-end` detection.
Test covers both the fix (approach-end fires with maxColumns=1) and the
bug reproduction (does not fire without it).
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-9646-fix-virtual-scroll-pagination-not-working-in-media-asset-list-view-31e6d73d3650813d973ad19638ad6933)
by [Unito](https://www.unito.io)
Thumbnail downscaling is currently being used in more places than it
should be.
- Nodes which display images will display incorrect resolution
indicators
<img width="255" height="372" alt="image"
src="https://github.com/user-attachments/assets/674790b6-04c8-4db0-84c2-2fa2dbaf123d"
/> <img width="255" height="372" alt="image"
src="https://github.com/user-attachments/assets/1dbe751b-7462-4408-9236-9446b005f5fc"
/>
This is particularly confusing with output nodes, which claim the output
is not of the intended resolution
- The "Download Image" and "Open Image" context menu actions will
incorrectly download the downscaled thumbnail.
- The assets panel will incorrectly display the thumbnail resolution as
the resolution of the output
- The lightbox (zoom) of an image will incorrectly display a downscaled
thumbnail.
This PR is a quick workaround to staunch the major problems
- Nodes always display full previews.
- Resolution downscaling is applied on the assert card, not on the
assetItem itself
- Due to implementation, this means that asset cards will still
incorrectly show the resolution of the thumbnail instead of the size of
the full image.
---------
Co-authored-by: GitHub Action <action@github.com>
Co-authored-by: Alexander Brown <drjkl@comfy.org>
## Summary
Cloud build dispatch was only triggering on the `labeled` event, not on
subsequent pushes to PRs that already had a preview label.
## Changes
- **What**: Add `synchronize` to `pull_request` event types and update
the `if` condition to support all three preview labels (`preview`,
`preview-cpu`, `preview-gpu`). For `labeled` events, check the added
label name; for `synchronize` events, check existing PR labels.
## Review Focus
The `if` condition now branches on `github.event.action` to use the
correct label-checking mechanism for each event type.
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-9636-fix-dispatch-cloud-build-on-synchronize-for-preview-labeled-PRs-31e6d73d3650814e9069e37d6199ffc9)
by [Unito](https://www.unito.io)
As a temporary fix for widgets being incorrectly hidden, #9669 allowed
all disabled widgets to be displayed.
This PR provides a more robust implementation to derive whether the
widget, as would be displayed from the root graph, is disabled.
Potential regression:
- Drag drop handlers are applied on node, not widgets. A subgraph
containing a "Load Image" node, does not allow dragging and dropping an
image onto the subgraphNode in order to load it. Because app mode
widgets would display from the original owning node prior to this PR,
these drag/drop handlers would apply. Placing "Load Image" nodes. I
believe this change makes behavior more consistent, but it warrants
consideration.
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-9671-Restore-hiding-of-linked-inputs-in-app-mode-31e6d73d365081688e37fbb931f3af68)
by [Unito](https://www.unito.io)
## Summary
Adds `isGraphReady` getter to `ComfyApp` and uses it in
`executionErrorStore` guards to prevent false 'ComfyApp graph accessed
before initialization' error logs during early store evaluation.
## Changes
- **What**: Added `isGraphReady` boolean getter to `ComfyApp` that
safely checks graph initialization without triggering the `rootGraph`
getter's error log. Updated 5 guard sites in `executionErrorStore` to
use `app.isGraphReady` instead of `app.rootGraph`.
- **Why**: The `rootGraph` getter logs an error when accessed before
initialization. Computed properties and watch callbacks in
`executionErrorStore` are evaluated early (before graph init), causing
false error noise in the console.
## Review Focus
- `isGraphReady` is intentionally minimal — just
`!!this.rootGraphInternal` — to avoid duplicating the error-logging
behavior of `rootGraph`
- The `watch(lastNodeErrors, ...)` callback now checks `isGraphReady` at
the top and early-returns, consistent with the computed property pattern
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-9672-fix-add-isGraphReady-guard-to-prevent-premature-graph-access-error-logs-31e6d73d365081be8e1fc77114ce9382)
by [Unito](https://www.unito.io)
Co-authored-by: Alexander Brown <drjkl@comfy.org>
Adds a concise guide to `docs/release-process.md` explaining how the
release workflows interact, with focus on the version semantics that
differ between minor and patch bumps.
Key sections:
- How minor bumps freeze the previous minor into `core/` and `cloud/`
branches
- How patch bumps on `main` vs `core/X.Y` differ (published vs draft
releases)
- Why unreleased commits are dual-homed when a minor bump happens
- Summary table, backporting, publishing, and bi-weekly automation
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-9548-docs-add-release-process-guide-31d6d73d365081f2bdaace48a7cb81ae)
by [Unito](https://www.unito.io)
---------
Co-authored-by: Alexander Brown <drjkl@comfy.org>
App mode stores the state of selected widgets as a tuple of `[NodeId,
WidgetName]`. With recent subgraph changes, for a given node,
`widget.name` will no longer uniquely resolve to a single widget.
- From both Vue and Litegraph, selecting an input for display in App
mode will now resolve the NodeId of the node which owns the widget
instead of the selected node.
- When displaying selections in litegraph, if the NodeId does not exist
in the current graph, instead of resolving the actual node the rootGraph
is searched for any subgraphNode which contains a view matching the
`[NodeId, WidgetName]` pair.
- When displaying widgets in App mode, the widget is always set as being
a view of the real widget (This means that they will not display a
purple promotion border.
Known Issue:
- These same subgraph changes made it so that a widget can be linked
without being disabled. This PR makes it so widgets which have been
linked instead display normally under the assumption that they are
incorrectly marked as disabled. As disabled widgets can not be selected
as inputs, this should handle normal usage fine, but a better solution
is being investigated
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-9669-Always-use-interior-nodeId-for-app-mode-31e6d73d365081f8a918d0e43cb659ee)
by [Unito](https://www.unito.io)
## Summary
When the actionbar is floating and has no docked buttons, the container
is now hidden (zero-width, transparent border) to avoid showing an empty
rounded box. Additionally, the error/destructive border is now applied
to the floating actionbar panel itself (via `ComfyActionbar`) instead of
the container, so it appears in the correct location when floating.
## Changes
- **TopMenuSection**: Added `hasDockedButtons` and
`isActionbarContainerEmpty` computed properties to detect when the
docked container has no visible buttons; `actionbarContainerClass`
computed hides the container by collapsing it when empty and floating,
while preserving the legacy drop zone via `:has(.border-dashed)` CSS
selector
- **TopMenuSection**: Error border
(`border-destructive-background-hover`) is now only applied to the
docked container when the actionbar is **not** floating
- **ComfyActionbar**: Accepts new `hasAnyError` prop and applies the
error border to the floating panel's `panelClass` when floating
## Review Focus
- The `has-[.border-dashed]` CSS selector restores the container visuals
when a legacy drag-target element is present inside it — verify this
works as expected
- Error border placement: docked mode shows border on container,
floating mode shows border on the fixed panel
## Screenshots
https://github.com/user-attachments/assets/75caabac-e391-4bfd-b4dc-62d564e55d37
## Problem
Output assets in the assets panel show content hashes (e.g.,
`a1b2c3d4.png`) instead of display names (e.g., `ComfyUI_00001_.png`).
## Root Cause
Cloud inference replaces `filename` with the content hash in the output
transform pipeline. The hashed filename gets stored in the jobs table's
`preview_output` JSONB. The frontend uses this hash as the display name.
## Solution
- Add `display_name` field to `AssetItem` schema and `ResultItemImpl`
- Backend (cloud PR) joins job→assets table to resolve the original name
and injects `display_name` into job responses
- Frontend prefers `display_name` over `name` **only for display text
and download filenames**
- `asset.name` remains unchanged (the hash) for URLs, drag-to-canvas,
export filters, and output key dedup
## Backwards Compatible
- OSS: `display_name` is undefined, falls back to `asset.name` (which is
already the real filename in OSS)
- Cloud pre-deploy: `display_name` absent from API, falls back
gracefully
- Old jobs with no assets: `display_name` not injected, no change
## Cloud PR
https://github.com/Comfy-Org/cloud/pull/2747https://github.com/user-attachments/assets/8a4c9cac-4ade-4ea2-9a70-9af240a56602
## Summary
After a user runs the workflow once in graph mode, switching to app mode
with no app built, incorrectly showed the app mode outputs view instead
of the intro screen
## Changes
- **What**: don't try and select outputs if no outputs & filter out all
outputs when nothing chosen
## Summary
Fix regression from PR #9535 where the multi-output count badge stopped
appearing in the asset sidebar.
## Root Cause
PR #9535 changed `outputCount` in `mapHistoryToAssets` from
`job.outputs_count` (backend-provided total) to
`task.previewableOutputs.length`. However, `TaskItemImpl` constructed
from a job listing only has the single `preview_output`, so
`previewableOutputs.length` is always **1** — the multi-output badge
never appears.
## Fix
Use the backend-provided `outputs_count` (via `task.outputsCount`) with
fallback to `task.previewableOutputs.length` when unavailable. This
restores the correct count while preserving the fallback for jobs that
don't have `outputs_count` from the server.
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-9627-fix-restore-backend-outputs_count-for-asset-sidebar-multi-output-badge-31d6d73d36508160b93fd03af4a01aa3)
by [Unito](https://www.unito.io)
## Summary
fix#8097.
This PR shifts the Mask Editor Brush Size slider from a linear scale to
a logarithmic (exponential) scale. Previously, the linear 1-250 range
heavily clumped the usable, small "fine-detail" brush sizes (e.g., 1px
to 20px) into the very first 10% of the slider, making it extremely
difficult to select precise sizes with the mouse.
This update borrows UX paradigms from other standard image editors like
Photoshop and GIMP, which map their scale entry widgets on an
exponential curve.
## GIMP Source
By inspecting the official **GIMP** source code under
`libgimpwidgets/gimpscaleentry.c`, we can see this exact mathematical
relationship being utilized when the logarithmic property is marked TRUE
on a brush radius adjustment widget:
```
// Mapping visual slider to internal value
value = gtk_adjustment_get_lower(...) + exp(t);
// Mapping internal value to visual slider
t = log (value - gtk_adjustment_get_lower(...) + 0.1);
```
https://github.com/user-attachments/assets/6d59ff12-f623-42cc-a52b-84147e9bb90b
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-9534-fix-maskeditor-make-brush-size-slider-logarithmic-8097-31c6d73d365081118508e8363e0c5312)
by [Unito](https://www.unito.io)
## Summary
Fix asset sidebar badge showing inflated output count by using
previewable outputs length instead of raw server count.
## Changes
- **What**: Changed `outputCount` in `mapHistoryToAssets` from
`job.outputs_count` (includes all output types: text, JSON, custom data)
to `task.previewableOutputs.length` (only image, video, audio, 3D). The
badge now matches what users actually see in the expanded view.
## Review Focus
One-line change. The `task.previewableOutputs` array is already computed
on the line immediately below and used for `allOutputs`, so this
introduces no new computation.
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-9535-fix-use-previewable-output-count-for-asset-sidebar-badge-31c6d73d365081c49161caec64cf3921)
by [Unito](https://www.unito.io)
## Summary
Fix non-widget inputs on nested subgraphs appearing twice — once as
slots and once as unresolved button widgets.
## Changes
- **What**: Add `getTargetWidget()` guard in the `isSubgraphNode()`
branch of `resolveSubgraphInputTarget`, matching the existing check for
non-subgraph nodes. Non-widget inputs (e.g. AUDIO, IMAGE) now return
`undefined` instead of a bogus promotion entry.
## Review Focus
`resolveSubgraphInputTarget` had an asymmetry: the non-subgraph branch
checked `getTargetWidget()` before returning, but the `isSubgraphNode()`
branch returned unconditionally for every input. For nested subgraphs
where non-widget slots are linked through to inner SubgraphNode inputs,
this created `PromotedWidgetView` entries that failed `resolveDeepest()`
(falling back to `type: 'button'`), while the inputs also rendered as
normal slot circles since `input.widget` was never set by
`_resolveInputWidget` (which correctly skipped them).
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-9542-fix-prevent-non-widget-inputs-on-nested-subgraphs-from-appearing-as-button-widgets-31c6d73d3650816387c3f97f0385e762)
by [Unito](https://www.unito.io)
---------
Co-authored-by: Amp <amp@ampcode.com>
## Summary
Remove the workspace switching confirmation dialog since switching
workspaces no longer discards unsaved changes.
## Changes
- **What**: Remove `hasUnsavedChanges` check, `dialogService.confirm`
call, and unused imports (`useI18n`, `useWorkflowStore`,
`useDialogService`) from `useWorkspaceSwitch`. Rename
`switchWithConfirmation` to `switchWorkspace`. Update callers
(`WorkspaceSwitcherPopover.vue`, `InviteAcceptedToast.vue`). Remove
`workspace.unsavedChanges` i18n entries from all 12 locale files.
Simplify tests to cover core switching behavior only.
## Review Focus
The confirmation dialog was showing inaccurate information (warning
about discarding unsaved changes when that no longer happens). This is a
pure removal with no new behavior.
<!-- Pipeline-Ticket: COM-15441 -->
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-9250-fix-remove-workspace-switching-confirmation-dialog-3136d73d365081d3b959da22e8f151d1)
by [Unito](https://www.unito.io)
## Summary
During workflow loading, the workflow data & active workflow object can
be out of sync, meaning any checkState calls will overwrite data into
the wrong workflow.
Recreation steps:
* Open 2-3 workflows
* Enter builder mode > select step
* Select some different inputs on each
* Quickly tap the shift key (this triggers checkState) while switching
tabs
* After a while, you'll see the wrong inputs on the workflows
Alternatively, register an extension that guarantees to call checkState
during the bad phase, run this in browser devtools and switch tabs:
```
window.app.registerExtension({
name: 'bad',
async afterConfigureGraph() {
window.app.extensionManager.workflow.activeWorkflow.changeTracker.checkState()
}
})
```
## Changes
- **What**:
- Add loading graph flag
- Prevent checkState calls while loading
- Prevent app mode data sync while loading
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-9531-fix-Prevent-corruption-of-workflow-data-due-to-checkState-during-graph-loading-31c6d73d365081e2ab91d9145bf1d025)
by [Unito](https://www.unito.io)
## Summary
Standardize the repo's Node contract on 24 while centralizing workflow
resolution through `.nvmrc` so local setup, CI, and package metadata
stay aligned from one version file.
## Changes
- **What**: Add `package.json` `engines.node = 24.x`, switch every
`actions/setup-node` workflow in the repo to `node-version-file:
'.nvmrc'`, and update contributor and Playwright docs to point to
`.nvmrc` as the Node source of truth.
## Review Focus
The workflow behavior should be unchanged apart from sourcing the Node
version from `.nvmrc` instead of repeating literals like `20`, `22`,
`24.x`, or `lts/*`. GitHub's formatter also moved the new `engines`
block to the package metadata section near the end of `package.json`.
---------
Co-authored-by: GitHub Action <action@github.com>
Co-authored-by: Alexander Brown <drjkl@comfy.org>