Compare commits

...

501 Commits

Author SHA1 Message Date
Comfy Org PR Bot
3c11226fdd 1.32.2 (#6603)
Patch version increment to 1.32.2

**Base branch:** `main`

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6603-1-32-2-2a26d73d365081aba4a5f7bd09a45882)
by [Unito](https://www.unito.io)

---------

Co-authored-by: christian-byrne <72887196+christian-byrne@users.noreply.github.com>
Co-authored-by: github-actions <github-actions@github.com>
2025-11-05 14:07:51 -07:00
Christian Byrne
437c3b2da0 set config via feature flags (#6590)
In cloud environment, allow all the config values to be set by the
feature flag endpoint and be updated dynamically (on 30s poll). Retain
origianl behavior for OSS. On cloud, config changes shouldn't be changed
via redeploy and the promoted image should match the staging image.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6590-set-config-via-feature-flags-2a26d73d3650819f8084eb2695c51f22)
by [Unito](https://www.unito.io)

---------

Co-authored-by: DrJKL <DrJKL0424@gmail.com>
2025-11-05 13:45:21 -07:00
Christian Byrne
549ef79e02 update minimap and canvas bg to use menu color tokens (#6589)
Update minimap and graph canvas menu (bottom right) to use menu tokens.
Change canvas BG color on default dark theme.

<img width="3840" height="2029" alt="image"
src="https://github.com/user-attachments/assets/6d168981-df27-40c0-829c-59150b8a6a12"
/>


┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6589-wip-Style-graph-canvas-color-2a26d73d365081cb88c4c4bdb2b6d3a5)
by [Unito](https://www.unito.io)

---------

Co-authored-by: github-actions <github-actions@github.com>
Co-authored-by: Alexander Brown <drjkl@comfy.org>
2025-11-05 12:16:19 -08:00
Arjan Singh
a2ef569b9c feat(ComboWidget): add ability to have mapped inputs (#6585)
## Summary

1. Add a `getOptionLabel` option to `ComboWidget` so users of it can map
of custom labels to widget values. (e.g., `"My Photo" ->
"my_photo_1235.png"`).
2. Utilize this ability in Cloud environment to map user uploaded
filenames to their corresponding input asset.
3. Copious unit tests to make sure I didn't (AFAIK) break anything
during the refactoring portion of development.
4. Bonus: Scope model browser to only show in cloud distributions until
it's released elsewhere; should prevent some undesired UI behavior if a
user accidentally enables the assetAPI.

## Review Focus

Widget code: please double check the work there.

## Screenshots (if applicable)



https://github.com/user-attachments/assets/a94b3203-c87f-4285-b692-479996859a5a


┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6585-Feat-input-mapping-2a26d73d365081faa667e49892c8d45a)
by [Unito](https://www.unito.io)
2025-11-05 11:33:00 -08:00
Johnpaul Chiwetelu
265f1257e7 Updated node tokens (#6569)
This pull request updates the design system color tokens and refactors
node and widget component styles throughout the codebase to use new,
more consistent CSS variables. The changes ensure that node and widget
components are styled using unified design tokens, improving
maintainability and theme support for both light and dark modes.

**Design System Token Updates**

* Added new component and node-related CSS variables for background,
border, foreground, and widget states in both light and dark themes in
`style.css`.
[[1]](diffhunk://#diff-71b6b57a56095b04e47c797a5016149b76b27971cab04b93f033f1f846e0f5a0R246-R256)
[[2]](diffhunk://#diff-71b6b57a56095b04e47c797a5016149b76b27971cab04b93f033f1f846e0f5a0R354-R364)
* Introduced `--color-graphite-400` and adjusted several existing color
assignments for better palette consistency.
[[1]](diffhunk://#diff-71b6b57a56095b04e47c797a5016149b76b27971cab04b93f033f1f846e0f5a0R76)
[[2]](diffhunk://#diff-71b6b57a56095b04e47c797a5016149b76b27971cab04b93f033f1f846e0f5a0L304-R316)
* Updated semantic CSS variables to reference the new component/node
tokens for easier usage in components.
* Changed `--secondary-background-hover` to match
`--secondary-background` for improved hover consistency.

**Component Refactoring: Node and Widget Styles**

* Refactored Vue component classes and inline styles to use the new CSS
variables for node backgrounds, borders, and widget states, replacing
legacy variables like `bg-node-component-surface` and
`border-node-component-border` with `bg-component-node-background` and
`border-component-node-border`.
[[1]](diffhunk://#diff-a7744614cf842e54416047326db79ad81f7c7ab7bfb66ae2b46f5c73ac7d47f2L11-R14)
[[2]](diffhunk://#diff-a7744614cf842e54416047326db79ad81f7c7ab7bfb66ae2b46f5c73ac7d47f2L39-R39)
[[3]](diffhunk://#diff-a7744614cf842e54416047326db79ad81f7c7ab7bfb66ae2b46f5c73ac7d47f2L384-R384)
[[4]](diffhunk://#diff-19537a67677431ecdc9aec43877d28814e37edf0e45b0b0b484ea08832cad299L5-R13)
* Updated widget dropdowns, select, and input components to use
`text-component-node-foreground-secondary` for icons and foregrounds,
and new background variables for buttons and inputs.
[[1]](diffhunk://#diff-489229f88dfdfd5d883a3ef7fad6effa0790a18a831d5a9d84642dfb246962a2L29-R29)
[[2]](diffhunk://#diff-489229f88dfdfd5d883a3ef7fad6effa0790a18a831d5a9d84642dfb246962a2L100-R100)
[[3]](diffhunk://#diff-661a09de2721335e118a693b25d09922ada0ccbd0a51284691ed784fbe18874eL13-R13)
[[4]](diffhunk://#diff-2856391d03b0d38db1ed922b5034a05bc32e978c51f8175057d84cf82399d986L13-R13)
[[5]](diffhunk://#diff-4ee47848821aff71b6da0a1bb7fb8976e7879d706f71ff2ab3c5b046f5ef528cL10-R10)
[[6]](diffhunk://#diff-8b7ed2ce6194a262fb1e950294699cb8722630920362143a765802b602ae5fc8L106-R113)
[[7]](diffhunk://#diff-8b7ed2ce6194a262fb1e950294699cb8722630920362143a765802b602ae5fc8L119-R123)
[[8]](diffhunk://#diff-597a77456bf4b0c2d390fc46a930f37156b2f26ca030259b6703e5d39ff6b20eL37-R53)
[[9]](diffhunk://#diff-29348fa2e5b8cec1301a99bdec241379aeefc1747cceeb0c39b7df452ca635ffL7-R7)

**Service Layer Updates**

* Updated the color palette service mapping to use the new CSS variable
names for node and widget colors, ensuring consistency across the
application.
* 



https://github.com/user-attachments/assets/d9535f9a-b459-49bf-b2fe-ed872916fa4e



These changes collectively modernize the styling approach for node and
widget components, making it easier to maintain and extend theme
support.

---------

Co-authored-by: github-actions <github-actions@github.com>
2025-11-05 01:13:17 -07:00
Johnpaul Chiwetelu
fac86e35bf Drag vuenodes input (#6514)
This pull request introduces several improvements to Vue reactivity and
user experience in the graph node and widget system. The main focus is
on ensuring that changes to node and widget data reliably trigger
updates in Vue components, improving drag-and-drop support for nodes,
and enhancing widget value handling for better compatibility and
reactivity.

**Vue Reactivity Improvements:**

* In `useGraphNodeManager.ts`, node data updates now create a completely
new object and add a timestamp (`_updateTs`) to force Vue's reactivity
system to detect changes. Additionally, node data is re-set on the next
tick to guarantee component updates.
[[1]](diffhunk://#diff-f980db6f42cef913c3fe92669783b255d617e40b9ccef9a1ab9cc8e326ff1790L272-R280)
[[2]](diffhunk://#diff-f980db6f42cef913c3fe92669783b255d617e40b9ccef9a1ab9cc8e326ff1790R326-R335)
* Widget value composables (`useWidgetValue` and related helpers) now
accept either a direct value or a getter function for `modelValue`, and
always normalize it to a getter. Watches are updated to use this getter
for more reliable reactivity.
[[1]](diffhunk://#diff-92dc3c8b09ab57105e400e115196aae645214f305685044f62edc3338afa0911L13-R14)
[[2]](diffhunk://#diff-92dc3c8b09ab57105e400e115196aae645214f305685044f62edc3338afa0911R49-R57)
[[3]](diffhunk://#diff-92dc3c8b09ab57105e400e115196aae645214f305685044f62edc3338afa0911L82-R91)
[[4]](diffhunk://#diff-92dc3c8b09ab57105e400e115196aae645214f305685044f62edc3338afa0911L100-R104)
[[5]](diffhunk://#diff-92dc3c8b09ab57105e400e115196aae645214f305685044f62edc3338afa0911L117-R121)
[[6]](diffhunk://#diff-92dc3c8b09ab57105e400e115196aae645214f305685044f62edc3338afa0911L140-R144)
[[7]](diffhunk://#diff-0c43cefa9fb524ae86541c7ca851e97a22b3fd01f95795c83273c977be77468fL47-R47)
* In `useImageUploadWidget.ts`, widget value updates now use a new
array/object to ensure Vue detects the change, especially for batch
uploads.

**Drag-and-Drop Support for Nodes:**

* The `LGraphNode.vue` component adds drag-and-drop event handlers
(`dragover`, `dragleave`, `drop`) and visual feedback (`isDraggingOver`
state and highlight ring) for improved user experience when dragging
files onto nodes. Node callbacks (`onDragOver`, `onDragDrop`) are used
for custom validation and handling.
[[1]](diffhunk://#diff-a7744614cf842e54416047326db79ad81f7c7ab7bfb66ae2b46f5c73ac7d47f2L26-R27)
[[2]](diffhunk://#diff-a7744614cf842e54416047326db79ad81f7c7ab7bfb66ae2b46f5c73ac7d47f2R47-R49)
[[3]](diffhunk://#diff-a7744614cf842e54416047326db79ad81f7c7ab7bfb66ae2b46f5c73ac7d47f2R482-R521)

**Widget and Audio Upload Handling:**

* In `uploadAudio.ts`, after uploading an audio file, the widget's
callback is manually triggered to ensure Vue nodes update. There is also
a commented-out call to mark the canvas as dirty for potential future
refresh logic.
[[1]](diffhunk://#diff-796b36f2cafb906a5e95b5750ca5ddc1bf57a304d4a022e0bdaee04b4ee5bbc4R61-R65)
[[2]](diffhunk://#diff-796b36f2cafb906a5e95b5750ca5ddc1bf57a304d4a022e0bdaee04b4ee5bbc4R190-R191)

These changes collectively improve the reliability and responsiveness of
UI updates in the graph node system, especially in scenarios involving
external updates, drag-and-drop interactions, and batch widget value
changes.



https://github.com/user-attachments/assets/8e3194c9-196c-4e13-ad0b-a32177f2d062



┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6514-Drag-vuenodes-input-29e6d73d3650817da1b7ef96b61b752d)
by [Unito](https://www.unito.io)
2025-11-05 09:11:56 +01:00
Alexander Brown
693fbbd3e4 Mainification: Bring Onboarding in from rh-test (#6564)
## Summary

Migrate the onboarding / login / sign-up / survey pieces from `rh-test`
to `main`.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6564-WIP-Bring-Onboarding-in-from-rh-test-2a16d73d365081318483f993e3ca0f89)
by [Unito](https://www.unito.io)

---------

Co-authored-by: Jin Yi <jin12cc@gmail.com>
Co-authored-by: GitHub Action <action@github.com>
2025-11-04 16:48:58 -08:00
Christian Byrne
47688fe363 fix minimap navigation on touch devices (#6580)
Fixes minimap navigation (dragging the viewport box on the minimap) on
touch devices.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6580-fix-minimap-navigation-on-touch-devices-2a16d73d36508195b070da2b8e4b908a)
by [Unito](https://www.unito.io)

---------

Co-authored-by: Alexander Brown <drjkl@comfy.org>
2025-11-04 15:18:30 -07:00
AustinMroz
7c2a768d83 More forgiving connections in vue (#6565)
The previous link connection code uses
[closest](https://developer.mozilla.org/en-US/docs/Web/API/Element/closest)
to find a slot. Closest only checks parents, not siblings. Since the
sought element has no children, this meant connection to a slot required
the mouse be directly over the slot.

This is changed by finding the closest (parent) widget or slot, and then
querying for the slot. For simplicity, this means introducing an
`lg-node-widget` class. As a result, connections can be made by hovering
anywhere over a valid widget.


![vue-connections_00001](https://github.com/user-attachments/assets/e556ff3f-8cbb-4198-998d-9c2aadf2c73c)


Resolves #6488

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6565-More-forgiving-connections-in-vue-2a16d73d365081e1bf46f5d54ec382d6)
by [Unito](https://www.unito.io)
2025-11-04 13:45:14 -08:00
Christian Byrne
a4fc68a9eb make subscribe-to-run button responsive (#6581)
## Summary

Change to just "Subscribe" on mobile breakpoint.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6581-make-subscribe-to-run-button-responsive-2a16d73d365081e3a776cde0290432f3)
by [Unito](https://www.unito.io)
2025-11-04 13:33:22 -08:00
Christian Byrne
6c9743c1a6 fix(vite): use dynamic base URL based on cloud distribution (#6562)
## Summary
- Replace hardcoded `<base href="/">` in index.html with dynamic vite
base config
- Set `base: DISTRIBUTION === 'cloud' ? '/' : ''` in vite.config.mts
- Ensures proper asset loading across different deployment contexts

## Test plan
- [ ] Verify cloud distribution builds work correctly
- [ ] Verify localhost/desktop distributions work correctly
- [ ] Test asset loading in both contexts

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6562-fix-vite-use-dynamic-base-URL-based-on-cloud-distribution-2a06d73d365081c8b5d2e58870ebd14d)
by [Unito](https://www.unito.io)
2025-11-04 13:34:41 -07:00
Christian Byrne
4ab1e824b7 hide minimap on mobile by default (#6579)
If below large breakpoint, hide the minimap by default. If the user has
set the setting before (or closed minimap), their preference will
override this.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6579-hide-minimap-on-mobile-by-default-2a16d73d365081db9c66d209ee046097)
by [Unito](https://www.unito.io)

---------

Co-authored-by: github-actions <github-actions@github.com>
2025-11-04 13:11:58 -07:00
Jin Yi
adecd258b6 Fix cloud routing issues caused by incorrect api_base calculation (#6572)
## Summary
- Resolves multiple cloud environment issues when accessing
`/cloud/user-check` directly
- Fixes API routing problems that caused 304 Not Modified errors and
JSON parsing failures
- Maintains compatibility with subpath deployments for OSS users

## Root Cause
The `api_base` was incorrectly calculated as `/cloud` on cloud SPA
routes, causing API calls to use wrong paths like `/cloud/api/user`
instead of `/api/user`.

## Issues Fixed
-  `/cloud/user-check` direct access resulted in infinite loading
-  JSON parsing errors: `Unexpected token '<', "<!DOCTYPE "... is not
valid JSON`
-  304 Not Modified responses on `/cloud/api/user`,
`/cloud/api/settings/onboarding_survey`, `/cloud/api/system_stats`

## Solution
Added conditional `api_base` calculation in `ComfyApi` constructor:
- **Cloud SPA routes** (`/cloud/*`): Use empty `api_base` → API calls
use `/api/*` paths
- **Regular deployments**: Keep existing logic → Supports subpath
deployments

## Test Plan
- [x] Verify `/cloud/user-check` direct access works without infinite
loading
- [x] Verify all API calls return 200 instead of 304
- [x] Verify OSS subpath deployment compatibility unchanged
- [x] Test authentication flow completion

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

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6572-Fix-cloud-routing-issues-caused-by-incorrect-api_base-calculation-2a16d73d36508163aeb2cbf6347b427d)
by [Unito](https://www.unito.io)
2025-11-04 10:37:09 -08:00
Jin Yi
2c4280a28d feat: Add Open Graph and Twitter meta tags for cloud distribution (#6571)
## Summary
- Add meta tags plugin for social media previews (Twitter, Facebook,
LinkedIn, etc.)
- Include SEO meta tags (title, description, keywords)
- Only applies to cloud distribution (`DISTRIBUTION === 'cloud'`)

## Changes
- Added Open Graph meta tags for better social media link previews
- Added Twitter Card meta tags for Twitter sharing
- Added SEO meta tags (title, description, keywords)
- Added `og-image.png` for preview image
- Meta tags only inject when `DISTRIBUTION === 'cloud'` to avoid
affecting other distributions

## Test plan
- [x] Build with `DISTRIBUTION=cloud pnpm build`
- [x] Verify meta tags appear in built HTML
- [ ] Test link sharing on Twitter, Facebook, Slack
- [ ] Verify meta tags don't appear in localhost/desktop builds

🤖 Generated with Claude Code

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6571-feat-Add-Open-Graph-and-Twitter-meta-tags-for-cloud-distribution-2a16d73d365081e58c73f6009513b2bb)
by [Unito](https://www.unito.io)
2025-11-04 08:18:17 -07:00
Johnpaul Chiwetelu
c2150c6046 [fix] improve FormDropdownMenu scrolling behavior (#6570)
This pull request makes a minor UI adjustment to the
`FormDropdownMenu.vue` component. The change improves the dropdown
menu's usability by allowing its contents to scroll vertically and adds
a small top margin for better spacing.

* UI improvement: Changed the dropdown list container to use
`overflow-y-scroll` and added a top margin (`mt-2`) for improved
scrolling and spacing.
(`src/renderer/extensions/vueNodes/widgets/components/form/dropdown/FormDropdownMenu.vue`)-
Add top margin (mt-2) for better spacing
- Change overflow-hidden to overflow-y-scroll for scrollable list

## Current

https://github.com/user-attachments/assets/40cb4600-ff4e-41a5-89d6-a9aee48fa633

## Before


https://github.com/user-attachments/assets/fbe7a399-06d5-4cda-b850-f28b09f20d73

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6570-fix-improve-FormDropdownMenu-scrolling-behavior-2a16d73d3650815c883fcfeb7d9a3ace)
by [Unito](https://www.unito.io)
2025-11-03 23:25:05 -08:00
Benjamin Lu
cdffcfaa33 Add .pnpm-store to gitignore (#6566)
Adds .pnpm-store to gitignore. It's a cache folder that sometimes
appears in certain scenarios that would never need to be checked into
git.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6566-Add-pnpm-store-to-gitignore-2a16d73d365081319220f90516cfad3c)
by [Unito](https://www.unito.io)
2025-11-03 19:59:22 -08:00
Alexander Brown
eae93c2a79 Style: Pinned, Muted, Bypassed header indicators (#6530)
## Summary

Match (mostly) the header design for Modern Nodes' statuses.

## Changes

- **What**: Styling to match designs for more states.

## Screenshot

<img width="591" height="687" alt="image"
src="https://github.com/user-attachments/assets/bf8fe5d1-bd42-455c-ab20-668632ab5049"
/>

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

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6530-WIP-Pinned-and-Muted-styles-29f6d73d36508191bb89dc13674db8ba)
by [Unito](https://www.unito.io)

---------

Co-authored-by: GitHub Action <action@github.com>
Co-authored-by: github-actions <github-actions@github.com>
2025-11-03 18:14:38 -08:00
Alexander Brown
4cfa5b4b5d Feat: Doubleclick to toggle edit for Markdown (litegraph) (#6560)
## Summary

See https://github.com/Comfy-Org/ComfyUI_frontend/pull/6537, but for
litegraph widget.

Doesn't allow dragging the node through the rendered markdown yet, that
would be more complicated (DOMWidget complication)

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6560-Feat-Doubleclick-to-toggle-edit-for-Markdown-litegraph-2a06d73d36508189bf6eedd7cdeba6db)
by [Unito](https://www.unito.io)

---------

Co-authored-by: github-actions <github-actions@github.com>
2025-11-03 16:28:35 -08:00
Christian Byrne
a10c01db4c Conditionally generate vendor import map outside cloud builds (#6559)
## Summary
- gate vendor import-map generation behind non-cloud distributions
- keep cloud bundles slim while preserving prebuilt chunks for
desktop/localhost builds
- document the conditional to guide future CDN/cache work

## Testing
- [ ] DISTRIBUTION=cloud pnpm build
- [ ] DISTRIBUTION=desktop pnpm build

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6559-Conditionally-generate-vendor-import-map-outside-cloud-builds-2a06d73d3650819d96a0e2b6c006baf1)
by [Unito](https://www.unito.io)
2025-11-03 15:36:30 -07:00
AustinMroz
f38255bff4 Fix sizing of image previews in vue mode (#6557)
#6352 apparently introduced a regression in scaling for image previews

This PR fixes image scaling and, as a more opinionated change, makes the
image dimensions stay adjacent to the image
<img width="1283" height="668" alt="image"
src="https://github.com/user-attachments/assets/56fabe30-665f-45bb-9ed5-1c1bb79d7e54"
/>

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6557-Fix-sizing-of-image-previews-in-vue-mode-2a06d73d365081e4b2a1ebff7b42e9a8)
by [Unito](https://www.unito.io)

---------

Co-authored-by: github-actions <github-actions@github.com>
2025-11-03 14:27:58 -07:00
AustinMroz
befa84130b Fix empty combo widget for missing models in vue (#6555)
When a loaded workflow uses a model which does not exist, the combo
widget would previously display as empty. This PR
- Sets the placeholder value to the localValue, so that an invalid
selection still displays
- Sets the selection as invalid when the value is not in valid options
so that it displays in red.

<img width="817" height="200" alt="image"
src="https://github.com/user-attachments/assets/be7d6372-3957-4fd6-ac1e-778f058ec0ba"
/>

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6555-Austin-vue-combo-missing-2a06d73d36508181a96ddd5c604efba4)
by [Unito](https://www.unito.io)

---------

Co-authored-by: github-actions <github-actions@github.com>
2025-11-03 14:26:17 -07:00
Arjan Singh
dc8a0e04a5 fix(AssetCard): use tooltip instead of title for overflow text (#6556)
## Summary

The default `title` attribute was too small and too slow to show for
users. We were getting complaints about not being able to see the full
name of the model.

Using [Prime Vue's tooltip](https://primevue.org/tooltip) instead.

## Screenshots (if applicable)

Before:

<img width="480" height="246" alt="image"
src="https://github.com/user-attachments/assets/db4d1cc7-9e9a-40b3-bf16-57f2def93726"
/>


After:

<img width="518" height="720" alt="image"
src="https://github.com/user-attachments/assets/500389e5-c781-4d3b-b62e-1e72d2b7e2c6"
/>

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6556-fix-AssetCard-use-tooltip-instead-of-title-for-overflow-text-2a06d73d365081939dc0d863b05f1e90)
by [Unito](https://www.unito.io)
2025-11-03 19:59:42 +00:00
Christian Byrne
9cd7a39f09 load template workflow via URL query param (#6546)
## Summary

Adds support for loading templates via URL query parameters. Users can
now share direct links to templates.

To test:

1. checkout this branch
2. start dev server on port 5173
3. go to http://localhost:5173/?template=image_qwen_image_edit_2509

**Examples:**
- `/?template=default` - Loads template with default source
- `/?template=flux_simple&source=custom` - Loads from custom source

Includes error handling with toast notifications for invalid templates
and comprehensive test coverage.

---------

Co-authored-by: Christian Byrne <c.byrne@comfy.org>
2025-11-02 21:23:56 -08:00
Christian Byrne
4810b5728a [feat] Add Partner Nodes virtual category and rename license filter (#6542)
This PR adds a 'Partner Nodes' virtual category that filters templates
where OpenSource === false, and renames the 'License' filter to 'Runs
on' with values 'ComfyUI' and 'Partner API'. The implementation is
backward compatible and works like the existing 'Basics' category - it
filters templates from any category without duplication. The filter
logic now uses the explicit OpenSource field instead of heuristic
detection. This change coordinates with upcoming workflow_templates repo
updates that will move API templates to GENERATION TYPE categories and
add the OpenSource field to all API node templates.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6542-feat-Add-Partner-Nodes-virtual-category-and-rename-license-filter-29f6d73d36508111a85bdf5017f0a100)
by [Unito](https://www.unito.io)

---------

Co-authored-by: Claude <noreply@anthropic.com>
2025-11-02 20:50:16 -08:00
Christian Byrne
6fe88dba54 fix(telemetry): remove redundant run tracking; keep click analytics + single execution event (#6518)
## Summary
Deduplicates workflow run telemetry and keeps a single source of truth
for execution while retaining click analytics and attributing initiator
source.

- Keep execution tracking in one place via `trackWorkflowExecution()`
- Keep click analytics via `trackRunButton(...)`
- Attribute initiator with `trigger_source` = 'button' | 'keybinding' |
'legacy_ui'
- Remove pre‑tracking from keybindings to avoid double/triple counting
- Update legacy UI buttons to emit both click + execution events (they
bypass commands)

## Problem
PR #6499 added tracking at multiple layers:
1) Keybindings tracked via a dedicated method and then executed a
command
2) Menu items tracked via a dedicated method and then executed a command
3) Commands also tracked execution

Because these ultimately trigger the same command path, this produced
duplicate (sometimes triplicate) events per user action and made it hard
to attribute initiator precisely.

## Solution
- Remove redundant tracking from keybindings (and previous legacy menu
handler)
- Commands now emit both:
- `trackRunButton(...)` (click analytics, includes `trigger_source` when
provided)
- `trackWorkflowExecution()` (single execution start; includes the last
`trigger_source`)
- Legacy UI buttons (which call `app.queuePrompt(...)` directly) now
also emit both events with `trigger_source = 'legacy_ui'`
- Add `ExecutionTriggerSource` type and wire `trigger_source` through
provider so `EXECUTION_START` matches the most recent click intent

### Telemetry behavior after this change
- `RUN_BUTTON_CLICKED` (click analytics)
  - Emitted when a run is initiated via:
    - Button: `trigger_source = 'button'`
    - Keybinding: `trigger_source = 'keybinding'`
    - Legacy UI: `trigger_source = 'legacy_ui'`
- `EXECUTION_START` (execution lifecycle)
- Emitted once per run at start; includes `trigger_source` matched to
the click intent above
- Paired with `EXECUTION_SUCCESS` / `EXECUTION_ERROR` from execution
handlers

## Benefits
-  Accurate counts by removing duplicated run events
-  Clear initiator attribution (button vs keybinding vs legacy UI)
-  Separation of “intent” (click) vs. “lifecycle” (execution)
-  Simpler implementation and maintenance

## Files Changed (high level)
- `src/services/keybindingService.ts`: Route run commands with
`trigger_source = 'keybinding'`
- `src/components/actionbar/ComfyRunButton/ComfyQueueButton.vue`: Send
`trigger_source = 'button'` to commands
- `src/scripts/ui.ts`: Legacy queue buttons emit `trackRunButton({
trigger_source: 'legacy_ui' })` and `trackWorkflowExecution()`
- `src/composables/useCoreCommands.ts`: Commands emit
`trackRunButton(...)` + `trackWorkflowExecution()`; accept telemetry
metadata
- `src/platform/telemetry/types.ts`: Add `ExecutionTriggerSource` and
optional `trigger_source` in click + execution payloads
- `src/platform/telemetry/providers/cloud/MixpanelTelemetryProvider.ts`:
Carry `trigger_source` from click → execution and reset after use
- `src/stores/commandStore.ts`: Allow commands to receive args (for
telemetry metadata)
- `src/extensions/core/groupNode.ts`: Adjust command function signatures
to new execute signature

## Related
- Reverts the multi‑event approach from #6499
- Keeps `trackWorkflowExecution()` as the canonical execution event
while preserving click analytics and initiator attribution with
`trackRunButton(...)`

┆Issue is synchronized with this Notion page by Unito

---------

Co-authored-by: Christian Byrne <c.byrne@comfy.org>
Co-authored-by: Alexander Brown <drjkl@comfy.org>
Co-authored-by: Benjamin Lu <benjaminlu1107@gmail.com>
Co-authored-by: Claude <noreply@anthropic.com>
2025-11-02 19:48:21 -08:00
Christian Byrne
8df0a3885d [feat] Rename license filter to 'Runs On' filter in template selector (#6543)
## Summary

Renamed the templates license filter to better reflect its actual
purpose - showing where a template executes (locally in ComfyUI vs
external/remote API).

The current "License" filter has been causing confusion with model
licensing terms (e.g., Apache vs flux-dev licensing). This PR clarifies
the filter's purpose by renaming it to "Runs On" and updating the
options to be more descriptive of inference location.

<img width="196" height="230" alt="image"
src="https://github.com/user-attachments/assets/8cbea263-f399-4945-82c1-357ec185f5a7"
/>

<img width="861" height="597" alt="image"
src="https://github.com/user-attachments/assets/af116876-d7a5-49c5-b791-1fda637ff3a3"
/>


## Changes

- **Filter name**: "License" → "Runs On"
- **Filter options**: 
  - "Open Source" → "ComfyUI"
  - "Closed Source (API Nodes)" → "External or Remote API"
- **Icon**: Changed from `file-text` to `server` for better visual
representation
- **Variable naming**: Updated all related variables, types, and tests
to use `runsOn` naming convention
- **Telemetry**: Updated metadata to track `selected_runs_on` instead of
`selected_licenses`

## Why "Runs On"?

- **Clear intent**: Users want to know if a template runs locally or
requires an API call
- **Avoids confusion**: Separates the concept from model licensing terms
- **Inclusive wording**: "Remote" is included alongside "API" to help
users who may not be familiar with API terminology
- **Cloud-agnostic**: "Runs On" works whether the app itself is running
locally or in the cloud

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6543-feat-Rename-license-filter-to-Runs-On-filter-in-template-selector-29f6d73d3650811f935bc1f3fce7d7ad)
by [Unito](https://www.unito.io)
2025-11-02 16:35:42 -08:00
Jin Yi
8dfdac3fc4 [UI] Redesign media asset card layout (#6541)
## Summary
- Reorganized media asset card layout for improved UX
- Moved duration/format chips to top-left (visible in default state)
- Replaced multiple action buttons with zoom + more menu pattern
- Repositioned output count to top-right for better visibility

## Changes
1. **Duration & format chips**: Moved from bottom-left to top-left,
shown when card is not hovered and media is not playing
2. **Media actions**: Simplified to zoom button + more menu (contains
delete, download options), shown on hover or during playback
3. **Output count**: Relocated from bottom-right to top-right for
consistent positioning

## Test plan
- [x] Verify duration/format chips appear in top-left when card is idle
- [x] Confirm action buttons (zoom + more) appear on hover
- [x] Check output count displays correctly in top-right
- [x] Test transitions between hover/non-hover states
- [x] Verify media playback doesn't interfere with UI elements

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

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6541-UI-Redesign-media-asset-card-layout-29f6d73d365081eb8614d5dc9b2dc214)
by [Unito](https://www.unito.io)
2025-11-02 14:06:55 -08:00
Comfy Org PR Bot
0692253e90 1.32.1 (#6547)
Patch version increment to 1.32.1

**Base branch:** `main`

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6547-1-32-1-29f6d73d365081259588c91da6aa87c5)
by [Unito](https://www.unito.io)

---------

Co-authored-by: christian-byrne <72887196+christian-byrne@users.noreply.github.com>
Co-authored-by: github-actions <github-actions@github.com>
2025-11-02 13:42:44 -08:00
Christian Byrne
3cded2c45f [fix] Remove unreliable CI wait logic from Claude review workflow (#6548)
Removes the wait-for-ci job that has been causing random workflow skips
and reliability issues due to race conditions with CI check creation.
Multiple attempts to fix timing issues have resulted in ongoing edge
cases, so this simplifies the workflow to run immediately when the
claude-review label is added. Reviews can now run in parallel with CI
checks, and while they may occasionally run on PRs with failing CI, the
review feedback is often valuable regardless of CI status.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6548-fix-Remove-unreliable-CI-wait-logic-from-Claude-review-workflow-29f6d73d36508125910ef4feba5abdf4)
by [Unito](https://www.unito.io)
2025-11-02 13:41:36 -08:00
pythongosssss
fd236b3587 UI color updates & tweaks (#6381)
## Summary

Small updates to the UI to make it more visually distinct from the graph
nodes and improving the login button

## Changes

- **What**: 
  - Improve theme support with dynamic colors
  - Standardize surface colors/borders
  - Add shadows to all floating UI elements
  - Change side toolbar to be docked by default
  - Decrease side toolbar width
  - Change login button to icon only
  - Update C button to be more distinctive

## Review Focus

- Theme tokens, I am not sure what the overall plan for how these tokens
will be supported for custom user palettes. I've implemented a method
where default variables are chosen that look nice over all existing
themes, but they can be overridden.

## Screenshots

Dark theme updated color
<img width="958" height="615" alt="image"
src="https://github.com/user-attachments/assets/a2c540cf-6c05-4ad3-996b-8b7b80df8d2d"
/>

Themed & updated menu button (active vs hover vs default)
<img width="58" height="338" alt="github"
src="https://github.com/user-attachments/assets/90244ee2-d5ee-4b26-9d99-f4b8439aa372"
/><img width="58" height="338" alt="nord"
src="https://github.com/user-attachments/assets/053e8e7b-d639-4b72-92d2-ec7e2167aab8"
/><img width="58" height="338" alt="arc"
src="https://github.com/user-attachments/assets/3caeb07b-d41b-4d88-83b4-ef8d45d4e5a6"
/><img width="58" height="338" alt="solarized"
src="https://github.com/user-attachments/assets/6ebf6afb-ec3a-436b-90eb-bda40be1c79f"
/><img width="58" height="338" alt="light"
src="https://github.com/user-attachments/assets/fbb7f96a-b722-40c5-86fa-a0732e166972"
/><img width="58" height="338" alt="dark"
src="https://github.com/user-attachments/assets/5459208b-9256-4c55-9373-169e9cd9f09a"
/>

With labels
<img width="58" height="394" alt="labels"
src="https://github.com/user-attachments/assets/f97ee354-35cd-42b8-896f-57ac39644c1d"
/>

Logged out (only visible on desktop)
<img width="323" height="48" alt="logged out"
src="https://github.com/user-attachments/assets/75b71420-0c1b-446f-8cdd-43c68674d346"
/>

Logged in
<img width="355" height="48" alt="logged in"
src="https://github.com/user-attachments/assets/324fd133-a793-49dd-844a-f93dd5d1effb"
/>


┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6381-UI-color-updates-tweaks-29b6d73d3650816384d2ef5617a776a0)
by [Unito](https://www.unito.io)

---------

Co-authored-by: GitHub Action <action@github.com>
Co-authored-by: github-actions <github-actions@github.com>
2025-11-02 12:43:21 -08:00
Jin Yi
e1f707ffe2 fix: Use environment-specific log API endpoints for Cloud and OSS (#6539)
## Problem
401 authentication errors were persistently occurring when calling
log-related APIs in the Cloud environment.

## Root Cause
- Frontend was calling `/internal/logs/*` endpoints in all environments
- Cloud backend provides public APIs at `/api/logs/*` (no authentication
required)
- OSS (open source) backend uses `/internal/logs/*` paths
- This caused Cloud to call non-existent paths → resulting in 401 errors

## Solution
Modified to use appropriate API endpoints based on environment using the
`isCloud` flag:
- Cloud environment: Use `/api/logs/*`
- OSS environment: Use `/internal/logs/*`

## Changes
- `getLogs()`: Added environment-specific URL branching
- `getRawLogs()`: Added environment-specific URL branching
- `subscribeLogs()`: Added environment-specific URL branching

## Testing
- [x] Verified log functionality works correctly in local (OSS)
environment
- [x] Confirmed 401 errors are resolved in Cloud environment

## Related Issues
This resolves the 401 errors tracked in Sentry for log API endpoints.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6539-fix-Use-environment-specific-log-API-endpoints-for-Cloud-and-OSS-29f6d73d365081da9e77f8b55556ca81)
by [Unito](https://www.unito.io)
2025-11-02 09:45:45 -08:00
Alexander Brown
fddebd4a73 Feat: Nicer click behavior for the Markdown Widget (#6537)
## Summary

Double click instead of single to edit. No longer changes background
color dramatically on hover.

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

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6537-Feat-Nicer-click-behavior-for-the-Markdown-Widget-29f6d73d365081a49119fcc2cc86fc11)
by [Unito](https://www.unito.io)
2025-11-02 08:42:45 -08:00
Christian Byrne
704de20245 fix: remove unsafe type assertions in subscription credits (#6536)
## Summary

Removes unsafe `as any` type assertions from subscription credits
composable now that the OpenAPI spec has been updated with the missing
balance breakdown fields.

The `GetCustomerBalance` API response now includes:
- `cloud_credit_balance_micros` - Monthly subscription credits
- `prepaid_balance_micros` - Pre-paid top-up credits

These fields were previously accessed with `as any` because they weren't
in the TypeScript type definitions. With the OpenAPI spec update (PR
#6531), these fields are now properly typed.


┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6536-fix-remove-unsafe-type-assertions-in-subscription-credits-29f6d73d365081ffae52cc85c01c139b)
by [Unito](https://www.unito.io)

Co-authored-by: Christian Byrne <c.byrne@comfy.org>
2025-11-02 00:15:02 -07:00
Benjamin Lu
feda2d47b0 feat(telemetry): track settings changes (#6504)
Summary
- Add telemetry event for settings changes when the global settings
dialog is open
- Clarify variable names in settings store (`settingParameter`,
`settingType`) for readability
- Introduce `SettingChangedMetadata` and
`TelemetryEvents.SETTING_CHANGED`
- Implement `trackSettingChanged` in Mixpanel provider
- Add focused unit test to verify telemetry triggers when settings
dialog is open vs closed

Quality
- Ran `pnpm lint:fix` and `pnpm typecheck`
- Unit tests pass locally

Notes
- Event fires only when the settings dialog is open (uses
`useDialogStore().isDialogOpen('global-settings')`)
- OSS builds are unaffected (`useTelemetry()` returns null)

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6504-feat-telemetry-track-settings-changes-clarify-names-add-unit-test-29e6d73d3650815ea919d832b310cc46)
by [Unito](https://www.unito.io)
2025-11-01 23:52:04 -07:00
Christian Byrne
2d22c9f7f0 Update diffusion_models display to 'Diffusion' in asset browser (#6533)
## Summary
- Update the display label for `diffusion_models` category from
"Diffusion Models" to "Diffusion" in the asset browser/gallery UI
- Added special case handling in `formatCategoryLabel` function for
cleaner display
- This is a display-only change that does not affect underlying data
structures or API calls

## Test plan
- [x] Unit tests pass
- [x] Linter passes
- [x] Formatter passes
- [x] Type checking passes

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

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6533-Update-diffusion_models-display-to-Diffusion-in-asset-browser-29f6d73d365081009fa5cb396861570b)
by [Unito](https://www.unito.io)

Co-authored-by: Christian Byrne <chrbyrne96@gmail.com>
Co-authored-by: Claude <noreply@anthropic.com>
2025-11-01 23:26:02 -07:00
Comfy Org PR Bot
d808998863 [chore] Update Comfy Registry API types from comfy-api@36df059 (#6531)
## Automated API Type Update

This PR updates the Comfy Registry API types from the latest comfy-api
OpenAPI specification.

- API commit: 36df059
- Generated on: 2025-11-01T23:56:46Z

These types are automatically generated using openapi-typescript.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6531-chore-Update-Comfy-Registry-API-types-from-comfy-api-36df059-29f6d73d3650818da7e2e6802727213e)
by [Unito](https://www.unito.io)

Co-authored-by: christian-byrne <72887196+christian-byrne@users.noreply.github.com>
2025-11-01 23:10:46 -07:00
Benjamin Lu
7bf8e5af38 feat(telemetry): add tracking for sidebar, run menu, dialogs, subgraphs, settings and credits (#6511)
Summary
- Add comprehensive telemetry for key UI interactions using existing
telemetry hooks (cloud-enabled, no-op in OSS):

Sidebar and top-level
- Node library button: `node_library`
- Model library button: `model_library`
- Workflows button: `workflows`
- Assets/Media button: `assets`
- Templates button: `templates`
- Keyboard Shortcuts: `keyboard_shortcuts`
- Console: `console`
- Help Center: `help_center`
- Settings (from Comfy logo menu): `settings_menu`

Floating canvas menu
- Minimap toggle: `minimap_toggle`
- Hide links toggle: `hide_links`

Run button and queue
- Run button handle (drag): `run_button_handle`
- Run mode selection: `run_instant`, `run_on_change`
- Queue multiple: `queue_multiple` fires on each run when batch count >
1 (moved from batch-count-change to run-time, per guidance)

Error dialogs
- Close (X/mask/ESC): `error_dialog_close` via dialog onClose
- Show report: `error_show_report`
- Help fix this: `error_help_fix_this`
- Find issues: `error_find_issues`

Nodes / Subgraphs
- Selection toolbox “Node info”: `node_info`
- Enter subgraph (node header enter): `open_subgraph`
- Subgraph breadcrumb navigation: `subgraph_breadcrumb_item` and
`subgraph_breadcrumb_root`

Settings / Credits / Search
- Settings menu button (under Comfy logo): `settings_menu`
- Purchase credits (Settings > Credits panel): tracked via existing
`trackAddApiCreditButtonClicked`
- Purchase credits (Avatar popover Top Up): tracked via existing
`trackAddApiCreditButtonClicked`
- Debounced search telemetry already present for node search and
template filters; left as-is

Notes and answers
- Error dialog onClose: only fires when the dialog actually closes (X,
mask, ESC, or programmatic close). “Show report” and “Help fix this” do
not close the dialog; they each emit their own events.
- Telemetry is behind the cloud provider; calls are optional
(`useTelemetry()?.…`). OSS builds send nothing.

Open questions / follow-ups
- Primary Run button click: today cloud-only `trackRunButton` exists; we
can also emit a UI-level `run` click (`UI_BUTTON_CLICKED` style)
alongside it if desired. Confirm preference and I can add it.
- Subgraph usage richness: if we want structured analytics (e.g.,
action, depth, subgraph id, node count), I can add a dedicated provider
method and include richer metadata at enter/breadcrumb.
- Optional parity: track the Comfy menu’s “Browse Templates” item in
addition to the sidebar Templates button.

Quality
- Ran `pnpm lint:fix` and `pnpm typecheck`; both pass locally.

Implementation details
- All handlers refactored to named functions where needed; used `void`
for intentionally unawaited async calls per lint rules.
- Event names kept consistent in `button_id` strings; happy to align to
a different naming scheme if you prefer.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6511-feat-telemetry-add-tracking-for-sidebar-run-menu-dialogs-subgraphs-settings-and-cre-29e6d73d365081a1b8b4fdfbbf40e18b)
by [Unito](https://www.unito.io)

---------

Co-authored-by: Christian Byrne <cbyrne@comfy.org>
Co-authored-by: Claude <noreply@anthropic.com>
2025-11-01 23:09:41 -07:00
Alexander Brown
31e405abc8 Feat: Loading state while loading dropped workflows (#6464)
## Summary

Indicate to the user that we're hard at work parsing their JSON behind
the scenes.

## Changes

- **What**: Turn on the loading spinner while processing a workflow
- **What else**: Refactored the code around figuring out how to grab the
data from the file to make this easier

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6464-WIP-Loading-state-for-dropped-workflows-29c6d73d3650812dba66f2a7d27a777c)
by [Unito](https://www.unito.io)

---------

Co-authored-by: GitHub Action <action@github.com>
2025-11-01 22:50:29 -07:00
AustinMroz
0e9c29e7b7 Fix node header height for subgraphs (#6525)
Node headers are slightly too tall on subgraph nodes due to the height
of the "enter subgraph" button. This is fixed by explicitly setting the
height of the enter subgraph button.
<img width="991" height="171" alt="image"
src="https://github.com/user-attachments/assets/34d049a7-1dee-46c2-ab28-169c3f546347"
/>

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6525-Fix-node-header-height-for-subgraphs-29f6d73d3650810eaf95e7a6d107c2bd)
by [Unito](https://www.unito.io)
2025-11-01 22:43:38 -07:00
Jin Yi
8d27c4fb1b fix: Change h-screen to h-svh in BaseViewTemplate (#6529)
## Summary
- Fixed height issue in BaseViewTemplate by changing `h-screen` to
`h-svh` for better viewport handling

## Changes
- Updated `BaseViewTemplate.vue` to use `h-svh` (small viewport height)
instead of `h-screen`
- This ensures proper height calculation on mobile devices with dynamic
browser UI

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

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6529-fix-Change-h-screen-to-h-svh-in-BaseViewTemplate-29f6d73d36508117871cd4ff165c2109)
by [Unito](https://www.unito.io)
2025-11-01 22:42:37 -07:00
AustinMroz
02aaf577ec Fix inability to select image from batch in vue (#6521)
Selecting a new image from a batch sets isLoading to true, but
handleImageLoad is never triggered so the image never displays.

Swapping to a different image from a batch is currently the only place
isLoading is set to true. This change, even if temporary, results in a
good chunk of dead code.

To my understanding, ImagePreviews are always object URLs and should
never need to load, so I don't foresee the loading placeholder being
needed here.

Resolves #6458

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6521-Fix-inability-to-select-image-from-batch-in-vue-29e6d73d36508162abeaeece7c5e0eed)
by [Unito](https://www.unito.io)
2025-11-01 16:56:41 -07:00
AustinMroz
431594a6fc Fix partial execution inside subgraphs (#6487)
`getExecutionIdsForSelectedNodes` is only used for partial execution.
The prior implementation solved the wrong problem. Given a list of
nodes, it would explore into subgraphs and return a list of partial
ExecutionIds for all contained nodes. Because this does not resolve the
partial execution path to the current subgraph, this is incorrect when
the current graph is not the root graph. Woefully, this incorrect
functionality is never useful because the recursive exploration only
applies to subgraph nodes which never satisfy the outputNode filter
applied by the parent function.

An extra function is used to correctly append the parent execution path,
but the existing, probably never useful code for recursively collecting
children is otherwise left in place.

Resolves #6480

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6487-Fix-partial-execution-inside-subgraphs-29d6d73d36508197924bfb3a0fb6699e)
by [Unito](https://www.unito.io)
2025-11-01 16:08:57 -07:00
AustinMroz
dffb07c745 Fix badge overflow (#6516)
Prevents node badges from overflowing out of the node header in vue mode
<img width="1157" height="593" alt="image"
src="https://github.com/user-attachments/assets/005e13e7-8bcc-46c4-9531-e6eba237cf60"
/>

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6516-Fix-badge-overflow-29e6d73d365081328250cbeb3ebbb670)
by [Unito](https://www.unito.io)

---------

Co-authored-by: github-actions <github-actions@github.com>
2025-11-01 13:49:33 -07:00
Benjamin Lu
72389637ed feat(telemetry): track API credit top-up success via audit events (#6500)
Summary
- Add TelemetryEvents.API_CREDIT_TOPUP_SUCCEEDED and provider method
trackApiCreditTopupSucceeded
- Introduce topupTrackerStore to persist pending top-ups per user
(localStorage) and reconcile against recent audit logs
- Hook purchase flow to start tracking before opening Stripe checkout
- Reconcile after fetching audit events (UsageLogsTable) and after
fetchBalance, then emit telemetry, refresh balance, and clear pending
- Minor refactor in customerEventsService to return awaited result

Implementation details
- Matching strategy:
  - Event type: credit_added
  - Time window: createdAt between top-up start time and +24h
  - Amount: if known, e.params.amount must equal expected cents
- Cross-tab/user changes: synchronize via storage event and userId
watcher

Limitations / Follow-up
- Reconciliation fetches only page 1 (limit 10) of events; in
high-volume cases, a recent credit_added could fall outside the first
page
- The window and pagination issue will be "resolved by a followup PR to
core and cloud"

Files touched
- src/stores/topupTrackerStore.ts (new)
- src/components/dialog/content/setting/UsageLogsTable.vue
- src/composables/auth/useFirebaseAuthActions.ts
- src/platform/telemetry/providers/cloud/MixpanelTelemetryProvider.ts
- src/platform/telemetry/types.ts
- src/services/customerEventsService.ts

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6500-feat-telemetry-track-API-credit-top-up-success-via-audit-events-29e6d73d365081169941efae70cf71fe)
by [Unito](https://www.unito.io)

---------

Co-authored-by: Christian Byrne <chrbyrne96@gmail.com>
Co-authored-by: Claude <noreply@anthropic.com>
2025-11-01 13:17:41 -07:00
Arjan Singh
de535269ee feat(telemetry): help center and workflow creation (#6505)
## Summary

For Cloud distribution:
1. Track help center usage
2. Track workflow creation

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6505-feat-telemetry-help-center-and-workflow-creation-29e6d73d36508185af8ccbf19d5af9e7)
by [Unito](https://www.unito.io)

---------

Co-authored-by: Christian Byrne <chrbyrne96@gmail.com>
Co-authored-by: Christian Byrne <cbyrne@comfy.org>
Co-authored-by: Claude <noreply@anthropic.com>
2025-11-01 12:21:50 -07:00
Benjamin Lu
dad2d803df feat(telemetry): add unified run_triggered event for all run initiations (#6499)
Summary
- Add new telemetry event: `app:run_triggered` with `{ trigger_source:
'button' | 'keybinding' | 'menu' }`.
- Instrument all run initiation paths:
- UI Queue button emits `run_triggered` (source `button`) and keeps
emitting `run_button_click` for UI-only tracking.
- Keybindings (Ctrl+Enter / Ctrl+Shift+Enter) emit `run_triggered`
(source `keybinding`).
- Menus (menubar + legacy menu buttons) emit `run_triggered` (source
`menu`).
- Mixpanel provider now supports `trackRunTriggered` and forwards
`run_triggered`.
- `execution_start` tracking remains unchanged.

Motivation
GTM observed more `execution_start` events than `run_button_click`. This
change clarifies attribution by adding a unified event across all
triggers while preserving the UI-only `run_button_click` metric.

Files
- src/platform/telemetry/types.ts: Add `RunTriggeredMetadata`,
`RUN_TRIGGERED`, provider method signature.
- src/platform/telemetry/providers/cloud/MixpanelTelemetryProvider.ts:
Implement `trackRunTriggered`.
- src/components/actionbar/ComfyRunButton/ComfyQueueButton.vue: Emit
`run_triggered` on button path.
- src/services/keybindingService.ts: Emit `run_triggered` when queue
commands are invoked via keybindings.
- src/stores/menuItemStore.ts: Emit `run_triggered` for queue commands
invoked via menubar.
- src/scripts/ui.ts: Emit `run_triggered` for legacy menu queue buttons.

Notes
- `run_button_click` continues to represent UI button presses only.
- `run_triggered` now represents all user-initiated runs with clear
source attribution.

QA
- Cloud build: verify `app:run_triggered` appears with the correct
`trigger_source` for button, keybinding, and menu triggers.
- Verify `app:run_button_click` only fires for the button path.
- Confirm `execution_start` still tracks as before.

If preferred, we can extend `run_triggered` with additional fields
(e.g., queue mode, batchCount) in a follow-up.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6499-feat-telemetry-add-unified-run_triggered-event-for-all-run-initiations-29e6d73d3650819fb481d3e0e925c50f)
by [Unito](https://www.unito.io)

---------

Co-authored-by: GitHub Action <action@github.com>
2025-11-01 02:42:36 -07:00
Christian Byrne
f2aea9c823 fix: Display appropriate title for unsubscribed state (#6396)
## Summary

Fixes misleading title display in unsubscribed state of the
SubscriptionPanel.

### Changes Made

- **Conditional Title Logic**: Added state-aware title display in
`SubscriptionPanel.vue`
- **New Translation Key**: Added `subscription.titleUnsubscribed` →
"Subscribe to Comfy Cloud"
- **Prevents Confusion**: Eliminates "account created" or other
inappropriate messages showing as titles

### Behavior

- **Subscribed Users**: See "Subscription" (existing behavior)
- **Unsubscribed Users**: See "Subscribe to Comfy Cloud" (new, clearer
messaging)

### Testing

-  All existing SubscriptionPanel tests pass
-  TypeScript compilation successful
-  Linting passes
-  Follows translation patterns

### Context

This addresses the UX issue discovered on testcloud.comfy.org where
unsubscribed users were seeing misleading title text instead of clear
subscription prompts.

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

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6396-fix-Display-appropriate-title-for-unsubscribed-state-29c6d73d3650818a8136f6a99e312651)
by [Unito](https://www.unito.io)

Co-authored-by: Claude <noreply@anthropic.com>
2025-11-01 02:40:17 -07:00
Christian Byrne
c8e24190cd Cloud/gate topup on subscription (#6399)
## Summary

Gate the Top up buttons in the user dropdown and credits page of the
settings.


┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6399-Cloud-gate-topup-on-subscription-29c6d73d36508127866cdcbcfda66c46)
by [Unito](https://www.unito.io)

---------

Co-authored-by: DrJKL <DrJKL0424@gmail.com>
2025-11-01 01:37:00 -07:00
Christian Byrne
6fc5748a32 fix: set transparent border for gradient subscribe button (#6510)
## Summary
- Fixed the subscribe to run button border to be transparent, allowing
the gradient background to display properly
- Used PrimeVue's `pt` (passthrough) props to customize the border color

## Context
The subscribe to run button was recently updated to use a gradient
background via inline styles, but the border was still using the default
color which clashed with the gradient.

## Changes
- Added `:pt` prop to the Button component in `SubscribeToRun.vue`
- Set `borderColor: 'transparent'` on the root element to remove the
default border color

## Test Plan
- [ ] Visual inspection of the subscribe to run button
- [ ] Verify the gradient background displays without border color clash
- [ ] Ensure the button still functions correctly when clicked

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6510-fix-set-transparent-border-for-gradient-subscribe-button-29e6d73d3650816d9ecfcb63e2ffb7d3)
by [Unito](https://www.unito.io)

Co-authored-by: Christian Byrne <chrbyrne96@gmail.com>
2025-11-01 01:35:59 -07:00
Terry Jia
99958d3aa9 add api node link (#6494)
## Summary

add api node page link on credit and subscription pannel

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6494-add-api-node-link-29e6d73d365081aaa2f4d1647683ecda)
by [Unito](https://www.unito.io)
2025-11-01 01:12:31 -07:00
Benjamin Lu
1cdfc6934a chore(pnpm): allow building @sentry/cli for sourcemap uploads (#6491)
- Add `@sentry/cli` to `onlyBuiltDependencies` in `pnpm-workspace.yaml`
so its postinstall can run.
- Ensures the Sentry Vite plugin can upload sourcemaps in cloud builds
when Sentry env vars are configured.
- No runtime code changes.

Checks run locally before commit:
- `pnpm lint:fix` ✓
- `pnpm typecheck` ✓

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6491-chore-pnpm-allow-building-sentry-cli-for-sourcemap-uploads-29d6d73d365081bd8548c19ef48d4f61)
by [Unito](https://www.unito.io)
2025-11-01 01:11:37 -07:00
Alexander Piskun
d1108867e2 feat(TextPreviewWidget): add minimal support for [[label|url]] links (#6482)
## Summary

This PR adds a minimal, backward-compatible way to render **labeled
hyperlinks** in node text previews without enabling full Markdown
rendering.

The syntax is:
```
[[Label|https://example.com]]
```

Links open in a new tab and preserve the existing look and behavior of
the widget.

## Motivation

I first implemented a `Markdown`-based version that correctly rendered
`[label](url)` and other inline Markdown. After some consideration, I
have decided against shipping Markdown here because it risks breaking
existing custom nodes that already rely on
`PromptServer.instance.send_progress_text` with the current
`linkifyHtml` --> `nl2br` behavior. The token approach is **smaller**,
safer, and avoids surprises.

## Changes

* No global behavior change.
* No new dependencies.
* Token parsing is opt-in. If a node does not emit `[[label|url]]`,
behavior is unchanged.

## ComfyUI backend changes

PR to ComfyUI with these will come later(as first version of ComfyUI
with these frontend changes should be released), and it will contain:

```python
def _display_text(
    node_cls: type[IO.ComfyNode],
    text: Optional[str],
    *,
    status: Optional[Union[str, int]] = None,
    price: Optional[float] = None,
    results: Optional[Union[list[str], str]] = None,
) -> None:
    display_lines: list[str] = []
    if status:
        display_lines.append(f"Status: {status.capitalize() if isinstance(status, str) else status}")
    if price is not None:
        display_lines.append(f"Price: ${float(price):,.4f}")
    if results:  # New code starts
        if isinstance(results, str):
            display_lines.append(f"Result link: [[1|{results}]]")
        elif len(results) == 1:
            display_lines.append(f"Result link: [[1|{results[0]}]]")
        else:
            links = ", ".join(f"[[{i}|{u}]]" for i, u in enumerate(results, start=1))
            display_lines.append(f"Result links: {links}")  # New code ends
    if text is not None:
        display_lines.append(text)
    if display_lines:
        PromptServer.instance.send_progress_text("\n".join(display_lines), get_node_id(node_cls))
```        

## Screenshots (if applicable)

<img width="692" height="716" alt="Screenshot From 2025-10-31 13-12-54"
src="https://github.com/user-attachments/assets/619b5f70-550c-442f-9cd9-05a95270e533"
/>

<img width="732" height="714" alt="Screenshot From 2025-10-31 13-14-15"
src="https://github.com/user-attachments/assets/836ff87b-a2ac-45ba-842c-b0a4af91c7de"
/>

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6482-feat-TextPreviewWidget-add-minimal-support-for-label-url-links-29d6d73d365081e9ac97dd7f41e85d8f)
by [Unito](https://www.unito.io)
2025-11-01 01:00:56 -07:00
sno
89b073f96f [docs] Add /output.txt to .gitignore (#6456)
## Summary
- Adds `/output.txt` to `.gitignore` to prevent tracking the output file
generated by the weekly-docs-check.yaml workflow

## Rationale
The weekly docs check workflow emits an `output.txt` file that is not
intended to be committed to the repository. This change ensures the file
is properly ignored by git.

## Test plan
- [x] Verify `output.txt` is no longer tracked by git
- [x] Confirm weekly-docs-check workflow still functions correctly

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6456-docs-Add-output-txt-to-gitignore-29c6d73d365081688d99f3855337d887)
by [Unito](https://www.unito.io)
2025-11-01 00:46:34 -07:00
Benjamin Lu
9197119ed8 feat(telemetry): include custom_node_count in run button click event (#6493)
Summary
- Add custom_node_count to app:run_button_click telemetry payload,
aligning with execution_start context naming.
- Keeps event name as app:run_button_click (no rename).

Changes
- src/platform/telemetry/types.ts: add custom_node_count to
RunButtonProperties.
- src/platform/telemetry/providers/cloud/MixpanelTelemetryProvider.ts:
include custom_node_count from execution context in trackRunButton
payload.

Rationale
- execution_start already reports custom_node_count via
ExecutionContext.
- This adds visibility at the moment of run initiation, improving funnel
and pre-execution analysis.

Validation
- Ran pnpm lint:fix and pnpm typecheck locally; both passed.

Notes
- No runtime behavior changes outside telemetry payload.
- No user-facing strings changed.

Requested Reviewers
- Telemetry/analytics owners to confirm property naming and coverage.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6493-feat-telemetry-include-custom_node_count-in-run-button-click-event-29d6d73d365081a5b23bd02e72e0bafd)
by [Unito](https://www.unito.io)
2025-10-31 17:56:35 -07:00
Benjamin Lu
6abff41f08 feat(telemetry): add workflow_opened with open_source and missing node metrics (#6476)
- Adds app:workflow_opened and plumbs open_source across drag/drop,
file-open button, workspace, and templates
- Tracks missing_node_count and missing_node_types for both
workflow_opened and workflow_imported
- Reuses WorkflowOpenSource type for consistency; no breaking changes to
loadGraphData callers (5th param remains options object; openSource
optional)

Validation
- pnpm lint:fix
- pnpm typecheck

Notes
- Telemetry only runs in cloud builds; OSS remains clean.
- loadGraphData telemetry is centralized where missing_node_types is
computed.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6476-feat-telemetry-add-workflow_opened-with-open_source-and-missing-node-metrics-29d6d73d365081f385c0da29958309da)
by [Unito](https://www.unito.io)

---------

Co-authored-by: bymyself <cbyrne@comfy.org>
2025-10-31 14:51:16 -07:00
Simula_r
a537124a9f feat: add telemetry to answer for user failed to find template (#6489)
## Summary

Adds mixpanel telemetry with goal of: "We currently only know when a
user opens a template workflow. But we also want to know if they failed
to find what they want"

Example mixpanel query: 

```
app:template_library_closed
  WHERE template_selected = false AND time_spent_seconds >= 10
  ```

But can drill down further into what filters were selected etc to answer what they were looking for but couldn't find. 

```
 1. Event: app:template_filter_changed
  2. Filter:
- Add formula: "Where user also triggered app:template_library_closed
with template_selected = false in same session"
  3. Breakdown by: search_query
  4. Sort by: Total Count (descending)

  Search Query           Failed Sessions
  -----------------------------------
  "flux video"             45 times
  "sdxl controlnet"     32 times
  "upscaler"               28 times
  (empty/just filter)   20 times
```

```
 Event: app:template_filter_changed
  WHERE filtered_count = 0
    AND user did app:template_library_closed
    with template_selected = false

  Breakdown by: search_query
  ```
  etc.


https://www.notion.so/comfy-org/Number-of-users-who-open-the-template-library-and-where-do-they-click-29b6d73d36508044a595c0fb653ca6dc?source=copy_link

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6489-feat-add-telemetry-to-answer-for-user-failed-to-find-template-29d6d73d365081cdad72fd7c6ada5dc7)
by [Unito](https://www.unito.io)
2025-10-31 14:45:59 -07:00
Terry Jia
e05e988730 update user profile dropdown (#6475)
## Summary

update user profile dropdown

## Screenshots

1. Subscribe to run button and Unsubscribe user panel
<img width="433" height="480" alt="image"
src="https://github.com/user-attachments/assets/bb859481-6405-44df-85ec-9935599c4be0"
/>

2. Subscribed User:
<img width="395" height="479" alt="image"
src="https://github.com/user-attachments/assets/683de2c0-8090-4e9a-ac4e-d211fcee8921"
/>

3. OSS:
<img width="392" height="480" alt="image"
src="https://github.com/user-attachments/assets/7d684c1a-8dee-48dd-8e7f-3c98bd98104d"
/>

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6475-update-user-profile-dropdown-29d6d73d365081ff9e14f9355a9a3bb7)
by [Unito](https://www.unito.io)

---------

Co-authored-by: GitHub Action <action@github.com>
2025-10-31 14:45:02 -07:00
Terry Jia
afa10f7a1e [refactor] refactor load3d (#5765)
Summary

Fully Refactored the Load3D module to improve architecture and
maintainability by consolidating functionality into a
centralized composable pattern and simplifying component structure. and
support VueNodes system

  Changes

- Architecture: Introduced new useLoad3d composable to centralize 3D
loading logic and state
  management
- Component Simplification: Removed redundant components
(Load3DAnimation.vue, Load3DAnimationScene.vue,
  PreviewManager.ts) 
- Support VueNodes
- improve config store
- remove lineart output due Animation doesnot support it, may add it
back later
- remove Preview screen and keep scene in fixed ratio in load3d (not
affect preview3d)
- improve record video feature which will already record video by same
ratio as scene
Need BE change https://github.com/comfyanonymous/ComfyUI/pull/10025


https://github.com/user-attachments/assets/9e038729-84a0-45ad-b0f2-11c57d7e0c9a



┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5765-refactor-refactor-load3d-2796d73d365081728297cc486e2e9052)
by [Unito](https://www.unito.io)
2025-10-31 16:19:35 -04:00
Benjamin Lu
91b5a7de17 feat(telemetry): track total node count, subgraphs, and API-node details in RUN_BUTTON_CLICKED (#6468)
Summary
- Add richer run-button telemetry: total node count, subgraph count,
whether API nodes are present, and unique API node names.
- Add provider method/event for “Add API credit” button clicks.
- Include a one-off script to normalize Mixpanel survey properties
(industry/useCase) for better analytics.

Changes
- Types: extend telemetry payloads
- RunButtonProperties adds total_node_count, subgraph_count,
has_api_nodes, api_node_names (src/platform/telemetry/types.ts:42)
- ExecutionContext adds same fields (src/platform/telemetry/types.ts:61)
- New event + provider API: ADD_API_CREDIT_BUTTON_CLICKED,
trackAddApiCreditButtonClicked() (src/platform/telemetry/types.ts:173,
src/platform/telemetry/types.ts:180)
- Mixpanel provider
- Compute node metrics in a single graph traversal and include them in
RUN_BUTTON_CLICKED
(src/platform/telemetry/providers/cloud/MixpanelTelemetryProvider.ts:169)
- Fields populated: total_node_count, subgraph_count, has_api_nodes,
api_node_names
(src/platform/telemetry/providers/cloud/MixpanelTelemetryProvider.ts:177)
- Add trackAddApiCreditButtonClicked() implementation
(src/platform/telemetry/providers/cloud/MixpanelTelemetryProvider.ts:152)
- Script
- Add scripts/survey-data-migration.ts to normalize industry/useCase
user properties using existing survey normalization utils; includes a
simulation and a production outline (scripts/survey-data-migration.ts:1)

Motivation
- Improves insight into workflow complexity and API-node adoption
directly at run time.
- Normalizes free-text survey fields to reduce category proliferation
and improve reporting quality.

Validation
- Run a workflow with/without API nodes and subgraphs; confirm telemetry
includes:
  - total_node_count, subgraph_count, has_api_nodes, api_node_names
- Click “Add API credit” and confirm app:add_api_credit_button_clicked
is sent.
- No user-visible changes; telemetry only runs in cloud builds.

Impact
- Telemetry payload shape expands; backend ingestion should accept the
new properties.
- Metrics computed in a single pass over the graph for efficiency.

---------

Co-authored-by: bymyself <cbyrne@comfy.org>
2025-10-31 12:55:00 -07:00
Comfy Org PR Bot
da078a6071 1.32.0 (#6474)
Minor version increment to 1.32.0

**Base branch:** `main`

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6474-1-32-0-29d6d73d365081758b7df1a759a85013)
by [Unito](https://www.unito.io)

---------

Co-authored-by: simula-r <18093452+simula-r@users.noreply.github.com>
Co-authored-by: github-actions <github-actions@github.com>
2025-10-30 21:12:57 -07:00
sno
e1706a8f13 fix: Remove assignees from weekly-docs-check workflow (#6375)
## Summary

Removes the `assignees` field from the weekly-docs-check workflow that
was causing failures.

## Problem

The workflow was attempting to assign `${{ github.repository_owner }}`
to created PRs. For organization-owned repositories,
`github.repository_owner` is the organization name (e.g., "Comfy-Org"),
which cannot be assigned to pull requests. Only individual GitHub users
can be assigned.

This was causing the workflow to fail:
https://github.com/Comfy-Org/ComfyUI_frontend/actions/runs/18901589988

## Solution

Removed the `assignees` line from the workflow configuration. The PRs
will still be created successfully, just without auto-assignment.

## Changes Made

- Removed `assignees: ${{ github.repository_owner }}` from
`.github/workflows/weekly-docs-check.yaml:145`

Fixes #6364

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

Co-Authored-By: Claude <noreply@anthropic.com>

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6375-fix-Remove-assignees-from-weekly-docs-check-workflow-29b6d73d365081769ec2dc3e68005cc7)
by [Unito](https://www.unito.io)
2025-10-30 20:47:07 -07:00
Christian Byrne
1322a56653 Cloud/tracking v2 (#6400)
## Summary

<!-- One sentence describing what changed and why. -->

## Changes

- **What**: <!-- Core functionality added/modified -->
- **Breaking**: <!-- Any breaking changes (if none, remove this line)
-->
- **Dependencies**: <!-- New dependencies (if none, remove this line)
-->

## Review Focus

<!-- Critical design decisions or edge cases that need attention -->

<!-- If this PR fixes an issue, uncomment and update the line below -->
<!-- Fixes #ISSUE_NUMBER -->

## Screenshots (if applicable)

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

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6400-Cloud-tracking-v2-29c6d73d365081a1ae32e9337f510a9e)
by [Unito](https://www.unito.io)

---------

Co-authored-by: Arjan Singh <arjan@comfy.org>
2025-10-30 20:46:42 -07:00
AustinMroz
6491db6f68 Further subgraph improvements (#6466)
- Prune disconnected widgets on config panel open
- Fix breakage of DOMWidgets on double nest
- Fix self clipping of proxied DOMWidget nodes
- Blacklist cloning of promtoed widget prop

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6466-Further-subgraph-improvements-29c6d73d365081c8b63dda068486805c)
by [Unito](https://www.unito.io)
2025-10-30 18:06:10 -07:00
Simula_r
041ce2decb Feat/vue nodes autoscale false (#6469)
## Summary

Switch back to false, for now. Because its a destructive action. Edge
cases and user information can come next. This saves us time so we can
cut a nightly release and move on.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6469-Feat-vue-nodes-autoscale-false-29c6d73d365081deabf0dc284a9b41ab)
by [Unito](https://www.unito.io)
2025-10-30 15:23:57 -07:00
Simula_r
ce298c43f4 feat: Comfy.VueNodes.AutoScaleLayout default true (#6467)
## Summary

Now that banner and toast are merged run the auto scale / node arrange
function by default.

## Changes

- **What**: Core settings Comfy.VueNodes.AutoScaleLayout defaultValue:
true,

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6467-feat-Comfy-VueNodes-AutoScaleLayout-default-true-29c6d73d365081c2b317f7f3450cbaec)
by [Unito](https://www.unito.io)
2025-10-30 14:29:55 -07:00
AustinMroz
40a7cbb83e Fix selectionToolbox not being on node (#6401)
`getBounding()` doesn't work if a draw hasn't occurred.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6401-Fix-selectionToolbox-not-being-on-node-29c6d73d3650812cbfbff116a9b8159d)
by [Unito](https://www.unito.io)
2025-10-30 12:13:00 -07:00
AustinMroz
9ddead24d6 On failed subgraph resolution, return undefined (#6460)
Resolving an executionId to a locatorId can fail if the current workflow
does not match the queued one. Rather than throwing an error, the
resolution functions have been changed to return undefined.

Of note, all consumers of these functions already had checks to ensure
the returned value is not undefined.
- Even the test for failure state already specified that it should
return instead of throwing an error.

This PR cleans up a frequent sentry error.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6460-On-failed-subgraph-resolution-return-undefined-29c6d73d365081c5860ed7d24a99414c)
by [Unito](https://www.unito.io)
2025-10-30 12:06:52 -07:00
Arjan Singh
6186e81b98 chore: add example sentry env vars (#6462)
## Summary

Add examples for future debugging documentations.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6462-chore-add-example-sentry-env-vars-29c6d73d365081b5bf06f8f248985fbf)
by [Unito](https://www.unito.io)
2025-10-30 11:05:15 -07:00
Arjan Singh
04eb224822 ci: add sentryVitePlugin (#6394)
## Summary

This will be used to upload source maps in configured environments.

Docs:
https://docs.sentry.io/platforms/javascript/sourcemaps/uploading/vite/

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6394-ci-add-sentryVitePlugin-29c6d73d365081239f48f2fd261736d5)
by [Unito](https://www.unito.io)
2025-10-30 10:28:48 -07:00
Terry Jia
dd90288846 code improve for 3d node (#6403)
## Summary

code improve for 3d node

## Changes

1. remove custom css
2. use tailwind instead
3. bug fix for right click context menu

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6403-code-improve-for-3d-node-29c6d73d3650819fab26feabdaa21821)
by [Unito](https://www.unito.io)
2025-10-30 05:31:36 -04:00
Johnpaul Chiwetelu
c642ed5703 Complete context menu migration for Load3D, Preview3D, and SaveGLB (#6454)
This pull request refactors how export menu options are added to
3D-related nodes, updating the API to use a new `getNodeMenuItems` hook
and simplifying the menu item creation logic. The changes improve
consistency and maintainability across extensions handling 3D nodes, and
clarify the expected return types for menu item hooks.

**Refactoring and API updates for node menu items:**

* Replaced usage of the legacy `createExportMenuOptions` function with
the new `createExportMenuItems` function in `load3d.ts`, `saveMesh.ts`,
and related imports, aligning all 3D node extensions to the new API.
[[1]](diffhunk://#diff-a5c612d9322ca4cbbeda097d13e2fa1ef017d4c3076d23fc228afee5a79a56e3L6-R12)
[[2]](diffhunk://#diff-7dede72060130d77ce5191fc86d115bd9b93311cb0438400730d8e20b2aa8e43L4-R7)
* Introduced and implemented the `getNodeMenuItems` hook in the
extension registration for `Load3D`, `Preview3D`, and `SaveGLB` nodes,
ensuring export menu items are only shown for the appropriate node
types.
[[1]](diffhunk://#diff-a5c612d9322ca4cbbeda097d13e2fa1ef017d4c3076d23fc228afee5a79a56e3R293-R302)
[[2]](diffhunk://#diff-a5c612d9322ca4cbbeda097d13e2fa1ef017d4c3076d23fc228afee5a79a56e3R521-R530)
[[3]](diffhunk://#diff-7dede72060130d77ce5191fc86d115bd9b93311cb0438400730d8e20b2aa8e43R48-R57)
* Removed assignment of `getExtraMenuOptions` to nodes, fully migrating
to the new menu item hook approach for context menus.
[[1]](diffhunk://#diff-a5c612d9322ca4cbbeda097d13e2fa1ef017d4c3076d23fc228afee5a79a56e3L301-L302)
[[2]](diffhunk://#diff-a5c612d9322ca4cbbeda097d13e2fa1ef017d4c3076d23fc228afee5a79a56e3L548-L549)
[[3]](diffhunk://#diff-7dede72060130d77ce5191fc86d115bd9b93311cb0438400730d8e20b2aa8e43L64-L67)

**Menu item creation logic simplification:**

* Refactored `createExportMenuOptions` to `createExportMenuItems` in
`exportMenuHelper.ts`, changing it to directly return an array of menu
items (with separators) instead of a function, simplifying its usage and
reducing boilerplate.
[[1]](diffhunk://#diff-42404da1a87a52d304371a13d5f021bdad837765b94d86d186abb2d99a8cb707L14-R22)
[[2]](diffhunk://#diff-42404da1a87a52d304371a13d5f021bdad837765b94d86d186abb2d99a8cb707L59-R58)

**Type and documentation improvements:**

* Updated the `ComfyExtension` interface in `comfy.ts` to clarify that
menu item hooks (`getCanvasMenuItems`, `getNodeMenuItems`) now return
arrays that may include `null` values as separators, improving type
safety and documentation.

See before and after below
<img width="1459" height="897" alt="Screenshot 2025-10-30 at 07 08 04"
src="https://github.com/user-attachments/assets/ec4464c9-f733-4b4c-87c4-bb5060ccaa68"
/>

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6454-Complete-context-menu-migration-for-Load3D-Preview3D-and-SaveGLB-29c6d73d3650819995b4c4dc1582cd86)
by [Unito](https://www.unito.io)
2025-10-30 07:26:05 +01:00
Simula_r
9e309308ed feat: show vue node settings toggle and show banner to switch back (#6382)
## Summary

- When switching from litegraph to vue nodes, show a toast that opens
settings to switch back
- Show a toggle in settings under Vue nodes for now called Modern Node
Design (Vue Nodes)
- Plan to update all the nomenclature as we get closer to GA

## Screenshots (if applicable)

<img width="1480" height="911" alt="image"
src="https://github.com/user-attachments/assets/fa3a34b9-2631-4175-917a-aa319f9fe415"
/>

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6382-feat-show-vue-node-settings-toggle-and-show-banner-to-switch-back-29b6d73d365081d29252c83349f06c0a)
by [Unito](https://www.unito.io)

---------

Co-authored-by: github-actions <github-actions@github.com>
2025-10-29 22:39:52 -07:00
Simula_r
b27c741d7d Feat/vue nodes try it now banner (#6362)
## Summary

Banner to try vue nodes. Clicking try it now will flip
shouldRenderVueNodes = true.

## Screenshots (if applicable)

<img width="1512" height="824" alt="image"
src="https://github.com/user-attachments/assets/dfd4bdc3-6753-45ee-86f1-ed7dc077f868"
/>

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6362-Feat-vue-nodes-try-it-now-banner-29b6d73d365081c29f04f9126f06ee9d)
by [Unito](https://www.unito.io)
2025-10-29 21:46:47 -07:00
AustinMroz
ca5729a8e7 Add pricing badge when a subgraph contains partner nodes (#6354)
<img width="596" height="213" alt="image"
src="https://github.com/user-attachments/assets/174c5461-f638-42de-b3ad-0e108dee3983"
/>


![api-badge-subgraph_00003](https://github.com/user-attachments/assets/067d0398-47e9-4e97-9e1d-67fac2935e55)


┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6354-Add-pricing-badge-when-a-subgraph-contains-partner-nodes-29b6d73d365081c685bec3e9446970eb)
by [Unito](https://www.unito.io)
2025-10-29 20:41:04 -07:00
Alexander Brown
e606ff34ec Feat: Vue Node Slot Improvements (#6359)
## Summary

Several fixes and improvements to the slot behavior on Vue nodes.

## Changes

- **What**: Restore the pseudo-slots, if there are slots being hidden by
collapse
- **What**: Connections while collapsed
- **What**: Display the links in a more reasonable location
- **What**: Fixes styling of linked widgets
- **What**: [~Fix reconnecting logic to prioritize newly disconnected
and now empty
slots~](https://github.com/Comfy-Org/ComfyUI_frontend/pull/6370)

## Review Focus

<!-- Critical design decisions or edge cases that need attention -->

<!-- If this PR fixes an issue, uncomment and update the line below -->
<!-- Fixes #ISSUE_NUMBER -->

## Screenshots (if applicable)


https://github.com/user-attachments/assets/913cfb8f-acdd-4f3d-b619-c280cc11cce5


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

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6359-WIP-Collapsed-nodes-multislots-29b6d73d3650817289d5f0a8efdade84)
by [Unito](https://www.unito.io)

---------

Co-authored-by: github-actions <github-actions@github.com>
2025-10-29 20:32:05 -07:00
Christian Byrne
5e9a9923e4 update subscription panel for new designs (#6378)
## Summary

Refactoring of subscription panel to improve maintainability and match
Figma design exactly. Extracted business logic into
`useSubscriptionCredits` and `useSubscriptionActions` composables, added
comprehensive testing, and enhanced the design system with proper
semantic tokens.

- Extract credit calculations and action handlers into reusable
composables
- Add component and unit tests with proper mocking patterns  
- Update terminology from "API Nodes" to "Partner Nodes"
- Make credit breakdown dynamic using real API data instead of hardcoded
values
- Add semantic design tokens for modal card surfaces with light/dark
theme support
- Reduce component complexity from ~100 lines to ~25 lines of logic
- Improve layout spacing, typography, and responsive behavior to match
Figma specs

<img width="1948" height="1494" alt="Selection_2220"
src="https://github.com/user-attachments/assets/b922582d-7edf-4884-b787-ad783c896b80"
/>

<img width="1948" height="1494" alt="Selection_2219"
src="https://github.com/user-attachments/assets/50a9f263-9adb-439d-8a89-94a498d394e3"
/>

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6378-update-subscription-panel-for-new-designs-29b6d73d3650815c9ce2c5977ac7f893)
by [Unito](https://www.unito.io)

---------

Co-authored-by: Claude <noreply@anthropic.com>
2025-10-29 20:15:30 -07:00
Christian Byrne
331372df44 use shared composable for subscription (#6390)
Refactor global composable to use useSharedComposable.

- Makes it obvious that subscription status is shared globally across
all components, rather than relying on implicit module-level refs
- Eliminates manual `isWatchSetup` flag and conditional watch setup
logic that was needed to prevent duplicate watchers
- Uses well-established VueUse pattern that other developers will
immediately recognize and understand

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6390-use-shared-composable-for-subscription-29c6d73d36508185a5c2e0b467549432)
by [Unito](https://www.unito.io)
2025-10-29 20:08:33 -07:00
Arjan Singh
09143c05c1 chore(vite.config): add GCS redirect for assets (#6389)
## Summary

Add GCS redirect in vite's proxy so we can load workflows by dragging
images in local cloud development.

Previously this action would result in a CORS error when the app tried
to download from `storage.googleapis.com`

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6389-chore-vite-config-add-GCS-redirect-for-assets-29c6d73d36508148aaabd24de9698fc4)
by [Unito](https://www.unito.io)
2025-10-29 19:34:38 -07:00
Alexander Brown
fac8bd68dc Fix: Disable pointer events on images to allow them to be drag targets (#6388)
## Summary

Allows SaveImage and other nodes with ImagePreviews to be dragged by the
image.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6388-Fix-Disable-pointer-events-on-images-to-allow-them-to-be-drag-targets-29c6d73d365081e796f0e8171a860062)
by [Unito](https://www.unito.io)
2025-10-30 01:45:50 +00:00
Arjan Singh
f2355a6ad1 feat(historyV2): load workflows for images (#6384)
## Summary

Hooked up the "Load Workflow" action to our `history_v2` API.

Note: Our cloud envs were being stress tested right now so images are
loading at time of recording. Images were loading for me during
development before I had time to create the video.


## Screenshots 📷 



https://github.com/user-attachments/assets/02145504-ceae-497b-9049-553796d698da



┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6384-Feat-history-v2-workflows-29b6d73d365081bcb706fe799b8ce66a)
by [Unito](https://www.unito.io)
2025-10-29 18:35:42 -07:00
AustinMroz
6f068c87da Multiple fixes related to copying subgraphs (#6383)
- DOMWidgets have ownership reset after a `subgraphNode.clone()`.
- Fixes Ctrl+C on a subgraphNode with a prompted prompt making the
prompt disappear.
- alt + drag uses the copy/paste pathway that deeply clones subgraphs.
- Fixed dangling references on nodes in subgraphs by updating subgraph
ids before configuration.
- Attempt to recursively resolve disconnected proxyWidgets (Can matter
when subgraphs load out of order).
- Fix Right click -> clone creating linked copies of subgraphs.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6383-Multiple-fixes-related-to-copying-subgraphs-29b6d73d365081819671ced440dde327)
by [Unito](https://www.unito.io)
2025-10-29 18:05:04 -07:00
Alexander Brown
86c0fb11f1 Fix: Only stop propagation of events forwarded to the canvas. (#6387)
## Summary

Allows users to drag nodes while clicking things like the widget labels.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6387-Fix-Only-stop-propagation-of-events-forwarded-to-the-canvas-29c6d73d365081bdb952c5e7f409031e)
by [Unito](https://www.unito.io)
2025-10-30 00:56:14 +00:00
Terry Jia
c76f017f92 add save option for 3d node on context menu (#6319)
## Summary

add save option for 3d node on context menu

## Changes
allow to export model from context menu, supported in
preview3d/load3d/saveMesh




https://github.com/user-attachments/assets/1f0f1a93-9cdb-477f-8bd3-e298c7e3892b

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6319-add-save-option-for-3d-node-on-context-menu-2996d73d365081f7853cd2ae9c69fe4d)
by [Unito](https://www.unito.io)
2025-10-29 20:52:02 -04:00
Alexander Brown
5e212156e1 Fix: Allow click and drag selection when editing text (e.g. Node name) (#6385)
## Summary

Previously, the node would be dragged when trying to select text.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6385-Fix-Allow-click-and-drag-selection-when-editing-text-e-g-Node-name-29b6d73d36508197b73fc8f965bb34ef)
by [Unito](https://www.unito.io)
2025-10-29 16:56:58 -07:00
Alexander Brown
7821120706 fix: Count dragging slot as a free slot for targeting (#6370)
## Summary

Prioritizes the slot that previously held the link instead of the first
valid slot for a given type.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6370-fix-Count-dragging-slot-as-a-free-slot-for-targeting-29b6d73d365081709034e272cef38320)
by [Unito](https://www.unito.io)
2025-10-29 11:43:57 -07:00
Christian Byrne
b8e5d1ff90 refactor subscription composable (#6365)
Some refactors of subscription composable:

- Swapped cloud subscription response shapes to `type` aliases to
emphasize fixed API payloads.
- Formatted renewal/end dates with the user’s locale instead of
hard-coding `en-US`.
- Reused the single `useFirebaseAuthActions()` instance and passed
`fetchSubscriptionStatus` directly into `wrapWithErrorHandlingAsync` to
trim duplicate wrappers.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6365-refactor-subscription-composable-29b6d73d365081b1b002d52abde8d195)
by [Unito](https://www.unito.io)
2025-10-29 09:05:24 -07:00
Johnpaul Chiwetelu
bde5244a71 Css token standardization (#6363)
This pull request introduces a comprehensive update to the design
system's color management, focusing on establishing semantic color
tokens for both light and dark themes. It replaces many hardcoded color
values and legacy CSS classes throughout the codebase with new semantic
CSS variables, ensuring consistent theming and easier future
maintenance. The changes affect core CSS files as well as numerous Vue
components, aligning their styling with the new design system.

**Design System Foundation**

* Added a wide range of new color variables to `style.css`, including
base colors (e.g., `--color-white`, `--color-black`), additional shades
for sand, azure, cobalt, gold, coral, and magenta, and new alpha
(transparency) colors.
[[1]](diffhunk://#diff-71b6b57a56095b04e47c797a5016149b76b27971cab04b93f033f1f846e0f5a0L52-R87)
[[2]](diffhunk://#diff-71b6b57a56095b04e47c797a5016149b76b27971cab04b93f033f1f846e0f5a0R119-R123)
* Introduced semantic color tokens for both light and dark modes
(`--base-background`, `--primary-background`,
`--destructive-background`, etc.), mapping them to the new base colors
for consistent usage across the application.
[[1]](diffhunk://#diff-71b6b57a56095b04e47c797a5016149b76b27971cab04b93f033f1f846e0f5a0R219-R239)
[[2]](diffhunk://#diff-71b6b57a56095b04e47c797a5016149b76b27971cab04b93f033f1f846e0f5a0R301-R321)
* Exposed semantic tokens as CSS variables (e.g.,
`--color-base-foreground`, `--color-secondary-background`) for use
throughout the app.

**Component Refactoring to Semantic Tokens**

* Updated Vue components and their tests to use the new semantic color
classes (e.g., `bg-base-background`, `text-base-foreground`,
`bg-secondary-background`) instead of hardcoded colors or legacy
dark-theme classes. This affects components such as
`WorkflowTemplateSelectorDialog.vue`, `BypassButton.vue`,
`ExecuteButton.vue`, `MenuOptionItem.vue`, `AssetCard.vue`,
`MediaAssetMoreMenu.vue`, `MediaTitle.vue`, `WidgetFileUpload.vue`,
`WidgetRecordAudio.vue`, `AudioPreviewPlayer.vue`, and
`FormDropdownMenuActions.vue`.
[[1]](diffhunk://#diff-2c860bdc48e907b1b85dbef846599d8376dd02cff90f49e490eebe61371fecedL149-R149)
[[2]](diffhunk://#diff-8ec606ef1100f3a56945ed24cbdc1965050932cd744d4172a3868cdfd23894c0L95-R95)
[[3]](diffhunk://#diff-80b781aeba31712968ae157bb70194e4b72bc73430d1cca6a79d718d839daed6L10-R10)
[[4]](diffhunk://#diff-55fd9056d35e50249dc9f2280017dc99294221fdbe56d8399cea60f8bac499b5L7-R7)
[[5]](diffhunk://#diff-c5e6830e63e2441d2dc70d2ecf7c9b56d0a93821f827e9c5377fc10ae6016f18L30-R32)
[[6]](diffhunk://#diff-a1091d53a4b5d493e045aab5960188d2e7c3b80002e7178427268835fadb5809L30-R30)
[[7]](diffhunk://#diff-ccdb389a5e355d525dcfa26ecd77519297b6232dd34522411c8bfdd4cde05a1cL6-R6)
[[8]](diffhunk://#diff-7ef9ebd48e6f38a644c1a4e7bae1c7bb818bb959b2d20985974824e299ea5c34L3-R3)
[[9]](diffhunk://#diff-489229f88dfdfd5d883a3ef7fad6effa0790a18a831d5a9d84642dfb246962a2L115-R115)
[[10]](diffhunk://#diff-7bee4b453fc869f546e7150a6e39992ab6442987f80c10f8260b8f3715491997L51-R51)
[[11]](diffhunk://#diff-29348fa2e5b8cec1301a99bdec241379aeefc1747cceeb0c39b7df452ca635ffL41-R41)
[[12]](diffhunk://#diff-d464ebe3a44bec4fda7155e5605bf173612aca409250b7ef6b78920a89ae2044L24-R24)

**Consistency and Maintainability**

* Ensured hover, active, and selected states use semantic background and
foreground colors for both light and dark themes, improving visual
consistency and simplifying future updates.
[[1]](diffhunk://#diff-2c860bdc48e907b1b85dbef846599d8376dd02cff90f49e490eebe61371fecedL183-R183)
[[2]](diffhunk://#diff-a1091d53a4b5d493e045aab5960188d2e7c3b80002e7178427268835fadb5809L115-R114)
[[3]](diffhunk://#diff-ccdb389a5e355d525dcfa26ecd77519297b6232dd34522411c8bfdd4cde05a1cL18-R18)
[[4]](diffhunk://#diff-ccdb389a5e355d525dcfa26ecd77519297b6232dd34522411c8bfdd4cde05a1cL29-R29)
[[5]](diffhunk://#diff-ccdb389a5e355d525dcfa26ecd77519297b6232dd34522411c8bfdd4cde05a1cL43-R43)
[[6]](diffhunk://#diff-ccdb389a5e355d525dcfa26ecd77519297b6232dd34522411c8bfdd4cde05a1cL55-R55)
[[7]](diffhunk://#diff-ccdb389a5e355d525dcfa26ecd77519297b6232dd34522411c8bfdd4cde05a1cL69-R69)
[[8]](diffhunk://#diff-ccdb389a5e355d525dcfa26ecd77519297b6232dd34522411c8bfdd4cde05a1cL83-R83)
[[9]](diffhunk://#diff-2c860bdc48e907b1b85dbef846599d8376dd02cff90f49e490eebe61371fecedL328-R328)
[[10]](diffhunk://#diff-489229f88dfdfd5d883a3ef7fad6effa0790a18a831d5a9d84642dfb246962a2L138-R138)
[[11]](diffhunk://#diff-29348fa2e5b8cec1301a99bdec241379aeefc1747cceeb0c39b7df452ca635ffL119-R119)
[[12]](diffhunk://#diff-29348fa2e5b8cec1301a99bdec241379aeefc1747cceeb0c39b7df452ca635ffL137-R137)
[[13]](diffhunk://#diff-d464ebe3a44bec4fda7155e5605bf173612aca409250b7ef6b78920a89ae2044L53-R53)
[[14]](diffhunk://#diff-d464ebe3a44bec4fda7155e5605bf173612aca409250b7ef6b78920a89ae2044L153-R153)

**Removal of Legacy Styles**

* Removed legacy dark-theme class usage and hardcoded color values,
replacing them with semantic tokens to unify the styling approach.
[[1]](diffhunk://#diff-c5e6830e63e2441d2dc70d2ecf7c9b56d0a93821f827e9c5377fc10ae6016f18L30-R32)
[[2]](diffhunk://#diff-d464ebe3a44bec4fda7155e5605bf173612aca409250b7ef6b78920a89ae2044L24-R24)
[[3]](diffhunk://#diff-d464ebe3a44bec4fda7155e5605bf173612aca409250b7ef6b78920a89ae2044L53-R53)
[[4]](diffhunk://#diff-d464ebe3a44bec4fda7155e5605bf173612aca409250b7ef6b78920a89ae2044L153-R153)

These changes lay the groundwork for a scalable and maintainable design
system, making it easier to implement future theme changes and ensuring
a consistent look and feel across all components.
2025-10-29 15:10:54 +01:00
Rizumu Ayaka
8c1beee719 chore: adjust manual chunks (#6368)
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6368-chore-adjust-manual-chunks-29b6d73d36508185a1bcdc77d4fd7519)
by [Unito](https://www.unito.io)
2025-10-29 01:44:26 -07:00
Jin Yi
9651d2a5df feat: Add multi-select support for media assets (#6256)
## Summary
Implements file explorer-style multi-selection functionality for media
assets in the AssetsSidebarTab component.

## Changes

### Multi-Selection Interactions
- **Normal click**: Single selection (clears previous, selects new)
- **Shift + click**: Range selection (from last selected to current)
- **Ctrl/Cmd + click**: Toggle individual selection

### State Management
- Added `assetSelectionStore` to manage selected asset IDs using Set
- Created `useAssetSelection` composable for selection logic and
keyboard state

### UI Enhancements
- Display selection count in footer (output tab only)
- Interactive selection count that shows "Deselect all" on hover
- Added bulk action buttons for download/delete (UI only)

### Translation Keys
Added new keys under `mediaAsset.selection`:
- `selectedCount`: "{count} selected"
- `deselectAll`: "Deselect all"
- `downloadSelected`: "Download"
- `deleteSelected`: "Delete"

## Test Plan
- [x] Open Assets sidebar tab
- [x] Switch to Generated tab
- [x] Test single selection with normal click
- [x] Test range selection with Shift + click
- [x] Test toggle selection with Ctrl/Cmd + click
- [x] Verify selection count updates correctly
- [x] Test hover interaction on selection count
- [x] Click "Deselect all" to clear selection
- [x] Test bulk action buttons (UI only)

## Notes
- Bulk download/delete functionality to be implemented in separate PR
- Selection UI currently only shows in output (Generated) tab


[screen-capture.webm](https://github.com/user-attachments/assets/740315bd-9254-4af3-a0be-10846d810d65)
2025-10-29 00:26:44 -07:00
sno
22f307b468 fix: Use PR_GH_TOKEN instead of GITHUB_TOKEN in weekly-docs-check workflow (#6364)
## Summary
- Updated weekly-docs-check.yaml to use `PR_GH_TOKEN` secret instead of
`GITHUB_TOKEN`

## Problem
The weekly documentation check workflow uses `GITHUB_TOKEN` when
creating pull requests, which can cause permission issues. The default
`GITHUB_TOKEN` has limited permissions and may not trigger other
workflows or perform certain PR operations.

## Solution
Changed the token in the "Create or Update Pull Request" step from
`secrets.GITHUB_TOKEN` to `secrets.PR_GH_TOKEN` to use a more permissive
token that can properly create and manage PRs.

## Changes Made
- `.github/workflows/weekly-docs-check.yaml:135` - Updated token
parameter

## Test Plan
- Workflow should now successfully create PRs with proper permissions
- Other workflows should be triggered correctly when PRs are created

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

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6364-fix-Use-PR_GH_TOKEN-instead-of-GITHUB_TOKEN-in-weekly-docs-check-workflow-29b6d73d3650812cbaddc1ea10aeb995)
by [Unito](https://www.unito.io)

Co-authored-by: Claude <noreply@anthropic.com>
2025-10-28 23:47:07 -07:00
Jin Yi
06ba106f59 Media Assets Management Sidebar Tab Implementation (#6112)
## 📋 Overview
Implemented a new Media Assets sidebar tab in ComfyUI for managing
user-uploaded input files and generated output files. This feature
supports both local and cloud environments and is currently enabled only
in development mode.

## 🎯 Key Features

### 1. Media Assets Sidebar Tab
- **Imported** / **Generated** files separated by tabs
- Visual display with file preview cards
- Gallery view support (navigable with arrow keys)

### 2. Environment-Specific Implementation
- **`useInternalMediaAssets`**: For local environment
  - Fetches file list via `/files` API
  - Retrieves generation task execution time via `/history` API
  - Processes history data using the same logic as QueueSidebarTab
- **`useCloudMediaAssets`**: For cloud environment
  - File retrieval through assetService
  - History data processing using TaskItemImpl
- Auto-truncation of long filenames over 20 characters (e.g.,
`very_long_filename_here.png` → `very_long_...here.png`)

### 3. Execution Time Display
- Shows task execution time on generated image cards (e.g., "2.3s")
- Calculated from History API's `execution_start` and
`execution_success` messages
- Displayed at MediaAssetCard's duration chip location

### 4. Gallery Feature
- Full-screen gallery mode on image click
- Navigate between images with keyboard arrows
- Exit gallery with ESC key
- Reuses ResultGallery component from QueueSidebarTab

### 5. Development Mode Only
- Excluded from production builds using `import.meta.env.DEV` condition
- Feature in development, scheduled for official release after
stabilization

## 🛠️ Technical Changes

### New Files Added
- `src/components/sidebar/tabs/AssetsSidebarTab.vue` - Main sidebar tab
component
- `src/composables/sidebarTabs/useAssetsSidebarTab.ts` - Sidebar tab
definition
- `src/composables/useInternalMediaAssets.ts` - Local environment
implementation
- `src/composables/useCloudMediaAssets.ts` - Cloud environment
implementation
- `packages/design-system/src/icons/image-ai-edit.svg` - Icon addition

### Modified Files
- `src/stores/workspace/sidebarTabStore.ts` - Added dev mode only tab
display logic
- `src/platform/assets/components/MediaAssetCard.vue` - Added execution
time display, zoom event
- `src/platform/assets/components/MediaImageTop.vue` - Added image
dimension detection
- `packages/shared-frontend-utils/src/formatUtil.ts` - Added media type
determination utility functions
- `src/locales/en/main.json` - Added translation keys


[media_asset_OSS_cloud.webm](https://github.com/user-attachments/assets/a6ee3b49-19ed-4735-baad-c2ac2da868ef)

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: github-actions <github-actions@github.com>
2025-10-29 03:39:16 +00:00
Jin Yi
5f3b8fb8c8 fix: Add viewport constraint to dropdown heights and improve filter layout (#6358)
## Summary
Fixed dropdown components exceeding viewport on mobile/tablet
environments and improved workflow template selector dialog filter
layout.

https://github.com/Comfy-Org/ComfyUI_frontend/issues/6153

## Changes

### 1. Dropdown Height Constraints (MultiSelect & SingleSelect)
- Applied CSS `min()` function to use the smaller value between
`listMaxHeight` prop and 50% viewport height
- Ensures dropdowns don't overflow on devices with smaller viewport
heights like tablets and mobiles

### 2. Workflow Template Dialog Layout Improvements
- Grouped filters (Model, Use Case, License) on the left side
- Positioned Sort by option on the right for clearer visual hierarchy
- Used `justify-between` to place filters and sort options at opposite
ends

## Test Plan
- [ ] Verify dropdown works correctly on desktop browsers
- [ ] Confirm dropdown doesn't exceed 50vh on tablet viewport
- [ ] Confirm dropdown doesn't exceed 50vh on mobile viewport
- [ ] Check workflow template dialog filter/sort layout

## Screenshots
**Before**

[before.webm](https://github.com/user-attachments/assets/64b4b969-54ed-4463-abdf-0a4adef01e72)

**After**

[after.webm](https://github.com/user-attachments/assets/b38973e5-9e77-4882-adf8-306279d302e1)

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

Co-authored-by: Claude <noreply@anthropic.com>
2025-10-28 20:25:19 -07:00
Simula_r
133662cdc7 Feat/vue noes arrange alg improved (#6357)
## Summary

Improve the previous [vue node arrange
alg](5a1284660c/src/renderer/extensions/vueNodes/layout/scaleLayoutForVueNodes.ts)
to match Litegraph much closer visually.

- In addition to the scaling of the LG node's x,y and then setting the
vue nodes position to that, we can also scale the width and height of
the LG node and set that for the Vue node wxh.
- Change from scaling from the center to the top left
- Change the zoom offset to account for the scale for seamless
transition

## Screenshots (if applicable)

<img width="1868" height="1646" alt="image"
src="https://github.com/user-attachments/assets/bc816819-211f-4619-a02a-8d167b5dc1fd"
/>

<img width="1868" height="1648" alt="image (1)"
src="https://github.com/user-attachments/assets/61faf530-1994-4cf9-bca9-a7ab6f9740c2"
/>

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6357-Feat-vue-noes-arrange-alg-improved-29b6d73d365081e7813bd6f19ce1202a)
by [Unito](https://www.unito.io)

---------

Co-authored-by: GitHub Action <action@github.com>
Co-authored-by: JakeSchroeder <jake@axiom.co>
2025-10-28 20:23:23 -07:00
Christian Byrne
a54c1516ae add dynamic config field for requiring/not requiring whitelist (#6355)
## Summary

Adds FF to toggle whitelist gating on client.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6355-add-dynamic-config-field-for-requiring-not-requiring-whitelist-29b6d73d36508172b7c2f6f65f6b8a65)
by [Unito](https://www.unito.io)
2025-10-28 19:40:52 -07:00
Arjan Singh
32a803c31e feat(historyV2): reconcile completed workflows (#6340)
## Summary

Running + Finished + History tasks now all work and reconcile correctly
in the queue.

## Changes

1. Reconcile complete workflows so they show up in history.
2. Do the above in a way that minimizes recreation of `TaskItemImpls`
3. Address some CR feedback on #6336 

## Review Focus

I tried to optimize `TaskItemImpls` so we aren't recreating ones for
history items tat already exist. Please give me feedback on if I did
this correctly, or if it was even necessary.

## Screenshots 🎃 



https://github.com/user-attachments/assets/afc08f31-cc09-4082-8e9d-cee977bc1e22

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6340-feat-historyV2-reconcile-completed-workflows-29a6d73d36508145a56aeb99cfa0e6ba)
by [Unito](https://www.unito.io)
2025-10-28 17:40:55 -07:00
Alexander Brown
32688b8e34 fix: Node Sizing that works in Firefox (#6352)
## Summary

I still love Firefox, even if it's special.

(Swap height/width and min-height min-width logic)

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6352-fix-Node-Sizing-that-works-in-Firefox-29a6d73d3650816b9a2fd271445e193f)
by [Unito](https://www.unito.io)
2025-10-28 17:20:35 -07:00
Christian Byrne
4ad7531269 update subscription dialog (#6350)
## Summary

- Align with design and new tokens
- Change to point to comfy.org/cloud/pricing

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6350-update-subscription-dialog-29a6d73d365081fc80dbdced405f6b21)
by [Unito](https://www.unito.io)
2025-10-28 15:20:21 -07:00
Comfy Org PR Bot
e8dabd2996 1.31.1 (#6349)
Patch version increment to 1.31.1

**Base branch:** `main`

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6349-1-31-1-29a6d73d365081fd8922ebe8be3d1749)
by [Unito](https://www.unito.io)

---------

Co-authored-by: christian-byrne <72887196+christian-byrne@users.noreply.github.com>
Co-authored-by: github-actions <github-actions@github.com>
2025-10-28 15:05:42 -07:00
Simula_r
f629d325b2 Feat/vue nodes arrange alg (#6212)
## Summary

### Problem: 
The Vue nodes renderer/feature introduces new designs for each node i.e.
the equivalent Litegraph node design is smaller and the vue node design
is non uniformly larger.

### Example: 

Litegraph Ksampler node: 200w x 220h

<img width="200" height="220" alt="image"
src="https://github.com/user-attachments/assets/eef0117b-7e02-407d-98ab-c610fd1ec54c"
/>

Vue Node Ksampler node: 445w x 430h 

<img width="445" height="430" alt="image"
src="https://github.com/user-attachments/assets/e78d9d45-5b32-4e8d-bf1c-bce1c699037f"
/>

This means if users load a workflow in Litegraph and then switches to
Vue nodes renderer the nodes are using the same Litegraph positions
which would cause a visual overlap and overall look broken.

### Example:

<img width="1510" height="726" alt="image"
src="https://github.com/user-attachments/assets/3b7ae9d2-6057-49b2-968e-c531a969fac4"
/>
<img width="1475" height="850" alt="image"
src="https://github.com/user-attachments/assets/ea10f361-09bd-4daa-97f1-6b45b5dde389"
/>

### Solution:

Scale the positions of the nodes in lite graph radially from the center
of the bounds of all nodes. And then simply move the Vue nodes to those
new positions.

1. Get the `center of the bounds of all LG nodes`.
2. Get the `xy of each LG node`.
3. Get the vector from `center of the bounds of all LG nodes` `-` `xy of
each LG node`.
4. Scale it by a factor (e.g. 1.75x which is the average Vue node size
increase plus some visual padding.)
5. Move each Vue node to the scaled `xy of each LG node`. 

Result: The nodes are spaced apart removing overlaps while keeping the
spatial layout intact.

<img width="2173" height="1096" alt="image"
src="https://github.com/user-attachments/assets/7817d866-4051-47bb-a589-69ca77a0bfd3"
/>

### Further concerns.

This vector scaling algorithm needs to run once per workflow when in vue
nodes. This means when in Litegraph and switching to Vue nodes, it needs
to run before the nodes render. And then now that the entire app is in
vue nodes, we need to run it each time we load a workflow. However, once
its run, we do not need to run it again. Therefore we must persist a
flag that it has run somewhere. This PR also adds that feature by
leveraging the `extra` field in the workflow schema.

---------

Co-authored-by: GitHub Action <action@github.com>
Co-authored-by: JakeSchroeder <jake@axiom.co>
2025-10-28 14:14:39 -07:00
sno
38525d8f3a feat: add weekly documentation accuracy check workflow (#6298)
## Summary
Adds a new automated workflow that runs weekly to check and update
documentation accuracy.

## Changes
- Created `.github/workflows/weekly-docs-check.yaml`
- Workflow runs every Monday at 9 AM UTC or via manual dispatch
- Uses Claude to fact-check all documentation against the current
codebase
- Automatically creates/updates a draft PR with documentation
corrections

## Workflow Features
1. **Schedule**: Runs weekly (Mondays at 9 AM UTC) or on-demand via
workflow_dispatch
2. **Documentation Review**: Claude analyzes:
   - All markdown files in `docs/`
   - Project guidelines in `CLAUDE.md`
   - README files throughout the repository
   - Claude command documentation in `.claude/commands/`
3. **Automated PR Creation**: Uses `peter-evans/create-pull-request`
action to:
   - Create or update the `docs/weekly-update` branch
   - Generate a draft PR with all documentation updates
   - Apply `documentation` and `automated` labels

## Benefits
- Keeps documentation synchronized with code changes
- Identifies outdated API references and deprecated functions
- Catches missing documentation for new features
- Ensures code examples remain valid and tested

## Test Plan
- [x] YAML syntax validated
- [ ] Workflow can be manually triggered to verify functionality
- [ ] PR creation works as expected

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

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6298-feat-add-weekly-documentation-accuracy-check-workflow-2986d73d365081d48ce0f4cf181c377f)
by [Unito](https://www.unito.io)

---------

Co-authored-by: Claude <noreply@anthropic.com>
2025-10-28 14:13:48 -07:00
Christian Byrne
c374975ddc re-enable "reconnecting WS" toast on cloud (#6348)
This error should not actually happen frequently at all, so we can
re-enable without fear of spamming an error that most users would have
no context to understand -- primary benefit is to help user report
better errors should they occur.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6348-re-enable-reconnecting-WS-toast-on-cloud-29a6d73d36508106b893cc0c0aee09fd)
by [Unito](https://www.unito.io)
2025-10-28 14:12:42 -07:00
Terry Jia
e7f640b436 subscription improve (#6339)
## Summary

Run keyboard shortcut bypasses paywall
Top Up button visible before paywall (confusing - hide until subscribed)
Question mark icon next to commercial models has no tooltip (hide until
added)

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6339-subscription-improve-29a6d73d3650818e92c7f60eda01646a)
by [Unito](https://www.unito.io)

---------

Co-authored-by: bymyself <cbyrne@comfy.org>
2025-10-28 13:00:27 -07:00
AustinMroz
6e4471ad62 Fix node sizing in vue mode (#6289)
Before

![broken-resize](https://github.com/user-attachments/assets/1d3651f7-8589-439c-99d1-fa67c5970945)
After

![vue-resize](https://github.com/user-attachments/assets/ef97ea8f-b601-4a5c-9c6a-34d3f4b776d2)

Also add ~~content~~
[contain](https://developer.mozilla.org/en-US/docs/Web/CSS/contain)
styling for improved render performance.

Future:
- Update size scaling for WidgetLayoutField widgets.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6289-Fix-node-sizing-in-vue-mode-2986d73d365081ac8fa0da35a635b226)
by [Unito](https://www.unito.io)

---------

Co-authored-by: github-actions <github-actions@github.com>
2025-10-28 12:18:57 -07:00
Alexander Brown
b03cf7e11d Style: Token renaming and style organization (#6337)
## Summary

Align color names and organize style.css some more

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6337-Style-Token-renaming-and-style-organization-29a6d73d365081b69f25ce1298c67fdc)
by [Unito](https://www.unito.io)
2025-10-28 12:13:28 -07:00
Christian Byrne
0a80a288c0 add title to asset names in model browser (#6338)
Show full title when hovering the text.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6338-add-title-to-asset-names-in-model-browser-29a6d73d36508106a1d5ce9c09007b78)
by [Unito](https://www.unito.io)
2025-10-28 08:32:16 -07:00
Christian Byrne
efed934418 remove tailwindcss eslint plugin (#6342)
This plugin does not seem to work well with tw v4. Can revert this PR
later.


---

the graph is forcing tailwind-api-utils@1.0.3 which tries to import v3
files, or something like that.

```
> @comfyorg/comfyui-frontend@1.31.0 lint:fix /home/c_byrne/projects/comfyui-frontend-testing/ComfyUI_frontend-clone-31
> eslint src --cache --fix


Oops! Something went wrong! :(

ESLint: 9.35.0

Error: Error while loading rule 'tailwindcss/enforces-negative-arbitrary-values': Cannot find module '/home/c_byrne/projects/comfyui-frontend-testing/ComfyUI_frontend-clone-31/node_modules/.pnpm/tailwindcss@4.1.12/node_modules/tailwindcss/dist/lib/setupContextUtils.js'
Require stack:
- /home/c_byrne/projects/comfyui-frontend-testing/ComfyUI_frontend-clone-31/node_modules/.pnpm/tailwind-api-utils@1.0.3_tailwindcss@4.1.12/node_modules/tailwind-api-utils/dist/index.cjs
- /home/c_byrne/projects/comfyui-frontend-testing/ComfyUI_frontend-clone-31/node_modules/.pnpm/eslint-plugin-tailwindcss@4.0.0-beta.0_tailwindcss@4.1.12/node_modules/eslint-plugin-tailwindcss/lib/util/customConfig.js
- /home/c_byrne/projects/comfyui-frontend-testing/ComfyUI_frontend-clone-31/node_modules/.pnpm/eslint-plugin-tailwindcss@4.0.0-beta.0_tailwindcss@4.1.12/node_modules/eslint-plugin-tailwindcss/lib/util/tailwindAPI.js
- /home/c_byrne/projects/comfyui-frontend-testing/ComfyUI_frontend-clone-31/node_modules/.pnpm/eslint-plugin-tailwindcss@4.0.0-beta.0_tailwindcss@4.1.12/node_modules/eslint-plugin-tailwindcss/lib/rules/classnames-order.js
- /home/c_byrne/projects/comfyui-frontend-testing/ComfyUI_frontend-clone-31/node_modules/.pnpm/eslint-plugin-tailwindcss@4.0.0-beta.0_tailwindcss@4.1.12/node_modules/eslint-plugin-tailwindcss/lib/index.js
Occurred while linting /home/c_byrne/projects/comfyui-frontend-testing/ComfyUI_frontend-clone-31/src/base/common/downloadUtil.ts
    at Module._resolveFilename (node:internal/modules/cjs/loader:1410:15)
    at defaultResolveImpl (node:internal/modules/cjs/loader:1051:19)
    at resolveForCJSWithHooks (node:internal/modules/cjs/loader:1056:22)
    at Module._load (node:internal/modules/cjs/loader:1219:37)
    at TracingChannel.traceSync (node:diagnostics_channel:322:14)
    at wrapModuleLoad (node:internal/modules/cjs/loader:238:24)
    at Module.require (node:internal/modules/cjs/loader:1493:12)
    at require (node:internal/modules/helpers:152:16)
    at TailwindUtils.loadConfigV3 (/home/c_byrne/projects/comfyui-frontend-testing/ComfyUI_frontend-clone-31/node_modules/.pnpm/tailwind-api-utils@1.0.3_tailwindcss@4.1.12/node_modules/tailwind-api-utils/dist/index.cjs:429:31)
    at getTailwindConfig (/home/c_byrne/projects/comfyui-frontend-testing/ComfyUI_frontend-clone-31/node_modules/.pnpm/eslint-plugin-tailwindcss@4.0.0-beta.0_tailwindcss@4.1.12/node_modules/eslint-plugin-tailwindcss/lib/util/tailwindAPI.js:14:11)
 ELIFECYCLE  Command failed with exit code 2.
```
2025-10-28 08:31:34 -07:00
Johnpaul Chiwetelu
b3da6cf1b4 Contextmenu extension migration (#5993)
This pull request refactors how context menu items are contributed by
extensions in the LiteGraph-based canvas. The legacy monkey-patching
approach for adding context menu options is replaced by a new, explicit
API (`getCanvasMenuItems` and `getNodeMenuItems`) for extensions. A
compatibility layer is added to support legacy extensions and warn
developers about deprecated usage. The changes improve maintainability,
extension interoperability, and migration to the new context menu
system.

### Context Menu System Refactor

* Introduced a new API for extensions to contribute context menu items
via `getCanvasMenuItems` and `getNodeMenuItems` methods, replacing
legacy monkey-patching of `LGraphCanvas.prototype.getCanvasMenuOptions`.
Major extension files (`groupNode.ts`, `groupOptions.ts`,
`nodeTemplates.ts`) now use this new API.
[[1]](diffhunk://#diff-b29f141b89433027e7bb7cde57fad84f9e97ffbe5c58040d3e0fdb7905022917L1779-R1771)
[[2]](diffhunk://#diff-91169f3a27ff8974d5c8fc3346bd99c07bdfb5399984484630125fdd647ff02fL232-R239)
[[3]](diffhunk://#diff-04c18583d2dbfc013888e4c02fd432c250acbcecdef82bf7f6d9fd888e632a6eL447-R458)
* Added a compatibility layer (`legacyMenuCompat` in
`contextMenuCompat.ts`) to detect and warn when legacy monkey-patching
is used, and to extract legacy-added menu items for backward
compatibility.
[[1]](diffhunk://#diff-2b724cb107c04e290369fb927e2ae9fad03be9e617a7d4de2487deab89d0d018R2-R45)
[[2]](diffhunk://#diff-d3a8284ec16ae3f9512e33abe44ae653ed1aa45c9926485ef6270cc8d2b94ae6R1-R115)

### Extension Migration

* Refactored core extensions (`groupNode`, `groupOptions`, and
`nodeTemplates`) to implement the new context menu API, moving menu item
logic out of monkey-patched methods and into explicit extension methods.
[[1]](diffhunk://#diff-b29f141b89433027e7bb7cde57fad84f9e97ffbe5c58040d3e0fdb7905022917L1633-L1683)
[[2]](diffhunk://#diff-91169f3a27ff8974d5c8fc3346bd99c07bdfb5399984484630125fdd647ff02fL19-R77)
[[3]](diffhunk://#diff-04c18583d2dbfc013888e4c02fd432c250acbcecdef82bf7f6d9fd888e632a6eL366-R373)

### Type and Import Cleanup

* Updated imports for context menu types (`IContextMenuValue`) across
affected files for consistency with the new API.
[[1]](diffhunk://#diff-b29f141b89433027e7bb7cde57fad84f9e97ffbe5c58040d3e0fdb7905022917R4-L7)
[[2]](diffhunk://#diff-91169f3a27ff8974d5c8fc3346bd99c07bdfb5399984484630125fdd647ff02fL1-R11)
[[3]](diffhunk://#diff-04c18583d2dbfc013888e4c02fd432c250acbcecdef82bf7f6d9fd888e632a6eL2-R6)
[[4]](diffhunk://#diff-bde0dce9fe2403685d27b0e94a938c3d72824d02d01d1fd6167a0dddc6e585ddR10)

### Backward Compatibility and Migration Guidance

* The compatibility layer logs a deprecation warning to the console when
legacy monkey-patching is detected, helping developers migrate to the
new API.

---

These changes collectively modernize the context menu extension
mechanism, improve code clarity, and provide a migration path for legacy
extensions.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5993-Contextmenu-extension-migration-2876d73d3650813fae07c1141679637a)
by [Unito](https://www.unito.io)

---------

Co-authored-by: github-actions <github-actions@github.com>
2025-10-28 04:02:28 +01:00
Alexander Brown
6afdb9529d Fix: Vue-Litegraph conversion bug with Reroutes (#6330)
## Summary

The private fields triggered an error in intitializing the
linkLayoutSync. Turns out that wasn't necessary anymore.

> [!NOTE]
> Edit: Doing some more investigation, it looks like the slot sync can
also be removed?

## Changes

- **What**: Converts JS private fields to typescript private, adds some
readonly declarations
- **What**: Removes the useLinkLayoutSync usage in useVueNodeLifecycle
- **What**: Removes the useSlotLayoutSync usage in useVueNodeLifecycle

## Review Focus

Was the sync doing something that wouldn't be caught in normal
usage/testing?

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6330-Fix-Vue-Litegraph-conversion-bug-with-Reroutes-2996d73d3650819ebb24e4aa2fc51c65)
by [Unito](https://www.unito.io)
2025-10-27 19:36:19 -07:00
Christian Byrne
d1c9ce5a66 change some settings for cloud-specific behavior (#6302)
## Summary

Make some settings dynamic based on whether in cloud or localhost

1. Comfy.Memory.AllowManualUnload (line 18-24)
   - Type: 'hidden' on cloud, 'boolean' on localhost
   - Default: false on cloud, true on localhost
2. Comfy.Validation.Workflows (line 25-30)
   - Default: false on cloud, true on localhost
3. Comfy.Workflow.ShowMissingModelsWarning (line 282-288)
   - Type: 'hidden' on cloud, 'boolean' on localhost
   - Default: false on cloud, true on localhost
4. Comfy.ModelLibrary.AutoLoadAll (line 387-394)
   - Type: 'hidden' on cloud, 'boolean' on localhost
5. Comfy.QueueButton.BatchCountLimit (line 595-603)
   - Default: 4 on cloud, 100 on localhost
6. Comfy.Toast.DisableReconnectingToast (line 943-949)
   - Default: true on cloud, false on localhost
7. Comfy.Assets.UseAssetAPI (line 1068-1075)
   - Default: true on cloud, false on localhost

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6302-change-some-settings-for-cloud-specific-behavior-2986d73d365081169be4ebd11823a7fa)
by [Unito](https://www.unito.io)
2025-10-27 16:57:00 -07:00
Arjan Singh
d26309c7ab feat(historyV2): create sythetic queue priority (#6336)
## Summary

Create display priority based on execution success timestamps.

Next up is displaying in progress prompts in the queue.

## Review Focus

@DrJKL and I discussed logic and decided for history, execution success
(when the prompt finishes) is the best way to assign priority.

This does differ from existing cloud logic which uses execution start
time.

For prompt progress I intend to create a priority for them based on
start time.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6336-feat-historyV2-create-sythetic-queue-priority-2996d73d365081ffa3a0f8071c178066)
by [Unito](https://www.unito.io)
2025-10-27 23:08:33 +00:00
Alexander Brown
d8657aaee3 devex: Add some acronyms (#6335)
## Summary

Suggested by @arjansingh, encourage the LLMs to consider more classic
practices.
2025-10-27 21:53:52 +00:00
Christian Byrne
ddbf2cc720 skip system notifications on cloud (#6333)
## Summary

This code has no effect but adding it so that when the `!isElectron()`
condition is removed, maintainers will remember this extra condition
that must still be enforced.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6333-skip-system-notifications-on-cloud-2996d73d3650818b8335d395c86badf3)
by [Unito](https://www.unito.io)
2025-10-27 14:25:43 -07:00
Christian Byrne
ed49a82c20 fix subscription panel badge bg color (#6334)
## Summary

This should use the dialog bg color instead of menu bg when it is on a
dialog.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6334-fix-subscription-panel-badge-bg-color-2996d73d36508171bf5bd2a8dc54f7c3)
by [Unito](https://www.unito.io)
2025-10-27 14:24:55 -07:00
Christian Byrne
234fc3433c use prompt id rather than queue index for history reconciliation (#6327)
## Summary

Changed history reconciliation to use `promptId` instead of `queueIndex`
to support cloud environments that don't have queue indices.

Refactored into pure functions with expressive naming.

## Changes

- Use `promptId` (unique UUID) for reconciliation instead of
`queueIndex` (not available in cloud)
- Extract `reconcileHistoryWithServer` with helpers: `extractPromptIds`,
`isAddedAfter`, `sortNewestFirst`
- Added 34 tests covering reconciliation edge cases, sorting, and limits

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6327-use-prompt-id-rather-than-queue-index-for-history-reconciliation-2996d73d3650818e9471dbeb53cc23bb)
by [Unito](https://www.unito.io)
2025-10-26 23:58:29 -07:00
Christian Byrne
0a957fb2ac ci: leave comment and abort early if commit already exists on target branch in backport workflow (#6326)
Follow up on https://github.com/Comfy-Org/ComfyUI_frontend/pull/6317:
Fixes confusing "merge conflicts detected" message when commit already
exists on target branch (e.g., PRs #6294 and #6307).

## Changes

- Added check to detect if merge commit already exists on target branch
before attempting cherry-pick
- New failure reason `already-exists` for this case
- Clear comment message: "Commit already exists on branch, no backport
needed" instead of confusing "Merge conflicts detected" with empty file
list

## Before

When a commit already existed on the target branch, users would see:
> @user Backport to `core/1.30` failed: Merge conflicts detected.
> Please manually cherry-pick commit `abc123` to the `core/1.30` branch.
> <details><summary>Conflicting files</summary>
> 
> </details>

This was confusing because there were no actual conflicts - the commit
was already present.

## After

Users now see:
> @user Commit `abc123` already exists on branch `core/1.30`. No
backport needed.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6326-ci-leave-comment-and-abort-early-if-commit-already-exists-on-target-branch-in-backport-w-2996d73d36508167aff3e6783e832c74)
by [Unito](https://www.unito.io)
2025-10-26 23:37:22 -07:00
Christian Byrne
28a6089a94 ci: automate cloud release branch tagging (#6321)
Changes the RC minor version branch release automation to create paired
`core/x.y` and `cloud/x.y` branches whenever a release bump merges.

Then changes the backport workflow to accept labels that match those
branch names directly, allowing engineers to route fixes to either OSS
or cloud release lines without extra labels or artifacts.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6321-ci-automate-cloud-release-branch-tagging-2996d73d365081b0b036ebd3f088354b)
by [Unito](https://www.unito.io)
2025-10-26 22:47:15 -07:00
Christian Byrne
298b3c629b ci: update automated backport workflow to skip if backport already in-flight or completed (#6317)
Updates the backport workflow so it filters out branches that have
already been backported, allowing new labels to trigger fresh
cherry-picks without reprocessing completed targets.

Also adds an automatic cleanup step that removes the `needs-backport`
trigger label once a run succeeds, aligning its behavior with the other
label-driven automations.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6317-ci-update-automated-backport-workflow-to-skip-if-backport-already-in-flight-or-completed-2996d73d36508113b90df43b1f68344f)
by [Unito](https://www.unito.io)
2025-10-26 22:14:30 -07:00
sno
20a1a9eda2 [bugfix] fix @intlify/vue-i18n/no-raw-text linting errors (#6280)
## Summary

This PR fixes all @intlify/vue-i18n/no-raw-text linting errors
identified in #5625 by replacing raw text strings with proper i18n
translation function calls.

## Changes

Fixed i18n linting errors in the following files:
- `src/components/widget/SampleModelSelector.vue` - "Upload Model" →
`$t('g.upload')`
- `src/components/topbar/CurrentUserButton.vue` - "user profile" →
`$t('g.currentUser')`
- `src/components/sidebar/tabs/nodeLibrary/NodeHelpPage.vue` - "Loading
help" → `$t('g.loading')`
- `src/components/sidebar/SidebarShortcutsToggleButton.vue` -
"shortcuts.shortcuts" → `$t('shortcuts.shortcuts')`
- `src/components/sidebar/SidebarLogoutIcon.vue` - "sideToolbar.logout"
→ `$t('sideToolbar.logout')`
- `src/components/sidebar/SidebarHelpCenterIcon.vue` - "menu.help" →
`$t('menu.help')`
- `src/components/sidebar/SidebarBottomPanelToggleButton.vue` -
"sideToolbar.labels.console" → `$t('sideToolbar.labels.console')`
- `src/components/load3d/controls/viewer/ViewerCameraControls.vue` -
"fov" → `t('load3d.fov')`
- `src/components/helpcenter/HelpCenterMenuContent.vue` - "Help Center
Menu" and "Recent releases" → `$t()` calls

All raw text strings have been replaced with appropriate i18n
translation keys that already exist in `src/locales/en/main.json`.

## Related Issue

Fixes errors reported in CI job:
https://github.com/Comfy-Org/ComfyUI_frontend/actions/runs/18705105609/job/53341658467?pr=5625

This PR aims to help #5625 pass CI/CD checks.

## Test Plan

- All i18n linting errors should be resolved
- No functionality changes - only proper use of i18n system
- Existing translation keys are used from the locale files

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6280-bugfix-fix-intlify-vue-i18n-no-raw-text-linting-errors-2976d73d365081369b43de01486fb409)
by [Unito](https://www.unito.io)

---------

Co-authored-by: GitHub Action <action@github.com>
2025-10-26 22:12:45 -07:00
Christian Byrne
ca45b2c4d6 [bugfix] use raw template ID for workflow_name in telemetry tracking (#6320)
## Summary

Fixes `trackTemplate` calls to use raw template ID instead of translated
workflow name for analytics tracking.

## Problem

When users load templates with non-English locale, the `workflow_name`
field was being tracked in their language (e.g., "默认工作流" for Chinese
users) instead of English. This makes statistical analysis difficult.

## Changes

- Updated both `trackTemplate` calls in `useTemplateWorkflows.ts` to use
raw `id` instead of translated `workflowName`
- The translated name is still used for display purposes in
`loadGraphData`

**Before:**
```typescript
useTelemetry()?.trackTemplate({
  workflow_name: workflowName, // Could be "默认工作流"
  template_source: sourceModule
})
```

**After:**
```typescript
useTelemetry()?.trackTemplate({
  workflow_name: id, // Always "default"
  template_source: sourceModule
})
```

## Testing

- [x] Type checking passes
- [x] Verified translated names still used for display

## Related

Part of comprehensive i18n audit for telemetry tracking. Related to
template metadata English tracking in PR #6304.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6320-bugfix-use-raw-template-ID-for-workflow_name-in-telemetry-tracking-2996d73d365081d8aef3fa2529a10247)
by [Unito](https://www.unito.io)
2025-10-26 22:02:27 -07:00
Christian Byrne
1453afad12 refactor: rename size report workflows to match naming pattern of other workflows (#6322)
## Summary

Changes gh workflow names and job names to match the unified naming
style of the other workflows.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6322-refactor-rename-size-report-workflows-to-match-naming-pattern-of-other-workflows-2996d73d365081c79cfcdfcb9013c3e1)
by [Unito](https://www.unito.io)
2025-10-26 21:51:38 -07:00
Christian Byrne
5de1a91f02 ci: fix "size report" (bundle size) markdown table comment formatting (#6318)
## Summary

Fixes https://github.com/Comfy-Org/ComfyUI_frontend/issues/6136 by
adding empty lines between html and markdown in the bundle size report
comments.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6318-ci-fix-size-report-bundle-size-markdown-table-comment-formatting-2996d73d36508104872be56c238339a8)
by [Unito](https://www.unito.io)
2025-10-26 21:06:59 -07:00
Christian Byrne
b3eee54abb fix: claude-review bails on cancelled and skipped checks (#6316)
## Summary

Expands the wait step in `pr-claude-review.yaml` so the
`lewagon/wait-on-check-action` accepts every terminal GitHub conclusion
instead of failing on outcomes like cancelled or failure. After the wait
completes, the `check-status` script still inspects those check runs and
only marks `should-proceed=true` when the tracked jobs finished
successfully.

Fixes what happened here:
https://github.com/Comfy-Org/ComfyUI_frontend/actions/runs/18828179272/job/53714488799?pr=6298
2025-10-26 20:39:18 -07:00
Christian Byrne
1ee33673ab [bugfix] fix survey properties mapping to match actual survey data (#6314)
## Summary

Updates `SurveyResponses` interface to match actual survey fields 1-to-1
based on analytics requirements.

## Changes

- Remove `team_size` property (no corresponding survey question exists)
- Change `use_case` to `useCase` to match actual survey field name
- Remove `intended_use` property (doesn't exist in actual survey)
- Add `making` field array for content generation type tracking (video,
image, etc.)

This fixes a Mixpanel analytics issue where survey properties weren't
mapping 1-to-1 with actual survey questions, making statistical analysis
difficult.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6314-bugfix-fix-survey-properties-mapping-to-match-actual-survey-data-2996d73d36508158a335e6b73e3e14ef)
by [Unito](https://www.unito.io)
2025-10-26 18:04:44 -07:00
Terry Jia
9f5245dc80 [refactor] Split mask editor into smaller files (#6308)
## Summary

Split mask editor structure into smaller files

## Changes

This PR is a prerequisite step for [the issue - refactoring the mask
editor using
Vue](https://github.com/Comfy-Org/ComfyUI_frontend/issues/5956). It
splits the current monolithic maskeditor.ts (about 5700 lines) into
separate files; otherwise, the original single file would be very
difficult to analyze or maintain.

This PR itself does not introduce any Vue, nor should it have any
functional changes or modifications. It's purely a code-level split,
with all related files placed in the maskeditor folder.

The original maskeditor.ts has been renamed to maskeditor.ts.backup for
future reference.

## Review Focus

Since this PR is only for splitting purposes, all logic remains
consistent with the original. Therefore, for any reviewer: any code
logic improvements should happen in the subsequent Vue-based
refactoring, not in this PR.

Following this PR, I will perform a Vue-based refactoring of the mask
editor to align with the frontend's overall Vue architecture and provide
better cloud-related support.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6308-refactor-Split-mask-editor-into-smaller-files-2986d73d36508131937dd43e465a47bd)
by [Unito](https://www.unito.io)
2025-10-26 17:34:36 -07:00
Comfy Org PR Bot
cacd7e3251 1.31.0 (#6313)
Minor version increment to 1.31.0

**Base branch:** `main`

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6313-1-31-0-2986d73d365081e9ac6ccb91b4ddad0a)
by [Unito](https://www.unito.io)

---------

Co-authored-by: christian-byrne <72887196+christian-byrne@users.noreply.github.com>
Co-authored-by: github-actions <github-actions@github.com>
2025-10-26 17:20:40 -07:00
Alexander Piskun
fca0ea72f7 feat(api-nodes): add pricing for new LTXV-2 models (#6307)
## Summary

For the upcoming LTXV API nodes.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6307-feat-api-nodes-add-pricing-for-new-LTXV-2-models-2986d73d365081db9994deffc219c6c4)
by [Unito](https://www.unito.io)
2025-10-26 11:45:55 -07:00
Christian Byrne
9f36158959 translate all analytics to English for template metadata (#6292)
## Summary

Track template metadata in English for analytics regardless of user's
locale to enable consistent statistical analysis.

## Changes

- **What**: Load English [template
index](https://github.com/Comfy-Org/ComfyUI_frontend/tree/main/public/templates)
alongside localized version (cloud builds only)
- **What**: Added `getEnglishMetadata()` method to
`workflowTemplatesStore` that returns English versions of template tags,
category, useCase, models, and license
- **What**: Updated `MixpanelTelemetryProvider` to prefer English
metadata for analytics events, falling back to localized values

## Review Focus

English template fetch only triggers in cloud builds via `isCloud` flag.
Non-cloud builds see no bundle size impact. Method returns null when
English templates unavailable, with fallback to localized data ensuring
analytics continue working in edge cases.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6292-translate-all-analytics-to-English-for-template-metadata-2986d73d365081fdbc21f372aa9bb41e)
by [Unito](https://www.unito.io)

---------

Co-authored-by: github-actions <github-actions@github.com>
2025-10-26 02:10:02 -07:00
Christian Byrne
857c13158b set Sentry config based on distribution (#6301)
Set the config based on compile-time DISTRIBUTION env var.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6301-set-Sentry-config-based-on-distribution-2986d73d3650815f9b7ef821bbdd745f)
by [Unito](https://www.unito.io)
2025-10-26 02:05:23 -07:00
Christian Byrne
cd50c54e61 add session cookie auth on cloud dist (#6295)
## Summary

Implemented cookie-based session authentication for cloud distribution,
replacing service worker approach with extension-based lifecycle hooks.

## Changes

- **What**: Added session cookie management via [extension
hooks](https://docs.comfy.org/comfyui/extensions) for login, token
refresh, and logout events
- **Architecture**: DDD-compliant structure with platform layer
(`src/platform/auth/session/`) and cloud-gated extension
- **New Extension Hooks**: `onAuthTokenRefreshed()` and
`onAuthUserLogout()` in [ComfyExtension
interface](src/types/comfy.ts:220-232)

```mermaid
sequenceDiagram
    participant User
    participant Firebase
    participant Extension
    participant Backend

    User->>Firebase: Login
    Firebase->>Extension: onAuthUserResolved
    Extension->>Backend: POST /auth/session (with JWT)
    Backend-->>Extension: Set-Cookie

    Firebase->>Firebase: Token Refresh
    Firebase->>Extension: onAuthTokenRefreshed
    Extension->>Backend: POST /auth/session (with new JWT)
    Backend-->>Extension: Update Cookie

    User->>Firebase: Logout
    Firebase->>Extension: onAuthUserLogout (user null)
    Extension->>Backend: DELETE /auth/session
    Backend-->>Extension: Clear Cookie
```

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6295-add-session-cookie-auth-on-cloud-dist-2986d73d365081868c56e5be1ad0d0d4)
by [Unito](https://www.unito.io)
2025-10-26 00:04:30 -07:00
Christian Byrne
3db1b153f3 make topbar badges responsive and fix server health badges showing on unrelated dialogs (#6291)
## Summary

Implemented responsive topbar badges with three display modes (full,
compact, icon-only) using Tailwind breakpoints and PrimeVue Popover
interactions.


https://github.com/user-attachments/assets/57912253-b1b5-4a68-953e-0be942ff09c4

## Changes

- **What**: Replaced hardcoded 880px breakpoint with [Tailwind
breakpoints](https://tailwindcss.com/docs/responsive-design) via
[@vueuse/core](https://vueuse.org/core/useBreakpoints/)
  - `xl (≥1280px)`: Full display (icon + label + text)
  - `lg (≥1024px)`: Compact (icon + label, click for popover)
  - `<lg (<1024px)`: Icon-only (icon/label/dot, click for popover)
- **What**: Added `CloudBadge` component to isolate static "Comfy Cloud
BETA" badge from runtime store badges
- **What**: Added `backgroundColor` prop to support different contexts
(topbar vs dialog backgrounds)

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6291-make-topbar-badges-responsive-and-fix-server-health-badges-showing-on-unrelated-dialogs-2986d73d365081d294e5c9a7af1aafb2)
by [Unito](https://www.unito.io)
2025-10-25 23:42:37 -07:00
Christian Byrne
d9e62985c6 remove all auth service work related code (#6294)
## Summary

Removes all service worker auth code, as it is being replaced by a more
robust standard solution for authenticating view and viewvideo requests
in https://github.com/Comfy-Org/ComfyUI_frontend/pull/6295.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6294-remove-all-auth-service-work-related-code-2986d73d36508170a24bf1c42cad401e)
by [Unito](https://www.unito.io)
2025-10-25 23:08:41 -07:00
Christian Byrne
e5d5c042c7 fix and enable skipped Vue nodes bypass test (#6290)
This test was failing on main for a few days so it was marked as
`fixme`. The failure was due to interaction with minimap. Just turn off
the minimap for now as it's not really related to what the test is
targeting (arguable). Better to at least have the coverage for now.

Context:

- https://github.com/Comfy-Org/ComfyUI_frontend/pull/6127

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6290-fix-and-enable-skipped-Vue-nodes-bypass-test-2986d73d3650819faeaaf414ce2b6e61)
by [Unito](https://www.unito.io)

---------

Co-authored-by: github-actions <github-actions@github.com>
2025-10-25 23:03:05 -07:00
Christian Byrne
9350c857a2 style: use "text-button-icon" token for dropdown icons on Vue nodes (#6284)
## Summary

Change these dropdown icons to match [the
design](https://www.figma.com/design/31uH3r4x3xbIctuRWYW6NM/V3---Vue-Nodes?node-id=6189-6820&m=dev).

**Before**

<img width="692" height="694" alt="Selection_2196"
src="https://github.com/user-attachments/assets/aa1fb135-0ad0-4ab6-8c79-7163349b5fae"
/>

**After**

<img width="600" height="602" alt="Selection_2195"
src="https://github.com/user-attachments/assets/742ddef4-4b51-48ba-98a0-9517267de0e1"
/>

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6284-style-use-text-button-icon-token-for-dropdown-icons-on-Vue-nodes-2976d73d365081d29d2aee51e836caaf)
by [Unito](https://www.unito.io)

---------

Co-authored-by: github-actions <github-actions@github.com>
2025-10-25 22:36:01 -07:00
Arjan Singh
c67c93ff4b feat(api): add history_v2 for cloud outputs (#6288)
## Summary

Backport outputs from new cloud history endpoint

Does:
1. Show history in the Queue
2. Show outputs from prompt execution

Does not:
1. Handle appending latest images generated to queue history
2. Making sure that workflow data from images is available from load
(requires additional API call to fetch)

Most of this PR is:
1. Test fixtures (truncated workflow to test).
2. The service worker so I could verify my changes locally.

## Changes

- Add `history_v2` to `history` adapter
- Add tests for mapping
- Do branded validation for promptIds (suggestion from @DrJKL)
- Create a dev environment service worker so we can view cloud hosted
images in development.

## Review Focus

1. Is the dev-only service work the right way to do it? It was the
easiest I could think of.
4. Are the validation changes too heavy? I can rip them out if needed.

## Screenshots 🎃 


https://github.com/user-attachments/assets/1787485a-8d27-4abe-abc8-cf133c1a52aa

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6288-Feat-history-v2-outputs-2976d73d365081a99864c40343449dcd)
by [Unito](https://www.unito.io)

---------

Co-authored-by: bymyself <cbyrne@comfy.org>
2025-10-25 22:16:38 -07:00
sno
e0e6d15cbb [refactor] remove downlevelIteration from tsconfig (#6279)
Remove downlevelIteration compiler option as it's no longer needed with
ES2023 target.

picked from - [\[Cleanup\] Remove TS migration lines from tsconfig by
webfiltered · Pull Request #5330 · Comfy-Org/ComfyUI_frontend](
https://github.com/Comfy-Org/ComfyUI_frontend/pull/5330 )

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

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6279-refactor-remove-downlevelIteration-from-tsconfig-2976d73d3650819a9d4be716006e8b85)
by [Unito](https://www.unito.io)

Co-authored-by: Claude <noreply@anthropic.com>
2025-10-26 13:30:13 +09:00
Christian Byrne
2ea6c92030 sort template workflows by required vram (#6285)
## Summary

Resolves https://github.com/Comfy-Org/ComfyUI_frontend/issues/6281 by
implementing the stubbed out vram sorting. Previously was waiting for it
to be added to the templates data and it now has
(https://github.com/Comfy-Org/workflow_templates/blob/main/templates/index.json)

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6285-sort-template-workflows-by-required-vram-2976d73d36508164a8f9fab438f53b21)
by [Unito](https://www.unito.io)
2025-10-25 15:52:24 -07:00
Christian Byrne
23e0d26ff8 add fuzzy searching to assets dialog (#6286)
## Summary

Add `useFuse` for assets searching to enable fuzzy searching with typo
tolerance.


https://github.com/user-attachments/assets/0c55bb77-3223-45ab-8c05-713f8ce4e58b

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6286-add-fuzzy-searching-to-assets-dialog-2976d73d3650813da63acadde2cf49c6)
by [Unito](https://www.unito.io)
2025-10-25 14:02:46 -07:00
Christian Byrne
95b3b509c7 [bugfix] add mode: no-cors to fix CORS error when following GCS redirects (#6277)
Fixes CORS error when service worker follows redirects to GCS by using
mode: 'no-cors' to allow cross-origin fetches without CORS headers.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6277-bugfix-add-mode-no-cors-to-fix-CORS-error-when-following-GCS-redirects-2976d73d36508101a4cbd7b59106dfc3)
by [Unito](https://www.unito.io)
2025-10-24 23:07:29 -07:00
Christian Byrne
936da14dbc [bugfix] fix service worker opaqueredirect error and ensure SW controls page before mount (#6275)
Fixes service worker network error by handling opaqueredirect responses
correctly and ensures SW registration completes before app mount to
prevent race conditions on first load.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6275-bugfix-fix-service-worker-opaqueredirect-error-and-ensure-SW-controls-page-before-mount-2976d73d36508106bc65dc82cdc62779)
by [Unito](https://www.unito.io)
2025-10-24 22:30:16 -07:00
Christian Byrne
bab47869c9 [bugfix] fix service worker registration timing to run after Pinia setup (#6272)
## Summary

Fixes Pinia initialization error that occurred when the auth service
worker tried to access stores before Pinia was set up.

## Problem

The auth service worker was being imported at the top of `main.ts`,
causing it to register immediately:

```typescript
import '@/platform/auth/serviceWorker'  // Runs immediately on import
```

This happened before Pinia was initialized, causing an error when the
service worker setup tried to use stores:

```
Error: [🍍]: "getActivePinia()" was called but there was no active Pinia.
```

## Solution

Moved the service worker registration to after Pinia is set up but
before mounting the app:

```typescript
  .use(pinia)
  .use(i18n)
  .use(VueFire, {
    firebaseApp,
    modules: [VueFireAuth()]
  })

// Register auth service worker after Pinia is initialized (cloud-only)
if (isCloud) {
  void import('@/platform/auth/serviceWorker')
}

app.mount('#vue-app')
```

## Benefits

-  Pinia stores are available when service worker setup runs
-  Still tree-shaken for non-cloud builds via dynamic import in `if
(isCloud)`
-  Registers before mounting, so service worker is active from the
start

## Testing

- Verified no Pinia errors in cloud builds
- Verified tree-shaking still works (service worker code not in
localhost builds)

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6272-bugfix-fix-service-worker-registration-timing-to-run-after-Pinia-setup-2976d73d365081b998dfd2eded782070)
by [Unito](https://www.unito.io)
2025-10-24 20:16:02 -07:00
Alexander Brown
3c28dd28cc Style: Remove currently non-functional collapsed slots (#6264)
## Summary

Address request to remove confusing hover behavior in the collapsed
state

## Review Focus

Confirmed with Alex that it's okay to remove these now and add them back
with the implementation instead of leaving them as decorative elements.

Also fixes a bug where the slots _all_ revealed when a node was hovered.

## Screenshots (if applicable)
<img width="769" height="376" alt="image"
src="https://github.com/user-attachments/assets/ec0037af-fd2e-4855-b0e4-f994f9ef9a0b"
/>

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6264-Style-Remove-currently-non-functional-collapsed-slots-2976d73d36508191914def8889491e8e)
by [Unito](https://www.unito.io)

---------

Co-authored-by: github-actions <github-actions@github.com>
2025-10-24 20:11:17 -07:00
Rizumu Ayaka
15c7c56f83 feat: deprecated API alert (#6090)
When an extension imports deprecated APIs, this log will appear:
```bash
[ComfyUI Deprecated] Importing from <file path> is deprecated. <guidance information>. This will be removed in <version>. See: <url>
```


┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6090-feat-deprecated-API-alert-28e6d73d365081898c5ddafcc025d6f8)
by [Unito](https://www.unito.io)
2025-10-24 19:26:11 -07:00
Comfy Org PR Bot
c7c513ae4a 1.30.3 (#6261)
Patch version increment to 1.30.3

**Base branch:** `main`

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6261-1-30-3-2966d73d365081799d90f8510c7e23dc)
by [Unito](https://www.unito.io)

---------

Co-authored-by: simula-r <18093452+simula-r@users.noreply.github.com>
Co-authored-by: github-actions <github-actions@github.com>
2025-10-24 19:22:54 -07:00
Christian Byrne
b1439be7f0 remove checkbox from sign up form (#6269)
## Summary

Removes the checkbox from the sign up form to simplify the user
experience. The "By clicking 'Next' or 'Sign Up'..." notice at the
bottom already covers terms and privacy.

## Changes

- Removed checkbox field from sign up schema
- Updated `SignUpForm.vue` component
- Removed unused `Checkbox` import

## Before/After

**Before:**
Sign up form included a checkbox that users had to check before
submitting

**After:**
Sign up form is cleaner without the checkbox. The existing notice text
covers the same information:
> "By clicking 'Next' or 'Sign Up', you agree to our Terms of Use and
Privacy Policy."

This notice appears on both sign in and sign up modals.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6269-remove-checkbox-from-sign-up-form-2976d73d3650819ab480e4db6685baee)
by [Unito](https://www.unito.io)
2025-10-24 19:21:16 -07:00
Christian Byrne
81fc65e59b [bugfix] fix auth service worker to handle cross-origin redirects to GCS (#6265)
## Summary

Fixes CORS errors in HTTPS environments where the auth service worker
blocked cross-origin redirects to Google Cloud Storage.

## Problem

The service worker was using `mode: 'same-origin'` which prevented
following redirects when `/api/view` returns a 302 redirect to GCS:

```
Unsafe attempt to load URL https://storage.googleapis.com/... 
from frame with URL https://testcloud.comfy.org/auth-sw.js. 
Domains, protocols and ports must match.
```

This only occurred in HTTPS/cloud environments where media is served
from GCS. Localhost/HTTP test environments serve files directly without
redirects, so the issue wasn't caught there.

## Solution

Changed redirect handling from automatic to manual:

1. **Initial request to `/api/view`**: Sends WITH auth headers
(validates user access)
2. **Detect redirect response**: Checks for 301/302/opaqueredirect 
3. **Follow redirect to GCS**: Fetches WITHOUT auth headers (signed URL
has built-in auth)

### Key Changes

- Removed `mode: 'same-origin'` (was blocking cross-origin redirects)
- Changed `redirect: event.request.redirect` to `redirect: 'manual'`
- Added manual redirect handling that follows to GCS without Firebase
auth headers

## Why This Works

The two requests have different authentication mechanisms:
- **`/api/view` request**: Uses Firebase auth header (backend validates
user access)
- **GCS request**: Uses signed URL with query params (`Signature=...`,
`GoogleAccessId=...`, `Expires=...`)

The security check still happens on the initial `/api/view` request, but
we allow the redirect to GCS to use its own authentication system.

## Testing

- Typecheck passed
- Should be tested in HTTPS cloud environment with media files stored in
GCS

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6265-bugfix-fix-auth-service-worker-to-handle-cross-origin-redirects-to-GCS-2976d73d365081d0b124db4918f8194e)
by [Unito](https://www.unito.io)
2025-10-24 18:31:38 -07:00
Alexander Brown
55c9cf7b57 Style: explicit font size for the badges (#6260)
## Summary

More consistent height regardless the text content.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6260-Style-explicit-font-size-for-the-badges-2966d73d365081bd90a0d01f7e104b9a)
by [Unito](https://www.unito.io)

---------

Co-authored-by: github-actions <github-actions@github.com>
2025-10-24 14:54:59 -07:00
AustinMroz
5897e00793 Fix disconnection of subgraphInput links (#6258)
`LLink.disconnect` is intended to cleanup only the link itself. #4800
mistakenly assumed that it would perform all required steps for
disconnection. Later, #5015 would partially resolve this by adding some
of the missing functionality into `LLink.disconnect`, but this still
left output cleanup unhandled and failed to call
`node.onConnectionsChanged`.

This PR instead moves the disconnection code to call the function that
already has robust handling for these items and removes the
no-longer-needed and potentially misleading workaround.

Resolves #6247

Also un-skipped several SubgraphIO tests. They appear to function fine.
I'm assuming the reasons for them being skipped have been resolved.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6258-Fix-disconnection-of-subgraphInput-links-2966d73d36508112ad1fe602cdcf461b)
by [Unito](https://www.unito.io)
2025-10-24 13:37:14 -07:00
Alexander Brown
233004c837 Feat: Add Badges for Vue Nodes (#6243)
## Summary

Adds the badges to the header for Vue nodes.


## Review Focus

Design, mostly. Any structures here I'm not handling but should be?

## Screenshots
<img width="1514" height="642" alt="image"
src="https://github.com/user-attachments/assets/387fd2f6-bb4b-4fee-b273-6166a52a3552"
/>

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

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6243-Feat-Add-Badges-for-Vue-Nodes-2956d73d36508184a250d67b127ed4b1)
by [Unito](https://www.unito.io)

---------

Co-authored-by: github-actions <github-actions@github.com>
2025-10-24 11:24:27 -07:00
Hendrik Wiese
7663517f54 Fix reference to .env_example in CONTRIBUTING.md (#6255)
## Summary

Found a broken link in the CONTRIBUTING.md, thought I'd quickly fix it.

// scoutsdeed

## Changes

- **What**: link in CONTRIBUTING.md
- **Breaking**: None
- **Dependencies**: None

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6255-Fix-reference-to-env_example-in-CONTRIBUTING-md-2966d73d365081a6bef2cc974a2ead9b)
by [Unito](https://www.unito.io)
2025-10-24 09:31:38 -07:00
AustinMroz
7f5efca00b Respect minimum node size on subgraph conversion (#6241)
Two small changes to improve sizing on subgraphs
- On conversion, automatically promoted widgets can increase the minimum
width of a node. When this occurs, the node is now automatically resized
to respect this new minimum. <img width="434" height="274" alt="image"
src="https://github.com/user-attachments/assets/8b642f12-24bf-439a-a07d-b392b1f406df"
/>

- On nodes with title_badges, titles now have greatly reduced empty
padding before being abbreviated. <img width="314" height="123"
alt="image"
src="https://github.com/user-attachments/assets/4d8fd899-a159-4c0d-b309-04844b6203fc"
/>


┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6241-Respect-minimum-node-size-on-subgraph-conversion-2956d73d3650817c867fe42d60afa28b)
by [Unito](https://www.unito.io)
2025-10-24 09:23:41 -07:00
AustinMroz
a108c52572 custom_node provided blueprints (#6172)
Frontend implementation for a system to allow custom_nodes to provide a
set of subgraph blueprints.

Requires comfyanonymous/ComfyUI#10438, but handles gracefully in unavailable.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6172-Early-POC-custom_node-provided-blueprints-2926d73d3650814982ecd43f12abd873)
by [Unito](https://www.unito.io)

---------

Co-authored-by: Christian Byrne <cbyrne@comfy.org>
2025-10-24 08:24:34 -07:00
Arjan Singh
8ed9be20a9 feat(useRemoteWidget): add cloud firebase auth (#6249)
## Summary

Add Firebase authentication for `useRemoteWidget` Cloud API calls.

## Changes

- Incorporate changes from
c27edb7e94
- Add tests

## Screenshots


<img width="849" height="552" alt="Screenshot 2025-10-23 at 8 41 00 PM"
src="https://github.com/user-attachments/assets/23e07aac-22ea-4222-a90c-00335937a011"
/>

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6249-feat-useRemoteWidget-add-cloud-firebase-auth-2966d73d36508121935efc9ed07c47d2)
by [Unito](https://www.unito.io)
2025-10-23 22:54:45 -07:00
Terry Jia
393f77e27a add widget-expands on Markdown widget (#6245)
## Summary
Markdown widget also needs widget-expands class

before adding  widget-expands
<img width="545" height="503" alt="image"
src="https://github.com/user-attachments/assets/db8fbf0b-5371-4d2b-9e81-0a37b5e07086"
/>

after added widget-expands
<img width="578" height="513" alt="image"
src="https://github.com/user-attachments/assets/9aa94807-167d-4ee1-bcc3-ee23d2673dfe"
/>

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6245-add-widget-expands-on-Markdown-widget-2966d73d365081c29ae4c4c82fdf4372)
by [Unito](https://www.unito.io)
2025-10-23 22:54:26 -07:00
AustinMroz
ec11d7825d Show default node list after clearing search input (#6231)
![searchbox-clear](https://github.com/user-attachments/assets/94737766-bf96-4f03-86a0-70057b3b0e81)
Resolves #4887 

<details>
<summary>The dirty details</summary>
There's really not a cleaner workaround here. Prime vue is hardcoded to
hide results when a query is received with length 0. With our search
box, we never want completions not to be shown and the sanest, if not
the only viable solution, is to simply block the hiding completely.

Future TODO:
- Completely remove the reFocusInput jank. If we can make the search box
a frame more responsive, we should.

```ts
onInput(event) {
    if (this.typeahead) {
        if (this.searchTimeout) {
            clearTimeout(this.searchTimeout);
        }

        let query = event.target.value;

        if (!this.multiple) {
            this.updateModel(event, query);
        }

        if (query.length === 0) {
            this.hide();
            this.$emit('clear');
        } else {
            if (query.length >= this.minLength) {
                this.focusedOptionIndex = -1;

                this.searchTimeout = setTimeout(() => {
                    this.search(event, query, 'input');
                }, this.delay);
            } else {
                this.hide();
            }
        }
    }
}
hide(isFocus) {
    const _hide = () => {
        this.$emit('before-hide');
        this.dirty = isFocus;
        this.overlayVisible = false;
        this.clicked = false;
        this.focusedOptionIndex = -1;

        isFocus && focus(this.multiple ? this.$refs.focusInput : this.$refs.focusInput?.$el);
    };

    setTimeout(() => {
        _hide();
    }, 0); // For ScreenReaders
}
```
</details>

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6231-Show-default-node-list-after-clearing-search-input-2956d73d36508134960df537c7409646)
by [Unito](https://www.unito.io)
2025-10-23 22:53:58 -07:00
Christian Byrne
93518f7f54 [chore] Regenerate browser test snapshots (#6253)
## Summary
Trigger snapshot regeneration via CI.

## Test plan
- CI will regenerate snapshots when the `New Browser Test Expectations`
label is added

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6253-chore-Regenerate-browser-test-snapshots-2966d73d365081009b0cdf1a924af7e6)
by [Unito](https://www.unito.io)

---------

Co-authored-by: github-actions <github-actions@github.com>
2025-10-23 22:34:08 -07:00
Johnpaul Chiwetelu
3fe74c7b1e Hotfix CI update test expectations (#6252)
This pull request updates the workflow for managing Playwright
expectation snapshots in
`.github/workflows/pr-update-playwright-expectations.yaml`. The main
focus is to improve how changed snapshot files are handled, ensuring
directory structures are correct and only necessary files are processed
and committed. The most important changes are grouped below:

**Snapshot file handling improvements:**

* When copying changed snapshot files, the workflow now strips the
`browser_tests/` prefix to avoid double nesting in the staging
directory. This ensures the directory structure remains correct when
files are merged back.
* During the merging step, the script clarifies that files are already
in the correct structure (without the `browser_tests/` prefix), so they
can be copied directly into `browser_tests/`.

**Workflow and commit logic enhancements:**

* The snapshot artifact download step is renamed for clarity, indicating
that only changed snapshot files are downloaded from shards.
* The commit step is improved to check for actual changes in
`browser_tests/` before attempting to commit and push. It sets an output
variable (`has-changes`) to control subsequent steps, adds more
informative logging, and only pushes when there are changes.
* The "Add Done Reaction" step is now conditional on both the event type
and whether there were changes to commit, preventing unnecessary
reactions.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6252-Hotfix-CI-update-test-expectations-2966d73d36508131968ee5a7f04ff787)
by [Unito](https://www.unito.io)

---------

Co-authored-by: github-actions <github-actions@github.com>
2025-10-23 21:58:10 -07:00
Johnpaul Chiwetelu
82e777bb7e Patch Update expectations CI (#6250)
This pull request updates the Playwright snapshot update workflow to
improve efficiency and clarity. The workflow now only uploads and merges
changed snapshot files from each shard, reducing unnecessary artifact
size and processing. It also adds better logging and summaries for
easier debugging and review.

**Snapshot handling improvements:**

* Only changed snapshot files are identified, staged, and uploaded as
artifacts per shard, instead of uploading all snapshot files. This
reduces artifact size and upload time.
* During the merge step, only the changed files from each shard are
merged back into the `browser_tests` directory, preserving directory
structure and avoiding redundant operations.

**Logging and debugging enhancements:**

* Added steps to list downloaded snapshot files and summarize the
changes after merging, making it easier to see what was updated and
debug any issues.
Sample run is here
https://github.com/Myestery/ComfyUI_frontend/actions/runs/18768857441

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6250-Patch-Update-expectations-CI-2966d73d365081b790a0fad66649a10b)
by [Unito](https://www.unito.io)
2025-10-23 21:19:21 -07:00
Christian Byrne
d7a58a7a9b change cloud feature flags to be loaded dynamically at runtime rather than set in build (#6246)
## Summary

Implements server-side remote configuration to decouple runtime behavior
from build artifacts, enabling dynamic configuration updates without
redeployment.

## Technical Changes

- **Replaced** build-time constants (`__MIXPANEL_TOKEN__`,
`__BUILD_FLAGS__`) with runtime configuration loaded from
`/api/features`
- Configuration now sourced from `window.__CONFIG__` (hydrated from
`/api/features` endpoint)
- **Added** `src/platform/remoteConfig/` service that polls server
configuration every 30 seconds
- **Modified** application bootstrap sequence in `main.ts` to load
remote config before module initialization (required for cloud builds)
- **Removed** global constants: `__BUILD_FLAGS__`, `__MIXPANEL_TOKEN__`.
Runtime subscription enforcement toggle via `subscription_required` flag
- Server health alerts with variant-based severity rendering
(info/warning/error) via topbar badges

## Rationale

- **Build-once-deploy-anywhere**: Single immutable artifact promoted
through environments (staging → production)
- **Zero-downtime configuration**: Update behavior without rebuilding or
redeploying the application
- **Incident response**: Disable features or display alerts dynamically
in response to outages or degraded service
- **Instant rollback**: Revert configuration changes server-side without
artifact redeployment
- **Progressive delivery**: Enable A/B testing, canary releases, and
user/region-based configuration
- **Environment parity**: Eliminate configuration drift between staging
and production builds
- Decouples deployment cadence from configuration changes
- Enables GitOps workflows for configuration management separate from
code deployments
- Supports real-time operational control of client behavior
- Reduces build matrix complexity (no per-environment builds)

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6246-change-cloud-feature-flags-to-be-loaded-dynamically-at-runtime-rather-than-set-in-build-2966d73d3650811cbb41c9093961037a)
by [Unito](https://www.unito.io)
2025-10-23 20:16:18 -07:00
Arjan Singh
a3bfc2e91a fix(TopMenuSection): show current user if logged in (#6239)
## Summary

`CurrentUserButton` was not showing at all. Now it shows when the user
is logged in.

## Changes

- Fix template logic
- Add test for `CurrentUserButton` and `LoginButton` display logic.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6239-fix-TopMenuSection-show-current-user-if-logged-in-2956d73d3650812bb9f8fcf5a3c01db5)
by [Unito](https://www.unito.io)
2025-10-23 20:10:06 -07:00
Christian Byrne
6d37834111 [chore] fix LOD test regressed by 4-way resize drag PR (#6238)
Trigger CI to regenerate Playwright snapshots affected by the
four-corner Vue node resize feature merged in #6187.

This empty commit will trigger the automatic snapshot update workflow.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6238-chore-update-Playwright-snapshots-2956d73d36508159b1f7e6e0e4d16d9f)
by [Unito](https://www.unito.io)
2025-10-23 17:53:52 -07:00
AustinMroz
671e33cf09 Fix asset browser on subgraph nodes (#6240)
When a widget is linked to a subgraph, the subgraph creates a copy of
the widget. The callback used by the asset browser to update the widget
still refers to the widget that lives inside the subgraph, but at time
of execution, this is overwritten by the unchanged value of the copy.

This is fixed by instead updating the value of the caller. It's a little
hacky, and may need future review.

See also #6237

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6240-Fix-asset-browser-on-subgraph-nodes-2956d73d365081b49bd1cd1a7a254763)
by [Unito](https://www.unito.io)
2025-10-23 15:28:08 -07:00
Christian Byrne
68f98e2624 style: change gaps between floating elements from 2 (8px) to 1 (4px) (#6226)
## Summary

Design request

**Before**

<img width="2643" height="2074" alt="Screenshot from 2025-10-23
11-16-26"
src="https://github.com/user-attachments/assets/1c93879e-bfc4-4054-b0dd-ee0cd296a7ca"
/>

**After**

<img width="2643" height="2074" alt="Screenshot from 2025-10-23
11-16-13"
src="https://github.com/user-attachments/assets/b9327dcc-9b97-424a-8b05-4f5d62beeefa"
/>

## Related

- https://github.com/Comfy-Org/ComfyUI_frontend/pull/6120

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6226-style-change-gaps-between-floating-elements-from-2-8px-to-1-4px-2956d73d3650815b8640c5f3891fe494)
by [Unito](https://www.unito.io)

---------

Co-authored-by: github-actions <github-actions@github.com>
2025-10-23 14:01:21 -07:00
Christian Byrne
8120ed9dfa load assets browser before fetch completes and show loading state (#6189)
## Summary

Moves the fetch and post-fetch logic associated with the asset browser
into the component and shows a loading state while fetching.

To test, use this branch:
https://github.com/comfyanonymous/ComfyUI/pull/10045



https://github.com/user-attachments/assets/718974d5-efc7-46a0-bcd6-e82596d4c389

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6189-load-assets-browser-before-fetch-completes-and-show-loading-state-2946d73d365081879d1bd05d86e8c036)
by [Unito](https://www.unito.io)

---------

Co-authored-by: GitHub Action <action@github.com>
2025-10-23 13:43:34 -07:00
Christian Byrne
89ff8255bd open markdown links in new window/tab (#6229)
## Summary

Changes links in markdown snippets (What's New popup, node info sidebar)
to open the link in a new tab/window rather than directly navigating and
potentially losing unsaved work.



https://github.com/user-attachments/assets/24331bba-e31a-484c-bc11-12cf61805c98



Fixes https://github.com/Comfy-Org/ComfyUI_frontend/issues/6223.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6229-open-markdown-links-in-new-window-tab-2956d73d365081edbb1efb21cd0e2ab2)
by [Unito](https://www.unito.io)

---------

Co-authored-by: Alexander Brown <drjkl@comfy.org>
Co-authored-by: GitHub Action <action@github.com>
2025-10-23 13:33:38 -07:00
Christian Byrne
f14a6beda5 allow Vue nodes to be resized from all 4 corners (#6187)
## Summary

Enables Vue nodes to resize from all four corners and consolidated the
interaction pipeline.

## Changes

- **What**: Added four-corner handles to `LGraphNode`, wired them
through the refactored `useNodeResize` composable, and centralized the
math/preset helpers under `interactions/resize/` with cleaner pure
functions and lint-compliant markup.

## Review Focus

Corner-to-corner resizing accuracy (position + size), pinned-node guard
preventing resize start, and snap-to-grid behavior at varied zoom
levels.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6187-allow-Vue-nodes-to-be-resized-from-all-4-corners-2936d73d365081c8bf14e944ab24c27f)
by [Unito](https://www.unito.io)

---------

Co-authored-by: DrJKL <DrJKL@users.noreply.github.com>
Co-authored-by: GitHub Action <action@github.com>
2025-10-23 13:24:28 -07:00
Christian Byrne
aeabc24bf2 make support URL dynamic based on distribution (#6205)
## Summary

Add query param to indicate whether support ticket is from cloud or OSS.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6205-make-support-URL-dynamic-based-on-distribution-2946d73d365081868093c52981021189)
by [Unito](https://www.unito.io)

---------

Co-authored-by: DrJKL <DrJKL@users.noreply.github.com>
Co-authored-by: GitHub Action <action@github.com>
2025-10-23 13:20:48 -07:00
Christian Byrne
56a6ad5660 fix: node hover previews overlapping with sidebar (#6232)
Fixes https://github.com/Comfy-Org/ComfyUI_frontend/issues/6130 -- node
previews are not positioned in correct location after
https://github.com/Comfy-Org/ComfyUI_frontend/pull/5980

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6232-fix-node-hover-previews-overlapping-with-sidebar-2956d73d365081b2a470f7fb399fda99)
by [Unito](https://www.unito.io)

---------

Co-authored-by: GitHub Action <action@github.com>
2025-10-23 13:11:08 -07:00
Christian Byrne
4e5eba6c54 refactor: centralize all download utils across app and apply special cloud-specific behavior (#6188)
## Summary

Centralized all download functionalities across app. Then changed
downloadFile on the cloud distribution to stream assets via blob fetches
while desktop/local retains direct anchor downloads. This fixes issue
where trying to download cross-origin resources opens them in the
window, potentially losing the user's unsaved changes.

## Changes

- **What**: Moved `downloadBlob` into `downloadUtil`, routed all callers
(3D exporter, recording manager, node template export, workflow/palette
export, Litegraph save, ~~`useDownload` consumers~~) through shared
helpers, and changed `downloadFile` to `fetch` first when `isCloud` so
cross-origin URLs download reliably
- `useDownload` is the exception since we simply cannot do model
downloads through blob (forcing user to transfer the entire model data
twice is bad). Fortunately on cloud, the user doesn't need to download
models locally anyway.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6188-refactor-centralize-all-download-utils-across-app-and-apply-special-cloud-specific-behav-2946d73d365081de9f27f0994950511d)
by [Unito](https://www.unito.io)
2025-10-23 12:08:30 -07:00
Christian Byrne
647e62d4b7 [ci] run stylelint as part of CI tests (#6143)
## Summary

Final PR continuing from

- https://github.com/Comfy-Org/ComfyUI_frontend/pull/5926
- https://github.com/Comfy-Org/ComfyUI_frontend/pull/5940

actually run stylelint in lint workflows.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6143-ci-run-stylelint-as-part-of-CI-and-pre-commit-hook-2916d73d3650811891bcc9f94379cc5f)
by [Unito](https://www.unito.io)

---------

Co-authored-by: DrJKL <DrJKL@users.noreply.github.com>
2025-10-23 12:06:52 -07:00
Arjan Singh
4555656bf2 feat(AssetCard): remove model size (#6227)
## Summary

Remove model file sizes.

It was confusing some users who thought that was they were being asked
to download them locally.

## Changes

- Remove `formattedSize` from `AssetDisplayItem`.
- Remove associated code.
- 

## Screenshots

<img width="1299" height="512" alt="Screenshot 2025-10-23 at 11 22
06 AM"
src="https://github.com/user-attachments/assets/625b588b-a605-49dd-97a2-16bcd604aef2"
/>

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

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6227-feat-AssetCard-remove-model-size-2956d73d36508155930fdb4d2db2522a)
by [Unito](https://www.unito.io)
2025-10-23 18:48:30 +00:00
Alexander Brown
1e85902242 fix: Remove old readonly and memos from LGraphNodePreview, default to empty string for widget contents. (#6217)
## Summary

Fixes the prop validation errors when the previews rendered.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6217-fix-Remove-old-readonly-and-memos-from-LGraphNodePreview-default-to-empty-string-for-wi-2956d73d3650812da482cf1fab400dfa)
by [Unito](https://www.unito.io)

---------

Co-authored-by: github-actions <github-actions@github.com>
2025-10-23 11:22:36 -07:00
ComfyUI Wiki
c436552ade Remove unnecessary Template Library translations. (#6222)
All the template translations are now maintained in the
[template](https://github.com/Comfy-Org/workflow_templates) repo, so
these translations in the FE repo are no longer needed.

<img width="696" height="762" alt="image"
src="https://github.com/user-attachments/assets/e27148c0-737d-4b8c-8513-50504e6f929c"
/>

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6222-Remove-unnecessary-Template-Library-translations-2956d73d36508194aa08fa968d8d627f)
by [Unito](https://www.unito.io)
2025-10-23 08:06:25 -07:00
AustinMroz
449a51b82d Move toasts downwards so they don't overlap run button (#6216)
<img width="431" height="127" alt="image"
src="https://github.com/user-attachments/assets/76fece3a-c001-40c7-9036-e12900869e1d"
/>

Could potentially be improved to not use magic constants.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6216-Move-toasts-downwards-so-they-don-t-overlap-run-button-2956d73d365081d0b361fa396962c806)
by [Unito](https://www.unito.io)

---------

Co-authored-by: github-actions <github-actions@github.com>
2025-10-23 07:51:39 -07:00
Jin Yi
bd3f02065a [Style] Add custom scrollbar styling for SelectBox components (#5879) 2025-10-23 23:10:07 +09:00
Johnpaul Chiwetelu
fb66637765 Don't remove labels if initial jobs were skipped (#6218)
This pull request makes a small change to the
`.github/workflows/pr-update-playwright-expectations.yaml` file,
removing the unconditional `if: always()` condition from the
`merge-and-commit` job. This means the job will now only run if its
dependencies succeed, rather than always running regardless of previous
job outcomes.
2025-10-23 06:43:07 +01:00
Johnpaul Chiwetelu
97f7c2149a Shard Update Test Expectations PR (#6100)
This pull request significantly refactors the Playwright expectations
update workflow to improve reliability, efficiency, and maintainability.
The workflow is now split into three coordinated jobs—setup, sharded
snapshot updates, and merge/commit—enabling parallel test execution and
artifact management. Key improvements include sharding Playwright
snapshot updates, robust caching and artifact handling, and more
reliable PR context handling.

**Workflow Restructuring and Sharding:**

* The workflow is split into three jobs: `setup` (prepares environment
and caches it), `update-snapshots-sharded` (runs Playwright snapshot
updates in four parallel shards), and `merge-and-commit` (merges results
and commits updates). This enables faster, more reliable snapshot
updates.
[[1]](diffhunk://#diff-0289f4b5962314fa2d58937651c3d2a0f2c6f76e26c95d6a04d43c18b3449917L15-R15)
[[2]](diffhunk://#diff-0289f4b5962314fa2d58937651c3d2a0f2c6f76e26c95d6a04d43c18b3449917R27-R175)

**Caching and Artifact Management:**

* The setup job builds and caches the entire workspace, which is then
restored by each shard for consistent environments. Each shard uploads
its updated snapshots and test reports as artifacts, which are later
downloaded and merged in the final job.

**Improved PR Context Handling:**

* PR number, branch, and comment IDs are now reliably extracted and
passed between jobs using outputs, ensuring correct association with the
PR throughout the workflow (e.g., for commenting, reactions, and pushing
updates).
[[1]](diffhunk://#diff-0289f4b5962314fa2d58937651c3d2a0f2c6f76e26c95d6a04d43c18b3449917R27-R175)
[[2]](diffhunk://#diff-0289f4b5962314fa2d58937651c3d2a0f2c6f76e26c95d6a04d43c18b3449917L92-R199)

**Job and Step Renaming/Cleanup:**

* The main job is renamed from `test` to `setup`, and redundant or
unnecessary steps (such as the old branch SHA extraction) are removed
for clarity and maintainability.
[[1]](diffhunk://#diff-0289f4b5962314fa2d58937651c3d2a0f2c6f76e26c95d6a04d43c18b3449917L15-R15)
[[2]](diffhunk://#diff-0289f4b5962314fa2d58937651c3d2a0f2c6f76e26c95d6a04d43c18b3449917R27-R175)

**Comment and Label Automation Improvements:**

* Automated GitHub comment reactions and label removals now use the
correct PR context, ensuring that feedback and status updates are
reliably posted to the right place.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6100-Shard-Update-Test-Expectations-PR-28f6d73d36508109bcd8d382c942d44d)
by [Unito](https://www.unito.io)

---------

Co-authored-by: sno <snomiao@gmail.com>
2025-10-23 06:22:44 +01:00
AustinMroz
f63d0f3289 Fix type on LoadClip being marked as asset (#6207)
Previously, asset conversion was performed on any combo widget on a
valid node. As the `type` widget on LoadClip was also a combo widget, it
was being incorrectly converted.

This PR changes the isAssetBrowserEligible check to also verify the
widget name is correct.
<img width="694" height="174" alt="image"
src="https://github.com/user-attachments/assets/a8523ade-7f59-4480-b5e6-8782fd680106"
/>


┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6207-Fix-type-on-LoadClip-being-marked-as-asset-2946d73d3650811690f8d3a221a5b76d)
by [Unito](https://www.unito.io)
2025-10-22 19:37:40 -07:00
Simula_r
e5a0466e40 feat: make login button show only on dekstop (#6213)
## Summary

Make login and current user button visible on desktop only. 

## Screenshots (if applicable)

<img width="519" height="162" alt="image"
src="https://github.com/user-attachments/assets/80d86145-2bb9-4396-a02d-dc577192cb17"
/>
<img width="482" height="130" alt="image"
src="https://github.com/user-attachments/assets/cd843611-ce23-4cd4-a864-78cac1c7101f"
/>

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6213-feat-make-login-button-show-only-on-dekstop-2956d73d365081eb93f1d5da92dc38ee)
by [Unito](https://www.unito.io)

---------

Co-authored-by: github-actions <github-actions@github.com>
2025-10-23 02:16:15 +00:00
Arjan Singh
c71ec35a5f refactor(vite.config): simplify cloud config (#6211)
## Summary

Simplify the `vite.config.mts` now that we know `addAuthHeaders` is not
necessary.

## Changes

- remove `addAuthHeaders`
- simplify cloud configuration objects accordingly.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6211-refactor-vite-config-simplify-cloud-config-2956d73d365081659cfade4e289a872f)
by [Unito](https://www.unito.io)
2025-10-22 18:48:12 -07:00
AustinMroz
7b213f135e Fix copying from multi-outputs with clipspace (#6210)
`copyToClipspace` sets the `paintedIndex` and `combinedIndex` regardless
of if any editing was actually performed on the image. Previously, they
were set to be simple increments above the currently selected image.

When attempting to copy from a node previewing multiple images, these
indexes may refer to images that already exist.
This is resolved by instead setting the indexes to be used for
paintedIndex and combinedIndex to always be beyond the range of current
images.
<img width="998" height="761" alt="image"
src="https://github.com/user-attachments/assets/dacd46b3-9029-45e3-a6b3-07e971125a14"
/>


Resolves #6202

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6210-Fix-copying-from-multi-outputs-with-clipspace-2956d73d365081c5a07be4ddb0b352a3)
by [Unito](https://www.unito.io)
2025-10-22 18:00:02 -07:00
Arjan Singh
8b7b580ed4 Cloud Auth Backport (#6195)
## Summary

Backports Firebase authentication with cloud environments.

Changes only work when developing for cloud environment locally.

## Changes

- Router guards to force unauthenticated users to sign in.
- Configure auth headers for REST and Websocket connections.
- Code implemented in a way that enables build tree-shaking based on
distribution
- Updates to build process to build cloud distribution and simplify
development workflow

## Review Focus

1. Idomatic Vue/codebase patterns.
2. Build logic (please double check that I integrated correctly with:
https://github.com/Comfy-Org/ComfyUI_frontend/blob/rh-test/vite.config.mts)

## Screenshots (if applicable)




https://github.com/user-attachments/assets/ee4ea3f7-afa6-4da0-ba43-d62ed8ba4e18





┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6195-Feat-cloud-auth-backport-2946d73d365081f395f5f2a89fb7d800)
by [Unito](https://www.unito.io)

---------

Co-authored-by: Alexander Brown <drjkl@comfy.org>
Co-authored-by: GitHub Action <action@github.com>
2025-10-23 00:06:37 +00:00
Comfy Org PR Bot
bfe53d7721 1.30.2 (#6171)
Patch version increment to 1.30.2

**Base branch:** `main`

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6171-1-30-2-2926d73d36508194b993d488cf43de6d)
by [Unito](https://www.unito.io)

---------

Co-authored-by: simula-r <18093452+simula-r@users.noreply.github.com>
Co-authored-by: github-actions <github-actions@github.com>
2025-10-22 15:42:41 -07:00
AustinMroz
6368647cde Fix empty padding on nodes with previews (#6208)
Followup to #6194. Fixes nodes with image previews having empty padding.
<img width="915" height="565" alt="image"
src="https://github.com/user-attachments/assets/852a5e95-10d5-4fde-a5f1-f2f72ae5ffb6"
/>

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6208-Fix-empty-padding-on-nodes-with-previews-2946d73d365081eca299e0cfae420be3)
by [Unito](https://www.unito.io)

---------

Co-authored-by: github-actions <github-actions@github.com>
2025-10-22 15:15:03 -07:00
sno
69d37e8949 feat: Add @prettier/plugin-oxc for faster formatting (#6088)
Note for reviewers: the code changes in src/* is because I've upgraded
prettier to latest.

the @prettier/plugin-oxc it self only improve performance and doesnt
affect format rules

## Summary

Integrates `@prettier/plugin-oxc` to improve Prettier performance by
~20%.

The oxc plugin provides a faster parser written in Rust, significantly
speeding up formatting operations across the codebase.

## Changes

- Added `@prettier/plugin-oxc` as dev dependency
- Updated `.prettierrc` to use oxc plugin alongside existing
sort-imports plugin
- Added `scripts/benchmark-prettier.js` to measure performance
improvements
- Updated `knip.config.ts` to ignore the oxc plugin
- Updated `eslint.config.ts` to ignore the benchmark script

## Benchmark Results

Ran 3 benchmarks comparing formatting performance on the entire
codebase:

**Without oxc:**
- Median: 32.76s
- Average: 32.89s
- Min: 32.49s
- Max: 33.43s

**With oxc:**
- Median: 26.13s
- Average: 26.35s
- Min: 25.24s
- Max: 27.69s

**Improvement: 20.26% faster (6.64s saved)**

## Testing

The benchmark script can be run with:
```bash
node scripts/benchmark-prettier.js
```

This will:
1. Test formatting performance without oxc plugin
2. Test formatting performance with oxc plugin
3. Display comparison results
4. Restore original configuration

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

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6088-feat-Add-prettier-plugin-oxc-for-faster-formatting-28e6d73d365081aabb24d3af98c11bb0)
by [Unito](https://www.unito.io)

---------

Co-authored-by: DrJKL <DrJKL0424@gmail.com>
Co-authored-by: GitHub Action <action@github.com>
2025-10-22 14:49:47 -07:00
Christian Byrne
dc5d41642d disable transform settling reflow when panning the graph (#6186)
## Summary

- disable pan tracking in `useTransformSettling` so we stop wiring
high-frequency pointer listeners during canvas drags
- the post-navigation-interaction forced reflow is only necessary when
zooming since it is for fixing pixel stretch that results from `scale`
(which doesn't happen during panning/`translate`)
- extend settle delay to 512ms to reduce unnecessary reflow while
preserving post-zoom pixel fix

After this PR, there should be 0 reflows when panning the graph.

First PR in series to address:

- https://github.com/Comfy-Org/ComfyUI_frontend/issues/6151

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6186-disable-transform-settling-reflow-when-panning-the-graph-2936d73d365081c2b357e3c72d711439)
by [Unito](https://www.unito.io)

---------

Co-authored-by: DrJKL <DrJKL@users.noreply.github.com>
2025-10-22 14:33:05 -07:00
AustinMroz
2f00893b27 Expand drop zone for docking run button (#6193)
Expand the drop zone for docking the run button to be a bit more
forgiving.
<img width="494" height="99" alt="image"
src="https://github.com/user-attachments/assets/97eb6948-211d-4ed6-b06c-d6fb57b45f0b"
/>

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6193-Expand-drop-zone-for-docking-run-button-2946d73d3650816d996fdc57022161db)
by [Unito](https://www.unito.io)
2025-10-22 14:05:53 -07:00
AustinMroz
a5c29a9826 Introduce grow-parent class for widgets (#6194)
Only some widgets actually want to grow. Flex makes this difficult. This
PR sets up a `widget-expands` class that widgets can use to indicate
that they want to dynamically grow and applies it to the textarea widget
<img width="709" height="860" alt="image"
src="https://github.com/user-attachments/assets/721d99e5-5939-4531-91b5-1cda69d4e8ed"
/>


There's a potential alternative avenue with using `flex-shrink` instead
of `flex-grow`, and using `min-content` for `calculateIntrinsicSize`,
but I've been poking around with that for a while with no success.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6194-Introduce-grow-parent-class-for-widgets-2946d73d3650812f9d03c305ab04e212)
by [Unito](https://www.unito.io)

---------

Co-authored-by: github-actions <github-actions@github.com>
2025-10-22 19:17:36 +00:00
Christian Byrne
b44a39569e style: remove pulsing animation on executing Vue nodes (#6206)
## Summary

Removes pulsing animation which wasn't part of the original design and
has performance overhead.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6206-style-remove-pulsing-animation-on-executing-Vue-nodes-2946d73d3650816ab877da8120ab4085)
by [Unito](https://www.unito.io)
2025-10-22 11:45:58 -07:00
sno
8e8a45c496 [fix] Update .gitignore to properly ignore Linux core dumps (#6201)
## Summary
Fixes the .gitignore pattern for Linux core dump files from `./core` to
`/core`.

## Problem
The pattern `./core` in .gitignore doesn't work as expected. Git
interprets the `./` prefix literally, looking for a path named `./core`
rather than matching `core` at the repository root.

## Solution
Change to `/core` which is the correct gitignore syntax to ignore
files/directories named `core` at the repository root only.

## Why This Matters
- Linux systems can generate core dump files named `core` when programs
crash
- These files shouldn't be tracked in version control
- The previous pattern wasn't actually ignoring these files

## Testing
The new pattern will properly ignore `core` files at the root while not
affecting subdirectories (e.g., `src/core/` would still be tracked).

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6201-fix-Update-gitignore-to-properly-ignore-Linux-core-dumps-2946d73d365081059e57d9919d03a501)
by [Unito](https://www.unito.io)
2025-10-22 18:40:14 +09:00
sno
187f59eed3 [fix] Remove pnpm cache from release-version-bump workflow (#6199)
## Summary
- Fixed the "Post Setup Node.js" failure in the release-version-bump
workflow
- Removed unnecessary pnpm cache configuration that was causing
validation errors

fixes this JOB
- [Release: Version Bump · Comfy-Org/ComfyUI_frontend@2e8e136](
https://github.com/Comfy-Org/ComfyUI_frontend/actions/runs/18695441150/job/53311521564
)
<img width="1361" height="229" alt="image"
src="https://github.com/user-attachments/assets/22f780f0-59b8-4e57-ad9b-540683289a10"
/>


## Problem
The workflow was failing with error: "Path(s) specified in the action
for caching do(es) not exist, hence no cache is being saved."

This occurred because `setup-node@v4` with `cache: 'pnpm'` expects the
pnpm store directory to exist, but the workflow never runs `pnpm
install`. The workflow only executes `pnpm version`, which doesn't
require dependencies to be installed.

## Solution
Removed the `cache: 'pnpm'` configuration from the Setup Node.js step
since:
1. The workflow doesn't install dependencies
2. The cache provides no benefit for this workflow
3. It was causing the post-setup cleanup step to fail

## Test Plan
- [ ] Verify workflow runs successfully without cache errors
- [ ] Confirm version bump functionality still works correctly

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

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6199-fix-Remove-pnpm-cache-from-release-version-bump-workflow-2946d73d3650813dae7cf987a800e28b)
by [Unito](https://www.unito.io)

Co-authored-by: Claude <noreply@anthropic.com>
2025-10-21 23:42:56 -07:00
Alexander Brown
9cd7d06a6d Fix: Make textarea fill the area available instead of being resizable. (#6190)
## Summary

Invert the sizing of textareas. They now grow based on the container
instead of being independently resizable.

## Review Focus

Tested the behavior in Note, Markdown Note, CLIP Text Encode, and
Subgrpahs with promoted mutliline text widgets.

Anything else that might break with this?

## Screenshots (if applicable)


https://github.com/user-attachments/assets/4e2da142-d0b7-4629-9814-b637566ac1d6


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

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6190-Fix-Make-textarea-fill-the-area-available-instead-of-being-resizable-2946d73d3650818a9f77c619deb93d0b)
by [Unito](https://www.unito.io)

---------

Co-authored-by: github-actions <github-actions@github.com>
2025-10-21 19:31:05 -07:00
Alexander Brown
4ad8ae2634 Fix: Make breadcrumbs non-draggable. (#6191)
## Summary

Prevents accidentally trying to load the current site as if it were a
workflow.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6191-Fix-Make-breadcrumbs-non-draggable-2946d73d365081bda833c7a413903e97)
by [Unito](https://www.unito.io)
2025-10-21 18:07:48 -07:00
Benjamin Lu
e4b52e5329 fix: surface knip hook output in vscode (#6183)
## Summary
- redirect pnpm knip output to stderr so VS Code surfaces failures

## Testing
<img width="1585" height="483" alt="image"
src="https://github.com/user-attachments/assets/d65af783-d168-45cf-b01e-2b727e429a91"
/>

before it would literally just say it failed and you'd have to manually
rerun pnpm knip in ur own terminal

------
https://chatgpt.com/codex/tasks/task_e_68f7d2f2f2548330a23ae74554f1a54a
2025-10-21 17:14:50 -07:00
Christian Byrne
2346ba1af0 fix Vue node number widget inc/dec buttons hover state style (#6121)
## Summary

Fix hover state on Vue node number widget incremenet/decrement buttons.
The problem was in `useNumberWidgetButtonPt.ts`, the button hover styles
were using `var(--color-node-component-surface-hovered)` which
references a Tailwind theme color created by the `@theme` inline
directive. This theme color doesn't properly inherit the `.dark-theme`
class overrides, so it was showing the light mode color (white) even in
dark mode.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6121-fix-Vue-node-number-widget-inc-dec-buttons-hover-state-style-2906d73d36508144b91aec3490e32d28)
by [Unito](https://www.unito.io)

---------

Co-authored-by: DrJKL <DrJKL0424@gmail.com>
2025-10-21 23:58:04 +00:00
Christian Byrne
45cefda6e1 [style] unify Vue widget/slot label colors (#6149)
## Summary

Change labels on all widgets and slots to the same value which matches
design spec.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6149-style-unify-Vue-widget-slot-label-colors-2916d73d3650810a98f3ee75e0b22da0)
by [Unito](https://www.unito.io)

---------

Co-authored-by: DrJKL <DrJKL0424@gmail.com>
2025-10-21 16:50:36 -07:00
AustinMroz
cc73c42f76 Fix circular dependency in setting registration (#6184)
`Comfy.Canvas.NavigationMode` and `Comfy.Canvas.LeftMouseClickBehavior`
introduce a circular dependency where setting the value of one will set
the value of the other.

This is solved by having `NavigationMode` skip changing other settings
when `oldValue` is undefined.
- Note that `oldValue` is only undefined during initial load. When a
user changes the value for the first time, oldValue will be the default
value.

In the unlikely event desync occurs (a user manually editing the backing
json?), the registration of the subsequent `LeftMouseClickBehavior` will
still correct `NavigationMode` back to custom

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6184-Fix-circular-dependency-in-setting-registration-2936d73d365081809aa5d8bff0bf2333)
by [Unito](https://www.unito.io)
2025-10-21 14:08:36 -07:00
AustinMroz
f8490d0939 Fix links to wrong slots in vue mode (#6181)
Error is divided into 2 parts
- A widget lacking slotMetadata would create a slot overwriting the
index 0 slot
- Slot data wasn't reactive. Any dynamic widgets would not have
slotMetadata
<img width="1065" height="436" alt="image"
src="https://github.com/user-attachments/assets/c83b04fb-3b3a-4abb-8b68-99b305336348"
/>

See #5705
- Describes incorrect links internal to subgraphs. Likely a different,
already solved issue

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6181-Fix-links-to-wrong-slots-in-vue-mode-2936d73d36508136ad43d8c818bf9fba)
by [Unito](https://www.unito.io)
2025-10-21 12:36:08 -07:00
filtered
2e8e1366bd Release desktop-ui v0.0.3 (#6182)
## What's Changed

### 🐛 Bug Fixes
- Remove broken installer terminal button (#6180)

### 🔧 Maintenance
- Remove redundant npm pack step from desktop-ui publish workflow
(#6176)
2025-10-21 11:41:01 -07:00
filtered
6ef5c2602f Remove broken installer terminal button (#6180)
## Summary

Removes broken terminal toggle button that causes UX issues during
desktop installation.

## Changes

- **What**: Removes "Show Terminal" button from ServerStartView during
install flow
- **Breaking**: None (removes broken functionality)

## Review Focus

Cherry-picked from feb3c078f (v1.27.9). The button causes broken UX when
clicked during install. The working "Show Logs" button remains and takes
users directly to the log directory. This patch was written directly for
the release, in a rush to fix issues. The fix was not PR'd into main as
well (due to timing), so the original issue has now resurfaced.
2025-10-21 11:23:21 -07:00
filtered
9cf3a318eb Remove redundant npm pack step from desktop-ui publish workflow (#6176)
## Summary

Removes duplicate tarball creation from desktop-ui publish workflow -
`pnpm publish` handles this internally.

## Changes

- **What**: Removes `npm pack` step and GitHub Actions artifact upload
- **Breaking**: None - workflow behavior unchanged, publish still works
identically

## Review Focus

The `npm pack` + artifact upload was creating a duplicate of what `pnpm
publish` generates and uploads to npm anyway. Verified
`publish-frontend-types.yaml` follows this same pattern (no pack step,
direct publish).
2025-10-21 10:39:18 -07:00
filtered
668e95501a Prepare desktop-ui 0.0.2 release (#6179)
## Summary

Bumps desktop-ui version for release.

## Changes

- **What**: Version bump from 0.0.1 to 0.0.2
2025-10-21 10:37:28 -07:00
filtered
d8860c87e8 Remove 'Desktop' suffix from desktop app title (#6177)
## Summary

Removes "Desktop" suffix from the desktop app window title.

## Changes

- **What**: Changes window title from "ComfyUI Desktop" to "ComfyUI"

## Review Focus

Fixes title regression introduced during desktop UI separation.
2025-10-21 10:34:20 -07:00
filtered
6f8789b9aa Fix asset path resolution in desktop GPU picker (#6178)
## Summary

Fixes regression where desktop UI GPU picker images failed to load due
to incorrect absolute path resolution.

## Changes

- **What**: Converts absolute image paths to relative paths with `./`
prefix in GpuPicker component
- **Breaking**: None

## Review Focus

ESLint rule incorrectly flagged relative paths as errors, leading to use
of absolute paths that don't resolve correctly in desktop app context.

The change is just adding `.` to the start of two lines. ESLint rules
reorganised the rest.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6178-Fix-asset-path-resolution-in-desktop-GPU-picker-2936d73d3650814e9d0df9faf8e28733)
by [Unito](https://www.unito.io)
2025-10-21 10:30:52 -07:00
AustinMroz
9ae66c778d Fix nodeDef resolution for virtual nodes. (#6175)
<img width="697" height="250" alt="image"
src="https://github.com/user-attachments/assets/71fe7d9b-0cd6-43c6-b0d5-7dcb64d385a6"
/>

Virtual nodes (like primitives) don't have a nodeData. As a result, the
existing call to attempt lookup from a node instance fails. This is
fixed by adding `node.type` as a fallback

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6175-Fix-nodeDef-resolution-for-virtual-nodes-2936d73d365081b0abfcfe8532a50f8e)
by [Unito](https://www.unito.io)
2025-10-20 20:22:30 -07:00
sno
80013bcd5c [bugfix] Fix i18n linting errors (#6170)
## Summary
- Fix i18n linting errors by adding missing locale keys to
`src/locales/en/main.json`
- Update all affected components to use `$t()` for internationalization

## Changes
Added the following locale keys:
- `comfyOrgLogoAlt`: "ComfyOrg Logo"
- `comfy`: "Comfy"
- `pressKeysForNewBinding`: "Press keys for new binding"
- `defaultBanner`: "default banner"
- `enableOrDisablePack`: "Enable or disable pack"
- `openManager`: "Open Manager"
- `graphNavigation`: "Graph navigation"

Updated components to use i18n keys:
- `ComfyOrgHeader.vue`
- `KeybindingPanel.vue`
- `PackBanner.vue`
- `PackIcon.vue`
- `PackEnableToggle.vue`
- `LoadWorkflowWarning.vue`
- `SubgraphBreadcrumb.vue`
- `SignInContent.vue`

## Test plan
- [x] Run `pnpm lint` - all i18n linting errors resolved
- [x] Pre-commit hooks pass

Aim to make #5625 CI/CD pass.

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

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6170-bugfix-Fix-i18n-linting-errors-2926d73d365081c3b7fbcbbf4a8e03d6)
by [Unito](https://www.unito.io)

Co-authored-by: Claude <noreply@anthropic.com>
2025-10-20 13:11:22 -07:00
Copilot
aa943ac565 CI: Remove .cache caching from GitHub Actions workflows (#6097)
## Overview

Removes **all `actions/cache` steps** from GitHub Actions workflows
after empirical testing showed that they actually **slow down CI/CD by
11%** rather than speeding it up.

## Context

As discussed in #5988, the codebase has evolved with components moving
into the `/packages` directory structure. The review comment suggested
removing the entire `actions/cache` step rather than just the `.cache`
path to properly evaluate performance impact.

## Performance Benchmark Results

Empirical testing on this PR (commits 38695ae0b vs ab16635c5) revealed
that **removing cache steps improves CI performance across all
workflows**:

| Workflow | WITHOUT Cache | WITH Cache | Improvement |
|----------|---------------|------------|-------------|
| **CI: Lint Format** | 208s (3m 28s) | 226s (3m 46s) | **-18s (-8.7%)**
 |
| **CI: Tests Unit** | 160s (2m 40s) | 177s (2m 57s) | **-17s (-10.6%)**
 |
| **CI: Tests Storybook** | 65s (1m 5s) | 78s (1m 18s) | **-13s
(-20.0%)**  |
| **Total Pipeline** | **433s (7m 13s)** | **481s (8m 1s)** | **-48s
(-11.1%)**  |

### Why is caching slower?

1. **Cache overhead exceeds benefits**: Time spent saving/restoring
cache > time saved from cached content
2. **Complex cache key computation**: Hash calculations for file
patterns add processing time
3. **Network I/O cost**: Each cache step adds network round-trips
4. **Tools already optimize incrementally**: ESLint, Vitest, Prettier
handle their own incremental checks efficiently

## Changes

Removed the entire `actions/cache` step from 8 workflow files:

- `ci-lint-format.yaml` - Removed tool outputs cache (.eslintcache,
.prettierCache, .knip-cache, tsconfig.tsbuildinfo)
- `ci-tests-storybook.yaml` - Removed storybook-static and
tsconfig.tsbuildinfo cache (both jobs)
- `ci-tests-unit.yaml` - Removed coverage and .vitest-cache
- `api-update-electron-api-types.yaml` - Removed tsconfig.tsbuildinfo
cache
- `api-update-manager-api-types.yaml` - Removed tool cache and
ComfyUI-Manager repo cache
- `api-update-registry-api-types.yaml` - Removed tool cache and
comfy-api repo cache
- `release-draft-create.yaml` - Removed tsconfig.tsbuildinfo cache
- `release-pypi-dev.yaml` - Removed dist and tsconfig.tsbuildinfo cache

**What remains cached:**
-  pnpm packages via `cache: 'pnpm'` in setup-node actions (the most
valuable cache)
-  Tool-specific incremental caches generated fresh each run
-  Docker layer caching (where applicable)

## Testing

-  Empirical performance testing completed (see benchmark results
above)
-  All cache steps removed successfully
-  No structural changes to workflow logic
-  pnpm package caching remains active

## Conclusion

The benchmark data clearly shows that removing `actions/cache` steps
results in **faster, simpler CI workflows**. The overhead of cache
management exceeds any benefit, especially with pnpm package caching
already handling the most time-consuming dependency installations.

**Recommendation:  Proceed with this change**

## Test Methodology

1. **WITHOUT cache** (commit
[38695ae0b](https://github.com/Comfy-Org/ComfyUI_frontend/commit/38695ae0b)):
Removed all `actions/cache` steps → [Workflow
run](https://github.com/Comfy-Org/ComfyUI_frontend/actions/runs/18654024806)
2. **WITH cache** (commit
[ab16635c5](https://github.com/Comfy-Org/ComfyUI_frontend/commit/ab16635c5)):
Temporarily restored all `actions/cache` steps → [Workflow
run](https://github.com/Comfy-Org/ComfyUI_frontend/actions/runs/18654143363)
3. **Final state** (commit
[3ce876f87](https://github.com/Comfy-Org/ComfyUI_frontend/commit/3ce876f87)):
Restored no-cache version (current)

Fixes #5988

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: snomiao <7323030+snomiao@users.noreply.github.com>
Co-authored-by: snomiao <snomiao@gmail.com>
Co-authored-by: Claude <noreply@anthropic.com>
2025-10-20 11:15:56 -07:00
AustinMroz
8eac19d06e Support cross domain/application copy/paste (#6087)
![AnimateDiff_00001](https://github.com/user-attachments/assets/8ae88dc5-bba8-40c0-9cc2-5e81f579761d)


Browsers place very heavy restrictions on what can be copied and pasted.
See:
- https://alexharri.com/blog/clipboard
- https://www.w3.org/TR/clipboard-apis/#mandatory-data-types-x

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6087-Experimental-cross-domain-application-copy-paste-28e6d73d36508154a0a8deeb392f43a4)
by [Unito](https://www.unito.io)
2025-10-20 10:03:15 -07:00
AustinMroz
55d2b300a6 Fix link resolution of virtual nodes (#6135)
Some virtual nodes (like get/set nodes) perform link redirection at
prompt resolution. The prior implementation incorrectly tried to return
the source of the virtual link after resolution, but this causes things
to break when the source of the virtual link is a subgraph IO.

Instead, this PR changes the code section to restart resolution from the
destination of the virtual link so that the existing subgraph boundary
resolution code is applied.

Also fix a bug with reconnection of complex/any types on
conversion to subgraph.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6135-Fix-link-resolution-of-virtual-nodes-2916d73d36508183b891ef6eb39bad4c)
by [Unito](https://www.unito.io)
2025-10-20 10:02:41 -07:00
Jin Yi
32ed446285 [bugfix] Fix enable pack functionality to use proper API endpoint (#6157)
## Summary
- Fixed issue where enabling a disabled pack incorrectly triggered
installation instead of using the dedicated enable endpoint
- Added proper `enablePack` method in the manager service layer
- Updated store and component to use the correct API call

## Problem
When users toggled a disabled pack to enable it via `PackEnableToggle`
component, the system was incorrectly calling the install endpoint with
full installation parameters instead of the simpler enable endpoint that
only requires the pack ID.

## Solution
1. **Added dedicated `enablePack` method in `comfyManagerService.ts`**:
   - Uses the `'enable'` task kind with `EnablePackParams`
   - Only requires `cnr_id` parameter (simpler than install)

2. **Updated `comfyManagerStore.ts`**:
   - Created proper `enablePack` function that queues an enable task
- Removed the incorrect aliasing where `enablePack` was pointing to
`installPack`

3. **Simplified `PackEnableToggle.vue`**:
   - Now calls `enablePack` with only required parameters (id, version)
   - Removed unnecessary installation-specific parameters

## Test plan
- [x] Enable a disabled pack and verify it uses the enable endpoint (not
install)
- [x] Confirm the pack is properly enabled after the operation
- [x] Check that the task queue shows "Enabling" message (not
"Installing")
- [x] Verify existing install/uninstall functionality still works

Fixes #6154

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6157-bugfix-Fix-enable-pack-functionality-to-use-proper-API-endpoint-2926d73d3650819fa4caf1b848f99735)
by [Unito](https://www.unito.io)

---------

Co-authored-by: Claude <noreply@anthropic.com>
2025-10-19 23:55:21 -07:00
Christian Byrne
4066fbd2d7 disable instant queue mode on cloud (#6141)
## Summary

Remove the _Instant_ mode from the queue mode options if the
distribution target is cloud.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6141-disable-instant-queue-mode-on-cloud-2916d73d36508197920fc8e462f0be9f)
by [Unito](https://www.unito.io)
2025-10-19 23:34:44 -07:00
Christian Byrne
26f587c956 [auth] add service worker on cloud distribution to attach auth header to browser native /view requests (#6139)
## Summary

Added Service Worker to inject Firebase auth headers into browser-native
`/api/view` requests (img, video, audio tags) for cloud distribution.

## Changes

- **What**: Implemented [Service
Worker](https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API)
to intercept and authenticate media requests that cannot natively send
custom headers
- **Dependencies**: None (uses native Service Worker API)

## Implementation Details

**Tree-shaking**: Uses compile-time `isCloud` constant - completely
removed from localhost/desktop builds (verified via bundle analysis).
Verify yourself by building the app and `grep -r
"registerAuthServiceWorker\|setupAuth" dist/`
**Caching**: 50-minute auth header cache with automatic invalidation on
login/logout to prevent redundant token fetches.

**Message Flow**:
```mermaid
sequenceDiagram
    participant IMG as Browser
    participant SW as Service Worker
    participant MT as Main Thread
    participant FB as Firebase Auth

    IMG->>SW: GET /api/view/image.png
    SW->>SW: Check cache (50min TTL)
    alt Cache miss
        SW->>MT: REQUEST_AUTH_HEADER
        MT->>FB: getAuthHeader()
        FB-->>MT: Bearer token
        MT-->>SW: AUTH_HEADER_RESPONSE
        SW->>SW: Cache token
    end
    SW->>IMG: Fetch with Authorization header

    Note over SW,MT: On login/logout: INVALIDATE_AUTH_HEADER
```

## Review Focus

- **Same-origin mode**: Service Worker uses `mode: 'same-origin'` to
allow custom headers (browser-native requests default to `no-cors` which
strips headers)
- **Request deduplication**: Prevents concurrent auth header requests
from timing out
- **Build verification**: Confirm `register-*.js` absent in localhost
builds, present (~3.2KB) in cloud builds

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6139-auth-add-service-worker-on-cloud-distribution-to-attach-auth-header-to-browser-native--2916d73d3650812698dccd07d943ab3c)
by [Unito](https://www.unito.io)
2025-10-19 22:51:37 -07:00
Christian Byrne
7ad1112535 add telemetry provider for cloud distribution (#6154)
## Summary

This code is entirely excluded from open-source, local, and desktop
builds. During minification and dead-code elimination, the Mixpanel
library is fully tree-shaken -- meaning no telemetry code is ever
included or downloaded in those builds. Even the inline callsites are
removed during the build (because `isCloud` becomes false and the entire
block becomes dead code and is removed). The code not only has no
effect, is not even distributed in the first place. We’ve gone to great
lengths to ensure this behavior.

Verification proof:


https://github.com/user-attachments/assets/b66c35f7-e233-447f-93da-4d70c433908d

Telemetry is *enabled only in the ComfyUI Cloud environment*. Its goal
is to help us understand and improve onboarding and new-user adoption.
ComfyUI aims to be accessible to everyone, but we know the learning
curve can be steep. Anonymous usage insights will help us identify where
users struggle and guide us toward making the experience more intuitive
and welcoming.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6154-add-telemetry-provider-for-cloud-distribution-2926d73d3650813cb9ccfb3a2733848b)
by [Unito](https://www.unito.io)

---------

Co-authored-by: Claude <noreply@anthropic.com>
2025-10-19 19:47:35 -07:00
Christian Byrne
522656a2dc [style] remove hover effect on Vue node socket labels (#6150)
## Summary

Align with the design by removing hover state. Hover state should not be
applied if clicking doesn't actually do anything.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6150-style-remove-hover-effect-on-Vue-node-socket-labels-2916d73d365081158edef8065edc42e8)
by [Unito](https://www.unito.io)
2025-10-19 14:29:03 -07:00
Christian Byrne
15d223ef9b [style] adjust Vue widget hover state (#6146)
## Summary

Change to match the design (hover changes background color slightly and
doesn't affect outline/border).

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6146-style-adjust-Vue-widget-hover-state-2916d73d365081a19297e77208dc9613)
by [Unito](https://www.unito.io)

---------

Co-authored-by: github-actions <github-actions@github.com>
2025-10-19 12:55:29 -07:00
Christian Byrne
c8146ffc64 Revert "fix dragging video/image components on Vue nodes triggers node drag (#5922)" (#6148)
## Summary

This PR reverts #5922 which fixed pointer capture behavior on video and
image preview components to prevent unintended node dragging.

## Changes

- Removes `data-capture-node="true"` attribute from `VideoPreview.vue`
and `ImagePreview.vue` components
- Removes pointer event delegation logic from
`useNodePointerInteractions.ts` composable
- Restores previous drag behavior where dragging on preview components
triggers node drag

## Reason for Revert

This changes the behavior from original Litegraph and is generally
annoying. Users would rather be able to drag the node than be able to
drag an image/video out from a node.

Reverts #5922

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6148-Revert-fix-dragging-video-image-components-on-Vue-nodes-triggers-node-drag-5922-2916d73d365081398bb5c20384e26bb8)
by [Unito](https://www.unito.io)
2025-10-19 12:28:40 -07:00
Christian Byrne
c263f6da25 [style] prevent Vue node selection outline being obscured by image output (#6061)
## Summary

Adds `px-2` on image to prevent this issue (below) - I think there's a
better solution but I'm not really sure what it is. We use outline for
selection state and it's somewhat complex how our ring/border/outline
with many different node states and interactions works right now. It
will take some CSS skill to allow the images to be totally flush.

<img width="720" height="715" alt="image"
src="https://github.com/user-attachments/assets/0283e036-7a31-45ef-b5cc-af3ac73171c9"
/>

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6061-style-prevent-Vue-node-selection-outline-being-obscured-by-image-output-28c6d73d365081d59b34d8f91252de92)
by [Unito](https://www.unito.io)

---------

Co-authored-by: github-actions <github-actions@github.com>
2025-10-19 11:52:14 -07:00
Christian Byrne
1f5191847a make "require subscription" toggleable in build (#6144)
## Summary

Adds build time feature flags system starting with a flag that indicates
whether subscription is required to use the app. This is only used on
cloud.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6144-make-require-subscription-toggleable-in-build-2916d73d3650813bb140c5e96bcce1ce)
by [Unito](https://www.unito.io)
2025-10-19 10:30:14 -07:00
sno
fc69924c4a [feat] implement dynamic imports for locale code splitting (#6076)
## Summary
- Implement dynamic imports for internationalization (i18n) locale files
to reduce initial bundle size
- Only load English locale eagerly as default/fallback, load other
locales on-demand
- Apply code splitting to both main ComfyUI frontend and desktop-ui
applications

## Technical Details
- **Before**: All locale files (main.json, nodeDefs.json, commands.json,
settings.json) for all 9 languages were bundled in the initial
JavaScript bundle
- **After**: Only English locale files are included in initial bundle,
other locales are loaded dynamically when needed
- Implemented `loadLocale()` function that uses dynamic imports with
`Promise.all()` for efficient parallel loading
- Added locale tracking with `loadedLocales` Set to prevent duplicate
loading
- Updated both `src/i18n.ts` and `apps/desktop-ui/src/i18n.ts` with
consistent implementation

## Bundle Size Impact
This change significantly reduces the initial bundle size by removing ~8
languages worth of JSON locale data from the main bundle. Locale files
are now loaded on-demand only when users switch languages.

## Implementation
- Uses dynamic imports: `import('./locales/[locale]/[file].json')`
- Maintains backward compatibility with existing locale switching
mechanism
- Graceful error handling for unsupported locales
- No breaking changes to the public API

## Test plan
- [x] Verify initial load only includes English locale
- [x] Test dynamic locale loading when switching languages in settings
- [x] Confirm fallback behavior for unsupported locales
- [x] Validate both web and desktop-ui applications work correctly

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

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6076-feat-implement-dynamic-imports-for-locale-code-splitting-28d6d73d36508189ae0ef060804a5cee)
by [Unito](https://www.unito.io)

---------

Co-authored-by: Claude <noreply@anthropic.com>
2025-10-18 23:05:26 -07:00
Christian Byrne
5b5151f41f [perf] manually chunk vendored code (#6137)
## Summary

Added a `manualChunks` strategy in `vite.config.mts` that splits
primevue, tiptap, chart.js, three/@xterm, core Vue/Pinia, and the
remaining dependencies into dedicated vendor bundles. This reduces the
main application chunk size and allows browsers to cache heavy
third-party code across releases, improving load times when those
libraries stay unchanged.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6137-perf-manually-chunk-vendored-code-2916d73d36508140a44ec0de228ef9cc)
by [Unito](https://www.unito.io)
2025-10-18 22:49:11 -07:00
Christian Byrne
2018f1e671 [ci] drop console statements (except warn and error) when building app (#6123)
## Summary

Marks all the console methods besides `warn` and `error` as pure
functions so they can be dropped during DCE in build pipeline. It's
simpler to use `drop` but that would remove errors/warnings.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6123-ci-drop-console-statements-except-warn-and-error-when-building-app-2906d73d365081f28303c0d784352b12)
by [Unito](https://www.unito.io)
2025-10-18 22:43:38 -07:00
Christian Byrne
8822f186e0 [style] adjust appearance of "delete account" component to be text rather than button (#6126)
Adjusts style of "Delete Account" button.

**Before**:

<img width="731" height="925" alt="Screenshot from 2025-10-18 04-22-24"
src="https://github.com/user-attachments/assets/497de4ca-9359-41d8-b944-0d69835f43b1"
/>

**After**:

<img width="731" height="925" alt="Screenshot from 2025-10-18 04-22-14"
src="https://github.com/user-attachments/assets/f42a21fb-7d4d-43f5-b27b-1f85e4470dbd"
/>

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6126-style-adjust-appearance-of-delete-account-component-to-be-text-rather-than-button-2906d73d365081eab48ece171fa4fd8a)
by [Unito](https://www.unito.io)
2025-10-18 22:37:06 -07:00
Christian Byrne
0ff1837ced [ci] allow setting GENERATE_SOURCEMAP as env var (#6134)
## Summary

Introduces a `GENERATE_SOURCEMAP` environment flag in `vite.config.mts`
that defaults to enabled (`true` unless set to `'false'`). This keeps
source maps on by default, while allowing opt-out for lean production
artifacts.

Allows the choice to be made as part of the distribution pipeline and
changed for different targets.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6134-ci-allow-setting-GENERATE_SOURCEMAP-as-env-var-2916d73d3650815d91b9eff12b6e55fd)
by [Unito](https://www.unito.io)
2025-10-18 20:23:27 -07:00
Terry Jia
7e1e8e3b65 subscription page (#6064)
Summary

Implements cloud subscription management UI and flow for ComfyUI Cloud
users.

  Core Features:
- Subscription Status Tracking: Global reactive state management for
subscription status across all components
  using shared subscriptionStatus ref
- Subscribe to Run Button: Replaces the Run button in the actionbar with
a "Subscribe to Run" button for users
  without active subscriptions
- Subscription Required Dialog: Modal dialog with subscription benefits,
pricing, and checkout flow with video
  background
- Subscription Settings Panel: New settings panel showing subscription
status, renewal date, and quick access to
  billing management
- Auto-detection & Polling: Automatically polls subscription status
after checkout completion and syncs state
  across the application


https://github.com/user-attachments/assets/f41b8e6a-5845-48a7-8169-3a6fc0d2e5c8



┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6064-subscription-page-28d6d73d36508135a2a0fe7c94b40852)
by [Unito](https://www.unito.io)

---------

Co-authored-by: GitHub Action <action@github.com>
2025-10-18 20:21:30 -07:00
Christian Byrne
d83e34d0fc [ci] collapsible sections in ci size report comments (#6118)
## Summary

Adjust size reporting scripts to aggregate baseline/current metrics,
render collapsible GitHub-friendly tables using `<details>`, and
expanded bundle categorisation heuristics

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6118-ci-collapsible-sections-in-ci-size-report-comments-2906d73d365081d0b302c09fa1b98ccb)
by [Unito](https://www.unito.io)
2025-10-18 20:15:51 -07:00
Christian Byrne
c6b528b8be [ci] allow manual workflow dispatch to do version bumping on core branches (rather than just on main) (#6117)
## Summary

Added configurable base branch selection to version bump workflows,
enabling patch releases from `core/*` branches via GitHub Actions UI.

## Changes

- **What**: Extended [workflow_dispatch
inputs](https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#workflow_dispatch)
with `branch` parameter for both main frontend and desktop-ui version
bump workflows
- **Validation**: Added branch existence check that lists available
`core/*` branches on error
- **Workflow modifications**:
- `release-version-bump.yaml`: Checkout and create PRs targeting
user-specified branch
- `version-bump-desktop-ui.yaml`: Same behavior for desktop-ui releases

## Review Focus

Branch validation logic correctly handles both local (`refs/heads/`) and
remote (`refs/remotes/origin/`) refs. Default value preserves backward
compatibility for release sheriffs unfamiliar with new feature.

## Use Case

Previously, patch releases from `core/1.29` required manual version
bumping. Now maintainers can trigger from Actions UI with dropdown
selections.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6117-ci-allow-manual-workflow-dispatch-to-do-version-bumping-on-core-branches-rather-than-j-2906d73d365081cba3aff46471206a9e)
by [Unito](https://www.unito.io)
2025-10-18 13:04:49 -07:00
Christian Byrne
37d2f25d2f [style] ensure all floating menus have same gap from screen edges (#6120)
## Summary

Ensures all the floating shell app elements (graph canvas menu, minmap,
floating sidebar) all have .5rem gap between edge and viewport. Was not
proper distance for graph canvas menu and minimap due to splitter
overlay (parent) itself having 8px gap from viewport.

**After**:

<img width="605" height="535" alt="Screenshot from 2025-10-18 01-34-51"
src="https://github.com/user-attachments/assets/215efd5c-541f-441a-8b0d-1eb1f6fce672"
/>

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6120-style-ensure-all-floating-menus-have-same-gap-from-screen-edges-2906d73d365081598ae4d16f2380baf8)
by [Unito](https://www.unito.io)

---------

Co-authored-by: github-actions <github-actions@github.com>
2025-10-18 05:07:06 -07:00
Christian Byrne
3dcabaeb0a Mark failing vue nodes bypass test as fixme temporarily (#6127)
## Summary

Mark this test as `fixme` while I fix it. Don't want to disrupt CI
signal for other repo maintainers - the test has already been broken for
nearly 2 days now which is not acceptable.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6127-Mark-failing-vue-nodes-bypass-test-as-fixme-temporarily-2906d73d36508186a1def1d2dc7f85a5)
by [Unito](https://www.unito.io)

---------

Co-authored-by: GitHub Action <action@github.com>
2025-10-18 05:06:24 -07:00
Comfy Org PR Bot
eb85f99b10 1.30.1 (#6124)
Patch version increment to 1.30.1

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6124-1-30-1-2906d73d36508176a9a0dca7ecfbb40c)
by [Unito](https://www.unito.io)

---------

Co-authored-by: christian-byrne <72887196+christian-byrne@users.noreply.github.com>
Co-authored-by: github-actions <github-actions@github.com>
2025-10-18 04:24:04 -07:00
Christian Byrne
2e99c394a0 fix minimap not re-rendering when Vue nodes change mode (bypass, mute) (#6110)
## Summary

Fixes minimap not updating when node bypass/mute state changes in vue
nodes mode by adding `onTrigger` tracking of relevant instrumented
properties.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6110-fix-minimap-not-re-rendering-when-Vue-nodes-change-mode-bypass-mute-28f6d73d36508112bab1ce031705c6a9)
by [Unito](https://www.unito.io)

---------

Co-authored-by: github-actions <github-actions@github.com>
2025-10-18 03:59:19 -07:00
Johnpaul Chiwetelu
9ef6f94f92 [fix] Enable AUDIO_RECORD widget in both LiteGraph and Vue nodes modes (#6094)
Fixed the AUDIO_RECORD widget to display correctly in both rendering
modes:

- Removed conflicting AUDIO_RECORD registration from ComfyWidgets that
was blocking the custom widget implementation in uploadAudio.ts
extension
- Changed canvasOnly flags from true to false on both audioUIWidget and
recordWidget to enable Vue nodes rendering
- Added type override (recordWidget.type = 'audiorecord') after widget
creation to enable Vue component lookup while preserving LiteGraph
button rendering
- Removed unused IAudioRecordWidget type definition

The widget now works correctly:
- LiteGraph mode: Displays as a functional button
- Vue nodes mode: Displays full recording UI with waveform visualization

## Summary

<!-- One sentence describing what changed and why. -->

## Changes

- **What**: <!-- Core functionality added/modified -->
- **Breaking**: <!-- Any breaking changes (if none, remove this line)
-->
- **Dependencies**: <!-- New dependencies (if none, remove this line)
-->

## Review Focus

<!-- Critical design decisions or edge cases that need attention -->

<!-- If this PR fixes an issue, uncomment and update the line below -->
<!-- Fixes #ISSUE_NUMBER -->

## Screenshots (if applicable)

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



https://github.com/user-attachments/assets/cf6513d4-0a4b-4210-88e2-a948855b5206


┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6094-fix-Enable-AUDIO_RECORD-widget-in-both-LiteGraph-and-Vue-nodes-modes-28e6d73d365081faa879f53e3e2dddad)
by [Unito](https://www.unito.io)

---------

Co-authored-by: bymyself <cbyrne@comfy.org>
2025-10-18 03:15:26 -07:00
Christian Byrne
598d170d10 [style] update dropdown button text class in Vue node upload widgets (#6062)
## Summary

Change colors to match Figma variables.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6062-style-update-dropdown-button-text-class-in-Vue-node-upload-widgets-28c6d73d365081ae8857ddcef06784ef)
by [Unito](https://www.unito.io)

---------

Co-authored-by: github-actions <github-actions@github.com>
2025-10-18 02:09:38 -07:00
Christian Byrne
2c221bdd7b [ci] enable minification in build workflows (#6116)
## Summary

Continuation of https://github.com/Comfy-Org/ComfyUI_frontend/pull/6068:
set `SHOULD_MINIFY` in build workflows to enable minification.

Our build plugin dictates what files are available for extensions to
import at runtime:
bfe083dcba/build/plugins/comfyAPIPlugin.ts (L52)

The shimming marks these files and their named exports and named memebrs
as side-effectful, so names should be preserved from the perspective of
3rd party extensions, making this safe in theory.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6116-ci-enable-minification-in-build-workflows-2906d73d36508141be57f63b619f9f16)
by [Unito](https://www.unito.io)
2025-10-18 01:47:43 -07:00
Christian Byrne
76b44d3de5 [perf] disable cache-busting param on cloud (#6105)
## Summary

Updates `ComfyApp.getRandParam()` to return an empty string on cloud
builds (detected via `isCloud`), letting hosted deployments rely on HTTP
caching for `/view` assets while local hosts retain the legacy cache
busting. The audio widget helpers now defer to the same method instead
of generating their own random suffix, so every frontend consumer shares
this logic.

This keeps the original overwrite protection for localhost workflows but
removes needless cache misses in the cloud stack, where GCS already
serve strong ETags and there is not really a concern about stale
filenames. Once the backend exposes a deterministic file-version field,
we can delete the remaining local-only randomness entirely.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6105-perf-disable-cache-busting-param-on-cloud-28f6d73d36508181b34afad406fabc59)
by [Unito](https://www.unito.io)
2025-10-18 01:26:40 -07:00
Christian Byrne
3fc8b49038 [refactor] simplify redundant check in cloud badge extension (#6106)
Simplifies code with tautological condition. The extension is only
loaded when `isCloud` is true due to
38f188759d/src/extensions/core/index.ts (L27-L29)

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6106-refactor-simplify-redundant-check-in-cloud-badge-extension-28f6d73d365081d69903f5a985fdc8be)
by [Unito](https://www.unito.io)
2025-10-18 01:25:59 -07:00
Christian Byrne
10748bdac9 [auth] handle auth/requires-recent-login for account deletion and password updates (#6109)
## Summary

Implemented error recovery system to handle Firebase
`auth/requires-recent-login` errors when deleting accounts or updating
passwords.


https://github.com/user-attachments/assets/92a79e2a-cff5-4b18-b529-dbaf5f2303f2

## Changes

- **What**: Added [ErrorRecoveryStrategy
pattern](https://firebase.google.com/docs/auth/web/manage-users#re-authenticate_a_user)
to `useErrorHandling` composable with automatic retry logic for
sensitive Firebase operations
- **Breaking**: None - recovery strategies are optional, all existing
code unchanged

## Technical Details

Firebase enforces
[reauthentication](https://firebase.google.com/docs/reference/js/auth#autherrorcodes)
for security-sensitive operations (account deletion, password changes)
after ~5 minutes of inactivity. Previously these operations failed with
cryptic error messages.

New flow:
1. Operation throws `auth/requires-recent-login`
2. Recovery strategy shows confirmation dialog
3. User logs out and re-authenticates
4. Operation automatically retries

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6109-auth-handle-auth-requires-recent-login-for-account-deletion-and-password-updates-28f6d73d36508119abf4ce30eecea976)
by [Unito](https://www.unito.io)
2025-10-18 01:24:52 -07:00
AustinMroz
bfe083dcba Move subgraph badge into node title (#6115)
<img width="1350" height="691" alt="image"
src="https://github.com/user-attachments/assets/4a1a909f-463e-4c77-8855-6fe0a177b659"
/>

Design docs show icon as being reversed in litegraph mode. If needed,
fixing this is basically just removing a negative sign.

LiteGraph implementation was not as clean as I hoped. I can spend some
more time divining some non-magic constants, and mayybe resolve the svg
data from css styles if desired.

Resolves #4647
Resolves #6107

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6115-Move-subgraph-badge-into-node-title-2906d73d365081e4ba82d321fc27afed)
by [Unito](https://www.unito.io)

---------

Co-authored-by: GitHub Action <action@github.com>
2025-10-18 00:32:54 -07:00
Rizumu Ayaka
917f8ae27d ci: size report (#6082)
## Summary

show bundle size info automatically in Pull Request

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6082-ci-size-report-28e6d73d365081c2bf73ce9919a7c01a)
by [Unito](https://www.unito.io)
2025-10-17 19:23:42 -07:00
Benjamin Lu
8dee207b8c WorkflowTabs: cleanup scroll/overflow handling and watcher disposal (#6080)
## Summary

Refactor and cleanup `WorkflowTabs` scroll/overflow handling to improve
stability, ensure proper watcher disposal, and keep the active tab in
view more reliably.

note: honestly a nit, can drop if review is too annoying

## Changes

- **What**: 
- Replace `ScrollPanel` ref with `containerRef` and query
`.p-scrollpanel-content` within the container.
- Add computed `scrollContent` to centralize access to the scrollable
element.
- Add `ensureActiveTabVisible({ waitForDom })` option to skip `nextTick`
when not needed.
- Rework watchers with explicit `WatchStopHandle`s and `onCleanup` to
stop previous watchers and dispose `overflowObserver` correctly when
`scrollContent` changes.
- Update arrow enable logic by watching `arrivedState.left/right`
together and setting `leftArrowEnabled`/`rightArrowEnabled` immediately.
- Only measure and re-ensure visibility when overflowing; call
`scrollState.measure()` and `ensureActiveTabVisible({ waitForDom: false
})` after arrows update.
- **Breaking**: None
- **Dependencies**: None

## Review Focus

- Correctness of watcher lifecycle and cleanup when `scrollContent`
changes
- Arrow enablement behavior at scroll boundaries
- Reliability of `ensureActiveTabVisible` across tab selection and
overflow changes
- Regressions in scroll performance and tab visibility

## Screenshots (if applicable)

N/A

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6080-WorkflowTabs-cleanup-scroll-overflow-handling-and-watcher-disposal-28d6d73d3650819cba6de70fb10354ad)
by [Unito](https://www.unito.io)
2025-10-17 15:12:03 -07:00
AustinMroz
a0d9cc7e33 Add subgraph management keybinds (#6114)
Adds a new keybind to attempt to toggle the promotion state of the
currently hovered widget

Adds a keybind to open the subgraph configuration panel

Neither of these options are given a default binding.

Cleans up some incorrect version and icon information that was forgotten
about.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6114-Add-subgraph-management-keybinds-28f6d73d365081e6b6f3c78f99afc094)
by [Unito](https://www.unito.io)

---------

Co-authored-by: Benjamin Lu <benceruleanlu@proton.me>
2025-10-17 15:09:37 -07:00
Christian Byrne
d1639c4377 [ci] extend backport workflow to work with arbitrary branches (#6108)
## Summary

Expands the PR backport workflow so maintainers can target any release
branch using labels, instead of being limited to the `core/x.y` release
lines. The workflow now collects labels formatted as plain version
numbers (`1.24`) as before, plus new prefixes like
`branch:release/hotfix` or `backport:partner/foo`, validates that each
referenced branch exists, and then cherry-picks the source merge commit
to every target.

All generated PRs and failure comments reference the actual branch name,
making it clear where the backport landed or why it failed. This keeps
the existing opt-in flow (`needs-backport`) but makes it flexible enough
for custom support and partner branches without extra manual work.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6108-ci-extend-backport-workflow-to-work-with-arbitrary-branches-28f6d73d365081bf85a3d4c40a23bb68)
by [Unito](https://www.unito.io)
2025-10-17 14:38:06 -07:00
Benjamin Lu
9f046a11ea Use scrollIntoView to keep active workflow tab visible (#6077)
> "The watcher that should keep the active workflow tab visible compares
the tab’s bounds against the scroll panel’s content element, whose
rectangle spans the entire scroll width instead of the visible viewport.
As a result, when the active tab starts off-screen, the computed offsets
stay ≤ 0 and no corrective scroll occurs, so the selected tab remains
hidden."

fixes #6057

------
https://chatgpt.com/codex/tasks/task_e_68efe2b5b4a08330940a20552c1db339
2025-10-17 00:37:43 -07:00
sno
e1cd88afe3 fix(ci): update claude review workflow to match renamed CI workflows (#6101)
## Summary
- Investigated Claude Review workflow failures after CI workflow
renaming
- Found and fixed outdated workflow references in `.claude/commands`
files
- Created comprehensive workflow evolution documentation

## Problem
Claude Review workflows were failing, initially thought to be due to
workflow name changes in commit 5a7ec8148. Investigation revealed the
real issues and uncovered additional documentation problems.

## Root Cause Analysis
After checking the [lewagon/wait-on-check-action
documentation](https://github.com/lewagon/wait-on-check-action):
- `check-regexp` matches against job names (`jobs.<job_id>.name`) 
- Job names like `lint-and-format`, `test`, `playwright-tests` were
never changed
- The real issue was Playwright test failures, not regex patterns

## Findings & Fixes

### 1. Claude Review Workflow 
- **Issue**: Failures were caused by actual test failures (e.g.,
`playwright-tests-chromium-sharded (8, 8): completed (failure)`)
- **Fix**: Reverted unnecessary regex changes since original patterns
were correct

### 2. Outdated Command References 
Found 5 outdated workflow references in `.claude/commands` files:

| File | Old Reference | New Reference |
|------|---------------|---------------|
| `create-frontend-release.md` | `release.yaml` |
`release-draft-create.yaml` |
| `create-frontend-release.md` | `create-release-candidate-branch.yaml`
| `release-branch-create.yaml` |
| `create-frontend-release.md` | `release.yaml` (in script) |
`release-draft-create.yaml` |
| `create-hotfix-release.md` | `release.yaml` |
`release-draft-create.yaml` |
| `create-frontend-release.md` | workflow text reference | updated to
match filename |

### 3. Historical Analysis & Documentation 📚
Created comprehensive workflow evolution timeline:

#### Documentation Added:
- `logs/workflow-renames.md`: Complete mapping of all workflow name
changes
- `logs/report.md`: Comprehensive audit report with findings and
recommendations
- `logs/track-workflow-renames.md`: **Exhaustive history of all 77
workflow files that ever existed**

#### Key Historical Insights:
- **77 unique workflow files** have existed since 2024-06-13
- **195 commits** analyzed affecting workflows
- **10 evolution phases** from ad-hoc naming to systematic
categorization
- **52 rename operations** and **10 file deletions**
- **68% reduction** through optimization (77 total → 25 active)

#### Timeline Highlights:
- **2024-06-13**: `test-ui.yaml` - first workflow ever created
- **2025-10-01**: First major reorganization (17 renames)
- **2025-10-02**: Category-based naming introduction (`ci-*`, `pr-*`,
`release-*`)
- **2025-10-16**: API category refinement (`api-update-*`)
- **2025-10-17**: Final consolidation (our current branch)

#### Evolution Patterns:
- **Era 1**: Descriptive names (`test-ui.yaml`, `format.yaml`)
- **Era 2**: Category prefixes (`ci-*`, `pr-*`, `release-*`)
- **Era 3**: Action-target patterns (`api-update-*-types.yaml`)
- **Era 4**: Semantic categorization (current state)

## Impact
-  Claude Review workflow now works correctly (job names were always
correct)
-  Command documentation now references actual workflow files
-  Complete archaeological record of workflow evolution created
-  Future workflow changes will be easier to track with comprehensive
documentation

## Test Plan
- [x] Workflow syntax validation passes
- [x] All `gh run list --workflow=<filename>` commands in docs now work
- [x] Documentation provides complete historical context
- [ ] Monitor if Claude Review works properly once underlying test
failures are resolved

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

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Alexander Brown <drjkl@comfy.org>
2025-10-17 00:25:43 -07:00
AustinMroz
38f188759d Fix mouse clicks being swallowed by new menu (#6103)
Moves around pointer events styling so that mouse events near the top
and side bar don't get swallowed.

Resolves #6102

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6103-Fix-mouse-clicks-being-swallowed-by-new-menu-28f6d73d3650819aa4a1dc1eacfd3adb)
by [Unito](https://www.unito.io)
2025-10-16 22:26:54 -07:00
Christian Byrne
ad5be8ec70 [style] update style of mask editor button on Vue node image preview (#6060)
Updates style of mask editor button on Vue nodes image preview overlay
to align with
[design](https://www.figma.com/design/31uH3r4x3xbIctuRWYW6NM/V3---Vue-Nodes?node-id=7744-84270&m=dev).
Also makes the custom mask have color be derived from text color class.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6060-style-update-style-of-mask-editor-button-on-Vue-node-image-preview-28c6d73d36508111a562c6a22c64f027)
by [Unito](https://www.unito.io)

---------

Co-authored-by: GitHub Action <action@github.com>
2025-10-16 21:02:55 -07:00
sno
653cf64e01 [chore] Upgrade Prettier from 3.3.2 to 3.6.2 (#6089)
## Summary
- Updated Prettier from 3.3.2 to 3.6.2 in pnpm-workspace.yaml
- Ran `pnpm install` to update dependencies
- Ran `pnpm format` to apply new formatting rules
- Verified typecheck passes

## Test plan
- [x] TypeScript typecheck passes
- [x] Prettier formatting applied successfully

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

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6089-chore-Upgrade-Prettier-from-3-3-2-to-3-6-2-28e6d73d3650816a888ff1129b14e0bc)
by [Unito](https://www.unito.io)

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Christian Byrne <cbyrne@comfy.org>
2025-10-16 20:46:59 -07:00
Comfy Org PR Bot
626fcff80b 1.30.0 (#6099)
Minor version increment to 1.30.0

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6099-1-30-0-28f6d73d3650817b9505d85b906047c2)
by [Unito](https://www.unito.io)

---------

Co-authored-by: arjansingh <1598641+arjansingh@users.noreply.github.com>
Co-authored-by: github-actions <github-actions@github.com>
2025-10-16 19:10:17 -07:00
AustinMroz
15b1b91b16 Implement a legacy canvas widget for vue mode (#6011)
![updated-legacy-widget](https://github.com/user-attachments/assets/3f0a1623-9445-4059-acbb-086baec54980)

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6011-Implement-a-legacy-canvas-widget-for-vue-mode-2896d73d36508127a5d1debcccb519a0)
by [Unito](https://www.unito.io)

---------

Co-authored-by: GitHub Action <action@github.com>
2025-10-16 18:50:19 -07:00
Christian Byrne
e48e11e434 fix LiteGraph capturing node pointer events if Vue and LG node positions become desynced (#6058)
## Summary

Added Vue mode guards to LiteGraph canvas to prevent dual event handling
between Vue node components and LiteGraph node event handlers. Fixes a
bug where the litegraph and vue positions become out of sync and then
there are effectively dead spots on the canvas (the user should still be
able to interact with the graph even if the positions desync - the only
real downside of desync should just be mispositioned nodes in the
serialized state).

Returns early only in `processNodeClick` and the node-related
(alt+click+drag node cloning) canvas handlers. In general, the LiteGraph
canvas still owns some events/interactions - but the node interactions
are fully owned by Vue when in Vue nodes mode.

## Changes

- **What**: Added `LiteGraph.vueNodesMode` checks in
[LGraphCanvas.ts](src/lib/litegraph/src/LGraphCanvas.ts) to skip native
event processing when Vue components handle interactions
- **Breaking**: This could have some side effects or break some
extensions if anything was relying on these. However, prior to this
change, things like `processNodeClick` should only have been getting the
click events in de-sync scenarios anyway (described in
[Summary](##Summary) section)

## Review Focus

Any possible side-effects you can think of.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6058-fix-LiteGraph-capturing-node-pointer-events-if-Vue-and-LG-node-positions-become-desynced-28c6d73d365081f389f8c72299c31b0b)
by [Unito](https://www.unito.io)
2025-10-16 18:46:37 -07:00
AustinMroz
09b1e1702c Skip rename operation on focus lost. (#6052)
When renaming a workflow through the breadcrumb bar. It is intended to
complete the rename operation if the user clicks away from the text
input. However, completing the text input by pressing enter can cause
the rename operation to occur twice: On pressing enter and on loss of
focus. When this occurs, the second rename causes, an error to be raised
because renaming a workflow to the same name is not allowed.

Worse, a user should be able to cancel a rename by pressing escape, but
this still causes the "loss of focus" event to fire and rename the
workflow anyways.

As a simple fix to both of these, this PR disables rename on loss of
focus. A rename only occurs when the user completes a rename operation
with the enter key.

Resolves #6051

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6052-Skip-rename-operation-on-focus-lost-28c6d73d3650815d9a5fecd8567d4299)
by [Unito](https://www.unito.io)
2025-10-16 18:45:47 -07:00
Rizumu Ayaka
64430708ea perf: tree shaking and minify (#6068)
## Summary

I believe tree-shaking does not affect [the
shim](https://github.com/Comfy-Org/ComfyUI_frontend/blob/rizumu/perf/cloud-minify/build/plugins/comfyAPIPlugin.ts#L73),
and it should safe to enable it.

maybe we need to find the extension that will broke in this case.


|   | Rendered | Gzip     | Brotli  |
|--------|-----------|----------|----------|
| Before | 16.69 MB  | 4.23 MB | 3.54 MB |
| After  | 12.07 MB  | 3 MB   | 2.53 MB |
2025-10-16 18:44:04 -07:00
Benjamin Lu
8396c9ae94 Rename ImagePreview spec to test (#6093)
## Summary
- rename the ImagePreview test file to use the .test.ts suffix required
outside of browser tests
- align with the eslint no-restricted-syntax rule that forbids .spec.ts
files in tests-ui

## Testing
- pnpm typecheck


------
https://chatgpt.com/codex/tasks/task_e_68f14718b0608330bd559182c9f7c62e

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6093-Rename-ImagePreview-spec-to-test-28e6d73d36508191b7e0c076fd96e4a3)
by [Unito](https://www.unito.io)
2025-10-16 18:16:02 -07:00
AustinMroz
1234e1c56d Support Right Click -> Save for animated webp (#6095)
Allow for Right Click -> Save Image to work for the "SaveAnimatedWEBP"
node.

Fixing this revealed other existing issues:
- Attempting to resize the node causes runaway scaling
- Right clicking on the image directly causes a browser context menu
without a save option.

Significant rewriting has been performed to resolve both of these
issues.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6095-Support-Right-Click-Save-for-animated-webp-28e6d73d3650818e85a2ec58c38c2aae)
by [Unito](https://www.unito.io)
2025-10-16 18:15:23 -07:00
pythongosssss
984ebef416 Floating Menus - UI rework (#5980)
## Summary

Enhancing and further modernizing the UI, giving users more usable area
whilst keeping farmiliar positioning and feel of elements.

## Changes

- **What**: Significant restructure of the UI elements, changing
elements from large blocks to floating elements, updating:
- Side toolbar menu (floating style, supports small/normal mode,
combines to scroll on height overflow)
- Bottom tabs panel (floating style, tabs redesigned)
- Action bar (support for docking/undocking menu)
    - Added login/user menu button to top right
- Restyled breadcrumbs (still collapse when overflows)
- Add litegraph support for fps info position (so it isn't covered by
the sidebar)

- **Breaking**: 
- Removed various elements and added new ones, I have tested custom
sidebars, custom actions, etc but if scripts are inserting elements into
"other" elements they may have been (re)moved.
- Remove support for bottom menu
- Remove support for 2nd-row tabs

## Screenshots 
<img width="1116" height="907" alt="ui"
src="https://github.com/user-attachments/assets/b040a215-67d3-4c88-8c4d-f402a16a34f6"
/>


https://github.com/user-attachments/assets/571dbda5-01ec-47e8-b235-ee1b88c93dd0

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5980-Floating-Menus-UI-rework-2866d73d3650810aac60cc1afe979b60)
by [Unito](https://www.unito.io)

---------

Co-authored-by: GitHub Action <action@github.com>
Co-authored-by: github-actions <github-actions@github.com>
2025-10-16 18:12:09 -07:00
snomiao
8cc5b52c64 refactor: reorganize GitHub workflows with consistent naming convention (#5891)
## Summary

This PR implements a systematic naming convention for all GitHub
workflows to improve organization and discoverability. All 22 workflows
have been renamed and grouped by logical categories with consistent
prefixes.

## Changes

### Naming Convention
- **`ci-*`**: Continuous Integration workflows (testing, linting,
validation)
- **`pr-*`**: PR-specific automation triggered by labels  
- **`release-*`**: Release management workflows
- **`types-*`**: TypeScript type generation workflows
- **`i18n-*`**: Internationalization workflows

### Key Renames
- `tests-ci.yaml` → `ci-tests-e2e.yaml`
- `vitest-tests.yaml` → `ci-tests-unit.yaml`
- `storybook-and-chromatic-ci.yaml` → `ci-tests-storybook.yaml`
- `auto-backport.yaml` → `pr-backport.yaml`
- `claude-pr-review.yml` → `pr-claude-review.yaml`
- `version-bump.yaml` → `release-version-bump.yaml`
- `publish-frontend-types.yaml` → `release-npm-types.yaml`
- `create-dev-pypi-package.yaml` → `release-pypi-dev.yaml`

### Test Workflow Improvements
- Grouped all test workflows under `ci-tests-*` pattern
- Fork-safe deployment workflows: `ci-tests-e2e-forks.yaml`,
`ci-tests-storybook-forks.yaml`
- Added comments explaining fork deployment security workarounds

### Documentation
- Added comprehensive `.github/workflows/README.md`
- Documents naming conventions, best practices, and workflow
organization
- Includes trigger patterns and external dependencies

## Benefits

1. **Better Organization**: Workflows are now grouped logically by
prefix
2. **Improved Discoverability**: Easy to find related workflows  
3. **Consistent Naming**: All workflows follow the same pattern
4. **Clear Purpose**: Workflow names immediately indicate their function
5. **Maintainable**: README provides guidelines for future workflows

## Test Plan

- [x] All workflow cross-references updated
- [x] Display names match new file names  
- [x] Fork deployment workflows properly reference main workflows
- [x] Release workflows reference correct npm types workflow
- [x] All workflows retain original functionality

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

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5891-refactor-reorganize-GitHub-workflows-with-consistent-naming-convention-2806d73d365081febe47c7511bf0507e)
by [Unito](https://www.unito.io)

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Alexander Brown <drjkl@comfy.org>
2025-10-17 08:32:33 +09:00
Arjan Singh
03681a12bd feat: AssetCard tweaks (#6085)
## Summary

1. fix `preview_url` logic
2. design tweaks for border radius, fallback gradient, and name line
clamping (2 lines)
3. handle image error states by showing gradient
4. misc. refactors

## Screenshots


<img width="1515" height="1087" alt="Screenshot 2025-10-15 at 8 13
41 PM"
src="https://github.com/user-attachments/assets/85642869-d8cb-4ee4-b23d-a381e33fe802"
/>

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6085-feat-AssetCard-tweaks-28e6d73d3650818da7a2f4148be48ff7)
by [Unito](https://www.unito.io)
2025-10-16 14:46:50 -07:00
AustinMroz
9bfc9b740d Add additional check when restoring widgets_values (#6054)
Prior to #6014, I had identified and started on #6015 to resolve what I
believed to be a larger class of issues. While the issue I was solving
produced the same error log on the same line, I misunderstood the
circumstances in which the reported issue would occur. As proxyWidgets
do not have their own value, only linked widgets should have their value
restored from `widgets_values`. This PR adds an additional check both
that the property entry is within bounds, and that the property entry is
for a linked widget.

See #6014
A potential additional issue is still being investigated.
- Additional issue seems unrelated to fix here. Will leave issue open,
but recommend merging this as is to not block v1.28.7

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6054-Add-additional-check-when-restoring-widgets_values-28c6d73d365081489d29fc9723132db0)
by [Unito](https://www.unito.io)
2025-10-15 19:37:41 -07:00
AustinMroz
7355a51282 In update-locales workflow, don't skip ci (#6081)
Adding `[skip ci]` causes release workflows to break and the added
safety of rerunning tests is beneficial for releases.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6081-In-update-locales-workflow-don-t-skip-ci-28e6d73d365081208332ef7b11ebbb52)
by [Unito](https://www.unito.io)
2025-10-15 18:50:23 -07:00
Arjan Singh
7ca8615947 feat(assetService): increase limit to 500 (#6078)
## Summary

Increase asset API limit to 500 to account for additional models being
added.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6078-feat-assetService-increase-limit-to-500-28d6d73d365081539760f986dd5bff23)
by [Unito](https://www.unito.io)
2025-10-15 14:38:10 -07:00
Jin Yi
4dab27a84e feat: Improve MediaAssetCard video controls and add gallery view (#6065)
## Summary
- Enhanced video control visibility logic for better UX
- Added fullscreen gallery view with zoom-in button  
- Fixed hover interaction issues with overlays

## Changes

### Video Controls
- **Before**: Controls hidden when not hovering
- **After**: Controls always visible when not playing, hover-based
during playback

### Overlay Behavior  
- **Before**: All overlays hidden during video playback
- **After**: All overlays (actions, tags, layers) show on hover even
during playback

### Gallery View
- Added zoom-in button to top-right corner (all media types except 3D)
- Integrated with existing ResultGallery component
- Gallery closes when clicking dimmed background area

### Bug Fixes
- Fixed hover flicker issue by proper event handling on overlay elements

## Test Plan
- [x] Test video controls visibility (paused vs playing)
- [x] Test overlay hover behavior during video playback
- [x] Test zoom-in button opens gallery view
- [x] Test gallery closes on background click
- [x] Test 3D assets don't show zoom button
- [x] Test in Storybook with various media types

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

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6065-feat-Improve-MediaAssetCard-video-controls-and-add-gallery-view-28d6d73d3650818c90cfc5d0d00e4826)
by [Unito](https://www.unito.io)

---------

Co-authored-by: Claude <noreply@anthropic.com>
2025-10-15 11:45:55 -07:00
Rizumu Ayaka
97417736be chore: rollup-plugin-visualizer (#6072)
## Summary

Using this tool, will generates dist/stats.html after build, we can see
the parts that are excessively large when packaged.

```bash
pnpm run build:analyze
```

<img width="2486" height="1780" alt="CleanShot 2025-10-15 at 17 25
17@2x"
src="https://github.com/user-attachments/assets/fd3ff5c9-2e7a-44a0-8b9f-4d87664c848c"
/>


┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6072-chore-rollup-plugin-visualizer-28d6d73d36508184800cf7a67141b782)
by [Unito](https://www.unito.io)
2025-10-15 11:39:12 -07:00
Alexander Piskun
86e6e7bf1f add pricing for new Veo3.1 model (#6074)
## Summary

The prices for Veo3.1 are the same as the existing Veo3, just added a
new model via `||`

## Screenshots (if applicable)

<img width="1125" height="1077" alt="Screenshot From 2025-10-15
20-39-37"
src="https://github.com/user-attachments/assets/441cba83-cdd7-4bae-af0a-259c2dd38a59"
/>

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6074-add-pricing-for-new-Veo3-1-model-28d6d73d3650817b8bb8ea7442ff3e1a)
by [Unito](https://www.unito.io)
2025-10-15 11:32:05 -07:00
Johnpaul Chiwetelu
fb3ab88f04 fix Cloudbadge (#6063)
This pull request refactors how top bar badges are handled in the
application, making badge rendering extensible and moving cloud badge
logic into the extension system. The main changes include replacing the
old `CloudBetaBadge` component with a new, generic badge system,
introducing a Pinia store for badge management, and updating the
extension API to support top bar badges.

**Badge System Refactor and Extensibility**

* Replaced the hardcoded `CloudBetaBadge` with a new `TopbarBadges`
component, which dynamically renders badges from the store instead of
relying on the `isCloud` flag in `TopMenubar.vue`.
[[1]](diffhunk://#diff-b7d7bf1028f09fb907c09edf27631214d005c93b80eaff7cf15cfd53671b1e8aL9-R9)
[[2]](diffhunk://#diff-b7d7bf1028f09fb907c09edf27631214d005c93b80eaff7cf15cfd53671b1e8aL43-R48)

* Renamed and refactored `CloudBetaBadge.vue` to `TopbarBadge.vue`,
making it accept a generic `badge` prop and removing i18n logic from the
component.

* Added a new `TopbarBadges.vue` component to render all badges from the
`topbarBadgeStore`.

**Badge Data Management**

* Introduced a new Pinia store `topbarBadgeStore` that aggregates top
bar badges from all extensions, enabling dynamic badge management.

**Extension System Integration**

* Updated the extension API (`ComfyExtension` interface) to support a
new `topbarBadges` property, allowing extensions to contribute badges to
the top bar.

* Added a core extension (`cloudBadge.ts`) that registers a "Comfy
Cloud" beta badge when running in a cloud environment, using the new
badge system.
[[1]](diffhunk://#diff-b7818ca9daae2411d56695777160b8132507f2a3ff4f700d2510453c8833ca75R1-R16)
[[2]](diffhunk://#diff-236993d9e4213efe96d267c75c3292d32b93aa4dd6c3318d26a397e0ae56bc87R2)

**Type Definitions**

* Added a new `TopbarBadge` type to `comfy.ts` to define the structure
for top bar badges, supporting optional labels.
2025-10-15 05:27:19 +01:00
Terry Jia
476d6df1ca fix mask editor bug under vueNodes (#5953)
## Summary

fix mask editor issues on vueNodes

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5953-fix-mask-editor-bug-under-vueNodes-2856d73d3650810aa8a2e1a94c4d97a6)
by [Unito](https://www.unito.io)
2025-10-14 17:42:36 -07:00
Johnpaul Chiwetelu
7caad10e93 Badge for cloud environment (#6048)
This pull request introduces a new system for displaying
environment-specific badges in the application's top bar, with a focus
on supporting a "Comfy Cloud" badge in production environments. The
changes include new badge types, extension support, UI components, and
environment detection logic to ensure badges are only shown in
appropriate contexts.

**Topbar Badge System**

* Added a new `TopbarBadge` type and support for topbar badges in the
`ComfyExtension` interface to allow extensions to specify badges for the
top bar.
[[1]](diffhunk://#diff-c29886a1b0c982c6fff3545af0ca8ec269876c2cf3948f867d08c14032c04d66R24-R31)
[[2]](diffhunk://#diff-c29886a1b0c982c6fff3545af0ca8ec269876c2cf3948f867d08c14032c04d66R85-R88)
* Created a Pinia store `topbarBadgeStore` to aggregate topbar badges
from all registered extensions for display.

**UI Integration**

* Added a new `TopbarBadges.vue` component to render topbar badges and
integrated it into the top menu bar UI.
[[1]](diffhunk://#diff-6f460b1398fd033a2059daca1f991c74ce572cef86046a3726d1b1a70a3a4325R1-R32)
[[2]](diffhunk://#diff-b7d7bf1028f09fb907c09edf27631214d005c93b80eaff7cf15cfd53671b1e8aL5-R14)
* Updated CSS variables and menu styling to support the new badge
visuals.
[[1]](diffhunk://#diff-71b6b57a56095b04e47c797a5016149b76b27971cab04b93f033f1f846e0f5a0R88-R89)
[[2]](diffhunk://#diff-b7d7bf1028f09fb907c09edf27631214d005c93b80eaff7cf15cfd53671b1e8aL5-R14)

**Environment Detection and Extension Registration**

* Added a runtime environment detection utility to determine if the app
is running in production or staging, replacing the previous build-time
constant approach.
* Registered a new `cloudBadge` extension that conditionally adds a
"Comfy Cloud" badge with a "BETA" label when running in production.
[[1]](diffhunk://#diff-b7818ca9daae2411d56695777160b8132507f2a3ff4f700d2510453c8833ca75R1-R15)
[[2]](diffhunk://#diff-236993d9e4213efe96d267c75c3292d32b93aa4dd6c3318d26a397e0ae56bc87R2)

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6048-Badge-for-cloud-environment-28c6d73d365081188050ece527c3c8f3)
by [Unito](https://www.unito.io)

<img width="996" height="897" alt="Screenshot 2025-10-14 at 20 02 40"
src="https://github.com/user-attachments/assets/5a3258c5-87fc-46ae-ad23-7669696cb8b6"
/>
2025-10-15 00:44:32 +01:00
Christian Byrne
6ea96f071e [style] update design of keybinding badges in menus (#6059)
## Summary

Updates the keybinding badges in the context menu (from selection
toolbox and right click on Vue node) to align with [the
design](https://www.figma.com/design/vALUV83vIdBzEsTJAhQgXq/Comfy-Design-System?node-id=3128-104039&m=dev)
exactly, including using the tokens from Figma variables.


https://github.com/user-attachments/assets/e37492f7-81a8-4598-bebb-56eb86b5dc56

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6059-style-update-design-of-keybinding-badges-in-menus-28c6d73d3650817784c8d8afac9ed8b8)
by [Unito](https://www.unito.io)
2025-10-14 16:02:04 -07:00
Christian Byrne
10af2300fa rework minimap, toolbox, and menu designs with unified theming (#6038)
## Summary

This PR redesigns the graph canvas interface components including
minimap, toolbox, and menu systems with updated spacing, colors, and
interaction patterns - using the design tokens directly from Figma,
which can be used elsewhere going forward.

There are some other changes to the designs, outlined
[here](https://www.notion.so/comfy-org/Update-Minimap-Menu-v2-2886d73d365080e88e12f8df027019c0):

- [x]  Update/standardize the padding between viewport and toolbox
- [x] Update toolbox component’s style to match the other floating menus
style (border radius, height, padding and follow theme colors)
- [x]  Expose the minimap button
- [x]  Remove the focus button and delete it’s keybinding
- [x]  Group the hand and the default cursor buttons


https://github.com/user-attachments/assets/92542e60-c32d-4a21-a6f6-e72837a70b17

## Review Focus

New CSS variables for cross-component theming consistency and
CanvasModeSelector component extraction for improved code organization.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6038-rework-minimap-toolbox-and-menu-designs-with-unified-theming-28b6d73d36508191a0c6cf8036d965c4)
by [Unito](https://www.unito.io)

---------

Co-authored-by: github-actions <github-actions@github.com>
2025-10-14 14:26:07 -07:00
Makki Shizu
2058967761 Fix Simplified Chinese Translation (#6039)
## Summary
Revise some traditional Chinese to simplified Chinese.
<!-- One sentence describing what changed and why. -->

## Changes

- **What**: <!-- Core functionality added/modified -->
- **Breaking**: <!-- Any breaking changes (if none, remove this line)
-->
- **Dependencies**: <!-- New dependencies (if none, remove this line)
-->

## Review Focus

<!-- Critical design decisions or edge cases that need attention -->

<!-- If this PR fixes an issue, uncomment and update the line below -->
<!-- Fixes #ISSUE_NUMBER -->

## Screenshots (if applicable)

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

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6039-Fix-Simplified-Chinese-Translation-28b6d73d365081aebba5f60893b75cb3)
by [Unito](https://www.unito.io)
2025-10-14 13:33:55 -07:00
Christian Byrne
d1af7c8256 fix terminal style (#6056)
## Summary

After #5292, at certain browser zoom levels, the xterm terminal did not
fill horizontal space properly, showing a gap with mismatched background
colors (`#171717` viewport vs `black` terminal content).

Fixes https://github.com/Comfy-Org/ComfyUI_frontend/issues/6049

## Problem

The hardcoded `#171717` terminal theme background only affected the
xterm viewport, while terminal content remained black, causing a visible
color mismatch at low zoom levels where the gap was more apparent.

Fixed by keeping original theme when not on desktop distribution.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6056-fix-terminal-style-28c6d73d36508132b521e5767f41d540)
by [Unito](https://www.unito.io)
2025-10-14 13:26:35 -07:00
Christian Byrne
5bc7c8a5c2 add aria labels on vue node widgets (2/2) (#6037)
## Summary

Continuation of https://github.com/Comfy-Org/ComfyUI_frontend/pull/6032

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6037-add-aria-labels-on-vue-node-widgets-2-2-28b6d73d365081d68795f5dfaca0b89a)
by [Unito](https://www.unito.io)
2025-10-14 11:33:15 -07:00
Arjan Singh
094d6e65a2 Trigger release workflow for 1.29.2 (#6046)
Empty commit to trigger release workflow after [skip ci] blocked
automatic release.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6046-Trigger-release-workflow-for-1-29-2-28c6d73d365081119ef7d8bf570d2804)
by [Unito](https://www.unito.io)
2025-10-13 20:01:38 -07:00
Comfy Org PR Bot
2808e0a437 1.29.2 (#6045)
## What's Changed

### 🚀 Features
- Add MediaAssetCard presentation components (#5878)
- Make Vue nodes' outputs/previews responsively sized and work with node
resizing (#5970)
- Allow connection to subgraphIOs in vue mode (#6016)
- Add distribution detection pattern (#6028)
- Make nodeData.widgets reactive (#6019)

### 🐛 Bug Fixes
- Fix FLOAT widget incrementing broken & disabled state styles on widget
number input (Vue) (#6036)
- Fix Vue node border styles in different states (executing, error,
selected) (#6018)
- Fix Vue node opacity conditions (user node opacity, bypass state,
muted state) (#6022)
- Fix: emit layout change for batch node bounds (#5939)
- Safer restoration of widgets_values on subgraph nodes (#6015)
- Fix(execution): reset progress state after runs to unfreeze tab
title/favicon (main) (#6026)
- Use type check instead of cast (#6041)

### 🎨 Style & Design
- [style] match widget border/outline styles with designs (#6021)
- [style] make Vue widget/slot/label width and spacing align with
designs (#6023)

###  Accessibility
- Add aria labels on vue node widgets (#6032)

### 🔧 Maintenance
- [refactor] adjust Vue node fixtures to not be coupled to Litegraph
(#6033)
- [refactor] reorganize devtools test nodes into modules (#6020)

### 🧪 Testing
- [test] add browser test for control+a selection of Vue nodes (#6031)

### 🔄 CI/CD
- [ci] fix update locales workflow (#6017)

**Full Changelog**:
https://github.com/Comfy-Org/ComfyUI_frontend/compare/v1.29.1...v1.29.2

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6045-1-29-2-28c6d73d3650817a8c36fba944ce69a8)
by [Unito](https://www.unito.io)

---------

Co-authored-by: arjansingh <1598641+arjansingh@users.noreply.github.com>
Co-authored-by: github-actions <github-actions@github.com>
2025-10-13 19:15:14 -07:00
Christian Byrne
95c2732de4 fix ctrl+alt+click to remove link on Vue nodes (#6035)
## Summary

Implements Ctrl+Alt+click batch disconnect functionality for Vue node
output slots to match LiteGraph behavior.

## Changes

- **Feature**: Add Ctrl+Alt+click handler in `useSlotLinkInteraction.ts`
to disconnect all links from output slots
- **Test**: Add test case in `linkInteraction.spec.ts` to verify batch
disconnect behavior
- Follows existing pattern from input slot disconnect implementation

## Implementation Details

The implementation:
- Checks for Ctrl+Alt+click on output slots with existing links
- Calls `resolvedNode.disconnectOutput(index)` to batch disconnect all
links
- Marks canvas as dirty and prevents event propagation
- Matches LiteGraph canvas behavior (`LGraphCanvas.ts:2727-2731`)
- Follows same pattern as existing input slot disconnect (lines 591-611)

Note: Test currently uses `dispatchEvent` for pointerdown with modifiers
and is failing. The feature implementation is correct and matches the
existing codebase patterns, but the test interaction needs debugging.
2025-10-13 18:58:21 -07:00
AustinMroz
e59d2dd8df Use type check instead of cast (#6041)
Under some infrequent circumstances, `audioWidget.value` is not a
string. Presumably if a workflow is loaded with a saved file choice that
does not exist and the value is set to undefined instead.

Instead of a cast, a proper type guard is used and the widget is not
updated if the new value is not a string.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6041-Use-type-check-instead-of-cast-28b6d73d365081249353f4f905769f89)
by [Unito](https://www.unito.io)
2025-10-13 11:52:03 -07:00
Christian Byrne
d54923f766 fix FLOAT widget incrementing broken & disabled state styles on widget number input (Vue) (#6036)
## Summary

Align Vue node number widgets with Figma by centralising button styling
and surfacing disabled-state tokens in the design system.

## Changes

- **What**: Added shared
[`useNumberWidgetButtonPt`](src/renderer/extensions/vueNodes/widgets/composables/useNumberWidgetButtonPt.ts)
helper so both PrimeVue `InputNumber` widgets reuse the same Tailwind
token classes, and added the `color/tokens/alpha` values in
[`packages/design-system/src/css/style.css`](packages/design-system/src/css/style.css#L89-L212)
so semantic aliases remain token-driven ([PrimeVue passthrough
docs](https://www.primefaces.org/primevue/passthrough) w
[`color-mix`](https://www.w3.org/TR/css-color-5/#color-mix))

## Review Focus

Confirm hover/active/disabled colours match the design tokens in both
light and dark themes and that float precision still respects the
safe-range guard.

<img width="1377" height="1150" alt="Screenshot from 2025-10-12
17-53-23"
src="https://github.com/user-attachments/assets/c7d34870-5d07-4ce1-9272-7def7ae813b6"
/>

<img width="1377" height="1150" alt="Screenshot from 2025-10-12
17-53-32"
src="https://github.com/user-attachments/assets/86872ec8-979b-4586-879c-41a126a5f932"
/>

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6036-fix-disabled-state-styles-on-Vue-widget-number-input-INT-and-FLOAT-widgets-28b6d73d365081f8aef7fa860b641f7d)
by [Unito](https://www.unito.io)

---------

Co-authored-by: github-actions <github-actions@github.com>
2025-10-12 23:34:08 -07:00
Christian Byrne
c30f528d11 [refactor] adjust Vue node fixtures to not be coupled to Litegraph (#6033)
## Summary

Changes the Vue node test fixture to not rely on Litegraph internal
objects (which should eventually be fully decoupled from Vue nodes) and
instead interact with nodes using black-box approach that emulates user
actions (preferred appraoch for e2e tests).

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6033-refactor-adjust-Vue-node-fixtures-to-not-be-coupled-to-Litegraph-28a6d73d3650817b8152d27dc4fe0017)
by [Unito](https://www.unito.io)
2025-10-12 19:56:42 -07:00
Christian Byrne
0497421349 add aria labels on vue node widgets (#6032)
## Summary

Adds aria labels to buttons and widgets without pre-existing text
labels.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6032-add-aria-labels-on-vue-node-widgets-28a6d73d36508198a1c0ef7098ad24e8)
by [Unito](https://www.unito.io)
2025-10-12 17:44:03 -07:00
Christian Byrne
01b4ad0dbb [test] add browser test for control+a selection of Vue nodes (#6031)
## Summary

Adds test case ensuring you can select all Vue nodes with `Ctrl`+`a`,
mirroring coverage of similar behavior when using Litegraph nodes.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6031-test-add-browser-test-for-control-a-selection-of-Vue-nodes-28a6d73d365081079860c3a083a946ef)
by [Unito](https://www.unito.io)
2025-10-12 17:01:45 -07:00
Christian Byrne
31c85387ba [style] match widget border/outline styles with designs (#6021)
## Summary

Use semantic color variables from
https://github.com/Comfy-Org/ComfyUI_frontend/pull/6018 on widget
borders to match
[design](https://www.figma.com/design/vALUV83vIdBzEsTJAhQgXq/Comfy-Design-System?node-id=2-5739&m=dev)

The layouting of the widgets doesn't align yet, but it's somewhat
annoying to change the `WidgetSelect` height without using line height.
But, the gap should be 4 (16px) instead of 2, the height of the rows
should be 35px instead of 30px and the widgets should be 32px instead of
30px.

## Before

<img width="2061" height="1386" alt="Screenshot from 2025-10-11
12-23-24"
src="https://github.com/user-attachments/assets/5aa7ba1e-9309-4bd5-95b4-8d8e3d95b50b"
/>

<img width="2061" height="1386" alt="Screenshot from 2025-10-11
12-23-16"
src="https://github.com/user-attachments/assets/9dbabd1b-2174-4dfd-83c2-fef8178c7206"
/>

## After

<img width="2061" height="1386" alt="Screenshot from 2025-10-11
12-23-06"
src="https://github.com/user-attachments/assets/d0b0a611-e65b-462f-ad94-c42639502951"
/>

<img width="2061" height="1386" alt="Screenshot from 2025-10-11
12-22-57"
src="https://github.com/user-attachments/assets/64fb42c8-3d9a-4a2b-956f-482fcd63b64c"
/>

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6021-style-match-widget-border-outline-styles-with-designs-2896d73d365081d18dd9cca41cc2b95e)
by [Unito](https://www.unito.io)

---------

Co-authored-by: github-actions <github-actions@github.com>
2025-10-12 00:32:51 -07:00
AustinMroz
8108aaa2d4 Allow connection to subgraphIOs in vue mode (#6016)
Adds support for link connections from nodes to subgraphInputs and
subgraphOutputs when in vue mode.

![vue-subgraphio](https://github.com/user-attachments/assets/5b1ef66f-d45a-40c7-ace0-932aaf811e1d)

Resolves #5706

Known Issues
- Creating a connection from a widget does not trigger an update of the
widget to the disabled state

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6016-Allow-connection-to-subgraphIOs-in-vue-mode-2896d73d3650816cbd88f645dced87df)
by [Unito](https://www.unito.io)
2025-10-11 23:29:10 -07:00
Christian Byrne
9c245e9c23 Add distribution detection pattern (#6028)
## Summary

Establishes distribution-specific code pattern using compile-time
constants and dead code elimination. Demonstrates with Help Center by
hiding extension manager and update buttons in cloud distribution.

Below commentary makes assumption that minifcation and tree-shaking is
enabled (which isn't true yet, but will be eventually).

## Changes

- **What**: Added `src/platform/distribution/types.ts` with distribution
detection via `__DISTRIBUTION__` variable
- **Build**: Vite replaces `__DISTRIBUTION__` at build time using
environment variables
- **Tree-shaking**: All code not relevant to target distribution is
DCR'd and eliminated from bundle
- **Example**: Help Center hides "Manager Extension" menu item and
"Update" buttons in cloud builds

## Pattern

This PR defines a `__DISTRIBUTION__` variable which gets replaced at
build time by Vite using environment variables. All code not relevant to
the given distribution is then DCR'd and tree-shaken.

For simple cases (like this Help Center PR), import `isCloud` and use
compile-time conditionals:

```typescript
import { isCloud } from '@/platform/distribution/types'

if (!isCloud) {
  items.push({
    key: 'manager',
    action: async () => {
      await useManagerState().openManager({ ... })
    }
  })
}
```

The code is DCR'd at build time so there's zero runtime overhead - we
don't even incur the `if (isCloud)` cost because Terser eliminates it.

For complex services later, we'll add interfaces and use an index.ts
that exports different implementations under the same alias per
distribution. It will resemble a DI container but simpler since we don't
need runtime discovery like backend devs do. This guarantees types and
makes testing easier.

Example for services:
```typescript
// src/platform/storage/index.ts
import { isCloud } from '@/platform/distribution/types'

if (isCloud) {
  export { CloudStorage as StorageService } from './cloud'
} else {
  export { LocalStorage as StorageService } from './local'
}
```

Example for component variants:
```typescript
// src/components/downloads/index.ts
import { isCloud } from '@/platform/distribution/types'

if (isCloud) {
  export { default as DownloadButton } from './DownloadButton.cloud.vue'
} else {
  export { default as DownloadButton } from './DownloadButton.desktop.vue'
}
```

## Implementation Details

Distribution types (`src/platform/distribution/types.ts`):
```typescript
type Distribution = 'desktop' | 'localhost' | 'cloud'

declare global {
  const __DISTRIBUTION__: Distribution
}

const DISTRIBUTION: Distribution = __DISTRIBUTION__
export const isCloud = DISTRIBUTION === 'cloud'
```

Vite configuration adds the define:
```typescript
const DISTRIBUTION = (process.env.DISTRIBUTION || 'localhost') as
  | 'desktop'
  | 'localhost'
  | 'cloud'

export default defineConfig({
  define: {
    __DISTRIBUTION__: JSON.stringify(DISTRIBUTION)
  }
})
```

## Build Commands

```bash
pnpm build                      # localhost (default)
DISTRIBUTION=cloud pnpm build   # cloud
DISTRIBUTION=desktop pnpm build # desktop
```

## Future Applications

This pattern can be used with auth or telemetry services - which will
guarantee all the telemetry code, for example, is not even in the code
distributed in OSS Comfy whatsoever while still being able to develop
off `main`.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6028-Add-distribution-detection-pattern-28a6d73d365081b08767d395472cd1bc)
by [Unito](https://www.unito.io)
2025-10-11 23:10:15 -07:00
AustinMroz
cb40da612b Safer restoration of widgets_values on subgraph nodes (#6015)
Reordering linked widgets requires special attention on load to restore
widgets_values. The method which was merged was optimistic and
insufficient for some rarer edge cases.

Resolves #6014 

Fix was already included in #6009. Backport is not required.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6015-Safer-restoration-of-widgets_values-on-subgraph-nodes-2896d73d3650813a9162e8459e686981)
by [Unito](https://www.unito.io)
2025-10-11 23:09:29 -07:00
Christian Byrne
ddb3a0bfc6 fix Vue node opacity conditions (user node opacity, bypass state, muted state) (#6022)
## Summary

Fixed Vue node opacity calculation to properly combine global opacity
setting with muted/bypassed state opacity.

**Root Cause**: When global opacity setting was added as inline style
(481aa8252), it began overriding CSS `opacity-50` classes due to higher
specificity.

**Solution**: Modified `nodeOpacity` computed property to calculate
effective opacity as `globalOpacity * 0.5` for muted/bypassed states,
removing conflicting CSS classes.

## Changes

- **What**: Fixed [CSS specificity
conflict](https://developer.mozilla.org/en-US/docs/Web/CSS/Specificity)
where inline `opacity` style overrode `opacity-50` classes for
muted/bypassed nodes
- **Breaking**: None - restores intended opacity behavior

## Review Focus

Multiplicative opacity calculation ensuring muted/bypassed nodes apply
0.5 opacity on top of global opacity setting rather than being
overridden by it.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6022-fix-Vue-node-opacity-conditions-user-node-opacity-bypass-state-muted-state-2896d73d365081c290f1da37c195c2f5)
by [Unito](https://www.unito.io)

---------

Co-authored-by: github-actions <github-actions@github.com>
2025-10-11 21:36:50 -07:00
AustinMroz
5773df6ef7 Make nodeData.widgets reactive (#6019)
Makes the litegraph `node.widgets` array `shallowReactive` and makes the
`nodeData.widgets` a `reactiveComputed` derived from the litegraph
widget data.

![reactive-widgets](https://github.com/user-attachments/assets/8eb8d712-8586-4f34-b699-30fc3dc3340b)


Making changes to the structure of litegraph items is somewhat
dangerous, but code search verifies that there are no custom nodes using
`defineProperty` on `node.widgets`

This fixes display of promoted widgets on subgraph node and any custom
nodes that dynamically add or remove widgets.

TODO:
- Investigate occasional dropped widgets.
- Some of this was confusion with `canvasOnly` widgets and widgets not
implemented in vue. Will keep investigating, but I'm not terribly
concerned with actual test cases and it being an objective improvement.
  
Known Issue:
- Node does not grow/shrink to fit changed widgets

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6019-Make-nodeData-widgets-reactive-2896d73d3650815691b6ee370a86a22c)
by [Unito](https://www.unito.io)
2025-10-11 20:32:15 -07:00
Christian Byrne
bc281b2513 [style] make Vue widget/slot/label width and spacing align with designs (#6023)
Make the widths and spacing of the widgets/slots/labels match the
[design](https://www.figma.com/design/31uH3r4x3xbIctuRWYW6NM/V3---Vue-Nodes?node-id=6489-33817&m=dev)
which also better matches the interal layout of litegraph nodes.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6023-style-make-Vue-widget-slot-label-width-and-spacing-align-with-designs-2896d73d365081a1a831f396cb4eafc8)
by [Unito](https://www.unito.io)

---------

Co-authored-by: github-actions <github-actions@github.com>
2025-10-11 20:08:05 -07:00
Benjamin Lu
1d06b4d63b fix(execution): reset progress state after runs to unfreeze tab title/favicon (main) (#6026)
Cherry picked over from #6025, should've been made to target main to
begin with

## Summary
Fixes the browser tab progress and favicon remaining at ~14% after
workflow completion on `main` by resetting execution state when a run
ends (success, error, or interruption).

## Changes
- Add `execution_success` listener in the execution store
- Centralize terminal-state cleanup in `resetExecutionState()`
- Clear `nodeProgressStates`, queued prompt entry,
`_executingNodeProgress`, and set `activePromptId` to `null`
- Ensures `isIdle` becomes `true` post-run so tab title and favicon no
longer freeze mid-progress

resolves #6024

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6026-fix-execution-reset-progress-state-after-runs-to-unfreeze-tab-title-favicon-main-28a6d73d365081f188ebc2e69d936dd9)
by [Unito](https://www.unito.io)
2025-10-11 18:53:58 -07:00
Christian Byrne
14c07fd734 [refactor] reorganize devtools test nodes into modules (#6020)
## Summary

Refactored monolithic devtools node definitions into organized module
structure for better maintainability and separation of concerns.

## Changes

- **What**: Split 700+ line `dev_nodes.py` into modular structure under
`tools/devtools/nodes/` with categorized files: `errors.py`,
`inputs.py`, `models.py`, `remote.py`
- **Dependencies**: None

## Review Focus

Module import structure and ensure all node registrations are properly
preserved in the consolidated mappings.

**Before:**
```
tools/devtools/
├── __init__.py
└── dev_nodes.py (738 lines)
```

**After:**
```
tools/devtools/
├── __init__.py
├── dev_nodes.py (65 lines - imports only)
└── nodes/
    ├── __init__.py (consolidated mappings)
    ├── errors.py (error/debug nodes)
    ├── inputs.py (input/widget nodes)
    ├── models.py (model/patch nodes)
    └── remote.py (remote/combo nodes)
```

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6020-refactor-reorganize-devtools-test-nodes-into-modules-2896d73d365081e89efef7e88ca8fee3)
by [Unito](https://www.unito.io)
2025-10-11 15:29:29 -07:00
Christian Byrne
7cc08e8e35 [ci] fix update locales workflow (#6017)
Similar to https://github.com/Comfy-Org/ComfyUI_frontend/pull/6005,
fixing the update-locales workflow by setting up the frontend before
launching ComfyUI server.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6017-ci-fix-update-locales-workflow-2896d73d36508173aaf9e0eefe4f7660)
by [Unito](https://www.unito.io)
2025-10-11 15:21:34 -07:00
Jin Yi
9c0b3c4f7d Add MediaAssetCard presentation components (#5878)
## Summary

Implements a comprehensive media asset card component system for the
Asset Manager sidebar, enabling display and interaction with various
media types (images, videos, audio, and 3D models).

## Changes

### New Components
- **MediaAssetCard**: Main card component for displaying media assets
- **Media type-specific components**: Specialized display logic for each
media type
  - MediaImageTop/Bottom
  - MediaVideoTop/Bottom  
  - MediaAudioTop/Bottom
  - Media3DTop/Bottom
- **MediaAssetActions**: Top-left action buttons (delete, download, more
options)
- **MediaAssetMoreMenu**: Dropdown menu for additional actions
- **SquareChip**: Chip component for displaying duration and file format
with dark/light variants
- **MediaAssetButtonDivider**: Visual separator for button groups

### Features
- **Video playback**: Autoplay with native video controls
  - Dynamic duration chip positioning based on control visibility
  - Hides overlays when video is playing
- **Audio playback**: Audio icon with HTML5 audio element
  - Duration chip with consistent positioning
- **3D model support**: Icon display for 3D assets
- **Selection state**: Proper hover and selected state handling with CSS
priority fixes

### Architecture Improvements
- **Domain-Driven Design structure**: Organized under
`src/platform/mediaAsset/` following DDD principles
- **Provide/Inject pattern**: Eliminates props drilling with
MediaAssetKey InjectionKey
- **Composable pattern**: `useMediaAssetActions` manages all action
handlers
- **Type safety**: Comprehensive TypeScript types for media assets and
actions

### UI/UX Enhancements
- **CardTop component**: Added custom class props for slot positioning
- **SquareChip component**: Backdrop blur effects with variant system
- **Lazy loading**: Image optimization with LazyImage component
- **Responsive states**: Loading, selected, and hover states

### Utilities
- **formatDuration**: Converts milliseconds to human-readable format
(45s, 1m 23s, 1h 2m)

## Testing
- Comprehensive Storybook stories for all media types
- Grid layout examples
- Loading and selected state demonstrations

## File Structure
```
src/platform/assets/
├── components/
│   ├── MediaAssetCard.vue
│   ├── MediaAssetCard.stories.ts
│   ├── MediaAssetActions.vue
│   ├── MediaAssetMoreMenu.vue
│   ├── MediaAssetButtonDivider.vue
│   ├── MediaImageTop.vue
│   ├── MediaImageBottom.vue
│   ├── MediaVideoTop.vue
│   ├── MediaVideoBottom.vue
│   ├── MediaAudioTop.vue
│   ├── MediaAudioBottom.vue
│   ├── Media3DTop.vue
│   └── Media3DBottom.vue
├── composables/
│   └── useMediaAssetActions.ts
└── schemas/
    └── mediaAssetSchema.ts
```

## Screenshots

[media_asset_record.webm](https://github.com/user-attachments/assets/d13b5cc0-a262-4850-bb81-ca1daa0dd969)

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: GitHub Action <action@github.com>
Co-authored-by: github-actions <github-actions@github.com>
2025-10-11 14:39:04 -07:00
Christian Byrne
bb83b0107c fix Vue node border styles in different states (executing, error, selected) (#6018)
- Use exact tokens from Figma
- Fix issue in which node is stuck in `executing` state after it errors

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6018-fix-Vue-node-border-styles-in-different-states-executing-error-selected-2896d73d365081f39000fc3e42811f0d)
by [Unito](https://www.unito.io)
2025-10-11 12:20:06 -07:00
Christian Byrne
a0c02dfca6 make Vue nodes' outputs/previews responsively sized and work with node resizing (#5970)
## Summary

Added dedicated component for sampling previews and change all image
outputs (outputs, videos, previews) to be responsive and respond to node
resizing.



https://github.com/user-attachments/assets/7e683d32-4914-460c-ba08-4573c40aef24

## Changes

- **What**: Implemented `LivePreview` component for mid-execution
sampling visualization with responsive layout system
- **Dependencies**: Added resize handle composable and transform state
integration

## Review Focus

Node resize interaction conflicts with canvas dragging, and image
dimension calculation performance during rapid sampling updates.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5970-make-Vue-nodes-outputs-previews-responsively-sized-and-work-with-node-resizing-2866d73d365081508d53e6e286a9a3fe)
by [Unito](https://www.unito.io)

---------

Co-authored-by: DrJKL <DrJKL@users.noreply.github.com>
Co-authored-by: GitHub Action <action@github.com>
Co-authored-by: github-actions <github-actions@github.com>
2025-10-10 21:52:24 -07:00
Christian Byrne
e6534f17e6 fix: emit layout change for batch node bounds (#5939)
## Summary

Fixes issue where node size changes are not serialized by routing
DOM-driven node bounds updates through a single CRDT operation so Vue
node geometry stays synchronized with LiteGraph.

## Changes

- **What**: Added `BatchUpdateBoundsOperation` to the layout store,
applied it via the existing Yjs pipeline, notified link sync to
recompute touched nodes, and covered the path with a regression test

## Review Focus

Correctness of the new batch operation when multiple nodes update
simultaneously, especially remote replay/undo scenarios and link
geometry recomputation.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5939-fix-emit-layout-change-for-batch-node-bounds-2846d73d365081db8f8cca5bf7b85308)
by [Unito](https://www.unito.io)

---------

Co-authored-by: github-actions <github-actions@github.com>
2025-10-10 20:47:12 -07:00
Comfy Org PR Bot
7e3c04399a 1.29.1 (#6012)
## What's Changed

### 🚀 Features
- Implement DOMWidget for vue (#6006)
- Implement drop-on-canvas + linkconnectoradapter consolidation (#5898)
- Vuenodes/audio widgets (#5627)
- Allow reordering of linked subgraph widgets (#5981)
- Contextmenu Monkeypatch Standardization (#5977)
- Fix/vue nodes snap to grid (#5973)
- Select Vue Nodes After Drag (#5863)
- fix Vue node widgets should be in disabled state if their slots are
connected with a link (#5834)

### 🐛 Bug Fixes
- [bugfix] Fix update-playwright-expectations workflow missing frontend
build (#6005)
- Fix: Reset size when collapsing (#6004)
- fix: misc LOD polish (#6001)
- Fix: Allow uncoloring Vue Nodes (#5991)
- [ci] Fix detached HEAD state in Playwright update workflow (#5985)
- Close zoom menu when toggling minimap visibility (#5974)

### 🔧 Maintenance
- Devex: Improve dev server (#6002)
- CI: Add concurrency checks to PR workflows (#6000)
- [feat] Auto-remove New Browser Test Expectations label after workflow
completes (#5998)
- CI: Simplify update playwright expectations (maybe) (#5994)
- Lint: Add tailwind linter (#5984)
- [feat] Auto-remove claude-review label after CI review completes
(#5983)
- Fix CI: Remove explicit repository parameter causing non-reproducible
test results (#5950)
- refactor: Reorganize GitHub Actions for better reusability (#5949)
- devex: Update CODEOWNERS (#5999)
- Docs: Update agent instructions about style classes (#5990)
- Style: Fix move cursors that should be grabs (#5989)
- Workflow templates review (#5975)

**Full Changelog**:
https://github.com/Comfy-Org/ComfyUI_frontend/compare/v1.29.0...v1.29.1

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6012-1-29-1-2896d73d365081b08418f46934651c41)
by [Unito](https://www.unito.io)

Co-authored-by: arjansingh <1598641+arjansingh@users.noreply.github.com>
2025-10-10 20:29:49 -07:00
AustinMroz
2599136296 Implement DOMWidget for vue (#6006)
![vue-dom-widget](https://github.com/user-attachments/assets/d0c0e5f6-bacb-4fd9-957e-4f19e8071c3d)

Did testing on about a dozen custom nodes. Most just work.
- Some custom nodes have copy/pasted the `addDOMWidget` call with types
like `customtext` and get converted to textareas -> Not feasible to fix
here. Can open PRs into custom nodes if complaints arise.
- Only the KJNodes spline editor had mouse issues -> Can
investigate/open PR into KJNodes later.
- Many nodes don't resize gracefully. Probably best handled in a future
PR.
- Some expect to be handled like textareas. These currently have minsize
and don't scale.
- Others, like VHS previews, scale self properly, but don't update
height inside a drag operation -> node height can be set to less than
fit.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6006-Implement-DOMWidget-for-vue-2886d73d3650817ca497c15d87d70f4f)
by [Unito](https://www.unito.io)
2025-10-10 14:11:38 -07:00
Johnpaul Chiwetelu
d7796fcda4 Vuenodes/audio widgets (#5627)
This pull request introduces a new audio playback widget for node UIs
and integrates it into the node widget system. The main changes include
the implementation of the `WidgetAudioUI` component, its registration in
the widget registry, and updates to pass node data to the new widget.
Additionally, some logging was added for debugging purposes.

**Audio Widget Implementation and Integration:**

* Added a new `WidgetAudioUI.vue` component that provides audio playback
controls (play/pause, progress slider, volume, options) and loads audio
files from the server based on node data.
* Registered the new `WidgetAudioUI` component in the widget registry by
importing it and adding an entry for the `audioUI` type.
[[1]](diffhunk://#diff-c2a60954f7fdf638716fa1f83e437774d5250e9c99f3aa83c84a1c0e9cc5769bR21)
[[2]](diffhunk://#diff-c2a60954f7fdf638716fa1f83e437774d5250e9c99f3aa83c84a1c0e9cc5769bR112-R115)
* Updated `NodeWidgets.vue` to pass `nodeInfo` as the `node-data` prop
to widgets of type `audioUI`, enabling the widget to access
node-specific audio file information.

**Debugging and Logging:**

* Added logging of `nodeData` in `LGraphNode.vue` and
`WidgetAudioUI.vue` to help with debugging and understanding the data
structure.
[[1]](diffhunk://#diff-a7744614cf842e54416047326db79ad81f7c7ab7bfb66ae2b46f5c73ac7d47f2R188-R189)
[[2]](diffhunk://#diff-71cce190d74c6b5359288857ab9917caededb8cdf1a7e6377578b78aa32be2fcR1-R284)

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5627-Vuenodes-audio-widgets-2716d73d365081fbbc06c1e6cf4ebf4d)
by [Unito](https://www.unito.io)

---------

Co-authored-by: GitHub Action <action@github.com>
Co-authored-by: Arjan Singh <1598641+arjansingh@users.noreply.github.com>
Co-authored-by: filtered <176114999+webfiltered@users.noreply.github.com>
Co-authored-by: Christian Byrne <cbyrne@comfy.org>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Alexander Brown <drjkl@comfy.org>
Co-authored-by: Jin Yi <jin12cc@gmail.com>
Co-authored-by: DrJKL <DrJKL@users.noreply.github.com>
Co-authored-by: Robin Huang <robin.j.huang@gmail.com>
Co-authored-by: github-actions <github-actions@github.com>
2025-10-09 21:29:06 -07:00
Benjamin Lu
4404c0461d Implement drop-on-canvas + linkconnectoradapter consolidation (#5898)
Implements droponcanvas functionality and a linkconnectoradapter
refactor.

- Drop on canvas (Shift and default) integrated via LinkConnector
‘dropped-on-canvas’ with proper CanvasPointerEvent.
- LinkConnector adapter: now wraps the live canvas linkConnector (no
duplicate state); added dropOnCanvas() helper.
- Tests: Playwright scenarios for Shift-drop context menu/searchbox,
pinned endpoint, type prefilter, and post-selection auto-connect
(browser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts).

There are some followup PRs that will fix/refactor some more noncritical
things, like the terrible slotid, the number/string nodeid confusion,
etc.

https://github.com/Comfy-Org/ComfyUI_frontend/pull/5780 (snapping) <--
https://github.com/Comfy-Org/ComfyUI_frontend/pull/5898 (drop on canvas
+ linkconnectoradapter refactor) <--
https://github.com/Comfy-Org/ComfyUI_frontend/pull/5903 (fix reroute
snapping)

---------

Co-authored-by: github-actions <github-actions@github.com>
Co-authored-by: Christian Byrne <cbyrne@comfy.org>
Co-authored-by: GitHub Action <action@github.com>
2025-10-09 20:55:30 -07:00
Johnpaul Chiwetelu
4cb03cf052 Select Vue Nodes After Drag (#5863)
This pull request refactors the node selection logic in the Vue nodes
event handler composable to simplify the function signature and improve
single vs. multi-selection behavior. The main change is the removal of
the `wasDragging` parameter from the `handleNodeSelect` function, with
selection logic now determined by the current selection state. Related
test code is updated to match the new function signature.

**Node selection logic improvements:**

* Refactored the `handleNodeSelect` function in
`useNodeEventHandlersIndividual` to remove the `wasDragging` parameter,
making the function signature simpler and relying on selection state to
handle single vs. multi-selection.
* Updated the selection logic to check if multiple nodes are already
selected using `isLGraphNode`, and only perform single selection if not.

**Code and test updates:**

* Updated all calls to `handleNodeSelect` in the composable to remove
the `wasDragging` argument, ensuring consistent usage throughout the
codebase.
[[1]](diffhunk://#diff-8d3820a1ca9c569bce00671fdd6290af81315ae11b8f3d6f29a5a9d30379d084L125-R123)
[[2]](diffhunk://#diff-8d3820a1ca9c569bce00671fdd6290af81315ae11b8f3d6f29a5a9d30379d084L146-R144)
[[3]](diffhunk://#diff-8d3820a1ca9c569bce00671fdd6290af81315ae11b8f3d6f29a5a9d30379d084L173-R171)
* Updated all related test cases to use the new `handleNodeSelect`
signature without the third parameter.
[[1]](diffhunk://#diff-89bfc2a05201c6ff7116578efa45f96097594eb346f18446c70aa7125ab1811aL105-R105)
[[2]](diffhunk://#diff-89bfc2a05201c6ff7116578efa45f96097594eb346f18446c70aa7125ab1811aL125-R125)
[[3]](diffhunk://#diff-89bfc2a05201c6ff7116578efa45f96097594eb346f18446c70aa7125ab1811aL144-R144)
[[4]](diffhunk://#diff-89bfc2a05201c6ff7116578efa45f96097594eb346f18446c70aa7125ab1811aL162-R162)
[[5]](diffhunk://#diff-89bfc2a05201c6ff7116578efa45f96097594eb346f18446c70aa7125ab1811aL174-R174)
[[6]](diffhunk://#diff-89bfc2a05201c6ff7116578efa45f96097594eb346f18446c70aa7125ab1811aL187-R187)

**Utility import:**

* Added an import for `isLGraphNode` from `@/utils/litegraphUtil` to
support the updated selection logic.## Summary

<!-- One sentence describing what changed and why. -->


## Screenshots (if applicable)



https://github.com/user-attachments/assets/71e856d3-afc2-497d-826e-5b485066e7fe

---------

Co-authored-by: github-actions <github-actions@github.com>
2025-10-09 20:48:03 -07:00
Johnpaul Chiwetelu
eeb0977738 Contextmenu Monkeypatch Standardization (#5977)
This pull request introduces a new extension API for context menu
customization, allowing extensions to contribute items to both canvas
and node right-click menus. It adds two collection methods to the
`ComfyApp` class to aggregate these menu items from all registered
extensions, and updates the extension interface accordingly.
Comprehensive unit tests are included to verify the correct aggregation
behavior and error handling.

**Extension API for Context Menus:**

* Added optional `getCanvasMenuItems` and `getNodeMenuItems` methods to
the `ComfyExtension` interface, enabling extensions to provide context
menu items for canvas and node right-click menus (`src/types/comfy.ts`).

* Updated type imports to support the new API, including
`IContextMenuValue`, `LGraphCanvas`, and `LGraphNode`
(`src/types/comfy.ts`, `src/scripts/app.ts`).
[[1]](diffhunk://#diff-c29886a1b0c982c6fff3545af0ca8ec269876c2cf3948f867d08c14032c04d66L1-R5)
[[2]](diffhunk://#diff-bde0dce9fe2403685d27b0e94a938c3d72824d02d01d1fd6167a0dddc6e585ddR10)

**Core Implementation:**

* Implemented `collectCanvasMenuItems` and `collectNodeMenuItems`
methods in the `ComfyApp` class to gather menu items from all
extensions, with robust error handling and logging for extension
failures (`src/scripts/app.ts`).

**Testing:**

* Added a comprehensive test suite for the new context menu extension
API, covering aggregation logic, error handling, and integration
scenarios (`tests-ui/tests/extensions/contextMenuExtension.test.ts`).

This is PR 1 of the 3 PRs in the Contextmenu standardizations.
-https://github.com/Comfy-Org/ComfyUI_frontend/pull/5992
-https://github.com/Comfy-Org/ComfyUI_frontend/pull/5993
2025-10-09 18:37:41 -07:00
Christian Byrne
9a505100ac [bugfix] Fix update-playwright-expectations workflow missing frontend build (#6005)
## Problem

The `update-playwright-expectations.yaml` workflow was failing with:
```
error: argument --front-end-root: The path '../dist' does not exist.
```

This was happening because the workflow was trying to launch the ComfyUI
server with `--front-end-root ../dist` before building the frontend.

## Root Cause

The workflow was missing the frontend build step entirely. It went
directly from checkout → setup server with `launch_server: true` → run
tests, skipping the crucial frontend build.

## Solution

1. Remove `launch_server: true` from `setup-comfyui-server` action call
2. Add `setup-frontend` action with `include_build_step: true` to build
the frontend
3. Add separate "Launch ComfyUI Server" step that runs AFTER frontend is
built

This ensures the `dist/` directory exists before the server tries to use
it.

## Testing

This fixes errors seen on PR #5863 and any PR using the
`/update-playwright` comment trigger.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6005-bugfix-Fix-update-playwright-expectations-workflow-missing-frontend-build-2876d73d36508182bb1af1123f3b2a87)
by [Unito](https://www.unito.io)
2025-10-09 15:43:54 -07:00
Alexander Brown
21873d40d5 Devex: Improve dev server (#6002)
## Summary

Keep Vite from watching extra files, should cut down on the amount of
times it tries to reload and hopefully fix a file contention issue with
git.
https://vite.dev/config/server-options.html#server-watch

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6002-Devex-Improve-dev-server-2876d73d365081a09417c2bf17da659f)
by [Unito](https://www.unito.io)
2025-10-09 15:07:02 -07:00
Simula_r
cbbbadf438 fix: misc LOD polish (#6001)
## Summary

Fix NodeHeader styes when LOD and prevent unwanted pointer-events at
LOD.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6001-fix-misc-LOD-polish-2876d73d365081efb5d6fcaf5815eee6)
by [Unito](https://www.unito.io)
2025-10-09 14:58:13 -07:00
Alexander Brown
d2972220bb Fix: Reset size when collapsing (#6004)
## Summary

Minimal initial fix to allow resized nodes to collapse.
Does not retain the size across collapse/expand.

## Screenshots (if applicable)


https://github.com/user-attachments/assets/bd6bf496-eb58-4f48-b5dc-b388f20ed0d9


┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6004-Fix-Reset-size-when-collapsing-2876d73d365081d7886cc4b708adddd6)
by [Unito](https://www.unito.io)
2025-10-09 14:51:14 -07:00
AustinMroz
4e08ed64f0 Allow reordering of linked subgraph widgets (#5981)
Widgets which are promoted by linking to a subgraphInput node are now
also displayed in the subgraph configuration window. They can now be
reordered by drag and drop along with proxyWidgets


![linked-reorder](https://github.com/user-attachments/assets/e1b8d590-211a-4d84-9f84-3a5fd5a7aa6c)

Known Issues:
- "Hide All" will incorrectly remove physically linked widgets

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5981-Allow-reordering-of-linked-subgraph-widgets-2866d73d365081d9b27cf4a9c3078007)
by [Unito](https://www.unito.io)
2025-10-09 13:50:15 -07:00
Alexander Brown
13db1e484b CI: Add concurrency checks to PR workflows (#6000)
## Summary


https://docs.github.com/en/actions/reference/workflows-and-actions/workflow-syntax#example-only-cancel-in-progress-jobs-or-runs-for-the-current-workflow

## Changes

- **What**: Will cancel ongoing checks when new commits are pushed to
the PR branch

## Review Focus

What other optimizations could we make?

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6000-CI-Add-concurrency-checks-to-PR-workflows-2876d73d3650813cbb65eb8c397ac748)
by [Unito](https://www.unito.io)
2025-10-09 13:28:38 -07:00
Alexander Brown
8b7bc5eb89 devex: Update CODEOWNERS (#5999)
## Summary

Exempts the instructions files from CODEOWNERS

## Review Focus

Should we have specific owners for these files?

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5999-devex-Update-CODEOWNERS-2876d73d365081dc8ed1e7cdd350cd36)
by [Unito](https://www.unito.io)
2025-10-09 12:21:49 -07:00
snomiao
fd474fe2aa refactor: Reorganize GitHub Actions for better reusability (#5949)
## Summary

This PR refactors the GitHub Actions workflow structure to improve
reusability, maintainability, and CI performance.

## Changes

### New Actions
- **setup-comfyui-server**: New composite action that handles ComfyUI
server setup and launch
  - Checks out ComfyUI repository
  - Installs ComfyUI_devtools custom node
  - Sets up Python environment and dependencies
  - Optionally launches the server with configurable parameters

### Refactored Actions
- **setup-frontend**: Simplified to focus only on frontend-specific
tasks
  - Installs pnpm and Node.js
  - Installs dependencies
- Optionally builds the frontend (can be skipped when using cached
builds)
  - No longer handles server setup or checkout

### Workflow Improvements

#### tests-ci.yaml
- Introduced a setup job that builds once and caches the entire
workspace
- Test jobs now restore the cached workspace instead of rebuilding
- Eliminated redundant setup steps in each test shard
- Better separation between setup and test execution phases
- Significant performance improvement through workspace caching

#### Locale Update Workflows
- Updated `update-locales.yaml` to use the new action structure
- Updated `update-locales-for-given-custom-node-repository.yaml` with
proper custom node installation
- Updated `update-node-definitions-locales.yaml` to use new actions
- Removed `working-directory` references where appropriate

#### Other Workflows
- Updated `update-playwright-expectations.yaml` to use new action
structure
- Consistent action usage across all workflows

## Benefits

1. **Better Performance**: Workspace caching eliminates redundant builds
in CI, significantly reducing test execution time
2. **Improved Maintainability**: Clear separation of concerns makes
actions easier to understand and modify
3. **Enhanced Reusability**: Actions can be composed in different ways
for different workflows
4. **DRY Principle**: Eliminated code duplication across workflows
5. **Easier Debugging**: Smaller, focused actions make it easier to
identify and fix issues

## Testing

- [ ] Verify tests-ci workflow runs successfully
- [ ] Verify locale update workflows function correctly
- [ ] Verify playwright expectations update workflow works
- [ ] Confirm cache/restore mechanism works as expected

## Related Issues

This refactoring addresses workflow complexity and reduces CI runtime by
leveraging GitHub Actions caching more effectively.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5949-refactor-Reorganize-GitHub-Actions-for-better-reusability-2846d73d365081ae8e16f151423b5a88)
by [Unito](https://www.unito.io)

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Alexander Brown <drjkl@comfy.org>
Co-authored-by: DrJKL <DrJKL0424@gmail.com>
2025-10-09 19:21:10 +00:00
Alexander Brown
b6b6455189 CI: Simplify update playwright expectations (maybe) (#5994)
## Summary

Follow-up to https://github.com/Comfy-Org/ComfyUI_frontend/pull/5985
See if it's possible to reduce the branching, maybe add reactions and a
Done comment?

## Changes

- **What**: Snapshot Update Updates

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5994-CI-Simplify-update-playwright-expectations-maybe-2876d73d365081eab031d8301c1360b7)
by [Unito](https://www.unito.io)
2025-10-09 11:32:38 -07:00
Simula_r
1455845a30 Fix/vue nodes snap to grid (#5973)
## Summary

Enable node snap to grid in vue nodes mirroring the same behavior as
litegraph.

- Show node snap preview (semi transparent white box target behind node)
- Resize snap to grid
- Shift + drag / Auto snap 
- Multi select + group snap

## Changes

- **What**: useNodeSnap.ts useShifyKeySync.ts setups the core hooks into
both the vue node positioning/resizing system and the event forwarding
technique for communicating to litegraph.

## Review Focus

Both new composables and specifically the useNodeLayout modifications to
batch the mutations when snapping.
A key tradeoff/note is why we are using the useShifyKeySync.ts which
dispatches a new shift event to the canvas layer. This approach is the
cleaner / more declaritive method mimicking how other vue node ->
litegraph realtime events are passed.

<!-- If this PR fixes an issue, uncomment and update the line below -->
<!-- Fixes #ISSUE_NUMBER -->

## Screenshots (if applicable)

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

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5973-Fix-vue-nodes-snap-to-grid-2866d73d365081c1a058d223c8c52576)
by [Unito](https://www.unito.io)
2025-10-09 11:27:18 -07:00
Christian Byrne
6b3a4d214b [feat] Auto-remove New Browser Test Expectations label after workflow completes (#5998)
## Summary
Automatically removes the `New Browser Test Expectations` label after
the Playwright expectations update workflow completes.

## Changes
- Added a cleanup step to
`.github/workflows/update-playwright-expectations.yaml` that removes the
label using `gh pr edit --remove-label`
- Uses `if: always() && github.event_name == 'pull_request'` to ensure:
  - The label is removed even if the workflow fails
- The label is only removed when triggered by the label event (not the
`/update-playwright` comment trigger)

## Benefits
- Cleaner PR label management
- Labels can be re-applied to trigger additional expectations updates
without manual cleanup
- Consistent with the claude-review workflow pattern
- Reduces noise in the PR interface

## Context
This is part of a broader effort to automatically clean up temporary
action-triggering labels across all workflows. The first PR in this
series (#5983) added the same functionality to the claude-review
workflow.

## Test Plan
- Apply the `New Browser Test Expectations` label to a PR to verify the
workflow removes it automatically after completion

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5998-feat-Auto-remove-New-Browser-Test-Expectations-label-after-workflow-completes-2876d73d365081e29fbbe6e3127ca973)
by [Unito](https://www.unito.io)
2025-10-09 11:08:48 -07:00
Christian Byrne
06b0eecfe4 fix Vue node widgets should be in disabled state if their slots are connected with a link (#5834)
## Summary

Fixes https://github.com/Comfy-Org/ComfyUI_frontend/issues/5692 by
making widget link connection status trigger on change so Vue widgets
with connected links could properly switch to the `disabled` state when
they are implicitly converted to inputs.

## Changes

- **What**: Added `node:slot-links:changed` event tracking and reactive
slot data synchronization for Vue widgets

```mermaid
graph TD
    A[Widget Link Change] --> B[NodeInputSlot.link setter]
    B --> C{Is Widget Input?}
    C -->|Yes| D[Trigger slot-links:changed]
    C -->|No| E[End]
    D --> F[Graph Event Handler]
    F --> G[syncNodeSlotData]
    G --> H[Update Vue Reactive Data]
    H --> I[Widget Re-render]
    
    style A fill:#f9f9f9,stroke:#333,color:#000
    style I fill:#f9f9f9,stroke:#333,color:#000
```

## Review Focus

Widget reactivity performance with frequent link changes and event
handler memory management in graph operations.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5834-fix-Vue-node-widgets-should-be-in-disabled-state-if-their-slots-are-connected-with-a-link-27c6d73d365081f6a6c3c1ddc3905c5e)
by [Unito](https://www.unito.io)
2025-10-09 10:30:12 -07:00
Christian Byrne
b222cae56e [ci] Fix detached HEAD state in Playwright update workflow (#5985)
## Summary

Fixes the Playwright update workflow broken by #5960. When triggered by
adding the "New Browser Test Expectations" label, the workflow was left
in a detached HEAD state, causing `git push` to fail.

## Changes

- **Restores branch checkout for label triggers**: Uses
`github.head_ref` to fetch and checkout the branch when triggered by
`pull_request` events
- **Preserves comment trigger functionality**: Keeps `gh pr checkout`
for `issue_comment` events using `github.event.issue.number`
- **Event-specific push logic**: Uses explicit `git push origin HEAD:${{
github.head_ref }}` for label triggers, plain `git push` for comment
triggers

## Root Cause

PR #5960 removed the original branch checkout logic:
```yaml
git fetch origin ${{ github.head_ref }}
git checkout -B ${{ github.head_ref }} origin/${{ github.head_ref }}
git push origin HEAD:${{ github.head_ref }}
```

This left the label-triggered workflow in detached HEAD after
`actions/checkout@v5`, breaking the push step.

## Testing

This fix properly uses `github.head_ref` only when it's available
(`pull_request` events) and `github.event.issue.number` only for
`issue_comment` events where `head_ref` isn't available.

Fixes #5960

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5985-ci-Fix-detached-HEAD-state-in-Playwright-update-workflow-2866d73d36508183b63bca03a40da4a8)
by [Unito](https://www.unito.io)
2025-10-08 23:58:23 -07:00
filtered
8188029c6c Close zoom menu when toggling minimap visibility (#5974)
## Summary

Closes the zoom menu popup when clicking show/hide minimap to prevent
the menu from remaining open after toggling.

## Changes

- **What**: Adds `close` event emission from `ZoomControlsModal` when
minimap toggle is clicked, wired to `hideModal` in parent
`GraphCanvasMenu`
- **Tests**: Adds unit tests verifying close behavior for minimap toggle
vs other commands

## Review Focus

This fixes the immediate UX issue where the zoom popup remained open
after toggling minimap visibility. However, the minimap toggle's
placement within the zoom menu is **not** ideal—it's not intuitive to
look for minimap controls within zoom controls. This PR addresses the
current UX friction without tackling the broader discoverability issue.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5974-Close-zoom-menu-when-toggling-minimap-visibility-2866d73d365081bdbb0bfeb0da4b8c2b)
by [Unito](https://www.unito.io)

---------

Co-authored-by: DrJKL <DrJKL0424@gmail.com>
2025-10-09 04:51:10 +00:00
Alexander Brown
b943c0fa75 Lint: Add tailwind linter (#5984)
## Summary

Adds the [tailwind lint
plugin](https://github.com/francoismassart/eslint-plugin-tailwindcss/?tab=readme-ov-file#eslint-plugin-tailwindcss)
and fixes the currently fixable rules ([v4 is still in
beta](https://github.com/francoismassart/eslint-plugin-tailwindcss/?tab=readme-ov-file#about-tailwind-css-4-support)).

## Changes

- **What**: Enforces things like consistent class order, and eventually
can prohibit extra classes that could be utilities instead
- **Dependencies**: The plugin and its types

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5984-Lint-Add-tailwind-linter-2866d73d365081d89db0d998232533bb)
by [Unito](https://www.unito.io)

---------

Co-authored-by: GitHub Action <action@github.com>
2025-10-08 19:39:14 -07:00
Alexander Brown
c9da8b200d Fix: Allow uncoloring Vue Nodes (#5991)
## Summary

Fixes an issue where trying to uncolor a node broke the vue color
syncing.

## Changes

- **What**: Changes litegraph property removal from `delete` to `=
undefined`

## Screenshots

### Before 


https://github.com/user-attachments/assets/81a1ad40-ba5d-4dec-8f90-5b61eb804a16

### After


https://github.com/user-attachments/assets/459d2d15-c728-49d2-abd9-6e255e5383e5

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5991-Fix-Allow-uncoloring-Vue-Nodes-2876d73d365081f4a74fc9fa423aae1c)
by [Unito](https://www.unito.io)
2025-10-08 19:32:32 -07:00
Alexander Brown
9f0fa7202d Docs: Update agent instructions about style classes (#5990)
## Summary

Very small change to help the LLMs follow the new patterns.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5990-Docs-Update-agent-instructions-about-style-classes-2876d73d365081339dbddf22d22947e7)
by [Unito](https://www.unito.io)
2025-10-08 18:59:36 -07:00
Alexander Brown
eda781ad37 Style: Fix move cursors that should be grabs (#5989)
## Summary

Align with designs that use `grab`/`grabbing` instead of `move`.

## Review Focus

Additionally
- Fixes the use of `@apply` in the places I touched
- Removed some `style.css` rules that were always overridden by the
component

## Screenshots

### Before 


https://github.com/user-attachments/assets/9ca65b92-33e5-4feb-853c-9c5ece574ab5


### After


https://github.com/user-attachments/assets/51569025-0156-473e-be93-5662c345901b



┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5989-Style-Fix-move-cursors-that-should-be-grabs-2876d73d3650813bbe95c761c5d46e03)
by [Unito](https://www.unito.io)
2025-10-08 17:48:26 -07:00
Christian Byrne
ec3a77355f [feat] Auto-remove claude-review label after CI review completes (#5983)
## Summary
Automatically removes the `claude-review` label after the Claude PR
review workflow completes, regardless of success or failure.

## Changes
- Added a cleanup step to `.github/workflows/claude-pr-review.yml` that
removes the label using `gh pr edit --remove-label`
- Uses `if: always()` to ensure the label is removed even if the review
fails
- This prevents label accumulation and allows the label to be re-applied
for additional reviews

## Benefits
- Cleaner PR label management
- Labels can be re-applied to trigger additional reviews without manual
cleanup
- Reduces noise in the PR interface

## Test Plan
- Apply the `claude-review` label to this PR to verify the workflow
removes it automatically after completion

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5983-feat-Auto-remove-claude-review-label-after-CI-review-completes-2866d73d365081da929cd393996010e1)
by [Unito](https://www.unito.io)
2025-10-08 14:31:54 -07:00
Johnpaul Chiwetelu
c56fff0b8b Workflow templates review (#5975)
This pull request introduces improvements to the workflow template
selector and search box components, focusing on better user experience
and more accurate terminology. The most significant changes include
adding debounced search input handling, updating sorting option labels,
and refining UI styling for consistency.

**Search functionality improvements:**
* Refactored `SearchBox.vue` to use an internal search query state and a
debounced update mechanism, reducing unnecessary parent updates and
improving responsiveness. The parent model is updated only after the
user stops typing for 300ms. (`src/components/input/SearchBox.vue`)
[[1]](diffhunk://#diff-08f3b0c51fbfe63171509b9944bf7558228f6c2596a1ef5338e88ab64585791bL6-R6)
[[2]](diffhunk://#diff-08f3b0c51fbfe63171509b9944bf7558228f6c2596a1ef5338e88ab64585791bR39-R62)
* Updated the search box in `WorkflowTemplateSelectorDialog.vue` to use
the new debounced search model and increased its size for better
visibility.
(`src/components/custom/widget/WorkflowTemplateSelectorDialog.vue`)

**Sorting and terminology updates:**
* Changed sorting option labels to use more precise terminology, such as
"VRAM Usage (Low to High)" and added new locale strings for sorting
options.
(`src/components/custom/widget/WorkflowTemplateSelectorDialog.vue`,
`src/locales/en/main.json`)
[[1]](diffhunk://#diff-2c860bdc48e907b1b85dbef846599d8376dd02cff90f49e490eebe61371fecedL623-R623)
[[2]](diffhunk://#diff-bbf3da78aeff5b4d868a17a6960d109cb0627316cda2f9b5fa7c08e9abd93be6L1032-R1035)

**UI and styling adjustments:**
* Adjusted the width of the sorting dropdown for better alignment and
consistency.
(`src/components/custom/widget/WorkflowTemplateSelectorDialog.vue`)
* Updated active navigation item background color for improved visual
clarity. (`src/components/widget/nav/NavItem.vue`)

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5975-Workflow-templates-review-2866d73d365081419257f9df2bab9c5b)
by [Unito](https://www.unito.io)



https://github.com/user-attachments/assets/4f72d515-f114-4cd4-8a76-6abbe906e5bb
2025-10-08 14:06:03 -07:00
snomiao
87f5480462 Fix CI: Remove explicit repository parameter causing non-reproducible test results (#5950)
## Problem

Our CI tests were experiencing non-reproducible results where:
- Tests would pass on a PR initially  
- The same PR would fail later when main HEAD changed
- Screenshot comparisons showed excessive differences between expected
vs actual
- Blake identified: *"tests are not reproducible inside a branch - they
change every time main HEAD changes"*

## Root Cause

The issue was caused by **explicit `repository` parameters** in our
`actions/checkout` steps:

```yaml
- name: Checkout ComfyUI_frontend
  uses: actions/checkout@v5
  with:
    repository: 'Comfy-Org/ComfyUI_frontend'  # ← This was the problem!
    path: 'ComfyUI_frontend'
```

According to GitHub Actions documentation:

> **When checking out the repository that triggered a workflow, `ref`
defaults to the reference or SHA for that event. Otherwise, uses the
default branch.**

When you specify an explicit `repository` parameter (even if it's the
same repo), GitHub Actions treats it as "otherwise" and defaults to the
**main branch** instead of using the **PR context**.

## The Fix

Remove the explicit `repository` parameter when checking out the same
repository:

```yaml
- name: Checkout ComfyUI_frontend
  uses: actions/checkout@v5
  with:
    path: 'ComfyUI_frontend'  # No repository parameter = uses PR context
```

## Changes Made

-  Removed `repository: 'Comfy-Org/ComfyUI_frontend'` from setup job
checkout
-  Removed `repository: 'Comfy-Org/ComfyUI_frontend'` from
merge-reports job checkout
-  Updated setup-frontend action to use `actions/checkout@v5` for
consistency
-  Simplified workflow by removing unnecessary `ref` and `fetch-depth`
parameters

## How This Fixes the Problem

**Before:** 
- Setup job checked out main branch (due to explicit repository)
- Tests ran PR code against main branch snapshots
- Results varied based on what was in main at the time

**After:**
- Setup job checks out PR merge commit (natural PR context)
- Tests run PR code against PR snapshots  
- Results are consistent and reproducible

## Why It Worked Before (Sometimes)

The explicit `repository` parameter has been there for a long time, but
the issue became more apparent recently due to:
1. GitHub Actions behavior changes over time
2. Increased frequency of main branch updates
3. More sensitive screenshot comparison tests
4. Complex cache/restore workflow where timing mattered

The fix ensures deterministic behavior regardless of GitHub's internal
changes.

## Testing

This change makes the CI behavior explicit and predictable:
-  PR tests will always use PR context
-  Push tests will always use pushed commit  
-  No dependency on GitHub's default behavior interpretation
-  Simplified workflow with fewer moving parts

Resolves the issues described in `.github/workflows/problem.log`.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5950-Fix-CI-Remove-explicit-repository-parameter-causing-non-reproducible-test-results-2846d73d36508159a848c4a2e14a0fb1)
by [Unito](https://www.unito.io)

Co-authored-by: Alexander Brown <drjkl@comfy.org>
2025-10-08 13:11:53 -07:00
Comfy Org PR Bot
ea01723249 1.29.0 (#5979)
Minor version increment to 1.29.0

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5979-1-29-0-2866d73d36508113b968cce1c974fb72)
by [Unito](https://www.unito.io)

---------

Co-authored-by: AustinMroz <4284322+AustinMroz@users.noreply.github.com>
Co-authored-by: github-actions <github-actions@github.com>
Co-authored-by: Austin Mroz <austin@comfy.org>
2025-10-08 12:50:11 -07:00
Christian Byrne
b2ea7b4a62 [ci] fix stylelint script command in package.json (missing target) (#5972)
## Summary

Correct usage of stylelint is

```
Usage: stylelint [input] [options]
```

The previous script omitted the `[input]` which defaulted to stdin,
which didn't work as expected.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5972-ci-fix-stylelint-script-command-in-package-json-missing-target-2866d73d36508193a55ecc8dd4e9e161)
by [Unito](https://www.unito.io)
2025-10-07 23:54:42 -07:00
Christian Byrne
a234dac038 [ci] add match-component-import-name eslint rule (#5971)
## Summary

Added ESLint rule to enforce imported Vue component names match their
file names.

Inspired by this PR which would have never been necessary in the first
place had this rule been in place:

- https://github.com/Comfy-Org/ComfyUI_frontend/pull/5919

## Changes

- **What**: Enabled
[`vue/match-component-import-name`](https://eslint.vuejs.org/rules/match-component-import-name.html)
rule in ESLint configuration

## Review Focus

Impact on existing imports that use aliases (e.g., importing
`MyComponent.vue` as `MyComp`). The rule enforces that import aliases
match the component's name definition.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5971-ci-add-match-component-import-name-eslint-rule-2866d73d3650811bba97c1ddcc75df5d)
by [Unito](https://www.unito.io)
2025-10-07 23:42:50 -07:00
Rizumu Ayaka
1461e371ad perf: avoid calling getBoundingClientRect too often in useSelectionToolboxPosition (#5976)
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5976-perf-avoid-calling-getBoundingClientRect-too-often-in-useSelectionToolboxPosition-2866d73d365081639755d8087e80377c)
by [Unito](https://www.unito.io)
2025-10-07 22:52:43 -07:00
Alexander Brown
874ef3ba0c Lint: Add eslint import plugin (#5955)
## Summary

Adds the linter, turns on the recommended and a few extra rules, fixes
existing violations.

Doesn't prohibit `../../...` imports yet, that'll be it's own PR.

## Changes

- **What**: Consistent and fixable imports
- **Dependencies**: The plugin and parser

## Review Focus

How do you feel about the recommended rules?
What about the extra ones?
[Any
more](https://github.com/un-ts/eslint-plugin-import-x?tab=readme-ov-file#rules)
you'd want to turn on?

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5955-Lint-Add-eslint-import-plugin-2856d73d3650819985c0fb9ca3fa94b0)
by [Unito](https://www.unito.io)
2025-10-07 20:31:00 -07:00
Arjan Singh
45ebc59033 chore(package.json): increase memory available for build command (#5968)
## Summary

Increased default node memory allocation to 8GB for building the app.

I was running out of memory on default settings and at 4GB manually
allocated.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5968-chore-package-json-increase-memory-available-for-build-command-2866d73d365081e78ab2e387113aaced)
by [Unito](https://www.unito.io)
2025-10-07 19:31:11 -07:00
Alexander Brown
fc1d040e06 Devex: Add additional trigger for Playwright updates (#5960)
## Summary

Allow for secondary trigger for updating screenshots:
> Adding a comment starting with `/update-playwright`

## Review Focus

- Is this the command you'd expect?
- Should I also add `/playwright-update`?

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5960-Devex-Add-additional-trigger-for-Playwright-updates-2856d73d365081768f70d0a8aafa9c11)
by [Unito](https://www.unito.io)
2025-10-07 19:06:51 -07:00
Christian Byrne
8fc54646de apply stylelint auto fixes (#5940)
## Summary

Applied stylelint auto-fixes and resolved manual CSS issues across 25
files to achieve full compliance with stylelint rules.

## Changes

- **What**: Auto-fixed 68 CSS issues (legacy color functions,
font-weight keywords, shorthand properties, pseudo-element notation) and
manually resolved 6 remaining issues (duplicate selectors, vendor prefix
duplicates, font fallbacks, Vue v-bind whitelisting)
- **Config**: Disabled `no-descending-specificity` rule (43 warnings
require architectural CSS refactor)

## Review Focus

Verify no visual regressions from modernized CSS syntax:
- Modern [color function
notation](https://www.w3.org/TR/css-color-4/#funcdef-rgb): `rgba(0, 0,
0, 0.5)` → `rgb(0 0 0 / 50%)`
- Numeric font weights: `bold`/`normal` → `700`/`400`
- Pseudo-element double colons: `:before` → `::before

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5940-apply-stylelint-auto-fixes-2846d73d365081ee8031c212a69a4bd4)
by [Unito](https://www.unito.io)

---------

Co-authored-by: DrJKL <DrJKL0424@gmail.com>
2025-10-07 18:49:50 -07:00
Christian Byrne
fe0eaaefb3 fix mask editor on cloud by allowing crossorigin on image data (#5957)
## Summary

Fixed cross-origin canvas taint
[error](https://comfy-org.sentry.io/issues/6927234287/?referrer=slack&notification_uuid=e2ac931f-c955-43a2-a345-76fa8b164504&alert_rule_id=16146009&alert_type=issue)
in mask editor by adding CORS support to image loading.

## Background

When images from different origins are drawn to canvas without CORS
headers, browsers "taint" the canvas to prevent data extraction attacks.
This breaks `getImageData()` calls with a SecurityError. The [W3C
standard
solution](https://developer.mozilla.org/en-US/docs/Web/API/HTMLImageElement/crossOrigin)
is `crossOrigin = 'anonymous'`.

Intended flow:

1. Frontend sets img.crossOrigin = 'anonymous'
2. Browser sends CORS preflight to GCS: "Can cloud.comfy.org access this
image?"
3. GCS must respond: "Yes, that origin is allowed"
4. Browser loads image with CORS headers enabled
5. Canvas operations work without taint

## Review Focus

Canvas security model compliance and compatibility with cloud deployment
image redirects to GCS.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5957-fix-mask-editor-on-cloud-by-allowing-crossorigin-on-image-data-2856d73d3650819a84b2fed19d85d815)
by [Unito](https://www.unito.io)
2025-10-07 18:04:38 -07:00
Alexander Brown
99b3a59679 Style: Standardize icon use Part 1 (#5947)
## Summary

Remove the mix of class based and component style icons in favor of just
[classes](https://iconify.design/docs/usage/css/tailwind/tailwind4/#basic-usage).

## Changes

- **What**: Migrate existing lucide icons

## Review Focus

What differs between the icons before and now?

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5947-Style-Standardize-icon-use-Part-1-2846d73d365081bfa66ceb6bdaa9ff02)
by [Unito](https://www.unito.io)

---------

Co-authored-by: github-actions <github-actions@github.com>
2025-10-07 17:53:38 -07:00
Alexander Brown
0616c049e4 hotfix: quick test updates for sora2 pricing badge. (#5966)
## Summary

Update expectations post-merge.

See https://github.com/Comfy-Org/ComfyUI_frontend/pull/5958
2025-10-07 17:14:43 -07:00
Marwan Ahmed
34d5a4523a OpenAIVideoSora2 Frontend pricing (#5958)
test: update OpenAIVideoSora2 tests to use `size` instead of
`resolution`

Refactored all OpenAIVideoSora2 test cases in useNodePricing.test.ts to
align with
the updated node logic that replaces the `resolution` widget with
`size`.
Adjusted validation, pricing, and error message expectations
accordingly.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5958-Update-OpenAIVideoSora2-tests-for-new-size-based-pricing-logic-2856d73d365081c9919dd256cce40492)
by [Unito](https://www.unito.io)
2025-10-07 16:48:15 -07:00
Alexander Brown
f62175ed0c Fix: Missing Node Title Editor bug (#5963)
Found by @marawan206

## Summary

Fixes the title editor glitching when the node doesn't have an initial
value

## Screenshots (if applicable)

### Before

https://github.com/user-attachments/assets/4c4efbfd-73b9-4733-8227-fe2de59648d4

### After

https://github.com/user-attachments/assets/30f3279e-aa2b-451b-9bee-c134d3f8374c

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5963-Fix-Missing-Node-Title-Editor-bug-2856d73d365081389edbda546eca3bbb)
by [Unito](https://www.unito.io)
2025-10-07 22:53:32 +00:00
Christian Byrne
d9157925f5 make Vue nodes resizable (#5936)
## Summary

Implemented node resizing functionality for Vue nodes.


https://github.com/user-attachments/assets/a7536045-1fa5-401b-8d18-7c26b4dfbfc3

Resolves https://github.com/Comfy-Org/ComfyUI_frontend/issues/5675.

## Review Focus

ResizeObserver as single source of truth pattern eliminates feedback
loops between manual resize and reactive layout updates. Intrinsic
content sizing calculation temporarily resets DOM styles to measure
natural content dimensions.

```mermaid
graph TD
    A[User Drags Handle] --> B[Direct DOM Style Update]
    B --> C[ResizeObserver Detects Change]
    C --> D[Layout Store Update]
    D --> E[Slot Position Sync]
    
    style A fill:#f9f9f9,stroke:#333,color:#000
    style E fill:#f9f9f9,stroke:#333,color:#000
```

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5936-make-Vue-nodes-resizable-2846d73d36508160b3b9db49ad8b273e)
by [Unito](https://www.unito.io)

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: DrJKL <DrJKL@users.noreply.github.com>
Co-authored-by: github-actions <github-actions@github.com>
2025-10-07 15:53:10 -07:00
Christian Byrne
89f4452488 [test] fix flaky Vue group/frame test (minimap flakiness) (#5962)
Attempts to fix flakiness when groups are enabled on the minimap and the
screenshot is taken too early, before the render completes. See
[comment](https://github.com/Comfy-Org/ComfyUI_frontend/pull/5942#issuecomment-3374615335)
for more context.

May or may not solve the flakiness.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5962-wait-for-frame-on-group-creation-test-2856d73d365081f6a059ebfc5a03857c)
by [Unito](https://www.unito.io)
2025-10-07 15:15:36 -07:00
Simula_r
247080f0d7 fix: node header DOM hierarchy (#5961)
## Summary

Fix: node header DOM hierarchy to position tooltip over text and
constrain LOD fallback to text instead of full width. Keep node header
full width to accommodate for colored background.

## Screenshots (if applicable)

<img width="1418" height="933" alt="image"
src="https://github.com/user-attachments/assets/804116d1-2444-4891-a04f-a2dfe8d586ff"
/>
<img width="1420" height="930" alt="image"
src="https://github.com/user-attachments/assets/a884edd0-055b-4dc7-b44c-a88b8376e018"
/>

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5961-fix-node-header-DOM-hierarchy-2856d73d3650814eae04ef96fae062fe)
by [Unito](https://www.unito.io)
2025-10-07 15:04:27 -07:00
Christian Byrne
61d0a12aae fix "what's changed" release toast attention level logic (#5959)
## Summary

Currently, the "What's Changed" popup toast in bottom left appears after
updating if three conditions are true:

1. Using Desktop app
2. Don't have notifications disabled in settings
3. Have not seen/dismissed the notification before

Then the fourth condition is

4. At least 1 of the last 2 notifications is medium or high priority

However, we only ever show the most recent notification, so this logic
is flawed. In addition, it presents issues:

- When the changelog is first generated by AI, it is marked as "low"
priority until human review. But if the changelog _prior_ to that is
"medium" or "high", the AI-generated one might get shown anyway - which
frustrates the intended process.

There's also a bug fixed here concidentally where if the server only
returns a single entry, it is never shown (due to `slice(0, -1)` syntax
when checking priorities).

## Changes

- **What**: Updated Pinia release store to read `attention` from the
newest release only and expanded unit coverage for toast visibility

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5959-fix-what-s-changed-release-toast-attention-level-logic-2856d73d36508141b9b2d8d3b11153b2)
by [Unito](https://www.unito.io)
2025-10-07 12:47:47 -07:00
snomiao
6617de771f fix: Add checkout step before using local action in update-locales workflow (#5938)
## Problem
The `update-locales` workflow was failing with the error:
```
Can't find 'action.yml', 'action.yaml' or 'Dockerfile' under '/home/runner/work/ComfyUI_frontend/ComfyUI_frontend/.github/actions/setup-frontend'. 
Did you forget to run actions/checkout before running your local action?
```

Ref:
https://github.com/Comfy-Org/ComfyUI_frontend/actions/runs/18270266173/job/52011427608

## Solution
Added a checkout step using `actions/checkout@v5` before the "Setup
Frontend" step. This ensures the repository code (including the local
action definition) is available before GitHub Actions tries to use it.

## Changes
- Added checkout step to `.github/workflows/update-locales.yaml`
- Uses `actions/checkout@v5` to checkout the repository before
referencing the local custom action

This is a minimal fix that follows GitHub Actions best practices.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5938-fix-Add-checkout-step-before-using-local-action-in-update-locales-workflow-2846d73d365081cfb720ffaa528ce26e)
by [Unito](https://www.unito.io)

---------

Co-authored-by: github-actions <github-actions@github.com>
2025-10-07 12:20:13 -07:00
Simula_r
893621265c fix: node deletion handling in vue nodes when switching from litegrap… (#5909)
## Summary

Fix node deletion when switching from litegraph -> vue node mode. 

## Background:
 
There is a race condition where we have [2 watch() functions that are
both tracking
shouldRenderVueNodes.value](f58365b20b/src/composables/graph/useVueNodeLifecycle.ts (L85)):
  - For vue node lifecycle
  - For slot/link sync lifecycle

Each watch() separately sets graph.onNodeRemoved to its own cleanup
handler. But without flush: 'sync', the slot sync watch runs AFTER vue
nodes and overwrites the callback. So when deletion happens,
graph.onNodeRemoved points to the slot handler (or undefined) instead of
the vue cleanup handler, and the vue node never gets removed from the
DOM.

## Review Focus
We are making use of the watch() option "fush: sync" in the link and
slot watch() so maybe there is side effects from firing this when
dependencies change synchronously.

## Screenshots (if applicable)


https://github.com/user-attachments/assets/6951fa50-61d5-4c63-88e9-e113f603ff77


┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5909-fix-node-deletion-handling-in-vue-nodes-when-switching-from-litegrap-2826d73d365081bdba35c3d8728d6251)
by [Unito](https://www.unito.io)

---------

Co-authored-by: github-actions <github-actions@github.com>
2025-10-06 23:51:20 -07:00
Arjan Singh
338cbd4eed feat(hostWhitelist): allow comfy.org hosts to authenticate (#5952)
## Summary

Add `comfy.org` host names to the list of hosts that can authenticate
via SSO.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5952-feat-hostWhitelist-allow-comfy-org-hosts-to-authenticate-2846d73d36508152a41af92ada2a698b)
by [Unito](https://www.unito.io)
2025-10-06 17:30:18 -07:00
Alexander Brown
9a452fc31a CI: Use main version number instead of pinning (#5951)
## Summary

Minimal change to use the versioned cache action instead of pinning to a
specific commit.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5951-CI-Use-main-version-number-instead-of-pinning-2846d73d36508130821ffb659cb52464)
by [Unito](https://www.unito.io)
2025-10-06 23:54:22 +00:00
Arjan Singh
0a73072ff1 feat(AssetBrowserModal): set initial focus to SearchBox (#5945)
## Summary

Some quick design feedback.

## Changes

1. Fix the placeholder text so it's showing up correctly.
2. Make the SearchBox take initial focus when the modal is opened.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5945-feat-AssetBrowserModal-set-initial-focus-to-SearchBox-2846d73d365081bfb3e0cde78c827d5f)
by [Unito](https://www.unito.io)
2025-10-06 16:32:27 -07:00
Alexander Brown
e7745eb2be Style: Make components themeable (#5908)
## Summary

Replace color/dark-color pairs in components with design tokens to allow
for easy overriding.
<!-- Also standardizes the icon pattern to simplify the tailwind config.
-->

## Changes

- **What**: Token based colors, for now, mostly.
- **Breaking**: Got approval from Design to collapse some very similar
pairs of colors that seem to have diverged in implementations over time.
Some of the colors might be a little different, but we can tweak them
later.

## Review Focus

Still have quite a few places from which to remove `dark-theme`, but
this at least gets the theming much closer.
Need to decide if I want to keep going in here or cut this and do the
rest in a subsequent PR.

## Screenshots (if applicable)

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

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5908-WIP-Make-components-themeable-2816d73d365081ffbc05d189fe71084b)
by [Unito](https://www.unito.io)

---------

Co-authored-by: github-actions <github-actions@github.com>
2025-10-06 16:27:08 -07:00
Simula_r
51f0f111ea refactor: remove unused tooltip appendTo code (#5943)
## Summary

Remove unused tooltip appendTo positioning code.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5943-refactor-remove-unused-tooltip-appendTo-code-2846d73d365081d99c00cd41e35eb496)
by [Unito](https://www.unito.io)

---------

Co-authored-by: Jake Schroeder <jake.schroeder@isophex.com>
2025-10-06 14:45:53 -07:00
AustinMroz
d69c54820f Subgraph widget promotion fixes (#5911)
- Fixes automatic promotion of image previews by ~~more correctly
handling a usage of `requestAnimationFrame` and~~ introducing a means to
perform actions upon successful load of preview.
- When workflows contain an old proxyWidget property in string form,
parse it instead of throwing an error.
- Do not overlay the `label` of promoted widgets. Further consideration
is needed, but this resolves the primary annoyance and prevents
untranslated widget names
See #5914

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5911-Subgraph-widget-promotion-fixes-2826d73d365081838ffeeea4b8d7068c)
by [Unito](https://www.unito.io)
2025-10-06 13:11:14 -07:00
AustinMroz
3eedff3876 Fix untyped subgraph node outputs in vue mode (#5930)
Under some infrequent circumstances, the node outputs of subgraphNodes
lack the boundingRect property. As this is a required property of
`INodeSlot`s, it's presence is required to satisfy isSlotObject and the
type information is discarded to instead display a placeholder output.

This boundingRect property is not used by vue nodes and the type of
nodeData.outputs already satisfies INodeSlot, so this entire typeguard
and computed can be removed.

<img width="405" height="209" alt="image"
src="https://github.com/user-attachments/assets/8563abb7-b619-495e-b9ec-e3274e7668cf"
/>


┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5930-Fix-untyped-subgraph-node-outputs-in-vue-mode-2836d73d3650814f993fc71590eca79b)
by [Unito](https://www.unito.io)
2025-10-06 13:09:54 -07:00
Alexander Brown
349f351f54 fix: unignore stylelint (#5935)
## Summary

Minimal fix to let Knip succeed but also use its built-in [stylelint
plugin](https://knip.dev/reference/plugins/stylelint).
2025-10-06 13:08:39 -07:00
ComfyUI Wiki
72ab84e37b Complete the missing i18n translations (#5927)
Complete the missing i18n translations

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5927-Complete-the-missing-i18n-translations-2836d73d3650815ab206cb82a01aae3e)
by [Unito](https://www.unito.io)

---------

Co-authored-by: Claude <noreply@anthropic.com>
2025-10-06 13:08:21 -07:00
filtered
022cf6dd8e Restrict Vite entry point to index.html (#5934)
## Summary

Restricts Vite's dependency optimization scanning entry points to the
root `index.html`, preventing excessive error messages from scanning
unintended files in other packages.

## Changes

- **What**: Adds `entries: ['index.html']` to `optimizeDeps` in
vite.config.mts
- **Breaking**: None

## Review Focus

May require additional entries or inversion - exclude-based approach
instead of include-based - if possible.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5934-Restrict-Vite-entry-point-to-index-html-2846d73d3650816fbf45fa29493891ae)
by [Unito](https://www.unito.io)
2025-10-06 12:04:00 -07:00
Christian Byrne
2cb078cd9e fix mmb navigation when click starts on Vue nodes (#5921)
## Summary

Fixes https://github.com/Comfy-Org/ComfyUI_frontend/issues/5860
by updating Vue node pointer interactions to forward middle mouse button
events to canvas instead of handling them locally.

## Review Focus

Middle mouse button event detection logic using both `button` property
and `buttons` bitmask for cross-browser compatibility. Test coverage for
pointer event forwarding behavior.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5921-fix-mmb-navigation-when-click-starts-on-Vue-nodes-2826d73d3650819688eec4600666755d)
by [Unito](https://www.unito.io)

---------

Co-authored-by: DrJKL <DrJKL@users.noreply.github.com>
2025-10-06 10:23:39 -07:00
Comfy Org PR Bot
dc0b729efa 1.28.6 (#5933)
Patch version increment to 1.28.6

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5933-1-28-6-2846d73d365081c59e1ed287f8c299b3)
by [Unito](https://www.unito.io)

Co-authored-by: AustinMroz <4284322+AustinMroz@users.noreply.github.com>
2025-10-05 23:53:55 -05:00
Christian Byrne
480f37e2f5 use PrimeVue tooltip for "enter subgraph" button on Vue node headers (#5920)
## Summary

Replaced HTML `title` attribute with PrimeVue tooltip component for the
"Enter Subgraph" button in Vue node headers.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5920-use-PrimeVue-tooltip-for-enter-subgraph-button-on-Vue-node-headers-2826d73d365081bdb0b9dcc7b4509d39)
by [Unito](https://www.unito.io)
2025-10-05 18:33:43 -07:00
Christian Byrne
1df6187972 [ci] add stylelint dependency and minimal config (#5926)
## Summary

Adds [stylelint](https://stylelint.io/) configuration and tooling to
enforce CSS/SCSS code quality and consistency across Vue components.

Starts with 21 focused rules for linting CSS in Vue SFC files and
standalone stylesheets. Configuration uses postcss-html to parse Vue
`<style>` blocks and includes whitelists for Tailwind v4 at-rules
(`@reference`, `@plugin`, `@custom-variant`, `@utility`) and
Electron-specific CSS properties (`speak: none`, `app-region`). Rules
emphasize modern CSS syntax (numeric font weights, modern color
functions, double-colon pseudo-elements) while avoiding overly
opinionated rules like hex color length enforcement (for now).

Currently finds 113 issues (79% auto-fixable). This PR only adds the
tooling via `pnpm stylelint` and `pnpm stylelint:fix` scripts - no
pre-commit hooks or CI integration yet. A follow-up PR will auto-fix the
fixable issues and optionally add enforcement to the commit workflow.

## Changes

- **What**: Integrated [stylelint](https://stylelint.io/) with Vue.js
support via postcss-html parser
- **Dependencies**: Added `stylelint@16.24.0`, `postcss-html@1.8.0`

## Review Focus

CSS rule strictness and Tailwind CSS compatibility - particularly the
`no-descending-specificity` rule and Tailwind-specific function ignores.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5926-ci-add-stylelint-dependency-and-minimal-config-2836d73d3650813ea7b7eb714ba7748a)
by [Unito](https://www.unito.io)
2025-10-05 10:41:47 -07:00
filtered
c636900487 Add Desktop UI npm publishing workflows (#5915)
## Summary

Adds automated npm publishing for @comfyorg/desktop-ui package with
version management and release workflows.

- Ref: #5912

## Changes

- **What**: Three GitHub Actions workflows for desktop-ui npm publishing
automation

### Two functions

1. Bump action - Just creates a version bump PR for `desktop-ui`
2. Publish action - Can be run manually - essentially a function with
params / void return

### One automation

- Watches for matching commits, then calls the Publish action with
pre-filled details

## Review Focus

Security hardening and workflow correctness.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5915-Add-Desktop-UI-npm-publishing-workflows-2826d73d365081d9b7f8d7f752536ceb)
by [Unito](https://www.unito.io)
2025-10-05 01:09:58 -07:00
Christian Byrne
f09590f269 fix border style on Vue nodes (#5924)
## Summary

Changes Vue nodes border to match
[design](https://www.figma.com/design/31uH3r4x3xbIctuRWYW6NM/V3---Vue-Nodes?node-id=6189-6820&m=dev).

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5924-fix-border-style-on-Vue-nodes-2826d73d36508121bb76cd786da06e5a)
by [Unito](https://www.unito.io)

---------

Co-authored-by: github-actions <github-actions@github.com>
2025-10-05 00:29:02 -07:00
filtered
07a74e3cdc Decouple Desktop UI into monorepo app (#5912)
## Summary

Extracts desktop UI into apps/desktop-ui package with minimal changes.

## Changes

- **What**:
- Separates desktop-specific code into standalone package with
independent Vite config, router, and i18n
- Drastically simplifies the main app router by removing all desktop
routes
  - Adds a some code duplication, most due to the existing design
- Some duplication can be refactored to be *simpler* on either side - no
need to split things by `isElectron()`
  - Rudimentary storybook support has been added
- **Breaking**: Stacked PR for publishing must be merged before this PR
makes it to stable core (but publishing _could_ be done manually)
  - #5915
- **Dependencies**: Takes full advantage of pnpm catalog. No additional
dependencies added.

## Review Focus

- Should be no changes to normal frontend operation
- Scripts added to root package.json are acceptable
- The duplication in this PR is copied as is, wherever possible. Any
corrections or fix-ups beyond the scope of simply migrating the
functionality as-is, can be addressed in later PRs. That said, if any
changes are made, it instantly becomes more difficult to separate the
duplicated code out into a shared utility.
  - Tracking issue to address concerns: #5925

### i18n

Fixing i18n is out of scope for this PR. It is a larger task that we
should consider carefully and implement properly. Attempting to isolate
the desktop i18n and duplicate the _current_ localisation scripts would
be wasted energy.
2025-10-05 05:04:27 +00:00
Alexander Piskun
ac9ebe1266 feat(api-nodes): add pricing for new Kling 2.5 Turbo model (#5892)
## Summary

Added prices for new model to the `KlingTextToVideoNode` and
`KlingImage2VideoNode` nodes

## Screenshots (if applicable)

<img width="2150" height="991" alt="Screenshot From 2025-10-02 10-51-04"
src="https://github.com/user-attachments/assets/489d2aeb-9c8b-4123-91ab-4e17df460862"
/>

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5892-feat-api-nodes-add-pricing-for-new-Kling-2-5-Turbo-model-2806d73d3650810eb33cf794dca23266)
by [Unito](https://www.unito.io)
2025-10-04 21:50:05 -07:00
Benjamin Lu
cd7310cb8c feat(vue-nodes): snap link preview; connect on drop (#5780)
## Summary

Snap link preview to the nearest compatible slot while dragging in Vue
Nodes mode, and complete the connection on drop using the snapped
target. Mirrors LiteGraph’s first-compatible-slot logic for node-level
snapping and reuses the computed candidate for performance.

## Changes

- Snap preview end to compatible slot
  - slot under cursor via `data-slot-key` fast-path
  - node under cursor via `findInputByType` / `findOutputByType`
- Render path
- `slotLinkPreviewRenderer.ts` now renders to
`state.candidate.layout.position`
- Complete on drop
  - Prefer `state.candidate` (no re-hit-testing)
  - Fallbacks: DOM slot → node first-compatible → reroute
  - Disconnects moving input link when dropped on canvas

## Review Focus

- UX feel of snapping and drop completion (both directions)
- Performance on large graphs (mousemove path is O(1) with dataset +
single validation)
- Edge cases: reroutes, moving existing links, collapsed nodes

## Screenshots (if applicable)


https://github.com/user-attachments/assets/fbed0ae2-2231-473b-a05a-9aaf68e3f820

https://github.com/Comfy-Org/ComfyUI_frontend/pull/5780 (snapping) <--
https://github.com/Comfy-Org/ComfyUI_frontend/pull/5898 (drop on canvas
+ linkconnectoradapter refactor) <--
https://github.com/Comfy-Org/ComfyUI_frontend/pull/5903 (fix reroute
snapping)

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5780-feat-vue-nodes-snap-link-preview-connect-on-drop-27a6d73d365081d89c8cf570e2049c89)
by [Unito](https://www.unito.io)

---------

Co-authored-by: github-actions <github-actions@github.com>
2025-10-04 21:48:59 -07:00
Christian Byrne
4e7e6e2bf3 fix dragging video/image components on Vue nodes triggers node drag (#5922)
## Summary

Fixed pointer capture behavior on video and image preview components to
prevent unintended node dragging. Below video shows behavior after fix:


https://github.com/user-attachments/assets/95563a2d-8958-47e1-a19c-977fb539d162

## Changes

- **What**: Added `data-capture-node="true"` attribute to
`VideoPreview.vue` and `ImagePreview.vue` components
- **What**: Enhanced `useNodePointerInteractions.ts` composable to
detect and handle pointer events on elements with capture attribute

## Review Focus

Pointer event delegation logic and interaction behavior between preview
components and canvas drag operations.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5922-fix-dragging-video-image-components-on-Vue-nodes-triggers-node-drag-2826d73d365081ce83e7fd61510167e2)
by [Unito](https://www.unito.io)

---------

Co-authored-by: DrJKL <DrJKL@users.noreply.github.com>
2025-10-04 21:30:02 -07:00
Christian Byrne
1c6edd146b fix stone-100 and stone-200 color variable values swapped (#5923)
## Summary

The `stone-100` and `stone-200` were flipped. This PR swaps them to
align with [color
variables](https://www.figma.com/design/vALUV83vIdBzEsTJAhQgXq/Comfy-Design-System?node-id=0-1&p=f&vars=1&var-id=538-1913&m=dev)

<img width="813" height="321" alt="image"
src="https://github.com/user-attachments/assets/9b2a19ff-566c-425b-a4fe-9998578b872a"
/>

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5923-fix-stone-100-and-stone-200-color-variable-values-swapped-2826d73d3650815c8c4ae0627aa045d9)
by [Unito](https://www.unito.io)

---------

Co-authored-by: github-actions <github-actions@github.com>
2025-10-04 19:27:26 -07:00
Christian Byrne
2c555efdfe [refactor] remove import alias on LGraphNode.vue (#5919)
## Summary

Changes import name such that it matches the SFC filename.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5919-refactor-remove-import-alias-on-LGraphNode-vue-2826d73d365081fc80d8f277a40f7d81)
by [Unito](https://www.unito.io)
2025-10-04 17:49:55 -07:00
Arjan Singh
59736c6d1d feat: create toggle command for AssetAPI (#5918)
## Summary

Taking inspiration from VueNodes to make testing easier

## Changes

1. Add a toggle to enable/disable AssetAPI
2. If you have the Model Browser bound, it will prompt to enable the
AssetAPI if you try to use it.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5918-feat-create-toggle-command-for-AssetAPI-2826d73d365081539aeaf3951928fdc1)
by [Unito](https://www.unito.io)
2025-10-04 13:54:43 -07:00
Alexander Brown
84e6e99f17 Cleanup: Removing monkeypatches for litegraph logic (#5902)
## Summary

Putting the litegraph specific pieces into litegraph itself, using the
CanvasGraph and LiteGraphGlobal to coordinate options.

This was one part of the Image Previews reloading/calculating with every
canvas draw.

## Review Focus

Is this keeping things decoupled enough?
Is this the right place to put things?
Are there assumptions about the options that I'm missing here?

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5902-WIP-Removing-monkeypatches-for-litegraph-logic-2816d73d3650818b860ec73579b89b54)
by [Unito](https://www.unito.io)

---------

Co-authored-by: github-actions <github-actions@github.com>
2025-10-03 21:22:42 -07:00
Arjan Singh
abf2b3b980 Full Asset Selection Experience (Assets API) (#5900)
## Summary

Full Integration of Asset Browsing and Selection when Assets API is
enabled.

## Changes

1. Replace Model Left Side Tab with experience
2. Configurable titles for the Asset Browser Modal
3. Refactors to simplify callback code
4. Refactor to make modal filters reactive (they change their values
based on assets displayed)
5. Add `browse()` mode with ability to create node directly from the
Asset Browser Modal (in `browse()` mode)

## Screenshots

Demo of many different types of Nodes getting configured by the Modal



https://github.com/user-attachments/assets/34f9c964-cdf2-4c5d-86a9-a8e7126a7de9

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5900-Feat-asset-selection-cloud-integration-2816d73d365081ccb4aeecdc14b0e5d3)
by [Unito](https://www.unito.io)
2025-10-03 20:34:59 -07:00
Rizumu Ayaka
661885f5e5 feat(widgets): lazy load images in FormDropdown (#5904)
some users may have a very large number of files, so we only need to
request/render the ones that are visible.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5904-feat-widgets-lazy-load-images-in-FormDropdown-2816d73d36508195b283ff469061f3f3)
by [Unito](https://www.unito.io)

Co-authored-by: Claude <noreply@anthropic.com>
2025-10-03 13:03:36 -07:00
Comfy Org PR Bot
5cff131144 1.28.5 (#5906)
Patch version increment to 1.28.5

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5906-1-28-5-2816d73d3650816c9f9bf791e20aa9c0)
by [Unito](https://www.unito.io)

Co-authored-by: AustinMroz <4284322+AustinMroz@users.noreply.github.com>
2025-10-03 09:49:55 -05:00
Christian Byrne
038ed27107 fix Vue node dragging/moving on touch devices (#5896)
## Summary

Enabled touch drag functionality on Vue nodes by adding CSS
`touchAction: 'none'`.

## Changes

- **What**: Added [`touchAction:
'none'`](https://developer.mozilla.org/en-US/docs/Web/CSS/touch-action)
CSS property to Vue nodes for touch device compatibility
- **What**: Added Playwright tests for both desktop and mobile drag
interactions

## Review Focus

Touch event handling on various mobile browsers and pointer event
compatibility across different devices.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5896-fix-Vue-node-dragging-moving-on-touch-devices-2806d73d365081578b02cd6714fd8fe0)
by [Unito](https://www.unito.io)

---------

Co-authored-by: github-actions <github-actions@github.com>
2025-10-02 21:20:33 -07:00
Benjamin Lu
139ae87983 chore(eslint): allow default project for Playwright configs to fix pre-commit linting (#5901)
Summary
- Adds playwright.config.ts and playwright.i18n.config.ts to
typescript-eslint projectService.allowDefaultProject in
eslint.config.ts.

Why
- Pre-commit runs lint-staged, which lints staged TypeScript files
including Playwright config files.
- These configs are not included in any tsconfig, so typescript-eslint’s
project service can’t find a project and fails with:
"Parsing error: .../playwright.config.ts was not found by the project
service. Consider either including it in the tsconfig.json or including
it in allowDefaultProject".

What this changes
- Whitelists the two Playwright config files to use the default project
(isolated file parsing) so ESLint can parse and lint them without being
part of a tsconfig.
- Does not affect application code linting, which remains fully
type-aware via existing tsconfigs.

Alternatives considered
- Include these configs in a dedicated ESLint tsconfig (e.g.,
tsconfig.eslint.json) and point ESLint to it.
- Exclude Playwright config files from lint-staged (would reduce lint
coverage for them).
- Keep as TypeScript but non-type-aware: current approach is minimal and
avoids touching tsconfig scopes.

Verification
- Reproduced pre-commit failure when changing playwright.config.ts.
- After this change, `pnpm exec eslint --cache --fix
playwright.config.ts` succeeds.
- `pnpm typecheck` passes.

Notes
- No changes to Playwright runtime behavior. This only affects linting.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5901-chore-eslint-allow-default-project-for-Playwright-configs-to-fix-pre-commit-linting-2816d73d36508156b94dfeff79a91c7f)
by [Unito](https://www.unito.io)
2025-10-02 21:14:34 -07:00
Alexander Brown
b994608506 Tests: Vitest configuration cleanup (#5888)
## Summary

Simplify default scripts. Filtering is still available to users, we can
revisit tagging or grouping later.
This fixes the issue where we had tests that were in the codebase but
never run because they weren't under `/src/components`

Also deletes the duplicate litegraph tests and their associated vitest
config file.

## Changes

- **What**: Test cleanup

## Review Focus

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5888-Tests-Vitest-configuration-cleanup-2806d73d36508197b800f68f0b028279)
by [Unito](https://www.unito.io)
2025-10-02 21:01:42 -07:00
filtered
7b1cce1d0e Add VS Code CSS validation support (#5893)
## Summary

Adds VS Code custom CSS data for proper validation of non-standard CSS
properties and Tailwind v4 directives.

## Changes

- **What**: Custom CSS data files for VS Code CSS language service
validation
  - `app-region` - Electron draggable regions  
  - `speak` - Deprecated aural stylesheet property
  - `@custom-variant` - Tailwind v4 custom variant definitions
  - `@utility` - Tailwind v4 custom utility definitions
- Fixes broken documentation links in existing Tailwind directive
references

## Review Focus

Documentation links verified against current Tailwind CSS and Electron
documentation.

## Screenshots

Current: Yellow squigglies

<img width="338" height="179" alt="image"
src="https://github.com/user-attachments/assets/652040f5-8e0b-486b-95f6-2fa8f9bf9ba7"
/><img width="499" height="180" alt="image"
src="https://github.com/user-attachments/assets/43d16210-7fbf-4ef8-b0e1-9a16e59d1d85"
/>

Proposed: Satisfying lack of warnings

<img width="173" height="62" alt="image"
src="https://github.com/user-attachments/assets/25f1c1c4-22b7-483b-9848-3030a3c0dc86"
/>

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5893-Add-VS-Code-CSS-validation-support-2806d73d3650813fb5f1e176360c5b7e)
by [Unito](https://www.unito.io)

---------

Consequences of `@apply` usage may include but are not limited to:
- A strong talking-to
- Receipt of a corrective directive missive
- Upsetting @DrJKL

Co-authored-by: Alexander Brown <drjkl@comfy.org>
2025-10-02 20:25:45 -07:00
Simula_r
39eca0bc0a fix: save video preview (#5897)
## Summary

Fix the save video preview by checking for video inputs.

## Screenshots (if applicable)


https://github.com/user-attachments/assets/26a1fbb1-f54c-4a17-a59d-ce89b4e0c389

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5897-fix-save-video-preview-2816d73d365081b79a47e0da29e1fed6)
by [Unito](https://www.unito.io)
2025-10-02 19:34:00 -07:00
Johnpaul Chiwetelu
2970692176 Move Frame Vue Nodes (#5886)
This pull request improves the selection and movement logic for groups
and nodes on the LiteGraph canvas, especially when using Vue-based node
rendering. The most notable changes are the addition of proper bounding
box handling for groups and a new coordinated movement mechanism that
updates both LiteGraph internals and the Vue layout store when dragging
nodes and groups.

**Selection and bounding box calculation:**

* Added support for including `LGraphGroup` bounding rectangles when
calculating the selection toolbox position, so groups are now properly
considered in selection overlays.
[[1]](diffhunk://#diff-57a51ac5e656e64ae7fd276d71b115058631621755de33b1eb8e8a4731d48713L8-R8)
[[2]](diffhunk://#diff-57a51ac5e656e64ae7fd276d71b115058631621755de33b1eb8e8a4731d48713R95-R97)

**Node and group movement synchronization (Vue nodes mode):**

* Introduced a new movement logic in `LGraphCanvas` for Vue nodes mode:
when dragging, groups and their child nodes are moved together, and all
affected node positions are batch-updated in both LiteGraph and the Vue
layout store via `moveNode`. This ensures canvas and UI stay in sync.
* Added imports for layout mutation operations and types to support the
above synchronization.

These changes make group selection and movement more robust and ensure
that UI and internal state remain consistent when using the Vue-based
node system.



https://github.com/user-attachments/assets/153792dc-08f2-4b53-b2bf-b0591ee76559

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5886-Move-Frame-Vue-Nodes-2806d73d365081e48b5ef96d6c6b6d6b)
by [Unito](https://www.unito.io)
2025-10-03 03:05:33 +01:00
Alexander Brown
4b1c165d43 Cleanup/Perf: Float32Array/Float64Array removal (#5877)
## Summary

Redoing https://github.com/Comfy-Org/ComfyUI_frontend/pull/5567, without
the link rendering changes.

## Changes

- **What**: Standardizing the Point/Size/Rect logic around numeric
tuples instead of typed arrays.

## Review Focus

Cutting here and going to continue in a second PR.

Do the simpler types make sense?
Do we want to keep the behavior of Rectangle as it is now?

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5877-WIP-Float32Array-Float64Array-removal-27f6d73d36508169a39eff1e4a87a61c)
by [Unito](https://www.unito.io)

---------

Co-authored-by: filtered <176114999+webfiltered@users.noreply.github.com>
Co-authored-by: github-actions <github-actions@github.com>
2025-10-02 17:20:31 -07:00
AustinMroz
720de8cc8a Add UI code for configuring subgraphNode widgets (#5826)
The third PR for managing display of widgets on subgraph nodes. This is
the one that actually makes the functionality usable and user visible.

Adds
- A right-side modal for configuring which widgets are promoted,
accessed by right click or selection toolbar
- This menu allows for re-arranging widget order by dragging and
dropping.
- Indicators inside the subgraph for which widgets have been promoted.
- Context menu options for promoting or demoting widget inside of a
subgraph.
<img width="767" height="694" alt="image"
src="https://github.com/user-attachments/assets/4f78645d-7b26-48ba-8c49-78f4807e89e8"
/>
<img width="784" height="435" alt="image"
src="https://github.com/user-attachments/assets/7005c730-a732-481e-befb-57019a8a31a7"
/>


Known issues
- Some preview widgets are not added to a node until a draw operation
occurs. The code does not yet have a way of determining which nodes
should have draw operations forced to facilitate initial widget
creation.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5826-Add-UI-code-for-configuring-subgraphNode-widgets-27c6d73d36508146accbf395e5bcd36a)
by [Unito](https://www.unito.io)
2025-10-02 17:19:47 -07:00
Arjan Singh
48335475dc Chores: Updates for Asset Services (#5872)
## Changes

1. Updates schema to match new API
2. Adds additional relevant models to the registry

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5872-Chores-Updates-for-Asset-Services-27f6d73d36508117b89fd473f1a7090d)
by [Unito](https://www.unito.io)
2025-10-02 15:52:36 -07:00
Simula_r
0d3d258995 Fix/vue nodes video (#5870)
## Summary

Fix the video preview widget and associated dropdown to load and select
videos.

Fixes:
-
https://www.notion.so/comfy-org/Video-thumbnails-not-being-used-in-asset-explorer-dialog-27e6d73d365080ec8a3ee7c7ec413657?source=copy_link
-
https://www.notion.so/comfy-org/Image-Video-upload-dialog-doesnt-set-mime-type-27e6d73d365080c5bffdf08842855ba0?source=copy_link
-
https://www.notion.so/comfy-org/Video-Previews-are-not-displayed-2756d73d365080b2bfb9e0004e9d784d?source=copy_link
-
https://www.notion.so/comfy-org/Cannot-load-video-in-Load-Video-node-2756d73d365080009c21d3a67add96c4?source=copy_link

## Screenshots (if applicable)


https://github.com/user-attachments/assets/b71dbecb-c9a7-4feb-83a3-c3e044a9c93c

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5870-Fix-vue-nodes-video-27e6d73d36508182b44bef8e90ef4018)
by [Unito](https://www.unito.io)

---------

Co-authored-by: JakeSchroeder <jake@axiom.co>
Co-authored-by: github-actions <github-actions@github.com>
Co-authored-by: Jake Schroeder <jake.schroeder@isophex.com>
Co-authored-by: Rizumu Ayaka <rizumu@ayaka.moe>
2025-10-02 13:58:47 -07:00
Christian Byrne
3818ba5d17 fix Vue node header width (#5895)
## Summary

Added `w-full` class to Vue node header to ensure full width layout
consistency.

## Changes

- **What**: Applied [Tailwind CSS w-full
utility](https://tailwindcss.com/docs/width#full-width) to NodeHeader
component for consistent width behavior

## Review Focus

If this is best place to set the class

## Screenshots (if applicable)

*Before*

<img width="931" height="919" alt="image"
src="https://github.com/user-attachments/assets/fe94ddad-f43b-4441-a924-60aeaff94041"
/>

*After*

<img width="931" height="919" alt="Screenshot from 2025-10-02 10-38-53"
src="https://github.com/user-attachments/assets/596681ad-309a-4fab-a343-bfbb73d72f6c"
/>

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5895-fix-Vue-node-header-width-2806d73d365081bb82ddf6abe8665f8b)
by [Unito](https://www.unito.io)
2025-10-02 13:29:43 -07:00
Alexander Brown
5090f41028 devex: Change to a standard prefix, don't mark as skip ci (#5890)
## Summary

Remove `[skip ci]` from the Playwright screenshot update commit message.

## Changes

- **What**: Also changes the lint-and-format commit message to match.
`[automated]` since they're not _fixes_, per se. I'm open to suggestions
there.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5890-devex-Change-to-a-standard-prefix-don-t-mark-as-skip-ci-2806d73d365081acbc13e01c6a98dc8b)
by [Unito](https://www.unito.io)

Co-authored-by: snomiao <snomiao@gmail.com>
2025-10-02 13:09:51 -07:00
Rizumu Ayaka
7e4c756258 feat: inputs/outputs filter to widget dropdown (#5894)
related https://github.com/Comfy-Org/ComfyUI_frontend/issues/5827

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5894-feat-inputs-outputs-filter-to-widget-dropdown-2806d73d365081498d92d0576b7da6a8)
by [Unito](https://www.unito.io)

---------

Co-authored-by: bymyself <cbyrne@comfy.org>
2025-10-02 13:06:08 -07:00
Alexander Brown
37fab21daf Cleanup: YAGNI readonly props, private swap on ComfyApp, Canvas resize events simplification, v-memos on individual instances (#5869)
## Summary

Assorted cleanup opportunities found while working through the Vue node
rendering logic cleanup.

## Review Focus

Am I wrong that the readonly logic was never actually executing because
it was defined as False in GraphCanvas when making each LGraphNode?

Is there an edge case or some other reason that the ResizeObserver
wouldn't work as a single signal to resize the canvas?

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5869-Cleanup-YAGNI-readonly-props-private-swap-on-ComfyApp-Canvas-resize-events-simplificat-27e6d73d3650811ba1dcf29e8d43091e)
by [Unito](https://www.unito.io)

---------

Co-authored-by: github-actions <github-actions@github.com>
2025-10-02 10:35:10 -07:00
filtered
706ff953de Adopt catalog references for all matching dependencies (#5889)
## Summary

Converts 81 package dependencies to use pnpm catalog references for
centralized version management.

## Changes

- **What**: All dependencies matching catalog versions now use
`catalog:` references
- **Dependencies**: axios catalog entry corrected from ^1.11.0 to ^1.8.2
- Also removes a redundant knip config line

### Some things that shouldn't matter

- TypeScript was updated from ^5.4.5 to catalog reference (^5.9.2), but
the project was already resolving to 5.9.2 so this has no practical
impact.
- axios catalog version was corrected from ^1.11.0 back to ^1.8.2 to
match the main package version.
- Autoformatted LGraphNode.ts from another PR by running pnpm lint.
Oops.
2025-10-01 23:54:01 -07:00
Alexander Piskun
9c97fb359d feat(auth): Allow SSO login only for whitelisted addresses (localhost) (#5815)
## Summary

Hide Google/GitHub SSO login options when the UI is accessed from
**non‑local** addresses.
This PR also adds a **static whitelist** (editable in code) so we can
allow additional hosts if needed.

Default whitelisted addresses:

1. `localhost` and any subdomain: `*.localhost`
2. IPv4 loopback `127.0.0.0/8` (e.g., `127.x.y.z`)
4. IPv6 loopback `::1` (including equivalent textual forms such as
`::0001`)

## Changes

- **What**: 
* Add `src/utils/hostWhitelist.ts` with `normalizeHost` and
`isHostWhitelisted` helpers.
  * Update `SignInContent.vue` to **hide** SSO options when
`isHostWhitelisted(normalizeHost(window.location.hostname))` returns
`false`.
- **Breaking**:
* Users accessing from Runpod or other previously allowed **non‑local**
hosts will **lose** SSO login options.
If we need to keep SSO there, we should add those hosts to the whitelist
in `hostWhitelist.ts`.

## Review Focus

1. Verify that logging in from local addresses (`localhost`,
`*.localhost`, `127.0.0.1`, `::1`) **does not change** the current
behavior: SSO is visible.
2. Verify that from a **non‑local** address, SSO options are **not**
displayed.

## Screenshots (if applicable)

UI opened from `192.168.2.109` address:

<img width="500" height="990" alt="Screenshot From 2025-09-27 13-22-15"
src="https://github.com/user-attachments/assets/c97b10a1-b069-43e4-a26b-a71eeb228a51"
/>

UI opened from default `127.0.0.1` address(nothing changed):

<img width="462" height="955" alt="Screenshot From 2025-09-27 13-35-27"
src="https://github.com/user-attachments/assets/bb2bf21c-dc8d-49cb-b48e-8fc6e408023c"
/>

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5815-feat-auth-Allow-SSO-login-only-for-whitelisted-addresses-localhost-27b6d73d365081ccbe84c034cf8e416d)
by [Unito](https://www.unito.io)
2025-10-01 23:09:11 -07:00
snomiao
c662c77305 Add Playwright composite action to reduce workflow duplication (#5754)
This PR introduces a reusable composite action for Playwright setup to
reduce duplication across workflows.

## Changes
- Created `.github/actions/setup-playwright/action.yml` composite action
that:
  - Detects or uses provided Playwright version
  - Caches Playwright browsers with intelligent cache keys
  - Installs browsers only when cache miss occurs
  - Installs OS dependencies when cache hit occurs
  
## Technical Details
- **Important:** The composite action requires `shell: bash` for all
`run` steps as per [GitHub Actions requirements for composite
actions](https://docs.github.com/en/actions/creating-actions/creating-a-composite-action#creating-an-action-metadata-file).
This is a mandatory field for composite actions, unlike regular workflow
steps.
- Updated workflow paths to account for repository checkout locations
(some workflows checkout to subdirectories like `ComfyUI_frontend/`)
- Uses conditional caching to avoid redundant browser installations

## Benefits
- Reduces code duplication across 6 workflow files
- Centralizes Playwright caching logic  
- Consistent browser setup across all workflows
- Easier maintenance and updates
- Faster CI runs through intelligent caching

## Affected Workflows
- `.github/workflows/test-ui.yaml` (2 uses)
- `.github/workflows/i18n-custom-nodes.yaml`
- `.github/workflows/i18n-node-defs.yaml`
- `.github/workflows/i18n.yaml`
- `.github/workflows/test-browser-exp.yaml`

---------

Co-authored-by: GitHub Action <action@github.com>
2025-10-02 14:20:48 +09:00
Marcel Petrick
d0e81cdd33 fix(docs): correct typos in comments and strings found during code view (#5880)
Non-functional changes only:
- Fixed minor spelling mistakes in comments
- Corrected typos in user-facing strings
- No variables, logic, or functional code was modified.

Signed-off-by: Marcel Petrick <mail@marcelpetrick.it>

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5880-fix-docs-correct-typos-in-comments-and-strings-found-during-code-view-27f6d73d3650815db62af6115991304a)
by [Unito](https://www.unito.io)

---------

Signed-off-by: Marcel Petrick <mail@marcelpetrick.it>
Co-authored-by: Alexander Brown <DrJKL0424@gmail.com>
Co-authored-by: Benjamin Lu <benjaminlu1107@gmail.com>
2025-10-01 18:35:38 -07:00
Johnpaul Chiwetelu
01b3aeae68 Prune console.log() (#5867)
Introduce a no-console rule in ESLint configuration and remove existing
console log statements throughout the codebase, replacing some with
warnings or comments.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5867-Prune-console-log-27e6d73d365081bcbad8c36cfb5b258c)
by [Unito](https://www.unito.io)
2025-10-01 18:34:58 -07:00
Terry Jia
20731fe3f0 pass nodeId to widget component (#5853)
## Summary

pass nodeId to widget component, which is a prerequirist for
https://github.com/Comfy-Org/ComfyUI_frontend/pull/5765

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5853-pass-nodeData-to-widget-component-27e6d73d3650811e9e48ceb7c3a00545)
by [Unito](https://www.unito.io)
2025-10-01 21:31:49 -04:00
Benjamin Lu
bf9659fb2c ci: fork-safe checkout for lint workflow (#5887)
Use conditional ref to support forks and avoid checkout failures.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5887-ci-fork-safe-checkout-for-lint-workflow-2806d73d36508139b9effa6d18e1cadb)
by [Unito](https://www.unito.io)
2025-10-01 17:26:21 -07:00
Alexander Brown
e9352f613e fix: Remove extra arguments to checkout in favor of the GitHub defaults (#5883)
## Summary

Should allow this action to run on fork PRs.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5883-fix-Remove-extra-arguments-to-checkout-in-favor-of-the-GitHub-defaults-27f6d73d365081a780f7cf4a5a62c368)
by [Unito](https://www.unito.io)

---------

Co-authored-by: GitHub Action <action@github.com>
2025-10-01 16:47:34 -07:00
Benjamin Lu
d76b1abc46 Rename workflows to match workflow names (#5866)
## Summary
- rename each GitHub Actions workflow file so its filename matches the
workflow `name` value for easier discovery

------
https://chatgpt.com/codex/tasks/task_e_68dc213f0a808330869ed73c27858eb9

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5866-Rename-workflows-to-match-workflow-names-27e6d73d36508103bca7ea2746f73a3b)
by [Unito](https://www.unito.io)
2025-10-01 13:27:14 -07:00
Christian Byrne
fd757027a9 [ci] allow Claude review even when Playwright and Vitest checks have failed (#5882)
Currently the claude review action will be skipped if any tests have
failed. This is not really necessary, it will be more efficient to allow
claude to review while still waiting for tests.

This accounts for scenario where there is an expected visual baseline
change, but the PR author doesn't want to regenerate baselines until
everything is approved and ready to merge (as generating right away
before you know whether changes will be requested can be a hassle).

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5882-ci-allow-Claude-review-even-when-Playwright-and-Vitest-checks-have-failed-27f6d73d365081ccbcdaff7104edc2fd)
by [Unito](https://www.unito.io)
2025-10-01 13:16:14 -07:00
Benjamin Lu
1efc2233c5 Fix Claude review workflow checkout ref (#5874)
By default, in this case the checkout action will checkout to github's
temporary merge base ref, which may include changes from the base branch
when the base branch moves.

This happened in this review:
https://github.com/Comfy-Org/ComfyUI_frontend/pull/5866#pullrequestreview-3287366541

To prevent this, the PR's HEAD SHA was specified to be used
specifically, keeping claude's reviews only looking at that PR's branch.
2025-10-01 12:52:03 -07:00
Comfy Org PR Bot
557b2fdb0e 1.28.4 (#5875)
Patch version increment to 1.28.4

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5875-1-28-4-27f6d73d3650819f984ac83c971197b0)
by [Unito](https://www.unito.io)

Co-authored-by: AustinMroz <4284322+AustinMroz@users.noreply.github.com>
2025-09-30 23:40:53 -05:00
filtered
7fd2dc304a pnpm catalog for centralized dependency management (#5871)
## Summary

Adds pnpm catalog to centralize dependency versions across the monorepo.

## Changes

- **What**: Consolidates dependencies into single default catalog with
[`prefer` mode](https://pnpm.io/catalogs#catalog-mode)
- **Dependencies**: No new dependencies - reorganizes existing version
management

## Review Focus

The catalog uses `prefer` mode which automatically uses catalog versions
for packages already in the catalog, falling back to direct versions for
packages not yet cataloged.

### Example Usage

When adding a dependency already in the catalog:
```bash
pnpm add vue
```

This automatically uses `"vue": "catalog:"` in `package.json` instead of
a direct version.
2025-09-30 20:05:41 -07:00
Christian Byrne
e8de474d42 [docs] Change grammar in docs (#5868)
## Summary

Updates docs.

This PR is mostly for testing some n8n automation

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5868-docs-Change-grammar-in-docs-27e6d73d3650812691d2dcf71efe7bf4)
by [Unito](https://www.unito.io)
2025-09-30 19:10:20 -07:00
Christian Byrne
3f291672d4 fix progress state on Vue nodes in subgraphs (#5842)
## Summary

Fixes two errors with subgraph progress states:

1. Nodes inside subgraphs were not having progress state shown
2. Subgraph nodes (outer representation) themselves did not have a
visible progress state

1 is fixed by using locator IDs instead of local node IDs.

2 is fixed by ensuring the subgraph title button does not wrap to a
newline and thus block the progress bar under the node header.

## Changes

- **What**: Updated `useNodeExecutionState` composable to use
`nodeLocatorId` for tracking execution state across subgraph boundaries
- **What**: Modified NodeHeader layout to fix subgraph enter button
positioning with proper flexbox gap

## Review Focus

Execution state tracking accuracy for nested subgraph nodes and
NodeHeader layout consistency across different node types.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5842-fix-progress-state-on-Vue-nodes-in-subgraphs-27c6d73d365081cb8335c8bb5dbd74f7)
by [Unito](https://www.unito.io)

---------

Co-authored-by: github-actions <github-actions@github.com>
2025-09-30 15:01:18 -07:00
Benjamin Lu
ac72999e26 Fix snapshot generation workflow (#5864)
Fixes playwright snapshot generation workflow.

Simple approach, can explore deduping the checkout later @snomiao

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5864-Fix-snapshot-generation-workflow-27e6d73d365081c8bf3bc00ac227415b)
by [Unito](https://www.unito.io)
2025-09-30 11:18:31 -07:00
Christian Byrne
e27b5311d1 [docs] fix broken link in browser tests README (#5865)
## Summary

Fixes a broken link showing demo of Playwright UI mode features.
2025-09-30 11:05:47 -07:00
snomiao
35ddb19a68 [ci] Upgrade actions/checkout from v4 to v5 (#5859)
## Summary
- Upgrades all GitHub Actions workflows to use `actions/checkout@v5` 
- Updates 33 instances across 17 workflow files
- Ensures we're using the latest version with security patches and
improvements

## Changes
Updated the following workflow files from `actions/checkout@v4` to
`actions/checkout@v5`:
- `.github/workflows/backport.yaml`
- `.github/workflows/chromatic.yaml`
- `.github/workflows/claude-pr-review.yml`
- `.github/workflows/create-release-candidate-branch.yaml`
- `.github/workflows/dev-release.yaml`
- `.github/workflows/devtools-python.yaml`
- `.github/workflows/i18n-custom-nodes.yaml`
- `.github/workflows/json-validate.yaml`
- `.github/workflows/lint-and-format.yaml`
- `.github/workflows/pr-playwright-deploy.yaml`
- `.github/workflows/pr-storybook-deploy.yaml`
- `.github/workflows/test-ui.yaml`
- `.github/workflows/update-electron-types.yaml`
- `.github/workflows/update-manager-types.yaml`
- `.github/workflows/update-registry-types.yaml`
- `.github/workflows/version-bump.yaml`
- `.github/workflows/vitest.yaml`

Note: `.github/workflows/publish-frontend-types.yaml` and
`.github/workflows/release.yaml` were already using v5.

## Test plan
- [ ] CI workflows should continue to run successfully
- [ ] No functional changes - this is a dependency version upgrade only

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

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5859-ci-Upgrade-actions-checkout-from-v4-to-v5-27e6d73d3650815488adee20c74c0464)
by [Unito](https://www.unito.io)

Co-authored-by: Claude <noreply@anthropic.com>
2025-09-30 06:22:02 +00:00
snomiao
aed342572c Remove optional ref: master from setup-frontend action (#5858)
## Summary
- Removed the optional `ref: master` parameter from the ComfyUI checkout
step in the setup-frontend action
- The ref parameter defaults to the repository's default branch when
omitted

## Details
The `ref: master` specification in
`.github/actions/setup-frontend/action.yml` is unnecessary since GitHub
Actions will automatically use the repository's default branch when the
ref parameter is not provided.

This simplifies the configuration and makes it more maintainable, as the
action will automatically follow any future changes to the default
branch name.

## Test plan
- [ ] Verify that GitHub Actions workflows using this composite action
still work correctly
- [ ] Confirm ComfyUI is checked out properly in CI/CD pipelines

Generated with [Claude Code](https://claude.ai/code)

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5858-Remove-optional-ref-master-from-setup-frontend-action-27e6d73d365081aeb632f2d0e76f267d)
by [Unito](https://www.unito.io)

Co-authored-by: Claude <noreply@anthropic.com>
2025-09-30 15:08:48 +09:00
Christian Byrne
f16f5a0bc6 When restoring workflow tabs from previous session, don't skip workflows that were modified (#5857)
## Summary

In https://github.com/Comfy-Org/ComfyUI_frontend/pull/2238, we added
feature to restore open tabs from previous session. At that time, it was
decided to not restore the tabs if they had a modified worklow - the
reason being that we would not be able to restore the actual state.
However, MANY users have expressed frustration with this - so, for now,
restore the tabs even if they were modified in the previous session (and
unsaved).

There are already e2e test cases for this feature here:
26cf45ed36/browser_tests/tests/interaction.spec.ts (L679)

And unit tests for the relevant workflow store methods here:
26cf45ed36/tests-ui/tests/store/workflowStore.test.ts (L184)

Resolves https://github.com/Comfy-Org/ComfyUI_frontend/issues/5848

## Changes

- **What**: Modified filter in `restoreState` computed property to
persist both saved and modified workflows

## Review Focus

Impact on browser storage when users have many modified workflows open
simultaneously.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5857-When-restoring-workflow-tabs-from-previous-session-don-t-skip-workflows-that-were-modifi-27e6d73d36508182a900d2918d0fb163)
by [Unito](https://www.unito.io)
2025-09-29 22:24:44 -07:00
filtered
4fc7d6352f [devex] Fix .vue import failures silently discarded (#5852)
## Summary

Removes the old Vue shim; this is not needed in modern Vue. It actually
appears to be detrimental, forcing `any` types and causing broken
imports to be ignored by some tooling.

## Changes

- **What**: Deletes unnecessary TS decl.
- **Breaking**: Nein.
- **Dependencies**: Null.

## Review Focus

The **Approve** button.

## Screenshots (if applicable)

<img width="114" height="31" alt="image"
src="https://github.com/user-attachments/assets/3bb3d895-8b4c-4540-8f9b-a110c338d666"
/>

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5852-devex-Fix-vue-import-failures-silently-discarded-27e6d73d3650819eb956f2aaf55420f2)
by [Unito](https://www.unito.io)
2025-09-29 22:24:28 -07:00
Simula_r
c47cedcec2 Fix/vue nodes tooltips (#5856)
## Summary

Make vue node tooltips support color modes and hide when user drags node
or zooms in or out.

IMPORTANT: this approach is non ideal. But for sake of time its a valid
escape hatch. The reason for this approach is because of how primevue
exposes tooltips via a directive and the inability to modify its state /
conditionally render it directly. Instead we must get the actual tooltip
element from the dom and trigger a mouseleave event.

Follow up PR would be refactoring the entire useNodeTooltip.ts
composable and tooltip system from primevue to something like RekaUI
which is a much more declarative approach. To start we could just
refactor Vue nodes themselves to RekaUI tooltips and then later migrate
the other instances.

## Changes

- **What**:  hide tooltips on drag and zoom. Use color themes

## Review Focus

Tooltip hover states after dragging. 
Fixes: 
-
https://www.notion.so/comfy-org/Bug-Fix-tooltip-appearing-when-clicking-and-dragging-Vue-node-header-2796d73d365081f692fdde30c51580e1?source=copy_link
-
https://www.notion.so/comfy-org/Bug-Fix-chevron-color-customization-in-tooltip-pseudo-element-27b6d73d3650814abc25fc0eefda1648?source=copy_link
-
https://www.notion.so/comfy-org/Bug-Bug-Tooltip-positioning-breaks-with-zoom-due-to-missing-appendTo-prop-27b6d73d36508186858bd1cb4336ec03?source=copy_link

## Screenshots (if applicable)


https://github.com/user-attachments/assets/06c70f6c-bf64-4b6d-8e6f-739995549940

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5856-Fix-vue-nodes-tooltips-27e6d73d36508177a77bf7f2f27344f4)
by [Unito](https://www.unito.io)

---------

Co-authored-by: Jake Schroeder <jake.schroeder@isophex.com>
2025-09-29 22:18:14 -07:00
snomiao
789617415d Add frontend setup composite action to reduce workflow duplication (#5805)
## Summary
- Adds a local composite action at `.github/actions/setup-frontend` to
replace the external `Comfy-Org/ComfyUI_frontend_setup_action`
- Follows the same pattern as PR #5754 (sno-playwright-composite-action)
for consistency
- Updates workflows to use the new local composite action

## Motivation
Similar to the Playwright composite action, this change:
- Reduces external dependencies on separate action repositories
- Provides better control over versioning and updates
- Maintains consistency with other composite actions in the repository
- Simplifies maintenance by keeping all CI/CD logic in one place

## Changes
### New composite action: `.github/actions/setup-frontend/action.yml`
Direct mirror of the external action with the same 2 inputs:
- `extra_server_params`: Additional parameters to pass to ComfyUI server
- `devtools_ref`: Reference to use for ComfyUI_devtools

The action:
1. Checks out ComfyUI, ComfyUI_frontend, and ComfyUI_devtools
2. Sets up pnpm, Node.js (LTS), and Python (3.10)
3. Installs all dependencies (Python packages, npm packages)
4. Builds the frontend
5. Starts the ComfyUI server with the built frontend

### Updated workflows:
- `.github/workflows/i18n.yaml`
- `.github/workflows/i18n-node-defs.yaml`
- `.github/workflows/test-browser-exp.yaml`

All workflows now use the local composite action instead of
`Comfy-Org/ComfyUI_frontend_setup_action@v3`

## Test plan
- [ ] Verify all updated workflows pass CI tests
- [ ] Confirm the composite action works in all scenarios
- [ ] Check that build and server startup work as expected

## Related PRs
- #5754 - Similar approach for Playwright composite action

🤖 Generated with [Claude Code](https://claude.ai/code)
2025-09-29 22:11:37 -07:00
filtered
26cf45ed36 Update browser test screenshots (#5855)
## Summary
- Empty commit to trigger GitHub Actions workflow for updating browser
test screenshots
- Will generate new baseline screenshots for visual regression tests

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5855-Update-browser-test-screenshots-27e6d73d3650813d9a86e792b9c16456)
by [Unito](https://www.unito.io)

---------

Co-authored-by: github-actions <github-actions@github.com>
2025-09-29 22:02:13 -07:00
snomiao
af10082134 [ci] Improve Storybook deployment and comment workflow (#5426)
## Summary
- Improves Storybook deployment and PR comment workflow similar to the
Playwright improvements in #5425
- Creates unified deployment and commenting system for better
maintainability
- Adds Cloudflare Pages deployment for Storybook previews

## Deployment Cases Matrix

| Case | PR Type | Branch | Deployment | Features |
|------|---------|--------|------------|----------|
| **1** | Non-forked PR | `version-bump-*` |  Chromatic | • Visual diff
testing<br>• Chromatic build URL<br>• Chromatic Storybook URL<br>• Shows
visual changes |
| **2** | Non-forked PR | All branches |  Cloudflare Pages | • Live
Storybook preview<br>• pages.dev URL<br>• No visual diff |
| **3** | Forked PR | Any branch |  Cloudflare Pages | • Live Storybook
preview<br>• pages.dev URL<br>• No visual diff<br>• Runs via separate
workflow to avoid permission problems |

### Key Points:
- **Chromatic** (paid service): Only for `version-bump-*` branches to
track visual changes between releases
- **Cloudflare Pages** (free): For all other PRs to provide Storybook
preview without visual diff
- **Security**: Forked PRs use a separate workflow with limited
permissions

## Changes

### New Features
- 🚀 **Cloudflare Pages Deployment**: Storybook builds are now deployed
to Cloudflare Pages for easy preview
- 🔄 **Unified Script**: Single reusable shell script handles both
deployment and PR comments
- 🔒 **Better Security**: Separate workflows for fork vs non-fork PRs

### Improvements
- ♻️ **Retry Logic**: Automatic retry (3 attempts) for failed
deployments
- 📝 **Better Comments**: Clearer PR comments with deployment links and
status
- 🎯 **Simplified Logic**: Workflow logic moved to reusable script for
easier maintenance
-  **Better Error Handling**: Proper handling of different workflow
conclusions
- 🐛 **Fixed Comment Output**: Deployment logs now properly redirected to
stderr

### Files Changed
- `scripts/cicd/pr-storybook-deploy-and-comment.sh` - New unified
deployment script
- `.github/workflows/chromatic.yaml` - Updated to use new script and add
deployment
- `.github/workflows/pr-storybook-deploy.yaml` - New workflow for forked
PRs
- `.github/workflows/pr-storybook-comment.yaml` - Removed (replaced by
new system)

## ⚠️ Required Setup

The Cloudflare Pages project `comfyui-storybook` needs to be created
under the organization's Cloudflare account:

```bash
# Using the account ID from GitHub secrets
export CLOUDFLARE_ACCOUNT_ID=5ae914d9b87bcf6bbe1ada5798f92a5f
export CLOUDFLARE_API_TOKEN=<org-token>
wrangler pages project create comfyui-storybook --production-branch main
```

**Note**: The project must be created under the same Cloudflare account
that's configured in the GitHub secrets.

## Test Plan
- [x] Create Cloudflare Pages project `comfyui-storybook`
- [x] Workflow runs successfully on all PRs
- [x] PR comments are posted correctly at start and completion
- [x] Storybook deploys to Cloudflare Pages with correct URL
- [ ] Fork PRs are handled by separate workflow
- [ ] Non-fork PRs get inline deployment
- [ ] version-bump-* branches show Chromatic info

## References
- Similar improvements for Playwright: #5459
- Based on pattern from sno-fix-playwright-comment-2 branch

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

---------

Co-authored-by: Claude <noreply@anthropic.com>
2025-09-29 21:42:00 -07:00
Terry Jia
28a779d41a Check if the wheel event is from an element that wants to capture wheel events (#5821)
## Summary

Add generic wheel event capture mechanism for interactive widgets in
vueNodes system to prevent event bubbling to canvas.

  ## Changes

- What: Add event handling logic in LGraphNode.vue and GraphCanvas.vue
that checks for data-capture-wheel attribute to determine whether wheel
events should be forwarded to the canvas
- How it works: Components that need to capture wheel events (like
Three.js scenes) can add data-capture-wheel="true" attribute to prevent
wheel events from bubbling up to the canvas zoom handler

prerequirist for https://github.com/Comfy-Org/ComfyUI_frontend/pull/5765

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5821-Check-if-the-wheel-event-is-from-an-element-that-wants-to-capture-wheel-events-27b6d73d3650812493b5f13849147e6c)
by [Unito](https://www.unito.io)
2025-09-29 19:51:37 -07:00
filtered
ed5d258ab6 Extract shared utilities into workspace package (#5843)
## Summary

Extracts shared formatting and network utilities into dedicated
workspace package.

## Changes

- **What**: Created `@comfyorg/shared-frontend-utils` package containing
formatUtil and networkUtil
- **Breaking**: None - utilities remain accessible via path aliases in
`tsconfig`

Split `createAnnotatedPath` and `electronMirrorCheck` out and left in
frontend, due to their tightly-coupled nature. See [discussion on this
PR](https://github.com/Comfy-Org/ComfyUI_frontend/pull/5843#issuecomment-3344724727).
2025-09-29 18:52:40 -07:00
Benjamin Lu
5d0aee59a6 Increase vue slot/link functionality (#5710)
## Summary

Increase functionality for slots and links, covered with playwright
tests.

## Features

- Allow for reroute anchors to work when dragging from input slot
- Allow for dragging existing links from input slot
- Allow for ctrl/command + alt to create new link from input slot
- Allow shift to drag all connected links on output slot
- Connect links with reroutes (only when dragged from vue slot)

## Tests Added

### Playwright

- Dragging input to input drags existing link
- Dropping an input link back on its slot restores the original
connection
- Ctrl+alt drag from an input starts a fresh link
- Should reuse the existing origin when dragging an input link
- Shift-dragging an output with multiple links should drag all links
- Rerouted input drag preview remains anchored to reroute
- Rerouted output shift-drag preview remains anchored to reroute

## Notes

The double rendering system for links being dragged, it works right now,
maybe they can be coalesced later.

Edit: As in the adapter, can be removed in a followup PR

Also, it's known that more features will arrive in smaller PRs, this PR
actually should've been much smaller.

The next ones coming up are drop on canvas support, snap to node, type
compatibility highlighting, and working with subgraphs.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5710-Increase-vue-slot-link-functionality-2756d73d3650814f8995f7782244803b)
by [Unito](https://www.unito.io)

---------

Co-authored-by: github-actions <github-actions@github.com>
2025-09-29 18:23:32 -07:00
Arjan Singh
d852bab617 [chore] make experiment asset api setting hidden (#5851)
## Summary

This way no one can accidentally enable it when they are messing around.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5851-chore-make-experiment-asset-api-setting-hidden-27d6d73d36508113a6c1e41a764325f3)
by [Unito](https://www.unito.io)
2025-09-29 16:14:16 -07:00
Alexander Brown
dd93ac43d9 fix: Only add the listeners for DomWidget components once (#5846)
## Summary

Fixes the laggy textarea select noted in Discord.

## Changes

- **What**: Fixes the case where a DomWidget that was repeatedly made
visible accumulated as many event listeners as times it was toggled on.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5846-fix-Only-add-the-listeners-for-DomWidget-components-once-27d6d73d3650818382e0d0c565fc862a)
by [Unito](https://www.unito.io)
2025-09-29 13:52:49 -05:00
Christian Byrne
e0b1a6d212 [test] fix flaky Vue upload widget browser tests using randomize url param (#5845)
## Summary

Fix browser tests that use filenames in loader nodes. When the files
don't exist, the fallback/error component shows the request URL which
contains random cache-busting param which makes tests fail due to
non-deterministic nature.


[Context](https://github.com/Comfy-Org/ComfyUI_frontend/pull/5831#discussion_r2386582256)

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5845-test-fix-flaky-Vue-upload-widget-browser-tests-using-randomize-url-param-27d6d73d36508107a0d0d52d2764a028)
by [Unito](https://www.unito.io)

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: github-actions <github-actions@github.com>
2025-09-28 23:28:52 -07:00
Christian Byrne
bbd8d67f5f designate canvasOnly on runtime-generated virtual widgets so they are hidden in Vue renderer (#5831)
## Summary

Added `canvasOnly` flag to runtime-generated widgets to prevent Vue
renderer from displaying them while keeping canvas functionality intact.

## Changes

- **What**: Added `canvasOnly` widget option to hide upload, webcam, and
refresh widgets from Vue renderer

In the Canvas (LiteGraph) system, there was a small set of widgets with
strictly defined components. There, if we wanted some unique or
relatively complex behavior (like an upload butotn), we needed to create
a separate widget that would be coupled to the original widget at
runtime (and would not be serialized).

In the Vue renderer system, we can simply add flags to the inputSpec or
widget options and conditionally render complex UI additions -- i.e.,
there is no need for the hard-to-maintain runtime widget associations.
Expressing such things entirely in the view layer simplifies business
logic related to graph state, as we no longer need to account for
preserving the connections between runtime widgets and their special
siblings -- we also do not need to worry about the implications for
state serialization.

## Related

- https://github.com/Comfy-Org/ComfyUI_frontend/pull/5798

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5831-designate-canvasOnly-on-runtime-generated-virtual-widgets-so-they-are-hidden-in-Vue-ren-27c6d73d365081fb8641feec010190df)
by [Unito](https://www.unito.io)

---------

Co-authored-by: github-actions <github-actions@github.com>
2025-09-28 18:48:29 -07:00
filtered
5c707a1b93 Extract registry types into workspace package (#5840)
## Summary

Extracts ComfyUI registry types into a dedicated workspace package for
better modularity.

## Changes

- **What**: Created `@comfyorg/registry-types` package to house
generated type definitions
- **Breaking**: None - maintains backward compatibility through
re-exports at original path
- **Dependencies**: Added `@comfyorg/registry-types` as workspace
dependency

## Review Focus

Is this the right granularity for package extraction, or should registry
types be part of a larger shared package?

PR split into two tiny diffs:
- [Part
one](f8d3d2fa01)
- [Part
two](f8d3d2fa01..c48ca84336)

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5840-Extract-registry-types-into-workspace-package-27c6d73d365081dbb824d680ce739316)
by [Unito](https://www.unito.io)
2025-09-28 18:28:42 -07:00
Christian Byrne
3077f4c4c4 [refactor] rename unit test files to ensure they all use .test.ts filename suffix (#5835)
Rename remaining files that fail the rule added in

- https://github.com/Comfy-Org/ComfyUI_frontend/pull/5820

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5835-refactor-rename-unit-test-files-to-ensure-they-all-use-test-ts-filename-suffix-27c6d73d3650816fba7ae857760d004e)
by [Unito](https://www.unito.io)
2025-09-28 16:38:52 -07:00
Christian Byrne
9734466a13 [ci] add Python syntax checking workflow on changes to devtools (#5841)
## Summary

Added CI workflow and npm script for Python syntax validation in
devtools directory.

## Changes

- **What**: Added GitHub Actions workflow for Python syntax checking
with `python3 -m compileall`
- **Dependencies**: Added `python3` binary to knip ignore list

## Review Focus

Workflow triggers correctly on devtools path changes and Python syntax
validation covers all relevant files.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5841-ci-add-Python-syntax-checking-workflow-on-changes-to-devtools-27c6d73d365081b8963dd4600a233852)
by [Unito](https://www.unito.io)
2025-09-28 16:36:30 -07:00
Christian Byrne
5d62e01c11 [ci] add JSON validation CI workflow (#5837)
## Summary

Added GitHub Actions workflow and shell script to validate JSON syntax
in repository files, as in the past we have committed locales files with
invalid JSON.

## Changes

- **What**: Added CI workflow for JSON validation with `jq` syntax
checking
- **Dependencies**: CI workflow requires `jq` (pre-installed on
ubuntu-latest runners)

## Review Focus

Script exclusion patterns for TSConfig files and environment variable
override mechanism (`JSON_LINT_EXCLUDES`).

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5837-ci-add-JSON-validation-CI-workflow-27c6d73d365081a199b1d9ae9e4edfa4)
by [Unito](https://www.unito.io)
2025-09-28 15:30:01 -07:00
filtered
8135b67f34 [devex] Generalize AGENTS.md for all contributors (#5836)
## Summary

Updates AGENTS.md to be more universal and contributor-friendly by
removing user-specific preferences.

## Changes

- **What**: Refined AGENTS.md to remove individual preferences, one-time
setup tasks, and conflicting i18n instructions

## Review Focus

These updates make agent instructions work better for all contributors
by:
- Removing individual coding preferences in favor of project-wide
standards
- Eliminating one-time environment setup that clutters agent context
- Removing i18n instructions that caused agents to make unwanted changes
- Improving PR link formatting for better GitHub rendering

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5836-Generalize-AGENTS-md-for-all-contributors-27c6d73d365081e7acf6f37d9f8ceeaf)
by [Unito](https://www.unito.io)
2025-09-28 15:18:30 -07:00
Christian Byrne
1215cb4670 [test] add test to verify panning on touch devices works as expected with Vue renderer (#5833)
## Summary

Added mobile touch pan interaction test for Vue nodes canvas
functionality.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5833-test-add-test-to-verify-panning-on-touch-devices-works-as-expected-with-Vue-renderer-27c6d73d3650810ebe01c0f7711670b9)
by [Unito](https://www.unito.io)

---------

Co-authored-by: github-actions <github-actions@github.com>
2025-09-28 12:35:08 -07:00
Christian Byrne
a30705161d [type] properly type Vue node tooltip config pt object (#5832)
## Summary

Changes `any` type to proper type per the documentation here
https://primevue.org/tooltip/#api.tooltip.interfaces.TooltipOptions.pt

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5832-type-properly-type-Vue-node-tooltip-config-pt-object-27c6d73d365081ea99c2c719d9ee6586)
by [Unito](https://www.unito.io)
2025-09-28 11:50:02 -07:00
Christian Byrne
02b3b8ef5b [test] add browser tests verifying Vue node groups behaviors (#5825)
## Summary

Adds Playwright tests verifying node groups behaviors work in the Vue
nodes system - in particular:

- Using hotkey to create a group around selected Vue nodes
- Using the "fit to contents" feature on a group that contains Vue nodes

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5825-test-add-browser-tests-verifying-Vue-node-groups-behaviors-27c6d73d365081e1a380ea5c43a861b0)
by [Unito](https://www.unito.io)

---------

Co-authored-by: github-actions <github-actions@github.com>
2025-09-28 10:49:30 -07:00
Alexander Piskun
aa212bea90 add pricing for WanImageToImageApi node (#5829)
## Summary

Currently the price is constant.

## Screenshots (if applicable)

<img width="452" height="380" alt="Screenshot From 2025-09-28 18-01-47"
src="https://github.com/user-attachments/assets/b9026be9-ef88-417f-aaca-b5a29eb23c55"
/>

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5829-add-pricing-for-WanImageToImageApi-node-27c6d73d36508137b1baf75c0031f172)
by [Unito](https://www.unito.io)
2025-09-28 09:48:18 -07:00
Arjan Singh
a2c7db9dc2 [fix] add InputSlot and dot error state (#5813)
## Summary

Error states were not getting propagated down to the InputSlots from the
API Repsonse

I created a provider and injected error state. It seemed like a way
better idea than prop drilling or building a composable that only two
nodes (`InputSlot` and `OutputSlot`) would need.

## Changes

The follow are now error code red when an input node has errors:
1. There's a error round border around the dot.
2. The dot is error colored.
3. The input text is error colored.

This treatment was okay after feedback from design.

## Screenshots - Error State

<img width="749" height="616" alt="Screenshot 2025-09-26 at 9 02 58 PM"
src="https://github.com/user-attachments/assets/55c7edc9-081b-4a9d-9753-120465959b5d"
/>

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5813-fix-add-InputSlot-and-dot-error-state-27b6d73d36508151a955e485f00a2d05)
by [Unito](https://www.unito.io)

---------

Co-authored-by: github-actions <github-actions@github.com>
2025-09-27 20:14:13 -07:00
Alexander Brown
840f7f04fa Cleanup: Litegraph/Vue synchronization work (#5789)
## Summary

Cleanup and fixes to the existing syncing logic.

## Review Focus

This is probably enough to review and test now.

Main things that should still work: 
- moving nodes around
- adding new ones
- switching back and forth between Vue and Litegraph

Let me know if you find any bugs that weren't already present there.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5789-WIP-Litegraph-Vue-synchronization-work-27a6d73d3650811682cacacb82367b9e)
by [Unito](https://www.unito.io)
2025-09-27 16:01:59 -07:00
Christian Byrne
042c2caa88 [test] extend Vue node selection browser test to cover Mac (Meta) key (#5824)
## Summary

Extend Vue node selection tests to also cover Meta key (Mac).

## Changes

- **What**: Extended node selection test modifiers to include [Meta
key](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/metaKey)
alongside existing Ctrl and Shift keys

## Review Focus

Cross-platform keyboard modifier testing coverage and Mac-specific
interaction patterns.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5824-test-extend-Vue-node-selection-browser-test-to-cover-Mac-Meta-key-27b6d73d365081778833cefb8592f158)
by [Unito](https://www.unito.io)
2025-09-27 15:45:56 -07:00
Christian Byrne
cd8b5b5d50 [refactor] reorganize Vue node browser tests into subfolders based on behaviors and states (#5823)
## Summary

Refactored Vue node browser tests by organizing them into behavioral
categories, better reflecting the nature of browser tests as behind
user-action/behavior specifications.

- **What**: Reorganized Playwright browser tests into logical behavioral
folders (`interactions/`, `nodeStates/`)
- **Structure**: Created subdirectories for canvas interactions, link
interactions, node interactions, and node states

## Review Focus

Test file path changes and associated snapshot relocations ensure all
browser tests continue to run correctly in the new structure.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5823-refactor-reorganize-Vue-node-browser-tests-into-subfolder-based-on-behaviors-and-states-27b6d73d365081aaa6f9e3a388165258)
by [Unito](https://www.unito.io)
2025-09-27 15:14:51 -07:00
Christian Byrne
a25d89881b Allow Vue nodes to have their colors changed from selection toolbox (#5720)
## Summary

Fixes https://github.com/Comfy-Org/ComfyUI_frontend/issues/5680 by
allowing Vue nodes to properly synchronize color changes with LiteGraph
nodes, implementing header darkening and light theme adjustments.

<img width="2496" height="1512" alt="Screenshot from 2025-09-21
20-00-36"
src="https://github.com/user-attachments/assets/e3bdf645-1e0b-4d11-9ae5-9401f43e8e96"
/>

## Changes

- **What**: Implemented color property synchronization between LiteGraph
and Vue node rendering systems
- **Core Fix**: Added `nodeData.color` and `nodeData.bgcolor` to [v-memo
dependencies](https://vuejs.org/api/built-in-directives.html#v-memo) to
trigger re-renders on color changes
- **Color Logic**: Added header darkening using [memoized color
adjustments](https://github.com/Comfy-Org/ComfyUI_frontend/blob/main/src/utils/colorUtil.ts)
to match LiteGraph's ColorOption system
- **Event System**: Enhanced property change instrumentation in
LGraphNode.setColorOption to emit color/bgcolor events

## Review Focus

Vue component reactivity timing - the v-memo fix was critical for
immediate color updates. Verify light theme color adjustments match the
drawNode monkey patch behavior in app.ts.

## Technical Details

```mermaid
graph TD
    A[User Sets Color] --> B[LGraphNode.setColorOption]
    B --> C[Sets node.color & node.bgcolor]
    C --> D[Triggers property:changed events]
    D --> E[Vue Node Manager Updates]
    E --> F[v-memo Detects Change]
    F --> G[NodeHeader Re-renders]
    G --> H[Header Darkening Applied]

    style A fill:#f9f9f9,stroke:#333,color:#000
    style H fill:#f9f9f9,stroke:#333,color:#000
```

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5720-Allow-Vue-nodes-to-have-their-colors-changed-from-selection-toolbox-2766d73d36508123b441d126a74a54b2)
by [Unito](https://www.unito.io)

---------

Co-authored-by: Myestery <Myestery@users.noreply.github.com>
Co-authored-by: DrJKL <DrJKL@users.noreply.github.com>
Co-authored-by: github-actions <github-actions@github.com>
2025-09-27 15:12:13 -07:00
Comfy Org PR Bot
5b866b74b8 1.28.3 (#5822)
Patch version increment to 1.28.3

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5822-1-28-3-27b6d73d365081f18315d03ad55987aa)
by [Unito](https://www.unito.io)

Co-authored-by: AustinMroz <4284322+AustinMroz@users.noreply.github.com>
2025-09-27 17:05:43 -05:00
Christian Byrne
e8b9f8f4bc enforce test file-naming rule (#5820)
## Summary

Enforced test file naming conventions with ESLint rules and renamed 26
test files from `.spec.ts` to `.test.ts`.

## Changes

- **What**: Added ESLint rules to enforce `.spec.ts` files only in
`browser_tests/tests/` and `.test.ts` files only in `src/`
- **What**: Renamed 26 component/unit test files from `.spec.ts` to
`.test.ts` to comply with new convention

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5820-enforce-test-file-naming-rule-27b6d73d365081269b32ddcc9d3a5048)
by [Unito](https://www.unito.io)
2025-09-27 13:53:25 -07:00
Christian Byrne
b0d2b44dad [test] add browser tests for Vue node multiline string widgets (#5819)
## Summary

Added Playwright E2E tests for Vue multiline string widget functionality
to ensure text input and persistence work correctly.

## Changes

- **What**: Created browser tests for multiline string widget in Vue
nodes implementation, covering text input, multiline text input, and
focus change behavior.

## Review Focus

Test coverage for text input persistence across focus changes and
multiline content handling in the CLIP Text Encode widget.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5819-test-add-browser-tests-for-Vue-node-multiline-string-widgets-27b6d73d365081e2916ae663e2d44899)
by [Unito](https://www.unito.io)
2025-09-27 12:45:24 -07:00
Christian Byrne
976b1a6bbd [test] add browser test to verify Vue node body double-click behavior (#5818)
## Summary

Added test coverage to verify double-clicking node body does not trigger
title edit mode, mirroring test in Litegraph
856eb446a5/browser_tests/tests/interaction.spec.ts (L361)

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5818-test-add-browser-test-to-verify-Vue-node-body-double-click-behavior-27b6d73d3650812f9efef1459d5d465f)
by [Unito](https://www.unito.io)
2025-09-27 12:07:33 -07:00
Christian Byrne
856eb446a5 Add node pinning functionality to Vue nodes (#5772)
## Summary

Added pinning functionality to Vue nodes with hotkey support and visual
indicators.

## Changes

- **What**: Added node pinning feature with 'p' hotkey toggle and pin
icon indicator
- **Components**: Updated `LGraphNode.vue` and `NodeHeader.vue` with pin
state tracking
- **State Management**: Extended `useGraphNodeManager` to sync pinned
flag with Vue components
- **Tests**: Added E2E tests for single and multi-node pin toggling

## Review Focus

Pin state persistence in graph serialization and visual indicator
positioning in node header layout. Verify hotkey doesn't conflict with
existing shortcuts.

## Technical Details

- Pin state tracked via `flags.pinned` property in `LGraphNode`
- Uses [Vue
memoization](https://vuejs.org/api/reactivity-advanced.html#v-memo) for
efficient header re-rendering
- Integrates with existing node property change detection system
- Visual indicator uses Lucide pin icon with theme-aware styling

## Screenshots (if applicable)

<img width="875" height="977" alt="Screenshot from 2025-09-25 13-02-21"
src="https://github.com/user-attachments/assets/51d46cea-08f0-44fb-8b07-56d1b939338f"
/>

<img width="875" height="977" alt="Screenshot from 2025-09-25 13-02-10"
src="https://github.com/user-attachments/assets/ce247426-1e39-48c0-924b-658b65c24f52"
/>

## Related 

- https://github.com/Comfy-Org/ComfyUI_frontend/pull/5715

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5772-Add-node-pinning-functionality-to-Vue-nodes-2796d73d36508195914bcfc986aa66b5)
by [Unito](https://www.unito.io)

---------

Co-authored-by: filtered <176114999+webfiltered@users.noreply.github.com>
2025-09-27 10:56:46 -07:00
Simula_r
992ba9d585 style: LOD color theme (#5812)
## Summary

light mode styles for lod system

## Screenshots (if applicable)

<img width="1353" height="751" alt="image"
src="https://github.com/user-attachments/assets/3caf2e7d-10c3-4ecb-bcd6-b605a643065b"
/>

<img width="1320" height="705" alt="image"
src="https://github.com/user-attachments/assets/cb4a2675-f644-4564-af57-aa9f3920fb05"
/>

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5812-style-LOD-color-theme-27b6d73d365081fe8cd2c8c5a52b2493)
by [Unito](https://www.unito.io)
2025-09-27 10:52:28 -07:00
Christian Byrne
4b9d448b6c fix style of Select COMBO Vue node widget (#5785)
## Summary

Fixed `WidgetSelect` component styling to match other Vue node widgets
by using shared base styles.

## Changes

- **What**: Replaced hardcoded color classes with `WidgetInputBaseClass`
for consistent styling across Vue node widgets

## Review Focus

Visual consistency with other input widgets in Vue nodes interface and
proper styling inheritance.

## Screenshots (if applicable)

*Before*:

<img width="1645" height="1436" alt="Screenshot from 2025-09-25
18-12-16"
src="https://github.com/user-attachments/assets/0b985616-0cf5-44e3-8459-739d194dec7a"
/>

*After*:

<img width="1645" height="1436" alt="Screenshot from 2025-09-25
18-12-07"
src="https://github.com/user-attachments/assets/235437cf-3e1f-4bc0-8822-26fb8606b437"
/>

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5785-fix-style-of-Select-COMBO-Vue-node-widget-27a6d73d3650811a959ef8a955274987)
by [Unito](https://www.unito.io)

---------

Co-authored-by: github-actions <github-actions@github.com>
2025-09-27 10:28:26 -07:00
Christian Byrne
0ac1074a25 fix Vue nodes capturing wheel event when Ctrl+Shift+drag zooming (#5791)
## Summary

Fixed canvas read-only state during Ctrl+Shift drag-to-zoom to prevent
node interaction conflicts. This fix also benefits Litegraph nodes.

## Changes

- **What**: Canvas becomes read-only during [Ctrl+Shift drag
zoom](https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent)
operations and restores original state when finished
- **What**: Added browser test coverage for zoom interaction behavior

## Review Focus

State restoration on zoom interruption and edge cases where drag zoom is
cancelled mid-operation.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5791-fix-Vue-nodes-capturing-wheel-event-when-Ctrl-Shift-drag-zooming-27a6d73d3650817d888ee59f8f7fcb01)
by [Unito](https://www.unito.io)

---------

Co-authored-by: github-actions <github-actions@github.com>
2025-09-27 10:25:12 -07:00
Arjan Singh
45f5ec15c2 [fix] remove unnecessary afterEach (#5817)
## Summary

We didn't need the `afterEach` in this test.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5817-fix-remove-unnecessary-afterEach-27b6d73d3650814bb4f1c426c5b17a54)
by [Unito](https://www.unito.io)
2025-09-27 09:36:41 -07:00
Christian Byrne
a519e681dd Use workspace in knip config (#5809)
## Summary

After the `pnpm`/`Nx` monorepo migration, `knip` emitted configuration
hints like “Remove or move unused top-level entry…/project…” because it
discovered multiple workspaces but our config still declared global
`entry`/`project` patterns:

```
  > knip --cache

  Configuration hints (4)
  Remove or move unused top-level entry to one of workspaces: [{build,scripts}/**/*.{js,ts}, …]
  Remove or move unused top-level project to one of workspaces: [**/*.{js,ts,vue}, …]
  Remove from ignoreBinaries: only-allow
  Remove from ignoreBinaries: openapi-typescript
```

Rewriting `knip.config.ts` to define those patterns inside the
`workspaces` map for `'.'`, `packages/tailwind-utils`, and
`packages/design-system` matches Knip’s documented workspace schema
(root key `'.'` plus per-package overrides) and aligns each package’s
source with the files it actually exposes.

Follow-up: trim `packages/design-system` entries to the real public
surface, add a wildcard workspace default for future packages, and
capture this expectation somewhere in the repo's docs.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5809-Use-workspace-in-knip-config-27b6d73d365081b8ace9c82aee78b1db)
by [Unito](https://www.unito.io)
2025-09-27 09:35:01 -07:00
Arjan Singh
fdb15e1296 [fix] remove unnecessary awaits (#5806)
## Summary

Cleans up
src/renderer/extensions/vueNodes/composables/useNodePointerInteractions.test.ts

1. Removes unnecessary ticks.
2. Cleans up `beforeEach`

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5806-fix-remove-unnecessary-awaits-27a6d73d36508103ae1ccb8199195905)
by [Unito](https://www.unito.io)
2025-09-27 09:17:23 -07:00
Alexander Brown
d444b2c285 Test: Double timeout for Version Mismatch Warnings (#5814)
## Summary

`should persist dismissed state across sessions`


https://ccfbaff5.comfyui-playwright-chromium.pages.dev/#?testId=b07187838e41a32597d7-951ff47f196f7f22f516


https://7bf44be0.comfyui-playwright-chromium.pages.dev/#?testId=b07187838e41a32597d7-951ff47f196f7f22f516

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5814-Test-Double-timeout-for-Version-Mismatch-Warnings-27b6d73d365081be8c73c9a12759f83e)
by [Unito](https://www.unito.io)
2025-09-27 20:08:00 +10:00
Christian Byrne
46ad1318e5 Implement fit-to-view for Vue nodes (#5782)
## Summary

Implemented fit-to-view functionality for Vue nodes with bounds
calculation and viewport animation support.


https://github.com/user-attachments/assets/2ec221f1-9194-4564-95f9-ad4da80f190a

## Changes

- **What**: Added Vue nodes support to fit-to-view command with [bounds
calculation](https://developer.mozilla.org/en-US/docs/Web/API/Element/getBoundingClientRect)
and LiteGraph integration
- **Dependencies**: Added dependency on `layoutStore` and
`selectionBounds` utility

## Review Focus

Bounds calculation accuracy for complex node layouts and animation
performance with large node selections. Verify proper fallback to legacy
LiteGraph behavior when Vue nodes disabled.

```mermaid
graph TD
    A[Fit to View Command] --> B{Vue Nodes Enabled?}
    B -->|Yes| C[Get Selected Nodes]
    B -->|No| D[Legacy LiteGraph Method]
    C --> E{Nodes Selected?}
    E -->|Yes| F[Calculate Selected Bounds]
    E -->|No| G[Calculate All Nodes Bounds]
    F --> H[Convert to LiteGraph Format]
    G --> H
    H --> I[Animate to Bounds]
    D --> J[Canvas fitViewToSelectionAnimated]
    
    style A fill:#f9f9f9,stroke:#333,color:#333
    style I fill:#f9f9f9,stroke:#333,color:#333
    style J fill:#f9f9f9,stroke:#333,color:#333
```

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5782-Implement-fit-to-view-for-Vue-nodes-27a6d73d365081cb822cd93f557e77b2)
by [Unito](https://www.unito.io)

---------

Co-authored-by: filtered <176114999+webfiltered@users.noreply.github.com>
2025-09-26 21:32:31 -07:00
Johnpaul Chiwetelu
da332ed75d Add compact menu style for smaller window heights (#5323)
This pull request enhances the responsiveness of the `CommandMenubar`
component by introducing a "compact" mode for screens with limited
vertical space. When the window height is less than 700px, the menubar
adapts its submenu positioning and styling to improve usability on
smaller displays. The most important changes are grouped below:

**Responsive Behavior and State Management:**

* Added a reactive `windowHeight` reference and a computed
`isCompactHeight` property to detect when the window height is below
700px, enabling "compact" mode for the menubar. Event listeners for
window resize are now managed using `onMounted` and `onUnmounted`
lifecycle hooks to keep the state updated.
[[1]](diffhunk://#diff-10ee0e60e600a168bdd45e0b9f05a148f5b9ee48f54e6c7dee737ebe7b6192e6R314-R326)
[[2]](diffhunk://#diff-10ee0e60e600a168bdd45e0b9f05a148f5b9ee48f54e6c7dee737ebe7b6192e6L103-R104)

**Dynamic Styling and Class Application:**

* Updated the root element's class bindings to apply the
`comfy-command-menu-compact` class when `isCompactHeight` is true,
enabling conditional styling for compact mode.

**Compact Mode Styling:**

* Introduced new CSS rules for `.comfy-command-menu-compact` to force
submenus to open to the right and align with the top of the menu
container, ensuring better usability on short screens. Adjustments
include submenu positioning, alignment for nested menus, and ensuring
the submenu container uses the full available height.
* 
/fix #5289




https://github.com/user-attachments/assets/e7908b57-3f07-4fc4-a119-66f2b7e398c5





┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5323-Add-compact-menu-style-for-smaller-window-heights-2636d73d3650816cb5aaf4ac76c39739)
by [Unito](https://www.unito.io)
2025-09-26 19:13:50 -07:00
Christian Byrne
75d31f9d57 fix large integer precision handling in Vue int widgets (#5787)
## Summary

Fixed increment/decrement button lockup in number widgets when values
exceed JavaScript's safe integer limit (2^53 - 1).

## Changes

- **What**: Added precision-aware button disabling and user feedback to
`WidgetInputNumberInput` component using
[Number.isSafeInteger()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/isSafeInteger)
- We still need to support values greater than 2^53 because they may be
in workflows.

## Review Focus

JavaScript floating-point precision behavior at scale - buttons hide
when arithmetic operations like `value + 1` would be unreliable due to
IEEE 754 limitations. Test coverage includes edge cases (NaN, Infinity)
and boundary conditions at MAX_SAFE_INTEGER.

```mermaid
graph TD
    A[User Input] --> B{Value > 2^53?}
    B -->|No| C[Show Buttons]
    B -->|Yes| D[Hide Buttons]
    D --> E[Show Tooltip]
    E --> F[User Can Still Type]
    
    style A fill:#f9f9f9,stroke:#333,color:#000
    style F fill:#f9f9f9,stroke:#333,color:#000
```

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5787-fix-large-integer-precision-handling-in-Vue-int-widgets-27a6d73d365081d9ae00e485740cfafb)
by [Unito](https://www.unito.io)
2025-09-26 18:11:41 -07:00
Christian Byrne
edcbcdfa84 fix flaky templates browser test (#5808)
Fixes flaky templates modal test introduced in
https://github.com/Comfy-Org/ComfyUI_frontend/pull/5802 by ensuring the
templates modal is visible before querying the visible card count and
asserting that it is greater than 0.

If we immediately count the number of cards after executing the "load
templates" command, it's possible that there are 0 visible due to them
loading (rather than being caused by a legitimate bug).

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5808-fix-flaky-templates-browser-test-27b6d73d365081e58c65f608944976a0)
by [Unito](https://www.unito.io)
2025-09-26 18:01:34 -07:00
Christian Byrne
1f4a52ca3e Make searchbox first focused element in settings dialog (#5804)
## Summary

Resolves https://github.com/Comfy-Org/ComfyUI_frontend/issues/5164 by
adding `autofocus` prop to common `SearchBox` component then enabled for
the settings dialog search box (changing first focused element from the
close button). Previously, searchbox was the 2nd item in tab order.

## Review Focus

Accessibility implications of automatic focus and potential interference
with screen readers or keyboard navigation patterns. Already discussed
this previously w.r.t. accessibility, but only now implementing.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5804-Make-searchbox-first-focused-element-in-settings-dialog-27a6d73d3650814997facfa2418e1d40)
by [Unito](https://www.unito.io)
2025-09-26 13:28:30 -07:00
Johnpaul Chiwetelu
f2e925de62 Right click vue nodes (#5790)
This pull request refactors and improves the "More Options" popover
functionality for graph nodes in the UI. The main change is a rename and
redesign of the menu component from `MoreOptions` to `NodeOptions`,
introducing a global singleton pattern for popover control and enabling
context menu support on node right-click. This results in better
maintainability, more flexible triggering, and improved user experience.

**Node Options popover refactor and global control:**

* Renamed and refactored `MoreOptions.vue` to `NodeOptions.vue`,
removing the embedded button and exposing imperative methods (`toggle`,
`hide`, `isOpen`) for external control. The component now
registers/unregisters itself globally via `registerNodeOptionsInstance`.
[[1]](diffhunk://#diff-e0dbd5e37efd2c79e7317415455340b0dd150b758077b170a663f67d2453605eL2-R2)
[[2]](diffhunk://#diff-e0dbd5e37efd2c79e7317415455340b0dd150b758077b170a663f67d2453605eL203-R197)
[[3]](diffhunk://#diff-e0dbd5e37efd2c79e7317415455340b0dd150b758077b170a663f67d2453605eR294-R309)
* Added `NodeOptionsButton.vue` as a dedicated button component for
triggering the popover, decoupling the button UI from the popover logic.
* Implemented a global singleton pattern in `useMoreOptionsMenu.ts` for
controlling the `NodeOptions` popover from anywhere, with
`toggleNodeOptions` and `registerNodeOptionsInstance` functions.
[[1]](diffhunk://#diff-ae87bdb1e06725eb19b8d0fc82ec40a5f8ca831a6632767cc5d214fc903b89b6R35-R65)
[[2]](diffhunk://#diff-ae87bdb1e06725eb19b8d0fc82ec40a5f8ca831a6632767cc5d214fc903b89b6L184-R216)

**UI integration and event handling improvements:**

* Updated `SelectionToolbox.vue` to use the new `NodeOptionsButton`
instead of the previous embedded `MoreOptions` button, and added the
`NodeOptions` popover to the main `GraphCanvas.vue` template for global
accessibility.
[[1]](diffhunk://#diff-05d80ee1e28e634dc758394ddf1bfaa8e5ec72a186a6ea2e2b6f5dfba867b264L41-R41)
[[2]](diffhunk://#diff-05d80ee1e28e634dc758394ddf1bfaa8e5ec72a186a6ea2e2b6f5dfba867b264L71-R71)
[[3]](diffhunk://#diff-aaf17c713f29c6db8ea03efe7fc3483a858982e818a324b23cff89859e71559cR65)
[[4]](diffhunk://#diff-aaf17c713f29c6db8ea03efe7fc3483a858982e818a324b23cff89859e71559cR91)
* Added right-click context menu support to `LGraphNode.vue`, triggering
the node options popover at the cursor position and integrating with
node selection logic.
[[1]](diffhunk://#diff-a7744614cf842e54416047326db79ad81f7c7ab7bfb66ae2b46f5c73ac7d47f2R45)
[[2]](diffhunk://#diff-a7744614cf842e54416047326db79ad81f7c7ab7bfb66ae2b46f5c73ac7d47f2R141)
[[3]](diffhunk://#diff-a7744614cf842e54416047326db79ad81f7c7ab7bfb66ae2b46f5c73ac7d47f2L180-R187)
[[4]](diffhunk://#diff-a7744614cf842e54416047326db79ad81f7c7ab7bfb66ae2b46f5c73ac7d47f2R249-R263)

**Minor improvements and cleanup:**

* Updated references and variable names throughout the codebase to
reflect the new `NodeOptions` naming and logic.
[[1]](diffhunk://#diff-e0dbd5e37efd2c79e7317415455340b0dd150b758077b170a663f67d2453605eL53)
[[2]](diffhunk://#diff-e0dbd5e37efd2c79e7317415455340b0dd150b758077b170a663f67d2453605eR50)
[[3]](diffhunk://#diff-e0dbd5e37efd2c79e7317415455340b0dd150b758077b170a663f67d2453605eL75-R60)
[[4]](diffhunk://#diff-e0dbd5e37efd2c79e7317415455340b0dd150b758077b170a663f67d2453605eL91-L95)
[[5]](diffhunk://#diff-e0dbd5e37efd2c79e7317415455340b0dd150b758077b170a663f67d2453605eL110-R90)
[[6]](diffhunk://#diff-e0dbd5e37efd2c79e7317415455340b0dd150b758077b170a663f67d2453605eL133-R113)
[[7]](diffhunk://#diff-e0dbd5e37efd2c79e7317415455340b0dd150b758077b170a663f67d2453605eL146-R126)
[[8]](diffhunk://#diff-e0dbd5e37efd2c79e7317415455340b0dd150b758077b170a663f67d2453605eL157-R140)

This refactor makes the node options menu more modular, easier to
maintain, and more flexible for future UI improvements.



https://github.com/user-attachments/assets/9c2f2556-4544-4e20-9f22-8f485b0ceadc

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5790-Right-click-vue-nodes-27a6d73d365081a98263c88d2e09e629)
by [Unito](https://www.unito.io)
2025-09-26 13:22:06 -07:00
Christian Byrne
5f7c7ca949 [ci] Update browser tests for new Templates modal (#5802)
## Summary
- Regenerate 3 Playwright screenshot baselines to reflect UI changes
from https://github.com/Comfy-Org/ComfyUI_frontend/pull/5142
- Also fixed [this
case](https://f01efc75.comfyui-playwright-chromium.pages.dev/#?testId=35f0453d615a452757ca-379124415c5b7e9060d2)
(test case for responsive sizing) as it was using outdated logic. New
logic: ensures that the nav is collapsed on mobile but visible on tablet
and desktop screen sizes. It also ensures that, at all the main
breakpoints, at least 1 card is visible (covers bug that the case was
originally written for wherein the nav was fully covering the cards at
narrow screen widths).

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5802-ci-Regenerate-Playwright-screenshot-baselines-27a6d73d365081768211da0d24bad2c3)
by [Unito](https://www.unito.io)

---------

Co-authored-by: github-actions <github-actions@github.com>
2025-09-26 13:10:41 -07:00
Arjan Singh
62441fa0f9 [fix] do not drag on right-click + fix refs (#5784)
## Summary

Fixes drag handling logic.

## Changes

Only check for drag on left-click.

Adds handler logic for following pointer events:
1. drag termination
2. context menu
3. pointer cancel

Adds tests.

Consolidates cleanup tasks.

## Screenshots

Fixed State:

Ignore first failed drag, browser window didn't have context.


https://github.com/user-attachments/assets/00ec685a-1ef7-4102-b19b-4cdb9b201d22

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5784-fix-do-not-drag-on-right-click-fix-refs-27a6d73d3650812ea797fccf14022568)
by [Unito](https://www.unito.io)
2025-09-26 12:05:36 -07:00
Rizumu Ayaka
c96f719f91 feat: dropdown widgets vue node ui (#5624)
- Load media dropdown widgets
- Load models dropdown widgets

I added a lot of feedback effects during interactions.
I tried my best to break the Dropdown into small components.
To make it more flexible, I provided many configurable props and
v-model.

<img width="1000" alt="CleanShot 2025-09-18 at 01 54 38"
src="https://github.com/user-attachments/assets/1a413078-1547-44b8-8b48-1ce8f8e764b5"
/>

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5624-feat-dropdown-widgets-vue-node-ui-2716d73d36508115a52bc1fb6d6376d0)
by [Unito](https://www.unito.io)

---------

Co-authored-by: Christian Byrne <cbyrne@comfy.org>
2025-09-26 12:04:39 -07:00
filtered
9f19d8fb4b Migrate Tailwind styles to design-system package (#5794)
## Summary

Consolidates Tailwind configuration and styles into a shared
`@comfyorg/design-system` package for reuse across monorepo apps.

The goal was not to make changes to how the design system works; merely
to separate it cleanly. I _would_ strongly recommend some drastic
sweeping changes, however I believe that should be done after the
migration.

## Changes

- **What**: Migrates CSS files, Tailwind config, and custom icons to
design-system package
- **Dependencies**: Moves `@iconify-json/lucide` and `@iconify/tailwind`
to design-system package
2025-09-26 12:01:53 -07:00
Johnpaul Chiwetelu
d954336973 New Workflow Templates Modal (#5142)
This pull request refactors and simplifies the template workflow card
components and related UI in the codebase. The main changes focus on
removing unused or redundant components, improving visual and
interaction consistency, and enhancing error handling for images. Below
are the most important changes grouped by theme:

**Template Workflow Card Refactor and Cleanup**

* Removed the `TemplateWorkflowCard.vue` component and its associated
test file `TemplateWorkflowCard.spec.ts`, as well as the
`TemplateWorkflowCardSkeleton.vue` and `TemplateWorkflowList.vue`
components, indicating a shift away from the previous card-based
template workflow UI.
[[1]](diffhunk://#diff-49569af0404058e8257f3cc0716b066517ce7397dd58744b02aa0d0c61f2a815L1-L139)
[[2]](diffhunk://#diff-9fa6fc1470371f0b520d4deda4129fb313b1bea69888a376556f4bd824f9d751L1-L263)
[[3]](diffhunk://#diff-bc35b6f77d1cee6e86b05d0da80b7bd40013c7a6a97a89706d3bc52573e1c574L1-L30)
[[4]](diffhunk://#diff-48171f792b22022526fca411d3c3a366d48b675dab77943a20846ae079cbaf3bL1-L68)
* Removed the `TemplateSearchBar.vue` component, suggesting a redesign
or replacement of the search/filter UI for templates.

**UI and Interaction Improvements**

* Improved the `CardBottom.vue` component by making its height
configurable via a `fullHeight` prop, enhancing layout flexibility.
* Updated the `CardContainer.vue` component to add hover effects
(background, border, shadow, and padding) and support a new `none`
aspect ratio for more flexible card layouts.

**Image and Input Enhancements**

* Enhanced the `LazyImage.vue` component to display a default
placeholder image when an image fails to load, improving error handling
and user experience.
* Improved the `SearchBox.vue` component by making the input focusable
when clicking anywhere on the wrapper, and added a template ref for
better accessibility and usability.
[[1]](diffhunk://#diff-08f3b0c51fbfe63171509b9944bf7558228f6c2596a1ef5338e88ab64585791bL2-R5)
[[2]](diffhunk://#diff-08f3b0c51fbfe63171509b9944bf7558228f6c2596a1ef5338e88ab64585791bL16-R17)
[[3]](diffhunk://#diff-08f3b0c51fbfe63171509b9944bf7558228f6c2596a1ef5338e88ab64585791bR33-R39)

**Minor UI Tweaks**

* Adjusted label styling in `SingleSelect.vue` to remove unnecessary
overflow handling, simplifying the visual layout.

---------

Co-authored-by: Benjamin Lu <benceruleanlu@proton.me>
Co-authored-by: Alexander Brown <drjkl@comfy.org>
Co-authored-by: github-actions <github-actions@github.com>
Co-authored-by: Christian Byrne <cbyrne@comfy.org>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: snomiao <snomiao@gmail.com>
Co-authored-by: GitHub Action <action@github.com>
Co-authored-by: filtered <176114999+webfiltered@users.noreply.github.com>
Co-authored-by: Comfy Org PR Bot <snomiao+comfy-pr@gmail.com>
Co-authored-by: Jin Yi <jin12cc@gmail.com>
2025-09-26 11:52:19 -07:00
snomiao
49f373c46f [chore] Replace npx with pnpx across the codebase (#5329)
## Summary
- Migrated all `npx` commands to `pnpx` to align with the pnpm ecosystem
- Updated all occurrences across the codebase for consistency

## Changes
- **Package.json scripts**: Updated test:browser, preinstall, and
collect-i18n scripts
- **GitHub Actions workflows**: Updated all workflow files that use npx
(test-ui, i18n, update-manager-types, etc.)
- **Documentation**: Updated browser_tests/README.md and
docs/extensions/development.md
- **Husky pre-commit hook**: Updated lint-staged and tsx commands
- **MCP configuration**: Updated .mcp.json to use pnpx

## Test Plan
- [ ] CI tests pass
- [ ] Local development commands work with pnpx
- [ ] GitHub Actions workflows execute successfully

🤖 Generated with Claude Code

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5329-chore-Replace-npx-with-pnpx-across-the-codebase-2646d73d36508127bd43d14b8364aeb1)
by [Unito](https://www.unito.io)
2025-09-26 10:06:41 -07:00
AustinMroz
9678a87846 Subgraph widget promotion - Part 2 (#5617)
Implements proxyWidget support on subgraph nodes.  This registers a
special proxyWidgets property on subgraph nodes which is directly mapped
to the proxyWidgets displayed on the node. Each proxyWidget directly
maps to a real widget inside the subgraph.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5617-Subgraph-widget-promotion-Part-2-2716d73d3650813d8621fefdce6ae518)
by [Unito](https://www.unito.io)

---------

Co-authored-by: GitHub Action <action@github.com>
2025-09-26 10:24:55 -05:00
snomiao
3a9365af13 fix(collect-i18n-node-defs): refactor to run ComfyNodeDefImpl only in browser context (#5775)
## The Problem

The `collect-i18n-node-defs.ts` script started failing ~3 weeks ago when
Vue nodes were introduced ([commit
006e6bd57](https://github.com/Comfy-Org/ComfyUI_frontend/commit/006e6bd57),
[PR #4263](https://github.com/Comfy-Org/ComfyUI_frontend/pull/4263)).
The issue stems from:

1. **Import chain bringing Vue components into Node.js context:**
   ```
   collect-i18n-node-defs.ts
     ↓ imports
   ComfyNodeDefImpl (from nodeDefStore.ts)
     ↓ imports
   useSubgraphStore (from subgraphStore.ts)
     ↓ transitively imports
   executionStore.ts
     ↓ imports
   ChatHistoryWidget.vue (Vue component!)
   ```

2. **TypeScript `declare` fields causing Babel errors:**
   ```
TypeScript 'declare' fields must first be transformed by
@babel/plugin-transform-typescript
   ```

## This Solution vs PR #5515

### PR #5515 Approach (Complex)
- Adds custom Babel plugins and configurations
- Implements automatic browser globals injection
- Requires **47,517 additions, 9,469 deletions**
- Modifies the entire Playwright babel transformation pipeline

### This PR's Approach (Simple)
- Uses dynamic imports to defer module loading until runtime
- Avoids Babel compilation of problematic TypeScript/Vue files
- **Only 40 lines changed** in a single file
- No configuration changes needed

## How This Fix Works

```typescript
// Instead of static import that Babel tries to compile:
// import { ComfyNodeDefImpl } from '../src/stores/nodeDefStore'

// We use:
// 1. Type-only import (erased at runtime)
import type { ComfyNodeDefImpl } from '../src/stores/nodeDefStore'

// 2. Dynamic import at runtime (bypasses Babel)
const { ComfyNodeDefImpl: ComfyNodeDefImplClass } = await import(
  '../src/stores/nodeDefStore'
)
```

---------

Co-authored-by: github-actions <github-actions@github.com>
2025-09-25 22:56:17 -07:00
Christian Byrne
3ee0d394ca fix flaky version mismatch warning browser test (#5792)
Adds a wait to ensure the dismissed state is saved to localstorage
properly before `setup` (page reload).

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5792-fix-broken-test-27a6d73d365081e69c1febbc246448fa)
by [Unito](https://www.unito.io)
2025-09-25 21:15:03 -07:00
Johnpaul Chiwetelu
6a70152220 Wheel Selection Toolbox and Popover (#5781)
This pull request updates event handling in the selection toolbox
components to ensure that mouse wheel events are properly forwarded to
the canvas, improving interaction consistency across the UI.

**Event handling improvements:**

* Changed the wheel event handler in `SelectionToolbox.vue` to use
`canvasInteractions.forwardEventToCanvas` instead of
`canvasInteractions.handleWheel`, ensuring wheel events are consistently
forwarded to the canvas.
* Added the wheel event handler to the `MoreOptions.vue` popover,
forwarding wheel events to the canvas for submenu interactions.

**Code organization updates:**

* Imported `useCanvasInteractions` in `MoreOptions.vue` to access the
new event forwarding method.
* Instantiated `canvasInteractions` in `MoreOptions.vue` for use in
event handling.## Summary


https://github.com/user-attachments/assets/46046086-35e8-4cd1-a11a-365705beb9cd

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5781-Wheel-Selection-Toolbox-and-Popover-27a6d73d3650812c9c4fe8440ff7dd1d)
by [Unito](https://www.unito.io)
2025-09-25 20:51:24 -07:00
Jin Yi
5c1e00ff8e Refactor conflict detection system and move to manager extension (#5436)
## Refactor conflict detection system and move to manager extension

### Description

This PR refactors the conflict detection system, moving it from the
global composables to the manager extension folder for better code
organization. Additionally, it improves test type safety and adds
comprehensive test coverage for utility functions.

### Main Changes

#### 📦 Code Organization
- **Moved conflict detection to manager extension** - Relocated all
conflict detection related composables, stores, and utilities from
global scope to `/workbench/extensions/manager/` for better modularity
(https://github.com/Comfy-Org/ComfyUI_frontend/pull/5722)
- **Moved from** `src/composables/useConflictDetection.ts` **to**
`src/workbench/extensions/manager/composables/useConflictDetection.ts`
- Moved related stores and composables to maintain cohesive module
structure

#### ♻️ Refactoring
- **Extracted utility functions** - Split conflict detection logic into
separate utility modules:
  - `conflictUtils.ts` - Conflict consolidation and summary generation
- `systemCompatibility.ts` - OS and accelerator compatibility checking
  - `versionUtil.ts` - Version compatibility checking
- **Removed duplicate state management** - Cleaned up redundant state
and unused functions
- **Improved naming conventions** - Renamed functions for better clarity
- **Removed unused system environment code** - Cleaned up deprecated
code

#### 🔧 Test Improvements
- **Fixed TypeScript errors** in all test files - removed all `any` type
usage
- **Added comprehensive test coverage**:
  - `conflictUtils.test.ts` - 299 lines of tests for conflict utilities
- `systemCompatibility.test.ts` - 270 lines of tests for compatibility
checking
  - `versionUtil.test.ts` - 342 lines of tests for version utilities
- **Updated mock objects** to match actual implementations
- **Aligned with backend changes** - Updated SystemStats structure to
include `pytorch_version`, `embedded_python`,
`required_frontend_version`

#### 🐛 Bug Fixes
- **Fixed OS detection bug** - Resolved issue where 'darwin' was
incorrectly matched as 'Windows' due to containing 'win' substring
- **Fixed import paths** - Updated all import paths after moving to
manager extension
- **Fixed unused exports** - Removed all unused function exports
- **Fixed lint errors** - Resolved all ESLint and Prettier issues

### File Structure Changes

```
Before:
src/
├── composables/
│   └── useConflictDetection.ts (1374 lines)
└── types/

After:
src/
├── utils/
│   ├── conflictUtils.ts (114 lines)
│   ├── systemCompatibility.ts (125 lines)
│   └── versionUtil.ts (enhanced)
└── workbench/extensions/manager/
    ├── composables/
    │   ├── useConflictDetection.ts (758 lines)
    │   └── [other composables]
    └── stores/
        └── conflictDetectionStore.ts
```

### Testing

All tests pass successfully:
-  **155 test files passed**
-  **2209 tests passed**
-  19 skipped (intentionally skipped subgraph-related tests)

### Impact

- **Better code organization** - Manager-specific code is now properly
isolated
- **Improved maintainability** - Smaller, focused utility functions are
easier to test and maintain
- **Enhanced type safety** - No more `any` types in tests
- **Comprehensive test coverage** - All utility functions are thoroughly
tested

### Commits in this PR
1. OS detection bug fix and refactor
2. Remove unused system environment code
3. Improve function naming
4. Refactor conflict detection
5. Remove duplicate state and unused functions
6. Fix unused function exports
7. Move manager features to workbench extension folder
8. Fix import paths
9. Rename systemCompatibility file
10. Improve test type safety
11. Apply ESLint and Prettier fixes

## Screenshots (if applicable)

[screen-capture.webm](https://github.com/user-attachments/assets/b4595604-3761-4d98-ae8e-5693cd0c95bd)


┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5436-Manager-refactor-conflict-detect-2686d73d36508186ba06f57dae3656e5)
by [Unito](https://www.unito.io)

---------

Co-authored-by: Claude <noreply@anthropic.com>
2025-09-26 12:21:05 +09:00
Christian Byrne
78d585eca0 fix restoring outputs in Vue nodes (persisting node outputs when switching between workflow tabs) (#5788)
## Summary

Fixed Vue node output restoration by consolidating state management
through the node output store.

## Changes

- **What**: Refactored node output cleanup and restoration logic in
`ChangeTracker` and `ComfyApp` to use centralized store methods
- **Breaking**: Removed direct manipulation of `app.nodeOutputs` in
favor of store-managed state

## Review Focus

State synchronization between `app.nodeOutputs` and `nodeOutputs.value`
during restore/reset operations, ensuring Vue reactivity is maintained.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5788-fix-restoring-outputs-in-Vue-nodes-persisting-node-outputs-when-switching-between-workfl-27a6d73d365081b39411fed7479d8ac5)
by [Unito](https://www.unito.io)
2025-09-25 19:49:49 -07:00
Christian Byrne
a6600aa109 Use localized labels for Vue node slots and inputs/outputs (fallback to names) (#5773)
## Summary

Fixes https://github.com/Comfy-Org/ComfyUI_frontend/issues/5697 by
adding internationalization support for Vue node widget labels and
output slot names with fallback to original names.

## Changes

- **What**: Added `label` field to widget data structures and
`localized_name` support for output slots
- **Breaking**: None - all changes maintain backward compatibility with
fallback to original names

## Review Focus

Translation key resolution for node definitions and proper fallback
chain for widget labels and slot names.

## Screenshots (if applicable)

Observe that there is parity between Litegraph and Vue nodes w.r.t.
localization of labels/names.

<img width="3455" height="1417" alt="Screenshot from 2025-09-25
13-19-54"
src="https://github.com/user-attachments/assets/93ee8fae-3671-4175-a941-6462f942d0f1"
/>

<img width="3455" height="1417" alt="Screenshot from 2025-09-25
13-19-38"
src="https://github.com/user-attachments/assets/9573eb86-d74a-40ed-a0b5-e30afd3da6eb"
/>

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5773-Use-localized-labels-for-Vue-node-slots-and-inputs-outputs-fallback-to-names-2796d73d365081e08fb7d38464f4aa90)
by [Unito](https://www.unito.io)

---------

Co-authored-by: github-actions <github-actions@github.com>
Co-authored-by: Alexander Brown <drjkl@comfy.org>
2025-09-25 19:47:18 -07:00
Christian Byrne
c033e9e4d7 Add theme-aware styling for Vue node tooltips (#5786)
Adds light theme colors from the
[designs](https://www.figma.com/design/31uH3r4x3xbIctuRWYW6NM/V3---Nodes?node-id=6267-16837&m=dev).

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5786-Add-theme-aware-styling-for-Vue-node-tooltips-27a6d73d36508167979fe797efcbe3c0)
by [Unito](https://www.unito.io)
2025-09-25 19:08:22 -07:00
Christian Byrne
ac93a6ba3f Disable number grouping (thousands comma separators) by default in Vue node number widgets (#5776)
## Summary

Makes the
[useGrouping](https://primevue.org/inputnumber/#api.inputnumber.props.useGrouping)
prop for number widgets disabled by default, aligning with the old UI
(also requested via design). Node authors can still enable if they want
by setting prop explicitly.

## Changes

- **What**: Modified
[WidgetInputNumberInput](https://primevue.org/inputnumber/) to disable
`useGrouping` by default, requiring explicit opt-in via widget options
- **Testing**: Added component tests covering value binding, component
rendering, step calculations, and grouping behavior

## Review Focus

UX impact on existing nodes that may have relied on default grouping
behavior and test coverage for edge cases with precision calculations.

## Screenshots (if applicable)

*Before*:

<img width="1685" height="879" alt="Screenshot from 2025-09-25 11-34-34"
src="https://github.com/user-attachments/assets/432097ab-203d-4f86-8ca0-721b27ee33de"
/>

*After*:

<img width="1951" height="1175" alt="Screenshot from 2025-09-25
11-35-27"
src="https://github.com/user-attachments/assets/74d35b62-612e-4dbf-b6e2-0ac17af03ea1"
/>


┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5776-Disable-number-grouping-thousands-comma-separators-by-default-in-Vue-node-number-widget-2796d73d365081369ca6c155335d0d57)
by [Unito](https://www.unito.io)

---------

Co-authored-by: DrJKL <448862+DrJKL@users.noreply.github.com>
Co-authored-by: Alexander Brown <drjkl@comfy.org>
Co-authored-by: github-actions <github-actions@github.com>
2025-09-25 17:46:46 -07:00
filtered
961af8731e Split Tailwind utility functions out to a shared package (#5777)
## Summary

Split tailwind utils out to a shared package within the monorepo.

## Changes

- Creates `@comfyorg/tailwind-utils` package
- Does not require export, publishing, etc
- Uses `export` to ensure this change does not impact other PRs (many
imports to update)
- If we _want_ to update all imports, there are two commits ready to be
re-applied
  - e.g. `git revert 80960c2a82c0d1ac06eee1bb83ac333216b2b376`

## Review Focus

- Is this pattern desirable?
- Should we just include this in a broader design-system split? I kind
of vote yes, but also it's a good small, first step.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5777-Split-Tailwind-utility-functions-out-to-a-shared-package-2796d73d3650815f976fc73b4fb86ef3)
by [Unito](https://www.unito.io)
2025-09-25 16:55:21 -07:00
Simula_r
703de3e669 Fix/vue nodes markdown (#5771)
## Summary

Improve markdown node by making the textarea match the rendered output
width and height.

## Screenshots (if applicable)


https://github.com/user-attachments/assets/4701f947-0a4f-40f3-83c0-94e53cd10106


┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5771-Fix-vue-nodes-markdown-2796d73d365081129428fe9c38178cc4)
by [Unito](https://www.unito.io)
2025-09-25 13:21:17 -07:00
Christian Byrne
415ebfd67b Add muted state to Vue nodes (#5770)
## Summary

Added mute state support to Vue nodes with visual feedback and keyboard
shortcut functionality.

## Changes

- **What**: Implemented mute state (mode 2) for Vue nodes with opacity
styling and `Ctrl+M` hotkey support

## Review Focus

Visual consistency between bypass and mute states, and keyboard shortcut
conflict detection with existing hotkeys.

## Test Coverage

- Single node mute/unmute with `Ctrl+M` hotkey
- Multi-selection mute/unmute operations
- Visual state verification with opacity changes

## Related

- https://github.com/Comfy-Org/ComfyUI_frontend/pull/5715

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5770-Add-muted-state-to-Vue-nodes-2796d73d36508143b3edfbcb782de7c1)
by [Unito](https://www.unito.io)
2025-09-25 13:07:29 -07:00
Alexander Brown
97542efc9b Refactor: Viewport Culling improvements (#5767)
## Summary

Reduce the number of DOM changes, but also restructure the logic to
create a clean point of separation for when we can make DragAndScale
reactive.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5767-Refactor-Viewport-Culling-improvements-2796d73d365081708f02cb8033aac9b1)
by [Unito](https://www.unito.io)
2025-09-25 11:23:15 -07:00
Arjan Singh
13ce23399c Asset Browser Design Review + Filters (#5737)
## Summary

Fixed design feedback and wired up the filter controls.

## Review Focus

Design Feedback:
-
[4872888](48728881af)
-
[9a0b63e](9a0b63edce)

Filters Hookup:
-
[07f22f8](07f22f8074)

Misc (can focus less on):
- claude guidance:
[23e6fa9](23e6fa9723)
- test helpers:
[7801ed9](7801ed9e28)

## Screenshots (if applicable)
<img width="1534" height="1175" alt="Screenshot 2025-09-23 at 1 03
12 PM"
src="https://github.com/user-attachments/assets/d82088e4-7d72-4c6f-904e-5180774d64a5"
/>

<img width="1794" height="793" alt="Screenshot 2025-09-23 at 1 03 22 PM"
src="https://github.com/user-attachments/assets/56eac2ba-5ecc-4a20-843f-ce683dea668c"
/>

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5737-Asset-Browser-Design-Review-Filters-2776d73d3650813e890bd16fa6a0433f)
by [Unito](https://www.unito.io)

---------

Co-authored-by: Alexander Brown <drjkl@comfy.org>
Co-authored-by: GitHub Action <action@github.com>
2025-09-25 11:17:26 -07:00
Christian Byrne
a0c06bd723 [test] add browser test for missing vue nodes error state (#5768)
## Summary

Added browser test to verify Vue nodes display error state when workflow
contains missing/unknown nodes, complementing

- https://github.com/Comfy-Org/ComfyUI_frontend/pull/5758

## Changes

- **What**: Added [Playwright
test](https://playwright.dev/docs/writing-tests) for Vue nodes error
state handling with missing nodes
- **Test Coverage**: Validates `border-error` class application on nodes
with `UNKNOWN NODE` text

## Review Focus

Test reliability when loading workflows with missing nodes and dialog
interaction timing.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5768-test-add-browser-test-for-missing-vue-nodes-error-state-2796d73d365081aea187cdbc7920a643)
by [Unito](https://www.unito.io)
2025-09-25 11:16:19 -07:00
Christian Byrne
3fc17ebdac [refactor] Migrate manager code from src/composables to src/workbench/extensions/manager (2/2) (#5722)
## Summary

Continuation of

- https://github.com/Comfy-Org/ComfyUI_frontend/pull/5662

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5722-refactor-Migrate-manager-code-from-src-composables-to-src-workbench-extensions-manag-2766d73d36508165a4f5e1940967248f)
by [Unito](https://www.unito.io)

---------

Co-authored-by: Alexander Brown <drjkl@comfy.org>
2025-09-25 02:40:04 +00:00
Comfy Org PR Bot
0db2a2c03e 1.28.2 (#5766)
Patch version increment to 1.28.2

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5766-1-28-2-2796d73d365081d6bcd9e4ee06f75d5f)
by [Unito](https://www.unito.io)

Co-authored-by: AustinMroz <4284322+AustinMroz@users.noreply.github.com>
2025-09-24 21:20:53 -05:00
Arjan Singh
b96bf3871c Fixes nits for #5758 (#5763)
## Summary

1. Fixes nits for #5758
2. Adds some git ignores

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5763-Fixes-nits-for-5758-2796d73d36508192b3e6feecfcacf38b)
by [Unito](https://www.unito.io)

---------

Co-authored-by: filtered <176114999+webfiltered@users.noreply.github.com>
2025-09-24 20:45:33 -05:00
Benjamin Lu
bc4549244e test(e2e): align test default menu to Top; make legacy specs explicit (#5746)
## Summary
UseNewMenu has been defaulted to Top in the app for over a year;
Playwright’s test default lagged behind. This PR aligns the test default
with reality and keeps legacy specs stable.

## Changes
- tests(e2e): default to 'Top' via fixture; specs that previously relied
on the old implicit default now explicitly set 'Comfy.UseNewMenu' to
'Disabled'.
- docs(browser-tests): remove outdated README note suggesting tests set
'Top' manually.

## Review Focus
- Intentional uses of 'Top' and 'Bottom' remain unchanged.
- Confirm ComfyPage default remains 'Top' (see
browser_tests/fixtures/ComfyPage.ts).

## Screenshots (if applicable)
N/A

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5746-test-e2e-align-test-default-menu-to-Top-make-legacy-specs-explicit-2786d73d365081218d06c1346f3ae18e)
by [Unito](https://www.unito.io)

---------

Co-authored-by: github-actions <github-actions@github.com>
2025-09-24 18:43:22 -07:00
Simula_r
f3e68804e8 fix: prevent pointer events on widgets when in LOD (#5762)
## Summary

Prevent pointer events on widgets themselves when isLOD.

Fixes:
https://www.notion.so/comfy-org/LOD-state-allows-pointer-events-on-widgets-2786d73d3650800da600d7b52c66475a?source=copy_link

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5762-fix-prevent-pointer-events-on-widgets-when-in-LOD-2796d73d365081ac95d4ec27eda5027c)
by [Unito](https://www.unito.io)

---------

Co-authored-by: JakeSchroeder <jake@axiom.co>
2025-09-24 17:53:32 -07:00
Arjan Singh
1749cfa678 [fix] properly show error states (#5758)
## Summary

I want to take a more general look at `comfyApp.graph.onTrigger` but
this is the cleanest fix I could come up with for #5694.

I will explore simplifying onTrigger in a separate PR.

## Changes

1. Create a `node:slot-errors:changed` trigger.
2. Trigger it if we find any of the node slots have errors.
3. Check each node to see if there is any error present.
4. Add an error class if there are.

## Screenshots (if applicable)

Working error states!

<img width="1049" height="987" alt="Screenshot 2025-09-23 at 8 40 04 PM"
src="https://github.com/user-attachments/assets/30e13283-129c-4d9c-b342-e7037582998a"
/>

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5758-fix-properly-show-error-states-2786d73d365081cbbf62c314c7f5f380)
by [Unito](https://www.unito.io)

---------

Co-authored-by: Alexander Brown <drjkl@comfy.org>
2025-09-24 16:34:39 -07:00
Alexander Brown
0fea54c542 Typing: Slots in VueNodeData (#5759)
## Summary

Replace the unknown type with the interface in Litegraph.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5759-Typing-Slots-in-VueNodeData-2786d73d36508194b286fad172b13c51)
by [Unito](https://www.unito.io)
2025-09-24 14:35:14 -07:00
filtered
cd7666e3bc Update desktop docs to platform-specific URLs (#5757)
Fixes #5751

## Summary
- Replaces outdated Notion link with docs.comfy.org URLs
- Uses Electron API to detect platform for Windows/macOS specific docs

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5757-Update-desktop-docs-to-platform-specific-URLs-2786d73d36508188a200ed3ad91b836f)
by [Unito](https://www.unito.io)
2025-09-24 14:28:53 -07:00
snomiao
8d1261133a [bugfix] Stabilize flaky load audio widget test (#5755)
## Summary
This PR fixes a flaky test in the load audio widget spec that was
causing intermittent failures in CI.

## Problem
The test was attempting to trigger a file chooser event before the
widget's upload button was fully rendered and ready for interaction,
leading to race conditions.

## Solution
Added an explicit wait for the widget element to be visible before
triggering the file chooser, ensuring the DOM is ready for interaction.

## Changes
- Added `waitFor` to verify widget visibility before file upload
- Ensures proper synchronization between DOM updates and test actions

## Test plan
- [x] Browser tests pass locally
- [x] Typecheck passes
- [ ] CI tests pass

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

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5755-bugfix-Stabilize-flaky-load-audio-widget-test-2786d73d365081cebaefdc6470333c5d)
by [Unito](https://www.unito.io)

---------

Co-authored-by: Claude <noreply@anthropic.com>
2025-09-24 14:27:46 -07:00
Simula_r
0919856a05 Feat/vue nodes preview (#5747)
## Summary

Create a LGraphNodePreview.vue component to use Vue Nodes for preview
when hovering over search results / sidebar tree list.

<!-- If this PR fixes an issue, uncomment and update the line below -->
<!-- Fixes #ISSUE_NUMBER -->

## Screenshots (if applicable)

<img width="3024" height="1642" alt="image"
src="https://github.com/user-attachments/assets/d102b08e-2970-407b-aff8-3fa6333d5e38"
/>
<img width="3024" height="1646" alt="image (1)"
src="https://github.com/user-attachments/assets/b5d378d5-3cf6-4cca-9fa1-741647e8d72c"
/>

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5747-Feat-vue-nodes-preview-2786d73d3650817dbf9af458bd5dda8c)
by [Unito](https://www.unito.io)

---------

Co-authored-by: JakeSchroeder <jake@axiom.co>
Co-authored-by: AustinMroz <AustinMroz@users.noreply.github.com>
2025-09-24 13:02:47 -07:00
filtered
b0f81b2245 Rework desktop install / startup UX (#5292)
### Summary

Complete redesign of Desktop app installer, implementing the new app
startup progress system and error reporting.
2025-09-24 12:06:58 -07:00
Alexander Brown
6449d26cee cleanup: remove useCanvasTransformSync composables. (#5742)
No I am not proud of the new placeholder arguments.

## Summary

Small change to unify the two composables before updating the logic.
Edit: Now unifies them both into the **void**.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5742-cleanup-unify-useCanvasTransformSync-composables-2776d73d36508147ad39d11de8588b2e)
by [Unito](https://www.unito.io)
2025-09-23 21:36:29 -07:00
Tristan Sommer
80cabc61ee fix: maskeditor - fixed color select and paint bucket settings not showing up (#5733)
## Summary

color select settings and paint bucket settings were not showing up -
fixed that

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5733-fix-maskeditor-fixed-color-select-and-paint-bucket-settings-not-showing-up-2776d73d365081e6be2ddc7784ab8535)
by [Unito](https://www.unito.io)
2025-09-23 16:27:16 -07:00
Arjan Singh
76dd935b35 [fix] use object-contain for image preview (#5739)
## Summary

Change css so preview images are fully contained within the image
preview component

## Screenshots (if applicable)

### Before

<img width="453" height="640" alt="Screenshot 2025-09-23 at 2 55 56 PM"
src="https://github.com/user-attachments/assets/892c7fd4-b9d9-4fdd-9846-d480c5aeb6be"
/>

### After

<img width="523" height="673" alt="Screenshot 2025-09-23 at 2 55 26 PM"
src="https://github.com/user-attachments/assets/ca3f4823-9d57-4bf4-93cc-492652ee9631"
/>

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5739-fix-use-object-contain-for-image-preview-2776d73d365081f3b77ce498bd8799ec)
by [Unito](https://www.unito.io)
2025-09-23 16:04:54 -07:00
Alexander Brown
f7f3240100 fix: Status indicator and close button appearing together (#5738)
## Summary

Small fix for the close/status visibility overlap

<img width="392" height="128" alt="image"
src="https://github.com/user-attachments/assets/af25f1d7-a8c3-4155-9123-9fa10724e8db"
/>

![20250923-2140-26
4569642](https://github.com/user-attachments/assets/e1b00a3f-d6e9-416b-9014-df0f9241082e)

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5738-fix-Status-indicator-and-close-button-appearing-together-2776d73d3650813e9601e519c8a85043)
by [Unito](https://www.unito.io)

---------

Co-authored-by: github-actions <github-actions@github.com>
2025-09-23 15:23:00 -07:00
Simula_r
e4022c455a fix: add LODFallback to markdown widget (#5734)
## Summary

Add LOD fallback to markdown widget

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5734-fix-add-LODFallback-to-markdown-widget-2776d73d36508128b923d45beab50f03)
by [Unito](https://www.unito.io)

---------

Co-authored-by: JakeSchroeder <jake@axiom.co>
2025-09-23 13:05:15 -07:00
filtered
c6e50d8f1b Fix overlapping elements in desktop installer (#5735)
Fixes regression from Tailwind v4 upgrade where CPU toggle overlaps
content.

Removed unnecessary translate transform causing element positioning
issues.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5735-Fix-overlapping-elements-in-desktop-installer-2776d73d365081299affc7894bc88faa)
by [Unito](https://www.unito.io)
2025-09-23 14:50:44 -05:00
snomiao
c004a2b8bd chore(tsconfig): ensure complete TypeScript coverage for all project files (#5655)
## Summary
- Added missing directories and files to tsconfig.json to ensure
complete TypeScript type checking coverage
- Expanded config file patterns to include all .mts configuration files
- Verified all 908 TypeScript files in the project are now properly
covered

## Changes
- Added `scripts/**/*.ts` to cover all TypeScript files in scripts
directory (i18n collection, CI/CD scripts)
- Added `build/**/*.ts` to cover customIconCollection.ts and future
build scripts
- Changed `vite.config.mts` to `*.config.mts` to include all vite config
files (vite.electron.config.mts, vite.types.config.mts)

## Test plan
- [x] Run `pnpm typecheck` to verify no TypeScript errors
- [x] Verified all TypeScript files are covered by tsconfig patterns
- [x] browser_tests/ directory confirmed to have its own extending
tsconfig

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

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5655-chore-tsconfig-ensure-complete-TypeScript-coverage-for-all-project-files-2736d73d36508103acbadc53ca2b2913)
by [Unito](https://www.unito.io)

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Alexander Brown <drjkl@comfy.org>
Co-authored-by: filtered <176114999+webfiltered@users.noreply.github.com>
2025-09-23 09:31:30 -07:00
snomiao
d0aee031e9 [feat] Merge ComfyUI_devtools into ComfyUI_frontend (#5166)
## Summary
Merges ComfyUI devtools components into the ComfyUI frontend monorepo to
consolidate development tools.

## Changes
- Added devtools components from ComfyUI repository
- Integrated development nodes and utilities
- Consolidated fake model assets for testing

## Related Issues
Fixes #4683

## Testing
- Devtools components are now available within the frontend monorepo
- Development workflow remains consistent

Co-authored-by: Claude <noreply@anthropic.com>
2025-09-22 23:15:50 -07:00
Simula_r
cec1de0147 feat: vue nodes LOD system (#5631)
## Summary

Replaced reactive (Vue-based) widget LOD with CSS visibility control.
Performance doesn't dramatically improve, but we avoid the mount/unmount
overhead during zoom/pan operations. This PR implements the visual
component of LOD—complex widgets that need lifecycle management will be
addressed separately.

### Problem & Solution
Problem: we want LOD to improve rendering performance and visual
feedback but discovered using reactivity in the current setup for it
meant mounting/unmounting caused worse lag than the performance it aimed
to fix. Switching to render all the details all the time but using css
visibility proved to be the best solution. However, it doesn't improve
rendering performance by much because the GPU texture size is the
bottleneck (from TransformPane.vue CSS transforms) and not
rasterization.

Solution: Keep all nodes/widgets mounted, use CSS visibility: hidden for
LOD. Trade memory for performance stability during zoom/pan/drag
operations.

### Technical Decision
We chose Performance > Memory:

- CSS transforms create a single GPU texture whose size depends on node
count, not widget complexity
- Mounting/unmounting hundreds of widgets during zoom = noticeable lag
from Vue VDOM diffing (since all components are mounted all the time
because of viewport culling challenge/trade off see
https://github.com/Comfy-Org/ComfyUI_frontend/pull/5510.)
- CSS visibility changes = no reactivity overhead, smooth interactions
- Result: Similar performance, but without interaction stutters

This is the visual layer only. If we want a hook into the LOD state per
node / widget that would be the next follow up system to implement.

### Next Steps (maybe)
- Chunked (split up single Transform Pane transform layer) when
rendering 1000+ nodes (maybe)
- ~~Selective unmounting API for widgets that register as "expensive"~~
- ~~Client bound hydration system~~

## Screenshots (if applicable)

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

<img width="1355" height="960" alt="image"
src="https://github.com/user-attachments/assets/41474d1b-9dbe-4240-a8cf-f4c9ff51d8e0"
/>
<img width="1354" height="963" alt="image"
src="https://github.com/user-attachments/assets/9f55edaa-5858-41b9-b6a8-c2d37e1649bd"
/>


┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5631-feat-vue-nodes-LOD-system-2726d73d365081c6a6c4e14aa634f19c)
by [Unito](https://www.unito.io)

---------

Co-authored-by: github-actions <github-actions@github.com>
2025-09-22 20:05:13 -07:00
Christian Byrne
b4976c1ddc Revert: Move VueFire persistence configuration to initialization (#5614) (#5729)
## Summary
This reverts PR #5614 which moved VueFire persistence configuration to
initialization.

## Reason for Revert

It breaks Google SSO login with error:

```
useErrorHandling.ts:12 FirebaseError: Firebase: Error (auth/argument-error).
    at createErrorInternal (index-c92d61ad.js:506:41)
    at _assert (index-c92d61ad.js:512:15)
    at _withDefaultResolver (index-c92d61ad.js:9237:5)
    at signInWithPopup (index-c92d61ad.js:9457:30)
    at executeAuthAction.createCustomer (firebaseAuthStore.ts:263:25)
    at executeAuthAction (firebaseAuthStore.ts:223:28)
    at Proxy.loginWithGoogle (firebaseAuthStore.ts:262:5)
    at Proxy.wrappedAction (pinia.mjs:1405:26)
    at useFirebaseAuthActions.ts:104:28
    at Object.signInWithGoogle (useErrorHandling.ts:39:22)
```

## Changes
- Reverts commit ea4e57b60 "Move VueFire persistence configuration to
initialization (#5614)"
- Restores previous Firebase auth persistence behavior

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5729-Revert-Move-VueFire-persistence-configuration-to-initialization-5614-2776d73d3650814c9b80d9c67c852874)
by [Unito](https://www.unito.io)
2025-09-22 19:04:08 -07:00
Alexander Brown
1611c7a224 Refactor: Further state management cleanup (#5727)
## Summary

Going through the GraphNodeManager and VueNodeLifecycle one property at
a time and removing the pieces that are not currently wired up or used
by the rest of the application

Fixes paste location by updating the layoutStore in LGraphCanvas (which
already mutates layoutStore elsewhere)

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5727-WIP-Refactor-Further-state-management-cleanup-2766d73d36508173b379c6009c194a5a)
by [Unito](https://www.unito.io)
2025-09-22 18:47:26 -07:00
Comfy Org PR Bot
d01081dab4 1.28.1 (#5728)
Patch version increment to 1.28.1

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5728-1-28-1-2776d73d365081b287e4fdc5cbcea07b)
by [Unito](https://www.unito.io)

Co-authored-by: AustinMroz <4284322+AustinMroz@users.noreply.github.com>
2025-09-22 20:36:12 -05:00
Alexander Brown
e5d4d07d32 Refactor: More state management simplification (#5721)
## Summary

Remove more procedural synchronization in favor of using reactive
references.

> Note: Also includes some fixes for issues caused during HMR.

## Review Focus

In testing it seems to work the same, but let me know if I missed
something.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5721-Refactor-More-state-management-simplification-2766d73d3650819b8d7ddc047c460f2b)
by [Unito](https://www.unito.io)
2025-09-22 13:15:33 -07:00
Alexander Piskun
f086377307 add pricing for new api nodes (#5724)
## Summary

Added prices for the new upcoming API nodes. Backport required.
2025-09-22 11:33:00 -07:00
filtered
687b9e659c Fix reroute ID 0 treated as invalid (#5723)
## Summary

Fixes old logic bug from refactor
https://github.com/Comfy-Org/litegraph.js/pull/602/files

## Changes

- Fixes truthy refactor to explicitly check undefined

## Review Focus

No expectation that this will impact prod, however it may impact
extensions IF someone has explicitly been setting link parentId to 0.
This would be very strange, as it would cause unexpected behaviour in
other parts of the code (which all explicitly check `undefined`).

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5723-Fix-reroute-ID-0-treated-as-invalid-2766d73d365081568124ce1f85cdf84e)
by [Unito](https://www.unito.io)
2025-09-22 11:13:38 -07:00
Christian Byrne
da0d51311b fix Vue node being dragged when interacting with widgets (e.g., resizing textarea) (#5719)
## Summary

Applying changes in
https://github.com/Comfy-Org/ComfyUI_frontend/pull/5516 to entire widget
wrapper.
 
## Changes

- **What**: Added `.stop` modifier to pointer events in NodeWidgets
component to prevent [event
propagation](https://developer.mozilla.org/en-US/docs/Web/API/Event/stopPropagation)

## Review Focus

Verify widget interactions remain functional while ensuring parent node
drag/selection behavior is properly isolated.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5719-fix-Vue-node-being-dragged-when-interacting-with-widgets-e-g-resizing-textarea-2766d73d3650815091adcd1d65197c7b)
by [Unito](https://www.unito.io)
2025-09-21 21:56:03 -07:00
Christian Byrne
e314d9cbd9 [refactor] Simplify current user resolved hook implementation (#5718)
## Summary

Refactored `onUserResolved` function in auth composable to use VueUse
`whenever` utility instead of manual watch implementation and use
`immediate` option instead of invoking manually before creating watcher.

## Changes

- **What**: Replaced manual watch + immediate check pattern with [VueUse
whenever](https://vueuse.org/shared/whenever/) utility in
`useCurrentUser.ts:37`

## Review Focus

Behavioral equivalence verification - `whenever` with `immediate: true`
should maintain identical callback timing and cleanup semantics.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5718-refactor-Simplify-current-user-resolved-hook-implementation-2766d73d365081008b6de156dd78f940)
by [Unito](https://www.unito.io)
2025-09-21 21:53:25 -07:00
Christian Byrne
95baf8d2f1 [style] update Vue node tooltip style (#5717)
## Summary

Change Vue node tooltips to align with
[design](https://www.figma.com/design/31uH3r4x3xbIctuRWYW6NM/V3---Nodes?node-id=6267-16837&m=dev)

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5717-style-update-Vue-node-tooltip-style-2766d73d365081bdb095faef17f6aeb6)
by [Unito](https://www.unito.io)
2025-09-21 20:01:33 -07:00
Christian Byrne
f951e07cea fix bypass hotkey in vue nodes and fix node data instrumentation setup issue when switching to Vue nodes after initial load (#5715)
## Summary

Fixed Vue node keybinding target element ID to enable
bypass/pin/collapse hotkeys in both LiteGraph and Vue rendering modes.

Also fixed a bug when starting in litegraph mode => switching to Vue
nodes without reloading => `graph.onTrigger` is set to `undefined` which
interferes with proper setup of node data instrumentation, among other
things.

## Changes

- **What**: Updated keybinding `targetElementId` from `graph-canvas` to
`graph-canvas-container` for node manipulation commands (parent of both
the canvas and transform pane -- vue nodes container).
- **What**: Added conditional `onTrigger` handler restoration in slot
layout sync to prevent Vue node manager conflicts

## Review Focus

Event handler precedence between Vue nodes and LiteGraph systems during
mode switching, ensuring hotkeys work consistently across rendering
modes.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5715-fix-bypass-hotkey-in-vue-nodes-and-fix-node-data-instrumentation-setup-issue-when-switchi-2756d73d3650815c8ec8d5e4d06232e3)
by [Unito](https://www.unito.io)
2025-09-21 17:32:12 -07:00
Christian Byrne
023e466dba fix using shift modifier to (de-)select Vue nodes (#5714)
## Summary

Fixes https://github.com/Comfy-Org/ComfyUI_frontend/issues/5688 by
adding shift modifier support for multi-selecting Vue nodes, enabling
standard shift+click selection behavior alongside existing
ctrl/cmd+click.

## Changes

- **What**: Updated Vue node event handlers to include `event.shiftKey`
in multi-select logic
- **Testing**: Added browser tests for both ctrl and shift modifier
selection behaviors

## Review Focus

Multi-select behavior consistency across different input modifiers and
platform compatibility (Windows/Mac/Linux shift key handling).

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5714-fix-using-shift-modifier-to-de-select-Vue-nodes-2756d73d365081bcb5e0fe80eacdb2f0)
by [Unito](https://www.unito.io)
2025-09-21 14:39:40 -07:00
Christian Byrne
abd6823744 [refactor] Remove redundant module comment (#5711)
Removes a comment added in initial Vue Nodes commit. The comment is
interpolated between import statements which is stylistically awkward
and it is almost totally redundant with the doc comment on the
composable:


c1d4709e96/src/renderer/extensions/vueNodes/layout/useNodeLayout.ts (L10-L14)

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5711-refactor-Remove-redundant-module-comment-2756d73d365081ef9bffe0257b3670f1)
by [Unito](https://www.unito.io)
2025-09-21 14:30:58 -07:00
Alexander Brown
c4c0e52e64 Refactor: Let LGraphNode handle more events itself (#5709)
## Summary

Don't route events up through GraphCanvas if the component itself can
handle the changes

## Changes

- **What**: Reduce the indirect access or action dispatch to
composables/stores.

## Review Focus

The behavior should be either equivalent or a little snappier than
before. Also, the local state in LGraphNode has (almost) all been
removed in favor of reacting to the nodeData prop.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5709-Refactor-Let-LGraphNode-handle-more-events-itself-2756d73d365081e6a88ce6241bceecc0)
by [Unito](https://www.unito.io)

---------

Co-authored-by: GitHub Action <action@github.com>
2025-09-20 22:14:30 -07:00
Christian Byrne
295332dc46 update CODEOWNERS (#5667)
Add explicit CODEOWNERS for new features to allow more domain-driven
review/approval/ownership processes.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5667-update-CODEOWNERS-2736d73d3650817ea52be9c4a8fe5ff2)
by [Unito](https://www.unito.io)
2025-09-20 20:11:35 -07:00
Christian Byrne
5c498348b8 fix: update to standardized mobile web app meta tag syntax (#5672)
## Summary

Fixed WebKit deprecation warning by updating to standardized mobile web
app meta tag syntax.

## Changes

- **What**: Replaced deprecated `apple-mobile-web-app-capable` with
cross-platform
[`mobile-web-app-capable`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/meta/name#mobile-web-app-capable)
meta tag to align with WebKit's move toward vendor-neutral standards

## Review Focus

Verify "Add to Home Screen" functionality still works on iOS/iPadOS and
that the WebKit console warning is resolved in production builds.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5672-fix-update-to-standardized-mobile-web-app-meta-tag-syntax-2736d73d3650811cb2a1f0b14ce0a0e7)
by [Unito](https://www.unito.io)
2025-09-20 20:10:51 -07:00
Alexander Brown
8133bd4b7b Refactor: Composable disentangling (#5695)
## Summary

Prerequisite refactor/cleanup to use a global store instead of having
nodes throw up events to a parent component that stores a reference to a
singleton service that itself bootstraps and synchronizes with a
separate service to maintain a partially reactive but not fully reactive
set of states that describe some but not all aspects of the nodes on
either the litegraph, the vue side, or both.

## Changes

- **What**: Refactoring, the behavior should not change.
- **Dependencies**: A type utility to help with Vue component props

## Review Focus

Is there something about the current structure that this could affect
that would not be caught by our tests or using the application?

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5695-Refactor-Composable-disentangling-2746d73d365081e6938ce656932f3e36)
by [Unito](https://www.unito.io)
2025-09-20 13:06:42 -07:00
Arjan Singh
fd12591756 [feat] integrate asset browser with widget system (#5629)
## Summary

Add asset browser dialog integration for combo widgets with full
animation support and proper state management.

(Thank you Claude from saving me me from merge conflict hell on this
one.)

## Changes

- Widget integration: combo widgets now use AssetBrowserModal for
eligible asset types
- Dialog animations: added animateHide() for smooth close transitions
- Async operations: proper sequencing of widget updates and dialog
animations
- Service layer: added getAssetsForNodeType() and getAssetDetails()
methods
- Type safety: comprehensive TypeScript types and error handling
- Test coverage: unit tests for all new functionality
- Bonus: fixed the hardcoded labels in AssetFilterBar

Widget behavior:
- Shows asset browser button for eligible widgets when asset API enabled
- Handles asset selection with proper callback sequencing
- Maintains widget value updates and litegraph notification

## Review Focus

I will call out some stuff inline.

## Screenshots


https://github.com/user-attachments/assets/9d3a72cf-d2b0-445f-8022-4c49daa04637

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5629-feat-integrate-asset-browser-with-widget-system-2726d73d365081a9a98be9a2307aee0b)
by [Unito](https://www.unito.io)

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: GitHub Action <action@github.com>
2025-09-20 11:44:18 -07:00
Christian Byrne
b3c939ff15 fix: add Safari requestIdleCallback polyfill (#5664)
## Summary

Implemented cross-browser requestIdleCallback polyfill to fix Safari
crashes during graph initialization.

## Changes

- **What**: Added
[requestIdleCallback](https://developer.mozilla.org/en-US/docs/Web/API/Window/requestIdleCallback)
polyfill following [VS Code's
pattern](https://github.com/microsoft/vscode/blob/main/src/vs/base/common/async.ts)
with setTimeout fallback for Safari
- **Breaking**: None - maintains existing GraphView behavior

## Review Focus

Safari compatibility testing and timeout handling in the 15ms fallback
window. Verify that initialization tasks (keybindings, server config,
model loading) still execute properly on Safari iOS.

## References

- [VS Code async.ts
implementation](https://github.com/microsoft/vscode/blob/main/src/vs/base/common/async.ts)
- Source pattern for our polyfill
- [MDN
requestIdleCallback](https://developer.mozilla.org/en-US/docs/Web/API/Window/requestIdleCallback)
- Browser API documentation
- [Safari requestIdleCallback
support](https://caniuse.com/requestidlecallback) - Browser
compatibility table

Fixes CLOUD-FRONTEND-STAGING-N

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5664-fix-add-Safari-requestIdleCallback-polyfill-2736d73d365081cdbcf1fb816fe098d6)
by [Unito](https://www.unito.io)

Co-authored-by: Claude <noreply@anthropic.com>
2025-09-19 23:34:15 -07:00
1311 changed files with 101372 additions and 71356 deletions

View File

@@ -294,7 +294,6 @@ echo "Last stable release: $LAST_STABLE"
1. Run complete test suite:
```bash
pnpm test:unit
pnpm test:component
```
2. Run type checking:
```bash
@@ -459,15 +458,15 @@ echo "Workflow triggered. Waiting for PR creation..."
3. **IMMEDIATELY CHECK**: Did release workflow trigger?
```bash
sleep 10
gh run list --workflow=release.yaml --limit=1
gh run list --workflow=release-draft-create.yaml --limit=1
```
4. **For Minor/Major Version Releases**: The create-release-candidate-branch workflow will automatically:
4. **For Minor/Major Version Releases**: The release-branch-create workflow will automatically:
- Create a `core/x.yy` branch for the PREVIOUS minor version
- Apply branch protection rules
- Document the feature freeze policy
```bash
# Monitor branch creation (for minor/major releases)
gh run list --workflow=create-release-candidate-branch.yaml --limit=1
gh run list --workflow=release-branch-create.yaml --limit=1
```
4. If workflow didn't trigger due to [skip ci]:
```bash
@@ -478,7 +477,7 @@ echo "Workflow triggered. Waiting for PR creation..."
```
5. If workflow triggered, monitor execution:
```bash
WORKFLOW_RUN_ID=$(gh run list --workflow=release.yaml --limit=1 --json databaseId --jq '.[0].databaseId')
WORKFLOW_RUN_ID=$(gh run list --workflow=release-draft-create.yaml --limit=1 --json databaseId --jq '.[0].databaseId')
gh run watch ${WORKFLOW_RUN_ID}
```

View File

@@ -246,7 +246,7 @@ For each commit:
3. Merge the PR: `gh pr merge --merge`
4. Monitor release workflow:
```bash
gh run list --workflow=release.yaml --limit=1
gh run list --workflow=release-draft-create.yaml --limit=1
gh run watch
```
5. Track progress:

View File

@@ -120,7 +120,6 @@ echo "Available commands:"
echo " pnpm dev - Start development server"
echo " pnpm build - Build for production"
echo " pnpm test:unit - Run unit tests"
echo " pnpm test:component - Run component tests"
echo " pnpm typecheck - Run TypeScript checks"
echo " pnpm lint - Run ESLint"
echo " pnpm format - Format code with Prettier"

View File

@@ -5,6 +5,10 @@ PLAYWRIGHT_TEST_URL=http://localhost:5173
# Proxy target of the local development server
# Note: localhost:8188 does not work.
# Cloud auto-detection: Setting this to any *.comfy.org URL automatically enables
# cloud mode (DISTRIBUTION=cloud) without needing to set DISTRIBUTION separately.
# Examples: https://testcloud.comfy.org/, https://stagingcloud.comfy.org/,
# https://pr-123.testenvs.comfy.org/, https://cloud.comfy.org/
DEV_SERVER_COMFYUI_URL=http://127.0.0.1:8188
# Allow dev server access from remote IP addresses.
@@ -19,10 +23,10 @@ TEST_COMFYUI_DIR=/home/ComfyUI
# Whether to enable minification of the frontend code.
ENABLE_MINIFY=true
# Whether to disable proxying the `/templates` route. If true, allows you to
# serve templates from the ComfyUI_frontend/public/templates folder (for
# locally testing changes to templates). When false or nonexistent, the
# templates are served via the normal method from the server's python site
# Whether to disable proxying the `/templates` route. If true, allows you to
# serve templates from the ComfyUI_frontend/public/templates folder (for
# locally testing changes to templates). When false or nonexistent, the
# templates are served via the normal method from the server's python site
# packages.
DISABLE_TEMPLATES_PROXY=false
@@ -33,3 +37,8 @@ DISABLE_VUE_PLUGINS=false
# Algolia credentials required for developing with the new custom node manager.
ALGOLIA_APP_ID=4E0RO38HS8
ALGOLIA_API_KEY=684d998c36b67a9a9fce8fc2d8860579
# Sentry ENV vars replace with real ones for debugging
# SENTRY_AUTH_TOKEN=private-token # get from sentry
# SENTRY_ORG=comfy-org
# SENTRY_PROJECT=cloud-frontend-staging

View File

@@ -25,3 +25,6 @@ e3bb29ceb8174b8bbca9e48ec7d42cd540f40efa
# [refactor] Improve updates/notifications domain organization (#5590)
27ab355f9c73415dc39f4d3f512b02308f847801
# Migrate Tailwind styles to design-system package
9f19d8fb4bd22518879343b49c05634dca777df0

3
.gitattributes vendored
View File

@@ -7,10 +7,11 @@
*.json text eol=lf
*.mjs text eol=lf
*.mts text eol=lf
*.snap text eol=lf
*.ts text eol=lf
*.vue text eol=lf
*.yaml text eol=lf
# Generated files
src/types/comfyRegistryTypes.ts linguist-generated=true
packages/registry-types/src/comfyRegistryTypes.ts linguist-generated=true
src/workbench/extensions/manager/types/generatedManagerTypes.ts linguist-generated=true

View File

@@ -0,0 +1,55 @@
name: Setup ComfyUI Server
description: 'Setup ComfyUI server for continuous integration (with ComfyUI_devtools node installed)'
inputs:
extra_server_params:
description: 'Additional parameters to pass to ComfyUI server'
required: false
default: ''
launch_server:
description: 'Whether to launch the server after setup'
required: false
default: 'false'
runs:
using: 'composite'
steps:
# Note: this workflow assume frontend repo is checked out and is built in ../dist
# Checkout ComfyUI repo, install the dev_tools node and start server
- name: Checkout ComfyUI
uses: actions/checkout@v5
with:
repository: 'comfyanonymous/ComfyUI'
path: 'ComfyUI'
- name: Install ComfyUI_devtools from frontend repo
shell: bash
run: |
mkdir -p ComfyUI/custom_nodes/ComfyUI_devtools
if ! cp -r ./tools/devtools/* ComfyUI/custom_nodes/ComfyUI_devtools/; then
echo "::error::Failed to copy ComfyUI_devtools from ./tools/devtools/"
echo "::error::This action assumes the ComfyUI_frontend repository is checked out in the current working directory."
echo "::error::Please ensure you have run 'actions/checkout@v5' before calling this action."
exit 1
fi
- name: Setup Python
uses: actions/setup-python@v4
with:
python-version: '3.10'
- name: Install Python requirements
shell: bash
working-directory: ComfyUI
run: |
python -m pip install --upgrade pip
pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cpu
pip install -r requirements.txt
pip install wait-for-it
- name: Start ComfyUI server
if: ${{ inputs.launch_server == 'true' }}
shell: bash
working-directory: ComfyUI
run: |
python main.py --cpu --multi-user --front-end-root ../dist ${{ inputs.extra_server_params }} &
wait-for-it --service 127.0.0.1:8188 -t 600

View File

@@ -0,0 +1,45 @@
name: Setup ComfyUI Frontend
description: 'Install nodejs/pnpm/dependencies and optionally build ComfyUI_frontend'
inputs:
include_build_step:
description: 'Include the build step to build the frontend. Set to true for workflows that need a built frontend'
required: false
default: 'false'
runs:
using: 'composite'
steps:
# Note: this workflow assume frontend repo is checked out in the root of the workspace
# Install pnpm, Node.js, build frontend
- name: Install pnpm
uses: pnpm/action-setup@v4
with:
version: 10
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 'lts/*'
cache: 'pnpm'
cache-dependency-path: './pnpm-lock.yaml'
# Restore tool caches before running any build/lint operations
- name: Restore tool output cache
uses: actions/cache/restore@v4
with:
path: |
./.cache
./tsconfig.tsbuildinfo
key: tool-cache-${{ runner.os }}-${{ hashFiles('./pnpm-lock.yaml') }}-${{ hashFiles('./src/**/*.{ts,vue,js,mts}', './*.config.*') }}
restore-keys: |
tool-cache-${{ runner.os }}-${{ hashFiles('./pnpm-lock.yaml') }}-
tool-cache-${{ runner.os }}-
- name: Install dependencies
shell: bash
run: pnpm install --frozen-lockfile
- name: Build ComfyUI_frontend
if: ${{ inputs.include_build_step == 'true' }}
shell: bash
run: pnpm build

View File

@@ -0,0 +1,28 @@
name: Setup Playwright
description: Cache and install Playwright browsers with dependencies
runs:
using: composite
steps:
- name: Detect Playwright version
id: detect-version
shell: bash
run: |
PLAYWRIGHT_VERSION=$(pnpm ls @playwright/test --json | jq --raw-output '.[0].devDependencies["@playwright/test"].version')
echo "playwright-version=$PLAYWRIGHT_VERSION" >> $GITHUB_OUTPUT
- name: Cache Playwright Browsers
uses: actions/cache@v4
id: cache-playwright-browsers
with:
path: '~/.cache/ms-playwright'
key: ${{ runner.os }}-playwright-browsers-${{ steps.detect-version.outputs.playwright-version }}
- name: Install Playwright Browsers
if: steps.cache-playwright-browsers.outputs.cache-hit != 'true'
shell: bash
run: pnpm exec playwright install chromium --with-deps
- name: Install Playwright Browsers (operating system dependencies)
if: steps.cache-playwright-browsers.outputs.cache-hit == 'true'
shell: bash
run: pnpm exec playwright install-deps

21
.github/workflows/README.md vendored Normal file
View File

@@ -0,0 +1,21 @@
# GitHub Workflows
## Naming Convention
Workflow files follow a consistent naming pattern: `<prefix>-<descriptive-name>.yaml`
### Category Prefixes
| Prefix | Purpose | Example |
| ---------- | ----------------------------------- | ------------------------------------ |
| `ci-` | Testing, linting, validation | `ci-tests-e2e.yaml` |
| `release-` | Version management, publishing | `release-version-bump.yaml` |
| `pr-` | PR automation (triggered by labels) | `pr-claude-review.yaml` |
| `api-` | External Api type generation | `api-update-registry-api-types.yaml` |
| `i18n-` | Internationalization updates | `i18n-update-core.yaml` |
## Documentation
Each workflow file contains comments explaining its purpose, triggers, and behavior. For specific details about what each workflow does, refer to the comments at the top of each `.yaml` file.
For GitHub Actions documentation, see [Events that trigger workflows](https://docs.github.com/en/actions/writing-workflows/choosing-when-your-workflow-runs/events-that-trigger-workflows).

View File

@@ -1,4 +1,5 @@
name: Update Electron Types
name: 'Api: Update Electron API Types'
description: 'When upstream electron API is updated, click dispatch to update the TypeScript type definitions in this repo'
on:
workflow_dispatch:
@@ -12,7 +13,7 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v5
- name: Install pnpm
uses: pnpm/action-setup@v4
@@ -25,15 +26,6 @@ jobs:
node-version: lts/*
cache: 'pnpm'
- name: Cache tool outputs
uses: actions/cache@v4
with:
path: |
.cache
key: electron-types-tools-cache-${{ runner.os }}-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
electron-types-tools-cache-${{ runner.os }}-
- name: Update electron types
run: pnpm install --workspace-root @comfyorg/comfyui-electron-types@latest

View File

@@ -1,4 +1,5 @@
name: Update ComfyUI-Manager API Types
name: 'Api: Update Manager API Types'
description: 'When upstream ComfyUI-Manager API is updated, click dispatch to update the TypeScript type definitions in this repo'
on:
# Manual trigger
@@ -17,7 +18,7 @@ jobs:
pull-requests: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v5
- name: Install pnpm
uses: pnpm/action-setup@v4
@@ -30,28 +31,11 @@ jobs:
node-version: lts/*
cache: 'pnpm'
- name: Cache tool outputs
uses: actions/cache@v4
with:
path: |
.cache
key: update-manager-tools-cache-${{ runner.os }}-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
update-manager-tools-cache-${{ runner.os }}-
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Cache ComfyUI-Manager repository
uses: actions/cache@v4
with:
path: ComfyUI-Manager
key: comfyui-manager-repo-${{ runner.os }}-${{ github.run_id }}
restore-keys: |
comfyui-manager-repo-${{ runner.os }}-
- name: Checkout ComfyUI-Manager repository
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
repository: Comfy-Org/ComfyUI-Manager
path: ComfyUI-Manager
@@ -68,7 +52,7 @@ jobs:
- name: Generate Manager API types
run: |
echo "Generating TypeScript types from ComfyUI-Manager@${{ steps.manager-info.outputs.commit }}..."
npx openapi-typescript ./ComfyUI-Manager/openapi.yaml --output ./src/types/generatedManagerTypes.ts
pnpm dlx openapi-typescript ./ComfyUI-Manager/openapi.yaml --output ./src/types/generatedManagerTypes.ts
- name: Validate generated types
run: |

View File

@@ -1,4 +1,5 @@
name: Update Comfy Registry API Types
name: 'Api: Update Registry API Types'
description: 'When upstream comfy-api is updated, click dispatch to update the TypeScript type definitions in this repo'
on:
# Manual trigger
@@ -16,7 +17,7 @@ jobs:
pull-requests: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v5
- name: Install pnpm
uses: pnpm/action-setup@v4
@@ -29,28 +30,11 @@ jobs:
node-version: lts/*
cache: 'pnpm'
- name: Cache tool outputs
uses: actions/cache@v4
with:
path: |
.cache
key: update-registry-tools-cache-${{ runner.os }}-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
update-registry-tools-cache-${{ runner.os }}-
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Cache comfy-api repository
uses: actions/cache@v4
with:
path: comfy-api
key: comfy-api-repo-${{ runner.os }}-${{ github.run_id }}
restore-keys: |
comfy-api-repo-${{ runner.os }}-
- name: Checkout comfy-api repository
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
repository: Comfy-Org/comfy-api
path: comfy-api
@@ -68,17 +52,18 @@ jobs:
- name: Generate API types
run: |
echo "Generating TypeScript types from comfy-api@${{ steps.api-info.outputs.commit }}..."
npx openapi-typescript ./comfy-api/openapi.yml --output ./src/types/comfyRegistryTypes.ts
mkdir -p ./packages/registry-types/src
pnpm dlx openapi-typescript ./comfy-api/openapi.yml --output ./packages/registry-types/src/comfyRegistryTypes.ts
- name: Validate generated types
run: |
if [ ! -f ./src/types/comfyRegistryTypes.ts ]; then
if [ ! -f ./packages/registry-types/src/comfyRegistryTypes.ts ]; then
echo "Error: Types file was not generated."
exit 1
fi
# Check if file is not empty
if [ ! -s ./src/types/comfyRegistryTypes.ts ]; then
if [ ! -s ./packages/registry-types/src/comfyRegistryTypes.ts ]; then
echo "Error: Generated types file is empty."
exit 1
fi
@@ -86,12 +71,12 @@ jobs:
- name: Lint generated types
run: |
echo "Linting generated Comfy Registry API types..."
pnpm lint:fix:no-cache -- ./src/types/comfyRegistryTypes.ts
pnpm lint:fix:no-cache -- ./packages/registry-types/src/comfyRegistryTypes.ts
- name: Check for changes
id: check-changes
run: |
if [[ -z $(git status --porcelain ./src/types/comfyRegistryTypes.ts) ]]; then
if [[ -z $(git status --porcelain ./packages/registry-types/src/comfyRegistryTypes.ts) ]]; then
echo "No changes to Comfy Registry API types detected."
echo "changed=false" >> $GITHUB_OUTPUT
exit 0
@@ -121,4 +106,4 @@ jobs:
labels: CNR
delete-branch: true
add-paths: |
src/types/comfyRegistryTypes.ts
packages/registry-types/src/comfyRegistryTypes.ts

View File

@@ -1,57 +0,0 @@
name: 'Chromatic'
# - [Automate Chromatic with GitHub Actions • Chromatic docs]( https://www.chromatic.com/docs/github-actions/ )
on:
workflow_dispatch: # Allow manual triggering
pull_request:
branches: [main]
jobs:
chromatic-deployment:
runs-on: ubuntu-latest
# Only run for PRs from version-bump-* branches or manual triggers
if: github.event_name == 'workflow_dispatch' || startsWith(github.head_ref, 'version-bump-')
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0 # Required for Chromatic baseline
- name: Install pnpm
uses: pnpm/action-setup@v4
with:
version: 10
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'pnpm'
- name: Cache tool outputs
uses: actions/cache@v4
with:
path: |
.cache
storybook-static
tsconfig.tsbuildinfo
key: storybook-cache-${{ runner.os }}-${{ hashFiles('**/pnpm-lock.yaml') }}-${{ hashFiles('src/**/*.{ts,vue,js}', '*.config.*', '.storybook/**/*') }}
restore-keys: |
storybook-cache-${{ runner.os }}-${{ hashFiles('**/pnpm-lock.yaml') }}-
storybook-cache-${{ runner.os }}-
storybook-tools-cache-${{ runner.os }}-
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Build Storybook and run Chromatic
id: chromatic
uses: chromaui/action@latest
with:
projectToken: ${{ secrets.CHROMATIC_PROJECT_TOKEN }}
buildScriptName: build-storybook
autoAcceptChanges: 'main' # Auto-accept changes on main branch
exitOnceUploaded: true # Don't wait for UI tests to complete

View File

@@ -0,0 +1,16 @@
name: "CI: JSON Validation"
description: "Validates JSON syntax in all tracked .json files (excluding tsconfig*.json) using jq"
on:
push:
branches:
- main
pull_request:
jobs:
json-lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- name: Validate JSON syntax
run: ./scripts/cicd/check-json.sh

View File

@@ -1,9 +1,14 @@
name: Lint and Format
name: "CI: Lint Format"
description: "Linting and code formatting validation for pull requests"
on:
pull_request:
branches-ignore: [wip/*, draft/*, temp/*]
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
permissions:
contents: write
pull-requests: write
@@ -13,11 +18,9 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout PR
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
token: ${{ secrets.GITHUB_TOKEN }}
ref: ${{ github.event.pull_request.head.ref }}
fetch-depth: 0
ref: ${{ !github.event.pull_request.head.repo.fork && github.head_ref || github.ref }}
- name: Install pnpm
uses: pnpm/action-setup@v4
@@ -30,27 +33,15 @@ jobs:
node-version: 'lts/*'
cache: 'pnpm'
- name: Cache tool outputs
uses: actions/cache@v4
with:
path: |
.cache
.eslintcache
tsconfig.tsbuildinfo
.prettierCache
.knip-cache
key: lint-format-cache-${{ runner.os }}-${{ hashFiles('**/pnpm-lock.yaml') }}-${{ hashFiles('src/**/*.{ts,vue,js,mts}', '*.config.*', '.eslintrc.*', '.prettierrc.*', 'tsconfig.json') }}
restore-keys: |
lint-format-cache-${{ runner.os }}-${{ hashFiles('**/pnpm-lock.yaml') }}-
lint-format-cache-${{ runner.os }}-
ci-tools-cache-${{ runner.os }}-
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Run ESLint with auto-fix
run: pnpm lint:fix
- name: Run Stylelint with auto-fix
run: pnpm stylelint:fix
- name: Run Prettier with auto-format
run: pnpm format
@@ -69,12 +60,13 @@ jobs:
git config --local user.email "action@github.com"
git config --local user.name "GitHub Action"
git add .
git commit -m "[auto-fix] Apply ESLint and Prettier fixes"
git commit -m "[automated] Apply ESLint and Prettier fixes"
git push
- name: Final validation
run: |
pnpm lint
pnpm stylelint
pnpm format:check
pnpm knip
@@ -102,4 +94,4 @@ jobs:
owner: context.repo.owner,
repo: context.repo.repo,
body: '## ⚠️ Linting/Formatting Issues Found\n\nThis PR has linting or formatting issues that need to be fixed.\n\n**Since this PR is from a fork, auto-fix cannot be applied automatically.**\n\n### Option 1: Set up pre-commit hooks (recommended)\nRun this once to automatically format code on every commit:\n```bash\npnpm prepare\n```\n\n### Option 2: Fix manually\nRun these commands and push the changes:\n```bash\npnpm lint:fix\npnpm format\n```\n\nSee [CONTRIBUTING.md](https://github.com/Comfy-Org/ComfyUI_frontend/blob/main/CONTRIBUTING.md#git-pre-commit-hooks) for more details.'
})
})

View File

@@ -0,0 +1,27 @@
name: "CI: Python Validation"
description: "Validates Python code in tools/devtools directory"
on:
pull_request:
paths:
- 'tools/devtools/**'
push:
branches: [ main ]
paths:
- 'tools/devtools/**'
jobs:
syntax:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v5
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Validate Python syntax
run: python3 -m compileall -q tools/devtools

52
.github/workflows/ci-size-data.yaml vendored Normal file
View File

@@ -0,0 +1,52 @@
name: "CI: Size Data"
on:
push:
branches:
- main
pull_request:
branches:
- main
permissions:
contents: read
jobs:
collect:
if: github.repository == 'Comfy-Org/ComfyUI_frontend'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- name: Install pnpm
uses: pnpm/action-setup@v4.1.0
with:
version: 10
- name: Install Node.js
uses: actions/setup-node@v5
with:
node-version: '24.x'
cache: pnpm
- name: Install dependencies
run: pnpm install
- name: Build project
run: pnpm build
- name: Collect size data
run: node scripts/size-collect.js
- name: Save PR number & base branch
if: ${{ github.event_name == 'pull_request' }}
run: |
echo ${{ github.event.number }} > ./temp/size/number.txt
echo ${{ github.base_ref }} > ./temp/size/base.txt
- name: Upload size data
uses: actions/upload-artifact@v4
with:
name: size-data
path: temp/size

View File

@@ -1,8 +1,9 @@
name: PR Playwright Deploy (Forks)
name: "CI: Tests E2E (Deploy for Forks)"
description: "Deploys test results from forked PRs (forks can't access deployment secrets)"
on:
workflow_run:
workflows: ["Tests CI"]
workflows: ["CI: Tests E2E"]
types: [requested, completed]
env:
@@ -30,7 +31,7 @@ jobs:
echo "Is forked: ${{ github.event.workflow_run.head_repository.full_name != github.event.workflow_run.repository.full_name }}"
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v5
- name: Get PR Number
id: pr

236
.github/workflows/ci-tests-e2e.yaml vendored Normal file
View File

@@ -0,0 +1,236 @@
name: "CI: Tests E2E"
description: "End-to-end testing with Playwright across multiple browsers, deploys test reports to Cloudflare Pages"
on:
push:
branches: [main, master, core/*, desktop/*]
pull_request:
branches-ignore:
[wip/*, draft/*, temp/*, vue-nodes-migration, sno-playwright-*]
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
setup:
runs-on: ubuntu-latest
outputs:
cache-key: ${{ steps.cache-key.outputs.key }}
steps:
- name: Checkout repository
uses: actions/checkout@v5
# Setup Test Environment, build frontend but do not start server yet
- name: Setup ComfyUI server
uses: ./.github/actions/setup-comfyui-server
- name: Setup frontend
uses: ./.github/actions/setup-frontend
with:
include_build_step: true
- name: Setup Playwright
uses: ./.github/actions/setup-playwright # Setup Playwright and cache browsers
# Save the entire workspace as cache for later test jobs to restore
- name: Generate cache key
id: cache-key
run: echo "key=$(date +%s)" >> $GITHUB_OUTPUT
- name: Save cache
uses: actions/cache/save@5a3ec84eff668545956fd18022155c47e93e2684
with:
path: .
key: comfyui-setup-${{ steps.cache-key.outputs.key }}
# Sharded chromium tests
playwright-tests-chromium-sharded:
needs: setup
runs-on: ubuntu-latest
permissions:
contents: read
strategy:
fail-fast: false
matrix:
shardIndex: [1, 2, 3, 4, 5, 6, 7, 8]
shardTotal: [8]
steps:
# download built frontend repo from setup job
- name: Wait for cache propagation
run: sleep 10
- name: Restore cached setup
uses: actions/cache/restore@5a3ec84eff668545956fd18022155c47e93e2684
with:
fail-on-cache-miss: true
path: .
key: comfyui-setup-${{ needs.setup.outputs.cache-key }}
# Setup Test Environment for this runner, start server, use cached built frontend ./dist from 'setup' job
- name: Setup ComfyUI server
uses: ./.github/actions/setup-comfyui-server
with:
launch_server: true
- name: Setup nodejs, pnpm, reuse built frontend
uses: ./.github/actions/setup-frontend
- name: Setup Playwright
uses: ./.github/actions/setup-playwright
# Run sharded tests and upload sharded reports
- name: Run Playwright tests (Shard ${{ matrix.shardIndex }}/${{ matrix.shardTotal }})
id: playwright
run: pnpm exec playwright test --project=chromium --shard=${{ matrix.shardIndex }}/${{ matrix.shardTotal }} --reporter=blob
env:
PLAYWRIGHT_BLOB_OUTPUT_DIR: ./blob-report
- name: Upload blob report
uses: actions/upload-artifact@v4
if: ${{ !cancelled() }}
with:
name: blob-report-chromium-${{ matrix.shardIndex }}
path: blob-report/
retention-days: 1
playwright-tests:
# Ideally, each shard runs test in 6 minutes, but allow up to 15 minutes
timeout-minutes: 15
needs: setup
runs-on: ubuntu-latest
permissions:
contents: read
strategy:
fail-fast: false
matrix:
browser: [chromium-2x, chromium-0.5x, mobile-chrome]
steps:
# download built frontend repo from setup job
- name: Wait for cache propagation
run: sleep 10
- name: Restore cached setup
uses: actions/cache/restore@5a3ec84eff668545956fd18022155c47e93e2684
with:
fail-on-cache-miss: true
path: .
key: comfyui-setup-${{ needs.setup.outputs.cache-key }}
# Setup Test Environment for this runner, start server, use cached built frontend ./dist from 'setup' job
- name: Setup ComfyUI server
uses: ./.github/actions/setup-comfyui-server
with:
launch_server: true
- name: Setup nodejs, pnpm, reuse built frontend
uses: ./.github/actions/setup-frontend
- name: Setup Playwright
uses: ./.github/actions/setup-playwright
# Run tests and upload reports
- name: Run Playwright tests (${{ matrix.browser }})
id: playwright
run: |
# Run tests with both HTML and JSON reporters
PLAYWRIGHT_JSON_OUTPUT_NAME=playwright-report/report.json \
pnpm exec playwright test --project=${{ matrix.browser }} \
--reporter=list \
--reporter=html \
--reporter=json
- name: Upload Playwright report
uses: actions/upload-artifact@v4
if: always()
with:
name: playwright-report-${{ matrix.browser }}
path: ./playwright-report/
retention-days: 30
# Merge sharded test reports
merge-reports:
needs: [playwright-tests-chromium-sharded]
runs-on: ubuntu-latest
if: ${{ !cancelled() }}
steps:
- name: Checkout repository
uses: actions/checkout@v5
# Setup Test Environment, we only need playwright to merge reports
- name: Setup frontend
uses: ./.github/actions/setup-frontend
- name: Setup Playwright
uses: ./.github/actions/setup-playwright
- name: Download blob reports
uses: actions/download-artifact@v4
with:
path: ./all-blob-reports
pattern: blob-report-chromium-*
merge-multiple: true
- name: Merge into HTML Report
run: |
# Generate HTML report
pnpm exec playwright merge-reports --reporter=html ./all-blob-reports
# Generate JSON report separately with explicit output path
PLAYWRIGHT_JSON_OUTPUT_NAME=playwright-report/report.json \
pnpm exec playwright merge-reports --reporter=json ./all-blob-reports
- name: Upload HTML report
uses: actions/upload-artifact@v4
with:
name: playwright-report-chromium
path: ./playwright-report/
retention-days: 30
#### BEGIN Deployment and commenting (non-forked PRs only)
# when using pull_request event, we have permission to comment directly
# if its a forked repo, we need to use workflow_run event in a separate workflow (pr-playwright-deploy.yaml)
# Post starting comment for non-forked PRs
comment-on-pr-start:
runs-on: ubuntu-latest
if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork == false
permissions:
pull-requests: write
steps:
- name: Checkout repository
uses: actions/checkout@v5
- name: Get start time
id: start-time
run: echo "time=$(date -u '+%m/%d/%Y, %I:%M:%S %p')" >> $GITHUB_OUTPUT
- name: Post starting comment
env:
GITHUB_TOKEN: ${{ github.token }}
run: |
chmod +x scripts/cicd/pr-playwright-deploy-and-comment.sh
./scripts/cicd/pr-playwright-deploy-and-comment.sh \
"${{ github.event.pull_request.number }}" \
"${{ github.head_ref }}" \
"starting" \
"${{ steps.start-time.outputs.time }}"
# Deploy and comment for non-forked PRs only
deploy-and-comment:
needs: [playwright-tests, merge-reports]
runs-on: ubuntu-latest
if: always() && github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork == false
permissions:
pull-requests: write
contents: read
steps:
- name: Checkout repository
uses: actions/checkout@v5
- name: Download all playwright reports
uses: actions/download-artifact@v4
with:
pattern: playwright-report-*
path: reports
- name: Deploy reports and comment on PR
env:
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
GITHUB_TOKEN: ${{ github.token }}
run: |
bash ./scripts/cicd/pr-playwright-deploy-and-comment.sh \
"${{ github.event.pull_request.number }}" \
"${{ github.head_ref }}" \
"completed"
#### END Deployment and commenting (non-forked PRs only)

View File

@@ -0,0 +1,91 @@
name: "CI: Tests Storybook (Deploy for Forks)"
description: "Deploys Storybook previews from forked PRs (forks can't access deployment secrets)"
on:
workflow_run:
workflows: ["CI: Tests Storybook"]
types: [requested, completed]
env:
DATE_FORMAT: '+%m/%d/%Y, %I:%M:%S %p'
jobs:
deploy-and-comment-forked-pr:
runs-on: ubuntu-latest
if: |
github.repository == 'Comfy-Org/ComfyUI_frontend' &&
github.event.workflow_run.event == 'pull_request' &&
github.event.workflow_run.head_repository != null &&
github.event.workflow_run.repository != null &&
github.event.workflow_run.head_repository.full_name != github.event.workflow_run.repository.full_name
permissions:
pull-requests: write
actions: read
steps:
- name: Log workflow trigger info
run: |
echo "Repository: ${{ github.repository }}"
echo "Event: ${{ github.event.workflow_run.event }}"
echo "Head repo: ${{ github.event.workflow_run.head_repository.full_name || 'null' }}"
echo "Base repo: ${{ github.event.workflow_run.repository.full_name || 'null' }}"
echo "Is forked: ${{ github.event.workflow_run.head_repository.full_name != github.event.workflow_run.repository.full_name }}"
- name: Checkout repository
uses: actions/checkout@v5
- name: Get PR Number
id: pr
uses: actions/github-script@v7
with:
script: |
const { data: prs } = await github.rest.pulls.list({
owner: context.repo.owner,
repo: context.repo.repo,
state: 'open',
});
const pr = prs.find(p => p.head.sha === context.payload.workflow_run.head_sha);
if (!pr) {
console.log('No PR found for SHA:', context.payload.workflow_run.head_sha);
return null;
}
console.log(`Found PR #${pr.number} from fork: ${context.payload.workflow_run.head_repository.full_name}`);
return pr.number;
- name: Handle Storybook Start
if: steps.pr.outputs.result != 'null' && github.event.action == 'requested'
env:
GITHUB_TOKEN: ${{ github.token }}
run: |
chmod +x scripts/cicd/pr-storybook-deploy-and-comment.sh
./scripts/cicd/pr-storybook-deploy-and-comment.sh \
"${{ steps.pr.outputs.result }}" \
"${{ github.event.workflow_run.head_branch }}" \
"starting" \
"$(date -u '${{ env.DATE_FORMAT }}')"
- name: Download and Deploy Storybook
if: steps.pr.outputs.result != 'null' && github.event.action == 'completed' && github.event.workflow_run.conclusion == 'success'
uses: actions/download-artifact@v4
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
run-id: ${{ github.event.workflow_run.id }}
name: storybook-static
path: storybook-static
- name: Handle Storybook Completion
if: steps.pr.outputs.result != 'null' && github.event.action == 'completed'
env:
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
GITHUB_TOKEN: ${{ github.token }}
WORKFLOW_CONCLUSION: ${{ github.event.workflow_run.conclusion }}
WORKFLOW_URL: ${{ github.event.workflow_run.html_url }}
run: |
chmod +x scripts/cicd/pr-storybook-deploy-and-comment.sh
./scripts/cicd/pr-storybook-deploy-and-comment.sh \
"${{ steps.pr.outputs.result }}" \
"${{ github.event.workflow_run.head_branch }}" \
"completed"

View File

@@ -0,0 +1,204 @@
name: "CI: Tests Storybook"
description: "Builds Storybook and runs visual regression testing via Chromatic, deploys previews to Cloudflare Pages"
on:
workflow_dispatch: # Allow manual triggering
pull_request:
branches: [main]
jobs:
# Post starting comment for non-forked PRs
comment-on-pr-start:
runs-on: ubuntu-latest
if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork == false
permissions:
pull-requests: write
steps:
- name: Checkout repository
uses: actions/checkout@v5
- name: Post starting comment
env:
GITHUB_TOKEN: ${{ github.token }}
run: |
chmod +x scripts/cicd/pr-storybook-deploy-and-comment.sh
./scripts/cicd/pr-storybook-deploy-and-comment.sh \
"${{ github.event.pull_request.number }}" \
"${{ github.head_ref }}" \
"starting" \
"$(date -u '+%m/%d/%Y, %I:%M:%S %p')"
# Build Storybook for all PRs (free Cloudflare deployment)
storybook-build:
runs-on: ubuntu-latest
if: github.event_name == 'pull_request'
outputs:
conclusion: ${{ steps.job-status.outputs.conclusion }}
workflow-url: ${{ steps.workflow-url.outputs.url }}
steps:
- name: Checkout code
uses: actions/checkout@v5
- name: Install pnpm
uses: pnpm/action-setup@v4
with:
version: 10
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'pnpm'
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Build Storybook
run: pnpm build-storybook
- name: Set job status
id: job-status
if: always()
run: |
echo "conclusion=${{ job.status }}" >> $GITHUB_OUTPUT
- name: Get workflow URL
id: workflow-url
if: always()
run: |
echo "url=${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" >> $GITHUB_OUTPUT
- name: Upload Storybook build
if: success() && github.event.pull_request.head.repo.fork == false
uses: actions/upload-artifact@v4
with:
name: storybook-static
path: storybook-static/
retention-days: 7
# Chromatic deployment only for version-bump-* branches or manual triggers
chromatic-deployment:
runs-on: ubuntu-latest
if: github.event_name == 'workflow_dispatch' || (github.event_name == 'pull_request' && startsWith(github.head_ref, 'version-bump-'))
outputs:
conclusion: ${{ steps.job-status.outputs.conclusion }}
workflow-url: ${{ steps.workflow-url.outputs.url }}
chromatic-build-url: ${{ steps.chromatic.outputs.buildUrl }}
chromatic-storybook-url: ${{ steps.chromatic.outputs.storybookUrl }}
steps:
- name: Checkout code
uses: actions/checkout@v5
with:
fetch-depth: 0 # Required for Chromatic baseline
- name: Install pnpm
uses: pnpm/action-setup@v4
with:
version: 10
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'pnpm'
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Build Storybook and run Chromatic
id: chromatic
uses: chromaui/action@latest
with:
projectToken: ${{ secrets.CHROMATIC_PROJECT_TOKEN }}
buildScriptName: build-storybook
autoAcceptChanges: 'main' # Auto-accept changes on main branch
exitOnceUploaded: true # Don't wait for UI tests to complete
onlyChanged: true # Only capture changed stories
- name: Set job status
id: job-status
if: always()
run: |
echo "conclusion=${{ job.status }}" >> $GITHUB_OUTPUT
- name: Get workflow URL
id: workflow-url
if: always()
run: |
echo "url=${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" >> $GITHUB_OUTPUT
# Deploy and comment for non-forked PRs only
deploy-and-comment:
needs: [storybook-build]
runs-on: ubuntu-latest
if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork == false && always()
permissions:
pull-requests: write
contents: read
steps:
- name: Checkout repository
uses: actions/checkout@v5
- name: Download Storybook build
if: needs.storybook-build.outputs.conclusion == 'success'
uses: actions/download-artifact@v4
with:
name: storybook-static
path: storybook-static
- name: Make deployment script executable
run: chmod +x scripts/cicd/pr-storybook-deploy-and-comment.sh
- name: Deploy Storybook and comment on PR
env:
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
GITHUB_TOKEN: ${{ github.token }}
WORKFLOW_CONCLUSION: ${{ needs.storybook-build.outputs.conclusion }}
WORKFLOW_URL: ${{ needs.storybook-build.outputs.workflow-url }}
run: |
./scripts/cicd/pr-storybook-deploy-and-comment.sh \
"${{ github.event.pull_request.number }}" \
"${{ github.head_ref }}" \
"completed"
# Update comment with Chromatic URLs for version-bump branches
update-comment-with-chromatic:
needs: [chromatic-deployment, deploy-and-comment]
runs-on: ubuntu-latest
if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork == false && startsWith(github.head_ref, 'version-bump-') && needs.chromatic-deployment.outputs.chromatic-build-url != ''
permissions:
pull-requests: write
steps:
- name: Update comment with Chromatic URLs
uses: actions/github-script@v7
with:
script: |
const buildUrl = '${{ needs.chromatic-deployment.outputs.chromatic-build-url }}';
const storybookUrl = '${{ needs.chromatic-deployment.outputs.chromatic-storybook-url }}';
// Find the existing Storybook comment
const { data: comments } = await github.rest.issues.listComments({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: ${{ github.event.pull_request.number }}
});
const storybookComment = comments.find(comment =>
comment.body.includes('<!-- STORYBOOK_BUILD_STATUS -->')
);
if (storybookComment && buildUrl && storybookUrl) {
// Append Chromatic info to existing comment
const updatedBody = storybookComment.body.replace(
/---\n(.*)$/s,
`---\n### 🎨 Chromatic Visual Tests\n- 📊 [View Chromatic Build](${buildUrl})\n- 📚 [View Chromatic Storybook](${storybookUrl})\n\n$1`
);
await github.rest.issues.updateComment({
owner: context.repo.owner,
repo: context.repo.repo,
comment_id: storybookComment.id,
body: updatedBody
});
}

36
.github/workflows/ci-tests-unit.yaml vendored Normal file
View File

@@ -0,0 +1,36 @@
name: "CI: Tests Unit"
description: "Unit and component testing with Vitest"
on:
push:
branches: [main, master, dev*, core/*, desktop/*]
pull_request:
branches-ignore: [wip/*, draft/*, temp/*]
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- name: Install pnpm
uses: pnpm/action-setup@v4
with:
version: 10
- name: Use Node.js
uses: actions/setup-node@v4
with:
node-version: "lts/*"
cache: "pnpm"
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Run Vitest tests
run: pnpm test:unit

View File

@@ -1,4 +1,5 @@
name: Update Locales
name: "i18n: Update Core"
description: "Generates and updates translations for core ComfyUI components using OpenAI"
on:
# Manual dispatch for urgent translation updates
@@ -14,42 +15,35 @@ jobs:
if: github.event_name == 'workflow_dispatch' || (github.event.pull_request.head.repo.full_name == github.repository && startsWith(github.head_ref, 'version-bump-'))
runs-on: ubuntu-latest
steps:
- uses: Comfy-Org/ComfyUI_frontend_setup_action@v3
- name: Cache tool outputs
uses: actions/cache@v4
- name: Checkout repository
uses: actions/checkout@v5
# Setup playwright environment
- name: Setup ComfyUI Frontend
uses: ./.github/actions/setup-frontend
with:
path: |
ComfyUI_frontend/.cache
ComfyUI_frontend/.cache
key: i18n-tools-cache-${{ runner.os }}-${{ hashFiles('ComfyUI_frontend/pnpm-lock.yaml') }}
restore-keys: |
i18n-tools-cache-${{ runner.os }}-
- name: Cache Playwright browsers
uses: actions/cache@v4
include_build_step: true
- name: Setup ComfyUI Server
uses: ./.github/actions/setup-comfyui-server
with:
path: ~/.cache/ms-playwright
key: playwright-browsers-${{ runner.os }}-${{ hashFiles('ComfyUI_frontend/pnpm-lock.yaml') }}
restore-keys: |
playwright-browsers-${{ runner.os }}-
- name: Install Playwright Browsers
run: npx playwright install chromium --with-deps
working-directory: ComfyUI_frontend
launch_server: true
- name: Setup Playwright
uses: ./.github/actions/setup-playwright
- name: Start dev server
# Run electron dev server as it is a superset of the web dev server
# We do want electron specific UIs to be translated.
run: pnpm dev:electron &
working-directory: ComfyUI_frontend
# Update locales, collect new strings and update translations using OpenAI, then commit changes
- name: Update en.json
run: pnpm collect-i18n
env:
PLAYWRIGHT_TEST_URL: http://localhost:5173
working-directory: ComfyUI_frontend
- name: Update translations
run: pnpm locale
env:
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
working-directory: ComfyUI_frontend
- name: Commit updated locales
run: |
git config --global user.name 'github-actions'
@@ -61,6 +55,5 @@ jobs:
# Apply the stashed changes if any
git stash pop || true
git add src/locales/
git diff --staged --quiet || git commit -m "Update locales [skip ci]"
git diff --staged --quiet || git commit -m "Update locales"
git push origin HEAD:${{ github.head_ref }}
working-directory: ComfyUI_frontend

View File

@@ -1,4 +1,4 @@
name: Update Locales for given custom node repository
name: i18n Update Custom Nodes
on:
workflow_dispatch:
@@ -21,92 +21,64 @@ jobs:
update-locales:
runs-on: ubuntu-latest
steps:
- name: Checkout ComfyUI
uses: actions/checkout@v4
- name: Checkout repository
uses: actions/checkout@v5
# Setup playwright environment with custom node repository
- name: Setup ComfyUI Server (without launching)
uses: ./.github/actions/setup-comfyui-server
- name: Setup frontend
uses: ./.github/actions/setup-frontend
with:
repository: comfyanonymous/ComfyUI
path: ComfyUI
ref: master
- name: Checkout ComfyUI_frontend
uses: actions/checkout@v4
with:
repository: Comfy-Org/ComfyUI_frontend
path: ComfyUI_frontend
- name: Checkout ComfyUI_devtools
uses: actions/checkout@v4
with:
repository: Comfy-Org/ComfyUI_devtools
path: ComfyUI/custom_nodes/ComfyUI_devtools
include_build_step: 'true'
- name: Setup Playwright
uses: ./.github/actions/setup-playwright
# Install the custom node repository
- name: Checkout custom node repository
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
repository: ${{ inputs.owner }}/${{ inputs.repository }}
path: 'ComfyUI/custom_nodes/${{ inputs.repository }}'
- name: Install pnpm
uses: pnpm/action-setup@v4
with:
version: 10
- uses: actions/setup-node@v4
with:
node-version: 'lts/*'
cache: 'pnpm'
- uses: actions/setup-python@v4
with:
python-version: '3.10'
- name: Install ComfyUI requirements
run: |
python -m pip install --upgrade pip
pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cpu
pip install -r requirements.txt
pip install wait-for-it
working-directory: ComfyUI
- name: Install custom node requirements
- name: Install custom node Python requirements
working-directory: ComfyUI/custom_nodes/${{ inputs.repository }}
run: |
if [ -f "requirements.txt" ]; then
pip install -r requirements.txt
fi
working-directory: ComfyUI/custom_nodes/${{ inputs.repository }}
- name: Build & Install ComfyUI_frontend
run: |
pnpm install --frozen-lockfile
pnpm build
rm -rf ../ComfyUI/web/*
mv dist/* ../ComfyUI/web/
working-directory: ComfyUI_frontend
- name: Start ComfyUI server
run: |
python main.py --cpu --multi-user &
wait-for-it --service 127.0.0.1:8188 -t 600
# Start ComfyUI Server
- name: Start ComfyUI Server
shell: bash
working-directory: ComfyUI
- name: Install Playwright Browsers
run: npx playwright install chromium --with-deps
working-directory: ComfyUI_frontend
run: |
python main.py --cpu --multi-user --front-end-root ../dist --custom-node-path ../ComfyUI/custom_nodes/${{ inputs.repository }} &
wait-for-it --service
- name: Start dev server
# Run electron dev server as it is a superset of the web dev server
# We do want electron specific UIs to be translated.
run: pnpm dev:electron &
working-directory: ComfyUI_frontend
- name: Capture base i18n
run: npx tsx scripts/diff-i18n capture
working-directory: ComfyUI_frontend
run: pnpm exec tsx scripts/diff-i18n capture
- name: Update en.json
run: pnpm collect-i18n
env:
PLAYWRIGHT_TEST_URL: http://localhost:5173
working-directory: ComfyUI_frontend
- name: Update translations
run: pnpm locale
env:
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
working-directory: ComfyUI_frontend
- name: Diff base vs updated i18n
run: npx tsx scripts/diff-i18n diff
working-directory: ComfyUI_frontend
run: pnpm exec tsx scripts/diff-i18n diff
- name: Update i18n in custom node repository
run: |
LOCALE_DIR=ComfyUI/custom_nodes/${{ inputs.repository }}/locales/
install -d "$LOCALE_DIR"
cp -rf ComfyUI_frontend/temp/diff/* "$LOCALE_DIR"
# Git ops for pushing changes and creating PR
- name: Check and create fork of custom node repository
run: |
# Try to fork the repository

View File

@@ -1,4 +1,4 @@
name: Update Node Definitions Locales
name: i18n Update Nodes
on:
workflow_dispatch:
@@ -13,25 +13,32 @@ jobs:
update-locales:
runs-on: ubuntu-latest
steps:
- uses: Comfy-Org/ComfyUI_frontend_setup_action@v3
- name: Install Playwright Browsers
run: npx playwright install chromium --with-deps
working-directory: ComfyUI_frontend
- name: Checkout repository
uses: actions/checkout@v5
# Setup playwright environment
- name: Setup ComfyUI Server (and start)
uses: ./.github/actions/setup-comfyui-server
with:
launch_server: true
- name: Setup frontend
uses: ./.github/actions/setup-frontend
with:
include_build_step: true
- name: Setup Playwright
uses: ./.github/actions/setup-playwright
- name: Start dev server
# Run electron dev server as it is a superset of the web dev server
# We do want electron specific UIs to be translated.
run: pnpm dev:electron &
working-directory: ComfyUI_frontend
- name: Update en.json
run: pnpm collect-i18n -- scripts/collect-i18n-node-defs.ts
env:
PLAYWRIGHT_TEST_URL: http://localhost:5173
working-directory: ComfyUI_frontend
- name: Update translations
run: pnpm locale
env:
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
working-directory: ComfyUI_frontend
- name: Create Pull Request
uses: peter-evans/create-pull-request@271a8d0340265f705b14b6d32b9829c1cb33d45e
with:
@@ -45,4 +52,3 @@ jobs:
branch: update-locales-node-defs-${{ github.event.inputs.trigger_type }}-${{ github.run_id }}
base: main
labels: dependencies
path: ComfyUI_frontend

View File

@@ -1,4 +1,4 @@
name: Auto Backport
name: PR Backport
on:
pull_request_target:
@@ -60,7 +60,7 @@ jobs:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
fetch-depth: 0
@@ -69,70 +69,139 @@ jobs:
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
- name: Check if backports already exist
id: check-existing
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
PR_NUMBER: ${{ github.event_name == 'workflow_dispatch' && inputs.pr_number || github.event.pull_request.number }}
- name: Collect backport targets
id: targets
run: |
# Check for existing backport PRs for this PR number
EXISTING_BACKPORTS=$(gh pr list --state all --search "backport-${PR_NUMBER}-to" --json title,headRefName,baseRefName | jq -r '.[].headRefName')
if [ -z "$EXISTING_BACKPORTS" ]; then
echo "skip=false" >> $GITHUB_OUTPUT
exit 0
fi
# For manual triggers with force_rerun, proceed anyway
if [ "${{ github.event_name }}" = "workflow_dispatch" ] && [ "${{ inputs.force_rerun }}" = "true" ]; then
echo "skip=false" >> $GITHUB_OUTPUT
echo "::warning::Force rerun requested - existing backports will be updated"
exit 0
fi
echo "Found existing backport PRs:"
echo "$EXISTING_BACKPORTS"
echo "skip=true" >> $GITHUB_OUTPUT
echo "::warning::Backport PRs already exist for PR #${PR_NUMBER}, skipping to avoid duplicates"
TARGETS=()
declare -A SEEN=()
- name: Extract version labels
if: steps.check-existing.outputs.skip != 'true'
id: versions
run: |
# Extract version labels (e.g., "1.24", "1.22")
VERSIONS=""
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
# For manual triggers, get labels from the PR
LABELS=$(gh pr view ${{ inputs.pr_number }} --json labels | jq -r '.labels[].name')
else
# For automatic triggers, extract from PR event
LABELS='${{ toJSON(github.event.pull_request.labels) }}'
LABELS=$(echo "$LABELS" | jq -r '.[].name')
fi
for label in $LABELS; do
# Match version labels like "1.24" (major.minor only)
if [[ "$label" =~ ^[0-9]+\.[0-9]+$ ]]; then
# Validate the branch exists before adding to list
if git ls-remote --exit-code origin "core/${label}" >/dev/null 2>&1; then
VERSIONS="${VERSIONS}${label} "
else
echo "::warning::Label '${label}' found but branch 'core/${label}' does not exist"
fi
fi
done
if [ -z "$VERSIONS" ]; then
echo "::error::No version labels found (e.g., 1.24, 1.22)"
add_target() {
local label="$1"
local target="$2"
if [ -z "$target" ]; then
return
fi
target=$(echo "$target" | xargs)
if [ -z "$target" ] || [ -n "${SEEN[$target]}" ]; then
return
fi
if git ls-remote --exit-code origin "$target" >/dev/null 2>&1; then
TARGETS+=("$target")
SEEN["$target"]=1
else
echo "::warning::Label '${label}' references missing branch '${target}'"
fi
}
while IFS= read -r label; do
[ -z "$label" ] && continue
if [[ "$label" =~ ^branch:(.+)$ ]]; then
add_target "$label" "${BASH_REMATCH[1]}"
elif [[ "$label" =~ ^backport:(.+)$ ]]; then
add_target "$label" "${BASH_REMATCH[1]}"
elif [[ "$label" =~ ^core\/([0-9]+)\.([0-9]+)$ ]]; then
SAFE_MAJOR="${BASH_REMATCH[1]}"
SAFE_MINOR="${BASH_REMATCH[2]}"
add_target "$label" "core/${SAFE_MAJOR}.${SAFE_MINOR}"
elif [[ "$label" =~ ^cloud\/([0-9]+)\.([0-9]+)$ ]]; then
SAFE_MAJOR="${BASH_REMATCH[1]}"
SAFE_MINOR="${BASH_REMATCH[2]}"
add_target "$label" "cloud/${SAFE_MAJOR}.${SAFE_MINOR}"
elif [[ "$label" =~ ^[0-9]+\.[0-9]+$ ]]; then
add_target "$label" "core/${label}"
fi
done <<< "$LABELS"
if [ "${#TARGETS[@]}" -eq 0 ]; then
echo "::error::No backport targets found (use labels like '1.24' or 'branch:release/hotfix')"
exit 1
fi
echo "versions=${VERSIONS}" >> $GITHUB_OUTPUT
echo "Found version labels: ${VERSIONS}"
echo "targets=${TARGETS[*]}" >> $GITHUB_OUTPUT
echo "Found backport targets: ${TARGETS[*]}"
- name: Filter already backported targets
id: filter-targets
env:
EVENT_NAME: ${{ github.event_name }}
FORCE_RERUN_INPUT: >-
${{ github.event_name == 'workflow_dispatch' && inputs.force_rerun
|| 'false' }}
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
PR_NUMBER: >-
${{ github.event_name == 'workflow_dispatch' && inputs.pr_number
|| github.event.pull_request.number }}
run: |
set -euo pipefail
REQUESTED_TARGETS="${{ steps.targets.outputs.targets }}"
if [ -z "$REQUESTED_TARGETS" ]; then
echo "skip=true" >> $GITHUB_OUTPUT
echo "pending-targets=" >> $GITHUB_OUTPUT
exit 0
fi
FORCE_RERUN=false
if [ "$EVENT_NAME" = "workflow_dispatch" ] && [ "$FORCE_RERUN_INPUT" = "true" ]; then
FORCE_RERUN=true
fi
mapfile -t EXISTING_BRANCHES < <(
git ls-remote --heads origin "backport-${PR_NUMBER}-to-*" || true
)
PENDING=()
SKIPPED=()
for target in $REQUESTED_TARGETS; do
SAFE_TARGET=$(echo "$target" | tr '/' '-')
BACKPORT_BRANCH="backport-${PR_NUMBER}-to-${SAFE_TARGET}"
if [ "$FORCE_RERUN" = true ]; then
PENDING+=("$target")
continue
fi
if printf '%s\n' "${EXISTING_BRANCHES[@]:-}" |
grep -Fq "refs/heads/${BACKPORT_BRANCH}"; then
SKIPPED+=("$target")
else
PENDING+=("$target")
fi
done
SKIPPED_JOINED="${SKIPPED[*]:-}"
PENDING_JOINED="${PENDING[*]:-}"
echo "already-exists=${SKIPPED_JOINED}" >> $GITHUB_OUTPUT
echo "pending-targets=${PENDING_JOINED}" >> $GITHUB_OUTPUT
if [ -z "$PENDING_JOINED" ]; then
echo "skip=true" >> $GITHUB_OUTPUT
if [ -n "$SKIPPED_JOINED" ]; then
echo "::warning::Backport branches already exist for: ${SKIPPED_JOINED}"
fi
else
echo "skip=false" >> $GITHUB_OUTPUT
if [ -n "$SKIPPED_JOINED" ]; then
echo "::notice::Skipping already backported targets: ${SKIPPED_JOINED}"
fi
fi
- name: Backport commits
if: steps.check-existing.outputs.skip != 'true'
if: steps.filter-targets.outputs.skip != 'true'
id: backport
env:
PR_NUMBER: ${{ github.event_name == 'workflow_dispatch' && inputs.pr_number || github.event.pull_request.number }}
@@ -150,16 +219,25 @@ jobs:
MERGE_COMMIT="${{ github.event.pull_request.merge_commit_sha }}"
fi
for version in ${{ steps.versions.outputs.versions }}; do
echo "::group::Backporting to core/${version}"
for target in ${{ steps.filter-targets.outputs.pending-targets }}; do
TARGET_BRANCH="${target}"
SAFE_TARGET=$(echo "$TARGET_BRANCH" | tr '/' '-')
BACKPORT_BRANCH="backport-${PR_NUMBER}-to-${SAFE_TARGET}"
TARGET_BRANCH="core/${version}"
BACKPORT_BRANCH="backport-${PR_NUMBER}-to-${version}"
echo "::group::Backporting to ${TARGET_BRANCH}"
# Fetch target branch (fail if doesn't exist)
if ! git fetch origin "${TARGET_BRANCH}"; then
echo "::error::Target branch ${TARGET_BRANCH} does not exist"
FAILED="${FAILED}${version}:branch-missing "
FAILED="${FAILED}${TARGET_BRANCH}:branch-missing "
echo "::endgroup::"
continue
fi
# Check if commit already exists on target branch
if git branch -r --contains "${MERGE_COMMIT}" | grep -q "origin/${TARGET_BRANCH}"; then
echo "::notice::Commit ${MERGE_COMMIT} already exists on ${TARGET_BRANCH}, skipping backport"
FAILED="${FAILED}${TARGET_BRANCH}:already-exists "
echo "::endgroup::"
continue
fi
@@ -170,7 +248,7 @@ jobs:
# Try cherry-pick
if git cherry-pick "${MERGE_COMMIT}"; then
git push origin "${BACKPORT_BRANCH}"
SUCCESS="${SUCCESS}${version}:${BACKPORT_BRANCH} "
SUCCESS="${SUCCESS}${TARGET_BRANCH}:${BACKPORT_BRANCH} "
echo "Successfully created backport branch: ${BACKPORT_BRANCH}"
# Return to main (keep the branch, we need it for PR)
git checkout main
@@ -180,7 +258,7 @@ jobs:
git cherry-pick --abort
echo "::error::Cherry-pick failed due to conflicts"
FAILED="${FAILED}${version}:conflicts:${CONFLICTS} "
FAILED="${FAILED}${TARGET_BRANCH}:conflicts:${CONFLICTS} "
# Clean up the failed branch
git checkout main
@@ -198,7 +276,7 @@ jobs:
fi
- name: Create PR for each successful backport
if: steps.check-existing.outputs.skip != 'true' && steps.backport.outputs.success
if: steps.filter-targets.outputs.skip != 'true' && steps.backport.outputs.success
env:
GH_TOKEN: ${{ secrets.PR_GH_TOKEN }}
PR_NUMBER: ${{ github.event_name == 'workflow_dispatch' && inputs.pr_number || github.event.pull_request.number }}
@@ -214,13 +292,13 @@ jobs:
fi
for backport in ${{ steps.backport.outputs.success }}; do
IFS=':' read -r version branch <<< "${backport}"
IFS=':' read -r target branch <<< "${backport}"
if PR_URL=$(gh pr create \
--base "core/${version}" \
--base "${target}" \
--head "${branch}" \
--title "[backport ${version}] ${PR_TITLE}" \
--body "Backport of #${PR_NUMBER} to \`core/${version}\`"$'\n\n'"Automatically created by backport workflow." \
--title "[backport ${target}] ${PR_TITLE}" \
--body "Backport of #${PR_NUMBER} to \`${target}\`"$'\n\n'"Automatically created by backport workflow." \
--label "backport" 2>&1); then
# Extract PR number from URL
@@ -230,14 +308,14 @@ jobs:
gh pr comment "${PR_NUMBER}" --body "@${PR_AUTHOR} Successfully backported to #${PR_NUM}"
fi
else
echo "::error::Failed to create PR for ${version}: ${PR_URL}"
echo "::error::Failed to create PR for ${target}: ${PR_URL}"
# Still try to comment on the original PR about the failure
gh pr comment "${PR_NUMBER}" --body "@${PR_AUTHOR} Backport branch created but PR creation failed for \`core/${version}\`. Please create the PR manually from branch \`${branch}\`"
gh pr comment "${PR_NUMBER}" --body "@${PR_AUTHOR} Backport branch created but PR creation failed for \`${target}\`. Please create the PR manually from branch \`${branch}\`"
fi
done
- name: Comment on failures
if: steps.check-existing.outputs.skip != 'true' && failure() && steps.backport.outputs.failed
if: steps.filter-targets.outputs.skip != 'true' && failure() && steps.backport.outputs.failed
env:
GH_TOKEN: ${{ github.token }}
run: |
@@ -253,16 +331,25 @@ jobs:
fi
for failure in ${{ steps.backport.outputs.failed }}; do
IFS=':' read -r version reason conflicts <<< "${failure}"
IFS=':' read -r target reason conflicts <<< "${failure}"
if [ "${reason}" = "branch-missing" ]; then
gh pr comment "${PR_NUMBER}" --body "@${PR_AUTHOR} Backport failed: Branch \`core/${version}\` does not exist"
gh pr comment "${PR_NUMBER}" --body "@${PR_AUTHOR} Backport failed: Branch \`${target}\` does not exist"
elif [ "${reason}" = "already-exists" ]; then
gh pr comment "${PR_NUMBER}" --body "@${PR_AUTHOR} Commit \`${MERGE_COMMIT}\` already exists on branch \`${target}\`. No backport needed."
elif [ "${reason}" = "conflicts" ]; then
# Convert comma-separated conflicts back to newlines for display
CONFLICTS_LIST=$(echo "${conflicts}" | tr ',' '\n' | sed 's/^/- /')
COMMENT_BODY="@${PR_AUTHOR} Backport to \`core/${version}\` failed: Merge conflicts detected."$'\n\n'"Please manually cherry-pick commit \`${MERGE_COMMIT}\` to the \`core/${version}\` branch."$'\n\n'"<details><summary>Conflicting files</summary>"$'\n\n'"${CONFLICTS_LIST}"$'\n\n'"</details>"
COMMENT_BODY="@${PR_AUTHOR} Backport to \`${target}\` failed: Merge conflicts detected."$'\n\n'"Please manually cherry-pick commit \`${MERGE_COMMIT}\` to the \`${target}\` branch."$'\n\n'"<details><summary>Conflicting files</summary>"$'\n\n'"${CONFLICTS_LIST}"$'\n\n'"</details>"
gh pr comment "${PR_NUMBER}" --body "${COMMENT_BODY}"
fi
done
- name: Remove needs-backport label
if: steps.filter-targets.outputs.skip != 'true' && success()
run: gh pr edit ${{ github.event_name == 'workflow_dispatch' && inputs.pr_number || github.event.pull_request.number }} --remove-label "needs-backport"
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View File

@@ -1,4 +1,5 @@
name: Claude PR Review
name: "PR: Claude Review"
description: "AI-powered code review triggered by adding the 'claude-review' label to a PR"
permissions:
contents: read
@@ -11,48 +12,21 @@ on:
pull_request:
types: [labeled]
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
wait-for-ci:
claude-review:
runs-on: ubuntu-latest
if: github.event.label.name == 'claude-review'
outputs:
should-proceed: ${{ steps.check-status.outputs.proceed }}
steps:
- name: Wait for other CI checks
uses: lewagon/wait-on-check-action@e106e5c43e8ca1edea6383a39a01c5ca495fd812
with:
ref: ${{ github.event.pull_request.head.sha }}
check-regexp: '^(lint-and-format|test|playwright-tests)'
wait-interval: 30
repo-token: ${{ secrets.GITHUB_TOKEN }}
- name: Check if we should proceed
id: check-status
run: |
# Get all check runs for this commit
CHECK_RUNS=$(gh api repos/${{ github.repository }}/commits/${{ github.event.pull_request.head.sha }}/check-runs --jq '.check_runs[] | select(.name | test("lint-and-format|test|playwright-tests")) | {name, conclusion}')
# Check if any required checks failed
if echo "$CHECK_RUNS" | grep -q '"conclusion": "failure"'; then
echo "Some CI checks failed - skipping Claude review"
echo "proceed=false" >> $GITHUB_OUTPUT
else
echo "All CI checks passed - proceeding with Claude review"
echo "proceed=true" >> $GITHUB_OUTPUT
fi
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
claude-review:
needs: wait-for-ci
if: needs.wait-for-ci.outputs.should-proceed == 'true'
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
fetch-depth: 0
ref: refs/pull/${{ github.event.pull_request.number }}/head
- name: Install pnpm
uses: pnpm/action-setup@v4
@@ -74,10 +48,10 @@ jobs:
with:
label_trigger: "claude-review"
prompt: |
Read the file .claude/commands/comprehensive-pr-review.md and follow ALL the instructions exactly.
CRITICAL: You must post individual inline comments using the gh api commands shown in the file.
DO NOT create a summary comment.
Read the file .claude/commands/comprehensive-pr-review.md and follow ALL the instructions exactly.
CRITICAL: You must post individual inline comments using the gh api commands shown in the file.
DO NOT create a summary comment.
Each issue must be posted as a separate inline comment on the specific line of code.
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
claude_args: "--max-turns 256 --allowedTools 'Bash(git:*),Bash(gh api:*),Bash(gh pr:*),Bash(gh repo:*),Bash(jq:*),Bash(echo:*),Read,Write,Edit,Glob,Grep,WebFetch'"
@@ -86,4 +60,10 @@ jobs:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
COMMIT_SHA: ${{ github.event.pull_request.head.sha }}
BASE_SHA: ${{ github.event.pull_request.base.sha }}
REPOSITORY: ${{ github.repository }}
REPOSITORY: ${{ github.repository }}
- name: Remove claude-review label
if: always()
run: gh pr edit ${{ github.event.pull_request.number }} --remove-label "claude-review"
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}

104
.github/workflows/pr-size-report.yaml vendored Normal file
View File

@@ -0,0 +1,104 @@
name: "PR: Size Report"
on:
workflow_run:
workflows: ['CI: Size Data']
types:
- completed
workflow_dispatch:
inputs:
pr_number:
description: 'PR number to report on'
required: true
type: number
run_id:
description: 'Size data workflow run ID'
required: true
type: string
permissions:
contents: read
pull-requests: write
issues: write
jobs:
comment:
runs-on: ubuntu-latest
if: >
github.repository == 'Comfy-Org/ComfyUI_frontend' &&
(
(github.event_name == 'workflow_run' &&
github.event.workflow_run.event == 'pull_request' &&
github.event.workflow_run.conclusion == 'success') ||
github.event_name == 'workflow_dispatch'
)
steps:
- uses: actions/checkout@v5
- name: Install pnpm
uses: pnpm/action-setup@v4.1.0
with:
version: 10
- name: Install Node.js
uses: actions/setup-node@v5
with:
node-version: '24.x'
cache: pnpm
- name: Install dependencies
run: pnpm install
- name: Download size data
uses: dawidd6/action-download-artifact@v11
with:
name: size-data
run_id: ${{ github.event_name == 'workflow_dispatch' && inputs.run_id || github.event.workflow_run.id }}
path: temp/size
- name: Set PR number
id: pr-number
run: |
if [ "${{ github.event_name }}" == "workflow_dispatch" ]; then
echo "content=${{ inputs.pr_number }}" >> $GITHUB_OUTPUT
else
echo "content=$(cat temp/size/number.txt)" >> $GITHUB_OUTPUT
fi
- name: Set base branch
id: pr-base
run: |
if [ "${{ github.event_name }}" == "workflow_dispatch" ]; then
echo "content=main" >> $GITHUB_OUTPUT
else
echo "content=$(cat temp/size/base.txt)" >> $GITHUB_OUTPUT
fi
- name: Download previous size data
uses: dawidd6/action-download-artifact@v11
with:
branch: ${{ steps.pr-base.outputs.content }}
workflow: ci-size-data.yaml
event: push
name: size-data
path: temp/size-prev
if_no_artifact_found: warn
- name: Generate size report
run: node scripts/size-report.js > size-report.md
- name: Read size report
id: size-report
uses: juliangruber/read-file-action@v1
with:
path: ./size-report.md
- name: Create or update PR comment
uses: actions-cool/maintain-one-comment@v3
with:
token: ${{ secrets.GITHUB_TOKEN }}
number: ${{ steps.pr-number.outputs.content }}
body: |
${{ steps.size-report.outputs.content }}
<!-- COMFYUI_FRONTEND_SIZE -->
body-include: '<!-- COMFYUI_FRONTEND_SIZE -->'

View File

@@ -1,126 +0,0 @@
name: PR Storybook Comment
on:
workflow_run:
workflows: ['Chromatic']
types: [requested, completed]
jobs:
comment-storybook:
runs-on: ubuntu-latest
if: >-
github.repository == 'Comfy-Org/ComfyUI_frontend'
&& github.event.workflow_run.event == 'pull_request'
&& startsWith(github.event.workflow_run.head_branch, 'version-bump-')
permissions:
pull-requests: write
actions: read
steps:
- name: Get PR number
id: pr
uses: actions/github-script@v7
with:
script: |
const { data: pullRequests } = await github.rest.pulls.list({
owner: context.repo.owner,
repo: context.repo.repo,
state: 'open',
head: `${context.repo.owner}:${context.payload.workflow_run.head_branch}`,
});
if (pullRequests.length === 0) {
console.log('No open PR found for this branch');
return null;
}
return pullRequests[0].number;
- name: Log when no PR found
if: steps.pr.outputs.result == 'null'
run: |
echo "⚠️ No open PR found for branch: ${{ github.event.workflow_run.head_branch }}"
echo "Workflow run ID: ${{ github.event.workflow_run.id }}"
echo "Repository: ${{ github.event.workflow_run.repository.full_name }}"
echo "Event: ${{ github.event.workflow_run.event }}"
- name: Get workflow run details
if: steps.pr.outputs.result != 'null' && github.event.action == 'completed'
id: workflow-run
uses: actions/github-script@v7
with:
script: |
const run = await github.rest.actions.getWorkflowRun({
owner: context.repo.owner,
repo: context.repo.repo,
run_id: context.payload.workflow_run.id,
});
return {
conclusion: run.data.conclusion,
html_url: run.data.html_url
};
- name: Get completion time
id: completion-time
run: echo "time=$(date -u '+%m/%d/%Y, %I:%M:%S %p')" >> $GITHUB_OUTPUT
- name: Comment PR - Storybook Started
if: steps.pr.outputs.result != 'null' && github.event.action == 'requested'
uses: edumserrano/find-create-or-update-comment@82880b65c8a3a6e4c70aa05a204995b6c9696f53 # v3.0.0
with:
issue-number: ${{ steps.pr.outputs.result }}
body-includes: '<!-- STORYBOOK_BUILD_STATUS -->'
comment-author: 'github-actions[bot]'
edit-mode: replace
body: |
<!-- STORYBOOK_BUILD_STATUS -->
## 🎨 Storybook Build Status
<img alt='comfy-loading-gif' src='https://github.com/user-attachments/assets/755c86ee-e445-4ea8-bc2c-cca85df48686' width='14px' height='14px' style='vertical-align: middle; margin-right: 4px;' /> **Build is starting...**
⏰ Started at: ${{ steps.completion-time.outputs.time }} UTC
### 🚀 Building Storybook
- 📦 Installing dependencies...
- 🔧 Building Storybook components...
- 🎨 Running Chromatic visual tests...
---
⏱️ Please wait while the Storybook build is in progress...
- name: Comment PR - Storybook Complete
if: steps.pr.outputs.result != 'null' && github.event.action == 'completed'
uses: edumserrano/find-create-or-update-comment@82880b65c8a3a6e4c70aa05a204995b6c9696f53 # v3.0.0
with:
issue-number: ${{ steps.pr.outputs.result }}
body-includes: '<!-- STORYBOOK_BUILD_STATUS -->'
comment-author: 'github-actions[bot]'
edit-mode: replace
body: |
<!-- STORYBOOK_BUILD_STATUS -->
## 🎨 Storybook Build Status
${{
fromJSON(steps.workflow-run.outputs.result).conclusion == 'success' && '✅'
|| fromJSON(steps.workflow-run.outputs.result).conclusion == 'skipped' && '⏭️'
|| fromJSON(steps.workflow-run.outputs.result).conclusion == 'cancelled' && '🚫'
|| '❌'
}} **${{
fromJSON(steps.workflow-run.outputs.result).conclusion == 'success' && 'Build completed successfully!'
|| fromJSON(steps.workflow-run.outputs.result).conclusion == 'skipped' && 'Build skipped.'
|| fromJSON(steps.workflow-run.outputs.result).conclusion == 'cancelled' && 'Build cancelled.'
|| 'Build failed!'
}}**
⏰ Completed at: ${{ steps.completion-time.outputs.time }} UTC
### 🔗 Links
- [📊 View Workflow Run](${{ fromJSON(steps.workflow-run.outputs.result).html_url }})
---
${{
fromJSON(steps.workflow-run.outputs.result).conclusion == 'success' && '🎉 Your Storybook is ready for review!'
|| fromJSON(steps.workflow-run.outputs.result).conclusion == 'skipped' && ' Chromatic was skipped for this PR.'
|| fromJSON(steps.workflow-run.outputs.result).conclusion == 'cancelled' && ' The Chromatic run was cancelled.'
|| '⚠️ Please check the workflow logs for error details.'
}}

View File

@@ -0,0 +1,309 @@
# Setting test expectation screenshots for Playwright
name: "PR: Update Playwright Expectations"
on:
pull_request:
types: [labeled]
issue_comment:
types: [created]
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
setup:
runs-on: ubuntu-latest
if: >
( github.event_name == 'pull_request' && github.event.label.name == 'New Browser Test Expectations' ) ||
( github.event.issue.pull_request &&
github.event_name == 'issue_comment' &&
(
github.event.comment.author_association == 'OWNER' ||
github.event.comment.author_association == 'MEMBER' ||
github.event.comment.author_association == 'COLLABORATOR'
) &&
startsWith(github.event.comment.body, '/update-playwright') )
outputs:
cache-key: ${{ steps.cache-key.outputs.key }}
pr-number: ${{ steps.pr-info.outputs.pr-number }}
branch: ${{ steps.pr-info.outputs.branch }}
comment-id: ${{ steps.find-update-comment.outputs.comment-id }}
steps:
- name: Get PR info
id: pr-info
run: |
echo "pr-number=${{ github.event.number || github.event.issue.number }}" >> $GITHUB_OUTPUT
echo "branch=$(gh pr view ${{ github.event.number || github.event.issue.number }} --repo ${{ github.repository }} --json headRefName --jq '.headRefName')" >> $GITHUB_OUTPUT
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Find Update Comment
uses: peter-evans/find-comment@b30e6a3c0ed37e7c023ccd3f1db5c6c0b0c23aad
id: "find-update-comment"
with:
issue-number: ${{ steps.pr-info.outputs.pr-number }}
comment-author: "github-actions[bot]"
body-includes: "Updating Playwright Expectations"
- name: Add Starting Reaction
uses: peter-evans/create-or-update-comment@e8674b075228eee787fea43ef493e45ece1004c9
with:
comment-id: ${{ steps.find-update-comment.outputs.comment-id }}
issue-number: ${{ steps.pr-info.outputs.pr-number }}
body: |
Updating Playwright Expectations
edit-mode: replace
reactions: eyes
- name: Checkout repository
uses: actions/checkout@v5
with:
ref: ${{ steps.pr-info.outputs.branch }}
- name: Setup frontend
uses: ./.github/actions/setup-frontend
with:
include_build_step: true
# Save expensive build artifacts (Python env, built frontend, node_modules)
# Source code will be checked out fresh in sharded jobs
- name: Generate cache key
id: cache-key
run: echo "key=$(date +%s)" >> $GITHUB_OUTPUT
- name: Save cache
uses: actions/cache/save@5a3ec84eff668545956fd18022155c47e93e2684
with:
path: |
ComfyUI
dist
key: comfyui-setup-${{ steps.cache-key.outputs.key }}
# Sharded snapshot updates
update-snapshots-sharded:
needs: setup
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
shardIndex: [1, 2, 3, 4]
shardTotal: [4]
steps:
# Checkout source code fresh (not cached)
- name: Checkout repository
uses: actions/checkout@v5
with:
ref: ${{ needs.setup.outputs.branch }}
# Restore expensive build artifacts from setup job
- name: Restore cached artifacts
uses: actions/cache/restore@5a3ec84eff668545956fd18022155c47e93e2684
with:
fail-on-cache-miss: true
path: |
ComfyUI
dist
key: comfyui-setup-${{ needs.setup.outputs.cache-key }}
- name: Setup ComfyUI server (from cache)
uses: ./.github/actions/setup-comfyui-server
with:
launch_server: true
- name: Setup nodejs, pnpm, reuse built frontend
uses: ./.github/actions/setup-frontend
- name: Setup Playwright
uses: ./.github/actions/setup-playwright
# Run sharded tests with snapshot updates
- name: Update snapshots (Shard ${{ matrix.shardIndex }}/${{ matrix.shardTotal }})
id: playwright-tests
run: pnpm exec playwright test --update-snapshots --shard=${{ matrix.shardIndex }}/${{ matrix.shardTotal }}
continue-on-error: true
# Identify and stage only changed snapshot files
- name: Stage changed snapshot files
id: changed-snapshots
run: |
echo "=========================================="
echo "STAGING CHANGED SNAPSHOTS (Shard ${{ matrix.shardIndex }})"
echo "=========================================="
# Get list of changed snapshot files
changed_files=$(git diff --name-only browser_tests/ 2>/dev/null | grep -E '\-snapshots/' || echo "")
if [ -z "$changed_files" ]; then
echo "No snapshot changes in this shard"
echo "has-changes=false" >> $GITHUB_OUTPUT
exit 0
fi
echo "✓ Found changed files:"
echo "$changed_files"
file_count=$(echo "$changed_files" | wc -l)
echo "Count: $file_count"
echo "has-changes=true" >> $GITHUB_OUTPUT
echo ""
# Create staging directory
mkdir -p /tmp/changed_snapshots_shard
# Copy only changed files, preserving directory structure
# Strip 'browser_tests/' prefix to avoid double nesting
echo "Copying changed files to staging directory..."
while IFS= read -r file; do
# Remove 'browser_tests/' prefix
file_without_prefix="${file#browser_tests/}"
# Create parent directories
mkdir -p "/tmp/changed_snapshots_shard/$(dirname "$file_without_prefix")"
# Copy file
cp "$file" "/tmp/changed_snapshots_shard/$file_without_prefix"
echo " → $file_without_prefix"
done <<< "$changed_files"
echo ""
echo "Staged files for upload:"
find /tmp/changed_snapshots_shard -type f
# Upload ONLY the changed files from this shard
- name: Upload changed snapshots
uses: actions/upload-artifact@v4
if: steps.changed-snapshots.outputs.has-changes == 'true'
with:
name: snapshots-shard-${{ matrix.shardIndex }}
path: /tmp/changed_snapshots_shard/
retention-days: 1
- name: Upload test report
uses: actions/upload-artifact@v4
if: always()
with:
name: playwright-report-shard-${{ matrix.shardIndex }}
path: ./playwright-report/
retention-days: 30
# Merge snapshots and commit
merge-and-commit:
needs: [setup, update-snapshots-sharded]
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v5
with:
ref: ${{ needs.setup.outputs.branch }}
# Download all changed snapshot files from shards
- name: Download snapshot artifacts
uses: actions/download-artifact@v4
with:
pattern: snapshots-shard-*
path: ./downloaded-snapshots
merge-multiple: false
- name: List downloaded files
run: |
echo "=========================================="
echo "DOWNLOADED SNAPSHOT FILES"
echo "=========================================="
find ./downloaded-snapshots -type f
echo ""
echo "Total files: $(find ./downloaded-snapshots -type f | wc -l)"
# Merge only the changed files into browser_tests
- name: Merge changed snapshots
run: |
set -euo pipefail
echo "=========================================="
echo "MERGING CHANGED SNAPSHOTS"
echo "=========================================="
# Verify target directory exists
if [ ! -d "browser_tests" ]; then
echo "::error::Target directory 'browser_tests' does not exist"
exit 1
fi
merged_count=0
# For each shard's changed files, copy them directly
for shard_dir in ./downloaded-snapshots/snapshots-shard-*/; do
if [ ! -d "$shard_dir" ]; then
continue
fi
shard_name=$(basename "$shard_dir")
file_count=$(find "$shard_dir" -type f | wc -l)
if [ "$file_count" -eq 0 ]; then
echo " $shard_name: no files"
continue
fi
echo "Processing $shard_name ($file_count file(s))..."
# Copy files directly, preserving directory structure
# Since files are already in correct structure (no browser_tests/ prefix), just copy them all
cp -v -r "$shard_dir"* browser_tests/ 2>&1 | sed 's/^/ /'
merged_count=$((merged_count + 1))
echo " ✓ Merged"
echo ""
done
echo "=========================================="
echo "MERGE COMPLETE"
echo "=========================================="
echo "Shards merged: $merged_count"
- name: Show changes
run: |
echo "=========================================="
echo "CHANGES SUMMARY"
echo "=========================================="
echo ""
echo "Changed files in browser_tests:"
git diff --name-only browser_tests/ | head -20 || echo "No changes"
echo ""
echo "Total changes:"
git diff --name-only browser_tests/ | wc -l || echo "0"
- name: Commit updated expectations
id: commit
run: |
git config --global user.name 'github-actions'
git config --global user.email 'github-actions@github.com'
if git diff --quiet browser_tests/; then
echo "No changes to commit"
echo "has-changes=false" >> $GITHUB_OUTPUT
exit 0
fi
echo "=========================================="
echo "COMMITTING CHANGES"
echo "=========================================="
echo "has-changes=true" >> $GITHUB_OUTPUT
git add browser_tests/
git commit -m "[automated] Update test expectations"
echo "Pushing to ${{ needs.setup.outputs.branch }}..."
git push origin ${{ needs.setup.outputs.branch }}
echo "✓ Commit and push successful"
- name: Add Done Reaction
uses: peter-evans/create-or-update-comment@e8674b075228eee787fea43ef493e45ece1004c9
if: github.event_name == 'issue_comment' && steps.commit.outputs.has-changes == 'true'
with:
comment-id: ${{ needs.setup.outputs.comment-id }}
issue-number: ${{ needs.setup.outputs.pr-number }}
reactions: +1
reactions-edit-mode: replace
- name: Remove New Browser Test Expectations label
if: always() && github.event_name == 'pull_request'
run: gh pr edit ${{ needs.setup.outputs.pr-number }} --remove-label "New Browser Test Expectations"
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View File

@@ -0,0 +1,59 @@
name: Publish Desktop UI on PR Merge
on:
pull_request:
types: [ closed ]
branches: [ main, core/* ]
paths:
- 'apps/desktop-ui/package.json'
jobs:
resolve:
name: Resolve Version and Dist Tag
runs-on: ubuntu-latest
if: >
github.event.pull_request.merged == true &&
contains(github.event.pull_request.labels.*.name, 'Release')
outputs:
version: ${{ steps.get_version.outputs.version }}
dist_tag: ${{ steps.dist.outputs.dist_tag }}
steps:
- name: Checkout code
uses: actions/checkout@v5
with:
ref: ${{ github.event.pull_request.merge_commit_sha }}
persist-credentials: false
- name: Setup Node.js
uses: actions/setup-node@v5
with:
node-version: '24.x'
- name: Read desktop-ui version
id: get_version
run: |
VERSION=$(node -p "require('./apps/desktop-ui/package.json').version")
echo "version=$VERSION" >> $GITHUB_OUTPUT
- name: Determine dist-tag
id: dist
env:
VERSION: ${{ steps.get_version.outputs.version }}
run: |
if [[ "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+- ]]; then
echo "dist_tag=next" >> $GITHUB_OUTPUT
else
echo "dist_tag=latest" >> $GITHUB_OUTPUT
fi
publish:
name: Publish Desktop UI to npm
needs: resolve
uses: ./.github/workflows/publish-desktop-ui.yaml
with:
version: ${{ needs.resolve.outputs.version }}
dist_tag: ${{ needs.resolve.outputs.dist_tag }}
ref: ${{ github.event.pull_request.merge_commit_sha }}
secrets:
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}

View File

@@ -0,0 +1,192 @@
name: Publish Desktop UI
on:
workflow_dispatch:
inputs:
version:
description: 'Version to publish (e.g., 1.2.3)'
required: true
type: string
dist_tag:
description: 'npm dist-tag to use'
required: true
default: latest
type: string
ref:
description: 'Git ref to checkout (commit SHA, tag, or branch)'
required: false
type: string
workflow_call:
inputs:
version:
required: true
type: string
dist_tag:
required: false
type: string
default: latest
ref:
required: false
type: string
secrets:
NPM_TOKEN:
required: true
concurrency:
group: publish-desktop-ui-${{ github.workflow }}-${{ inputs.version }}-${{ inputs.dist_tag }}
cancel-in-progress: false
jobs:
publish_desktop_ui:
name: Publish @comfyorg/desktop-ui
runs-on: ubuntu-latest
permissions:
contents: read
env:
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: '1'
ENABLE_MINIFY: 'true'
steps:
- name: Validate inputs
env:
VERSION: ${{ inputs.version }}
shell: bash
run: |
set -euo pipefail
SEMVER_REGEX='^(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)(-((0|[1-9][0-9]*|[0-9]*[A-Za-z-][0-9A-Za-z-]*)(\.(0|[1-9][0-9]*|[0-9]*[A-Za-z-][0-9A-Za-z-]*))*))?(\+([0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*))?$'
if [[ ! "$VERSION" =~ $SEMVER_REGEX ]]; then
echo "::error title=Invalid version::Version '$VERSION' must follow semantic versioning (x.y.z[-suffix][+build])" >&2
exit 1
fi
- name: Determine ref to checkout
id: resolve_ref
env:
REF: ${{ inputs.ref }}
VERSION: ${{ inputs.version }}
shell: bash
run: |
set -euo pipefail
if [ -n "$REF" ]; then
if ! git check-ref-format --allow-onelevel "$REF"; then
echo "::error title=Invalid ref::Ref '$REF' fails git check-ref-format validation." >&2
exit 1
fi
echo "ref=$REF" >> "$GITHUB_OUTPUT"
else
echo "ref=refs/tags/v$VERSION" >> "$GITHUB_OUTPUT"
fi
- name: Checkout repository
uses: actions/checkout@v5
with:
ref: ${{ steps.resolve_ref.outputs.ref }}
fetch-depth: 1
persist-credentials: false
- name: Install pnpm
uses: pnpm/action-setup@v4
with:
version: 10
- name: Setup Node.js
uses: actions/setup-node@v5
with:
node-version: '24.x'
cache: 'pnpm'
registry-url: https://registry.npmjs.org
- name: Install dependencies
run: pnpm install --frozen-lockfile --ignore-scripts
- name: Build Desktop UI
run: pnpm build:desktop
- name: Prepare npm package
id: pkg
shell: bash
run: |
set -euo pipefail
APP_PKG=apps/desktop-ui/package.json
ROOT_PKG=package.json
NAME=$(jq -r .name "$APP_PKG")
APP_VERSION=$(jq -r .version "$APP_PKG")
ROOT_LICENSE=$(jq -r .license "$ROOT_PKG")
REPO=$(jq -r .repository "$ROOT_PKG")
if [ -z "$NAME" ] || [ "$NAME" = "null" ]; then
echo "::error title=Missing name::apps/desktop-ui/package.json is missing 'name'" >&2
exit 1
fi
INPUT_VERSION="${{ inputs.version }}"
if [ "$APP_VERSION" != "$INPUT_VERSION" ]; then
echo "::error title=Version mismatch::apps/desktop-ui version $APP_VERSION does not match input $INPUT_VERSION" >&2
exit 1
fi
if [ ! -d apps/desktop-ui/dist ]; then
echo "::error title=Missing build::apps/desktop-ui/dist not found. Did build succeed?" >&2
exit 1
fi
PUBLISH_DIR=apps/desktop-ui/.npm-publish
rm -rf "$PUBLISH_DIR"
mkdir -p "$PUBLISH_DIR"
cp -R apps/desktop-ui/dist "$PUBLISH_DIR/dist"
INPUT_VERSION="${{ inputs.version }}"
jq -n \
--arg name "$NAME" \
--arg version "$INPUT_VERSION" \
--arg description "Static assets for the ComfyUI Desktop UI" \
--arg license "$ROOT_LICENSE" \
--arg repository "$REPO" \
'{
name: $name,
version: $version,
description: $description,
license: $license,
repository: $repository,
type: "module",
private: false,
files: ["dist"],
publishConfig: { access: "public" }
}' > "$PUBLISH_DIR/package.json"
if [ -f apps/desktop-ui/README.md ]; then
cp apps/desktop-ui/README.md "$PUBLISH_DIR/README.md"
fi
echo "publish_dir=$PUBLISH_DIR" >> "$GITHUB_OUTPUT"
echo "name=$NAME" >> "$GITHUB_OUTPUT"
- name: Check if version already on npm
id: check_npm
env:
NAME: ${{ steps.pkg.outputs.name }}
VER: ${{ inputs.version }}
shell: bash
run: |
set -euo pipefail
STATUS=0
OUTPUT=$(npm view "${NAME}@${VER}" --json 2>&1) || STATUS=$?
if [ "$STATUS" -eq 0 ]; then
echo "exists=true" >> "$GITHUB_OUTPUT"
echo "::warning title=Already published::${NAME}@${VER} already exists on npm. Skipping publish."
else
if echo "$OUTPUT" | grep -q "E404"; then
echo "exists=false" >> "$GITHUB_OUTPUT"
else
echo "::error title=Registry lookup failed::$OUTPUT" >&2
exit "$STATUS"
fi
fi
- name: Publish package
if: steps.check_npm.outputs.exists == 'false'
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
DIST_TAG: ${{ inputs.dist_tag }}
run: pnpm publish --access public --tag "$DIST_TAG" --no-git-checks --ignore-scripts
working-directory: ${{ steps.pkg.outputs.publish_dir }}

View File

@@ -1,4 +1,4 @@
name: Create Release Branch
name: Release Branch Create
on:
pull_request:
@@ -18,7 +18,7 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
fetch-depth: 0
token: ${{ secrets.PR_GH_TOKEN || secrets.GITHUB_TOKEN }}
@@ -69,6 +69,9 @@ jobs:
echo "prev_version=$PREV_VERSION" >> $GITHUB_OUTPUT
echo "prev_minor=$PREV_MINOR" >> $GITHUB_OUTPUT
BASE_COMMIT=$(git rev-parse HEAD)
echo "base_commit=$BASE_COMMIT" >> $GITHUB_OUTPUT
# Get previous major version for comparison
PREV_MAJOR=$(echo $PREV_VERSION | cut -d. -f1)
@@ -87,13 +90,13 @@ jobs:
elif [[ "$MAJOR" -gt "$PREV_MAJOR" && "$MINOR" == "0" && "$PATCH" == "0" ]]; then
# Major version bump (e.g., 1.99.x → 2.0.0)
echo "is_minor_bump=true" >> $GITHUB_OUTPUT
BRANCH_NAME="core/${PREV_MAJOR}.${PREV_MINOR}"
echo "branch_name=$BRANCH_NAME" >> $GITHUB_OUTPUT
BRANCH_BASE="${PREV_MAJOR}.${PREV_MINOR}"
echo "branch_base=$BRANCH_BASE" >> $GITHUB_OUTPUT
elif [[ "$MAJOR" == "$PREV_MAJOR" && "$MINOR" -gt "$PREV_MINOR" && "$PATCH" == "0" ]]; then
# Minor version bump (e.g., 1.23.x → 1.24.0)
echo "is_minor_bump=true" >> $GITHUB_OUTPUT
BRANCH_NAME="core/${MAJOR}.${PREV_MINOR}"
echo "branch_name=$BRANCH_NAME" >> $GITHUB_OUTPUT
BRANCH_BASE="${MAJOR}.${PREV_MINOR}"
echo "branch_base=$BRANCH_BASE" >> $GITHUB_OUTPUT
else
echo "is_minor_bump=false" >> $GITHUB_OUTPUT
fi
@@ -101,64 +104,97 @@ jobs:
# Return to main branch
git checkout main
- name: Create release branch
- name: Create release branches
id: create_branches
if: steps.check_version.outputs.is_minor_bump == 'true'
run: |
BRANCH_NAME="${{ steps.check_version.outputs.branch_name }}"
BRANCH_BASE="${{ steps.check_version.outputs.branch_base }}"
PREV_VERSION="${{ steps.check_version.outputs.prev_version }}"
# Check if branch already exists
if git ls-remote --heads origin "$BRANCH_NAME" | grep -q "$BRANCH_NAME"; then
echo "⚠️ Branch $BRANCH_NAME already exists, skipping creation"
echo "branch_exists=true" >> $GITHUB_ENV
exit 0
else
echo "branch_exists=false" >> $GITHUB_ENV
if [[ -z "$BRANCH_BASE" ]]; then
echo "::error::Branch base not set; unable to determine release branches"
exit 1
fi
# Create branch from the commit BEFORE the version bump
# This ensures the release branch has the previous minor version
git checkout -b "$BRANCH_NAME" HEAD^1
BASE_COMMIT="${{ steps.check_version.outputs.base_commit }}"
# Push the new branch
git push origin "$BRANCH_NAME"
if [[ -z "$BASE_COMMIT" ]]; then
echo "::error::Base commit not provided; cannot create release branches"
exit 1
fi
echo "✅ Created release branch: $BRANCH_NAME"
echo "This branch is now in feature freeze and will only receive:"
echo "- Bug fixes"
echo "- Critical security patches"
echo "- Documentation updates"
RESULTS_FILE=$(mktemp)
trap 'rm -f "$RESULTS_FILE"' EXIT
for PREFIX in core cloud; do
BRANCH_NAME="${PREFIX}/${BRANCH_BASE}"
if git ls-remote --exit-code --heads origin \
"$BRANCH_NAME" >/dev/null 2>&1; then
echo "⚠️ Branch $BRANCH_NAME already exists"
echo " Skipping creation for $BRANCH_NAME"
STATUS="exists"
else
# Create branch from the commit BEFORE the version bump
if ! git push origin "$BASE_COMMIT:refs/heads/$BRANCH_NAME"; then
echo "::error::Failed to push release branch $BRANCH_NAME"
exit 1
fi
echo "✅ Created release branch: $BRANCH_NAME"
STATUS="created"
fi
echo "$BRANCH_NAME|$STATUS|$PREV_VERSION" >> "$RESULTS_FILE"
done
{
echo "results<<'EOF'"
cat "$RESULTS_FILE"
echo "EOF"
} >> $GITHUB_OUTPUT
- name: Post summary
if: steps.check_version.outputs.is_minor_bump == 'true'
run: |
BRANCH_NAME="${{ steps.check_version.outputs.branch_name }}"
PREV_VERSION="${{ steps.check_version.outputs.prev_version }}"
CURRENT_VERSION="${{ steps.check_version.outputs.current_version }}"
RESULTS="${{ steps.create_branches.outputs.results }}"
if [[ "${{ env.branch_exists }}" == "true" ]]; then
if [[ -z "$RESULTS" ]]; then
cat >> $GITHUB_STEP_SUMMARY << EOF
## 🌿 Release Branch Already Exists
## 🌿 Release Branch Summary
The release branch for the previous minor version already exists:
EOF
else
cat >> $GITHUB_STEP_SUMMARY << EOF
## 🌿 Release Branch Created
A new release branch has been created for the previous minor version:
Release branch creation skipped; no eligible branches were found.
EOF
exit 0
fi
cat >> $GITHUB_STEP_SUMMARY << EOF
## 🌿 Release Branch Summary
- **Branch**: \`$BRANCH_NAME\`
- **Version**: \`$PREV_VERSION\` (feature frozen)
- **Main branch**: \`$CURRENT_VERSION\` (active development)
### Branch Status
EOF
while IFS='|' read -r BRANCH STATUS PREV_VERSION; do
if [[ "$STATUS" == "created" ]]; then
cat >> $GITHUB_STEP_SUMMARY << EOF
- \`$BRANCH\` created from version \`$PREV_VERSION\`
EOF
else
cat >> $GITHUB_STEP_SUMMARY << EOF
- \`$BRANCH\` already existed (based on version \`$PREV_VERSION\`)
EOF
fi
done <<< "$RESULTS"
cat >> $GITHUB_STEP_SUMMARY << EOF
### Branch Policy
The \`$BRANCH_NAME\` branch is now in **feature freeze** and will only accept:
Release branches are feature-frozen and only accept:
- 🐛 Bug fixes
- 🔒 Security patches
- 📚 Documentation updates
@@ -167,9 +203,9 @@ jobs:
### Backporting Changes
To backport a fix to this release branch:
To backport a fix:
1. Create your fix on \`main\` first
2. Cherry-pick to \`$BRANCH_NAME\`
3. Create a PR targeting \`$BRANCH_NAME\`
4. Use the \`Release\` label on the PR
2. Cherry-pick to the target release branch
3. Create a PR targeting that branch
4. Apply the matching \`core/x.y\` or \`cloud/x.y\` label
EOF

View File

@@ -1,4 +1,4 @@
name: Create Release Draft
name: Release Draft Create
on:
pull_request:
@@ -28,16 +28,6 @@ jobs:
node-version: 'lts/*'
cache: 'pnpm'
- name: Cache tool outputs
uses: actions/cache@v4
with:
path: |
.cache
tsconfig.tsbuildinfo
key: release-tools-cache-${{ runner.os }}-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
release-tools-cache-${{ runner.os }}-
- name: Get current version
id: current_version
run: echo "version=$(node -p "require('./package.json').version")" >> $GITHUB_OUTPUT
@@ -55,6 +45,7 @@ jobs:
SENTRY_DSN: ${{ secrets.SENTRY_DSN }}
ALGOLIA_APP_ID: ${{ secrets.ALGOLIA_APP_ID }}
ALGOLIA_API_KEY: ${{ secrets.ALGOLIA_API_KEY }}
ENABLE_MINIFY: 'true'
USE_PROD_CONFIG: 'true'
run: |
pnpm install --frozen-lockfile
@@ -126,7 +117,7 @@ jobs:
publish_types:
needs: build
uses: ./.github/workflows/publish-frontend-types.yaml
uses: ./.github/workflows/release-npm-types.yaml
with:
version: ${{ needs.build.outputs.version }}
ref: ${{ github.event.pull_request.merge_commit_sha }}

View File

@@ -1,4 +1,4 @@
name: Publish Frontend Types
name: Release NPM Types
on:
workflow_dispatch:

View File

@@ -1,4 +1,4 @@
name: Create Dev PyPI Package
name: Release PyPI Dev
on:
workflow_dispatch:
@@ -15,7 +15,7 @@ jobs:
version: ${{ steps.current_version.outputs.version }}
steps:
- name: Checkout code
uses: actions/checkout@v4
uses: actions/checkout@v5
- name: Install pnpm
uses: pnpm/action-setup@v4
with:
@@ -25,17 +25,6 @@ jobs:
node-version: 'lts/*'
cache: 'pnpm'
- name: Cache tool outputs
uses: actions/cache@v4
with:
path: |
.cache
dist
tsconfig.tsbuildinfo
key: dev-release-tools-cache-${{ runner.os }}-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
dev-release-tools-cache-${{ runner.os }}-
- name: Get current version
id: current_version
run: echo "version=$(node -p "require('./package.json').version")" >> $GITHUB_OUTPUT
@@ -44,6 +33,7 @@ jobs:
SENTRY_DSN: ${{ secrets.SENTRY_DSN }}
ALGOLIA_APP_ID: ${{ secrets.ALGOLIA_APP_ID }}
ALGOLIA_API_KEY: ${{ secrets.ALGOLIA_API_KEY }}
ENABLE_MINIFY: 'true'
USE_PROD_CONFIG: 'true'
run: |
pnpm install --frozen-lockfile
@@ -62,7 +52,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
uses: actions/checkout@v5
- name: Download dist artifact
uses: actions/download-artifact@v4
with:

View File

@@ -1,4 +1,5 @@
name: Version Bump
name: "Release: Version Bump"
description: "Manual workflow to increment package version with semantic versioning support"
on:
workflow_dispatch:
@@ -14,6 +15,11 @@ on:
required: false
default: ''
type: string
branch:
description: 'Base branch to bump (e.g., main, core/1.29, core/1.30)'
required: true
default: 'main'
type: string
jobs:
bump-version:
@@ -24,7 +30,25 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
ref: ${{ github.event.inputs.branch }}
fetch-depth: 0
- name: Validate branch exists
run: |
BRANCH="${{ github.event.inputs.branch }}"
if ! git show-ref --verify --quiet "refs/heads/$BRANCH" && ! git show-ref --verify --quiet "refs/remotes/origin/$BRANCH"; then
echo "❌ Branch '$BRANCH' does not exist"
echo ""
echo "Available core branches:"
git branch -r | grep 'origin/core/' | sed 's/.*origin\// - /' || echo " (none found)"
echo ""
echo "Main branch:"
echo " - main"
exit 1
fi
echo "✅ Branch '$BRANCH' exists"
- name: Install pnpm
uses: pnpm/action-setup@v4
@@ -35,7 +59,6 @@ jobs:
uses: actions/setup-node@v4
with:
node-version: lts/*
cache: 'pnpm'
- name: Bump version
id: bump-version
@@ -58,7 +81,9 @@ jobs:
title: ${{ steps.bump-version.outputs.NEW_VERSION }}
body: |
${{ steps.capitalised.outputs.capitalised }} version increment to ${{ steps.bump-version.outputs.NEW_VERSION }}
**Base branch:** `${{ github.event.inputs.branch }}`
branch: version-bump-${{ steps.bump-version.outputs.NEW_VERSION }}
base: main
base: ${{ github.event.inputs.branch }}
labels: |
Release

View File

@@ -1,49 +0,0 @@
# Setting test expectation screenshots for Playwright
name: Update Playwright Expectations
on:
pull_request:
types: [ labeled ]
jobs:
test:
runs-on: ubuntu-latest
if: github.event.label.name == 'New Browser Test Expectations'
steps:
- uses: Comfy-Org/ComfyUI_frontend_setup_action@v3
- name: Cache Playwright browsers
uses: actions/cache@v4
with:
path: ~/.cache/ms-playwright
key: playwright-browsers-${{ runner.os }}-${{ hashFiles('ComfyUI_frontend/pnpm-lock.yaml') }}
restore-keys: |
playwright-browsers-${{ runner.os }}-
- name: Install Playwright Browsers
run: npx playwright install chromium --with-deps
working-directory: ComfyUI_frontend
- name: Run Playwright tests and update snapshots
id: playwright-tests
run: npx playwright test --update-snapshots
continue-on-error: true
working-directory: ComfyUI_frontend
- uses: actions/upload-artifact@v4
if: always()
with:
name: playwright-report
path: ComfyUI_frontend/playwright-report/
retention-days: 30
- name: Debugging info
run: |
echo "Branch: ${{ github.head_ref }}"
git status
working-directory: ComfyUI_frontend
- name: Commit updated expectations
run: |
git config --global user.name 'github-actions'
git config --global user.email 'github-actions@github.com'
git fetch origin ${{ github.head_ref }}
git checkout -B ${{ github.head_ref }} origin/${{ github.head_ref }}
git add browser_tests
git commit -m "Update test expectations [skip ci]"
git push origin HEAD:${{ github.head_ref }}
working-directory: ComfyUI_frontend

View File

@@ -1,359 +0,0 @@
name: Tests CI
on:
push:
branches: [main, master, core/*, desktop/*]
pull_request:
branches-ignore:
[wip/*, draft/*, temp/*, vue-nodes-migration, sno-playwright-*]
jobs:
setup:
runs-on: ubuntu-latest
outputs:
cache-key: ${{ steps.cache-key.outputs.key }}
playwright-version: ${{ steps.playwright-version.outputs.PLAYWRIGHT_VERSION }}
steps:
- name: Checkout ComfyUI
uses: actions/checkout@v4
with:
repository: 'comfyanonymous/ComfyUI'
path: 'ComfyUI'
ref: master
- name: Checkout ComfyUI_frontend
uses: actions/checkout@v4
with:
repository: 'Comfy-Org/ComfyUI_frontend'
path: 'ComfyUI_frontend'
- name: Checkout ComfyUI_devtools
uses: actions/checkout@v4
with:
repository: 'Comfy-Org/ComfyUI_devtools'
path: 'ComfyUI/custom_nodes/ComfyUI_devtools'
ref: 'd05fd48dd787a4192e16802d4244cfcc0e2f9684'
- name: Install pnpm
uses: pnpm/action-setup@v4
with:
version: 10
- uses: actions/setup-node@v4
with:
node-version: lts/*
cache: 'pnpm'
cache-dependency-path: 'ComfyUI_frontend/pnpm-lock.yaml'
- name: Cache tool outputs
uses: actions/cache@v4
with:
path: |
ComfyUI_frontend/.cache
ComfyUI_frontend/tsconfig.tsbuildinfo
key: playwright-setup-cache-${{ runner.os }}-${{ hashFiles('ComfyUI_frontend/pnpm-lock.yaml') }}-${{ hashFiles('ComfyUI_frontend/src/**/*.{ts,vue,js}', 'ComfyUI_frontend/*.config.*') }}
restore-keys: |
playwright-setup-cache-${{ runner.os }}-${{ hashFiles('ComfyUI_frontend/pnpm-lock.yaml') }}-
playwright-setup-cache-${{ runner.os }}-
playwright-tools-cache-${{ runner.os }}-
- name: Build ComfyUI_frontend
run: |
pnpm install --frozen-lockfile
pnpm build
working-directory: ComfyUI_frontend
- name: Generate cache key
id: cache-key
run: echo "key=$(date +%s)" >> $GITHUB_OUTPUT
- name: Playwright Version
id: playwright-version
run: |
PLAYWRIGHT_VERSION=$(pnpm ls @playwright/test --json | jq --raw-output '.[0].devDependencies["@playwright/test"].version')
echo "PLAYWRIGHT_VERSION=$PLAYWRIGHT_VERSION" >> $GITHUB_OUTPUT
working-directory: ComfyUI_frontend
- name: Save cache
uses: actions/cache/save@5a3ec84eff668545956fd18022155c47e93e2684
with:
path: |
ComfyUI
ComfyUI_frontend
key: comfyui-setup-${{ steps.cache-key.outputs.key }}
# Sharded chromium tests
playwright-tests-chromium-sharded:
needs: setup
runs-on: ubuntu-latest
permissions:
contents: read
strategy:
fail-fast: false
matrix:
shardIndex: [1, 2, 3, 4, 5, 6, 7, 8]
shardTotal: [8]
steps:
- name: Wait for cache propagation
run: sleep 10
- name: Restore cached setup
uses: actions/cache/restore@5a3ec84eff668545956fd18022155c47e93e2684
with:
fail-on-cache-miss: true
path: |
ComfyUI
ComfyUI_frontend
key: comfyui-setup-${{ needs.setup.outputs.cache-key }}
- name: Install pnpm
uses: pnpm/action-setup@v4
with:
version: 10
- uses: actions/setup-python@v4
with:
python-version: '3.10'
cache: 'pip'
- name: Install requirements
run: |
python -m pip install --upgrade pip
pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cpu
pip install -r requirements.txt
pip install wait-for-it
working-directory: ComfyUI
- name: Cache Playwright Browsers
uses: actions/cache@v4
id: cache-playwright-browsers
with:
path: '~/.cache/ms-playwright'
key: '${{ runner.os }}-playwright-browsers-${{ needs.setup.outputs.playwright-version }}'
- name: Install Playwright Browsers
if: steps.cache-playwright-browsers.outputs.cache-hit != 'true'
run: pnpm exec playwright install chromium --with-deps
working-directory: ComfyUI_frontend
- name: Install Playwright Browsers (operating system dependencies)
if: steps.cache-playwright-browsers.outputs.cache-hit == 'true'
run: pnpm exec playwright install-deps
working-directory: ComfyUI_frontend
- name: Start ComfyUI server
run: |
python main.py --cpu --multi-user --front-end-root ../ComfyUI_frontend/dist &
wait-for-it --service 127.0.0.1:8188 -t 600
working-directory: ComfyUI
- name: Run Playwright tests (Shard ${{ matrix.shardIndex }}/${{ matrix.shardTotal }})
id: playwright
run: npx playwright test --project=chromium --shard=${{ matrix.shardIndex }}/${{ matrix.shardTotal }} --reporter=blob
working-directory: ComfyUI_frontend
env:
PLAYWRIGHT_BLOB_OUTPUT_DIR: ../blob-report
- uses: actions/upload-artifact@v4
if: ${{ !cancelled() }}
with:
name: blob-report-chromium-${{ matrix.shardIndex }}
path: blob-report/
retention-days: 1
playwright-tests:
# Ideally, each shard runs test in 6 minutes, but allow up to 15 minutes
timeout-minutes: 15
needs: setup
runs-on: ubuntu-latest
permissions:
contents: read
strategy:
fail-fast: false
matrix:
browser: [chromium-2x, chromium-0.5x, mobile-chrome]
steps:
- name: Wait for cache propagation
run: sleep 10
- name: Restore cached setup
uses: actions/cache/restore@5a3ec84eff668545956fd18022155c47e93e2684
with:
fail-on-cache-miss: true
path: |
ComfyUI
ComfyUI_frontend
key: comfyui-setup-${{ needs.setup.outputs.cache-key }}
- name: Install pnpm
uses: pnpm/action-setup@v4
with:
version: 10
- uses: actions/setup-python@v4
with:
python-version: '3.10'
cache: 'pip'
- name: Install requirements
run: |
python -m pip install --upgrade pip
pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cpu
pip install -r requirements.txt
pip install wait-for-it
working-directory: ComfyUI
- name: Cache Playwright Browsers
uses: actions/cache@v4
id: cache-playwright-browsers
with:
path: '~/.cache/ms-playwright'
key: '${{ runner.os }}-playwright-browsers-${{ needs.setup.outputs.playwright-version }}'
- name: Install Playwright Browsers
if: steps.cache-playwright-browsers.outputs.cache-hit != 'true'
run: pnpm exec playwright install chromium --with-deps
working-directory: ComfyUI_frontend
- name: Install Playwright Browsers (operating system dependencies)
if: steps.cache-playwright-browsers.outputs.cache-hit == 'true'
run: pnpm exec playwright install-deps
working-directory: ComfyUI_frontend
- name: Start ComfyUI server
run: |
python main.py --cpu --multi-user --front-end-root ../ComfyUI_frontend/dist &
wait-for-it --service 127.0.0.1:8188 -t 600
working-directory: ComfyUI
- name: Run Playwright tests (${{ matrix.browser }})
id: playwright
run: |
# Run tests with both HTML and JSON reporters
PLAYWRIGHT_JSON_OUTPUT_NAME=playwright-report/report.json \
npx playwright test --project=${{ matrix.browser }} \
--reporter=list \
--reporter=html \
--reporter=json
working-directory: ComfyUI_frontend
- uses: actions/upload-artifact@v4
if: always()
with:
name: playwright-report-${{ matrix.browser }}
path: ComfyUI_frontend/playwright-report/
retention-days: 30
# Merge sharded test reports
merge-reports:
needs: [playwright-tests-chromium-sharded]
runs-on: ubuntu-latest
if: ${{ !cancelled() }}
steps:
- name: Checkout ComfyUI_frontend
uses: actions/checkout@v4
with:
repository: 'Comfy-Org/ComfyUI_frontend'
path: 'ComfyUI_frontend'
- name: Install pnpm
uses: pnpm/action-setup@v4
with:
version: 10
- uses: actions/setup-node@v4
with:
node-version: lts/*
cache: 'pnpm'
cache-dependency-path: 'ComfyUI_frontend/pnpm-lock.yaml'
- name: Install dependencies
run: |
pnpm install --frozen-lockfile
working-directory: ComfyUI_frontend
- name: Download blob reports
uses: actions/download-artifact@v4
with:
path: ComfyUI_frontend/all-blob-reports
pattern: blob-report-chromium-*
merge-multiple: true
- name: Merge into HTML Report
run: |
# Generate HTML report
npx playwright merge-reports --reporter=html ./all-blob-reports
# Generate JSON report separately with explicit output path
PLAYWRIGHT_JSON_OUTPUT_NAME=playwright-report/report.json \
npx playwright merge-reports --reporter=json ./all-blob-reports
working-directory: ComfyUI_frontend
- name: Upload HTML report
uses: actions/upload-artifact@v4
with:
name: playwright-report-chromium
path: ComfyUI_frontend/playwright-report/
retention-days: 30
#### BEGIN Deployment and commenting (non-forked PRs only)
# when using pull_request event, we have permission to comment directly
# if its a forked repo, we need to use workflow_run event in a separate workflow (pr-playwright-deploy.yaml)
# Post starting comment for non-forked PRs
comment-on-pr-start:
runs-on: ubuntu-latest
if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork == false
permissions:
pull-requests: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Get start time
id: start-time
run: echo "time=$(date -u '+%m/%d/%Y, %I:%M:%S %p')" >> $GITHUB_OUTPUT
- name: Post starting comment
env:
GITHUB_TOKEN: ${{ github.token }}
run: |
chmod +x scripts/cicd/pr-playwright-deploy-and-comment.sh
./scripts/cicd/pr-playwright-deploy-and-comment.sh \
"${{ github.event.pull_request.number }}" \
"${{ github.head_ref }}" \
"starting" \
"${{ steps.start-time.outputs.time }}"
# Deploy and comment for non-forked PRs only
deploy-and-comment:
needs: [playwright-tests, merge-reports]
runs-on: ubuntu-latest
if: always() && github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork == false
permissions:
pull-requests: write
contents: read
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Download all playwright reports
uses: actions/download-artifact@v4
with:
pattern: playwright-report-*
path: reports
- name: Make deployment script executable
run: chmod +x scripts/cicd/pr-playwright-deploy-and-comment.sh
- name: Deploy reports and comment on PR
env:
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
GITHUB_TOKEN: ${{ github.token }}
run: |
./scripts/cicd/pr-playwright-deploy-and-comment.sh \
"${{ github.event.pull_request.number }}" \
"${{ github.head_ref }}" \
"completed"
#### END Deployment and commenting (non-forked PRs only)

View File

@@ -0,0 +1,95 @@
name: Version Bump Desktop UI
on:
workflow_dispatch:
inputs:
version_type:
description: 'Version increment type'
required: true
default: 'patch'
type: 'choice'
options: [patch, minor, major, prepatch, preminor, premajor, prerelease]
pre_release:
description: Pre-release ID (suffix)
required: false
default: ''
type: string
branch:
description: 'Base branch to bump (e.g., main, core/1.29, core/1.30)'
required: true
default: 'main'
type: string
jobs:
bump-version-desktop-ui:
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
steps:
- name: Checkout repository
uses: actions/checkout@v5
with:
ref: ${{ github.event.inputs.branch }}
fetch-depth: 0
persist-credentials: false
- name: Validate branch exists
run: |
BRANCH="${{ github.event.inputs.branch }}"
if ! git show-ref --verify --quiet "refs/heads/$BRANCH" && ! git show-ref --verify --quiet "refs/remotes/origin/$BRANCH"; then
echo "❌ Branch '$BRANCH' does not exist"
echo ""
echo "Available core branches:"
git branch -r | grep 'origin/core/' | sed 's/.*origin\// - /' || echo " (none found)"
echo ""
echo "Main branch:"
echo " - main"
exit 1
fi
echo "✅ Branch '$BRANCH' exists"
- name: Install pnpm
uses: pnpm/action-setup@v4
with:
version: 10
- name: Setup Node.js
uses: actions/setup-node@v5
with:
node-version: '24.x'
cache: 'pnpm'
- name: Bump desktop-ui version
id: bump-version
env:
VERSION_TYPE: ${{ github.event.inputs.version_type }}
PRE_RELEASE: ${{ github.event.inputs.pre_release }}
run: |
pnpm -C apps/desktop-ui version "$VERSION_TYPE" --preid "$PRE_RELEASE" --no-git-tag-version
NEW_VERSION=$(node -p "require('./apps/desktop-ui/package.json').version")
echo "NEW_VERSION=$NEW_VERSION" >> $GITHUB_OUTPUT
- name: Format PR string
id: capitalised
env:
VERSION_TYPE: ${{ github.event.inputs.version_type }}
run: |
echo "capitalised=${VERSION_TYPE@u}" >> $GITHUB_OUTPUT
- name: Create Pull Request
uses: peter-evans/create-pull-request@271a8d0340265f705b14b6d32b9829c1cb33d45e
with:
token: ${{ secrets.PR_GH_TOKEN }}
commit-message: '[release] Increment desktop-ui to ${{ steps.bump-version.outputs.NEW_VERSION }}'
title: desktop-ui ${{ steps.bump-version.outputs.NEW_VERSION }}
body: |
${{ steps.capitalised.outputs.capitalised }} version increment for @comfyorg/desktop-ui to ${{ steps.bump-version.outputs.NEW_VERSION }}
**Base branch:** `${{ github.event.inputs.branch }}`
branch: desktop-ui-version-bump-${{ steps.bump-version.outputs.NEW_VERSION }}
base: ${{ github.event.inputs.branch }}
labels: |
Release

View File

@@ -1,46 +0,0 @@
name: Vitest Tests
on:
push:
branches: [ main, master, dev*, core/*, desktop/* ]
pull_request:
branches-ignore: [ wip/*, draft/*, temp/* ]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install pnpm
uses: pnpm/action-setup@v4
with:
version: 10
- name: Use Node.js
uses: actions/setup-node@v4
with:
node-version: 'lts/*'
cache: 'pnpm'
- name: Cache tool outputs
uses: actions/cache@v4
with:
path: |
.cache
coverage
.vitest-cache
key: vitest-cache-${{ runner.os }}-${{ hashFiles('**/pnpm-lock.yaml') }}-${{ hashFiles('src/**/*.{ts,vue,js}', 'vitest.config.*', 'tsconfig.json') }}
restore-keys: |
vitest-cache-${{ runner.os }}-${{ hashFiles('**/pnpm-lock.yaml') }}-
vitest-cache-${{ runner.os }}-
test-tools-cache-${{ runner.os }}-
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Run Vitest tests
run: |
pnpm test:component
pnpm test:unit

144
.github/workflows/weekly-docs-check.yaml vendored Normal file
View File

@@ -0,0 +1,144 @@
name: "Weekly Documentation Check"
description: "Automated weekly documentation accuracy check and update via Claude"
permissions:
contents: write
pull-requests: write
id-token: write
on:
schedule:
# Run every Monday at 9 AM UTC
- cron: '0 9 * * 1'
workflow_dispatch:
concurrency:
group: ${{ github.workflow }}
cancel-in-progress: true
jobs:
docs-check:
runs-on: ubuntu-latest
timeout-minutes: 45
steps:
- name: Checkout repository
uses: actions/checkout@v5
with:
fetch-depth: 0
ref: main
- name: Install pnpm
uses: pnpm/action-setup@v4
with:
version: 10
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'pnpm'
- name: Install dependencies for analysis tools
run: |
# Check if packages are already available locally
if ! pnpm list typescript @vue/compiler-sfc >/dev/null 2>&1; then
echo "Installing TypeScript and Vue compiler globally..."
pnpm install -g typescript @vue/compiler-sfc
else
echo "TypeScript and Vue compiler already available locally"
fi
- name: Run Claude Documentation Review
uses: anthropics/claude-code-action@v1.0.6
with:
prompt: |
Is all documentation still 100% accurate?
INSTRUCTIONS:
1. Fact-check all documentation against the current codebase
2. Look for:
- Outdated API references
- Deprecated functions or components still documented
- Missing documentation for new features
- Incorrect code examples
- Broken internal references
- Configuration examples that no longer work
- Documentation that contradicts actual implementation
3. Update any inaccurate or outdated documentation
4. Add documentation for significant undocumented features
5. Ensure all code examples are valid and tested against current code
Focus on these key areas:
- docs/**/*.md (all documentation files)
- CLAUDE.md (project guidelines)
- README.md files throughout the repository
- .claude/commands/*.md (Claude command documentation)
Make changes directly to the documentation files as needed.
DO NOT modify any source code files unless absolutely necessary for documentation accuracy.
After making all changes, create a comprehensive PR message summary:
1. Write a detailed PR body to /tmp/pr-body-${{ github.run_id }}.md in markdown format
2. Include:
- ## Summary section with bullet points of what was changed
- ## Changes Made section with details organized by category
- ## Review Notes section with any important context
3. Be specific about which files were updated and why
4. If no changes were needed, write a brief message stating documentation is up to date
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
claude_args: "--max-turns 256 --allowedTools 'Bash(git status),Bash(git diff),Bash(git log),Bash(pnpm:*),Bash(npm:*),Bash(node:*),Bash(tsc:*),Bash(echo:*),Read,Write,Edit,Glob,Grep'"
continue-on-error: false
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Check for changes
id: check_changes
run: |
if git diff --quiet && git diff --cached --quiet; then
echo "has_changes=false" >> $GITHUB_OUTPUT
echo "No documentation changes needed"
else
echo "has_changes=true" >> $GITHUB_OUTPUT
echo "Documentation changes detected"
fi
- name: Create default PR body if not generated
if: steps.check_changes.outputs.has_changes == 'true'
run: |
if [ ! -f /tmp/pr-body-${{ github.run_id }}.md ]; then
cat > /tmp/pr-body-${{ github.run_id }}.md <<'EOF'
## Automated Documentation Review
This PR contains documentation updates identified by the weekly automated review.
### Review Process
- Automated fact-checking against current codebase
- Verification of code examples and API references
- Detection of outdated or missing documentation
### What was checked
- All markdown documentation in `docs/`
- Project guidelines in `CLAUDE.md`
- README files throughout the repository
- Claude command documentation in `.claude/commands/`
**Note**: This is an automated PR. Please review all changes carefully before merging.
🤖 Generated by weekly documentation check workflow
EOF
fi
- name: Create or Update Pull Request
if: steps.check_changes.outputs.has_changes == 'true'
uses: peter-evans/create-pull-request@v7
with:
token: ${{ secrets.PR_GH_TOKEN }}
commit-message: 'docs: weekly documentation accuracy update'
branch: docs/weekly-update
delete-branch: true
title: 'docs: Weekly Documentation Update'
body-path: /tmp/pr-body-${{ github.run_id }}.md
labels: |
documentation
automated
draft: true

11
.gitignore vendored
View File

@@ -15,13 +15,17 @@ yarn.lock
# Cache files
.eslintcache
.prettiercache
.stylelintcache
node_modules
.pnpm-store
dist
dist-ssr
*.local
# Claude configuration
.claude/*.local.json
.claude/*.local.md
.claude/*.local.txt
CLAUDE.local.md
# Editor directories and files
@@ -29,6 +33,7 @@ CLAUDE.local.md
*.code-workspace
!.vscode/extensions.json
!.vscode/tailwind.json
!.vscode/custom-css.json
!.vscode/settings.json.default
!.vscode/launch.json.default
.idea
@@ -44,6 +49,7 @@ components.d.ts
tests-ui/data/*
tests-ui/ComfyUI_examples
tests-ui/workflows/examples
coverage/
# Browser tests
/test-results/
@@ -73,7 +79,7 @@ templates_repo/
vite.config.mts.timestamp-*.mjs
# Linux core dumps
./core
/core
*storybook.log
storybook-static
@@ -87,3 +93,6 @@ storybook-static
.github/instructions/nx.instructions.md
vite.config.*.timestamp*
vitest.config.*.timestamp*
# Weekly docs check output
/output.txt

View File

@@ -1,4 +1,4 @@
#!/usr/bin/env bash
pnpm exec lint-staged
pnpm exec tsx scripts/check-unused-i18n-keys.ts
pnpm exec tsx scripts/check-unused-i18n-keys.ts

View File

@@ -1,5 +1,5 @@
#!/usr/bin/env bash
# Run Knip with cache via package script
pnpm knip
pnpm knip 1>&2

1
.npmrc
View File

@@ -1 +1,2 @@
ignore-workspace-root-check=true
catalog-mode=prefer

View File

@@ -1,2 +1,2 @@
src/types/comfyRegistryTypes.ts
src/types/generatedManagerTypes.ts
packages/registry-types/src/comfyRegistryTypes.ts
src/types/generatedManagerTypes.ts

View File

@@ -7,12 +7,5 @@
"importOrder": ["^@core/(.*)$", "<THIRD_PARTY_MODULES>", "^@/(.*)$", "^[./]"],
"importOrderSeparation": true,
"importOrderSortSpecifiers": true,
"overrides": [
{
"files": "*.{js,cjs,mjs,ts,cts,mts,tsx,vue}",
"options": {
"plugins": ["@trivago/prettier-plugin-sort-imports"]
}
}
]
"plugins": ["@prettier/plugin-oxc", "@trivago/prettier-plugin-sort-imports"]
}

View File

@@ -4,7 +4,7 @@
- `pnpm storybook`: Start Storybook development server
- `pnpm build-storybook`: Build static Storybook
- `pnpm test:component`: Run component tests (includes Storybook components)
- `pnpm test:unit`: Run unit tests (includes Storybook components)
## Development Workflow for Storybook

View File

@@ -211,18 +211,17 @@ This Storybook setup includes:
## Icon Usage in Storybook
In this project, the `<i-lucide:... />` syntax from unplugin-icons is not supported in Storybook.
In this project, only the `<i class="icon-[lucide--folder]" />` syntax from unplugin-icons is supported in Storybook.
**Example:**
```vue
<script setup lang="ts">
import { Trophy, Settings } from 'lucide-vue-next'
</script>
<template>
<Trophy :size="16" class="text-neutral" />
<Settings :size="16" class="text-neutral" />
<i class="icon-[lucide--trophy] text-neutral size-4" />
<i class="icon-[lucide--settings] text-neutral size-4" />
</template>
```

View File

@@ -45,7 +45,7 @@ const config: StorybookConfig = {
compiler: 'vue3',
customCollections: {
comfy: FileSystemIconLoader(
process.cwd() + '/src/assets/icons/custom'
process.cwd() + '/packages/design-system/src/icons'
)
}
}),
@@ -76,11 +76,6 @@ const config: StorybookConfig = {
},
build: {
rollupOptions: {
external: () => {
// Don't externalize any modules in Storybook build
// This ensures PrimeVue and other dependencies are bundled
return false
},
onwarn: (warning, warn) => {
// Suppress specific warnings
if (

74
.stylelintrc.json Normal file
View File

@@ -0,0 +1,74 @@
{
"extends": [],
"overrides": [
{
"files": ["*.vue", "**/*.vue"],
"customSyntax": "postcss-html"
}
],
"rules": {
"import-notation": "string",
"font-family-no-missing-generic-family-keyword": true,
"declaration-property-value-no-unknown": [
true,
{
"ignoreProperties": {
"speak": ["none"],
"app-region": ["drag", "no-drag"],
"/^(width|height)$/": ["/^v-bind/"]
}
}
],
"color-function-notation": "modern",
"shorthand-property-no-redundant-values": true,
"selector-pseudo-element-colon-notation": "double",
"no-duplicate-selectors": true,
"font-weight-notation": "numeric",
"length-zero-no-unit": true,
"color-no-invalid-hex": true,
"number-max-precision": 4,
"property-no-vendor-prefix": true,
"value-no-vendor-prefix": true,
"selector-no-vendor-prefix": true,
"media-feature-name-no-vendor-prefix": true,
"selector-max-universal": 1,
"selector-max-type": 2,
"declaration-block-no-duplicate-properties": true,
"block-no-empty": true,
"no-descending-specificity": null,
"no-duplicate-at-import-rules": true,
"at-rule-no-unknown": [
true,
{
"ignoreAtRules": [
"tailwind",
"apply",
"layer",
"config",
"theme",
"reference",
"plugin",
"custom-variant",
"utility"
]
}
],
"function-no-unknown": [
true,
{
"ignoreFunctions": [
"theme",
"v-bind"
]
}
]
},
"ignoreFiles": [
"node_modules/**",
"dist/**",
"playwright-report/**",
"public/**",
"src/lib/litegraph/**"
],
"files": ["**/*.css", "**/*.vue"]
}

50
.vscode/custom-css.json vendored Normal file
View File

@@ -0,0 +1,50 @@
{
"version": 1.1,
"properties": [
{
"name": "app-region",
"description": "Electron-specific CSS property that defines draggable regions in custom title bar windows. Setting 'drag' marks a rectangular area as draggable for moving the window; 'no-drag' excludes areas from the draggable region.",
"values": [
{
"name": "drag",
"description": "Marks the element as draggable for moving the Electron window"
},
{
"name": "no-drag",
"description": "Excludes the element from being used to drag the Electron window"
}
],
"references": [
{
"name": "Electron Window Customization",
"url": "https://www.electronjs.org/docs/latest/tutorial/window-customization"
}
]
},
{
"name": "speak",
"description": "Deprecated CSS2 aural stylesheet property for controlling screen reader speech. Use ARIA attributes instead.",
"values": [
{
"name": "auto",
"description": "Content is read aurally if element is not a block and is visible"
},
{
"name": "never",
"description": "Content will not be read aurally"
},
{
"name": "always",
"description": "Content will be read aurally regardless of display settings"
}
],
"references": [
{
"name": "CSS-Tricks Reference",
"url": "https://css-tricks.com/almanac/properties/s/speak/"
}
],
"status": "obsolete"
}
]
}

View File

@@ -1,5 +1,6 @@
{
"css.customData": [
".vscode/tailwind.json"
".vscode/tailwind.json",
".vscode/custom-css.json"
]
}

36
.vscode/tailwind.json vendored
View File

@@ -7,7 +7,7 @@
"references": [
{
"name": "Tailwind Documentation",
"url": "https://tailwindcss.com/docs/functions-and-directives#import"
"url": "https://tailwindcss.com/docs/functions-and-directives#import-directive"
}
]
},
@@ -17,7 +17,7 @@
"references": [
{
"name": "Tailwind Documentation",
"url": "https://tailwindcss.com/docs/functions-and-directives#theme"
"url": "https://tailwindcss.com/docs/functions-and-directives#theme-directive"
}
]
},
@@ -27,17 +27,17 @@
"references": [
{
"name": "Tailwind Documentation",
"url": "https://tailwindcss.com/docs/functions-and-directives#layer"
"url": "https://tailwindcss.com/docs/theme#layers"
}
]
},
{
"name": "@apply",
"description": "Use the `@apply` directive to inline any existing utility classes into your own custom CSS. This is useful when you find a common utility pattern in your HTML that youd like to extract to a new component.",
"description": "DO NOT USE. IF YOU ARE CAUGHT USING @apply YOU WILL FACE SEVERE CONSEQUENCES.",
"references": [
{
"name": "Tailwind Documentation",
"url": "https://tailwindcss.com/docs/functions-and-directives#apply"
"url": "https://tailwindcss.com/docs/functions-and-directives#apply-directive"
}
]
},
@@ -47,7 +47,7 @@
"references": [
{
"name": "Tailwind Documentation",
"url": "https://tailwindcss.com/docs/functions-and-directives#config"
"url": "https://tailwindcss.com/docs/functions-and-directives#config-directive"
}
]
},
@@ -57,7 +57,7 @@
"references": [
{
"name": "Tailwind Documentation",
"url": "https://tailwindcss.com/docs/functions-and-directives#reference"
"url": "https://tailwindcss.com/docs/functions-and-directives#reference-directive"
}
]
},
@@ -67,7 +67,27 @@
"references": [
{
"name": "Tailwind Documentation",
"url": "https://tailwindcss.com/docs/functions-and-directives#plugin"
"url": "https://tailwindcss.com/docs/functions-and-directives#plugin-directive"
}
]
},
{
"name": "@custom-variant",
"description": "Use the `@custom-variant` directive to add a custom variant to your project. Custom variants can be used with utilities like `hover`, `focus`, and responsive breakpoints. Use `@slot` inside the variant to indicate where the utility's styles should be inserted.",
"references": [
{
"name": "Tailwind Documentation",
"url": "https://tailwindcss.com/docs/adding-custom-styles#adding-custom-variants"
}
]
},
{
"name": "@utility",
"description": "Use the `@utility` directive to add custom utilities to your project. Custom utilities work with all variants like `hover`, `focus`, and responsive variants. Use `--value()` to create functional utilities that accept arguments.",
"references": [
{
"name": "Tailwind Documentation",
"url": "https://tailwindcss.com/docs/adding-custom-styles#adding-custom-utilities"
}
]
}

View File

@@ -5,15 +5,14 @@
- Routing/i18n/entry: `src/router.ts`, `src/i18n.ts`, `src/main.ts`.
- Tests: unit/component in `tests-ui/` and `src/components/**/*.{test,spec}.ts`; E2E in `browser_tests/`.
- Public assets: `public/`. Build output: `dist/`.
- Config: `vite.config.mts`, `vitest.config.ts`, `playwright.config.ts`, `eslint.config.js`, `.prettierrc`.
- Config: `vite.config.mts`, `vitest.config.ts`, `playwright.config.ts`, `eslint.config.ts`, `.prettierrc`.
## Build, Test, and Development Commands
- `pnpm dev`: Start Vite dev server.
- `pnpm dev:electron`: Dev server with Electron API mocks.
- `pnpm build`: Type-check then production build to `dist/`.
- `pnpm preview`: Preview the production build locally.
- `pnpm test:unit`: Run Vitest unit tests (`tests-ui/`).
- `pnpm test:component`: Run component tests (`src/components/`).
- `pnpm test:unit`: Run Vitest unit tests.
- `pnpm test:browser`: Run Playwright E2E tests (`browser_tests/`).
- `pnpm lint` / `pnpm lint:fix`: Lint (ESLint). `pnpm format` / `format:check`: Prettier.
- `pnpm typecheck`: Vue TSC type checking.
@@ -31,10 +30,9 @@
- Playwright: place tests in `browser_tests/`; optional tags like `@mobile`, `@2x` are respected by config.
## Commit & Pull Request Guidelines
- Commits: Prefer Conventional Commits (e.g., `feat(ui): add sidebar`), `refactor(litegraph): …`. Use `[skip ci]` for locale-only updates when appropriate.
- PRs: Include clear description, linked issues (`Fixes #123`), and screenshots/GIFs for UI changes. Add/adjust tests and i18n strings when applicable.
- Commits: Use `[skip ci]` for locale-only updates when appropriate.
- PRs: Include clear description, linked issues (`- Fixes #123`), and screenshots/GIFs for UI changes.
- Quality gates: `pnpm lint`, `pnpm typecheck`, and relevant tests must pass. Keep PRs focused and small.
## Security & Configuration Tips
- Secrets: Use `.env` (see `.env_example`); do not commit secrets.
- Backend: Dev server expects ComfyUI backend at `localhost:8188` by default; configure via `.env`.

View File

@@ -18,7 +18,6 @@ This bootstraps the monorepo with dependencies, builds, tests, and dev server ve
- `pnpm build`: Build for production (via nx)
- `pnpm lint`: Linting (via nx)
- `pnpm format`: Prettier formatting
- `pnpm test:component`: Run component tests with browser environment
- `pnpm test:unit`: Run all unit tests
- `pnpm test:browser`: Run E2E tests via Playwright
- `pnpm test:unit -- tests-ui/tests/example.test.ts`: Run single test file
@@ -63,6 +62,11 @@ Key Nx features:
## Project Philosophy
- Follow good software engineering principles
- YAGNI
- AHA
- DRY
- SOLID
- Clean, stable public APIs
- Domain-driven design
- Thousands of users and extensions
@@ -127,6 +131,5 @@ const value = api.getServerFeature('config_name', defaultValue) // Get config
- NEVER use `--no-verify` flag when committing
- NEVER delete or disable tests to make them pass
- NEVER circumvent quality checks
- NEVER use `dark:` prefix - always use `dark-theme:` for dark mode styles, for example: `dark-theme:text-white dark-theme:bg-black`
- NEVER use `:class="[]"` to merge class names - always use `import { cn } from '@/utils/tailwindUtil'`, for example: `<div :class="cn('bg-red-500', { 'bg-blue-500': condition })" />`
- NEVER use `dark:` or `dark-theme:` tailwind variants. Instead use a semantic value from the `style.css` theme, e.g. `bg-node-component-surface`
- NEVER use `:class="[]"` to merge class names - always use `import { cn } from '@/utils/tailwindUtil'`, for example: `<div :class="cn('text-node-component-header-icon', hasError && 'text-danger')" />`

View File

@@ -1,17 +1,63 @@
# Admins
* @Comfy-Org/comfy_frontend_devs
# Desktop/Electron
/apps/desktop-ui/ @webfiltered
/src/stores/electronDownloadStore.ts @webfiltered
/src/extensions/core/electronAdapter.ts @webfiltered
/vite.electron.config.mts @webfiltered
# Maintainers
*.md @Comfy-Org/comfy_maintainer
/tests-ui/ @Comfy-Org/comfy_maintainer
/browser_tests/ @Comfy-Org/comfy_maintainer
/.env_example @Comfy-Org/comfy_maintainer
# Common UI Components
/src/components/chip/ @viva-jinyi
/src/components/card/ @viva-jinyi
/src/components/button/ @viva-jinyi
/src/components/input/ @viva-jinyi
# Translations (AIGODLIKE team + shinshin86)
/src/locales/ @Yorha4D @KarryCharon @DorotaLuna @shinshin86 @Comfy-Org/comfy_maintainer
# Topbar
/src/components/topbar/ @pythongosssss
# Load 3D extension
/src/extensions/core/load3d.ts @jtydhr88 @Comfy-Org/comfy_frontend_devs
# Thumbnail
/src/renderer/core/thumbnail/ @pythongosssss
# Mask Editor extension
/src/extensions/core/maskeditor.ts @brucew4yn3rp @trsommer @Comfy-Org/comfy_frontend_devs
# Legacy UI
/scripts/ui/ @pythongosssss
# Link rendering
/src/renderer/core/canvas/links/ @benceruleanlu
# Node help system
/src/utils/nodeHelpUtil.ts @benceruleanlu
/src/stores/workspace/nodeHelpStore.ts @benceruleanlu
/src/services/nodeHelpService.ts @benceruleanlu
# Selection toolbox
/src/components/graph/selectionToolbox/ @Myestery
# Minimap
/src/renderer/extensions/minimap/ @jtydhr88
# Assets
/src/platform/assets/ @arjansingh
# Workflow Templates
/src/platform/workflow/templates/ @Myestery @christian-byrne @comfyui-wiki
/src/components/templates/ @Myestery @christian-byrne @comfyui-wiki
# Mask Editor
/src/extensions/core/maskeditor.ts @trsommer @brucew4yn3rp
/src/extensions/core/maskEditorLayerFilenames.ts @trsommer @brucew4yn3rp
/src/extensions/core/maskEditorOld.ts @trsommer @brucew4yn3rp
# 3D
/src/extensions/core/load3d.ts @jtydhr88
/src/components/load3d/ @jtydhr88
# Manager
/src/workbench/extensions/manager/ @viva-jinyi @christian-byrne @ltdrdata
# Translations
/src/locales/ @Yorha4D @KarryCharon @shinshin86 @Comfy-Org/comfy_maintainer
# LLM Instructions (blank on purpose)
.claude/
.cursor/
.cursorrules
**/AGENTS.md
**/CLAUDE.md

View File

@@ -43,7 +43,7 @@ Have another idea? Drop into Discord or open an issue, and let's chat!
```
3. Configure environment (optional):
Create a `.env` file in the project root based on the provided [.env.example](.env.example) file.
Create a `.env` file in the project root based on the provided [.env_example](.env_example) file.
**Note about ports**: By default, the dev server expects the ComfyUI backend at `localhost:8188`. If your ComfyUI instance runs on a different port, update this in your `.env` file.
@@ -213,12 +213,6 @@ Here's how Claude Code can use the Playwright MCP server to inspect the interfac
- `pnpm i` to install all dependencies
- `pnpm test:unit` to execute all unit tests
### Component Tests
Component tests verify Vue components in `src/components/`.
- `pnpm test:component` to execute all component tests
### Playwright Tests
Playwright tests verify the whole app. See [browser_tests/README.md](browser_tests/README.md) for details.
@@ -229,7 +223,6 @@ Before submitting a PR, ensure all tests pass:
```bash
pnpm test:unit
pnpm test:component
pnpm test:browser
pnpm typecheck
pnpm lint
@@ -262,12 +255,12 @@ pnpm format
The project supports three types of icons, all with automatic imports (no manual imports needed):
1. **PrimeIcons** - Built-in PrimeVue icons using CSS classes: `<i class="pi pi-plus" />`
2. **Iconify Icons** - 200,000+ icons from various libraries: `<i-lucide:settings />`, `<i-mdi:folder />`
2. **Iconify Icons** - 200,000+ icons from various libraries: `<i class="icon-[lucide--settings]" />`, `<i class="icon-[mdi--folder]" />`
3. **Custom Icons** - Your own SVG icons: `<i-comfy:workflow />`
Icons are powered by the unplugin-icons system, which automatically discovers and imports icons as Vue components. Custom icons are stored in `src/assets/icons/custom/` and processed by `build/customIconCollection.ts` with automatic validation.
Icons are powered by the unplugin-icons system, which automatically discovers and imports icons as Vue components. Custom icons are stored in `packages/design-system/src/icons/` and processed by `packages/design-system/src/iconCollection.ts` with automatic validation.
For detailed instructions and code examples, see [src/assets/icons/README.md](src/assets/icons/README.md).
For detailed instructions and code examples, see [packages/design-system/src/icons/README.md](packages/design-system/src/icons/README.md).
## Working with litegraph.js
@@ -332,4 +325,4 @@ If you have questions about contributing:
- Ask in our [Discord](https://discord.com/invite/comfyorg)
- Open a new issue for clarification
Thank you for contributing to ComfyUI Frontend!
Thank you for contributing to ComfyUI Frontend!

View File

@@ -0,0 +1,103 @@
import type { StorybookConfig } from '@storybook/vue3-vite'
import { FileSystemIconLoader } from 'unplugin-icons/loaders'
import IconsResolver from 'unplugin-icons/resolver'
import Icons from 'unplugin-icons/vite'
import Components from 'unplugin-vue-components/vite'
import type { InlineConfig } from 'vite'
const config: StorybookConfig = {
stories: ['../src/**/*.stories.@(js|jsx|mjs|ts|tsx)'],
addons: ['@storybook/addon-docs'],
framework: {
name: '@storybook/vue3-vite',
options: {}
},
staticDirs: [{ from: '../public', to: '/' }],
async viteFinal(config) {
// Use dynamic import to avoid CJS deprecation warning
const { mergeConfig } = await import('vite')
const { default: tailwindcss } = await import('@tailwindcss/vite')
// Filter out any plugins that might generate import maps
if (config.plugins) {
config.plugins = config.plugins
// Type guard: ensure we have valid plugin objects with names
.filter(
(plugin): plugin is NonNullable<typeof plugin> & { name: string } => {
return (
plugin !== null &&
plugin !== undefined &&
typeof plugin === 'object' &&
'name' in plugin &&
typeof plugin.name === 'string'
)
}
)
// Business logic: filter out import-map plugins
.filter((plugin) => !plugin.name.includes('import-map'))
}
return mergeConfig(config, {
// Replace plugins entirely to avoid inheritance issues
plugins: [
// Only include plugins we explicitly need for Storybook
tailwindcss(),
Icons({
compiler: 'vue3',
customCollections: {
comfy: FileSystemIconLoader(
process.cwd() + '/../../packages/design-system/src/icons'
)
}
}),
Components({
dts: false, // Disable dts generation in Storybook
resolvers: [
IconsResolver({
customCollections: ['comfy']
})
],
dirs: [
process.cwd() + '/src/components',
process.cwd() + '/src/views'
],
deep: true,
extensions: ['vue'],
directoryAsNamespace: true
})
],
server: {
allowedHosts: true
},
resolve: {
alias: {
'@': process.cwd() + '/src',
'@frontend-locales': process.cwd() + '/../../src/locales'
}
},
build: {
rollupOptions: {
onwarn: (warning, warn) => {
// Suppress specific warnings
if (
warning.code === 'UNUSED_EXTERNAL_IMPORT' &&
warning.message?.includes('resolveComponent')
) {
return
}
// Suppress Storybook font asset warnings
if (
warning.code === 'UNRESOLVED_IMPORT' &&
warning.message?.includes('nunito-sans')
) {
return
}
warn(warning)
}
},
chunkSizeWarningLimit: 1000
}
} satisfies InlineConfig)
}
}
export default config

View File

@@ -0,0 +1,88 @@
import { definePreset } from '@primevue/themes'
import Aura from '@primevue/themes/aura'
import { setup } from '@storybook/vue3'
import type { Preview, StoryContext, StoryFn } from '@storybook/vue3-vite'
import { createPinia } from 'pinia'
import 'primeicons/primeicons.css'
import PrimeVue from 'primevue/config'
import ConfirmationService from 'primevue/confirmationservice'
import ToastService from 'primevue/toastservice'
import Tooltip from 'primevue/tooltip'
import '@/assets/css/style.css'
import { i18n } from '@/i18n'
const ComfyUIPreset = definePreset(Aura, {
semantic: {
// @ts-expect-error prime type quirk
primary: Aura['primitive'].blue
}
})
setup((app) => {
app.directive('tooltip', Tooltip)
const pinia = createPinia()
app.use(pinia)
app.use(i18n)
app.use(PrimeVue, {
theme: {
preset: ComfyUIPreset,
options: {
prefix: 'p',
cssLayer: { name: 'primevue', order: 'primevue, tailwind-utilities' },
darkModeSelector: '.dark-theme, :root:has(.dark-theme)'
}
}
})
app.use(ConfirmationService)
app.use(ToastService)
})
export const withTheme = (Story: StoryFn, context: StoryContext) => {
const theme = context.globals.theme || 'light'
if (theme === 'dark') {
document.documentElement.classList.add('dark-theme')
document.body.classList.add('dark-theme')
} else {
document.documentElement.classList.remove('dark-theme')
document.body.classList.remove('dark-theme')
}
return Story(context.args, context)
}
const preview: Preview = {
parameters: {
controls: {
matchers: { color: /(background|color)$/i, date: /Date$/i }
},
backgrounds: {
default: 'light',
values: [
{ name: 'light', value: '#ffffff' },
{ name: 'dark', value: '#0a0a0a' }
]
}
},
globalTypes: {
theme: {
name: 'Theme',
description: 'Global theme for components',
defaultValue: 'light',
toolbar: {
icon: 'circlehollow',
items: [
{ value: 'light', icon: 'sun', title: 'Light' },
{ value: 'dark', icon: 'moon', title: 'Dark' }
],
showName: true,
dynamicTitle: true
}
}
},
decorators: [withTheme]
}
export default preview

View File

@@ -0,0 +1,12 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>ComfyUI</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no" />
</head>
<body>
<div id="desktop-app"></div>
<script type="module" src="src/main.ts"></script>
</body>
</html>

View File

@@ -0,0 +1,117 @@
{
"name": "@comfyorg/desktop-ui",
"version": "0.0.3",
"type": "module",
"nx": {
"tags": [
"scope:desktop",
"type:app"
],
"targets": {
"dev": {
"executor": "nx:run-commands",
"continuous": true,
"options": {
"cwd": "apps/desktop-ui",
"command": "vite --config vite.config.mts"
}
},
"serve": {
"executor": "nx:run-commands",
"continuous": true,
"options": {
"cwd": "apps/desktop-ui",
"command": "vite --config vite.config.mts"
}
},
"build": {
"executor": "nx:run-commands",
"cache": true,
"dependsOn": [
"^build"
],
"options": {
"cwd": "apps/desktop-ui",
"command": "vite build --config vite.config.mts"
},
"outputs": [
"{projectRoot}/dist"
]
},
"preview": {
"executor": "nx:run-commands",
"continuous": true,
"dependsOn": [
"build"
],
"options": {
"cwd": "apps/desktop-ui",
"command": "vite preview --config vite.config.mts"
}
},
"storybook": {
"executor": "nx:run-commands",
"continuous": true,
"options": {
"cwd": "apps/desktop-ui",
"command": "storybook dev -p 6007"
}
},
"build-storybook": {
"executor": "nx:run-commands",
"cache": true,
"options": {
"cwd": "apps/desktop-ui",
"command": "storybook build -o dist/storybook"
},
"outputs": [
"{projectRoot}/dist/storybook"
]
},
"lint": {
"executor": "nx:run-commands",
"cache": true,
"options": {
"cwd": "apps/desktop-ui",
"command": "eslint src --cache"
}
},
"typecheck": {
"executor": "nx:run-commands",
"cache": true,
"options": {
"cwd": "apps/desktop-ui",
"command": "vue-tsc --noEmit -p tsconfig.json"
}
}
}
},
"scripts": {
"storybook": "storybook dev -p 6007",
"build-storybook": "storybook build -o dist/storybook"
},
"dependencies": {
"@comfyorg/comfyui-electron-types": "0.4.73-0",
"@comfyorg/shared-frontend-utils": "workspace:*",
"@primevue/core": "catalog:",
"@primevue/themes": "catalog:",
"@vueuse/core": "catalog:",
"pinia": "catalog:",
"primeicons": "catalog:",
"primevue": "catalog:",
"vue": "catalog:",
"vue-i18n": "catalog:",
"vue-router": "catalog:"
},
"devDependencies": {
"@tailwindcss/vite": "catalog:",
"@vitejs/plugin-vue": "catalog:",
"dotenv": "catalog:",
"unplugin-icons": "catalog:",
"unplugin-vue-components": "catalog:",
"vite": "catalog:",
"vite-plugin-html": "catalog:",
"vite-plugin-vue-devtools": "catalog:",
"vue-tsc": "catalog:"
}
}

View File

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 2.6 KiB

View File

Before

Width:  |  Height:  |  Size: 66 KiB

After

Width:  |  Height:  |  Size: 66 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 8.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

View File

Before

Width:  |  Height:  |  Size: 174 KiB

After

Width:  |  Height:  |  Size: 174 KiB

View File

@@ -0,0 +1,7 @@
<template>
<RouterView />
</template>
<script setup lang="ts">
import { RouterView } from 'vue-router'
</script>

View File

@@ -0,0 +1,6 @@
@import '@comfyorg/design-system/css/style.css';
#desktop-app {
position: absolute;
inset: 0;
}

View File

@@ -0,0 +1,113 @@
<template>
<div
ref="rootEl"
class="relative overflow-hidden h-full w-full bg-neutral-900"
>
<div class="p-terminal rounded-none h-full w-full p-2">
<div ref="terminalEl" class="h-full terminal-host" />
</div>
<Button
v-tooltip.left="{
value: tooltipText,
showDelay: 300
}"
icon="pi pi-copy"
severity="secondary"
size="small"
:class="
cn('absolute top-2 right-8 transition-opacity', {
'opacity-0 pointer-events-none select-none': !isHovered
})
"
:aria-label="tooltipText"
@click="handleCopy"
/>
</div>
</template>
<script setup lang="ts">
import { useElementHover, useEventListener } from '@vueuse/core'
import type { IDisposable } from '@xterm/xterm'
import Button from 'primevue/button'
import type { Ref } from 'vue'
import { computed, onMounted, onUnmounted, ref } from 'vue'
import { useI18n } from 'vue-i18n'
import { useTerminal } from '@/composables/bottomPanelTabs/useTerminal'
import { electronAPI, isElectron } from '@/utils/envUtil'
import { cn } from '@/utils/tailwindUtil'
const { t } = useI18n()
const emit = defineEmits<{
created: [ReturnType<typeof useTerminal>, Ref<HTMLElement | undefined>]
unmounted: []
}>()
const terminalEl = ref<HTMLElement | undefined>()
const rootEl = ref<HTMLElement | undefined>()
const hasSelection = ref(false)
const isHovered = useElementHover(rootEl)
const terminalData = useTerminal(terminalEl)
emit('created', terminalData, ref(rootEl))
const { terminal } = terminalData
let selectionDisposable: IDisposable | undefined
const tooltipText = computed(() => {
return hasSelection.value
? t('serverStart.copySelectionTooltip')
: t('serverStart.copyAllTooltip')
})
const handleCopy = async () => {
const existingSelection = terminal.getSelection()
const shouldSelectAll = !existingSelection
if (shouldSelectAll) terminal.selectAll()
const selectedText = shouldSelectAll
? terminal.getSelection()
: existingSelection
if (selectedText) {
await navigator.clipboard.writeText(selectedText)
if (shouldSelectAll) {
terminal.clearSelection()
}
}
}
const showContextMenu = (event: MouseEvent) => {
event.preventDefault()
electronAPI()?.showContextMenu({ type: 'text' })
}
if (isElectron()) {
useEventListener(terminalEl, 'contextmenu', showContextMenu)
}
onMounted(() => {
selectionDisposable = terminal.onSelectionChange(() => {
hasSelection.value = terminal.hasSelection()
})
})
onUnmounted(() => {
selectionDisposable?.dispose()
emit('unmounted')
})
</script>
<style scoped>
@reference '../../../../assets/css/style.css';
:deep(.p-terminal) .xterm {
@apply overflow-hidden;
}
:deep(.p-terminal) .xterm-screen {
@apply bg-neutral-900 overflow-hidden;
}
</style>

View File

@@ -0,0 +1,71 @@
<template>
<div :class="wrapperClass">
<div class="grid grid-rows-2 gap-8">
<!-- Top container: Logo -->
<div class="flex items-end justify-center">
<img
src="/assets/images/comfy-brand-mark.svg"
:alt="t('g.logoAlt')"
class="w-60"
/>
</div>
<!-- Bottom container: Progress and text -->
<div class="flex flex-col items-center justify-center gap-4">
<ProgressBar
v-if="!hideProgress"
:mode="progressMode"
:value="progressPercentage ?? 0"
:show-value="false"
class="w-90 h-2 mt-8"
:pt="{ value: { class: 'bg-brand-yellow' } }"
/>
<h1 v-if="title" class="font-inter font-bold text-3xl text-neutral-300">
{{ title }}
</h1>
<p v-if="statusText" class="text-lg text-neutral-400">
{{ statusText }}
</p>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import ProgressBar from 'primevue/progressbar'
import { computed } from 'vue'
import { useI18n } from 'vue-i18n'
const { t } = useI18n()
/** Props for the StartupDisplay component */
interface StartupDisplayProps {
/** Progress: 0-100 for determinate, undefined for indeterminate */
progressPercentage?: number
/** Main title text */
title?: string
/** Status text shown below the title */
statusText?: string
/** Hide the progress bar */
hideProgress?: boolean
/** Use full screen wrapper (default: true) */
fullScreen?: boolean
}
const {
progressPercentage,
title,
statusText,
hideProgress = false,
fullScreen = true
} = defineProps<StartupDisplayProps>()
const progressMode = computed(() =>
progressPercentage === undefined ? 'indeterminate' : 'determinate'
)
const wrapperClass = computed(() =>
fullScreen
? 'flex items-center justify-center min-h-screen'
: 'flex items-center justify-center'
)
</script>

View File

@@ -0,0 +1,129 @@
<template>
<IconField class="w-full">
<InputText
v-bind="$attrs"
:model-value="internalValue"
class="w-full"
:invalid="validationState === ValidationState.INVALID"
@update:model-value="handleInput"
@blur="handleBlur"
/>
<InputIcon
:class="{
'pi pi-spin pi-spinner text-neutral-400':
validationState === ValidationState.LOADING,
'pi pi-check text-green-500 cursor-pointer':
validationState === ValidationState.VALID,
'pi pi-times text-red-500 cursor-pointer':
validationState === ValidationState.INVALID
}"
@click="validateUrl(props.modelValue)"
/>
</IconField>
</template>
<script setup lang="ts">
import { isValidUrl } from '@comfyorg/shared-frontend-utils/formatUtil'
import { checkUrlReachable } from '@comfyorg/shared-frontend-utils/networkUtil'
import IconField from 'primevue/iconfield'
import InputIcon from 'primevue/inputicon'
import InputText from 'primevue/inputtext'
import { onMounted, ref, watch } from 'vue'
import { ValidationState } from '@/utils/validationUtil'
const props = defineProps<{
modelValue: string
validateUrlFn?: (url: string) => Promise<boolean>
}>()
const emit = defineEmits<{
'update:modelValue': [value: string]
'state-change': [state: ValidationState]
}>()
const validationState = ref<ValidationState>(ValidationState.IDLE)
const cleanInput = (value: string): string =>
value ? value.replace(/\s+/g, '') : ''
// Add internal value state
const internalValue = ref(cleanInput(props.modelValue))
// Watch for external modelValue changes
watch(
() => props.modelValue,
async (newValue: string) => {
internalValue.value = cleanInput(newValue)
await validateUrl(newValue)
}
)
watch(validationState, (newState) => {
emit('state-change', newState)
})
// Validate on mount
onMounted(async () => {
await validateUrl(props.modelValue)
})
const handleInput = (value: string | undefined) => {
// Update internal value without emitting
internalValue.value = cleanInput(value ?? '')
// Reset validation state when user types
validationState.value = ValidationState.IDLE
}
const handleBlur = async () => {
const input = cleanInput(internalValue.value)
let normalizedUrl = input
try {
const url = new URL(input)
normalizedUrl = url.toString()
} catch {
// If URL parsing fails, just use the cleaned input
}
// Emit the update only on blur
emit('update:modelValue', normalizedUrl)
}
// Default validation implementation
const defaultValidateUrl = async (url: string): Promise<boolean> => {
if (!isValidUrl(url)) return false
try {
return await checkUrlReachable(url)
} catch {
return false
}
}
const validateUrl = async (value: string) => {
if (validationState.value === ValidationState.LOADING) return
const url = cleanInput(value)
// Reset state
validationState.value = ValidationState.IDLE
// Skip validation if empty
if (!url) return
validationState.value = ValidationState.LOADING
try {
const isValid = await (props.validateUrlFn ?? defaultValidateUrl)(url)
validationState.value = isValid
? ValidationState.VALID
: ValidationState.INVALID
} catch {
validationState.value = ValidationState.INVALID
}
}
// Add inheritAttrs option to prevent attrs from being applied to root element
defineOptions({
inheritAttrs: false
})
</script>

View File

@@ -10,14 +10,14 @@
</p>
</div>
<div class="flex flex-col bg-neutral-800 p-4 rounded-lg">
<div class="flex flex-col bg-neutral-800 p-4 rounded-lg text-sm">
<!-- Auto Update Setting -->
<div class="flex items-center gap-4">
<div class="flex-1">
<h3 class="text-lg font-medium text-neutral-100">
{{ $t('install.settings.autoUpdate') }}
</h3>
<p class="text-sm text-neutral-400 mt-1">
<p class="text-neutral-400 mt-1">
{{ $t('install.settings.autoUpdateDescription') }}
</p>
</div>
@@ -32,14 +32,10 @@
<h3 class="text-lg font-medium text-neutral-100">
{{ $t('install.settings.allowMetrics') }}
</h3>
<p class="text-sm text-neutral-400 mt-1">
<p class="text-neutral-400">
{{ $t('install.settings.allowMetricsDescription') }}
</p>
<a
href="#"
class="text-sm text-blue-400 hover:text-blue-300 mt-1 inline-block"
@click.prevent="showMetricsInfo"
>
<a href="#" @click.prevent="showMetricsInfo">
{{ $t('install.settings.learnMoreAboutData') }}
</a>
</div>
@@ -51,7 +47,9 @@
<Dialog
v-model:visible="showDialog"
modal
dismissable-mask
:header="$t('install.settings.dataCollectionDialog.title')"
class="select-none"
>
<div class="text-neutral-300">
<h4 class="font-medium mb-2">
@@ -110,11 +108,7 @@
</ul>
<div class="mt-4">
<a
href="https://comfy.org/privacy"
target="_blank"
class="text-blue-400 hover:text-blue-300 underline"
>
<a href="https://comfy.org/privacy" target="_blank">
{{ $t('install.settings.dataCollectionDialog.viewFullPolicy') }}
</a>
</div>

View File

@@ -0,0 +1,103 @@
<template>
<div
class="mx-auto grid h-[40rem] w-full max-w-3xl grid-rows-[1fr_auto_auto_1fr] select-none"
>
<h2 class="text-center font-inter text-3xl font-bold text-neutral-100">
{{ $t('install.gpuPicker.title') }}
</h2>
<!-- GPU Selection buttons - takes up remaining space and centers content -->
<div class="flex flex-1 items-center justify-center gap-8">
<!-- Apple Metal / NVIDIA -->
<HardwareOption
v-if="platform === 'darwin'"
:image-path="'./assets/images/apple-mps-logo.png'"
placeholder-text="Apple Metal"
subtitle="Apple Metal"
:value="'mps'"
:selected="selected === 'mps'"
:recommended="true"
@click="pickGpu('mps')"
/>
<HardwareOption
v-else
:image-path="'./assets/images/nvidia-logo-square.jpg'"
placeholder-text="NVIDIA"
:subtitle="$t('install.gpuPicker.nvidiaSubtitle')"
:value="'nvidia'"
:selected="selected === 'nvidia'"
:recommended="true"
@click="pickGpu('nvidia')"
/>
<!-- CPU -->
<HardwareOption
placeholder-text="CPU"
:subtitle="$t('install.gpuPicker.cpuSubtitle')"
:value="'cpu'"
:selected="selected === 'cpu'"
@click="pickGpu('cpu')"
/>
<!-- Manual Install -->
<HardwareOption
placeholder-text="Manual Install"
:subtitle="$t('install.gpuPicker.manualSubtitle')"
:value="'unsupported'"
:selected="selected === 'unsupported'"
@click="pickGpu('unsupported')"
/>
</div>
<div class="h-16 px-24 pt-12">
<div v-show="showRecommendedBadge" class="flex items-center gap-2">
<Tag
:value="$t('install.gpuPicker.recommended')"
class="rounded-full bg-neutral-300 px-2 py-[1px] text-sm font-bold text-neutral-900"
/>
<i class="icon-[lucide--badge-check] text-lg text-neutral-300" />
</div>
</div>
<div class="px-24 text-neutral-300">
<p v-show="descriptionText" class="leading-relaxed">
{{ descriptionText }}
</p>
</div>
</div>
</template>
<script setup lang="ts">
import type { TorchDeviceType } from '@comfyorg/comfyui-electron-types'
import Tag from 'primevue/tag'
import { computed } from 'vue'
import HardwareOption from '@/components/install/HardwareOption.vue'
import { st } from '@/i18n'
import { electronAPI } from '@/utils/envUtil'
const selected = defineModel<TorchDeviceType | null>('device', {
required: true
})
const electron = electronAPI()
const platform = electron.getPlatform()
const showRecommendedBadge = computed(
() => selected.value === 'mps' || selected.value === 'nvidia'
)
const descriptionKeys = {
mps: 'appleMetal',
nvidia: 'nvidia',
cpu: 'cpu',
unsupported: 'manual'
} as const
const descriptionText = computed(() => {
const key = selected.value ? descriptionKeys[selected.value] : undefined
return st(`install.gpuPicker.${key}Description`, '')
})
const pickGpu = (value: TorchDeviceType) => {
selected.value = value
}
</script>

View File

@@ -0,0 +1,73 @@
// eslint-disable-next-line storybook/no-renderer-packages
import type { Meta, StoryObj } from '@storybook/vue3'
import HardwareOption from './HardwareOption.vue'
const meta: Meta<typeof HardwareOption> = {
title: 'Desktop/Components/HardwareOption',
component: HardwareOption,
parameters: {
layout: 'centered',
backgrounds: {
default: 'dark',
values: [{ name: 'dark', value: '#1a1a1a' }]
}
},
argTypes: {
selected: { control: 'boolean' },
imagePath: { control: 'text' },
placeholderText: { control: 'text' },
subtitle: { control: 'text' }
}
}
export default meta
type Story = StoryObj<typeof meta>
export const AppleMetalSelected: Story = {
args: {
imagePath: '/assets/images/apple-mps-logo.png',
placeholderText: 'Apple Metal',
subtitle: 'Apple Metal',
value: 'mps',
selected: true
}
}
export const AppleMetalUnselected: Story = {
args: {
imagePath: '/assets/images/apple-mps-logo.png',
placeholderText: 'Apple Metal',
subtitle: 'Apple Metal',
value: 'mps',
selected: false
}
}
export const CPUOption: Story = {
args: {
placeholderText: 'CPU',
subtitle: 'Subtitle',
value: 'cpu',
selected: false
}
}
export const ManualInstall: Story = {
args: {
placeholderText: 'Manual Install',
subtitle: 'Subtitle',
value: 'unsupported',
selected: false
}
}
export const NvidiaSelected: Story = {
args: {
imagePath: '/assets/images/nvidia-logo-square.jpg',
placeholderText: 'NVIDIA',
subtitle: 'NVIDIA',
value: 'nvidia',
selected: true
}
}

View File

@@ -0,0 +1,55 @@
<template>
<div class="relative">
<!-- Recommended Badge -->
<button
:class="
cn(
'hardware-option w-[170px] h-[190px] p-5 flex flex-col items-center rounded-3xl transition-all duration-200 bg-neutral-900/70 border-4',
selected ? 'border-solid border-brand-yellow' : 'border-transparent'
)
"
@click="$emit('click')"
>
<!-- Icon/Logo Area - Rounded square container -->
<div
class="icon-container w-[110px] h-[110px] shrink-0 rounded-2xl bg-neutral-800 flex items-center justify-center overflow-hidden"
>
<img
v-if="imagePath"
:src="imagePath"
:alt="placeholderText"
class="w-full h-full object-cover"
style="object-position: 57% center"
draggable="false"
/>
<span v-else class="text-xl font-medium text-neutral-400">
{{ placeholderText }}
</span>
</div>
<!-- Text Content -->
<div v-if="subtitle" class="text-center mt-4">
<div class="text-sm text-neutral-500">{{ subtitle }}</div>
</div>
</button>
</div>
</template>
<script setup lang="ts">
import type { TorchDeviceType } from '@comfyorg/comfyui-electron-types'
import { cn } from '@/utils/tailwindUtil'
interface Props {
imagePath?: string
placeholderText: string
subtitle?: string
value: TorchDeviceType
selected?: boolean
recommended?: boolean
}
defineProps<Props>()
defineEmits<{ click: [] }>()
</script>

View File

@@ -0,0 +1,79 @@
<template>
<div class="grid grid-cols-[1fr_auto_1fr] items-center gap-4">
<!-- Back button -->
<Button
v-if="currentStep !== '1'"
:label="$t('g.back')"
severity="secondary"
icon="pi pi-arrow-left"
class="font-inter rounded-lg border-0 px-6 py-2 justify-self-start"
@click="$emit('previous')"
/>
<div v-else></div>
<!-- Step indicators in center -->
<StepList class="flex justify-center items-center gap-3 select-none">
<Step value="1" :pt="stepPassthrough">
{{ $t('install.gpu') }}
</Step>
<Step value="2" :disabled="disableLocationStep" :pt="stepPassthrough">
{{ $t('install.installLocation') }}
</Step>
<Step value="3" :disabled="disableSettingsStep" :pt="stepPassthrough">
{{ $t('install.desktopSettings') }}
</Step>
</StepList>
<!-- Next/Install button -->
<Button
:label="currentStep !== '3' ? $t('g.next') : $t('g.install')"
class="px-8 py-2 bg-brand-yellow hover:bg-brand-yellow/90 font-inter rounded-lg border-0 transition-colors justify-self-end"
:pt="{
label: { class: 'text-neutral-900 font-inter font-black' }
}"
:disabled="!canProceed"
@click="currentStep !== '3' ? $emit('next') : $emit('install')"
/>
</div>
</template>
<script setup lang="ts">
import type { PassThrough } from '@primevue/core'
import Button from 'primevue/button'
import Step, { type StepPassThroughOptions } from 'primevue/step'
import StepList from 'primevue/steplist'
defineProps<{
/** Current step index as string ('1', '2', '3', '4') */
currentStep: string
/** Whether the user can proceed to the next step */
canProceed: boolean
/** Whether the location step should be disabled */
disableLocationStep: boolean
/** Whether the migration step should be disabled */
disableMigrationStep: boolean
/** Whether the settings step should be disabled */
disableSettingsStep: boolean
}>()
defineEmits<{
previous: []
next: []
install: []
}>()
const stepPassthrough: PassThrough<StepPassThroughOptions> = {
root: { class: 'flex-none p-0 m-0' },
header: ({ context }) => ({
class: [
'h-2.5 p-0 m-0 border-0 rounded-full transition-all duration-300',
context.active
? 'bg-brand-yellow w-8 rounded-sm'
: 'bg-neutral-700 w-2.5',
context.disabled ? 'opacity-60 cursor-not-allowed' : ''
].join(' ')
}),
number: { class: 'hidden' },
title: { class: 'hidden' }
}
</script>

View File

@@ -0,0 +1,148 @@
// eslint-disable-next-line storybook/no-renderer-packages
import type { Meta, StoryObj } from '@storybook/vue3'
import { ref } from 'vue'
import InstallLocationPicker from './InstallLocationPicker.vue'
const meta: Meta<typeof InstallLocationPicker> = {
title: 'Desktop/Components/InstallLocationPicker',
component: InstallLocationPicker,
parameters: {
layout: 'padded',
backgrounds: {
default: 'dark',
values: [
{ name: 'dark', value: '#0a0a0a' },
{ name: 'neutral-900', value: '#171717' },
{ name: 'neutral-950', value: '#0a0a0a' }
]
}
},
decorators: [
() => {
// Mock electron API
;(window as any).electronAPI = {
getSystemPaths: () =>
Promise.resolve({
defaultInstallPath: '/Users/username/ComfyUI'
}),
validateInstallPath: () =>
Promise.resolve({
isValid: true,
exists: false,
canWrite: true,
freeSpace: 100000000000,
requiredSpace: 10000000000,
isNonDefaultDrive: false
}),
validateComfyUISource: () =>
Promise.resolve({
isValid: true
}),
showDirectoryPicker: () => Promise.resolve('/Users/username/ComfyUI')
}
return { template: '<story />' }
}
]
}
export default meta
type Story = StoryObj<typeof meta>
// Default story with accordion expanded
export const Default: Story = {
render: (args) => ({
components: { InstallLocationPicker },
setup() {
const installPath = ref('/Users/username/ComfyUI')
const pathError = ref('')
const migrationSourcePath = ref('/Users/username/ComfyUI-old')
const migrationItemIds = ref<string[]>(['models', 'custom_nodes'])
return {
args,
installPath,
pathError,
migrationSourcePath,
migrationItemIds
}
},
template: `
<div class="min-h-screen bg-neutral-950 p-8">
<InstallLocationPicker
v-model:installPath="installPath"
v-model:pathError="pathError"
v-model:migrationSourcePath="migrationSourcePath"
v-model:migrationItemIds="migrationItemIds"
/>
</div>
`
})
}
// Story with different background to test transparency
export const OnNeutral900: Story = {
render: (args) => ({
components: { InstallLocationPicker },
setup() {
const installPath = ref('/Users/username/ComfyUI')
const pathError = ref('')
const migrationSourcePath = ref('/Users/username/ComfyUI-old')
const migrationItemIds = ref<string[]>(['models', 'custom_nodes'])
return {
args,
installPath,
pathError,
migrationSourcePath,
migrationItemIds
}
},
template: `
<div class="min-h-screen bg-neutral-900 p-8">
<InstallLocationPicker
v-model:installPath="installPath"
v-model:pathError="pathError"
v-model:migrationSourcePath="migrationSourcePath"
v-model:migrationItemIds="migrationItemIds"
/>
</div>
`
})
}
// Story with debug overlay showing background colors
export const DebugBackgrounds: Story = {
render: (args) => ({
components: { InstallLocationPicker },
setup() {
const installPath = ref('/Users/username/ComfyUI')
const pathError = ref('')
const migrationSourcePath = ref('/Users/username/ComfyUI-old')
const migrationItemIds = ref<string[]>(['models', 'custom_nodes'])
return {
args,
installPath,
pathError,
migrationSourcePath,
migrationItemIds
}
},
template: `
<div class="min-h-screen bg-neutral-950 p-8 relative">
<div class="absolute top-4 right-4 text-white text-xs space-y-2 z-50">
<div>Parent bg: neutral-950 (#0a0a0a)</div>
<div>Accordion content: bg-transparent</div>
<div>Migration options: bg-transparent + p-4 rounded-lg</div>
</div>
<InstallLocationPicker
v-model:installPath="installPath"
v-model:pathError="pathError"
v-model:migrationSourcePath="migrationSourcePath"
v-model:migrationItemIds="migrationItemIds"
/>
</div>
`
})
}

View File

@@ -0,0 +1,312 @@
<template>
<div class="flex flex-col gap-8 w-full max-w-3xl mx-auto select-none">
<!-- Installation Path Section -->
<div class="grow flex flex-col gap-6 text-neutral-300">
<h2 class="font-inter font-bold text-3xl text-neutral-100 text-center">
{{ $t('install.locationPicker.title') }}
</h2>
<p class="text-center text-neutral-400 px-12">
{{ $t('install.locationPicker.subtitle') }}
</p>
<!-- Path Input -->
<div class="flex gap-2 px-12">
<InputText
v-model="installPath"
:placeholder="$t('install.locationPicker.pathPlaceholder')"
class="flex-1 bg-neutral-800/50 border-neutral-700 text-neutral-200 placeholder:text-neutral-500"
:class="{ 'p-invalid': pathError }"
@update:model-value="validatePath"
@focus="onFocus"
/>
<Button
icon="pi pi-folder-open"
severity="secondary"
class="bg-neutral-700 hover:bg-neutral-600 border-0"
@click="browsePath"
/>
</div>
<!-- Error Messages -->
<div v-if="pathError || pathExists || nonDefaultDrive" class="px-12">
<Message
v-if="pathError"
severity="error"
class="whitespace-pre-line w-full"
>
{{ pathError }}
</Message>
<Message v-if="pathExists" severity="warn" class="w-full">
{{ $t('install.pathExists') }}
</Message>
<Message v-if="nonDefaultDrive" severity="warn" class="w-full">
{{ $t('install.nonDefaultDrive') }}
</Message>
</div>
<!-- Collapsible Sections using PrimeVue Accordion -->
<Accordion
v-model:value="activeAccordionIndex"
:multiple="true"
class="location-picker-accordion"
:pt="{
root: 'bg-transparent border-0',
panel: {
root: 'border-0 mb-0'
},
header: {
root: 'border-0',
content:
'text-neutral-400 hover:text-neutral-300 px-4 py-2 flex items-center gap-3',
toggleicon: 'text-xs order-first mr-0'
},
content: {
root: 'bg-transparent border-0',
content: 'text-neutral-500 text-sm pl-11 pb-3 pt-0'
}
}"
>
<AccordionPanel value="0">
<AccordionHeader>
{{ $t('install.locationPicker.migrateFromExisting') }}
</AccordionHeader>
<AccordionContent>
<MigrationPicker
v-model:source-path="migrationSourcePath"
v-model:migration-item-ids="migrationItemIds"
/>
</AccordionContent>
</AccordionPanel>
<AccordionPanel value="1">
<AccordionHeader>
{{ $t('install.locationPicker.chooseDownloadServers') }}
</AccordionHeader>
<AccordionContent>
<template
v-for="([item, modelValue], index) in mirrors"
:key="item.settingId + item.mirror"
>
<Divider v-if="index > 0" class="my-8" />
<MirrorItem
v-model="modelValue.value"
:item="item"
@state-change="validationStates[index] = $event"
/>
</template>
</AccordionContent>
</AccordionPanel>
</Accordion>
</div>
</div>
</template>
<script setup lang="ts">
import type { TorchDeviceType } from '@comfyorg/comfyui-electron-types'
import { TorchMirrorUrl } from '@comfyorg/comfyui-electron-types'
import { isInChina } from '@comfyorg/shared-frontend-utils/networkUtil'
import Accordion from 'primevue/accordion'
import AccordionContent from 'primevue/accordioncontent'
import AccordionHeader from 'primevue/accordionheader'
import AccordionPanel from 'primevue/accordionpanel'
import Button from 'primevue/button'
import Divider from 'primevue/divider'
import InputText from 'primevue/inputtext'
import Message from 'primevue/message'
import { type ModelRef, computed, onMounted, ref } from 'vue'
import { useI18n } from 'vue-i18n'
import MigrationPicker from '@/components/install/MigrationPicker.vue'
import MirrorItem from '@/components/install/mirror/MirrorItem.vue'
import {
PYPI_MIRROR,
PYTHON_MIRROR,
type UVMirror
} from '@/constants/uvMirrors'
import { electronAPI } from '@/utils/envUtil'
import { ValidationState } from '@/utils/validationUtil'
const { t } = useI18n()
const installPath = defineModel<string>('installPath', { required: true })
const pathError = defineModel<string>('pathError', { required: true })
const migrationSourcePath = defineModel<string>('migrationSourcePath')
const migrationItemIds = defineModel<string[]>('migrationItemIds')
const pythonMirror = defineModel<string>('pythonMirror', {
default: ''
})
const pypiMirror = defineModel<string>('pypiMirror', {
default: ''
})
const torchMirror = defineModel<string>('torchMirror', {
default: ''
})
const { device } = defineProps<{ device: TorchDeviceType | null }>()
const pathExists = ref(false)
const nonDefaultDrive = ref(false)
const inputTouched = ref(false)
// Accordion state - array of active panel values
const activeAccordionIndex = ref<string[] | undefined>(undefined)
const electron = electronAPI()
// Mirror configuration logic
const getTorchMirrorItem = (device: TorchDeviceType): UVMirror => {
const settingId = 'Comfy-Desktop.UV.TorchInstallMirror'
switch (device) {
case 'mps':
return {
settingId,
mirror: TorchMirrorUrl.NightlyCpu,
fallbackMirror: TorchMirrorUrl.NightlyCpu
}
case 'nvidia':
return {
settingId,
mirror: TorchMirrorUrl.Cuda,
fallbackMirror: TorchMirrorUrl.Cuda
}
case 'cpu':
default:
return {
settingId,
mirror: PYPI_MIRROR.mirror,
fallbackMirror: PYPI_MIRROR.fallbackMirror
}
}
}
const userIsInChina = ref(false)
const useFallbackMirror = (mirror: UVMirror) => ({
...mirror,
mirror: mirror.fallbackMirror
})
const mirrors = computed<[UVMirror, ModelRef<string>][]>(() =>
(
[
[PYTHON_MIRROR, pythonMirror],
[PYPI_MIRROR, pypiMirror],
[getTorchMirrorItem(device ?? 'cpu'), torchMirror]
] as [UVMirror, ModelRef<string>][]
).map(([item, modelValue]) => [
userIsInChina.value ? useFallbackMirror(item) : item,
modelValue
])
)
const validationStates = ref<ValidationState[]>(
mirrors.value.map(() => ValidationState.IDLE)
)
// Get default install path on component mount
onMounted(async () => {
const paths = await electron.getSystemPaths()
installPath.value = paths.defaultInstallPath
await validatePath(paths.defaultInstallPath)
userIsInChina.value = await isInChina()
})
const validatePath = async (path: string | undefined) => {
try {
pathError.value = ''
pathExists.value = false
nonDefaultDrive.value = false
const validation = await electron.validateInstallPath(path ?? '')
// Create a pre-formatted list of errors
if (!validation.isValid) {
const errors: string[] = []
if (validation.cannotWrite) errors.push(t('install.cannotWrite'))
if (validation.freeSpace < validation.requiredSpace) {
const requiredGB = validation.requiredSpace / 1024 / 1024 / 1024
errors.push(`${t('install.insufficientFreeSpace')}: ${requiredGB} GB`)
}
if (validation.parentMissing) errors.push(t('install.parentMissing'))
if (validation.isOneDrive) errors.push(t('install.isOneDrive'))
if (validation.error)
errors.push(`${t('install.unhandledError')}: ${validation.error}`)
pathError.value = errors.join('\n')
}
if (validation.isNonDefaultDrive) nonDefaultDrive.value = true
if (validation.exists) pathExists.value = true
} catch (error) {
pathError.value = t('install.pathValidationFailed')
}
}
const browsePath = async () => {
try {
const result = await electron.showDirectoryPicker()
if (result) {
installPath.value = result
await validatePath(result)
}
} catch (error) {
pathError.value = t('install.failedToSelectDirectory')
}
}
const onFocus = async () => {
if (!inputTouched.value) {
inputTouched.value = true
return
}
// Refresh validation on re-focus
await validatePath(installPath.value)
}
</script>
<style scoped>
@reference '../../assets/css/style.css';
:deep(.location-picker-accordion) {
@apply px-12;
.p-accordionpanel {
@apply border-0 bg-transparent;
}
.p-accordionheader {
@apply bg-neutral-800/50 border-0 rounded-xl mt-2 hover:bg-neutral-700/50;
transition:
background-color 0.2s ease,
border-radius 0.5s ease;
}
/* When panel is expanded, adjust header border radius */
.p-accordionpanel-active {
.p-accordionheader {
@apply rounded-t-xl rounded-b-none;
}
.p-accordionheader-toggle-icon {
&::before {
content: '\e902';
}
}
}
.p-accordioncontent {
@apply bg-neutral-800/50 border-0 rounded-b-xl rounded-t-none;
}
.p-accordioncontent-content {
@apply bg-transparent pt-3 pr-5 pb-5 pl-5;
}
/* Override default chevron icons to use up/down */
.p-accordionheader-toggle-icon {
&::before {
content: '\e933';
}
}
}
</style>

View File

@@ -0,0 +1,45 @@
// eslint-disable-next-line storybook/no-renderer-packages
import type { Meta, StoryObj } from '@storybook/vue3'
import { ref } from 'vue'
import MigrationPicker from './MigrationPicker.vue'
const meta: Meta<typeof MigrationPicker> = {
title: 'Desktop/Components/MigrationPicker',
component: MigrationPicker,
parameters: {
backgrounds: {
default: 'dark',
values: [
{ name: 'dark', value: '#0a0a0a' },
{ name: 'neutral-900', value: '#171717' }
]
}
},
decorators: [
() => {
;(window as any).electronAPI = {
validateComfyUISource: () => Promise.resolve({ isValid: true }),
showDirectoryPicker: () => Promise.resolve('/Users/username/ComfyUI')
}
return { template: '<story />' }
}
]
}
export default meta
type Story = StoryObj<typeof meta>
export const Default: Story = {
render: () => ({
components: { MigrationPicker },
setup() {
const sourcePath = ref('')
const migrationItemIds = ref<string[]>([])
return { sourcePath, migrationItemIds }
},
template:
'<MigrationPicker v-model:sourcePath="sourcePath" v-model:migrationItemIds="migrationItemIds" />'
})
}

View File

@@ -2,10 +2,6 @@
<div class="flex flex-col gap-6 w-[600px]">
<!-- Source Location Section -->
<div class="flex flex-col gap-4">
<h2 class="text-2xl font-semibold text-neutral-100">
{{ $t('install.migrateFromExistingInstallation') }}
</h2>
<p class="text-neutral-400 my-0">
{{ $t('install.migrationSourcePathDescription') }}
</p>
@@ -13,7 +9,7 @@
<div class="flex gap-2">
<InputText
v-model="sourcePath"
placeholder="Select existing ComfyUI installation (optional)"
:placeholder="$t('install.locationPicker.migrationPathPlaceholder')"
class="flex-1"
:class="{ 'p-invalid': pathError }"
@update:model-value="validateSource"
@@ -27,10 +23,7 @@
</div>
<!-- Migration Options -->
<div
v-if="isValidSource"
class="flex flex-col gap-4 bg-neutral-800 p-4 rounded-lg"
>
<div v-if="isValidSource" class="flex flex-col gap-4 p-4 rounded-lg">
<h3 class="text-lg mt-0 font-medium text-neutral-100">
{{ $t('install.selectItemsToMigrate') }}
</h3>

View File

@@ -0,0 +1,109 @@
<template>
<div class="flex flex-col gap-4 text-neutral-400 text-sm">
<div>
<h3 class="text-lg font-medium text-neutral-100 mb-3 mt-0">
{{ $t(`settings.${normalizedSettingId}.name`) }}
</h3>
<p class="my-1">
{{ $t(`settings.${normalizedSettingId}.tooltip`) }}
</p>
</div>
<UrlInput
v-model="modelValue"
:validate-url-fn="
(mirror: string) =>
checkMirrorReachable(mirror + (item.validationPathSuffix ?? ''))
"
@state-change="validationState = $event"
/>
<div v-if="secondParagraph" class="mt-2">
<a href="#" @click.prevent="showDialog = true">
{{ $t('g.learnMore') }}
</a>
<Dialog
v-model:visible="showDialog"
modal
dismissable-mask
:header="$t(`settings.${normalizedSettingId}.urlFormatTitle`)"
class="select-none max-w-3xl"
>
<div class="text-neutral-300">
<p class="mt-1 whitespace-pre-wrap">{{ secondParagraph }}</p>
<div class="mt-2 break-all">
<span class="text-neutral-300 font-semibold">
{{ EXAMPLE_URL_FIRST_PART }}
</span>
<span>{{ EXAMPLE_URL_SECOND_PART }}</span>
</div>
<Divider />
<p>
{{ $t(`settings.${normalizedSettingId}.fileUrlDescription`) }}
</p>
<span class="text-neutral-300 font-semibold">
{{ FILE_URL_SCHEME }}
</span>
<span>
{{ EXAMPLE_FILE_URL }}
</span>
</div>
</Dialog>
</div>
</div>
</template>
<script setup lang="ts">
import { normalizeI18nKey } from '@comfyorg/shared-frontend-utils/formatUtil'
import Dialog from 'primevue/dialog'
import Divider from 'primevue/divider'
import { computed, onMounted, ref, watch } from 'vue'
import UrlInput from '@/components/common/UrlInput.vue'
import type { UVMirror } from '@/constants/uvMirrors'
import { st } from '@/i18n'
import { checkMirrorReachable } from '@/utils/electronMirrorCheck'
import { ValidationState } from '@/utils/validationUtil'
const FILE_URL_SCHEME = 'file://'
const EXAMPLE_FILE_URL = '/C:/MyPythonInstallers/'
const EXAMPLE_URL_FIRST_PART =
'https://github.com/astral-sh/python-build-standalone/releases/download'
const EXAMPLE_URL_SECOND_PART =
'/20250902/cpython-3.12.11+20250902-x86_64-pc-windows-msvc-install_only.tar.gz'
const { item } = defineProps<{
item: UVMirror
}>()
const emit = defineEmits<{
'state-change': [state: ValidationState]
}>()
const modelValue = defineModel<string>('modelValue', { required: true })
const validationState = ref<ValidationState>(ValidationState.IDLE)
const showDialog = ref(false)
const normalizedSettingId = computed(() => {
return normalizeI18nKey(item.settingId)
})
const secondParagraph = computed(() =>
st(`settings.${normalizedSettingId.value}.urlDescription`, '')
)
onMounted(() => {
modelValue.value = item.mirror
})
watch(validationState, (newState) => {
emit('state-change', newState)
// Set fallback mirror if default mirror is invalid
if (
newState === ValidationState.INVALID &&
modelValue.value === item.mirror
) {
modelValue.value = item.fallbackMirror
}
})
</script>

View File

@@ -1,23 +1,23 @@
<template>
<div
class="task-div max-w-48 min-h-52 grid relative"
class="task-div relative grid min-h-52 max-w-48"
:class="{ 'opacity-75': isLoading }"
>
<Card
class="max-w-48 relative h-full overflow-hidden"
class="relative h-full max-w-48 overflow-hidden"
:class="{ 'opacity-65': runner.state !== 'error' }"
v-bind="(({ onClick, ...rest }) => rest)($attrs)"
>
<template #header>
<i
v-if="runner.state === 'error'"
class="pi pi-exclamation-triangle text-red-500 absolute m-2 top-0 -right-14 opacity-15"
class="pi pi-exclamation-triangle absolute top-0 -right-14 m-2 text-red-500 opacity-15"
style="font-size: 10rem"
/>
<img
v-if="task.headerImg"
:src="task.headerImg"
class="object-contain w-full h-full opacity-25 pt-4 px-4"
class="h-full w-full object-contain px-4 pt-4 opacity-25"
/>
</template>
<template #title>
@@ -27,7 +27,7 @@
{{ description }}
</template>
<template #footer>
<div class="flex gap-4 mt-1">
<div class="mt-1 flex gap-4">
<Button
:icon="task.button?.icon"
:label="task.button?.text"
@@ -73,7 +73,7 @@ defineEmits<{
// Bindings
const description = computed(() =>
runner.value.state === 'error'
? props.task.errorDescription ?? props.task.shortDescription
? (props.task.errorDescription ?? props.task.shortDescription)
: props.task.shortDescription
)

View File

@@ -0,0 +1,105 @@
import { FitAddon } from '@xterm/addon-fit'
import { Terminal } from '@xterm/xterm'
import '@xterm/xterm/css/xterm.css'
import { debounce } from 'es-toolkit/compat'
import type { Ref } from 'vue'
import { markRaw, onMounted, onUnmounted } from 'vue'
export function useTerminal(element: Ref<HTMLElement | undefined>) {
const fitAddon = new FitAddon()
const terminal = markRaw(
new Terminal({
convertEol: true,
theme: { background: '#171717' }
})
)
terminal.loadAddon(fitAddon)
terminal.attachCustomKeyEventHandler((event) => {
// Allow default browser copy/paste handling
if (
event.type === 'keydown' &&
(event.ctrlKey || event.metaKey) &&
((event.key === 'c' && terminal.hasSelection()) || event.key === 'v')
) {
// TODO: Deselect text after copy/paste; use IPC.
return false
}
return true
})
onMounted(async () => {
if (element.value) {
terminal.open(element.value)
}
})
onUnmounted(() => {
terminal.dispose()
})
return {
terminal,
useAutoSize({
root,
autoRows = true,
autoCols = true,
minCols = Number.NEGATIVE_INFINITY,
minRows = Number.NEGATIVE_INFINITY,
onResize
}: {
root: Ref<HTMLElement | undefined>
autoRows?: boolean
autoCols?: boolean
minCols?: number
minRows?: number
onResize?: () => void
}) {
const ensureValidRows = (rows: number | undefined): number => {
if (rows == null || isNaN(rows)) {
return (root.value?.clientHeight ?? 80) / 20
}
return rows
}
const ensureValidCols = (cols: number | undefined): number => {
if (cols == null || isNaN(cols)) {
// Sometimes this is NaN if so, estimate.
return (root.value?.clientWidth ?? 80) / 8
}
return cols
}
const resize = () => {
const dims = fitAddon.proposeDimensions()
// Sometimes propose returns NaN, so we may need to estimate.
terminal.resize(
Math.max(
autoCols ? ensureValidCols(dims?.cols) : terminal.cols,
minCols
),
Math.max(
autoRows ? ensureValidRows(dims?.rows) : terminal.rows,
minRows
)
)
onResize?.()
}
const resizeObserver = new ResizeObserver(debounce(resize, 25))
onMounted(async () => {
if (root.value) {
resizeObserver.observe(root.value)
resize()
}
})
onUnmounted(() => {
resizeObserver.disconnect()
})
return { resize }
}
}
}

View File

@@ -0,0 +1,34 @@
export interface UVMirror {
/**
* The setting id defined for the mirror.
*/
settingId: string
/**
* The default mirror to use.
*/
mirror: string
/**
* The fallback mirror to use.
*/
fallbackMirror: string
/**
* The path suffix to validate the mirror is reachable.
*/
validationPathSuffix?: string
}
export const PYTHON_MIRROR: UVMirror = {
settingId: 'Comfy-Desktop.UV.PythonInstallMirror',
mirror:
'https://github.com/astral-sh/python-build-standalone/releases/download',
fallbackMirror:
'https://python-standalone.org/mirror/astral-sh/python-build-standalone',
validationPathSuffix:
'/20250115/cpython-3.10.16+20250115-aarch64-apple-darwin-debug-full.tar.zst.sha256'
}
export const PYPI_MIRROR: UVMirror = {
settingId: 'Comfy-Desktop.UV.PypiInstallMirror',
mirror: 'https://pypi.org/simple/',
fallbackMirror: 'https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple'
}

184
apps/desktop-ui/src/i18n.ts Normal file
View File

@@ -0,0 +1,184 @@
// Import only English locale eagerly as the default/fallback
// ESLint cannot statically resolve dynamic imports with path aliases (@frontend-locales/*),
// but these are properly configured in tsconfig.json and resolved by Vite at build time.
// eslint-disable-next-line import-x/no-unresolved
import enCommands from '@frontend-locales/en/commands.json' with { type: 'json' }
// eslint-disable-next-line import-x/no-unresolved
import en from '@frontend-locales/en/main.json' with { type: 'json' }
// eslint-disable-next-line import-x/no-unresolved
import enNodes from '@frontend-locales/en/nodeDefs.json' with { type: 'json' }
// eslint-disable-next-line import-x/no-unresolved
import enSettings from '@frontend-locales/en/settings.json' with { type: 'json' }
import { createI18n } from 'vue-i18n'
function buildLocale<
M extends Record<string, unknown>,
N extends Record<string, unknown>,
C extends Record<string, unknown>,
S extends Record<string, unknown>
>(main: M, nodes: N, commands: C, settings: S) {
return {
...main,
nodeDefs: nodes,
commands: commands,
settings: settings
} as M & { nodeDefs: N; commands: C; settings: S }
}
// Locale loader map - dynamically import locales only when needed
// ESLint cannot statically resolve these dynamic imports, but they are valid at build time
/* eslint-disable import-x/no-unresolved */
const localeLoaders: Record<
string,
() => Promise<{ default: Record<string, unknown> }>
> = {
ar: () => import('@frontend-locales/ar/main.json'),
es: () => import('@frontend-locales/es/main.json'),
fr: () => import('@frontend-locales/fr/main.json'),
ja: () => import('@frontend-locales/ja/main.json'),
ko: () => import('@frontend-locales/ko/main.json'),
ru: () => import('@frontend-locales/ru/main.json'),
tr: () => import('@frontend-locales/tr/main.json'),
zh: () => import('@frontend-locales/zh/main.json'),
'zh-TW': () => import('@frontend-locales/zh-TW/main.json')
}
const nodeDefsLoaders: Record<
string,
() => Promise<{ default: Record<string, unknown> }>
> = {
ar: () => import('@frontend-locales/ar/nodeDefs.json'),
es: () => import('@frontend-locales/es/nodeDefs.json'),
fr: () => import('@frontend-locales/fr/nodeDefs.json'),
ja: () => import('@frontend-locales/ja/nodeDefs.json'),
ko: () => import('@frontend-locales/ko/nodeDefs.json'),
ru: () => import('@frontend-locales/ru/nodeDefs.json'),
tr: () => import('@frontend-locales/tr/nodeDefs.json'),
zh: () => import('@frontend-locales/zh/nodeDefs.json'),
'zh-TW': () => import('@frontend-locales/zh-TW/nodeDefs.json')
}
const commandsLoaders: Record<
string,
() => Promise<{ default: Record<string, unknown> }>
> = {
ar: () => import('@frontend-locales/ar/commands.json'),
es: () => import('@frontend-locales/es/commands.json'),
fr: () => import('@frontend-locales/fr/commands.json'),
ja: () => import('@frontend-locales/ja/commands.json'),
ko: () => import('@frontend-locales/ko/commands.json'),
ru: () => import('@frontend-locales/ru/commands.json'),
tr: () => import('@frontend-locales/tr/commands.json'),
zh: () => import('@frontend-locales/zh/commands.json'),
'zh-TW': () => import('@frontend-locales/zh-TW/commands.json')
}
const settingsLoaders: Record<
string,
() => Promise<{ default: Record<string, unknown> }>
> = {
ar: () => import('@frontend-locales/ar/settings.json'),
es: () => import('@frontend-locales/es/settings.json'),
fr: () => import('@frontend-locales/fr/settings.json'),
ja: () => import('@frontend-locales/ja/settings.json'),
ko: () => import('@frontend-locales/ko/settings.json'),
ru: () => import('@frontend-locales/ru/settings.json'),
tr: () => import('@frontend-locales/tr/settings.json'),
zh: () => import('@frontend-locales/zh/settings.json'),
'zh-TW': () => import('@frontend-locales/zh-TW/settings.json')
}
// Track which locales have been loaded
const loadedLocales = new Set<string>(['en'])
// Track locales currently being loaded to prevent race conditions
const loadingLocales = new Map<string, Promise<void>>()
/**
* Dynamically load a locale and its associated files (nodeDefs, commands, settings)
*/
export async function loadLocale(locale: string): Promise<void> {
if (loadedLocales.has(locale)) {
return
}
// If already loading, return the existing promise to prevent duplicate loads
const existingLoad = loadingLocales.get(locale)
if (existingLoad) {
return existingLoad
}
const loader = localeLoaders[locale]
const nodeDefsLoader = nodeDefsLoaders[locale]
const commandsLoader = commandsLoaders[locale]
const settingsLoader = settingsLoaders[locale]
if (!loader || !nodeDefsLoader || !commandsLoader || !settingsLoader) {
console.warn(`Locale "${locale}" is not supported`)
return
}
// Create and track the loading promise
const loadPromise = (async () => {
try {
const [main, nodes, commands, settings] = await Promise.all([
loader(),
nodeDefsLoader(),
commandsLoader(),
settingsLoader()
])
const messages = buildLocale(
main.default,
nodes.default,
commands.default,
settings.default
)
i18n.global.setLocaleMessage(locale, messages as LocaleMessages)
loadedLocales.add(locale)
} catch (error) {
console.error(`Failed to load locale "${locale}":`, error)
throw error
} finally {
// Clean up the loading promise once complete
loadingLocales.delete(locale)
}
})()
loadingLocales.set(locale, loadPromise)
return loadPromise
}
// Only include English in the initial bundle
const messages = {
en: buildLocale(en, enNodes, enCommands, enSettings)
}
// Type for locale messages - inferred from the English locale structure
type LocaleMessages = typeof messages.en
export const i18n = createI18n({
// Must set `false`, as Vue I18n Legacy API is for Vue 2
legacy: false,
locale: navigator.language.split('-')[0] || 'en',
fallbackLocale: 'en',
messages,
// Ignore warnings for locale options as each option is in its own language.
// e.g. "English", "中文", "Русский", "日本語", "한국어", "Français", "Español"
missingWarn: /^(?!settings\.Comfy_Locale\.options\.).+/,
fallbackWarn: /^(?!settings\.Comfy_Locale\.options\.).+/
})
/** Convenience shorthand: i18n.global */
export const { t, te } = i18n.global
/**
* Safe translation function that returns the fallback message if the key is not found.
*
* @param key - The key to translate.
* @param fallbackMessage - The fallback message to use if the key is not found.
*/
export function st(key: string, fallbackMessage: string) {
return te(key) ? t(key) : fallbackMessage
}

View File

@@ -0,0 +1,46 @@
import { definePreset } from '@primevue/themes'
import Aura from '@primevue/themes/aura'
import { createPinia } from 'pinia'
import 'primeicons/primeicons.css'
import PrimeVue from 'primevue/config'
import ConfirmationService from 'primevue/confirmationservice'
import ToastService from 'primevue/toastservice'
import Tooltip from 'primevue/tooltip'
import { createApp } from 'vue'
import App from './App.vue'
import './assets/css/style.css'
import { i18n } from './i18n'
import router from './router'
const ComfyUIPreset = definePreset(Aura, {
semantic: {
// @ts-expect-error fixme ts strict error
primary: Aura['primitive'].blue
}
})
const app = createApp(App)
const pinia = createPinia()
app.directive('tooltip', Tooltip)
app
.use(router)
.use(PrimeVue, {
theme: {
preset: ComfyUIPreset,
options: {
prefix: 'p',
cssLayer: {
name: 'primevue',
order: 'theme, base, primevue'
},
darkModeSelector: '.dark-theme, :root:has(.dark-theme)'
}
}
})
.use(ConfirmationService)
.use(ToastService)
.use(pinia)
.use(i18n)
.mount('#desktop-app')

View File

@@ -0,0 +1,92 @@
import {
createRouter,
createWebHashHistory,
createWebHistory
} from 'vue-router'
import { isElectron } from '@/utils/envUtil'
import LayoutDefault from '@/views/layouts/LayoutDefault.vue'
const isFileProtocol = window.location.protocol === 'file:'
const basePath = isElectron() ? '/' : window.location.pathname
const router = createRouter({
history: isFileProtocol ? createWebHashHistory() : createWebHistory(basePath),
routes: [
{
path: '/',
component: LayoutDefault,
children: [
{
path: '',
name: 'WelcomeView',
component: () => import('@/views/WelcomeView.vue')
},
{
path: 'welcome',
name: 'WelcomeViewAlias',
component: () => import('@/views/WelcomeView.vue')
},
{
path: 'install',
name: 'InstallView',
component: () => import('@/views/InstallView.vue')
},
{
path: 'download-git',
name: 'DownloadGitView',
component: () => import('@/views/DownloadGitView.vue')
},
{
path: 'desktop-start',
name: 'DesktopStartView',
component: () => import('@/views/DesktopStartView.vue')
},
{
path: 'desktop-update',
name: 'DesktopUpdateView',
component: () => import('@/views/DesktopUpdateView.vue')
},
{
path: 'server-start',
name: 'ServerStartView',
component: () => import('@/views/ServerStartView.vue')
},
{
path: 'manual-configuration',
name: 'ManualConfigurationView',
component: () => import('@/views/ManualConfigurationView.vue')
},
{
path: 'metrics-consent',
name: 'MetricsConsentView',
component: () => import('@/views/MetricsConsentView.vue')
},
{
path: 'maintenance',
name: 'MaintenanceView',
component: () => import('@/views/MaintenanceView.vue')
},
{
path: 'not-supported',
name: 'NotSupportedView',
component: () => import('@/views/NotSupportedView.vue')
},
{
path: 'desktop-dialog/:dialogId',
name: 'DesktopDialogView',
component: () => import('@/views/DesktopDialogView.vue')
}
]
}
],
scrollBehavior(_to, _from, savedPosition) {
if (savedPosition) {
return savedPosition
}
return { top: 0 }
}
})
export default router

View File

@@ -151,7 +151,6 @@ export const useMaintenanceTaskStore = defineStore('maintenanceTask', () => {
/** @todo Refreshes Electron tasks only. */
const refreshDesktopTasks = async () => {
isRefreshing.value = true
console.log('Refreshing desktop tasks')
await electron.Validation.validateInstallation(processUpdate)
}

Some files were not shown because too many files have changed in this diff Show More