Commit Graph

428 Commits

Author SHA1 Message Date
Johnpaul Chiwetelu
adcb663b3e fix: link dragging offset on external monitors in Vue nodes mode (#8809)
## Summary

Fix link dragging offset when using Vue nodes mode on external monitors
with different DPI than the primary display.

## Changes

- **What**: Derive overlay canvas scale from actual canvas dimensions
(`canvas.width / canvas.clientWidth`) instead of
`window.devicePixelRatio`, fixing DPR mismatch. Map `LinkDirection.NONE`
to `'none'` in `convertDirection()` instead of falling through to
`'right'`.

## Before


https://github.com/user-attachments/assets/f5d04617-369f-4649-af60-11d31e27a75c



## After


https://github.com/user-attachments/assets/76434d2b-d485-43de-94f6-202a91f73edf


## Review Focus

The overlay canvas copies dimensions from the main canvas (which
includes DPR scaling from `resizeCanvas`). When the page loads on a
monitor whose DPR differs from what `resizeCanvas` used,
`window.devicePixelRatio` no longer matches the canvas's internal-to-CSS
ratio, causing all drawn link positions to be offset. The fix derives
scale directly from the canvas itself.

`LinkDirection.NONE = 0` is falsy, so it was caught by the `default`
case in `convertDirection()`, adding an unwanted directional curve to
moved input links.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8809-fix-link-dragging-offset-on-external-monitors-in-Vue-nodes-mode-3046d73d36508143b600f23f5fe07044)
by [Unito](https://www.unito.io)
2026-02-11 22:40:17 +01:00
AustinMroz
28b171168a New bottom button and badges (#8603)
- "Enter Subgraph" "Show advanced inputs" and a new "show node Errors"
button now use a combined button design at the bottom of the node.
- A new "Errors" tab is added to the right side panel
- After a failed queue, the label of an invalid widget is now red.
- Badges other than price are now displayed on the bottom of the node.
- Price badge will now truncate from the first space, prioritizing the
sizing of the node title
- An indicator for the node resize handle is now displayed while mousing
over the node.

<img width="669" height="233" alt="image"
src="https://github.com/user-attachments/assets/53b3b59c-830b-474d-8f20-07f557124af7"
/>


![resize](https://github.com/user-attachments/assets/e2473b5b-fe4d-4f1e-b1c3-57c23d2a0349)

---------

Co-authored-by: github-actions <github-actions@github.com>
2026-02-10 23:29:45 -08:00
Alexander Brown
a7c2115166 feat: add WidgetValueStore for centralized widget value management (#8594)
## Summary

Implements Phase 1 of the **Vue-owns-truth** pattern for widget values.
Widget values are now canonical in a Pinia store; `widget.value`
delegates to the store while preserving full backward compatibility.

## Changes

- **New store**: `src/stores/widgetValueStore.ts` - centralized widget
value storage with `get/set/remove/removeNode` API
- **BaseWidget integration**: `widget.value` getter/setter now delegates
to store when widget is associated with a node
- **LGraphNode wiring**: `addCustomWidget()` automatically calls
`widget.setNodeId(this.id)` to wire widgets to their nodes
- **Test fixes**: Added Pinia setup to test files that use widgets

## Why

This foundation enables:
- Vue components to reactively bind to widget values via `computed(() =>
store.get(...))`
- Future Yjs/CRDT backing for real-time collaboration
- Cleaner separation between Vue state and LiteGraph rendering

## Backward Compatibility

| Extension Pattern | Status |
|-------------------|--------|
| `widget.value = x` |  Works unchanged |
| `node.widgets[i].value` |  Works unchanged |
| `widget.callback` |  Still fires |
| `node.onWidgetChanged` |  Still fires |

## Testing

-  4252 unit tests pass
-  Build succeeds

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8594-feat-add-WidgetValueStore-for-centralized-widget-value-management-2fc6d73d36508160886fcb9f3ebd941e)
by [Unito](https://www.unito.io)

---------

Co-authored-by: Amp <amp@ampcode.com>
Co-authored-by: github-actions <github-actions@github.com>
Co-authored-by: GitHub Action <action@github.com>
2026-02-10 19:37:17 -08:00
Johnpaul Chiwetelu
7f30d6b6a5 feat: add visual indicator for list output slots (#8766)
## Summary

Add rounded square dot shape and "(Iterative)" tooltip for list-type
output slots in Vue nodes, matching litegraph's visual indicator.

## Changes

- **What**: `SlotConnectionDot.vue` renders `rounded-[1px]` instead of
`rounded-full` when slot shape is `RenderShape.GRID`. `OutputSlot.vue`
appends "(Iterative)" to the tooltip for these slots.

<img width="807" height="542" alt="Screenshot 2026-02-10 at 03 38 42"
src="https://github.com/user-attachments/assets/137b60c5-ac3b-457f-a52d-58f5f28a59ea"
/>


## Review Focus
- i18n key added for the iterative suffix

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8766-feat-add-visual-indicator-for-list-output-slots-3036d73d3650813aad85ce094d29c42b)
by [Unito](https://www.unito.io)
2026-02-11 01:49:58 +01:00
Johnpaul Chiwetelu
1e1d5c8308 fix: stop suppressing link rendering during node resize (#8780)
## Summary

Stop link flickering when resizing nodes by removing the
`pendingSlotSync` flag assertion from `scheduleSlotLayoutSync`.

## Changes

- **What**: Remove `layoutStore.setPendingSlotSync(true)` from
`scheduleSlotLayoutSync()` in `useSlotElementTracking.ts`. This call was
introduced in #8367 for graph reconfiguration but was also triggered on
every node resize, causing all links to disappear for one frame per
resize tick. The reconfigure path in `app.ts`
(`addAfterConfigureHandler`) still sets the flag explicitly, so
undo/redo link suppression is unaffected.

## Review Focus

The `pendingSlotSync` flag is still managed correctly for graph
reconfiguration: `app.ts:748` sets it before configure, and the
`finally` block flushes it synchronously. The
`flushScheduledSlotLayoutSync` early-return (pendingNodes empty but
graph has nodes) continues to handle late-mounting Vue components during
reconfigure.

## Before

https://github.com/user-attachments/assets/28cfe4d8-f3f0-46f1-a717-5cb81a28dd75



## After




https://github.com/user-attachments/assets/9445fd00-91f8-4d1e-90ac-86d138d29842

Fixes #8696

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8780-fix-stop-suppressing-link-rendering-during-node-resize-3036d73d365081029820ccfd57425a07)
by [Unito](https://www.unito.io)

---------

Co-authored-by: github-actions <github-actions@github.com>
2026-02-11 00:03:40 +01:00
Terry Jia
9ecbb3af27 Feat/3d dropdown (#8765)
## Summary
Add mesh_upload and upload_subfolder to combo input schema so
WidgetSelect detects mesh uploads generically instead of hardcoding node
type checks. Inject these flags in load3dLazy.ts so they are available
before THREE.js loads.

Also unify SUPPORTED_EXTENSIONS_ACCEPT across load3d and dropdown, pass
uploadSubfolder prop through to WidgetSelectDropdown for correct upload
path, and update error message to list all supported extensions.

replacement for https://github.com/Comfy-Org/ComfyUI_frontend/pull/7975

(We should include thumbnail but not yet, will do it later)

## Screenshots (if applicable)


https://github.com/user-attachments/assets/2cb4b1da-af4f-439b-9786-3ac780c2480d

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8765-Feat-3d-dropdown-3036d73d365081d8a10ee19d3ed7d295)
by [Unito](https://www.unito.io)

---------

Co-authored-by: Kelly Yang <124ykl@gmail.com>
2026-02-10 15:36:57 -05:00
Terry Jia
3238ad3d32 fix: re-mount DOM widget elements after leaving Linear mode (#8753)
## Summary

LinearView renders its own WidgetDOM instances which steal the
widget.element via replaceChildren. When LinearView unmounts
(v-if="linearMode") the element is removed from the DOM entirely.
The canvas-side WidgetDOM stays mounted but its container is now empty,
so DOM widgets (e.g. Three.js scenes) disappear.

Watch canvasStore.linearMode and reclaim the element when switching back
from Linear to Canvas mode.

## Screenshots (if applicable)
before


https://github.com/user-attachments/assets/78cea2bc-c4b3-4b21-bdb3-a521bb0d3062


after


https://github.com/user-attachments/assets/8f92c44d-9514-4001-bbdb-bc4c80468ed7

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8753-fix-re-mount-DOM-widget-elements-after-leaving-Linear-mode-3026d73d3650810b8968eff13dc84e9a)
by [Unito](https://www.unito.io)
2026-02-08 22:51:57 -05:00
Christian Byrne
c91d811d00 feat(cloud): add asset widget support for PrimitiveNode model selection (#8598)
Add cloud asset widget creation in `_createWidget()` using
`isAssetBrowserEligible()`
- Extract shared `createAssetWidget` factory to
`src/platform/assets/utils/`
- Refactor `useComboWidget.ts` to use the shared factory
- Add `_finalizeWidget()` helper to DRY up widget sizing/callback setup
- Pass target node's `comfyClass` and input name to Asset Browser for
correct model filtering
- Check `Comfy.Assets.UseAssetAPI` setting (matches `useComboWidget.ts`
behavior)
- Sync existing target widget value to asset widget
- Add toast notifications for asset validation errors
- Add i18n translations for invalidAsset and invalidFilename errors

Supersedes #8461 (clean rebase, no merge commits)

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8598-feat-cloud-add-asset-widget-support-for-PrimitiveNode-model-selection-2fd6d73d365081a8afa7c2e91762f11c)
by [Unito](https://www.unito.io)


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **New Features**
* Introduced asset widget integration for cloud-based model selection,
enabling users to browse and select assets through an improved
interface.
* Added comprehensive asset validation with enhanced error messages for
invalid assets and filenames.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Amp <amp@ampcode.com>
Co-authored-by: Subagent 5 <subagent@example.com>
Co-authored-by: GitHub Action <action@github.com>
Co-authored-by: guill <jacob.e.segal@gmail.com>
Co-authored-by: Jin Yi <jin12cc@gmail.com>
Co-authored-by: Alexander Brown <drjkl@comfy.org>
Co-authored-by: AustinMroz <austin@comfy.org>
Co-authored-by: Comfy Org PR Bot <snomiao+comfy-pr@gmail.com>
Co-authored-by: github-actions <github-actions@github.com>
Co-authored-by: Johnpaul Chiwetelu <49923152+Myestery@users.noreply.github.com>
Co-authored-by: Rizumu Ayaka <rizumu@ayaka.moe>
Co-authored-by: Kelly Yang <124ykl@gmail.com>
Co-authored-by: sno <snomiao@gmail.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
Co-authored-by: Benjamin Lu <benjaminlu1107@gmail.com>
Co-authored-by: Terry Jia <terryjia88@gmail.com>
Co-authored-by: Luke Mino-Altherr <luke@comfy.org>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
2026-02-08 12:18:13 -08:00
Christian Byrne
6535138e0b fix(vue-nodes): hide slot labels for reroute nodes with empty names (#8574)
## Summary
Fixes reroute node styling in Vue Nodes 2.0 by hiding slot labels when
slot names are intentionally empty.


| Before | After |
|
---------------------------------------------------------------------------------------------------------------------------------------------
|
---------------------------------------------------------------------------------------------------------------------------------------------
|
| <img width="1437" height="473" alt="image"
src="https://github.com/user-attachments/assets/603f52e0-7b75-4822-8c91-0a8374cc0cb6"
/> | <img width="1350" height="493" alt="image"
src="https://github.com/user-attachments/assets/38168955-4d35-4c61-a685-a54efb44cd5d"
/> |


## Problem
Reroute nodes displayed unwanted fallback labels ("Input 0", "Output 0")
instead of appearing as minimal connection-only nodes. This happened
because:
- Reroute nodes intentionally use empty string (`""`) for slot names
- Slot components used `||` operator for fallback labels, treating `''`
as falsy

## Solution
- Add `hasNoLabel` computed property to detect when all label sources
(`label`, `localized_name`, `name`) are empty/falsy
- Derive `dotOnly` from either the existing prop OR `hasNoLabel` being
true
- When `dotOnly` is true: label text is hidden, padding removed
(`lg-slot--dot-only` class), only connection dot visible

Combined with existing `NO_TITLE` support from #7589, reroutes now
display as minimal nodes with just connection dots—matching classic
reroute appearance.

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

## Release Notes

* **Bug Fixes**
* Enhanced input and output slot label handling to automatically conceal
labels when unavailable
* Improved fallback display names for slots with more reliable naming
logic

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8574-fix-vue-nodes-hide-slot-labels-for-reroute-nodes-with-empty-names-2fc6d73d365081c38031e260402283d3)
by [Unito](https://www.unito.io)
2026-02-07 15:30:20 -08:00
Terry Jia
7ed71c7769 fix: exclude transient image URLs from ImageCompare workflow serialization (#8715)
## Summary
Image URLs set by onExecuted are execution results that don't exist on
other machines. Disable workflow persistence (widget.serialize) while
keeping prompt serialization (widget.options.serialize) so compare_view
is still sent to the backend.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8715-fix-exclude-transient-image-URLs-from-ImageCompare-workflow-serialization-3006d73d365081b8aa87c7e05cb25f2f)
by [Unito](https://www.unito.io)
2026-02-07 15:08:05 -05:00
Alexander Brown
69c8c84aef fix: resolve i18n no-restricted-imports lint warnings (#8704)
## Summary

Fix all i18n `no-restricted-imports` lint warnings and upgrade rules
from `warn` to `error`.

## Changes

- **What**: Migrate Vue components from `import { t/d } from '@/i18n'`
to `const { t } = useI18n()`. Migrate non-component `.ts` files from
`useI18n()` to `import { t/d } from '@/i18n'`. Allow `st` import from
`@/i18n` in Vue components (it wraps `te`/`t` for safe fallback
translation). Remove `@deprecated` tag from `i18n.ts` global exports
(still used by `st` and non-component code). Upgrade both lint rules
from `warn` to `error`.

## Review Focus

- The `st` helper is intentionally excluded from the Vue component
restriction since it provides safe fallback translation needed for
custom node definitions.

Fixes #8701

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8704-fix-resolve-i18n-no-restricted-imports-lint-warnings-2ff6d73d365081ae84d8eb0dfef24323)
by [Unito](https://www.unito.io)

---------

Co-authored-by: Amp <amp@ampcode.com>
2026-02-06 20:54:53 -08:00
Simula_r
c5431de123 Feat/workspaces 6 billing (#8508)
## Summary

Implements billing infrastructure for team workspaces, separate from
legacy personal billing.

## Changes

- **Billing abstraction**: New `useBillingContext` composable that
switches between legacy (personal) and workspace billing based on
context
- **Workspace subscription flows**: Pricing tables, plan transitions,
cancellation dialogs, and payment preview components for workspace
billing
- **Top-up credits**: Workspace-specific top-up dialog with polling for
payment confirmation
- **Workspace API**: Extended with billing endpoints (subscriptions,
invoices, payment methods, credits top-up)
- **Workspace switcher**: Now displays tier badges for each workspace
- **Subscribe polling**: Added polling mechanisms
(`useSubscribePolling`, `useTopupPolling`) for async payment flows

## Review Focus

- Billing flow correctness for workspace vs legacy contexts
- Polling timeout and error handling in payment flows

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8508-Feat-workspaces-6-billing-2f96d73d365081f69f65c1ddf369010d)
by [Unito](https://www.unito.io)

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-06 20:52:53 -08:00
AustinMroz
e7932f2fc2 Tighter image sizing in vue mode (#8702)
Fixes multiple overlapping issues with both the ImagePreviews (LoadImage
node) and LivePreview (Ksampler node) to eliminate empty space and move
the bahviour to be closer to the litegraph implementation.
- NodeWidgets will no longer no longer flex-grow when it contains no
widgets capable of growing
<img width="278" height="585" alt="image"
src="https://github.com/user-attachments/assets/c4c39805-1474-457b-86d1-b64ecf01319f"
/>

- The number of element layers for LivePreview has been reduced. Sizing
is difficult to properly spread across nested flex levels.
- The ImagePreview and LivePreview now have `contain-size` set with a
min height of 220 pixels (the same as the litegraph implementation).
This allows images to "pillarbox" by increasing width without increasing
height.
  | Before | After |
  | ------ | ----- |
| <img width="360" alt="before"
src="https://github.com/user-attachments/assets/3fe38a20-47d3-4a77-a0db-63167f76b0be"/>
| <img width="360" alt="after"
src="https://github.com/user-attachments/assets/22dc6bf6-1812-49bb-95a1-3febfb3e40dd"
/>|
| <img width="360" alt="before"
src="https://github.com/user-attachments/assets/99b24547-6850-4b46-a972-53411676c78f"
/> | <img width="360" alt="after"
src="https://github.com/user-attachments/assets/0a7783c8-cf93-47aa-8c60-608b9a4b4498"
/>|

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8702-Tighter-image-sizing-in-vue-mode-2ff6d73d3650814196f0d55d81a42e2d)
by [Unito](https://www.unito.io)

---------

Co-authored-by: github-actions <github-actions@github.com>
Co-authored-by: Alexander Brown <drjkl@comfy.org>
2026-02-06 16:39:26 -08:00
pythongosssss
0e3314bbd3 Node ghost mode when adding nodes (#8694)
## Summary

Adds option for adding a node as a "ghost" that follows the cursor until
the user left clicks to confirm, or esc/right click to cancel.

## Changes

- **What**: 
Adds option for `ghost` when calling `graph.add`  
This adds the node with a `flag` of ghost which causes it to render
transparent
Selects the node, then sets the canvas as dragging to stick the node to
the cursor

## Screenshots (if applicable)



https://github.com/user-attachments/assets/dcb5702f-aba3-4983-aa40-c51f24a4767a

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8694-Node-ghost-mode-when-adding-nodes-2ff6d73d3650815591f2c28415050463)
by [Unito](https://www.unito.io)
2026-02-06 13:42:38 -08:00
Terry Jia
17c1b1f989 fix: prevent node height shrinking on vueNodes/litegraph mode switch (#8697)
## Summary
Three interacting bugs caused cumulative height loss (~66px per cycle):

1. useVueNodeLifecycle incorrectly subtracted NODE_TITLE_HEIGHT from
LGraphNode.size[1] during layout init, but size[1] is already
content-only (title excluded per LGraphNode.measure()).

2. ensureCorrectLayoutScale added/subtracted NODE_TITLE_HEIGHT in scale
formulas, breaking the round-trip.
Simplified to pure ratio scaling (size * scaleFactor). 
Also set LayoutSource.Canvas before batchUpdateNodeBounds to prevent
stale DOM source from triggering incorrect height normalization.

3. initSizeStyles set --node-height (min-height of the full DOM element
including title) to the content-only layout height.
Added NODE_TITLE_HEIGHT so ResizeObserver captures the correct total
height and normalization recovers the exact content height.

## Screenshots (if applicable)
before

https://github.com/user-attachments/assets/ae41124b-f9e3-4061-8127-eeacddc67a55

after

https://github.com/user-attachments/assets/5ff288a6-73a3-481a-a750-150d9bdbc8fe

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8697-fix-prevent-node-height-shrinking-on-vueNodes-litegraph-mode-switch-2ff6d73d365081c7a2acdc7ec57e2e19)
by [Unito](https://www.unito.io)

---------

Co-authored-by: github-actions <github-actions@github.com>
2026-02-06 12:52:13 -08:00
AustinMroz
8283438ee6 Fix incorrect widgetValue migration (#8625)
Under a combination of many edge cases, the `widget_values` migration
code added in #3326 would cause the progress text on a "Recraft Text to
Image" node to incorrectly deserialize into the `control_after_generate`
- widgets_values is of length 1 greater than it should be because
progress text serializes
  - It should not, there is no code to deserialize it
- negative_prompt has force_input set and skips serialization
- Migration only applies when `widgets_values` is equal to actual inputs
length. The two above edge cases cancel to make this true
- Seed is accounted for when calculating the length of widgets, but not
when applying the migration
- Migration occurs even though we track workflow version now and have an
accurate way of determining that it can not be needed

The two primary edge cases which cause the bug are both addressed
- `options.serialize` does nothing and has never done anything. I've
been guilty of making the same mistake in the ancient past, and want to
clean up the misconception where I can.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8625-Fix-incorrect-widgetValue-migration-2fe6d73d365081a683b4c675eaeebb6c)
by [Unito](https://www.unito.io)
2026-02-05 21:27:11 -08:00
Christian Byrne
23c8757447 fix: increase active node border weight from 2 to 3 (#8654)
## Summary

Increase stroke/outline weight for active node states to improve
visibility during workflow execution (COM-7770).

## Changes

- **What**: Increased border/stroke weight from 2 to 3 for active nodes
in both Vue Nodes and LiteGraph renderers
  - Vue Nodes: `outline-2` → `outline-3` in `LGraphNode.vue`
- LiteGraph: `lineWidth: 3` for `running` stroke (was default 1) and
`executionError` stroke (was 2) in `litegraphService.ts`
  - Updated test assertion to match

## Review Focus

Minimal visual change. The `executionError` lineWidth was also bumped
from 2 → 3 so error states remain at least as prominent as running
states.

> **Note:**
[#8603](https://github.com/Comfy-Org/ComfyUI_frontend/pull/8603) (by
@AustinMroz) also modifies `LGraphNode.vue` with a larger restructuring
(bottom buttons, badges, resize handle). The two PRs do not conflict —
#8603 does not touch the outline/border state classes or
`litegraphService.ts`, so both changes merge cleanly.

---------

Co-authored-by: Alexander Brown <drjkl@comfy.org>
Co-authored-by: github-actions <github-actions@github.com>
2026-02-05 17:44:19 -08:00
Johnpaul Chiwetelu
7d3d00858a feat: add download button to audio preview player (#8628)
## Summary
- Adds a download icon button to the `AudioPreviewPlayer` widget for
PreviewAudio and SaveAudio nodes
- Reuses the existing `downloadFile` utility (same as video download)
- Button appears inline next to volume/options controls, matching the
player's existing UI style

## Test plan
- [x] Add a PreviewAudio or SaveAudio node, run a workflow that produces
audio output
- [x] Verify the download icon appears in the audio player controls
- [x] Click the download button and confirm the audio file downloads
correctly
- [x] Verify the button does not appear when no audio is loaded


https://github.com/user-attachments/assets/7fb2df16-82a6-40aa-a938-aed57032e30b

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8628-feat-add-download-button-to-audio-preview-player-2fe6d73d365081e3997fc45d3bb8cffc)
by [Unito](https://www.unito.io)

---------

Co-authored-by: github-actions <github-actions@github.com>
2026-02-06 01:35:32 +01:00
Johnpaul Chiwetelu
90a701dd67 Road to No Explicit Any Part 11 (#8565)
## Summary

This PR removes `any` types from widgets, services, stores, and test
files, replacing them with proper TypeScript types.

### Key Changes

#### Type Safety Improvements
- Replaced `any` with `unknown`, explicit types, or proper interfaces
across widgets and services
- Added proper type imports (TgpuRoot, Point, StyleValue, etc.)
- Created typed interfaces (NumericWidgetOptions, TestWindow,
ImportFailureDetail, etc.)
- Fixed function return types to be non-nullable where appropriate
- Added type guards and null checks instead of non-null assertions
- Used `ComponentProps` from vue-component-type-helpers for component
testing

#### Widget System
- Added index signature to IWidgetOptions for Record compatibility
- Centralized disabled logic in WidgetInputNumberInput
- Moved template type assertions to computed properties
- Fixed ComboWidget getOptionLabel type assertions
- Improved remote widget type handling with runtime checks

#### Services & Stores
- Fixed getOrCreateViewer to return non-nullable values
- Updated addNodeOnGraph to use specific options type `{ pos?: Point }`
- Added proper type assertions for settings store retrieval
- Fixed executionIdToCurrentId return type (string | undefined)

#### Test Infrastructure
- Exported GraphOrSubgraph from litegraph barrel to avoid circular
dependencies
- Updated test fixtures with proper TypeScript types (TestInfo,
LGraphNode)
- Replaced loose Record types with ComponentProps in tests
- Added proper error handling in WebSocket fixture

#### Code Organization
- Created shared i18n-types module for locale data types
- Made ImportFailureDetail non-exported (internal use only)
- Added @public JSDoc tag to ElectronWindow type
- Fixed console.log usage in scripts to use allowed methods

### Files Changed

**Widgets & Components:**
-
src/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberInput.vue
-
src/renderer/extensions/vueNodes/widgets/components/WidgetSelectDefault.vue
-
src/renderer/extensions/vueNodes/widgets/components/WidgetSelectDropdown.vue
- src/renderer/extensions/vueNodes/widgets/components/WidgetTextarea.vue
-
src/renderer/extensions/vueNodes/widgets/composables/useRemoteWidget.ts
- src/lib/litegraph/src/widgets/ComboWidget.ts
- src/lib/litegraph/src/types/widgets.ts
- src/components/common/LazyImage.vue
- src/components/load3d/Load3dViewerContent.vue

**Services & Stores:**
- src/services/litegraphService.ts
- src/services/load3dService.ts
- src/services/colorPaletteService.ts
- src/stores/maskEditorStore.ts
- src/stores/nodeDefStore.ts
- src/platform/settings/settingStore.ts
- src/platform/workflow/management/stores/workflowStore.ts

**Composables & Utils:**
- src/composables/node/useWatchWidget.ts
- src/composables/useCanvasDrop.ts
- src/utils/widgetPropFilter.ts
- src/utils/queueDisplay.ts
- src/utils/envUtil.ts

**Test Files:**
- browser_tests/fixtures/ComfyPage.ts
- browser_tests/fixtures/ws.ts
- browser_tests/tests/actionbar.spec.ts
-
src/workbench/extensions/manager/components/manager/skeleton/PackCardGridSkeleton.test.ts
- src/lib/litegraph/src/subgraph/subgraphUtils.test.ts
- src/components/rightSidePanel/shared.test.ts
- src/platform/cloud/subscription/composables/useSubscription.test.ts
-
src/platform/workflow/persistence/composables/useWorkflowPersistence.test.ts

**Scripts & Types:**
- scripts/i18n-types.ts (new shared module)
- scripts/diff-i18n.ts
- scripts/check-unused-i18n-keys.ts
- src/workbench/extensions/manager/types/conflictDetectionTypes.ts
- src/types/algoliaTypes.ts
- src/types/simplifiedWidget.ts

**Infrastructure:**
- src/lib/litegraph/src/litegraph.ts (added GraphOrSubgraph export)
- src/lib/litegraph/src/infrastructure/CustomEventTarget.ts
- src/platform/assets/services/assetService.ts

**Stories:**
- apps/desktop-ui/src/views/InstallView.stories.ts
- src/components/queue/job/JobDetailsPopover.stories.ts

**Extension Manager:**
- src/workbench/extensions/manager/composables/useConflictDetection.ts
- src/workbench/extensions/manager/composables/useManagerQueue.ts
- src/workbench/extensions/manager/services/comfyManagerService.ts
- src/workbench/extensions/manager/utils/conflictMessageUtil.ts

### Testing

- [x] All TypeScript type checking passes (`pnpm typecheck`)
- [x] ESLint passes without errors (`pnpm lint`)
- [x] Format checks pass (`pnpm format:check`)
- [x] Knip (unused exports) passes (`pnpm knip`)
- [x] Pre-commit and pre-push hooks pass

Part of the "Road to No Explicit Any" initiative.

### 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
- Part 9: #8498
- Part 10: #8499

---------

Co-authored-by: Comfy Org PR Bot <snomiao+comfy-pr@gmail.com>
Co-authored-by: christian-byrne <72887196+christian-byrne@users.noreply.github.com>
Co-authored-by: github-actions <github-actions@github.com>
2026-02-05 16:29:28 -08:00
Terry Jia
507500a9d7 fix: use Intl.NumberFormat instead of vue-i18n n() for number formatting (#8600)
## Summary

vue-i18n's n() function does not support passing
Intl.NumberFormatOptions directly as the second argument. When widgets
without precision defined (e.g., KJNodes' CreateShapeImageOnPath) were
rendered, the n() function threw a SyntaxError in parseNumberArgs.

Replace n() with native Intl.NumberFormat which properly supports
NumberFormatOptions while still using the i18n locale for localization.

## Screenshots (if applicable)
before

https://github.com/user-attachments/assets/eb48379b-fe45-4f1c-a674-b92130f0fcac

after

https://github.com/user-attachments/assets/1abcd2da-ad8b-4432-831a-2a7e91f375a5



<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Refactor**
* Enhanced number input widgets with improved locale-aware number
formatting, ensuring proper decimal and group separator display based on
user locale settings.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8600-fix-use-Intl-NumberFormat-instead-of-vue-i18n-n-for-number-formatting-2fd6d73d365081b78509e87d516c6067)
by [Unito](https://www.unito.io)
2026-02-03 20:57:49 -05:00
Christian Byrne
f98232c272 fix: suppress link rendering during slot sync after graph reconfigure (#8367)
## Description

Fixes link alignment issues after undo/redo operations in Vue Nodes 2.0.
When multiple connections exist from the same output to different nodes,
performing an undo would cause the connections to become misaligned with
their inputs.

## Root Cause

When undo triggers `loadGraphData`, the graph is reconfigured and Vue
node components are destroyed and recreated. The new slot elements mount
and schedule RAF-batched position syncs via `scheduleSlotLayoutSync`.
However, links are drawn **before** the RAF batch completes, causing
`getSlotPosition()` to return stale/missing positions.

## Solution

- Export a new `flushPendingSlotLayoutSyncs()` function from
`useSlotElementTracking.ts`
- Create a `useGraphConfigureSlotSync` composable that flushes pending
syncs after graph configuration
- Integrate the flush into `addAfterConfigureHandler` in `app.ts`,
called after `onAfterGraphConfigured`
- Force canvas redraw after flushing to render links with correct
positions

## Testing

- Added unit tests for `flushPendingSlotLayoutSyncs`
- Added unit tests for `useGraphConfigureSlotSync` composable
- Manual verification: connections now align correctly after undo/redo
operations

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8367-fix-flush-pending-slot-layout-syncs-after-graph-configure-2f66d73d365081a3a564fac96c44a048)
by [Unito](https://www.unito.io)

---------

Co-authored-by: Subagent 5 <subagent@example.com>
Co-authored-by: Amp <amp@ampcode.com>
2026-02-03 15:56:31 -08:00
Christian Byrne
278d491030 refactor: move ellipsis and punctuation into i18n translation strings (#8573)
## Summary

Move ellipsis and punctuation characters into i18n translation strings
for proper internationalization support.

## Changes

- Add 12 new translation keys with punctuation included:
- Placeholder keys with trailing ellipsis (e.g.,
`searchNodesPlaceholder: "Search Nodes..."`)
  - `downloadWithSize` with interpolation: `"Download ({size})"`
  - `completedWithCheckmark`: `"Completed ✓"`
- Prompt keys with colons (e.g., `enterNewNamePrompt: "Enter new
name:"`)
- Update 20 files to use new translation keys instead of string
concatenation

## Review Focus

This eliminates string concatenation patterns like `$t('key') + '...'`
that break proper internationalization, since different languages may
use different punctuation or may not need ellipsis/colons in the same
contexts.

Fixes #7333


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Chores**
* Standardized localization across the app: unified search placeholders
and input hints; updated dialog prompt texts for renaming,
saving/exporting, and related prompts.
* **New Features**
  * Download buttons now show file size via localized text.
  * Completed status displays a localized label with a checkmark.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8573-refactor-move-ellipsis-and-punctuation-into-i18n-translation-strings-2fc6d73d365081828ad3f257bcac7799)
by [Unito](https://www.unito.io)
2026-02-03 15:50:18 -08:00
Christian Byrne
67107d6f56 fix: use consistent chevron icon in Vue node dropdown widgets (#8576)
## Summary

Fixes styling inconsistency where Vue node select widget chevrons look
different between dropdown implementations.

## Changes

- **What**: Added `#dropdownicon` slot to `WidgetSelectDefault.vue`
using Lucide chevron icon with
`text-component-node-foreground-secondary` styling, matching
`FormDropdownInput.vue`

## Review Focus

Both dropdown implementations now use identical chevron icons with
consistent sizing (`size-4`) and color tokens.

Fixes #COM-11645


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Style**
* Updated select/dropdown appearance: replaced the previous icon
rendering with a customizable dropdown icon slot, improving visual
consistency and allowing a custom icon to display in select controls
across the UI. This change affects only presentation—no behavior or data
handling was altered.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8576-fix-use-consistent-chevron-icon-in-Vue-node-dropdown-widgets-2fc6d73d3650814e923bcbee83fae498)
by [Unito](https://www.unito.io)

---------

Co-authored-by: github-actions <github-actions@github.com>
2026-02-03 15:48:25 -08:00
Christian Byrne
ae7728cd91 feat: add welcome message for Linear Mode (#8562)
## Summary

Adds a welcome/onboarding message for App Mode (Linear Mode) that
displays when no workflow output is selected, targeting first-time
users.


| Before | After |
|
---------------------------------------------------------------------------------------------------------------------------------------------
|
---------------------------------------------------------------------------------------------------------------------------------------------
|
| <img width="1914" height="1084" alt="Screenshot from 2026-02-02
20-47-30"
src="https://github.com/user-attachments/assets/f3693422-b6ee-496a-a354-d9cede501330"
/> | <img width="1919" height="1085" alt="Screenshot from 2026-02-02
21-32-20"
src="https://github.com/user-attachments/assets/dbfda4d0-251c-4099-ad36-3d7c0220d264"
/> |


## Changes

- **LinearWelcome.vue**: New component with 4 sections explaining App
Mode interface, sharing workflows, and widget control
- **LinearPreview.vue**: Replace dimmed logo fallback with LinearWelcome
component
- **i18n**: Add `linearMode.welcome.*` translation keys

## Review Focus

- Copy targets users who have never used ComfyUI—assumes no knowledge of
nodes/graphs
- Component uses semantic Tailwind classes from design system

Fixes COM-14274

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8562-feat-add-welcome-message-for-Linear-Mode-2fc6d73d365081b2b736f211152e1cce)
by [Unito](https://www.unito.io)


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **New Features**
* Added a comprehensive welcome screen in App Mode that introduces users
to the simplified interface hiding node graphs, explains the layout
structure with outputs and controls, provides instructions for sharing
workflows as simple tools, and guides users on customizing which
settings are visible through widget promotion.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-02-02 21:44:30 -08:00
Alexander Brown
cbdc7d030f feat: code splitting optimization - reduce initial bundle by 36% (#8542)
## Summary

Reduces initial module preload from 12.94 MB to 8.24 MB (-4.7 MB, -36%).

## Changes

- Split vendor chunks for better cache isolation (firebase, sentry,
i18n, zod, etc.)
- Exclude heavy optional chunks from initial preload: THREE.js, xterm,
tiptap, chart.js, yjs
- Lazy load 16 dialog components in dialogService
- Add \endor-yjs\ chunk for CRDT library

## Metrics

| Metric | Before | After |
|--------|--------|-------|
| Initial preload | 12.94 MB | 8.24 MB |
| vendor-other | 4,006 KB | 2,156 KB |
| dialogService | 1,860 KB | 1,304 KB |

All excluded chunks still load on-demand when their features are used.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8542-feat-code-splitting-optimization-reduce-initial-bundle-by-36-2fb6d73d36508146aaf7fdaed3274033)
by [Unito](https://www.unito.io)

---------

Co-authored-by: Amp <amp@ampcode.com>
Co-authored-by: github-actions <github-actions@github.com>
2026-02-02 19:05:28 -08:00
Alexander Brown
21492ecca5 fix: update imports for VueUse v14 compatibility (#8550)
Update imports for VueUse v14 compatibility:
- `toValue` → import from `vue` (dropped from VueUse v14)
- `MaybeRef` type → import from `vue` (dropped from VueUse v14)

These APIs were deprecated in VueUse v12 and removed in v14 in favor of
Vue's native exports.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8550-fix-update-imports-for-VueUse-v14-compatibility-2fb6d73d365081b0bac1d01d4092c176)
by [Unito](https://www.unito.io)


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Chores**
* Updated VueUse dependencies to newer versions for improved
compatibility
  * Minor package entry reorganization (no functional changes)

* **Refactor**
  * Consolidated imports to use native Vue utilities where applicable

* **Tests**
* Updated unit tests: improved outside-click simulation and cleanup;
migrated tests to use the real store setup for more realistic test
behavior
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Amp <amp@ampcode.com>
2026-02-02 18:58:14 -08:00
Johnpaul Chiwetelu
cfdd002b7c Road to No Explicit Any Part 10 (#8499)
## Summary

This PR removes `any` types from UI component files and replaces them
with proper TypeScript types.

### Key Changes

#### Type Safety Improvements
- Replaced `any` with `unknown`, explicit types, or proper interfaces
across UI components
- Used `ComponentPublicInstance` with explicit method signatures for
component refs
- Used `Record<string, unknown>` for dynamic property access
- Added generics for form components with flexible value types
- Used `CSSProperties` for style objects

### Files Changed

UI Components:
- src/components/common/ComfyImage.vue - Used proper class prop type
- src/components/common/DeviceInfo.vue - Used `string | number` for
formatValue
- src/components/common/FormItem.vue - Used `unknown` for model value
- src/components/common/FormRadioGroup.vue - Added generic type
parameter
- src/components/common/TreeExplorer.vue - Used proper async function
signature
- src/components/custom/widget/WorkflowTemplateSelectorDialog.vue -
Fixed duplicate import
- src/components/graph/CanvasModeSelector.vue - Used
`ComponentPublicInstance` for ref
- src/components/node/NodePreview.vue - Changed `any` to `unknown`
- src/components/queue/job/JobDetailsPopover.vue - Removed unnecessary
casts
- src/components/queue/job/JobFiltersBar.vue - Removed `as any` casts
- src/platform/assets/components/MediaAssetContextMenu.vue - Added
`ContextMenuInstance` type
- src/renderer/extensions/minimap/MiniMapPanel.vue - Used
`CSSProperties`
- src/renderer/extensions/vueNodes/composables/useNodeTooltips.ts -
Added `PrimeVueTooltipElement` interface
-
src/renderer/extensions/vueNodes/widgets/components/form/FormSelectButton.vue
- Used `Record<string, unknown>`
-
src/workbench/extensions/manager/components/manager/infoPanel/tabs/DescriptionTabPanel.vue
- Added `LicenseObject` interface

### Testing
- All TypeScript type checking passes (`pnpm typecheck`)
- Linting passes without errors (`pnpm lint`)

Part of the "Road to No Explicit Any" initiative.

### 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
- Part 9: #8498
- Part 10: #8499

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8499-Road-to-No-Explicit-Any-Part-10-2f86d73d365081aab129f165c7d02434)
by [Unito](https://www.unito.io)
2026-02-02 17:16:40 +01:00
Terry Jia
4e9f581625 fix: align BOUNDING_BOX type with backend and target ImageCropV2 node (#8531)
## Summary
- Update imageCrop extension to target new ImageCropV2 comfyClass
- Fix BOUNDING_BOX io_type mismatch (was BOUNDINGBOX, backend sends
BOUNDING_BOX)
- Keep old ImageCrop node_id unchanged for backward compatibility

BE Changes is https://github.com/Comfy-Org/ComfyUI/pull/11594

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8531-fix-align-BOUNDING_BOX-type-with-backend-and-target-ImageCropV2-node-2fa6d73d36508160a06ad06108b8a15e)
by [Unito](https://www.unito.io)
2026-02-02 04:03:55 -05:00
Alexander Brown
eaa3ff1579 feat: add ownership and base model filtering, unify asset/dropdown types (#8497)
Add ownership and base model filtering to AssetBrowserModal and
FormDropdown widgets.

## Changes

- **Ownership filter**: Filter by All/My Models/Public Models (uses
`is_immutable` field)
- **Base model filter**: Multi-select filter with Clear Filters button
- **Type unification**: Replace `AssetDropdownItem` with
`FormDropdownItem`
- **Sorting unification**: Extract shared utilities to
`assetSortUtils.ts`
- **UI refactor**: Use `Button` component, Vue 3.5 prop shorthand, i18n
improvements

---------

Co-authored-by: Amp <amp@ampcode.com>
2026-02-01 20:01:18 -08:00
Benjamin Lu
4e20b7522b Forward scroll unless focused (#6597)
## Summary

Forward wheel events to the canvas unless a wheel-capturing element is
focused, and ensure the Load3D scene becomes focusable on pointer
interaction so its wheel zoom/pan works after the user clicks into it.

## Changes

- **What**: gate wheel forwarding on focused capture elements; focus the
Load3D scene container on pointerdown to opt into wheel capture.
- **Dependencies**: none

## Review Focus

- Validate wheel forwarding behavior across focusable inputs vs.
non-focusable capture zones.
- Confirm Load3D zoom/pan only captures wheel after a user click (canvas
pan should still work when merely hovering).

## Screenshots (if applicable)

N/A

---------

Co-authored-by: Christian Byrne <cbyrne@comfy.org>
Co-authored-by: Subagent 5 <subagent@example.com>
Co-authored-by: Amp <amp@ampcode.com>
2026-02-01 09:32:10 -05: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
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
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
Rizumu Ayaka
fc38f16543 fix: default image input for the template is displayed as empty on dropdown selection (#8276)
The image input for nodes loaded from templates appears empty in the
Properties Panel.

When the widget's current value (saved in the template) is not in the
available file list returned by the server, the selectedSet is empty,
causing a placeholder to be displayed instead of the actual value.

Added a missingValueItem computed property in WidgetSelectDropdown.vue.
When the current value is not in inputItems or outputItems, it creates a
fallback item and adds it to dropdownItems.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8276-fix-default-image-input-for-the-template-is-displayed-as-empty-on-dropdown-selection-2f16d73d3650817eaad5e4e33637fb74)
by [Unito](https://www.unito.io)

---------

Co-authored-by: Alexander Brown <drjkl@comfy.org>
Co-authored-by: github-actions <github-actions@github.com>
2026-01-29 14:19:52 -08:00
AustinMroz
af8433fb3d Support widget specific contextmenu options in vue (#8431)
<img width="614" height="485" alt="image"
src="https://github.com/user-attachments/assets/2a635dec-8bed-4fab-9881-5e6057d482e1"
/>

These options were defined in `litegraphService`. While the existing
code for defining options is reused (to ensure there's no implementation
drift) these extra widget options use the litegraph format for context
menu options and do not belong in `useSelectionMenuOptions`. They have
been moved out of `useLitegraphService` (good), but left in
`litegraphService` (not great)

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8431-Support-widget-specific-contextmenu-options-in-vue-2f76d73d3650814fb20fca352dc81e3b)
by [Unito](https://www.unito.io)
2026-01-29 11:38:41 -08:00
Johnpaul Chiwetelu
cabd08f0ec Road to No explicit any: Group 8 (part 6) test files (#8344)
## 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 all instances of "as unknown as" patterns from test files
- Used proper factory functions from litegraphTestUtils instead of
custom mocks
- Made incomplete mocks explicit using Partial<T> types
- Fixed DialogStore mocking with proper interface exports
- Improved type safety with satisfies operator where applicable

#### App Parameter Removal
- **Removed the unused `app` parameter from all ComfyExtension interface
methods**
- The app parameter was always undefined at runtime as it was never
passed from invokeExtensions
- Affected methods: init, setup, addCustomNodeDefs,
beforeRegisterNodeDef, beforeRegisterVueAppNodeDefs,
registerCustomNodes, loadedGraphNode, nodeCreated, beforeConfigureGraph,
afterConfigureGraph

##### Breaking Change Analysis
Verified via Sourcegraph that this is NOT a breaking change:
- Searched all 10 affected methods across GitHub repositories
- Only one external repository
([drawthingsai/draw-things-comfyui](https://github.com/drawthingsai/draw-things-comfyui))
declares the app parameter in their extension methods
- That repository never actually uses the app parameter (just declares
it in the function signature)
- All other repositories already omit the app parameter
- Search queries used:
- [init method
search](https://sourcegraph.com/search?q=context:global+repo:%5Egithub%5C.com/.*+lang:typescript+%22init%28app%22+-repo:Comfy-Org/ComfyUI_frontend&patternType=standard)
- [setup method
search](https://sourcegraph.com/search?q=context:global+repo:%5Egithub%5C.com/.*+lang:typescript+%22setup%28app%22+-repo:Comfy-Org/ComfyUI_frontend&patternType=standard)
  - Similar searches for all 10 methods confirmed no usage

### Files Changed

Test files:
-
src/components/settings/widgets/__tests__/WidgetInputNumberInput.test.ts
- src/services/keybindingService.escape.test.ts  
- src/services/keybindingService.forwarding.test.ts
- src/utils/__tests__/newUserService.test.ts →
src/utils/__tests__/useNewUserService.test.ts
- src/services/jobOutputCache.test.ts
-
src/renderer/extensions/vueNodes/widgets/composables/useRemoteWidget.test.ts
-
src/renderer/extensions/vueNodes/widgets/composables/useIntWidget.test.ts
-
src/renderer/extensions/vueNodes/widgets/composables/useFloatWidget.test.ts

Source files:
- src/types/comfy.ts - Removed app parameter from ComfyExtension
interface
- src/services/extensionService.ts - Improved type safety with
FunctionPropertyNames helper
- src/scripts/metadata/isobmff.ts - Fixed extractJson return type per
review
- src/extensions/core/*.ts - Updated extension implementations
- src/scripts/app.ts - Updated app initialization

### Testing
- All existing tests pass
- Type checking passes  
- ESLint/oxlint checks pass
- No breaking changes for external repositories

Part of the "Road to No Explicit Any" initiative.

### 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 (this PR)
2026-01-29 11:03:17 -08:00
AustinMroz
44baadd7ca Implement clickable badges (#8401)
Adds an `onClick` handler to LGraphBadge

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8401-Implement-clickable-badges-2f76d73d365081b3b23fc1eaa3bc65b8)
by [Unito](https://www.unito.io)
2026-01-29 00:04:28 -08:00
Christian Byrne
0faf2220b8 fix: dragging (e.g., when selecting text) in Markdown note causes node to drag (#8413)
## Summary

When attempting to select text inside Vue node Markdown widget's
textarea (edit mode), the node would drag instead of text being
selected.

Root cause: WidgetMarkdown.vue's Textarea only had @click.stop and
@keydown.stop, but was missing pointer event modifiers. The pointerdown
event bubbled up to LGraphNode.vue which initiated node drag.

*Fix*: Add @pointerdown.capture.stop, @pointermove.capture.stop, and
@pointerup.capture.stop to match the pattern used in WidgetTextarea.vue.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8413-fix-dragging-e-g-when-selecting-text-in-Markdown-note-causes-node-to-drag-2f76d73d3650816dbf9bdf893775c3d4)
by [Unito](https://www.unito.io)

Co-authored-by: Subagent 5 <subagent@example.com>
Co-authored-by: Amp <amp@ampcode.com>
2026-01-28 23:18:23 -08:00
Alexander Brown
6ce60a11a4 test: use createTestingPinia instead of createPinia (#8376)
Replace \createPinia\ with \createTestingPinia({ stubActions: false })\
from \@pinia/testing\ across 45 test files for proper test isolation.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8376-test-use-createTestingPinia-instead-of-createPinia-2f66d73d36508137a9f0daffcddc86f7)
by [Unito](https://www.unito.io)

Co-authored-by: Amp <amp@ampcode.com>
2026-01-28 22:21:38 -08:00
Christian Byrne
2103dcc788 fix: increase Vue node resize handle size for better usability (#8391)
## Summary

Increases the resize handle size on Vue nodes to improve usability,
especially when nodes are selected.

## Changes

- **What**: Increased resize handle from 12px to 20px and offset it
slightly outside the node boundary to avoid overlap with selection
outline

## Review Focus

The resize handle was too small and became harder to grab when the node
was selected (the 2px outline rendered outside the box, visually
obscuring the corner). This fix increases the hit area and positions it
to extend beyond the node edge.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8391-fix-increase-Vue-node-resize-handle-size-for-better-usability-2f76d73d36508136b2aac51bc0d53551)
by [Unito](https://www.unito.io)

---------

Co-authored-by: Subagent 5 <subagent@example.com>
Co-authored-by: Amp <amp@ampcode.com>
Co-authored-by: GitHub Action <action@github.com>
2026-01-28 19:15:40 -08:00
AustinMroz
d5e9be6a64 Additional linear tweaks (#8375)
- Textareas have a fixed (larger) height. This was requested by design
and I thought I fixed it while back.
  | Before | After |
  | ------ | ----- |
| <img width="360" alt="before"
src="https://github.com/user-attachments/assets/35ecfa42-4812-43b3-9844-4ef1f757ae40"
/> | <img width="360" alt="after"
src="https://github.com/user-attachments/assets/8d13e114-6524-4f3e-ae61-0d31d754466c"
/>|
- Since the typeform survey popover doesn't work well on mobile, the
button will instead open the survey in a new tab for mobile users.
- Workaround for the first linear output on local not being
automatically selected for display
- Add the linear mode toggle to the bottom left of mobile layout
<img width="634" height="90" alt="image"
src="https://github.com/user-attachments/assets/571c672c-5913-4dc9-84f9-d16c49b4a587"
/>
- Adds a hamburger menu to the mobile layout providing buttons for
opening workflows, templates, and an additional exit linear option.
- Button takes up a full line of space, so it doesn't provide much space
savings currently. May need some design iteration.
<img width="635" height="225" alt="image"
src="https://github.com/user-attachments/assets/a305e795-db0d-4265-b64b-04326a69216d"
/>

And an unrelated tweak requested by Comfy: when opening templates, the
searchbar is autofocused.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8375-Additional-linear-tweaks-2f66d73d36508172a5e7e716d0cba873)
by [Unito](https://www.unito.io)
2026-01-28 15:22:41 -08:00
Benjamin Lu
cbd073f89d Add inline queue progress bar and text summary (#8271)
Add inline queue progress bar and summary per the new designs.

This adds an inline 3px progress bar in the actionbar container (docked
or floating) and a compact summary line below the top menu that follows
when floating, both gated by the QPO V2 flag and hidden while the
overlay is expanded.


https://github.com/user-attachments/assets/da8ec7b7-35f4-4d52-a83b-15c21b484eba

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8271-Add-inline-queue-progress-bar-and-summary-for-QPO-V2-2f16d73d36508132a6dff247f71e11e4)
by [Unito](https://www.unito.io)

---------

Co-authored-by: github-actions <github-actions@github.com>
2026-01-28 12:20:13 -08:00
Alexander Brown
e44b411ff6 test: simplify test file mocking patterns (#8320)
Simplifies test mocking patterns across multiple test files.

- Removes redundant `vi.hoisted()` calls
- Cleans up mock implementations
- Removes unused imports and variables

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8320-test-simplify-test-file-mocking-patterns-2f46d73d36508150981bd8ecb99a6a11)
by [Unito](https://www.unito.io)

---------

Co-authored-by: Amp <amp@ampcode.com>
Co-authored-by: GitHub Action <action@github.com>
2026-01-28 12:17:16 -08:00
Rizumu Ayaka
dd3e4d3edc fix: hide label of textarea in right side panel + align switch to the left (#8279)
<img width="350" alt="image"
src="https://github.com/user-attachments/assets/3ff3f83c-163b-44df-8b68-7fe18c3266c4"
/>
 | 
<img width="350" alt="CleanShot 2026-01-23 at 21 25 04@2x"
src="https://github.com/user-attachments/assets/c2c630f3-6990-4a55-aa8f-a19297ffee52"
/>

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8279-fix-hide-label-of-textarea-in-right-side-panel-align-switch-to-the-left-2f16d73d365081e9b932fb2be873b660)
by [Unito](https://www.unito.io)

---------

Co-authored-by: GitHub Action <action@github.com>
Co-authored-by: Alexander Brown <drjkl@comfy.org>
2026-01-27 23:21:03 -08:00
pythongosssss
d890e7568a Add asset deletion progress indicator (#7906)
## Summary

Currently the delay between user action and visual response is too long
and it looks like it hasn't worked.
This adds a visual indicator that the action is processing.

## Changes

- add loading indicator while asset is deleting

## Screenshots (if applicable)
(Artifically delayed deletion)


https://github.com/user-attachments/assets/a9a8b9fe-896d-4666-b643-ec8b990f6444

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-7906-Add-asset-deletion-progress-indicator-2e26d73d365081ed82b8e770ba3d0615)
by [Unito](https://www.unito.io)

---------

Co-authored-by: Alexander Brown <drjkl@comfy.org>
Co-authored-by: Jin Yi <jin12cc@gmail.com>
2026-01-27 21:21:50 -07:00
Kelly Yang
f0eecdfdfa Refactor DropZone to use VueUse useDropZone (#8174)
## Summary

Refactor DropZone to use VueUse useDropZone. (#7971, #7734)

## Changes

- **What**: Replaced `@dragover`, `@dragleave`, and `@drop` bindings
with `useDropZone`.

## Review Focus

## Screenshots

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8174-Refactor-DropZone-to-use-VueUse-useDropZone-2ee6d73d365081ed9d31d68f7cf42263)
by [Unito](https://www.unito.io)
2026-01-27 20:25:58 -05:00
Alexander Brown
14369c08a3 refactor: parallelize bootstrap and simplify lifecycle with VueUse (#8307)
## Summary

Refactors bootstrap and lifecycle management to parallelize
initialization, use Vue best practices, and fix a logout state bug.

## Changes

### Bootstrap Store (`bootstrapStore.ts`)
- Extract early bootstrap logic into a dedicated store using
`useAsyncState`
- Parallelize settings, i18n, and workflow sync loading (previously
sequential)
- Handle multi-user login scenarios by deferring settings/workflows
until authenticated

### GraphCanvas Refactoring
- Move non-DOM composables (`useGlobalLitegraph`, `useCopy`, `usePaste`,
etc.) to script setup level for earlier initialization
- Move `watch` and `whenever` declarations outside `onMounted` (Vue best
practice)
- Use `until()` from VueUse to await bootstrap store readiness instead
of direct async calls

### GraphView Simplification
- Replace manual `addEventListener`/`removeEventListener` with
`useEventListener`
- Replace `setInterval` with `useIntervalFn` for automatic cleanup
- Move core command registration to script setup level

### Bug Fix
Using `router.push()` for logout caused `isSettingsReady` to persist as
`true`, making new users inherit the previous user's cached settings.
Reverted to `window.location.reload()` with TODOs for future store reset
implementation.

## Testing

- Verified login/logout cycle clears settings correctly
- Verified bootstrap sequence completes without errors

---------

Co-authored-by: Amp <amp@ampcode.com>
2026-01-27 12:50:13 -08:00
Alexander Brown
75fd4f0e67 feat: Make the text for the value control a clickable label (#8334)
## Summary

More clickable!

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8334-feat-Make-the-text-for-the-value-control-a-clickable-label-2f56d73d365081e79886eddeec03458e)
by [Unito](https://www.unito.io)

---------

Co-authored-by: GitHub Action <action@github.com>
2026-01-27 20:16:06 +00:00
Johnpaul Chiwetelu
3946d7b5ff Road to no explicit any part 8 group 5 (#8329)
## Summary
- Add `createMockLLink` and `createMockLinks` factory functions to
handle hybrid Map/Record types
- Replace `as any` assertions with type-safe factory functions in
minimap tests
- Implement proper Pinia store mocking using `vi.hoisted()` pattern
- Remove unused `createMockSubgraph` export (shadowed by local
implementations)

## Test plan
- [x] All minimap tests pass (29 tests)
- [x] `pnpm typecheck` passes
- [x] `pnpm lint` passes
- [x] `pnpm knip` passes

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8329-Road-to-no-explicit-any-part-8-group-5-2f56d73d365081218882de81d5526220)
by [Unito](https://www.unito.io)

---------

Co-authored-by: AustinMroz <austin@comfy.org>
2026-01-27 19:25:15 +01:00
Rizumu Ayaka
e5e51c8e00 fix: image selection modal Inputs Outputs filtering is not working (#8272)
filteredItems only updates during search, and does not update when the
filter changes. Trigger a re-search when items change through the
updateKey prop chain.
2026-01-26 19:40:33 -07:00