Compare commits

..

106 Commits

Author SHA1 Message Date
Benjamin Lu
d963b8ea3c Widen drop zone 2025-12-10 13:27:57 -08:00
Benjamin Lu
33626a579c Fix menu position docked issue 2025-12-10 13:15:48 -08:00
Benjamin Lu
36fda56d95 Implement progress text on actionbar 2025-12-10 06:36:41 -08:00
Benjamin Lu
268ab1d38a Implement progress bar on actionbar 2025-12-10 04:12:34 -08:00
Benjamin Lu
8bec2df73d nit 2025-12-09 03:38:38 -08:00
Benjamin Lu
b21cf86739 Merge branch 'bl-semantic-jellyfish' of https://github.com/Comfy-Org/ComfyUI_frontend into bl-semantic-jellyfish 2025-12-09 03:19:58 -08:00
Benjamin Lu
b277cbffa7 nit 2025-12-09 03:19:50 -08:00
Benjamin Lu
ae1d6f9021 nit 2025-12-09 03:19:28 -08:00
Benjamin Lu
3fe2d39959 nit 2025-12-09 02:48:42 -08:00
Benjamin Lu
3ce8cca664 nit 2025-12-09 02:46:49 -08:00
Benjamin Lu
38b589c88e nit 2025-12-09 02:46:38 -08:00
Benjamin Lu
8fdf3558ed nit 2025-12-09 02:44:15 -08:00
Benjamin Lu
f7d3a8cb56 nit 2025-12-09 02:43:25 -08:00
github-actions
cbbf3eb4bf [automated] Update test expectations 2025-12-09 10:42:49 +00:00
Benjamin Lu
96893f79f3 nit 2025-12-09 02:40:37 -08:00
Benjamin Lu
a48360d0d8 nit 2025-12-09 01:37:11 -08:00
Benjamin Lu
a5b5fe839c Merge remote-tracking branch 'origin/main' into bl-semantic-jellyfish 2025-12-09 01:35:04 -08:00
Benjamin Lu
db8029291f Fix text expectation after digit shift change 2025-12-09 01:33:26 -08:00
Christian Byrne
4d3f918e8e feat: Enable system notifications on cloud (#7277)
Re-enables the system notification popup for cloud distribution,
allowing cloud devs to notify cloud users about new features and updates
without requiring a new release.

Cloud now fetches release notes from the "cloud" project (instead of
"comfyui") and uses the `cloud_version` field for version comparison.
Since cloud versions are git hashes rather than semver, a helper handles
both formats gracefully.

The "What's New" popup is enabled for cloud, while the update toast and
red dot indicator remain desktop-only since cloud auto-updates and
doesn't require user action.

You can test this by doing `pnpm dev:cloud` and you will see a
notification I added (for testing):

<img width="1891" height="2077" alt="image"
src="https://github.com/user-attachments/assets/6599a6dc-a3e1-406f-a22d-14262be1f258"
/>

Content is controlled by non-devs at cms.comfy.org.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-7277-feat-Enable-system-notifications-on-cloud-2c46d73d365081bcb33cd79ec18faefe)
by [Unito](https://www.unito.io)
2025-12-09 02:32:13 -07:00
Benjamin Lu
f6ffb1f7f0 Fix actionbar digit shifting 2025-12-09 00:56:03 -08:00
Benjamin Lu
ab820bfa26 Readd missing DTID 2025-12-09 00:47:23 -08:00
Benjamin Lu
b42889206c Add tests 2025-12-09 00:43:10 -08:00
Benjamin Lu
f0b9d28ff7 fix(queue): rename clearQueued translation key to clearQueue 2025-12-09 00:08:36 -08:00
Rizumu Ayaka
82fc96155f fix: mouse accidentally sticks and drag the node (#7186)
https://github.com/user-attachments/assets/88b76852-0050-4f16-a371-916af5232517

---------

Co-authored-by: Johnpaul Chiwetelu <49923152+Myestery@users.noreply.github.com>
2025-12-09 01:01:00 -07:00
Benjamin Lu
8b4f174477 refactor(actionbar): move cancel and queue buttons into draggable container 2025-12-08 23:59:57 -08:00
Benjamin Lu
79a6421329 Fix cloud queue cancel to target specific jobs (#7176)
## Summary
- switch QueueProgressOverlay cancel action to target explicit prompt
ids on cloud via /api/queue delete
- keep non-cloud behavior using /interrupt for local installs
- ensure prompt id list generation is straightforward

## Testing
- pnpm typecheck
- pnpm lint:fix

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-7176-Fix-cloud-queue-cancel-to-target-specific-jobs-2c06d73d365081b38ab2f81d71f62186)
by [Unito](https://www.unito.io)
2025-12-09 00:59:43 -07:00
Benjamin Lu
42314d227f Enable shift-drop context menu test (#7140)
## Summary
- turn the shift-drop context menu release action test back on
- keep the drag distance shorter to reduce flake risk while preserving
behavior

## Testing
- pnpm typecheck
- pnpm lint:fix

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-7140-Enable-shift-drop-context-menu-test-2be6d73d36508104a913d55279d4d3e5)
by [Unito](https://www.unito.io)
2025-12-09 00:56:05 -07:00
Christian Byrne
8f300c7163 fix: release notifications unit tests missing i18n mocks (#7281)
## Summary
- Fix missing `i18n` export in `@/i18n` mock for WhatsNewPopup tests
- Fix missing `createI18n` export in `vue-i18n` mock for
ReleaseNotificationToast tests

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-7281-fix-release-notifications-unit-tests-missing-i18n-mocks-2c46d73d365081e595eadaca6a4ace5d)
by [Unito](https://www.unito.io)
2025-12-09 00:35:37 -07:00
Alexander Brown
5b91434ac4 Cleanup: Sidebar Tabs component and style alignment (#7215)
## Summary

Unify the current sidebar tabs, structurally and aesthetically.

## Changes

- Removes the Assets only Layout
- Standardizes the title styling and spacing across the tabs.

## 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-7215-WIP-Sidebar-Tabs-component-and-style-alignment-2c26d73d3650817193bfd752e0d0bbde)
by [Unito](https://www.unito.io)
2025-12-09 00:33:08 -07:00
Christian Byrne
51a336fd36 style: update ui and design of system notification components (what's new, new release notification, help center) (#6300)
## Summary

Migrated help center and release notification components from hardcoded
colors to semantic design tokens for automatic light/dark theme support.

<img width="808" height="874" alt="Selection_2298"
src="https://github.com/user-attachments/assets/c7fb956e-700b-49df-bba0-b85705e89ce7"
/>

<img width="852" height="710" alt="Selection_2265"
src="https://github.com/user-attachments/assets/618205e1-5068-499d-80ab-72626b32d7e1"
/>

<img width="493" height="838" alt="Screenshot from 2025-10-25 21-46-11"
src="https://github.com/user-attachments/assets/7b696673-ec19-4a16-a0b5-ca744ae62fe1"
/>

<img width="493" height="838" alt="Screenshot from 2025-10-25 21-46-25"
src="https://github.com/user-attachments/assets/2767d722-a0e1-426d-82d9-6d5a59f373ee"
/>

## Changes

- **What**: Replaced hardcoded hex/rgb colors with semantic tokens in
HelpCenterMenuContent, WhatsNewPopup, and ReleaseNotificationToast
components
- **Design System**: Added `--interface-menu-surface` and
`--interface-menu-stroke` tokens to style.css for consistent menu
theming
- **UX**: Updated help center menu structure - added "Give Feedback"
button, renamed "Help & Feedback" to "Help & Support", switched to
Lucide icons (except Discord brand logo), added external-link icons

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6300-style-update-ui-and-design-of-system-notification-components-what-s-new-new-release-no-2986d73d365081238458ea7d304b641e)
by [Unito](https://www.unito.io)

---------

Co-authored-by: Alexander Brown <drjkl@comfy.org>
2025-12-09 00:07:02 -07:00
AustinMroz
ad630cfbfe Add label to open subgraph button (#7244)
Also 
- Updates button icon and text color
- Removes the subgraphNode icon, so that space available for node title
is not reduced.

| Before | After |
| ------ | ----- |
| <img width="360" alt="before"
src="https://github.com/user-attachments/assets/e63593d6-2dad-4779-aed5-e0c1e544fb17"
/>| <img width="360" alt="after"
src="https://github.com/user-attachments/assets/b1ad034b-dc1a-4ce0-8ca1-b78acdf8ab0e"
/>|

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-7244-Add-label-to-open-subgraph-button-2c36d73d3650815a8602dce456350b39)
by [Unito](https://www.unito.io)
2025-12-08 21:47:16 -08:00
Benjamin Lu
41eb45754b Fix desktop menu docs links regression (#7181)
## Summary
- make `useExternalLink` rely on the global i18n locale so it can be
used safely outside setup
- restore `electronAdapter` to use the shared `useExternalLink` helper
for docs URLs and static links

## Motivation
Desktop menu items disappeared because a top-level call to
`useExternalLink` in `electronAdapter` triggered `useI18n` at
module-eval time, throwing and blocking extension registration. By
making the composable global-locale-only and using it in
`electronAdapter`, the module can load without setup context while
preserving link behavior.

## Testing
- pnpm typecheck
- pnpm lint:fix

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-7181-Fix-desktop-menu-docs-links-regression-2c06d73d36508157ae48cff078b9173e)
by [Unito](https://www.unito.io)
2025-12-08 22:20:07 -07:00
Benjamin Lu
73741df4e2 fix: lint issues and unused exports 2025-12-08 19:50:42 -08:00
Comfy Org PR Bot
f0a99a0a75 1.35.0 (#7270)
Minor version increment to 1.35.0

**Base branch:** `main`

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-7270-1-35-0-2c46d73d3650815c84fcd30f0e2d291d)
by [Unito](https://www.unito.io)

Co-authored-by: christian-byrne <72887196+christian-byrne@users.noreply.github.com>
2025-12-08 20:22:21 -07:00
github-actions
ad44241e89 [automated] Update test expectations 2025-12-09 03:18:54 +00:00
Alexander Brown
5139e0564e Style: Font Consistency (#7220)
## Summary

Reduce lower level font definitions in most places. Default to Inter.

See #6912 

## Review Focus

Comic Sans is still an option...

## 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-7220-Style-Font-Consistency-2c26d73d365081348f2dd8909dd9bb8f)
by [Unito](https://www.unito.io)

---------

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: github-actions <github-actions@github.com>
2025-12-08 19:48:11 -07:00
Alexander Brown
c9d5d5ab3e 🤖 More Rules (#7208)
## Summary

Adding more to the AGENTS.md lists

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-7208-More-Rules-2c16d73d365081d199d0cb07f4051906)
by [Unito](https://www.unito.io)
2025-12-08 19:43:56 -07:00
Benjamin Lu
87abea992b Merge remote-tracking branch 'origin/main' into bl-semantic-jellyfish
# Conflicts:
#	src/components/TopMenuSection.vue
#	src/components/queue/job/QueueJobItem.vue
2025-12-08 18:37:05 -08:00
Terry Jia
f385ee8ca2 feat: add showScrollbar prop to VirtualGrid (#7227)
## Summary

Enable vertical scrollbar in Media Assets sidebar for better navigation
when content overflows.

The original implementation hid the scrollbar by default. 
Since the intent behind hiding it wasn't documented, this change
preserves the default behavior (showScrollbar: false) while allowing
components to opt-in to visible scrollbars when needed.

fix https://github.com/Comfy-Org/ComfyUI_frontend/issues/6914

## Screenshots (if applicable)
<img width="681" height="890" alt="image"
src="https://github.com/user-attachments/assets/af48a440-6d04-4226-9482-eb17f8d11a40"
/>

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-7227-feat-add-showScrollbar-prop-to-VirtualGrid-2c36d73d365081c8955ee632c6c644f7)
by [Unito](https://www.unito.io)
2025-12-08 21:31:16 -05:00
Terry Jia
4a3098f1f2 performance fix: prevent gcd infinite loop with floating-point step values (#7258)
## Summary

report and fix https://github.com/Comfy-Org/ComfyUI_frontend/issues/3919

- Convert recursive gcd to iterative to avoid stack overflow
- Add epsilon tolerance (1e-10) for floating-point precision issues

This fixes workflow loading hangs when node trying merge values like
0.01 and 0.001, which caused the original recursive gcd to run
indefinitely due to floating-point modulo never reaching exactly zero.

please notice, we need both iterative and epsilon together to fix this
gcd issue

Call Chain

PrimitiveNode.onAfterGraphConfigured
  → #mergeWidgetConfig
    → #isValidConnection
      → mergeIfValid
        → mergeInputSpec
          → mergeNumericInputSpec
            → lcm(step1, step2)
              → gcd(a, b)  ← Problem here

Why It Happened
When some nodes connect to multiple nodes, it may merge values using
LCM, which internally calls GCD.

Original recursive implementation:
```
export const gcd = (a: number, b: number): number => {
   return b === 0 ? a : gcd(b, a % b)
}
```

Issues:
1. Stack Overflow: Recursive calls with many nodes exhausted the call
stack.
2. Floating-Point Precision: For values like gcd(0.01, 0.001):
 ` 0.01 % 0.001 = 0.0009999999999999994  // Not exactly 0!`
3. Due to Ifloating-point representation, the modulo never reaches
exactly zero, causing hundreds or thousands of iterations.

## Screenshots
### before


https://github.com/user-attachments/assets/cca4342c-a882-4590-a8d4-1e0bea19e5b7

### fix with only iterative, without epsilon


https://github.com/user-attachments/assets/1dc52aa4-a86a-40b5-8bac-904094c4c36b


### final fix with iterative and epsilon

https://github.com/user-attachments/assets/7b868b50-c3c9-4be4-8594-27cecbc08a26

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-7258-performance-fix-prevent-gcd-infinite-loop-with-floating-point-step-values-2c46d73d3650818cbe8cf455c934a114)
by [Unito](https://www.unito.io)
2025-12-08 20:41:23 -05:00
Johnpaul Chiwetelu
7b11510f9f fix: autofocus filter input in Select dropdowns to prevent shortcut triggers (#7253)
## Summary
- Adds `auto-filter-focus` prop to Select component when filtering is
enabled
- When the dropdown opens, the filter input is automatically focused
- This prevents keystrokes from triggering global shortcuts while the
user is trying to filter options

## Root Cause
When a user opens a Select dropdown with a filter and starts typing
without explicitly clicking the search box, the filter input doesn't
have focus. Keystrokes are then captured by global shortcut handlers
(e.g., pressing "R" triggers "refresh nodes") instead of filtering the
options.

## Solution
PrimeVue's Select component has an `auto-filter-focus` prop that
automatically focuses the filter input when the dropdown opens. By
enabling this whenever filtering is enabled (`selectOptions.length >
4`), users can immediately start typing to filter without needing to
click the search box first.

## Test plan
- [ ] Open a Select dropdown with more than 4 options (e.g., Load
Checkpoint's ckpt_name)
- [ ] Verify the filter input is automatically focused when the dropdown
opens
- [ ] Type a character and verify it filters the list instead of
triggering shortcuts
- [ ] Verify no console errors when opening/closing the dropdown

Fixes #7221

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-7253-fix-autofocus-filter-input-in-Select-dropdowns-to-prevent-shortcut-triggers-2c46d73d365081feacd1f3301aa3b413)
by [Unito](https://www.unito.io)
2025-12-09 02:21:23 +01:00
Johnpaul Chiwetelu
2636136f87 feat(sidebar): autofocus search input in Workflows, Model, and Node tabs (#7179)
## Summary
This PR improves the user experience by automatically focusing the
search input field when opening the Workflows, Model Library, or Node
Library sidebar tabs.

## Changes
- **SearchBox.vue**: Exposed a `focus()` method to allow parent
components to programmatically set focus on the input.
- **WorkflowsSidebarTab.vue**: Added a watcher to focus the search box
when the tab is activated.
- **ModelLibrarySidebarTab.vue**: Added autofocus on component mount.
- **NodeLibrarySidebarTab.vue**: Added autofocus on component mount.

## Motivation
Users often switch to these tabs specifically to search for an item.
Automatically focusing the search box reduces friction and saves a
click.



https://github.com/user-attachments/assets/8438e71c-a5e4-4b6c-8665-04d535d3ad8e

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-7179-feat-sidebar-autofocus-search-input-in-Workflows-Model-and-Node-tabs-2c06d73d36508199b8c0e83d19f1cd84)
by [Unito](https://www.unito.io)
2025-12-09 01:45:40 +01:00
Luke Mino-Altherr
5a4fd9ec40 fix: Vue Nodes dropdown doesn't show selected model from Asset Browser (#7251)
## Summary
Fixes Vue Nodes 2.0 dropdowns not displaying the selected model when
created from the Asset Browser.

## Root Cause
Widget values were set AFTER the node was added to the graph, causing
Vue's reactivity system to capture stale initial values.

## Solution
Set widget value BEFORE adding node to graph in
`createModelNodeFromAsset.ts`.

## Changes
- **1 file changed**: `createModelNodeFromAsset.ts`
- **4 lines added, 1 removed**: Move widget value assignment before
graph.add()

This ensures Vue's reactivity system captures the correct initial widget
value when the node is created.

Co-authored-by: Claude <noreply@anthropic.com>
2025-12-08 16:12:21 -08:00
Johnpaul Chiwetelu
418f8fff4e fix: Vue Nodes 2.0 slot link drag not working on mobile/touch devices (#7233)
## Summary
- Fix slot link drag and snap/attraction not working on mobile browsers
in Vue Nodes 2.0 mode
- Use `document.elementFromPoint()` to get the actual element under the
pointer instead of relying on `event.target`

## Root Cause
On touch/mobile devices, pointer events have "implicit pointer capture"
- when you touch an element, all subsequent pointer events
(`pointermove`, `pointerup`) for that touch are sent to the same element
where the touch started, regardless of where the pointer moves.

The code was using `event.target` to find slots under the pointer for
snap/attraction. On touch devices, this always returned the original
slot element (where the drag started), not the element currently under
the touch point. This caused:
- No snap/attraction when dragging links over other slots
- Connections failing when dropping on target slots

## Before

https://github.com/user-attachments/assets/55b56d5c-9744-4d6c-abfd-3a2136ab25bc

## After

https://github.com/user-attachments/assets/5bdf2a22-0025-4ae1-9358-35f0100b67d4

## Test plan
- [ ] Enable Vue Nodes 2.0 mode in settings
- [ ] Test on mobile browser or Chrome DevTools mobile simulation
- [ ] Drag a link from one node's output slot to another node's input
slot
- [ ] Verify the link snaps/attracts to compatible slots during drag
- [ ] Verify the connection is made successfully on drop

Fixes #7224
2025-12-09 00:55:13 +01:00
Alexander Brown
5c01861f4e Tests: Playwright test timeouts (#7231)
## Summary

See where we can use proper DOM waiting instead of waitForTimeout.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-7231-WIP-Playwright-test-timeouts-2c36d73d3650812b966ac3d9c338dfd4)
by [Unito](https://www.unito.io)
2025-12-08 15:50:35 -08:00
Luke Mino-Altherr
973d7678a1 fix: Refresh model dropdowns after upload (#7232)
## Summary
Model selection dropdowns now automatically refresh after uploading a
new model, ensuring users see newly uploaded models immediately.

## Changes
- **Cache Orchestration**: Upload wizard now refreshes model caches by
coordinating between `modelToNodeStore` and `assetsStore`
- **Smart Refetching**: Only refreshes node types that use the uploaded
model category (e.g., uploading a checkpoint refreshes
`CheckpointLoaderSimple` but not `LoraLoader`)
- **Clean Architecture**: Stores remain decoupled - the upload wizard
composable orchestrates the refresh logic

## Technical Details
After successful upload, `useUploadModelWizard`:
1. Gets all node providers for the model type from `modelToNodeStore`
2. Calls `assetsStore.updateModelsForNodeType()` for each affected node
type
3. Model dropdowns reactively update via Pinia store watchers

## Review Focus
- Orchestration pattern in upload wizard keeps stores decoupled
- Efficient cache invalidation - only refreshes affected node types

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Alexander Brown <drjkl@comfy.org>
2025-12-08 23:42:08 +00:00
Benjamin Lu
259e9563c8 Hotfix: restore cancel button in top menu (#7234)
## Summary
- restore the interrupt/cancel button in the top menu action bar

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-7234-Hotfix-restore-cancel-button-in-top-menu-2c36d73d365081b18dede1e49183a429)
by [Unito](https://www.unito.io)

---------

Co-authored-by: github-actions <github-actions@github.com>
2025-12-08 15:31:04 -08:00
Comfy Org PR Bot
63592af314 1.34.7 (#7236)
Patch version increment to 1.34.7

**Base branch:** `main`

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-7236-1-34-7-2c36d73d365081799e62e92ba60adacf)
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-12-08 16:25:38 -07:00
Alexander Brown
dd7e7e7383 Hotfix: Templates spec (#7235)
## Summary

Top row of templates no longer contains an Image Generation. That's
interesting, huh?
2025-12-09 00:13:27 +01:00
Benjamin Lu
97c7b33713 Fix job details popover sticking after cancel/delete (#6930)
## Summary
- close the job details popover when its job disappears or timers fire
after list changes, and clear hover timers on unmount
- emit an explicit details-leave on cancel/delete clicks so the popover
closes even if hover-out never fires

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

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6930-Fix-job-details-popover-sticking-after-cancel-delete-2b66d73d365081dc990ae87d01455bad)
by [Unito](https://www.unito.io)
2025-12-08 15:00:12 -08:00
Benjamin Lu
48aa3d9a40 Remove unused event 2025-12-08 13:51:46 -08:00
AustinMroz
248929c655 When moving subgraphInput link, properly disconnect old link (#7229)
When moving an existing link with subgraphInput as source to a new node,
the prior link is removed, but the previous target node would not have
it's link property cleared.

Resolves #7225

Also re-enables several functional skipped tests.

It feels like I'm having to play whack-a-mole reimplementing the same
fixes on every permutation of subgraphIO links. I'd like to setup up a
unified test set that covers them all, but wouldn't want the added work
to further delay this fix.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-7229-When-moving-subgraphInput-link-properly-disconnect-old-link-2c36d73d36508149aca0ce477fee5c9e)
by [Unito](https://www.unito.io)
2025-12-08 13:22:05 -08:00
Benjamin Lu
c4b53c14f5 Make job list button visual seperation change 2025-12-08 11:49:47 -08:00
Terry Jia
6081404abb feat: Don't hide scrollbar in BaseModalLayout (#7226)
## Summary
- Add showScrollbar prop to BaseModalLayout component to control
scrollbar visibility
- Enable scrollbar in Templates dialog for better navigation when
content overflows

The original implementation used scrollbar-hide class by default. Since
the intent behind hiding the scrollbar wasn't documented in the original
PR (#5290), this change preserves the default behavior (showScrollbar:
false) while allowing individual dialogs to opt-in to visible
scrollbars.

The Templates dialog specifically benefits from a visible scrollbar as
it contains a large grid of template cards that requires scrolling.

fix https://github.com/Comfy-Org/ComfyUI_frontend/issues/6915

## Screenshots (if applicable)
<img width="2490" height="1097" alt="image"
src="https://github.com/user-attachments/assets/711b060c-9752-4cee-84c0-d90210969f5a"
/>

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-7226-feat-add-showScrollbar-prop-to-BaseModalLayout-2c36d73d365081b1b55bdcd50ca89030)
by [Unito](https://www.unito.io)
2025-12-08 13:53:35 -05:00
Terry Jia
6820633fea fix: preserve Vue node reactivity during undo/redo operations (#7222)
## Summary
preserve Vue node reactivity during undo/redo operations

Root Cause: The Vue reactivity chain was broken during undo/redo
operations:
1. handleDeleteNode was deleting nodeRefs and nodeTriggers
2. Vue components still held references to the old refs
3. When nodes were recreated, finalizeOperation tried to call triggers
but they were already deleted
4. Vue didn't know the data had changed, so nodes didn't visually update

fix https://github.com/Comfy-Org/ComfyUI_frontend/issues/7040

## Screenshots


https://github.com/user-attachments/assets/2feb294a-36e8-4bbe-b3f7-b7015066abc5

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-7222-fix-preserve-Vue-node-reactivity-during-undo-redo-operations-2c36d73d3650819ab72afb10cbdaf39a)
by [Unito](https://www.unito.io)
2025-12-08 13:38:27 -05:00
Terry Jia
8c5584c997 fix: preserve custom nodes i18n data when locales are lazily loaded (#7214)
## Summary
Custom nodes can provide localized translations via their locales
folder.
After the switch to lazy-loading locales (only English pre-loaded),
custom node i18n data was being lost because:
1. loadCustomNodesI18n() merges data before locale is loaded
2. loadLocale() uses setLocaleMessage() which overwrites everything

Solution: Store custom nodes i18n data and re-merge it after each
lazy-loaded locale completes loading.

- Add mergeCustomNodesI18n() function to store and merge custom data
- Modify loadLocale() to re-merge stored data after loading
- Add unit tests for the i18n merging behavior

fix https://github.com/Comfy-Org/ComfyUI_frontend/issues/7025

## Screenshots (if applicable)
<img width="706" height="1335" alt="image"
src="https://github.com/user-attachments/assets/41e4ba2c-b4c0-4d0d-a104-1ede8939ff92"
/>
<img width="672" height="1319" alt="image"
src="https://github.com/user-attachments/assets/6c3e55e5-20d2-4093-a86a-7496db3dfe94"
/>

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-7214-fix-preserve-custom-nodes-i18n-data-when-locales-are-lazily-loaded-2c26d73d3650812fab31edd71cf51a19)
by [Unito](https://www.unito.io)
2025-12-07 20:31:13 -05:00
Benjamin Lu
0a5fc8b814 Delete unused file 2025-12-07 00:52:02 -08:00
Benjamin Lu
99019cf31c Align expanded QPO to new design 2025-12-07 00:50:49 -08:00
Benjamin Lu
05d2cae61c Add close button back to queue progress overlay 2025-12-07 00:49:25 -08:00
Benjamin Lu
d965fb01fc Add progress bars and progress text 2025-12-07 00:27:11 -08:00
Benjamin Lu
5e1dc915a0 Extract to own component file 2025-12-06 22:19:20 -08:00
Benjamin Lu
da4b2412a4 Remove unused QueueOverlayActive files 2025-12-06 22:16:14 -08:00
Benjamin Lu
5c106bd2cb Add progress bar embedded into top menu section 2025-12-06 22:15:41 -08:00
Benjamin Lu
ca084c31a5 Fix text size 2025-12-06 20:49:19 -08:00
Terry Jia
f80654ae31 fix: Prevent image panning when drawing with stylus in mask editor (#7216)
## Summary

Track pen pointer IDs to prevent touch events from triggering pan/zoom
while drawing with a stylus (Apple Pencil, Surface Pen, Android stylus).

fix https://github.com/Comfy-Org/ComfyUI_frontend/issues/6549,
https://github.com/Comfy-Org/ComfyUI_frontend/issues/7135

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-7216-fix-Prevent-image-panning-when-drawing-with-stylus-in-mask-editor-2c26d73d3650813a926bda40ae83832c)
by [Unito](https://www.unito.io)
2025-12-06 18:40:33 -08:00
AustinMroz
795733b333 Add tests for link type (#7213)
Quick followup adding tests to #7211

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-7213-Add-tests-for-link-type-2c16d73d365081898707f94a40f2e866)
by [Unito](https://www.unito.io)
2025-12-06 16:42:59 -08:00
Benjamin Lu
e65fc56c75 Remove active state 2025-12-06 16:23:03 -08:00
Luke Mino-Altherr
1b95cd25d1 fix: Prioritize filename over name in model upload dialogs (#7203)
## Summary
Updates model upload dialogs to display filename instead of name and
removes redundant background styling from confirmation dialog.

## Changes
- **What**: Swap priority of filename/name display in upload
confirmation and progress dialogs
- **What**: Remove background styling from filename display in
confirmation dialog

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-7203-fix-Prioritize-filename-over-name-in-model-upload-dialogs-2c16d73d365081b788deec1bb2989ed5)
by [Unito](https://www.unito.io)

Co-authored-by: Claude <noreply@anthropic.com>
2025-12-06 15:51:07 -08:00
Benjamin Lu
b790aaf24b Add new N queued toggle 2025-12-06 15:20:00 -08:00
Christian Byrne
10288ee239 feat: propagate errors up subgraphs and show slot errors in subgraphs (#6963)
https://github.com/user-attachments/assets/6531879d-a8a2-420a-aaca-ee329386dd1a

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6963-feat-propagate-errors-up-subgraphs-and-show-slot-errors-in-subgraphs-2b76d73d3650813e8391fac0a5e6dc9b)
by [Unito](https://www.unito.io)

---------

Co-authored-by: github-actions <github-actions@github.com>
2025-12-06 16:02:57 -07:00
Benjamin Lu
71c6da2656 Readd cancel button 2025-12-06 14:24:53 -08:00
Alexander Brown
bca7a435ed fix: Button color and default ordering (#7199)
## Summary

Small fixes for the button and Modal sorting.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-7199-fix-Button-color-and-default-ordering-2c16d73d3650812a8a08f8e8fcd7fd55)
by [Unito](https://www.unito.io)
2025-12-06 13:24:58 -08:00
Terry Jia
23ab924405 fix: add fixed min-width to sidebar panels to prevent content clipping (#7212)
## Summary

The default 10% min-size is too small for panels like Media Assets,
causing content to be clipped.
Use a fixed minimum width to ensure the panel content displays properly.

fix https://github.com/Comfy-Org/ComfyUI_frontend/issues/7210

## Screenshots


https://github.com/user-attachments/assets/65a15f0f-45c1-4361-adc9-eee4ccfad0ee

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-7212-fix-add-fixed-min-width-to-sidebar-panels-to-prevent-content-clipping-2c16d73d365081d8a5a4de30c8eb5e07)
by [Unito](https://www.unito.io)
2025-12-06 12:42:14 -08:00
AustinMroz
a8f6bea371 Color links as common type (#7211)
Previously the color of a link would simply use the type of the target
slot and fallback to the type of the origin slot. When a connection is
made to a node that accepts the any type ('*'), the link has the green
color of an unknown type.

Instead, when a connection is made, the type of a link is now calculated
as the greatest common type of the source and destination. This means
that connections to reroutes are correctly colored.

| Before | After |
| ------ | ----- |
| <img width="360" alt="before"
src="https://github.com/user-attachments/assets/a5544730-e69a-4c85-af33-b303bb30ae71"
/>| <img width="360" alt="after"
src="https://github.com/user-attachments/assets/7d7b59fd-1b79-440b-a97d-a1657313c484"
/>|

The code for calculating common types already exists, it has simply been
moved into litegraph and given a more descriptive name.

Resolves #7196

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-7211-Color-links-as-common-type-2c16d73d365081188460f6b5973db962)
by [Unito](https://www.unito.io)

---------

Co-authored-by: Alexander Brown <drjkl@comfy.org>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
2025-12-06 12:00:07 -08:00
Alexander Piskun
1d18583e42 feat(api-nodes): add pricing for seedream4.5 (#7189)
## Summary

The same logic, with price increased by 25% compared to previous version
of this model.

## Screenshots (if applicable)

<img width="1438" height="685" alt="Screenshot From 2025-12-05 20-47-45"
src="https://github.com/user-attachments/assets/3d2846ff-c1c7-4e2f-a789-45dc32018e25"
/>

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-7189-feat-api-nodes-add-pricing-for-seedream4-5-2c06d73d3650817b975cf6dc64cbe084)
by [Unito](https://www.unito.io)
2025-12-06 14:15:48 +02:00
Alexander Brown
f74c176423 Cleanup: Properties Panel (#7137)
## Summary

- Code cleanup
- Copy, padding, color, alignment of components
- Subgraph Edit mode changes
- Partial fix for the Node Info location (need to do context menu still)
- Editing node title

### Still to-do

- Bi-directionality in values

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-7137-WIP-Cleanup-Properties-Panel-2be6d73d3650813e9430f6bcb09dfb4d)
by [Unito](https://www.unito.io)

---------

Co-authored-by: github-actions <github-actions@github.com>
2025-12-05 21:33:52 -08:00
Christian Byrne
cde49d5b64 refresh feature flags on auth or subscription state change (#7197)
Adds watch on auth state that refreshes remote config. In future PR, the
remote config system and feature flags should be consolidated and moved
out of ComfyApi. Currently, we need feature flags before GraphView
mounts, but also need to add auth headers after auth, which necessitates
these parallel systems temporarily

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-7197-refresh-feature-flags-on-auth-or-subscription-state-change-2c06d73d3650810a906ad36a60c86600)
by [Unito](https://www.unito.io)
2025-12-05 19:40:11 -07:00
Luke Mino-Altherr
5db6d1af9a [feat] Add video help dialog to Upload Model flow (#7177)
## Summary
Adds an interactive video tutorial dialog to help users find CivitAI
model URLs during the Upload Model wizard.

## Changes
- **New Component**: Created reusable `VideoHelpDialog.vue` component
  - Full-width video player with floating close button
  - Configurable props: `videoUrl`, `loop`, `showControls`
  - Custom ESC key handling to prevent parent dialog from closing
  - Click backdrop to dismiss
  - 70% dark backdrop for better video focus
- **Upload Model Flow**: Integrated video help button in step 1 footer
  - "How do I find this?" button opens tutorial video
  - Video demonstrates finding model URLs on CivitAI
- PostHog tracking attribute maintained (`upload-model-step1-help-link`)

## Review Focus
- ESC key event handling uses capture phase to prevent propagation to
parent dialogs
- Component follows existing patterns from `MediaVideoTop.vue` and
`BaseModalLayout.vue`
- Video player accessibility (ARIA labels, keyboard navigation)

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

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-7177-feat-Add-video-help-dialog-to-Upload-Model-flow-2c06d73d36508148963ad9ee60038ea3)
by [Unito](https://www.unito.io)

---------

Co-authored-by: Claude <noreply@anthropic.com>
2025-12-05 18:38:52 -08:00
Alexander Brown
4bf766d451 🤖 Address comments from #7194 (#7198)
## Summary

Addresses Christian's fantastic comments.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-7198-Address-comments-from-7194-2c16d73d365081aaa7a1ea06c2d86ec0)
by [Unito](https://www.unito.io)
2025-12-05 17:52:02 -07:00
Alexander Brown
10fddc9694 🤖 AGENTS.md consolidation and updates (#7194)
## Summary

Unify some of the different documentation intended for LLM consumption.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-7194-AGENTS-md-consolidation-and-updates-2c06d73d36508123b10ed75f03d41e75)
by [Unito](https://www.unito.io)

---------

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
2025-12-05 15:03:31 -08:00
Alexander Brown
57523a0c57 Design: Model management (#7190)
## Summary

Assorted updates to the components involved in uploading personal
models.

## Changes

- Standardize Import buttons
- Let the images fill the space on the card
- Order by recent by default
- Nicer display on the model select popover

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-7190-Design-Model-management-2c06d73d365081e7b9fed7a83b730c0f)
by [Unito](https://www.unito.io)
2025-12-05 15:53:28 -07:00
Simula_r
ec1a7f1da4 fix: vue nodes image preview widget, better multi image gallery support (#7178)
## Summary

Fix image preview to better handle multiple images, switching between
them, and showing the skeleton correctly.

## Changes

- **What**: ImagePreview.vue

## Screenshots (if applicable)

Old (broken)


https://github.com/user-attachments/assets/e4997569-bdf5-4015-a83c-bbaabeac96d6

New (fixed)


https://github.com/user-attachments/assets/19dda841-c909-4fcb-b4d4-99aa1372843b

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-7178-fix-vue-nodes-image-preview-widget-better-multi-image-gallery-support-2c06d73d365081a2afa9e398200e8379)
by [Unito](https://www.unito.io)

---------

Co-authored-by: github-actions <github-actions@github.com>
2025-12-05 13:52:18 -08:00
sno
5e606f274f [bugfix] Fix E2E test report generation for non-chromium browsers (#7193)
## Summary

Fixes an issue where Playwright HTML reports were not being generated
for `chromium-2x`, `chromium-0.5x`, and `mobile-chrome` test runs,
causing 404 errors when accessing report links in PR comments.

## Problem

Only the first report link (chromium) had contents - the other three
browsers returned 404 errors. Investigation revealed that artifacts for
non-chromium browsers were only uploading 1 file (report.json) instead
of the full HTML report with ~32 files.

The root cause was that these browsers run very few tests (1-6 tests
each), and when using `--reporter=html --reporter=json` together,
Playwright would only generate the JSON report without the HTML assets.

## Solution

Changed the workflow for non-chromium browsers to use the same approach
as the sharded chromium tests:
1. Run tests with `--reporter=blob`
2. Generate HTML and JSON reports separately using `merge-reports`

This ensures both HTML and JSON reports are always generated with
complete assets, regardless of test count.

## Testing

The fix can be verified by:
1. Checking that this PR's workflow run uploads similar file counts for
all browsers
2. Confirming all 4 report links are accessible and show proper HTML
content

Related to #7186

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

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-7193-bugfix-Fix-E2E-test-report-generation-for-non-chromium-browsers-2c06d73d365081ba8ea3ed0d3f5d8d38)
by [Unito](https://www.unito.io)

Co-authored-by: Claude <noreply@anthropic.com>
2025-12-05 13:30:24 -08:00
Rizumu Ayaka
3443c8fb65 fix: unintended text selection when resizing the splitter (#7187)
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-7187-fix-unintended-text-selection-when-resizing-the-splitter-2c06d73d365081b38644daa1f07fa1fb)
by [Unito](https://www.unito.io)

---------

Co-authored-by: DrJKL <DrJKL0424@gmail.com>
2025-12-05 18:04:43 +00:00
Johnpaul Chiwetelu
3c8def778e chore: Regenerate screenshots (#7180)
## Summary
Empty PR to trigger screenshot regeneration in CI.

## Test plan
- [ ] CI generates updated screenshots

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-7180-chore-Regenerate-screenshots-2c06d73d3650814187cfd5d11852a0cf)
by [Unito](https://www.unito.io)

---------

Co-authored-by: github-actions <github-actions@github.com>
2025-12-05 06:26:49 +01:00
Kelly Yang
dbda8b47e8 Fix Load Audio nodes (#7175)
### Fix: Fail to load audio node

## Summary
When using Modern Node Design (Nodes 2.0), the Load Audio node is able
to work as expected.
https://github.com/Comfy-Org/ComfyUI_frontend/issues/7155

## Changes

Aligned `Load Audio` node appearance with the `Load Image`
(/src/renderer/extensions/vueNodes/components/NodeWidgets.vue) node for
consistency.

## Screenshots 

<img width="674" height="464" alt="zzzzw"
src="https://github.com/user-attachments/assets/a7bf6a81-00e3-41a8-962b-560e7acb5c41"
/>

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-7175-Fix-issue-No-7155-Nodes-2-0-Load-Audio-node-is-broken-2c06d73d365081ae9ab3c2ea20f061a4)
by [Unito](https://www.unito.io)
2025-12-05 04:09:02 +01:00
Luke Mino-Altherr
57191151ac [feat] Add PostHog data-attr attributes to Upload Model flow (#7173)
## Summary
Adds step-specific `data-attr` attributes throughout the Upload Model
wizard to enable PostHog analytics tracking and action creation,
following PostHog's recommended best practices for element labeling.

## Changes
- **What**: Added `data-attr` attributes to all key interactive elements
in the Upload Model flow (buttons, inputs, selectors)
- **Step-based naming**: Attributes include step numbers for funnel
analysis (e.g., `upload-model-step1-continue-button`)
- **Coverage**: Entry button, URL input, help link, navigation buttons
(cancel/back/continue/confirm/finish), and model type selector

## Benefits
- Enables creation of stable PostHog actions using CSS selectors like
`[data-attr="upload-model-step2-confirm-button"]`
- Allows funnel analysis to track user progression through the 3-step
upload wizard
- Identifies drop-off points and help-seeking behavior
- Follows PostHog best practice of using data attributes over CSS
classes (especially important with Tailwind)

## Review Focus
- Naming consistency and clarity of data-attr values
- Completeness of coverage across the upload flow

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

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-7173-feat-Add-PostHog-data-attr-attributes-to-Upload-Model-flow-2c06d73d365081699861d3d910250e32)
by [Unito](https://www.unito.io)

Co-authored-by: Claude <noreply@anthropic.com>
2025-12-04 19:04:16 -08:00
Alexander Brown
fe2676e8cd A11y: Focus ring for widgets (#7167)
## Summary

Addresses #7165

Add focus state to widget inputs (including textarea)


## Screenshot
<img width="912" height="688" alt="image"
src="https://github.com/user-attachments/assets/329b1747-1c16-499c-9a17-58332d731c35"
/>


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

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-7167-A11y-Focus-ring-for-widgets-2bf6d73d365081b9805ef5921c1d9b6e)
by [Unito](https://www.unito.io)

---------

Co-authored-by: github-actions <github-actions@github.com>
2025-12-05 00:11:34 +00:00
Simula_r
ab777bc65c fix: vue nodes preview node to match lg and add node when clicked (#7146)
## Summary

Make the preview node match recent LGraphNode.vue look. Also add support
to click from search.

## Changes

- **What**: NodeSearchBox.vue, LGraphNodePreview.vue, nodeDefStore.ts

## Screenshots (if applicable)


https://github.com/user-attachments/assets/ed46d641-66bf-4e23-a207-9102609a7a4a


┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-7146-fix-vue-nodes-preview-node-to-match-lg-and-add-node-when-clicked-2bf6d73d3650814383b2c786e2ab4d02)
by [Unito](https://www.unito.io)
2025-12-04 15:31:38 -08:00
Johnpaul Chiwetelu
431fe33ea3 fix: preserve node selection on right-click (#7162)
Previously, right-clicking on a Vue node would deselect all other
selected nodes because the pointerup event handler was calling
toggleNodeSelectionAfterPointerUp regardless of which mouse button was
released.

This fix skips selection handling when the right mouse button (button 2)
is released, allowing the context menu to operate on the existing
selection.

  ## Summary
- Fixes right-click deselecting all selected nodes when using Vue node
rendering
- Now right-clicking preserves the existing selection, allowing context
menu
  actions on multiple nodes

  ## Problem
When multiple nodes were selected and user right-clicked on one of them,
the
`pointerup` event handler would call
`toggleNodeSelectionAfterPointerUp`, which
deselected everything except the clicked node. This broke multi-node
context menu
  operations.

  ## Solution
Skip selection handling in `onPointerup` when `event.button === 2`
(right-click).
  The context menu handler manages selection independently
fix https://github.com/Comfy-Org/ComfyUI_frontend/issues/7136
Before 


https://github.com/user-attachments/assets/23ac5e03-c464-44b7-8950-67c14da9e02b





After


https://github.com/user-attachments/assets/9d1bd6a8-6386-442b-9dc4-6bc8fbe4a0a8

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-7162-fix-preserve-node-selection-on-right-click-2bf6d73d365081acaf75f2fc845bbffb)
by [Unito](https://www.unito.io)

---------

Co-authored-by: GitHub Action <action@github.com>
2025-12-04 22:41:18 +01:00
Johnpaul Chiwetelu
5233749fe3 Fix copy not working when text is selected in dialogs (#7166)
## Summary
- Allow default browser copy (Ctrl+C / Cmd+C) when text is selected
anywhere in the document
- Previously, the graph node copy handler intercepted copy events even
in dialogs

## Problem
Users could not copy error messages from the PromptExecutionError dialog
or other modal dialogs. When pressing Ctrl+C with text selected in a
dialog, the graph copy handler would intercept the event and prevent the
default browser copy behavior.

## Solution
Add a `hasTextSelection()` check to `shouldIgnoreCopyPaste()`. When the
user has any text selected in the document, the function returns `true`,
allowing the default browser copy to proceed.

## Test plan
- [ ] Open an error dialog (trigger a workflow error)
- [ ] Click "Show Report" to expand error details
- [ ] Select some text in the dialog
- [ ] Press Ctrl+C (or Cmd+C on Mac)
- [ ] Paste elsewhere to verify the text was copied
- [ ] Verify graph node copy still works when no text is selected



https://github.com/user-attachments/assets/30a0c501-95ee-4148-b321-3d60339a41c5

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-7166-Fix-copy-not-working-when-text-is-selected-in-dialogs-2bf6d73d36508182a240fd3153cb6969)
by [Unito](https://www.unito.io)

---------

Co-authored-by: GitHub Action <action@github.com>
2025-12-04 22:39:40 +01:00
AustinMroz
45bcf4096c On adding output matchType, initialize type (#7161)
The output type of a matchType output is initialized to
COMFY_MATCHTYPE_V3, but is updated soon after to the value calculated
from input types. Under some difficult to reproduce circumstances, this
output type may be incorrectly evaluated in connections to other switch
nodes. Since the initial type is never a valid connection, this can
produce errors.

Instead, the output type of a matchtype node is initialized to allow
connections to anything to ensure that the subsequent restriction of
output types is guaranteed to be directional

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-7161-On-adding-output-matchType-initialize-type-2bf6d73d3650819ab169ffe9a4ecfeb4)
by [Unito](https://www.unito.io)

---------

Co-authored-by: Alexander Brown <drjkl@comfy.org>
2025-12-04 13:39:35 -08:00
AustinMroz
f800c4091c Fix zombie linkIds on node deletion, add safety check (#7153)
Resolves #7152

- Adds a safety check to make sure link exists.
- Actually solves the problem by making sure the linkId is removed from
the subgraphOutput node when a node is deleted.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-7153-Fix-zombie-linkIds-on-node-deletion-add-safety-check-2bf6d73d365081d98583e6a987431bd1)
by [Unito](https://www.unito.io)

---------

Co-authored-by: Alexander Brown <drjkl@comfy.org>
2025-12-04 12:20:03 -08:00
Rvage
3feeecc740 Fix workflow loading from PNG images with both workflow and parameter… (#7154)
…s metadata

- Reorder handleFile() to check workflow before parameters
- Add validation to prevent JSON parse errors from crashing imports
- Fix loadGraphData() to use explicit type validation instead of falsy
check
- Ensures ComfyUI-generated PNGs with both metadata types load the
workflow, not parameters

Fixes issue where large workflows (e.g., 634 nodes) were replaced with
basic A1111 format when importing PNG files.

## Summary

Fixed workflow loading from PNG images to prioritize workflow metadata
over parameters, preventing large workflows from being replaced with
basic A1111 format.

## Changes

- **What**: Reordered `handleFile()` to check workflow before
parameters, added JSON parse error handling and validation, fixed
`loadGraphData()` to use explicit type checking instead of falsy check
- **Dependencies**: None

## Review Focus

The key issue was in `handleFile()` where parameters were checked before
workflow, causing ComfyUI-generated PNGs (which contain both workflow
and parameters metadata) to incorrectly import as A1111 format. The fix
ensures:
1. Workflow is always checked first and validated properly
2. Parameters are only used as a fallback when no workflow exists
3. Invalid/malformed workflow data doesn't crash the import process

Additionally, `loadGraphData()` was using a falsy check (`if
(!graphData)`) which could incorrectly replace valid but falsy values.
Now uses explicit type validation.

Tested with real-world PNG containing 634-node workflow (780KB) +
parameters (1KB) - now correctly loads the workflow instead of
discarding it.

<!-- Fixes #ISSUE_NUMBER -->

## Screenshots (if applicable)

N/A - Backend logic fix, no UI changes

Fixes #6633

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-7154-Fix-workflow-loading-from-PNG-images-with-both-workflow-and-parameter-2bf6d73d365081ecb7a6c4bf6b6ccd51)
by [Unito](https://www.unito.io)

---------

Co-authored-by: Alexander Brown <DrJKL0424@gmail.com>
Co-authored-by: Alexander Brown <drjkl@comfy.org>
2025-12-04 11:14:38 -08:00
Comfy Org PR Bot
c087f37fcf 1.34.6 (#7130)
Patch version increment to 1.34.6

**Base branch:** `main`

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-7130-1-34-6-2be6d73d36508135a03dd9a179027f5e)
by [Unito](https://www.unito.io)

Co-authored-by: christian-byrne <72887196+christian-byrne@users.noreply.github.com>
2025-12-04 01:31:06 -07:00
Luke Mino-Altherr
8d4a6df7f8 feat: add upgrade modal for model upload when private models disabled (#7124)
## Summary
Adds a dedicated upgrade modal that appears when users without private
models access try to upload models, providing a clear path to upgrade
their subscription.

## Changes
- **New upgrade modal**: Created `UploadModelUpgradeModal` with
dedicated body, header, and footer components
- **Conditional rendering**: Modified `AssetBrowserModal` to show
upgrade modal when `privateModelsEnabled` flag is false
- **Subscription integration**: Connected upgrade flow to existing
subscription system via `showSubscriptionDialog()`
- **Localization**: Added localization keys for upgrade messaging

## Review Focus
- Conditional logic in `AssetBrowserModal.handleUploadClick()` based on
feature flags
- Component naming consistency (all upgrade-related components prefixed
with `UploadModelUpgrade`)
- Footer component refactoring maintains existing upload wizard behavior

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

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-7124-feat-add-upgrade-modal-for-model-upload-when-private-models-disabled-2be6d73d36508147b72eea8a1d6ab772)
by [Unito](https://www.unito.io)

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Alexander Brown <drjkl@comfy.org>
2025-12-03 20:14:00 -08:00
Luke Mino-Altherr
52e915baf0 [feat] Add remote config support for model upload and asset update feature flags (#7143)
## Summary
Feature flags for model upload button and asset update options now check
remote config from `/api/features` first, falling back to websocket
feature flags.

## Changes
- **What**: Added `model_upload_button_enabled` and
`asset_update_options_enabled` to `RemoteConfig` type
- **What**: Updated feature flag getters to prioritize remote config
over websocket flags
- **Why**: Enables dynamic feature control without requiring websocket
connection, consistent with other feature flags pattern

## Review Focus
- Pattern consistency with other remote config feature flags
- Proper fallback behavior when remote config is unavailable

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-7143-feat-Add-remote-config-support-for-model-upload-and-asset-update-feature-flags-2bf6d73d3650819cb364f0ab69d77dd0)
by [Unito](https://www.unito.io)

Co-authored-by: Claude <noreply@anthropic.com>
2025-12-03 19:19:14 -08:00
Terry Jia
643533c572 fix: should allow autoCols=true when server doesn't provide size (#7132)
## Summary

set autoCols = true by default for Logs Terminal

## Changes
Previously, the logs terminal had autoCols: false to preserve
server-side progress bar rendering, However, this caused large black
empty areas when the server terminal was narrower than the UI panel,
Many server environments don't provide terminal size at all, making this
tradeoff not worthwhile.

The original implementation set autoCols: false because the server
renders progress bars based on its own terminal width. If the frontend
used a different column count, progress bars would display incorrectly
(e.g., wrapped or truncated).

However, this created a poor user experience:
1. The terminal only filled a portion of the panel width (often 80
columns)
  2. The remaining space appeared as a large black empty area
3. Many backend environments don't provide size data at all, making the
fixed-width approach pointless

fix https://github.com/Comfy-Org/ComfyUI_frontend/issues/7117

## Screenshots (if applicable)
before

https://github.com/user-attachments/assets/e38af410-9cf1-4175-8acc-39d11a5b73ce

after

https://github.com/user-attachments/assets/3d180318-609f-44c5-aac5-230f9e4ef596

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-7132-fix-should-allow-autoCols-true-when-server-doesn-t-provide-size-2be6d73d365081e7bf3bf5988bdba39a)
by [Unito](https://www.unito.io)
2025-12-03 22:18:27 -05:00
Terry Jia
8b5e43d7f3 fix: reset touch state when iPad Safari loses focus (#7134)
## Summary
When the browser loses focus (e.g., switching apps, showing the dock),
touchend events may not fire, causing touchCount to remain non-zero.
This blocks all subsequent single-finger interactions since
processMouseDown returns early when touchCount > 0.

Added visibilitychange and touchcancel event listeners to reset touch
state when the page becomes hidden or touch is interrupted by the
system.

fix https://github.com/Comfy-Org/ComfyUI_frontend/issues/6721

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-7134-fix-reset-touch-state-when-iPad-Safari-loses-focus-2be6d73d3650819abc7cf9c602909228)
by [Unito](https://www.unito.io)
2025-12-03 20:34:56 -05:00
Terry Jia
e9d5ce7f3f selection rectangle for vueNodes (#7088)
## Summary

fix: render selection rectangle in DOM layer for Vue nodes mode.

When Vue nodes are enabled, the canvas selection rectangle was being
rendered behind Vue node elements due to DOM stacking order (canvas
layer is below the TransformPane layer).

## Changes
- Adds a new SelectionRectangle.vue component that renders the selection
box as a DOM element
- Places it above the Vue nodes layer so it's always visible during drag
selection
- Skips canvas-based selection rectangle rendering when Vue nodes mode
is active
- Bonus: adds a semi-transparent blue fill style for better visibility


## Screenshots
before

https://github.com/user-attachments/assets/a8ee2ca3-00fd-4fdc-925a-dc9f846f4280

after

https://github.com/user-attachments/assets/66b7f2f5-f0a0-486f-9556-3872d07d65be

One more thing, the following improvement will be live selection,
something like:


https://github.com/user-attachments/assets/05a2b7ea-89b1-4568-bd2a-792f4fc11d8e

but I don't want to increase this PR, so I will send live selection
after this selection rectangle

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-7088-selection-rectangle-for-vueNodes-2bd6d73d3650817aa2e9cf4526f179d8)
by [Unito](https://www.unito.io)
2025-12-03 08:26:57 -05:00
Christian Byrne
19d98a09ea test: temporarily mark linkInteraction.spec.ts as test case as fixme (#7129)
This test
68274134c8/browser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts (L830-L879)

started failing after
https://github.com/Comfy-Org/ComfyUI_frontend/pull/6952


cf5b5bf984.zip

No idea how they could be related, disabling temporarily to not mess up
the CI signal on `main`.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-7129-test-temporarily-mark-linkInteraction-spec-ts-as-test-case-as-fixme-2be6d73d36508172925af3e7fa681f25)
by [Unito](https://www.unito.io)

---------

Co-authored-by: GitHub Action <action@github.com>
2025-12-03 00:36:43 -07:00
Rizumu Ayaka
39d305df86 test: fix test for form select button (#7128)
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-7128-test-fix-test-for-form-select-button-2be6d73d365081f3a889d51d2c4b3358)
by [Unito](https://www.unito.io)
2025-12-03 00:32:41 -07:00
Rizumu Ayaka
68274134c8 feat: right side panel (#6952)
<img width="1183" height="809" alt="CleanShot 2025-11-26 at 16 01 15"
src="https://github.com/user-attachments/assets/c14dc5c3-a672-4dcd-917d-14f16310188e"
/>

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6952-feat-right-side-panel-2b76d73d36508112b121c283a479f42a)
by [Unito](https://www.unito.io)

---------

Co-authored-by: github-actions <github-actions@github.com>
2025-12-02 22:55:24 -07:00
Comfy Org PR Bot
fb54669dc3 [chore] Update Comfy Registry API types from comfy-api@de3478d (#7126)
## Automated API Type Update

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

- API commit: de3478d
- Generated on: 2025-12-03T03:34:46Z

These types are automatically generated using openapi-typescript.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-7126-chore-Update-Comfy-Registry-API-types-from-comfy-api-de3478d-2be6d73d36508179a6cdf3df78806873)
by [Unito](https://www.unito.io)

Co-authored-by: christian-byrne <72887196+christian-byrne@users.noreply.github.com>
2025-12-02 22:39:14 -07:00
Alexander Brown
662974b222 Speed: Remove some optimizations that weren't optimizing (#6209)
## Summary

Simplify the TransformPane.

## Changes

- **What**: Remove the settling and culling composables. Gets rid of a
frequent event emission and some event handling addition/removals.

## Review Focus

In testing with a huge workflow in Vue mode, it was a lot faster without
these than with.
Can you check to see if you experience the same benefits?

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6209-Speed-Remove-some-optimizations-that-weren-t-optimizing-2946d73d3650815197a4df3c58a61575)
by [Unito](https://www.unito.io)

---------

Co-authored-by: github-actions <github-actions@github.com>
2025-12-02 21:28:52 -08:00
Alexander Brown
2bf45f23dc Feat: add floating label to textarea (#7121)
## Summary

Adds the label/name of the widget as a floating label.

## Screenshots

<img width="543" height="469" alt="image"
src="https://github.com/user-attachments/assets/99d70e0b-f7b7-4b62-bf15-a2db8437c886"
/>


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

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-7121-Feat-add-floating-label-to-textarea-2be6d73d3650819c958efa893e0e304a)
by [Unito](https://www.unito.io)

---------

Co-authored-by: github-actions <github-actions@github.com>
2025-12-02 19:34:41 -08:00
396 changed files with 9021 additions and 4023 deletions

View File

@@ -1,61 +0,0 @@
# Vue 3 Composition API Project Rules
## Vue 3 Composition API Best Practices
- Use setup() function for component logic
- Utilize ref and reactive for reactive state
- Implement computed properties with computed()
- Use watch and watchEffect for side effects
- Implement lifecycle hooks with onMounted, onUpdated, etc.
- Utilize provide/inject for dependency injection
- Use vue 3.5 style of default prop declaration. Example:
```typescript
const { nodes, showTotal = true } = defineProps<{
nodes: ApiNodeCost[]
showTotal?: boolean
}>()
```
- Organize vue component in <template> <script> <style> order
## Project Structure
```
src/
components/
constants/
composables/
views/
stores/
services/
App.vue
main.ts
```
## Styling Guidelines
- Use Tailwind CSS for styling
- Implement responsive design with Tailwind CSS
## PrimeVue Component Guidelines
DO NOT use deprecated PrimeVue components. Use these replacements instead:
- Dropdown → Use Select (import from 'primevue/select')
- OverlayPanel → Use Popover (import from 'primevue/popover')
- Calendar → Use DatePicker (import from 'primevue/datepicker')
- InputSwitch → Use ToggleSwitch (import from 'primevue/toggleswitch')
- Sidebar → Use Drawer (import from 'primevue/drawer')
- Chips → Use AutoComplete with multiple enabled and typeahead disabled
- TabMenu → Use Tabs without panels
- Steps → Use Stepper without panels
- InlineMessage → Use Message component
## Development Guidelines
1. Leverage VueUse functions for performance-enhancing styles
2. Use es-toolkit for utility functions
3. Use TypeScript for type safety
4. Implement proper props and emits definitions
5. Utilize Vue 3's Teleport component when needed
6. Use Suspense for async components
7. Implement proper error handling
8. Follow Vue 3 style guide and naming conventions
9. Use Vite for fast development and building
10. Use vue-i18n in composition API for any string literals. Place new translation entries in src/locales/en/main.json
11. Never use deprecated PrimeVue components listed above

View File

@@ -124,12 +124,19 @@ jobs:
- name: Run Playwright tests (${{ matrix.browser }})
id: playwright
run: |
# Run tests with both HTML and JSON reporters
# Run tests with blob reporter first
pnpm exec playwright test --project=${{ matrix.browser }} --reporter=blob
env:
PLAYWRIGHT_BLOB_OUTPUT_DIR: ./blob-report
- name: Generate HTML and JSON reports
if: always()
run: |
# Generate HTML report from blob
pnpm exec playwright merge-reports --reporter=html ./blob-report
# Generate JSON report separately with explicit output path
PLAYWRIGHT_JSON_OUTPUT_NAME=playwright-report/report.json \
pnpm exec playwright test --project=${{ matrix.browser }} \
--reporter=list \
--reporter=html \
--reporter=json
pnpm exec playwright merge-reports --reporter=json ./blob-report
- name: Upload Playwright report
uses: actions/upload-artifact@v4

233
AGENTS.md
View File

@@ -1,38 +1,225 @@
# Repository Guidelines
## Project Structure & Module Organization
- Source: `src/` (Vue 3 + TypeScript). Key areas: `components/`, `views/`, `stores/` (Pinia), `composables/`, `services/`, `utils/`, `assets/`, `locales/`.
- 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.ts`, `.prettierrc`.
- Source: `src/`
- Vue 3.5+
- TypeScript
- Tailwind 4
- Key areas:
- `components/`
- `views/`
- `stores/` (Pinia)
- `composables/`
- `services/`
- `utils/`
- `assets/`
- `locales/`
- Routing: `src/router.ts`,
- i18n: `src/i18n.ts`,
- Entry Point: `src/main.ts`.
- Tests:
- unit/component in `tests-ui/` and `src/**/*.test.ts`
- E2E (Playwright) in `browser_tests/**/*.spec.ts`
- Public assets: `public/`
- Build output: `dist/`
- Configs
- `vite.config.mts`
- `vitest.config.ts`
- `playwright.config.ts`
- `eslint.config.ts`
- `.prettierrc`
- etc.
## Monorepo Architecture
The project uses **Nx** for build orchestration and task management
## 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.
- `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.
- `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
- `pnpm test:browser`: Run Playwright E2E tests (`browser_tests/`)
- `pnpm lint` / `pnpm lint:fix`: Lint (ESLint)
- `pnpm format` / `pnpm format:check`: Prettier
- `pnpm typecheck`: Vue TSC type checking
## Coding Style & Naming Conventions
- Language: TypeScript, Vue SFCs (`.vue`). Indent 2 spaces; single quotes; no semicolons; width 80 (see `.prettierrc`).
- Imports: sorted/grouped by plugin; run `pnpm format` before committing.
- ESLint: Vue + TS rules; no floating promises; unused imports disallowed; i18n raw text restrictions in templates.
- Naming: Vue components in PascalCase (e.g., `MenuHamburger.vue`); composables `useXyz.ts`; Pinia stores `*Store.ts`.
- Language:
- TypeScript (exclusive, no new JavaScript)
- Vue 3 SFCs (`.vue`)
- Composition API only
- Tailwind 4 styling
- Avoid `<style>` blocks
- Style: (see `.prettierrc`)
- Indent 2 spaces
- single quotes
- no trailing semicolons
- width 80
- Imports:
- sorted/grouped by plugin
- run `pnpm format` before committing
- ESLint:
- Vue + TS rules
- no floating promises
- unused imports disallowed
- i18n raw text restrictions in templates
- Naming:
- Vue components in PascalCase (e.g., `MenuHamburger.vue`)
- composables `useXyz.ts`
- Pinia stores `*Store.ts`
## Testing Guidelines
- Frameworks: Vitest (unit/component, happy-dom) and Playwright (E2E).
- Test files: `**/*.{test,spec}.{ts,tsx,js}` under `tests-ui/`, `src/components/`, and `src/lib/litegraph/test/`.
- Coverage: text/json/html reporters enabled; aim to cover critical logic and new features.
- Playwright: place tests in `browser_tests/`; optional tags like `@mobile`, `@2x` are respected by config.
- Frameworks:
- Vitest (unit/component, happy-dom)
- Playwright (E2E)
- Test files:
- Unit/Component: `**/*.test.ts`
- E2E: `browser_tests/**/*.spec.ts`
- Litegraph Specific: `src/lib/litegraph/test/`
- Coverage: text/json/html reporters enabled
- aim to cover critical logic and new features
- Playwright:
- optional tags like `@mobile`, `@2x` are respected by config
- Tests to avoid
- Change detector tests
- e.g. a test that just asserts that the defaults are certain values
- Tests that are dependent on non-behavioral features like utility classes or styles
- Redundant tests
## Commit & Pull Request Guidelines
- 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.
- PRs:
- Include clear description
- Reference linked issues (e.g. `- Fixes #123`)
- Keep it extremely concise and information-dense
- Don't use emojis or add excessive headers/sections
- Follow the PR description template in the `.github/` folder.
- Quality gates:
- `pnpm lint`
- `pnpm typecheck`
- `pnpm knip`
- Relevant tests must pass
- Never use `--no-verify` to bypass failing tests
- Identify the issue and present root cause analysis and possible solutions if you are unable to solve quickly yourself
- Keep PRs focused and small
- If it looks like the current changes will have 300+ lines of non-test code, suggest ways it could be broken into multiple PRs
## Security & Configuration Tips
- Secrets: Use `.env` (see `.env_example`); do not commit secrets.
## Vue 3 Composition API Best Practices
- Use `<script setup lang="ts">` for component logic
- Utilize `ref` for reactive state
- Implement computed properties with computed()
- Use watch and watchEffect for side effects
- Avoid using a `ref` and a `watch` if a `computed` would work instead
- Implement lifecycle hooks with onMounted, onUpdated, etc.
- Utilize provide/inject for dependency injection
- Do not use dependency injection if a Store or a shared composable would be simpler
- Use Vue 3.5 TypeScript style of default prop declaration
- Example:
```typescript
const { nodes, showTotal = true } = defineProps<{
nodes: ApiNodeCost[]
showTotal?: boolean
}>()
```
- Prefer reactive props destructuring to `const props = defineProps<...>`
- Do not use `withDefaults` or runtime props declaration
- Do not import Vue macros unnecessarily
- Prefer `useModel` to separately defining a prop and emit
- Be judicious with addition of new refs or other state
- If it's possible to accomplish the design goals with just a prop, don't add a `ref`
- If it's possible to use the `ref` or prop directly, don't add a `computed`
- If it's possible to use a `computed` to name and reuse a derived value, don't use a `watch`
## Development Guidelines
1. Leverage VueUse functions for performance-enhancing styles
2. Use es-toolkit for utility functions
3. Use TypeScript for type safety
4. If a complex type definition is inlined in multiple related places, extract and name it for reuse
5. In Vue Components, implement proper props and emits definitions
6. Utilize Vue 3's Teleport component when needed
7. Use Suspense for async components
8. Implement proper error handling
9. Follow Vue 3 style guide and naming conventions
10. Use Vite for fast development and building
11. Use vue-i18n in composition API for any string literals. Place new translation entries in src/locales/en/main.json
12. Avoid new usage of PrimeVue components
13. Write tests for all changes, especially bug fixes to catch future regressions
14. Write code that is expressive and self-documenting to the furthest degree possible. This reduces the need for code comments which can get out of sync with the code itself. Try to avoid comments unless absolutely necessary
15. Do not add or retain redundant comments, clean as you go
16. Whenever a new piece of code is written, the author should ask themselves 'is there a simpler way to introduce the same functionality?'. If the answer is yes, the simpler course should be chosen
17. Refactoring should be used to make complex code simpler
18. Try to minimize the surface area (exported values) of each module and composable
19. Don't use barrel files, e.g. `/some/package/index.ts` to re-export within `/src`
20. Keep functions short and functional
21. Minimize [nesting](https://wiki.c2.com/?ArrowAntiPattern), e.g. `if () { ... }` or `for () { ... }`
22. Avoid mutable state, prefer immutability and assignment at point of declaration
23. Favor pure functions (especially testable ones)
24. Watch out for [Code Smells](https://wiki.c2.com/?CodeSmell) and refactor to avoid them
## External Resources
- Vue: <https://vuejs.org/api/>
- Tailwind: <https://tailwindcss.com/docs/styling-with-utility-classes>
- VueUse: <https://vueuse.org/functions.html>
- shadcn/vue: <https://www.shadcn-vue.com/>
- Reka UI: <https://reka-ui.com/>
- PrimeVue: <https://primevue.org>
- ComfyUI: <https://docs.comfy.org>
- Electron: <https://www.electronjs.org/docs/latest/>
- Wiki: <https://deepwiki.com/Comfy-Org/ComfyUI_frontend/1-overview>
- Nx: <https://nx.dev/docs/reference/nx-commands>
## Project Philosophy
- Follow good software engineering principles
- YAGNI
- AHA
- DRY
- SOLID
- Clean, stable public APIs
- Domain-driven design
- Thousands of users and extensions
- Prioritize clean interfaces that restrict extension access
## Repository Navigation
- Check README files in key folders (tests-ui, browser_tests, composables, etc.)
- Prefer running single tests for performance
- Use --help for unfamiliar CLI tools
## GitHub Integration
When referencing Comfy-Org repos:
1. Check for local copy
2. Use GitHub API for branches/PRs/metadata
3. Curl GitHub website if needed
## Common Pitfalls
- NEVER use `any` type - use proper TypeScript types
- NEVER use `as any` type assertions - fix the underlying type issue
- NEVER use `--no-verify` flag when committing
- NEVER delete or disable tests to make them pass
- NEVER circumvent quality checks
- NEVER use the `dark:` tailwind variant
- 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'`
- e.g. `<div :class="cn('text-node-component-header-icon', hasError && 'text-danger')" />`
- Use `cn()` inline in the template when feasible instead of creating a `computed` to hold the value

120
CLAUDE.md
View File

@@ -1,44 +1,19 @@
# ComfyUI Frontend Project Guidelines
# Claude Code specific instructions
@Agents.md
## Repository Setup
For first-time setup, use the Claude command:
```
```sh
/setup_repo
```
This bootstraps the monorepo with dependencies, builds, tests, and dev server verification.
**Prerequisites:** Node.js >= 24, Git repository, available ports (5173, 6006)
## Quick Commands
- `pnpm`: See all available commands
- `pnpm dev`: Start development server (port 5173, via nx)
- `pnpm typecheck`: Type checking
- `pnpm build`: Build for production (via nx)
- `pnpm lint`: Linting (via nx)
- `pnpm oxlint`: Fast Rust-based linting with Oxc
- `pnpm format`: Prettier formatting
- `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
- `pnpm storybook`: Start Storybook development server (port 6006)
- `pnpm knip`: Detect unused code and dependencies
## Monorepo Architecture
The project now uses **Nx** for build orchestration and task management:
- **Task Orchestration**: Commands like `dev`, `build`, `lint`, and `test:browser` run via Nx
- **Caching**: Nx provides intelligent caching for faster rebuilds
- **Configuration**: Managed through `nx.json` with plugins for ESLint, Storybook, Vite, and Playwright
- **Dependencies**: Nx handles dependency graph analysis and parallel execution
Key Nx features:
- Build target caching and incremental builds
- Parallel task execution across the monorepo
- Plugin-based architecture for different tools
## Development Workflow
1. **First-time setup**: Run `/setup_repo` Claude command
@@ -50,87 +25,6 @@ Key Nx features:
## Git Conventions
- Use [prefix] format: [feat], [bugfix], [docs]
- Use `prefix:` format: `feat:`, `fix:`, `test:`
- Add "Fixes #n" to PR descriptions
- Never mention Claude/AI in commits
## External Resources
- PrimeVue docs: <https://primevue.org>
- ComfyUI docs: <https://docs.comfy.org>
- Electron: <https://www.electronjs.org/docs/latest/>
- Wiki: <https://deepwiki.com/Comfy-Org/ComfyUI_frontend/1-overview>
## Project Philosophy
- Follow good software engineering principles
- YAGNI
- AHA
- DRY
- SOLID
- Clean, stable public APIs
- Domain-driven design
- Thousands of users and extensions
- Prioritize clean interfaces that restrict extension access
## Repository Navigation
- Check README files in key folders (tests-ui, browser_tests, composables, etc.)
- Prefer running single tests for performance
- Use --help for unfamiliar CLI tools
## GitHub Integration
When referencing Comfy-Org repos:
1. Check for local copy
2. Use GitHub API for branches/PRs/metadata
3. Curl GitHub website if needed
## Settings and Feature Flags Quick Reference
### Settings Usage
```typescript
const settingStore = useSettingStore()
const value = settingStore.get('Comfy.SomeSetting') // Get setting
await settingStore.set('Comfy.SomeSetting', newValue) // Update setting
```
### Dynamic Defaults
```typescript
{
id: 'Comfy.Example.Setting',
defaultValue: () => window.innerWidth < 1024 ? 'small' : 'large' // Runtime context
}
```
### Version-Based Defaults
```typescript
{
id: 'Comfy.Example.Feature',
defaultValue: 'legacy',
defaultsByInstallVersion: { '1.25.0': 'enhanced' } // Gradual rollout
}
```
### Feature Flags
```typescript
if (api.serverSupportsFeature('feature_name')) { // Check capability
// Use enhanced feature
}
const value = api.getServerFeature('config_name', defaultValue) // Get config
```
**Documentation:**
- Settings system: `docs/SETTINGS.md`
- Feature flags system: `docs/FEATURE_FLAGS.md`
## Common Pitfalls
- NEVER use `any` type - use proper TypeScript types
- NEVER use `as any` type assertions - fix the underlying type issue
- NEVER use `--no-verify` flag when committing
- NEVER delete or disable tests to make them pass
- NEVER circumvent quality checks
- 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,13 +1,13 @@
// 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'
@@ -27,7 +27,7 @@ function buildLocale<
// 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> }>

View File

@@ -13,6 +13,7 @@ import { ComfyTemplates } from '../helpers/templates'
import { ComfyMouse } from './ComfyMouse'
import { VueNodeHelpers } from './VueNodeHelpers'
import { ComfyNodeSearchBox } from './components/ComfyNodeSearchBox'
import { QueueList } from './components/QueueList'
import { SettingDialog } from './components/SettingDialog'
import {
NodeLibrarySidebarTab,
@@ -27,18 +28,32 @@ dotenv.config()
type WorkspaceStore = ReturnType<typeof useWorkspaceStore>
class ComfyPropertiesPanel {
readonly root: Locator
readonly panelTitle: Locator
readonly searchBox: Locator
constructor(readonly page: Page) {
this.root = page.getByTestId('properties-panel')
this.panelTitle = this.root.locator('h3')
this.searchBox = this.root.getByPlaceholder('Search...')
}
}
class ComfyMenu {
private _nodeLibraryTab: NodeLibrarySidebarTab | null = null
private _workflowsTab: WorkflowsSidebarTab | null = null
private _topbar: Topbar | null = null
public readonly sideToolbar: Locator
public readonly propertiesPanel: ComfyPropertiesPanel
public readonly themeToggleButton: Locator
public readonly saveButton: Locator
constructor(public readonly page: Page) {
this.sideToolbar = page.locator('.side-tool-bar-container')
this.themeToggleButton = page.locator('.comfy-vue-theme-toggle')
this.propertiesPanel = new ComfyPropertiesPanel(page)
this.saveButton = page
.locator('button[title="Save the current workflow"]')
.nth(0)
@@ -137,6 +152,7 @@ export class ComfyPage {
// Components
public readonly searchBox: ComfyNodeSearchBox
public readonly queueList: QueueList
public readonly menu: ComfyMenu
public readonly actionbar: ComfyActionbar
public readonly templates: ComfyTemplates
@@ -169,6 +185,7 @@ export class ComfyPage {
this.visibleToasts = page.locator('.p-toast-message:visible')
this.searchBox = new ComfyNodeSearchBox(page)
this.queueList = new QueueList(page)
this.menu = new ComfyMenu(page)
this.actionbar = new ComfyActionbar(page)
this.templates = new ComfyTemplates(page)
@@ -310,19 +327,6 @@ export class ComfyPage {
}
await this.goto()
// Unify font for consistent screenshots.
await this.page.addStyleTag({
url: 'https://fonts.googleapis.com/css2?family=Roboto:ital,wght@0,100;0,300;0,400;0,500;0,700;0,900;1,100;1,300;1,400;1,500;1,700;1,900&display=swap'
})
await this.page.addStyleTag({
url: 'https://fonts.googleapis.com/css2?family=Noto+Color+Emoji&family=Roboto+Mono:ital,wght@0,100..700;1,100..700&family=Roboto:ital,wght@0,100;0,300;0,400;0,500;0,700;0,900;1,100;1,300;1,400;1,500;1,700;1,900&display=swap'
})
await this.page.addStyleTag({
content: `
* {
font-family: 'Roboto Mono', 'Noto Color Emoji';
}`
})
await this.page.waitForFunction(() => document.fonts.ready)
await this.page.waitForFunction(
() =>
@@ -1258,9 +1262,6 @@ export class ComfyPage {
}, 'image/png')
})
}, filename)
// Wait a bit for the download to process
await this.page.waitForTimeout(500)
}
/**

View File

@@ -60,9 +60,6 @@ export class ComfyNodeSearchBox {
await this.input.waitFor({ state: 'visible' })
await this.input.fill(nodeName)
await this.dropdown.waitFor({ state: 'visible' })
// Wait for some time for the auto complete list to update.
// The auto complete list is debounced and may take some time to update.
await this.page.waitForTimeout(500)
await this.dropdown
.locator('li')
.nth(options?.suggestionIndex || 0)

View File

@@ -0,0 +1,57 @@
import type { Page } from '@playwright/test'
import { expect } from '@playwright/test'
export class QueueList {
constructor(public readonly page: Page) {}
get toggleButton() {
return this.page.getByTestId('queue-toggle-button')
}
get inlineProgress() {
return this.page.getByTestId('queue-inline-progress')
}
get overlay() {
return this.page.getByTestId('queue-overlay')
}
get closeButton() {
return this.page.getByTestId('queue-overlay-close-button')
}
get jobItems() {
return this.page.getByTestId('queue-job-item')
}
get clearHistoryButton() {
return this.page.getByRole('button', { name: /Clear History/i })
}
async open() {
if (!(await this.overlay.isVisible())) {
await this.toggleButton.click()
await expect(this.overlay).toBeVisible()
}
}
async close() {
if (await this.overlay.isVisible()) {
await this.closeButton.click()
await expect(this.overlay).not.toBeVisible()
}
}
async getJobCount(state?: string) {
if (state) {
return await this.page
.locator(`[data-testid="queue-job-item"][data-job-state="${state}"]`)
.count()
}
return await this.jobItems.count()
}
getJobAction(actionKey: string) {
return this.page.getByTestId(`job-action-${actionKey}`)
}
}

View File

@@ -116,7 +116,6 @@ export class WorkflowsSidebarTab extends SidebarTab {
async switchToWorkflow(workflowName: string) {
const workflowLocator = this.getOpenedItem(workflowName)
await workflowLocator.click()
await this.page.waitForTimeout(300)
}
getOpenedItem(name: string) {
@@ -138,7 +137,6 @@ export class WorkflowsSidebarTab extends SidebarTab {
.click()
await this.page.keyboard.type(newName)
await this.page.keyboard.press('Enter')
await this.page.waitForTimeout(300)
}
async insertWorkflow(locator: Locator) {

View File

@@ -95,7 +95,6 @@ export class Topbar {
}
async openTopbarMenu() {
await this.page.waitForTimeout(1000)
await this.menuTrigger.click()
await this.menuLocator.waitFor({ state: 'visible' })
return this.menuLocator

View File

@@ -462,7 +462,6 @@ export class NodeReference {
async convertToSubgraph() {
await this.clickContextMenuOption('Convert to Subgraph')
await this.comfyPage.nextFrame()
await this.comfyPage.page.waitForTimeout(256)
const nodes = await this.comfyPage.getNodeRefsByTitle('New Subgraph')
if (nodes.length !== 1) {
throw new Error(
@@ -511,7 +510,6 @@ export class NodeReference {
// Double-click to enter subgraph
await this.comfyPage.canvas.dblclick({ position, force: true })
await this.comfyPage.nextFrame()
await this.comfyPage.page.waitForTimeout(500)
// Check if we successfully entered the subgraph
isInSubgraph = await this.comfyPage.page.evaluate(() => {

View File

@@ -1,7 +1,11 @@
import { test as base } from '@playwright/test'
import type { StatusWsMessage } from '../../src/schemas/apiSchema'
export type WsMessage = { type: 'status'; data: StatusWsMessage }
export const webSocketFixture = base.extend<{
ws: { trigger(data: any, url?: string): Promise<void> }
ws: { trigger(data: WsMessage, url?: string): Promise<void> }
}>({
ws: [
async ({ page }, use) => {

View File

@@ -5,14 +5,18 @@ import type { AutoQueueMode } from '../../src/stores/queueStore'
export class ComfyActionbar {
public readonly root: Locator
public readonly queueButton: ComfyQueueButton
public readonly propertiesButton: Locator
constructor(public readonly page: Page) {
this.root = page.locator('.actionbar')
this.root = page.locator('.actionbar-container')
this.queueButton = new ComfyQueueButton(this)
this.propertiesButton = this.root.getByLabel('Toggle properties panel')
}
async isDocked() {
const className = await this.root.getAttribute('class')
const className = await this.root
.locator('.actionbar')
.getAttribute('class')
return className?.includes('static') ?? false
}
}

View File

@@ -1,9 +1,9 @@
import type { Response } from '@playwright/test'
import { expect, mergeTests } from '@playwright/test'
import type { StatusWsMessage } from '../../src/schemas/apiSchema.ts'
import { comfyPageFixture } from '../fixtures/ComfyPage.ts'
import { webSocketFixture } from '../fixtures/ws.ts'
import { comfyPageFixture } from '../fixtures/ComfyPage'
import { webSocketFixture } from '../fixtures/ws'
import type { WsMessage } from '../fixtures/ws'
const test = mergeTests(comfyPageFixture, webSocketFixture)
@@ -61,7 +61,7 @@ test.describe('Actionbar', () => {
// Trigger a status websocket message
const triggerStatus = async (queueSize: number) => {
await ws.trigger({
const message = {
type: 'status',
data: {
status: {
@@ -70,7 +70,9 @@ test.describe('Actionbar', () => {
}
}
}
} as StatusWsMessage)
} satisfies WsMessage
await ws.trigger(message)
}
// Extract the width from the queue response

View File

@@ -75,9 +75,6 @@ test.describe('Background Image Upload', () => {
// Upload the test image
await fileChooser.setFiles(comfyPage.assetPath('image32x32.webp'))
// Wait for upload to complete and verify the setting was updated
await comfyPage.page.waitForTimeout(500) // Give time for file reading
// Verify the URL input now has an API URL
const urlInput = backgroundImageSetting.locator('input[type="text"]')
const inputValue = await urlInput.inputValue()
@@ -191,14 +188,11 @@ test.describe('Background Image Upload', () => {
)
await uploadButton.hover()
// Wait for tooltip to appear and verify it exists
await comfyPage.page.waitForTimeout(700) // Tooltip delay
const uploadTooltip = comfyPage.page.locator('.p-tooltip:visible')
await expect(uploadTooltip).toBeVisible()
// Move away to hide tooltip
await comfyPage.page.locator('body').hover()
await comfyPage.page.waitForTimeout(100)
// Set a background to enable clear button
const urlInput = backgroundImageSetting.locator('input[type="text"]')
@@ -209,8 +203,6 @@ test.describe('Background Image Upload', () => {
const clearButton = backgroundImageSetting.locator('button:has(.pi-trash)')
await clearButton.hover()
// Wait for tooltip to appear and verify it exists
await comfyPage.page.waitForTimeout(700) // Tooltip delay
const clearTooltip = comfyPage.page.locator('.p-tooltip:visible')
await expect(clearTooltip).toBeVisible()
})

View File

@@ -203,7 +203,6 @@ test.describe('Node Color Adjustments', () => {
comfyPage
}) => {
await comfyPage.setSetting('Comfy.Node.Opacity', 0.5)
await comfyPage.page.waitForTimeout(128)
// Drag mouse to force canvas to redraw
await comfyPage.page.mouse.move(0, 0)
@@ -211,7 +210,6 @@ test.describe('Node Color Adjustments', () => {
await expect(comfyPage.canvas).toHaveScreenshot('node-opacity-0.5.png')
await comfyPage.setSetting('Comfy.Node.Opacity', 1.0)
await comfyPage.page.waitForTimeout(128)
await comfyPage.page.mouse.move(8, 8)
await expect(comfyPage.canvas).toHaveScreenshot('node-opacity-1.png')
@@ -235,7 +233,6 @@ test.describe('Node Color Adjustments', () => {
await comfyPage.setSetting('Comfy.Node.Opacity', 0.5)
await comfyPage.setSetting('Comfy.ColorPalette', 'light')
const saveWorkflowInterval = 1000
await comfyPage.page.waitForTimeout(saveWorkflowInterval)
const workflow = await comfyPage.page.evaluate(() => {
return localStorage.getItem('workflow')
})

Binary file not shown.

Before

Width:  |  Height:  |  Size: 137 KiB

After

Width:  |  Height:  |  Size: 138 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 131 KiB

After

Width:  |  Height:  |  Size: 132 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 98 KiB

After

Width:  |  Height:  |  Size: 99 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 133 KiB

After

Width:  |  Height:  |  Size: 133 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 137 KiB

After

Width:  |  Height:  |  Size: 138 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 157 KiB

After

Width:  |  Height:  |  Size: 158 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 150 KiB

After

Width:  |  Height:  |  Size: 151 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 149 KiB

After

Width:  |  Height:  |  Size: 150 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 146 KiB

After

Width:  |  Height:  |  Size: 147 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 133 KiB

After

Width:  |  Height:  |  Size: 133 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 102 KiB

After

Width:  |  Height:  |  Size: 104 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 112 KiB

After

Width:  |  Height:  |  Size: 115 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 96 KiB

After

Width:  |  Height:  |  Size: 97 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 102 KiB

After

Width:  |  Height:  |  Size: 104 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 96 KiB

After

Width:  |  Height:  |  Size: 97 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 100 KiB

After

Width:  |  Height:  |  Size: 103 KiB

View File

@@ -43,7 +43,6 @@ test('Does not report warning on undo/redo', async ({ comfyPage }) => {
// Wait for any async operations to complete after dialog closes
await comfyPage.nextFrame()
await comfyPage.page.waitForTimeout(100)
// Make a change to the graph
await comfyPage.doubleClickCanvas()

Binary file not shown.

Before

Width:  |  Height:  |  Size: 82 KiB

After

Width:  |  Height:  |  Size: 86 KiB

View File

@@ -36,10 +36,6 @@ test.describe('Execute to selected output nodes', () => {
await output1.click('title')
await comfyPage.executeCommand('Comfy.QueueSelectedOutputNodes')
// @note: Wait for the execution to finish. We might want to move to a more
// reliable way to wait for the execution to finish. Workflow in this test
// is simple enough that this is fine for now.
await comfyPage.page.waitForTimeout(200)
expect(await (await input.getWidget(0)).getValue()).toBe('foo')
expect(await (await output1.getWidget(0)).getValue()).toBe('foo')

Binary file not shown.

Before

Width:  |  Height:  |  Size: 95 KiB

After

Width:  |  Height:  |  Size: 97 KiB

View File

@@ -94,9 +94,6 @@ test.describe('Feature Flags', () => {
test('Server feature flags are received and accessible', async ({
comfyPage
}) => {
// Wait for connection to establish
await comfyPage.page.waitForTimeout(1000)
// Get the actual server feature flags from the backend
const serverFlags = await comfyPage.page.evaluate(() => {
return window['app'].api.serverFeatureFlags
@@ -116,9 +113,6 @@ test.describe('Feature Flags', () => {
test('serverSupportsFeature method works with real backend flags', async ({
comfyPage
}) => {
// Wait for connection
await comfyPage.page.waitForTimeout(1000)
// Test serverSupportsFeature with real backend flags
const supportsPreviewMetadata = await comfyPage.page.evaluate(() => {
return window['app'].api.serverSupportsFeature(
@@ -170,9 +164,6 @@ test.describe('Feature Flags', () => {
test('getServerFeature method works with real backend data', async ({
comfyPage
}) => {
// Wait for connection
await comfyPage.page.waitForTimeout(1000)
// Test getServerFeature method
const previewMetadataValue = await comfyPage.page.evaluate(() => {
return window['app'].api.getServerFeature('supports_preview_metadata')
@@ -199,9 +190,6 @@ test.describe('Feature Flags', () => {
test('getServerFeatures returns all backend feature flags', async ({
comfyPage
}) => {
// Wait for connection
await comfyPage.page.waitForTimeout(1000)
// Test getServerFeatures returns all flags
const allFeatures = await comfyPage.page.evaluate(() => {
return window['app'].api.getServerFeatures()
@@ -248,9 +236,6 @@ test.describe('Feature Flags', () => {
test('Server features are immutable when accessed via getServerFeatures', async ({
comfyPage
}) => {
// Wait for connection to establish
await comfyPage.page.waitForTimeout(1000)
const immutabilityTest = await comfyPage.page.evaluate(() => {
// Get a copy of server features
const features1 = window['app'].api.getServerFeatures()

Binary file not shown.

Before

Width:  |  Height:  |  Size: 85 KiB

After

Width:  |  Height:  |  Size: 86 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 99 KiB

After

Width:  |  Height:  |  Size: 100 KiB

View File

@@ -104,8 +104,6 @@ test.describe('Group Node', () => {
await comfyPage.setSetting('Comfy.EnableTooltips', true)
await comfyPage.convertAllNodesToGroupNode('Group Node')
await comfyPage.page.mouse.move(47, 173)
const tooltipTimeout = 500
await comfyPage.page.waitForTimeout(tooltipTimeout + 16)
await expect(comfyPage.page.locator('.node-tooltip')).toBeVisible()
})
@@ -320,14 +318,12 @@ test.describe('Group Node', () => {
test('Convert to group node, no selection', async ({ comfyPage }) => {
expect(await comfyPage.getVisibleToastCount()).toBe(0)
await comfyPage.page.keyboard.press('Alt+g')
await comfyPage.page.waitForTimeout(300)
expect(await comfyPage.getVisibleToastCount()).toBe(1)
})
test('Convert to group node, selected 1 node', async ({ comfyPage }) => {
expect(await comfyPage.getVisibleToastCount()).toBe(0)
await comfyPage.clickTextEncodeNode1()
await comfyPage.page.keyboard.press('Alt+g')
await comfyPage.page.waitForTimeout(300)
expect(await comfyPage.getVisibleToastCount()).toBe(1)
})
})

View File

@@ -307,8 +307,6 @@ test.describe('Node Interaction', () => {
position: numberWidgetPos
})
await expect(comfyPage.canvas).toHaveScreenshot('prompt-dialog-opened.png')
// Wait for 1s so that it does not trigger the search box by double click.
await comfyPage.page.waitForTimeout(1000)
await comfyPage.canvas.click({
position: {
x: 10,
@@ -332,7 +330,6 @@ test.describe('Node Interaction', () => {
await expect(comfyPage.canvas).toHaveScreenshot(
'prompt-dialog-opened-text.png'
)
await comfyPage.page.waitForTimeout(1000)
await comfyPage.canvas.click({
position: {
x: 10,
@@ -663,9 +660,6 @@ test.describe('Load workflow', () => {
await comfyPage.loadWorkflow('nodes/single_ksampler')
const node = (await comfyPage.getFirstNodeRef())!
await node.click('collapse')
// Wait 300ms between 2 clicks so that it is not treated as a double click
// by litegraph.
await comfyPage.page.waitForTimeout(300)
await comfyPage.clickEmptySpace()
await expect(comfyPage.canvas).toHaveScreenshot(
'single_ksampler_modified.png'

Binary file not shown.

Before

Width:  |  Height:  |  Size: 96 KiB

After

Width:  |  Height:  |  Size: 97 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 90 KiB

After

Width:  |  Height:  |  Size: 91 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 75 KiB

After

Width:  |  Height:  |  Size: 75 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 75 KiB

After

Width:  |  Height:  |  Size: 75 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 99 KiB

After

Width:  |  Height:  |  Size: 98 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 96 KiB

After

Width:  |  Height:  |  Size: 97 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 92 KiB

After

Width:  |  Height:  |  Size: 93 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 99 KiB

After

Width:  |  Height:  |  Size: 99 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 95 KiB

After

Width:  |  Height:  |  Size: 96 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 91 KiB

After

Width:  |  Height:  |  Size: 92 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 63 KiB

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 106 KiB

After

Width:  |  Height:  |  Size: 107 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 61 KiB

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 88 KiB

After

Width:  |  Height:  |  Size: 89 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 97 KiB

After

Width:  |  Height:  |  Size: 98 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 88 KiB

After

Width:  |  Height:  |  Size: 89 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 88 KiB

After

Width:  |  Height:  |  Size: 89 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 99 KiB

After

Width:  |  Height:  |  Size: 101 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 96 KiB

After

Width:  |  Height:  |  Size: 97 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 94 KiB

After

Width:  |  Height:  |  Size: 95 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 57 KiB

After

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 111 KiB

After

Width:  |  Height:  |  Size: 113 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 102 KiB

After

Width:  |  Height:  |  Size: 103 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 101 KiB

After

Width:  |  Height:  |  Size: 102 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 101 KiB

After

Width:  |  Height:  |  Size: 102 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 41 KiB

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 96 KiB

After

Width:  |  Height:  |  Size: 97 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 54 KiB

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 71 KiB

After

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 54 KiB

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 41 KiB

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.1 KiB

After

Width:  |  Height:  |  Size: 9.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 88 KiB

After

Width:  |  Height:  |  Size: 89 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 79 KiB

After

Width:  |  Height:  |  Size: 80 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 96 KiB

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 41 KiB

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 99 KiB

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 76 KiB

After

Width:  |  Height:  |  Size: 77 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 100 KiB

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 97 KiB

After

Width:  |  Height:  |  Size: 98 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 101 KiB

After

Width:  |  Height:  |  Size: 101 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 97 KiB

After

Width:  |  Height:  |  Size: 98 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 56 KiB

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 56 KiB

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 67 KiB

After

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 74 KiB

After

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 95 KiB

After

Width:  |  Height:  |  Size: 96 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 97 KiB

After

Width:  |  Height:  |  Size: 98 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 99 KiB

After

Width:  |  Height:  |  Size: 101 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 96 KiB

After

Width:  |  Height:  |  Size: 97 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 97 KiB

After

Width:  |  Height:  |  Size: 98 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 101 KiB

After

Width:  |  Height:  |  Size: 102 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 88 KiB

After

Width:  |  Height:  |  Size: 89 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 97 KiB

After

Width:  |  Height:  |  Size: 98 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 96 KiB

After

Width:  |  Height:  |  Size: 97 KiB

Some files were not shown because too many files have changed in this diff Show More