Commit Graph

551 Commits

Author SHA1 Message Date
jaeone94
31a33a0ba2 feat: auto-resolve simple validation errors on widget change and slot connection (#9464)
## Summary

Automatically clears transient validation errors
(`value_bigger_than_max`, `value_smaller_than_min`, `value_not_in_list`,
`required_input_missing`) when the user modifies a widget value or
connects an input slot, so resolved errors don't linger in the error
panel. Also clears missing model state when the user changes a combo
widget value.

## Changes

- **`useNodeErrorAutoResolve` composable**: watches widget changes and
slot connections, clears matching errors via `executionErrorStore`
- **`executionErrorStore`**: adds `clearSimpleNodeErrors` and
`clearSimpleWidgetErrorIfValid` with granular per-slot error removal
- **`executionErrorUtil`**: adds `isValueStillOutOfRange` to prevent
premature clearing when a new value still violates the constraint
- **`graphTraversalUtil`**: adds `getExecutionIdFromNodeData` for
subgraph-aware execution ID resolution
- **`GraphCanvas.vue`**: fixes subgraph error key lookup by using
`getExecutionIdByNode` instead of raw `node.id`
- **`NodeWidgets.vue`**: wires up the new composable to the widget layer
- **`missingModelStore`**: adds `removeMissingModelByWidget` to clear
missing model state on widget value change
- **`useGraphNodeManager`**: registers composable per node
- **Tests**: 126 new unit tests covering error clearing, range
validation, and graph traversal edge cases

## Screenshots



https://github.com/user-attachments/assets/515ea811-ff84-482a-a866-a17e5c779c39



https://github.com/user-attachments/assets/a2b30f02-4929-4537-952c-a0febe20f02e


┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-9464-feat-auto-resolve-simple-validation-errors-on-widget-change-and-slot-connection-31b6d73d3650816b8afdc34f4b40295a)
by [Unito](https://www.unito.io)

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 23:49:44 +09:00
Christian Byrne
b5ddc70233 feat: show ComfyUI context menu on textarea widget right-click (#9840)
## Summary

Right-clicking a textarea widget (e.g. text node) shows the browser's
native context menu instead of ComfyUI's context menu, preventing access
to promote/un-promote options in subgraphs.

## Changes

- **What**: Replace `@contextmenu.capture.stop` on
`WidgetTextarea.vue`'s `<Textarea>` with a handler implementing
double-right-click toggling: first right-click shows ComfyUI's context
menu, second right-click (while menu is open) allows browser native
menu. Exposes `isNodeOptionsOpen()` from `useMoreOptionsMenu.ts` to
check menu state.

## Review Focus

The capture-phase handler in `WidgetTextarea.vue` only changes
`contextmenu` handling — pointer event modifiers
(`pointerdown/move/up.capture.stop`) that prevent canvas panning are
untouched. The double-right-click pattern matches Notion/YouTube
behavior for editable text fields.

<!-- Pipeline-Ticket: d7a53160-e1e1-42bb-a5ac-c0c2702c629c -->

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-9840-fix-show-ComfyUI-context-menu-on-textarea-widget-right-click-3216d73d36508102b4c9c13a5915bc48)
by [Unito](https://www.unito.io)

---------

Co-authored-by: Alexander Brown <drjkl@comfy.org>
2026-03-13 07:03:20 -07:00
Dante
07d5bd50f6 feat: extract SeedControlButton component (#9744)
<img width="1048" height="482" alt="스크린샷 2026-03-12 오전 9 11 56"
src="https://github.com/user-attachments/assets/68009980-097c-4736-b7c4-eb8f9a6f05be"
/>

## Summary
- Extract inline value control button from `WidgetWithControl` into
reusable `SeedControlButton` component
- Support `badge` (pill) and `button` (square) variants per Figma design
system spec
- Use native `<button>` element for proper a11y (works with Reka UI's
`PopoverTrigger as-child`)

## Test plan
- [x] Verify seed control button renders correctly on KSampler node's
seed widget
- [x] Verify popover opens on click and mode selection works
- [x] Verify all 4 modes display correct icon/text (shuffle, pencil-off,
+1, -1)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-9744-feat-extract-SeedControlButton-component-3206d73d365081a3823cc19e48d205c1)
by [Unito](https://www.unito.io)

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: github-actions <github-actions@github.com>
2026-03-13 02:49:18 -07:00
Dante
c318cc4c14 feat: replace PrimeVue ColorPicker with custom component (#9647)
## Summary
- Replace PrimeVue `ColorPicker` with a custom component built on Reka
UI Popover
- New `ColorPicker` supports HSV saturation-value picking, hue/alpha
sliders, hex/rgba display toggle
- Simplify `WidgetColorPicker` by removing PrimeVue-specific
normalization logic
- Add Storybook stories for both `ColorPicker` and `WidgetColorPicker`

## Test plan
- [x] Unit tests pass (9 widget tests, 47 colorUtil tests)
- [x] Typecheck passes
- [x] Lint passes
- [ ] Verify color picker visually in Storybook
- [ ] Test color picking in node widgets with hex/rgb/hsb formats

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-9647-feat-replace-PrimeVue-ColorPicker-with-custom-component-31e6d73d36508114bc54d958ff8d0448)
by [Unito](https://www.unito.io)

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Alexander Brown <drjkl@comfy.org>
2026-03-13 12:45:10 +09:00
Rizumu Ayaka
8f07468fdd fix: dropdown widget fetching output files (#6734)
related
https://github.com/Comfy-Org/ComfyUI_frontend/issues/5827#issuecomment-3539629932

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6734-fix-dropdown-widget-fetching-output-files-2af6d73d365081988340f74694b7cee7)
by [Unito](https://www.unito.io)

Co-authored-by: bymyself <cbyrne@comfy.org>
2026-03-12 13:43:33 -07:00
Rizumu Ayaka
8c93567019 perf: detect pointer drag in useTransformSettling for pan optimization (#9649)
## Summary

When many nodes are rendered in the transform container, both zoom and
pan can cause FPS drops because the browser re-rasterizes all visible
content at the new transform. `will-change: transform` tells the browser
to keep the layer as a GPU texture and skip re-rasterization during
active interaction, restoring visual quality only after settling.

- Add pointer drag detection so `will-change: transform` covers pan in
addition to zoom. Without this, dragging with 256+ nodes causes jank as
the browser re-rasterizes the entire layer on every frame of the pan.
- Fix settleDelay from 16ms to 256ms. At 16ms the debounce fires between
consecutive wheel events (~50ms apart on a physical mouse), causing
`will-change` to toggle on/off rapidly. Each toggle forces the browser
to promote/demote the compositor layer, which is more expensive than not
having the optimization at all.
- Replace scoped CSS with Tailwind `will-change-transform`.
- Remove per-node `will-change: transform` on `.lg-node`. Promoting each
node to its own compositor layer (256 nodes = 256 GPU textures)
increases memory pressure and compositing overhead, making performance
worse than a single promoted container.
- Previously, the virtual DOM of Nodes was updated during zooming and
dragging, but now this update is avoided through some techniques.
- Using the 3D versions of scale and translate can provide a smoother
experience when dealing with a large number of nodes.

## Test plan
- [x] Unit tests updated and passing
- [x] Manual: verify during both zoom and pan
- [x] Manual: compare pan FPS with 256 nodes before/after

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-9649-perf-detect-pointer-drag-in-useTransformSettling-for-pan-optimization-31e6d73d3650818bb2c3ccd01a465140)
by [Unito](https://www.unito.io)

---------

Co-authored-by: github-actions <github-actions@github.com>
2026-03-12 13:30:18 -07:00
AustinMroz
ffda940e5a Make the vue toggle ring surround toggle (#9071)
| Before | After |
| ------ | ----- |
| <img width="360" alt="before"
src="https://github.com/user-attachments/assets/3d414c6e-972e-4c87-8765-c30dc8288ddb"
/> | <img width="360" alt="after"
src="https://github.com/user-attachments/assets/a3ec3eb4-f61f-42ac-bcf3-bc4c766040d7"/>|

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-9071-Make-the-vue-toggle-ring-surround-toggle-30f6d73d365081e0a427cf270ef2763a)
by [Unito](https://www.unito.io)
2026-03-12 12:00:02 -07:00
jaeone94
55c42ee484 [bugfix] Asset widget search matches display label (#9774)
## 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>
2026-03-12 20:40:44 +09:00
pythongosssss
975d6a360d fix: switching tabs in app mode clearing outputs (#9745)
## Summary

- remove reset on exiting app mode and instead cleanup at specific
stages instead of a reset all
- more job<->workflow specificity updates
- ensure pending data is cleared up and doesnt leak over time

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-9745-fix-switching-tabs-in-app-mode-clearing-outputs-3206d73d365081038cb0c83f0d953e71)
by [Unito](https://www.unito.io)
2026-03-12 03:27:52 -07:00
Dante
8db6fb7733 feat: replace PrimeVue Galleria/Skeleton with custom DisplayCarousel and ImagePreview (#9712)
## Summary
- Replace `primevue/galleria` with custom `DisplayCarousel` component
featuring Single (carousel) and Grid display modes
- Hover action buttons (mask, download, remove) appear on image
hover/focus
- Thumbnail strip with prev/next navigation; arrows at edges, thumbnails
centered
- Grid mode uses fixed 56px image tiles matching Figma spec
- Replace `primevue/skeleton` and `useToast()` in `ImagePreview` with
`Skeleton.vue` and `useToastStore()`
- Rename `WidgetGalleria` → `DisplayCarousel` across registry, stories,
and tests
- Add Storybook stories for both `DisplayCarousel` and `ImagePreview`
- Retain `WidgetGalleriaOriginal` with its own story for side-by-side
comparison

## Test plan
- [x] Unit tests pass (30 DisplayCarousel + 21 ImagePreview)
- [x] `pnpm typecheck` clean
- [x] `pnpm lint` clean
- [x] `pnpm knip` clean
- [x] Visual verification via Storybook: hover controls, nav, grid mode,
single/grid toggle
- [x] Manual Storybook check: Components/Display/DisplayCarousel,
Components/Display/ImagePreview


## screenshot
<img width="604" height="642" alt="스크린샷 2026-03-12 오후 2 01 51"
src="https://github.com/user-attachments/assets/94df3070-9910-470b-a8f5-5507433ef6e6"
/>
<img width="609" height="651" alt="스크린샷 2026-03-12 오후 2 04 47"
src="https://github.com/user-attachments/assets/3d9884b4-f1bd-4ef5-957a-c7cf7fdc04d8"
/>
<img width="729" height="681" alt="스크린샷 2026-03-12 오후 2 04 49"
src="https://github.com/user-attachments/assets/715f9367-17a3-4d7b-b81f-a7cd6bd446bf"
/>
<img width="534" height="460" alt="스크린샷 2026-03-12 오후 2 05 39"
src="https://github.com/user-attachments/assets/b810eee2-55cb-4dbd-aaca-6331527d13ca"
/>


🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 18:10:42 +09:00
Deep Mehta
7c2c59b9fb fix: center ComfyUI logo in sidebar menu button with chevron (#9722)
## Summary

Center the ComfyUI logo in the sidebar menu button after chevron icon
was added, and add padding to floating sidebar item groups.

## Changes

Before / after:
<img width="127" height="417" alt="image"
src="https://github.com/user-attachments/assets/aa327fd9-5550-49aa-b28d-a90435e9d1bc"
/>


- **What**: Use negative margin on chevron icon so it doesn't affect
logo centering. Add `p-0.5` padding to floating sidebar item groups.

## Review Focus

Visual alignment of the logo in both floating and connected sidebar
modes.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-9722-fix-center-ComfyUI-logo-in-sidebar-menu-button-with-chevron-31f6d73d3650811a9392cda3070310f9)
by [Unito](https://www.unito.io)

---------

Co-authored-by: github-actions <github-actions@github.com>
Co-authored-by: Alexander Brown <drjkl@comfy.org>
Co-authored-by: GitHub Action <action@github.com>
2026-03-12 01:10:26 -07:00
jaeone94
2f7f3c4e56 [feat] Surface missing models in Errors tab (Cloud) (#9743)
## 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)
2026-03-12 16:21:54 +09:00
Christian Byrne
4c00d39ade fix: app mode widgets disappear after hard refresh (#9621)
## 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>
2026-03-11 23:54:27 -07:00
Dante
f1fc5fa9b3 feat: add DropZone Storybook coverage for file upload states (#9690)
## 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>
2026-03-11 23:39:08 -07:00
Kelly Yang
65655ba35f fix: update WidgetLayoutField border styling (#9456)
## 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>
2026-03-11 23:34:40 -07:00
Terry Jia
852d77159e fix: prevent WebGLRenderer leak in app mode 3D preview (#9766)
## Summary
Reuse the Load3d instance when switching between 3D results in app mode
instead of creating a new WebGLRenderer each time. Add onUnmounted
cleanup to Preview3d to release WebGL resources when the component is
removed.

## Screenshots (if applicable)
before


https://github.com/user-attachments/assets/c9818d10-941f-4994-9b48-2710c88454e7



after


https://github.com/user-attachments/assets/36361763-6800-4bc8-8089-14d64b7fcd16

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-9766-fix-prevent-WebGLRenderer-leak-in-app-mode-3D-preview-3216d73d365081e19305d7255b71bc49)
by [Unito](https://www.unito.io)
2026-03-12 01:12:20 -04:00
Christian Byrne
0a62ea0b2c fix: call checkState after image input changes for proper undo tracking (#9623)
## 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>
2026-03-11 19:44:16 -07:00
Dante
cccc0944a0 fix: restore widget.inputEl backward compatibility for custom nodes (#9759)
## 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`.

- Fixes Comfy-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>
2026-03-12 00:48:22 +00:00
Kelly Yang
aadec87bff fix(minimap): minimap re-render/perf issue (#9741)
## 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"
/>
2026-03-11 20:20:51 -04:00
Dante
76006fca52 feat: add text widget stories and Number input stories (#9527)
<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>
2026-03-11 08:34:57 +09:00
Dante
d2792cfac6 feat: add Storybook stories for Display components (#9702)
## Summary
- Add Storybook stories for `WidgetImageCompare` (Default,
WithBatchNavigation, SingleImageFallback, NoImages)
- WidgetGalleria and ImagePreview stories are deferred pending PrimeVue
removal

## Test plan
- [x] `pnpm typecheck` passes
- [x] `pnpm lint` passes
- [x] Verified all stories render correctly in Storybook

Figma ref:
https://www.figma.com/design/vALUV83vIdBzEsTJAhQgXq/Comfy-Design-System?node-id=55-1536

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-9702-feat-add-Storybook-stories-for-Display-components-31f6d73d365081e781faf3a8735aa3dc)
by [Unito](https://www.unito.io)

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-11 08:30:03 +09:00
AustinMroz
1d7a5b9e0b Mobile input tweaks (#9686)
- 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)
2026-03-09 23:08:42 -07:00
Dante
2ef354447d feat: add Storybook stories for Slider components (#9634)
## Summary
- Add Storybook stories for `Slider` component matching Figma design
system variants 1:1
- Stories at `Components/Slider`: **Default** and **Disabled**
- Add hover background
(`hover:bg-component-node-widget-background-hovered`) to
`WidgetInputNumberSlider` only

## Figma reference
[Comfy Design System —
Slider](https://www.figma.com/design/vALUV83vIdBzEsTJAhQgXq/Comfy-Design-System?node-id=2-9718&m=dev)

## Scope decisions
- **Hover**: Added to `WidgetInputNumberSlider.vue` only — other widgets
need separate verification before applying
- **Invalid**: Not implemented in `Slider.vue` — excluded until
component supports it

## Files changed
- `src/components/ui/slider/Slider.stories.ts` — new
-
`src/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberSlider.vue`
— add hover background

## Test plan
- [ ] `pnpm storybook` — verify Default and Disabled render under
`Components/Slider`
- [ ] Hover background visible on slider widget in app (e.g. KSampler
`cfg` slider)
- [ ] Other widget inputs (text, textarea, select) unchanged
- [ ] `pnpm typecheck` — passes
- [ ] `pnpm lint` — passes

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-10 11:49:47 +09:00
AustinMroz
2ccfb822b4 Restore hiding of linked inputs in app mode (#9671)
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)
2026-03-09 13:18:05 -07:00
Alexander Brown
3b5af4960f fix: show load widget inputs in media dropdown (#9670)
Main targeted, built on
https://github.com/Comfy-Org/ComfyUI_frontend/pull/9551

## Summary

Fix Load Image/Load Video input dropdown tabs not showing available
input assets in Vue node select dropdown.

## Changes

- **What**: Keep combo widget `options` object identity while exposing
dynamic `values` for cloud/remote combos.
- **What**: Remove temporary debug logging and restore clearer dropdown
filter branching.
- **What**: Remove stale `searcher`/`updateKey` prop plumbing in
dropdown menu/actions and update related tests.

## Review Focus

Verify `Load Image` / `Load Video` Inputs tab behavior and confirm
cloud/remote combo option values still update correctly.

Relates to #9551

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-9670-fix-show-load-widget-inputs-in-media-dropdown-31e6d73d36508148b845e18268a60c2a)
by [Unito](https://www.unito.io)

---------

Co-authored-by: bymyself <cbyrne@comfy.org>
Co-authored-by: Amp <amp@ampcode.com>
2026-03-09 12:49:47 -07:00
AustinMroz
7f0472fde4 Always use interior nodeId for app mode (#9669)
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)
2026-03-09 11:36:33 -07:00
AustinMroz
592f992d1d Even further app fixes (#9617)
- Allow dragging zoom pane with middle click
- Prevent selection of canvasOnly widgets
- These widgets would not display in app mode, so allow selection would
only cause confusion.
- Support displaying the error dialogue in app mode
- Add a somewhat involved mobile app mode error indication system
<img width="300" alt="image"
src="https://github.com/user-attachments/assets/d8793bbd-fff5-4b2a-a316-6ff154bae2c4"
/> <img width="300" alt="image"
src="https://github.com/user-attachments/assets/cb88b0f6-f7e5-409e-ae43-f1348f946b19"
/>

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-9617-Even-further-app-fixes-31d6d73d365081c891dfdfe3477cfd61)
by [Unito](https://www.unito.io)
2026-03-09 09:35:31 -07:00
pythongosssss
892a9cf2c5 fix: prevent showing outputs in app mode when no output nodes configured (#9625)
## 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
2026-03-08 17:36:15 -07:00
Christian Byrne
725a0a2b89 fix: remove timeouts from error toasts so they persist until dismissed (#9543)
## Summary

Remove `life` (timeout) property from all error-severity toast calls so
they persist until manually dismissed, preventing users from missing
important error messages.

## Changes

- **What**: Removed `life` property from 86 error toast calls across 46
files. Error toasts now use PrimeVue's default behavior (no
auto-dismiss). Non-error toasts (success, warn, info) are unchanged.
- Also fixed a pre-existing lint issue in `TaskListPanel.vue` (`import {
t } from '@/i18n'` → `useI18n()`)

## Review Focus

- One conditional toast in `useMediaAssetActions.ts` intentionally keeps
`life` because its severity alternates between `warn` and `error`

Fixes
https://www.notion.so/comfy-org/Implement-Remove-timeouts-for-all-error-toasts-31b6d73d365081cead54fddc77ae7c3d

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-9543-fix-remove-timeouts-from-error-toasts-so-they-persist-until-dismissed-31c6d73d365081fa8d30f6366e9bfe38)
by [Unito](https://www.unito.io)
2026-03-07 15:08:13 -08:00
AustinMroz
83ffaf30c8 Yet further app fixes (#9523)
- Prevent selection of note nodes
- Prevents selection  of nodes with errors
- A bit broader than the reported "can select missing nodes". A node
with an error is a node that can not execute and thus can not be used in
an app.
- Updates the typeform survey
- Add a collapsible list of all api nodes(/prices) contained in an app.
  - Needs to be prettied up for mobile still.
<img width="322" height="751" alt="image"
src="https://github.com/user-attachments/assets/ebfeeada-9b80-488e-88d6-feaa8bd53629"
/>

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-9523-Yet-further-app-fixes-31c6d73d365081de9150fbf2d3ec54dd)
by [Unito](https://www.unito.io)
2026-03-07 14:22:15 -08:00
pythongosssss
5bb742ac3a feat/fix: App mode QA fixes (#9530)
## Summary

Additional updates for app mode

## Changes

- **What**:
- Reposition toolbar button in app mode so it doesnt jump between modes
- Move node name to under title on input/output selection
- Change delete asset button text
- Change exit builder button icon

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-9530-feat-fix-App-mode-QA-fixes-31c6d73d365081738b5cc5beaf2cbd41)
by [Unito](https://www.unito.io)
2026-03-07 18:54:13 +00:00
pythongosssss
1058b7d12d feat/fix: App mode QA feedback 2 (#9511)
## Summary

Additional fixes and updates based on testing

## Changes

- **What**: 
- add warning to welcome screen & when sharing an app that has had all
outputs removed
- fix target workflow when changing mode via tab right click menu
- change build app text to be conditional "edit" vs "build" depending on
if an app is already defined
- update empty apps sidebar tab button text to make it clearer
- remove templates button from app mode (we will reintroduce this once
we have app templates)
- add "exit to graph" after applying default mode of node graph
- update cancel button to remove item from queue if it hasn't started
yet
- improve scoping of jobs/outputs to the current workflow [not perfect
but should be much improved]
- close sidebar tabs on entering app mode
- change tooltip to be under the workflow menu rather than covering the
button

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-9511-feat-fix-App-mode-QA-feedback-2-31b6d73d365081d59bbbc13111100d46)
by [Unito](https://www.unito.io)
2026-03-06 18:57:03 -08:00
jaeone94
8bfd93963f [style] Update error/subgraph node footer design with layered overlay approach (#9360)
## Summary

Refactors the error and subgraph node footer UI by extracting a
dedicated `NodeFooter` component and replacing the CSS `outline`
approach with a layered border overlay for selection/executing state
indicators.

## Changes

- **What**: Extracted `NodeFooter.vue` from `LGraphNode.vue` to
encapsulate the footer tab logic (subgraph enter, error, advanced
inputs). Replaced CSS `outline` with an absolutely-positioned border
overlay div for selection and executing state. Added a separate root
border overlay div for the node body border. Removed unused
`isTransparent` function from `colorUtil.ts`.
- **Dependencies**: None

## Review Focus

- The layered overlay approach (`absolute -inset-[3px] border-3`) for
selection/executing outlines vs the previous `outline-3` approach —
ensures the outline renders outside the node bounds correctly including
the footer area
- `NodeFooter` handles 4 cases: subgraph+error (dual tabs), error only,
subgraph only, advanced inputs — verify edge cases render correctly
- Resize handle bottom offset adjustments for nodes with footers
(`hasFooter`)

## Screenshots
<img width="1142" height="603" alt="image"
src="https://github.com/user-attachments/assets/e0d401f0-8516-4f5f-ab77-48a79530f4bd"
/>
<img width="1175" height="577" alt="image"
src="https://github.com/user-attachments/assets/bcf08fff-728a-491c-add9-5b96d2f3bfce"
/>

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-9360-style-Update-error-subgraph-node-footer-design-with-layered-overlay-approach-3186d73d365081b2ac31f166f4d1944a)
by [Unito](https://www.unito.io)

---------

Co-authored-by: github-actions <github-actions@github.com>
Co-authored-by: GitHub Action <action@github.com>
2026-03-06 17:51:08 -08:00
AustinMroz
7a01be388f More app fixes (#9432)
- Increased the z-index on app mode outputs so that they display above a
zoomed image
- The "view job" button on the job queued toast in mobile app mode will
take you to outputs instead of assets
- Image previews now have a minimum zoom of ~20% and a maximum zoom of
~50x
- The enter panel in linear mode now has a minimum size of ~1/5th screen
size
- In arrange mode, dragging to rearrange inputs will no longer cause a
horizontal scrollbar to appear.
- Videos will now display the first frame instead of a generic video
icon
- Muted/Bypassed nodes can no longer be selected as inputs/outputs, or
be displayed when in app mode.
- Linked input can no longer be selected or displayed
- Adds a share workflow button in app mode and wires up the existing
context menu

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-9432-More-app-fixes-31a6d73d365081509cd0ea74bfdc9b95)
by [Unito](https://www.unito.io)

---------

Co-authored-by: GitHub Action <action@github.com>
2026-03-06 13:41:52 -08:00
pythongosssss
4ff14b5eb9 feat/fix: App mode QA updates (#9439)
## Summary

Various fixes from app mode QA

## Changes

- **What**: 
- fix: prevent inserting nodes from workflow/apps sidebar tabs
- fix: hide json extension in workflow tab
- fix: hide apps nav button in apps tab when already in apps mode
- fix: center text on arrange page
- fix: prevent IoItems from "jumping" due to stale transform after drag
and drop op
- fix: refactor side panels and add custom stable pixel based sizing
- fix: make outputs/inputs lists in app builder scrollable
- fix: fix rerun not working correctly

- feat: add text to interrupt button
- feat: add enter app mode button to builder toolbar
- feat: add tooltip to download button on linear view
- feat: show last output of workflow in arrange tab if available
- feat: show download count in download all button, hide if only 1 asset
to download

## Review Focus

- Rerun - I am not sure why it was triggering widget actions, removing
it seemed like the correct fix
- useStablePrimeVueSplitter - this is a workaround for the fact it uses
percent sizing, I also tried switching to reka-ui splitters, but they
also only support % sizing in our version [pixel based looks to have
been added in a newer version, will log an issue to upgrade & replace
splitters with this]


## Screenshots (if applicable)

<img width="1314" height="1129" alt="image"
src="https://github.com/user-attachments/assets/c430f9d6-7c29-4853-803e-5b6fe7086fca"
/>
<img width="511" height="283" alt="image"
src="https://github.com/user-attachments/assets/b7e594d4-70a1-41e3-8ba1-78512f2a5c8b"
/>
<img width="254" height="232" alt="image"
src="https://github.com/user-attachments/assets/1d146399-39ea-4b0e-928c-340b74957535"
/>
<img width="487" height="198" alt="image"
src="https://github.com/user-attachments/assets/e2ba7f5d-8ff5-47f4-9526-61ebb99514b8"
/>
<img width="378" height="647" alt="image"
src="https://github.com/user-attachments/assets/a47a3054-9320-4327-bdc0-b0a16e19f83d"
/>
<img width="1016" height="476" alt="image"
src="https://github.com/user-attachments/assets/479ae50e-d380-4d56-a5c9-5df142b14ed0"
/>


┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-9439-feat-fix-App-mode-QA-updates-31a6d73d365081b38337d63207b88817)
by [Unito](https://www.unito.io)
2026-03-06 20:02:19 +00:00
Jin Yi
96fd25de5c feat: add Logo C fill and Comfy wave loading indicator components (#9433)
## Summary

Add SVG-based brand loading indicators (LogoCFillLoader,
LogoComfyWaveLoader) and use the wave loader as the app loading screen.

## Changes

- **What**: New `LogoCFillLoader` (bottom-to-top fill, plays once) and
`LogoComfyWaveLoader` (wave water-fill animation) components with
`size`, `color`, `bordered`, and `disableAnimation` props. Move all
loaders from `components/common/` to `components/loader/`. Use
`LogoComfyWaveLoader` in `App.vue` and `WorkspaceAuthGate.vue`. Render
loader above BlockUI overlay (z-1200) to prevent dim wash-out.
- **Dependencies**: None

## Review Focus

- SVG mask-based animation approach using `currentColor` for flexible
theming
- z-index layering: loader at z-1200 renders above PrimeVue BlockUI's
z-1100 modal overlay
- `disableAnimation` prop used in WorkspaceAuthGate to show static logo
outline during auth loading

## Screenshots (if applicable)

[loading_record.webm](https://github.com/user-attachments/assets/b34f7296-9904-4a42-9273-a7d5fda49d15)

Storybook stories added for both components under `Components/Loader/`.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-9433-feat-add-Logo-C-fill-and-Comfy-wave-loading-indicator-components-31a6d73d3650811cacfdcf867b1f835f)
by [Unito](https://www.unito.io)

---------

Co-authored-by: GitHub Action <action@github.com>
2026-03-06 00:32:20 -08:00
Alexander Brown
9ae85068eb feat: Transparent background for the Image and Video Previews (#9455)
## Summary

Less jarring appearance, especially with different aspect ratios or
Alpha channels.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-9455-feat-Transparent-background-for-the-Image-and-Video-Previews-31b6d73d3650819eaa82def10e66da21)
by [Unito](https://www.unito.io)

---------

Co-authored-by: GitHub Action <action@github.com>
2026-03-05 18:06:53 -08:00
Christian Byrne
ef4e4a69d5 fix: enable enforce-consistent-class-order tailwind lint rule (#9428)
## Summary

Enable `better-tailwindcss/enforce-consistent-class-order` lint rule and
auto-fix all 1027 violations across 263 files. Stacked on #9427.

## Changes

- **What**: Sort Tailwind classes into consistent order via `eslint
--fix`
- Enable `enforce-consistent-class-order` as `'error'` in eslint config
- Purely cosmetic reordering — no behavioral or visual changes

## Review Focus

Mechanical auto-fix PR — all changes are class reordering only. This is
the largest diff but lowest risk since it changes no class names, only
their order.

**Stack:** #9417#9427 → **this PR**

Fixes #9300 (partial — 3 of 3 rules)

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-9428-fix-enable-enforce-consistent-class-order-tailwind-lint-rule-31a6d73d3650811c9065f5178ba3e724)
by [Unito](https://www.unito.io)
2026-03-05 17:24:34 -08:00
Dante
60267fc64c refactor: unify image classification and fix cloud preview param handling (#9408)
## Summary

Addresses review feedback from PR #9298 and resolves the divergence
between `ResultItemImpl.isImage` and `appendCloudResParam`'s image
classification.

### Changes

- **Unify suffix-based classification**: Replace narrow
`isImageBySuffix` (gif/webp only), `isVideoBySuffix` (webm/mp4), and
`isAudioBySuffix` with `getMediaTypeFromFilename()` from
shared-frontend-utils, using the same `IMAGE_EXTENSIONS` set (png, jpg,
jpeg, gif, webp, bmp, avif, tif, tiff) that `appendCloudResParam` uses
- **imageCompare.ts**: Pass `record.filename` to `appendCloudResParam`
(was called without filename, bypassing image-extension guard)
- **imagePreviewStore.ts**: Use per-image `image.filename` instead of
first image's filename for all images in batch
- **LinearControls.vue**: Use `resultItem.filename` (already a string)
instead of `String(filename)` which converts undefined to `"undefined"`

### Related review comments

- [imageCompare.ts — missing
filename](https://github.com/Comfy-Org/ComfyUI_frontend/pull/9298#discussion_r2886137498)
- [imagePreviewStore.ts — per-image
filename](https://github.com/Comfy-Org/ComfyUI_frontend/pull/9298#discussion_r2886138718)
- [LinearControls.vue —
String(filename)](https://github.com/Comfy-Org/ComfyUI_frontend/pull/9298#discussion_r2886140159)
- [queueStore.ts — diverging image
classification](https://github.com/Comfy-Org/ComfyUI_frontend/pull/9298#discussion_r2886142886)

## Test plan

- [x] 66 unit tests pass (queueStore + cloudPreviewUtil)
- [x] `pnpm typecheck` passes
- [x] `pnpm lint` passes

- Fixes #9386

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-05 17:20:18 -08:00
Christian Byrne
47f2b63628 fix: prevent persistent loading state when cycling batches with identical URLs (#8999)
## Summary

Fix persistent loading/skeleton state when cycling through batch images
or videos that share the same URL (common on Cloud).

## Changes

- **What**: In `setCurrentIndex()` for both `ImagePreview.vue` and
`VideoPreview.vue`, only start the loader when the target URL differs
from the current URL. When batch items share the same URL, the browser
doesn't fire a new `load`/`loadeddata` event since `src` didn't change,
so the loader was never dismissed.
- Also fixes `VideoPreview.vue` navigation dots using hardcoded
`bg-white` instead of semantic `bg-base-foreground` tokens.

## Review Focus

This bug has regressed 3+ times (PRs #6521, #7094, #8366). The
regression tests specifically target the root cause — cycling through
identical URLs — to prevent future reintroduction.

Fixes
https://www.notion.so/comfy-org/Bug-Cycling-through-image-batches-results-in-persistent-loading-state-on-Cloud-30c6d73d3650816e9738d5dbea52c47d

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8999-fix-prevent-persistent-loading-state-when-cycling-batches-with-identical-URLs-30d6d73d36508180831edbaf8ad8ad48)
by [Unito](https://www.unito.io)

---------

Co-authored-by: Simula_r <18093452+simula-r@users.noreply.github.com>
2026-03-05 17:14:40 -08:00
Christian Byrne
1221756e05 fix: enable enforce-canonical-classes tailwind lint rule (#9427)
## Summary

Enable `better-tailwindcss/enforce-canonical-classes` lint rule and
auto-fix all 611 violations across 173 files. Stacked on #9417.

## Changes

- **What**: Simplify Tailwind classes to canonical forms via `eslint
--fix`:
  - `h-X w-X` → `size-X`
  - `overflow-x-hidden overflow-y-hidden` → `overflow-hidden`
  - and other canonical simplifications
- Enable `enforce-canonical-classes` as `'error'` in eslint config

## Review Focus

Mechanical auto-fix PR — all changes produced by `eslint --fix`. No
visual or behavioral changes; canonical forms are functionally
identical.

**Stack:** #9417 → **this PR** → PR 3 (class order)

Fixes #9300 (partial — 2 of 3 rules)

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-9427-fix-enable-enforce-canonical-classes-tailwind-lint-rule-31a6d73d365081a49340d7d4640ede45)
by [Unito](https://www.unito.io)
2026-03-05 17:07:46 -08:00
Dante
5843dced84 feat: add Storybook stories for WidgetInputText and WidgetTextarea (#9398)
Add Storybook stories for WidgetInputText and WidgetTextarea, aligned
with the Figma Design System spec.

Task: COM-15821

## Summary

Add comprehensive Storybook stories for text widget components and
implement missing Figma design system variants for WidgetInputText.

## Changes

- **WidgetInputText component enhancements**:
- Add `size` prop (`medium` | `large`) matching Figma size variants
(32px / 40px)
- Add `invalid` prop with destructive border style per Figma Invalid
state
- Add `loading` prop showing spinning loader icon per Figma Status state
  - Add hover background (`bg-component-node-widget-background-hovered`)
  - Fix `readonly` not being applied from `widget.options.read_only`
- **WidgetTextarea component fixes**:
  - Show copy button on hover for all states (not just read-only)
  - Apply `text-component-node-foreground` token to copy icon
  - Add hover background to wrapper
- **Storybook stories**:
- WidgetInputText: Default, Disabled, Invalid, Status, WithPlaceholder,
WithLabel stories
- WidgetTextarea: Default, Disabled, HiddenLabel, WithPlaceholder
stories
  - Interactive controls for size, readOnly, disabled, invalid, loading

## Review Focus

- Figma alignment: size/invalid/loading/status variants for
WidgetInputText
- Copy icon color token (`text-component-node-foreground`) for
light/dark theme support
- `layoutWidget` computed pattern to merge `borderStyle` with invalid
state

## Screenshots (if applicable)

<!-- Add screenshots or video recording to help explain your changes -->

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 08:42:53 +09:00
Christian Byrne
b2915ed42a refactor: extract shared createMockWidget factory for widget component tests (#9423)
## Summary

Extract a shared `createMockWidget` test factory to eliminate duplicated
`SimplifiedWidget` object construction across 13 widget component test
files.

## Changes

- **What**: Add `widgetTestUtils.ts` with a generic
`createMockWidget<T>` factory providing sensible defaults (`name`,
`type`, `options`). Refactor 13 test files to delegate to it via thin
local wrappers that supply component-specific defaults (combo values,
slider ranges, etc.).

## Review Focus

- The shared factory only covers `SimplifiedWidget`-based tests. Three
files using different base types (`NodeWidgets.test.ts`,
`useRemoteWidget.test.ts`, `useComboWidget.test.ts`) are intentionally
excluded.
- `mountComponent` helpers remain per-file since plugin/component setups
vary too much to share.

Fixes #5554

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-9423-refactor-extract-shared-createMockWidget-factory-for-widget-component-tests-31a6d73d36508159b65ee0e7b49212c3)
by [Unito](https://www.unito.io)

---------

Co-authored-by: Alexander Brown <drjkl@comfy.org>
Co-authored-by: GitHub Action <action@github.com>
2026-03-05 15:39:41 -08:00
Christian Byrne
e7588c33e1 refactor: rename imagePreviewStore to nodeOutputStore (#9416)
## Summary

Rename `imagePreviewStore.ts` → `nodeOutputStore.ts` to match the store
it houses (`useNodeOutputStore`, Pinia ID `nodeOutput`).

## Changes

- **What**: Rename file + test file, update all 21 import paths, mock
paths, and describe labels
- **Breaking**: None — exported symbol (`useNodeOutputStore`) and Pinia
store ID (`nodeOutput`) are unchanged

## Custom Node Ecosystem Audit

Searched the ComfyUI custom node ecosystem for `imagePreviewStore` and
`useNodeOutputStore`:
- **Not part of the public API** — neither filename nor export appear in
`comfyui_frontend_package` or `vite.types.config.mts`
- **1 external repo found:** `wallen0322/ComfyUI-AE-Animation` —
contains a full fork of the frontend source tree; it copies the file
internally and does not import from the published package. **No
breakage.**
- **No custom nodes import this store via the extension API.** This is a
safe internal-only rename.

## Review Focus

Pure mechanical rename — no logic changes. Verify no stale
`imagePreviewStore` references remain.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-9416-refactor-rename-imagePreviewStore-to-nodeOutputStore-31a6d73d3650816086c5e62959861ddb)
by [Unito](https://www.unito.io)

Co-authored-by: Alexander Brown <drjkl@comfy.org>
2026-03-05 13:52:50 -08:00
Christian Byrne
c817563cf0 feat: GLSLPreviewEngine + GLSL utility functions (#9200)
## Summary

Standalone WebGL2 rendering engine for client-side GLSL shader preview,
with utility functions that mirror the backend `nodes_glsl.py` detection
logic.

## Changes

- **What**: New `GLSLPreviewEngine` class (OffscreenCanvas + WebGL2),
`glslUtils.ts` (detectOutputCount, detectPassCount,
hasVersionDirective), and unit tests
- **GLSLPreviewEngine**: Fullscreen triangle via `gl_VertexID` (no VBO),
ping-pong FBOs for multi-pass rendering, MRT via `gl.drawBuffers()`,
blob output via `canvas.convertToBlob()`
- **glslUtils**: Pure functions ported from backend Python to
TypeScript, regex-based detection matching `_detect_output_count()` and
`_detect_pass_count()`

## Review Focus

- WebGL2 resource lifecycle (context loss, texture cleanup, FBO teardown
in `dispose()`)
- Ping-pong FBO logic for multi-pass shaders
- Engine tests are WebGL2-gated (`describe.skip` in happy-dom) — they
run in real browser environments

## Stacked PR

PR 2 of 3. Stacked on #9198 (fix: GLSLShader preview promotion).
PR 3: `feat/glsl-live-preview` (composable + LGraphNode.vue integration)

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-9200-feat-GLSLPreviewEngine-GLSL-utility-functions-3126d73d3650812fadc6df4a26387d0e)
by [Unito](https://www.unito.io)
2026-03-05 03:04:33 -08:00
pythongosssss
5376b7ed1e feat: App mode empty graph handling (#9393)
## Summary

Adds handling for entering app mode with an empty graph prompting the
user to load a template as a starting point

## Changes

- **What**: 
- app mode handle empty workflows, disable builder button, show
different message
- fix fitView when switching from app mode to graph

## Review Focus

Moving the fitView since the canvas is hidden in app mode until after
the workflow is loaded and the mode has been switched back to graph, I
don't see how this could cause any issues but worth a closer eye

## Screenshots (if applicable)

<img width="1057" height="916" alt="image"
src="https://github.com/user-attachments/assets/2ffe2b6d-9ce1-4218-828a-b7bc336c365a"
/>

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-9393-feat-App-mode-empty-graph-handling-3196d73d3650812cab0ce878109ed5c9)
by [Unito](https://www.unito.io)
2026-03-05 02:27:05 -08:00
pythongosssss
e0089d93d0 feat: App mode progress updates (#9375)
## Summary

- move progress bar below preview thumbnail instead of overlaying it
- add interactive pending placeholder
- fix: scope in-progress items to active workflow in output history

## Screenshots (if applicable)

<img width="209" height="86" alt="image"
src="https://github.com/user-attachments/assets/46590fdc-3df9-4a40-8492-a54e63e3f44c"
/>

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-9375-feat-App-mode-progress-updates-3196d73d3650817ea891c9e744893846)
by [Unito](https://www.unito.io)
2026-03-05 02:26:03 -08:00
Christian Byrne
493b1e42aa fix: enable no-deprecated-classes tailwind lint rule (#9417)
## Summary

Enable `better-tailwindcss/no-deprecated-classes` lint rule and auto-fix
all 103 violations across 65 files. First PR in a stacked series for
#9300.

## Changes

- **What**: Replace deprecated Tailwind v3 classes with v4 equivalents:
  - `rounded` → `rounded-sm` (85)
  - `flex-shrink-0` → `shrink-0` (16)
  - `flex-grow` → `grow` (2)
- Enable `no-deprecated-classes` as `'error'` in eslint config
- Update one test asserting on `'rounded'` class string

## Review Focus

Mechanical auto-fix PR — all changes produced by `eslint --fix`. No
visual or behavioral changes (Tailwind v4 aliases these classes
identically).

Fixes #9300 (partial — 1 of 3 rules)

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-9417-fix-enable-no-deprecated-classes-tailwind-lint-rule-31a6d73d3650819eaef4cf8ad84fb186)
by [Unito](https://www.unito.io)

Co-authored-by: Alexander Brown <drjkl@comfy.org>
2026-03-05 07:41:23 +00:00
Jin Yi
706060a2bf [refactor] Replace PrimeVue ProgressSpinner with Lucide loader icon (#9372)
## Summary
- Replace PrimeVue `ProgressSpinner` with Lucide `loader-circle` icon in
App.vue and WorkspaceAuthGate.vue
- Use white color for loading spinner for better visibility on dark
backgrounds
- Remove `primevue/progressspinner` imports and update related test

## Changes
- **App.vue**: Replace `ProgressSpinner` with
`icon-[lucide--loader-circle]`
- **WorkspaceAuthGate.vue**: Same replacement
- **WorkspaceAuthGate.test.ts**: Remove ProgressSpinner mock, use
`.animate-spin` selector

## Review Focus
- Visual consistency of white spinner on dark background during initial
load

<img width="1596" height="1189" alt="스크린샷 2026-03-04 오후 6 28 27"
src="https://github.com/user-attachments/assets/d703db74-4123-4328-912a-45ac45cf6eeb"
/>
<img width="1680" height="1304" alt="스크린샷 2026-03-04 오후 6 28 24"
src="https://github.com/user-attachments/assets/8026d10a-7e06-4f95-849c-bc891756823c"
/>

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-9372-refactor-Replace-PrimeVue-ProgressSpinner-with-Lucide-loader-icon-3196d73d3650815bb1d1d4554f7f744e)
by [Unito](https://www.unito.io)
2026-03-05 16:23:22 +09:00
jaeone94
f00a65ced0 feat: add collapse/expand all toggle to right panel tabs (#9333)
## Summary
Adds a collapse/expand all toggle button to all parameter and error tabs
in the right side panel, letting users quickly collapse or expand all
accordion sections at once.

## Changes
- **New component**: `CollapseToggleButton.vue` — a reusable icon button
(list-collapse / list-tree icon) with tooltip, bound via `v-model`
- **Error tab**: Toggle collapses/expands all error groups; per-group
state is now managed through `isSectionCollapsed` /
`setSectionCollapsed` helpers
- **Nodes tab** (`TabNodes.vue`): Per-node `collapseMap`; toggle
overrides per-section state; defaults to collapsed (nodes tab default
was already collapsed)
- **Normal inputs tab** (`TabNormalInputs.vue`): Per-node `collapseMap`
+ `advancedCollapsed`; toggle covers both normal and advanced sections;
defaults to collapsed when multiple nodes selected
- **Subgraph inputs tab** (`TabSubgraphInputs.vue`): Toggle covers both
main and advanced inputs sections
- **Global parameters tab** (`TabGlobalParameters.vue`): Toggle bound to
single `SectionWidgets`
- **i18n**: Added `g.collapseAll` and `g.expandAll` keys

## Review Focus
- `isAllCollapsed` getter in each tab: reads from the same per-section
state so the toggle accurately reflects current state rather than being
independently tracked
- `TabNormalInputs`: multi-node selection default collapse behaviour is
preserved through `isSectionCollapsed` fallback logic

## Screenshots
<img width="778" height="643" alt="image"
src="https://github.com/user-attachments/assets/04d07f32-5135-47f9-b029-78ca78a996fb"
/>


┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-9333-feat-add-collapse-expand-all-toggle-to-right-panel-tabs-3176d73d36508123ba22d6e81983bb1b)
by [Unito](https://www.unito.io)
2026-03-04 21:41:15 -08:00