## Summary
Fix input asset previews (images/videos) disappearing from
LoadImage/LoadVideo nodes after execution and a browser tab switch.
## Changes
- **What**: Guard `setOutputsByLocatorId` in `imagePreviewStore` to
preserve existing input-type preview images (`type: 'input'`) when the
incoming execution output has no images. Execution outputs with actual
images still overwrite as expected.
## Review Focus
- The guard only applies when existing output is an input preview (`type
=== 'input'` for all images) AND incoming output has no images — this is
the exact scenario where execution clobbers upload widget previews.
- Root cause: execution results from the backend overwrite the upload
widget's synthetic preview for LoadImage/LoadVideo nodes (which produce
no output images). Combined with the deferred resize-observer
re-observation from PR #8805, returning to a hidden tab reads the
now-empty store entry.
## Summary
- When a media node (LoadImage/LoadAudio/LoadVideo) is selected and the
clipboard contains stale node metadata from a prior Ctrl+C, pasting
skips the node-metadata deserialization so that the paste falls through
to litegraph's default handler instead of incorrectly pasting the old
copied node.
- FixesComfy-Org/ComfyUI#12896
## Root Cause
The paste handler in `usePaste.ts` checks clipboard `text/html` for
`data-metadata` (serialized node data) **before** falling through to
litegraph's default paste. When a user copies a node, then copies a web
image, the browser clipboard may retain the old `data-metadata` in
`text/html` while the image data is not available as a
`DataTransferItem` file. This causes the stale node to be pasted instead
of the image.
## Fix
Skip `pasteClipboardItems()` when a media node is selected, allowing the
paste to fall through to litegraph's default handler which can handle
the clipboard content appropriately.
## Test plan
- [x] Added unit test verifying node metadata paste is skipped when
media node is selected
- [x] Manual: Copy a node → copy a web image → select LoadImage node →
Ctrl+V → verify image is pasted, not the node
## AS IS
https://github.com/user-attachments/assets/210d77d3-5c49-4e38-91b7-b9d9ea0e7ca0
## TO BE
https://github.com/user-attachments/assets/b68e4582-0c57-48b8-9ed9-0b3243bb1554🤖 Generated with [Claude Code](https://claude.com/claude-code)
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-9773-fix-skip-node-metadata-paste-when-media-node-is-selected-3216d73d3650814d92dadcd0c0ec79c7)
by [Unito](https://www.unito.io)
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: GitHub Action <action@github.com>
Adds a reusable agent skill for managing cherry-pick backports across
stable release branches.
## What
Agent skill at `.claude/skills/backport-management/` with routing-table
SKILL.md + 4 reference files (discovery, analysis, execution, logging).
## Why
Codifies lessons from backporting 57 PRs across cloud/1.41, core/1.41,
and core/1.40. Makes future backport sessions faster and less
error-prone.
## Key learnings baked in
- Cloud-only PRs must not be backported to `core/*` branches (wasted
effort)
- Wave verification (`pnpm typecheck`) between batches to catch breakage
early
- Human review required for non-trivial conflict resolutions before
admin-merge
- MUST vs SHOULD decision guide with clear criteria
- Continuous backporting preference over bulk sessions
- Mermaid diagram as final session deliverable
- Conflict triage table (never skip based on file count alone)
- `gh api` for labels instead of `gh pr edit` (Projects Classic
deprecation)
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-9619-chore-add-backport-management-agent-skill-31d6d73d3650815b9808c3916b8e3343)
by [Unito](https://www.unito.io)
---------
Co-authored-by: GitHub Action <action@github.com>
## Summary
Asset widget dropdown search only matched against `item.name`
(filename), but users see `item.label` (display name). Now searches both
fields so filtering matches what is visually displayed.
## Changes
- **What**: `defaultSearcher` in `FormDropdown` now matches against both
`name` and `label` fields
- Added 3 unit tests covering label-based search scenarios
## Review Focus
- The change only affects cloud asset mode where `name` (filename) and
`label` (display name) differ. In local mode, `label` is either
`undefined` or identical to `name`, so behavior is unchanged.
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-9774-bugfix-Asset-widget-search-matches-display-label-3216d73d365081ca8befdf7260c66a26)
by [Unito](https://www.unito.io)
---------
Co-authored-by: GitHub Action <action@github.com>
## Summary
- Fixes incorrect workflow loading after page refresh when the active
workflow is saved and unmodified
- Adds saved-workflow fallback in `loadPreviousWorkflowFromStorage()`
before falling back to latest draft
- Calls `openWorkflow()` in `restoreWorkflowTabsState()` to activate the
correct tab after restoration
- Fixes#9317
## Test plan
- [x] Unit tests for saved-workflow fallback (draft missing, saved
workflow exists)
- [x] Unit tests for correct tab activation after restoration
- [x] Unit tests for existing behavior preservation (draft preferred
over saved, no-session-path fallback)
- [x] `pnpm typecheck` passes
- [x] `pnpm lint` passes
- [x] All 117 persistence tests pass
🤖 Generated with [Claude Code](https://claude.com/claude-code)
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-9318-fix-restore-correct-workflow-on-page-reload-3166d73d365081ba9139f7c23c917aa4)
by [Unito](https://www.unito.io)
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Alexander Brown <drjkl@comfy.org>
Co-authored-by: GitHub Action <action@github.com>
## Summary
When a workflow is loaded with missing models, users currently have no
way to identify or resolve them from within the UI. This PR adds a full
missing-model detection and resolution pipeline that surfaces missing
models in the Errors tab, allowing users to install or import them
without leaving the editor.
## Changes
### Missing Model Detection
- Scan all COMBO widgets across root graph and subgraphs for model-like
filenames during workflow load
- Enrich candidates with embedded workflow metadata (url, hash,
directory) when available
- Verify asset-supported candidates against the asset store
asynchronously to confirm installation status
- Propagate missing model state to `executionErrorStore` alongside
existing node/prompt errors
### Errors Tab UI — Model Resolution
- Group missing models by directory (e.g. `checkpoints`, `loras`, `vae`)
with collapsible category cards
- Each model row displays:
- Model name with copy-to-clipboard button
- Expandable list of referencing nodes with locate-on-canvas button
- **Library selector**: Pick an alternative from the user's existing
models to substitute the missing model with one click
- **URL import**: Paste a Civitai or HuggingFace URL to import a model
directly; debounced metadata fetch shows filename and file size before
confirming; type-mismatch warnings (e.g. importing a LoRA into
checkpoints directory) are surfaced with an "Import Anyway" option
- **Upgrade prompt**: In cloud environment, free-tier subscribers are
shown an upgrade modal when attempting URL import
- Separate "Import Not Supported" section for custom-node models that
cannot be auto-resolved
- Status card with live download progress, completion, failure, and
category-mismatch states
### Canvas Integration
- Highlight nodes and widgets that reference missing models with error
indicators
- Propagate missing-model badges through subgraph containers so issues
are visible at every graph level
### Code Cleanup
- Simplify `surfacePendingWarnings` in workflowService, remove stale
widget-detected model merging logic
- Add `flattenWorkflowNodes` utility to workflowSchema for traversing
nested subgraph structures
- Extract `MissingModelUrlInput`, `MissingModelLibrarySelect`,
`MissingModelStatusCard` as focused single-responsibility components
## Testing
- Unit tests for scan pipeline (`missingModelScan.test.ts`): enrichment,
skip-installed, subgraph flattening
- Unit tests for store (`missingModelStore.test.ts`): state management,
removal helpers
- Unit tests for interactions (`useMissingModelInteractions.test.ts`):
combo select, URL input, import flow, library confirm
- Component tests for `MissingModelCard` and error grouping
(`useErrorGroups.test.ts`)
- Updated `workflowService.test.ts` and `workflowSchema.test.ts` for new
logic
## Review Focus
- Missing model scan + enrichment pipeline in `missingModelScan.ts`
- Interaction composable `useMissingModelInteractions.ts` — URL metadata
fetch, library install, upload fallback
- Store integration and canvas-level error propagation
## Screenshots
https://github.com/user-attachments/assets/339a6d5b-93a3-43cd-98dd-0fb00681b66f
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-9743-feat-Surface-missing-models-in-Errors-tab-Cloud-3206d73d365081678326d3a16c2165d8)
by [Unito](https://www.unito.io)
## Summary
Fix all app mode widgets (including seed) disappearing after hard
refresh due to a race condition in `pruneLinearData` and a missing
reactivity dependency in `mappedSelections`.
## Changes
- **What**: Guard `pruneLinearData` with `!ChangeTracker.isLoadingGraph`
so inputs are preserved while `rootGraph.configure()` hasn't populated
nodes yet. Add `graphNodes` dependency to `mappedSelections` computed in
`LinearControls.vue` so it re-evaluates when the graph finishes
configuring.
## Review Focus
The core fix is a one-line guard change: `app.rootGraph &&
!ChangeTracker.isLoadingGraph` instead of just `app.rootGraph`. The
previous guard failed because `rootGraph` exists as an empty graph
during loading — `resolveNode()` returns `undefined` for all nodes and
everything gets filtered out.
Fixes COM-16193
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-9621-fix-app-mode-widgets-disappear-after-hard-refresh-31d6d73d3650811193f5e1bc8f3c15c8)
by [Unito](https://www.unito.io)
---------
Co-authored-by: GitHub Action <action@github.com>
## Summary
- align the linear-mode `DropZone` upload indicator with the Figma file
upload states
- add a co-located Storybook story for the default and hover variants
- add a `forceHovered` preview prop so Storybook can render the hover
state deterministically
## Validation
- `pnpm typecheck` (run in the original workspace with dependencies
installed)
- `pnpm lint` (passes with one pre-existing warning in
`src/lib/litegraph/src/ContextMenu.ts`)
- Storybook smoke check is currently blocked by an existing workspace
issue: `vite-plugin-inspect` fails with `Can not found environment
context for client`
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-9690-feat-add-DropZone-Storybook-coverage-for-file-upload-states-31f6d73d365081ae9eabdde6b5915f26)
by [Unito](https://www.unito.io)
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: GitHub Action <action@github.com>
## Summary
Rename the `Comfy.Queue.QPOV2` settings label to `Docked job
history/queue panel` to improve searchability/discoverability in the
settings UI.
## Changes
- **What**: Updated the visible setting name in the core settings
definition and the English locale string.
## Review Focus
The change is intentionally limited to the display label. The persisted
setting key remains `Comfy.Queue.QPOV2`, so existing user configuration
is preserved.
## Screenshots (if applicable)
Not applicable.
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-9620-fix-rename-docked-queue-panel-setting-31d6d73d36508189a2d1d3a621739a22)
by [Unito](https://www.unito.io)
## Summary
This makes the focus ring only appear on keyboard navigation (Tab), not
on mouse click for widgets like toggle switches, while text inputs still
show the ring on click since browsers apply ` :focus-visible` to them.
## Mozilla Standard
Legacy `focus-within` triggers a highlight ring on every mouse click,
creating unnecessary visual noise during canvas navigation. Following
[MDN
standards](https://developer.mozilla.org/en-US/docs/Web/CSS/:focus-visible),
`:focus-visible` only triggers the highlight when the browser determines
a visual cue is needed (e.g., keyboard navigation).
Using the `:has()` relational selector allows the container to react to
the state of its children natively in CSS. Removes the need for Vue
event listeners or complex state bubbling to highlight the field border.
This reduces JavaScript overhead and simplifies component logic. FYI
[MDN
:has()](https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Selectors/:has).
Reordered Tailwind classes to move `transition-all` to the end,
following official best practices. Groups layout/shape first, followed
by interaction states, and finally animations. This improves code
readability and maintainability.
## Screenshots
before
<img width="558" height="252" alt="12efd5721fb792a7e2dab7e022c2bed6"
src="https://github.com/user-attachments/assets/f881fe13-9f4f-40fd-a8cc-f438b1ba4bde"
/>
after
<img width="538" height="235" alt="e5ffec0a34d3b237c4fca9818ec598dd"
src="https://github.com/user-attachments/assets/5ada4112-64bd-48a4-9e9c-b59de6984370"
/>
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-9456-fix-update-WidgetLayoutField-border-styling-31b6d73d36508193a31ed02bfdef414f)
by [Unito](https://www.unito.io)
Co-authored-by: Terry Jia <terryjia88@gmail.com>
## Summary
Show tab-specific empty state messages instead of a misleading registry
connection error on tabs that don't depend on the Manager API.
## Changes
- **What**: Scoped `comfyManagerStore.error` to only affect tabs that
actually use the Manager API (AllInstalled, UpdateAvailable,
Conflicting). Missing and Workflow tabs use the registry directly, so
the Manager API error is irrelevant to them. Previously, any prior
Manager API failure would cause `"Error connecting to the Comfy Node
Registry"` to appear on the Missing tab even when there are simply no
missing nodes.
## Review Focus
The `isManagerErrorRelevant` computed only shows the error for Manager
API-dependent tabs. Verify that AllInstalled/UpdateAvailable/Conflicting
still correctly show the error when Manager API fails.
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-9640-fix-show-correct-empty-state-on-Missing-tab-instead-of-misleading-registry-error-31e6d73d365081868da1d226a9bb5fdc)
by [Unito](https://www.unito.io)
## Summary
Image input changes (dropdown selection and file upload) in app/linear
mode did not create their own undo entries, causing undo to skip or
bundle image changes with subsequent actions.
## Changes
- **What**: Add explicit `checkState()` calls in
`WidgetSelectDropdown.vue` after `modelValue` is set in
`updateSelectedItems()` (dropdown selection) and `handleFilesUpdate()`
(file upload), ensuring each image change gets its own undo entry.
## Review Focus
The fix is intentionally scoped to `WidgetSelectDropdown` rather than
the generic `updateHandler` in `NodeWidgets.vue`, which would create
excessive undo entries for text inputs. The pattern follows existing
usage in `useSelectedNodeActions.ts` and other composables.
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-9623-fix-call-checkState-after-image-input-changes-for-proper-undo-tracking-31d6d73d3650814781dbca5db459ab6d)
by [Unito](https://www.unito.io)
---------
Co-authored-by: GitHub Action <action@github.com>
## Summary
Restores `widget.inputEl` assignment on STRING multiline widgets that
was removed in commit a7c211516 (PR #8594) when it was renamed to
`widget.element`. Custom nodes (e.g. comfyui-custom-scripts) rely on
`widget.inputEl` to call `addEventListener` or set `readOnly`.
- FixesComfy-Org/ComfyUI#12893
## Test plan
- Verify custom nodes that access `widget.inputEl` on STRING widgets
work correctly
- Verify `widget.element` still works as before
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
## Summary
Fix#9732
To clarify how preventing the 60 FPS object assignment solves the
`vue-i18n` (intlify) issue, here is the complete chain reaction leading
to the performance loop:
1. The Root Cause: In `useMinimapViewport.ts`, `useRafFn` acts as a
timer bound to the browser's **refresh rate** (60 FPS). In the original
code, it unconditionally executed the `viewportTransform.value = { ... }
`assignment 60 times a second.
2. Vue's Reactivity Interception: Because `viewportTransform` is a
reactive variable (`ref`), updating it causes its corresponding
**computed** property (`viewportStyles`) to register a data dependency
update.
3. Forced Re-rendering: The `<template> ` in `MiniMap.vue` is bound to
`:style="viewportStyles"`. Since the dependent value changed, Vue's
Virtual DOM decides: "I must re-render the entire `MiniMap.vue`
interface **60 times** per second to ensure the element positions are
up-to-date!"
4. The Victim Emerges: Inside the template of `MiniMap.vue`, there are
several internationalization translation functions: `<button
:aria-label="$t('g.settings')" ...> <button :aria-label="$t('g.close')"
...> `In Vue, whenever a component re-renders, all functions within its
template (including `$t()`) must be re-evaluated. Because the component
was being forced to re-render **60 times** per second, and there are
approximately **6 calls** to `$t()` within this UI, it multiplied into
60 × 6 = **360** intlify compilation and evaluate events per second.
## Solution
Only assemble objects and hand them over to Vue for rendering when the
mouse is actually dragging the canvas.
By extracting the math into **stack-allocated** primitive variables `(x,
y, w, h) `and strictly comparing them, it completely halts the CPU burn
at the source with minimal runtime overhead.
## Screenshot
before
<img width="1820" height="908" alt="image"
src="https://github.com/user-attachments/assets/b48d1e76-6498-47c0-af41-e0594d4e7e2f"
/>
after
<img width="1566" height="486" alt="image"
src="https://github.com/user-attachments/assets/5848b7b7-c57c-494f-a99e-4f7c92889ed0"
/>
Tooltips are normally resolved through the node definition. Since
DynamicCombo added widgets are nested in the spec definition, this
lookup fails to find them. This PR makes it so that when a widget is
dynamically added using `litegraphService:addNodeInput`, any 'tooltiptip
defined in the provided inputSpec is applied on the widget.
The tooltip system does not current support tooltips for dynamiclly
added inputs. That can be considered for a followup PR.
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-9717-Support-tooltips-on-DynamicCombos-31f6d73d365081dc93f9eadd98572b3c)
by [Unito](https://www.unito.io)
---------
Co-authored-by: Alexander Brown <drjkl@comfy.org>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
## Summary
Fix Vue warning about extraneous non-props attributes (`data-index`,
`style`) in `TreeExplorerV2Node`, which caused the Node Library sidebar
to freeze.
## Changes
- **What**: Added `defineOptions({ inheritAttrs: false })` and
`v-bind="$attrs"` on both node/folder `<div>` elements so the
virtualizer's positioning attributes are properly applied to the
rendered DOM.
## Review Focus
`TreeExplorerV2Node` has a fragment root (`<TreeItem as-child>` +
`<Teleport>`), so Vue cannot auto-inherit attrs. The virtualizer's
`style` (positioning) merges cleanly with `rowStyle` (`paddingLeft`
only).
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-9735-fix-resolve-extraneous-attrs-warning-in-TreeExplorerV2Node-3206d73d3650817c8619e2145e98813d)
by [Unito](https://www.unito.io)
## Summary
The canvas mode selector popover (Select/Hand mode) uses plain `div`
elements for its menu items, making them completely inaccessible to
keyboard-only users and screen readers. This PR replaces them with
proper semantic HTML and ARIA attributes.
## Problem (AS-IS)
As reported in #9519, the canvas mode selector popover has the following
accessibility issues:
1. **Menu items are `div` elements** — they cannot receive keyboard
focus, so users cannot Tab into the popover or activate items with
Enter/Space. Keyboard-only users are completely locked out of switching
between Select and Hand (pan) mode via the popover.
2. **No ARIA roles** — screen readers announce the popover content as
generic text rather than an interactive menu. Users relying on assistive
technology have no way to understand that these are selectable options.
3. **No keyboard navigation within the popover** — even if a user
somehow focuses an item, there are no ArrowUp/ArrowDown handlers to move
between options, which is the standard interaction pattern for
`role="menu"` widgets (WAI-ARIA Menu Pattern).
4. **Decorative icons are not hidden from assistive technology** — icon
elements (`<i>` tags) are exposed to screen readers, adding noise to the
announcement.
5. **The bottom toolbar (`GraphCanvasMenu`) lacks semantic grouping** —
the `ButtonGroup` container has no `role="toolbar"` or `aria-label`, so
screen readers cannot convey that these buttons form a related control
group.
> Note: Pan mode itself already has keyboard shortcuts (`H` for
Hand/Lock, `V` for Select/Unlock), but the popover UI that surfaces
these options is not keyboard-accessible.
## Solution (TO-BE)
1. **Replace `div` → `button`** for menu items in `CanvasModeSelector` —
buttons are natively focusable and activatable via Enter/Space without
extra work.
2. **Add `role="menu"` on the container and `role="menuitem"` on each
option** — screen readers now announce "Canvas Mode menu" with two menu
items.
3. **Add ArrowUp/ArrowDown keyboard navigation** with wrap-around —
pressing ArrowDown on "Select" moves focus to "Hand", and vice versa.
This follows the WAI-ARIA Menu Pattern.
4. **Add `aria-label` to each menu item and `aria-hidden="true"` to
decorative icons** — screen readers announce "Select" / "Hand" clearly
without icon noise.
5. **Add `role="toolbar"` with `aria-label="Canvas Toolbar"` to the
`ButtonGroup`** — screen readers identify the bottom control bar as a
coherent toolbar.
## Changes
- **What**: Accessibility improvements to `CanvasModeSelector` popover
and `GraphCanvasMenu` toolbar
- **Dependencies**: None — only HTML/ARIA attribute changes and two new
i18n keys (`canvasMode`, `canvasToolbar`)
## Review Focus
- Verify the `button` elements render visually identical to the previous
`div` elements (same padding, hover styles, cursor)
- Confirm ArrowUp/ArrowDown navigation works correctly in the popover
- Check that screen readers announce the menu and toolbar correctly
Fixes#9519
> Note: The issue also requests Space-bar hold-to-pan, Tab through node
ports, and link creation mode keyboard shortcuts. These are significant
new features beyond the scope of this accessibility fix and should be
tracked separately.
## Test plan
- [x] Unit tests for ARIA roles, button elements, aria-labels,
aria-hidden, and arrow key navigation (7 tests)
- [ ] Manual: open canvas mode selector popover → Tab focuses first item
→ ArrowDown/ArrowUp navigates → Enter/Space selects
- [ ] Screen reader: verify "Canvas Mode menu" with "Select" and "Hand"
menu items are announced
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-9526-fix-improve-canvas-menu-keyboard-navigation-and-ARIA-accessibility-31c6d73d3650814c9487ecf748cf6a99)
by [Unito](https://www.unito.io)
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
## Summary
Fixes Zod validation crash when the jobs batch contains text-only
preview outputs (e.g. from LLM nodes), which caused the entire Assets
sidebar to show nothing.
## Changes
- **What**: Made `filename`, `subfolder`, and `type` optional in
`zPreviewOutput` and added `.passthrough()` for extra fields like
`content`. Text-only jobs are safely filtered out downstream by
`supportsPreview`.
- Added tests in `fetchJobs.test.ts` verifying a mixed batch (image +
text-only + no-preview) parses successfully.
- Added test in `assetsStore.test.ts` verifying text-only jobs are
skipped without breaking sibling image jobs. Improved `TaskItemImpl`
mock to realistically handle media types.
## Review Focus
- The `zPreviewOutput` schema now uses `.passthrough()` to allow extra
fields from new preview types (like `content` on text previews). This is
consistent with `zRawJobListItem` and `zExecutionError` which also use
`.passthrough()`.
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-9724-fix-make-zPreviewOutput-accept-text-only-job-outputs-31f6d73d36508119a7aef99f9b765ecd)
by [Unito](https://www.unito.io)
## 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>