## Summary
Implements stale-while-revalidate pattern for AssetBrowserModal to show
cached assets immediately while refreshing in background.
## Changes
### AssetBrowserModal.vue
- Reads assets directly from store cache via computed properties
- Shows loading spinner only when loading AND no cached data exists
- Simplified refresh logic: single `refreshAssets()` call on mount
### assetsStore.ts
- Added `updateModelsForTag(tag)` for tag-based fetching
- Added `updateModelsForKey()` internal helper to unify node type and
tag fetching
- Cache key convention: node types as-is, tags prefixed with `tag:`
- Added `isEqual` check before cache updates to prevent unnecessary
re-renders
### useModelUpload.ts
- Simplified signature from `UseAsyncStateReturn<...>['execute']` to `()
=> Promise<unknown> | void`
## UX Improvement
| Scenario | Before | After |
|----------|--------|-------|
| First open | Spinner → Assets | Spinner → Assets |
| Re-open same type | Spinner → Assets | Instant + silent refresh |
| Re-open after download | Spinner → Assets | Cached + auto-update |
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-7880-feat-Stale-while-revalidate-pattern-for-AssetBrowserModal-2e16d73d365081ba93f4d6e0415ebfae)
by [Unito](https://www.unito.io)
---------
Co-authored-by: Amp <amp@ampcode.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
## Summary
- Define `touch:` Tailwind variant using `@media (hover: none)` to
target touch devices
- Add `touch:opacity-100` to `TreeExplorerTreeNode` for node action
buttons
- Add `useMediaQuery('(hover: none)')` to `MediaAssetCard` for action
overlay visibility
## Problem
On touch devices, sidebar buttons that appear on hover are inaccessible
because:
1. The `touch:` Tailwind variant was used but never defined (classes
silently ignored)
2. `TreeExplorerTreeNode` had no touch support for action buttons
3. `MediaAssetCard` used JS-based `useElementHover` which doesn't work
on touch
## Screenshots (Touch Device Emulation)
### Before (main branch)
- No "Generated"/"Imported" tabs visible in header
- Only duration chips shown on cards, no action buttons (zoom, menu)

### After (with fix)
- "Generated"/"Imported" tabs visible in header
- Action buttons (zoom, menu) visible on left of cards
- Duration chips moved to right side

## Test plan
- [ ] On touch device: verify Media Assets sidebar
"Imported"/"Generated" tabs are visible
- [ ] On touch device: verify Node Library filter buttons are visible
- [ ] On touch device: verify tree node action buttons (bookmark, help)
are visible
- [ ] On touch device: verify media asset card zoom/menu buttons are
visible
- [ ] On desktop with mouse: verify hover behavior still works as
expected
A couple small dynamic input fixes.
- When removing widgets, call any onRemove methods
- This is required for DOMWidgets to properly clean themself up.
- Resolve actual current link state when initializing match type
- This is only a partial fix for combing matchtype with autogrow, there
is a separate issue with skipped initialization that will need to be
resolved separately in the future.
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-7837-Dynamic-input-fixes-2de6d73d365081bdb263ed659e25e6ea)
by [Unito](https://www.unito.io)
## Summary
Replace single `asset_update_options_enabled` feature flag with two
granular flags:
- `asset_deletion_enabled`: controls delete button visibility
- `asset_rename_enabled`: controls rename button visibility
The context menu only shows when at least one flag is enabled.
## Changes
- Updated `ServerFeatureFlag` enum with new flag names
- Updated `RemoteConfig` type with new properties
- Updated `AssetCard.vue` to conditionally show rename/delete buttons
based on their respective flags
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-7864-feat-split-asset_update_options_enabled-into-separate-deletion-and-rename-flags-2e06d73d365081f9ac0afa12b87bd988)
by [Unito](https://www.unito.io)
---------
Co-authored-by: Amp <amp@ampcode.com>
## Summary
Remove 178 `@ts-expect-error` suppressions (935 → 757, 19% reduction) by
fixing underlying type issues instead of suppressing errors.
## Changes
- **What**: Type safety improvements across `src/lib/litegraph/` and
related test files
- Prefix unused callback parameters with `_` instead of suppressing
- Use type intersections for mock methods on real objects
- Use `Partial<T>` for incomplete test objects instead of `as unknown`
- Add non-null assertions after `.toBeDefined()` checks in tests
- Let TypeScript infer vitest fixture parameter types
- **Breaking**: None
## Review Focus
- `LGraphCanvas.ts` has the largest changes (232 lines) — all mechanical
unused parameter fixes
- Test files use type intersection pattern for mocks: `node as
LGraphNode & { mockFn: ... }`
- Removed dead code: `src/platform/cloud/onboarding/auth.ts` (47 lines,
unused)
### Key Files
| File | Change |
|------|--------|
| `LGraphCanvas.ts` | 57 suppressions removed (unused params) |
| `subgraph/__fixtures__/*` | Fixture type improvements |
| `*.test.ts` files | Mock typing with intersections |
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-7817-WIP-Chore-Typescript-cleanup-2da6d73d365081d1ade9e09a6c5bf935)
by [Unito](https://www.unito.io)
---------
Co-authored-by: Amp <amp@ampcode.com>
Co-authored-by: GitHub Action <action@github.com>
## Summary
- Adds asynchronous model upload support with HTTP 202 responses
- Implements WebSocket-based real-time download progress tracking via
`asset_download` events
- Creates `assetDownloadStore` for centralized download state management
and toast notifications
- Updates upload wizard UI to show "processing" state when downloads
continue in background
## Changes
- **Core**: New `assetDownloadStore` for managing async downloads with
WebSocket events
- **API**: Support for HTTP 202 async upload responses with task
tracking
- **UI**: Upload wizard now shows "processing" state and allows closing
dialog during download
- **Progress**: Periodic toast notifications (every 5s) during active
downloads with completion/error toasts
- **Schema**: Updated task statuses (`created`, `running`, `completed`,
`failed`) and WebSocket message types
## Review Focus
- WebSocket event handling and download state management in
`assetDownloadStore`
- Upload flow UX - users can now close the dialog and download continues
in background
- Toast notification frequency and timing
- Schema alignment with backend async upload API
Fixes#7748
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-7746-feat-Add-async-model-upload-with-WebSocket-progress-tracking-2d36d73d3650811cb79ae06f470dcded)
by [Unito](https://www.unito.io)
---------
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: GitHub Action <action@github.com>
- Add capture:true to removeEventListener calls that were added with capture
to prevent event listener leaks when unbinding
- Reuse existing target variable in Delete/Backspace check instead of
re-accessing e.target with @ts-expect-error
- Add isContentEditable check to Delete/Backspace guard for consistency
Broaden the target guard in processKey to skip all text-editable surfaces
(input, textarea, contenteditable) before handling shortcuts. This prevents
space/Ctrl+A/C from being blocked when typing in prompt textareas or other
multiline fields in Vue nodes mode.
## Summary
Implement the new design for template library
## Changes
- What
- New sort option: `Popular` and `Recommended`
- New category: `Popular`, leverage the `Popular` sorting
- Support add category stick to top of the side bar
- Support template customized visible in different platform by
`includeOnDistributions` field
### How to make `Popular` and `Recommended` work
Add usage-based ordering to workflow templates with position bias
correction, manual ranking (searchRank), and freshness boost.
New sort modes:
- "Recommended" (default): usage × 0.5 + searchRank × 0.3 + freshness ×
0.2
- "Popular": usage × 0.9 + freshness × 0.1
## Screenshots (if applicable)
New default ordering:
<img width="1812" height="1852" alt="Selection_2485"
src="https://github.com/user-attachments/assets/8f4ed6e9-9cf4-43a8-8796-022dcf4c277e"
/>
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-7062-feat-usage-based-template-ordering-2bb6d73d365081f1ac65f8ad55fe8ce6)
by [Unito](https://www.unito.io)
Popular category:
<img width="281" height="283" alt="image"
src="https://github.com/user-attachments/assets/fd54fcb8-6caa-4982-a6b6-1f70ca4b31e3"
/>
---------
Co-authored-by: Yourz <crazilou@vip.qq.com>
Co-authored-by: GitHub Action <action@github.com>
## 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>
Remove the LiteGraph.vueNodesMode check when removing the document-level
keydown listener in unbindEvents. If vueNodesMode was true during bindEvents
but changed before unbindEvents, the listener would be left dangling.
Now guarded only by existence of _key_callback.
Remove duplicate panning state management from useSlotLinkInteraction.
Instead of Vue manually manipulating canvas.ds.offset, sync pointer.isDown
with litegraph and let events bubble when in panning mode (read_only).
- Remove isPanningDuringLinkDrag and lastPanningMouse local state
- Remove manual offset manipulation in handlePointerMove
- Conditionally stopPropagation only when NOT in panning mode
- Set canvas.pointer.isDown so spacebar triggers litegraph panning
Move spacebar detection to document-level listener in LGraphCanvas when
vueNodesMode is enabled. Implement direct panning in useSlotLinkInteraction
when spacebar is held during connection drag, bypassing litegraph event
handling for smoother panning while maintaining link position updates.
Fixes#7806
## Summary
Refactored BrushSettingsPanel layout to stack labels and number inputs
above sliders, and fixed brush size keybinding limits to match the
updated 1-250 range.
## Changes
- **What**:
- Reorganized BrushSettingsPanel UI to display labels and number inputs
in a row above each slider (instead of side-by-side), creating a cleaner
vertical layout with better visual hierarchy.
- Updated brush size increase/decrease keybindings to clamp between
1-250 (previously 1-100) to match the refactored slider limits.
- Added setting for color picker keybinding
- **Breaking**: None
## Review Focus
- Verify the stacked layout (label + number input above slider) works
well across different panel widths
- Confirm all slider controls properly sync with their corresponding
number inputs
- Test brush size keybindings (increase/decrease) respect the new 1-250
limits
## Screenshot
<img width="1713" height="848" alt="image"
src="https://github.com/user-attachments/assets/22a26ad2-61be-4031-92d0-b4577a003552"
/>
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-7783-Fixed-Brush-Settings-Port-Refactor-and-Added-Numeric-Control-2d76d73d365081bda7a8e12d3c649085)
by [Unito](https://www.unito.io)
---------
Co-authored-by: Alexander Brown <drjkl@comfy.org>
### Problem
The "Validate workflow links" test fails because workflow validation is
disabled by default, preventing toast notifications from appearing.
### Solution
Enable the `Comfy.Validation.Workflows` setting before loading the
bad_link workflow in the test.
### Changes
Modified `browser_tests/tests/graph.spec.ts` to enable workflow
validation setting before test execution
### Root Cause
The `Comfy.Validation.Workflows` setting defaults to `false` (per
`src/stores/settingStore.ts`). Without this setting enabled, the
validation code path in `src/scripts/app.ts#L1085` is skipped, so no
toast notifications are generated.
## Testing
- Test now passes locally and should pass in CI
- Verified setting enables validation flow that generates expected 2
toasts:
1. "Workflow Validation" with validation logs
2. "Workflow Links Fixed" confirming successful fixes
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-7833-fix-enable-workflow-validation-2dc6d73d36508152b863f2e64ae57ecb)
by [Unito](https://www.unito.io)
Add AMD ROCm GPU option to the desktop installer
## What changed
- Add an AMD GPU choice to the installer picker with updated recommended
badge logic, logo asset, and i18n copy.
- Accept and auto-select the new `amd` device type in the install flow
when it is detected.
- Update `@comfyorg/comfyui-electron-types` and lockfile entries
required for the new device enum.
## Why
- Desktop users with AMD GPUs need a first-class install path instead of
falling back to CPU/manual options.
- This reuses the existing picker/device model to keep the change scoped
and consistent with current UX.
- Tradeoffs: torch mirror selection still falls back to the CPU mirror
for AMD until a dedicated ROCm mirror is available.
## Evidence
- Interactive Storybook file
`apps/desktop-ui/src/components/install/GpuPicker.stories.ts`
<img width="1377" height="834" alt="image"
src="https://github.com/user-attachments/assets/34145f46-d8cc-4e59-b587-0ab5ee79f888"
/>