## Summary
(Not sure we need this, and I don't know the reason why we only have one
cornor support previously, but it is requested by QA reporting in
Notion)
Add resize handles at all four corners (NW, NE, SW, SE) of Vue nodes,
matching litegraph's multi-corner resize behavior.
Vue nodes previously only supported resizing from the bottom-right (SE)
corner. This adds handles at all four corners with direction-aware delta
math, snap-to-grid support, and minimum size enforcement that keeps the
opposite corner anchored.
The content-driven minimum height is measured from the DOM at resize
start to prevent the node from sliding when dragged past its minimum
size.
## Screenshots (if applicable)
https://github.com/user-attachments/assets/c9d30d93-8243-4c44-a417-5ca6e9978b3b
## Summary
If you load the window in Nodes 2.0, then switch tabs while it is still
loading, the position of the nodes is calculated incorrectly due to
useElementBounding returning left=0, top=0 for the canvas element in a
hidden tab, causing clientPosToCanvasPos to miscalculate node positions
from the ResizeObserver measurements
## Changes
- **What**:
- Store observed elements while document is in hidden state
- Re-observe when tab becomes visible
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8805-Defer-vue-node-layout-calculations-on-hidden-browser-tabs-3046d73d365081019ae6c403c0ac6d1a)
by [Unito](https://www.unito.io)
## Summary
Resolves the following issue:
1. Enable Nodes 2.0
2. Load default workflow
3. Move any node e.g. VAE decode
4. Undo
All links go invisible, input/output slots no longer function
## Changes
- **What**
- Fixes slot layouts being deleted during undo/redo in
`handleDeleteNode`, which prevented link dragging from nodes after undo.
Vue patches (not remounts) components with the same key, so `onMounted`
never fires to re-register them - these were already being cleared up on
unmounted
- Fixes links disappearing after undo by clearing `pendingSlotSync` when
slot layouts already exist (undo/redo preserved them), rather than
waiting for Vue mounts that do not happen
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8808-Resolve-issues-with-undo-with-Nodes-2-0-to-fix-link-dragging-rendering-3046d73d3650818bbb0adf0104a5792d)
by [Unito](https://www.unito.io)
## Summary
<!-- One sentence describing what changed and why. -->
Added feature to drag and drop multiple images into the UI and connect
them with a Batch Images node with tests to add convenience for users.
Only works with a group of images, mixing files not supported.
## Review Focus
<!-- Critical design decisions or edge cases that need attention -->
I've updated our usage of Litegraph.createNode, honestly, that method is
pretty bad, onNodeCreated option method doesn't even return the node
created. I think I will probably go check out their repo to do a PR over
there. Anyways, I made a createNode method to avoid race conditions when
creating nodes for the paste actions. Will allow us to better
programmatically create nodes that do not have workflows that also need
to be connected to other nodes.
<!-- If this PR fixes an issue, uncomment and update the line below -->
https://www.notion.so/comfy-org/Implement-Multi-image-drag-and-drop-to-canvas-2eb6d73d36508195ad8addfc4367db10
## Screenshots (if applicable)
https://github.com/user-attachments/assets/d4155807-56e2-4e39-8ab1-16eda90f6a53
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8282-Batch-Drag-Drop-Images-2f16d73d365081c1ab31ce9da47a7be5)
by [Unito](https://www.unito.io)
---------
Co-authored-by: Alexander Brown <drjkl@comfy.org>
Co-authored-by: Austin Mroz <austin@comfy.org>
## Summary
Set `audioWidget.value` after uploading an audio file so the combo
dropdown reflects the newly uploaded file.
## Changes
- **What**: Added `audioWidget.value = path` in the `uploadFile`
function before the callback call, matching the pattern used by the
image upload widget.
## Review Focus
One-line fix. The image upload widget already does this correctly in
`useImageUploadWidget.ts:86`. Without this line, the file uploads but
the dropdown doesn't update to show the selection.
Fixes#8800
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8814-fix-set-audio-widget-value-after-file-upload-3056d73d365081a0af90d4e096eb4975)
by [Unito](https://www.unito.io)
## Summary
- Add `typescript/no-explicit-any` rule to `.oxlintrc.json` to enforce
no explicit `any` types
- Fix all 40 instances of explicit `any` throughout the codebase
- Improve type safety with proper TypeScript types
## Changes Made
### Configuration
- Added `typescript/no-explicit-any` rule to `.oxlintrc.json`
### Type Fixes
- Replaced `any` with `unknown` for truly unknown types
- Updated generic type parameters to use `unknown` defaults instead of
`any`
- Fixed method `this` parameters to avoid variance issues
- Updated component props to match new generic types
- Fixed test mocks to use proper type assertions
### Key Files Modified
- `src/types/treeExplorerTypes.ts`: Updated TreeExplorerNode interface
generics
- `src/platform/settings/types.ts`: Fixed SettingParams generic default
- `src/lib/litegraph/src/LGraph.ts`: Fixed ParamsArray type constraint
- `src/extensions/core/electronAdapter.ts`: Fixed onChange callbacks
- `src/views/GraphView.vue`: Added proper type imports
- Multiple test files: Fixed type assertions and mocks
## Test Plan
- [x] All lint checks pass (`pnpm lint`)
- [x] TypeScript compilation succeeds (`pnpm typecheck`)
- [x] Pre-commit hooks pass
- [x] No regression in functionality
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8601-feat-add-typescript-no-explicit-any-rule-and-fix-all-instances-2fd6d73d365081fd9beef75d5a6daf5b)
by [Unito](https://www.unito.io)
---------
Co-authored-by: GitHub Action <action@github.com>
Co-authored-by: Alexander Brown <drjkl@comfy.org>
## Summary
Scope the top-menu Manager button red dot to manager conflict state
only, so release-update notifications do not appear on the Manager
button.
## Changes
- **What**:
- In `TopMenuSection`, remove release-store coupling and use only
`useConflictAcknowledgment().shouldShowRedDot` for the Manager button
indicator.
- Add a regression test in `TopMenuSection.test.ts` that keeps the
release red dot true while asserting the Manager button dot only appears
when the conflict red dot is true.
## Review Focus
- Confirm Manager button notification semantics are conflict-specific
and no longer mirror release notifications.
- Confirm the new test fails if release-store coupling is reintroduced.
## Screenshots (if applicable)
N/A
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8810-fix-scope-manager-button-red-dot-to-conflicts-3046d73d3650817887b9ca9c33919f48)
by [Unito](https://www.unito.io)
## Summary
Fix link dragging offset when using Vue nodes mode on external monitors
with different DPI than the primary display.
## Changes
- **What**: Derive overlay canvas scale from actual canvas dimensions
(`canvas.width / canvas.clientWidth`) instead of
`window.devicePixelRatio`, fixing DPR mismatch. Map `LinkDirection.NONE`
to `'none'` in `convertDirection()` instead of falling through to
`'right'`.
## Before
https://github.com/user-attachments/assets/f5d04617-369f-4649-af60-11d31e27a75c
## After
https://github.com/user-attachments/assets/76434d2b-d485-43de-94f6-202a91f73edf
## Review Focus
The overlay canvas copies dimensions from the main canvas (which
includes DPR scaling from `resizeCanvas`). When the page loads on a
monitor whose DPR differs from what `resizeCanvas` used,
`window.devicePixelRatio` no longer matches the canvas's internal-to-CSS
ratio, causing all drawn link positions to be offset. The fix derives
scale directly from the canvas itself.
`LinkDirection.NONE = 0` is falsy, so it was caught by the `default`
case in `convertDirection()`, adding an unwanted directional curve to
moved input links.
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8809-fix-link-dragging-offset-on-external-monitors-in-Vue-nodes-mode-3046d73d36508143b600f23f5fe07044)
by [Unito](https://www.unito.io)
- "Enter Subgraph" "Show advanced inputs" and a new "show node Errors"
button now use a combined button design at the bottom of the node.
- A new "Errors" tab is added to the right side panel
- After a failed queue, the label of an invalid widget is now red.
- Badges other than price are now displayed on the bottom of the node.
- Price badge will now truncate from the first space, prioritizing the
sizing of the node title
- An indicator for the node resize handle is now displayed while mousing
over the node.
<img width="669" height="233" alt="image"
src="https://github.com/user-attachments/assets/53b3b59c-830b-474d-8f20-07f557124af7"
/>

---------
Co-authored-by: github-actions <github-actions@github.com>
## Summary
Implements Phase 1 of the **Vue-owns-truth** pattern for widget values.
Widget values are now canonical in a Pinia store; `widget.value`
delegates to the store while preserving full backward compatibility.
## Changes
- **New store**: `src/stores/widgetValueStore.ts` - centralized widget
value storage with `get/set/remove/removeNode` API
- **BaseWidget integration**: `widget.value` getter/setter now delegates
to store when widget is associated with a node
- **LGraphNode wiring**: `addCustomWidget()` automatically calls
`widget.setNodeId(this.id)` to wire widgets to their nodes
- **Test fixes**: Added Pinia setup to test files that use widgets
## Why
This foundation enables:
- Vue components to reactively bind to widget values via `computed(() =>
store.get(...))`
- Future Yjs/CRDT backing for real-time collaboration
- Cleaner separation between Vue state and LiteGraph rendering
## Backward Compatibility
| Extension Pattern | Status |
|-------------------|--------|
| `widget.value = x` | ✅ Works unchanged |
| `node.widgets[i].value` | ✅ Works unchanged |
| `widget.callback` | ✅ Still fires |
| `node.onWidgetChanged` | ✅ Still fires |
## Testing
- ✅ 4252 unit tests pass
- ✅ Build succeeds
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8594-feat-add-WidgetValueStore-for-centralized-widget-value-management-2fc6d73d36508160886fcb9f3ebd941e)
by [Unito](https://www.unito.io)
---------
Co-authored-by: Amp <amp@ampcode.com>
Co-authored-by: github-actions <github-actions@github.com>
Co-authored-by: GitHub Action <action@github.com>
When we download an output, we now check if there's a filename defined
in the content-disposition and use that if there is.
## Summary
This has been primarily an issue on Comfy Cloud where assets are
content-addressed. Before now,
the downloaded files have retained the hash as the filename. With this
change, downloaded files
will use the user-supplied filename instead.
## Changes
- **What**: Use content-disposition filename when downloading assets
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8785-fix-download-Use-content-disposition-filename-3046d73d365081ec952ef3c1930e773d)
by [Unito](https://www.unito.io)
Implement Impact telemetry and checkout attribution through cloud
subscription checkout flows.
This PR adds Impact.com tracking support and carries attribution context
from landing-page visits into subscription checkout requests so
conversion attribution can be validated end-to-end.
- Register a new `ImpactTelemetryProvider` during cloud telemetry
initialization.
- Initialize the Impact queue/runtime (`ire`) and load the Universal
Tracking Tag script once.
- Invoke `ire('identify', ...)` on page views with dynamic `customerId`
and SHA-1 `customerEmail` (or empty strings when unknown).
- Expand checkout attribution capture to include `im_ref`, UTM fields,
and Google click IDs, with local persistence across navigation.
- Attempt `ire('generateClickId')` with a timeout and fall back to
URL/local attribution when unavailable.
- Include attribution payloads in checkout creation requests for both:
- `/customers/cloud-subscription-checkout`
- `/customers/cloud-subscription-checkout/{tier}`
- Extend begin-checkout telemetry metadata typing to include attribution
fields.
- Add focused unit coverage for provider behavior, attribution
persistence/fallback logic, and checkout request payloads.
Tradeoffs / constraints:
- Attribution collection is treated as best-effort in tiered checkout
flow to avoid blocking purchases.
- Backend checkout handlers must accept and process the additional JSON
attribution fields.
## Screenshots
<img width="908" height="208" alt="image"
src="https://github.com/user-attachments/assets/03c16d60-ffda-40c9-9bd6-8914d841be50"/>
<img width="1144" height="460" alt="image"
src="https://github.com/user-attachments/assets/74b97fde-ce0a-43e6-838e-9a4aba484488"/>
<img width="1432" height="320" alt="image"
src="https://github.com/user-attachments/assets/30c22a9f-7bd8-409f-b0ef-e4d02343780a"/>
<img width="341" height="135" alt="image"
src="https://github.com/user-attachments/assets/f6d918ae-5f80-45e0-855a-601abea61dec"/>
## Summary
- Move `NodeContextMenu` from `SelectionToolbox.vue` to
`GraphCanvas.vue` so the right-click context menu renders independently
of the `Comfy.Canvas.SelectionToolbox` setting
- Fixes#8417
## Test plan
- [x] Disable selection toolbox in settings, right-click a node —
context menu appears
- [x] Enable selection toolbox, right-click a node — context menu still
appears
- [x] `pnpm typecheck` passes
- [x] `pnpm lint` passes
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8781-fix-right-click-context-menu-disabled-when-selection-toolbox-is-off-3036d73d36508168a9add58e060b7e93)
by [Unito](https://www.unito.io)
## Summary
Stop link flickering when resizing nodes by removing the
`pendingSlotSync` flag assertion from `scheduleSlotLayoutSync`.
## Changes
- **What**: Remove `layoutStore.setPendingSlotSync(true)` from
`scheduleSlotLayoutSync()` in `useSlotElementTracking.ts`. This call was
introduced in #8367 for graph reconfiguration but was also triggered on
every node resize, causing all links to disappear for one frame per
resize tick. The reconfigure path in `app.ts`
(`addAfterConfigureHandler`) still sets the flag explicitly, so
undo/redo link suppression is unaffected.
## Review Focus
The `pendingSlotSync` flag is still managed correctly for graph
reconfiguration: `app.ts:748` sets it before configure, and the
`finally` block flushes it synchronously. The
`flushScheduledSlotLayoutSync` early-return (pendingNodes empty but
graph has nodes) continues to handle late-mounting Vue components during
reconfigure.
## Before
https://github.com/user-attachments/assets/28cfe4d8-f3f0-46f1-a717-5cb81a28dd75
## After
https://github.com/user-attachments/assets/9445fd00-91f8-4d1e-90ac-86d138d29842Fixes#8696
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8780-fix-stop-suppressing-link-rendering-during-node-resize-3036d73d365081029820ccfd57425a07)
by [Unito](https://www.unito.io)
---------
Co-authored-by: github-actions <github-actions@github.com>
## Summary
- Adds `settingId` parameter to `showSettingsDialog` that auto-navigates
to the correct category tab, scrolls to the setting, and briefly
highlights it with a CSS pulse animation
- Adds `data-setting-id` attributes to setting items for stable DOM
targeting
- Adds "Don't show this again" checkbox with "Re-enable in Settings"
deep-link to the missing nodes dialog
- Adds "Re-enable in Settings" deep-link to missing models and blueprint
overwrite "Don't show this again" checkboxes
- Fixes#3437
## Test plan
- [x] `pnpm typecheck` passes
- [x] `pnpm lint` passes
- [x] Unit tests pass (59/59 including 5 new tests for `useSettingUI`)
https://github.com/user-attachments/assets/a9e80aea-7b69-4686-b030-55a2e0570ff0
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8761-feat-scroll-to-specific-setting-when-opening-settings-dialog-3036d73d365081d18d9afe9f9ed41ebc)
by [Unito](https://www.unito.io)
## Summary
Sort workspaces so that the personal workspace appears first, followed
by the rest in ascending order (oldest first) by created_at / joined_at.
## Changes
- **What**: teamWorkspaceStore.ts, teamWorkspaceStore.test.ts
- **Breaking**: <!-- Any breaking changes (if none, remove this line)
-->
- **Dependencies**: <!-- New dependencies (if none, remove this line)
-->
## Summary
- Adds `setMany()` method to `settingStore` for updating multiple
settings in a single API call via the existing `storeSettings` endpoint
- Extracts shared setting-apply logic (`applySettingLocally`) to reduce
duplication between `set()` and `setMany()`
- Migrates all call sites where multiple settings were updated
sequentially to use `setMany()`
## Call sites updated
- `releaseStore.ts` — `handleSkipRelease`, `handleShowChangelog`,
`handleWhatsNewSeen` (3 settings each)
- `keybindingService.ts` — `persistUserKeybindings` (2 settings)
- `coreSettings.ts` — `NavigationMode.onChange` (2 settings)
## Test plan
- [x] Unit tests for `setMany` (batch update, skip unchanged, no-op when
unchanged)
- [x] Updated `releaseStore.test.ts` assertions to verify `setMany`
usage
- [x] Updated `useCoreCommands.test.ts` mock to include `setMany`
- [x] All existing tests pass
- [x] `pnpm typecheck`, `pnpm lint`, `pnpm format` pass
Fixes#1079
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8767-feat-add-setMany-to-settingStore-for-batch-setting-updates-3036d73d36508161b8b6d298e1be1b7a)
by [Unito](https://www.unito.io)
## Summary
Add `ensureGlobalIdUniqueness` to reassign duplicate node IDs across
subgraphs when loading workflows, gated behind an experimental setting.
## Changes
- **What**: Shared `LGraphState` between root graph and subgraphs so ID
counters are global. Added `ensureGlobalIdUniqueness()` method that
detects and remaps colliding node IDs in subgraphs, preserving root
graph IDs as canonical and patching link references. Gated behind
`Comfy.Graph.DeduplicateSubgraphNodeIds` (experimental, default
`false`).
- **Dependencies**: None
## Review Focus
- Shared state override on `Subgraph` (getter delegates to root, setter
is no-op) — verify no existing code sets `subgraph.state` directly.
- `Math.max` state merging in `configure()` prevents ID counter
regression when loading subgraph definitions.
- Feature flag wiring: static property on `LGraph`, synced from settings
via `useLitegraphSettings`.
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8762-feat-deduplicate-subgraph-node-IDs-on-workflow-load-experimental-3036d73d36508184b6cee5876dc4d935)
by [Unito](https://www.unito.io)
---------
Co-authored-by: Amp <amp@ampcode.com>
Co-authored-by: GitHub Action <action@github.com>
#8187 made removal of subgraphs cleanup the subgraph definition for the
removed graph and call onRemove handlers. However, it missed some edge
cases and broke subgraph conversion of selections containing subgraphs
which this PR tries to address.
- Deeply nested subgraphs are now also cleaned
- Adding a subgraphNode to the graph also ensures nested subgraphs are
added to subgraph definitions
Reminder: under this change, nodes can continue to exist after their
onRemoved handler has been called
- It may be better to instead perform the "garbage collection" of
subgraphs outside of the graph removal step to better handle edge cases
like subgraph conversion where a subgraph may continue to persist after
a parent subgraphNode has been removed from a graph.
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8758-Fix-edge-cases-in-subgraph-removal-logic-3026d73d36508177b34ffdd2e0a114fe)
by [Unito](https://www.unito.io)
## Summary
Show live KSampler previews on active job cards/list items in the Assets
sidebar, while preserving existing fallback behavior.
## Changes
- **What**:
- Added a prompt-scoped job preview store (`jobPreviewStore`) gated by
`Comfy.Execution.PreviewMethod`.
- Wired `b_preview_with_metadata` handling to map previews by
`promptId`.
- Extended queue job view model with `livePreviewUrl` and consumed it in
both sidebar list and grid active job UIs.
- Cleared prompt previews on execution reset.
- Added ref-counted shared blob URL lifecycle utility (`objectUrlUtil`)
and updated preview stores to retain/release shared URLs so each preview
event creates one object URL.
- Added/updated unit coverage in `useJobList.test.ts` for preview
enable/disable mapping.
## Review Focus
- Object URL lifecycle correctness across node previews and job previews
(retain/release behavior).
- Preview gating behavior when `Comfy.Execution.PreviewMethod` is
`none`.
- Active job UI fallback behavior (`livePreviewUrl` -> `iconImageUrl`).
## Screenshots (if applicable)
<img width="808" height="614" alt="image"
src="https://github.com/user-attachments/assets/37c66eb2-8c28-4eb4-bb86-5679cb77d740"
/>
<img width="775" height="345" alt="image"
src="https://github.com/user-attachments/assets/aa420642-b0d4-4ae6-b94a-e7934b5df9d6"
/>
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8723-feat-add-KSampler-live-previews-to-assets-sidebar-jobs-3006d73d365081aeb81dd8279bf99f94)
by [Unito](https://www.unito.io)
## Summary
Use reactive `userId` reads for `begin_checkout` telemetry so delayed
auth state updates are reflected at event time instead of using a stale
snapshot.
## Changes
- **What**: switched subscription checkout telemetry paths to
`storeToRefs(useFirebaseAuthStore())` and read `userId.value` when
dispatching `trackBeginCheckout`.
- **What**: added regression tests that mutate `userId` after setup /
after checkout starts and assert telemetry uses the updated ID.
## Review Focus
- Verify `PricingTable` and `performSubscriptionCheckout` still emit
exactly one `begin_checkout` event per action, with `checkout_type:
change` and `checkout_type: new` in their respective paths.
- Verify the new tests would fail with stale store destructuring
(manually validated during development).
## Screenshots (if applicable)
N/A
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8726-fix-keep-begin_checkout-user_id-reactive-in-subscription-flows-3006d73d365081888c84c0335ab52e09)
by [Unito](https://www.unito.io)
## Summary
Wire `renewal_date` from the cloud `/billing/status` response into the
workspace subscription UI so users can see when their subscription
renews.
## Problem
The workspace billing adapter hardcoded `renewalDate: null` because the
cloud billing status endpoint didn't return a renewal date. The
`SubscriptionPanelContentWorkspace` component already has UI for
displaying it — it just had no data.
Personal Workspace (existing `cloud-subscription-status`):
<img width="181" height="112" alt="Screenshot 2026-02-08 at 7 54 53 PM"
src="https://github.com/user-attachments/assets/a96ba2cd-10d0-442a-ae72-dbc663a9e52b"
/>
Current missing data from `/billing/status`:
<img width="240" height="124" alt="Screenshot 2026-02-08 at 7 55 38 PM"
src="https://github.com/user-attachments/assets/a3f51ce3-6663-43e1-97ed-d012a6a8a5ba"
/>
## Solution
- Add `renewal_date?: string` to `BillingStatusResponse` interface
- Use `status.renewal_date ?? null` instead of hardcoded `null` in
`useWorkspaceBilling`
### Related
- Cloud PR: Comfy-Org/cloud#2370 (adds `renewal_date` to the endpoint)
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8754-feat-wire-renewal_date-from-cloud-billing-status-3026d73d365081c7ae51d79ef0633a1d)
by [Unito](https://www.unito.io)
## Description
The Playwright test comment was showing a "❌ Failed Tests" section
header even when there were only flaky tests (no actual failures). This
was confusing because the red X suggested failure when tests actually
passed.
**Before:** Shows "❌ Failed Tests" section for flaky-only runs
**After:** Only shows "❌ Failed Tests" section when there are actual
failures; flaky tests are treated as passing
## Related Issue
Fixes the misleading comment behavior seen in PR #8573
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8575-fix-only-show-Failed-Tests-section-when-there-are-actual-failures-2fc6d73d36508167889cc252e4e06f2e)
by [Unito](https://www.unito.io)
---------
Co-authored-by: Alexander Brown <drjkl@comfy.org>
## Summary
Fix WEBP workflow loading failures for files with odd-sized chunks
before the EXIF chunk.
## Problem
WEBP files use the RIFF container format which [requires odd-sized
chunks to be padded with a single
byte](https://developers.google.com/speed/webp/docs/riff_container#riff_file_format):
> If Chunk Size is odd, a single padding byte -- which MUST be 0 to
conform with RIFF -- is added.
The `getWebpMetadata` function was not accounting for this padding,
causing it to miss the EXIF chunk in files with odd-sized preceding
chunks. This resulted in "Unable to find workflow in [filename].webp"
errors for valid WEBP files.
## Solution
Add the padding byte to the offset calculation:
```typescript
offset += 8 + chunk_length + (chunk_length % 2)
```
## Testing
- Tested with the sample image provided in the issue which previously
failed to load
Fixes#8076
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8527-fix-handle-RIFF-padding-for-odd-sized-WEBP-chunks-2fa6d73d3650815fb849fb6a4e767162)
by [Unito](https://www.unito.io)
---------
Co-authored-by: GitHub Action <action@github.com>
Add cloud asset widget creation in `_createWidget()` using
`isAssetBrowserEligible()`
- Extract shared `createAssetWidget` factory to
`src/platform/assets/utils/`
- Refactor `useComboWidget.ts` to use the shared factory
- Add `_finalizeWidget()` helper to DRY up widget sizing/callback setup
- Pass target node's `comfyClass` and input name to Asset Browser for
correct model filtering
- Check `Comfy.Assets.UseAssetAPI` setting (matches `useComboWidget.ts`
behavior)
- Sync existing target widget value to asset widget
- Add toast notifications for asset validation errors
- Add i18n translations for invalidAsset and invalidFilename errors
Supersedes #8461 (clean rebase, no merge commits)
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8598-feat-cloud-add-asset-widget-support-for-PrimitiveNode-model-selection-2fd6d73d365081a8afa7c2e91762f11c)
by [Unito](https://www.unito.io)
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* Introduced asset widget integration for cloud-based model selection,
enabling users to browse and select assets through an improved
interface.
* Added comprehensive asset validation with enhanced error messages for
invalid assets and filenames.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
---------
Co-authored-by: Amp <amp@ampcode.com>
Co-authored-by: Subagent 5 <subagent@example.com>
Co-authored-by: GitHub Action <action@github.com>
Co-authored-by: guill <jacob.e.segal@gmail.com>
Co-authored-by: Jin Yi <jin12cc@gmail.com>
Co-authored-by: Alexander Brown <drjkl@comfy.org>
Co-authored-by: AustinMroz <austin@comfy.org>
Co-authored-by: Comfy Org PR Bot <snomiao+comfy-pr@gmail.com>
Co-authored-by: github-actions <github-actions@github.com>
Co-authored-by: Johnpaul Chiwetelu <49923152+Myestery@users.noreply.github.com>
Co-authored-by: Rizumu Ayaka <rizumu@ayaka.moe>
Co-authored-by: Kelly Yang <124ykl@gmail.com>
Co-authored-by: sno <snomiao@gmail.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
Co-authored-by: Benjamin Lu <benjaminlu1107@gmail.com>
Co-authored-by: Terry Jia <terryjia88@gmail.com>
Co-authored-by: Luke Mino-Altherr <luke@comfy.org>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
## Summary
Stabilize flaky workflows sidebar browser tests by waiting for eventual
UI state after `Save As`/overwrite operations.
## Changes
- **What**: Replace immediate assertions with retrying
`expect.poll(...)` in `browser_tests/tests/sidebar/workflows.spec.ts`
for:
- `Can save workflow as`
- `Can overwrite other workflows with save as`
## Review Focus
Verify the polling assertions are scoped to the intended eventual UI
state and do not hide real regressions.
## Screenshots (if applicable)
N/A
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8735-fix-stabilize-flaky-workflows-save-as-browser-assertions-3016d73d3650814abad1d767c0910ef6)
by [Unito](https://www.unito.io)
Updated CODEOWNERS file to include @Comfy-org/comfy_frontend_devs as an
owner for multiple paths.
## Summary
<!-- One sentence describing what changed and why. -->
## Changes
- **What**: <!-- Core functionality added/modified -->
- **Breaking**: <!-- Any breaking changes (if none, remove this line)
-->
- **Dependencies**: <!-- New dependencies (if none, remove this line)
-->
## Review Focus
<!-- Critical design decisions or edge cases that need attention -->
<!-- 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 -->
## Summary
Updates the templates modal to default to the "Getting Started" category
for new users.
## Changes
- Add `initialCategory` prop to `WorkflowTemplateSelectorDialog`
component
- Integrate `useNewUserService` in the dialog composable to detect
first-time users
- New users automatically see the "basics-getting-started" category
- Existing users continue to see "all" templates as default
- Allow explicit category override via options parameter
## Testing
- Added unit tests covering all scenarios (new user, non-new user,
undetermined, explicit override)
- 6 tests pass
Fixes COM-9146
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8599-feat-default-to-Getting-Started-category-for-new-users-in-templates-modal-2fd6d73d365081d4a5fad2abdb768269)
by [Unito](https://www.unito.io)
## Summary
Credit balance was not displayed in the user popover for personal
workspace users without an active subscription. The `displayedCredits`
computed returned `"0"` and `refreshBalance` skipped the API call when
there was no active subscription, hiding any existing balance.
## Changes
- **What**: Remove subscription-gated guards in
`CurrentUserPopoverWorkspace.vue`:
- `displayedCredits`: no longer returns early `""` / `"0"` when
subscription is null or inactive — always reads from the balance API
response
- `refreshBalance`: always fetches balance on popover open regardless of
subscription status
## Review Focus
The credits section visibility is already gated by `showCreditsSection`
(personal workspace or owner role). This change only affects what value
is displayed and whether the balance API is called — it does not change
who sees the section.
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8719-fix-show-credit-balance-for-unsubscribed-personal-workspaces-3006d73d3650812e9d70e5a8629c5f60)
by [Unito](https://www.unito.io)
## Summary
Replace all runtime `isElectron()` function calls with the build-time
`isDesktop` constant from `@/platform/distribution/types`, enabling
dead-code elimination in non-desktop builds.
## Changes
- **What**: Migrate 30 files from runtime `isElectron()` detection
(checking `window.electronAPI`) to the compile-time `isDesktop` constant
(driven by `__DISTRIBUTION__` Vite define). Remove `isElectron` from
`envUtil.ts`. Update `isNativeWindow()` to use `isDesktop`. Guard
`electronAPI()` calls behind `isDesktop` checks in stores. Update 7 test
files to use `vi.hoisted` + getter mock pattern for per-test `isDesktop`
toggling. Add `DISTRIBUTION=desktop` to `dev:electron` script.
## Review Focus
- The `electronDownloadStore.ts` now guards the top-level
`electronAPI()` call behind `isDesktop` to prevent crashes on
non-desktop builds.
- Test mocking pattern uses `vi.hoisted` with a getter to allow per-test
toggling of the `isDesktop` value.
- Pre-existing issues not addressed: `as ElectronAPI` cast in
`envUtil.ts`, `:class="[]"` in `BaseViewTemplate.vue`,
`@ts-expect-error` in `ModelLibrarySidebarTab.vue`.
- This subsumes PR #8627 and renders PR #6122 and PR #7374 obsolete.
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8710-refactor-replace-runtime-isElectron-with-build-time-isDesktop-constant-3006d73d365081c08037f0e61c2f6c77)
by [Unito](https://www.unito.io)
## Summary
Fixes reroute node styling in Vue Nodes 2.0 by hiding slot labels when
slot names are intentionally empty.
| Before | After |
|
---------------------------------------------------------------------------------------------------------------------------------------------
|
---------------------------------------------------------------------------------------------------------------------------------------------
|
| <img width="1437" height="473" alt="image"
src="https://github.com/user-attachments/assets/603f52e0-7b75-4822-8c91-0a8374cc0cb6"
/> | <img width="1350" height="493" alt="image"
src="https://github.com/user-attachments/assets/38168955-4d35-4c61-a685-a54efb44cd5d"
/> |
## Problem
Reroute nodes displayed unwanted fallback labels ("Input 0", "Output 0")
instead of appearing as minimal connection-only nodes. This happened
because:
- Reroute nodes intentionally use empty string (`""`) for slot names
- Slot components used `||` operator for fallback labels, treating `''`
as falsy
## Solution
- Add `hasNoLabel` computed property to detect when all label sources
(`label`, `localized_name`, `name`) are empty/falsy
- Derive `dotOnly` from either the existing prop OR `hasNoLabel` being
true
- When `dotOnly` is true: label text is hidden, padding removed
(`lg-slot--dot-only` class), only connection dot visible
Combined with existing `NO_TITLE` support from #7589, reroutes now
display as minimal nodes with just connection dots—matching classic
reroute appearance.
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
## Release Notes
* **Bug Fixes**
* Enhanced input and output slot label handling to automatically conceal
labels when unavailable
* Improved fallback display names for slots with more reliable naming
logic
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8574-fix-vue-nodes-hide-slot-labels-for-reroute-nodes-with-empty-names-2fc6d73d365081c38031e260402283d3)
by [Unito](https://www.unito.io)
## Summary
PricingTableWorkspace.vue was missed in #8704 which migrated all Vue
components from `import { t } from '@/i18n'` to `useI18n()` and upgraded
the lint rule to `error`. This breaks `pnpm lint` on main.
## Changes
- **What**: Removed `import { t } from '@/i18n'` and destructured `t`
from the existing `useI18n()` call. Moved `useI18n()` above static
initializers that reference `t`.
## Review Focus
The `billingCycleOptions` and `tiers` arrays call `t()` at module init
time — this is fine in `<script setup>` since `useI18n()` is called
first in the same synchronous scope.
Wire checkout attribution into GTM events and checkout POST payloads.
This updates the cloud telemetry flow so the backend team can correlate checkout events without relying on frontend cookie parsing. We now surface GA4 identity via a GTM-provided global and include attribution on both `begin_checkout` telemetry and the checkout POST body. The backend should continue to derive the Firebase UID from the auth header; the checkout POST body does not include a user ID.
GTM events pushed (unchanged list, updated payloads):
- `page_view` (page title/location/referrer as before)
- `sign_up` / `login`
- `begin_checkout` now includes:
- `user_id`, `tier`, `cycle`, `checkout_type`, `previous_tier` (if change flow)
- `ga_client_id`, `ga_session_id`, `ga_session_number`
- `gclid`, `gbraid`, `wbraid`
Backend-facing change:
- `POST /customers/cloud-subscription-checkout/:tier` now includes a JSON body with attribution fields only:
- `ga_client_id`, `ga_session_id`, `ga_session_number`
- `gclid`, `gbraid`, `wbraid`
- Backend should continue to derive the Firebase UID from the auth header.
Required GTM setup:
- Provide `window.__ga_identity__` via a GTM Custom HTML tag (after GA4/Google tag) with `{ client_id, session_id, session_number }`. The frontend reads this to populate the GA fields.
<img width="1416" height="1230" alt="image" src="https://github.com/user-attachments/assets/b77cf0ed-be69-4497-a540-86e5beb7bfac" />
## Screenshots (if applicable)
<img width="991" height="385" alt="image" src="https://github.com/user-attachments/assets/8309cd9e-5ab5-4fba-addb-2d101aaae7e9"/>
Manual Testing:
<img width="3839" height="2020" alt="image" src="https://github.com/user-attachments/assets/36901dfd-08db-4c07-97b8-a71e6783c72f"/>
<img width="2141" height="851" alt="image" src="https://github.com/user-attachments/assets/2e9f7aa4-4716-40f7-b147-1c74b0ce8067"/>
<img width="2298" height="982" alt="image" src="https://github.com/user-attachments/assets/72cbaa53-9b92-458a-8539-c987cf753b02"/>
<img width="2125" height="999" alt="image" src="https://github.com/user-attachments/assets/4b22387e-8027-4f50-be49-a410282a1adc"/>
To manually test, you will need to override api/features in devtools to also return this:
```
"gtm_container_id": "GTM-NP9JM6K7"
```
┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-8354-fix-route-gtm-through-telemetry-entrypoint-2f66d73d36508138afacdeffe835f28a) by [Unito](https://www.unito.io)
<!-- This is an auto-generated comment: release notes by coderabbit.ai -->
## Summary by CodeRabbit
* **New Features**
* Analytics expanded: page view tracking, richer auth telemetry (includes user IDs), and checkout begin events with attribution.
* Google Tag Manager support and persistent checkout attribution (GA/client/session IDs, gclid/gbraid/wbraid).
* **Chores**
* Telemetry reworked to support multiple providers via a registry with cloud-only initialization.
* Workflow module refactored for clearer exports.
* **Tests**
* Added/updated tests for attribution, telemetry, and subscription flows.
* **CI**
* New check prevents telemetry from leaking into distribution artifacts.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
## Summary
Fix all i18n `no-restricted-imports` lint warnings and upgrade rules
from `warn` to `error`.
## Changes
- **What**: Migrate Vue components from `import { t/d } from '@/i18n'`
to `const { t } = useI18n()`. Migrate non-component `.ts` files from
`useI18n()` to `import { t/d } from '@/i18n'`. Allow `st` import from
`@/i18n` in Vue components (it wraps `te`/`t` for safe fallback
translation). Remove `@deprecated` tag from `i18n.ts` global exports
(still used by `st` and non-component code). Upgrade both lint rules
from `warn` to `error`.
## Review Focus
- The `st` helper is intentionally excluded from the Vue component
restriction since it provides safe fallback translation needed for
custom node definitions.
Fixes#8701
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8704-fix-resolve-i18n-no-restricted-imports-lint-warnings-2ff6d73d365081ae84d8eb0dfef24323)
by [Unito](https://www.unito.io)
---------
Co-authored-by: Amp <amp@ampcode.com>
## Summary
Implements billing infrastructure for team workspaces, separate from
legacy personal billing.
## Changes
- **Billing abstraction**: New `useBillingContext` composable that
switches between legacy (personal) and workspace billing based on
context
- **Workspace subscription flows**: Pricing tables, plan transitions,
cancellation dialogs, and payment preview components for workspace
billing
- **Top-up credits**: Workspace-specific top-up dialog with polling for
payment confirmation
- **Workspace API**: Extended with billing endpoints (subscriptions,
invoices, payment methods, credits top-up)
- **Workspace switcher**: Now displays tier badges for each workspace
- **Subscribe polling**: Added polling mechanisms
(`useSubscribePolling`, `useTopupPolling`) for async payment flows
## Review Focus
- Billing flow correctness for workspace vs legacy contexts
- Polling timeout and error handling in payment flows
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8508-Feat-workspaces-6-billing-2f96d73d365081f69f65c1ddf369010d)
by [Unito](https://www.unito.io)
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
### Motivation
- The Assets sidebar shows a notification-style badge immediately when a
job is queued using the legacy queue, which is misleading because that
badge is intended for the newer Queue Panel V2 experience.
### Description
- Gate the Assets sidebar `iconBadge` on the `Comfy.Queue.QPOV2` setting
by importing `useSettingStore` and returning `null` when QPO V2 is
disabled; otherwise show `pendingTasks.length` as before
(`src/composables/sidebarTabs/useAssetsSidebarTab.ts`).
- Add a focused unit test that mocks the settings and queue store to
verify the badge is hidden when QPO V2 is disabled and shows the pending
count when enabled
(`src/composables/sidebarTabs/useAssetsSidebarTab.test.ts`).
- Keep the Assets component import mocked in the test to avoid
bootstrapping the full UI during unit runs.
### Testing
- Ran the new unit test with `pnpm vitest
src/composables/sidebarTabs/useAssetsSidebarTab.test.ts` and it passed
(2 tests).
- Ran type checking with `pnpm typecheck` and it completed successfully.
- Ran linting with `pnpm lint` and no errors were reported.
------
[Codex
Task](https://chatgpt.com/codex/tasks/task_e_69867f4ceaac8330b9806d5b51006a6a)
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8708-fix-hide-assets-sidebar-badge-when-legacy-queue-is-enabled-3006d73d3650818eb809de399583088e)
by [Unito](https://www.unito.io)
## Summary
Await all registerNodeDef calls in registerNodesFromDefs to prevent race
condition where lazy-loaded 3D node types (Load3D, Preview3D, SaveGLB)
are not registered in LiteGraph before workflow loading.
Replay lazily loaded extensions' beforeRegisterNodeDef hooks so that
input type modifications (e.g. Preview3D → PREVIEW_3D) are applied
correctly despite the extensions being registered mid-invocation.
Fixes the issue introduced by code splitting (#8542) where THREE.js lazy
import caused node registration to complete after workflow load.
## Screenshots (if applicable)
before
https://github.com/user-attachments/assets/370545dc-4081-4164-83ed-331a092fc690
after
https://github.com/user-attachments/assets/bf9dc887-0076-41fe-93ad-ab0bb984c5ce
## Summary
Enforce i18n import conventions via ESLint: Vue components must use
`useI18n()`, non-composable `.ts` files must use the global `t` from
`@/i18n`.
## Changes
- **What**: Two new `no-restricted-imports` rules in `eslint.config.ts`
(both `warn` severity for incremental migration). Removed the disabled
`@/i18n--to-enable` placeholder from `.oxlintrc.json`.
- `.vue` files: disallow importing `t`/`d`/`st`/`te` from `@/i18n` (37
existing violations to migrate)
- Non-composable `.ts` files: disallow importing `useI18n` from
`vue-i18n` (2 existing violations)
- Composable `use*.ts`, test files, and `src/i18n.ts` are excluded from
Rule 2
## Review Focus
- The rules are placed after `oxlint.buildFromOxlintConfigFile()` to
re-enable ESLint's `no-restricted-imports` for these specific file
scopes without conflicting with oxlint's base rule (which handles
PrimeVue deprecations).
- `warn` severity chosen so CI is not blocked while existing violations
are migrated.
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8701-feat-enforce-i18n-import-conventions-via-ESLint-2ff6d73d36508123b6f9edf2693fb5e0)
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
Vue node slots extend outside the bounds of the node:
<img width="123" height="107" alt="image"
src="https://github.com/user-attachments/assets/96f7f28b-de54-4978-bc78-f38fc1fd4ea1"
/>
When clicking on the outer half of the slot, the matching node is not
found as the click was technically not over a node, however in reality
the action should still be associated with the node the slot is for.
This specifically fixes middle click to add reroute not working on the
outer half of the slot.
## Changes
- **What**:
- If the event is not over a node, check if is over a Vue slot, if so,
use the node associated with that slot.
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8609-Fix-hit-detection-on-vue-node-slots-2fd6d73d3650815c8328f9ea8fa66b0c)
by [Unito](https://www.unito.io)
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Tests**
* Added comprehensive test suite for slot hit-detection in Vue nodes
mode, covering standard and fallback interaction paths.
* **Bug Fixes**
* Improved hit-detection accuracy for slots that extend beyond node
boundaries in Vue mode, ensuring clicks map to the correct node.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
Under a combination of many edge cases, the `widget_values` migration
code added in #3326 would cause the progress text on a "Recraft Text to
Image" node to incorrectly deserialize into the `control_after_generate`
- widgets_values is of length 1 greater than it should be because
progress text serializes
- It should not, there is no code to deserialize it
- negative_prompt has force_input set and skips serialization
- Migration only applies when `widgets_values` is equal to actual inputs
length. The two above edge cases cancel to make this true
- Seed is accounted for when calculating the length of widgets, but not
when applying the migration
- Migration occurs even though we track workflow version now and have an
accurate way of determining that it can not be needed
The two primary edge cases which cause the bug are both addressed
- `options.serialize` does nothing and has never done anything. I've
been guilty of making the same mistake in the ancient past, and want to
clean up the misconception where I can.
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8625-Fix-incorrect-widgetValue-migration-2fe6d73d365081a683b4c675eaeebb6c)
by [Unito](https://www.unito.io)
## Summary
- `getAssetUrl()` was constructing `/view` URLs without the `subfolder`
query parameter, causing backend to return "file not found" for assets
stored in subfolders (common for audio/video outputs)
- Preview/playback was unaffected because it uses `preview_url` from
`ResultItemImpl.url` which correctly includes `subfolder`
- Fixed `getAssetUrl()` to include `subfolder` from
`asset.user_metadata` when present
- Simplified download functions to prefer `preview_url` (already
correct) with `getAssetUrl` as fallback
## Test plan
- [ ] Generate audio/video output (e.g. via SaveAudio node) that saves
to a subfolder
- [ ] Right-click the asset in the assets sidebar and click Download —
should download successfully
- [ ] Select multiple audio/video assets and use bulk download — should
download all
- [ ] Verify image downloads still work as before
- [ ] Verify cloud environment downloads still work (uses `preview_url`
path)
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
## Release Notes
* **New Features**
* Added support for organizing and downloading assets from subfolders.
* **Refactor**
* Improved asset URL generation and download handling for better
reliability and performance.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
## Summary
Increase stroke/outline weight for active node states to improve
visibility during workflow execution (COM-7770).
## Changes
- **What**: Increased border/stroke weight from 2 to 3 for active nodes
in both Vue Nodes and LiteGraph renderers
- Vue Nodes: `outline-2` → `outline-3` in `LGraphNode.vue`
- LiteGraph: `lineWidth: 3` for `running` stroke (was default 1) and
`executionError` stroke (was 2) in `litegraphService.ts`
- Updated test assertion to match
## Review Focus
Minimal visual change. The `executionError` lineWidth was also bumped
from 2 → 3 so error states remain at least as prominent as running
states.
> **Note:**
[#8603](https://github.com/Comfy-Org/ComfyUI_frontend/pull/8603) (by
@AustinMroz) also modifies `LGraphNode.vue` with a larger restructuring
(bottom buttons, badges, resize handle). The two PRs do not conflict —
#8603 does not touch the outline/border state classes or
`litegraphService.ts`, so both changes merge cleanly.
---------
Co-authored-by: Alexander Brown <drjkl@comfy.org>
Co-authored-by: github-actions <github-actions@github.com>
## Summary
Replace the manager button puzzle icon with a custom extensions-blocks
SVG icon and add a "Manage Extensions" text label to the top bar button.
## Changes
- **What**: Swap `icon-[lucide--puzzle]` →
`icon-[comfy--extensions-blocks]` in TopMenuSection, ComfyMenuButton,
and ManagerDialog. Add visible "Manage Extensions" label (hidden below
md). Align tooltip with new label text.
## Review Focus
- Visual appearance of the new icon at different sizes
- Button layout with text label at md+ breakpoints
- Red dot notification positioning with wider button
Fixes COM-12161
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8644-feat-replace-puzzle-icon-with-extensions-blocks-icon-for-manager-button-2fe6d73d3650815c8867efc5a0842ef7)
by [Unito](https://www.unito.io)
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
- Adds `--grep @screenshot` to the Playwright command in the
update-snapshots CI workflow
- Skips ~146 non-screenshot tests that don't produce any snapshot files,
reducing CI time and resource usage
## Details
All tests that call `toHaveScreenshot` are already tagged with
`@screenshot` (either at the `test.describe` or individual `test`
level). The snapshot update job was previously running every test
unnecessarily.
The `--grep` CLI flag is ANDed with each project's existing
`grep`/`grepInvert` settings, so all projects continue to work
correctly:
- `chromium`: `@screenshot` AND NOT `@mobile`
- `chromium-2x`: `@screenshot` AND `@2x`
- `mobile-chrome`: `@screenshot` AND `@mobile`
## Test plan
- [x] Trigger the update-snapshots workflow on a PR with the "New
Browser Test Expectations" label and verify only screenshot-tagged tests
run
- [x] Verify snapshot files are still correctly updated
#7591 added a one tick delay to layout initialization in an attempt to
resolve some layouting discrepancies. However, it appears to have
reintroduced node scaling issues and introduced a new bug that prevents
cloning nodes with alt+drag in vue.
Alternatives methods of resolving the original issue are being
investigated, but this change was causing more harm than good.
The prior PR included other changes (like a testing fix). Those changes
remain beneficial and do not need to be reverted.
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8619-Revert-delay-of-layout-initialization-2fe6d73d365081fc9111c9457ea9752d)
by [Unito](https://www.unito.io)
---------
Co-authored-by: github-actions <github-actions@github.com>
## Summary
- Switches from reference-based to content-based duplicate detection for
context menu items
- Fixes cases where extensions create duplicate menu items (different
objects with same content)
- Improves removal detection accuracy by comparing content strings
instead of object references
## Details
The previous implementation compared menu items by object reference,
which would miss duplicates when extensions added new objects with the
same content. This change:
- Creates Sets of content strings from menu items for efficient
duplicate detection
- Filters additions by checking if their content already exists
- Provides accurate count of removed items through content comparison
## Test plan
- [x] Unit tests pass (`pnpm test:unit
src/lib/litegraph/src/contextMenuCompat.test.ts`)
- [x] TypeScript compilation succeeds (`pnpm typecheck`)
- [x] Linting passes (`pnpm lint`)
- [x] Pre-commit hooks pass
## Before
<img width="633" height="1316" alt="Screenshot 2026-02-04 045422"
src="https://github.com/user-attachments/assets/972871e2-1fd6-45a4-bb6c-9ce73ce7aed7"
/>
## After
<img width="531" height="854" alt="Screenshot 2026-02-04 045918"
src="https://github.com/user-attachments/assets/977bed37-dfb8-41d7-b659-88c477ff8a02"
/>
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Bug Fixes**
* Improved context menu compatibility: more accurate detection of
added/removed menu items and clearer warnings when items are removed.
* **Refactor**
* Updated numeric input formatting to use locale-aware formatting logic,
preserving grouping and precision behavior.
* **Tests**
* Added a test ensuring legacy menu extraction handles items with
undefined content correctly.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8602-fix-prevent-duplicate-context-menu-items-by-using-content-based-comparison-2fd6d73d36508197aa74c3409c7425fa)
by [Unito](https://www.unito.io)
---------
Co-authored-by: Terry Jia <terryjia88@gmail.com>
## Summary
- add commands for setting search aliases and description when in
subgraph
- in future we can add these fields to the dialog when publishing a
subgraph
- map workflow extra metadata on save/load from from/to subgraph node to
allow access via `canvas.subgraph.extra`
## Changes
**What**:
- new core commands for Comfy.Subgraph.SetSearchAliases &
Comfy.Subgraph.SetDescription to be called when in a subgraph context
- update Publish command to allow command metadata arg for name
- update test executeCommand to allow passing metadata arg
## Review Focus
- When saving a subgraph, the outer workflow "wrapper" is created at the
point of publishing. So unlike a normal workflow `extra` property that
is available at any point, for a subgraph this is not accessible.
To workaround this, the `extra` property that exists on the inner
subgraph node is copied to the top level on save, and restored on load
so extra properties can be set via `canvas.subgraph.extra`.
- I have kept the existing naming format matching `BlueprintDescription`
for `BlueprintSearchAliases` but i'm not sure if the description was
ever used before
## Screenshots
https://github.com/user-attachments/assets/4d4df9c1-2281-4589-aa56-ab07cdecd353
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8608-Add-support-for-search-aliases-on-subgraphs-2fd6d73d365081d083caebd6befcacdd)
by [Unito](https://www.unito.io)
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* Set subgraph search aliases (comma-separated) and descriptions;
aliases enable discovery by alternative names.
* Publish subgraphs using a provided name.
* Node definitions now support search aliases so nodes can be found by
alternate names.
* UI strings added for entering descriptions and search aliases.
* **Tests**
* Added end-to-end and unit tests covering aliases, descriptions, search
behavior, publishing, and persistence.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
Removed background and theme colors from manifest.
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Chores**
* Removed custom color theme settings from the app's configuration. The
app's display mode and all other properties remain unchanged.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
## Summary
Fixes multiple Safari-specific issues in the Secrets panel dialog:
1. **Dropdown not opening** - Safari has issues with click events on
portaled content inside dialogs. Added `disablePortal` prop to
`SelectContent` to render content inline.
2. **Close button focused on open** - Added `autofocus` to
`SelectTrigger` so focus goes to the first form field.
3. **Buttons not focusable** - Safari doesn't focus buttons on click by
default. Added `tabindex="0"` to Cancel/Save buttons.
## Changes
- `SelectContent.vue`: Added `disablePortal` prop with explanatory
comment linking to upstream issue
- `SecretFormDialog.vue`: Applied Safari workarounds
## References
- https://github.com/chakra-ui/ark/issues/1782 (Portal issue in Safari)
- https://mayank.co/blog/safari-focus/ (Button focus behavior)
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* Added option to disable portal rendering in select components for
greater control over component behavior.
* **Bug Fixes**
* Improved keyboard accessibility in form dialogs with enhanced focus
management, auto-focus on select triggers, and optimized tab navigation
for action buttons.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
Co-authored-by: Amp <amp@ampcode.com>
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)
## Summary
Forward wheel events to the canvas unless a wheel-capturing element is
focused, and ensure the Load3D scene becomes focusable on pointer
interaction so its wheel zoom/pan works after the user clicks into it.
## Changes
- **What**: gate wheel forwarding on focused capture elements; focus the
Load3D scene container on pointerdown to opt into wheel capture.
- **Dependencies**: none
## Review Focus
- Validate wheel forwarding behavior across focusable inputs vs.
non-focusable capture zones.
- Confirm Load3D zoom/pan only captures wheel after a user click (canvas
pan should still work when merely hovering).
## Screenshots (if applicable)
N/A
---------
Co-authored-by: Christian Byrne <cbyrne@comfy.org>
Co-authored-by: Subagent 5 <subagent@example.com>
Co-authored-by: Amp <amp@ampcode.com>
## Summary
Prevents stale API responses from overwriting fresh queue state during
rapid consecutive `update()` calls.
## Problem
When `queueStore.update()` is called multiple times in quick succession
(e.g., during websocket reconnection), responses could resolve
out-of-order. A stale response resolving after a fresh one would
overwrite the current state with outdated data.
## Solution
Added request ID tracking that:
1. Increments a counter on each `update()` call
2. Guards all state mutations with a staleness check
3. Only sets `isLoading=false` for the latest request
This pattern matches the existing approach in `jobOutputCache.ts`.
## Changes
- `src/stores/queueStore.ts`: Added `updateRequestId` counter and
staleness guards
- `src/stores/queueStore.test.ts`: Added tests for deduplication
behavior
## Testing
- All 49 existing tests pass
- Added 3 new tests for race condition handling:
- Stale responses are discarded
- isLoading state is correctly managed
- Stale request failures don't affect latest state
## Fixes COM-12784
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8523-fix-dedupe-queueStore-update-to-prevent-race-conditions-2fa6d73d3650810daa0dc63af359e1ea)
by [Unito](https://www.unito.io)
---------
Co-authored-by: Amp <amp@ampcode.com>
## Summary
Add support for overlaying provider logos on workflow template
thumbnails at runtime.
## Changes
- **What**:
- Add `LogoInfo` interface and `logos` field to `TemplateInfo` type
- Create `LogoOverlay.vue` component for rendering positioned logos
- Fetch logo index from `templates/index_logo.json` in store
- Add `getLogoUrl` helper to `useTemplateWorkflows` composable
- Integrate `LogoOverlay` into `WorkflowTemplateSelectorDialog`
## Review Focus
- Logo positioning uses Tailwind classes (e.g. `absolute bottom-2
right-2`)
- Supports multiple logos per template with configurable size/opacity
- Gracefully handles missing logos (returns empty string, renders
nothing)
- Templates must explicitly declare logos - no magic inference from
models
## Dependencies
Requires separate PR in workflow_templates repo to:
1. Update `index.schema.json` with logos definition
2. Add `logos` field to templates in `index.json`
## Screenshots (if applicable)
<img width="869" height="719" alt="image"
src="https://github.com/user-attachments/assets/65ed1ee4-fbb4-42c9-95d4-7e37813b3655"
/>
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8365-feat-add-provider-logo-overlays-to-workflow-template-thumbnails-2f66d73d365081309236c6b991cb6f7b)
by [Unito](https://www.unito.io)
---------
Co-authored-by: Subagent 5 <subagent@example.com>
Co-authored-by: Amp <amp@ampcode.com>
## Summary
Reverts PR #5216 delete account functionality. The delete button only
removed Firebase accounts without canceling Stripe subscriptions,
causing orphaned accounts and support issues.
## Changes
- Removed delete account button from UserPanel.vue
- Added text directing users to contact support@comfy.org for account
deletion (clickable mailto: link)
- Cleaned up related code: removed `handleDeleteAccount` from
useCurrentUser.ts, `deleteAccount` from useFirebaseAuthActions.ts,
`_deleteAccount` from firebaseAuthStore.ts
- Updated en/main.json locale with `contactSupport` key using {email}
placeholder
## Testing
- Typecheck and lint pass
- Manual verification: user settings panel shows contact support text
instead of delete button
## Related Issues
Fixes COM-14243
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8515-fix-remove-delete-account-button-and-direct-users-to-support-2fa6d73d3650819dbc83efb41c07a809)
by [Unito](https://www.unito.io)
Co-authored-by: Amp <amp@ampcode.com>
## Summary
Disables the legacy "node templates" feature on ComfyUI Cloud. This
feature is accessed via right-clicking on the canvas and is distinct
from both subgraphs and workflow templates.
## Problem
The legacy node templates feature presents false-positive errors on
cloud:
- Errors appear when no existing templates exist initially
- After refresh, workflow progress is lost from workflow tabs
- Node templates fail to delete (except the first one)
## Solution
Conditionally load the `nodeTemplates` extension only for non-cloud
builds by wrapping the import in `if (!isCloud)`.
## Testing
- [ ] Verify right-click canvas menu no longer shows "Node Templates"
submenu on cloud
- [ ] Verify right-click canvas menu no longer shows "Save Selected as
Template" on cloud
- [ ] Verify the feature still works on desktop/OSS builds
## Related
- Refs: COM-14105
- Related issue: #4056 (Migrate Node Templates to Workflows)
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8462-fix-cloud-disable-legacy-node-templates-feature-on-cloud-2f86d73d36508162948cc897d4c7ef5e)
by [Unito](https://www.unito.io)
Co-authored-by: Amp <amp@ampcode.com>
## Summary
Fix for COM-14110: Preview image does not display new outputs in
vue-nodes.
## Problem
The merge logic in `setOutputsByLocatorId` updated `app.nodeOutputs` but
returned early without updating the reactive `nodeOutputs.value` ref.
This caused Vue components to never receive merged output updates
because only the non-reactive `app.nodeOutputs` was being updated.
## Solution
Added `nodeOutputs.value[nodeLocatorId] = existingOutput` after the
merge loop, before the return statement.
## Testing
- Added 2 unit tests covering the merge behavior
- All 4076 existing unit tests pass
- Typechecks pass
- Lint passes
## Notes
- Related open PRs touching same files: #8143, #8366 - potential minor
conflicts possible
Fixes COM-14110
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8479-fix-update-reactive-ref-after-merge-in-imagePreviewStore-2f86d73d365081f1a145fa5a9782515f)
by [Unito](https://www.unito.io)
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 improper `as unknown as Type` patterns from test files in
Group 8 part 8
- Replaced with proper TypeScript patterns using Pinia store testing
patterns
- Fixed parameter shadowing issue in typeGuardUtil.test.ts (constructor
→ nodeConstructor)
- Fixed stale mock values in useConflictDetection.test.ts using getter
functions
- Refactored useManagerState tests to follow proper Pinia store testing
patterns with createTestingPinia
### Files Changed
Test files (Group 8 part 8 - utils and manager composables):
- src/utils/typeGuardUtil.test.ts - Fixed parameter shadowing
- src/utils/graphTraversalUtil.test.ts - Removed unsafe type assertions
- src/utils/litegraphUtil.test.ts - Improved type handling
- src/workbench/extensions/manager/composables/useManagerState.test.ts -
Complete rewrite using Pinia testing patterns
-
src/workbench/extensions/manager/composables/useConflictDetection.test.ts
- Fixed stale mock values with getters
- src/workbench/extensions/manager/composables/useManagerQueue.test.ts -
Type safety improvements
-
src/workbench/extensions/manager/composables/nodePack/useMissingNodes.test.ts
- Removed unsafe casts
-
src/workbench/extensions/manager/composables/nodePack/usePacksSelection.test.ts
- Type improvements
-
src/workbench/extensions/manager/composables/nodePack/usePacksStatus.test.ts
- Type improvements
- src/workbench/extensions/manager/utils/versionUtil.test.ts - Type
safety fixes
Source files (minor type fixes):
- src/utils/fuseUtil.ts - Type improvements
- src/utils/linkFixer.ts - Type safety fixes
- src/utils/syncUtil.ts - Type improvements
-
src/workbench/extensions/manager/composables/nodePack/useWorkflowPacks.ts
- Type fix
-
src/workbench/extensions/manager/composables/useConflictAcknowledgment.ts
- Type fix
### Testing
- All TypeScript type checking passes (`pnpm typecheck`)
- All affected test files pass (`pnpm test:unit`)
- Linting passes without errors (`pnpm lint`)
- Code formatting applied (`pnpm format`)
Part of the "Road to No Explicit Any" initiative, cleaning up type
casting issues from branch `fix/remove-any-types-part8`.
### 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 (this PR)
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8496-Road-to-No-explicit-any-Group-8-part-8-test-files-2f86d73d365081f3afdcf8d01fba81e1)
by [Unito](https://www.unito.io)
---------
Co-authored-by: GitHub Action <action@github.com>
When bypassing or muting a subgraph the contents are no longer bypassed
along with it. This behaviour was less than ideal because it meant that
toggling the bypass of a single subgraph node twice could change the
behaviour of the node.
It is entirely intended that a subgraph node which is bypassed does not
have it's children execute. As part of testing this behaviour, it was
found that nodes inside of a bypassed subgraph are still considered for
execution even if boundry links are treated as disconnected. The
following example would execute even if the subgraph is muted.
<img width="826" height="476" alt="image"
src="https://github.com/user-attachments/assets/7b282873-e114-494d-b8f1-74c373859151"
/>
To resolve this, the PR does not add the contents of a subgraphNode
which is muted or bypassed to the execution map.
Resolves#8489
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8494-Don-t-bypass-subgraph-contents-with-subgraph-2f86d73d365081aeba8dd2990b6ba0ad)
by [Unito](https://www.unito.io)
## Summary
Add `PR_GH_TOKEN` to ci-lint-format and i18n-update-core workflows to
ensure commits trigger e2e test runs.
## Changes
- Add `token: ${{ secrets.PR_GH_TOKEN }}` to checkout step in
`.github/workflows/ci-lint-format.yaml`
- Add `token: ${{ secrets.PR_GH_TOKEN }}` to checkout step in
`.github/workflows/i18n-update-core.yaml`
## Context
This matches the pattern used consistently across all other workflows in
the repository (release-version-bump, version-bump-desktop-ui,
api-update-registry-api-types, i18n-update-nodes, etc.).
Without this token, commits made by these workflows don't trigger
downstream e2e tests, which can lead to issues being missed.
## Original Work
Original idea & implementation by @drjkl in commits:
- be3a8d61c - fix: use GitHub App token for lint/i18n workflows to
trigger e2e tests
- 50ccb254b - Add github app token action to pinact
This PR simplifies the approach to use the existing `PR_GH_TOKEN` secret
instead of GitHub App tokens, for consistency with the rest of the
repository.
## Test Plan
- [x] Verify workflows can checkout code successfully
- [x] Confirm commits from these workflows trigger e2e test runs
## It works!
<img width="1075" height="402" alt="image"
src="https://github.com/user-attachments/assets/31762d68-a37f-4926-b463-84d184d4309d"
/>
---------
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
Co-authored-by: GitHub Action <action@github.com>
## Summary
Fixes a bug where socket map data was not properly removed when sockets
are dynamically added/removed via DynamicCombo widgets in Vue mode
(Nodes 2.0).
## Problem
When DynamicCombo widgets (e.g., `should_remesh` on Meshy nodes) change
their selection, inputs are dynamically added/removed. The Vue `v-for`
loop in `NodeSlots.vue` was using array index as the `:key`, causing Vue
to **reuse** slot components instead of properly unmounting them.
This led to:
- Socket map entries leaking (never cleaned up)
- Socket positions becoming desynced
- Stale cached offset data
## Solution
1. **Use slot `name` as Vue key** instead of array index in
`NodeSlots.vue`
- Slot names are unique per node (enforced by ComfyUI backend)
- When a slot is removed, Vue sees the key disappear and properly
unmounts the component
- `onUnmounted` cleanup in `useSlotElementTracking` now runs correctly
2. **Add defensive cleanup** in `useSlotElementTracking.ts`
- Before registering a new slot, check if a stale entry exists with the
same key
- Clean up stale entry to handle any edge cases
## Related
- Fixes COM-12970
- Related to #7837 (fixed LiteGraph version of this bug, but not Vue
mode)
## Testing
- Quality checks pass (typecheck, lint, format)
- Manual testing with DynamicCombo nodes (Meshy, nodes_logic)
recommended
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8469-fix-Vue-mode-socket-map-data-not-cleaned-up-on-dynamic-input-changes-2f86d73d365081e599eadca4f15e6b6e)
by [Unito](https://www.unito.io)
---------
Co-authored-by: Subagent 5 <subagent@example.com>
Co-authored-by: Amp <amp@ampcode.com>
## Summary
Improve Template search input performance issue #8134
This was caused by the search logic running too frequently (throttled at
50ms), causing the main thread to block on every few keystrokes.
## Changes
Use debouncing that wait until you stop typing for a specific time
(300ms) before running.
It makes the searching function more smoothly.
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8343-Improve-template-search-input-performance-issue-2f56d73d36508144bdf9fa5e0cd76818)
by [Unito](https://www.unito.io)
---------
Co-authored-by: Alexander Brown <drjkl@comfy.org>
## 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 improper `as unknown as Type` patterns from 17 test files in
Group 8 part 7
- Replaced with proper TypeScript patterns using factory functions and
Mock types
- Fixed createTestingPinia usage in test files (was incorrectly using
createPinia)
- Fixed vi.hoisted pattern for mockSetDirty in viewport tests
- Fixed vi.doMock lint issues with vi.mock and vi.hoisted pattern
- Retained necessary `as unknown as` casts only for complex mock objects
where direct type assertions would fail
### Files Changed
Test files (Group 8 part 7 - services, stores, utils):
- src/services/nodeOrganizationService.test.ts
- src/services/providers/algoliaSearchProvider.test.ts
- src/services/providers/registrySearchProvider.test.ts
- src/stores/comfyRegistryStore.test.ts
- src/stores/domWidgetStore.test.ts
- src/stores/executionStore.test.ts
- src/stores/firebaseAuthStore.test.ts
- src/stores/modelToNodeStore.test.ts
- src/stores/queueStore.test.ts
- src/stores/subgraphNavigationStore.test.ts
- src/stores/subgraphNavigationStore.viewport.test.ts
- src/stores/subgraphStore.test.ts
- src/stores/systemStatsStore.test.ts
- src/stores/workspace/nodeHelpStore.test.ts
- src/utils/colorUtil.test.ts
- src/utils/executableGroupNodeChildDTO.test.ts
Source files:
- src/stores/modelStore.ts - Improved type handling
### Testing
- All TypeScript type checking passes (`pnpm typecheck`)
- All affected test files pass (`pnpm test:unit`)
- Linting passes without errors (`pnpm lint`)
- Code formatting applied (`pnpm format`)
Part of the "Road to No Explicit Any" initiative, cleaning up type
casting issues from branch `fix/remove-any-types-part8`.
### 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 (this PR)
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8459-Road-to-No-explicit-any-Group-8-part-7-test-files-2f86d73d36508114ad28d82e72a3a5e9)
by [Unito](https://www.unito.io)
## Summary
Migrates ECMAScript private fields (`#`) to TypeScript private
(`private`) across LiteGraph to fix Vue Proxy reactivity
incompatibility.
## Problem
ES private fields (`#field`) are incompatible with Vue's Proxy-based
reactivity system - accessing `#field` through a Proxy throws
`TypeError: Cannot read private member from an object whose class did
not declare it`.
## Solution
- Converted all `#field` to `private _field` across 10 phases
- Added `toJSON()` methods to `LGraph`, `NodeSlot`, `NodeInputSlot`, and
`NodeOutputSlot` to prevent circular reference errors during
serialization (TypeScript private fields are visible to `JSON.stringify`
unlike true ES private fields)
- Made `DragAndScale.element.data` non-enumerable to break canvas
circular reference chain
## Testing
- All 4027 unit tests pass
- Added 9 new serialization tests to catch future circular reference
issues
- Browser tests (undo/redo, save workflows) verified working
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8440-refactor-migrate-ES-private-fields-to-TypeScript-private-for-Vue-Proxy-compatibility-2f76d73d365081a3bd82d429a3e0fcb7)
by [Unito](https://www.unito.io)
---------
Co-authored-by: Amp <amp@ampcode.com>
## Summary
Adds support for creating Chatterbox TTS nodes when clicking Chatterbox
models in the Cloud asset browser.
## Changes
### modelToNodeStore.ts
- Add `findProvidersWithFallback()` helper for hierarchical model type
lookups (e.g., `parent/child` falls back to `parent`)
- Register 4 Chatterbox model directories with empty widget keys:
- `chatterbox/chatterbox` → `FL_ChatterboxTTS`
- `chatterbox/chatterbox_turbo` → `FL_ChatterboxTurboTTS`
- `chatterbox/chatterbox_multilingual` → `FL_ChatterboxMultilingualTTS`
- `chatterbox/chatterbox_vc` → `FL_ChatterboxVC`
### createModelNodeFromAsset.ts
- Skip widget assignment when `provider.key` is empty (for nodes that
auto-load models without a widget selector)
### Tests
- Add tests for hierarchical fallback behavior
- Add tests for empty widget key (auto-load nodes)
- Add Chatterbox node types to mock data
## Notes
- Empty `key` convention: Chatterbox nodes auto-load their models and
don't have a model selector widget, so we register them with `key: ''`
and skip the widget assignment step
- Hierarchical fallback only goes one level deep (`a/b/c` → `a`, not
`a/b`)
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8418-feat-add-Chatterbox-model-support-for-Cloud-asset-browser-2f76d73d365081be822bc369b155f099)
by [Unito](https://www.unito.io)
---------
Co-authored-by: Subagent 5 <subagent@example.com>
Co-authored-by: Amp <amp@ampcode.com>
Co-authored-by: Alexander Brown <drjkl@comfy.org>
Co-authored-by: GitHub Action <action@github.com>
Previously the save keybind action would
- apply the new keybind
- wait for a network request to persist the change
- close the dialogue regardless of the results of the above changes
During this network request, the dialog would show a warning that the
keybind is invalid because the dialogue "contains a modified keybind
which conflicts with an existing keybind"
<img width="506" height="261" alt="image"
src="https://github.com/user-attachments/assets/e46150ce-9349-4f8e-b3b5-fb0b20dd3db9"
/>
This PR changes the order these actions are applied in.
- The dialogue is immediately closed
- The keybinding is updated if valid
- The keybinding is persisted.
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8435-Fix-invalid-keybind-flash-2f76d73d3650815c9657f35e77d331fe)
by [Unito](https://www.unito.io)
## Summary
Fix shift+click range selection not properly deselecting assets when
selecting a smaller range, and improve selection performance.
## Changes
- **Bug Fix**: Shift+click now replaces selection with the new range
instead of combining with existing selection
- **Performance**: Remove unnecessary `.every()` check in `setSelection`
(O(n) → O(1))
- **Tests**: Add 23 unit tests for asset selection logic
## Test Plan
- [x] Click 1st asset → only 1st selected
- [x] Shift+click 3rd asset → items 1-3 selected
- [x] Shift+click 1st asset → only 1st selected (was broken, now fixed)
- [x] All 23 new unit tests pass
🤖 Generated with [Claude Code](https://claude.com/claude-code)
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8396-bugfix-Fix-shift-click-deselection-in-asset-panel-2f76d73d3650814ca060d1e6a40cf6d4)
by [Unito](https://www.unito.io)
## Summary
When removing a SubgraphNode via `LGraph.remove()`:
- Fire `onRemoved` for all nodes inside the subgraph
- Fire `onNodeRemoved` callback on the subgraph for each inner node
- Remove subgraph definition from `rootGraph.subgraphs` when no other
nodes reference it (checks both root graph nodes and nodes inside other
subgraphs)
This ensures proper cleanup of subgraph definitions and lifecycle
callbacks for nested nodes when subgraph nodes are deleted.
## Changes
### LGraph.ts
Added SubgraphNode-specific cleanup in `remove()` method that:
1. Iterates inner nodes and fires their `onRemoved` callbacks
2. Fires `onNodeRemoved` on the subgraph for downstream listeners (e.g.,
minimap)
3. Garbage collects the subgraph definition when no other nodes
reference it
### SubgraphNode.ts
Fixed `graph` property to match `LGraphNode` lifecycle contract.
Previously it was declared as `override readonly graph` via constructor
parameter promotion, which prevented `LGraph.remove()` from setting
`node.graph = null`. Changed to a regular mutable property with null
guard in `rootGraph` getter.
### LGraph.test.ts
Added 4 tests:
- `removing SubgraphNode fires onRemoved for inner nodes`
- `removing SubgraphNode fires onNodeRemoved callback`
- `subgraph definition is removed when last referencing node is removed`
- `subgraph definition is retained when other nodes still reference it`
## Related
- Fixes#8145
- Part of the subgraph lifecycle cleanup plan (Slice 2: Definition
garbage collection)
## Summary
Shows tab-specific empty state messages in Node Manager instead of
generic "No search results found" message.
## Changes
- Added computed properties to determine empty state messages based on
current tab and search state
- Display tab-specific messages when a tab is empty without active
search (e.g., "No Missing Nodes" for Missing tab)
- Fall back to search-related messages only when there's an active
search query
- Added Korean translations for empty state messages
| Tab | Empty State Title |
|-----|-------------------|
| All Installed | No Extensions Installed |
| Update Available | All Up to Date |
| Conflicting | No Conflicts Detected |
| Workflow | No Extensions in Workflow |
| Missing | No Missing Nodes |
## Review Focus
- Verify i18n key structure matches existing patterns
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8415-feat-Show-context-appropriate-empty-state-messages-in-Manager-tabs-2f76d73d3650817ab8a0d41b45df3411)
by [Unito](https://www.unito.io)
## Summary
Refactors the model assets cache in `assetsStore.ts` to be keyed by
category (e.g., 'checkpoints', 'loras') instead of nodeType (e.g.,
'CheckpointLoaderSimple').
## Changes
- Rename `modelStateByKey` to `modelStateByCategory`
- Add `resolveCategory()` helper to translate nodeType to category for
cache lookup
- Multiple node types sharing the same category now share one cache
entry
- Add `invalidateCategory()` method for cache invalidation
- Maintain backwards-compatible public API accepting nodeType
- Update tests for new category-keyed behavior
## Benefits
1. **Deduplication**: Same category = same cache entry = single API call
2. **Simple invalidation**: Delete asset with tag 'checkpoints' then
invalidate cache
3. **Cleaner mental model**: Store to View reactive flow works naturally
## Testing
- All existing tests pass with updates
- Added new tests for category-keyed cache sharing, invalidateCategory,
and unknown node type handling
## Part of Stack
This is **PR 1 of 2** in a stacked PR series:
1. **This PR**: Refactor asset cache to category-keyed (architectural
improvement)
2. **[PR 2
#8434](https://github.com/Comfy-Org/ComfyUI_frontend/pull/8434)**: Fix
deletion invalidation
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8433-refactor-change-asset-cache-from-nodeType-keyed-to-category-keyed-2f76d73d365081999b7fda12c9706ab5)
by [Unito](https://www.unito.io)
---------
Co-authored-by: Subagent 5 <subagent@example.com>
Co-authored-by: Amp <amp@ampcode.com>
Co-authored-by: GitHub Action <action@github.com>
The image input for nodes loaded from templates appears empty in the
Properties Panel.
When the widget's current value (saved in the template) is not in the
available file list returned by the server, the selectedSet is empty,
causing a placeholder to be displayed instead of the actual value.
Added a missingValueItem computed property in WidgetSelectDropdown.vue.
When the current value is not in inputItems or outputItems, it creates a
fallback item and adds it to dropdownItems.
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8276-fix-default-image-input-for-the-template-is-displayed-as-empty-on-dropdown-selection-2f16d73d3650817eaad5e4e33637fb74)
by [Unito](https://www.unito.io)
---------
Co-authored-by: Alexander Brown <drjkl@comfy.org>
Co-authored-by: github-actions <github-actions@github.com>
## Summary
Fixes the snapshot merge failure introduced by PR #8377
(actions/download-artifact v4→v7 upgrade).
## Root Cause
The v5+ release of `download-artifact` changed behavior: when a
`pattern` matches only a **single artifact**, files are extracted
directly to `path/` without the artifact name subdirectory. When only
one shard had changes, the merge loop couldn't find the expected
`snapshots-shard-*/` directories.
## Fix
Use `merge-multiple: true` — the documented pattern for combining
sharded artifacts. This merges all matched artifacts directly into the
target path, eliminating directory structure assumptions.
## Testing
This fix can be validated by re-running the workflow on [PR
#8276](https://github.com/Comfy-Org/ComfyUI_frontend/pull/8276) after
merge.
---
- Fixes snapshot update workflow regression from #8377
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8432-fix-use-merge-multiple-for-snapshot-artifact-download-2f76d73d3650810b97fdfe28cd3c7694)
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
Prevent re-installing an already installed version by disabling the
Install button and the version option in the selector.
## Changes
- Add `isVersionInstalled()` function to check if a version is already
installed
- Add `isInstallDisabled` computed to disable Install button when
selected version is installed
- Add `option-disabled="isInstalled"` to Listbox to prevent selecting
installed versions
Fixes the issue where users could trigger duplicate install operations
by selecting the currently installed version.
🤖 Generated with [Claude Code](https://claude.ai/code)
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8412-bugfix-Disable-install-button-when-already-installed-version-is-selected-2f76d73d365081cb859ff98a3d1c64e6)
by [Unito](https://www.unito.io)
## Summary
This PR adds two related features for subgraph blueprints:
### 1. Protect Global Blueprints from Deletion
- Added `isGlobalBlueprint()` helper that distinguishes blueprints by
`python_module` field
- User blueprints have `python_module: 'blueprint'`
- Global (installed) blueprints have `python_module` set to the node
pack name
- Guarded `deleteBlueprint()` to show a warning toast for global
blueprints
- Hidden delete menu in node library for global blueprints
### 2. Category Support for Blueprints
- User blueprints now use category `Subgraph Blueprints/User`
- Global blueprints use `Subgraph Blueprints/{category}` if category is
provided, otherwise `Subgraph Blueprints`
- Extended `GlobalSubgraphData.info` type with optional `category` field
- Added `category` to `zSubgraphDefinition` schema
## Files Changed
- `src/stores/subgraphStore.ts` - Core logic for both features
- `src/stores/subgraphStore.test.ts` - Tests for `isGlobalBlueprint`
- `src/components/sidebar/tabs/nodeLibrary/NodeTreeLeaf.vue` - Hide
delete menu for global blueprints
- `src/scripts/api.ts` - Extended `GlobalSubgraphData` type
- `src/platform/workflow/validation/schemas/workflowSchema.ts` - Added
category to schema
- `src/locales/en/main.json` - Added translation key
## Testing
- ✅ `pnpm typecheck` passed
- ✅ `pnpm lint` passed
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8378-feat-add-category-support-for-blueprints-and-protect-global-blueprints-2f66d73d36508137aa67c2d88c358b69)
by [Unito](https://www.unito.io)
---------
Co-authored-by: Subagent 5 <subagent@example.com>
Co-authored-by: Amp <amp@ampcode.com>
Co-authored-by: GitHub Action <action@github.com>
Co-authored-by: AustinMroz <austin@comfy.org>
## Summary
When attempting to select text inside Vue node Markdown widget's
textarea (edit mode), the node would drag instead of text being
selected.
Root cause: WidgetMarkdown.vue's Textarea only had @click.stop and
@keydown.stop, but was missing pointer event modifiers. The pointerdown
event bubbled up to LGraphNode.vue which initiated node drag.
*Fix*: Add @pointerdown.capture.stop, @pointermove.capture.stop, and
@pointerup.capture.stop to match the pattern used in WidgetTextarea.vue.
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8413-fix-dragging-e-g-when-selecting-text-in-Markdown-note-causes-node-to-drag-2f76d73d3650816dbf9bdf893775c3d4)
by [Unito](https://www.unito.io)
Co-authored-by: Subagent 5 <subagent@example.com>
Co-authored-by: Amp <amp@ampcode.com>
## Summary
Increases the resize handle size on Vue nodes to improve usability,
especially when nodes are selected.
## Changes
- **What**: Increased resize handle from 12px to 20px and offset it
slightly outside the node boundary to avoid overlap with selection
outline
## Review Focus
The resize handle was too small and became harder to grab when the node
was selected (the 2px outline rendered outside the box, visually
obscuring the corner). This fix increases the hit area and positions it
to extend beyond the node edge.
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8391-fix-increase-Vue-node-resize-handle-size-for-better-usability-2f76d73d36508136b2aac51bc0d53551)
by [Unito](https://www.unito.io)
---------
Co-authored-by: Subagent 5 <subagent@example.com>
Co-authored-by: Amp <amp@ampcode.com>
Co-authored-by: GitHub Action <action@github.com>
## Summary
- Move WorkspaceAuthGate from App.vue to LayoutDefault.vue so it only
wraps authenticated routes
- Change initialize() to run in onMounted() for proper Vue lifecycle
- Restore immediate: true in cloudRemoteConfig watcher as backup
- Gate now mounts fresh after login, fixing re-login feature flag issue
The root cause was a race condition: after logout + page reload, the
cloudRemoteConfig watcher could be set up after the user already logged
in, missing the isLoggedIn change and never calling /features endpoint.
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8381-fix-move-WorkspaceAuthGate-to-LayoutDefault-for-proper-re-login-hand-2f66d73d36508182a3dec09a49214a00)
by [Unito](https://www.unito.io)
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
## Summary
Updates the error message shown when users enter an unsupported URL in
the BYOM (Bring Your Own Model) upload dialog.
**Before:** "Only URLs from Civitai, Hugging Face are supported"
**After:** "Please check the link format. Only URLs from Civitai,
Hugging Face are supported."
This provides more actionable guidance by suggesting users verify their
link format before listing the supported sources.
## Changes
- Updated `unsupportedUrlSource` i18n key in `src/locales/en/main.json`
## Testing
- `pnpm typecheck` ✅
- `pnpm lint` ✅
- Manual: Enter invalid URL (e.g.,
`https://example.com/model.safetensors`) in model upload dialog
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8368-feat-make-invalid-URL-error-message-more-actionable-2f66d73d3650810bbcc1e9fa3d1cd962)
by [Unito](https://www.unito.io)
Co-authored-by: Subagent 5 <subagent@example.com>
Co-authored-by: Amp <amp@ampcode.com>
## Summary
- Fix auth related race conditions with a new WorkspaceAuthGate in
App.vue
- De dup initialization calls
- Add state machine to track state of refreshRemoteConfig
- Fix websocket not using new workspace jwt
- Misc improvments
## Changes
- **What**: Mainly WorkspaceAuthGate.vue
- **Breaking**: <!-- Any breaking changes (if none, remove this line)
-->
- **Dependencies**: <!-- New dependencies (if none, remove this line)
-->
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8350-Feat-workspaces-5-auth-gate-check-2f66d73d365081b1a49afcd418fab3e7)
by [Unito](https://www.unito.io)
Litegraph uses `-1 ` as a placeholder node id to indicate that a node
should be assigned a new node id when added to the graph. Under some
unknown circumstances it's possible for a node to have this placeholder
id saved in a workflow file.
When this occurs, the node is loaded and assigned a new id, but then
configured back to the placeholder id. This PR makes it so the newly
assigned id is kept instead.
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8342-Prevent-configuring-a-node-to-a-placehodler-nodeId-2f56d73d365081ed8217e989187b15c8)
by [Unito](https://www.unito.io)
The `toConcrete` call creates a restricted view of a widget that extends
from `BaseWidget`. A copy of the widget created by `createCopyForNode`
will also inherit this restricted view. This creates two problems
- Some widget properties (like `displayValue`) have been judged unsafe
and are explicitly blacklisted from being copied
- The widget now extends from `BaseWidget`. This results in the widget
being processed differently in some logic, such as `#processWidgetClick`
- Because `LegacyWidget` provides an implementation for `onClick`, the
presence of click handlers can not be used to determine which should be
used.
As a proposed, minimal workaround. Widgets which do not already extend
from BaseWidget are no longer cloned through `createCopyForNode`.
Because this PR involves side-stepping properties which have been
explicitly blacklisted. I'd recommend waiting to merge/backport until
after the release of 1.28.7
ResolvesKosinkadink/ComfyUI-VideoHelperSuite#569
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6079-Prevent-partial-copy-of-custom-widgets-when-performing-linked-promotion-on-subgraphs-28d6d73d36508198950efd401186ddef)
by [Unito](https://www.unito.io)
---------
Co-authored-by: Alexander Brown <drjkl@comfy.org>
Co-authored-by: GitHub Action <action@github.com>
## Summary
- Fixes template filtering to be scope-aware, preventing empty results
when switching categories
- Filters now only show as selected when they exist in the current
category scope
- User selections are still persisted in localStorage for convenience
## Problem
When users selected filters (e.g., Image-specific filters) and switched
to a different category (e.g., Video), the persisted filters would cause
empty results because those filter values didn't exist in the new scope.
## Solution
- Modified `useTemplateFiltering` composable to track which filters are
"active" (applicable to current scope)
- Only active filters are shown as selected in the UI
- Only active filters are applied to the filtering logic
- All user selections are still persisted in localStorage
- When returning to a category, previously selected filters
automatically reactivate if applicable
## Test Plan
Tested manually in browser:
1. Navigate to Image category
2. Select filters like "Image" and "Image Edit" from Tasks dropdown
3. Switch to Video category - filters show as "0 selected" (Image/Image
Edit don't exist in Video)
4. Return to Image category - filters are automatically reselected (2
selected)
5. No empty results when switching between categories
## Changes
- `useTemplateFiltering.ts`: Added scope-aware filtering logic with
active/inactive filter distinction
- `WorkflowTemplateSelectorDialog.vue`: Pass navigation context and use
active filters for UI
- `useTemplateFiltering.test.ts`: Added comprehensive tests for
scope-aware behavior
https://github.com/user-attachments/assets/706ddabf-abad-4ff9-a378-6603d275d15a
## Summary
Fixed the `站贴` misspelling. The original translation is a common
misspelling using Pinyin IME because they are pronounced the same in
Chinese.
## Changes
- Change translation: `站贴` to `粘贴`
## Review Focus
Check the fact that `粘贴` is correct in Chinese. If you're not a Chinese
speaker, copy `粘贴` to Google Translate, and it should be `paste`
exactly.
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8316-Fix-Chinese-translation-paste-misspelling-2f46d73d3650814884e3e4adf8edeb5d)
by [Unito](https://www.unito.io)
## Summary
Refactors bootstrap and lifecycle management to parallelize
initialization, use Vue best practices, and fix a logout state bug.
## Changes
### Bootstrap Store (`bootstrapStore.ts`)
- Extract early bootstrap logic into a dedicated store using
`useAsyncState`
- Parallelize settings, i18n, and workflow sync loading (previously
sequential)
- Handle multi-user login scenarios by deferring settings/workflows
until authenticated
### GraphCanvas Refactoring
- Move non-DOM composables (`useGlobalLitegraph`, `useCopy`, `usePaste`,
etc.) to script setup level for earlier initialization
- Move `watch` and `whenever` declarations outside `onMounted` (Vue best
practice)
- Use `until()` from VueUse to await bootstrap store readiness instead
of direct async calls
### GraphView Simplification
- Replace manual `addEventListener`/`removeEventListener` with
`useEventListener`
- Replace `setInterval` with `useIntervalFn` for automatic cleanup
- Move core command registration to script setup level
### Bug Fix
Using `router.push()` for logout caused `isSettingsReady` to persist as
`true`, making new users inherit the previous user's cached settings.
Reverted to `window.location.reload()` with TODOs for future store reset
implementation.
## Testing
- Verified login/logout cycle clears settings correctly
- Verified bootstrap sequence completes without errors
---------
Co-authored-by: Amp <amp@ampcode.com>
filteredItems only updates during search, and does not update when the
filter changes. Trigger a re-search when items change through the
updateKey prop chain.
## Summary
Removes all `as unknown as Type` double-cast patterns from group 4 files
as part of the ongoing TypeScript cleanup effort.
## Changes
### Type Safety Improvements
- Replaced `as unknown as Type` with `as Partial<Type> as Type` for
valid mock objects
- Added proper null/undefined validation in `Subgraph.addInput()` and
`Subgraph.addOutput()`
- Updated test expectations to match new validation behavior
### Files Modified (17 files)
**Core Implementation:**
- `src/lib/litegraph/src/LGraph.ts` - Added input validation for
subgraph add methods
- `src/lib/litegraph/src/interfaces.ts` - Cleaned up type definitions
- `src/lib/litegraph/src/subgraph/ExecutableNodeDTO.ts` - Improved type
safety
- `src/platform/telemetry/types.ts` - Better type definitions
- `src/platform/telemetry/utils/surveyNormalization.ts` - Type cleanup
**Test Files:**
- `src/lib/litegraph/src/subgraph/SubgraphEdgeCases.test.ts` - Updated
to expect validation errors
- `src/lib/litegraph/src/subgraph/SubgraphMemory.test.ts` - Proper mock
typing
- `src/lib/litegraph/src/subgraph/SubgraphNode.test.ts` - Type
improvements
- `src/lib/litegraph/src/subgraph/SubgraphNode.titleButton.test.ts` -
Mock type fixes
- `src/lib/litegraph/src/subgraph/SubgraphSlotVisualFeedback.test.ts` -
Proper typing
- `src/lib/litegraph/src/subgraph/SubgraphWidgetPromotion.test.ts` -
Type cleanup
- `src/lib/litegraph/src/utils/textUtils.test.ts` - Mock improvements
- `src/lib/litegraph/src/widgets/ComboWidget.test.ts` - Type safety
updates
- `src/platform/assets/services/assetService.test.ts` - Type
improvements
- `src/platform/settings/composables/useSettingSearch.test.ts` - Mock
type fixes
- `src/platform/settings/settingStore.test.ts` - Type cleanup
- `src/platform/telemetry/utils/__tests__/surveyNormalization.test.ts` -
Type improvements
## Testing
- ✅ All modified test files pass
- ✅ TypeScript compilation passes
- ✅ Linting passes
## Related
Part of the TypeScript cleanup effort. Follows patterns from groups 1-3.
Previous PRs:
- Group 1: (merged)
- Group 2: (merged)
- Group 3: #8304
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8314-Road-to-No-Explicit-Any-Part-8-Group4-2f46d73d36508172a9d4e53b2c5cbadd)
by [Unito](https://www.unito.io)
## Summary
Replace spread operators with Object.assign() to fix 3 no-misused-spread
lint warnings in test utilities and test files.
## Changes
- **What**: Replaced spread operators with Object.assign() in
createMockFileList and mock node creation functions to avoid spreading
arrays into objects and class instances that could lose prototypes.
Simplified BypassButton.test.ts by removing redundant type annotations.
- **Breaking**: None
## Review Focus
Type safety is preserved without using weak TypeScript patterns (no
`any`, `as unknown as`, or unnecessary casts).
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8318-fix-resolve-no-misused-spread-lint-warnings-in-test-files-2f46d73d365081aca3f6cf208556d492)
by [Unito](https://www.unito.io)
Allows creation of Int and Float widgets with configurable, min, max,
step, and precision.
This PR has been fairly heavily reworked. Options are no longer exposed
as widgets, but set as properties on the node.
Since the changes no longer modify the sizing or serialization of the
node, backend changes are no longer required and the extended
functionality has been added directly onto the existing PrimitiveFloat
and PrimitiveInt nodes.
There's intent to expose these configuration parameters on the new
properties panel, but this PR can be merged as is.
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-7768-Frontend-code-for-custom-number-nodes-2d66d73d365081879541cbda7db3c24f)
by [Unito](https://www.unito.io)
## Summary
- Keep all drafts in localStorage, mirroring the logic from VSCode.
- Fix a bug where newly created blank workflow tabs would incorrectly
restore as defaultGraph instead of blankGraph after page refresh.
Resolves https://github.com/Comfy-Org/desktop/issues/910, Resolves
https://github.com/Comfy-Org/ComfyUI_frontend/issues/4057, Fixes
https://github.com/Comfy-Org/ComfyUI_frontend/issues/3665
## Changes
### What
- Fix `restoreWorkflowTabsState` to parse and pass workflow data from
drafts when recreating temporary workflows
- Add error handling for invalid draft data with fallback to default
workflow
- Fix E2E test `should not serialize color adjustments in workflow` to
wait for workflow persistence before assertions
- Add proper validation for workflow nodes array in test assertions
### Breaking
- None
### Dependencies
- No new dependencies added
## Review Focus
1. **Workflow restoration**: Verify that blank workflows correctly
restore as blankGraph after page refresh
2. **Error handling**: Check that invalid draft data gracefully falls
back to default workflow
3. **Test coverage**: Ensure E2E test correctly waits for workflow
persistence before checking node properties
4. **Edge cases**: Test with multiple tabs, switching between tabs, and
rapid refresh scenarios
---------
Co-authored-by: Yourz <crazilou@vip.qq.com>
## Summary
- Eliminated all `as unknown as` type assertions from Group 3 test files
- Created reusable factory functions in `litegraphTestUtils.ts` for
better type safety
- Improved test mock composition using `Partial` types with single `as`
casts
- Fixed LGraphNode tests to use proper API methods instead of direct
property assignment
## Changes by Category
### New Factory Functions in `litegraphTestUtils.ts`
- `createMockLGraphNodeWithArrayBoundingRect()` - Creates LGraphNode
with proper boundingRect for position tests
- `createMockFileList()` - Creates mock FileList with proper structure
including `item()` method
### Test File Improvements
**Composables:**
- `useLoad3dDrag.test.ts` - Used `createMockFileList` factory
- `useLoad3dViewer.test.ts` - Created local `MockSceneManager` interface
with proper typing
**LiteGraph Tests:**
- `LGraphNode.test.ts` - Replaced direct `boundingRect` assignments with
`updateArea()` calls
- `LinkConnector.test.ts` - Improved mock composition with proper
Partial types
- `ToOutputRenderLink.test.ts` - Added `MockEvents` interface for
type-safe event mocking
- Updated integration and core tests to use new factory functions
**Extension Tests:**
- `contextMenuFilter.test.ts` - Updated menu factories to accept
`(IContextMenuValue | null)[]`
## Type Safety Improvements
- Zero `as unknown as` instances (was: multiple instances across 17
files)
- All mocks use proper `Partial<T>` composition with single `as T` casts
- Improved IntelliSense and type checking in test files
- Centralized mock creation reduces duplication and improves
maintainability
## Test Plan
- ✅ All TypeScript type checks pass
- ✅ ESLint passes with no new errors
- ✅ Pre-commit hooks (format, lint, typecheck) all pass
- ✅ Knip unused export check passes
- ✅ No behavioral changes to actual tests (only type improvements)
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8304-Road-to-No-explicit-any-Improve-type-safety-in-Group-3-test-mocks-2f36d73d365081ab841de96e5f01306d)
by [Unito](https://www.unito.io)
## Summary
Add team workspace member management and invite system.
## Changes
- Add members panel with role management (owner/admin/member) and member
removal
- Add invite system with email invites, pending invite display, and
revoke functionality
- Add invite URL loading for accepting invites
- Add subscription panel updates for member management
- Add i18n translations for member and invite features
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8245-Workspaces-4-members-invites-2f06d73d36508176b2caf852a1505c4a)
by [Unito](https://www.unito.io)
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: GitHub Action <action@github.com>
Refactors modal dialog layouts for improved flexibility and consistency.
**Changes:**
- Add dedicated slot for left panel header title with dynamic
content/icons
- Consolidate side panel rendering within `BaseModalLayout`
- Remove redundant `PanelHeader` and `RightSidePanel` components
- Apply `select-none` to text elements to prevent accidental selection
---------
Co-authored-by: Amp <amp@ampcode.com>
## Summary
Improved type safety in test files by eliminating unsafe type assertions
and adopting official testing patterns. Reduced unsafe `as unknown as`
type assertions and eliminated all `null!` assertions.
## Changes
- **Adopted @pinia/testing patterns**
- Replaced manual Pinia store mocking with `createTestingPinia()` in
`useSelectionState.test.ts`
- Eliminated ~120 lines of mock boilerplate
- Created `createMockSettingStore()` helper to replace duplicated store
mocks in `useCoreCommands.test.ts`
- **Eliminated unsafe null assertions**
- Created explicit `MockMaskEditorStore` interface with proper nullable
types in `useCanvasTools.test.ts`
- Replaced `null!` initializations with `null` and used `!` at point of
use or `?.` for optional chaining
- **Made partial mock intent explicit**
- Updated test utilities in `litegraphTestUtils.ts` to use explicit
`Partial<T>` typing
- Changed cast pattern from `as T` to `as Partial<T> as T` to show
incomplete mock intent
- Applied to `createMockLGraphNode()`, `createMockPositionable()`, and
`createMockLGraphGroup()`
- **Created centralized mock utilities** in
`src/utils/__tests__/litegraphTestUtils.ts`
- `createMockLGraphNode()`, `createMockPositionable()`,
`createMockLGraphGroup()`, `createMockSubgraphNode()`
- Updated 8+ test files to use centralized utilities
- Used union types `Partial<T> | Record<string, unknown>` for flexible
mock creation
## Results
- ✅ 0 typecheck errors
- ✅ 0 lint errors
- ✅ All tests passing in modified files
- ✅ Eliminated all `null!` assertions
- ✅ Reduced unsafe double-cast patterns significantly
## Files Modified (18)
- `src/components/graph/SelectionToolbox.test.ts`
-
`src/components/graph/selectionToolbox/{BypassButton,ColorPickerButton,ExecuteButton}.test.ts`
- `src/components/sidebar/tabs/queue/ResultGallery.test.ts`
- `src/composables/canvas/useSelectedLiteGraphItems.test.ts`
- `src/composables/graph/{useGraphHierarchy,useSelectionState}.test.ts`
-
`src/composables/maskeditor/{useCanvasHistory,useCanvasManager,useCanvasTools,useCanvasTransform}.test.ts`
- `src/composables/node/{useNodePricing,useWatchWidget}.test.ts`
- `src/composables/{useBrowserTabTitle,useCoreCommands}.test.ts`
- `src/utils/__tests__/litegraphTestUtils.ts`
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8258-refactor-eliminate-unsafe-type-assertions-from-Group-2-test-files-2f16d73d365081549c65fd546cc7c765)
by [Unito](https://www.unito.io)
---------
Co-authored-by: GitHub Action <action@github.com>
Co-authored-by: Alexander Brown <drjkl@comfy.org>
Co-authored-by: Amp <amp@ampcode.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: AustinMroz <austin@comfy.org>
Co-authored-by: Christian Byrne <cbyrne@comfy.org>
Co-authored-by: Benjamin Lu <benjaminlu1107@gmail.com>
## Summary
- Fixes remote config polling to use authenticated API
- Consolidates `loadRemoteConfig` into `refreshRemoteConfig` with auth
control
- Adds unit tests for both auth modes
## Problem
The cloud extension's polling interval was using unauthenticated
`fetch`, causing it to receive only default feature flags instead of
user-specific configurations.
**Root cause:**
1. Bootstrap called `loadRemoteConfig()` (raw `fetch`, no auth) -
correct, auth not initialized yet
2. Extension watch called `refreshRemoteConfig()` (`api.fetchApi`, with
auth) - correct
3. Extension interval called `loadRemoteConfig()` (raw `fetch`, no auth)
- **bug**
## Solution
- Consolidate into single `refreshRemoteConfig()` with optional
`useAuth` parameter (defaults to `true`)
- Bootstrap: `refreshRemoteConfig({ useAuth: false })`
- Polling: `refreshRemoteConfig()` (authenticated by default)
## Test Plan
- Unit tests verify both auth modes
- `pnpm typecheck`, `pnpm lint`, `pnpm test:unit` all pass
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8266-fix-use-authenticated-API-for-remote-config-polling-2f16d73d3650817ea7b0e3a7e3ccf12a)
by [Unito](https://www.unito.io)
## Summary
Replace MDI font icons with Tailwind Iconify (Lucide) icons for Settings
and Manage Extensions menu items to fix size inconsistency with Browse
Template icon.
## Changes
- **What**: MDI font icons (`mdi mdi-cog-outline`, `mdi
mdi-puzzle-outline`) rendered at 14px (`text-sm` font-size), while the
Tailwind Iconify icon (`icon-[comfy--template]`) rendered at ~16.8px due
to `scale: 1.2` in the Tailwind plugin config. Replaced with
`icon-[lucide--settings]` and `icon-[lucide--puzzle]` so all icons use
the same rendering pipeline.
## 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
Model management improvements: refactored tag API, enhanced UX for
trigger phrases and editing.
## Changes
### API Refactor
- Add `addAssetTags` (POST) and `removeAssetTags` (DELETE) endpoints in
assetService
- `updateAssetTags` now computes diff and calls remove/add serially
- Add `TagsOperationResult` schema; cache syncs with server response
### UX Improvements
- **Trigger phrases**: click to copy individual phrases, copy-all button
in header
- **Display name**: show edit icon on hover instead of relying on
double-click
- **Model type**: show plain text when immutable instead of disabled
select
- **Tags input**: only show edit icon on hover
- **Field labels**: updated styling to use muted foreground color
- **Optimistic update** for model type selection
---------
Co-authored-by: Amp <amp@ampcode.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: GitHub Action <action@github.com>
## Summary
Enables the feedback button on nightly releases to collect user feedback
and distinguish it from stable releases in Zendesk.
## Changes
- Add distribution tracking to feedback URL (cloud/nightly/stable)
- Export `getDistribution()` and `ZENDESK_FIELDS` from support config
for reuse
- Enable feedback button for both cloud and nightly builds
- Track distribution in Zendesk as: `ccloud`, `oss-nightly`, or `oss`
- Fix type signatures for `normalizeIndustry`/`normalizeUseCase` to
accept `unknown`
## Requirements
- [ ] Support team needs to add `oss-nightly` as a valid value for the
distribution field in Zendesk
## Test plan
- [ ] Build and run nightly version, verify feedback button appears
- [ ] Click feedback button, verify Zendesk form opens with correct
distribution parameter
- [ ] Verify cloud builds still show feedback button as before
- [ ] Verify stable OSS builds don't show feedback button
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8220-feat-enable-feedback-button-on-nightly-releases-2ef6d73d3650816db81ef970919770a4)
by [Unito](https://www.unito.io)
## Summary
Backend part: https://github.com/Comfy-Org/ComfyUI/pull/11582
- Move API node pricing definitions from hardcoded frontend functions to
backend-defined JSONata expressions
- Add `price_badge` field to node definition schema containing JSONata
expression and dependency declarations
- Implement async JSONata evaluation with signature-based caching for
efficient reactive updates
- Show one decimal in credit badges when meaningful (e.g., 1.5 credits
instead of 2 credits)
## 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-7816-Move-price-badges-to-python-nodes-2da6d73d365081ec815ef61f7e3c65f7)
by [Unito](https://www.unito.io)
- Fix deserialization of matchtype inputs spawned by autogrow.
- Rotate multitype slot indicators to align with design changes.
- Fix several instance of incorrect group matching
- MatchType reactively updates input type in vue
- Support the "hollow circle" optional input indicator in vue
- Custom combo sends index of selection to backend
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8026-Further-dynamic-input-fixes-2e76d73d3650819680fef327a94f4294)
by [Unito](https://www.unito.io)
Use es-toolkit orderBy for queue list sorting.
The queue overlay list already sorts by create time, but the
implementation used Array.sort with a custom comparator and mutated the
array in place. Switch to es-toolkit's orderBy to make the sort intent
explicit, avoid mutation, and align with the utility set we already
depend on. Sorting keys and direction remain the same, so behavior is
unchanged.
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8228-fix-use-orderBy-for-queue-list-sorting-2f06d73d365081e791fff7d2212537f8)
by [Unito](https://www.unito.io)
Co-authored-by: Alexander Brown <drjkl@comfy.org>
## Summary
Adds an editable Model Info Panel to show and modify asset details in
the asset browser.
## Changes
- Add `ModelInfoPanel` component with editable display name,
description, model type, base models, and tags
- Add `updateAssetMetadata` action in `assetsStore` with optimistic
cache updates
- Add shadcn-vue `Select` components with design system styling
- Add utility functions in `assetMetadataUtils` for extracting model
metadata
- Convert `BaseModalLayout` right panel state to `defineModel` pattern
- Add slide-in animation and collapse button for right panel
- Add `class` prop to `PropertiesAccordionItem` for custom styling
- Fix keyboard handling: Escape in TagsInput/TextArea doesn't close
parent modal
## Testing
- Unit tests for `ModelInfoPanel` component
- Unit tests for `assetMetadataUtils` functions
---------
Co-authored-by: Amp <amp@ampcode.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: GitHub Action <action@github.com>
## Summary
Adds a "Preview Version" badge to the topbar on nightly builds to help
users identify when they're using an unreleased version and encourage
feedback.
## Changes
- Add `nightly.badge` i18n translations (label and tooltip)
- Create `nightlyBadges.ts` extension with info variant badge
- Load nightly badges extension when `isNightly` is true
- Badge tooltip encourages users to provide feedback via the feedback
button
## Test plan
- [ ] Build and run nightly version, verify "NIGHTLY | Preview Version"
badge appears in topbar
- [ ] Hover over badge, verify tooltip shows: "You are using a nightly
version of ComfyUI. Please use the feedback button to share your
thoughts about these features."
- [ ] Verify stable OSS builds don't show the badge
- [ ] Verify cloud builds don't show the nightly badge
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8222-feat-add-preview-version-badge-for-nightly-builds-2ef6d73d36508102aa0dfc22ef14c9c1)
by [Unito](https://www.unito.io)
## Summary
Show active jobs in grid view matching the list view behavior, with
refactored component structure.
## Changes
- **ActiveJobCard**: New component for grid view job display with
progress bar
- **AssetsSidebarGridView**: Extracted grid view logic from
AssetsSidebarTab (matching ListView pattern)
- **Progress styling**: Use `useProgressBarBackground` composable for
consistent progress bar styling
- **Assets header**: Add "Generated/Imported assets" header in grid view
## Summary
Implements progressive pagination for model assets - returns the first
batch immediately while loading remaining batches in the background.
## Changes
### Store (`assetsStore.ts`)
- Adds `ModelPaginationState` tracking (assets Map, offset, hasMore,
loading, error)
- `updateModelsForKey()` returns first batch, then calls
`loadRemainingBatches()` to fetch the rest
- Accessor functions `getAssets(key)`, `isModelLoading(key)` replace
direct Map access
### API (`assetService.ts`)
- Adds `PaginationOptions` interface (`{ limit?, offset? }`)
### Components
- `AssetBrowserModal.vue` uses new accessor API
### Tests
- Updated mocks for new accessor pattern
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8212-feat-implement-progressive-pagination-for-Asset-Browser-model-assets-2ef6d73d36508157af04d1264780997e)
by [Unito](https://www.unito.io)
---------
Co-authored-by: Amp <amp@ampcode.com>
## Summary
Marks the `comfy-api-plugin` Vite plugin as build-time only by adding
`apply: 'build'`.
This prevents the plugin's transform from running during development
(`vite dev`), improving dev server startup time and avoiding unnecessary
processing when the plugin's output is not needed in development mode.
Also updates `build/tsconfig.json` to use `moduleResolution: "bundler"`
which is the recommended setting for Vite projects.
## Changes
- **build/plugins/comfyAPIPlugin.ts**: Add `apply: 'build'` to restrict
plugin to production builds
- **build/tsconfig.json**: Update `moduleResolution` from `"node"` to
`"bundler"`
## Testing
- `pnpm typecheck` passes
- `pnpm build` produces correct output
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8224-fix-Mark-comfy-api-plugin-as-build-time-only-2ef6d73d36508145a48ae849087fbad7)
by [Unito](https://www.unito.io)
## Summary
Add session download tracking to track which assets were downloaded
during the current session. This enables UI features like:
- Badge count on "Imported" nav showing newly downloaded assets
- Visual indicator on asset cards for recently downloaded items
## Changes
- Add `acknowledged` flag to `AssetDownload` interface
- Add `unacknowledgedDownloads` computed for filtering
- Add `sessionDownloadCount` computed for badge display
- Add `isDownloadedThisSession(identifier)` to check individual assets
- Add `acknowledgeDownload(identifier)` to mark assets as seen
## Testing
- 6 new unit tests covering all session tracking functionality
- Run: `pnpm test:unit -- src/stores/assetDownloadStore.test.ts`
## Related
- Part of Asset Browser improvements (#8090)
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8213-feat-add-session-download-tracking-to-assetDownloadStore-2ef6d73d365081538045e8544d26bafa)
by [Unito](https://www.unito.io)
---------
Co-authored-by: Amp <amp@ampcode.com>
## Summary
Refactors `BaseModalLayout` from a flexbox-based layout to CSS Grid,
enabling smoother panel transitions and improved layout control.
## Changes
### Layout Restructure
- **Flexbox → CSS Grid**: Replaced nested flexbox with a 3-column CSS
Grid (`nav | main | aside`)
- **Smooth panel transitions**: Panel show/hide now animates via
`grid-template-columns` instead of Vue `<Transition>` with `translateX`
- **Removed transition CSS**: Deleted `.slide-panel-*` and `.fade-*`
transition styles (no longer needed with grid approach)
### Right Panel Improvements
- **Dedicated header**: Added header with close button, right panel
toggle, and customizable title slot (`rightPanelHeaderTitle`,
`rightPanelHeaderActions`)
- **New prop**: Added `rightPanelTitle` prop for simple text title in
right panel header
### UX & Accessibility
- **ESC key handling**: Pressing Escape closes the right panel (if open)
before closing the dialog
- **Accessibility**: Added `aria-label` attributes to all panel toggle
and close buttons
- **i18n**: Added translation keys: `showLeftPanel`, `hideLeftPanel`,
`showRightPanel`, `hideRightPanel`, `closeDialog`
---------
Co-authored-by: Amp <amp@ampcode.com>
## Summary
- Add `teamWorkspaceStore` Pinia store for workspace state management
(workspaces, members, invites, current workspace)
- Add `workspaceApi` client for workspace CRUD, member management, and
invite operations
- Update `useWorkspaceSwitch` composable for workspace switching logic
- Update `useSessionCookie` for workspace-aware sessions
- Update `firebaseAuthStore` for workspace aware auth
- Use `workspaceAuthStore` for workspace auth flow
## Test plan
- [x] 59 unit tests passing (50 store tests + 9 switch tests)
- [x] Typecheck passing
- [x] Lint passing
- [x] Knip passing
Note: This PR depends on the `team_workspaces_enabled` feature flag
being available (already in main).
🤖 Generated with [Claude Code](https://claude.ai/code)
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8194-feat-add-workspace-session-auth-and-store-infrastructure-2ef6d73d3650814984afe8ee7ba0a209)
by [Unito](https://www.unito.io)
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.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
This PR improves keyboard event handling consistency and fixes an issue
where pressing Escape in nested input components would unintentionally
close parent modals/dialogs.
## Changes
### Keyboard Event Fixes
**TagsInput Escape Key Handling**
- Added `@keydown.escape.stop` handler to `TagsInputInput.vue` to
prevent Escape from bubbling up and closing parent modals
- The handler blurs the input and exits editing mode without propagating
the event
**EditableText keyup → keydown Migration**
- Changed `@keyup.enter` to `@keydown.enter` and `@keyup.escape` to
`@keydown.escape`
- Using `keydown` is more consistent with how other UI frameworks handle
these events and provides more responsive feedback
- Updated corresponding unit tests to use `keydown` triggers
### Why keydown over keyup?
- `keydown` fires immediately when the key is pressed, providing faster
perceived response
- Better consistency with browser/OS conventions for action triggers
- Prevents default behaviors (like form submission) more reliably when
needed
- Aligns with other keyboard handlers in the codebase
## Testing
- Updated `EditableText.test.ts` to use `keydown` events
- Updated `NodeHeader.test.ts` to use `keydown.enter`
- Manual testing: Escape in TagsInput no longer closes parent modal
## Checklist
- [x] Unit tests updated
- [x] Keyboard event handlers consistent
- [x] No breaking changes to component API
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8204-fix-Consistent-keydown-handling-for-EditableText-and-TagsInput-escape-key-2ef6d73d365081f0ac6bed8bcae57657)
by [Unito](https://www.unito.io)
In vue mode, the VHS Load Audio (Upload) node had 2 audio previews. This
occurred because the native AudioPreview widget was being applied to any
combo widget with the name `audio`. This native preview does not support
the advanced preview functions VHS provides like seeking to specific
start time, trimming to a target duration, or converting from formats
the browser may not support.
This is fixed through a fairly involved cleanup to instead display the
litegraph AudioUI widget as an AudioPreview widget when in vue mode.
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8206-Fix-doubled-player-on-VHS-LoadAudio-in-vue-2ef6d73d365081ce8907dca2706214a1)
by [Unito](https://www.unito.io)
---------
Co-authored-by: github-actions <github-actions@github.com>
Co-authored-by: Alexander Brown <drjkl@comfy.org>
## Summary
Add `maxColumns` prop to VirtualGrid for responsive column capping.
## Changes
- **VirtualGrid**: Add `maxColumns` prop to cap grid columns; refactor
inline styles to Tailwind classes; extract spacer height computation to
helper function
- **AssetGrid**: Use `useBreakpoints` to set responsive `maxColumns`
(1-5 based on Tailwind breakpoints)
## Review Focus
- `maxColumns` behavior when `Infinity` vs finite: does
`gridTemplateColumns` override work correctly?
- Tailwind scrollbar classes replacing scoped CSS
## Summary
- Fix: Initializing jobs now properly disappear from UI when cancelled
or cleared
- Add `clearInitializationByPromptIds` batch function for optimized Set
operations
- Handle Cloud vs local environment correctly (use `api.deleteItem` for
Cloud, `api.interrupt` for local)
## Problem
When clicking 'Clear queue' button or X button on initializing jobs, the
jobs remained visible in both AssetsSidebarListView and JobQueue
components until page refresh.
## Root Cause
1. `initializingPromptIds` in `executionStore` was not being cleared
when jobs were cancelled/deleted
2. Cloud environment requires `api.deleteItem()` instead of
`api.interrupt()` for cancellation
## Changes
- `src/stores/executionStore.ts`: Export `clearInitializationByPromptId`
and add batch `clearInitializationByPromptIds` function
- `src/composables/queue/useJobMenu.ts`: Add Cloud branch handling and
initialization cleanup
- `src/components/queue/QueueProgressOverlay.vue`: Fix `onCancelItem()`,
`cancelQueuedWorkflows()`, `interruptAll()`
- `src/components/sidebar/tabs/AssetsSidebarTab.vue`: Add initialization
cleanup to `handleClearQueue()`
[screen-capture.webm](https://github.com/user-attachments/assets/0bf911c2-d8f4-427c-96e0-4784e8fe0f08)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8203-bugfix-Clear-queue-button-now-properly-removes-initializing-jobs-from-UI-2ef6d73d36508162a55bd84ad39ab49c)
by [Unito](https://www.unito.io)
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
## Summary
Fix 404 error when adding imported assets to workflow as LoadImage nodes
in Cloud mode.
## Changes
- **What**: Use `asset_hash` (hash-based filename) instead of `name`
(original filename) when creating LoadImage nodes in Cloud mode
- **Files**: `useMediaAssetActions.ts` - modified `addWorkflow` and
`addMultipleToWorkflow` functions
- **Tests**: Added `useMediaAssetActions.test.ts` with Cloud/OSS
filename selection tests
## Review Focus
- Cloud vs OSS branching logic using `isCloud && asset.asset_hash`
🤖 Generated with [Claude Code](https://claude.ai/code)
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8200-bugfix-Use-asset_hash-for-LoadImage-node-in-Cloud-mode-2ef6d73d365081d785b0d7a94e73c55e)
by [Unito](https://www.unito.io)
A frequent pattern is to add a node to the graph, and then update the
nodes position afterwards.
Some of these cases (like subgraph unpacking) can set the node position
in advance, but others, (like importA1111) require information on nodes
in order to perform arranging.
Alternatives, like allowing code to either modify `app.configuringGraph`
or otherwise set a temporary state were considered, but create the same
problem of requiring fixes in many places.
As a proposed alternative, when a node is created, an extra tick of
delay is always added before initializing layout.
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-7591-Always-wait-for-next-tick-before-layout-init-2cc6d73d365081f4ababc38020645670)
by [Unito](https://www.unito.io)
---------
Co-authored-by: DrJKL <DrJKL0424@gmail.com>
Co-authored-by: github-actions <github-actions@github.com>
Introduces `useFeatureUsageTracker` composable that tracks how many
times a user has used a specific feature, along with first and last
usage timestamps. Data persists to localStorage using `@vueuse/core`'s
`useStorage`. This composable provides the foundation for triggering
surveys after a configurable number of feature uses. Includes
comprehensive unit tests.
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8175-feat-add-feature-usage-tracker-for-nightly-surveys-2ee6d73d36508118859ece6fcf17561d)
by [Unito](https://www.unito.io)
Extends the existing 'Show Advanced' button (previously subgraph-only)
to also appear on regular nodes that have widgets marked with
`options.advanced = true`.
## Changes
- Updates `showAdvancedInputsButton` computed to check for advanced
widgets on regular nodes
- Updates `handleShowAdvancedInputs` to set `node.showAdvanced = true`
and trigger canvas redraw for regular nodes
## Related
- Backend PR that adds `advanced` flag: comfyanonymous/ComfyUI#11939
- Canvas hide PR: feat/advanced-widgets-canvas-hide (this PR provides
the toggle for that)
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8148-feat-canvas-show-Show-Advanced-button-on-nodes-with-advanced-widgets-2ec6d73d36508155a8adfa0a8ec84d46)
by [Unito](https://www.unito.io)
## Summary
Adds a compile-time `__IS_NIGHTLY__` constant that detects whether the
build is from the main branch (nightly) or a core/* branch (RC/stable).
The detection logic in vite.config.mts auto-detects based on
`GITHUB_REF_NAME === 'main'` in CI, with explicit override support via
`IS_NIGHTLY` environment variable. Exports `isNightly` from
`src/platform/distribution/types.ts` for use throughout the codebase.
Includes unit tests for the detection logic.
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8149-feat-add-isNightly-build-flag-for-nightly-only-features-2ec6d73d365081c09930edec1c6644f5)
by [Unito](https://www.unito.io)
## Summary
Continues the TypeScript strict typing improvements by removing `any`
types from core scripts and dialog components.
### Changes
**api.ts (6 instances)**
- Define `V1RawPrompt` and `CloudRawPrompt` tuple types for queue prompt
formats
- Export `QueueIndex`, `PromptInputs`, `ExtraData`, `OutputsToExecute`
from apiSchema
- Type `#postItem` body, `storeUserData` data, and `getCustomNodesI18n`
return
**groupNodeManage.ts (all @ts-expect-error removed)**
- Add `GroupNodeConfigEntry` interface to LGraph.ts
- Extend `GroupNodeWorkflowData` with `title`, `widgets_values`, and
typed `config`
- Type all class properties with definite assignment assertions
- Type all method parameters and event handlers
- Fix save button callback with proper generic types for node ordering
**changeTracker.ts (4 instances)**
- Type `nodeOutputs` as `Record<string, ExecutedWsMessage['output']>`
- Type prompt callback with `CanvasPointerEvent` and proper value types
**asyncDialog.ts and dialog.ts**
- Make `ComfyAsyncDialog` generic with `DialogAction<T>` type
- Type `ComfyDialog` constructor and show method parameters
- Update `ManageGroupDialog.show` signature to match base class
## Test plan
- [x] `pnpm typecheck` passes
- [x] `pnpm lint` passes
- [x] Sourcegraph checks for external usage
---
Related: Continues from #8083
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8092-Road-to-No-Explicit-Any-Part-7-Scripts-and-Dialog-Cleanup-2ea6d73d365081fbb890e73646a6ad16)
by [Unito](https://www.unito.io)
## Summary
Optimizes the Manager dialog to use the cached `GET /nodes` endpoint
instead of `GET /nodes/search` for empty search queries (when the dialog
first opens). This significantly reduces Algolia usage since empty
searches account for the majority of search requests.
## Changes
- **registrySearchProvider.ts**: Modified `searchPacks()` to detect
empty queries and route them to `listAllPacks()` instead of `search()`
- **registrySearchProvider.test.ts**: Added 5 new test cases covering
empty query behavior
- Cache clearing now clears both `search` and `listAllPacks` caches
## Technical Details
**Empty Query Flow (NEW):**
- Query: `""` or whitespace
- Endpoint: `GET /nodes?limit=X&page=Y`
- Cache: Server-side cached (via omitting `latest` parameter)
- Result: Fast, cached node pack list
**Non-Empty Query Flow (UNCHANGED):**
- Query: Any non-empty string
- Endpoint: `GET /nodes/search?search=X` or `comfy_node_search=X`
- Result: Search results as before
## Testing
```bash
pnpm test:unit -- src/services/providers/registrySearchProvider.test.ts
pnpm typecheck
```
#8112 updated control widgets to be disabled when the controlled widget
is disabled. However, some workflows already exist that contain a
promoted control widget which does not function. This widget wouldn't be
marked as disabled (and thus, demoted) until the interior subgraph was
entered as updating `computedDisabled` is tacked to node draw. This is
fixed by having subgraphs eagerly update the `computedDisabled` state on
each node when configured.
Additionally, when `createCopyForNode` was used, linkedWidget retained
pointers to widgets which no longer have relation to the newly cloned
widget. This is resolved by instead not copying linkedWidgets.
Functionally, linkedWidgets is only used for control widgets and not
copying has the effect of ensuring that seed widgets linked to a
subgraph input will not display a control popover button in vue mode
which does nothing.
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8160-Control-widget-fixes-2ed6d73d3650816cb397f83f558471b3)
by [Unito](https://www.unito.io)
Sometimes it's difficult to gauge the valid range of values for a
widget. Litegraph includes a "slider" widget which displays the distance
from the min and max values as a colored bar. However, this
implementation is rather strongly disliked because it prevents entering
an exact number. Vue mode makes it simple to add just the indicator onto
our existing widget.
In addition to requiring both min and max be set, not every widget would
want this functionality. It's not useful information for seed, but also
has potential to cause confusion on widgets like CFG, that allow
inputting numbers up to 100 even though values beyond ~15 are rarely
desirable.
As a proposed heuristic, the ratio of "step" to distance between min and
max is currently used, but this could fairly easily be changed to an
opt-in only system.
<img width="617" height="487" alt="image"
src="https://github.com/user-attachments/assets/9c5f2119-0a03-4b56-bcf5-e4a0d0250784"
/>
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8122-Add-a-slider-indicator-for-number-widgets-in-vue-mode-2eb6d73d365081218fc8e86f37001958)
by [Unito](https://www.unito.io)
The parentId property on links and reroutes was not handled at all in
the "Convert to Subgraph" code.
This needs to be addressed in 4 cases
- A new external input link must have parentId set to the first
non-migrated reroute
- A new external output link must have the parentId of it's eldest
remaining child set to undefined
- A new internal input link must have the parentId of it's eldest
remaining child set to undefined
- A new internal output link must have the parentId set to the first
migrated reroute
This is handled in two parts by adding logic where the boundry links is
created
- The change involves mutation of inputs (which isn't great) but the
function here was already mutating inputs into an invalid state
- @DrJKL Do you see a quick way to better fix both these cases?
Looks like litegraph tests aren't enabled and cursory glance shows
multiple need to be updated to reflect recent changes. I'll still try to
add some tests anyways.
EDIT: Tests are non functional. Seems the subgraph conversion call
requires the rest of the frontend is running and has event listeners to
register the subgraph node def. More work than anticipated, best
revisited later
Resolves#5669
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5708-Migrate-parentIds-when-converting-to-subgraph-2746d73d365081f78acff4454092c74a)
by [Unito](https://www.unito.io)
---------
Co-authored-by: Alexander Brown <drjkl@comfy.org>
## Summary
Upgrades Vite from v7.3.0 to v8.0.0-beta.8, which uses Rolldown
(Rust-based bundler) instead of Rollup.
## Changes
- Updated `vite` to `^8.0.0-beta.8` in pnpm-workspace.yaml catalog
- Added pnpm overrides to ensure all dependencies (including vitest) use
Vite 8
## Notes
- Vite 8 is still in **beta** - no stable release yet
- Uses [Rolldown](https://rolldown.rs/) instead of Rollup for production
builds
- Build, typecheck, and lint all pass
- Per the [Vite 8 migration
guide](https://vite.dev/blog/announcing-vite8-beta), pnpm overrides are
required for tools like Vitest that bundle their own Vite types
## Testing
- [x] `pnpm typecheck` passes
- [x] `pnpm build` succeeds (~13s build time)
- [x] `pnpm lint` passes
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8127-feat-upgrade-vite-to-v8-0-0-beta-8-Rolldown-powered-2eb6d73d365081e3bdb6f500e140eb88)
by [Unito](https://www.unito.io)
Co-authored-by: Amp <amp@ampcode.com>
## Summary
<!-- One sentence describing what changed and why. -->
## Changes
- **What**: <!-- Core functionality added/modified -->
PNG images causes getWorkflowDataFromFile() to return an empty object,
added a check to handle it.
## Review Focus
<!-- If this PR fixes an issue, uncomment and update the line below -->
<!-- Fixes #ISSUE_NUMBER -->
I don't think it exists yet? From Slack Conversation. Just make sure to
use a PNG image and not a JPEG disguised as a PNG.
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8075-Check-for-empty-object-2e96d73d3650816b8812eb7244b48f1a)
by [Unito](https://www.unito.io)
## Summary
Updates the node preview rendering to use the same app context as the
main app so it can access the same plugins
## Changes
Assigns manually created vnode app context to the current instances
context
## Review Focus
This is using somewhat advanced/almost-internal Vue functionality,
however I couldn't come up with a better alternative that didn't require
recreating an entirely new app and re-registering all dependencies or
redoing how draggable node previews are done.
The draggable image needs to be rendered synchronously, so rendering a
node in the active app and capturing that isn't possible to guarantee to
be done synchronously (afaik - suggestions welcome)
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8118-Fix-dragging-Vue-nodes-into-canvas-from-library-2eb6d73d365081a0a956d8280e009592)
by [Unito](https://www.unito.io)
## Summary
When the node library is open and you click on the node toolbar info
button, this causes the node library info panel & right panel node info
to show the same details.
## Changes
- Extract useNodeHelpContent composable so NodeHelpContent fetches its
own content, allowing multiple panels to show help independently
- Remove sync behavior from NodeHelpPage that caused left sidebar to
change when selecting different graph nodes since we want to prioritise
right panel for this behavior
- Add telemetry tracking for node library help button to identify how
frequently this is used
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8110-Decouple-node-help-between-sidebar-and-right-panel-2ea6d73d365081a9b3afd25aa51b34bd)
by [Unito](https://www.unito.io)
Under some circumstances, subgraph widgets in the properties panel would
have incorrect drag state
- Debouncing list state would cause a race condition where a double
click would initiate a drag on an element that no longer exists
- This is solved by removing the debounce.
- Right clicking on widgets would initiate a drag
- Dragging now explicity requires a primary button click.
Additionally, drag is now ended on `pointercancel`. This is purely for
safety and may not actually be required.
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8114-Fix-dragndrop-subgraph-widgets-sticking-to-cursor-2ea6d73d365081f08515e8231bd87bdc)
by [Unito](https://www.unito.io)
#8074 included some refactoring to the asset dialogue to ensure that it
wouldn't pop up multiple times in vue mode
But moving the openModal function to be contained in options means that
`this` is no longer the widget, but instead the options object. This is
fixed by requiring that widget be explicitly passed as a parameter.
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8117-Fix-asset-selection-in-litegraph-2eb6d73d36508176b5a3f6d21964be39)
by [Unito](https://www.unito.io)
## Summary
- Fixes issue where locale commits would cancel in-progress E2E tests on
release PRs
- E2E tests now run **once** after i18n workflow completes for
version-bump PRs
## Changes
1. **Modified `ci-tests-e2e.yaml`**:
- Added `workflow_call` trigger with `ref` and `pr_number` inputs
- Added skip condition for version-bump PRs on `pull_request` trigger
- Updated checkout steps to use `inputs.ref` when called via
`workflow_call`
2. **Created `ci-tests-e2e-release.yaml`**:
- Triggers on `workflow_run` completion of `i18n: Update Core`
- Only runs for version-bump PRs from main repo
- Calls original E2E workflow via `workflow_call` (no job duplication)
## How it works
**Regular PRs:** `CI: Tests E2E` runs normally via `pull_request`
trigger
**Version-bump PRs:**
1. `CI: Tests E2E` skips (setup job condition fails)
2. `i18n: Update Core` runs and commits locale updates
3. `CI: Tests E2E (Release PRs)` triggers after i18n completes
4. Calls `CI: Tests E2E` via `workflow_call`
5. E2E tests run to completion without cancellation
## Test plan
- [x] Tested workflow_call chain on fork
- [x] Verify version-bump PR skips regular E2E workflow
- [x] Verify E2E runs after i18n completes on next release
Fixes the issue identified during v1.38.2 release where locale commits
caused E2E tests to restart multiple times.
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8091-fix-run-E2E-tests-after-i18n-completes-on-release-PRs-2ea6d73d36508151a315ed1f415afcc6)
by [Unito](https://www.unito.io)
## Summary
Simplifies the Manager dialog by consolidating components and using
BaseModalLayout with v-model support for right panel state.
## Changes
- **Consolidation**: Merged ManagerDialogContent, ManagerHeader,
ManagerNavSidebar, RegistrySearchBar, and SearchFilterDropdown into
single ManagerDialog component
- **Right panel**: Added v-model:rightPanelOpen to BaseModalLayout for
external panel state control; clicking a node card now auto-opens the
info panel
- **Cleanup**: Removed unused useResponsiveCollapse composable, TabItem
and SearchOption types
- **UI tweaks**: Moved action buttons (Install All/Update All) from
header-right-area to contentFilter area
[manager-capture.webm](https://github.com/user-attachments/assets/2dd6092a-965d-4885-8ba6-6a2cc51f024a)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8041-refactor-Manager-dialog-simplification-2e86d73d3650815ba699e49a2748b682)
by [Unito](https://www.unito.io)
---------
Co-authored-by: GitHub Action <action@github.com>
Co-authored-by: github-actions <github-actions@github.com>
## Summary
Removes which is dead code left over from the Jobs API migration.
## Details
This fixture file:
- References legacy and types that were removed in the Jobs API
migration
- Is not referenced anywhere in the codebase
- Cannot be used since the types it imports no longer exist
## Related PRs
Follow-up cleanup to the Jobs API migration:
- #7169 - Add Jobs API infrastructure (PR 1 of 3)
- #7170 - Migrate to Jobs API (PR 2 of 3)
- #7650 - Encapsulate error extraction in TaskItemImpl getters
## Testing
- ✅ Typecheck passes
- ✅ No references to this file in the codebase
- ✅ File imports types that no longer exist (cannot be used)
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8099-chore-remove-dead-browser-test-fixture-after-Jobs-API-migration-2ea6d73d36508172be89c9c5a74b33ee)
by [Unito](https://www.unito.io)
Copying a subgraph which contains primitive nodes would cause the
primitives to fail to initialize. This was caused because they did not
have their `onGraphConfigured` and `onAfterGraphConfigured` callbacks
applied.
There's already a copy of `forEachNode` in
`@/utils/graphTraversalUtil.ts`, but the method is small and I want to
avoid litegraph referencing outside code.
See also #6606, where a similar fix was needed
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8094-Fix-copypasted-primitives-inside-subgraphs-2ea6d73d365081f189f1ea4c9248f5ed)
by [Unito](https://www.unito.io)
## Summary
- Add `errorMessage` and `executionError` getters to `TaskItemImpl` that
extract error info from status messages
- Update `useJobErrorReporting` composable to use these getters instead
of standalone function
- Remove the standalone `extractExecutionError` function
This encapsulates error extraction within `TaskItemImpl`, preparing for
the Jobs API migration where the underlying data format will change but
the getter interface will remain stable.
## Test plan
- [x] All existing tests pass
- [x] New tests added for `TaskItemImpl.errorMessage` and
`TaskItemImpl.executionError` getters
- [x] TypeScript, lint, and knip checks pass
🤖 Generated with [Claude Code](https://claude.com/claude-code)
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-7650-refactor-encapsulate-error-extraction-in-TaskItemImpl-getters-2ce6d73d365081caae33dcc7e1e07720)
by [Unito](https://www.unito.io)
---------
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Christian Byrne <cbyrne@comfy.org>
## Summary
- Type `onExecuted` callbacks with `NodeExecutionOutput` in saveMesh.ts
and uploadAudio.ts
- Type composable parameters and return values properly
(useLoad3dViewer, useImageMenuOptions, useJobMenu, useResultGallery,
useContextMenuTranslation)
- Type `taskRef` as `TaskItemImpl` with updated test mocks
- Fix error catch and index signature patterns without `any`
- Add `NodeOutputWith<T>` generic helper for typed access to passthrough
properties on `NodeExecutionOutput`
## Test plan
- [x] `pnpm typecheck` passes
- [x] `pnpm lint` passes
- [x] Unit tests pass for affected files
- [x] Sourcegraph checks confirm no external usage of modified types
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8083-Road-to-No-Explicit-Any-Part-6-Composables-and-Extensions-2e96d73d3650810fb033d745bf88a22b)
by [Unito](https://www.unito.io)
## Summary
Add TagsInput component based on shadcn-vue/Reka UI primitives with a
click-to-edit UX pattern.
## Features
- **Click-to-edit behavior**: Starts in read-only state; clicking
enables editing and focuses input; clicking outside exits edit mode
- **Disabled state**: When `disabled=true`, component is completely
inert
- **Empty state placeholder**: Shows input placeholder when tag list is
empty
- **Animated transitions**: Delete button animates when toggling edit
mode
---------
Co-authored-by: Amp <amp@ampcode.com>
## Summary
Discussion here:
https://comfy-organization.slack.com/archives/C0A0XANFJRE/p1764899027465379
Implement: Subscription tier query parameter for direct checkout flow
Example button link: `/cloud/subscribe?tier=standard`
`tier` could be `standard`, `creator` or `pro`
`cycle` could be `monthly` or `yearly`. it is optional, and `monthly` by
default.
<!-- One sentence describing what changed and why. -->
## Changes
- **What**: <!-- Core functionality added/modified -->
- Add a landing page called `CloudSubscriptionRedirectView.vue` to
handling the subscription tier button link parameter
- Extract subscription handling logic from `PriceTable.vue`
- **Breaking**: <!-- Any breaking changes (if none, remove this line)
-->
- Code change touched `PriceTable.vue`
- **Dependencies**: <!-- New dependencies (if none, remove this line)
-->
## Review Focus
- link will redirect to login url, when cloud app not login
- after login, the cloud app will redirect to CloudSubscriptionRedirect
page
- wait for several seconds, the cloud app will be redirected to checkout
page
<!-- Critical design decisions or edge cases that need attention -->
<!-- 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-7553-feat-handling-subscription-tier-button-link-parameter-2cb6d73d365081ee9580e89090248300)
by [Unito](https://www.unito.io)
---------
Co-authored-by: GitHub Action <action@github.com>
A major, full rewrite of linear mode, now under the name "Simple Mode".
- Fixes widget styling
- Adds a new simplified history
- Adds support for non-image outputs
- Supports right sidebar
- Allows and panning on the output image preview
- Provides support for drag and drop zones
- Moves workflow notes into a popover.
- Allows scrolling through outputs with Ctrl+scroll or arrow keys
The primary means of accessing Simple Mode is a toggle button on the
bottom right. This button is only shown if a feature flag is enabled, or
the user has already seen linear mode during the current session. Simple
Mode can also be accessed by
- Using the toggle linear mode keybind
- Loading a workflow that that was saved in Simple Mode workflow
- Loading a template url with appropriate parameter
<img width="1790" height="1387" alt="image"
src="https://github.com/user-attachments/assets/d86a4a41-dfbf-41e7-a6d9-146473005606"
/>
Known issues:
- Outputs on cloud are not filtered to those produced by the current
workflow.
- Output filtering has been globally disabled for consistency
- Outputs will load more items on scroll, but does not unload
- Performance may be reduced on weak devices with very large histories.
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-7734-linear-v2-2d16d73d3650819b8a10f150ff12ea22)
by [Unito](https://www.unito.io)
Most of the features in this pull request are completed and can be
reviewed and merged.
## TODO
- [x] no selection panel
- [x] group selected panel
- [x] tabs
- [x] favorites tab
- [x] global settings tab
- [x] nodes tab
- [x] widget actions menu
- [x] [Bug]: style bugs
- [x] button zoom to the node on canvas.
- [x] rename widgets on widget actions
- [ ] [Bug]: the canvas has not been updated after renaming.
- [x] global settings
- [ ] setting item: "show advanced parameters"
- blocked by other things. skip for now.
- [x] setting item: show toolbox on selection
- [x] setting item: nodes 2.0
- [ ] setting item: "background color"
- blocked by other things. skip for now.
- [x] setting item: grid spacing
- [x] setting item: snap nodes to grid
- [x] setting item: link shape
- [x] setting item: show connected links
- [x] form style reuses the form style of node widgets
- [x] group node cases
- [x] group node settings
- [x] show all nodes in group
- [x] show frame name on nodes when multiple selections are made
- [x] group multiple selections
- [x] [Bug]: nodes without widgets cannot display the location and their
group
- [x] [Bug]: labels layout
- [x] favorites
- [x] the indicator on widgets
- [x] favorite and unfavorite buttons on widgets
- [x] [Bug]: show node name in favorite widgets + improve labels layout
- [ ] [Bug]: After canceling the like, the like list will not be updated
immediately.
- [x] [Bug]: The favorite function does not work for the project on
Subgraph.
- [x] subgraph
- [x] add the node name from where this parameter comes from when node
is subgraph
- [x] show and hide directly on Inputs
- [x] some bugs need to be fixed.
- [x] advanced widgets
- [x] button: show advanced inputs
- Clicking button expands the "Advanced Inputs" section on the right
side panel, regardless of whether the panel is open or not
- [x] [Bug]: style bugs
- [x] advanced inputs section when node is subgraph
- [x] inputs tab rearranging
- [x] favorited inputs rearranging
- [x] subgraph inputs rearranging
- [ ] review and reconstruction to improve complexity and architecture
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-7812-feat-right-side-panel-favorites-no-selection-state-and-more-2da6d73d36508134b503d676f9b3d248)
by [Unito](https://www.unito.io)
---------
Co-authored-by: GitHub Action <action@github.com>
Co-authored-by: github-actions <github-actions@github.com>
Co-authored-by: bymyself <cbyrne@comfy.org>
PR #7004 added a setting to disable version warnings in e2e tests, but
it wasn't working on release branches. The issue was a race condition
(hypothesis): the version check ran before settings finished loading
from the backend, so the DisableWarnings setting read its default value
(false) instead of the configured value (true).
Fixed by making the warningsDisabled check reactive so it updates when
settings load and adding `nextTick` (settings are loaded, but ref
updates flush in a microtask. The immediate `whenever` runs before that
flush, so computeds may see stale/default values -- `nextTick` waits for
reactive microtasks to flush, so computeds will be correct. It's fine).
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8036-fix-version-mismatch-warning-appearing-in-Playwright-tests-despite-DisableWarnings-setti-2e86d73d36508132b4d1fd73ade76e63)
by [Unito](https://www.unito.io)
## Summary
- Remove all remaining `any` types from LiteGraph module
- Define proper `Panel` interface for createPanel function and related
callbacks
- Type `SlotTypeDefaultNodeOpts` for slot type defaults configuration
- Fix type inference issues in sendActionToCanvas and contextMenuCompat
## Changes
- **SubgraphIONodeBase.ts**: Remove unnecessary `as any` cast in
showSlotContextMenu
- **interfaces.ts**: Add Panel, PanelButton, PanelWidget,
PanelWidgetOptions, PanelWidgetCallback types
- **LGraphNode.ts**: Type panel callbacks (onShowCustomPanelInfo,
onAddPropertyToPanel) and onGetPropertyInfo
- **LGraphCanvas.ts**:
- Type node_panel and options_panel as Panel
- Type inner_clicked callback parameters
- Type local variables (nodeNewType, nodeNewOpts, prevent_timeout, iS,
sIn, sOut)
- Add SlotTypeDefaultNodeOpts interface for slot type configuration
- **LGraph.ts**: Fix sendActionToCanvas type inference with proper array
handling
- **contextMenuCompat.ts**: Add null guard for newImpl parameter
## Test plan
- [x] pnpm typecheck passes
- [x] pnpm lint passes
- [x] knip passes (no unused exports)
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-7970-Road-to-No-Explicit-Any-Part-4-LiteGraph-Cleanup-2e66d73d3650812c939bf9b2cb0ff2f5)
by [Unito](https://www.unito.io)
---------
Co-authored-by: GitHub Action <action@github.com>
Adds support for entering math inside number widgets in vue mode

Migrates components to simple html elements (div and button) by
borrowing styling from the (reverted) reka-ui migration in #6985. The
existing (evil) litegraph eval code is extracted as a utility function
and reused.
This PR means we're entirely writing our own NumberField.
Also adds support for scrubbing widgets like in litegraph

### Known Issue
- Scrubbing causes text to be highlighted, ~~starting a scrub from
highlighted text will instead drag the text~~.
- It seems this can only be prevented with `pointerdown.prevent`, but
this requires a manual `input.focus()` which does not place the cursor
at location of mouse click.
(Obligatory: _It won't do you a bit of good to review math_)
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-7759-Implement-vue-math-2d46d73d365081b9acd4d6422669016e)
by [Unito](https://www.unito.io)
---------
Co-authored-by: github-actions <github-actions@github.com>
Co-authored-by: DrJKL <DrJKL0424@gmail.com>
## Summary
- Remove `setup-frontend` action from `merge-reports` job
- Use `npx @playwright/test` instead of `pnpm exec playwright`
## Why
The `merge-reports` job was spending ~16-18s on `pnpm install` just to
run a CLI command that takes ~3s. Since `npx` is pre-installed on GitHub
runners, we can eliminate the setup overhead entirely.
**Expected savings: ~16-18 seconds per CI run**
## Test Plan
- [ ] Verify merge-reports job completes successfully
- [ ] Verify HTML report is generated and uploaded correctly
- [ ] Compare job timing before/after
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8030-perf-ci-remove-unnecessary-pnpm-install-from-merge-reports-job-2e76d73d36508134b3e6c11726170f64)
by [Unito](https://www.unito.io)
## Summary
Refactors asset download state management and fixes asset deletion UI
issues.
## Changes
### assetDownloadStore simplification
- Replace `pendingModelTypes` Map with `modelType` stored directly on
`AssetDownload`
- Replace `completedDownloads` array with single `lastCompletedDownload`
ref
- `trackDownload()` now creates a placeholder entry immediately
- Use VueUse `whenever` instead of `watch` for cleaner null handling
### Asset refresh on download completion
- Refresh all relevant caches when a download completes:
- Node type caches (e.g., "CheckpointLoaderSimple")
- Tag caches (e.g., "tag:checkpoints")
- "All Models" cache ("tag:models")
### Asset deletion fix
- Remove local `deletedLocal` state that caused blank grid cells
- Emit `deleted` event from AssetCard → AssetGrid → AssetBrowserModal
- Trigger store refresh on deletion to properly remove the asset from
the grid
## Testing
- Added test for out-of-order websocket message handling
- All existing tests pass
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-7974-refactor-simplify-asset-download-state-and-fix-deletion-UI-2e76d73d365081c69bcde9150a0d460c)
by [Unito](https://www.unito.io)
---------
Co-authored-by: Amp <amp@ampcode.com>
## Summary
Addresses UI/UX feedback on the Upload Model Dialog (BYOM feature).
## Changes
1. **Standardize link styling** - Use consistent `text-muted-foreground
underline` for all links in both URL input variants
2. **Increase warning/example text font size** - Changed from 12px
(`text-xs`) to 14px (`text-sm`) for better readability
3. **Fix padding inconsistency** - Aligned padding between model name
box and SingleSelect dropdown; moved "Not sure?" help text inline with
the label
4. **Add "Upload Another" button** - Allows users to upload multiple
models without closing and reopening the dialog
## Testing
- Verified link styling consistency across both Civitai and generic URL
input components
- Confirmed padding alignment in confirmation step
- Tested Upload Another button resets wizard to step 1
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-7969-fix-upload-model-UI-UX-improvements-for-Upload-Model-Dialog-2e66d73d3650815c8184cedb3d02672d)
by [Unito](https://www.unito.io)
## Task: Add English translations for all new localized strings
### Step 1: Identify new translation keys
Find all translation keys that were added in the current branch's changes. These keys appear as arguments to translation functions: `t()`, `st()`, `$t()`, or similar i18n functions.
### Step 2: Add translations to English locale file
For each new translation key found, add the corresponding English text to the file `src/locales/en/main.json`.
### Key-to-JSON mapping rules:
- Translation keys use dot notation to represent nested JSON structure
- Convert dot notation to nested JSON objects when adding to the locale file
- Example: The key `g.user.name` maps to:
@@ -23,6 +26,7 @@ For each new translation key found, add the corresponding English text to the fi
```
### Important notes:
1. **Only modify the English locale file** (`src/locales/en/main.json`)
2. **Do not modify other locale files** - translations for other languages are automatically generated by the `i18n.yaml` workflow
3. **Exception for manual translations**: Only add translations to non-English locale files if:
@@ -30,6 +34,7 @@ For each new translation key found, add the corresponding English text to the fi
- The automated translation would likely be incorrect due to technical terminology or context-specific meaning
### Example workflow:
1. If you added `t('settings.advanced.enable')` in a Vue component
@@ -100,9 +105,9 @@ Intelligently load only relevant knowledge:
1. Use GitHub API to discover available knowledge folders: `https://api.github.com/repos/Comfy-Org/comfy-claude-prompt-library/contents/.claude/knowledge`
2. For each knowledge folder, check if it's relevant by searching for the folder name in:
- Changed file paths
- PR title
- PR body
- Changed file paths
- PR title
- PR body
3. If relevant, download all files from that knowledge folder
### Step 2.4: Load Validation Rules
@@ -193,12 +198,14 @@ Consider:
For each issue found, create a concise inline comment with this structure:
Repeat this process for every issue you find in the PR.
@@ -282,9 +294,9 @@ Analyze the PR to determine its type:
1. Extract PR title and body from pr_info.json
2. Count files, additions, and deletions
3. Determine PR type:
- Feature: Check for tests, documentation, backward compatibility
- Bug fix: Verify root cause addressed, includes regression tests
- Refactor: Ensure behavior preservation, tests still pass
- Feature: Check for tests, documentation, backward compatibility
- Bug fix: Verify root cause addressed, includes regression tests
- Refactor: Ensure behavior preservation, tests still pass
## Phase 7: Generate Comprehensive Summary
@@ -292,16 +304,17 @@ After ALL inline comments are posted, create a summary:
1. Calculate total issues by category and severity
2. Use `gh pr review $PR_NUMBER --comment` to post a summary with:
- Review disclaimer
- Issue distribution (counts by severity)
- Category breakdown
- Key findings for each category
- Positive observations
- References to guidelines
- Next steps
- Review disclaimer
- Issue distribution (counts by severity)
- Category breakdown
- Key findings for each category
- Positive observations
- References to guidelines
- Next steps
Include in the summary:
```
# Comprehensive PR Review
This review is generated by Claude. It may not always be accurate, as with human reviewers. If you believe that any of the comments are invalid or incorrect, please state why for each. For others, please implement the changes in one way or another.
@@ -312,12 +325,14 @@ This review is generated by Claude. It may not always be accurate, as with human
**Impact**: [X] additions, [Y] deletions across [Z] files
### Issue Distribution
- Critical: [CRITICAL_COUNT]
- High: [HIGH_COUNT]
- Medium: [MEDIUM_COUNT]
- Low: [LOW_COUNT]
### Category Breakdown
- Architecture: [ARCHITECTURE_ISSUES] issues
- Security: [SECURITY_ISSUES] issues
- Performance: [PERFORMANCE_ISSUES] issues
@@ -326,33 +341,42 @@ This review is generated by Claude. It may not always be accurate, as with human
## Key Findings
### Architecture & Design
[Detailed architectural analysis based on repository patterns]
This command creates patch/hotfix releases for ComfyUI Frontend by backporting fixes to stable core branches. It handles both automated backports (preferred) and manual cherry-picking (fallback).
description: 'Writes Playwright e2e tests for ComfyUI_frontend. Use when creating, modifying, or debugging browser tests. Triggers on: playwright, e2e test, browser test, spec file.'
---
# Writing Playwright Tests for ComfyUI_frontend
## Golden Rules
1.**ALWAYS look at existing tests first.** Search `browser_tests/tests/` for similar patterns before writing new tests.
2.**ALWAYS read the fixture code.** The APIs are in `browser_tests/fixtures/` - read them directly instead of guessing.
3.**Use premade JSON workflow assets** instead of building workflows programmatically.
- Assets live in `browser_tests/assets/`
- Load with `await comfyPage.workflow.loadWorkflow('feature/my_workflow')`
- Create new assets by starting with `browser_tests/assets/default.json` and manually editing the JSON to match your desired graph state
## Vue Nodes vs LiteGraph: Decision Guide
Choose based on **what you're testing**, not personal preference:
- label:I am running the latest version of ComfyUI
required:true
- label:I have searched existing issues to make sure this isn't a duplicate
required:true
- label:I have tested with all custom nodes disabled ([see how](https://docs.comfy.org/troubleshooting/custom-node-issues#step-1%3A-test-with-all-custom-nodes-disabled))
required:true
- label:I have custom nodes enabled
- type:textarea
id:description
@@ -60,7 +57,7 @@ body:
attributes:
label:ComfyUI Frontend Version
description:Found in Settings > About (e.g., "1.3.45")
body: '## 🔧 Auto-fixes Applied\n\nThis PR has been automatically updated to fix linting and formatting issues.\n\n**⚠️ Important**: Your local branch is now behind. Run `git pull` before making additional changes to avoid conflicts.\n\n### Changes made:\n- ESLint auto-fixes\n- Prettier formatting'
body: '## 🔧 Auto-fixes Applied\n\nThis PR has been automatically updated to fix linting and formatting issues.\n\n**⚠️ Important**: Your local branch is now behind. Run `git pull` before making additional changes to avoid conflicts.\n\n### Changes made:\n- ESLint auto-fixes\n- Oxfmt formatting'
@@ -83,8 +85,7 @@ Make sure your desktop machine and touch device are on the same network. On your
navigate to `http://<server_ip>:5173` (e.g. `http://192.168.2.20:5173` here), to access the ComfyUI frontend.
> ⚠️ IMPORTANT:
The dev server will NOT load JavaScript extensions from custom nodes. Only core extensions (built into the frontend) will be loaded. This is because the shim system that allows custom node JavaScript to import frontend modules only works in production builds. Python custom nodes still function normally. See [Extension Development Guide](docs/extensions/development.md) for details and workarounds. And See [Extension Overview](docs/extensions/README.md) for extensions overview.
> The dev server will NOT load JavaScript extensions from custom nodes. Only core extensions (built into the frontend) will be loaded. This is because the shim system that allows custom node JavaScript to import frontend modules only works in production builds. Python custom nodes still function normally. See [Extension Development Guide](docs/extensions/development.md) for details and workarounds. And See [Extension Overview](docs/extensions/README.md) for extensions overview.
## Development Workflow
@@ -109,7 +110,6 @@ When you fix a bug that affects a version in feature freeze, we use an automated
2. Before merging, add these labels to your PR:
- `needs-backport` - triggers the automated backport workflow
- `core/1.24` - targets the `core/1.24` release candidate branch
3. Merge your PR normally
4. The automated workflow will:
- Create a new branch from `core/1.24`
@@ -127,6 +127,7 @@ When you fix a bug that affects a version in feature freeze, we use an automated
#### Handling Conflicts
If the automated cherry-pick fails due to conflicts, the workflow will comment on your PR with:
- The list of conflicting files
- Instructions to manually cherry-pick to the release candidate branch
@@ -200,7 +201,7 @@ The project supports three types of icons, all with automatic imports (no manual
2. **Iconify Icons** - 200,000+ icons from various libraries: `<i class="icon-[lucide--settings]" />`, `<i class="icon-[mdi--folder]" />`
3. **Custom Icons** - Your own SVG icons: `<i-comfy:workflow />`
Icons are powered by the unplugin-icons system, which automatically discovers and imports icons as Vue components. Custom icons are stored in `packages/design-system/src/icons/` and processed by `packages/design-system/src/iconCollection.ts` with automatic validation.
Icons are powered by the unplugin-icons system, which automatically discovers and imports icons as Vue components. Tailwind CSS icon classes (`icon-[comfy--template]`) are provided by `@iconify/tailwind4`, configured in `packages/design-system/src/css/style.css`. Custom icons are stored in `packages/design-system/src/icons/` and loaded via `from-folder` at build time.
For detailed instructions and code examples, see [packages/design-system/src/icons/README.md](packages/design-system/src/icons/README.md).
@@ -241,6 +242,7 @@ The original litegraph repository (https://github.com/Comfy-Org/litegraph.js) is
## Questions?
If you have questions about contributing:
- Check existing issues and discussions
- Ask in our [Discord](https://discord.com/invite/comfyorg)
@@ -47,6 +46,7 @@ The project follows a structured release process for each minor version, consist
- Version is finalized and made available to all users
### Nightly Releases
Nightly releases are published daily at [https://github.com/Comfy-Org/ComfyUI_frontend/releases](https://github.com/Comfy-Org/ComfyUI_frontend/releases).
To use the latest nightly release, add the following command line argument to your ComfyUI launch script:
@@ -56,16 +56,17 @@ To use the latest nightly release, add the following command line argument to yo
```
## Overlapping Release Cycles
The development of successive minor versions overlaps. For example, while version 1.1 is in feature freeze, development for version 1.2 begins simultaneously. Each feature has approximately 4 weeks from merge to ComfyUI stable release (2 weeks on main, 2 weeks frozen on RC).
### Example Release Cycle
| Week | Date Range | Version 1.1 | Version 1.2 | Version 1.3 | Patch Releases |
See `@docs/guidance/playwright.md` for Playwright best practices (auto-loaded for `*.spec.ts`).
## Directory Structure
```text
browser_tests/
├── assets/ - Test data (JSON workflows, images)
├── fixtures/
│ ├── ComfyPage.ts - Main fixture (delegates to helpers)
│ ├── ComfyMouse.ts - Mouse interaction helper
│ ├── VueNodeHelpers.ts - Vue Nodes 2.0 helpers
│ ├── selectors.ts - Centralized TestIds
│ ├── components/ - Page object components
│ │ ├── ContextMenu.ts
│ │ ├── SettingDialog.ts
│ │ ├── SidebarTab.ts
│ │ └── Topbar.ts
│ ├── helpers/ - Focused helper classes
│ │ ├── CanvasHelper.ts
│ │ ├── CommandHelper.ts
│ │ ├── KeyboardHelper.ts
│ │ ├── NodeOperationsHelper.ts
│ │ ├── SettingsHelper.ts
│ │ ├── WorkflowHelper.ts
│ │ └── ...
│ └── utils/ - Utility functions
├── helpers/ - Test-specific utilities
└── tests/ - Test files (*.spec.ts)
```
## After Making Changes
- Run `pnpm typecheck:browser` after modifying TypeScript files in this directory
- Run `pnpm exec eslint browser_tests/path/to/file.ts` to lint specific files
- Run `pnpm exec oxlint browser_tests/path/to/file.ts` to check with oxlint
## Skill Documentation
A Playwright test-writing skill exists at `.claude/skills/writing-playwright-tests/SKILL.md`.
The skill documents **meta-level guidance only** (gotchas, anti-patterns, decision guides). It does **not** duplicate fixture APIs - agents should read the fixture code directly in `browser_tests/fixtures/`.
@@ -17,9 +17,10 @@ Without this flag, parallel tests will conflict and fail randomly.
### ComfyUI devtools
ComfyUI_devtools is included in this repository under `tools/devtools/`. During CI/CD, these files are automatically copied to the `custom_nodes` directory.
_ComfyUI_devtools adds additional API endpoints and nodes to ComfyUI for browser testing._
ComfyUI_devtools adds additional API endpoints and nodes to ComfyUI for browser testing.
For local development, copy the devtools files to your ComfyUI installation:
Some files were not shown because too many files have changed in this diff
Show More
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.