Compare commits

...

145 Commits

Author SHA1 Message Date
snomiao
c004a2b8bd chore(tsconfig): ensure complete TypeScript coverage for all project files (#5655)
## Summary
- Added missing directories and files to tsconfig.json to ensure
complete TypeScript type checking coverage
- Expanded config file patterns to include all .mts configuration files
- Verified all 908 TypeScript files in the project are now properly
covered

## Changes
- Added `scripts/**/*.ts` to cover all TypeScript files in scripts
directory (i18n collection, CI/CD scripts)
- Added `build/**/*.ts` to cover customIconCollection.ts and future
build scripts
- Changed `vite.config.mts` to `*.config.mts` to include all vite config
files (vite.electron.config.mts, vite.types.config.mts)

## Test plan
- [x] Run `pnpm typecheck` to verify no TypeScript errors
- [x] Verified all TypeScript files are covered by tsconfig patterns
- [x] browser_tests/ directory confirmed to have its own extending
tsconfig

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

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5655-chore-tsconfig-ensure-complete-TypeScript-coverage-for-all-project-files-2736d73d36508103acbadc53ca2b2913)
by [Unito](https://www.unito.io)

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Alexander Brown <drjkl@comfy.org>
Co-authored-by: filtered <176114999+webfiltered@users.noreply.github.com>
2025-09-23 09:31:30 -07:00
snomiao
d0aee031e9 [feat] Merge ComfyUI_devtools into ComfyUI_frontend (#5166)
## Summary
Merges ComfyUI devtools components into the ComfyUI frontend monorepo to
consolidate development tools.

## Changes
- Added devtools components from ComfyUI repository
- Integrated development nodes and utilities
- Consolidated fake model assets for testing

## Related Issues
Fixes #4683

## Testing
- Devtools components are now available within the frontend monorepo
- Development workflow remains consistent

Co-authored-by: Claude <noreply@anthropic.com>
2025-09-22 23:15:50 -07:00
Simula_r
cec1de0147 feat: vue nodes LOD system (#5631)
## Summary

Replaced reactive (Vue-based) widget LOD with CSS visibility control.
Performance doesn't dramatically improve, but we avoid the mount/unmount
overhead during zoom/pan operations. This PR implements the visual
component of LOD—complex widgets that need lifecycle management will be
addressed separately.

### Problem & Solution
Problem: we want LOD to improve rendering performance and visual
feedback but discovered using reactivity in the current setup for it
meant mounting/unmounting caused worse lag than the performance it aimed
to fix. Switching to render all the details all the time but using css
visibility proved to be the best solution. However, it doesn't improve
rendering performance by much because the GPU texture size is the
bottleneck (from TransformPane.vue CSS transforms) and not
rasterization.

Solution: Keep all nodes/widgets mounted, use CSS visibility: hidden for
LOD. Trade memory for performance stability during zoom/pan/drag
operations.

### Technical Decision
We chose Performance > Memory:

- CSS transforms create a single GPU texture whose size depends on node
count, not widget complexity
- Mounting/unmounting hundreds of widgets during zoom = noticeable lag
from Vue VDOM diffing (since all components are mounted all the time
because of viewport culling challenge/trade off see
https://github.com/Comfy-Org/ComfyUI_frontend/pull/5510.)
- CSS visibility changes = no reactivity overhead, smooth interactions
- Result: Similar performance, but without interaction stutters

This is the visual layer only. If we want a hook into the LOD state per
node / widget that would be the next follow up system to implement.

### Next Steps (maybe)
- Chunked (split up single Transform Pane transform layer) when
rendering 1000+ nodes (maybe)
- ~~Selective unmounting API for widgets that register as "expensive"~~
- ~~Client bound hydration system~~

## Screenshots (if applicable)

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

<img width="1355" height="960" alt="image"
src="https://github.com/user-attachments/assets/41474d1b-9dbe-4240-a8cf-f4c9ff51d8e0"
/>
<img width="1354" height="963" alt="image"
src="https://github.com/user-attachments/assets/9f55edaa-5858-41b9-b6a8-c2d37e1649bd"
/>


┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5631-feat-vue-nodes-LOD-system-2726d73d365081c6a6c4e14aa634f19c)
by [Unito](https://www.unito.io)

---------

Co-authored-by: github-actions <github-actions@github.com>
2025-09-22 20:05:13 -07:00
Christian Byrne
b4976c1ddc Revert: Move VueFire persistence configuration to initialization (#5614) (#5729)
## Summary
This reverts PR #5614 which moved VueFire persistence configuration to
initialization.

## Reason for Revert

It breaks Google SSO login with error:

```
useErrorHandling.ts:12 FirebaseError: Firebase: Error (auth/argument-error).
    at createErrorInternal (index-c92d61ad.js:506:41)
    at _assert (index-c92d61ad.js:512:15)
    at _withDefaultResolver (index-c92d61ad.js:9237:5)
    at signInWithPopup (index-c92d61ad.js:9457:30)
    at executeAuthAction.createCustomer (firebaseAuthStore.ts:263:25)
    at executeAuthAction (firebaseAuthStore.ts:223:28)
    at Proxy.loginWithGoogle (firebaseAuthStore.ts:262:5)
    at Proxy.wrappedAction (pinia.mjs:1405:26)
    at useFirebaseAuthActions.ts:104:28
    at Object.signInWithGoogle (useErrorHandling.ts:39:22)
```

## Changes
- Reverts commit ea4e57b60 "Move VueFire persistence configuration to
initialization (#5614)"
- Restores previous Firebase auth persistence behavior

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5729-Revert-Move-VueFire-persistence-configuration-to-initialization-5614-2776d73d3650814c9b80d9c67c852874)
by [Unito](https://www.unito.io)
2025-09-22 19:04:08 -07:00
Alexander Brown
1611c7a224 Refactor: Further state management cleanup (#5727)
## Summary

Going through the GraphNodeManager and VueNodeLifecycle one property at
a time and removing the pieces that are not currently wired up or used
by the rest of the application

Fixes paste location by updating the layoutStore in LGraphCanvas (which
already mutates layoutStore elsewhere)

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5727-WIP-Refactor-Further-state-management-cleanup-2766d73d36508173b379c6009c194a5a)
by [Unito](https://www.unito.io)
2025-09-22 18:47:26 -07:00
Comfy Org PR Bot
d01081dab4 1.28.1 (#5728)
Patch version increment to 1.28.1

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5728-1-28-1-2776d73d365081b287e4fdc5cbcea07b)
by [Unito](https://www.unito.io)

Co-authored-by: AustinMroz <4284322+AustinMroz@users.noreply.github.com>
2025-09-22 20:36:12 -05:00
Alexander Brown
e5d4d07d32 Refactor: More state management simplification (#5721)
## Summary

Remove more procedural synchronization in favor of using reactive
references.

> Note: Also includes some fixes for issues caused during HMR.

## Review Focus

In testing it seems to work the same, but let me know if I missed
something.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5721-Refactor-More-state-management-simplification-2766d73d3650819b8d7ddc047c460f2b)
by [Unito](https://www.unito.io)
2025-09-22 13:15:33 -07:00
Alexander Piskun
f086377307 add pricing for new api nodes (#5724)
## Summary

Added prices for the new upcoming API nodes. Backport required.
2025-09-22 11:33:00 -07:00
filtered
687b9e659c Fix reroute ID 0 treated as invalid (#5723)
## Summary

Fixes old logic bug from refactor
https://github.com/Comfy-Org/litegraph.js/pull/602/files

## Changes

- Fixes truthy refactor to explicitly check undefined

## Review Focus

No expectation that this will impact prod, however it may impact
extensions IF someone has explicitly been setting link parentId to 0.
This would be very strange, as it would cause unexpected behaviour in
other parts of the code (which all explicitly check `undefined`).

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5723-Fix-reroute-ID-0-treated-as-invalid-2766d73d365081568124ce1f85cdf84e)
by [Unito](https://www.unito.io)
2025-09-22 11:13:38 -07:00
Christian Byrne
da0d51311b fix Vue node being dragged when interacting with widgets (e.g., resizing textarea) (#5719)
## Summary

Applying changes in
https://github.com/Comfy-Org/ComfyUI_frontend/pull/5516 to entire widget
wrapper.
 
## Changes

- **What**: Added `.stop` modifier to pointer events in NodeWidgets
component to prevent [event
propagation](https://developer.mozilla.org/en-US/docs/Web/API/Event/stopPropagation)

## Review Focus

Verify widget interactions remain functional while ensuring parent node
drag/selection behavior is properly isolated.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5719-fix-Vue-node-being-dragged-when-interacting-with-widgets-e-g-resizing-textarea-2766d73d3650815091adcd1d65197c7b)
by [Unito](https://www.unito.io)
2025-09-21 21:56:03 -07:00
Christian Byrne
e314d9cbd9 [refactor] Simplify current user resolved hook implementation (#5718)
## Summary

Refactored `onUserResolved` function in auth composable to use VueUse
`whenever` utility instead of manual watch implementation and use
`immediate` option instead of invoking manually before creating watcher.

## Changes

- **What**: Replaced manual watch + immediate check pattern with [VueUse
whenever](https://vueuse.org/shared/whenever/) utility in
`useCurrentUser.ts:37`

## Review Focus

Behavioral equivalence verification - `whenever` with `immediate: true`
should maintain identical callback timing and cleanup semantics.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5718-refactor-Simplify-current-user-resolved-hook-implementation-2766d73d365081008b6de156dd78f940)
by [Unito](https://www.unito.io)
2025-09-21 21:53:25 -07:00
Christian Byrne
95baf8d2f1 [style] update Vue node tooltip style (#5717)
## Summary

Change Vue node tooltips to align with
[design](https://www.figma.com/design/31uH3r4x3xbIctuRWYW6NM/V3---Nodes?node-id=6267-16837&m=dev)

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5717-style-update-Vue-node-tooltip-style-2766d73d365081bdb095faef17f6aeb6)
by [Unito](https://www.unito.io)
2025-09-21 20:01:33 -07:00
Christian Byrne
f951e07cea fix bypass hotkey in vue nodes and fix node data instrumentation setup issue when switching to Vue nodes after initial load (#5715)
## Summary

Fixed Vue node keybinding target element ID to enable
bypass/pin/collapse hotkeys in both LiteGraph and Vue rendering modes.

Also fixed a bug when starting in litegraph mode => switching to Vue
nodes without reloading => `graph.onTrigger` is set to `undefined` which
interferes with proper setup of node data instrumentation, among other
things.

## Changes

- **What**: Updated keybinding `targetElementId` from `graph-canvas` to
`graph-canvas-container` for node manipulation commands (parent of both
the canvas and transform pane -- vue nodes container).
- **What**: Added conditional `onTrigger` handler restoration in slot
layout sync to prevent Vue node manager conflicts

## Review Focus

Event handler precedence between Vue nodes and LiteGraph systems during
mode switching, ensuring hotkeys work consistently across rendering
modes.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5715-fix-bypass-hotkey-in-vue-nodes-and-fix-node-data-instrumentation-setup-issue-when-switchi-2756d73d3650815c8ec8d5e4d06232e3)
by [Unito](https://www.unito.io)
2025-09-21 17:32:12 -07:00
Christian Byrne
023e466dba fix using shift modifier to (de-)select Vue nodes (#5714)
## Summary

Fixes https://github.com/Comfy-Org/ComfyUI_frontend/issues/5688 by
adding shift modifier support for multi-selecting Vue nodes, enabling
standard shift+click selection behavior alongside existing
ctrl/cmd+click.

## Changes

- **What**: Updated Vue node event handlers to include `event.shiftKey`
in multi-select logic
- **Testing**: Added browser tests for both ctrl and shift modifier
selection behaviors

## Review Focus

Multi-select behavior consistency across different input modifiers and
platform compatibility (Windows/Mac/Linux shift key handling).

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5714-fix-using-shift-modifier-to-de-select-Vue-nodes-2756d73d365081bcb5e0fe80eacdb2f0)
by [Unito](https://www.unito.io)
2025-09-21 14:39:40 -07:00
Christian Byrne
abd6823744 [refactor] Remove redundant module comment (#5711)
Removes a comment added in initial Vue Nodes commit. The comment is
interpolated between import statements which is stylistically awkward
and it is almost totally redundant with the doc comment on the
composable:


c1d4709e96/src/renderer/extensions/vueNodes/layout/useNodeLayout.ts (L10-L14)

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5711-refactor-Remove-redundant-module-comment-2756d73d365081ef9bffe0257b3670f1)
by [Unito](https://www.unito.io)
2025-09-21 14:30:58 -07:00
Alexander Brown
c4c0e52e64 Refactor: Let LGraphNode handle more events itself (#5709)
## Summary

Don't route events up through GraphCanvas if the component itself can
handle the changes

## Changes

- **What**: Reduce the indirect access or action dispatch to
composables/stores.

## Review Focus

The behavior should be either equivalent or a little snappier than
before. Also, the local state in LGraphNode has (almost) all been
removed in favor of reacting to the nodeData prop.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5709-Refactor-Let-LGraphNode-handle-more-events-itself-2756d73d365081e6a88ce6241bceecc0)
by [Unito](https://www.unito.io)

---------

Co-authored-by: GitHub Action <action@github.com>
2025-09-20 22:14:30 -07:00
Christian Byrne
295332dc46 update CODEOWNERS (#5667)
Add explicit CODEOWNERS for new features to allow more domain-driven
review/approval/ownership processes.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5667-update-CODEOWNERS-2736d73d3650817ea52be9c4a8fe5ff2)
by [Unito](https://www.unito.io)
2025-09-20 20:11:35 -07:00
Christian Byrne
5c498348b8 fix: update to standardized mobile web app meta tag syntax (#5672)
## Summary

Fixed WebKit deprecation warning by updating to standardized mobile web
app meta tag syntax.

## Changes

- **What**: Replaced deprecated `apple-mobile-web-app-capable` with
cross-platform
[`mobile-web-app-capable`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/meta/name#mobile-web-app-capable)
meta tag to align with WebKit's move toward vendor-neutral standards

## Review Focus

Verify "Add to Home Screen" functionality still works on iOS/iPadOS and
that the WebKit console warning is resolved in production builds.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5672-fix-update-to-standardized-mobile-web-app-meta-tag-syntax-2736d73d3650811cb2a1f0b14ce0a0e7)
by [Unito](https://www.unito.io)
2025-09-20 20:10:51 -07:00
Alexander Brown
8133bd4b7b Refactor: Composable disentangling (#5695)
## Summary

Prerequisite refactor/cleanup to use a global store instead of having
nodes throw up events to a parent component that stores a reference to a
singleton service that itself bootstraps and synchronizes with a
separate service to maintain a partially reactive but not fully reactive
set of states that describe some but not all aspects of the nodes on
either the litegraph, the vue side, or both.

## Changes

- **What**: Refactoring, the behavior should not change.
- **Dependencies**: A type utility to help with Vue component props

## Review Focus

Is there something about the current structure that this could affect
that would not be caught by our tests or using the application?

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5695-Refactor-Composable-disentangling-2746d73d365081e6938ce656932f3e36)
by [Unito](https://www.unito.io)
2025-09-20 13:06:42 -07:00
Arjan Singh
fd12591756 [feat] integrate asset browser with widget system (#5629)
## Summary

Add asset browser dialog integration for combo widgets with full
animation support and proper state management.

(Thank you Claude from saving me me from merge conflict hell on this
one.)

## Changes

- Widget integration: combo widgets now use AssetBrowserModal for
eligible asset types
- Dialog animations: added animateHide() for smooth close transitions
- Async operations: proper sequencing of widget updates and dialog
animations
- Service layer: added getAssetsForNodeType() and getAssetDetails()
methods
- Type safety: comprehensive TypeScript types and error handling
- Test coverage: unit tests for all new functionality
- Bonus: fixed the hardcoded labels in AssetFilterBar

Widget behavior:
- Shows asset browser button for eligible widgets when asset API enabled
- Handles asset selection with proper callback sequencing
- Maintains widget value updates and litegraph notification

## Review Focus

I will call out some stuff inline.

## Screenshots


https://github.com/user-attachments/assets/9d3a72cf-d2b0-445f-8022-4c49daa04637

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5629-feat-integrate-asset-browser-with-widget-system-2726d73d365081a9a98be9a2307aee0b)
by [Unito](https://www.unito.io)

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: GitHub Action <action@github.com>
2025-09-20 11:44:18 -07:00
Christian Byrne
b3c939ff15 fix: add Safari requestIdleCallback polyfill (#5664)
## Summary

Implemented cross-browser requestIdleCallback polyfill to fix Safari
crashes during graph initialization.

## Changes

- **What**: Added
[requestIdleCallback](https://developer.mozilla.org/en-US/docs/Web/API/Window/requestIdleCallback)
polyfill following [VS Code's
pattern](https://github.com/microsoft/vscode/blob/main/src/vs/base/common/async.ts)
with setTimeout fallback for Safari
- **Breaking**: None - maintains existing GraphView behavior

## Review Focus

Safari compatibility testing and timeout handling in the 15ms fallback
window. Verify that initialization tasks (keybindings, server config,
model loading) still execute properly on Safari iOS.

## References

- [VS Code async.ts
implementation](https://github.com/microsoft/vscode/blob/main/src/vs/base/common/async.ts)
- Source pattern for our polyfill
- [MDN
requestIdleCallback](https://developer.mozilla.org/en-US/docs/Web/API/Window/requestIdleCallback)
- Browser API documentation
- [Safari requestIdleCallback
support](https://caniuse.com/requestidlecallback) - Browser
compatibility table

Fixes CLOUD-FRONTEND-STAGING-N

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5664-fix-add-Safari-requestIdleCallback-polyfill-2736d73d365081cdbcf1fb816fe098d6)
by [Unito](https://www.unito.io)

Co-authored-by: Claude <noreply@anthropic.com>
2025-09-19 23:34:15 -07:00
Christian Byrne
0801778f60 feat: Add Vue node subgraph title button and fix subgraph navigation with vue nodes (#5572)
## Summary
- Adds subgraph title button to Vue node headers (matching LiteGraph
behavior)
- Fixes Vue node lifecycle issues during subgraph navigation and tab
switching
- Extracts reusable `useSubgraphNavigation` composable with
callback-based API
- Adds comprehensive tests for subgraph functionality
- Ensures proper graph context restoration during tab switches



https://github.com/user-attachments/assets/fd4ff16a-4071-4da6-903f-b2be8dd6e672



┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5572-feat-Add-Vue-node-subgraph-title-button-with-lifecycle-management-26f6d73d365081bfbd9cfd7d2775e1ef)
by [Unito](https://www.unito.io)

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: DrJKL <DrJKL@users.noreply.github.com>
2025-09-19 14:19:06 -07:00
Johnpaul Chiwetelu
8ffe63f54e Layoutstore Minimap calculation (#5547)
This pull request refactors the minimap rendering system to use a
unified, extensible data source abstraction for all minimap operations.
By introducing a data source interface and factory, the minimap can now
seamlessly support multiple sources of node layout (such as the
`LayoutStore` or the underlying `LiteGraph`), improving maintainability
and future extensibility. Rendering logic and change detection
throughout the minimap have been updated to use this new abstraction,
resulting in cleaner code and easier support for new data models.

**Core architecture improvements:**

* Introduced a new `IMinimapDataSource` interface and related data types
(`MinimapNodeData`, `MinimapLinkData`, `MinimapGroupData`) to
standardize node, link, and group data for minimap rendering.
* Added an abstract base class `AbstractMinimapDataSource` that provides
shared logic for bounds and group/link extraction, and implemented two
concrete data sources: `LiteGraphDataSource` (for classic graph data)
and `LayoutStoreDataSource` (for layout store data).
[[1]](diffhunk://#diff-ea46218fc9ffced84168a5ff975e4a30e43f7bf134ee8f02ed2eae66efbb729dR1-R95)
[[2]](diffhunk://#diff-9a6b7c6be25b4dbeb358fea18f3a21e78797058ccc86c818ed1e5f69c7355273R1-R30)
[[3]](diffhunk://#diff-f200ba9495a03157198abff808ed6c3761746071404a52adbad98f6a9d01249bR1-R42)
* Created a `MinimapDataSourceFactory` that selects the appropriate data
source based on the presence of layout store data, enabling seamless
switching between data models.

**Minimap rendering and logic refactoring:**

* Updated all minimap rendering functions (`renderGroups`,
`renderNodes`, `renderConnections`) and the main `renderMinimapToCanvas`
entry point to use the unified data source interface, significantly
simplifying the rendering code and decoupling it from the underlying
graph structure.
[[1]](diffhunk://#diff-3670f99330b2e24aca3cffeeac6600adf8abadd6dd585f596d60fde1dd093121L1-R11)
[[2]](diffhunk://#diff-3670f99330b2e24aca3cffeeac6600adf8abadd6dd585f596d60fde1dd093121R33-R75)
[[3]](diffhunk://#diff-3670f99330b2e24aca3cffeeac6600adf8abadd6dd585f596d60fde1dd093121L66-R124)
[[4]](diffhunk://#diff-3670f99330b2e24aca3cffeeac6600adf8abadd6dd585f596d60fde1dd093121L134-R161)
[[5]](diffhunk://#diff-3670f99330b2e24aca3cffeeac6600adf8abadd6dd585f596d60fde1dd093121L153-R187)
[[6]](diffhunk://#diff-3670f99330b2e24aca3cffeeac6600adf8abadd6dd585f596d60fde1dd093121L187-L188)
[[7]](diffhunk://#diff-3670f99330b2e24aca3cffeeac6600adf8abadd6dd585f596d60fde1dd093121R227-R231)
[[8]](diffhunk://#diff-3670f99330b2e24aca3cffeeac6600adf8abadd6dd585f596d60fde1dd093121L230-R248)
* Refactored minimap viewport and graph change detection logic to use
the data source abstraction for bounds, node, and link change detection,
and to respond to layout store version changes.
[[1]](diffhunk://#diff-d92e448dee5e30782a66b9e66d8c8b05626dffd0b2ff1032f2612b9a9b9c51f6L2-R10)
[[2]](diffhunk://#diff-d92e448dee5e30782a66b9e66d8c8b05626dffd0b2ff1032f2612b9a9b9c51f6R33-R35)
[[3]](diffhunk://#diff-d92e448dee5e30782a66b9e66d8c8b05626dffd0b2ff1032f2612b9a9b9c51f6L99-R141)
[[4]](diffhunk://#diff-d92e448dee5e30782a66b9e66d8c8b05626dffd0b2ff1032f2612b9a9b9c51f6R157-R160)
[[5]](diffhunk://#diff-338d14c67dabffaf6f68fbf09b16e8d67bead2b9df340e46601b2fbd57331521L8-R11)
[[6]](diffhunk://#diff-338d14c67dabffaf6f68fbf09b16e8d67bead2b9df340e46601b2fbd57331521L56-R64)

These changes make the minimap codebase more modular and robust, and lay
the groundwork for supporting additional node layout strategies in the
future.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5547-Layoutstore-Minimap-calculation-26e6d73d3650813e9457c051dff41ca1)
by [Unito](https://www.unito.io)
2025-09-19 13:52:57 -07:00
Benjamin Lu
893409dfc8 Add playwright tests for links and slots in vue nodes mode (#5668)
Tests added
- Should show a link dragging out from a slot when dragging on a slot
- Should create a link when dropping on a compatible slot
- Should not create a link when dropping on an incompatible slot(s)

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5668-Add-playwright-tests-for-links-and-slots-in-vue-nodes-mode-2736d73d36508188a47dceee5d1a11e5)
by [Unito](https://www.unito.io)

---------

Co-authored-by: github-actions <github-actions@github.com>
2025-09-19 13:51:47 -07:00
Christian Byrne
df2fda6077 [refactor] Replace manual semantic version utilities/functions with semver package (#5653)
## Summary
- Replace custom `compareVersions()` with `semver.compare()`
- Replace custom `isSemVer()` with `semver.valid()`  
- Remove deprecated version comparison functions from `formatUtil.ts`
- Update all version comparison logic across components and stores
- Fix tests to use semver mocking instead of formatUtil mocking

## Benefits
- **Industry standard**: Uses well-maintained, battle-tested `semver`
package
- **Better reliability**: Handles edge cases more robustly than custom
implementation
- **Consistent behavior**: All version comparisons now use the same
underlying logic
- **Type safety**: Better TypeScript support with proper semver types


Fixes #4787

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5653-refactor-Replace-manual-semantic-version-utilities-functions-with-semver-package-2736d73d365081fb8498ee11cbcc10e2)
by [Unito](https://www.unito.io)

---------

Co-authored-by: DrJKL <DrJKL@users.noreply.github.com>
Co-authored-by: Claude <noreply@anthropic.com>
2025-09-19 12:27:49 -07:00
Christian Byrne
4f5bbe0605 [refactor] Remove legacy manager UI support and tag from header (#5665)
## Summary

Removed the informational "Use Legacy UI" tag from the ManagerHeader
component while preserving all underlying legacy manager functionality.

## Changes

- **What**: Removed Tag component displaying legacy UI information from
ManagerHeader
- **Breaking**: None - all legacy manager functionality remains intact
- **Dependencies**: None

## Review Focus

Visual cleanup only - the `--enable-manager-legacy-ui` CLI flag and all
related functionality continues to work normally. Only the informational
UI tag has been removed from the header.
2025-09-19 02:07:51 -07:00
Christian Byrne
a975e50f1b [feat] Add tooltip support for Vue nodes (#5577)
## Summary

Added tooltip support for Vue node components using PrimeVue's v-tooltip
directive with proper data integration and container scoping.


https://github.com/user-attachments/assets/d1af31e6-ef6a-4df8-8de4-5098aa4490a1

## Changes

- **What**: Implemented tooltip functionality for Vue node headers,
input/output slots, and widgets using [PrimeVue
v-tooltip](https://primevue.org/tooltip/) directive
- **Dependencies**: Leverages existing PrimeVue tooltip system, no new
dependencies

## Review Focus

Container scoping implementation via provide/inject pattern for tooltip
positioning, proper TypeScript interfaces eliminating `as any` casts,
and integration with existing settings store for tooltip delays and
enable/disable functionality.

```mermaid
graph TD
    A[LGraphNode Container] --> B[provide tooltipContainer]
    B --> C[NodeHeader inject]
    B --> D[InputSlot inject]
    B --> E[OutputSlot inject]
    B --> F[NodeWidgets inject]

    G[useNodeTooltips composable] --> H[NodeDefStore lookup]
    G --> I[Settings integration]
    G --> J[i18n fallback]

    C --> G
    D --> G
    E --> G
    F --> G

    style A fill:#f9f9f9,stroke:#333,color:#000
    style G fill:#e8f4fd,stroke:#0066cc,color:#000
```

---------

Co-authored-by: DrJKL <DrJKL@users.noreply.github.com>
Co-authored-by: GitHub Action <action@github.com>
2025-09-19 01:07:50 -07:00
Christian Byrne
a17c74fa0c fix: add optional chaining to nodeDef access in NodeTooltip (#5663)
## Summary

Extension of https://github.com/Comfy-Org/ComfyUI_frontend/pull/5659:
Added optional chaining to NodeTooltip component to prevent TypeError
when `nodeDef` is undefined for unknown node types.

## Changes

- **What**: Added [optional chaining
operator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Optional_chaining)
(`?.`) to safely access `nodeDef` properties in NodeTooltip component

## Review Focus

Error handling for node types not found in nodeDefStore and tooltip
display behavior for unrecognized nodes.

Fixes CLOUD-FRONTEND-STAGING-3N

Co-authored-by: Claude <noreply@anthropic.com>
2025-09-19 01:04:12 -07:00
Christian Byrne
5e625a5002 [test] add Vue FormSelectButton widget component tests (#5576)
## Summary

Added comprehensive component tests for FormSelectButton widget with 497
test cases covering all interaction patterns and edge cases.

## Changes

- **What**: Created test suite for
[FormSelectButton.vue](https://vuejs.org/guide/scaling-up/testing.html)
component with full coverage of string/number/object options, PrimeVue
compatibility, disabled states, and visual styling
- **Dependencies**: No new dependencies (uses existing vitest,
@vue/test-utils)

## Review Focus

Test completeness covering edge cases like unicode characters, duplicate
values, and objects with missing properties. Verify test helper
functions correctly simulate user interactions.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5576-Add-Vue-FormSelectButton-widget-component-tests-26f6d73d36508171ae08ee74d0605db2)
by [Unito](https://www.unito.io)

---------

Co-authored-by: DrJKL <DrJKL@users.noreply.github.com>
2025-09-19 00:12:25 -07:00
Christian Byrne
002fac0232 [refactor] Migrate manager code to DDD structure (#5662)
## Summary

Reorganized custom nodes manager functionality from scattered technical
layers into a cohesive domain-focused module following [domain-driven
design](https://en.wikipedia.org/wiki/Domain-driven_design) principles.

## Changes

- **What**: Migrated all manager code from technical layers
(`src/components/`, `src/stores/`, etc.) to unified domain structure at
`src/workbench/extensions/manager/`
- **Breaking**: Import paths changed for all manager-related modules
(40+ files updated)

## Review Focus

Verify all import path updates are correct and no circular dependencies
introduced. Check that [Vue 3 composition
API](https://vuejs.org/guide/reusability/composables.html) patterns
remain consistent across relocated composables.


┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5662-refactor-Migrate-manager-code-to-DDD-structure-2736d73d3650812c87faf6ed0fffb196)
by [Unito](https://www.unito.io)
2025-09-19 00:03:05 -07:00
Christian Byrne
7e115543fa fix: prevent TypeError when nodeDef is undefined in NodeTooltip (#5659)
## Summary

Fix TypeError in NodeTooltip component when `nodeDef` is undefined. This
occurs when hovering over nodes whose type is not found in the
nodeDefStore.

## Changes

- Add optional chaining (`?.`) to `nodeDef.description` access on line
71
- Follows the same defensive pattern used in previous fixes for similar
issues

## Context

This addresses Sentry issue
[CLOUD-FRONTEND-STAGING-1B](https://comfy-org.sentry.io/issues/6829258525/)
which shows 19 occurrences affecting 14 users.

The fix follows the same pattern as previous commits:
-
[290bf52fc](290bf52fc5)
- Fixed similar issue on line 112
-
[e8997a765](e8997a7653)
- Fixed multiple similar issues


┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5659-fix-prevent-TypeError-when-nodeDef-is-undefined-in-NodeTooltip-2736d73d3650816e8be3f44889198b58)
by [Unito](https://www.unito.io)
2025-09-18 23:53:58 -07:00
Christian Byrne
80d75bb164 fix TypeError: nodes is not iterable when loading graph (#5660)
## Summary
- Fixes Sentry issue CLOUD-FRONTEND-STAGING-29 (TypeError: nodes is not
iterable)
- Adds defensive guard to check if nodes is valid array before iteration
- Gracefully handles malformed workflow data by skipping node processing

## Root Cause
The `collectMissingNodesAndModels` function in `src/scripts/app.ts:1135`
was attempting to iterate over `nodes` without checking if it was a
valid iterable, causing crashes when workflow data was malformed or
missing the nodes property.

## Fix
Added null/undefined/array validation before the for-loop:
```typescript
if (\!nodes || \!Array.isArray(nodes)) {
  console.warn('Workflow nodes data is missing or invalid, skipping node processing', { nodes, path })
  return
}
```

Fixes CLOUD-FRONTEND-STAGING-29

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5660-fix-TypeError-nodes-is-not-iterable-when-loading-graph-2736d73d365081cfb828d27e59a4811c)
by [Unito](https://www.unito.io)
2025-09-18 23:27:42 -07:00
snomiao
d59885839a fix: correct Claude PR review to use BASE_SHA for accurate diff comparison (#5654)
## Summary
- Fixes the Claude automated PR review comparing against wrong commits
- Updates the comprehensive-pr-review.md command to use `$BASE_SHA`
instead of `origin/$BASE_BRANCH`
- Resolves issue where Claude was reviewing unrelated changes from other
PRs

## Problem
As identified in #5651 (comment
https://github.com/Comfy-Org/ComfyUI_frontend/pull/5651#issuecomment-3310416767),
the Claude automated review was incorrectly analyzing changes that
weren't part of the PR being reviewed. The review was mentioning Turkish
language removal, linkRenderer changes, and other modifications that
weren't in the actual PR diff.

## Root Cause Analysis

### The Issue Explained (from Discord discussion)
When Christian Byrne noticed Claude was referencing things from previous
reviews on other PRs, we investigated and found:

1. **The backport branch was created from origin/main BEFORE Turkish
language support was merged**
   - Branch state: `main.A`
   - Backport changes committed: `main.A.Backport`

2. **Turkish language support was then merged into origin/main**
   - Main branch updated to: `main.A.Turkish`

3. **Claude review workflow checked out `main.A.Backport` and ran git
diff against `origin/main`**
   - This compared: `main.A.Backport <> main.A.Turkish`
   - The diff showed: `+++Backport` changes and `---Turkish` removal
   - Because the common parent of both branches was `main.A`

### Why This Happens
When using `origin/$BASE_BRANCH`, git resolves to the latest commit on
that branch. The diff includes:
1. The PR's actual changes (+++Backport)
2. The reverse of all commits merged to main since the PR was created
(---Turkish)

This causes Claude to review changes that appear as "removals" of code
from other merged PRs, leading to confusing comments about unrelated
code.

## Solution
Changed the git diff commands to use `$BASE_SHA` directly, which GitHub
Actions provides as the exact commit SHA that represents the merge base.
This ensures Claude only reviews the actual changes introduced by the
PR.

### Before (incorrect):
```bash
git diff --name-only "origin/$BASE_BRANCH"  # Compares against latest main
git diff "origin/$BASE_BRANCH"
git diff --name-status "origin/$BASE_BRANCH"
```

### After (correct):
```bash
git diff --name-only "$BASE_SHA"  # Compares against merge base
git diff "$BASE_SHA"
git diff --name-status "$BASE_SHA"
```

## Technical Details

### GitHub Actions Environment Variables
- `BASE_SHA`: The commit SHA of the merge base (where PR branched from
main)
- `BASE_BRANCH`: Not provided by GitHub Actions (this was the bug)
- Using `origin/$BASE_BRANCH` was falling back to comparing against the
latest main commit

### Alternative Approaches Considered
1. **Approach 1**: Rebase/update branch before running Claude review
   - Downside: Changes the PR's commits, not always desirable
2. **Approach 2**: Use BASE_SHA to diff against the merge base 
   - This is what GitHub's PR diff view does
   - Shows only the changes introduced by the PR

## Testing
The BASE_SHA environment variable is already correctly set in the
claude-pr-review.yml workflow (line 88), so this change will work
immediately once merged.

## Impact
- Claude reviews will now be accurate and only analyze the actual PR
changes
- No false positives about "removed" code from other PRs
- More reliable automated PR review process
- Developers won't be confused by comments about code they didn't change

## Verification
You can verify this fix by:
1. Creating a PR from an older branch
2. Merging another PR to main
3. Triggering Claude review with the label
4. Claude should only review the PR's changes, not show removals from
the newly merged commits

## Credits
Thanks to @Christian-Byrne for reporting the issue and @snomiao for the
root cause analysis.

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

Co-authored-by: Claude <noreply@anthropic.com>
2025-09-18 22:09:20 -07:00
snomiao
cbb0f765b8 feat: enable verbatimModuleSyntax in TypeScript config (#5533)
## Summary
- Enable `verbatimModuleSyntax` compiler option in TypeScript
configuration
- Update all type imports to use explicit `import type` syntax
- This change will Improve tree-shaking and bundler compatibility

## Motivation
The `verbatimModuleSyntax` option ensures that type-only imports are
explicitly marked with the `type` keyword. This:
- Makes import/export intentions clearer
- Improves tree-shaking by helping bundlers identify what can be safely
removed
- Ensures better compatibility with modern bundlers
- Follows TypeScript best practices for module syntax

## Changes
- Added `"verbatimModuleSyntax": true` to `tsconfig.json`
- Updated another 48+ files to use explicit `import type` syntax for
type-only imports
- No functional changes, only import/export syntax improvements

## Test Plan
- [x] TypeScript compilation passes
- [x] Build completes successfully  
- [x] Tests pass
- [ ] No runtime behavior changes

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

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5533-feat-enable-verbatimModuleSyntax-in-TypeScript-config-26d6d73d36508190b424ef9b379b5130)
by [Unito](https://www.unito.io)
2025-09-18 21:05:56 -07:00
Christian Byrne
726a2fbbc9 feat: add manual dispatch to backport workflow (#5651)
Enables manual backport triggering for scenarios where labels are added
after PR merge.

Adds workflow_dispatch trigger to the backport workflow with support
for:
- Specifying PR number to backport post-merge
- Force rerun option to override duplicate detection  
- Proper handling of multi-version backport scenarios

Solves the issue where adding version labels (e.g., 1.27) after a PR is
already merged and backported (e.g., to 1.26) would not trigger
additional backports.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5651-feat-add-manual-dispatch-to-backport-workflow-2736d73d365081b6ba00c7a43c9ba06b)
by [Unito](https://www.unito.io)
2025-09-18 21:01:07 -07:00
snomiao
553b5aa02b feat: Add Turkish language support (#5438)
## Summary
- Added complete Turkish language translation for ComfyUI Frontend
- Integrated Turkish locale into the i18n system
- Added Turkish as a selectable language option in settings

## Implementation Details
- Added Turkish translation files provided by @naxci1:
  - `src/locales/tr/main.json` - Main UI translations
  - `src/locales/tr/commands.json` - Command translations
  - `src/locales/tr/nodeDefs.json` - Node definitions translations
  - `src/locales/tr/settings.json` - Settings translations
- Updated `src/i18n.ts` to import and register Turkish locale
- Added Turkish option to language selector in
`src/constants/coreSettings.ts`

## Test Plan
- [ ] Verify Turkish translations load correctly
- [ ] Test language switching to/from Turkish
- [ ] Check all UI elements display properly in Turkish
- [ ] Verify node descriptions and tooltips in Turkish
- [ ] Test command palette in Turkish

Fixes #5437

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

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5438-feat-Add-Turkish-language-support-2686d73d36508184bbf2dc1e0cd15350)
by [Unito](https://www.unito.io)
2025-09-18 19:43:53 -07:00
Benjamin Lu
2ff0d951ed Slot functionality for vue nodes (#5628)
Allows for simple slot functionality in vue nodes mode.

Has:
- Drag new link from slot
- Connect new link from dropping on slot

Now:
- Tests

After:
- Drop on reroute
- Correct link color on connect
- Drop on node
- Hover effects

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5628-Slot-functionality-for-vue-nodes-2716d73d365081c59a3cef7c8a5e539e)
by [Unito](https://www.unito.io)

---------

Co-authored-by: bymyself <cbyrne@comfy.org>
Co-authored-by: AustinMroz <austin@comfy.org>
Co-authored-by: Claude <noreply@anthropic.com>
2025-09-18 19:35:15 -07:00
Christian Byrne
1f88925144 fix: don't immediately close missing nodes dialog if manager is disabled (#5647)
If manager is disabled, it assumed all missing nodes are installed and
immediately closes the missing nodes warning when loading a workflow
with missing nodes.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5647-fix-don-t-immediately-close-missing-nodes-dialog-if-manager-is-disabled-2736d73d36508199a50bca2026528ab6)
by [Unito](https://www.unito.io)
2025-09-18 17:54:37 -07:00
AustinMroz
250433a91a Fix SaveAs (#5643)
Implementing subgraph blueprints (#5139) included changes to saving to
ensure that SaveAs generates a new workflow of the correct type. However
this code failed to utilize the pre-prepared state when performing the
actual save. This produced a couple of problems with both failing to
detach the workflow and failing to apply the correct state

This error is only encountered when using Save As from a non temporary
workflow (one loaded from the workflows sidebar tab).

As this state calculation code is only used in this code path, it has
been moved into the saveAs function of the workflowStore.

Resolves #5592

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5643-Fix-SaveAs-2726d73d3650818faa7af449d1f13c26)
by [Unito](https://www.unito.io)
2025-09-18 16:56:49 -07:00
AustinMroz
eb664f47af Fix cyclic prototype errors with subgraphNodes (#5637)
#5024 added support for connecting primitive nodes to subgraph inputs.
To accomplish this, it pulls WidgetLocator information from the node
owning the widget.

This `node` property does not exist on all IBaseWidget. `toConcrete` was
used to instead have a BaseWidget which is guaranteed to have a node
property. The issue that was missed, is that a widget which lacks this
information (such as most implemented by custom nodes) sets the node
value to the argument which was passed. Here that is the reference to
the subgraph node. Sometimes, this `#setWidget` call is made multiple
times, and when this occurs, the `input.widget` has itself set as the
protoyep, throwing an error.

This is resolved by instead taking an additional input which is
unambiguous.

For reference, this is a near minimal workflow using comfy_mtb that
replicates the issue

[cyclic.json](https://github.com/user-attachments/files/22412187/cyclic.json)

Special thanks to @melMass for assistance discovering this issue.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5637-Fix-cyclic-prototype-errors-with-subgraphNodes-2726d73d365081fea356f5197e4c2b42)
by [Unito](https://www.unito.io)
2025-09-18 16:06:12 -07:00
Christian Byrne
bc85d4e87b Make Vue nodes read-only when in panning mode (#5574)
## Summary

Integrated Vue node components with canvas panning mode to prevent UI
interference during navigation.

## Changes

- **What**: Added
[canCapturePointerEvents](https://docs.comfy.org/guide/vue-nodes)
computed property to `useCanvasInteractions` composable that checks
canvas read-only state
- **What**: Modified Vue node components (LGraphNode, NodeWidgets) to
conditionally handle pointer events based on canvas navigation mode
- **What**: Updated node event handlers to respect panning mode and
forward events to canvas when appropriate

## Review Focus

Event forwarding logic in panning mode and pointer event capture state
management across Vue node hierarchy.

```mermaid
graph TD
    A[User Interaction] --> B{Canvas in Panning Mode?}
    B -->|Yes| C[Forward to Canvas]
    B -->|No| D[Handle in Vue Component]
    C --> E[Canvas Navigation]
    D --> F[Node Selection/Widget Interaction]

    G[canCapturePointerEvents] --> H{read_only === false}
    H -->|Yes| I[Allow Vue Events]
    H -->|No| J[Block Vue Events]

    style A fill:#f9f9f9,stroke:#333,color:#000
    style E fill:#f9f9f9,stroke:#333,color:#000
    style F fill:#f9f9f9,stroke:#333,color:#000
    style I fill:#e1f5fe,stroke:#01579b,color:#000
    style J fill:#ffebee,stroke:#c62828,color:#000
```

## Screenshots




https://github.com/user-attachments/assets/00dc5e4a-2b56-43be-b92e-eaf511e52542

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5574-Make-Vue-nodes-read-only-when-in-panning-mode-26f6d73d3650818c951cd82c8fe58972)
by [Unito](https://www.unito.io)

---------

Co-authored-by: GitHub Action <action@github.com>
2025-09-18 15:43:35 -07:00
Comfy Org PR Bot
7585444ce6 1.28.0 (#5640)
Minor version increment to 1.28.0

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5640-1-28-0-2726d73d3650818e846fcf78cbf33b73)
by [Unito](https://www.unito.io)

Co-authored-by: benceruleanlu <162923238+benceruleanlu@users.noreply.github.com>
2025-09-18 14:13:33 -07:00
Robin Huang
a886798a10 Explicitly add email scope for social auth login. (#5638)
## Summary

Some users were authenticating successfully but their email addresses
weren't being extracted from the Firebase token. This happened because
we weren't explicitly requesting the email scope during OAuth
authentication.
 
While Firebase's default configuration includes basic profile info, it
doesn't guarantee email access for all account types - particularly
Google Workspace accounts with restrictive policies or users with
privacy-conscious settings.

[Github
Scopes](https://docs.github.com/en/apps/oauth-apps/building-oauth-apps/scopes-for-oauth-apps)

## Changes

Adding email scope for Google + Github social OAuth.

## Review Focus
N/A

## Screenshots (if applicable)

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5638-Explicitly-add-email-scope-for-social-auth-login-2726d73d3650817ab356fc9c04f8641b)
by [Unito](https://www.unito.io)

---------

Co-authored-by: Alexander Brown <drjkl@comfy.org>
2025-09-18 14:09:16 -07:00
Christian Byrne
37975e4eac [test] Add component test for image compare widget (#5549)
## Summary

Added comprehensive component test suite for WidgetImageCompare widget
with 410 test assertions covering display, edge cases, and integration
scenarios.

## Changes

- **What**: Created [Vue Test Utils](https://vue-test-utils.vuejs.org/)
test suite for [WidgetImageCompare
component](src/renderer/extensions/vueNodes/widgets/components/WidgetImageCompare.vue)
using [Vitest](https://vitest.dev/) testing framework

## Review Focus

Test coverage completeness for string vs object value handling,
accessibility attribute propagation, and edge case robustness including
malformed URLs and empty states.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5549-test-Add-component-test-for-image-compare-widget-26e6d73d365081189fe0d010f87d1eec)
by [Unito](https://www.unito.io)

---------

Co-authored-by: DrJKL <DrJKL@users.noreply.github.com>
2025-09-18 13:44:21 -07:00
Jin Yi
a41b8a6d4f refactor: Change manager flag from --disable-manager to --enable-manager (#5635)
## Summary
- Updated frontend to align with backend changes in ComfyUI core PR
#7555
- Changed manager startup argument from `--disable-manager` (opt-out) to
`--enable-manager` (opt-in)
- Manager is now disabled by default unless explicitly enabled

## Changes
- Modified `useManagerState.ts` to check for `--enable-manager` flag
presence
- Inverted logic: manager is disabled when the flag is NOT present
- Updated all related tests to reflect the new opt-in behavior
- Fixed edge case where `systemStats` is null

## Related
- Backend PR: https://github.com/comfyanonymous/ComfyUI/pull/7555

## Test Plan
- [x] All unit tests pass
- [x] Verified manager state logic with different flag combinations
- [x] TypeScript type checking passes
- [x] Linting passes

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

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5635-refactor-Change-manager-flag-from-disable-manager-to-enable-manager-2726d73d36508153a88bd9f152132b2a)
by [Unito](https://www.unito.io)
2025-09-18 11:45:07 -07:00
Alexander Brown
b264685052 lint: add tsconfig for browser_tests, fix existing violations (#5633)
## Summary

See https://typescript-eslint.io/blog/project-service/ for context.
Creates a browser_tests specific tsconfig so that they can be linted.

Does not add a package.json script to do the linting yet, but `pnpm exec
eslint browser_tests` should work for now.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5633-lint-add-tsconfig-for-browser_tests-fix-existing-violations-2726d73d3650819d8ef2c4b0abc31e14)
by [Unito](https://www.unito.io)
2025-09-18 11:35:44 -07:00
Johnpaul Chiwetelu
78d0ea6fa5 LazyImage on Safari (#5626)
This pull request improves the lazy loading behavior and caching
strategy for images in the `LazyImage.vue` component. The most
significant changes are focused on optimizing image rendering and
resource management, as well as improving code clarity.

**Lazy loading behavior improvements:**

* Changed the `<img>` element to render only when `cachedSrc` is
available, ensuring that images are not displayed before they are ready.
* Updated watchers in `LazyImage.vue` to use clearer variable names
(`shouldLoadVal` instead of `shouldLoad`) for better readability and
maintainability.
[[1]](diffhunk://#diff-3a1bfa7eb8cb26b04bea73f7b4b4e3c01e9d20a7eba6c3738fb47f96da1a7c95L80-R81)
[[2]](diffhunk://#diff-3a1bfa7eb8cb26b04bea73f7b4b4e3c01e9d20a7eba6c3738fb47f96da1a7c95L96-R96)

**Caching strategy enhancement:**

* Modified the `fetch` call in `mediaCacheService.ts` to use `{ cache:
'force-cache' }`, which leverages the browser's cache more aggressively
when loading media, potentially improving performance and reducing
network requests.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5626-LazyImage-on-Safari-2716d73d365081eeb1d3c2a96be4d408)
by [Unito](https://www.unito.io)
2025-09-18 11:20:19 -07:00
Christian Byrne
ea4e57b602 Move VueFire persistence configuration to initialization (#5614)
Currently, we set persistence method in the auth store setup. This
creates pattern of using the default on init (indexed DB) up until the
firebase store is initialized and `setPersistence` is called. For
devices that don't support indexed DB or have the connection aggresively
terminated or cleared, like
[Safari](https://comfy-org.sentry.io/issues/6879071102/?project=4509681221369857&query=is%3Aunresolved&referrer=issue-stream),
this can create problems with maintaing auth persistence.

Fix by setting persistence method in the initialization in main.ts

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5614-Move-VueFire-persistence-configuration-to-initialization-2716d73d3650817480e0c8feb1f37b9a)
by [Unito](https://www.unito.io)

---------

Co-authored-by: Claude <noreply@anthropic.com>
2025-09-18 11:18:05 -07:00
Johnpaul Chiwetelu
4789d86fe8 Line Selection toolbox up with Vue Nodes (#5601)
This pull request improves the selection toolbox behavior during node
dragging by ensuring that it correctly responds to both LiteGraph and
Vue node drag events. The main changes introduce a reactive drag state
for Vue nodes in the layout store and update the selection toolbox
composable and Vue node component to use this state.

**Selection toolbox behavior improvements:**

* Added a helper function and separate watchers in
`useSelectionToolboxPosition.ts` to hide the selection toolbox when
either LiteGraph or Vue nodes are being dragged. This ensures consistent
UI feedback regardless of node type.
[[1]](diffhunk://#diff-57a51ac5e656e64ae7fd276d71b115058631621755de33b1eb8e8a4731d48713L171-R172)
[[2]](diffhunk://#diff-57a51ac5e656e64ae7fd276d71b115058631621755de33b1eb8e8a4731d48713R212-R224)

**Vue node drag state management:**

* Added a reactive `isDraggingVueNodes` property to the
`LayoutStoreImpl` class, along with getter and setter methods to manage
Vue node drag state. This allows other components to reactively track
when Vue nodes are being dragged.
[[1]](diffhunk://#diff-80d32fe0fb72730c16cf7259adef8b20732ff214df240b1d39ae516737beaf3bR133-R135)
[[2]](diffhunk://#diff-80d32fe0fb72730c16cf7259adef8b20732ff214df240b1d39ae516737beaf3bR354-R367)
* Updated `LGraphNode.vue` to set and clear the Vue node dragging state
in the layout store during pointer down and up events, ensuring the
selection toolbox is hidden while dragging Vue nodes.
[[1]](diffhunk://#diff-a7744614cf842e54416047326db79ad81f7c7ab7bfb66ae2b46f5c73ac7d47f2R357-R360)
[[2]](diffhunk://#diff-a7744614cf842e54416047326db79ad81f7c7ab7bfb66ae2b46f5c73ac7d47f2R376-R378)

**Dependency updates:**

* Imported the `layoutStore` in `LGraphNode.vue` to access the new drag
state management methods.
* Added missing `ref` import in `layoutStore.ts` to support the new
reactive property.



https://github.com/user-attachments/assets/d6e9c15e-63b5-4de2-9688-ebbc6a3be545

---------

Co-authored-by: GitHub Action <action@github.com>
2025-09-18 11:17:14 -07:00
filtered
09e7d1040e Add desktop dialogs framework (#5605)
### Summary

Adds desktop dialog framework with data-driven dialog definitions.

### Changes

- Data-driven dialog structure in `desktopDialogs.ts`
- Dynamic dialog view component with i18n support
- Button action types: openUrl, close, cancel
- Button severity levels for styling (primary, secondary, danger, warn)
- Fallback invalid dialog for error handling
- i18n collection script updated for dialog strings
2025-09-17 20:32:53 -07:00
Arjan Singh
dfa1cbba4f Asset Browser Modal Component (#5607)
* [ci] ignore playwright mcp directory

* [feat] add AssetBrowserModal

And all related sub components

* [feat] reactive filter functions

* [ci] clean up storybook config

* [feat] add sematic AssetCard

* [fix] i love lucide

* [fix] AssetCard layout issues

* [fix] add AssetBadge type

* [fix] simplify useAssetBrowser

* [fix] modal layout

* [fix] simplify useAssetBrowserDialog

* [fix] add tailwind back to storybook

* [fix] better reponsive layout

* [fix] missed i18n string

* [fix] missing i18n translations

* [fix] remove erroneous prevent on keyboard.space

* [feat] add asset metadata validation utilities

* [fix] remove erroneous test code

* [fix] remove forced min and max width on AssetCard

* [fix] import statement nits
2025-09-17 16:17:09 -07:00
Alexander Brown
08220d50d9 Lint: Turn on rules that should allow for verbatimModuleSyntax (#5616)
* lint: turn on type import rules setting up for verbatimModuleSyntax

* lint: --fix for type imports
2025-09-16 22:03:41 -07:00
Comfy Org PR Bot
045232a99b [release] Increment version to 1.27.5 (#5619)
Co-authored-by: benceruleanlu <162923238+benceruleanlu@users.noreply.github.com>
2025-09-16 20:41:55 -07:00
Benjamin Lu
fb07941700 Bump vite-plugin-dts (#5618) 2025-09-16 20:33:03 -07:00
Terry Jia
6866e1277a new design for left click and wheel (#5566)
* new design for left click and wheel

* update snap

* fix import

* fix test

* default value

* fix test

* Update test expectations [skip ci]

---------

Co-authored-by: github-actions <github-actions@github.com>
2025-09-16 20:11:17 -07:00
Benjamin Lu
ff5d0923ca Refactor vue slot tracking (#5463)
* add dom element resize observer registry for vue node components

* Update src/renderer/extensions/vueNodes/composables/useVueNodeResizeTracking.ts

Co-authored-by: AustinMroz <austin@comfy.org>

* refactor(vue-nodes): typed TransformState InjectionKey, safer ResizeObserver sizing, centralized slot tracking, and small readability updates

* chore: make TransformState interface non-exported to satisfy knip pre-push

* Revert "chore: make TransformState interface non-exported to satisfy knip pre-push"

This reverts commit 110ecf31da.

* Revert "refactor(vue-nodes): typed TransformState InjectionKey, safer ResizeObserver sizing, centralized slot tracking, and small readability updates"

This reverts commit 428752619c.

* [refactor] Improve resize tracking composable documentation and test utilities

- Rename parameters in useVueElementTracking for clarity (appIdentifier, trackingType)
- Add comprehensive docstring with examples to prevent DOM attribute confusion
- Extract mountLGraphNode test utility to eliminate repetitive mock setup
- Add technical implementation notes documenting optimization decisions

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

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

* remove typo comment

* convert to functional bounds collection

* remove inline import

* add interfaces for bounds mutations

* remove change log

* fix bounds collection when vue nodes turned off

* fix title offset on y

* move from resize observer to selection toolbox bounds

* refactor(vue-nodes): typed TransformState InjectionKey, safer ResizeObserver sizing, centralized slot tracking, and small readability updates

* Fix conversion

* Readd padding

* revert churn reducings from layoutStore.ts

* Rely on RO for resize, and batch

* Improve churn

* Cache canvas offset

* rename from measure

* remove unused

* address review comments

* Update legacy injection

* nit

* Split into store

* nit

* perf improvement

---------

Co-authored-by: bymyself <cbyrne@comfy.org>
Co-authored-by: AustinMroz <austin@comfy.org>
Co-authored-by: Claude <noreply@anthropic.com>
2025-09-16 19:28:04 -07:00
Alexander Brown
6b59f839e0 Devex: Faster linting (#5611)
* devex: Keep the presubmit from wiping the lint cache

* devex: typescript for eslint config

* devex: upgrade lint plugins and dedupe lockfile

* lint: Fix autofixable rules with updated vue linter

* lint: Remove default for required prop

* lint: temporarily disable warnings for missing defaults

* deps: Update vue-tsc

* lint: use the config convenience utility, switch to using projectService

* lint: Fix redundant eslint config blocks and misplaced parser

* lint: Split up parsing options for typescript vs vue files
2025-09-16 19:23:03 -07:00
AustinMroz
6a01b08ebf Subgraph widget promotion - Part 1 (#5537)
* Prerequisite tweaks for subgraph widget promotion

* Clean up DOMWidget tracking on graph change

* Mark migrated CombOWidget functions private

* Cleanup placeholder node cast
2025-09-16 19:17:35 -07:00
Benjamin Lu
ede43c5e5c Add --no-git-checks and ignore playwright (#5602) 2025-09-16 19:00:00 -07:00
Christian Byrne
0483630f82 Show sampling previews on Vue nodes (#5579)
* refactor: simplify preview state provider

- Remove unnecessary event listeners and manual syncing
- Use computed() to directly reference app.nodePreviewImages
- Eliminate data duplication and any types
- Rely on Vue's reactivity for automatic updates
- Follow established patterns from execution state provider

* feat: optimize Vue node preview image display with reactive store

- Move preview display logic from inline ternaries to computed properties
- Add useNodePreviewState composable for preview state management
- Implement reactive store approach using Pinia storeToRefs
- Use VueUse useTimeoutFn for modern timeout management instead of window.setTimeout
- Add v-memo optimization for preview image template rendering
- Maintain proper sync between app.nodePreviewImages and reactive store state

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

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

* fix: update props usage for Vue 3.5 destructured props syntax

* [refactor] improve code style and architecture based on review feedback

- Replace inject pattern with direct store access in useNodePreviewState
- Use optional chaining for more concise conditional checks
- Use modern Array.at(-1) for accessing last element
- Remove provide/inject for nodePreviewImages in favor of direct store refs
- Update preview image styling: remove rounded borders, use flexible height
- Simplify scheduleRevoke function with optional chaining

Co-authored-by: DrJKL <DrJKL@users.noreply.github.com>

* [cleanup] remove unused NodePreviewImagesKey injection key

Addresses knip unused export warning after switching from provide/inject
to direct store access pattern.

* [test] add mock for useNodePreviewState in LGraphNode test

Fixes test failure after adding preview functionality to LGraphNode component.

* [fix] update workflowStore import path after rebase

Updates import to new location: @/platform/workflow/management/stores/workflowStore

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: DrJKL <DrJKL@users.noreply.github.com>
2025-09-16 17:34:04 -07:00
Alexander Brown
15cffe9d9e style: Flip the shade direction for charoal to go from light to dark (#5609) 2025-09-16 16:31:13 -07:00
Christian Byrne
f5b949762d [test] Add Vue node markdown widget component test (#5575)
* add markdown widget test

* [fix] correct test comments from 'is exposed' to 'is not exposed' - addresses review feedback

The TypeScript suppression comments incorrectly stated that properties
were exposed when they should indicate they are not exposed, since
@ts-expect-error is used to access private properties.

Co-authored-by: christian-byrne <christian-byrne@users.noreply.github.com>

---------

Co-authored-by: christian-byrne <christian-byrne@users.noreply.github.com>
2025-09-16 16:06:33 -07:00
Christian Byrne
6786d8e4fb Add hook to expose uid (#5612)
* add hook to expose uid

* use whenever for cleaner code
2025-09-16 14:42:40 -07:00
Christian Byrne
71ca28a46f [test] Add component test for TreeSelect Vue widget (#5551)
* add tree select widget component test

* [refactor] export TreeNode type from component - addresses review feedback

Co-authored-by: DrJKL <DrJKL@users.noreply.github.com>

* [refactor] move createTreeData to module scope - addresses review feedback

Co-authored-by: DrJKL <DrJKL@users.noreply.github.com>

---------

Co-authored-by: DrJKL <DrJKL@users.noreply.github.com>
2025-09-16 14:40:02 -07:00
filtered
4ff18fd7f0 Add Inter font used by Figma designs (#5535)
* Add ABC ROM fonts from Comfy.org

* Import ABC ROM fonts CSS in main.ts

* Move font import to style.css

* Add ABC ROM fonts as CSS variables in @theme

* Add Inter font .woff2 files

* Replace ABC ROM with Inter font declarations

* Update CSS variables to use Inter font

* Remove unused ABC ROM font files

* Autoformat style.css

* Remove redundant font declarations
2025-09-16 10:31:36 -07:00
filtered
45a46be513 Remove redundant output from prettier format script (#5603)
* Add --list-changed to prettier format scripts

* Remove --list-changed from check commands

* Fix cursor tab complete emits woe
2025-09-16 09:15:44 -07:00
Comfy Org PR Bot
631746939a [chore] Update electron-types to 0.4.73-0 (#5606)
Co-authored-by: webfiltered <176114999+webfiltered@users.noreply.github.com>
2025-09-16 00:56:29 -07:00
Alexander Brown
4f8e820c51 feat: Let mode changes trigger a re-render for Vue nodes (#5599)
* feat: Let mode changes trigger a re-render for Vue nodes

* Oops!
2025-09-15 18:25:58 -07:00
filtered
b8d8193a38 Copy startup terminal (#5585)
* Add copyTerminal translation key

* Add copy terminal button with select all functionality

* Remove copy button from error view button group

* Add hover-based copy button overlay to terminal

* Fix clipboard copy implementation in BaseTerminal

* Add 'Copy all' tooltip to terminal copy button

* Fix copy button to be away from right hand side

* Update copy button to respect existing selection

- Copy only selected text if any exists
- Copy all text and clear selection if nothing selected
- Update tooltip to reflect new behavior

* Add dynamic tooltip showing actual copy action

- Show 'Copy selection' when text is selected
- Show 'Copy all' when no text is selected

* Remove redundant i18n

* Fix aria-label to use dynamic tooltip text

* Remove debug console.error statements from useTerminal

Clean up debug logging added during development:
- Remove selection change debug logging
- Remove focus state debug logging
- Remove keyboard event debug logging
- Remove copy/paste debug logging

* Remove redundant keyboard handling from useTerminal

The rebase commit already fixed basic copy/paste.
Removed only the complex keyboard event handling that
duplicates the rebase fix. Kept the valuable UI features:
- Hover copy button overlay
- Right-click context menu

* Use Tailwind transition classes instead of custom CSS

Replace custom .animate-fade-in with standard Tailwind
transition-opacity duration-200 classes

* Use VueUse useElementHover for robust hover handling

Replace manual mouseenter/mouseleave events with VueUse
useElementHover composable which properly handles all
edge cases including mouseout and interrupted events

* Move tooltip to left of button

Relieves squished tooltip

* Simplify code

* Fix listener lifecycle management

Consolidate setup into single onMounted block instead
of creating unnecessary duplicate lifecycle hooks

* Replace any type with proper IDisposable type

* Refactor copy logic for clarity

* Use v-show for proper opacity transitions

* Prefer optional chaining

* Use useEventListener for context menu

* Remove redundant opacity classes

* Add BaseTerminal component tests

* Use pointer-events for button interactivity

* Update tests for pointer-events button behavior

* Fix clipboard mock in tests

* Fix test expectations for opacity classes

* Simplify hover tests for button state

* Remove low-value 'renders terminal container' test

* Remove non-functional 'button responds to hover' test

* Remove implementation detail test for dispose listener

* Remove redundant 'tracks selection changes' test

* Remove obvious comments from test file

* Use cn() utility for conditional classes

* Update tests-ui/tests/components/bottomPanel/tabs/terminal/BaseTerminal.spec.ts

Co-authored-by: Alexander Brown <drjkl@comfy.org>

* [auto-fix] Apply ESLint and Prettier fixes

* Remove 'any' types from wrapper and terminalMock variables

Add assertion to verify onSelectionChange was called

* Move mountBaseTerminal factory to module scope

* Rename test file

- Current consensus is .test.ts for component files

* Update src/components/bottomPanel/tabs/terminal/BaseTerminal.vue

* nit

---------

Co-authored-by: Alexander Brown <drjkl@comfy.org>
Co-authored-by: GitHub Action <action@github.com>
2025-09-15 17:06:41 -07:00
AustinMroz
2b57291756 Fix context menu creating nodes in wrong position (#5595)
* Fix context menu creating nodes in wrong position

When nodes are created from the context menu, they previously had there
position set immediately after the node itself was created. Under some
circumstances, this new position would be overwritten by the layout
store.

This is solved by setting the position before node initialization.

* nit: Move size fix to named variable

Also remove ternary. The elements are always numberic, so checking if a
number is truthy before multiplying by 0 is a little silly.

* nit: Further variable extraction
2025-09-15 14:49:10 -07:00
Arjan Singh
62897c669b [fix] add focus state and aria to select components (#5596)
* [fix] add focus state and aria to select components

* [fix] typo in gitignore

* [fix] code review feedback
2025-09-15 21:48:52 +00:00
Benjamin Lu
9918914b9d Add publish frontend types workflow (#5495)
* Add workflow

* Add concurrency

* Add warning

* Use publish-frontend-types for release.yaml

* address claude review

* nit
2025-09-15 14:47:29 -07:00
Arjan Singh
e601bcb300 [refactor] create src/platform/assets (#5598)
* [refactor] create src/platform/assets

Per @christian-byrne's feedback. Just bringing this into the repo sooner to clean up from my feature branch

* [fix] code review feedback
2025-09-15 14:36:33 -07:00
Comfy Org PR Bot
853038859b [release] Increment version to 1.27.4 (#5594)
Co-authored-by: benceruleanlu <162923238+benceruleanlu@users.noreply.github.com>
2025-09-15 11:53:28 -07:00
Alexander Brown
186e065528 Feat: Bypass styling for Vue nodes (#5593)
* cleanup: Extract the logic for outline/border colors

* feat: Add coloring for bypass style
2025-09-15 10:30:12 -07:00
Christian Byrne
237e807fc9 [refactor] Add DDD refactor commits to git-blame-ignore-revs (#5591)
* [refactor] Add DDD refactor commits to git-blame-ignore-revs

Add recent domain-driven design refactor commits to .git-blame-ignore-revs
to improve git blame output by excluding large structural reorganizations.

Added commits:
- 6349ceee6 [refactor] Improve renderer domain organization (#5552)
- 4c8c4a1ad [refactor] Improve settings domain organization (#5550)
- ca312fd1e [refactor] Improve workflow domain organization (#5584)
- e3bb29ceb [refactor] Move thumbnail functionality to renderer/core domain (#5586)
- 27ab355f9 [refactor] Improve updates/notifications domain organization (#5590)

This allows git blame to focus on actual logic changes rather than file moves.

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

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

* Fix: Use full 40-character SHAs in git-blame-ignore-revs

GitHub requires full commit SHAs to properly recognize and link to commits.
Shortened SHAs appear as invalid references in the GitHub UI.

---------

Co-authored-by: Claude <noreply@anthropic.com>
2025-09-15 04:54:27 -07:00
Christian Byrne
27ab355f9c [refactor] Improve updates/notifications domain organization (#5590)
* [refactor] Move update-related functionality to platform/updates domain

Reorganizes release management, version compatibility, and notification functionality
following Domain-Driven Design principles, mirroring VSCode's architecture pattern.

- Move releaseService.ts to platform/updates/common/
- Move releaseStore.ts to platform/updates/common/
- Move versionCompatibilityStore.ts to platform/updates/common/
- Move useFrontendVersionMismatchWarning.ts to platform/updates/common/
- Move toastStore.ts to platform/updates/common/
- Move ReleaseNotificationToast.vue to platform/updates/components/
- Move WhatsNewPopup.vue to platform/updates/components/
- Update 25+ import paths across codebase and tests

This creates a cohesive "updates" domain containing all functionality related to
software updates, version checking, release notifications, and user communication
about application state changes.

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

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

* fix imports

---------

Co-authored-by: Claude <noreply@anthropic.com>
2025-09-15 04:34:37 -07:00
Christian Byrne
4c8c4a1ad4 [refactor] Improve settings domain organization (#5550)
* refactor: move settingStore to platform/settings

Move src/stores/settingStore.ts to src/platform/settings/settingStore.ts
to separate platform infrastructure from domain logic following DDD principles.

Updates all import references across ~70 files to maintain compatibility.

* fix: update remaining settingStore imports after rebase

* fix: complete remaining settingStore import updates

* fix: update vi.mock paths for settingStore in tests

Update all test files to mock the new settingStore location at
@/platform/settings/settingStore instead of @/stores/settingStore

* fix: resolve remaining settingStore imports and unused imports after rebase

* fix: update settingStore mock path in SelectionToolbox test

Fix vi.mock path from @/stores/settingStore to @/platform/settings/settingStore
to resolve failing Load3D viewer button test.

* refactor: complete comprehensive settings migration to platform layer

This commit completes the migration of all settings-related code to the platform layer
as part of the Domain-Driven Design (DDD) architecture refactoring.

- constants/coreSettings.ts → platform/settings/constants/coreSettings.ts
- types/settingTypes.ts → platform/settings/types.ts
- stores/settingStore.ts → platform/settings/settingStore.ts (already moved)

- composables/setting/useSettingUI.ts → platform/settings/composables/useSettingUI.ts
- composables/setting/useSettingSearch.ts → platform/settings/composables/useSettingSearch.ts
- composables/useLitegraphSettings.ts → platform/settings/composables/useLitegraphSettings.ts

- components/dialog/content/SettingDialogContent.vue → platform/settings/components/SettingDialogContent.vue
- components/dialog/content/setting/SettingItem.vue → platform/settings/components/SettingItem.vue
- components/dialog/content/setting/SettingGroup.vue → platform/settings/components/SettingGroup.vue
- components/dialog/content/setting/SettingsPanel.vue → platform/settings/components/SettingsPanel.vue
- components/dialog/content/setting/ColorPaletteMessage.vue → platform/settings/components/ColorPaletteMessage.vue
- components/dialog/content/setting/ExtensionPanel.vue → platform/settings/components/ExtensionPanel.vue
- components/dialog/content/setting/ServerConfigPanel.vue → platform/settings/components/ServerConfigPanel.vue

- ~100+ import statements updated across the codebase
- Test file imports corrected
- Component imports fixed in dialog service and command menubar
- Composable imports updated in GraphCanvas.vue

```
src/platform/settings/
├── components/          # All settings UI components
├── composables/         # Settings-related composables
├── constants/          # Core settings definitions
├── types.ts           # Settings type definitions
└── settingStore.ts    # Central settings state management
```

 TypeScript compilation successful
 All tests passing (settings store, search functionality, UI components)
 Production build successful
 Domain boundaries properly established

This migration consolidates all settings functionality into a cohesive platform domain,
improving maintainability and following DDD principles for better code organization.

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

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

* fix: format and lint after rebase conflict resolution

* fix: update remaining import paths to platform settings

- Fix browser test import: extensionAPI.spec.ts
- Fix script import: collect-i18n-general.ts
- Complete settings migration import path updates

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

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

---------

Co-authored-by: Claude <noreply@anthropic.com>
2025-09-15 03:53:08 -07:00
Christian Byrne
e3bb29ceb8 [refactor] Move thumbnail functionality to renderer/core domain (#5586)
Move thumbnail functionality from src/renderer/thumbnail/ to src/renderer/core/thumbnail/
to align with domain-driven design architecture. Thumbnail generation is core rendering
infrastructure and belongs alongside other core renderer utilities.

Changes:
- Move useWorkflowThumbnail.ts and graphThumbnailRenderer.ts to renderer/core/thumbnail/
- Update all import paths in consuming files
- Fix relative imports within moved files

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

Co-authored-by: Claude <noreply@anthropic.com>
2025-09-15 02:58:35 -07:00
Christian Byrne
ca312fd1ea [refactor] Improve workflow domain organization (#5584)
* [refactor] move workflow domain to its own folder

* [refactor] Fix workflow platform architecture organization

- Move workflow rendering functionality to renderer/thumbnail domain
- Rename ui folder to management for better semantic clarity
- Update all import paths to reflect proper domain boundaries
- Fix test imports to use new structure

Architecture improvements:
- rendering → renderer/thumbnail (belongs with other rendering logic)
- ui → management (better name for state management and UI integration)

This ensures proper separation of concerns and domain boundaries.

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

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

* [fix] Resolve circular dependency between nodeDefStore and subgraphStore

* [fix] Update browser test imports to use new workflow platform paths

---------

Co-authored-by: Claude <noreply@anthropic.com>
2025-09-15 02:22:37 -07:00
filtered
bbff9c8217 Desktop: Add terminal context menu (text) (#5563)
* Add text context menu to terminal on right-click

Enable single right-click context menu in BaseTerminal for desktop app users.  Previously required two right clicks.

* Use useEventListener for context menu

* Fix overzealous tab-complete

* nit
2025-09-15 01:10:02 -07:00
Christian Byrne
6349ceee6c [refactor] Improve renderer domain organization (#5552)
* [refactor] Improve renderer architecture organization

Building on PR #5388, this refines the renderer domain structure:

**Key improvements:**
- Group all transform utilities in `transform/` subdirectory for better cohesion
- Move canvas state to dedicated `renderer/core/canvas/` domain
- Consolidate coordinate system logic (TransformPane, useTransformState, sync utilities)

**File organization:**
- `renderer/core/canvas/canvasStore.ts` (was `stores/graphStore.ts`)
- `renderer/core/layout/transform/` contains all coordinate system utilities
- Transform sync utilities co-located with core transform logic

This creates clearer domain boundaries and groups related functionality
while building on the foundation established in PR #5388.

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

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

* fix: Clean up linter-modified files

* Fix import paths and clean up unused imports after rebase

- Update all remaining @/stores/graphStore references to @/renderer/core/canvas/canvasStore
- Remove unused imports from selection toolbox components
- All tests pass, only reka-ui upstream issue remains in typecheck

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

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

* [auto-fix] Apply ESLint and Prettier fixes

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: GitHub Action <action@github.com>
2025-09-14 21:28:08 -07:00
Christian Byrne
8c6ee026c0 fix: Set Vue node initial size in layout store instead of CSS (#5571)
* fix: Vue nodes now respect deserialized width from LiteGraph

* fix: Set Vue node initial size in layout store instead of CSS

Vue nodes now properly set their initial size in the layout store using
the resize() function from useNodeLayout on component mount. This ensures
the layout store is the single source of truth for sizing, preventing
conflicts with the ResizeObserver that was overriding CSS-based sizing.

This resolves the issue where Vue nodes would shrink to minimal size
after user interaction due to ResizeObserver feedback loops.

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

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

* fix: Remove duplicate onMounted call in LGraphNode.vue

---------

Co-authored-by: Claude <noreply@anthropic.com>
2025-09-14 20:46:43 -07:00
filtered
668500cfa5 Fix light_theme changes default node background (#5562)
* Fix light_theme changes default node background

This is an issue where the background of nodes which have no background set were being lightened by this switch, when they should be skipped.

Went unnoticed because the only theme using this was the built-in light theme, which used white for node backgrounds anyway.

* Fix bypassed nodes

* nit

* Revert "nit"

This reverts commit e22f03a0e9.

* Revert "Fix bypassed nodes"

This reverts commit 6121634c09.

* Revert "Fix light_theme changes default node background"

This reverts commit 3206973e5a.

* Fix opacity not rendered to default nodes

Also causes bypassed nodes in light mode to once again render in light pink (again).

Not sure when this regression occurred.

* Revert "Fix opacity not rendered to default nodes"

This reverts commit da65a1dbaf.

* Fix backgrounds not adjusting for light mode
2025-09-14 19:35:59 -07:00
Christian Byrne
07fbe7267e Show error state on Vue nodes that caused execution errors (#5541)
* add missing node error border

* update vue node data after configure

* provide locatorId of execution error node to vue nodes

* [refactor] use execution store directly instead of provide/inject pattern

- Add lastExecutionErrorNodeId computed property to execution store
- Replace inject() with useExecutionStore() in LGraphNode component
- Remove useExecutionErrorProvider composable and provider call
- Clean up unused ExecutionErrorNodeIdKey injection key
- Add explicit return type annotation to hasAnyError computed

Addresses @DrJKL's architecture feedback and type safety suggestions.

* simplify error styling to match main branch conventions

Remove redundant dark-theme prefixes from border-error and outline-error
classes since these CSS custom properties handle both themes automatically.

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

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

* address review feedback on hasAnyError computed function

- Add explicit boolean return type
- Destructure props with defaults for cleaner code
- Use \!\! for proper boolean conversion to satisfy TypeScript

Addresses @DrJKL review comment on error state computation.

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

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

* destructure props at top level as suggested in review

Replace `props.nodeData` and `props.error` references with destructured
variables for cleaner code and proper defaults.

Addresses @DrJKL review comment about props destructuring.

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

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

* fix rebase issues: correct node ID comparison and border colors

- Use lastExecutionErrorNodeId instead of lastExecutionErrorNodeLocatorId
  for proper comparison with nodeData.id (both are local node IDs)
- Restore border-blue-100 colors that were incorrectly changed during rebase
- These were unrelated changes that snuck in during conflict resolution

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

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

* remove unused lastExecutionErrorNodeLocatorId from exports

The computed property is defined but not used by any external modules.
Only lastExecutionErrorNodeId is actually consumed by components.

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

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

---------

Co-authored-by: Claude <noreply@anthropic.com>
2025-09-14 19:27:15 -07:00
Christian Byrne
fe09f88ea3 [refactor] deduplicate download functionality in useImageMenuOptions (#5569)
Replace duplicated download implementation in saveImage function with
the existing downloadFile utility from downloadUtil. This removes 18
lines of duplicate code while maintaining identical functionality.

Changes:
- Import downloadFile from @/base/common/downloadUtil
- Replace manual anchor element creation with downloadFile call
- Maintain same URL preprocessing (removing preview parameter)
- Keep existing error handling

The downloadFile utility already includes comprehensive test coverage
and handles filename extraction, DOM manipulation, and cleanup.
2025-09-14 18:26:41 -07:00
Christian Byrne
96a663704f fix prop bindings on Vue nodes' gallery widgets (#5542)
* fix gallery widget navigators binding

* [refactor] improve test structure and type organization - addresses @DrJKL's review feedback

- Move types comment to reference component file (types already exist there)
- Extract helper functions outside describe blocks as pure functions
- Convert to function declarations for better clarity
- Fix 0-indexed vs 1-indexed consistency in test data generation
- Add placeholder for future test constants organization

* [test] Add WidgetTextarea component test with improved structure - addresses @DrJKL's review feedback

- Move helper functions outside describe blocks as pure function declarations
- Fix options type in createMockWidget to use SimplifiedWidget['options'] instead of Partial<TextareaProps>
- Replace emitted\![0] with safer emitted?.[0] optional chaining pattern
- Add comprehensive test coverage for textarea value binding, events, readonly mode, and edge cases

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

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

* Update src/renderer/extensions/vueNodes/widgets/components/WidgetGalleria.vue

Co-authored-by: Alexander Brown <drjkl@comfy.org>

* [refactor] export types from component and use 0-indexed numbering - addresses @DrJKL's review feedback

- Export GalleryImage and GalleryValue types from WidgetGalleria.vue component
- Import types in test file instead of redefining them locally
- Change image alt text from 1-indexed to 0-indexed (Image 0, Image 1, etc.)
- Move helper functions outside describe blocks using function declarations
- Add readonly test data constants for better test isolation
- Fix type compatibility issues with readonly arrays

This addresses DrJKL's review comments about:
1. Types being defined in test suite instead of component
2. Helper functions placement and clarity
3. 1-indexed numbering preference
4. Better test organization with readonly constants

* [refactor] implement remaining review feedback - addresses accessibility and factory pattern suggestions

- Fix accessibility: Add descriptive alt text with position context for screen readers
  instead of repetitive "Gallery image" (e.g., "Gallery image 2 of 5")
- Implement factory pattern: Add createGalleriaWrapper() function that takes images,
  creates widget internally, and returns wrapper for cleaner test code
- Update tests to use factory pattern for readonly constant test cases
- Add proper i18n translations for galleryImage and galleryThumbnail
- Use more descriptive alt text following WebAIM accessibility guidelines

Addresses review comments about screen reader accessibility and test factory pattern

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Alexander Brown <drjkl@comfy.org>
2025-09-14 16:31:56 -07:00
Terry Jia
11cb525545 Settings radio (#5536)
* add radio support for setting

* remove wrong code

* add tests
2025-09-14 14:21:42 -07:00
filtered
4eb13ca17c Remove duplicate Manager key from i18n (#5560) 2025-09-14 14:07:25 -07:00
filtered
6017fc43a6 Fix copy & paste is not possible from desktop app start (#5559)
- Fixes the inability to copy & paste from server start terminal
- Allows users to copy & paste error messages into e.g. google, support issues
2025-09-14 12:59:44 -07:00
Terry Jia
6e2d86520b add sortOrder for setting item (#5534)
* add priority for setting item

* keep original order

* remove comment
2025-09-14 00:40:44 -07:00
Christian Byrne
6e2a3a0d07 [test] Add component test for SelectButton Vue widget (#5530)
* add component test for select button

* [refactor] improve test structure and typing - addresses @DrJKL review comments

- Use proper SimplifiedWidget['options'] type instead of loose object type
- Extract helper functions as module-level function declarations for better organization
- Remove type assertion violation by using proper union type for null/undefined values
- Format code with prettier to maintain consistency

* [refactor] use safer optional chaining in test assertions - addresses @DrJKL's safety preference

Replace emitted\![0] with emitted?.[0] for safer array access in test expectations.
This follows the same pattern as applied to the textarea widget tests for consistency.
2025-09-14 00:20:56 -07:00
Christian Byrne
c7325c4da9 fix: File Upload widget disabled prop treats undefined as true (#5528)
* fix file upload widget disabled prop

* [test] extract createMockWidget to shared test utility - addresses @DrJKL's code replication concern

Creates testUtils.ts with shared createMockWidget and createMockFile functions
to reduce duplication across widget component tests. This ensures consistency
and maintainability of test setup code.

* [test] replace type assertions with type narrowing - addresses @DrJKL's type safety suggestion

Replaces unsafe `as HTMLInputElement` casts with proper instanceof checks
and error throwing. Also refactors File Type Detection tests to use it.for
instead of conditionals to eliminate anti-pattern.

* [feat] use destructuring with default value for readonly prop - addresses @DrJKL's Vue best practice suggestion

Replace manual fallback expressions like `readonly || false` with modern Vue 3
destructuring pattern: `const { readonly = false } = defineProps()`.
This is cleaner than withDefaults() and follows current Vue best practices.

* [test] improve test utilities usage - addresses @DrJKL's additional suggestions

- Replace findComponent with getComponent for better error handling
- Use optional chaining (?.()) instead of conditional checks for cleaner syntax
- Remove unnecessary existence checks since getComponent throws on failure
2025-09-14 00:20:30 -07:00
Christian Byrne
d146a7896a Add progress bars on Vue Nodes (#5469)
* track execution progress in vue nodes

* add test

* remove pointless execution state test

The test was mocking everything including the provide/inject mechanism,
so it wasn't testing real behavior. The execution progress feature
works correctly as verified through manual testing.

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

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

* remove accidentally committed PR_TEMPLATE.md

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

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

* [refactor] address PR review feedback from @DrJKL

- Replace hardcoded #0B8CE9 color with blue-100 class for consistency
- Replace magic number 56px with top-14 class for progress bar positioning
- Use storeToRefs() for better Pinia reactivity
- Reduce heavy commenting per maintainer preference

* fix: update LGraphNode test to mock useNodeExecutionState properly

The test was failing because it passed executing as a prop, but the component
uses the useNodeExecutionState composable. Added proper mock for the composable
to test the animate-pulse class application during execution.

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

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

---------

Co-authored-by: Claude <noreply@anthropic.com>
2025-09-14 00:19:13 -07:00
Christian Byrne
6b166a9d2f Fix Vue node selection detection in browser tests (#5555)
The VueNodeHelpers was using incorrect CSS selector for detecting selected nodes.
Vue nodes use outline-black/outline-white classes for selection state, not border-blue-500.

This fixes the failing delete key interaction tests that were showing 0 selected nodes
when they should have been detecting the actual selection state.

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

Co-authored-by: Claude <noreply@anthropic.com>
2025-09-13 23:40:27 -07:00
Christian Byrne
c145fd9df1 Show node output images on Vue Nodes (#5456)
* add image outputs on Vue nodes

* add unit tests and update cursor pointer

* use testing pinia

* properly mock i18n in component test

* get node via current graph

* use subgraph ID from node creation

* add better error handling for downloadFile util

* refactor: simplify image preview component architecture

- Replace awkward composable pattern with standard Vue component state
- Fix reactivity issues where images didn't update on new outputs
- Add proper subgraph-aware node resolution using NodeLocatorId
- Enhance accessibility with keyboard navigation and ARIA labels
- Add comprehensive error handling and loading states
- Include PrimeVue Skeleton for better loading UX
- Remove unused composable and test files

The image preview now properly updates when new outputs are generated
and follows standard Vue reactivity patterns.

* resolve merge conflict with main

- Keep both subgraphId field and hasErrors field from main
- No conflicts in other files (LGraphNode.vue and main.json merged cleanly)

* Fix LGraphNode test by adding proper Pinia testing setup

Added createTestingPinia and i18n configuration following the pattern
from working ImagePreview tests. Resolves test failures due to missing
Pinia store dependencies. All 6 tests now pass successfully.
2025-09-13 23:34:39 -07:00
Christian Byrne
c04af09956 [test] Add component test for Vue Textarea widget (multiline STRING widget) (#5539)
* add component test for textarea widget

* [refactor] improve test structure and typing - addresses @DrJKL's review feedback

- Move helper functions outside describe block for better scoping
- Fix SimplifiedWidget options typing from Partial<TextareaProps> to Record<string, any>
- Replace emitted\![0] with emitted?.[0] for safer optional chaining
- Remove unused TextareaProps import
2025-09-13 23:10:43 -07:00
Johnpaul Chiwetelu
ac107b45ea Floating Selection Toolbox Improvements (#5218)
* WIP

* WIP: UI design for right click menu

* feat: add composable for node customization and information handling

* fix: correct v-show directive in MaskEditorButton and enhance MoreOptions functionality

* feat: add selection and subgraph operations composables for enhanced graph management

* fix: update computed properties to use 'void' for non-reactive calls and add MenuOptionItem component

* feat: add composables for More Options menu and submenu positioning logic

* feat: refactor MoreOptions component to use MenuOptionItem for menu rendering and streamline submenu handling

* feat: implement SubmenuPopover component for enhanced submenu functionality and selection handling

* feat: add 'More Options' label and enhance shape options in localization file

* refactor: simplify shape name handling by removing Pascal case conversion and using localized names

* refactor: enhance submenu handling by dynamically setting refs and improving key assignment

* feat: implement useNodeArrangement composable for node alignment and distribution functionality

* feat: enhance useMoreOptionsMenu with image node operations and alignment options

* feat: localize context menu options and enhance submenu handling

* refactor: improve type safety for title assignment in selection operations and enhance color option retrieval in node customization

* fix: adjust component order in SelectionToolbox for improved layout

* feat: update FrameNodes button visibility and tooltip, and add localization for frameNodes

* feat: enhance button visibility logic in SelectionToolbox based on selection types

* refactor: reorganize properties panel option in More Options menu for single nodes

* remove excessive logging and alerts

* fix component tests

* ad browser tests

* feat: enhance popover behavior in MoreOptions component to manage visibility state during selection overlay changes

* refactor: update visibility logic for buttons in SelectionToolbox and ExecuteButton components

* refactor: remove duplicate shape option and clean up shapeOptions array

* refactor: update help toggle logic in InfoButton and useMoreOptionsMenu to manage sidebar and help state

* refactor: streamline node info handling and integrate output node filtering in useNodeInfo and useMoreOptionsMenu

* Added useSelectionState composable consolidating all selection-derived state and the node help toggle

* Updated toolbox buttons (InfoButton, BookmarkButton, BypassButton, MaskEditorButton, ConvertToSubgraphButton, PinButton, DeleteButton, ColorPickerButton, ExecuteButton, FrameNodes, Load3DViewerButton) to remove duplicated selection logic and use useSelectionState

* Introduced HideReason ('manual' | 'drag') to differentiate drag-induced hides from manual/outside hides in MoreOptions

* refactor: enhance popover visibility handling during drag events using canvas state

* fix: update shape option name from 'default' to 'box' and add localization for 'box'

* refactor: streamline BypassButton logic and enhance MoreOptions menu with state bumping

* refactor: remove toast notifications from subgraph operations for cleaner logic

* refactor: ensure menu options re-compute when selection flags change

* feat: Enhance MoreOptions behavior with drag-and-drop support

* fix: Update mask icon class for consistent styling in MaskEditorButton

* refactor: Standardize icon sizes and classes across selection toolbox buttons

* refactor: Update layout and styling in SelectionToolbox and MoreOptions components

* refactor: Improve selection toolbox behavior with more options state management

* Refactor: Remove unused imports and conditionally add subgraph option in menu

* Enhance popover behavior: add show/hide event handlers and improve positioning logic

* Cleanup: Remove debug comments from popover functions for clarity

* Refactor: Clean up FrameNodes component and add MenuOptionBadge for better option display

* Cleanup: Remove debug comments from useSelectionToolboxPosition for clarity

* Add useFrameNodes composable for grouping selected nodes

* Refactor: Update shape options in useNodeCustomization and localize frame nodes label

* fix tests

* Cleanup: Remove packageManager entry from package.json

* Refactor: Replace ILucide icons with named imports from lucide-vue-next

* Refactor: Update shape selection and improve color picker behavior in selection toolbox

* Update test expectations [skip ci]

* feat: Enhance More Options Menu for group node management and update localization strings

* refactor: Comment out PublishButton

* refactor: Comment out test for bookmark button visibility in SelectionToolbox

* refactor: Update class names for dark theme compatibility in ExecuteButton and MenuOptionItem components

* refactor: Modularize menu options by creating dedicated composables for group, image, node, and selection operations

* refactor: Update selectors in tests to match design changes

* refactor: Update help button selector in Node Help tests

* refactor: Update getGroupColorOptions to accept groupContext and bump parameters

* Update test expectations [skip ci]

* refactor: Center KSampler node before interaction in More Options submenu tests

* refactor: Adjust KSampler node positioning and simplify button click in More Options submenu tests

* refactor: Rename comfyPageFixture import for clarity

* refactor: use gap-1 instead of the explicit gap-[4px]

* refactor: Replace app.canvas with canvasStore.getCanvas for  state management

* refactor: Simplify prop access by removing 'props.' prefix in MenuOptionItem component

* refactor: Remove explicit type annotation for item in buildSelectionSignature function

* refactor: Replace Lucide icons with string-based icon references in menu options

* refactor: Remove export from interface declarations for improved clarity

* refactor: Simplify class binding in BypassButton component for improved readability

* refactor: Update button class for consistent sizing in ExecuteButton component

* refactor: Update help button locator class for consistency in Node Help tests

* fix node help test

* refactor: Remove unused imports and simplify visibility conditions in selection toolbox components

* feat: Add 3D node selection logic and cleanup on unmount for selection toolbox

* refactor: Update help button locator to use consistent data-testid in Node Help tests

* fix: Correct help button locator syntax in Node Help tests

* refactor: Change resetMoreOptionsState to an internal function in useSelectionToolboxPosition

* test: Add Load3D node visibility logic for ColorPickerButton and remove redundant test case

* fix: Increase tooltip show delay for ColorPickerButton

* fix: Update selectedOutputNodes computation to filter by isLGraphNode

* fix: Remove unused nodeDef reference from InfoButton and submenu trigger from MenuOptionItem

* fix: Update showInfoButton logic to depend on nodeDef value

* refactor: Remove deprecated getBasicNodeOptions function for cleaner code

* refactor: Replace useNodeInfo with useSelectedNodeActions

* refactor: Integrate useNodeDefStore for improved node definition handling in SelectionToolbox and InfoButton tests

* refactor: Introduce useCanvasRefresh composable for consistent canvas refresh logic across node operations

* refactor: Remove irrelevant append-to attribute from Popover

* refactor: Use storeToRefs for selectedItems in useSelectionState and add tests for selection logic

* refactor: Update ExecuteButton to use hasOutputNodesSelected for visibility and remove unnecessary computed property

* refactor: move display of execution button tests to selectionToolbox

---------

Co-authored-by: github-actions <github-actions@github.com>
2025-09-13 22:52:30 -07:00
Christian Byrne
f6a115e182 [feat] Add I/O slot component tests (#5523)
* add i/o slot component component tests

* refactor: use separate mount functions for type safety

Replace generic mount helper with dedicated mountInputSlot and mountOutputSlot functions to avoid type casting and improve type safety per review feedback.

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

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

* [auto-fix] Apply ESLint and Prettier fixes

* [refactor] rename test file from .spec.ts to .test.ts - addresses @DrJKL's naming convention feedback

* [refactor] use component prop types instead of custom interface - addresses @DrJKL's type safety feedback

* [refactor] add beforeEach for mock reset - addresses @DrJKL's test cleanup feedback

* [refactor] use standard assertions instead of manual mock call extraction - addresses @DrJKL's assertion feedback

* [auto-fix] Apply ESLint and Prettier fixes

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: GitHub Action <action@github.com>
2025-09-13 22:23:44 -07:00
Christian Byrne
aa7a99f4e3 fix: Show title, inputs, and outputs on Vue node component even when node fails to initalize (e.g., if missing) (#5540)
* add missing node error border

* update vue node data after configure

* [refactor] extract node type resolution to named const - addresses @DrJKL's readability concern

Extracted the multi-fallback type resolution logic into a clearly named
variable for improved readability and maintainability.

* [refactor] convert watch to computed pattern - addresses @DrJKL's structure comment

Replaced ref + watch pattern with computed for displayTitle, providing cleaner
reactive behavior and eliminating the need for manual sync logic.
2025-09-13 22:17:22 -07:00
Christian Byrne
8804755ffa fix: Use generic type in multi-select widget and fix options binding (#5525)
* use generic type and fix options binding

* [refactor] improve type safety in WidgetMultiSelect - addresses review comments

- Simplify array check to use Array.isArray(options?.values)
- Add generic type parameter to useWidgetValue call
- Remove unnecessary type assertion by leveraging TypeScript inference
2025-09-13 21:45:49 -07:00
Christian Byrne
f28ebcac19 Fix Vue slot label colors for light theme (#5529)
* [fix] use stone-200 for Vue slot labels in light theme

Updates Vue node slot label components to use stone-200 color (#828282)
for light theme instead of the previous #888682 color. This improves
theme consistency across the Vue nodes system.

Components updated:
- InputSlot.vue: Input slot labels
- OutputSlot.vue: Output slot labels
- NodeHeader.vue: Collapse/expand icon
- WidgetLayoutField.vue: Widget labels

* Update src/renderer/extensions/vueNodes/components/NodeHeader.vue

Co-authored-by: Alexander Brown <drjkl@comfy.org>

---------

Co-authored-by: Alexander Brown <drjkl@comfy.org>
2025-09-13 21:32:55 -07:00
Christian Byrne
bfef8d7d4b Add error border on missing nodes (vue nodes) (#5538)
* add missing node error border

* Update src/renderer/extensions/vueNodes/components/LGraphNode.vue

Co-authored-by: Alexander Brown <drjkl@comfy.org>

* Update src/renderer/extensions/vueNodes/components/LGraphNode.vue

Co-authored-by: Alexander Brown <drjkl@comfy.org>

* [auto-fix] Apply ESLint and Prettier fixes

---------

Co-authored-by: Alexander Brown <drjkl@comfy.org>
Co-authored-by: GitHub Action <action@github.com>
2025-09-13 21:32:35 -07:00
Christian Byrne
90bf8dc74a [refactor] Move pure functions from layout store to separate modules so they can be tested (and add tests) (#5462)
* refactor layout store utils

* [refactor] use nullish coalescing in getOr helper - addresses @DrJKL's suggestion

Replaces manual undefined/null checks with more concise ?? operator
for cleaner code that achieves the same functionality.

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

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

* [refactor] improve Y.Map typing for better type safety - addresses @DrJKL's typing suggestions

- Use Y.Map<NodeLayout[keyof NodeLayout]> instead of Y.Map<unknown>
- Provides compile-time type safety for stored values
- Improves IntelliSense and prevents type mismatches
- Updates mappers, store, tests, and helper functions consistently
- No runtime changes, pure TypeScript improvement

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

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

* [refactor] address @arjansingh code quality feedback

- Remove AI-generated refactoring comment that adds no value
- Reorganize tests with nested describe blocks for better readability
- Group related test cases by function for easier scanning

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

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

* [refactor] move makeLinkSegmentKey to layoutUtils - addresses @arjansingh's file organization feedback

- Move string concatenation function from layoutMath.ts to new layoutUtils.ts
- Keep layoutMath.ts focused on pure geometric calculations
- Create dedicated layoutUtils.ts for general layout utilities
- Update imports in store and create separate test file
- Improves module cohesion and clarity of purpose

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

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

* [cleanup] remove leftover AI refactoring comments

- Remove "Constants moved to utils" and "Node layout mapping moved to utils"
- Clean up extra blank lines from previous refactoring
- Keep meaningful organizational comments like "Helper methods"

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

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

* [cleanup] remove unnecessary import aliases

Remove pointInBoundsUtil/boundsIntersectUtil aliases as there are no
naming conflicts. Use direct function names for cleaner code.

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

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

* [refactor] improve Y.Map typing with named NodeLayoutMap type - addresses @DrJKL's performance and type safety suggestions

- Create named NodeLayoutMap type for TypeScript performance optimization
- Improve getOr function with proper key constraints and type safety
- Update all Y.Map<NodeLayout[keyof NodeLayout]> usages to use NodeLayoutMap
- Remove manual type assertions in favor of generic key constraints
- Clean up unused imports and fix formatting issues

* [cleanup] remove explanatory comment per @DrJKL's preference

* don't wait for dialog close button to be stable

---------

Co-authored-by: Claude <noreply@anthropic.com>
2025-09-13 21:17:09 -07:00
Christian Byrne
6f878abea4 Fix: Delete/Backspace hotkey to remove Vue Nodes (#5470)
* fix delete hotkey with vue nodes

* add playwright test for deletion and selection with vue nodes

* add unit test for keybinding service event forwarding

* [refactor] improve type safety and remove wrapper functions in VueNodeHelpers - addresses @DrJKL review comments

- Replace type cast with proper type predicate in getNodeIds method
- Remove unnecessary getNodeCount() and getSelectedNodeCount() wrapper functions
- Remove deleteSelected() helper methods to make key presses explicit in tests

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

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

* [refactor] make key presses explicit in Vue node tests - addresses @DrJKL review comment

- Remove commented line in test setup
- Replace helper method calls with direct keyboard.press() for better test clarity
- Use direct locator access instead of wrapper functions for node counts

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

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

* [enhance] add input filtering and improve shouldForwardToCanvas logic - addresses @DrJKL review comments

- Add filtering for input, textarea, and contentEditable elements to prevent forwarding when typing
- Allow shift key while blocking other modifiers (ctrl, alt, meta)
- Include existing property_value span check for consistency

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

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

* [refactor] remove mutable global state from keybinding unit tests - addresses @DrJKL review comment

- Remove global mockCommandExecute and mockProcessKey variables
- Access vi.mocked() directly in test assertions for better isolation
- Keep keybindingService as local variable since it's properly scoped

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

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

* [fix] remove duplicate input filtering that broke delete key functionality

The shouldForwardToCanvas function was duplicating input field checks already
handled by keyCombo.isReservedByTextInput, causing delete keys to be blocked.

- Remove duplicate input filtering from shouldForwardToCanvas
- Keep contentEditable enhancement in existing isReservedByTextInput check
- Maintain shift key support as requested in review

Fixes regression where delete key tests were failing after review changes.

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

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

* [fix] restore working test structure while implementing review improvements

Root cause: Changed test approach from helper methods to direct keyboard calls,
which introduced timing/focus issues that broke delete key functionality.

Solution:
- Restore working test structure using helper methods (deleteSelected, getNodeCount)
- Keep type safety improvement: replace type cast with proper type predicate
- Keep code cleanup: remove commented line in test setup
- Maintain working keybinding service with contentEditable enhancement

This preserves the original working behavior while addressing all review feedback.

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

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

* [auto-fix] Apply ESLint and Prettier fixes

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: GitHub Action <action@github.com>
2025-09-13 21:06:08 -07:00
Simula_r
b1917c6469 Fix/vue nodes viewport culling (#5510)
* debug: disable culling flag

* fix: vue nodes LOD

* fix: viewport culling and improve perf

* fix: PR feedback and more perf improvements

* refactor: forEach to for of

* fix: PR feedback

* fix: PR feedback

* fix: PR feedback

* fix: PR feedback

---------

Co-authored-by: Jake Schroeder <jake.schroeder@isophex.com>
2025-09-13 20:59:53 -07:00
Christian Byrne
bf054113f8 Mark flaky animated webp test as fixme (#5548)
* Remove duplicate snapshot image

Removes animated-image-preview-saved-webp-chromium-linux.png to leave only one image in the PR as requested.

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

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

* Mark flaky animated webp test as fixme

The animated webp test keeps flip-flopping due to timing issues with webp animation frames. The test asset is an animated webp with 2 frames, and the test relies on animation timing which makes it inherently flaky.

The bug being tested was that animated webp were being treated as normal webp, but since the test depends on webp animation timing, it's unreliable for CI.

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

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

* Add explanatory comment for fixme test

Add detailed comment explaining why the animated webp test is marked as fixme, documenting the timing dependency issues that cause flakiness in CI.

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

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

---------

Co-authored-by: Claude <noreply@anthropic.com>
2025-09-13 20:12:29 -07:00
Christian Byrne
4604bbd669 fix: make Color Picker Widget coerce to HEX with hashtag regardless of format/value in the UI (#5472)
* fix color picker value prefix and add component tests

* test(widgets): make color text assertion specific in WidgetColorPicker.test per review (DrJKL)

* test(widgets): use expect.soft for valid hex colors loop (suggestion by DrJKL)

* test(widgets): normalize color display to single leading # to address review question (AustinMroz)

* feat(widgets): normalize color widget values to #hex across inputs (hex/rgb/hsb); always emit with leading # using colorUtil conversions

* test(widgets): use data-testid selector for color text instead of generic span; add data-testid to component span for robustness

* support hsb|rgb|hex and coerce to hex with hashtag internally

refactor(widgets,utils): format-driven color normalization to lowercase #hex without casts; add typed toHexFromFormat and guards; simplify WidgetColorPicker state and types\n\n- utils: add ColorFormat, HSB/HSV types, isColorFormat/isHSBObject/isHSVObject, toHexFromFormat; reuse parseToRgb/hsbToRgb/rgbToHex\n- widgets: emit normalized #hex, display derived via toHexFromFormat, keep picker native v-model; typed widget options {format?}\n- tests: consolidate colorUtil tests into tests-ui/tests/colorUtil.test.ts; keep conversion + adjustColor suites; selectors robust\n- docs: add PR-5472-change-summary.md explaining changes\n\nAll type checks pass; ready for your final review before push.

refactor(widgets,utils): format-driven color normalization to lowercase #hex without casts; add typed toHexFromFormat and guards; simplify WidgetColorPicker state and types\n\n- utils: add ColorFormat, HSB/HSV types, isColorFormat/isHSBObject/isHSVObject, toHexFromFormat; reuse parseToRgb/hsbToRgb/rgbToHex\n- widgets: emit normalized #hex, display derived via toHexFromFormat, keep picker native v-model; typed widget options {format?}\n- tests: consolidate colorUtil tests into tests-ui/tests/colorUtil.test.ts; keep conversion + adjustColor suites; selectors robust\n- docs: add PR-5472-change-summary.md explaining changes\n\nAll type checks pass; ready for your final review before push.

chore: untrack PR-5472-change-summary.md and ignore locally (keep file on disk)

* fix(utils): use floor in hsbToRgb to match expected hex (#7f0000) for 50% brightness rounding behavior

* test(widgets): restore invalid-format fallback test and use data-testid selector in hex loop; chore: revert .gitignore change (remove PR-5472-change-summary.md entry)

* chore: restore .gitignore to match main (remove local note/comment)

* [refactor] improve color parsing in ColorPicker widget - addresses review feedback

- Use fancy color parsing for initial value normalization per @DrJKL's suggestion
- Simplify onPickerUpdate to trust configured format per @AustinMroz's feedback
- Remove redundant type checking and format guessing logic

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

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

* [refactor] simplify color parsing - remove unnecessary helper function

- Remove normalizeColorValue helper and inline null checks
- Remove verbose comments
- Keep the same functionality with cleaner code

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

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

* remove unused exports

---------

Co-authored-by: Claude <noreply@anthropic.com>
2025-09-13 19:58:26 -07:00
Christian Byrne
77ee20597f Update test expectations to match core node change (#5544)
* trigger CI

* Update test expectations [skip ci]

* Remove duplicate snapshot image

Removes animated-image-preview-saved-webp-chromium-linux.png to leave only one image in the PR as requested.

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

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

---------

Co-authored-by: github-actions <github-actions@github.com>
Co-authored-by: Claude <noreply@anthropic.com>
2025-09-13 19:55:51 -07:00
Christian Byrne
8ae50d140d [test] Add component test for Vue button widget (#5468)
* add component test for button widget

* [move] relocate WidgetButton test to proper directory

Move test from src/ to tests-ui/ directory structure and update import path
to work from new location - addresses review comment about test organization

* [refactor] make widget name optional in mock factory

Add optional name parameter to createMockWidget function for more flexible
test setup - addresses @DrJKL's suggestion about optional parameters

* [refactor] use it.for for parameterized button tests

Replace forEach loops with it.for syntax for testing button severities
and variants - addresses @DrJKL's suggestion for better test structure

* [auto-fix] Apply ESLint and Prettier fixes

* Revert "[move] relocate WidgetButton test to proper directory"

This reverts commit e9f4d57334.

* [test] increase rapid clicks test from 5 to 16

Better test coverage for concurrent click handling

* name click number a shared variable

---------

Co-authored-by: GitHub Action <action@github.com>
2025-09-13 14:51:39 -07:00
Comfy Org PR Bot
63f194b394 [chore] Update electron-types to 0.4.72 (#5526)
Co-authored-by: webfiltered <176114999+webfiltered@users.noreply.github.com>
2025-09-13 13:37:59 -07:00
filtered
2b8abca6d8 Fix update Action version extraction (#5527)
Was pulling frontend version instead.
2025-09-13 13:37:41 -07:00
snomiao
48d01745cd feat: add test count display to Playwright PR comments (#5458)
* feat: add test count display to Playwright PR comments

- Add extract-playwright-counts.mjs script to parse test results from Playwright reports
- Update pr-playwright-deploy-and-comment.sh to extract and display test counts
- Show overall summary with passed/failed/flaky/skipped counts
- Display per-browser test counts inline with report links
- Use dynamic status icons based on test results (//⚠️)

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

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

* feat: include skipped test count in per-browser display

- Add skipped test extraction for individual browser reports
- Update per-browser display format to show all four counts:
  ( passed /  failed / ⚠️ flaky / ⏭️ skipped)
- Provides complete test result visibility at a glance

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

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

* fix: improve test count extraction reliability in CI

- Use absolute paths for script and report directories
- Add debug logging to help diagnose extraction issues
- Move counts display after View Report link as requested
- Format: [View Report](url) •  passed /  failed / ⚠️ flaky / ⏭️ skipped

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

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

* fix: generate JSON reports alongside HTML for test count extraction

- Add JSON reporter to Playwright test runs
- Generate report.json alongside HTML reports
- Store JSON report in playwright-report directory
- This enables accurate test count extraction from CI artifacts

The HTML reports alone don't contain easily extractable test statistics
as they use a React app with dynamically loaded data. JSON reports
provide direct access to test counts.

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

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

* fix: correct JSON reporter syntax for Playwright tests

- Use proper syntax for JSON reporter with outputFile option
- Run separate commands for HTML and JSON report merging
- Specify output path directly in reporter configuration
- Ensures report.json is created in playwright-report directory

This fixes the "No such file or directory" error when trying to move
report.json file, as it wasn't being created in the first place.

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

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

* Revert "fix: correct JSON reporter syntax for Playwright tests"

This reverts commit 605d7cc1e2.

* fix: use correct Playwright reporter syntax with comma-separated list

- Use --reporter=html,json syntax (comma-separated, not space)
- Move test-results.json to playwright-report/report.json after generation
- Remove incorrect PLAYWRIGHT_JSON_OUTPUT_NAME env variable
- Add || true to prevent failure if JSON file doesn't exist

The JSON reporter outputs to test-results.json by default when using
the comma-separated reporter list syntax.

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

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

* fix: improve test count extraction reliability in CI

- Use separate --reporter flags for list, html, and json
- Set PLAYWRIGHT_JSON_OUTPUT_NAME env var to specify JSON output path
- Run HTML and JSON report generation separately for merged reports
- Ensures report.json is created in playwright-report directory

The combined reporter syntax wasn't creating the JSON file properly.
Using separate reporter flags with env var ensures JSON is generated.

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

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

* Update scripts/cicd/pr-playwright-deploy-and-comment.sh

Co-authored-by: Alexander Brown <drjkl@comfy.org>

* refactor: convert extraction script to TypeScript and use tsx

- Convert extract-playwright-counts.mjs to TypeScript (.ts)
- Add proper TypeScript types for better type safety
- Use tsx for execution instead of node
- Auto-install tsx in CI if not available
- Better alignment with the TypeScript codebase

This provides better type safety and consistency with the rest of
the codebase while maintaining the same functionality.

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

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

* chore(pr-playwright-deploy-and-comment.sh): move tsx installation check to the beginning of the script for better organization and efficiency

* [auto-fix] Apply ESLint and Prettier fixes

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Alexander Brown <drjkl@comfy.org>
Co-authored-by: GitHub Action <action@github.com>
2025-09-13 01:18:18 -07:00
Christian Byrne
90b1b47dd0 [test] Add Vue Node header component test (#5457)
* add node header component test

* [refactor] use separate const declarations instead of mutable variable in test - addresses @DrJKL's code style suggestion

Replace mutable `let icon` with descriptive `const expandedIcon` and `const collapsedIcon`
variables for better code clarity and immutability in the chevron icon test.

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

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

* [config] remove unnecessary vitest exclude patterns - addresses @DrJKL's configuration review

Remove redundant exclude patterns from vitest config as they are already covered by
vitest's default exclusions. Simplifies configuration while maintaining functionality.

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

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

* [config] remove unnecessary vitest exclude patterns - addresses @DrJKL's configuration review

Remove redundant exclude patterns from vitest config as they are already covered by
vitest's default exclusions. Simplifies configuration while maintaining functionality.

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

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

---------

Co-authored-by: Claude <noreply@anthropic.com>
2025-09-13 00:31:36 -07:00
Alexander Brown
0e01ca0a98 a11y: Bigger click/touch target for Slider track (#5524)
* a11y: Bigger click/touch target for Slider track

* a11y: z-index fix and bigger thumb too.
2025-09-12 23:34:39 -07:00
Christian Byrne
cf093d02a7 ADR: PrimeVue Fork Decision (#5230)
* ADR: Add PrimeVue fork decision record

Adds ADR-0003 documenting the decision to fork PrimeVue as a monorepo workspace package. Key rationale includes transform coordinate system conflicts and virtual canvas scroll interference that require component-level modifications.

* ADR: Reject PrimeVue fork decision

- Change status from Proposed to Rejected
- Document rationale: implementation complexity with dual monorepos,
  maintenance burden, alternative solutions available
- Add specific code citations and repository links
- Include alternative approach using shadcn/ui for selective replacement
2025-09-12 23:06:28 -07:00
Alexander Brown
1845708ddb Component: Vue Widget Slider (new) (#5516)
* feat: Initial shadcn configuration

* component: Add Slider component from shadcn-vue

* deps: Add tw-animate-css

* component: Align slider with Figma styles

* component: Set the step value for the slider, update styles

* fix: update component tests to work with Array of values

* vite: Don't reload dev server for test changes

* component: Swap text for a number input kept in sync with the slider

* cleanup: Don't need the override if the input isn't type="number"

* test: add step size tests

* cleanup: Don't need cn for these

* css: Update token names to match new Figma Variables

* lint: Fix camelCase vs train-case in passthrough

* feat: If the value is deleted, revert to the slider state cc: @PabloWiedemann

* feat: Improve cursor styles, grabbable thumb, clickable track

* lint: temporarily disable some warnings

* feat: Grabbing while sliding (most of the time)
2025-09-12 18:52:18 -07:00
Alexander Brown
c588f2f457 Feat: Change the Run button / ActionBar to dock by default (#5519)
* Feat: Change the Run button / ActionBar to dock by default

@PabloWiedemann

* Update test expectations [skip ci]

---------

Co-authored-by: github-actions <github-actions@github.com>
2025-09-12 14:07:32 -07:00
Benjamin Lu
29ad47d20f fix(links): remove drag-end offset in straight/linear modes by honoring CENTER/NONE as no-offset (#5520)
- Add 'none' direction to path renderer
- Map CENTER/NONE to 'none' in adapter
- Keep start-end offset intentional; end follows cursor exactly
- Preserve spline behavior and arrow rendering

Verified with typecheck; no visual changes outside dragging behavior.
2025-09-12 21:01:52 +00:00
Rizumu Ayaka
9aeeab9a9f feat: remove the divider that between widgets and slots (#5517) 2025-09-12 12:33:02 -07:00
Christian Byrne
6645b3917f Update ApiNodesSignInContent.vue (#5514) 2025-09-12 00:09:04 -07:00
snomiao
4ec6223189 fix: Add JSON import assertions for Node.js ESM compatibility (#5507)
Added `with { type: 'json' }` assertions to all JSON imports to ensure compatibility with Node.js ES modules and Playwright environments. This follows the current ESM specification where JSON imports require explicit type assertions.

Affected areas:
- Tailwind config
- i18n locale imports (36 files)
- Test fixtures and spec files
- API client feature flags
- Core color palettes

References:
- https://nodejs.org/api/esm.html
- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import/with

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

Co-authored-by: Claude <noreply@anthropic.com>
2025-09-11 20:22:21 -07:00
Benjamin Lu
dfcbbec2b9 Use env (#5501) 2025-09-11 17:33:25 -07:00
Johnpaul Chiwetelu
c051c3a507 Drag Multiple Vue Nodes (#5459)
* feat: enhance dragging functionality to support multiple selected nodes

* feat: enhance node selection handling to support drag state detection

* feat: enhance node selection handling to support drag state detection

* fix: update event trigger from pointer down to pointer up in LGraphNode tests
2025-09-11 17:25:23 -07:00
Alexander Brown
54cbf42a84 fix: Missing .value led to the release dot always showing (#5500) 2025-09-12 00:11:55 +00:00
Alexander Brown
ef7575b8d6 Revert "chore(lint): make ESLint concurrency configurable via pnpm config" (#5499)
* Revert "chore(lint): make ESLint concurrency configurable via pnpm config (#5…"

This reverts commit 9997053290.

* chore: Remove --concurrency
2025-09-11 17:07:19 -07:00
AustinMroz
68845ce33a Fix Connection of Primitive nodes to Subgraph node (#5024)
* Fix connection of primitives to subgraphNodes

* Fix loading and nested subgraphs with primitives

Medium hackyness, but this saves ~100 lines.

* Use improved type check

* Remove requirement for type assertion

* Add warning comment

---------

Co-authored-by: filtered <176114999+webfiltered@users.noreply.github.com>
2025-09-11 14:27:44 -07:00
Rizumu Ayaka
46f4ce3890 feat: style of progress bar in various scenarios (#5492) 2025-09-11 14:07:44 -07:00
Comfy Org PR Bot
5df037dfc4 1.27.3 (#5497)
* [release] Increment version to 1.27.3

* fix(i18n): use import attributes for JSON to support Node/Playwright in i18n workflow

* Revert "fix(i18n): use import attributes for JSON to support Node/Playwright in i18n workflow"

This reverts commit b525242c32.

---------

Co-authored-by: benceruleanlu <162923238+benceruleanlu@users.noreply.github.com>
Co-authored-by: Benjamin Lu <benjaminlu1107@gmail.com>
2025-09-11 13:44:02 -07:00
AustinMroz
3bc25b7aeb Add Asset Widget (#5475)
* [feat] carve out path to call asset browser in combo widget

* Add Asset Widget

* [feat] add fallback "Select model" label

---------

Co-authored-by: Arjan Singh <arjan@comfy.org>
2025-09-11 12:00:34 -07:00
Rizumu Ayaka
08fe2829d4 feat: node border and hover and selected style, and when error (#5491)
* feat: node border and hover and selected style, and when error

* fix test error
2025-09-11 03:04:16 -07:00
Jin Yi
e70ddea684 fix: Add dropdown size control to Select components and improve UI (#5290)
* feature: size adjust

* feature: design adjust

* fix: popover width, height added

* fix: li style override

* refactor: improve component readability and
  maintainability per PR feedback

  - Replace CardGridList component with
  createGridStyle utility function
  - Add runtime validation for grid column values
  - Remove !important usage in MultiSelect, use cn()
  function instead
  - Extract popover sizing logic into
  usePopoverSizing composable
  - Improve class string readability by splitting
  into logical groups
  - Use Tailwind size utilities (size-8, size-10)
  instead of separate width/height
  - Remove magic numbers in SearchBox, align with
  button sizes
  - Rename BaseWidgetLayout to BaseModalLayout for
  clarity
  - Enhance SearchBox click area to cover entire
  component
  - Refactor long class strings using cn() utility
  across components

* fix: BaseWidgetLayout => BaseModalLayout

* fix: CardGrid deleted

* fix: unused exported types

* Update test expectations [skip ci]

* chore: code review

* Update test expectations [skip ci]

* chore: restore screenshot

---------

Co-authored-by: github-actions <github-actions@github.com>
2025-09-11 03:01:06 -07:00
Benjamin Lu
038f86fe84 fix(canvas): use vertical-align: top to eliminate baseline gap (#5484)
* fix(canvas): make graph canvas block-level to eliminate baseline gap

- Change <canvas id=graph-canvas> to display:block via Tailwind class
- Removes 1–5 px baseline offset between canvas and container
- Aligns canvas and TransformPane origins; fixes link/slot endpoint drift

No behavioral changes beyond layout origin alignment; no dependent CSS relies on inline/baseline.

* switch block to align-top

* Update test expectations [skip ci]

* Revert "Update test expectations [skip ci]"

This reverts commit ee0dfd4e0a.

* empty commit for ci

* Update test expectations [skip ci]

---------

Co-authored-by: github-actions <github-actions@github.com>
2025-09-11 01:52:15 -07:00
snomiao
cb0dab6cdc [bugfix] Fix flaky test 'Does not report warning on undo/redo' (#5488)
Add additional wait after closing the dialog to ensure all async operations
complete before continuing with the test. This prevents race conditions
where the dialog might not be fully closed when the test proceeds.

The test was failing intermittently because closeDialog() waits for the
dialog to be hidden, but there may be additional async state updates that
need to complete after the dialog closes.

Fixes flaky test in dialog.spec.ts:33
2025-09-11 01:30:30 -07:00
AustinMroz
568be0c44c When toggling selected, align state (#5482)
Previously, when toggling the mode of multiple nodes, each node would
have its state individually toggled. Now it enables mode if any node is
not currently set to that mode and only disables if all already match.
2025-09-11 00:50:26 -07:00
Jin Yi
6ea021d595 feat: Auto-close LoadWorkflowWarning dialog when all missing nodes are installed (#5321)
* feat: Auto-close LoadWorkflowWarning dialog when all missing nodes are installed

- Add computed property to check if all missing nodes are installed
- Watch for completion and automatically close dialog with 500ms delay
- Show success toast notification when installation completes
- Add translation key for success message

This improves UX by automatically dismissing the warning dialog once the user has successfully installed all missing nodes through the manager.

* fix: settimeout to nexttick

* [auto-fix] Apply ESLint and Prettier fixes

---------

Co-authored-by: GitHub Action <action@github.com>
2025-09-10 23:19:04 -07:00
Alexander Brown
7245213ed6 Fix: In standard mode, don't stop when you hit a Vue node. (#5445)
* fix: Forward the scrolling events to the litegraph canvas.

* prior-art: Use the existing event forwarding logic from useCanvasInteractions (h/t Ben)

* fix: Get proper scaling from properties in the original event, fix browser zoom

* tests: Fix missing property on mock

* types: Cleanup type annotations in the test

* cleanup: Initialize the mocks in place.

* tests: extract createMockPointerEvent

* tests: extract createMockWheelEvent

* tests: extract createMockLGraphCanvas

* tests: Add additional assertion for stopPropagation

* tests: Comment pruning, test rename suggested by @arjansingh
2025-09-10 23:17:06 -07:00
Christian Byrne
b72e22f6be Add Centralized Vue Node Size/Pos Tracking (#5442)
* add dom element resize observer registry for vue node components

* Update src/renderer/extensions/vueNodes/composables/useVueNodeResizeTracking.ts

Co-authored-by: AustinMroz <austin@comfy.org>

* refactor(vue-nodes): typed TransformState InjectionKey, safer ResizeObserver sizing, centralized slot tracking, and small readability updates

* chore: make TransformState interface non-exported to satisfy knip pre-push

* Revert "chore: make TransformState interface non-exported to satisfy knip pre-push"

This reverts commit 110ecf31da.

* Revert "refactor(vue-nodes): typed TransformState InjectionKey, safer ResizeObserver sizing, centralized slot tracking, and small readability updates"

This reverts commit 428752619c.

* [refactor] Improve resize tracking composable documentation and test utilities

- Rename parameters in useVueElementTracking for clarity (appIdentifier, trackingType)
- Add comprehensive docstring with examples to prevent DOM attribute confusion
- Extract mountLGraphNode test utility to eliminate repetitive mock setup
- Add technical implementation notes documenting optimization decisions

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

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

* remove typo comment

* convert to functional bounds collection

* remove inline import

* add interfaces for bounds mutations

* remove change log

* fix bounds collection when vue nodes turned off

* fix title offset on y

* move from resize observer to selection toolbox bounds

---------

Co-authored-by: AustinMroz <austin@comfy.org>
Co-authored-by: Benjamin Lu <benjaminlu1107@gmail.com>
Co-authored-by: Claude <noreply@anthropic.com>
2025-09-10 22:38:49 -07:00
Jin Yi
5f045b335d [feat] Improve UX for disabled node packs in Manager dialog (#5478)
* [feat] Improve UX for disabled node packs in Manager dialog

- Hide "Update All" button when only disabled packs have updates
- Add tooltip on "Update All" hover to indicate disabled nodes won't be updated
- Disable version selector and show tooltip for disabled node packs
- Filter updates to only show enabled packs in the update queue
- Add visual indicators (opacity, cursor) for disabled pack cards
- Add comprehensive test coverage for new functionality

This improves the user experience by clearly indicating which packs
can be updated and preventing confusion about disabled packs.

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

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

* chore: missing nodes description added

* test: test code modified

---------

Co-authored-by: Claude <noreply@anthropic.com>
2025-09-10 22:35:16 -07:00
Arjan Singh
44e470488d [feat] carve out path to call asset browser in combo widget (#5464)
* [ci] ignore local browser tests files

this is where i have claude put its one off playwright scripts

* [feat] carve out path to call asset browser in combo widget

* [feat] use buttons on Model Loaders when Asset API setting is on
2025-09-10 22:26:07 -07:00
Alexander Brown
ca220440b2 Tailwind: Move customization into CSS (#5477)
* tailwind: Migrate out of the js/ts config part 1

* tailwind: Migrate custom variant and utility

* Update test expectations [skip ci]

* tailwind: Use relative colors for alpha variants

* fix: Use the new numbered color tokens

---------

Co-authored-by: github-actions <github-actions@github.com>
2025-09-10 18:39:37 -07:00
Alexander Piskun
2e64c64ac7 add pricing for new ByteDance node (#5481) 2025-09-10 17:46:47 -07:00
snomiao
d561f315d3 fix: resolve flaky color picker test by adding missing await (#5467)
- Add missing await for async getProperty call in selectionToolbox test
- Add timestamp to test username generation to prevent duplicate user conflicts
- Test now passes consistently without race conditions

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

Co-authored-by: Claude <noreply@anthropic.com>
2025-09-10 18:06:36 +09:00
snomiao
080334754c fix: update Claude PR Review workflow to use correct action parameters (#5473)
* fix: update Claude PR Review workflow to use correct action parameters

- Changed 'direct_prompt' to 'prompt' (correct parameter name)
- Moved max_turns and timeout to claude_args parameter
- Changed allowed_tools to additional_permissions parameter

The workflow was failing silently because it was using invalid input parameters
that the claude-code-action doesn't recognize.

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

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

* fix: pin claude-code-action to v1.0.6 to prevent future breakage

Using @main tag could cause unexpected breakage when the action updates.
Pinning to a specific version ensures stability.

* fix: apply review feedback - correct migration to v1.0 format

- Moved timeout-minutes to job level (not in claude_args)
- Changed additional_permissions to --allowedTools in claude_args
- Fixed tool specification format per migration guide

These changes follow the official v0.x to v1.0 migration guide exactly.

---------

Co-authored-by: Claude <noreply@anthropic.com>
2025-09-09 23:25:36 -07:00
Christian Byrne
169d7404fe fix text prop area type error (#5471) 2025-09-09 22:24:04 -07:00
935 changed files with 37933 additions and 6931 deletions

View File

@@ -67,9 +67,9 @@ This is critical for better file inspection:
Use git locally for much faster analysis:
1. Get list of changed files: `git diff --name-only "origin/$BASE_BRANCH" > changed_files.txt`
2. Get the full diff: `git diff "origin/$BASE_BRANCH" > pr_diff.txt`
3. Get detailed file changes with status: `git diff --name-status "origin/$BASE_BRANCH" > file_changes.txt`
1. Get list of changed files: `git diff --name-only "$BASE_SHA" > changed_files.txt`
2. Get the full diff: `git diff "$BASE_SHA" > pr_diff.txt`
3. Get detailed file changes with status: `git diff --name-status "$BASE_SHA" > file_changes.txt`
### Step 1.5: Create Analysis Cache

View File

@@ -33,4 +33,3 @@ DISABLE_VUE_PLUGINS=false
# Algolia credentials required for developing with the new custom node manager.
ALGOLIA_APP_ID=4E0RO38HS8
ALGOLIA_API_KEY=684d998c36b67a9a9fce8fc2d8860579

View File

@@ -7,3 +7,21 @@ c53f197de2a3e0fa66b16dedc65c131235c1c4b6
# Reorganize renderer components into domain-driven folder structure
c8a83a9caede7bdb5f8598c5492b07d08c339d49
# Domain-driven design (DDD) refactors - September 2025
# These commits reorganized the codebase into domain-driven architecture
# [refactor] Improve renderer domain organization (#5552)
6349ceee6c0a57fc7992e85635def9b6e22eaeb2
# [refactor] Improve settings domain organization (#5550)
4c8c4a1ad4f53354f700a33ea1b95262aeda2719
# [refactor] Improve workflow domain organization (#5584)
ca312fd1eab540cc4ddc0e3d244d38b3858574f0
# [refactor] Move thumbnail functionality to renderer/core domain (#5586)
e3bb29ceb8174b8bbca9e48ec7d42cd540f40efa
# [refactor] Improve updates/notifications domain organization (#5590)
27ab355f9c73415dc39f4d3f512b02308f847801

2
.gitattributes vendored
View File

@@ -13,4 +13,4 @@
# Generated files
src/types/comfyRegistryTypes.ts linguist-generated=true
src/types/generatedManagerTypes.ts linguist-generated=true
src/workbench/extensions/manager/types/generatedManagerTypes.ts linguist-generated=true

View File

@@ -4,10 +4,25 @@ on:
pull_request_target:
types: [closed, labeled]
branches: [main]
workflow_dispatch:
inputs:
pr_number:
description: 'PR number to backport'
required: true
type: string
force_rerun:
description: 'Force rerun even if backports exist'
required: false
type: boolean
default: false
jobs:
backport:
if: github.event.pull_request.merged == true && contains(github.event.pull_request.labels.*.name, 'needs-backport')
if: >
(github.event_name == 'pull_request_target' &&
github.event.pull_request.merged == true &&
contains(github.event.pull_request.labels.*.name, 'needs-backport')) ||
github.event_name == 'workflow_dispatch'
runs-on: ubuntu-latest
permissions:
contents: write
@@ -15,6 +30,35 @@ jobs:
issues: write
steps:
- name: Validate inputs for manual triggers
if: github.event_name == 'workflow_dispatch'
run: |
# Validate PR number format
if ! [[ "${{ inputs.pr_number }}" =~ ^[0-9]+$ ]]; then
echo "::error::Invalid PR number format. Must be a positive integer."
exit 1
fi
# Validate PR exists and is merged
if ! gh pr view "${{ inputs.pr_number }}" --json merged >/dev/null 2>&1; then
echo "::error::PR #${{ inputs.pr_number }} not found or inaccessible."
exit 1
fi
MERGED=$(gh pr view "${{ inputs.pr_number }}" --json merged --jq '.merged')
if [ "$MERGED" != "true" ]; then
echo "::error::PR #${{ inputs.pr_number }} is not merged. Only merged PRs can be backported."
exit 1
fi
# Validate PR has needs-backport label
if ! gh pr view "${{ inputs.pr_number }}" --json labels --jq '.labels[].name' | grep -q "needs-backport"; then
echo "::error::PR #${{ inputs.pr_number }} does not have 'needs-backport' label."
exit 1
fi
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Checkout repository
uses: actions/checkout@v4
with:
@@ -29,7 +73,7 @@ jobs:
id: check-existing
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
PR_NUMBER: ${{ github.event.pull_request.number }}
PR_NUMBER: ${{ github.event_name == 'workflow_dispatch' && inputs.pr_number || github.event.pull_request.number }}
run: |
# Check for existing backport PRs for this PR number
EXISTING_BACKPORTS=$(gh pr list --state all --search "backport-${PR_NUMBER}-to" --json title,headRefName,baseRefName | jq -r '.[].headRefName')
@@ -39,6 +83,13 @@ jobs:
exit 0
fi
# For manual triggers with force_rerun, proceed anyway
if [ "${{ github.event_name }}" = "workflow_dispatch" ] && [ "${{ inputs.force_rerun }}" = "true" ]; then
echo "skip=false" >> $GITHUB_OUTPUT
echo "::warning::Force rerun requested - existing backports will be updated"
exit 0
fi
echo "Found existing backport PRs:"
echo "$EXISTING_BACKPORTS"
echo "skip=true" >> $GITHUB_OUTPUT
@@ -50,8 +101,17 @@ jobs:
run: |
# Extract version labels (e.g., "1.24", "1.22")
VERSIONS=""
LABELS='${{ toJSON(github.event.pull_request.labels) }}'
for label in $(echo "$LABELS" | jq -r '.[].name'); do
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
# For manual triggers, get labels from the PR
LABELS=$(gh pr view ${{ inputs.pr_number }} --json labels | jq -r '.labels[].name')
else
# For automatic triggers, extract from PR event
LABELS='${{ toJSON(github.event.pull_request.labels) }}'
LABELS=$(echo "$LABELS" | jq -r '.[].name')
fi
for label in $LABELS; do
# Match version labels like "1.24" (major.minor only)
if [[ "$label" =~ ^[0-9]+\.[0-9]+$ ]]; then
# Validate the branch exists before adding to list
@@ -75,12 +135,20 @@ jobs:
if: steps.check-existing.outputs.skip != 'true'
id: backport
env:
PR_NUMBER: ${{ github.event.pull_request.number }}
PR_TITLE: ${{ github.event.pull_request.title }}
MERGE_COMMIT: ${{ github.event.pull_request.merge_commit_sha }}
PR_NUMBER: ${{ github.event_name == 'workflow_dispatch' && inputs.pr_number || github.event.pull_request.number }}
run: |
FAILED=""
SUCCESS=""
# Get PR data for manual triggers
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
PR_DATA=$(gh pr view ${{ inputs.pr_number }} --json title,mergeCommit)
PR_TITLE=$(echo "$PR_DATA" | jq -r '.title')
MERGE_COMMIT=$(echo "$PR_DATA" | jq -r '.mergeCommit.oid')
else
PR_TITLE="${{ github.event.pull_request.title }}"
MERGE_COMMIT="${{ github.event.pull_request.merge_commit_sha }}"
fi
for version in ${{ steps.versions.outputs.versions }}; do
echo "::group::Backporting to core/${version}"
@@ -133,11 +201,18 @@ jobs:
if: steps.check-existing.outputs.skip != 'true' && steps.backport.outputs.success
env:
GH_TOKEN: ${{ secrets.PR_GH_TOKEN }}
PR_NUMBER: ${{ github.event_name == 'workflow_dispatch' && inputs.pr_number || github.event.pull_request.number }}
run: |
PR_TITLE="${{ github.event.pull_request.title }}"
PR_NUMBER="${{ github.event.pull_request.number }}"
PR_AUTHOR="${{ github.event.pull_request.user.login }}"
# Get PR data for manual triggers
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
PR_DATA=$(gh pr view ${{ inputs.pr_number }} --json title,author)
PR_TITLE=$(echo "$PR_DATA" | jq -r '.title')
PR_AUTHOR=$(echo "$PR_DATA" | jq -r '.author.login')
else
PR_TITLE="${{ github.event.pull_request.title }}"
PR_AUTHOR="${{ github.event.pull_request.user.login }}"
fi
for backport in ${{ steps.backport.outputs.success }}; do
IFS=':' read -r version branch <<< "${backport}"
@@ -166,9 +241,16 @@ jobs:
env:
GH_TOKEN: ${{ github.token }}
run: |
PR_NUMBER="${{ github.event.pull_request.number }}"
PR_AUTHOR="${{ github.event.pull_request.user.login }}"
MERGE_COMMIT="${{ github.event.pull_request.merge_commit_sha }}"
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
PR_DATA=$(gh pr view ${{ inputs.pr_number }} --json author,mergeCommit)
PR_NUMBER="${{ inputs.pr_number }}"
PR_AUTHOR=$(echo "$PR_DATA" | jq -r '.author.login')
MERGE_COMMIT=$(echo "$PR_DATA" | jq -r '.mergeCommit.oid')
else
PR_NUMBER="${{ github.event.pull_request.number }}"
PR_AUTHOR="${{ github.event.pull_request.user.login }}"
MERGE_COMMIT="${{ github.event.pull_request.merge_commit_sha }}"
fi
for failure in ${{ steps.backport.outputs.failed }}; do
IFS=':' read -r version reason conflicts <<< "${failure}"

View File

@@ -47,6 +47,7 @@ jobs:
needs: wait-for-ci
if: needs.wait-for-ci.outputs.should-proceed == 'true'
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- name: Checkout repository
uses: actions/checkout@v4
@@ -69,19 +70,17 @@ jobs:
pnpm install -g typescript @vue/compiler-sfc
- name: Run Claude PR Review
uses: anthropics/claude-code-action@main
uses: anthropics/claude-code-action@v1.0.6
with:
label_trigger: "claude-review"
direct_prompt: |
prompt: |
Read the file .claude/commands/comprehensive-pr-review.md and follow ALL the instructions exactly.
CRITICAL: You must post individual inline comments using the gh api commands shown in the file.
DO NOT create a summary comment.
Each issue must be posted as a separate inline comment on the specific line of code.
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
max_turns: 256
timeout_minutes: 30
allowed_tools: "Bash(git:*),Bash(gh api:*),Bash(gh pr:*),Bash(gh repo:*),Bash(jq:*),Bash(echo:*),Read,Write,Edit,Glob,Grep,WebFetch"
claude_args: "--max-turns 256 --allowedTools 'Bash(git:*),Bash(gh api:*),Bash(gh pr:*),Bash(gh repo:*),Bash(jq:*),Bash(echo:*),Read,Write,Edit,Glob,Grep,WebFetch'"
env:
PR_NUMBER: ${{ github.event.pull_request.number }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View File

@@ -32,11 +32,10 @@ jobs:
with:
repository: Comfy-Org/ComfyUI_frontend
path: ComfyUI_frontend
- name: Checkout ComfyUI_devtools
uses: actions/checkout@v4
with:
repository: Comfy-Org/ComfyUI_devtools
path: ComfyUI/custom_nodes/ComfyUI_devtools
- name: Copy ComfyUI_devtools from frontend repo
run: |
mkdir -p ComfyUI/custom_nodes/ComfyUI_devtools
cp -r ComfyUI_frontend/tools/devtools/* ComfyUI/custom_nodes/ComfyUI_devtools/
- name: Checkout custom node repository
uses: actions/checkout@v4
with:

View File

@@ -0,0 +1,139 @@
name: Publish Frontend Types
on:
workflow_dispatch:
inputs:
version:
description: 'Version to publish (e.g., 1.26.7)'
required: true
type: string
dist_tag:
description: 'npm dist-tag to use'
required: true
default: latest
type: string
ref:
description: 'Git ref to checkout (commit SHA, tag, or branch)'
required: false
type: string
workflow_call:
inputs:
version:
required: true
type: string
dist_tag:
required: false
type: string
default: latest
ref:
required: false
type: string
concurrency:
group: publish-frontend-types-${{ github.workflow }}-${{ inputs.version }}-${{ inputs.dist_tag }}
cancel-in-progress: false
jobs:
publish_types_manual:
name: Publish @comfyorg/comfyui-frontend-types
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- name: Validate inputs
shell: bash
run: |
set -euo pipefail
VERSION="${{ inputs.version }}"
SEMVER_REGEX='^(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)(-((0|[1-9][0-9]*|[0-9]*[A-Za-z-][0-9A-Za-z-]*)(\.(0|[1-9][0-9]*|[0-9]*[A-Za-z-][0-9A-Za-z-]*))*))?(\+([0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*))?$'
if [[ ! "$VERSION" =~ $SEMVER_REGEX ]]; then
echo "::error title=Invalid version::Version '$VERSION' must follow semantic versioning (x.y.z[-suffix][+build])" >&2
exit 1
fi
- name: Determine ref to checkout
id: resolve_ref
shell: bash
run: |
set -euo pipefail
REF="${{ inputs.ref }}"
VERSION="${{ inputs.version }}"
if [ -n "$REF" ]; then
if ! git check-ref-format --allow-onelevel "$REF"; then
echo "::error title=Invalid ref::Ref '$REF' fails git check-ref-format validation." >&2
exit 1
fi
echo "ref=$REF" >> "$GITHUB_OUTPUT"
else
echo "ref=refs/tags/v$VERSION" >> "$GITHUB_OUTPUT"
fi
- name: Checkout repository
uses: actions/checkout@v5
with:
ref: ${{ steps.resolve_ref.outputs.ref }}
fetch-depth: 1
- name: Install pnpm
uses: pnpm/action-setup@v4
with:
version: 10
- name: Setup Node.js
uses: actions/setup-node@v5
with:
node-version: 'lts/*'
cache: 'pnpm'
registry-url: https://registry.npmjs.org
- name: Install dependencies
run: pnpm install --frozen-lockfile
env:
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: '1'
- name: Build types
run: pnpm build:types
- name: Verify version matches input
id: verify
shell: bash
run: |
PKG_VERSION=$(node -p "require('./package.json').version")
TYPES_PKG_VERSION=$(node -p "require('./dist/package.json').version")
if [ "$PKG_VERSION" != "${{ inputs.version }}" ]; then
echo "Error: package.json version $PKG_VERSION does not match input ${{ inputs.version }}" >&2
exit 1
fi
if [ "$TYPES_PKG_VERSION" != "${{ inputs.version }}" ]; then
echo "Error: dist/package.json version $TYPES_PKG_VERSION does not match input ${{ inputs.version }}" >&2
exit 1
fi
echo "version=${{ inputs.version }}" >> $GITHUB_OUTPUT
- name: Check if version already on npm
id: check_npm
shell: bash
run: |
set -euo pipefail
NAME=$(node -p "require('./dist/package.json').name")
VER="${{ steps.verify.outputs.version }}"
STATUS=0
OUTPUT=$(npm view "${NAME}@${VER}" --json 2>&1) || STATUS=$?
if [ "$STATUS" -eq 0 ]; then
echo "exists=true" >> "$GITHUB_OUTPUT"
echo "::warning title=Already published::${NAME}@${VER} already exists on npm. Skipping publish."
else
if echo "$OUTPUT" | grep -q "E404"; then
echo "exists=false" >> "$GITHUB_OUTPUT"
else
echo "::error title=Registry lookup failed::$OUTPUT" >&2
exit "$STATUS"
fi
fi
- name: Publish package
if: steps.check_npm.outputs.exists == 'false'
run: pnpm publish --access public --tag "${{ inputs.dist_tag }}" --no-git-checks
working-directory: dist
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

View File

@@ -18,7 +18,7 @@ jobs:
is_prerelease: ${{ steps.check_prerelease.outputs.is_prerelease }}
steps:
- name: Checkout code
uses: actions/checkout@v4
uses: actions/checkout@v5
- name: Install pnpm
uses: pnpm/action-setup@v4
with:
@@ -73,7 +73,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
uses: actions/checkout@v5
- name: Download dist artifact
uses: actions/download-artifact@v4
with:
@@ -98,7 +98,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
uses: actions/checkout@v5
- name: Download dist artifact
uses: actions/download-artifact@v4
with:
@@ -126,34 +126,8 @@ jobs:
publish_types:
needs: build
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install pnpm
uses: pnpm/action-setup@v4
with:
version: 10
- uses: actions/setup-node@v4
with:
node-version: 'lts/*'
cache: 'pnpm'
registry-url: https://registry.npmjs.org
- name: Cache tool outputs
uses: actions/cache@v4
with:
path: |
.cache
tsconfig.tsbuildinfo
dist
key: types-tools-cache-${{ runner.os }}-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
types-tools-cache-${{ runner.os }}-
- run: pnpm install --frozen-lockfile
- run: pnpm build:types
- name: Publish package
run: pnpm publish --access public
working-directory: dist
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
uses: ./.github/workflows/publish-frontend-types.yaml
with:
version: ${{ needs.build.outputs.version }}
ref: ${{ github.event.pull_request.merge_commit_sha }}
secrets: inherit

View File

@@ -27,12 +27,10 @@ jobs:
repository: 'Comfy-Org/ComfyUI_frontend'
path: 'ComfyUI_frontend'
- name: Checkout ComfyUI_devtools
uses: actions/checkout@v4
with:
repository: 'Comfy-Org/ComfyUI_devtools'
path: 'ComfyUI/custom_nodes/ComfyUI_devtools'
ref: 'd05fd48dd787a4192e16802d4244cfcc0e2f9684'
- name: Copy ComfyUI_devtools from frontend repo
run: |
mkdir -p ComfyUI/custom_nodes/ComfyUI_devtools
cp -r ComfyUI_frontend/tools/devtools/* ComfyUI/custom_nodes/ComfyUI_devtools/
- name: Install pnpm
uses: pnpm/action-setup@v4
@@ -229,7 +227,13 @@ jobs:
- name: Run Playwright tests (${{ matrix.browser }})
id: playwright
run: npx playwright test --project=${{ matrix.browser }} --reporter=html
run: |
# Run tests with both HTML and JSON reporters
PLAYWRIGHT_JSON_OUTPUT_NAME=playwright-report/report.json \
npx playwright test --project=${{ matrix.browser }} \
--reporter=list \
--reporter=html \
--reporter=json
working-directory: ComfyUI_frontend
- uses: actions/upload-artifact@v4
@@ -275,7 +279,12 @@ jobs:
merge-multiple: true
- name: Merge into HTML Report
run: npx playwright merge-reports --reporter html ./all-blob-reports
run: |
# Generate HTML report
npx playwright merge-reports --reporter=html ./all-blob-reports
# Generate JSON report separately with explicit output path
PLAYWRIGHT_JSON_OUTPUT_NAME=playwright-report/report.json \
npx playwright merge-reports --reporter=json ./all-blob-reports
working-directory: ComfyUI_frontend
- name: Upload HTML report

View File

@@ -40,7 +40,7 @@ jobs:
- name: Get new version
id: get-version
run: |
NEW_VERSION=$(pnpm list @comfyorg/comfyui-electron-types --json --depth=0 | jq -r '.[0].version')
NEW_VERSION=$(pnpm list @comfyorg/comfyui-electron-types --json --depth=0 | jq -r '.[0].dependencies."@comfyorg/comfyui-electron-types".version')
echo "NEW_VERSION=$NEW_VERSION" >> $GITHUB_OUTPUT
- name: Create Pull Request

6
.gitignore vendored
View File

@@ -44,6 +44,7 @@ components.d.ts
tests-ui/data/*
tests-ui/ComfyUI_examples
tests-ui/workflows/examples
coverage/
# Browser tests
/test-results/
@@ -51,6 +52,7 @@ tests-ui/workflows/examples
/blob-report/
/playwright/.cache/
browser_tests/**/*-win32.png
browser_tests/local/
.env
@@ -77,8 +79,8 @@ vite.config.mts.timestamp-*.mjs
*storybook.log
storybook-static
# MCP Servers
.playwright-mcp/*
.nx/cache
.nx/workspace-data

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'],
outputLocales: ['zh', 'zh-TW', 'ru', 'ja', 'ko', 'fr', 'es', 'ar', 'tr'],
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.

1
.npmrc Normal file
View File

@@ -0,0 +1 @@
ignore-workspace-root-check=true

View File

@@ -15,21 +15,32 @@ const config: StorybookConfig = {
async viteFinal(config) {
// Use dynamic import to avoid CJS deprecation warning
const { mergeConfig } = await import('vite')
const { default: tailwindcss } = await import('@tailwindcss/vite')
// Filter out any plugins that might generate import maps
if (config.plugins) {
config.plugins = config.plugins.filter((plugin: any) => {
if (plugin && plugin.name && plugin.name.includes('import-map')) {
return false
}
return true
})
config.plugins = config.plugins
// Type guard: ensure we have valid plugin objects with names
.filter(
(plugin): plugin is NonNullable<typeof plugin> & { name: string } => {
return (
plugin !== null &&
plugin !== undefined &&
typeof plugin === 'object' &&
'name' in plugin &&
typeof plugin.name === 'string'
)
}
)
// Business logic: filter out import-map plugins
.filter((plugin) => !plugin.name.includes('import-map'))
}
return mergeConfig(config, {
// Replace plugins entirely to avoid inheritance issues
plugins: [
// Only include plugins we explicitly need for Storybook
tailwindcss(),
Icons({
compiler: 'vue3',
customCollections: {

View File

@@ -57,9 +57,8 @@
/* Override Storybook's problematic & selector styles */
/* Reset only the specific properties that Storybook injects */
#storybook-root li+li,
#storybook-docs li+li {
margin: inherit;
padding: inherit;
li+li {
margin: 0;
padding: revert-layer;
}
</style>

View File

@@ -1,7 +1,7 @@
import { definePreset } from '@primevue/themes'
import Aura from '@primevue/themes/aura'
import { setup } from '@storybook/vue3'
import type { Preview } from '@storybook/vue3-vite'
import type { Preview, StoryContext, StoryFn } from '@storybook/vue3-vite'
import { createPinia } from 'pinia'
import 'primeicons/primeicons.css'
import PrimeVue from 'primevue/config'
@@ -9,11 +9,9 @@ import ConfirmationService from 'primevue/confirmationservice'
import ToastService from 'primevue/toastservice'
import Tooltip from 'primevue/tooltip'
import '../src/assets/css/style.css'
import { i18n } from '../src/i18n'
import '../src/lib/litegraph/public/css/litegraph.css'
import { useWidgetStore } from '../src/stores/widgetStore'
import { useColorPaletteStore } from '../src/stores/workspace/colorPaletteStore'
import '@/assets/css/style.css'
import { i18n } from '@/i18n'
import '@/lib/litegraph/public/css/litegraph.css'
const ComfyUIPreset = definePreset(Aura, {
semantic: {
@@ -25,13 +23,11 @@ const ComfyUIPreset = definePreset(Aura, {
// Setup Vue app for Storybook
setup((app) => {
app.directive('tooltip', Tooltip)
// Create Pinia instance
const pinia = createPinia()
app.use(pinia)
// Initialize stores
useColorPaletteStore(pinia)
useWidgetStore(pinia)
app.use(i18n)
app.use(PrimeVue, {
theme: {
@@ -50,8 +46,8 @@ setup((app) => {
app.use(ToastService)
})
// Dark theme decorator
export const withTheme = (Story: any, context: any) => {
// Theme and dialog decorator
export const withTheme = (Story: StoryFn, context: StoryContext) => {
const theme = context.globals.theme || 'light'
// Apply theme class to document root
@@ -63,7 +59,7 @@ export const withTheme = (Story: any, context: any) => {
document.body.classList.remove('dark-theme')
}
return Story()
return Story(context.args, context)
}
const preview: Preview = {

View File

@@ -1,17 +1,61 @@
# Admins
* @Comfy-Org/comfy_frontend_devs
# Desktop/Electron
/src/types/desktop/ @webfiltered
/src/constants/desktopDialogs.ts @webfiltered
/src/constants/desktopMaintenanceTasks.ts @webfiltered
/src/stores/electronDownloadStore.ts @webfiltered
/src/extensions/core/electronAdapter.ts @webfiltered
/src/views/DesktopDialogView.vue @webfiltered
/src/components/install/ @webfiltered
/src/components/maintenance/ @webfiltered
/vite.electron.config.mts @webfiltered
# Maintainers
*.md @Comfy-Org/comfy_maintainer
/tests-ui/ @Comfy-Org/comfy_maintainer
/browser_tests/ @Comfy-Org/comfy_maintainer
/.env_example @Comfy-Org/comfy_maintainer
# Common UI Components
/src/components/chip/ @viva-jinyi
/src/components/card/ @viva-jinyi
/src/components/button/ @viva-jinyi
/src/components/input/ @viva-jinyi
# Translations (AIGODLIKE team + shinshin86)
/src/locales/ @Yorha4D @KarryCharon @DorotaLuna @shinshin86 @Comfy-Org/comfy_maintainer
# Topbar
/src/components/topbar/ @pythongosssss
# Load 3D extension
/src/extensions/core/load3d.ts @jtydhr88 @Comfy-Org/comfy_frontend_devs
# Thumbnail
/src/renderer/core/thumbnail/ @pythongosssss
# Mask Editor extension
/src/extensions/core/maskeditor.ts @brucew4yn3rp @trsommer @Comfy-Org/comfy_frontend_devs
# Legacy UI
/scripts/ui/ @pythongosssss
# Link rendering
/src/renderer/core/canvas/links/ @benceruleanlu
# Node help system
/src/utils/nodeHelpUtil.ts @benceruleanlu
/src/stores/workspace/nodeHelpStore.ts @benceruleanlu
/src/services/nodeHelpService.ts @benceruleanlu
# Selection toolbox
/src/components/graph/selectionToolbox/ @Myestery
# Minimap
/src/renderer/extensions/minimap/ @jtydhr88
# Assets
/src/platform/assets/ @arjansingh
# Workflow Templates
/src/platform/workflow/templates/ @Myestery @christian-byrne @comfyui-wiki
/src/components/templates/ @Myestery @christian-byrne @comfyui-wiki
# Mask Editor
/src/extensions/core/maskeditor.ts @trsommer @brucew4yn3rp
/src/extensions/core/maskEditorLayerFilenames.ts @trsommer @brucew4yn3rp
/src/extensions/core/maskEditorOld.ts @trsommer @brucew4yn3rp
# 3D
/src/extensions/core/load3d.ts @jtydhr88
/src/components/load3d/ @jtydhr88
# Manager
/src/workbench/extensions/manager/ @viva-jinyi @christian-byrne @ltdrdata
# Translations
/src/locales/ @Yorha4D @KarryCharon @shinshin86 @Comfy-Org/comfy_maintainer

View File

@@ -16,9 +16,14 @@ Without this flag, parallel tests will conflict and fail randomly.
### ComfyUI devtools
Clone <https://github.com/Comfy-Org/ComfyUI_devtools> to your `custom_nodes` directory.
ComfyUI_devtools is now included in this repository under `tools/devtools/`. During CI/CD, these files are automatically copied to the `custom_nodes` directory.
_ComfyUI_devtools adds additional API endpoints and nodes to ComfyUI for browser testing._
For local development, copy the devtools files to your ComfyUI installation:
```bash
cp -r tools/devtools/* /path/to/your/ComfyUI/custom_nodes/ComfyUI_devtools/
```
### Node.js & Playwright Prerequisites
Ensure you have Node.js v20 or v22 installed. Then, set up the Chromium test driver:

View File

@@ -0,0 +1 @@
{"id":"4412323e-2509-4258-8abc-68ddeea8f9e1","revision":0,"last_node_id":39,"last_link_id":29,"nodes":[{"id":37,"type":"KSampler","pos":[3635.923095703125,870.237548828125],"size":[428,437],"flags":{},"order":0,"mode":0,"inputs":[{"localized_name":"model","name":"model","type":"MODEL","link":null},{"localized_name":"positive","name":"positive","type":"CONDITIONING","link":null},{"localized_name":"negative","name":"negative","type":"CONDITIONING","link":null},{"localized_name":"latent_image","name":"latent_image","type":"LATENT","link":null},{"localized_name":"seed","name":"seed","type":"INT","widget":{"name":"seed"},"link":null},{"localized_name":"steps","name":"steps","type":"INT","widget":{"name":"steps"},"link":null},{"localized_name":"cfg","name":"cfg","type":"FLOAT","widget":{"name":"cfg"},"link":null},{"localized_name":"sampler_name","name":"sampler_name","type":"COMBO","widget":{"name":"sampler_name"},"link":null},{"localized_name":"scheduler","name":"scheduler","type":"COMBO","widget":{"name":"scheduler"},"link":null},{"localized_name":"denoise","name":"denoise","type":"FLOAT","widget":{"name":"denoise"},"link":null}],"outputs":[{"localized_name":"LATENT","name":"LATENT","type":"LATENT","links":null}],"properties":{"Node name for S&R":"KSampler"},"widgets_values":[0,"randomize",20,8,"euler","simple",1]},{"id":38,"type":"VAEDecode","pos":[4164.01611328125,925.5230712890625],"size":[193.25,107],"flags":{},"order":1,"mode":0,"inputs":[{"localized_name":"samples","name":"samples","type":"LATENT","link":null},{"localized_name":"vae","name":"vae","type":"VAE","link":null}],"outputs":[{"localized_name":"IMAGE","name":"IMAGE","type":"IMAGE","links":null}],"properties":{"Node name for S&R":"VAEDecode"}},{"id":39,"type":"CLIPTextEncode","pos":[3259.289794921875,927.2508544921875],"size":[239.9375,155],"flags":{},"order":2,"mode":0,"inputs":[{"localized_name":"clip","name":"clip","type":"CLIP","link":null},{"localized_name":"text","name":"text","type":"STRING","widget":{"name":"text"},"link":null}],"outputs":[{"localized_name":"CONDITIONING","name":"CONDITIONING","type":"CONDITIONING","links":null}],"properties":{"Node name for S&R":"CLIPTextEncode"},"widgets_values":[""]}],"links":[],"groups":[],"config":{},"extra":{"ds":{"scale":1.1576250000000001,"offset":[-2808.366467322067,-478.34316506594797]}},"version":0.4}

View File

@@ -5,13 +5,14 @@ import dotenv from 'dotenv'
import * as fs from 'fs'
import type { LGraphNode } from '../../src/lib/litegraph/src/litegraph'
import type { NodeId } from '../../src/schemas/comfyWorkflowSchema'
import type { NodeId } from '../../src/platform/workflow/validation/schemas/workflowSchema'
import type { KeyCombo } from '../../src/schemas/keyBindingSchema'
import type { useWorkspaceStore } from '../../src/stores/workspaceStore'
import { NodeBadgeMode } from '../../src/types/nodeSource'
import { ComfyActionbar } from '../helpers/actionbar'
import { ComfyTemplates } from '../helpers/templates'
import { ComfyMouse } from './ComfyMouse'
import { VueNodeHelpers } from './VueNodeHelpers'
import { ComfyNodeSearchBox } from './components/ComfyNodeSearchBox'
import { SettingDialog } from './components/SettingDialog'
import {
@@ -144,6 +145,7 @@ export class ComfyPage {
public readonly templates: ComfyTemplates
public readonly settingDialog: SettingDialog
public readonly confirmDialog: ConfirmDialog
public readonly vueNodes: VueNodeHelpers
/** Worker index to test user ID */
public readonly userIds: string[] = []
@@ -172,6 +174,7 @@ export class ComfyPage {
this.templates = new ComfyTemplates(page)
this.settingDialog = new SettingDialog(page, this)
this.confirmDialog = new ConfirmDialog(page)
this.vueNodes = new VueNodeHelpers(page)
}
convertLeafToContent(structure: FolderStructure): FolderStructure {
@@ -1421,7 +1424,7 @@ export class ComfyPage {
}
async closeDialog() {
await this.page.locator('.p-dialog-close-button').click()
await this.page.locator('.p-dialog-close-button').click({ force: true })
await expect(this.page.locator('.p-dialog')).toBeHidden()
}

View File

@@ -1,4 +1,5 @@
import { Page, test as base } from '@playwright/test'
import type { Page } from '@playwright/test'
import { test as base } from '@playwright/test'
export class UserSelectPage {
constructor(

View File

@@ -0,0 +1,110 @@
/**
* Vue Node Test Helpers
*/
import type { Locator, Page } from '@playwright/test'
export class VueNodeHelpers {
constructor(private page: Page) {}
/**
* Get locator for all Vue node components in the DOM
*/
get nodes(): Locator {
return this.page.locator('[data-node-id]')
}
/**
* Get locator for selected Vue node components (using visual selection indicators)
*/
get selectedNodes(): Locator {
return this.page.locator(
'[data-node-id].outline-black, [data-node-id].outline-white'
)
}
/**
* Get total count of Vue nodes in the DOM
*/
async getNodeCount(): Promise<number> {
return await this.nodes.count()
}
/**
* Get count of selected Vue nodes
*/
async getSelectedNodeCount(): Promise<number> {
return await this.selectedNodes.count()
}
/**
* Get all Vue node IDs currently in the DOM
*/
async getNodeIds(): Promise<string[]> {
return await this.nodes.evaluateAll((nodes) =>
nodes
.map((n) => n.getAttribute('data-node-id'))
.filter((id): id is string => id !== null)
)
}
/**
* Select a specific Vue node by ID
*/
async selectNode(nodeId: string): Promise<void> {
await this.page.locator(`[data-node-id="${nodeId}"]`).click()
}
/**
* Select multiple Vue nodes by IDs using Ctrl+click
*/
async selectNodes(nodeIds: string[]): Promise<void> {
if (nodeIds.length === 0) return
// Select first node normally
await this.selectNode(nodeIds[0])
// Add additional nodes with Ctrl+click
for (let i = 1; i < nodeIds.length; i++) {
await this.page.locator(`[data-node-id="${nodeIds[i]}"]`).click({
modifiers: ['Control']
})
}
}
/**
* Clear all selections by clicking empty space
*/
async clearSelection(): Promise<void> {
await this.page.mouse.click(50, 50)
}
/**
* Delete selected Vue nodes using Delete key
*/
async deleteSelected(): Promise<void> {
await this.page.locator('#graph-canvas').focus()
await this.page.keyboard.press('Delete')
}
/**
* Delete selected Vue nodes using Backspace key
*/
async deleteSelectedWithBackspace(): Promise<void> {
await this.page.locator('#graph-canvas').focus()
await this.page.keyboard.press('Backspace')
}
/**
* Wait for Vue nodes to be rendered
*/
async waitForNodes(expectedCount?: number): Promise<void> {
if (expectedCount !== undefined) {
await this.page.waitForFunction(
(count) => document.querySelectorAll('[data-node-id]').length >= count,
expectedCount
)
} else {
await this.page.waitForSelector('[data-node-id]')
}
}
}

View File

@@ -1,4 +1,4 @@
import { Locator, Page } from '@playwright/test'
import type { Locator, Page } from '@playwright/test'
export class ComfyNodeSearchFilterSelectionPanel {
constructor(public readonly page: Page) {}

View File

@@ -1,6 +1,6 @@
import { Page } from '@playwright/test'
import type { Page } from '@playwright/test'
import { ComfyPage } from '../ComfyPage'
import type { ComfyPage } from '../ComfyPage'
export class SettingDialog {
constructor(

View File

@@ -1,4 +1,4 @@
import { Locator, Page } from '@playwright/test'
import type { Locator, Page } from '@playwright/test'
class SidebarTab {
constructor(

View File

@@ -1,4 +1,5 @@
import { Locator, Page, expect } from '@playwright/test'
import type { Locator, Page } from '@playwright/test'
import { expect } from '@playwright/test'
export class Topbar {
private readonly menuLocator: Locator

View File

@@ -1,6 +1,6 @@
import type { Page } from '@playwright/test'
import type { NodeId } from '../../../src/schemas/comfyWorkflowSchema'
import type { NodeId } from '../../../src/platform/workflow/validation/schemas/workflowSchema'
import { ManageGroupNode } from '../../helpers/manageGroupNode'
import type { ComfyPage } from '../ComfyPage'
import type { Position, Size } from '../types'

View File

@@ -12,9 +12,10 @@ export const webSocketFixture = base.extend<{
// so we can look it up to trigger messages
const store: Record<string, WebSocket> = ((window as any).__ws__ = {})
window.WebSocket = class extends window.WebSocket {
constructor() {
// @ts-expect-error
super(...arguments)
constructor(
...rest: ConstructorParameters<typeof window.WebSocket>
) {
super(...rest)
store[this.url] = this
}
}

View File

@@ -1,4 +1,4 @@
import { FullConfig } from '@playwright/test'
import type { FullConfig } from '@playwright/test'
import dotenv from 'dotenv'
import { backupPath } from './utils/backupUtils'

View File

@@ -1,4 +1,4 @@
import { FullConfig } from '@playwright/test'
import type { FullConfig } from '@playwright/test'
import dotenv from 'dotenv'
import { restorePath } from './utils/backupUtils'

View File

@@ -0,0 +1,104 @@
import type { ReadOnlyRect } from '../../src/lib/litegraph/src/interfaces'
import type { ComfyPage } from '../fixtures/ComfyPage'
interface FitToViewOptions {
selectionOnly?: boolean
zoom?: number
padding?: number
}
/**
* Instantly fits the canvas view to graph content without waiting for UI animation.
*
* Lives outside the shared fixture to keep the default ComfyPage interactions user-oriented.
*/
export async function fitToViewInstant(
comfyPage: ComfyPage,
options: FitToViewOptions = {}
) {
const { selectionOnly = false, zoom = 0.75, padding = 10 } = options
const rectangles = await comfyPage.page.evaluate<
ReadOnlyRect[] | null,
{ selectionOnly: boolean }
>(
({ selectionOnly }) => {
const app = window['app']
if (!app?.canvas) return null
const canvas = app.canvas
const items = (() => {
if (selectionOnly && canvas.selectedItems?.size) {
return Array.from(canvas.selectedItems)
}
try {
return Array.from(canvas.positionableItems ?? [])
} catch {
return []
}
})()
if (!items.length) return null
const rects: ReadOnlyRect[] = []
for (const item of items) {
const rect = item?.boundingRect
if (!rect) continue
const x = Number(rect[0])
const y = Number(rect[1])
const width = Number(rect[2])
const height = Number(rect[3])
rects.push([x, y, width, height] as const)
}
return rects.length ? rects : null
},
{ selectionOnly }
)
if (!rectangles || rectangles.length === 0) return
let minX = Infinity
let minY = Infinity
let maxX = -Infinity
let maxY = -Infinity
for (const [x, y, width, height] of rectangles) {
minX = Math.min(minX, Number(x))
minY = Math.min(minY, Number(y))
maxX = Math.max(maxX, Number(x) + Number(width))
maxY = Math.max(maxY, Number(y) + Number(height))
}
const hasFiniteBounds =
Number.isFinite(minX) &&
Number.isFinite(minY) &&
Number.isFinite(maxX) &&
Number.isFinite(maxY)
if (!hasFiniteBounds) return
const bounds: ReadOnlyRect = [
minX - padding,
minY - padding,
maxX - minX + 2 * padding,
maxY - minY + 2 * padding
]
await comfyPage.page.evaluate(
({ bounds, zoom }) => {
const app = window['app']
if (!app?.canvas) return
const canvas = app.canvas
canvas.ds.fitToBounds(bounds, { zoom })
canvas.setDirty(true, true)
},
{ bounds, zoom }
)
await comfyPage.nextFrame()
}

View File

@@ -1,4 +1,4 @@
import { Locator, Page } from '@playwright/test'
import type { Locator, Page } from '@playwright/test'
export class ManageGroupNode {
footer: Locator

View File

@@ -1,10 +1,10 @@
import { Locator, Page } from '@playwright/test'
import type { Locator, Page } from '@playwright/test'
import path from 'path'
import {
import type {
TemplateInfo,
WorkflowTemplates
} from '../../src/types/workflowTemplateTypes'
} from '../../src/platform/workflow/templates/types/template'
export class ComfyTemplates {
readonly content: Locator

View File

@@ -29,9 +29,9 @@ test.describe('Actionbar', () => {
// Intercept the prompt queue endpoint
let promptNumber = 0
comfyPage.page.route('**/api/prompt', async (route, req) => {
await comfyPage.page.route('**/api/prompt', async (route, req) => {
await new Promise((r) => setTimeout(r, 100))
route.fulfill({
await route.fulfill({
status: 200,
body: JSON.stringify({
prompt_id: promptNumber,

View File

@@ -1,5 +1,5 @@
import type { ComfyPage } from '../fixtures/ComfyPage'
import {
ComfyPage,
comfyExpect as expect,
comfyPageFixture as test
} from '../fixtures/ComfyPage'

View File

@@ -1,4 +1,5 @@
import { Page, expect } from '@playwright/test'
import type { Page } from '@playwright/test'
import { expect } from '@playwright/test'
import { comfyPageFixture as test } from '../fixtures/ComfyPage'

Binary file not shown.

Before

Width:  |  Height:  |  Size: 154 KiB

After

Width:  |  Height:  |  Size: 137 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 152 KiB

After

Width:  |  Height:  |  Size: 131 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 112 KiB

After

Width:  |  Height:  |  Size: 98 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 150 KiB

After

Width:  |  Height:  |  Size: 133 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 154 KiB

After

Width:  |  Height:  |  Size: 137 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 180 KiB

After

Width:  |  Height:  |  Size: 157 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 169 KiB

After

Width:  |  Height:  |  Size: 150 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 168 KiB

After

Width:  |  Height:  |  Size: 149 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 165 KiB

After

Width:  |  Height:  |  Size: 146 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 150 KiB

After

Width:  |  Height:  |  Size: 133 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 114 KiB

After

Width:  |  Height:  |  Size: 102 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 124 KiB

After

Width:  |  Height:  |  Size: 112 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 106 KiB

After

Width:  |  Height:  |  Size: 96 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 113 KiB

After

Width:  |  Height:  |  Size: 102 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 106 KiB

After

Width:  |  Height:  |  Size: 96 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 111 KiB

After

Width:  |  Height:  |  Size: 100 KiB

View File

@@ -1,4 +1,5 @@
import { Locator, expect } from '@playwright/test'
import type { Locator } from '@playwright/test'
import { expect } from '@playwright/test'
import type { Keybinding } from '../../src/schemas/keyBindingSchema'
import { comfyPageFixture as test } from '../fixtures/ComfyPage'
@@ -36,6 +37,10 @@ test('Does not report warning on undo/redo', async ({ comfyPage }) => {
await comfyPage.loadWorkflow('missing/missing_nodes')
await comfyPage.closeDialog()
// 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()
await comfyPage.searchBox.fillAndSelectFirstNode('KSampler')

Binary file not shown.

Before

Width:  |  Height:  |  Size: 101 KiB

After

Width:  |  Height:  |  Size: 88 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 106 KiB

After

Width:  |  Height:  |  Size: 95 KiB

View File

@@ -1,6 +1,6 @@
import { expect } from '@playwright/test'
import { SettingParams } from '../../src/types/settingTypes'
import type { SettingParams } from '../../src/platform/settings/types'
import { comfyPageFixture as test } from '../fixtures/ComfyPage'
test.describe('Topbar commands', () => {
@@ -247,7 +247,7 @@ test.describe('Topbar commands', () => {
test.describe('Dialog', () => {
test('Should allow showing a prompt dialog', async ({ comfyPage }) => {
await comfyPage.page.evaluate(() => {
window['app'].extensionManager.dialog
void window['app'].extensionManager.dialog
.prompt({
title: 'Test Prompt',
message: 'Test Prompt Message'
@@ -267,7 +267,7 @@ test.describe('Topbar commands', () => {
comfyPage
}) => {
await comfyPage.page.evaluate(() => {
window['app'].extensionManager.dialog
void window['app'].extensionManager.dialog
.confirm({
title: 'Test Confirm',
message: 'Test Confirm Message'
@@ -284,7 +284,7 @@ test.describe('Topbar commands', () => {
test('Should allow dismissing a dialog', async ({ comfyPage }) => {
await comfyPage.page.evaluate(() => {
window['value'] = 'foo'
window['app'].extensionManager.dialog
void window['app'].extensionManager.dialog
.confirm({
title: 'Test Confirm',
message: 'Test Confirm Message'

Binary file not shown.

Before

Width:  |  Height:  |  Size: 95 KiB

After

Width:  |  Height:  |  Size: 86 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 111 KiB

After

Width:  |  Height:  |  Size: 100 KiB

View File

@@ -1,6 +1,7 @@
import { expect } from '@playwright/test'
import { ComfyPage, comfyPageFixture as test } from '../fixtures/ComfyPage'
import type { ComfyPage } from '../fixtures/ComfyPage'
import { comfyPageFixture as test } from '../fixtures/ComfyPage'
import type { NodeReference } from '../fixtures/utils/litegraphUtils'
test.describe('Group Node', () => {

View File

@@ -1,12 +1,13 @@
import { Locator, expect } from '@playwright/test'
import { Position } from '@vueuse/core'
import type { Locator } from '@playwright/test'
import { expect } from '@playwright/test'
import type { Position } from '@vueuse/core'
import {
type ComfyPage,
comfyPageFixture as test,
testComfySnapToGridGridSize
} from '../fixtures/ComfyPage'
import { type NodeReference } from '../fixtures/utils/litegraphUtils'
import type { NodeReference } from '../fixtures/utils/litegraphUtils'
test.describe('Item Interaction', () => {
test('Can select/delete all items', async ({ comfyPage }) => {
@@ -1012,6 +1013,8 @@ test.describe('Canvas Navigation', () => {
test('Shift + mouse wheel should pan canvas horizontally', async ({
comfyPage
}) => {
await comfyPage.setSetting('Comfy.Canvas.MouseWheelScroll', 'panning')
await comfyPage.page.click('canvas')
await comfyPage.nextFrame()

Binary file not shown.

Before

Width:  |  Height:  |  Size: 106 KiB

After

Width:  |  Height:  |  Size: 96 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 100 KiB

After

Width:  |  Height:  |  Size: 90 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 81 KiB

After

Width:  |  Height:  |  Size: 75 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 81 KiB

After

Width:  |  Height:  |  Size: 75 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 100 KiB

After

Width:  |  Height:  |  Size: 99 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 106 KiB

After

Width:  |  Height:  |  Size: 96 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 102 KiB

After

Width:  |  Height:  |  Size: 92 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 110 KiB

After

Width:  |  Height:  |  Size: 98 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 105 KiB

After

Width:  |  Height:  |  Size: 95 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 100 KiB

After

Width:  |  Height:  |  Size: 91 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 72 KiB

After

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 118 KiB

After

Width:  |  Height:  |  Size: 106 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 68 KiB

After

Width:  |  Height:  |  Size: 61 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 97 KiB

After

Width:  |  Height:  |  Size: 88 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 108 KiB

After

Width:  |  Height:  |  Size: 97 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 97 KiB

After

Width:  |  Height:  |  Size: 88 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 97 KiB

After

Width:  |  Height:  |  Size: 88 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 109 KiB

After

Width:  |  Height:  |  Size: 99 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 106 KiB

After

Width:  |  Height:  |  Size: 96 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 104 KiB

After

Width:  |  Height:  |  Size: 94 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 62 KiB

After

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 122 KiB

After

Width:  |  Height:  |  Size: 110 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 113 KiB

After

Width:  |  Height:  |  Size: 102 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 112 KiB

After

Width:  |  Height:  |  Size: 101 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 112 KiB

After

Width:  |  Height:  |  Size: 101 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 106 KiB

After

Width:  |  Height:  |  Size: 96 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 58 KiB

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 78 KiB

After

Width:  |  Height:  |  Size: 71 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 58 KiB

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 9.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 96 KiB

After

Width:  |  Height:  |  Size: 88 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 87 KiB

After

Width:  |  Height:  |  Size: 79 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 106 KiB

After

Width:  |  Height:  |  Size: 96 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 109 KiB

After

Width:  |  Height:  |  Size: 99 KiB

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