## Summary
Sort execution error cards within each error group by their node
execution ID in ascending numeric order, ensuring consistent and
predictable display order.
## Changes
- **What**: Added `compareExecutionId` utility to
`src/types/nodeIdentification.ts` that splits node IDs on `:` and
compares segments numerically left-to-right; applied it as a sort
comparator when building `ErrorGroup.cards` in `useErrorGroups.ts`
## Review Focus
- The comparison treats missing segments as `0`, so `"1"` sorts before
`"1:20"` (subgraph nodes follow their parent); confirm this ordering
matches user expectations
- All comparisons are purely numeric — non-numeric segment values would
sort as `NaN` (treated as `0`)
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-9334-feat-error-groups-sort-execution-error-cards-by-node-execution-ID-3176d73d365081e1b3e4e4fa8831fe16)
by [Unito](https://www.unito.io)
## Summary
Implement 11 Figma design discrepancies for the Node Library sidebar and
V2 Node Search dialog, aligning the UI with the [Toolbox Figma
design](https://www.figma.com/design/xMFxCziXJe6Denz4dpDGTq/Toolbox?node-id=2074-21394&m=dev).
## Changes
- **What**: Sidebar: reorder tabs (All/Essentials/Blueprints), rename
Custom→Blueprints, uppercase section headers, chevron-left of folder
icon, bookmark-on-hover for node rows, filter dropdown with checkbox
items, sort labels (Categorized/A-Z) with label-left/check-right layout,
hide section headers in A-Z mode. Search dialog: expand filter chips
from 3→6, add Recents and source categories to sidebar, remove "Filter
by" label. Pull foundation V2 components from merged PR #8548.
- **Dependencies**: Depends on #8987 (V2 Node Search) and #8548
(NodeLibrarySidebarTabV2)
## Review Focus
- Filter dropdown (`filterOptions`) is UI-scaffolded but not yet wired
to filtering logic (pending V2 integration)
- "Recents" category currently returns frequency-based results as
placeholder until a usage-tracking store is implemented
- Pre-existing type errors from V2 PR dependencies not in the base
commit (SearchBoxV2, usePerTabState, TextTicker, getProviderIcon,
getLinkTypeColor, SidebarContainerKey) are expected and will resolve
when rebased onto main after parent PRs land
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-9085-feat-Node-Library-sidebar-and-V2-Search-dialog-Figma-design-improvements-30f6d73d36508175bf72d716f5904476)
by [Unito](https://www.unito.io)
---------
Co-authored-by: Yourz <crazilou@vip.qq.com>
Co-authored-by: github-actions <github-actions@github.com>
## Summary
Wire `essentials_category` through from backend to the Essentials tab
UI. Creates a single source of truth for node categorization and
ordering.
### Changes
**New file — `src/constants/essentialsNodes.ts`:**
- Single source of truth: `ESSENTIALS_NODES` (ordered nodes per
category), `ESSENTIALS_CATEGORIES` (folder display order),
`ESSENTIALS_CATEGORY_MAP` (flat lookup), `TOOLKIT_NOVEL_NODE_NAMES`
(telemetry), `TOOLKIT_BLUEPRINT_MODULES`
**Refactored files:**
- `src/types/nodeSource.ts`: Removed inline `ESSENTIALS_CATEGORY_MOCK`,
imports `ESSENTIALS_CATEGORY_MAP` from centralized constants
- `src/services/nodeOrganizationService.ts`: Removed inline
`NODE_ORDER_BY_FOLDER`, imports `ESSENTIALS_NODES` and
`ESSENTIALS_CATEGORIES`
- `src/constants/toolkitNodes.ts`: Re-exports from `essentialsNodes.ts`
instead of maintaining a separate list
**Subgraph passthrough:**
- `src/stores/subgraphStore.ts`: Passes `essentials_category` from
`GlobalSubgraphData` and extracts it from `definitions.subgraphs[0]` as
fallback
- `src/platform/workflow/validation/schemas/workflowSchema.ts`: Added
`essentials_category` to `SubgraphDefinitionBase` and
`zSubgraphDefinition`
**Tests:**
- `src/constants/essentialsNodes.test.ts`: 6 tests validating no
duplicates, complete coverage, basics exclusion
- `src/stores/subgraphStore.test.ts`: 2 tests for essentials_category
passthrough
All 43 relevant tests pass. Typecheck, lint, format clean.
**Depends on:** Comfy-Org/ComfyUI#12573
Fixes COM-15221
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-9091-feat-wire-essentials_category-for-Essentials-tab-display-30f6d73d3650814ab3d4c06b451c273b)
by [Unito](https://www.unito.io)
---------
Co-authored-by: github-actions <github-actions@github.com>
## Summary
Surfaces missing node pack information in the Errors Tab, grouped by
registry pack, with one-click install support via ComfyUI Manager.
## Changes
- **What**: Errors Tab now groups missing nodes by their registry pack
and shows a `MissingPackGroupRow` with pack name, node/pack counts, and
an Install button that triggers Manager installation. A
`MissingNodeCard` shows individual unresolvable nodes that have no
associated pack. `useErrorGroups` was extended to resolve missing node
types to their registry packs using the `/api/workflow/missing_nodes`
endpoint. `executionErrorStore` was refactored to track missing node
types separately from execution errors and expose them reactively.
- **Breaking**: None
## Review Focus
- `useErrorGroups.ts` — the new `resolveMissingNodePacks` logic fetches
pack metadata and maps node types to pack IDs; edge cases around partial
resolution (some nodes have a pack, some don't) produce both
`MissingPackGroupRow` and `MissingNodeCard` entries
- `executionErrorStore.ts` — the store now separates `missingNodeTypes`
state from `errors`; the deferred-warnings path in `app.ts` now calls
`setMissingNodeTypes` so the Errors Tab is populated even when a
workflow loads without executing
## Screenshots (if applicable)
https://github.com/user-attachments/assets/97f8d009-0cac-4739-8740-fd3333b5a85b
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-9213-feat-show-missing-node-packs-in-Errors-Tab-with-install-support-3126d73d36508197bc4bf8ebfd2125c8)
by [Unito](https://www.unito.io)
## Summary
- Show a canvas-based loading spinner on image upload nodes (LoadImage)
during file upload via drag-drop, paste, or file picker
- Display the uploading file's name immediately in the filename dropdown
instead of showing the previous file's name
- Show the uploading audio file's name immediately in the audio widget
during upload
## Changes
- **`useNodeImageUpload.ts`**: Add `isUploading` flag and
`onUploadStart` callback to the upload lifecycle; clear `node.imgs`
during upload to prevent stale previews
- **`useImagePreviewWidget.ts`**: Add `renderUploadSpinner` that draws
an animated arc spinner on the canvas when `node.isUploading` is true;
guard against empty `imgs` array
- **`useImageUploadWidget.ts`**: Set `fileComboWidget.value` to the new
filename on upload start; clear `node.imgs` on combo widget change
- **`uploadAudio.ts`**: Set `audioWidget.value` to the new filename on
upload start
- **`litegraph-augmentation.d.ts`**: Add `isUploading` property to
`LGraphNode`
https://github.com/user-attachments/assets/818ce529-cb83-428a-8c98-dd900a128343
## Test plan
- [x] Upload an image via file picker on LoadImage node — spinner shows
during upload, filename updates immediately
- [x] Drag-and-drop an image onto LoadImage node — same behavior
- [x] Paste an image onto LoadImage node — same behavior
- [x] Change the dropdown selection on LoadImage — old preview clears,
new image loads
- [x] Upload an audio file — filename updates immediately in the widget
🤖 Generated with [Claude Code](https://claude.com/claude-code)
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-9189-feat-show-loading-spinner-and-uploading-filename-during-image-upload-3126d73d365081e4af27cd7252f34298)
by [Unito](https://www.unito.io)
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
## Summary
Adds `initialPackId` support to the manager dialog so callers can
deep-link directly to a specific node pack — pre-filling the search
query, switching to packs search mode, and auto-selecting the matching
pack once results load.
## Changes
- **ManagerDialog.vue**: Added `initialPackId` prop; wires it into
`useRegistrySearch` (forces `packs` mode and pre-fills query) and uses
VueUse `until()` to auto-select the target pack and open the right panel
once `resultsWithKeys` is populated (one-shot, never re-triggers). Also
fixes a latent bug where the effective initial tab (resolving the
persisted tab) was not used when determining the initial search mode and
query — previously `initialTab` (the raw prop) was checked directly,
which would produce incorrect pre-fill when no tab prop was passed but a
Missing tab was persisted.
- **useManagerDialog.ts**: Threads `initialPackId` through `show()` into
the dialog props
- **useManagerState.ts**: Exposes `initialPackId` in `openManager`
options and passes it to `managerDialog.show()`; also removes a stale
fallback `show(ManagerTab.All)` call that was redundant for the
legacy-only error path
### Refactor: remove `executionIdUtil.ts` and distribute its functions
- **`getAncestorExecutionIds` / `getParentExecutionIds`** → moved to
`src/types/nodeIdentification.ts`: both are pure `NodeExecutionId`
string operations with no external dependencies, consistent with the
existing `parseNodeExecutionId` / `createNodeExecutionId` helpers
already in that file
- **`buildSubgraphExecutionPaths`** → moved to
`src/platform/workflow/validation/schemas/workflowSchema.ts`: operates
entirely on `ComfyNode[]` and `SubgraphDefinition` (both defined there),
and `isSubgraphDefinition` is already co-located in the same file
- Tests redistributed accordingly: ancestor/parent ID tests into
`nodeIdentification.test.ts`, `buildSubgraphExecutionPaths` tests into
`workflowSchema.test.ts`
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-9169-feat-enhance-manager-dialog-with-initial-pack-id-support-3116d73d365081f7b6a3cbfb2f2755bf)
by [Unito](https://www.unito.io)
## Summary
Extracts error-related state and logic from `executionStore` into a
dedicated `executionErrorStore` for better separation of concerns.
## Changes
- **New store**: `executionErrorStore` with all error state
(`lastNodeErrors`, `lastExecutionError`, `lastPromptError`), computed
properties (`hasAnyError`, `totalErrorCount`,
`activeGraphErrorNodeIds`), and UI state (`isErrorOverlayOpen`,
`showErrorOverlay`, `dismissErrorOverlay`)
- **Moved util**: `executionIdToNodeLocatorId` extracted to
`graphTraversalUtil`, reusing `traverseSubgraphPath` and accepting
`rootGraph` as parameter
- **Updated consumers**: 12 files updated to import from
`executionErrorStore`
- **Backward compat**: Deprecated getters retained in `ComfyApp` for
extension compatibility
## Review Focus
- Deprecated getters in `app.ts` — can be removed in a future
breaking-change PR once extension authors migrate
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-9060-refactor-Extract-executionErrorStore-from-executionStore-30e6d73d36508101973de835ab6b199f)
by [Unito](https://www.unito.io)
Read `category` from `definitions.subgraphs[0].category` in blueprint
JSON files as a fallback default for node categorization.
This allows blueprint authors to set the category directly in the
blueprint file without needing backend `index.json` support. The
precedence order is:
1. Explicit overrides (e.g. `info.category` from API, or `'Subgraph
Blueprints/User'` for user blueprints)
2. `definitions.subgraphs[0].category` from the blueprint JSON content
3. Bare `'Subgraph Blueprints'` fallback
Companion PR: Comfy-Org/ComfyUI#12552 (adds essential blueprints with
categories matching the Figma design)
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-9053-feat-read-category-from-blueprint-subgraph-definition-30e6d73d3650810ca23bfc5a1e97cb31)
by [Unito](https://www.unito.io)
---------
Co-authored-by: github-actions <github-actions@github.com>
## Summary
<img width="985" height="417" alt="스크린샷 2026-02-21 오전 1 05 10"
src="https://github.com/user-attachments/assets/4543a5aa-d1ae-4be7-971e-7b1454625829"
/>
- Add `installed_templates_version` and `required_templates_version` to
the system stats schema
- Display the template version as a badge on the About page alongside
ComfyUI and Frontend badges
- Display the template version as a row in the local System Info table
- Highlight badge (red severity) and table row (red text) when installed
version doesn't match required version, so users can quickly spot
outdated templates
Fixes#4006
## Test plan
- [x] Open Settings > About and verify "Templates v{version}" badge
appears
- [x] Verify "Templates Version" row appears in System Info table
- [ ] When `installed_templates_version` matches
`required_templates_version`: badge is default color, table row is
default color
- [ ] When versions don't match: badge turns red, table row turns red
- [ ] When `installed_templates_version` is absent (package not
installed): badge is hidden, table row shows empty
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
## Summary
- Extend `MissingNodeType` with `isReplaceable` and `replacement` fields
- Classify missing nodes by checking
`nodeReplacementStore.getReplacementFor()` during graph load
- Wrap hardcoded node patches (T2IAdapterLoader, ConditioningAverage,
etc.) in `if (!isEnabled)` guard so they only run when node replacement
setting is disabled
- Change `useNodeReplacementStore().load()` from fire-and-forget
(`void`) to `await` so replacement data is available before missing node
detection
- Fix guard condition order in `nodeReplacementStore.load()`: check
`isEnabled` before `isLoaded`
- Align `InputMap` types with actual API response (flat
`old_id`/`set_value` fields instead of nested `assign` wrapper)
## Test plan
- [x] Load workflow with deprecated nodes (T2IAdapterLoader,
Load3DAnimation, SDV_img2vid_Conditioning)
- [x] Verify missing nodes are classified with `isReplaceable: true` and
`replacement` object
- [x] Verify hardcoded patches only run when node replacement setting is
OFF
- [x] Verify `nodeReplacementStore.load()` is not called when setting is
disabled
- [x] Unit tests pass (`nodeReplacementStore.test.ts` - 16 tests)
- [x] Typecheck passes
🤖 Generated with [Claude Code](https://claude.ai/code)
## 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
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>
## Summary
This PR removes `any` types from core source files and replaces them
with proper TypeScript types.
### Key Changes
#### Type Safety Improvements
- Replaced `any` with `unknown`, explicit types, or proper interfaces
across core files
- Introduced new type definitions: `SceneConfig`, `Load3DNode`,
`ElectronWindow`
- Used `Object.assign` instead of `as any` for dynamic property
assignment
- Replaced `as any` casts with proper type assertions
### Files Changed
Source files:
- src/extensions/core/widgetInputs.ts - Removed unnecessary `as any`
cast
- src/platform/cloud/onboarding/auth.ts - Used `Record<string, unknown>`
and Sentry types
- src/platform/telemetry/providers/cloud/MixpanelTelemetryProvider.ts -
Used `AuditLog[]` type
- src/platform/workflow/management/stores/workflowStore.ts - Used
`typeof ComfyWorkflow` constructor type
- src/scripts/app.ts - Used `ResultItem[]` for Clipspace images
- src/services/colorPaletteService.ts - Used `Object.assign` instead of
`as any`
- src/services/customerEventsService.ts - Used `unknown` instead of
`any`
- src/services/load3dService.ts - Added proper interface types for
Load3D nodes
- src/types/litegraph-augmentation.d.ts - Used `TWidgetValue[]` type
- src/utils/envUtil.ts - Added ElectronWindow interface
- src/workbench/extensions/manager/stores/comfyManagerStore.ts - Typed
event as `CustomEvent<{ ui_id?: string }>`
### Testing
- All TypeScript type checking passes (`pnpm typecheck`)
- Linting passes without errors (`pnpm lint`)
- Code formatting applied (`pnpm format`)
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 (this PR)
## Summary
Migrate keybindings domain to `src/platform/keybindings/` following DDD
principles.
## Changes
- **What**: Consolidate keybinding-related code (types, store, service,
defaults, reserved keys) into a single domain module with flat structure
- Extracted `KeyComboImpl` and `KeybindingImpl` classes into separate
files
- Updated all consumers to import from new location
- Colocated tests with source files
- Updated stores/README.md and services/README.md to remove migrated
entries
## Review Focus
- Verify all import paths were updated correctly
- Check that the flat structure is appropriate (vs nested core/data/ui
layers)
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8369-refactor-migrate-keybindings-to-DDD-structure-2f66d73d36508120b169dc737075fb45)
by [Unito](https://www.unito.io)
---------
Co-authored-by: Subagent 5 <subagent@example.com>
Co-authored-by: Amp <amp@ampcode.com>
## 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)
## Summary
Improves type safety in test files by replacing unsafe type patterns
with proper TypeScript idioms.
## Changes
- Define typed `TestWindow` interface extending `Window` for Playwright
tests with custom properties
- Use `Partial<HTMLElement>` with single type assertion for DOM element
mocks
- Remove redundant type imports
- Fix `console.log` → `console.warn` in test fixture
## Files Changed
16 test files across browser_tests, packages, and src/components
## Test Plan
- ✅ `pnpm typecheck` passes
- ✅ No new `any` types introduced
- ✅ All pre-commit hooks pass
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8253-refactor-improve-TypeScript-patterns-in-test-files-Group-1-8-2f16d73d365081548f9ece7bcf0525ee)
by [Unito](https://www.unito.io)
---------
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
## Summary
Adds optional badge support to the `NavItem` component and `NavItemData`
interface, enabling navigation items in the left sidebar of the Asset
Browser Modal to display counts or status indicators.
## Changes
- **`src/types/navTypes.ts`**: Added optional `badge?: string | number`
property to `NavItemData` interface
- **`src/components/widget/nav/NavItem.vue`**: Added `StatusBadge`
rendering when `badge` prop is provided
- **`src/components/widget/panel/LeftSidePanel.vue`**: Wired `badge`
prop from `NavItemData` to `NavItem` for both grouped and ungrouped
items
## Usage
```ts
const navItem: NavItemData = {
id: 'assets',
label: 'Assets',
icon: 'pi pi-folder',
badge: 5 // Optional - displays count badge
}
```
## Related
- Builds on #8170 which added queue badge functionality
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8207-feat-add-badge-support-to-NavItem-component-2ef6d73d365081669f86fe2fc618e87f)
by [Unito](https://www.unito.io)
## Summary
Migrates all unit tests from `tests-ui/` to colocate with their source
files in `src/`, improving discoverability and maintainability.
## Changes
- **What**: Relocated all unit tests to be adjacent to the code they
test, following the `<source>.test.ts` naming convention
- **Config**: Updated `vitest.config.ts` to remove `tests-ui` include
pattern and `@tests-ui` alias
- **Docs**: Moved testing documentation to `docs/testing/` with updated
paths and patterns
## Review Focus
- Migration patterns documented in
`temp/plans/migrate-tests-ui-to-src.md`
- Tests use `@/` path aliases instead of relative imports
- Shared fixtures placed in `__fixtures__/` directories
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-7811-chore-migrate-tests-from-tests-ui-to-colocate-with-source-files-2da6d73d36508147a4cce85365dee614)
by [Unito](https://www.unito.io)
---------
Co-authored-by: Amp <amp@ampcode.com>
Co-authored-by: GitHub Action <action@github.com>
With reactivity fixed, control widgets would apply twice. This is fixed
by using the litegraph implementation.
Also adds control widget support for combos
Followup to #7539.
Known Issue:
- Primitive node do not have litegraph callbacks properly setup. As a
result, they will display an updated value when modified by control
widgets. Fixing this will requires a larger, separate PR
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-7550-Fix-doubled-control-application-2cb6d73d365081739a2fc40fdfb3630e)
by [Unito](https://www.unito.io)
Prior to the release of subgraphs, there was a single graph accessed
through `app.graph`. Now that there's multiple graphs, there's a lot of
code that needs to be reviewed and potentially updated depending on if
it cares about nearby nodes, all nodes, or something else requiring
specific attention.
This was done by simply changing the type of `app.graph` to unknown so
the typechecker will complain about every place it's currently used.
References were then updated to `app.rootGraph` if the previous usage
was correct, or actually rewritten.
By not getting rid of `app.graph`, this change already ensures that
there's no loss of functionality for custom nodes, but the prior typing
of `app.graph` can always be restored if future dissuasion of
`app.graph` usage creates issues.
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-7399-Cleanup-app-graph-usage-2c76d73d365081178743dfdcf07f44d0)
by [Unito](https://www.unito.io)
## Summary
- Add right-click context menu functionality to MediaAssetCard
- Separate context menu into its own component
(MediaAssetContextMenu.vue)
- Ensure only one context menu is visible at a time within the same tab
## Changes
- Add `MediaAssetContextMenu.vue` - new component for context menu
- Update `MediaAssetCard.vue` - show context menu on right-click and
more button click
- Delete `MediaAssetMoreMenu.vue` - consolidated into context menu
- Delete `MediaAssetButtonDivider.vue` - unused
- Update `AssetsSidebarTab.vue` - add context menu state management
- Refactor `useMediaAssetActions.ts`
## Screenshot
[screen-capture.webm](https://github.com/user-attachments/assets/6fe414ef-b134-4fbe-98aa-6437bb354b41)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Co-authored-by: Claude <noreply@anthropic.com>
Adds a workflow progress panel component underneath the
`actionbar-container`.
I suggest starting a review at the extraneous changes that were needed.
Including but not limited to:
- `get createTime()` in queueStore
- `promptIdToWorkflowId`, `initializingPromptIds`, and
`nodeProgressStatesByPrompt` in executionStore
- `create_time` handling in v2ToV1Adapter
- `pointer-events-auto` on ComfyActionbar.vue
The rest of the changes should be contained under
`QueueProgressOverlay.vue`, and has less of a blast radius in case
something goes wrong.
---------
Co-authored-by: pythongosssss <125205205+pythongosssss@users.noreply.github.com>
Co-authored-by: GitHub Action <action@github.com>
Co-authored-by: github-actions <github-actions@github.com>
Co-authored-by: Jin Yi <jin12cc@gmail.com>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Alexander Brown <drjkl@comfy.org>
Co-authored-by: Johnpaul Chiwetelu <49923152+Myestery@users.noreply.github.com>
Co-authored-by: Christian Byrne <cbyrne@comfy.org>
Co-authored-by: Comfy Org PR Bot <snomiao+comfy-pr@gmail.com>
Co-authored-by: christian-byrne <72887196+christian-byrne@users.noreply.github.com>
## Summary
Adds an upload button to the asset browser modal, controlled by the
`model_upload_button_enabled` backend feature flag.
## Changes
- **What**: Added upload button with PrimeVue primary styling to asset
browser header
- **Feature Flag**: Button only appears when backend returns
`model_upload_button_enabled: true`
- **Localization**: Added `assetBrowser.uploadModel` translation key
- **Click Handler**: Currently logs to console (implementation pending)
## Review Focus
- Feature flag integration using `useFeatureFlags` composable
- Button styling matches PrimeVue primary color scheme
- Proper placement in header with flexbox layout
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6665-feat-Add-feature-flagged-upload-button-to-asset-browser-2a96d73d365081c7a05bdc33bed7f7fd)
by [Unito](https://www.unito.io)
---------
Co-authored-by: Claude <noreply@anthropic.com>
This pull request introduces a new extensible system for adding custom
action bar buttons to the top menu, and demonstrates its use by adding a
cloud feedback button. The changes include defining a new
`ActionBarButton` type, updating the extension system to support action
bar buttons, creating a Pinia store to aggregate buttons from
extensions, and updating the UI to render these buttons in the top menu.
The cloud feedback button is now conditionally loaded for cloud
environments.
**Extensible Action Bar Button System**
* Defined a new `ActionBarButton` interface in `comfy.ts` for describing
buttons (icon, label, tooltip, class, click handler) and added an
`actionBarButtons` property to the `ComfyExtension` interface to allow
extensions to contribute buttons.
[[1]](diffhunk://#diff-c29886a1b0c982c6fff3545af0ca
<img width="1134" height="437" alt="Screenshot 2025-11-07 at 01 07 36"
src="https://github.com/user-attachments/assets/36b6145a-0b82-4f7d-88e8-f2ea350a359b"
/>
8ec269876c2cf3948f867d08c14032c04d66R60-R82)
[[2]](diffhunk://#diff-c29886a1b0c982c6fff3545af0ca8ec269876c2cf3948f867d08c14032c04d66R128-R131)
* Created a Pinia store (`actionBarButtonStore.ts`) that collects all
action bar buttons from registered extensions for use in the UI.
**UI Integration**
* Added a new `ActionBarButtons.vue` component that renders all action
bar buttons using the store, and integrated it into the top menu section
(`TopMenuSection.vue`).
[[1]](diffhunk://#diff-d6820f57cde865083d515ac0c23e1ad09e6fbc6792d742ae948a1d3b772be473R1-R28)
[[2]](diffhunk://#diff-45dffe390927ed2d5ba2426a139c52adae28ce15f81821c88e7991944562074cR10)
[[3]](diffhunk://#diff-45dffe390927ed2d5ba2426a139c52adae28ce15f81821c88e7991944562074cR28)
**Cloud Feedback Button Extension**
* Implemented a new extension (`cloudFeedbackTopbarButton.ts`) that
registers a "Feedback" button opening the Zendesk feedback page, and
ensured it loads only in cloud environments.
[[1]](diffhunk://#diff-b84a6a0dfaca19fd77b3fae6999a40c3ab8d04ed22636fcdecc65b385a8e9a98R1-R24)
[[2]](diffhunk://#diff-236993d9e4213efe96d267c75c3292d32b93aa4dd6c3318d26a397e0ae56bc87R32)
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6626-Cloud-feedback-Extension-2a46d73d36508170bd07f582ccfabb3c)
by [Unito](https://www.unito.io)
## Summary
Deduplicates workflow run telemetry and keeps a single source of truth
for execution while retaining click analytics and attributing initiator
source.
- Keep execution tracking in one place via `trackWorkflowExecution()`
- Keep click analytics via `trackRunButton(...)`
- Attribute initiator with `trigger_source` = 'button' | 'keybinding' |
'legacy_ui'
- Remove pre‑tracking from keybindings to avoid double/triple counting
- Update legacy UI buttons to emit both click + execution events (they
bypass commands)
## Problem
PR #6499 added tracking at multiple layers:
1) Keybindings tracked via a dedicated method and then executed a
command
2) Menu items tracked via a dedicated method and then executed a command
3) Commands also tracked execution
Because these ultimately trigger the same command path, this produced
duplicate (sometimes triplicate) events per user action and made it hard
to attribute initiator precisely.
## Solution
- Remove redundant tracking from keybindings (and previous legacy menu
handler)
- Commands now emit both:
- `trackRunButton(...)` (click analytics, includes `trigger_source` when
provided)
- `trackWorkflowExecution()` (single execution start; includes the last
`trigger_source`)
- Legacy UI buttons (which call `app.queuePrompt(...)` directly) now
also emit both events with `trigger_source = 'legacy_ui'`
- Add `ExecutionTriggerSource` type and wire `trigger_source` through
provider so `EXECUTION_START` matches the most recent click intent
### Telemetry behavior after this change
- `RUN_BUTTON_CLICKED` (click analytics)
- Emitted when a run is initiated via:
- Button: `trigger_source = 'button'`
- Keybinding: `trigger_source = 'keybinding'`
- Legacy UI: `trigger_source = 'legacy_ui'`
- `EXECUTION_START` (execution lifecycle)
- Emitted once per run at start; includes `trigger_source` matched to
the click intent above
- Paired with `EXECUTION_SUCCESS` / `EXECUTION_ERROR` from execution
handlers
## Benefits
- ✅ Accurate counts by removing duplicated run events
- ✅ Clear initiator attribution (button vs keybinding vs legacy UI)
- ✅ Separation of “intent” (click) vs. “lifecycle” (execution)
- ✅ Simpler implementation and maintenance
## Files Changed (high level)
- `src/services/keybindingService.ts`: Route run commands with
`trigger_source = 'keybinding'`
- `src/components/actionbar/ComfyRunButton/ComfyQueueButton.vue`: Send
`trigger_source = 'button'` to commands
- `src/scripts/ui.ts`: Legacy queue buttons emit `trackRunButton({
trigger_source: 'legacy_ui' })` and `trackWorkflowExecution()`
- `src/composables/useCoreCommands.ts`: Commands emit
`trackRunButton(...)` + `trackWorkflowExecution()`; accept telemetry
metadata
- `src/platform/telemetry/types.ts`: Add `ExecutionTriggerSource` and
optional `trigger_source` in click + execution payloads
- `src/platform/telemetry/providers/cloud/MixpanelTelemetryProvider.ts`:
Carry `trigger_source` from click → execution and reset after use
- `src/stores/commandStore.ts`: Allow commands to receive args (for
telemetry metadata)
- `src/extensions/core/groupNode.ts`: Adjust command function signatures
to new execute signature
## Related
- Reverts the multi‑event approach from #6499
- Keeps `trackWorkflowExecution()` as the canonical execution event
while preserving click analytics and initiator attribution with
`trackRunButton(...)`
┆Issue is synchronized with this Notion page by Unito
---------
Co-authored-by: Christian Byrne <c.byrne@comfy.org>
Co-authored-by: Alexander Brown <drjkl@comfy.org>
Co-authored-by: Benjamin Lu <benjaminlu1107@gmail.com>
Co-authored-by: Claude <noreply@anthropic.com>
## Summary
- Reorganized media asset card layout for improved UX
- Moved duration/format chips to top-left (visible in default state)
- Replaced multiple action buttons with zoom + more menu pattern
- Repositioned output count to top-right for better visibility
## Changes
1. **Duration & format chips**: Moved from bottom-left to top-left,
shown when card is not hovered and media is not playing
2. **Media actions**: Simplified to zoom button + more menu (contains
delete, download options), shown on hover or during playback
3. **Output count**: Relocated from bottom-right to top-right for
consistent positioning
## Test plan
- [x] Verify duration/format chips appear in top-left when card is idle
- [x] Confirm action buttons (zoom + more) appear on hover
- [x] Check output count displays correctly in top-right
- [x] Test transitions between hover/non-hover states
- [x] Verify media playback doesn't interfere with UI elements
🤖 Generated with [Claude Code](https://claude.ai/code)
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6541-UI-Redesign-media-asset-card-layout-29f6d73d365081eb8614d5dc9b2dc214)
by [Unito](https://www.unito.io)
This pull request refactors how export menu options are added to
3D-related nodes, updating the API to use a new `getNodeMenuItems` hook
and simplifying the menu item creation logic. The changes improve
consistency and maintainability across extensions handling 3D nodes, and
clarify the expected return types for menu item hooks.
**Refactoring and API updates for node menu items:**
* Replaced usage of the legacy `createExportMenuOptions` function with
the new `createExportMenuItems` function in `load3d.ts`, `saveMesh.ts`,
and related imports, aligning all 3D node extensions to the new API.
[[1]](diffhunk://#diff-a5c612d9322ca4cbbeda097d13e2fa1ef017d4c3076d23fc228afee5a79a56e3L6-R12)
[[2]](diffhunk://#diff-7dede72060130d77ce5191fc86d115bd9b93311cb0438400730d8e20b2aa8e43L4-R7)
* Introduced and implemented the `getNodeMenuItems` hook in the
extension registration for `Load3D`, `Preview3D`, and `SaveGLB` nodes,
ensuring export menu items are only shown for the appropriate node
types.
[[1]](diffhunk://#diff-a5c612d9322ca4cbbeda097d13e2fa1ef017d4c3076d23fc228afee5a79a56e3R293-R302)
[[2]](diffhunk://#diff-a5c612d9322ca4cbbeda097d13e2fa1ef017d4c3076d23fc228afee5a79a56e3R521-R530)
[[3]](diffhunk://#diff-7dede72060130d77ce5191fc86d115bd9b93311cb0438400730d8e20b2aa8e43R48-R57)
* Removed assignment of `getExtraMenuOptions` to nodes, fully migrating
to the new menu item hook approach for context menus.
[[1]](diffhunk://#diff-a5c612d9322ca4cbbeda097d13e2fa1ef017d4c3076d23fc228afee5a79a56e3L301-L302)
[[2]](diffhunk://#diff-a5c612d9322ca4cbbeda097d13e2fa1ef017d4c3076d23fc228afee5a79a56e3L548-L549)
[[3]](diffhunk://#diff-7dede72060130d77ce5191fc86d115bd9b93311cb0438400730d8e20b2aa8e43L64-L67)
**Menu item creation logic simplification:**
* Refactored `createExportMenuOptions` to `createExportMenuItems` in
`exportMenuHelper.ts`, changing it to directly return an array of menu
items (with separators) instead of a function, simplifying its usage and
reducing boilerplate.
[[1]](diffhunk://#diff-42404da1a87a52d304371a13d5f021bdad837765b94d86d186abb2d99a8cb707L14-R22)
[[2]](diffhunk://#diff-42404da1a87a52d304371a13d5f021bdad837765b94d86d186abb2d99a8cb707L59-R58)
**Type and documentation improvements:**
* Updated the `ComfyExtension` interface in `comfy.ts` to clarify that
menu item hooks (`getCanvasMenuItems`, `getNodeMenuItems`) now return
arrays that may include `null` values as separators, improving type
safety and documentation.
See before and after below
<img width="1459" height="897" alt="Screenshot 2025-10-30 at 07 08 04"
src="https://github.com/user-attachments/assets/ec4464c9-f733-4b4c-87c4-bb5060ccaa68"
/>
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6454-Complete-context-menu-migration-for-Load3D-Preview3D-and-SaveGLB-29c6d73d3650819995b4c4dc1582cd86)
by [Unito](https://www.unito.io)
## Summary
Implements server-side remote configuration to decouple runtime behavior
from build artifacts, enabling dynamic configuration updates without
redeployment.
## Technical Changes
- **Replaced** build-time constants (`__MIXPANEL_TOKEN__`,
`__BUILD_FLAGS__`) with runtime configuration loaded from
`/api/features`
- Configuration now sourced from `window.__CONFIG__` (hydrated from
`/api/features` endpoint)
- **Added** `src/platform/remoteConfig/` service that polls server
configuration every 30 seconds
- **Modified** application bootstrap sequence in `main.ts` to load
remote config before module initialization (required for cloud builds)
- **Removed** global constants: `__BUILD_FLAGS__`, `__MIXPANEL_TOKEN__`.
Runtime subscription enforcement toggle via `subscription_required` flag
- Server health alerts with variant-based severity rendering
(info/warning/error) via topbar badges
## Rationale
- **Build-once-deploy-anywhere**: Single immutable artifact promoted
through environments (staging → production)
- **Zero-downtime configuration**: Update behavior without rebuilding or
redeploying the application
- **Incident response**: Disable features or display alerts dynamically
in response to outages or degraded service
- **Instant rollback**: Revert configuration changes server-side without
artifact redeployment
- **Progressive delivery**: Enable A/B testing, canary releases, and
user/region-based configuration
- **Environment parity**: Eliminate configuration drift between staging
and production builds
- Decouples deployment cadence from configuration changes
- Enables GitOps workflows for configuration management separate from
code deployments
- Supports real-time operational control of client behavior
- Reduces build matrix complexity (no per-environment builds)
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6246-change-cloud-feature-flags-to-be-loaded-dynamically-at-runtime-rather-than-set-in-build-2966d73d3650811cbb41c9093961037a)
by [Unito](https://www.unito.io)