This pull request introduces several improvements to Vue reactivity and
user experience in the graph node and widget system. The main focus is
on ensuring that changes to node and widget data reliably trigger
updates in Vue components, improving drag-and-drop support for nodes,
and enhancing widget value handling for better compatibility and
reactivity.
**Vue Reactivity Improvements:**
* In `useGraphNodeManager.ts`, node data updates now create a completely
new object and add a timestamp (`_updateTs`) to force Vue's reactivity
system to detect changes. Additionally, node data is re-set on the next
tick to guarantee component updates.
[[1]](diffhunk://#diff-f980db6f42cef913c3fe92669783b255d617e40b9ccef9a1ab9cc8e326ff1790L272-R280)
[[2]](diffhunk://#diff-f980db6f42cef913c3fe92669783b255d617e40b9ccef9a1ab9cc8e326ff1790R326-R335)
* Widget value composables (`useWidgetValue` and related helpers) now
accept either a direct value or a getter function for `modelValue`, and
always normalize it to a getter. Watches are updated to use this getter
for more reliable reactivity.
[[1]](diffhunk://#diff-92dc3c8b09ab57105e400e115196aae645214f305685044f62edc3338afa0911L13-R14)
[[2]](diffhunk://#diff-92dc3c8b09ab57105e400e115196aae645214f305685044f62edc3338afa0911R49-R57)
[[3]](diffhunk://#diff-92dc3c8b09ab57105e400e115196aae645214f305685044f62edc3338afa0911L82-R91)
[[4]](diffhunk://#diff-92dc3c8b09ab57105e400e115196aae645214f305685044f62edc3338afa0911L100-R104)
[[5]](diffhunk://#diff-92dc3c8b09ab57105e400e115196aae645214f305685044f62edc3338afa0911L117-R121)
[[6]](diffhunk://#diff-92dc3c8b09ab57105e400e115196aae645214f305685044f62edc3338afa0911L140-R144)
[[7]](diffhunk://#diff-0c43cefa9fb524ae86541c7ca851e97a22b3fd01f95795c83273c977be77468fL47-R47)
* In `useImageUploadWidget.ts`, widget value updates now use a new
array/object to ensure Vue detects the change, especially for batch
uploads.
**Drag-and-Drop Support for Nodes:**
* The `LGraphNode.vue` component adds drag-and-drop event handlers
(`dragover`, `dragleave`, `drop`) and visual feedback (`isDraggingOver`
state and highlight ring) for improved user experience when dragging
files onto nodes. Node callbacks (`onDragOver`, `onDragDrop`) are used
for custom validation and handling.
[[1]](diffhunk://#diff-a7744614cf842e54416047326db79ad81f7c7ab7bfb66ae2b46f5c73ac7d47f2L26-R27)
[[2]](diffhunk://#diff-a7744614cf842e54416047326db79ad81f7c7ab7bfb66ae2b46f5c73ac7d47f2R47-R49)
[[3]](diffhunk://#diff-a7744614cf842e54416047326db79ad81f7c7ab7bfb66ae2b46f5c73ac7d47f2R482-R521)
**Widget and Audio Upload Handling:**
* In `uploadAudio.ts`, after uploading an audio file, the widget's
callback is manually triggered to ensure Vue nodes update. There is also
a commented-out call to mark the canvas as dirty for potential future
refresh logic.
[[1]](diffhunk://#diff-796b36f2cafb906a5e95b5750ca5ddc1bf57a304d4a022e0bdaee04b4ee5bbc4R61-R65)
[[2]](diffhunk://#diff-796b36f2cafb906a5e95b5750ca5ddc1bf57a304d4a022e0bdaee04b4ee5bbc4R190-R191)
These changes collectively improve the reliability and responsiveness of
UI updates in the graph node system, especially in scenarios involving
external updates, drag-and-drop interactions, and batch widget value
changes.
https://github.com/user-attachments/assets/8e3194c9-196c-4e13-ad0b-a32177f2d062
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6514-Drag-vuenodes-input-29e6d73d3650817da1b7ef96b61b752d)
by [Unito](https://www.unito.io)
Summary
- Add comprehensive telemetry for key UI interactions using existing
telemetry hooks (cloud-enabled, no-op in OSS):
Sidebar and top-level
- Node library button: `node_library`
- Model library button: `model_library`
- Workflows button: `workflows`
- Assets/Media button: `assets`
- Templates button: `templates`
- Keyboard Shortcuts: `keyboard_shortcuts`
- Console: `console`
- Help Center: `help_center`
- Settings (from Comfy logo menu): `settings_menu`
Floating canvas menu
- Minimap toggle: `minimap_toggle`
- Hide links toggle: `hide_links`
Run button and queue
- Run button handle (drag): `run_button_handle`
- Run mode selection: `run_instant`, `run_on_change`
- Queue multiple: `queue_multiple` fires on each run when batch count >
1 (moved from batch-count-change to run-time, per guidance)
Error dialogs
- Close (X/mask/ESC): `error_dialog_close` via dialog onClose
- Show report: `error_show_report`
- Help fix this: `error_help_fix_this`
- Find issues: `error_find_issues`
Nodes / Subgraphs
- Selection toolbox “Node info”: `node_info`
- Enter subgraph (node header enter): `open_subgraph`
- Subgraph breadcrumb navigation: `subgraph_breadcrumb_item` and
`subgraph_breadcrumb_root`
Settings / Credits / Search
- Settings menu button (under Comfy logo): `settings_menu`
- Purchase credits (Settings > Credits panel): tracked via existing
`trackAddApiCreditButtonClicked`
- Purchase credits (Avatar popover Top Up): tracked via existing
`trackAddApiCreditButtonClicked`
- Debounced search telemetry already present for node search and
template filters; left as-is
Notes and answers
- Error dialog onClose: only fires when the dialog actually closes (X,
mask, ESC, or programmatic close). “Show report” and “Help fix this” do
not close the dialog; they each emit their own events.
- Telemetry is behind the cloud provider; calls are optional
(`useTelemetry()?.…`). OSS builds send nothing.
Open questions / follow-ups
- Primary Run button click: today cloud-only `trackRunButton` exists; we
can also emit a UI-level `run` click (`UI_BUTTON_CLICKED` style)
alongside it if desired. Confirm preference and I can add it.
- Subgraph usage richness: if we want structured analytics (e.g.,
action, depth, subgraph id, node count), I can add a dedicated provider
method and include richer metadata at enter/breadcrumb.
- Optional parity: track the Comfy menu’s “Browse Templates” item in
addition to the sidebar Templates button.
Quality
- Ran `pnpm lint:fix` and `pnpm typecheck`; both pass locally.
Implementation details
- All handlers refactored to named functions where needed; used `void`
for intentionally unawaited async calls per lint rules.
- Event names kept consistent in `button_id` strings; happy to align to
a different naming scheme if you prefer.
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6511-feat-telemetry-add-tracking-for-sidebar-run-menu-dialogs-subgraphs-settings-and-cre-29e6d73d365081a1b8b4fdfbbf40e18b)
by [Unito](https://www.unito.io)
---------
Co-authored-by: Christian Byrne <cbyrne@comfy.org>
Co-authored-by: Claude <noreply@anthropic.com>
Selecting a new image from a batch sets isLoading to true, but
handleImageLoad is never triggered so the image never displays.
Swapping to a different image from a batch is currently the only place
isLoading is set to true. This change, even if temporary, results in a
good chunk of dead code.
To my understanding, ImagePreviews are always object URLs and should
never need to load, so I don't foresee the loading placeholder being
needed here.
Resolves#6458
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6521-Fix-inability-to-select-image-from-batch-in-vue-29e6d73d36508162abeaeece7c5e0eed)
by [Unito](https://www.unito.io)
Summary
Fully Refactored the Load3D module to improve architecture and
maintainability by consolidating functionality into a
centralized composable pattern and simplifying component structure. and
support VueNodes system
Changes
- Architecture: Introduced new useLoad3d composable to centralize 3D
loading logic and state
management
- Component Simplification: Removed redundant components
(Load3DAnimation.vue, Load3DAnimationScene.vue,
PreviewManager.ts)
- Support VueNodes
- improve config store
- remove lineart output due Animation doesnot support it, may add it
back later
- remove Preview screen and keep scene in fixed ratio in load3d (not
affect preview3d)
- improve record video feature which will already record video by same
ratio as scene
Need BE change https://github.com/comfyanonymous/ComfyUI/pull/10025https://github.com/user-attachments/assets/9e038729-84a0-45ad-b0f2-11c57d7e0c9a
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5765-refactor-refactor-load3d-2796d73d365081728297cc486e2e9052)
by [Unito](https://www.unito.io)
This pull request introduces a comprehensive update to the design
system's color management, focusing on establishing semantic color
tokens for both light and dark themes. It replaces many hardcoded color
values and legacy CSS classes throughout the codebase with new semantic
CSS variables, ensuring consistent theming and easier future
maintenance. The changes affect core CSS files as well as numerous Vue
components, aligning their styling with the new design system.
**Design System Foundation**
* Added a wide range of new color variables to `style.css`, including
base colors (e.g., `--color-white`, `--color-black`), additional shades
for sand, azure, cobalt, gold, coral, and magenta, and new alpha
(transparency) colors.
[[1]](diffhunk://#diff-71b6b57a56095b04e47c797a5016149b76b27971cab04b93f033f1f846e0f5a0L52-R87)
[[2]](diffhunk://#diff-71b6b57a56095b04e47c797a5016149b76b27971cab04b93f033f1f846e0f5a0R119-R123)
* Introduced semantic color tokens for both light and dark modes
(`--base-background`, `--primary-background`,
`--destructive-background`, etc.), mapping them to the new base colors
for consistent usage across the application.
[[1]](diffhunk://#diff-71b6b57a56095b04e47c797a5016149b76b27971cab04b93f033f1f846e0f5a0R219-R239)
[[2]](diffhunk://#diff-71b6b57a56095b04e47c797a5016149b76b27971cab04b93f033f1f846e0f5a0R301-R321)
* Exposed semantic tokens as CSS variables (e.g.,
`--color-base-foreground`, `--color-secondary-background`) for use
throughout the app.
**Component Refactoring to Semantic Tokens**
* Updated Vue components and their tests to use the new semantic color
classes (e.g., `bg-base-background`, `text-base-foreground`,
`bg-secondary-background`) instead of hardcoded colors or legacy
dark-theme classes. This affects components such as
`WorkflowTemplateSelectorDialog.vue`, `BypassButton.vue`,
`ExecuteButton.vue`, `MenuOptionItem.vue`, `AssetCard.vue`,
`MediaAssetMoreMenu.vue`, `MediaTitle.vue`, `WidgetFileUpload.vue`,
`WidgetRecordAudio.vue`, `AudioPreviewPlayer.vue`, and
`FormDropdownMenuActions.vue`.
[[1]](diffhunk://#diff-2c860bdc48e907b1b85dbef846599d8376dd02cff90f49e490eebe61371fecedL149-R149)
[[2]](diffhunk://#diff-8ec606ef1100f3a56945ed24cbdc1965050932cd744d4172a3868cdfd23894c0L95-R95)
[[3]](diffhunk://#diff-80b781aeba31712968ae157bb70194e4b72bc73430d1cca6a79d718d839daed6L10-R10)
[[4]](diffhunk://#diff-55fd9056d35e50249dc9f2280017dc99294221fdbe56d8399cea60f8bac499b5L7-R7)
[[5]](diffhunk://#diff-c5e6830e63e2441d2dc70d2ecf7c9b56d0a93821f827e9c5377fc10ae6016f18L30-R32)
[[6]](diffhunk://#diff-a1091d53a4b5d493e045aab5960188d2e7c3b80002e7178427268835fadb5809L30-R30)
[[7]](diffhunk://#diff-ccdb389a5e355d525dcfa26ecd77519297b6232dd34522411c8bfdd4cde05a1cL6-R6)
[[8]](diffhunk://#diff-7ef9ebd48e6f38a644c1a4e7bae1c7bb818bb959b2d20985974824e299ea5c34L3-R3)
[[9]](diffhunk://#diff-489229f88dfdfd5d883a3ef7fad6effa0790a18a831d5a9d84642dfb246962a2L115-R115)
[[10]](diffhunk://#diff-7bee4b453fc869f546e7150a6e39992ab6442987f80c10f8260b8f3715491997L51-R51)
[[11]](diffhunk://#diff-29348fa2e5b8cec1301a99bdec241379aeefc1747cceeb0c39b7df452ca635ffL41-R41)
[[12]](diffhunk://#diff-d464ebe3a44bec4fda7155e5605bf173612aca409250b7ef6b78920a89ae2044L24-R24)
**Consistency and Maintainability**
* Ensured hover, active, and selected states use semantic background and
foreground colors for both light and dark themes, improving visual
consistency and simplifying future updates.
[[1]](diffhunk://#diff-2c860bdc48e907b1b85dbef846599d8376dd02cff90f49e490eebe61371fecedL183-R183)
[[2]](diffhunk://#diff-a1091d53a4b5d493e045aab5960188d2e7c3b80002e7178427268835fadb5809L115-R114)
[[3]](diffhunk://#diff-ccdb389a5e355d525dcfa26ecd77519297b6232dd34522411c8bfdd4cde05a1cL18-R18)
[[4]](diffhunk://#diff-ccdb389a5e355d525dcfa26ecd77519297b6232dd34522411c8bfdd4cde05a1cL29-R29)
[[5]](diffhunk://#diff-ccdb389a5e355d525dcfa26ecd77519297b6232dd34522411c8bfdd4cde05a1cL43-R43)
[[6]](diffhunk://#diff-ccdb389a5e355d525dcfa26ecd77519297b6232dd34522411c8bfdd4cde05a1cL55-R55)
[[7]](diffhunk://#diff-ccdb389a5e355d525dcfa26ecd77519297b6232dd34522411c8bfdd4cde05a1cL69-R69)
[[8]](diffhunk://#diff-ccdb389a5e355d525dcfa26ecd77519297b6232dd34522411c8bfdd4cde05a1cL83-R83)
[[9]](diffhunk://#diff-2c860bdc48e907b1b85dbef846599d8376dd02cff90f49e490eebe61371fecedL328-R328)
[[10]](diffhunk://#diff-489229f88dfdfd5d883a3ef7fad6effa0790a18a831d5a9d84642dfb246962a2L138-R138)
[[11]](diffhunk://#diff-29348fa2e5b8cec1301a99bdec241379aeefc1747cceeb0c39b7df452ca635ffL119-R119)
[[12]](diffhunk://#diff-29348fa2e5b8cec1301a99bdec241379aeefc1747cceeb0c39b7df452ca635ffL137-R137)
[[13]](diffhunk://#diff-d464ebe3a44bec4fda7155e5605bf173612aca409250b7ef6b78920a89ae2044L53-R53)
[[14]](diffhunk://#diff-d464ebe3a44bec4fda7155e5605bf173612aca409250b7ef6b78920a89ae2044L153-R153)
**Removal of Legacy Styles**
* Removed legacy dark-theme class usage and hardcoded color values,
replacing them with semantic tokens to unify the styling approach.
[[1]](diffhunk://#diff-c5e6830e63e2441d2dc70d2ecf7c9b56d0a93821f827e9c5377fc10ae6016f18L30-R32)
[[2]](diffhunk://#diff-d464ebe3a44bec4fda7155e5605bf173612aca409250b7ef6b78920a89ae2044L24-R24)
[[3]](diffhunk://#diff-d464ebe3a44bec4fda7155e5605bf173612aca409250b7ef6b78920a89ae2044L53-R53)
[[4]](diffhunk://#diff-d464ebe3a44bec4fda7155e5605bf173612aca409250b7ef6b78920a89ae2044L153-R153)
These changes lay the groundwork for a scalable and maintainable design
system, making it easier to implement future theme changes and ensuring
a consistent look and feel across all components.
## 📋 Overview
Implemented a new Media Assets sidebar tab in ComfyUI for managing
user-uploaded input files and generated output files. This feature
supports both local and cloud environments and is currently enabled only
in development mode.
## 🎯 Key Features
### 1. Media Assets Sidebar Tab
- **Imported** / **Generated** files separated by tabs
- Visual display with file preview cards
- Gallery view support (navigable with arrow keys)
### 2. Environment-Specific Implementation
- **`useInternalMediaAssets`**: For local environment
- Fetches file list via `/files` API
- Retrieves generation task execution time via `/history` API
- Processes history data using the same logic as QueueSidebarTab
- **`useCloudMediaAssets`**: For cloud environment
- File retrieval through assetService
- History data processing using TaskItemImpl
- Auto-truncation of long filenames over 20 characters (e.g.,
`very_long_filename_here.png` → `very_long_...here.png`)
### 3. Execution Time Display
- Shows task execution time on generated image cards (e.g., "2.3s")
- Calculated from History API's `execution_start` and
`execution_success` messages
- Displayed at MediaAssetCard's duration chip location
### 4. Gallery Feature
- Full-screen gallery mode on image click
- Navigate between images with keyboard arrows
- Exit gallery with ESC key
- Reuses ResultGallery component from QueueSidebarTab
### 5. Development Mode Only
- Excluded from production builds using `import.meta.env.DEV` condition
- Feature in development, scheduled for official release after
stabilization
## 🛠️ Technical Changes
### New Files Added
- `src/components/sidebar/tabs/AssetsSidebarTab.vue` - Main sidebar tab
component
- `src/composables/sidebarTabs/useAssetsSidebarTab.ts` - Sidebar tab
definition
- `src/composables/useInternalMediaAssets.ts` - Local environment
implementation
- `src/composables/useCloudMediaAssets.ts` - Cloud environment
implementation
- `packages/design-system/src/icons/image-ai-edit.svg` - Icon addition
### Modified Files
- `src/stores/workspace/sidebarTabStore.ts` - Added dev mode only tab
display logic
- `src/platform/assets/components/MediaAssetCard.vue` - Added execution
time display, zoom event
- `src/platform/assets/components/MediaImageTop.vue` - Added image
dimension detection
- `packages/shared-frontend-utils/src/formatUtil.ts` - Added media type
determination utility functions
- `src/locales/en/main.json` - Added translation keys
[media_asset_OSS_cloud.webm](https://github.com/user-attachments/assets/a6ee3b49-19ed-4735-baad-c2ac2da868ef)
---------
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: github-actions <github-actions@github.com>
## Summary
### Problem:
The Vue nodes renderer/feature introduces new designs for each node i.e.
the equivalent Litegraph node design is smaller and the vue node design
is non uniformly larger.
### Example:
Litegraph Ksampler node: 200w x 220h
<img width="200" height="220" alt="image"
src="https://github.com/user-attachments/assets/eef0117b-7e02-407d-98ab-c610fd1ec54c"
/>
Vue Node Ksampler node: 445w x 430h
<img width="445" height="430" alt="image"
src="https://github.com/user-attachments/assets/e78d9d45-5b32-4e8d-bf1c-bce1c699037f"
/>
This means if users load a workflow in Litegraph and then switches to
Vue nodes renderer the nodes are using the same Litegraph positions
which would cause a visual overlap and overall look broken.
### Example:
<img width="1510" height="726" alt="image"
src="https://github.com/user-attachments/assets/3b7ae9d2-6057-49b2-968e-c531a969fac4"
/>
<img width="1475" height="850" alt="image"
src="https://github.com/user-attachments/assets/ea10f361-09bd-4daa-97f1-6b45b5dde389"
/>
### Solution:
Scale the positions of the nodes in lite graph radially from the center
of the bounds of all nodes. And then simply move the Vue nodes to those
new positions.
1. Get the `center of the bounds of all LG nodes`.
2. Get the `xy of each LG node`.
3. Get the vector from `center of the bounds of all LG nodes` `-` `xy of
each LG node`.
4. Scale it by a factor (e.g. 1.75x which is the average Vue node size
increase plus some visual padding.)
5. Move each Vue node to the scaled `xy of each LG node`.
Result: The nodes are spaced apart removing overlaps while keeping the
spatial layout intact.
<img width="2173" height="1096" alt="image"
src="https://github.com/user-attachments/assets/7817d866-4051-47bb-a589-69ca77a0bfd3"
/>
### Further concerns.
This vector scaling algorithm needs to run once per workflow when in vue
nodes. This means when in Litegraph and switching to Vue nodes, it needs
to run before the nodes render. And then now that the entire app is in
vue nodes, we need to run it each time we load a workflow. However, once
its run, we do not need to run it again. Therefore we must persist a
flag that it has run somewhere. This PR also adds that feature by
leveraging the `extra` field in the workflow schema.
---------
Co-authored-by: GitHub Action <action@github.com>
Co-authored-by: JakeSchroeder <jake@axiom.co>
When a widget is linked to a subgraph, the subgraph creates a copy of
the widget. The callback used by the asset browser to update the widget
still refers to the widget that lives inside the subgraph, but at time
of execution, this is overwritten by the unchanged value of the copy.
This is fixed by instead updating the value of the caller. It's a little
hacky, and may need future review.
See also #6237
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6240-Fix-asset-browser-on-subgraph-nodes-2956d73d365081b49bd1cd1a7a254763)
by [Unito](https://www.unito.io)
## Summary
Enables Vue nodes to resize from all four corners and consolidated the
interaction pipeline.
## Changes
- **What**: Added four-corner handles to `LGraphNode`, wired them
through the refactored `useNodeResize` composable, and centralized the
math/preset helpers under `interactions/resize/` with cleaner pure
functions and lint-compliant markup.
## Review Focus
Corner-to-corner resizing accuracy (position + size), pinned-node guard
preventing resize start, and snap-to-grid behavior at varied zoom
levels.
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6187-allow-Vue-nodes-to-be-resized-from-all-4-corners-2936d73d365081c8bf14e944ab24c27f)
by [Unito](https://www.unito.io)
---------
Co-authored-by: DrJKL <DrJKL@users.noreply.github.com>
Co-authored-by: GitHub Action <action@github.com>
## Summary
Fix hover state on Vue node number widget incremenet/decrement buttons.
The problem was in `useNumberWidgetButtonPt.ts`, the button hover styles
were using `var(--color-node-component-surface-hovered)` which
references a Tailwind theme color created by the `@theme` inline
directive. This theme color doesn't properly inherit the `.dark-theme`
class overrides, so it was showing the light mode color (white) even in
dark mode.
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6121-fix-Vue-node-number-widget-inc-dec-buttons-hover-state-style-2906d73d36508144b91aec3490e32d28)
by [Unito](https://www.unito.io)
---------
Co-authored-by: DrJKL <DrJKL0424@gmail.com>
## Summary
This PR reverts #5922 which fixed pointer capture behavior on video and
image preview components to prevent unintended node dragging.
## Changes
- Removes `data-capture-node="true"` attribute from `VideoPreview.vue`
and `ImagePreview.vue` components
- Removes pointer event delegation logic from
`useNodePointerInteractions.ts` composable
- Restores previous drag behavior where dragging on preview components
triggers node drag
## Reason for Revert
This changes the behavior from original Litegraph and is generally
annoying. Users would rather be able to drag the node than be able to
drag an image/video out from a node.
Reverts #5922
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6148-Revert-fix-dragging-video-image-components-on-Vue-nodes-triggers-node-drag-5922-2916d73d365081398bb5c20384e26bb8)
by [Unito](https://www.unito.io)