- Change NodePreviewCard width to w-50 with scale-50
- Add gap-2 between input/output name and type
- Truncate type text instead of name when overflowing
- Teleport preview to body to fix z-index stacking context issues
- Remove dropEffect check in handleDragEnd for consistent behavior
- Update unit tests to reflect new drag-drop behavior
Amp-Thread-ID: https://ampcode.com/threads/T-019c56f7-b162-760c-999d-024481032451
Co-authored-by: Amp <amp@ampcode.com>
- Add main_category field to node definitions in api.ts
- Add main_category to ComfyNodeDefImpl class
- Group nodes by main_category in 'all' and 'custom' tabs
- Format category names from snake_case to Title Case
Amp-Thread-ID: https://ampcode.com/threads/T-019c1ee5-bb3c-70fb-8c24-33966d8dbef8
Co-authored-by: Amp <amp@ampcode.com>
- Add three-tab structure (Essential, All, Custom) using Reka UI Tabs
- Implement TreeExplorerV2 with virtualized tree using TreeRoot/TreeVirtualizer
- Add node hover preview with teleport to show NodePreview component
- Implement context menu for toggling favorites on nodes
- Add search functionality that auto-expands matching folders
- Create panel components: EssentialNodesPanel, AllNodesPanel, CustomNodesPanel
- Add 'Open Manager' button in CustomNodesPanel
- Use custom icons: comfy--node for nodes, ph--folder-fill for folders
Co-authored-by: Amp <amp@ampcode.com>
Amp-Thread-ID: https://ampcode.com/threads/T-019c1ee5-bb3c-70fb-8c24-33966d8dbef8
## Description
When clicking a multi-output job to enter folder view,
`resolveOutputAssetItems` fetches job details asynchronously. During
this fetch, the panel showed "No generated files found" because there
was no loading state for the folder resolution—only the media list fetch
had one.
This replaces the empty state flash with skeleton cards that match the
asset grid layout, using the known output count from metadata to render
the correct number of placeholders.
Supersedes #8960.
### Changes
- **Add shadcn/vue `Skeleton` component**
(`src/components/ui/skeleton/Skeleton.vue`)
- **Use `useAsyncState`** from VueUse to track folder asset resolution,
providing `isLoading` automatically
- **Wire `folderLoading`** into `showLoadingState` and `showEmptyState`
computeds
- **Replace `ProgressSpinner`** with a skeleton grid that mirrors the
asset card layout
- **Use `metadata.outputCount`** to predict skeleton count; falls back
to 6
### Before / After
| Before | After |
|--------|-------|
| "No generated files found" flash | Skeleton cards matching grid layout
|
## Checklist
- [x] Code follows project conventions
- [x] No `any` types introduced
- [x] Lint and typecheck pass
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8979-fix-show-skeleton-loading-state-in-asset-folder-view-30c6d73d365081fa9809f616204ed234)
by [Unito](https://www.unito.io)
---------
Co-authored-by: Amp <amp@ampcode.com>
## Summary
Adds bulk asset export with ZIP download for cloud users. When selecting
2+ assets and clicking download, the frontend now requests a server-side
ZIP export instead of triggering individual file downloads.
## Changes
### New files
- **`AssetExportProgressDialog.vue`** — HoneyToast-based progress dialog
showing per-job export status with progress percentages, error
indicators, and a manual re-download button for completed exports
- **`assetExportStore.ts`** — Pinia store that tracks export jobs,
handles `asset_export` WebSocket events for real-time progress, polls
stale exports via the task API as a fallback, and auto-triggers ZIP
download on completion
### Modified files
- **`useMediaAssetActions.ts`** — `downloadMultipleAssets` now routes to
ZIP export (via `createAssetExport`) in cloud mode when 2+ assets are
selected; single assets and OSS mode still use direct download
- **`assetService.ts`** — Added `createAssetExport()` and
`getExportDownloadUrl()` endpoints
- **`apiSchema.ts`** — Added `AssetExportWsMessage` type for the
WebSocket event
- **`api.ts`** — Wired up `asset_export` WebSocket event
- **`GraphView.vue`** — Mounted `AssetExportProgressDialog`
- **`main.json`** — Added i18n keys for export toast UI
## How it works
1. User selects multiple assets and clicks download
2. Frontend calls `POST /assets/export` with asset/job IDs
3. Backend creates a ZIP task and streams progress via `asset_export`
WebSocket events
4. `AssetExportProgressDialog` shows real-time progress
5. On completion, the ZIP is auto-downloaded via a presigned URL from
`GET /assets/exports/{name}`
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8712-feat-bulk-asset-export-with-ZIP-download-3006d73d365081839ec3dd3e7b0d3b77)
by [Unito](https://www.unito.io)
---------
Co-authored-by: Amp <amp@ampcode.com>
Co-authored-by: GitHub Action <action@github.com>
## Summary
Address follow-up review feedback from #8740 by consolidating
completed-banner thumbnail fields and tightening queue count
sanitization.
## Changes
- **What**:
- Consolidated completed notification thumbnail data to `thumbnailUrls`
only by removing legacy `thumbnailUrl` support from the queue banner
notification type and renderer.
- Updated queue notification banner stories to use `thumbnailUrls`
consistently.
- Simplified `sanitizeCount` to treat any non-positive or non-number
value as the fallback count (`1`).
## Review Focus
Confirm the completed notification payload shape is now consistently
`thumbnailUrls` and no consumer relies on `thumbnailUrl`.
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8880-fix-queue-address-follow-up-review-comments-from-8740-3076d73d365081719a70d860691c5f05)
by [Unito](https://www.unito.io)
## Summary
Add a dedicated **Errors tab** to the Right Side Panel that displays
prompt-level, node validation, and runtime execution errors in a
unified, searchable, grouped view — replacing the need to rely solely on
modal dialogs for error inspection.
## Changes
- **What**:
- **New components** (`errors/` directory):
- `TabErrors.vue` — Main error tab with search, grouping by class type,
and canvas navigation (locate node / enter subgraph).
- `ErrorNodeCard.vue` — Renders a single error card with node ID badge,
title, action buttons, and error details.
- `types.ts` — Shared type definitions (`ErrorItem`, `ErrorCardData`,
`ErrorGroup`).
- **`executionStore.ts`** — Added `PromptError` interface,
`lastPromptError` ref and `hasAnyError` computed getter. Clears
`lastPromptError` alongside existing error state on execution start and
graph clear.
- **`rightSidePanelStore.ts`** — Registered `'errors'` as a valid tab
value.
- **`app.ts`** — On prompt submission failure (`PromptExecutionError`),
stores prompt-level errors (when no node errors exist) into
`lastPromptError`. On both runtime execution error and prompt error,
deselects all nodes and opens the errors tab automatically.
- **`RightSidePanel.vue`** — Shows the `'errors'` tab (with ⚠ icon) when
errors exist and no node is selected. Routes to `TabErrors` component.
- **`TopMenuSection.vue`** — Highlights the action bar with a red border
when any error exists, using `hasAnyError`.
- **`SectionWidgets.vue`** — Detects per-node errors by matching
execution IDs to graph node IDs. Shows an error icon (⚠) and "See Error"
button that navigates to the errors tab.
- **`en/main.json`** — Added i18n keys: `errors`, `noErrors`,
`enterSubgraph`, `seeError`, `promptErrors.*`, and `errorHelp*`.
- **Testing**: 6 unit tests (`TabErrors.test.ts`) covering
prompt/node/runtime errors, search filtering, and clipboard copy.
- **Storybook**: 7 stories (`ErrorNodeCard.stories.ts`) for badge
visibility, subgraph buttons, multiple errors, runtime tracebacks, and
prompt-only errors.
- **Breaking**: None
- **Dependencies**: None — uses only existing project dependencies
(`vue-i18n`, `pinia`, `primevue`)
## Related Work
> **Note**: Upstream PR #8603 (`New bottom button and badges`)
introduced a separate `TabError.vue` (singular) that shows per-node
errors when a specific node is selected. Our `TabErrors.vue` (plural)
provides the **global error overview** — a different scope. The two tabs
coexist:
> - `'error'` (singular) → appears when a node with errors is selected →
shows only that node's errors
> - `'errors'` (plural) → appears when no node is selected and errors
exist → shows all errors grouped by class type
>
> A future consolidation of these two tabs may be desirable after design
review.
## Architecture
```
executionStore
├── lastPromptError: PromptError | null ← NEW (prompt-level errors without node IDs)
├── lastNodeErrors: Record<string, NodeError> (existing)
├── lastExecutionError: ExecutionError (existing)
└── hasAnyError: ComputedRef<boolean> ← NEW (centralized error detection)
TabErrors.vue (errors tab - global view)
├── errorGroups: ComputedRef<ErrorGroup[]> ← normalizes all 3 error sources
├── filteredGroups ← search-filtered view
├── locateNode() ← pan canvas to node
├── enterSubgraph() ← navigate into subgraph
└── ErrorNodeCard.vue ← per-node card with copy/locate actions
types.ts
├── ErrorItem { message, details?, isRuntimeError? }
├── ErrorCardData { id, title, nodeId?, errors[] }
└── ErrorGroup { title, cards[], priority }
```
## Review Focus
1. **Error normalization logic** (`TabErrors.vue` L75–150): Three
different error sources (prompt, node validation, runtime) are
normalized into a common `ErrorGroup → ErrorCardData → ErrorItem`
hierarchy. Edge cases to verify:
- Prompt errors with known vs unknown types (known types use localized
descriptions)
- Multiple errors on the same node (grouped into one card)
- Runtime errors with long tracebacks (capped height with scroll)
2. **Canvas navigation** (`TabErrors.vue` L210–250): The `locateNode`
and `enterSubgraph` functions navigate to potentially nested subgraphs.
The double `requestAnimationFrame` is required due to LiteGraph's
asynchronous subgraph switching — worth verifying this timing is
sufficient.
3. **Store getter consolidation**: `hasAnyError` replaces duplicated
logic in `TopMenuSection` and `RightSidePanel`. Confirm that the
reactive dependency chain works correctly (it depends on 3 separate
refs).
4. **Coexistence with upstream `TabError.vue`**: The singular `'error'`
tab (upstream, PR #8603) and our plural `'errors'` tab serve different
purposes but share similar naming. Consider whether a unified approach
is preferred.
## Test Results
```
✓ renders "no errors" state when store is empty
✓ renders prompt-level errors (Group title = error message)
✓ renders node validation errors grouped by class_type
✓ renders runtime execution errors from WebSocket
✓ filters errors based on search query
✓ calls copyToClipboard when copy button is clicked
Test Files 1 passed (1)
Tests 6 passed (6)
```
## Screenshots (if applicable)
<img width="1238" height="1914" alt="image"
src="https://github.com/user-attachments/assets/ec39b872-cca1-4076-8795-8bc7c05dc665"
/>
<img width="669" height="1028" alt="image"
src="https://github.com/user-attachments/assets/bdcaa82a-34b0-46a5-a08f-14950c5a479b"
/>
<img width="644" height="1005" alt="image"
src="https://github.com/user-attachments/assets/ffef38c6-8f42-4c01-a0de-11709d54b638"
/>
<img width="672" height="505" alt="image"
src="https://github.com/user-attachments/assets/5cff7f57-8d79-4808-a71e-9ad05bab6e17"
/>
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8807-Feat-errors-tab-panel-3046d73d36508127981ac670a70da467)
by [Unito](https://www.unito.io)
## Summary
Add node replacement UI to the missing nodes dialog. Users can select
and replace deprecated/missing nodes with compatible alternatives
directly from the dialog.
## Changes
- Classify missing nodes into **Replaceable** (quick fix) and **Install
Required** sections
- Add select-all checkbox + per-node checkboxes for batch replacement
- `useNodeReplacement` composable handles in-place node replacement on
the graph:
- Simple replacement (configure+copy) for nodes without mapping
- Input/output connection remapping for nodes with mapping
- Widget value transfer via `old_widget_ids`
- Dot-notation input handling for Autogrow/DynamicCombo
- Undo/redo support via `changeTracker` (try/finally)
- Title and properties preservation
- Footer UX: "Skip for Now" button when all nodes are replaceable (cloud
+ OSS)
- Auto-close dialog when all replaceable nodes are replaced and no
non-replaceable remain
- Settings navigation link from "Don't show again" checkbox
- 505-line unit test suite for `useNodeReplacement`
## Review Focus
- `useNodeReplacement.ts` — core graph manipulation logic
- `MissingNodesContent.vue` — checkbox selection state management
- `MissingNodesFooter.vue` — conditional button rendering (cloud vs OSS
vs all-replaceable)
[screen-capture.webm](https://github.com/user-attachments/assets/7dae891c-926c-4f26-987f-9637c4a2ca16)
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8604-feat-Node-replacement-UI-2fd6d73d36508148a371dabb8f4115af)
by [Unito](https://www.unito.io)
---------
Co-authored-by: github-actions <github-actions@github.com>
## Summary
Move the queue overlay "Show assets" action into the filter controls as
an icon button, so the action is available inline with other list
controls while keeping existing behavior.
## Changes
- **What**:
- Remove the full-width "Show assets" button from
`QueueOverlayExpanded`.
- Add a secondary icon button in `JobFiltersBar` with tooltip +
aria-label and emit `showAssets` on click.
- Wire `showAssets` from `JobFiltersBar` through `QueueOverlayExpanded`
to the existing handler.
- Add `JobFiltersBar` unit coverage to verify `showAssets` is emitted
when the icon button is clicked.
## Review Focus
- Verify the icon button placement in the filter row is sensible and
discoverable.
- Verify clicking the new button opens the assets panel as before.
- Verify tooltip and accessibility label copy are correct.
## Screenshots (if applicable)
Design:
https://www.figma.com/design/LVilZgHGk5RwWOkVN6yCEK/Queue-Progress-Modal?node-id=3924-38560&m=dev
<img width="349" height="52" alt="Screenshot 2026-02-16 at 4 53 34 PM"
src="https://github.com/user-attachments/assets/347772d6-5536-457a-a65f-de251e35a0e4"
/>
## Summary
- Move queued-count summary and clear-queued action into the Queue Overlay header so controls remain visible while expanded content scrolls.
## What changed
- `QueueOverlayExpanded.vue`
- Passes `queuedCount` and `clearQueued` through to the header.
- Removes duplicated summary/action content from the lower section.
- `QueueOverlayHeader.vue`
- Accepts new header data/actions for queued count and clear behavior.
- Renders queued summary and clear button beside the title.
- Adjusts layout to support persistent header actions.
- Updated header unit tests to cover queued summary rendering and clear action behavior.
## Testing
- Header unit tests were updated for the new behavior.
- No additional test execution was requested.
## Notes
- UI composition change only; queue execution semantics are unchanged.
Design: https://www.figma.com/design/LVilZgHGk5RwWOkVN6yCEK/Queue-Progress-Modal?node-id=3924-38560&m=dev
<img width="356" height="59" alt="Screenshot 2026-02-16 at 3 30 44 PM" src="https://github.com/user-attachments/assets/987e42bd-9e24-4e65-9158-3f96b5338199" />
## Summary
- Localize QueueProgressOverlay header counts with dedicated i18n pluralization keys for running and queued jobs.
- Replace the previous aggregate active-job wording with a translated running/queued summary.
## What changed
- Updated `QueueProgressOverlay.vue` to derive `runningJobsLabel`, `queuedJobsLabel`, and `runningQueuedSummary` via `useI18n`.
- Added `QueueProgressOverlay.test.ts` coverage for expanded-header text in both active and empty queue states.
- Added new English locale keys in `src/locales/en/main.json`:
- `runningJobsLabel`
- `queuedJobsLabel`
- `runningQueuedSummary`
## Testing
- `Storybook Build Status` passed.
- `Playwright Tests` were still running at the last check; merge should wait for completion.
## Notes
- Behavioral scope is limited to queue overlay header text/rendering.
Design: https://www.figma.com/design/LVilZgHGk5RwWOkVN6yCEK/Queue-Progress-Modal?node-id=3924-38560&m=dev
<img width="356" height="59" alt="Screenshot 2026-02-16 at 3 30 44 PM" src="https://github.com/user-attachments/assets/987e42bd-9e24-4e65-9158-3f96b5338199" />
## Summary
Add a small pulsing blue indicator dot to the top-right of the `N
active` queue button when there are active jobs.
## Changes
- **What**: Reused `StatusBadge` (`variant="dot"`) in `TopMenuSection`
as a top-right indicator on the queue toggle button, shown only when
`activeJobsCount > 0` and animated with `animate-pulse`.
- **What**: Added tests to verify the indicator appears for nonzero
active jobs and is hidden when there are no active jobs.
## Review Focus
- Dot positioning on the queue button (`-top-0.5 -right-0.5`) across top
menu layouts.
- Indicator visibility behavior tied to `activeJobsCount > 0`.
## Screenshots (if applicable)
https://github.com/user-attachments/assets/9bdb7675-3e58-485b-abdd-446a76b2dafc
won't be shown on 0 active, I was just testing locally
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8915-fix-add-pulsing-active-jobs-indicator-on-queue-button-3096d73d36508181abf5c27662e0d9ae)
by [Unito](https://www.unito.io)
---------
Co-authored-by: Alexander Brown <drjkl@comfy.org>
Sometimes, middle mouse clicks would fail to initiate a canvas pan,
depending on the target of the initial pan. This PR adds a capturing
event handler to the transform pane that forwards the pointer event to
canvas if
- It is a middle mouse click
- The target element is not a focused text element
Resolves#6911
While testing this, I encountered infrequent cases of "some nodes
unintentionally translating continually to the left". Reproduction was
too unreliable to properly track down, but did appear unrelated to this
PR.
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8865-A-heavy-handed-fix-for-middlemouse-pan-3076d73d365081ea9a4ddd5786fc647a)
by [Unito](https://www.unito.io)
## Summary
Add per-widget and reset-all-parameters functionality to the right side
panel, allowing users to quickly revert widget values to their defaults.
## Changes
- **What**: Per-widget "Reset to default" option in the WidgetActions
overflow menu, plus a "Reset all parameters" button in each
SectionWidgets header. Defaults are derived from the InputSpec (explicit
default, then type-specific fallbacks: 0 for INT/FLOAT, false for
BOOLEAN, empty string for STRING, first option for COMBO).
- **Dependencies**: Builds on #8594 (WidgetValueStore) for reactive UI
updates after reset.
## Review Focus
- `getWidgetDefaultValue` fallback logic in `src/utils/widgetUtil.ts` —
are the type-specific defaults appropriate?
- Deep equality check (`isEqual`) for disabling the reset button when
the value already matches the default.
- Event flow: WidgetActions emits `resetToDefault` → WidgetItem forwards
→ SectionWidgets handles via `writeWidgetValue` (sets value, triggers
callback, marks canvas dirty).
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8861-feat-add-reset-to-default-for-widget-parameters-in-right-side-panel-3076d73d365081d1aa08d5b965a16cf4)
by [Unito](https://www.unito.io)
Co-authored-by: Terry Jia <terryjia88@gmail.com>
## Summary
Replace the old completion-summary overlay path with queue notification
banners for queueing/completed/failed lifecycle feedback.
## Key changes
- Added `QueueNotificationBanner`, `QueueNotificationBannerHost`,
stories, and tests.
- Added `useQueueNotificationBanners` to handle:
- immediate `queuedPending` on `promptQueueing`
- transition to `queued` on `promptQueued` (request-id aware)
- completed/failed notification sequencing from finished batch history
- timed notification queueing/dismissal
- Removed completion-summary implementation:
- `useCompletionSummary`
- `CompletionSummaryBanner`
- `QueueOverlayEmpty`
- Simplified `QueueProgressOverlay` to `hidden | active | expanded`
states.
- Top menu behavior:
- restored `QueueInlineProgressSummary` as separate UI
- ordering is inline summary first, notification banner below
- notification banner remains under the top menu section (not teleported
to floating actionbar target)
- Kept established API-event signaling pattern
(`promptQueueing`/`promptQueued`) instead of introducing a separate bus.
- Updated tests for top-menu visibility/ordering and notification
behavior across QPOV2 enabled/disabled.
## Notes
- Completion notifications now support stacked thumbnails (cap: 3).
-
https://www.figma.com/design/LVilZgHGk5RwWOkVN6yCEK/Queue-Progress-Modal?node-id=3843-20314&m=dev
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8740-feat-Queue-Notification-Toasts-3016d73d3650814c8a50d9567a40f44d)
by [Unito](https://www.unito.io)
## Summary
- Add `typescript/no-explicit-any` rule to `.oxlintrc.json` to enforce
no explicit `any` types
- Fix all 40 instances of explicit `any` throughout the codebase
- Improve type safety with proper TypeScript types
## Changes Made
### Configuration
- Added `typescript/no-explicit-any` rule to `.oxlintrc.json`
### Type Fixes
- Replaced `any` with `unknown` for truly unknown types
- Updated generic type parameters to use `unknown` defaults instead of
`any`
- Fixed method `this` parameters to avoid variance issues
- Updated component props to match new generic types
- Fixed test mocks to use proper type assertions
### Key Files Modified
- `src/types/treeExplorerTypes.ts`: Updated TreeExplorerNode interface
generics
- `src/platform/settings/types.ts`: Fixed SettingParams generic default
- `src/lib/litegraph/src/LGraph.ts`: Fixed ParamsArray type constraint
- `src/extensions/core/electronAdapter.ts`: Fixed onChange callbacks
- `src/views/GraphView.vue`: Added proper type imports
- Multiple test files: Fixed type assertions and mocks
## Test Plan
- [x] All lint checks pass (`pnpm lint`)
- [x] TypeScript compilation succeeds (`pnpm typecheck`)
- [x] Pre-commit hooks pass
- [x] No regression in functionality
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8601-feat-add-typescript-no-explicit-any-rule-and-fix-all-instances-2fd6d73d365081fd9beef75d5a6daf5b)
by [Unito](https://www.unito.io)
---------
Co-authored-by: GitHub Action <action@github.com>
Co-authored-by: Alexander Brown <drjkl@comfy.org>
## Summary
Scope the top-menu Manager button red dot to manager conflict state
only, so release-update notifications do not appear on the Manager
button.
## Changes
- **What**:
- In `TopMenuSection`, remove release-store coupling and use only
`useConflictAcknowledgment().shouldShowRedDot` for the Manager button
indicator.
- Add a regression test in `TopMenuSection.test.ts` that keeps the
release red dot true while asserting the Manager button dot only appears
when the conflict red dot is true.
## Review Focus
- Confirm Manager button notification semantics are conflict-specific
and no longer mirror release notifications.
- Confirm the new test fails if release-store coupling is reintroduced.
## Screenshots (if applicable)
N/A
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8810-fix-scope-manager-button-red-dot-to-conflicts-3046d73d3650817887b9ca9c33919f48)
by [Unito](https://www.unito.io)
- "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"
/>

---------
Co-authored-by: github-actions <github-actions@github.com>