Commit Graph

6570 Commits

Author SHA1 Message Date
Alexander Brown
aa1aef17de fix: useAssetBrowser test failures
- Add default is_immutable: false to createApiAsset fixture

- Add second category to ownership tests requiring typeCategories.length > 1

Amp-Thread-ID: https://ampcode.com/threads/T-019c18d5-4ae7-72b2-ab01-644aa4739e13
Co-authored-by: Amp <amp@ampcode.com>
2026-02-01 03:02:24 -08:00
Alexander Brown
5597776491 Merge branch 'main' into drjkl/mine-mine-mine 2026-02-01 02:53:53 -08:00
Christian Byrne
544ef5bb70 fix: dedupe queueStore.update() to prevent race conditions (#8523)
## Summary

Prevents stale API responses from overwriting fresh queue state during
rapid consecutive `update()` calls.

## Problem

When `queueStore.update()` is called multiple times in quick succession
(e.g., during websocket reconnection), responses could resolve
out-of-order. A stale response resolving after a fresh one would
overwrite the current state with outdated data.

## Solution

Added request ID tracking that:
1. Increments a counter on each `update()` call
2. Guards all state mutations with a staleness check
3. Only sets `isLoading=false` for the latest request

This pattern matches the existing approach in `jobOutputCache.ts`.

## Changes

- `src/stores/queueStore.ts`: Added `updateRequestId` counter and
staleness guards
- `src/stores/queueStore.test.ts`: Added tests for deduplication
behavior

## Testing

- All 49 existing tests pass
- Added 3 new tests for race condition handling:
  - Stale responses are discarded
  - isLoading state is correctly managed
  - Stale request failures don't affect latest state

## Fixes COM-12784

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8523-fix-dedupe-queueStore-update-to-prevent-race-conditions-2fa6d73d3650810daa0dc63af359e1ea)
by [Unito](https://www.unito.io)

---------

Co-authored-by: Amp <amp@ampcode.com>
2026-01-31 22:01:14 -08:00
Alexander Brown
69129414d0 fix: use PR_GH_TOKEN to trigger e2e after updating expectations (#8525)
## Summary

See https://github.com/Comfy-Org/ComfyUI_frontend/pull/8484

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8525-fix-use-PR_GH_TOKEN-to-trigger-e2e-after-updating-expectations-2fa6d73d3650817c81fccd1cbb3ddadb)
by [Unito](https://www.unito.io)
2026-01-31 21:07:13 -08:00
Christian Byrne
1bbbcfedf0 feat: add provider logo overlays to workflow template thumbnails (#8365)
## Summary

Add support for overlaying provider logos on workflow template
thumbnails at runtime.

## Changes

- **What**: 
  - Add `LogoInfo` interface and `logos` field to `TemplateInfo` type
  - Create `LogoOverlay.vue` component for rendering positioned logos
  - Fetch logo index from `templates/index_logo.json` in store
  - Add `getLogoUrl` helper to `useTemplateWorkflows` composable
  - Integrate `LogoOverlay` into `WorkflowTemplateSelectorDialog`

## Review Focus

- Logo positioning uses Tailwind classes (e.g. `absolute bottom-2
right-2`)
- Supports multiple logos per template with configurable size/opacity
- Gracefully handles missing logos (returns empty string, renders
nothing)
- Templates must explicitly declare logos - no magic inference from
models

## Dependencies

Requires separate PR in workflow_templates repo to:
1. Update `index.schema.json` with logos definition
2. Add `logos` field to templates in `index.json`

## Screenshots (if applicable)

<img width="869" height="719" alt="image"
src="https://github.com/user-attachments/assets/65ed1ee4-fbb4-42c9-95d4-7e37813b3655"
/>


┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8365-feat-add-provider-logo-overlays-to-workflow-template-thumbnails-2f66d73d365081309236c6b991cb6f7b)
by [Unito](https://www.unito.io)

---------

Co-authored-by: Subagent 5 <subagent@example.com>
Co-authored-by: Amp <amp@ampcode.com>
2026-01-31 19:18:13 -08:00
Christian Byrne
4f2872460c fix: remove delete account button and direct users to support (#8515)
## Summary
Reverts PR #5216 delete account functionality. The delete button only
removed Firebase accounts without canceling Stripe subscriptions,
causing orphaned accounts and support issues.

## Changes
- Removed delete account button from UserPanel.vue
- Added text directing users to contact support@comfy.org for account
deletion (clickable mailto: link)
- Cleaned up related code: removed `handleDeleteAccount` from
useCurrentUser.ts, `deleteAccount` from useFirebaseAuthActions.ts,
`_deleteAccount` from firebaseAuthStore.ts
- Updated en/main.json locale with `contactSupport` key using {email}
placeholder

## Testing
- Typecheck and lint pass
- Manual verification: user settings panel shows contact support text
instead of delete button

## Related Issues
Fixes COM-14243

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8515-fix-remove-delete-account-button-and-direct-users-to-support-2fa6d73d3650819dbc83efb41c07a809)
by [Unito](https://www.unito.io)

Co-authored-by: Amp <amp@ampcode.com>
2026-01-31 19:14:44 -08:00
Comfy Org PR Bot
ea6928268f 1.39.4 (#8513)
Patch version increment to 1.39.4

**Base branch:** `main`

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8513-1-39-4-2fa6d73d365081f1b8c2d5ae8e764eb3)
by [Unito](https://www.unito.io)

---------

Co-authored-by: christian-byrne <72887196+christian-byrne@users.noreply.github.com>
Co-authored-by: github-actions <github-actions@github.com>
v1.39.4
2026-01-31 18:18:57 -08:00
Christian Byrne
4a85bffb1f fix: node header on preview has a gap on the right (not flush) (#8487)
## Summary

After changes:

[Screencast from 2026-01-30
00-34-59.webm](https://github.com/user-attachments/assets/9111b8ce-936d-4b30-8b56-6f44aabfe009)

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8487-fix-node-header-on-preview-has-a-gap-on-the-right-not-flush-2f86d73d365081f38cabfca9ab9e04e4)
by [Unito](https://www.unito.io)
2026-01-31 15:57:38 -08:00
AustinMroz
be7c34e28b Update control_after_generate schema (#8505)
Updates `control_after_generate` in the schema to support specifying the
default control value as a string

See Comfy-Org/ComfyUI#12187

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8505-Update-control_after_generate-schema-2f96d73d365081f9bf73c804072bb415)
by [Unito](https://www.unito.io)
2026-01-30 21:34:50 -08:00
Comfy Org PR Bot
a807986835 1.39.3 (#8500)
Patch version increment to 1.39.3

**Base branch:** `main`

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

---------

Co-authored-by: christian-byrne <72887196+christian-byrne@users.noreply.github.com>
Co-authored-by: github-actions <github-actions@github.com>
v1.39.3
2026-01-30 20:30:35 -08:00
Alexander Brown
a2b23075bd fix: This logic needs to be cleaned up... 2026-01-30 18:09:45 -08:00
Alexander Brown
07ceb3ecc9 fix: use factory functions for mutable defaults in defineModel
- FormDropdown: use () => new Set() and () => [] for selected, baseModelSelected, and files defaults

- WidgetSelectDropdown.test: extend interface and remove unsafe casts

Amp-Thread-ID: https://ampcode.com/threads/T-019c11bf-3df5-75ed-bca4-7685ad19885e
Co-authored-by: Amp <amp@ampcode.com>
2026-01-30 17:56:38 -08:00
Alexander Brown
e9c5015cf2 fix: Ownership filter on the general Model Modal 2026-01-30 17:46:42 -08:00
Christian Byrne
679288500a fix(cloud): disable legacy node templates feature on cloud (#8462)
## Summary

Disables the legacy "node templates" feature on ComfyUI Cloud. This
feature is accessed via right-clicking on the canvas and is distinct
from both subgraphs and workflow templates.

## Problem

The legacy node templates feature presents false-positive errors on
cloud:
- Errors appear when no existing templates exist initially
- After refresh, workflow progress is lost from workflow tabs
- Node templates fail to delete (except the first one)

## Solution

Conditionally load the `nodeTemplates` extension only for non-cloud
builds by wrapping the import in `if (!isCloud)`.

## Testing

- [ ] Verify right-click canvas menu no longer shows "Node Templates"
submenu on cloud
- [ ] Verify right-click canvas menu no longer shows "Save Selected as
Template" on cloud
- [ ] Verify the feature still works on desktop/OSS builds

## Related

- Refs: COM-14105
- Related issue: #4056 (Migrate Node Templates to Workflows)

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8462-fix-cloud-disable-legacy-node-templates-feature-on-cloud-2f86d73d36508162948cc897d4c7ef5e)
by [Unito](https://www.unito.io)

Co-authored-by: Amp <amp@ampcode.com>
2026-01-30 16:51:31 -08:00
Christian Byrne
34d42311ea fix: add security params to Contact Support window.open call (#8470)
## Summary

Adds `'noopener,noreferrer'` to the `window.open()` call in
`Comfy.ContactSupport` command, aligning with the established codebase
pattern for external links.

## Changes

- Updated `src/composables/useCoreCommands.ts` line 865 to include
security parameters

## Testing

- [x] Typecheck passes
- [x] Lint passes

## Related

- Fixes COM-13631
- Notion: https://www.notion.so/2ee6d73d365081328ae5cc7bce64b310

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8470-fix-add-security-params-to-Contact-Support-window-open-call-2f86d73d365081e38ea1c1ea9c9883f5)
by [Unito](https://www.unito.io)

Co-authored-by: Amp <amp@ampcode.com>
2026-01-30 16:51:04 -08:00
Christian Byrne
c4e5fc8dbf fix: update reactive ref after merge in imagePreviewStore (#8479)
## Summary
Fix for COM-14110: Preview image does not display new outputs in
vue-nodes.

## Problem
The merge logic in `setOutputsByLocatorId` updated `app.nodeOutputs` but
returned early without updating the reactive `nodeOutputs.value` ref.
This caused Vue components to never receive merged output updates
because only the non-reactive `app.nodeOutputs` was being updated.

## Solution
Added `nodeOutputs.value[nodeLocatorId] = existingOutput` after the
merge loop, before the return statement.

## Testing
- Added 2 unit tests covering the merge behavior
- All 4076 existing unit tests pass
- Typechecks pass
- Lint passes

## Notes
- Related open PRs touching same files: #8143, #8366 - potential minor
conflicts possible

Fixes COM-14110

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8479-fix-update-reactive-ref-after-merge-in-imagePreviewStore-2f86d73d365081f1a145fa5a9782515f)
by [Unito](https://www.unito.io)

Co-authored-by: Amp <amp@ampcode.com>
2026-01-30 16:49:57 -08:00
Alexander Brown
1f0888995a feat: add base model filter to FormDropdown (#8501)
## Summary

Adds a base model filter to the FormDropdown component, allowing users
to filter dropdown options by base model type.

## Changes

- **Base model filter**: New filter popover in FormDropdownMenuActions
that lets users select one or more base models to filter by
- **Clear Filters button**: Added to the base model filter popover for
quick reset
- **Button component refactor**: Replaced native `<button>` elements
with the `Button` component for consistent styling
- **New i18n key**: Added `assets.baseModel` translation

## Files Changed

- `FormDropdownMenuActions.vue` - Main implementation of base model
filter UI
- `WidgetSelectDropdown.vue` - Passes base model options to the dropdown
- `FormDropdown.vue`, `FormDropdownMenu.vue`,
`FormDropdownMenuFilter.vue` - Prop threading
- `filterTypes.ts`, `types.ts` - Type definitions for filter options

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8501-feat-add-base-model-filter-to-FormDropdown-2f96d73d3650813c994debb06070c7dd)
by [Unito](https://www.unito.io)

---------

Co-authored-by: Amp <amp@ampcode.com>
2026-01-30 16:28:05 -08:00
Alexander Brown
98fb1ee407 fix: type safety and i18n improvements for dropdown components
- Add i18n for sort options (sortDefault, sortAZ)

- Fix type assertions in FormSearchInput and FormDropdown

- Fix missing value check using getAssetFilename

- Add aria-label to ownership filter button

- Update test for undefined date handling

Amp-Thread-ID: https://ampcode.com/threads/T-019c1136-78ff-7149-a185-a814b0e4f933
Co-authored-by: Amp <amp@ampcode.com>
2026-01-30 15:22:27 -08:00
Alexander Brown
9e8b221ee6 refactor: apply Vue 3.5 same-name shorthand for prop bindings
Amp-Thread-ID: https://ampcode.com/threads/T-019c1104-8e9c-7364-9132-315e5d9f7694
Co-authored-by: Amp <amp@ampcode.com>
2026-01-30 14:41:06 -08:00
Alexander Brown
89b62b9710 chore: remove unnecessary comments from PR files
Amp-Thread-ID: https://ampcode.com/threads/T-019c10ff-a769-744c-a6dd-367eaa79f98b
Co-authored-by: Amp <amp@ampcode.com>
2026-01-30 14:25:09 -08:00
Alexander Brown
0d88869091 feat: add ownership filtering to FormDropdown
- Add ownership filter popover to FormDropdownMenuActions

- Extend FormDropdownItem with is_immutable field

- Filter assets by my-models/public-models in WidgetSelectDropdown

- Add OwnershipFilterOption type to filterTypes.ts

Amp-Thread-ID: https://ampcode.com/threads/T-019c10d8-2c5c-7763-a6cf-c033c6229eec
Co-authored-by: Amp <amp@ampcode.com>
2026-01-30 14:15:35 -08:00
Alexander Brown
c10a10c02d refactor: replace AssetDropdownItem with FormDropdownItem
- Create minimal FormDropdownItem interface in dropdown/types.ts

- Remove dropdownItems computed from useAssetWidgetData

- Transform assets in WidgetSelectDropdown using getAssetFilename()

- Delete assetDropdownTypes.ts and assetDropdownUtils.ts

- Update all FormDropdown components to use FormDropdownItem

Amp-Thread-ID: https://ampcode.com/threads/T-019c10c6-5e4c-774a-90ce-cb00178ad6e3
Co-authored-by: Amp <amp@ampcode.com>
2026-01-30 13:31:50 -08:00
Johnpaul Chiwetelu
a64c561a5f Road to No explicit any: Group 8 (part 8) test files (#8496)
## Summary

This PR removes unsafe type assertions ("as unknown as Type") from test
files and improves type safety across the codebase.

### Key Changes

#### Type Safety Improvements
- Removed improper `as unknown as Type` patterns from test files in
Group 8 part 8
- Replaced with proper TypeScript patterns using Pinia store testing
patterns
- Fixed parameter shadowing issue in typeGuardUtil.test.ts (constructor
→ nodeConstructor)
- Fixed stale mock values in useConflictDetection.test.ts using getter
functions
- Refactored useManagerState tests to follow proper Pinia store testing
patterns with createTestingPinia

### Files Changed

Test files (Group 8 part 8 - utils and manager composables):
- src/utils/typeGuardUtil.test.ts - Fixed parameter shadowing
- src/utils/graphTraversalUtil.test.ts - Removed unsafe type assertions
- src/utils/litegraphUtil.test.ts - Improved type handling
- src/workbench/extensions/manager/composables/useManagerState.test.ts -
Complete rewrite using Pinia testing patterns
-
src/workbench/extensions/manager/composables/useConflictDetection.test.ts
- Fixed stale mock values with getters
- src/workbench/extensions/manager/composables/useManagerQueue.test.ts -
Type safety improvements
-
src/workbench/extensions/manager/composables/nodePack/useMissingNodes.test.ts
- Removed unsafe casts
-
src/workbench/extensions/manager/composables/nodePack/usePacksSelection.test.ts
- Type improvements
-
src/workbench/extensions/manager/composables/nodePack/usePacksStatus.test.ts
- Type improvements
- src/workbench/extensions/manager/utils/versionUtil.test.ts - Type
safety fixes

Source files (minor type fixes):
- src/utils/fuseUtil.ts - Type improvements
- src/utils/linkFixer.ts - Type safety fixes
- src/utils/syncUtil.ts - Type improvements
-
src/workbench/extensions/manager/composables/nodePack/useWorkflowPacks.ts
- Type fix
-
src/workbench/extensions/manager/composables/useConflictAcknowledgment.ts
- Type fix

### Testing
- All TypeScript type checking passes (`pnpm typecheck`)
- All affected test files pass (`pnpm test:unit`)
- Linting passes without errors (`pnpm lint`)
- Code formatting applied (`pnpm format`)

Part of the "Road to No Explicit Any" initiative, cleaning up type
casting issues from branch `fix/remove-any-types-part8`.

### Previous PRs in this series:
- Part 2: #7401
- Part 3: #7935
- Part 4: #7970
- Part 5: #8064
- Part 6: #8083
- Part 7: #8092
- Part 8 Group 1: #8253
- Part 8 Group 2: #8258
- Part 8 Group 3: #8304
- Part 8 Group 4: #8314
- Part 8 Group 5: #8329
- Part 8 Group 6: #8344
- Part 8 Group 7: #8459
- Part 8 Group 8: #8496 (this PR)

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8496-Road-to-No-explicit-any-Group-8-part-8-test-files-2f86d73d365081f3afdcf8d01fba81e1)
by [Unito](https://www.unito.io)

---------

Co-authored-by: GitHub Action <action@github.com>
2026-01-30 22:25:10 +01:00
Alexander Brown
59c58379fe fix: migrate remaining ECMAScript private fields to TypeScript private (#8495)
Migrates remaining `#field` syntax to `private _field` for Vue
reactivity compatibility.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8495-fix-migrate-remaining-ECMAScript-private-fields-to-TypeScript-private-2f86d73d365081ec87afe2273c0ff6eb)
by [Unito](https://www.unito.io)

Co-authored-by: Amp <amp@ampcode.com>
2026-01-30 13:24:16 -08:00
Alexander Brown
22bb79adcc refactor: unify DropdownItem with AssetDropdownItem
- Create AssetDropdownItem in platform/assets/types

- Create toAssetDropdownItem() transform utility

- Remove deprecated DropdownItem, SelectedKey aliases

- Rename mediaSrc to previewUrl, remove unused metadata field

- Import types directly from source, remove re-exports

Amp-Thread-ID: https://ampcode.com/threads/T-019c10b4-cabd-779d-a787-1ebf5dc8a067
Co-authored-by: Amp <amp@ampcode.com>
2026-01-30 13:15:22 -08:00
Alexander Brown
bbceecc94a refactor: unify asset sorting between AssetBrowser and FormDropdown
- Create shared sortAssets() utility in src/platform/assets/utils/assetSortUtils.ts
- Add 'default' option to AssetSortOption type for preserve-order sorting
- Extract shared filter types (OptionId, FilterOption, OwnershipOption, AssetSortOption) to src/platform/assets/types/filterTypes.ts
- Update useAssetBrowser.ts to use sortAssets() (removes inline switch statement)
- Update FormDropdown shared.ts to delegate to sortAssets()
- Make SortOption interface generic for type-safe sort IDs
- Add comprehensive tests for assetSortUtils

Amp-Thread-ID: https://ampcode.com/threads/T-019c10a1-1209-75bd-9c79-f312cee89f50
Co-authored-by: Amp <amp@ampcode.com>
2026-01-30 12:59:23 -08:00
Alexander Brown
b067bf733b Reorder by volatility 2026-01-30 12:21:20 -08:00
Alexander Brown
de2992aca6 feat: show ownership filter when category is selected in left nav
Show the ownership filter dropdown in AssetFilterBar when a specific

category (not 'all' or 'imported') is selected, allowing users to

filter by ownership within category views.

Amp-Thread-ID: https://ampcode.com/threads/T-019c108b-1791-71fd-b480-cb6c5ab4a17e
Co-authored-by: Amp <amp@ampcode.com>
2026-01-30 12:18:07 -08:00
Alexander Brown
845a2b42ea feat: add ownership filter to AssetFilterBar for modal without left nav
When AssetBrowserModal opens with a category filter (e.g., from a node widget), the left navigation panel is hidden. This adds an ownership dropdown to the filter bar that allows users to filter by 'All', 'My models', or 'Public models' - providing the same functionality as the 'Imported' nav item.

Amp-Thread-ID: https://ampcode.com/threads/T-019c1066-b236-705f-bc33-25781a6f9e9e
Co-authored-by: Amp <amp@ampcode.com>
2026-01-30 12:09:48 -08:00
AustinMroz
6c14ae6f90 Don't bypass subgraph contents with subgraph (#8494)
When bypassing or muting a subgraph the contents are no longer bypassed
along with it. This behaviour was less than ideal because it meant that
toggling the bypass of a single subgraph node twice could change the
behaviour of the node.

It is entirely intended that a subgraph node which is bypassed does not
have it's children execute. As part of testing this behaviour, it was
found that nodes inside of a bypassed subgraph are still considered for
execution even if boundry links are treated as disconnected. The
following example would execute even if the subgraph is muted.
<img width="826" height="476" alt="image"
src="https://github.com/user-attachments/assets/7b282873-e114-494d-b8f1-74c373859151"
/>

To resolve this, the PR does not add the contents of a subgraphNode
which is muted or bypassed to the execution map.

Resolves #8489

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8494-Don-t-bypass-subgraph-contents-with-subgraph-2f86d73d365081aeba8dd2990b6ba0ad)
by [Unito](https://www.unito.io)
2026-01-30 10:56:44 -08:00
Christian Byrne
ee600a8951 fix: prevent image/video preview reset on dynamic widget addition (#8366)
## Summary

Fixes image/video previews getting stuck in loading state when widgets
are added dynamically to a node.

## Problem

When dynamic widgets are added to a node (e.g., by extensions), Vue
reactivity triggers the watch on `imageUrls` prop even when the URL
content is identical—the array has a new reference but the same values.
This caused:
1. `startDelayedLoader()` to reset loading state to pending
2. If the cached image doesn't trigger `@load` before the 250ms timeout,
the loader shows and stays stuck

## Solution

Compare URL arrays by content, not reference. Only reset loading state
when URLs actually change:
- Check array length and element-by-element equality
- Return early if URLs are identical (just a new array reference)
- Remove `deep: true` since we compare manually

## Screenshots

<img width="749" height="647" alt="image"
src="https://github.com/user-attachments/assets/3a1ff656-59ed-467a-a121-b70b91423a50"
/>


<img width="749" height="647" alt="Screenshot from 2026-01-28 15-24-18"
src="https://github.com/user-attachments/assets/28265dad-1d79-47c8-9fd4-5a82b94e72cd"
/>

<img width="749" height="647" alt="Screenshot from 2026-01-28 15-24-05"
src="https://github.com/user-attachments/assets/c7af93b7-c898-405f-860b-0f82abe5af6d"
/>

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8366-fix-prevent-image-video-preview-reset-on-dynamic-widget-addition-2f66d73d3650819483b2d5cbfb78187f)
by [Unito](https://www.unito.io)

---------

Co-authored-by: Subagent 5 <subagent@example.com>
Co-authored-by: Amp <amp@ampcode.com>
2026-01-30 08:27:39 -08:00
Christian Byrne
e7d3bc7285 fix: properties panel obscures menus in legacy layout (#8474)
## Summary

Fixes overlap issue where the new menu's node properties panel could
cover the old floating menu.

## Changes

Hide the properties panel when using the legacy menu, so it doesn't
obscure the old menu. The properties panel did not work anyway (empy
content).


┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8474-fix-increase-z-index-of-legacy-floating-menu-2f86d73d365081a0ab44db24c2ea6357)
by [Unito](https://www.unito.io)

---------

Co-authored-by: Amp <amp@ampcode.com>
2026-01-30 08:16:11 -08:00
Alexander Brown
b4649bc96d Set IS_NIGHTLY right when we build (#8482)
## Summary

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8482-Set-IS_NIGHTLY-right-when-we-build-2f86d73d365081009fdbc2aee8f7545d)
by [Unito](https://www.unito.io)
2026-01-30 01:11:41 -08:00
sno
d7ec24abc3 fix: use PR_GH_TOKEN in lint/i18n workflows to trigger e2e tests (#8484)
## Summary

Add `PR_GH_TOKEN` to ci-lint-format and i18n-update-core workflows to
ensure commits trigger e2e test runs.

## Changes

- Add `token: ${{ secrets.PR_GH_TOKEN }}` to checkout step in
`.github/workflows/ci-lint-format.yaml`
- Add `token: ${{ secrets.PR_GH_TOKEN }}` to checkout step in
`.github/workflows/i18n-update-core.yaml`

## Context

This matches the pattern used consistently across all other workflows in
the repository (release-version-bump, version-bump-desktop-ui,
api-update-registry-api-types, i18n-update-nodes, etc.).

Without this token, commits made by these workflows don't trigger
downstream e2e tests, which can lead to issues being missed.

## Original Work

Original idea & implementation by @drjkl in commits:
- be3a8d61c - fix: use GitHub App token for lint/i18n workflows to
trigger e2e tests
- 50ccb254b - Add github app token action to pinact

This PR simplifies the approach to use the existing `PR_GH_TOKEN` secret
instead of GitHub App tokens, for consistency with the rest of the
repository.

## Test Plan

- [x] Verify workflows can checkout code successfully
- [x] Confirm commits from these workflows trigger e2e test runs

## It works!
<img width="1075" height="402" alt="image"
src="https://github.com/user-attachments/assets/31762d68-a37f-4926-b463-84d184d4309d"
/>

---------

Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
Co-authored-by: GitHub Action <action@github.com>
2026-01-30 00:09:28 -08:00
Christian Byrne
1ac214a1cd docs: add Vite preload error handling documentation comment (#8475)
## Summary

Add documentation comment explaining the Vite preload error handler with
a link to official documentation.

## Changes

- **What**: Added a 2-line comment above the `vite:preloadError` event
listener in App.vue explaining its purpose and linking to
https://vite.dev/guide/build#load-error-handling

Fixes COM-14132

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8475-docs-add-Vite-preload-error-handling-documentation-comment-2f86d73d3650815f9731f30bd9c5eb57)
by [Unito](https://www.unito.io)

Co-authored-by: Amp <amp@ampcode.com>
2026-01-29 21:06:30 -08:00
Christian Byrne
985d024a6e fix: handle non-string serverLogs in error report (#8460)
## Summary
- Fix `[object Object]` display in error report logs section

## Changes
- Add runtime type check for `serverLogs` in error report template
- JSON stringify object logs with proper formatting

## Test plan
- [x] Verify string logs still display correctly
- [x] Verify object logs are properly stringified instead of showing
`[object Object]`

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

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8460-fix-handle-non-string-serverLogs-in-error-report-2f86d73d36508179af5afcdeec025a75)
by [Unito](https://www.unito.io)

Co-authored-by: Subagent 5 <subagent@example.com>
2026-01-29 19:47:59 -08:00
Christian Byrne
ee4a205d32 fix: Vue mode socket map data not cleaned up on dynamic input changes (#8469)
## Summary

Fixes a bug where socket map data was not properly removed when sockets
are dynamically added/removed via DynamicCombo widgets in Vue mode
(Nodes 2.0).

## Problem

When DynamicCombo widgets (e.g., `should_remesh` on Meshy nodes) change
their selection, inputs are dynamically added/removed. The Vue `v-for`
loop in `NodeSlots.vue` was using array index as the `:key`, causing Vue
to **reuse** slot components instead of properly unmounting them.

This led to:
- Socket map entries leaking (never cleaned up)
- Socket positions becoming desynced
- Stale cached offset data

## Solution

1. **Use slot `name` as Vue key** instead of array index in
`NodeSlots.vue`
   - Slot names are unique per node (enforced by ComfyUI backend)
- When a slot is removed, Vue sees the key disappear and properly
unmounts the component
- `onUnmounted` cleanup in `useSlotElementTracking` now runs correctly

2. **Add defensive cleanup** in `useSlotElementTracking.ts`
- Before registering a new slot, check if a stale entry exists with the
same key
   - Clean up stale entry to handle any edge cases

## Related

- Fixes COM-12970
- Related to #7837 (fixed LiteGraph version of this bug, but not Vue
mode)

## Testing

- Quality checks pass (typecheck, lint, format)
- Manual testing with DynamicCombo nodes (Meshy, nodes_logic)
recommended

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8469-fix-Vue-mode-socket-map-data-not-cleaned-up-on-dynamic-input-changes-2f86d73d365081e599eadca4f15e6b6e)
by [Unito](https://www.unito.io)

---------

Co-authored-by: Subagent 5 <subagent@example.com>
Co-authored-by: Amp <amp@ampcode.com>
2026-01-29 19:47:43 -08:00
Kelly Yang
d784d4982b Improve template search input performance issue (#8343)
## Summary

Improve Template search input performance issue #8134
This was caused by the search logic running too frequently (throttled at
50ms), causing the main thread to block on every few keystrokes.


## Changes
Use debouncing that wait until you stop typing for a specific time
(300ms) before running.
It makes the searching function more smoothly.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8343-Improve-template-search-input-performance-issue-2f56d73d36508144bdf9fa5e0cd76818)
by [Unito](https://www.unito.io)

---------

Co-authored-by: Alexander Brown <drjkl@comfy.org>
2026-01-29 19:05:37 -08:00
Christian Byrne
47113b117e refactor: migrate keybindings to DDD structure (#8369)
## Summary

Migrate keybindings domain to `src/platform/keybindings/` following DDD
principles.

## Changes

- **What**: Consolidate keybinding-related code (types, store, service,
defaults, reserved keys) into a single domain module with flat structure
- Extracted `KeyComboImpl` and `KeybindingImpl` classes into separate
files
- Updated all consumers to import from new location
- Colocated tests with source files
- Updated stores/README.md and services/README.md to remove migrated
entries

## Review Focus

- Verify all import paths were updated correctly
- Check that the flat structure is appropriate (vs nested core/data/ui
layers)

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8369-refactor-migrate-keybindings-to-DDD-structure-2f66d73d36508120b169dc737075fb45)
by [Unito](https://www.unito.io)

---------

Co-authored-by: Subagent 5 <subagent@example.com>
Co-authored-by: Amp <amp@ampcode.com>
2026-01-29 18:40:58 -08:00
Johnpaul Chiwetelu
13311a46ea Road to No explicit any: Group 8 (part 7) test files (#8459)
## Summary

This PR removes unsafe type assertions ("as unknown as Type") from test
files and improves type safety across the codebase.

### Key Changes

#### Type Safety Improvements
- Removed improper `as unknown as Type` patterns from 17 test files in
Group 8 part 7
- Replaced with proper TypeScript patterns using factory functions and
Mock types
- Fixed createTestingPinia usage in test files (was incorrectly using
createPinia)
- Fixed vi.hoisted pattern for mockSetDirty in viewport tests  
- Fixed vi.doMock lint issues with vi.mock and vi.hoisted pattern
- Retained necessary `as unknown as` casts only for complex mock objects
where direct type assertions would fail

### Files Changed

Test files (Group 8 part 7 - services, stores, utils):
- src/services/nodeOrganizationService.test.ts
- src/services/providers/algoliaSearchProvider.test.ts
- src/services/providers/registrySearchProvider.test.ts
- src/stores/comfyRegistryStore.test.ts
- src/stores/domWidgetStore.test.ts
- src/stores/executionStore.test.ts
- src/stores/firebaseAuthStore.test.ts
- src/stores/modelToNodeStore.test.ts
- src/stores/queueStore.test.ts
- src/stores/subgraphNavigationStore.test.ts
- src/stores/subgraphNavigationStore.viewport.test.ts
- src/stores/subgraphStore.test.ts
- src/stores/systemStatsStore.test.ts
- src/stores/workspace/nodeHelpStore.test.ts
- src/utils/colorUtil.test.ts
- src/utils/executableGroupNodeChildDTO.test.ts

Source files:
- src/stores/modelStore.ts - Improved type handling

### Testing
- All TypeScript type checking passes (`pnpm typecheck`)
- All affected test files pass (`pnpm test:unit`)
- Linting passes without errors (`pnpm lint`)
- Code formatting applied (`pnpm format`)

Part of the "Road to No Explicit Any" initiative, cleaning up type
casting issues from branch `fix/remove-any-types-part8`.

### Previous PRs in this series:
- Part 2: #7401
- Part 3: #7935
- Part 4: #7970
- Part 5: #8064
- Part 6: #8083
- Part 7: #8092
- Part 8 Group 1: #8253
- Part 8 Group 2: #8258
- Part 8 Group 3: #8304
- Part 8 Group 4: #8314
- Part 8 Group 5: #8329
- Part 8 Group 6: #8344
- Part 8 Group 7: #8459 (this PR)

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8459-Road-to-No-explicit-any-Group-8-part-7-test-files-2f86d73d36508114ad28d82e72a3a5e9)
by [Unito](https://www.unito.io)
2026-01-30 03:38:06 +01:00
Alexander Brown
067d80c4ed refactor: migrate ES private fields to TypeScript private for Vue Proxy compatibility (#8440)
## Summary

Migrates ECMAScript private fields (`#`) to TypeScript private
(`private`) across LiteGraph to fix Vue Proxy reactivity
incompatibility.

## Problem

ES private fields (`#field`) are incompatible with Vue's Proxy-based
reactivity system - accessing `#field` through a Proxy throws
`TypeError: Cannot read private member from an object whose class did
not declare it`.

## Solution

- Converted all `#field` to `private _field` across 10 phases
- Added `toJSON()` methods to `LGraph`, `NodeSlot`, `NodeInputSlot`, and
`NodeOutputSlot` to prevent circular reference errors during
serialization (TypeScript private fields are visible to `JSON.stringify`
unlike true ES private fields)
- Made `DragAndScale.element.data` non-enumerable to break canvas
circular reference chain

## Testing

- All 4027 unit tests pass
- Added 9 new serialization tests to catch future circular reference
issues
- Browser tests (undo/redo, save workflows) verified working

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8440-refactor-migrate-ES-private-fields-to-TypeScript-private-for-Vue-Proxy-compatibility-2f76d73d365081a3bd82d429a3e0fcb7)
by [Unito](https://www.unito.io)

---------

Co-authored-by: Amp <amp@ampcode.com>
2026-01-29 18:18:58 -08:00
Alexander Brown
82bacb82a7 test: add Playwright test tags for filtering (@smoke, @slow, @screenshot, domains) (#8441)
## Summary

Adds structured test tags to all 54 Playwright test files to enable
flexible test filtering during development and CI.

## Tags Added

| Tag | Count | Purpose |
|-----|-------|---------|
| `@screenshot` | 32 files | Tests with visual assertions
(`toHaveScreenshot`) |
| `@smoke` | 5 files | Quick essential tests for fast validation |
| `@slow` | 5 files | Long-running tests (templates, subgraph,
featureFlags) |
| `@canvas` | 15 files | Canvas/graph rendering tests |
| `@node` | 10 files | Node behavior tests |
| `@ui` | 8 files | UI component tests |
| `@widget` | 5 files | Widget-specific tests |
| `@workflow` | 3 files | Workflow operations |
| `@subgraph` | 1 file | Subgraph functionality |
| `@keyboard` | 2 files | Keyboard shortcuts |
| `@settings` | 2 files | Settings/preferences |

## Usage Examples

```bash
# Quick validation (~16 tests, ~30s)
pnpm test:browser -- --grep @smoke

# Skip slow tests for faster CI feedback
pnpm test:browser -- --grep-invert @slow

# Skip visual tests (useful for local development without snapshots)
pnpm test:browser -- --grep-invert @screenshot

# Run only canvas-related tests
pnpm test:browser -- --grep @canvas

# Combine filters
pnpm test:browser -- --grep @smoke --grep-invert @screenshot
```

## Implementation Details

- Uses Playwright's native tag syntax: `test.describe('Name', { tag:
'@tag' }, ...)`
- Tags inherit from describe blocks to child tests
- Preserves existing project-level tags: `@mobile`, `@2x`, `@0.5x`
- Multiple tags supported: `{ tag: ['@screenshot', '@smoke'] }`

## Test Plan

- [x] All existing tests pass unchanged
- [x] Tag filtering works with `--grep` and `--grep-invert`

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8441-test-add-Playwright-test-tags-for-filtering-smoke-slow-screenshot-domains-2f76d73d36508184990ec859c8fd7629)
by [Unito](https://www.unito.io)

---------

Co-authored-by: Amp <amp@ampcode.com>
Co-authored-by: GitHub Action <action@github.com>
2026-01-29 16:34:56 -08:00
Christian Byrne
80ccc13659 feat: add Chatterbox model support for Cloud asset browser (#8418)
## Summary

Adds support for creating Chatterbox TTS nodes when clicking Chatterbox
models in the Cloud asset browser.

## Changes

### modelToNodeStore.ts
- Add `findProvidersWithFallback()` helper for hierarchical model type
lookups (e.g., `parent/child` falls back to `parent`)
- Register 4 Chatterbox model directories with empty widget keys:
  - `chatterbox/chatterbox` → `FL_ChatterboxTTS`
  - `chatterbox/chatterbox_turbo` → `FL_ChatterboxTurboTTS`
- `chatterbox/chatterbox_multilingual` → `FL_ChatterboxMultilingualTTS`
  - `chatterbox/chatterbox_vc` → `FL_ChatterboxVC`

### createModelNodeFromAsset.ts
- Skip widget assignment when `provider.key` is empty (for nodes that
auto-load models without a widget selector)

### Tests
- Add tests for hierarchical fallback behavior
- Add tests for empty widget key (auto-load nodes)
- Add Chatterbox node types to mock data

## Notes

- Empty `key` convention: Chatterbox nodes auto-load their models and
don't have a model selector widget, so we register them with `key: ''`
and skip the widget assignment step
- Hierarchical fallback only goes one level deep (`a/b/c` → `a`, not
`a/b`)

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8418-feat-add-Chatterbox-model-support-for-Cloud-asset-browser-2f76d73d365081be822bc369b155f099)
by [Unito](https://www.unito.io)

---------

Co-authored-by: Subagent 5 <subagent@example.com>
Co-authored-by: Amp <amp@ampcode.com>
Co-authored-by: Alexander Brown <drjkl@comfy.org>
Co-authored-by: GitHub Action <action@github.com>
2026-01-29 16:26:51 -08:00
AustinMroz
dce6fd1040 Fix invalid keybind flash (#8435)
Previously the save keybind action would
- apply the new keybind
- wait for a network request to persist the change
- close the dialogue regardless of the results of the above changes

During this network request, the dialog would show a warning that the
keybind is invalid because the dialogue "contains a modified keybind
which conflicts with an existing keybind"
<img width="506" height="261" alt="image"
src="https://github.com/user-attachments/assets/e46150ce-9349-4f8e-b3b5-fb0b20dd3db9"
/>


This PR changes the order these actions are applied in.
- The dialogue is immediately closed
- The keybinding is updated if valid
- The keybinding is persisted.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8435-Fix-invalid-keybind-flash-2f76d73d3650815c9657f35e77d331fe)
by [Unito](https://www.unito.io)
2026-01-29 16:26:07 -08:00
AustinMroz
e2625a4055 Fix paste-with-links breaking autogrow connections (#8442)
The extra `linf` check was made in 878c8c0f39. I'm no longer able to
replicate the cloning bug the check was introduced for.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8442-Fix-paste-with-links-breaking-autogrow-connections-2f76d73d3650817aa99cc3b9e4e6412c)
by [Unito](https://www.unito.io)
2026-01-29 16:25:16 -08:00
Alexander Brown
a4cf9a1ca8 Fix: Hide Jobs in Assets Panel when Queue V2 is disabled. (#8450)
## Summary

See Title.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8450-Fix-Hide-Jobs-in-Assets-Panel-when-Queue-V2-is-disabled-2f86d73d3650810c8155c1fea92fc0aa)
by [Unito](https://www.unito.io)
2026-01-29 16:23:16 -08:00
Jin Yi
d437b96238 [bugfix] Fix shift+click deselection in asset panel (#8396)
## Summary
Fix shift+click range selection not properly deselecting assets when
selecting a smaller range, and improve selection performance.

## Changes
- **Bug Fix**: Shift+click now replaces selection with the new range
instead of combining with existing selection
- **Performance**: Remove unnecessary `.every()` check in `setSelection`
(O(n) → O(1))
- **Tests**: Add 23 unit tests for asset selection logic

## Test Plan
- [x] Click 1st asset → only 1st selected
- [x] Shift+click 3rd asset → items 1-3 selected
- [x] Shift+click 1st asset → only 1st selected (was broken, now fixed)
- [x] All 23 new unit tests pass

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

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8396-bugfix-Fix-shift-click-deselection-in-asset-panel-2f76d73d3650814ca060d1e6a40cf6d4)
by [Unito](https://www.unito.io)
2026-01-29 16:21:08 -08:00
Christian Byrne
f1cf8073d6 fix: garbage collect subgraph definitions when SubgraphNode is removed (#8187)
## Summary

When removing a SubgraphNode via `LGraph.remove()`:
- Fire `onRemoved` for all nodes inside the subgraph
- Fire `onNodeRemoved` callback on the subgraph for each inner node
- Remove subgraph definition from `rootGraph.subgraphs` when no other
nodes reference it (checks both root graph nodes and nodes inside other
subgraphs)

This ensures proper cleanup of subgraph definitions and lifecycle
callbacks for nested nodes when subgraph nodes are deleted.

## Changes

### LGraph.ts
Added SubgraphNode-specific cleanup in `remove()` method that:
1. Iterates inner nodes and fires their `onRemoved` callbacks
2. Fires `onNodeRemoved` on the subgraph for downstream listeners (e.g.,
minimap)
3. Garbage collects the subgraph definition when no other nodes
reference it

### SubgraphNode.ts
Fixed `graph` property to match `LGraphNode` lifecycle contract.
Previously it was declared as `override readonly graph` via constructor
parameter promotion, which prevented `LGraph.remove()` from setting
`node.graph = null`. Changed to a regular mutable property with null
guard in `rootGraph` getter.

### LGraph.test.ts
Added 4 tests:
- `removing SubgraphNode fires onRemoved for inner nodes`
- `removing SubgraphNode fires onNodeRemoved callback`
- `subgraph definition is removed when last referencing node is removed`
- `subgraph definition is retained when other nodes still reference it`

## Related

- Fixes #8145
- Part of the subgraph lifecycle cleanup plan (Slice 2: Definition
garbage collection)
2026-01-29 16:19:25 -08:00
Jin Yi
d7654baebf [feat] Show context-appropriate empty state messages in Manager tabs (#8415)
## Summary
Shows tab-specific empty state messages in Node Manager instead of
generic "No search results found" message.

## Changes
- Added computed properties to determine empty state messages based on
current tab and search state
- Display tab-specific messages when a tab is empty without active
search (e.g., "No Missing Nodes" for Missing tab)
- Fall back to search-related messages only when there's an active
search query
- Added Korean translations for empty state messages

| Tab | Empty State Title |
|-----|-------------------|
| All Installed | No Extensions Installed |
| Update Available | All Up to Date |
| Conflicting | No Conflicts Detected |
| Workflow | No Extensions in Workflow |
| Missing | No Missing Nodes |

## Review Focus
- Verify i18n key structure matches existing patterns

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8415-feat-Show-context-appropriate-empty-state-messages-in-Manager-tabs-2f76d73d3650817ab8a0d41b45df3411)
by [Unito](https://www.unito.io)
2026-01-29 16:16:28 -08:00
Comfy Org PR Bot
a032e50721 1.39.2 (#8447)
Patch version increment to 1.39.2

**Base branch:** `main`

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

Co-authored-by: christian-byrne <72887196+christian-byrne@users.noreply.github.com>
v1.39.2
2026-01-29 16:12:49 -08:00