Patch version increment to 1.39.6
**Base branch:** `main`
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Chores**
* Version bumped to 1.39.6.
* **New Features**
* Added multilingual UI strings for secrets management, asset import
errors, app-mode prompts, and HitPaw tools.
* New node types for image/video/audio workflows and expanded export
formats (GLB/FBX/OBJ) for 3D/model outputs.
* **Bug Fixes / Removals**
* Removed the "Open 3D Viewer (Beta) for Selected Node" menu entry and
related 3D viewer settings.
* Added setting to enable automatic node replacement when mappings
exist.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8595-1-39-6-2fd6d73d3650818cba9cffca313909e8)
by [Unito](https://www.unito.io)
---------
Co-authored-by: christian-byrne <72887196+christian-byrne@users.noreply.github.com>
Co-authored-by: github-actions <github-actions@github.com>
## Summary
Fix `SubgraphNode.graph` property to match `LGraphNode` lifecycle
contract. Previously declared as `override readonly graph` via
constructor parameter promotion, which prevented `LGraph.remove()` from
setting `node.graph = null`.
## Changes
- Remove `readonly` from `SubgraphNode.graph` constructor parameter
- Add `override graph: GraphOrSubgraph | null` as class property
- Add `NullGraphError` guard in `rootGraph` getter with node ID for
debugging
- Add null guards in `ExecutableNodeDTO.resolveInput` and
`imagePreviewStore.revokeSubgraphPreviews`
- Add test verifying `rootGraph` throws after node removal
## Testing
- Existing subgraph tests pass
- New test confirms `NullGraphError` is thrown when accessing
`rootGraph` on removed node
---------
Co-authored-by: Amp <amp@ampcode.com>
## Summary
Add default keyboard shortcuts for mode toggle and assets panel access.
## Changes
- **Ctrl+Shift+A**: Toggle between Simple Mode and Graph Mode
(`Comfy.ToggleLinear`)
- **A**: Open/toggle assets panel (`Workspace.ToggleSidebarTab.assets`)
- Show keybinding in ModeToggle button tooltip (e.g. "Simple Mode
(Ctrl+Shift+A)")
## Keybinding Rationale
- `A` follows the existing pattern: `w` (workflows), `n` (node-lib), `m`
(model-lib)
- `Ctrl+Shift+A` chosen because:
- Ctrl+Shift is the standard modifier pattern for toggle commands
- "A" mnemonic for "App mode"
- Does not conflict with existing keybindings
## Testing
- Verified typecheck passes
- Verified lint passes
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* Tooltips now show dynamic keyboard shortcut suffixes for mode and tab
controls.
* Added keyboard shortcut: A — toggles the assets sidebar.
* Added keyboard shortcut: Ctrl+Shift+A — toggles linear mode.
* **Localization**
* Added a localized shortcut suffix template so displayed shortcuts
respect translations.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8593-feat-add-default-keybindings-for-toggle-mode-and-assets-panel-2fc6d73d36508172bd6ed3378f43de55)
by [Unito](https://www.unito.io)
---------
Co-authored-by: GitHub Action <action@github.com>
## 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>
## 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)
## Summary
Eliminates code duplication by replacing local `cancellableStates`
arrays with the centralized `isActiveJobState` utility function.
## Changes
- Remove duplicated `cancellableStates: JobState[] = ['pending',
'initialization', 'running']` from:
- `src/composables/queue/useJobActions.ts`
- `src/storybook/mocks/useJobActions.ts`
- Import and use `isActiveJobState` from `src/utils/queueUtil.ts`
instead
## Testing
- Typecheck passes
- Lint passes
- Related queue tests pass
Fixes#7947
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8572-refactor-use-isActiveJobState-instead-of-duplicated-cancellableStates-2fc6d73d365081f89decfc869fa952a0)
by [Unito](https://www.unito.io)
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Refactor**
* Internal code improvements to simplify job state management logic.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
## 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>
## Summary
Audits and updates litegraph documentation files that came from the old
litegraph.js repo merged via git subtree (per #8341 discussion).
## Changes
- **Delete** `CONTRIBUTING.md` - completely outdated, referenced
original jagenjo/litegraph.js from 2020
- **Fix** `API.md` - remove Subgraph from "Removed public interfaces"
(actively used in 25+ files)
- **Update** `README.md` - replace standalone releasing instructions
with note about embedded subtree workflow
- **Fix** `AGENTS.md` and `__fixtures__/README.md` - correct import path
typos (`./fixtures/` → `./__fixtures__/`)
Fixes#8347
cc @DrJKL
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
## Documentation
* Updated release information to clarify that the library is embedded
via git subtree and managed through the parent repository's release
process
* Removed contribution guidelines documentation file
* Updated API documentation by removing Subgraph from the list of
removed public interfaces
* Updated documentation examples to align with current project structure
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8588-docs-audit-and-update-litegraph-documentation-2fc6d73d365081fca335c78ce8493d3e)
by [Unito](https://www.unito.io)
Enable CodeRabbit to automatically review draft pull requests.
## Changes
- Added `reviews.auto_review.drafts: true` to `.coderabbit.yaml`
This allows getting early feedback from CodeRabbit while PRs are still
in draft status.
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* Enabled automatic review drafting to streamline the review process.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8587-feat-enable-CodeRabbit-reviews-on-draft-PRs-2fc6d73d365081ea8f1bcae4a2a8277b)
by [Unito](https://www.unito.io)
## Summary
Adds an educational hint to the favorites empty state to teach users how
to favorite inputs.
## Changes
- Add new i18n key `favoritesNoneHint` with text "In the Parameters tab,
click ⋮ on any input to add it here"
- Update empty state template to show hint below the existing
description
- Hint is hidden during search to avoid redundant messaging
## Before/After
**Before:** "Inputs you favorite will show up here"
**After:**
- "Inputs you favorite will show up here"
- "In the Parameters tab, click ⋮ on any input to add it here*
## Context
Per Slack discussion, uses in-app education only (no external docs link)
to ensure text matches the user's specific version of the feature.
Fixes #2fa6d73d
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8559-feat-Add-educational-hint-to-favorites-empty-state-2fc6d73d365081f5827dfafcc1ece95c)
by [Unito](https://www.unito.io)
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* Improved empty state in the Parameters panel: message is now two
paragraphs and shows an additional hint (with an icon) when not
searching.
* **Localization**
* Added a new localized hint that explains how to add inputs to
favorites, shown in the Parameters panel empty state.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
---------
Co-authored-by: Amp <amp@ampcode.com>
Co-authored-by: Rizumu Ayaka <rizumu@ayaka.moe>
## Summary
Updates all user-facing instances of 'Simple Mode' / 'Linear Mode' to
'App Mode' as discussed in PR #8562.
## Changes
- Updated command label in `useCoreCommands.ts`
- Updated `src/locales/en/commands.json`
- Updated all 12 locale `main.json` files with:
- `linearMode.linearMode` → "App Mode" (translated)
- `linearMode.beta` → "App Mode in Beta - Feedback" (translated)
- `Toggle Simple Mode` command → `Toggle App Mode` (translated)
## Languages Updated
en, zh, zh-TW, ja, ko, fr, es, pt-BR, ru, tr, ar, fa
## Notes
Internal variable names (`linearMode`, `canvasStore.linearMode`) remain
unchanged to avoid breaking changes.
Fixes#8577
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Style**
* Renamed application terminology from "Simple Mode" to "App Mode" in
user interface labels, mode descriptions, and mode toggle command.
Updates applied consistently across all 12 supported language
translations to ensure consistent terminology throughout the
application.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8590-feat-update-user-facing-Simple-Mode-terminology-to-App-Mode-2fc6d73d36508160a19af70b228b0a7e)
by [Unito](https://www.unito.io)
## Summary
Replaces `Record<string, unknown>` catch-all with properly typed generic
props in `showLayoutDialog`, improving type safety.
## Changes
- Made `showLayoutDialog` generic over component type `C extends
Component`
- Changed props type from `{ onClose: () => void } & Record<string,
unknown>` to `ComponentAttrs<C> & { onClose: () => void }`
This matches existing patterns used elsewhere in `dialogService.ts`
(e.g., `showLoadWorkflowWarning`, `showMissingModelsWarning`).
Fixes#8102
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Refactor**
* Dialog handling updated to infer props per component, resulting in
more consistent and reliable dialog behavior and fewer runtime prop
mismatches when opening dialogs. Developers will have clearer, safer
dialog configuration, while end users should experience steadier dialog
interactions. No visible UI changes are expected.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8571-fix-replace-type-catch-all-with-generic-ComponentAttrs-in-showLayoutDialog-2fc6d73d36508104bf60d27c9b94af69)
by [Unito](https://www.unito.io)
## Summary
Major refactoring of browser tests to improve reliability,
maintainability, and type safety.
## Changes
### Test Infrastructure Decomposition
- Decomposed `ComfyPage.ts` (~1000 lines) into focused helpers:
- `CanvasHelper`, `DebugHelper`, `SubgraphHelper`,
`NodeOperationsHelper`
- `SettingsHelper`, `WorkflowHelper`, `ClipboardHelper`,
`KeyboardHelper`
- Created `ContextMenu` page object, `BaseDialog` base class, and
`BottomPanel` page object
- Extracted `DefaultGraphPositions` constants
### Locator Stability
- Added `data-testid` attributes to Vue components (sidebar, dialogs,
node library)
- Created centralized `selectors.ts` with test ID constants
- Replaced fragile CSS selectors (`.nth()`, `:nth-child()`) with
`getByTestId`/`getByRole`
### Performance & Reliability
- Removed `setTimeout` anti-patterns (replaced with `waitForFunction`)
- Replaced `waitForTimeout` with retrying assertions
- Replaced hardcoded coordinates with computed `NodeReference` positions
- Enforced LF line endings for all text files
### Type Safety
- Enabled `no-explicit-any` lint rule for browser_tests via oxlint
- Purged `as any` casts from browser_tests
- Added Window type augmentation for standardized window access
- Added proper type annotations throughout
### Bug Fixes
- Restored `ExtensionManager` API contract
- Removed test-only settings from production schema
- Fixed flaky selectors and missing test setup
## Testing
- All browser tests pass
- Typecheck passes
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Tests**
* Overhauled browser E2E test infrastructure with many new
helpers/fixtures, updated test APIs, and CI test container image bumped
for consistency.
* **Chores**
* Standardized line endings and applied stricter lint rules for browser
tests; workspace dependency version updated.
* **Documentation**
* Updated Playwright and TypeScript testing guidance and test-run
commands.
* **UI**
* Added stable data-testids to multiple components to improve
testability.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
---------
Co-authored-by: Amp <amp@ampcode.com>
Co-authored-by: GitHub Action <action@github.com>
Co-authored-by: github-actions <github-actions@github.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
## Summary
Scale down node previews in the manager info panel to 75% for better
visual fit.
## Changes
- Add 75% scale to node preview items in NodesTabPanel
- Add gap between scaled items for better spacing
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Style**
* Enhanced the visual layout of node items in the manager panel with
improved spacing and individual item scaling for better visual
presentation.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8579-style-scale-down-node-previews-in-manager-info-panel-2fc6d73d36508176a307d980cd3b9329)
by [Unito](https://www.unito.io)
## Summary
Add ratio preset dropdown (1:1, 3:4, 4:3, 16:9, 9:16, custom) and lock
toggle to the ImageCrop widget. When locked, only corner handles are
shown and resizing maintains the selected aspect ratio using diagonal
projection for smooth, jump-free interaction.
Locking with custom selected captures the current crop's ratio.
## Screenshots (if applicable)
https://github.com/user-attachments/assets/a7b5c0a0-c18c-4785-940f-59793702e892
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8533-feat-add-aspect-ratio-lock-for-ImageCrop-widget-2fa6d73d365081a98546e43ed0d7d4fe)
by [Unito](https://www.unito.io)
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* Image crop widget adds a ratio selector with preset and custom
options, plus a lock/unlock toggle to preserve aspect while cropping.
* Constrained corner-resize behavior enforces locked aspect ratios
during resizing.
* **Documentation**
* Added UI text entries for ratio controls (labels for ratio,
lock/unlock, custom).
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
## Summary
Moves static data outside the `useJobActions` composable to
module-level.
## Changes
- **What**: Extracted `cancellableStates` array to a module-level
`CANCELLABLE_STATES` constant. The `cancelAction` object remains inside
the composable because it depends on `t()` for i18n reactivity.
## Review Focus
This is a pure refactor with no behavior change - the constant is now
allocated once per module instead of per-composable-call.
Fixes#7946
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8570-refactor-move-cancellable-states-to-module-level-constant-2fc6d73d3650814fa1fefa172f7ace2f)
by [Unito](https://www.unito.io)
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Chores**
* Internal code refactoring to improve maintainability of job
cancellation logic.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
## 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 -->
## Summary
Add infrastructure for automatic node replacement feature that allows
missing/deprecated nodes to be replaced with their newer equivalents.
## Changes
- **Types**: `NodeReplacement`, `NodeReplacementResponse` types matching
backend API spec (PR #12014)
- **Service**: `fetchNodeReplacements()` API wrapper
- **Store**: `useNodeReplacementStore` with `getReplacementFor()`,
`hasReplacement()`, `isEnabled()`
- **Setting**: `Comfy.NodeReplacement.Enabled` toggle (experimental)
- **Tests**: 11 unit tests covering store functionality
## Related
- Backend PR: Comfy-Org/ComfyUI#12014
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8364-feat-Add-node-replacement-store-and-types-2f66d73d3650816bb771c9cc6a8e1774)
by [Unito](https://www.unito.io)
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* Automatic node replacement is now available, allowing missing nodes to
be replaced with their newer equivalents when replacement mappings
exist.
* Added "Enable automatic node replacement" experimental setting in
Workflow preferences (enabled by default).
* Replacement data is loaded during app initialization.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
Enables the linear mode toggle for all nightly build users by
short-circuiting the `linearToggleEnabled` feature flag.
## Changes
- Adds `isNightly` check in `linearToggleEnabled` getter
- Returns `true` for nightly builds, bypassing remote config/server
feature checks
- Adds unit tests for the new behavior
## Reviewers
- @AustinMroz (linear mode maintainer)
- @christian-byrne (isNightly author)
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8569-feat-enable-linear-mode-toggle-for-nightly-builds-2fc6d73d3650819681f8dcdc23b6eefe)
by [Unito](https://www.unito.io)
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* Enabled linear toggle feature for nightly distribution builds.
* Feature flag system now respects nightly vs. standard build
configurations.
* **Tests**
* Added test coverage for nightly build feature flag behavior and remote
configuration handling.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
## Summary
Adds the legacy `nodeTemplates` extension to the `DEPRECATED_FILES` list
in the build plugin, causing a console warning when external extensions
import from this file.
## Changes
Adds `'extensions/core/nodeTemplates'` to `DEPRECATED_FILES` in
`build/plugins/comfyAPIPlugin.ts`.
## Effect
When an external extension imports from
`extensions/core/nodeTemplates.js`, they will see:
```
[ComfyUI Deprecated] Importing from "extensions/core/nodeTemplates.js" is deprecated and will be removed in v1.34.
```
## Related
- Follows pattern from PR #6090 (feat: deprecated API alert)
- Related issue: #4056 (Migrate Node Templates to Workflows)
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8463-chore-add-deprecation-warning-for-legacy-node-templates-extension-2f86d73d3650811e9f53fa3e5a572332)
by [Unito](https://www.unito.io)
Co-authored-by: Amp <amp@ampcode.com>
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>
## Summary
Active jobs (pending/running) in the media assets panel now display in
FIFO order - oldest jobs first (first to be processed at top). This
matches the queue processing order and the old queue panel behavior.
## Changes
- **AssetsSidebarListView.vue**: Add `.toReversed()` to `activeJobItems`
computed to reverse job order for display
- **AssetsSidebarGridView.vue**: Same change for grid view consistency
- **AssetsSidebarListView.test.ts**: Unit test verifying oldest-first
ordering
## Root Cause
PR #8225 changed sorting from `queueIndex` to `createTime` descending in
`useJobList.ts`, which placed newest jobs first. For active jobs, users
expect oldest first (FIFO - first to be processed appears at top).
## Solution
Rather than modifying the shared `useJobList` composable (which serves
both the assets panel and queue overlay), the fix applies
`.toReversed()` at the view layer for the active jobs section only.
This:
- Preserves the newest-first order for completed/history jobs
- Displays active jobs in oldest-first order
- Maintains backward compatibility with the queue overlay
## Testing
- Unit test added to verify FIFO ordering of active jobs
- All existing `useJobList` tests pass
Fixes COM-14151
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Bug Fixes**
* Corrected the display order of active jobs in the sidebar to show jobs
in oldest-first (FIFO) sequence.
* **Tests**
* Added unit tests for the assets sidebar list view to verify job
ordering and filtering behavior.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8561-fix-display-active-jobs-in-oldest-first-order-in-media-assets-panel-2fc6d73d365081c6bf31cb076a8d6014)
by [Unito](https://www.unito.io)
## Summary
Remove feature flags that are now enabled for all users and clean up
dead code paths.
## Changes
**Removed feature flags:**
- `huggingfaceModelImportEnabled` - always show generic URL input with
both Civitai/HuggingFace support
- `assetDeletionEnabled` - delete button always shown for mutable assets
- `asyncModelUploadEnabled` - always use async upload path (removed sync
upload and file size warnings)
**Kept feature flags:**
- `modelUploadButtonEnabled` - controls upload button visibility (local
vs cloud)
- `privateModelsEnabled` - controls upgrade modal vs upload dialog
**Deleted unused component:**
- `UploadModelUrlInputCivitai.vue` (replaced by generic input)
## Testing
- `pnpm typecheck` passes
- `pnpm lint` passes
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8566-feat-remove-obsolete-model-asset-feature-flags-2fc6d73d365081149ec6e78d995c8a44)
by [Unito](https://www.unito.io)
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
## Release Notes
* **Improvements**
* Asset deletion option is now always available
* Model upload experience simplified with unified import flow
* Model uploads now consistently use asynchronous processing
* HuggingFace model imports are now always supported
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
Co-authored-by: Amp <amp@ampcode.com>
## Summary
Add a Secrets panel to user settings for managing third-party API keys
(HuggingFace, Civitai). Secrets are encrypted server-side; plaintext
values are never returned.
## Changes
- Add `SecretsPanel` to settings for managing third-party API keys
- Create `secretsApi` service following the `workspaceApi` pattern
- Add `SecretListItem` and `SecretFormDialog` components
- Add `user_secrets_enabled` feature flag
- Support HuggingFace and Civitai providers
- Add i18n translations for secrets UI
## Files Added
- `src/platform/secrets/types.ts` - TypeScript types
- `src/platform/secrets/api/secretsApi.ts` - Axios-based API service
- `src/platform/secrets/components/SecretsPanel.vue` - Main settings
panel
- `src/platform/secrets/components/SecretListItem.vue` - Individual
secret row
- `src/platform/secrets/components/SecretFormDialog.vue` - Create/edit
dialog
## Files Modified
- `src/platform/remoteConfig/types.ts` - Add `user_secrets_enabled` flag
type
- `src/composables/useFeatureFlags.ts` - Add flag getter
- `src/platform/settings/composables/useSettingUI.ts` - Integrate
secrets panel
- `src/locales/en/main.json` - Add translations
## Testing
Panel appears in Settings under:
- "Workspace" group when team workspaces is enabled
- "Account" group in legacy mode
Only visible when user is logged in AND `user_secrets_enabled` feature
flag is enabled.
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8473-feat-add-user-secrets-management-panel-2f86d73d36508187b4a1ed04ce07ce51)
by [Unito](https://www.unito.io)
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* Secrets management UI in Settings with create/edit/delete for API
keys/secrets; settings dialog entry and contextual Secrets hint in
upload/import flows (feature-flag gated).
* **APIs**
* Added backend-facing secrets CRUD surface and client-side
form/composable support for managing secrets.
* **Localization**
* New English translations for Secrets UI and many expanded asset
import/upload error and hint messages.
* **Tests**
* Comprehensive unit tests for secrets UI, form flows, and composables.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
---------
Co-authored-by: Amp <amp@ampcode.com>
## Summary
Fixes PNG iTXt chunk parsing to comply with the PNG specification.
## Problem
The current iTXt parser incorrectly reads the text content immediately
after the keyword null terminator, but iTXt chunks have additional
fields:
- Compression flag (1 byte)
- Compression method (1 byte)
- Language tag (null-terminated)
- Translated keyword (null-terminated)
- Text content
This caused PNGs that correctly follow the spec to fail loading with
JSON parse errors due to leading null bytes.
## Solution
Skip the compression flag, method, language tag, and translated keyword
fields before reading the text content.
Fixes#8150
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8530-fix-properly-parse-PNG-iTXt-chunks-per-specification-2fa6d73d36508189bef4cc5fa3899096)
by [Unito](https://www.unito.io)
---------
Co-authored-by: GitHub Action <action@github.com>
Co-authored-by: github-actions <github-actions@github.com>
Patch version increment to 1.39.5
**Base branch:** `main`
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* Added and updated localized UI text across multiple languages: new
assetBrowser.baseModel and sortDefault labels, nodeFilters group (filter
labels/descriptions), templates.logoProviderSeparator, and
legacyManagerSearchTip; unified account-deletion messaging to a
contactSupport entry.
* **Chores**
* Updated front-end version to 1.39.5.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
---------
Co-authored-by: christian-byrne <72887196+christian-byrne@users.noreply.github.com>
Co-authored-by: github-actions <github-actions@github.com>
## Summary
Lower the barrier for bug reports by making custom-node info optional
rather than a required prerequisite. Many users run custom nodes and can
still hit core or interaction bugs that aren't caused by those nodes;
even when custom nodes are involved, the issue may still be ours, so
this change unblocks reports and feature requests while preserving
context.
## Changes
- **What**: Replace the required "tested with all custom nodes disabled"
checkbox with a neutral "I'm using custom nodes" checkbox to capture
context without blocking submissions.
## Review Focus
Confirm the new checkbox wording captures custom-node context without
discouraging reports from users who can't or shouldn't disable custom
nodes.
<!-- If this PR fixes an issue, uncomment and update the line below -->
<!-- Fixes #ISSUE_NUMBER -->
## Screenshots (if applicable)
<!-- Add screenshots or video recording to help explain your changes -->
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8546-fix-make-custom-nodes-checkbox-optional-2fb6d73d3650816fa743e355f1877de1)
by [Unito](https://www.unito.io)
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Documentation**
* Updated the bug report issue template to clarify prerequisites: the
primary prerequisite remains required, while the custom-nodes option is
now optional. This reduces mandatory checklist items and improves
guidance on which setup details are essential for effective bug reports.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
## Summary
Enable CodeRabbit issue enrichment and remove duplicate-check
requirements to lower the barrier for reporting problems.
## Changes
- **What**: Add `.coderabbit.yaml` enabling issue enrichment; remove
dedupe checkboxes from bug and feature issue templates.
## Review Focus
Confirm we want CodeRabbit/Dosu to handle deduplication so users aren’t
blocked by a mandatory “search for duplicates” step.
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8543-Enable-CodeRabbit-issue-enrichment-2fb6d73d36508132947acf5f01c9cb22)
by [Unito](https://www.unito.io)
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Chores**
* Enabled automatic issue enrichment to enhance issue tracking.
* Streamlined issue submission forms by removing prerequisite
verification steps from bug report and feature request templates.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
## Summary
Adds deprecation warning for the `widgets_up` property which has known
layout bugs and is not officially supported.
## Changes
- Added `@deprecated` JSDoc comment on `widgets_up` property
- Added console warning during configure path
- Warning directs users to use `widgets_start_y` or custom `arrange()`
override instead
## Context
The `widgets_up` property is an undocumented legacy LiteGraph feature
that positions widgets at the top of nodes. However, the `arrange()`
method doesn't properly handle slot positioning when this is enabled,
causing widgets and slots to overlap (see #7878).
Rather than fix this edge case in an unsupported feature, we're formally
deprecating it with a warning.
- Closes#7878
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8541-deprecate-add-warning-for-unsupported-widgets_up-property-2fb6d73d365081a89bb6e41038d94455)
by [Unito](https://www.unito.io)
## Summary
Fix "Frame Nodes" option being incorrectly categorized as an extension
item instead of appearing in the core menu section when multiple nodes
are selected.
## Changes
- **What**: Added "Frame Nodes" to `CORE_MENU_ITEMS` set and
`MENU_ORDER` array in contextMenuConverter.ts, and added frame
equivalents to duplicate detection to prevent duplicate menu items
## Review Focus
The fix ensures the Vue-based "Frame Nodes" menu option (i18n key
`g.frameNodes`) is recognized as a core menu item alongside the legacy
LiteGraph "Frame selection" label. Both are now treated as equivalent
for deduplication.
Fixes COM-13922
## Screenshots (if applicable)
N/A - menu item positioning fix
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8524-fix-add-Frame-Nodes-to-core-menu-items-for-multi-selection-context-menu-2fa6d73d365081989799f7bc74c65868)
by [Unito](https://www.unito.io)
---------
Co-authored-by: Amp <amp@ampcode.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
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)
## Summary
Use the shared NodeId type for output asset key construction to keep
node id typing consistent across assets and workflow schemas.
## Changes
- **What**: replace the local `string | number` nodeId type in output
key parts with the shared `NodeId` alias
## Review Focus
Type consistency for `nodeId` used in output asset key generation.
## Screenshots (if applicable)
N/A
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8547-fix-use-NodeId-for-output-key-parts-2fb6d73d36508127a022cc6052e5f59e)
by [Unito](https://www.unito.io)
Add expandable output stacks to the assets list view.
Monolith ver. of https://github.com/Comfy-Org/ComfyUI_frontend/pull/8298
and its children
List view currently collapses multi-output jobs into a single row, which
makes sibling outputs easy to miss and causes selection/zoom behavior to
drift once items are expanded elsewhere. This change adds a stack toggle
to list rows, expands child outputs derived from job data, and keeps
list-view selection and gallery navigation aligned with the expanded
list. Output mapping and “load full outputs” checks are centralized so
folder view and stacks share the same helper, and job-detail parsing now
yields previewable outputs for the list view. Asset actions now prefer
metadata prompt IDs to support the composite IDs used by stacked
outputs.
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8283-Add-expandable-output-stacks-to-assets-list-view-2f16d73d365081a99fc6f1519ac2e57c)
by [Unito](https://www.unito.io)
---------
Co-authored-by: GitHub Action <action@github.com>
Co-authored-by: Johnpaul Chiwetelu <49923152+Myestery@users.noreply.github.com>
# Documentation Audit and Corrections
## Summary
- Fixed incorrect test directory reference in AGENTS.md (`tests-ui/` →
removed)
- Updated outdated timestamps in composables and stores READMEs
- Fixed critical workflow name error in create-frontend-release command
- Removed misleading i18n workflow warnings from release documentation
## Changes Made
### AGENTS.md
- **Line 24**: Removed non-existent `tests-ui/` directory from test file
locations
- Unit/component tests are located in `src/**/*.test.ts` only
- **Line 267**: Removed `tests-ui` from repository navigation guidance
### src/composables/README.md
- **Line 88**: Updated timestamp from "2025-01-30" to "2026-01-30" to
reflect current state
### src/stores/README.md
- **Line 105**: Updated timestamp from "2025-09-01" to "2026-01-29" to
reflect last modification date
### .claude/commands/create-frontend-release.md
- **Line 394**: Fixed critical workflow name error
- Changed `version-bump.yaml` → `release-version-bump.yaml`
- This was causing command failures as the workflow file doesn't exist
- **Lines 446-450**: Removed outdated warning about `update-locales`
workflow adding `[skip ci]`
- No such workflow exists in the repository
- Current i18n workflows are: `i18n-update-core.yaml`,
`i18n-update-custom-nodes.yaml`, `i18n-update-nodes.yaml`
- None of these add `[skip ci]` to commits
- **Lines 464-465**: Removed duplicate critical warning about
non-existent `update-locales` workflow
## Review Notes
### Documentation Accuracy Verification Process
Conducted comprehensive fact-checking of all documentation against the
current codebase:
1. **Configuration Files**: Verified all referenced config files exist
(vite.config.mts, playwright.config.ts, eslint.config.ts, .oxfmtrc.json,
.oxlintrc.json)
2. **Package Scripts**: Validated all npm/pnpm commands referenced in
documentation
3. **Directory Structure**: Confirmed project structure matches
documented layout
4. **Extension Documentation**: Verified TypeScript interface paths and
external resource links
5. **Testing Documentation**: Confirmed test file patterns and framework
references
6. **Command Documentation**: Validated workflow names and GitHub CLI
commands
### Known Documentation Gaps (Not Addressed)
These items were identified but not changed as they represent
aspirational guidance or current migration paths:
1. **docs/guidance/vue-components.md**: Documents Vue 3.5 prop
destructuring pattern (`const { prop } = defineProps<>()`) while
codebase still uses `withDefaults()` in many files
- This is intentional - guidance shows preferred pattern for new code
2. **docs/guidance/playwright.md**: Advises against `waitForTimeout` but
4 test files still use it
- Existing violations are technical debt
3. **AGENTS.md Line 163**: States "Avoid new usage of PrimeVue
components" but some components still use PrimeVue
- Guidance is for new code; existing usage being gradually migrated
4. **docs/guidance/typescript.md**: Discourages `as any` but 11
instances exist in codebase
- Known technical debt
### Additional Findings
- All README files in key directories (composables, stores, services,
extensions, testing) are accurate and comprehensive
- External documentation links (Vue, Tailwind, VueUse, etc.) are valid
- Code examples in documentation are syntactically correct
- i18n structure and paths are correctly documented
## Verification Commands
To verify the fixes:
```bash
# Verify tests-ui directory doesn't exist
ls tests-ui 2>&1 | grep "No such file"
# Verify correct workflow file exists
ls .github/workflows/release-version-bump.yaml
# Verify no workflows add [skip ci]
grep -r "skip ci" .github/workflows/*.yaml || echo "None found (expected)"
# Verify test files are in src/
find src -name "*.test.ts" | wc -l # Should show many test files
```
---------
Co-authored-by: christian-byrne <72887196+christian-byrne@users.noreply.github.com>
Co-authored-by: GitHub Action <action@github.com>
## Summary
When a workflow is queued with a missing node type, show the Missing
Nodes dialog instead of a generic error toast. This gives users
actionable options like "Open Manager" and "Install All".
## Changes
- Detect `missing_node_type` error from backend in `queuePrompt()` catch
block
- Construct `MissingNodeType` object with class type and contextual hint
- Reuse existing `showMissingNodesError()` to trigger the dialog
## Dependencies
⚠️ **Requires backend PR:**
https://github.com/Comfy-Org/ComfyUI/pull/12177
The backend PR changes the error type from `invalid_prompt` to
`missing_node_type` and adds `extra_info` with node details.
## Related
- Fixes COM-12528
- Addresses ~49 GitHub issues with confusing "missing class_type" errors
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8511-fix-show-Missing-Nodes-dialog-for-missing-node-prompt-errors-2f96d73d3650812b95f0f08e51abaabb)
by [Unito](https://www.unito.io)
---------
Co-authored-by: Amp <amp@ampcode.com>
## Summary
Show a non-intrusive tip in the no-results empty state when users search
legacy manager-related terms in the new manager UI.
## Changes
- **What**: Add `useLegacySearchTip` composable that detects when search
query matches legacy manager keywords ("manager", "comfyui-manager",
etc.) and appends a tip to the empty state message suggesting the
`--enable-manager-legacy-ui` flag
- **Integration**: Tip appears as secondary muted text below "Try a
different search" message when no results found
- **Tests**: 9 test cases covering detection logic and edge cases
## Review Focus
- Keyword list covers common legacy manager search terms
- Non-intrusive approach: tip only shows in no-results state, no dismiss
button needed
Fixes COM-12509
## Summary
Fix template logo loading by using `api.fileURL()` instead of
`api.fetchApi()` for the static logo index file.
## Problem
`fetchLogoIndex()` was using
`api.fetchApi('/templates/index_logo.json')` which adds an `/api` prefix
to URLs, resulting in requests to `/api/templates/index_logo.json`
instead of `/templates/index_logo.json`.
Since `/templates/index_logo.json` is a static asset file (not an API
endpoint), it should use `api.fileURL()` which doesn't add the `/api`
prefix.
## Changes
- Change `api.fetchApi('/templates/index_logo.json')` to
`fetch(api.fileURL('/templates/index_logo.json'))`
## Testing
- Template logos should now load correctly in the workflow template
selector dialog
Fixes COM-14279
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8539-fix-use-fileURL-for-static-template-logo-index-path-2fb6d73d365081178788dc0cf196ada4)
by [Unito](https://www.unito.io)
## Summary
Refactors `keybindingService.escape.test.ts` to use the singleton mock
pattern with reactive state, following project testing guidance.
## Changes
- **What**: Replaced `vi.fn(() => ({dialogStack: []}))` anti-pattern
with `reactive()` array defined inline in `vi.mock()` factory. Tests now
use array mutation (`.push()`, `.length = 0`) instead of
`mockReturnValue` calls.
## Review Focus
Pattern aligns with docs/testing/unit-testing.md section "Mocking
Composables with Reactive State".
Fixes#8467
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8538-refactor-use-singleton-mock-pattern-for-useDialogStore-in-escape-test-2fb6d73d3650812a95c6e223e38d50ad)
by [Unito](https://www.unito.io)