Compare commits

...

140 Commits

Author SHA1 Message Date
Johnpaul Chiwetelu
3230e600e6 fix: trigger widget callback when model is selected
Fixes model dropdown not properly updating when a model is selected from the model library or dropped onto canvas by calling the widget callback and forcing reactivity.
2025-12-10 02:56:48 +01:00
Benjamin Lu
2903560416 Move cancel button into actionbar (#7297)
## Summary
Move the interrupt control into the actionbar so cancellation sits with
the run controls.

## Changes
- add a cancel button to the actionbar with the existing interrupt
tooltip and disabled state
- remove the cancel button and related execution wiring from the top
menu section to avoid duplication

## Review Focus
- spacing/hover states of the new cancel control in both docked and
floating modes

## Screenshots (if applicable)
- n/a

Tests: pnpm typecheck; pnpm lint:fix

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-7297-Move-cancel-button-into-actionbar-2c46d73d36508198b00cf011390289f6)
by [Unito](https://www.unito.io)
2025-12-09 14:32:03 -08:00
Luke Mino-Altherr
6850c45d63 [feat] Add ownership filter to model browser (#7201)
## Summary
Adds a dropdown filter to the model browser that allows users to filter
assets by ownership (All, My models, Public models), based on the
`is_immutable` property.

## Changes
- **Filter UI**: Added ownership dropdown in
[AssetFilterBar.vue](src/platform/assets/components/AssetFilterBar.vue#L30-L38)
that only appears when user has uploaded models
- **Filter Logic**: Implemented `filterByOwnership` function in
[useAssetBrowser.ts](src/platform/assets/composables/useAssetBrowser.ts#L38-L45)
to filter by `is_immutable` property
- **i18n**: Added translation strings for ownership filter options
- **Tests**: Added comprehensive tests for ownership filtering in both
composable and component test files

## Review Focus
- The ownership filter visibility logic correctly checks for mutable
assets (`!is_immutable`)
- Default filter value is 'all' to show all models initially
- Filter integrates cleanly with existing file format and base model
filters

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

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-7201-feat-Add-ownership-filter-to-model-browser-2c16d73d365081f280f6d1e42e5400af)
by [Unito](https://www.unito.io)

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: GitHub Action <action@github.com>
2025-12-09 13:52:33 -08:00
Alexander Brown
2b9f7ecedf 🤖 Testing section update (#7295)
## Summary

Standing on the shoulders of giants.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-7295-Testing-section-update-2c46d73d3650818f935bcb2ac65d9830)
by [Unito](https://www.unito.io)
2025-12-09 13:21:21 -07:00
Terry Jia
73b08acfe0 fix: Note/MarkdownNote node color change not reactive in vueNodes mode (#7294)
## Summary

Move color/bgcolor initialization from class field overrides to
constructor to preserve LGraphNodeProperties getter/setter
instrumentation.

Class field overrides were replacing the reactive property descriptors
set by the parent constructor, preventing change events from firing.

issue found while tesing in
https://github.com/Comfy-Org/ComfyUI_frontend/issues/3449

## Screenshots
Before


https://github.com/user-attachments/assets/04499a3a-15c2-44fd-9819-6dd5f6849f20


After


https://github.com/user-attachments/assets/ba93278b-9761-4d45-abb3-2a57ff95a900

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-7294-fix-Note-MarkdownNote-node-color-change-not-reactive-in-vueNodes-mode-2c46d73d3650818f8ee6f6f0c0e61d39)
by [Unito](https://www.unito.io)
2025-12-09 09:17:07 -08:00
Christian Byrne
c524ce3a2f make jojodecayz and bigcat88 owners of partner node pricing (#7284)
## Summary

Allows partner node team to quickly approve/merge each other's PRs.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-7284-make-jojodecayz-and-bigcat88-owners-of-partner-node-pricing-2c46d73d365081b08295d3204f8ca13c)
by [Unito](https://www.unito.io)
2025-12-09 07:55:57 -05:00
Christian Byrne
aef40834f3 add shared comfy credit conversion helpers (#7061)
Introduces cents<->usd<->credit converters plus basic formatters and
adds test. Lays groundwork to start converting UI components into
displaying comfy credits.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-7061-add-shared-comfy-credit-conversion-helpers-2bb6d73d3650810bb34fdf9bb3fc115b)
by [Unito](https://www.unito.io)

---------

Co-authored-by: Claude <noreply@anthropic.com>
2025-12-09 05:11:27 -07:00
Christian Byrne
8209f5a108 feat: add Stripe pricing table integration for subscription dialog (conditional on feature flag) (#7288)
Integrates Stripe's pricing table web component into the subscription
dialog when the subscription_tiers_enabled feature flag is active. The
implementation includes a new StripePricingTable component that loads
Stripe's pricing table script and renders the table with proper error
handling and loading states. The subscription dialog now displays the
Stripe pricing table with contact us and enterprise links, using a
1100px width that balances multi-column layout with visual design.
Configuration supports environment variables, remote config, and window
config for the Stripe publishable key and pricing table ID.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-7288-feat-add-Stripe-pricing-table-integration-for-subscription-dialog-conditional-on-featur-2c46d73d365081fa9d93c213df118996)
by [Unito](https://www.unito.io)
2025-12-09 04:45:45 -07:00
Christian Byrne
77e453db36 [fix] Fall back to current minor when next minor branch doesn't exist (#7286)
## Summary
- When the next minor branch (e.g., `core/1.35`) doesn't exist yet, fall
back to current minor (`core/1.34`) for patch releases instead of
failing
- Fixes the weekly release workflow failure when ComfyUI is on version
1.33.x and `core/1.34` doesn't exist yet

## Test plan
- [x] Tested locally with version where next minor exists (1.33.5 →
finds core/1.34)
- [x] Tested fallback when next minor doesn't exist (1.34.7 → falls back
to core/1.34)
- [x] Tested error case when neither branch exists

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-7286-fix-Fall-back-to-current-minor-when-next-minor-branch-doesn-t-exist-2c46d73d365081009762c732954e148d)
by [Unito](https://www.unito.io)
2025-12-09 02:45:56 -07:00
Christian Byrne
d3e9e15f07 change credits icons and tooltips (conditional on feature flag) (#7276)
This PR changes the credits icons and tooltips based on state of the
`subscription_tiers_enabled` feature flag.

When the flag is enabled (or undefined -- for local), the dollar icon is
replaced with the lucide-component icon in UserCredit and node price
badges (Partner Nodes), and a new tooltip row appears in
CurrentUserPopover displaying "Credits have been unified" with a
detailed hover tooltip explaining the credit unification across Partner
Nodes and Cloud workflows.

<img width="539" height="535" alt="image"
src="https://github.com/user-attachments/assets/7e952f9b-0abb-4979-85b7-0eecdeaf808c"
/>

Related:

- https://github.com/Comfy-Org/ComfyUI_frontend/pull/6115 (borrows badge
implementation from this PR)

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-7276-change-credits-icons-and-tooltips-conditional-on-feature-flag-2c46d73d365081809a6afd5861018a15)
by [Unito](https://www.unito.io)
2025-12-09 02:41:32 -07: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
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
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
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
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
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
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
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
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
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
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
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
Comfy Org PR Bot
2f87acf9aa 1.34.5 (#7122)
Patch version increment to 1.34.5

**Base branch:** `main`

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-7122-1-34-5-2be6d73d365081968612e8c03a0658da)
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-02 20:29:26 -07:00
Alexander Brown
497bafcaeb Fix: Widget sizing with multiple expanding items (#7118)
## Summary

Textarea/Markdown/3D pieces grow, other widget rows size to their
contents.

## Screenshots (if applicable)
<img width="564" height="420" alt="image"
src="https://github.com/user-attachments/assets/35aeb9bf-a44d-4e3f-b2cd-a9f3604a7778"
/>

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-7118-Fix-Widget-sizing-with-multiple-expanding-items-2be6d73d3650816c8153ff3f1e49176d)
by [Unito](https://www.unito.io)

---------

Co-authored-by: Simula_r <18093452+simula-r@users.noreply.github.com>
Co-authored-by: github-actions <github-actions@github.com>
2025-12-02 18:49:45 -08:00
Johnpaul Chiwetelu
c6988380c2 [feat] Allow opening workflows with same name as new tabs (#7104)
When importing a workflow file that has the same name as an existing
open workflow, create a new tab with a unique suffix (2), (3), etc.
instead of silently failing or replacing the existing workflow.

- Add forceNew parameter to createTemporary() in workflowStore
- Always create new temporary workflow when importing via file picker
- Remove logic that looked up persisted workflows by name during import




https://github.com/user-attachments/assets/9c9aebd3-37c2-464f-9fb4-9ef871ec2673





┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-7104-feat-Allow-opening-workflows-with-same-name-as-new-tabs-2bd6d73d365081cd9182eaa35bf37c29)
by [Unito](https://www.unito.io)
2025-12-03 02:47:26 +00:00
Johnpaul Chiwetelu
d50a2fabc0 Fix skeleton loaders for Image/Video Previews (#7094)
This pull request refines the loading and error handling logic for both
the `VideoPreview.vue` and `ImagePreview.vue` components. The main
improvements include making the loading skeleton more accurate and
visually consistent, updating how loading and error states are managed
when URLs change, and ensuring that the main media elements are hidden
while loading. These changes enhance the user experience by providing
clearer feedback during media load operations.

**Loading and error state improvements:**

* The loading skeleton in both `VideoPreview.vue` and `ImagePreview.vue`
now only appears when loading and no error is present, with updated
styling and fixed dimensions for better consistency. (`VideoPreview.vue`
[[1]](diffhunk://#diff-17b5c19b4628f22e45570b66a85ed1fc16e931dd368fe420584d487e522ab8aaL29-R41)
`ImagePreview.vue`
[[2]](diffhunk://#diff-0c0b17c5c68464e0284398ad42b823509d414c9cf297f3bc2aa2b00e0f9c2015L29-R48)
* The main video and image elements are now hidden (using the
`invisible` class) while loading, preventing display glitches before the
media is ready. (`VideoPreview.vue`
[[1]](diffhunk://#diff-17b5c19b4628f22e45570b66a85ed1fc16e931dd368fe420584d487e522ab8aaL29-R41)
`ImagePreview.vue`
[[2]](diffhunk://#diff-0c0b17c5c68464e0284398ad42b823509d414c9cf297f3bc2aa2b00e0f9c2015L29-R48)
* The loading state (`isLoading`) is now set to `true` whenever new URLs
are provided, and reset appropriately when navigating between media
items, ensuring accurate feedback to the user. (`VideoPreview.vue`
[[1]](diffhunk://#diff-17b5c19b4628f22e45570b66a85ed1fc16e931dd368fe420584d487e522ab8aaL145-R152)
`ImagePreview.vue`
[[2]](diffhunk://#diff-0c0b17c5c68464e0284398ad42b823509d414c9cf297f3bc2aa2b00e0f9c2015L164-R176)
[[3]](diffhunk://#diff-0c0b17c5c68464e0284398ad42b823509d414c9cf297f3bc2aa2b00e0f9c2015L224-R236)

**Code consistency and maintainability:**

* Both components now import and use the shared `cn` utility for
conditional class names, improving code consistency and maintainability.
(`VideoPreview.vue`
[[1]](diffhunk://#diff-17b5c19b4628f22e45570b66a85ed1fc16e931dd368fe420584d487e522ab8aaR115)
`ImagePreview.vue`
[[2]](diffhunk://#diff-0c0b17c5c68464e0284398ad42b823509d414c9cf297f3bc2aa2b00e0f9c2015R132)

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-7094-Fix-skeleton-loaders-for-Image-Video-Previews-2bd6d73d3650817989e1f4d597094016)
by [Unito](https://www.unito.io)
2025-12-03 03:39:33 +01:00
Simula_r
9c5f8a619c feat: add workflowRendererVersion workflow schema (#7086)
## Summary

add workflowRendererVersion to workflow schema extra

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-7086-feat-add-workflowRendererVersion-workflow-schema-2bd6d73d3650818dbcecf2b85b0553a2)
by [Unito](https://www.unito.io)
2025-12-02 19:28:48 -07:00
Comfy Org PR Bot
c7eac496c1 1.34.4 (#7092)
Patch version increment to 1.34.4

**Base branch:** `main`

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-7092-1-34-4-2bd6d73d3650812496f4cef1231808c1)
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-02 19:26:22 -07:00
Christian Byrne
1e066ee6c8 fix: subpath routing for reverse proxy, embedded frontends, nginx/apache subpath hosting, etc. (like SwarmUI) (#7115)
Fixes #6995 -- restores subpath routing for subpath deployments (e.g.,
SwarmUI, nginx/apache subpaths, etc.). Fix only applies to non-cloud,
non-Electron deployments

As pointed out
[here](693fbbd3e4 (r171491977)),
[693fbbd](693fbbd3e4)
changed router base path logic from `window.location.pathname` to
`import.meta.env.BASE_URL`. Combined with
[6c9743c1a](6c9743c1a6)
setting `base: ''` for non-cloud builds, the router defaults to `/` and
ignores actual subpaths.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-7115-fix-subpath-routing-for-reverse-proxy-embedded-frontends-nginx-apache-subpath-hosting--2be6d73d365081188ec8d360c1cd88f7)
by [Unito](https://www.unito.io)
2025-12-02 17:55:46 -08:00
Simula_r
5c330fdd25 fix: layout scale to handle downscale correctly (#7109)
## Summary

Fixes drift when switching between modes introduced after
https://github.com/Comfy-Org/ComfyUI_frontend/pull/6966

## Changes

- **What**: ensureCorrectLayoutScale

## Screenshots (if applicable)


https://github.com/user-attachments/assets/e40803f1-8ae5-4890-bc1a-3f2413a35c7d


https://www.notion.so/Bug-Spamming-Nodes-2-0-button-causes-nodes-to-grow-longer-2bd6d73d3650818492a4e0cc53da7017

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-7109-fix-layout-scale-to-handle-downscale-correctly-2be6d73d365081c29fe2c481feafebc1)
by [Unito](https://www.unito.io)
2025-12-02 17:52:31 -08:00
Christian Byrne
2b7b100e2e cloud: increase feature flag polling interval to 10min (from 30s) (#7100)
Increases the `/features` endpoint polling interval (runtime config
polling) from the current 30 seconds to 10 minutes.

## Background

The polling was originally added for two main purposes:

1. Server alert badges
2. Extra assurance that states are synchronized between frontend and
backend

However, both of these use cases are not critical:

- Server alert badges are unlikely to be needed in practice. WebSocket
(WS) can be used eventually as an alternative
- For synchronization, the system should be redesigned to be explicit
about marking client state as stale, or use WebSocket/Server-Sent Events
(WS/SSE) on a per-data basis rather than polling for the entire feature
flag set

## Motivation

The reason to reduce polling frequency is that per-user feature flags
are being added, which will make `/features` endpoint handling
significantly heavier. The endpoint will no longer just return static
JSON with high cache age, making frequent polling more costly.

## Future Considerations

- Eventually migrate server alert badges to use WebSocket
- Design a system that explicitly marks client state as stale when
needed
- Consider using WebSocket or Server-Sent Events for targeted data
updates instead of polling entire feature flag set

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-7100-cloud-increase-feature-flag-polling-interval-to-10min-from-30s-2bd6d73d365081dfa8abeec901d0d975)
by [Unito](https://www.unito.io)

---------

Co-authored-by: Alexander Brown <drjkl@comfy.org>
2025-12-02 17:12:45 -07:00
Terry Jia
379af28678 fix: handle Unicode characters in clipboard copy/paste and add Paste menu option (#7103)
## Summary
Use TextEncoder/TextDecoder for UTF-8 safe base64 encoding/decoding

When copying nodes containing non-Latin1 characters (e.g., Chinese
characters in localized_name field), btoa() throws an error:

InvalidCharacterError: Failed to execute 'btoa' on 'Window': The string
to be encoded contains characters outside of the Latin1 range.

The copy operation saved data to localStorage successfully, but failed
to write to the browser clipboard. On paste, the browser clipboard still
contained old data, causing the wrong node to be pasted.

<!-- Fixes #ISSUE_NUMBER -->
https://github.com/Comfy-Org/ComfyUI_frontend/issues/6993
https://github.com/Comfy-Org/ComfyUI_frontend/issues/5449
https://github.com/comfyanonymous/ComfyUI/issues/8481

## Screenshots (if applicable)
before

https://github.com/user-attachments/assets/8abd9049-91bb-4200-8853-e26753376007

after

https://github.com/user-attachments/assets/7d969f32-bb0f-4c7a-baa2-65d576a4eba2

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-7103-fix-handle-Unicode-characters-in-clipboard-copy-paste-and-add-Paste-menu-option-2bd6d73d365081f39c40e7e7f832b97c)
by [Unito](https://www.unito.io)

---------

Co-authored-by: github-actions <github-actions@github.com>
2025-12-02 15:31:27 -08:00
Alexander Brown
c263111eeb Feat: Add preview as plaintext toggle for Preview As Text (#7102)
## Summary

Adds a toggle to conditionally render the text as Markdown.

## Also...

Fixes some type issues across our myriad Widget types. We should
probably clean those up.

## Example


https://github.com/user-attachments/assets/24fed943-1e79-4ea4-a962-826b06d68761



This could be a good minimal testcase for dynamic widgets @AustinMroz

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-7102-WIP-Feat-Add-preview-as-plaintext-toggle-for-Preview-As-Text-2bd6d73d3650810c8b25c84866c8875c)
by [Unito](https://www.unito.io)
2025-12-02 14:37:57 -08:00
Christian Byrne
e887d69cdc [fix] Fix tsx command not found in weekly release workflow (#7099)
Fixes the "tsx: command not found" error from the manual dispatch test
run.

## Issue

The workflow failed with:


## Fix

Changed to since tsx is a devDependency and needs to be run through
pnpm.

## Related

- Failed run:
https://github.com/Comfy-Org/ComfyUI_frontend/actions/runs/19833631926/job/56825533678

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-7099-fix-Fix-tsx-command-not-found-in-weekly-release-workflow-2bd6d73d3650812fb401fb8010154bf6)
by [Unito](https://www.unito.io)
2025-12-02 11:25:09 -08:00
Alexander Brown
7ff8bcfea3 Feat: Add is_immutable gate for model edit button. (#7091)
## Summary

Only show the buttons for models that can be _mutated_.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-7091-Feat-Add-is_immutable-gate-for-model-edit-button-2bd6d73d3650812e98c4e5cfb8726242)
by [Unito](https://www.unito.io)
2025-12-02 10:51:54 -08:00
Terry Jia
573cda853b fix: should only trigger asset panel when it opening (#7098)
## Summary

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

## Screenshots

<!-- Add screenshots or video recording to help explain your changes -->
before
<img width="2085" height="452" alt="image"
src="https://github.com/user-attachments/assets/437f85f0-103f-422a-ba07-2e4e43f763d4"
/>

after
<img width="1208" height="265" alt="image"
src="https://github.com/user-attachments/assets/bcb246ee-d963-4c74-9e0b-8d02266cc6ac"
/>

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-7098-fix-should-only-trigger-asset-panel-when-it-opening-2bd6d73d36508118a8a6e7db994da775)
by [Unito](https://www.unito.io)
2025-12-02 10:44:53 -08:00
AustinMroz
49824824e6 Add support for growable inputs (#6830)
![autogrow-optional_00002](https://github.com/user-attachments/assets/79bfe703-23d7-45fb-86ce-88baa9eaf582)

Also fixes connections to widget inputs created by a dynamic combo
breaking on reload.

Performs some refactoring to group the prior dynamic inputs code.

See also, the overarching frontend PR: comfyanonymous/ComfyUI#10832

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6830-Add-support-for-growable-inputs-2b36d73d365081c484ebc251a10aa6dd)
by [Unito](https://www.unito.io)
2025-12-01 21:05:25 -08:00
sno
8e006bb8a3 [bugfix] Add vite-define shim for Playwright i18n collection (#6906)
## Summary

Fixes the `ReferenceError: __DISTRIBUTION__ is not defined` error when
running i18n collection tests.

## Problem

PR #6879 added conditional menu commands based on distribution (hiding
memory unload commands in cloud). This introduced a dependency on
`isCloud` which uses the `__DISTRIBUTION__` Vite define variable in
`coreMenuCommands.ts`.

When Playwright's test runner imports this file during i18n collection,
it fails because Vite define variables are only replaced during Vite's
build/dev process, not during Playwright's TypeScript compilation.

## Solution

Created a simple shim (`scripts/vite-define-shim.ts`) that:
1. Defines all Vite define variables as global constants with default
values
2. Provides a minimal `window` shim for Node environment
3. Is imported at the top of `collect-i18n-general.ts` before any code
that uses these variables

This approach is simpler than:
- Creating a custom Babel plugin (attempted in this PR, see commit
history)
- Using `ctViteConfig` (only works for component testing, not regular
Playwright tests)
- Post-build regex replacement (fragile and error-prone)

## Test Plan

Run `pnpm collect-i18n` and verify:
-  No more `ReferenceError: __DISTRIBUTION__ is not defined`
-  No more `ReferenceError: window is not defined`
- ⏱️ Tests may timeout if dev server is not running on port 5173, but
that's a separate issue

## Related

- Fixes issue introduced by PR #6879
- Related to Notion task:
https://www.notion.so/comfy-org/Implement-Babel-plugin-for-Vite-define-replacements-in-Playwright-2b56d73d365081d5bb63e02712912b17

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

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6906-bugfix-Add-vite-define-shim-for-Playwright-i18n-collection-2b66d73d36508182b4d6d69b88ae2771)
by [Unito](https://www.unito.io)

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: github-actions <github-actions@github.com>
Co-authored-by: Alexander Brown <drjkl@comfy.org>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
2025-12-02 03:43:20 +00:00
Alexander Brown
a5f1eb0b92 Feat: Add model import button to Vue model list popover. (#7085)
## Summary

Adds a button to trigger the model upload on the Asset dropdown.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-7085-Feat-Add-model-import-button-to-Vue-model-list-popover-2bd6d73d365081958c8ef23de55a341e)
by [Unito](https://www.unito.io)
2025-12-01 19:19:23 -08:00
Terry Jia
1b30880e6c fix: normalize path separators in comfyAPIPlugin for Windows compatibility (#7087)
## Summary
Normalize the path separators by replacing all backslashes with forward
slashes before using relativePath
Root Cause

In build/plugins/comfyAPIPlugin.ts, the path.relative() function
generates relative paths that contain backslashes on Windows (e.g.,
scripts\ui.ts). When this path is directly embedded into a JavaScript
string literal, the \u sequence is interpreted as the start of a Unicode
escape sequence, causing the SyntaxError: Invalid Unicode escape
sequence error

## Screenshots
previous error
<img width="720" height="208" alt="image"
src="https://github.com/user-attachments/assets/45b9a6e1-f35e-473d-af29-9e181bbe24eb"
/>

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-7087-fix-normalize-path-separators-in-comfyAPIPlugin-for-Windows-compatibility-2bd6d73d365081cb88c1c6a0f168a5b5)
by [Unito](https://www.unito.io)
2025-12-01 20:15:44 -07:00
Alexander Brown
6d22562d40 Polish: Make the clickable area for the slots more forgiving (#7084)
## Summary

Makes the area a bit to the left and right of the dot also clickable.

Addresses complaints about it being tricky to connect nodes in Nodes
2.0.

---------

Co-authored-by: github-actions <github-actions@github.com>
2025-12-02 01:23:25 +00:00
Alexander Brown
04158deb02 Feat: Rename and Delete for imported Models ☁️ (#6969)
## Summary

Add Rename and Delete options for Personal Models.

Also updates and standardizes some styles for Cards and adds a simple
Confirmation dialog.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6969-WIP-Feat-Rename-and-Delete-for-custom-Models-2b86d73d36508140a687e929b0544ae6)
by [Unito](https://www.unito.io)

---------

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
2025-12-01 17:16:05 -08:00
Alexander Brown
072f1f6ced Fix: Slots without type colors (#7081)
## Summary

Default to a gray

## Screenshots (if applicable)
<img width="416" height="205" alt="image"
src="https://github.com/user-attachments/assets/0eeee37d-f9c1-4893-ae1d-ee20cabf0f46"
/>

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-7081-Fix-Slots-without-type-colors-2bc6d73d365081499e54e8a10b4b4b02)
by [Unito](https://www.unito.io)

---------

Co-authored-by: github-actions <github-actions@github.com>
2025-12-01 15:05:59 -08:00
AustinMroz
202dc3bbb2 Support on slot click listeners in vue (#7059)
Adds support for the `node.onInputDblClick` and `node.onInputClick`
callbacks in vue
- Fixes vue link drop code so that a link which is not dropped on the
canvas does not fallback to the dropped on canvas code.

![input-double-click_00001](https://github.com/user-attachments/assets/8b138a6e-e569-4a5d-b59a-cd688ff062e6)

Resolves #7037

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-7059-Support-on-slot-click-listeners-in-vue-2bb6d73d3650812993fdef244e91683b)
by [Unito](https://www.unito.io)
2025-12-01 14:34:43 -08:00
Alexander Piskun
2c437acff6 feat(api-nodes-pricing): add prices for Kling O1 video model (#7077)
## Summary

Add price badges for the upcoming API nodes.

## Screenshots (if applicable)

<img width="1242" height="1094" alt="Screenshot From 2025-12-01
14-03-22"
src="https://github.com/user-attachments/assets/8f7909ea-f629-4dde-a075-ca31a9fff2ac"
/>

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-7077-feat-api-nodes-pricing-add-prices-for-Kling-O1-video-model-2bc6d73d36508152843ff96196317ff8)
by [Unito](https://www.unito.io)
2025-12-01 11:02:34 -08:00
Comfy Org PR Bot
b97b21add0 1.34.3 (#7074)
Patch version increment to 1.34.3

**Base branch:** `main`

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

Co-authored-by: christian-byrne <72887196+christian-byrne@users.noreply.github.com>
2025-11-30 23:48:38 -07:00
Christian Byrne
dfe098897a [fix] Prevent drag activation during Vue node resize (#7064)
## Summary
- Add isResizingVueNodes flag to layoutStore
- Set flag in useNodeResize during resize operation
- Check flag in useNodePointerInteractions to skip drag activation

Fixes a bug where resizing a Vue node after selecting it would
incorrectly trigger drag logic, causing the node to teleport.


┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-7064-fix-Prevent-drag-activation-during-Vue-node-resize-2bb6d73d365081a2ad0ad4c7be76afee)
by [Unito](https://www.unito.io)

---------

Co-authored-by: DrJKL <DrJKL0424@gmail.com>
2025-11-30 20:23:30 -08:00
Christian Byrne
d76c59cb14 [refactor] Simplify Vue node resize to bottom-right corner only (#7063)
## Summary

Temporarily simplifies the resize logic to only work on bottom right
corner and eliminates edge cases where corner resizing caused position
drift issues.

- Remove multi-corner resize handles in favor of bottom-right only
- Delete resizeMath.ts and its tests (no longer needed)
- Simplify useNodeResize to only handle bottom-right resize
- Remove position tracking from resize callback

---------

Co-authored-by: github-actions <github-actions@github.com>
2025-11-30 18:27:37 -08:00
Christian Byrne
c298d8a870 [fix] Weekly release workflow adjustments (#7060)
Follow-up adjustments to the weekly ComfyUI release automation workflow.

## Changes

1. **Rename workflow to follow conventions**
   - File: `weekly-comfyui-release.yaml` → `release-weekly-comfyui.yaml`
   - Name: "Weekly ComfyUI Release" → "Release: Weekly ComfyUI"
   - Matches pattern of other `release-*` workflows

2. **Sync fork with upstream before creating PR**
   - Fetches latest upstream/master before making changes
   - Ensures PR only shows requirements.txt diff, not stale fork commits
- Does not modify fork's master branch (only pushes automation branch)

## Testing

After merge, can test via manual workflow dispatch in Actions tab.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-7060-fix-Weekly-release-workflow-adjustments-2bb6d73d365081008436d1b9e5f7dd65)
by [Unito](https://www.unito.io)
2025-11-30 18:27:04 -08:00
niknah
b50b34ac95 Expose LGraphNode.getSlotPosition (#7042)
Before node v2, you could use getInputPos, getOutputPos. In node v2,
that is not possible.

## Summary

Want to get the position of the output / inputs on the graph like before
node v2.

## Changes

- **What**: <!-- Core functionality added/modified --> Added
LGraphNode.getSlotPosition

## Review Focus


## Screenshots (if applicable)

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-7042-Expose-LGraphNode-getSlotPosition-2ba6d73d36508195a0fed2d1fe2b64f6)
by [Unito](https://www.unito.io)
2025-11-30 12:55:16 -07:00
Christian Byrne
7a6cc39c39 [feat] Add weekly ComfyUI release automation (#6877)
Adds scheduled workflow to bump ComfyUI frontend RC releases every
Monday at noon PST.

## Implementation

- **Resolver script** (`scripts/cicd/resolve-comfyui-release.ts`):
Checks ComfyUI `requirements.txt` and determines next minor version,
compares branch commits to latest patch tag
- **Workflow** (`.github/workflows/weekly-comfyui-release.yaml`): 
  - Scheduled for Monday 20:00 UTC (noon PST)
  - Manual dispatch supported for testing/off-cycle runs
- Three jobs: resolve version → trigger release if needed → create
ComfyUI PR
- Reuses existing `release-version-bump.yaml` workflow
- Creates draft PR from fork to `comfyanonymous/ComfyUI` with updated
`requirements.txt`
- Includes error handling and validation for all steps
- Force pushes to same branch weekly to maintain single open PR

## Testing

- Resolver script tested locally: correctly identified v1.28.8 → v1.29.4
with release needed
- yamllint passes
- knip passes with `gh` binary added to ignore list

## Follow-up

Can test via workflow_dispatch once merged, or in this PR if we enable
Actions on branch.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6877-feat-Add-weekly-ComfyUI-release-automation-2b46d73d36508154aa05c783c6942d9a)
by [Unito](https://www.unito.io)
2025-11-30 12:45:47 -07:00
Christian Byrne
2b751200be test: add setting to ignore version compatibility toast warnings in e2e tests (#7004)
There's a warning toast shown if the frontend is considered out-of-date
(relative to the version in the requirements.txt of
comfyanonymous/ComfyUI). As a result, e2e tests run on older release
branches (e.g., when backporting or hotfixing) can sometimes trigger the
warning which obviously causes visual regression tests to fail. This PR
adds a hidden setting to disable the warning and sets it to `true` in
the e2e test fixtures.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-7004-test-add-setting-to-ignore-version-compatibility-toast-warnings-in-e2e-tests-2b86d73d3650812d9e07f54a0c86b996)
by [Unito](https://www.unito.io)
2025-11-29 19:56:19 -07:00
Alexander Brown
5b03d3fcbc Minor/Lint: Fix two warnings (#7045)
## Summary

Unnecessary import of a compiler macro and importing the default instead
of the named export.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-7045-Minor-Lint-Fix-two-warnings-2bb6d73d365081758369d14bafcd7aa0)
by [Unito](https://www.unito.io)
2025-11-29 19:15:06 -07:00
Christian Byrne
1caf3fdb0c remove temporary markdown file (#7048)
Deletes a file accidentally committed when rebasing and doing `git add
.`

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-7048-remove-temporary-markdown-file-2bb6d73d36508139b95ef3c7e4ab0798)
by [Unito](https://www.unito.io)
2025-11-29 19:13:37 -07:00
Christian Byrne
f39faf6b8e mark vue nodes menu toggle with beta tag (#7047)
## Summary

Adds a "Beta" tag next to the Nodes 2.0 menu item toggle to better
indicate to users, especially new users, that the feature is in beta.

<img width="752" height="918" alt="Selection_2477"
src="https://github.com/user-attachments/assets/cf341b2b-1af6-4259-8449-bf157989b7b3"
/>

<img width="433" height="880" alt="Selection_2476"
src="https://github.com/user-attachments/assets/a0f658bf-1904-4d79-9ca7-c81c458adc54"
/>

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-7047-mark-vue-nodes-menu-toggle-with-beta-tag-2bb6d73d3650815e8a9aceaa9250c02e)
by [Unito](https://www.unito.io)
2025-11-29 18:33:57 -07:00
Christian Byrne
3ee921119d fix: loader node widget value shows placeholder instead of filename on cloud (#7005)
Fixes issue on cloud where the Loader node dropdowns showed placeholder
values (after refresh) in the widget UI despite a value being loaded.

Cloud loader dropdowns hydrate via `useAssetWidgetData(nodeType)`, so
`dropdownItems` stays empty until the Asset API returns friendly
filenames. Meanwhile `modelValue` already holds the saved asset and the
watcher only tracks `modelValue`:
df653d6ce1/src/renderer/extensions/vueNodes/widgets/components/WidgetSelectDropdown.vue (L215-L226)

This watcher runs before assets load, fails to find a match, clears
`selectedSet`, and the placeholder persists:


df653d6ce1/src/renderer/extensions/vueNodes/widgets/components/WidgetSelectDropdown.vue (L222-L224)

Once the assets API resolves, `dropdownItems` recomputes but nothing
resyncs because the watcher never sees that change. Desktop doesn’t hit
this because it still reads from `widget.options.values` immediately.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-7005-fix-loader-node-widget-value-shows-placeholder-instead-of-filename-on-cloud-2b86d73d36508125a16de5c9e366b014)
by [Unito](https://www.unito.io)
2025-11-29 18:13:32 -07:00
Christian Byrne
f61bfe666e style: move border and shadow style to outer container to fix topbar badges shadow/border (#7044)
## Summary

Moves the shadow and border from workflow tabs to outer container so
that there is no longer an inconsistency with the badges and topbar
bottom border and shadows.

## Changes

| Before | After |
|
---------------------------------------------------------------------------------------------------------------------------------------------
|
---------------------------------------------------------------------------------------------------------------------------------------------
|
| <img width="1522" height="598" alt="Selection_2473"
src="https://github.com/user-attachments/assets/92aa602c-a018-4b3d-8606-9c00552ffc20"
/> | <img width="1522" height="598" alt="Selection_2472"
src="https://github.com/user-attachments/assets/13c75da3-a938-442a-9bf6-9c30d822a610"
/> |
| <img width="1522" height="598" alt="Selection_2474"
src="https://github.com/user-attachments/assets/e84efd2a-6ae1-44a6-9901-0f902e942b64"
/> | <img width="1522" height="598" alt="Selection_2475"
src="https://github.com/user-attachments/assets/66d4852e-eed7-49f9-8f0f-9e8bbcdcb0b8"
/> |

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-7044-style-move-border-and-shadow-style-to-outer-container-to-fix-topbar-badges-shadow-border-2ba6d73d365081b991a3f981db87f434)
by [Unito](https://www.unito.io)

---------

Co-authored-by: github-actions <github-actions@github.com>
2025-11-29 17:52:03 -07:00
Christian Byrne
d8795ec997 fix: actionbar can get stuck behind floating menu and not be moved with drag handle (#7034)
Fixes issue where actionbar (and therefore "Run" button) can get stuck
behind the floating menus and not be physically moved (especially on
mobile) leading to an inability to press "Run" button.

`z1010` and `z1000` do not seem to be actual classes in our system.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-7034-fix-actionbar-can-get-stuck-behind-floating-menu-and-not-be-moved-with-drag-handle-2b96d73d365081fc87d1ec4446a50e43)
by [Unito](https://www.unito.io)

---------

Co-authored-by: Alexander Brown <drjkl@comfy.org>
2025-11-30 00:22:08 +00:00
Alexander Brown
d3aa8dfc88 A11y/style: Make properties panel use theme colors. Not comprehensive. (#7036)
## Summary

Addressing the theme aspect of #6976 

## Changes

- **What**: Not comprehensive, but swaps many of the colors in the
litegraph panel styles to reference theme tokens.

## Screenshots (if applicable)
<img width="730" height="637" alt="image"
src="https://github.com/user-attachments/assets/1f8b0c0f-f957-487e-96b5-324ed2a6a717"
/>
<img width="721" height="719" alt="image"
src="https://github.com/user-attachments/assets/37f098a3-39f6-4cae-8bcb-601121d106f5"
/>

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-7036-A11y-style-Make-properties-panel-use-theme-colors-Not-comprehensive-2ba6d73d365081038481c66be68a599a)
by [Unito](https://www.unito.io)

---------

Co-authored-by: github-actions <github-actions@github.com>
2025-11-29 16:06:29 -08:00
Christian Byrne
2cce0fe611 style: make feedback button icon-only after mobile breakpoint (#7043)
## Summary

On mobile breakpoint, change the "Feedback" button to only show an icon,
preserving crucial space on the breadcrumb axis.

## Changes

| Before | After |
|
---------------------------------------------------------------------------------------------------------------------------------------------
|
---------------------------------------------------------------------------------------------------------------------------------------------
|
| <img width="732" height="1458" alt="Selection_2471"
src="https://github.com/user-attachments/assets/8491a755-5817-4021-a2bd-8dff09a8345f"
/> | <img width="732" height="1458" alt="Selection_2470"
src="https://github.com/user-attachments/assets/6229badb-46c4-4617-9984-5636ad4b803c"
/> |

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-7043-style-make-feedback-button-icon-only-after-mobile-breakpoint-2ba6d73d365081489d9adc89eb5ec42b)
by [Unito](https://www.unito.io)
2025-11-29 15:23:48 -07:00
Johnpaul Chiwetelu
b9cb335255 [fix] Add fraction digits props to number input widget (#7033)
Add min-fraction-digits and max-fraction-digits props to InputNumber
component to allow typing decimal values. Without these props, users
could not manually type values like "0.8" even though the increment
buttons worked correctly.

This aligns the input-only variant with the slider variant which
already had these props configured.

fix https://github.com/Comfy-Org/ComfyUI_frontend/issues/7016
## Screenshots (if applicable)



https://github.com/user-attachments/assets/fa205023-13c4-45ac-874b-b241808cf56d



┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-7033-fix-Add-fraction-digits-props-to-number-input-widget-2b96d73d36508124ac65e466802bbb0c)
by [Unito](https://www.unito.io)

---------

Co-authored-by: github-actions <github-actions@github.com>
2025-11-29 15:10:04 -07:00
Christian Byrne
2f89eb070c when on cloud, link to cloud version of "getting started" page for "docs" button (#7041)
Changes docs button in bottom left help center to open
https://docs.comfy.org/get_started/cloud when on cloud (as opposed to
https://docs.comfy.org/).

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-7041-when-on-cloud-link-to-cloud-version-of-getting-started-page-for-docs-button-2ba6d73d365081b79bcaf91cfeb3dffd)
by [Unito](https://www.unito.io)
2025-11-29 13:11:53 -07:00
Benjamin Lu
d94e0720f3 Handle HTML fallbacks for node help and skip blueprint docs (#7021)
explicitly prevents subgraphs from making an api call since they don't
have docs, this was previously reliant on a non-ok resolution

also doesn't try returning anything that has contenttype of text/html to
prevent the markdown renderer from crashing

## Summary
- short-circuit blueprint/subgraph nodes in help: skip doc fetch and
return the node description, avoiding SPA fallback responses
- guard node help fetch against HTML/SPA fallbacks using content-type
checks; treat them as missing and trigger the existing description
fallback
- keep base URL logic unchanged for non-blueprint nodes

## Testing
- pnpm typecheck
- pnpm lint:fix
- pnpm test:unit
2025-11-28 10:09:09 -08:00
Alexander Piskun
fe47a487bb feat(api-nodes-pricing): add prices for ByteDance seedance-1-0-pro-fast model (#7026)
## Summary

Price badges for this PR:
https://github.com/comfyanonymous/ComfyUI/pull/10947

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-7026-feat-api-nodes-pricing-add-prices-for-ByteDance-seedance-1-0-pro-fast-model-2b96d73d365081eeb7f7de1dc892389b)
by [Unito](https://www.unito.io)
2025-11-28 10:38:58 -07:00
Benjamin Lu
63f68543e4 [feat] Show "Finished in" duration for completed jobs in cloud (#6895)
## Summary
In cloud distribution, completed jobs now show "Finished in Xh Ym Zs" as
the primary text instead of the filename.

- Uses `formatDuration` to display time as `1h 30m 45s`, `30m 45s`, or
`45s`
- Gated with `isCloud` - non-cloud continues to show filename
- Added i18n key `queue.completedIn` for localization

Filename is not fetchable right now in cloud. This is what design wanted
as the alternative.

<img width="679" height="1097" alt="image"
src="https://github.com/user-attachments/assets/291deb42-77d8-4de9-b4f8-ee65f3c25011"
/>

Co-authored-by: Claude <noreply@anthropic.com>
2025-11-27 16:41:45 -07:00
Johnpaul Chiwetelu
9806ba807a [fix] Re-encode cloud-subscription video to VP9 for Safari compatibility (#7006)
The original video was encoded with AV1 codec which Safari does not
support, causing "Plug-in handled load" errors. Re-encoded to VP9
which is supported across all major browsers including Safari 14.1+.

 Summary

- Re-encode cloud-subscription.webm from AV1 to VP9 codec for Safari
browser compatibility

  Problem

Safari does not support AV1 codec in webm containers, causing the cloud
subscription video to
  fail with "Plug-in handled load" error.

  Solution

Re-encoded the video using VP9 codec (libvpx-vp9) which is supported by
all major browsers
  including Safari 14.1+.

  Test plan

  - Verify video plays correctly in Safari
  - Verify video plays correctly in Chrome/Firefox/Edge


[cloud-subscription.webm](https://github.com/user-attachments/assets/ab2dcaa1-f3b3-47c9-84d0-9fc8ea8c6f17)

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-7006-fix-Re-encode-cloud-subscription-video-to-VP9-for-Safari-compatibility-2b86d73d365081ad9262f97fc1ebb5ee)
by [Unito](https://www.unito.io)
2025-11-27 16:41:00 -07:00
Simula_r
7433f470fc Fix/vue nodes banner text (#7007)
## Summary

ensure banner text is white

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-7007-Fix-vue-nodes-banner-text-2b86d73d365081e1ab60c7013cc9d37c)
by [Unito](https://www.unito.io)
2025-11-27 16:40:27 -07:00
AustinMroz
490bb22bd3 Remove app.graph usage from widgetInput code (#7008)
Resolves #7000

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-7008-Remove-app-graph-usage-from-widgetInput-code-2b86d73d3650819eb1bcc98130f18cbe)
by [Unito](https://www.unito.io)
2025-11-27 16:39:53 -07:00
Comfy Org PR Bot
895775c319 1.34.2 (#7009)
Patch version increment to 1.34.2

**Base branch:** `main`

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

Co-authored-by: christian-byrne <72887196+christian-byrne@users.noreply.github.com>
2025-11-27 16:38:31 -07:00
JonatanAtila
e248ecfa4e feat(i18n): add Portuguese (Brazil) locale (pt-BR) (#6943)
Finalmente o idioma em Portugues do Brasil verá a luz do dia (se tudo
correr bem).

What has been done:

- Added pt-BR to .i18nrc.cjs and settings
- Included loaders in src/i18n.ts and apps/desktop-ui/src/i18n.ts
- Now Portuguese (BR) is displayed in the language selector
- Created empty main.json, commands.json, settings.json and
nodeDefs.json files to be populated by CI

- Checklist: the language appears in the dropdown list, selection occurs
without errors, the fallback to English, in case technical terms have no
translation, is working correctly.

- I will maintain the pt-br translation and review for as long as
necessary.

---------

Co-authored-by: Comfy Contributor <dev@example.com>
2025-11-27 15:46:27 -07:00
Kelly Yang
923695ffde fix(ui): update button style for mask editor (#6991)
## Summary

Updated the styling of the `resetToDefault` button to match the visual
design and UX behavior (hover states, dimensions) of other buttons in
the `TopBarHeader`

## Changes

Applied the standard top bar button CSS classes to the resetToDefault
button to ensure visual consistency. @jtydhr88


## Review Focus

Please verify that the button alignment and hover effects now match the
adjacent UI elements.

## Screenshots
<img width="2746" height="2300" alt="ScreenShot_2025-11-27_010713_634"
src="https://github.com/user-attachments/assets/758e108f-7c38-4b51-a236-fee2e049186e"
/>

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6991-fix-ui-update-button-style-for-mask-editor-2b86d73d3650813ea3cbd28203239cd8)
by [Unito](https://www.unito.io)
2025-11-27 15:43:55 -07:00
Comfy Org PR Bot
df653d6ce1 1.34.1 (#6986)
Patch version increment to 1.34.1

**Base branch:** `main`

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

Co-authored-by: christian-byrne <72887196+christian-byrne@users.noreply.github.com>
2025-11-27 13:07:03 -07:00
Alexander Piskun
28dc9314d2 feat(api-nodes-pricing): add prices for Kling-v2-5-turbo for node KlingStartEndFrame (#6996)
## Summary

Pricing for new model for this node

## Screenshots (if applicable)

<img width="1174" height="422" alt="Screenshot From 2025-11-27 14-44-38"
src="https://github.com/user-attachments/assets/78edb4d2-3aef-427c-98d5-ea9b6b18b203"
/>

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6996-feat-api-nodes-pricing-add-prices-for-Kling-v2-5-turbo-for-node-KlingStartEndFrame-2b86d73d36508171ba2cd186248228b7)
by [Unito](https://www.unito.io)
2025-11-27 13:06:44 -07:00
Johnpaul Chiwetelu
3acf9aae0a fix: add filter for combo widgets (#6999)
This pull request introduces a minor enhancement to the
`WidgetSelectDefault.vue` component. The change conditionally enables
the filtering feature in the select dropdown when there are more than
four options, improving usability for larger lists.


<img width="932" height="622" alt="Screenshot 2025-11-27 160239"
src="https://github.com/user-attachments/assets/76f6080f-13c7-4fa9-a813-2799af15363e"
/>

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6999-fix-add-filter-for-combo-widgets-2b86d73d365081f994e5caf78e64a196)
by [Unito](https://www.unito.io)
2025-11-27 13:06:22 -07:00
Alexander Brown
a055241e2e Change node-component-header-surface color variable (#6990)
## Summary

Light mode node header color update.
2025-11-27 16:15:59 +01:00
Jin Yi
9d131a4267 [feat] Add right-click context menu to MediaAssetCard (#6844)
## Summary
- Add right-click context menu functionality to MediaAssetCard
- Separate context menu into its own component
(MediaAssetContextMenu.vue)
- Ensure only one context menu is visible at a time within the same tab

## Changes
- Add `MediaAssetContextMenu.vue` - new component for context menu
- Update `MediaAssetCard.vue` - show context menu on right-click and
more button click
- Delete `MediaAssetMoreMenu.vue` - consolidated into context menu
- Delete `MediaAssetButtonDivider.vue` - unused
- Update `AssetsSidebarTab.vue` - add context menu state management
- Refactor `useMediaAssetActions.ts`

## Screenshot

[screen-capture.webm](https://github.com/user-attachments/assets/6fe414ef-b134-4fbe-98aa-6437bb354b41)

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

---------

Co-authored-by: Claude <noreply@anthropic.com>
2025-11-26 23:02:32 -07:00
Christian Byrne
c57ceaf826 fix: add missing translations (#6970)
## Summary

Adds translations for things identified as "always English" during QA
testing.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6970-fix-add-missing-translations-2b86d73d365081ffa58ccef156d28028)
by [Unito](https://www.unito.io)
2025-11-26 18:25:49 -07:00
Christian Byrne
29dbfa3f60 fix: Vue Node <-> Litegraph node height offset normalization (#6966)
## Summary

Changes the layout store to treat node sizes as body-only measurements
while LiteGraph continues to reason about full heights. DOM-driven
updates are tagged with `LayoutSource.DOM`, which lets the store strip
the title height exactly once before persisting. That classification (a
new mutation source - `LayoutSource.DOM`) is accurate because those
mutations are triggered by the browser’s layout engine via
ResizeObserver, rather than by direct calls into the layout APIs (e.g.,
`moveNodeTo`, `useNodeDrag`). So all sources are:

- `LayoutSource.DOM`: browser layout/ResizeObserver measurements that
include the title bar
- `LayoutSource.Vue`: direct Vue-driven mutations routed through the
layout store
- `LayoutSource.Canvas`: legacy LiteGraph/canvas updates that will be
phased out over time
- `LayoutSource.External`: for multiplayer or syncing with a when going
online after making changes offline (in teams/workspace)

When layout state flows back into LiteGraph we add the title height just
in time for `liteNode.setSize`, so LiteGraph’s rendering stays
unchanged. This makes Vue node resizing and workflow persistence
deterministic - multiline widgets hold their dimensions across reloads
because every path that crosses the layout/LiteGraph boundary performs
the same normalization.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6966-normalize-height-at-sites-2b76d73d365081b6bcb4f4ce6a27663a)
by [Unito](https://www.unito.io)

---------

Co-authored-by: github-actions <github-actions@github.com>
2025-11-26 17:37:24 -07:00
Christian Byrne
83f04490ba fix: don't use registry when only checking for presence of missing nodes (#6965)
Changes the client-side validation of whether a graph has missing nodes
to use a simpler check (is the node name in the node defs from
`/object_info`), rather than using the Comfy Node Registry API
(api.comfy.org). The Registry API is still needed to get full
metadata/info about missing nodes, but that can be deferred until the
user actually needs that info.

This also fixes an issue where a node you coded yourself locally which
is neither a core node nor a node in the registry would be considered
"missing."

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6965-fix-don-t-use-registry-when-only-checking-for-presence-of-missing-nodes-2b76d73d365081e7b8fbcb3f2a1c4502)
by [Unito](https://www.unito.io)
2025-11-26 17:21:16 -07:00
Christian Byrne
c5fe617347 feat: open template via URL in linear mode (#6945)
## Summary

Adds `mode` as a valid url query param when opening template from URL.



https://github.com/user-attachments/assets/8e7efb1c-d842-4953-822a-f3cea7ddecb6

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6945-feat-open-template-via-URL-in-linear-mode-2b76d73d365081d8ad4af3d49a68a4ff)
by [Unito](https://www.unito.io)
2025-11-26 16:44:28 -07:00
Christian Byrne
8b2c1fc45d allow telemetry events to be disabled by name in feature flags (#6946)
## Summary

Adds ability to toggle events on/off by name from dynamic feature flags.
Also makes some events disabled by default (not being used for any
analysis and taking too much of event quota currently).

Note: telemetry is only enabled on cloud - this doesn't affect local
users in any way.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6946-allow-telemetry-events-to-be-disabled-by-name-in-feature-flags-2b76d73d365081ea8676cfbb8217e640)
by [Unito](https://www.unito.io)
2025-11-26 14:20:21 -07:00
Alexander Brown
df66a96976 Feat: Double Click on Image Assets to open the lightbox (#6955)
## Summary

Make it easier to open the lightbox preview.

## Review Focus

The first click still selects the image. I liked the suggestion to
require shift or ctrl to initiate selection and have click go back to
the previous behavior.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6955-Feat-Double-Click-on-Image-Assets-to-open-the-lightbox-2b76d73d365081f0bf0de847add5c820)
by [Unito](https://www.unito.io)
2025-11-26 11:52:54 -08:00
Alexander Brown
96d12330bb Fix: Toolbox position desync (#6962)
## Summary

Toggle the toolbox position check based on visibility, not just when
selection changes.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6962-Fix-Toolbox-position-desync-2b76d73d3650818da327c7adee6c1098)
by [Unito](https://www.unito.io)
2025-11-26 19:43:58 +00:00
Simula_r
4b87b1fdc5 fix: remove LOD from vue nodes (#6950)
## Summary

Refactor to remove LOD from vue nodes. Also, hide Litegraph LOD settings
in vue nodes to prevent confusion / stale setting. Keep litegraph LOD
the exact same.

## Changes

- **What**: settings, all LOD related code in vue nodes

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6950-fix-remove-LOD-from-vue-nodes-2b76d73d365081568723f8cbc7be7e17)
by [Unito](https://www.unito.io)

---------

Co-authored-by: github-actions <github-actions@github.com>
2025-11-26 12:26:05 -07:00
Comfy Org PR Bot
08b256c29d 1.34.0 (#6961)
Minor version increment to 1.34.0

**Base branch:** `main`

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

Co-authored-by: christian-byrne <72887196+christian-byrne@users.noreply.github.com>
2025-11-26 12:24:25 -07:00
534 changed files with 14149 additions and 6196 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

@@ -42,3 +42,7 @@ ALGOLIA_API_KEY=684d998c36b67a9a9fce8fc2d8860579
# SENTRY_AUTH_TOKEN=private-token # get from sentry
# SENTRY_ORG=comfy-org
# SENTRY_PROJECT=cloud-frontend-staging
# Stripe pricing table configuration (used by feature-flagged subscription tiers UI)
# VITE_STRIPE_PUBLISHABLE_KEY=pk_test_123
# VITE_STRIPE_PRICING_TABLE_ID=prctbl_123

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

View File

@@ -0,0 +1,292 @@
# Automated weekly workflow to bump ComfyUI frontend RC releases
name: "Release: Weekly ComfyUI"
on:
# Schedule for Monday at 12:00 PM PST (20:00 UTC)
schedule:
- cron: '0 20 * * 1'
# Allow manual triggering
workflow_dispatch:
inputs:
comfyui_fork:
description: 'ComfyUI fork to use for PR (e.g., Comfy-Org/ComfyUI)'
required: false
default: 'Comfy-Org/ComfyUI'
type: string
jobs:
resolve-version:
runs-on: ubuntu-latest
outputs:
current_version: ${{ steps.resolve.outputs.current_version }}
target_version: ${{ steps.resolve.outputs.target_version }}
target_minor: ${{ steps.resolve.outputs.target_minor }}
target_branch: ${{ steps.resolve.outputs.target_branch }}
needs_release: ${{ steps.resolve.outputs.needs_release }}
diff_url: ${{ steps.resolve.outputs.diff_url }}
latest_patch_tag: ${{ steps.resolve.outputs.latest_patch_tag }}
steps:
- name: Checkout ComfyUI_frontend
uses: actions/checkout@v5
with:
fetch-depth: 0
path: frontend
- name: Checkout ComfyUI (sparse)
uses: actions/checkout@v5
with:
repository: comfyanonymous/ComfyUI
sparse-checkout: |
requirements.txt
path: comfyui
- name: Install pnpm
uses: pnpm/action-setup@v4
with:
version: 10
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: lts/*
- name: Install dependencies
working-directory: frontend
run: pnpm install --frozen-lockfile
- name: Resolve release information
id: resolve
working-directory: frontend
run: |
set -euo pipefail
# Run the resolver script
if ! RESULT=$(pnpm exec tsx scripts/cicd/resolve-comfyui-release.ts ../comfyui .); then
echo "Failed to resolve release information"
exit 1
fi
echo "Resolver output:"
echo "$RESULT"
# Validate JSON output
if ! echo "$RESULT" | jq empty 2>/dev/null; then
echo "Invalid JSON output from resolver"
exit 1
fi
# Parse JSON output and set outputs
echo "current_version=$(echo "$RESULT" | jq -r '.current_version')" >> $GITHUB_OUTPUT
echo "target_version=$(echo "$RESULT" | jq -r '.target_version')" >> $GITHUB_OUTPUT
echo "target_minor=$(echo "$RESULT" | jq -r '.target_minor')" >> $GITHUB_OUTPUT
echo "target_branch=$(echo "$RESULT" | jq -r '.target_branch')" >> $GITHUB_OUTPUT
echo "needs_release=$(echo "$RESULT" | jq -r '.needs_release')" >> $GITHUB_OUTPUT
echo "diff_url=$(echo "$RESULT" | jq -r '.diff_url')" >> $GITHUB_OUTPUT
echo "latest_patch_tag=$(echo "$RESULT" | jq -r '.latest_patch_tag')" >> $GITHUB_OUTPUT
- name: Summary
run: |
echo "## Release Information" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "- Current version: ${{ steps.resolve.outputs.current_version }}" >> $GITHUB_STEP_SUMMARY
echo "- Target version: ${{ steps.resolve.outputs.target_version }}" >> $GITHUB_STEP_SUMMARY
echo "- Target branch: ${{ steps.resolve.outputs.target_branch }}" >> $GITHUB_STEP_SUMMARY
echo "- Needs release: ${{ steps.resolve.outputs.needs_release }}" >> $GITHUB_STEP_SUMMARY
echo "- Diff: [${{ steps.resolve.outputs.current_version }}...${{ steps.resolve.outputs.target_version }}](${{ steps.resolve.outputs.diff_url }})" >> $GITHUB_STEP_SUMMARY
trigger-release-if-needed:
needs: resolve-version
if: needs.resolve-version.outputs.needs_release == 'true'
runs-on: ubuntu-latest
steps:
- name: Trigger release workflow
env:
GH_TOKEN: ${{ secrets.PR_GH_TOKEN }}
run: |
set -euo pipefail
echo "Triggering release workflow for branch ${{ needs.resolve-version.outputs.target_branch }}"
# Trigger the release-version-bump workflow
if ! gh workflow run release-version-bump.yaml \
--repo Comfy-Org/ComfyUI_frontend \
--ref main \
--field version_type=patch \
--field branch=${{ needs.resolve-version.outputs.target_branch }}; then
echo "Failed to trigger release workflow"
exit 1
fi
echo "Release workflow triggered successfully for ${{ needs.resolve-version.outputs.target_branch }}"
- name: Summary
run: |
echo "## Release Workflow Triggered" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "- Branch: ${{ needs.resolve-version.outputs.target_branch }}" >> $GITHUB_STEP_SUMMARY
echo "- Target version: ${{ needs.resolve-version.outputs.target_version }}" >> $GITHUB_STEP_SUMMARY
echo "- [View workflow runs](https://github.com/Comfy-Org/ComfyUI_frontend/actions/workflows/release-version-bump.yaml)" >> $GITHUB_STEP_SUMMARY
create-comfyui-pr:
needs: [resolve-version, trigger-release-if-needed]
if: always() && needs.resolve-version.result == 'success'
runs-on: ubuntu-latest
steps:
- name: Checkout ComfyUI fork
uses: actions/checkout@v5
with:
repository: ${{ inputs.comfyui_fork || 'Comfy-Org/ComfyUI' }}
token: ${{ secrets.PR_GH_TOKEN }}
path: comfyui
- name: Sync with upstream
working-directory: comfyui
run: |
set -euo pipefail
# Fetch latest upstream to base our branch on fresh code
# Note: This only affects the local checkout, NOT the fork's master branch
# We only push the automation branch, leaving the fork's master untouched
echo "Fetching upstream master..."
if ! git fetch https://github.com/comfyanonymous/ComfyUI.git master; then
echo "Failed to fetch upstream master"
exit 1
fi
echo "Checking out upstream master..."
if ! git checkout FETCH_HEAD; then
echo "Failed to checkout FETCH_HEAD"
exit 1
fi
echo "Successfully synced with upstream master"
- name: Update requirements.txt
working-directory: comfyui
run: |
set -euo pipefail
TARGET_VERSION="${{ needs.resolve-version.outputs.target_version }}"
echo "Updating comfyui-frontend-package to ${TARGET_VERSION}"
# Update the comfyui-frontend-package version (POSIX-compatible)
sed -i.bak "s/comfyui-frontend-package==[0-9.][0-9.]*/comfyui-frontend-package==${TARGET_VERSION}/" requirements.txt
rm requirements.txt.bak
# Verify the change was made
if ! grep -q "comfyui-frontend-package==${TARGET_VERSION}" requirements.txt; then
echo "Failed to update requirements.txt"
exit 1
fi
echo "Updated requirements.txt:"
grep comfyui-frontend-package requirements.txt
- name: Build PR description
id: pr-body
run: |
BODY=$(cat <<'EOF'
Bumps frontend to ${{ needs.resolve-version.outputs.target_version }}
Test quickly:
```bash
python main.py --front-end-version Comfy-Org/ComfyUI_frontend@${{ needs.resolve-version.outputs.target_version }}
```
- Diff: [v${{ needs.resolve-version.outputs.current_version }}...v${{ needs.resolve-version.outputs.target_version }}](${{ needs.resolve-version.outputs.diff_url }})
- PyPI: https://pypi.org/project/comfyui-frontend-package/${{ needs.resolve-version.outputs.target_version }}/
- npm: https://www.npmjs.com/package/@comfyorg/comfyui-frontend-types/v/${{ needs.resolve-version.outputs.target_version }}
EOF
)
# Add release PR note if release was triggered
if [ "${{ needs.resolve-version.outputs.needs_release }}" = "true" ]; then
RELEASE_NOTE="⚠️ **Release PR must be merged first** - check [release workflow runs](https://github.com/Comfy-Org/ComfyUI_frontend/actions/workflows/release-version-bump.yaml)"
BODY=$''"${RELEASE_NOTE}"$'\n\n'"${BODY}"
fi
# Save to file for later use
printf '%s\n' "$BODY" > pr-body.txt
cat pr-body.txt
- name: Create PR to ComfyUI
working-directory: comfyui
env:
GH_TOKEN: ${{ secrets.PR_GH_TOKEN }}
COMFYUI_FORK: ${{ inputs.comfyui_fork || 'Comfy-Org/ComfyUI' }}
run: |
set -euo pipefail
# Extract fork owner from repository name
FORK_OWNER=$(echo "$COMFYUI_FORK" | cut -d'/' -f1)
echo "Creating PR from ${COMFYUI_FORK} to comfyanonymous/ComfyUI"
# Configure git
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
# Create/update branch (reuse same branch name each week)
BRANCH="automation/comfyui-frontend-bump"
git checkout -B "$BRANCH"
git add requirements.txt
if ! git diff --cached --quiet; then
git commit -m "Bump comfyui-frontend-package to ${{ needs.resolve-version.outputs.target_version }}"
else
echo "No changes to commit"
exit 0
fi
# Force push to fork (overwrites previous week's branch)
# Note: This intentionally destroys branch history to maintain a single PR
# Any review comments or manual commits will need to be re-applied
if ! git push -f origin "$BRANCH"; then
echo "Failed to push branch to fork"
exit 1
fi
# Create draft PR from fork to upstream
PR_BODY=$(cat ../pr-body.txt)
# Try to create PR, ignore error if it already exists
if ! gh pr create \
--repo comfyanonymous/ComfyUI \
--head "${FORK_OWNER}:${BRANCH}" \
--base master \
--title "Bump comfyui-frontend-package to ${{ needs.resolve-version.outputs.target_version }}" \
--body "$PR_BODY" \
--draft 2>&1; then
# Check if PR already exists
set +e
EXISTING_PR=$(gh pr list --repo comfyanonymous/ComfyUI --head "${FORK_OWNER}:${BRANCH}" --json number --jq '.[0].number' 2>&1)
PR_LIST_EXIT=$?
set -e
if [ $PR_LIST_EXIT -ne 0 ]; then
echo "Failed to check for existing PR: $EXISTING_PR"
exit 1
fi
if [ -n "$EXISTING_PR" ] && [ "$EXISTING_PR" != "null" ]; then
echo "PR already exists (#${EXISTING_PR}), updating branch will update the PR"
else
echo "Failed to create PR and no existing PR found"
exit 1
fi
fi
- name: Summary
run: |
echo "## ComfyUI PR Created" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "Draft PR created in comfyanonymous/ComfyUI" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "### PR Body:" >> $GITHUB_STEP_SUMMARY
cat pr-body.txt >> $GITHUB_STEP_SUMMARY

View File

@@ -9,7 +9,7 @@ module.exports = defineConfig({
entry: 'src/locales/en',
entryLocale: 'en',
output: 'src/locales',
outputLocales: ['zh', 'zh-TW', 'ru', 'ja', 'ko', 'fr', 'es', 'ar', 'tr'],
outputLocales: ['zh', 'zh-TW', 'ru', 'ja', 'ko', 'fr', 'es', 'ar', 'tr', 'pt-BR'],
reference: `Special names to keep untranslated: flux, photomaker, clip, vae, cfg, stable audio, stable cascade, stable zero, controlnet, lora, HiDream.
'latent' is the short form of 'latent space'.
'mask' is in the context of image processing.

View File

@@ -38,6 +38,7 @@
"typescript/no-redundant-type-constituents": "off",
"typescript/restrict-template-expressions": "off",
"typescript/unbound-method": "off",
"typescript/no-floating-promises": "error"
"typescript/no-floating-promises": "error",
"vue/no-import-compiler-macros": "error"
}
}

254
AGENTS.md
View File

@@ -1,38 +1,244 @@
# 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`.
## 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.
- 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`
## 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](https://refactoring.com/catalog/) 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
## Testing Guidelines
- 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/`
### General
1. Do not write change detector tests
e.g. a test that just asserts that the defaults are certain values
2. Do not write tests that are dependent on non-behavioral features like utility classes or styles
3. Be parsimonious in testing, do not write redundant tests
See <https://tidyfirst.substack.com/p/composable-tests>
4. [Dont Mock What You Dont Own](https://hynek.me/articles/what-to-mock-in-5-mins/)
### Vitest / Unit Tests
1. Do not write tests that just test the mocks
Ensure that the tests fail when the code itself would behave in a way that was not expected or desired
2. For mocking, leverage [Vitest's utilities](https://vitest.dev/guide/mocking.html) where possible
3. Keep your module mocks contained
Do not use global mutable state within the test file
Use `vi.hoisted()` if necessary to allow for per-test Arrange phase manipulation of deeper mock state
4. For Component testing, use [Vue Test Utils](https://test-utils.vuejs.org/) and especially follow the advice [about making components easy to test](https://test-utils.vuejs.org/guide/essentials/easy-to-test.html)
5. Aim for behavioral coverage of critical and new features
### Playwright / Browser / E2E Tests
1. Follow the Best Practices described [in the Playwright documentation](https://playwright.dev/docs/best-practices)
2. Do not use waitForTimeout, use Locator actions and [retrying assertions](https://playwright.dev/docs/test-assertions#auto-retrying-assertions)
3. Tags like `@mobile`, `@2x` are respected by config and should be used for relevant tests
## 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>
- [Practical Test Pyramid](https://martinfowler.com/articles/practical-test-pyramid.html)
## 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

@@ -25,6 +25,9 @@
# Link rendering
/src/renderer/core/canvas/links/ @benceruleanlu
# Partner Nodes
/src/composables/node/useNodePricing.ts @jojodecayz @bigcat88
# Node help system
/src/utils/nodeHelpUtil.ts @benceruleanlu
/src/stores/workspace/nodeHelpStore.ts @benceruleanlu
@@ -54,10 +57,11 @@
# Translations
/src/locales/ @Yorha4D @KarryCharon @shinshin86 @Comfy-Org/comfy_maintainer @Comfy-org/comfy_frontend_devs
/src/locales/pt-BR/ @JonatanAtila @Yorha4D @KarryCharon @shinshin86
# LLM Instructions (blank on purpose)
.claude/
.cursor/
.cursorrules
**/AGENTS.md
**/CLAUDE.md
**/CLAUDE.md

View File

@@ -59,7 +59,8 @@ const LOCALES = [
['fr', 'Français'],
['es', 'Español'],
['ar', 'عربي'],
['tr', 'Türkçe']
['tr', 'Türkçe'],
['pt-BR', 'Português (BR)']
] as const satisfies ReadonlyArray<[string, string]>
type SupportedLocale = (typeof LOCALES)[number][0]

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> }>
@@ -40,7 +40,8 @@ const localeLoaders: Record<
ru: () => import('@frontend-locales/ru/main.json'),
tr: () => import('@frontend-locales/tr/main.json'),
zh: () => import('@frontend-locales/zh/main.json'),
'zh-TW': () => import('@frontend-locales/zh-TW/main.json')
'zh-TW': () => import('@frontend-locales/zh-TW/main.json'),
'pt-BR': () => import('@frontend-locales/pt-BR/main.json')
}
const nodeDefsLoaders: Record<
@@ -55,7 +56,8 @@ const nodeDefsLoaders: Record<
ru: () => import('@frontend-locales/ru/nodeDefs.json'),
tr: () => import('@frontend-locales/tr/nodeDefs.json'),
zh: () => import('@frontend-locales/zh/nodeDefs.json'),
'zh-TW': () => import('@frontend-locales/zh-TW/nodeDefs.json')
'zh-TW': () => import('@frontend-locales/zh-TW/nodeDefs.json'),
'pt-BR': () => import('@frontend-locales/pt-BR/nodeDefs.json')
}
const commandsLoaders: Record<
@@ -70,7 +72,8 @@ const commandsLoaders: Record<
ru: () => import('@frontend-locales/ru/commands.json'),
tr: () => import('@frontend-locales/tr/commands.json'),
zh: () => import('@frontend-locales/zh/commands.json'),
'zh-TW': () => import('@frontend-locales/zh-TW/commands.json')
'zh-TW': () => import('@frontend-locales/zh-TW/commands.json'),
'pt-BR': () => import('@frontend-locales/pt-BR/commands.json')
}
const settingsLoaders: Record<
@@ -85,7 +88,8 @@ const settingsLoaders: Record<
ru: () => import('@frontend-locales/ru/settings.json'),
tr: () => import('@frontend-locales/tr/settings.json'),
zh: () => import('@frontend-locales/zh/settings.json'),
'zh-TW': () => import('@frontend-locales/zh-TW/settings.json')
'zh-TW': () => import('@frontend-locales/zh-TW/settings.json'),
'pt-BR': () => import('@frontend-locales/pt-BR/settings.json')
}
// Track which locales have been loaded

View File

@@ -27,18 +27,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)
@@ -310,19 +324,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 +1259,6 @@ export class ComfyPage {
}, 'image/png')
})
}, filename)
// Wait a bit for the download to process
await this.page.waitForTimeout(500)
}
/**
@@ -1651,7 +1649,10 @@ export const comfyPageFixture = base.extend<{
// Set tutorial completed to true to avoid loading the tutorial workflow.
'Comfy.TutorialCompleted': true,
'Comfy.SnapToGrid.GridSize': testComfySnapToGridGridSize,
'Comfy.VueNodes.AutoScaleLayout': false
'Comfy.VueNodes.AutoScaleLayout': false,
// Disable toast warning about version compatibility, as they may or
// may not appear - depending on upstream ComfyUI dependencies
'Comfy.VersionCompatibility.DisableWarnings': true
})
} catch (e) {
console.error(e)

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

@@ -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

@@ -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

@@ -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: 97 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

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