Compare commits

..

251 Commits

Author SHA1 Message Date
Benjamin Lu
246de7863d Readd unit test 2025-11-03 19:16:32 -08:00
Benjamin Lu
6fa4c7a1a3 Delete unit test 2025-11-03 17:29:10 -08:00
Benjamin Lu
3ba23dde03 Remove uneeded inflight coalescing 2025-11-03 17:17:44 -08:00
Benjamin Lu
3719a46b41 fix(auth/session): dedupe session creation, skip initial token refresh, and wrap extension auth hooks\n\nPrevents concurrent createSession calls and avoids treating the initial onIdTokenChanged as a refresh. This eliminates a race where useSessionCookie.createSession() could run before a Firebase ID token existed, causing:\n\n Error: No auth header available for session creation\n\nAlso wraps extension auth hooks with async error handling to avoid unhandled rejections.\n\nRefs: Sentry issue 6990347926 2025-11-03 16:34:58 -08:00
AustinMroz
dffb07c745 Fix badge overflow (#6516)
Prevents node badges from overflowing out of the node header in vue mode
<img width="1157" height="593" alt="image"
src="https://github.com/user-attachments/assets/005e13e7-8bcc-46c4-9531-e6eba237cf60"
/>

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6516-Fix-badge-overflow-29e6d73d365081328250cbeb3ebbb670)
by [Unito](https://www.unito.io)

---------

Co-authored-by: github-actions <github-actions@github.com>
2025-11-01 13:49:33 -07:00
Benjamin Lu
72389637ed feat(telemetry): track API credit top-up success via audit events (#6500)
Summary
- Add TelemetryEvents.API_CREDIT_TOPUP_SUCCEEDED and provider method
trackApiCreditTopupSucceeded
- Introduce topupTrackerStore to persist pending top-ups per user
(localStorage) and reconcile against recent audit logs
- Hook purchase flow to start tracking before opening Stripe checkout
- Reconcile after fetching audit events (UsageLogsTable) and after
fetchBalance, then emit telemetry, refresh balance, and clear pending
- Minor refactor in customerEventsService to return awaited result

Implementation details
- Matching strategy:
  - Event type: credit_added
  - Time window: createdAt between top-up start time and +24h
  - Amount: if known, e.params.amount must equal expected cents
- Cross-tab/user changes: synchronize via storage event and userId
watcher

Limitations / Follow-up
- Reconciliation fetches only page 1 (limit 10) of events; in
high-volume cases, a recent credit_added could fall outside the first
page
- The window and pagination issue will be "resolved by a followup PR to
core and cloud"

Files touched
- src/stores/topupTrackerStore.ts (new)
- src/components/dialog/content/setting/UsageLogsTable.vue
- src/composables/auth/useFirebaseAuthActions.ts
- src/platform/telemetry/providers/cloud/MixpanelTelemetryProvider.ts
- src/platform/telemetry/types.ts
- src/services/customerEventsService.ts

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6500-feat-telemetry-track-API-credit-top-up-success-via-audit-events-29e6d73d365081169941efae70cf71fe)
by [Unito](https://www.unito.io)

---------

Co-authored-by: Christian Byrne <chrbyrne96@gmail.com>
Co-authored-by: Claude <noreply@anthropic.com>
2025-11-01 13:17:41 -07:00
Arjan Singh
de535269ee feat(telemetry): help center and workflow creation (#6505)
## Summary

For Cloud distribution:
1. Track help center usage
2. Track workflow creation

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6505-feat-telemetry-help-center-and-workflow-creation-29e6d73d36508185af8ccbf19d5af9e7)
by [Unito](https://www.unito.io)

---------

Co-authored-by: Christian Byrne <chrbyrne96@gmail.com>
Co-authored-by: Christian Byrne <cbyrne@comfy.org>
Co-authored-by: Claude <noreply@anthropic.com>
2025-11-01 12:21:50 -07:00
Benjamin Lu
dad2d803df feat(telemetry): add unified run_triggered event for all run initiations (#6499)
Summary
- Add new telemetry event: `app:run_triggered` with `{ trigger_source:
'button' | 'keybinding' | 'menu' }`.
- Instrument all run initiation paths:
- UI Queue button emits `run_triggered` (source `button`) and keeps
emitting `run_button_click` for UI-only tracking.
- Keybindings (Ctrl+Enter / Ctrl+Shift+Enter) emit `run_triggered`
(source `keybinding`).
- Menus (menubar + legacy menu buttons) emit `run_triggered` (source
`menu`).
- Mixpanel provider now supports `trackRunTriggered` and forwards
`run_triggered`.
- `execution_start` tracking remains unchanged.

Motivation
GTM observed more `execution_start` events than `run_button_click`. This
change clarifies attribution by adding a unified event across all
triggers while preserving the UI-only `run_button_click` metric.

Files
- src/platform/telemetry/types.ts: Add `RunTriggeredMetadata`,
`RUN_TRIGGERED`, provider method signature.
- src/platform/telemetry/providers/cloud/MixpanelTelemetryProvider.ts:
Implement `trackRunTriggered`.
- src/components/actionbar/ComfyRunButton/ComfyQueueButton.vue: Emit
`run_triggered` on button path.
- src/services/keybindingService.ts: Emit `run_triggered` when queue
commands are invoked via keybindings.
- src/stores/menuItemStore.ts: Emit `run_triggered` for queue commands
invoked via menubar.
- src/scripts/ui.ts: Emit `run_triggered` for legacy menu queue buttons.

Notes
- `run_button_click` continues to represent UI button presses only.
- `run_triggered` now represents all user-initiated runs with clear
source attribution.

QA
- Cloud build: verify `app:run_triggered` appears with the correct
`trigger_source` for button, keybinding, and menu triggers.
- Verify `app:run_button_click` only fires for the button path.
- Confirm `execution_start` still tracks as before.

If preferred, we can extend `run_triggered` with additional fields
(e.g., queue mode, batchCount) in a follow-up.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6499-feat-telemetry-add-unified-run_triggered-event-for-all-run-initiations-29e6d73d3650819fb481d3e0e925c50f)
by [Unito](https://www.unito.io)

---------

Co-authored-by: GitHub Action <action@github.com>
2025-11-01 02:42:36 -07:00
Christian Byrne
f2aea9c823 fix: Display appropriate title for unsubscribed state (#6396)
## Summary

Fixes misleading title display in unsubscribed state of the
SubscriptionPanel.

### Changes Made

- **Conditional Title Logic**: Added state-aware title display in
`SubscriptionPanel.vue`
- **New Translation Key**: Added `subscription.titleUnsubscribed` →
"Subscribe to Comfy Cloud"
- **Prevents Confusion**: Eliminates "account created" or other
inappropriate messages showing as titles

### Behavior

- **Subscribed Users**: See "Subscription" (existing behavior)
- **Unsubscribed Users**: See "Subscribe to Comfy Cloud" (new, clearer
messaging)

### Testing

-  All existing SubscriptionPanel tests pass
-  TypeScript compilation successful
-  Linting passes
-  Follows translation patterns

### Context

This addresses the UX issue discovered on testcloud.comfy.org where
unsubscribed users were seeing misleading title text instead of clear
subscription prompts.

🤖 Generated with [Claude Code](https://claude.ai/code)

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6396-fix-Display-appropriate-title-for-unsubscribed-state-29c6d73d3650818a8136f6a99e312651)
by [Unito](https://www.unito.io)

Co-authored-by: Claude <noreply@anthropic.com>
2025-11-01 02:40:17 -07:00
Christian Byrne
c8e24190cd Cloud/gate topup on subscription (#6399)
## Summary

Gate the Top up buttons in the user dropdown and credits page of the
settings.


┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6399-Cloud-gate-topup-on-subscription-29c6d73d36508127866cdcbcfda66c46)
by [Unito](https://www.unito.io)

---------

Co-authored-by: DrJKL <DrJKL0424@gmail.com>
2025-11-01 01:37:00 -07:00
Christian Byrne
6fc5748a32 fix: set transparent border for gradient subscribe button (#6510)
## Summary
- Fixed the subscribe to run button border to be transparent, allowing
the gradient background to display properly
- Used PrimeVue's `pt` (passthrough) props to customize the border color

## Context
The subscribe to run button was recently updated to use a gradient
background via inline styles, but the border was still using the default
color which clashed with the gradient.

## Changes
- Added `:pt` prop to the Button component in `SubscribeToRun.vue`
- Set `borderColor: 'transparent'` on the root element to remove the
default border color

## Test Plan
- [ ] Visual inspection of the subscribe to run button
- [ ] Verify the gradient background displays without border color clash
- [ ] Ensure the button still functions correctly when clicked

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6510-fix-set-transparent-border-for-gradient-subscribe-button-29e6d73d3650816d9ecfcb63e2ffb7d3)
by [Unito](https://www.unito.io)

Co-authored-by: Christian Byrne <chrbyrne96@gmail.com>
2025-11-01 01:35:59 -07:00
Terry Jia
99958d3aa9 add api node link (#6494)
## Summary

add api node page link on credit and subscription pannel

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6494-add-api-node-link-29e6d73d365081aaa2f4d1647683ecda)
by [Unito](https://www.unito.io)
2025-11-01 01:12:31 -07:00
Benjamin Lu
1cdfc6934a chore(pnpm): allow building @sentry/cli for sourcemap uploads (#6491)
- Add `@sentry/cli` to `onlyBuiltDependencies` in `pnpm-workspace.yaml`
so its postinstall can run.
- Ensures the Sentry Vite plugin can upload sourcemaps in cloud builds
when Sentry env vars are configured.
- No runtime code changes.

Checks run locally before commit:
- `pnpm lint:fix` ✓
- `pnpm typecheck` ✓

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6491-chore-pnpm-allow-building-sentry-cli-for-sourcemap-uploads-29d6d73d365081bd8548c19ef48d4f61)
by [Unito](https://www.unito.io)
2025-11-01 01:11:37 -07:00
Alexander Piskun
d1108867e2 feat(TextPreviewWidget): add minimal support for [[label|url]] links (#6482)
## Summary

This PR adds a minimal, backward-compatible way to render **labeled
hyperlinks** in node text previews without enabling full Markdown
rendering.

The syntax is:
```
[[Label|https://example.com]]
```

Links open in a new tab and preserve the existing look and behavior of
the widget.

## Motivation

I first implemented a `Markdown`-based version that correctly rendered
`[label](url)` and other inline Markdown. After some consideration, I
have decided against shipping Markdown here because it risks breaking
existing custom nodes that already rely on
`PromptServer.instance.send_progress_text` with the current
`linkifyHtml` --> `nl2br` behavior. The token approach is **smaller**,
safer, and avoids surprises.

## Changes

* No global behavior change.
* No new dependencies.
* Token parsing is opt-in. If a node does not emit `[[label|url]]`,
behavior is unchanged.

## ComfyUI backend changes

PR to ComfyUI with these will come later(as first version of ComfyUI
with these frontend changes should be released), and it will contain:

```python
def _display_text(
    node_cls: type[IO.ComfyNode],
    text: Optional[str],
    *,
    status: Optional[Union[str, int]] = None,
    price: Optional[float] = None,
    results: Optional[Union[list[str], str]] = None,
) -> None:
    display_lines: list[str] = []
    if status:
        display_lines.append(f"Status: {status.capitalize() if isinstance(status, str) else status}")
    if price is not None:
        display_lines.append(f"Price: ${float(price):,.4f}")
    if results:  # New code starts
        if isinstance(results, str):
            display_lines.append(f"Result link: [[1|{results}]]")
        elif len(results) == 1:
            display_lines.append(f"Result link: [[1|{results[0]}]]")
        else:
            links = ", ".join(f"[[{i}|{u}]]" for i, u in enumerate(results, start=1))
            display_lines.append(f"Result links: {links}")  # New code ends
    if text is not None:
        display_lines.append(text)
    if display_lines:
        PromptServer.instance.send_progress_text("\n".join(display_lines), get_node_id(node_cls))
```        

## Screenshots (if applicable)

<img width="692" height="716" alt="Screenshot From 2025-10-31 13-12-54"
src="https://github.com/user-attachments/assets/619b5f70-550c-442f-9cd9-05a95270e533"
/>

<img width="732" height="714" alt="Screenshot From 2025-10-31 13-14-15"
src="https://github.com/user-attachments/assets/836ff87b-a2ac-45ba-842c-b0a4af91c7de"
/>

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6482-feat-TextPreviewWidget-add-minimal-support-for-label-url-links-29d6d73d365081e9ac97dd7f41e85d8f)
by [Unito](https://www.unito.io)
2025-11-01 01:00:56 -07:00
sno
89b073f96f [docs] Add /output.txt to .gitignore (#6456)
## Summary
- Adds `/output.txt` to `.gitignore` to prevent tracking the output file
generated by the weekly-docs-check.yaml workflow

## Rationale
The weekly docs check workflow emits an `output.txt` file that is not
intended to be committed to the repository. This change ensures the file
is properly ignored by git.

## Test plan
- [x] Verify `output.txt` is no longer tracked by git
- [x] Confirm weekly-docs-check workflow still functions correctly

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6456-docs-Add-output-txt-to-gitignore-29c6d73d365081688d99f3855337d887)
by [Unito](https://www.unito.io)
2025-11-01 00:46:34 -07:00
Benjamin Lu
9197119ed8 feat(telemetry): include custom_node_count in run button click event (#6493)
Summary
- Add custom_node_count to app:run_button_click telemetry payload,
aligning with execution_start context naming.
- Keeps event name as app:run_button_click (no rename).

Changes
- src/platform/telemetry/types.ts: add custom_node_count to
RunButtonProperties.
- src/platform/telemetry/providers/cloud/MixpanelTelemetryProvider.ts:
include custom_node_count from execution context in trackRunButton
payload.

Rationale
- execution_start already reports custom_node_count via
ExecutionContext.
- This adds visibility at the moment of run initiation, improving funnel
and pre-execution analysis.

Validation
- Ran pnpm lint:fix and pnpm typecheck locally; both passed.

Notes
- No runtime behavior changes outside telemetry payload.
- No user-facing strings changed.

Requested Reviewers
- Telemetry/analytics owners to confirm property naming and coverage.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6493-feat-telemetry-include-custom_node_count-in-run-button-click-event-29d6d73d365081a5b23bd02e72e0bafd)
by [Unito](https://www.unito.io)
2025-10-31 17:56:35 -07:00
Benjamin Lu
6abff41f08 feat(telemetry): add workflow_opened with open_source and missing node metrics (#6476)
- Adds app:workflow_opened and plumbs open_source across drag/drop,
file-open button, workspace, and templates
- Tracks missing_node_count and missing_node_types for both
workflow_opened and workflow_imported
- Reuses WorkflowOpenSource type for consistency; no breaking changes to
loadGraphData callers (5th param remains options object; openSource
optional)

Validation
- pnpm lint:fix
- pnpm typecheck

Notes
- Telemetry only runs in cloud builds; OSS remains clean.
- loadGraphData telemetry is centralized where missing_node_types is
computed.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6476-feat-telemetry-add-workflow_opened-with-open_source-and-missing-node-metrics-29d6d73d365081f385c0da29958309da)
by [Unito](https://www.unito.io)

---------

Co-authored-by: bymyself <cbyrne@comfy.org>
2025-10-31 14:51:16 -07:00
Simula_r
a537124a9f feat: add telemetry to answer for user failed to find template (#6489)
## Summary

Adds mixpanel telemetry with goal of: "We currently only know when a
user opens a template workflow. But we also want to know if they failed
to find what they want"

Example mixpanel query: 

```
app:template_library_closed
  WHERE template_selected = false AND time_spent_seconds >= 10
  ```

But can drill down further into what filters were selected etc to answer what they were looking for but couldn't find. 

```
 1. Event: app:template_filter_changed
  2. Filter:
- Add formula: "Where user also triggered app:template_library_closed
with template_selected = false in same session"
  3. Breakdown by: search_query
  4. Sort by: Total Count (descending)

  Search Query           Failed Sessions
  -----------------------------------
  "flux video"             45 times
  "sdxl controlnet"     32 times
  "upscaler"               28 times
  (empty/just filter)   20 times
```

```
 Event: app:template_filter_changed
  WHERE filtered_count = 0
    AND user did app:template_library_closed
    with template_selected = false

  Breakdown by: search_query
  ```
  etc.


https://www.notion.so/comfy-org/Number-of-users-who-open-the-template-library-and-where-do-they-click-29b6d73d36508044a595c0fb653ca6dc?source=copy_link

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6489-feat-add-telemetry-to-answer-for-user-failed-to-find-template-29d6d73d365081cdad72fd7c6ada5dc7)
by [Unito](https://www.unito.io)
2025-10-31 14:45:59 -07:00
Terry Jia
e05e988730 update user profile dropdown (#6475)
## Summary

update user profile dropdown

## Screenshots

1. Subscribe to run button and Unsubscribe user panel
<img width="433" height="480" alt="image"
src="https://github.com/user-attachments/assets/bb859481-6405-44df-85ec-9935599c4be0"
/>

2. Subscribed User:
<img width="395" height="479" alt="image"
src="https://github.com/user-attachments/assets/683de2c0-8090-4e9a-ac4e-d211fcee8921"
/>

3. OSS:
<img width="392" height="480" alt="image"
src="https://github.com/user-attachments/assets/7d684c1a-8dee-48dd-8e7f-3c98bd98104d"
/>

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6475-update-user-profile-dropdown-29d6d73d365081ff9e14f9355a9a3bb7)
by [Unito](https://www.unito.io)

---------

Co-authored-by: GitHub Action <action@github.com>
2025-10-31 14:45:02 -07:00
Terry Jia
afa10f7a1e [refactor] refactor load3d (#5765)
Summary

Fully Refactored the Load3D module to improve architecture and
maintainability by consolidating functionality into a
centralized composable pattern and simplifying component structure. and
support VueNodes system

  Changes

- Architecture: Introduced new useLoad3d composable to centralize 3D
loading logic and state
  management
- Component Simplification: Removed redundant components
(Load3DAnimation.vue, Load3DAnimationScene.vue,
  PreviewManager.ts) 
- Support VueNodes
- improve config store
- remove lineart output due Animation doesnot support it, may add it
back later
- remove Preview screen and keep scene in fixed ratio in load3d (not
affect preview3d)
- improve record video feature which will already record video by same
ratio as scene
Need BE change https://github.com/comfyanonymous/ComfyUI/pull/10025


https://github.com/user-attachments/assets/9e038729-84a0-45ad-b0f2-11c57d7e0c9a



┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5765-refactor-refactor-load3d-2796d73d365081728297cc486e2e9052)
by [Unito](https://www.unito.io)
2025-10-31 16:19:35 -04:00
Benjamin Lu
91b5a7de17 feat(telemetry): track total node count, subgraphs, and API-node details in RUN_BUTTON_CLICKED (#6468)
Summary
- Add richer run-button telemetry: total node count, subgraph count,
whether API nodes are present, and unique API node names.
- Add provider method/event for “Add API credit” button clicks.
- Include a one-off script to normalize Mixpanel survey properties
(industry/useCase) for better analytics.

Changes
- Types: extend telemetry payloads
- RunButtonProperties adds total_node_count, subgraph_count,
has_api_nodes, api_node_names (src/platform/telemetry/types.ts:42)
- ExecutionContext adds same fields (src/platform/telemetry/types.ts:61)
- New event + provider API: ADD_API_CREDIT_BUTTON_CLICKED,
trackAddApiCreditButtonClicked() (src/platform/telemetry/types.ts:173,
src/platform/telemetry/types.ts:180)
- Mixpanel provider
- Compute node metrics in a single graph traversal and include them in
RUN_BUTTON_CLICKED
(src/platform/telemetry/providers/cloud/MixpanelTelemetryProvider.ts:169)
- Fields populated: total_node_count, subgraph_count, has_api_nodes,
api_node_names
(src/platform/telemetry/providers/cloud/MixpanelTelemetryProvider.ts:177)
- Add trackAddApiCreditButtonClicked() implementation
(src/platform/telemetry/providers/cloud/MixpanelTelemetryProvider.ts:152)
- Script
- Add scripts/survey-data-migration.ts to normalize industry/useCase
user properties using existing survey normalization utils; includes a
simulation and a production outline (scripts/survey-data-migration.ts:1)

Motivation
- Improves insight into workflow complexity and API-node adoption
directly at run time.
- Normalizes free-text survey fields to reduce category proliferation
and improve reporting quality.

Validation
- Run a workflow with/without API nodes and subgraphs; confirm telemetry
includes:
  - total_node_count, subgraph_count, has_api_nodes, api_node_names
- Click “Add API credit” and confirm app:add_api_credit_button_clicked
is sent.
- No user-visible changes; telemetry only runs in cloud builds.

Impact
- Telemetry payload shape expands; backend ingestion should accept the
new properties.
- Metrics computed in a single pass over the graph for efficiency.

---------

Co-authored-by: bymyself <cbyrne@comfy.org>
2025-10-31 12:55:00 -07:00
Comfy Org PR Bot
da078a6071 1.32.0 (#6474)
Minor version increment to 1.32.0

**Base branch:** `main`

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6474-1-32-0-29d6d73d365081758b7df1a759a85013)
by [Unito](https://www.unito.io)

---------

Co-authored-by: simula-r <18093452+simula-r@users.noreply.github.com>
Co-authored-by: github-actions <github-actions@github.com>
2025-10-30 21:12:57 -07:00
sno
e1706a8f13 fix: Remove assignees from weekly-docs-check workflow (#6375)
## Summary

Removes the `assignees` field from the weekly-docs-check workflow that
was causing failures.

## Problem

The workflow was attempting to assign `${{ github.repository_owner }}`
to created PRs. For organization-owned repositories,
`github.repository_owner` is the organization name (e.g., "Comfy-Org"),
which cannot be assigned to pull requests. Only individual GitHub users
can be assigned.

This was causing the workflow to fail:
https://github.com/Comfy-Org/ComfyUI_frontend/actions/runs/18901589988

## Solution

Removed the `assignees` line from the workflow configuration. The PRs
will still be created successfully, just without auto-assignment.

## Changes Made

- Removed `assignees: ${{ github.repository_owner }}` from
`.github/workflows/weekly-docs-check.yaml:145`

Fixes #6364

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6375-fix-Remove-assignees-from-weekly-docs-check-workflow-29b6d73d365081769ec2dc3e68005cc7)
by [Unito](https://www.unito.io)
2025-10-30 20:47:07 -07:00
Christian Byrne
1322a56653 Cloud/tracking v2 (#6400)
## 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 -->

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6400-Cloud-tracking-v2-29c6d73d365081a1ae32e9337f510a9e)
by [Unito](https://www.unito.io)

---------

Co-authored-by: Arjan Singh <arjan@comfy.org>
2025-10-30 20:46:42 -07:00
AustinMroz
6491db6f68 Further subgraph improvements (#6466)
- Prune disconnected widgets on config panel open
- Fix breakage of DOMWidgets on double nest
- Fix self clipping of proxied DOMWidget nodes
- Blacklist cloning of promtoed widget prop

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6466-Further-subgraph-improvements-29c6d73d365081c8b63dda068486805c)
by [Unito](https://www.unito.io)
2025-10-30 18:06:10 -07:00
Simula_r
041ce2decb Feat/vue nodes autoscale false (#6469)
## Summary

Switch back to false, for now. Because its a destructive action. Edge
cases and user information can come next. This saves us time so we can
cut a nightly release and move on.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6469-Feat-vue-nodes-autoscale-false-29c6d73d365081deabf0dc284a9b41ab)
by [Unito](https://www.unito.io)
2025-10-30 15:23:57 -07:00
Simula_r
ce298c43f4 feat: Comfy.VueNodes.AutoScaleLayout default true (#6467)
## Summary

Now that banner and toast are merged run the auto scale / node arrange
function by default.

## Changes

- **What**: Core settings Comfy.VueNodes.AutoScaleLayout defaultValue:
true,

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6467-feat-Comfy-VueNodes-AutoScaleLayout-default-true-29c6d73d365081c2b317f7f3450cbaec)
by [Unito](https://www.unito.io)
2025-10-30 14:29:55 -07:00
AustinMroz
40a7cbb83e Fix selectionToolbox not being on node (#6401)
`getBounding()` doesn't work if a draw hasn't occurred.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6401-Fix-selectionToolbox-not-being-on-node-29c6d73d3650812cbfbff116a9b8159d)
by [Unito](https://www.unito.io)
2025-10-30 12:13:00 -07:00
AustinMroz
9ddead24d6 On failed subgraph resolution, return undefined (#6460)
Resolving an executionId to a locatorId can fail if the current workflow
does not match the queued one. Rather than throwing an error, the
resolution functions have been changed to return undefined.

Of note, all consumers of these functions already had checks to ensure
the returned value is not undefined.
- Even the test for failure state already specified that it should
return instead of throwing an error.

This PR cleans up a frequent sentry error.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6460-On-failed-subgraph-resolution-return-undefined-29c6d73d365081c5860ed7d24a99414c)
by [Unito](https://www.unito.io)
2025-10-30 12:06:52 -07:00
Arjan Singh
6186e81b98 chore: add example sentry env vars (#6462)
## Summary

Add examples for future debugging documentations.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6462-chore-add-example-sentry-env-vars-29c6d73d365081b5bf06f8f248985fbf)
by [Unito](https://www.unito.io)
2025-10-30 11:05:15 -07:00
Arjan Singh
04eb224822 ci: add sentryVitePlugin (#6394)
## Summary

This will be used to upload source maps in configured environments.

Docs:
https://docs.sentry.io/platforms/javascript/sourcemaps/uploading/vite/

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6394-ci-add-sentryVitePlugin-29c6d73d365081239f48f2fd261736d5)
by [Unito](https://www.unito.io)
2025-10-30 10:28:48 -07:00
Terry Jia
dd90288846 code improve for 3d node (#6403)
## Summary

code improve for 3d node

## Changes

1. remove custom css
2. use tailwind instead
3. bug fix for right click context menu

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6403-code-improve-for-3d-node-29c6d73d3650819fab26feabdaa21821)
by [Unito](https://www.unito.io)
2025-10-30 05:31:36 -04:00
Johnpaul Chiwetelu
c642ed5703 Complete context menu migration for Load3D, Preview3D, and SaveGLB (#6454)
This pull request refactors how export menu options are added to
3D-related nodes, updating the API to use a new `getNodeMenuItems` hook
and simplifying the menu item creation logic. The changes improve
consistency and maintainability across extensions handling 3D nodes, and
clarify the expected return types for menu item hooks.

**Refactoring and API updates for node menu items:**

* Replaced usage of the legacy `createExportMenuOptions` function with
the new `createExportMenuItems` function in `load3d.ts`, `saveMesh.ts`,
and related imports, aligning all 3D node extensions to the new API.
[[1]](diffhunk://#diff-a5c612d9322ca4cbbeda097d13e2fa1ef017d4c3076d23fc228afee5a79a56e3L6-R12)
[[2]](diffhunk://#diff-7dede72060130d77ce5191fc86d115bd9b93311cb0438400730d8e20b2aa8e43L4-R7)
* Introduced and implemented the `getNodeMenuItems` hook in the
extension registration for `Load3D`, `Preview3D`, and `SaveGLB` nodes,
ensuring export menu items are only shown for the appropriate node
types.
[[1]](diffhunk://#diff-a5c612d9322ca4cbbeda097d13e2fa1ef017d4c3076d23fc228afee5a79a56e3R293-R302)
[[2]](diffhunk://#diff-a5c612d9322ca4cbbeda097d13e2fa1ef017d4c3076d23fc228afee5a79a56e3R521-R530)
[[3]](diffhunk://#diff-7dede72060130d77ce5191fc86d115bd9b93311cb0438400730d8e20b2aa8e43R48-R57)
* Removed assignment of `getExtraMenuOptions` to nodes, fully migrating
to the new menu item hook approach for context menus.
[[1]](diffhunk://#diff-a5c612d9322ca4cbbeda097d13e2fa1ef017d4c3076d23fc228afee5a79a56e3L301-L302)
[[2]](diffhunk://#diff-a5c612d9322ca4cbbeda097d13e2fa1ef017d4c3076d23fc228afee5a79a56e3L548-L549)
[[3]](diffhunk://#diff-7dede72060130d77ce5191fc86d115bd9b93311cb0438400730d8e20b2aa8e43L64-L67)

**Menu item creation logic simplification:**

* Refactored `createExportMenuOptions` to `createExportMenuItems` in
`exportMenuHelper.ts`, changing it to directly return an array of menu
items (with separators) instead of a function, simplifying its usage and
reducing boilerplate.
[[1]](diffhunk://#diff-42404da1a87a52d304371a13d5f021bdad837765b94d86d186abb2d99a8cb707L14-R22)
[[2]](diffhunk://#diff-42404da1a87a52d304371a13d5f021bdad837765b94d86d186abb2d99a8cb707L59-R58)

**Type and documentation improvements:**

* Updated the `ComfyExtension` interface in `comfy.ts` to clarify that
menu item hooks (`getCanvasMenuItems`, `getNodeMenuItems`) now return
arrays that may include `null` values as separators, improving type
safety and documentation.

See before and after below
<img width="1459" height="897" alt="Screenshot 2025-10-30 at 07 08 04"
src="https://github.com/user-attachments/assets/ec4464c9-f733-4b4c-87c4-bb5060ccaa68"
/>

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6454-Complete-context-menu-migration-for-Load3D-Preview3D-and-SaveGLB-29c6d73d3650819995b4c4dc1582cd86)
by [Unito](https://www.unito.io)
2025-10-30 07:26:05 +01:00
Simula_r
9e309308ed feat: show vue node settings toggle and show banner to switch back (#6382)
## Summary

- When switching from litegraph to vue nodes, show a toast that opens
settings to switch back
- Show a toggle in settings under Vue nodes for now called Modern Node
Design (Vue Nodes)
- Plan to update all the nomenclature as we get closer to GA

## Screenshots (if applicable)

<img width="1480" height="911" alt="image"
src="https://github.com/user-attachments/assets/fa3a34b9-2631-4175-917a-aa319f9fe415"
/>

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6382-feat-show-vue-node-settings-toggle-and-show-banner-to-switch-back-29b6d73d365081d29252c83349f06c0a)
by [Unito](https://www.unito.io)

---------

Co-authored-by: github-actions <github-actions@github.com>
2025-10-29 22:39:52 -07:00
Simula_r
b27c741d7d Feat/vue nodes try it now banner (#6362)
## Summary

Banner to try vue nodes. Clicking try it now will flip
shouldRenderVueNodes = true.

## Screenshots (if applicable)

<img width="1512" height="824" alt="image"
src="https://github.com/user-attachments/assets/dfd4bdc3-6753-45ee-86f1-ed7dc077f868"
/>

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6362-Feat-vue-nodes-try-it-now-banner-29b6d73d365081c29f04f9126f06ee9d)
by [Unito](https://www.unito.io)
2025-10-29 21:46:47 -07:00
AustinMroz
ca5729a8e7 Add pricing badge when a subgraph contains partner nodes (#6354)
<img width="596" height="213" alt="image"
src="https://github.com/user-attachments/assets/174c5461-f638-42de-b3ad-0e108dee3983"
/>


![api-badge-subgraph_00003](https://github.com/user-attachments/assets/067d0398-47e9-4e97-9e1d-67fac2935e55)


┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6354-Add-pricing-badge-when-a-subgraph-contains-partner-nodes-29b6d73d365081c685bec3e9446970eb)
by [Unito](https://www.unito.io)
2025-10-29 20:41:04 -07:00
Alexander Brown
e606ff34ec Feat: Vue Node Slot Improvements (#6359)
## Summary

Several fixes and improvements to the slot behavior on Vue nodes.

## Changes

- **What**: Restore the pseudo-slots, if there are slots being hidden by
collapse
- **What**: Connections while collapsed
- **What**: Display the links in a more reasonable location
- **What**: Fixes styling of linked widgets
- **What**: [~Fix reconnecting logic to prioritize newly disconnected
and now empty
slots~](https://github.com/Comfy-Org/ComfyUI_frontend/pull/6370)

## 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)


https://github.com/user-attachments/assets/913cfb8f-acdd-4f3d-b619-c280cc11cce5


<!-- Add screenshots or video recording to help explain your changes -->

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6359-WIP-Collapsed-nodes-multislots-29b6d73d3650817289d5f0a8efdade84)
by [Unito](https://www.unito.io)

---------

Co-authored-by: github-actions <github-actions@github.com>
2025-10-29 20:32:05 -07:00
Christian Byrne
5e9a9923e4 update subscription panel for new designs (#6378)
## Summary

Refactoring of subscription panel to improve maintainability and match
Figma design exactly. Extracted business logic into
`useSubscriptionCredits` and `useSubscriptionActions` composables, added
comprehensive testing, and enhanced the design system with proper
semantic tokens.

- Extract credit calculations and action handlers into reusable
composables
- Add component and unit tests with proper mocking patterns  
- Update terminology from "API Nodes" to "Partner Nodes"
- Make credit breakdown dynamic using real API data instead of hardcoded
values
- Add semantic design tokens for modal card surfaces with light/dark
theme support
- Reduce component complexity from ~100 lines to ~25 lines of logic
- Improve layout spacing, typography, and responsive behavior to match
Figma specs

<img width="1948" height="1494" alt="Selection_2220"
src="https://github.com/user-attachments/assets/b922582d-7edf-4884-b787-ad783c896b80"
/>

<img width="1948" height="1494" alt="Selection_2219"
src="https://github.com/user-attachments/assets/50a9f263-9adb-439d-8a89-94a498d394e3"
/>

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6378-update-subscription-panel-for-new-designs-29b6d73d3650815c9ce2c5977ac7f893)
by [Unito](https://www.unito.io)

---------

Co-authored-by: Claude <noreply@anthropic.com>
2025-10-29 20:15:30 -07:00
Christian Byrne
331372df44 use shared composable for subscription (#6390)
Refactor global composable to use useSharedComposable.

- Makes it obvious that subscription status is shared globally across
all components, rather than relying on implicit module-level refs
- Eliminates manual `isWatchSetup` flag and conditional watch setup
logic that was needed to prevent duplicate watchers
- Uses well-established VueUse pattern that other developers will
immediately recognize and understand

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6390-use-shared-composable-for-subscription-29c6d73d36508185a5c2e0b467549432)
by [Unito](https://www.unito.io)
2025-10-29 20:08:33 -07:00
Arjan Singh
09143c05c1 chore(vite.config): add GCS redirect for assets (#6389)
## Summary

Add GCS redirect in vite's proxy so we can load workflows by dragging
images in local cloud development.

Previously this action would result in a CORS error when the app tried
to download from `storage.googleapis.com`

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6389-chore-vite-config-add-GCS-redirect-for-assets-29c6d73d36508148aaabd24de9698fc4)
by [Unito](https://www.unito.io)
2025-10-29 19:34:38 -07:00
Alexander Brown
fac8bd68dc Fix: Disable pointer events on images to allow them to be drag targets (#6388)
## Summary

Allows SaveImage and other nodes with ImagePreviews to be dragged by the
image.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6388-Fix-Disable-pointer-events-on-images-to-allow-them-to-be-drag-targets-29c6d73d365081e796f0e8171a860062)
by [Unito](https://www.unito.io)
2025-10-30 01:45:50 +00:00
Arjan Singh
f2355a6ad1 feat(historyV2): load workflows for images (#6384)
## Summary

Hooked up the "Load Workflow" action to our `history_v2` API.

Note: Our cloud envs were being stress tested right now so images are
loading at time of recording. Images were loading for me during
development before I had time to create the video.


## Screenshots 📷 



https://github.com/user-attachments/assets/02145504-ceae-497b-9049-553796d698da



┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6384-Feat-history-v2-workflows-29b6d73d365081bcb706fe799b8ce66a)
by [Unito](https://www.unito.io)
2025-10-29 18:35:42 -07:00
AustinMroz
6f068c87da Multiple fixes related to copying subgraphs (#6383)
- DOMWidgets have ownership reset after a `subgraphNode.clone()`.
- Fixes Ctrl+C on a subgraphNode with a prompted prompt making the
prompt disappear.
- alt + drag uses the copy/paste pathway that deeply clones subgraphs.
- Fixed dangling references on nodes in subgraphs by updating subgraph
ids before configuration.
- Attempt to recursively resolve disconnected proxyWidgets (Can matter
when subgraphs load out of order).
- Fix Right click -> clone creating linked copies of subgraphs.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6383-Multiple-fixes-related-to-copying-subgraphs-29b6d73d365081819671ced440dde327)
by [Unito](https://www.unito.io)
2025-10-29 18:05:04 -07:00
Alexander Brown
86c0fb11f1 Fix: Only stop propagation of events forwarded to the canvas. (#6387)
## Summary

Allows users to drag nodes while clicking things like the widget labels.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6387-Fix-Only-stop-propagation-of-events-forwarded-to-the-canvas-29c6d73d365081bdb952c5e7f409031e)
by [Unito](https://www.unito.io)
2025-10-30 00:56:14 +00:00
Terry Jia
c76f017f92 add save option for 3d node on context menu (#6319)
## Summary

add save option for 3d node on context menu

## Changes
allow to export model from context menu, supported in
preview3d/load3d/saveMesh




https://github.com/user-attachments/assets/1f0f1a93-9cdb-477f-8bd3-e298c7e3892b

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6319-add-save-option-for-3d-node-on-context-menu-2996d73d365081f7853cd2ae9c69fe4d)
by [Unito](https://www.unito.io)
2025-10-29 20:52:02 -04:00
Alexander Brown
5e212156e1 Fix: Allow click and drag selection when editing text (e.g. Node name) (#6385)
## Summary

Previously, the node would be dragged when trying to select text.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6385-Fix-Allow-click-and-drag-selection-when-editing-text-e-g-Node-name-29b6d73d36508197b73fc8f965bb34ef)
by [Unito](https://www.unito.io)
2025-10-29 16:56:58 -07:00
Alexander Brown
7821120706 fix: Count dragging slot as a free slot for targeting (#6370)
## Summary

Prioritizes the slot that previously held the link instead of the first
valid slot for a given type.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6370-fix-Count-dragging-slot-as-a-free-slot-for-targeting-29b6d73d365081709034e272cef38320)
by [Unito](https://www.unito.io)
2025-10-29 11:43:57 -07:00
Christian Byrne
b8e5d1ff90 refactor subscription composable (#6365)
Some refactors of subscription composable:

- Swapped cloud subscription response shapes to `type` aliases to
emphasize fixed API payloads.
- Formatted renewal/end dates with the user’s locale instead of
hard-coding `en-US`.
- Reused the single `useFirebaseAuthActions()` instance and passed
`fetchSubscriptionStatus` directly into `wrapWithErrorHandlingAsync` to
trim duplicate wrappers.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6365-refactor-subscription-composable-29b6d73d365081b1b002d52abde8d195)
by [Unito](https://www.unito.io)
2025-10-29 09:05:24 -07:00
Johnpaul Chiwetelu
bde5244a71 Css token standardization (#6363)
This pull request introduces a comprehensive update to the design
system's color management, focusing on establishing semantic color
tokens for both light and dark themes. It replaces many hardcoded color
values and legacy CSS classes throughout the codebase with new semantic
CSS variables, ensuring consistent theming and easier future
maintenance. The changes affect core CSS files as well as numerous Vue
components, aligning their styling with the new design system.

**Design System Foundation**

* Added a wide range of new color variables to `style.css`, including
base colors (e.g., `--color-white`, `--color-black`), additional shades
for sand, azure, cobalt, gold, coral, and magenta, and new alpha
(transparency) colors.
[[1]](diffhunk://#diff-71b6b57a56095b04e47c797a5016149b76b27971cab04b93f033f1f846e0f5a0L52-R87)
[[2]](diffhunk://#diff-71b6b57a56095b04e47c797a5016149b76b27971cab04b93f033f1f846e0f5a0R119-R123)
* Introduced semantic color tokens for both light and dark modes
(`--base-background`, `--primary-background`,
`--destructive-background`, etc.), mapping them to the new base colors
for consistent usage across the application.
[[1]](diffhunk://#diff-71b6b57a56095b04e47c797a5016149b76b27971cab04b93f033f1f846e0f5a0R219-R239)
[[2]](diffhunk://#diff-71b6b57a56095b04e47c797a5016149b76b27971cab04b93f033f1f846e0f5a0R301-R321)
* Exposed semantic tokens as CSS variables (e.g.,
`--color-base-foreground`, `--color-secondary-background`) for use
throughout the app.

**Component Refactoring to Semantic Tokens**

* Updated Vue components and their tests to use the new semantic color
classes (e.g., `bg-base-background`, `text-base-foreground`,
`bg-secondary-background`) instead of hardcoded colors or legacy
dark-theme classes. This affects components such as
`WorkflowTemplateSelectorDialog.vue`, `BypassButton.vue`,
`ExecuteButton.vue`, `MenuOptionItem.vue`, `AssetCard.vue`,
`MediaAssetMoreMenu.vue`, `MediaTitle.vue`, `WidgetFileUpload.vue`,
`WidgetRecordAudio.vue`, `AudioPreviewPlayer.vue`, and
`FormDropdownMenuActions.vue`.
[[1]](diffhunk://#diff-2c860bdc48e907b1b85dbef846599d8376dd02cff90f49e490eebe61371fecedL149-R149)
[[2]](diffhunk://#diff-8ec606ef1100f3a56945ed24cbdc1965050932cd744d4172a3868cdfd23894c0L95-R95)
[[3]](diffhunk://#diff-80b781aeba31712968ae157bb70194e4b72bc73430d1cca6a79d718d839daed6L10-R10)
[[4]](diffhunk://#diff-55fd9056d35e50249dc9f2280017dc99294221fdbe56d8399cea60f8bac499b5L7-R7)
[[5]](diffhunk://#diff-c5e6830e63e2441d2dc70d2ecf7c9b56d0a93821f827e9c5377fc10ae6016f18L30-R32)
[[6]](diffhunk://#diff-a1091d53a4b5d493e045aab5960188d2e7c3b80002e7178427268835fadb5809L30-R30)
[[7]](diffhunk://#diff-ccdb389a5e355d525dcfa26ecd77519297b6232dd34522411c8bfdd4cde05a1cL6-R6)
[[8]](diffhunk://#diff-7ef9ebd48e6f38a644c1a4e7bae1c7bb818bb959b2d20985974824e299ea5c34L3-R3)
[[9]](diffhunk://#diff-489229f88dfdfd5d883a3ef7fad6effa0790a18a831d5a9d84642dfb246962a2L115-R115)
[[10]](diffhunk://#diff-7bee4b453fc869f546e7150a6e39992ab6442987f80c10f8260b8f3715491997L51-R51)
[[11]](diffhunk://#diff-29348fa2e5b8cec1301a99bdec241379aeefc1747cceeb0c39b7df452ca635ffL41-R41)
[[12]](diffhunk://#diff-d464ebe3a44bec4fda7155e5605bf173612aca409250b7ef6b78920a89ae2044L24-R24)

**Consistency and Maintainability**

* Ensured hover, active, and selected states use semantic background and
foreground colors for both light and dark themes, improving visual
consistency and simplifying future updates.
[[1]](diffhunk://#diff-2c860bdc48e907b1b85dbef846599d8376dd02cff90f49e490eebe61371fecedL183-R183)
[[2]](diffhunk://#diff-a1091d53a4b5d493e045aab5960188d2e7c3b80002e7178427268835fadb5809L115-R114)
[[3]](diffhunk://#diff-ccdb389a5e355d525dcfa26ecd77519297b6232dd34522411c8bfdd4cde05a1cL18-R18)
[[4]](diffhunk://#diff-ccdb389a5e355d525dcfa26ecd77519297b6232dd34522411c8bfdd4cde05a1cL29-R29)
[[5]](diffhunk://#diff-ccdb389a5e355d525dcfa26ecd77519297b6232dd34522411c8bfdd4cde05a1cL43-R43)
[[6]](diffhunk://#diff-ccdb389a5e355d525dcfa26ecd77519297b6232dd34522411c8bfdd4cde05a1cL55-R55)
[[7]](diffhunk://#diff-ccdb389a5e355d525dcfa26ecd77519297b6232dd34522411c8bfdd4cde05a1cL69-R69)
[[8]](diffhunk://#diff-ccdb389a5e355d525dcfa26ecd77519297b6232dd34522411c8bfdd4cde05a1cL83-R83)
[[9]](diffhunk://#diff-2c860bdc48e907b1b85dbef846599d8376dd02cff90f49e490eebe61371fecedL328-R328)
[[10]](diffhunk://#diff-489229f88dfdfd5d883a3ef7fad6effa0790a18a831d5a9d84642dfb246962a2L138-R138)
[[11]](diffhunk://#diff-29348fa2e5b8cec1301a99bdec241379aeefc1747cceeb0c39b7df452ca635ffL119-R119)
[[12]](diffhunk://#diff-29348fa2e5b8cec1301a99bdec241379aeefc1747cceeb0c39b7df452ca635ffL137-R137)
[[13]](diffhunk://#diff-d464ebe3a44bec4fda7155e5605bf173612aca409250b7ef6b78920a89ae2044L53-R53)
[[14]](diffhunk://#diff-d464ebe3a44bec4fda7155e5605bf173612aca409250b7ef6b78920a89ae2044L153-R153)

**Removal of Legacy Styles**

* Removed legacy dark-theme class usage and hardcoded color values,
replacing them with semantic tokens to unify the styling approach.
[[1]](diffhunk://#diff-c5e6830e63e2441d2dc70d2ecf7c9b56d0a93821f827e9c5377fc10ae6016f18L30-R32)
[[2]](diffhunk://#diff-d464ebe3a44bec4fda7155e5605bf173612aca409250b7ef6b78920a89ae2044L24-R24)
[[3]](diffhunk://#diff-d464ebe3a44bec4fda7155e5605bf173612aca409250b7ef6b78920a89ae2044L53-R53)
[[4]](diffhunk://#diff-d464ebe3a44bec4fda7155e5605bf173612aca409250b7ef6b78920a89ae2044L153-R153)

These changes lay the groundwork for a scalable and maintainable design
system, making it easier to implement future theme changes and ensuring
a consistent look and feel across all components.
2025-10-29 15:10:54 +01:00
Rizumu Ayaka
8c1beee719 chore: adjust manual chunks (#6368)
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6368-chore-adjust-manual-chunks-29b6d73d36508185a1bcdc77d4fd7519)
by [Unito](https://www.unito.io)
2025-10-29 01:44:26 -07:00
Jin Yi
9651d2a5df feat: Add multi-select support for media assets (#6256)
## Summary
Implements file explorer-style multi-selection functionality for media
assets in the AssetsSidebarTab component.

## Changes

### Multi-Selection Interactions
- **Normal click**: Single selection (clears previous, selects new)
- **Shift + click**: Range selection (from last selected to current)
- **Ctrl/Cmd + click**: Toggle individual selection

### State Management
- Added `assetSelectionStore` to manage selected asset IDs using Set
- Created `useAssetSelection` composable for selection logic and
keyboard state

### UI Enhancements
- Display selection count in footer (output tab only)
- Interactive selection count that shows "Deselect all" on hover
- Added bulk action buttons for download/delete (UI only)

### Translation Keys
Added new keys under `mediaAsset.selection`:
- `selectedCount`: "{count} selected"
- `deselectAll`: "Deselect all"
- `downloadSelected`: "Download"
- `deleteSelected`: "Delete"

## Test Plan
- [x] Open Assets sidebar tab
- [x] Switch to Generated tab
- [x] Test single selection with normal click
- [x] Test range selection with Shift + click
- [x] Test toggle selection with Ctrl/Cmd + click
- [x] Verify selection count updates correctly
- [x] Test hover interaction on selection count
- [x] Click "Deselect all" to clear selection
- [x] Test bulk action buttons (UI only)

## Notes
- Bulk download/delete functionality to be implemented in separate PR
- Selection UI currently only shows in output (Generated) tab


[screen-capture.webm](https://github.com/user-attachments/assets/740315bd-9254-4af3-a0be-10846d810d65)
2025-10-29 00:26:44 -07:00
sno
22f307b468 fix: Use PR_GH_TOKEN instead of GITHUB_TOKEN in weekly-docs-check workflow (#6364)
## Summary
- Updated weekly-docs-check.yaml to use `PR_GH_TOKEN` secret instead of
`GITHUB_TOKEN`

## Problem
The weekly documentation check workflow uses `GITHUB_TOKEN` when
creating pull requests, which can cause permission issues. The default
`GITHUB_TOKEN` has limited permissions and may not trigger other
workflows or perform certain PR operations.

## Solution
Changed the token in the "Create or Update Pull Request" step from
`secrets.GITHUB_TOKEN` to `secrets.PR_GH_TOKEN` to use a more permissive
token that can properly create and manage PRs.

## Changes Made
- `.github/workflows/weekly-docs-check.yaml:135` - Updated token
parameter

## Test Plan
- Workflow should now successfully create PRs with proper permissions
- Other workflows should be triggered correctly when PRs are created

🤖 Generated with [Claude Code](https://claude.com/claude-code)

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6364-fix-Use-PR_GH_TOKEN-instead-of-GITHUB_TOKEN-in-weekly-docs-check-workflow-29b6d73d3650812cbaddc1ea10aeb995)
by [Unito](https://www.unito.io)

Co-authored-by: Claude <noreply@anthropic.com>
2025-10-28 23:47:07 -07:00
Jin Yi
06ba106f59 Media Assets Management Sidebar Tab Implementation (#6112)
## 📋 Overview
Implemented a new Media Assets sidebar tab in ComfyUI for managing
user-uploaded input files and generated output files. This feature
supports both local and cloud environments and is currently enabled only
in development mode.

## 🎯 Key Features

### 1. Media Assets Sidebar Tab
- **Imported** / **Generated** files separated by tabs
- Visual display with file preview cards
- Gallery view support (navigable with arrow keys)

### 2. Environment-Specific Implementation
- **`useInternalMediaAssets`**: For local environment
  - Fetches file list via `/files` API
  - Retrieves generation task execution time via `/history` API
  - Processes history data using the same logic as QueueSidebarTab
- **`useCloudMediaAssets`**: For cloud environment
  - File retrieval through assetService
  - History data processing using TaskItemImpl
- Auto-truncation of long filenames over 20 characters (e.g.,
`very_long_filename_here.png` → `very_long_...here.png`)

### 3. Execution Time Display
- Shows task execution time on generated image cards (e.g., "2.3s")
- Calculated from History API's `execution_start` and
`execution_success` messages
- Displayed at MediaAssetCard's duration chip location

### 4. Gallery Feature
- Full-screen gallery mode on image click
- Navigate between images with keyboard arrows
- Exit gallery with ESC key
- Reuses ResultGallery component from QueueSidebarTab

### 5. Development Mode Only
- Excluded from production builds using `import.meta.env.DEV` condition
- Feature in development, scheduled for official release after
stabilization

## 🛠️ Technical Changes

### New Files Added
- `src/components/sidebar/tabs/AssetsSidebarTab.vue` - Main sidebar tab
component
- `src/composables/sidebarTabs/useAssetsSidebarTab.ts` - Sidebar tab
definition
- `src/composables/useInternalMediaAssets.ts` - Local environment
implementation
- `src/composables/useCloudMediaAssets.ts` - Cloud environment
implementation
- `packages/design-system/src/icons/image-ai-edit.svg` - Icon addition

### Modified Files
- `src/stores/workspace/sidebarTabStore.ts` - Added dev mode only tab
display logic
- `src/platform/assets/components/MediaAssetCard.vue` - Added execution
time display, zoom event
- `src/platform/assets/components/MediaImageTop.vue` - Added image
dimension detection
- `packages/shared-frontend-utils/src/formatUtil.ts` - Added media type
determination utility functions
- `src/locales/en/main.json` - Added translation keys


[media_asset_OSS_cloud.webm](https://github.com/user-attachments/assets/a6ee3b49-19ed-4735-baad-c2ac2da868ef)

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: github-actions <github-actions@github.com>
2025-10-29 03:39:16 +00:00
Jin Yi
5f3b8fb8c8 fix: Add viewport constraint to dropdown heights and improve filter layout (#6358)
## Summary
Fixed dropdown components exceeding viewport on mobile/tablet
environments and improved workflow template selector dialog filter
layout.

https://github.com/Comfy-Org/ComfyUI_frontend/issues/6153

## Changes

### 1. Dropdown Height Constraints (MultiSelect & SingleSelect)
- Applied CSS `min()` function to use the smaller value between
`listMaxHeight` prop and 50% viewport height
- Ensures dropdowns don't overflow on devices with smaller viewport
heights like tablets and mobiles

### 2. Workflow Template Dialog Layout Improvements
- Grouped filters (Model, Use Case, License) on the left side
- Positioned Sort by option on the right for clearer visual hierarchy
- Used `justify-between` to place filters and sort options at opposite
ends

## Test Plan
- [ ] Verify dropdown works correctly on desktop browsers
- [ ] Confirm dropdown doesn't exceed 50vh on tablet viewport
- [ ] Confirm dropdown doesn't exceed 50vh on mobile viewport
- [ ] Check workflow template dialog filter/sort layout

## Screenshots
**Before**

[before.webm](https://github.com/user-attachments/assets/64b4b969-54ed-4463-abdf-0a4adef01e72)

**After**

[after.webm](https://github.com/user-attachments/assets/b38973e5-9e77-4882-adf8-306279d302e1)

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-authored-by: Claude <noreply@anthropic.com>
2025-10-28 20:25:19 -07:00
Simula_r
133662cdc7 Feat/vue noes arrange alg improved (#6357)
## Summary

Improve the previous [vue node arrange
alg](5a1284660c/src/renderer/extensions/vueNodes/layout/scaleLayoutForVueNodes.ts)
to match Litegraph much closer visually.

- In addition to the scaling of the LG node's x,y and then setting the
vue nodes position to that, we can also scale the width and height of
the LG node and set that for the Vue node wxh.
- Change from scaling from the center to the top left
- Change the zoom offset to account for the scale for seamless
transition

## Screenshots (if applicable)

<img width="1868" height="1646" alt="image"
src="https://github.com/user-attachments/assets/bc816819-211f-4619-a02a-8d167b5dc1fd"
/>

<img width="1868" height="1648" alt="image (1)"
src="https://github.com/user-attachments/assets/61faf530-1994-4cf9-bca9-a7ab6f9740c2"
/>

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6357-Feat-vue-noes-arrange-alg-improved-29b6d73d365081e7813bd6f19ce1202a)
by [Unito](https://www.unito.io)

---------

Co-authored-by: GitHub Action <action@github.com>
Co-authored-by: JakeSchroeder <jake@axiom.co>
2025-10-28 20:23:23 -07:00
Christian Byrne
a54c1516ae add dynamic config field for requiring/not requiring whitelist (#6355)
## Summary

Adds FF to toggle whitelist gating on client.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6355-add-dynamic-config-field-for-requiring-not-requiring-whitelist-29b6d73d36508172b7c2f6f65f6b8a65)
by [Unito](https://www.unito.io)
2025-10-28 19:40:52 -07:00
Arjan Singh
32a803c31e feat(historyV2): reconcile completed workflows (#6340)
## Summary

Running + Finished + History tasks now all work and reconcile correctly
in the queue.

## Changes

1. Reconcile complete workflows so they show up in history.
2. Do the above in a way that minimizes recreation of `TaskItemImpls`
3. Address some CR feedback on #6336 

## Review Focus

I tried to optimize `TaskItemImpls` so we aren't recreating ones for
history items tat already exist. Please give me feedback on if I did
this correctly, or if it was even necessary.

## Screenshots 🎃 



https://github.com/user-attachments/assets/afc08f31-cc09-4082-8e9d-cee977bc1e22

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6340-feat-historyV2-reconcile-completed-workflows-29a6d73d36508145a56aeb99cfa0e6ba)
by [Unito](https://www.unito.io)
2025-10-28 17:40:55 -07:00
Alexander Brown
32688b8e34 fix: Node Sizing that works in Firefox (#6352)
## Summary

I still love Firefox, even if it's special.

(Swap height/width and min-height min-width logic)

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6352-fix-Node-Sizing-that-works-in-Firefox-29a6d73d3650816b9a2fd271445e193f)
by [Unito](https://www.unito.io)
2025-10-28 17:20:35 -07:00
Christian Byrne
4ad7531269 update subscription dialog (#6350)
## Summary

- Align with design and new tokens
- Change to point to comfy.org/cloud/pricing

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6350-update-subscription-dialog-29a6d73d365081fc80dbdced405f6b21)
by [Unito](https://www.unito.io)
2025-10-28 15:20:21 -07:00
Comfy Org PR Bot
e8dabd2996 1.31.1 (#6349)
Patch version increment to 1.31.1

**Base branch:** `main`

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6349-1-31-1-29a6d73d365081fd8922ebe8be3d1749)
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>
2025-10-28 15:05:42 -07:00
Simula_r
f629d325b2 Feat/vue nodes arrange alg (#6212)
## Summary

### Problem: 
The Vue nodes renderer/feature introduces new designs for each node i.e.
the equivalent Litegraph node design is smaller and the vue node design
is non uniformly larger.

### Example: 

Litegraph Ksampler node: 200w x 220h

<img width="200" height="220" alt="image"
src="https://github.com/user-attachments/assets/eef0117b-7e02-407d-98ab-c610fd1ec54c"
/>

Vue Node Ksampler node: 445w x 430h 

<img width="445" height="430" alt="image"
src="https://github.com/user-attachments/assets/e78d9d45-5b32-4e8d-bf1c-bce1c699037f"
/>

This means if users load a workflow in Litegraph and then switches to
Vue nodes renderer the nodes are using the same Litegraph positions
which would cause a visual overlap and overall look broken.

### Example:

<img width="1510" height="726" alt="image"
src="https://github.com/user-attachments/assets/3b7ae9d2-6057-49b2-968e-c531a969fac4"
/>
<img width="1475" height="850" alt="image"
src="https://github.com/user-attachments/assets/ea10f361-09bd-4daa-97f1-6b45b5dde389"
/>

### Solution:

Scale the positions of the nodes in lite graph radially from the center
of the bounds of all nodes. And then simply move the Vue nodes to those
new positions.

1. Get the `center of the bounds of all LG nodes`.
2. Get the `xy of each LG node`.
3. Get the vector from `center of the bounds of all LG nodes` `-` `xy of
each LG node`.
4. Scale it by a factor (e.g. 1.75x which is the average Vue node size
increase plus some visual padding.)
5. Move each Vue node to the scaled `xy of each LG node`. 

Result: The nodes are spaced apart removing overlaps while keeping the
spatial layout intact.

<img width="2173" height="1096" alt="image"
src="https://github.com/user-attachments/assets/7817d866-4051-47bb-a589-69ca77a0bfd3"
/>

### Further concerns.

This vector scaling algorithm needs to run once per workflow when in vue
nodes. This means when in Litegraph and switching to Vue nodes, it needs
to run before the nodes render. And then now that the entire app is in
vue nodes, we need to run it each time we load a workflow. However, once
its run, we do not need to run it again. Therefore we must persist a
flag that it has run somewhere. This PR also adds that feature by
leveraging the `extra` field in the workflow schema.

---------

Co-authored-by: GitHub Action <action@github.com>
Co-authored-by: JakeSchroeder <jake@axiom.co>
2025-10-28 14:14:39 -07:00
sno
38525d8f3a feat: add weekly documentation accuracy check workflow (#6298)
## Summary
Adds a new automated workflow that runs weekly to check and update
documentation accuracy.

## Changes
- Created `.github/workflows/weekly-docs-check.yaml`
- Workflow runs every Monday at 9 AM UTC or via manual dispatch
- Uses Claude to fact-check all documentation against the current
codebase
- Automatically creates/updates a draft PR with documentation
corrections

## Workflow Features
1. **Schedule**: Runs weekly (Mondays at 9 AM UTC) or on-demand via
workflow_dispatch
2. **Documentation Review**: Claude analyzes:
   - All markdown files in `docs/`
   - Project guidelines in `CLAUDE.md`
   - README files throughout the repository
   - Claude command documentation in `.claude/commands/`
3. **Automated PR Creation**: Uses `peter-evans/create-pull-request`
action to:
   - Create or update the `docs/weekly-update` branch
   - Generate a draft PR with all documentation updates
   - Apply `documentation` and `automated` labels

## Benefits
- Keeps documentation synchronized with code changes
- Identifies outdated API references and deprecated functions
- Catches missing documentation for new features
- Ensures code examples remain valid and tested

## Test Plan
- [x] YAML syntax validated
- [ ] Workflow can be manually triggered to verify functionality
- [ ] PR creation works as expected

🤖 Generated with [Claude Code](https://claude.com/claude-code)

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6298-feat-add-weekly-documentation-accuracy-check-workflow-2986d73d365081d48ce0f4cf181c377f)
by [Unito](https://www.unito.io)

---------

Co-authored-by: Claude <noreply@anthropic.com>
2025-10-28 14:13:48 -07:00
Christian Byrne
c374975ddc re-enable "reconnecting WS" toast on cloud (#6348)
This error should not actually happen frequently at all, so we can
re-enable without fear of spamming an error that most users would have
no context to understand -- primary benefit is to help user report
better errors should they occur.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6348-re-enable-reconnecting-WS-toast-on-cloud-29a6d73d36508106b893cc0c0aee09fd)
by [Unito](https://www.unito.io)
2025-10-28 14:12:42 -07:00
Terry Jia
e7f640b436 subscription improve (#6339)
## Summary

Run keyboard shortcut bypasses paywall
Top Up button visible before paywall (confusing - hide until subscribed)
Question mark icon next to commercial models has no tooltip (hide until
added)

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6339-subscription-improve-29a6d73d3650818e92c7f60eda01646a)
by [Unito](https://www.unito.io)

---------

Co-authored-by: bymyself <cbyrne@comfy.org>
2025-10-28 13:00:27 -07:00
AustinMroz
6e4471ad62 Fix node sizing in vue mode (#6289)
Before

![broken-resize](https://github.com/user-attachments/assets/1d3651f7-8589-439c-99d1-fa67c5970945)
After

![vue-resize](https://github.com/user-attachments/assets/ef97ea8f-b601-4a5c-9c6a-34d3f4b776d2)

Also add ~~content~~
[contain](https://developer.mozilla.org/en-US/docs/Web/CSS/contain)
styling for improved render performance.

Future:
- Update size scaling for WidgetLayoutField widgets.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6289-Fix-node-sizing-in-vue-mode-2986d73d365081ac8fa0da35a635b226)
by [Unito](https://www.unito.io)

---------

Co-authored-by: github-actions <github-actions@github.com>
2025-10-28 12:18:57 -07:00
Alexander Brown
b03cf7e11d Style: Token renaming and style organization (#6337)
## Summary

Align color names and organize style.css some more

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6337-Style-Token-renaming-and-style-organization-29a6d73d365081b69f25ce1298c67fdc)
by [Unito](https://www.unito.io)
2025-10-28 12:13:28 -07:00
Christian Byrne
0a80a288c0 add title to asset names in model browser (#6338)
Show full title when hovering the text.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6338-add-title-to-asset-names-in-model-browser-29a6d73d36508106a1d5ce9c09007b78)
by [Unito](https://www.unito.io)
2025-10-28 08:32:16 -07:00
Christian Byrne
efed934418 remove tailwindcss eslint plugin (#6342)
This plugin does not seem to work well with tw v4. Can revert this PR
later.


---

the graph is forcing tailwind-api-utils@1.0.3 which tries to import v3
files, or something like that.

```
> @comfyorg/comfyui-frontend@1.31.0 lint:fix /home/c_byrne/projects/comfyui-frontend-testing/ComfyUI_frontend-clone-31
> eslint src --cache --fix


Oops! Something went wrong! :(

ESLint: 9.35.0

Error: Error while loading rule 'tailwindcss/enforces-negative-arbitrary-values': Cannot find module '/home/c_byrne/projects/comfyui-frontend-testing/ComfyUI_frontend-clone-31/node_modules/.pnpm/tailwindcss@4.1.12/node_modules/tailwindcss/dist/lib/setupContextUtils.js'
Require stack:
- /home/c_byrne/projects/comfyui-frontend-testing/ComfyUI_frontend-clone-31/node_modules/.pnpm/tailwind-api-utils@1.0.3_tailwindcss@4.1.12/node_modules/tailwind-api-utils/dist/index.cjs
- /home/c_byrne/projects/comfyui-frontend-testing/ComfyUI_frontend-clone-31/node_modules/.pnpm/eslint-plugin-tailwindcss@4.0.0-beta.0_tailwindcss@4.1.12/node_modules/eslint-plugin-tailwindcss/lib/util/customConfig.js
- /home/c_byrne/projects/comfyui-frontend-testing/ComfyUI_frontend-clone-31/node_modules/.pnpm/eslint-plugin-tailwindcss@4.0.0-beta.0_tailwindcss@4.1.12/node_modules/eslint-plugin-tailwindcss/lib/util/tailwindAPI.js
- /home/c_byrne/projects/comfyui-frontend-testing/ComfyUI_frontend-clone-31/node_modules/.pnpm/eslint-plugin-tailwindcss@4.0.0-beta.0_tailwindcss@4.1.12/node_modules/eslint-plugin-tailwindcss/lib/rules/classnames-order.js
- /home/c_byrne/projects/comfyui-frontend-testing/ComfyUI_frontend-clone-31/node_modules/.pnpm/eslint-plugin-tailwindcss@4.0.0-beta.0_tailwindcss@4.1.12/node_modules/eslint-plugin-tailwindcss/lib/index.js
Occurred while linting /home/c_byrne/projects/comfyui-frontend-testing/ComfyUI_frontend-clone-31/src/base/common/downloadUtil.ts
    at Module._resolveFilename (node:internal/modules/cjs/loader:1410:15)
    at defaultResolveImpl (node:internal/modules/cjs/loader:1051:19)
    at resolveForCJSWithHooks (node:internal/modules/cjs/loader:1056:22)
    at Module._load (node:internal/modules/cjs/loader:1219:37)
    at TracingChannel.traceSync (node:diagnostics_channel:322:14)
    at wrapModuleLoad (node:internal/modules/cjs/loader:238:24)
    at Module.require (node:internal/modules/cjs/loader:1493:12)
    at require (node:internal/modules/helpers:152:16)
    at TailwindUtils.loadConfigV3 (/home/c_byrne/projects/comfyui-frontend-testing/ComfyUI_frontend-clone-31/node_modules/.pnpm/tailwind-api-utils@1.0.3_tailwindcss@4.1.12/node_modules/tailwind-api-utils/dist/index.cjs:429:31)
    at getTailwindConfig (/home/c_byrne/projects/comfyui-frontend-testing/ComfyUI_frontend-clone-31/node_modules/.pnpm/eslint-plugin-tailwindcss@4.0.0-beta.0_tailwindcss@4.1.12/node_modules/eslint-plugin-tailwindcss/lib/util/tailwindAPI.js:14:11)
 ELIFECYCLE  Command failed with exit code 2.
```
2025-10-28 08:31:34 -07:00
Johnpaul Chiwetelu
b3da6cf1b4 Contextmenu extension migration (#5993)
This pull request refactors how context menu items are contributed by
extensions in the LiteGraph-based canvas. The legacy monkey-patching
approach for adding context menu options is replaced by a new, explicit
API (`getCanvasMenuItems` and `getNodeMenuItems`) for extensions. A
compatibility layer is added to support legacy extensions and warn
developers about deprecated usage. The changes improve maintainability,
extension interoperability, and migration to the new context menu
system.

### Context Menu System Refactor

* Introduced a new API for extensions to contribute context menu items
via `getCanvasMenuItems` and `getNodeMenuItems` methods, replacing
legacy monkey-patching of `LGraphCanvas.prototype.getCanvasMenuOptions`.
Major extension files (`groupNode.ts`, `groupOptions.ts`,
`nodeTemplates.ts`) now use this new API.
[[1]](diffhunk://#diff-b29f141b89433027e7bb7cde57fad84f9e97ffbe5c58040d3e0fdb7905022917L1779-R1771)
[[2]](diffhunk://#diff-91169f3a27ff8974d5c8fc3346bd99c07bdfb5399984484630125fdd647ff02fL232-R239)
[[3]](diffhunk://#diff-04c18583d2dbfc013888e4c02fd432c250acbcecdef82bf7f6d9fd888e632a6eL447-R458)
* Added a compatibility layer (`legacyMenuCompat` in
`contextMenuCompat.ts`) to detect and warn when legacy monkey-patching
is used, and to extract legacy-added menu items for backward
compatibility.
[[1]](diffhunk://#diff-2b724cb107c04e290369fb927e2ae9fad03be9e617a7d4de2487deab89d0d018R2-R45)
[[2]](diffhunk://#diff-d3a8284ec16ae3f9512e33abe44ae653ed1aa45c9926485ef6270cc8d2b94ae6R1-R115)

### Extension Migration

* Refactored core extensions (`groupNode`, `groupOptions`, and
`nodeTemplates`) to implement the new context menu API, moving menu item
logic out of monkey-patched methods and into explicit extension methods.
[[1]](diffhunk://#diff-b29f141b89433027e7bb7cde57fad84f9e97ffbe5c58040d3e0fdb7905022917L1633-L1683)
[[2]](diffhunk://#diff-91169f3a27ff8974d5c8fc3346bd99c07bdfb5399984484630125fdd647ff02fL19-R77)
[[3]](diffhunk://#diff-04c18583d2dbfc013888e4c02fd432c250acbcecdef82bf7f6d9fd888e632a6eL366-R373)

### Type and Import Cleanup

* Updated imports for context menu types (`IContextMenuValue`) across
affected files for consistency with the new API.
[[1]](diffhunk://#diff-b29f141b89433027e7bb7cde57fad84f9e97ffbe5c58040d3e0fdb7905022917R4-L7)
[[2]](diffhunk://#diff-91169f3a27ff8974d5c8fc3346bd99c07bdfb5399984484630125fdd647ff02fL1-R11)
[[3]](diffhunk://#diff-04c18583d2dbfc013888e4c02fd432c250acbcecdef82bf7f6d9fd888e632a6eL2-R6)
[[4]](diffhunk://#diff-bde0dce9fe2403685d27b0e94a938c3d72824d02d01d1fd6167a0dddc6e585ddR10)

### Backward Compatibility and Migration Guidance

* The compatibility layer logs a deprecation warning to the console when
legacy monkey-patching is detected, helping developers migrate to the
new API.

---

These changes collectively modernize the context menu extension
mechanism, improve code clarity, and provide a migration path for legacy
extensions.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5993-Contextmenu-extension-migration-2876d73d3650813fae07c1141679637a)
by [Unito](https://www.unito.io)

---------

Co-authored-by: github-actions <github-actions@github.com>
2025-10-28 04:02:28 +01:00
Alexander Brown
6afdb9529d Fix: Vue-Litegraph conversion bug with Reroutes (#6330)
## Summary

The private fields triggered an error in intitializing the
linkLayoutSync. Turns out that wasn't necessary anymore.

> [!NOTE]
> Edit: Doing some more investigation, it looks like the slot sync can
also be removed?

## Changes

- **What**: Converts JS private fields to typescript private, adds some
readonly declarations
- **What**: Removes the useLinkLayoutSync usage in useVueNodeLifecycle
- **What**: Removes the useSlotLayoutSync usage in useVueNodeLifecycle

## Review Focus

Was the sync doing something that wouldn't be caught in normal
usage/testing?

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6330-Fix-Vue-Litegraph-conversion-bug-with-Reroutes-2996d73d3650819ebb24e4aa2fc51c65)
by [Unito](https://www.unito.io)
2025-10-27 19:36:19 -07:00
Christian Byrne
d1c9ce5a66 change some settings for cloud-specific behavior (#6302)
## Summary

Make some settings dynamic based on whether in cloud or localhost

1. Comfy.Memory.AllowManualUnload (line 18-24)
   - Type: 'hidden' on cloud, 'boolean' on localhost
   - Default: false on cloud, true on localhost
2. Comfy.Validation.Workflows (line 25-30)
   - Default: false on cloud, true on localhost
3. Comfy.Workflow.ShowMissingModelsWarning (line 282-288)
   - Type: 'hidden' on cloud, 'boolean' on localhost
   - Default: false on cloud, true on localhost
4. Comfy.ModelLibrary.AutoLoadAll (line 387-394)
   - Type: 'hidden' on cloud, 'boolean' on localhost
5. Comfy.QueueButton.BatchCountLimit (line 595-603)
   - Default: 4 on cloud, 100 on localhost
6. Comfy.Toast.DisableReconnectingToast (line 943-949)
   - Default: true on cloud, false on localhost
7. Comfy.Assets.UseAssetAPI (line 1068-1075)
   - Default: true on cloud, false on localhost

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6302-change-some-settings-for-cloud-specific-behavior-2986d73d365081169be4ebd11823a7fa)
by [Unito](https://www.unito.io)
2025-10-27 16:57:00 -07:00
Arjan Singh
d26309c7ab feat(historyV2): create sythetic queue priority (#6336)
## Summary

Create display priority based on execution success timestamps.

Next up is displaying in progress prompts in the queue.

## Review Focus

@DrJKL and I discussed logic and decided for history, execution success
(when the prompt finishes) is the best way to assign priority.

This does differ from existing cloud logic which uses execution start
time.

For prompt progress I intend to create a priority for them based on
start time.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6336-feat-historyV2-create-sythetic-queue-priority-2996d73d365081ffa3a0f8071c178066)
by [Unito](https://www.unito.io)
2025-10-27 23:08:33 +00:00
Alexander Brown
d8657aaee3 devex: Add some acronyms (#6335)
## Summary

Suggested by @arjansingh, encourage the LLMs to consider more classic
practices.
2025-10-27 21:53:52 +00:00
Christian Byrne
ddbf2cc720 skip system notifications on cloud (#6333)
## Summary

This code has no effect but adding it so that when the `!isElectron()`
condition is removed, maintainers will remember this extra condition
that must still be enforced.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6333-skip-system-notifications-on-cloud-2996d73d3650818b8335d395c86badf3)
by [Unito](https://www.unito.io)
2025-10-27 14:25:43 -07:00
Christian Byrne
ed49a82c20 fix subscription panel badge bg color (#6334)
## Summary

This should use the dialog bg color instead of menu bg when it is on a
dialog.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6334-fix-subscription-panel-badge-bg-color-2996d73d36508171bf5bd2a8dc54f7c3)
by [Unito](https://www.unito.io)
2025-10-27 14:24:55 -07:00
Christian Byrne
234fc3433c use prompt id rather than queue index for history reconciliation (#6327)
## Summary

Changed history reconciliation to use `promptId` instead of `queueIndex`
to support cloud environments that don't have queue indices.

Refactored into pure functions with expressive naming.

## Changes

- Use `promptId` (unique UUID) for reconciliation instead of
`queueIndex` (not available in cloud)
- Extract `reconcileHistoryWithServer` with helpers: `extractPromptIds`,
`isAddedAfter`, `sortNewestFirst`
- Added 34 tests covering reconciliation edge cases, sorting, and limits

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6327-use-prompt-id-rather-than-queue-index-for-history-reconciliation-2996d73d3650818e9471dbeb53cc23bb)
by [Unito](https://www.unito.io)
2025-10-26 23:58:29 -07:00
Christian Byrne
0a957fb2ac ci: leave comment and abort early if commit already exists on target branch in backport workflow (#6326)
Follow up on https://github.com/Comfy-Org/ComfyUI_frontend/pull/6317:
Fixes confusing "merge conflicts detected" message when commit already
exists on target branch (e.g., PRs #6294 and #6307).

## Changes

- Added check to detect if merge commit already exists on target branch
before attempting cherry-pick
- New failure reason `already-exists` for this case
- Clear comment message: "Commit already exists on branch, no backport
needed" instead of confusing "Merge conflicts detected" with empty file
list

## Before

When a commit already existed on the target branch, users would see:
> @user Backport to `core/1.30` failed: Merge conflicts detected.
> Please manually cherry-pick commit `abc123` to the `core/1.30` branch.
> <details><summary>Conflicting files</summary>
> 
> </details>

This was confusing because there were no actual conflicts - the commit
was already present.

## After

Users now see:
> @user Commit `abc123` already exists on branch `core/1.30`. No
backport needed.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6326-ci-leave-comment-and-abort-early-if-commit-already-exists-on-target-branch-in-backport-w-2996d73d36508167aff3e6783e832c74)
by [Unito](https://www.unito.io)
2025-10-26 23:37:22 -07:00
Christian Byrne
28a6089a94 ci: automate cloud release branch tagging (#6321)
Changes the RC minor version branch release automation to create paired
`core/x.y` and `cloud/x.y` branches whenever a release bump merges.

Then changes the backport workflow to accept labels that match those
branch names directly, allowing engineers to route fixes to either OSS
or cloud release lines without extra labels or artifacts.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6321-ci-automate-cloud-release-branch-tagging-2996d73d365081b0b036ebd3f088354b)
by [Unito](https://www.unito.io)
2025-10-26 22:47:15 -07:00
Christian Byrne
298b3c629b ci: update automated backport workflow to skip if backport already in-flight or completed (#6317)
Updates the backport workflow so it filters out branches that have
already been backported, allowing new labels to trigger fresh
cherry-picks without reprocessing completed targets.

Also adds an automatic cleanup step that removes the `needs-backport`
trigger label once a run succeeds, aligning its behavior with the other
label-driven automations.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6317-ci-update-automated-backport-workflow-to-skip-if-backport-already-in-flight-or-completed-2996d73d36508113b90df43b1f68344f)
by [Unito](https://www.unito.io)
2025-10-26 22:14:30 -07:00
sno
20a1a9eda2 [bugfix] fix @intlify/vue-i18n/no-raw-text linting errors (#6280)
## Summary

This PR fixes all @intlify/vue-i18n/no-raw-text linting errors
identified in #5625 by replacing raw text strings with proper i18n
translation function calls.

## Changes

Fixed i18n linting errors in the following files:
- `src/components/widget/SampleModelSelector.vue` - "Upload Model" →
`$t('g.upload')`
- `src/components/topbar/CurrentUserButton.vue` - "user profile" →
`$t('g.currentUser')`
- `src/components/sidebar/tabs/nodeLibrary/NodeHelpPage.vue` - "Loading
help" → `$t('g.loading')`
- `src/components/sidebar/SidebarShortcutsToggleButton.vue` -
"shortcuts.shortcuts" → `$t('shortcuts.shortcuts')`
- `src/components/sidebar/SidebarLogoutIcon.vue` - "sideToolbar.logout"
→ `$t('sideToolbar.logout')`
- `src/components/sidebar/SidebarHelpCenterIcon.vue` - "menu.help" →
`$t('menu.help')`
- `src/components/sidebar/SidebarBottomPanelToggleButton.vue` -
"sideToolbar.labels.console" → `$t('sideToolbar.labels.console')`
- `src/components/load3d/controls/viewer/ViewerCameraControls.vue` -
"fov" → `t('load3d.fov')`
- `src/components/helpcenter/HelpCenterMenuContent.vue` - "Help Center
Menu" and "Recent releases" → `$t()` calls

All raw text strings have been replaced with appropriate i18n
translation keys that already exist in `src/locales/en/main.json`.

## Related Issue

Fixes errors reported in CI job:
https://github.com/Comfy-Org/ComfyUI_frontend/actions/runs/18705105609/job/53341658467?pr=5625

This PR aims to help #5625 pass CI/CD checks.

## Test Plan

- All i18n linting errors should be resolved
- No functionality changes - only proper use of i18n system
- Existing translation keys are used from the locale files

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6280-bugfix-fix-intlify-vue-i18n-no-raw-text-linting-errors-2976d73d365081369b43de01486fb409)
by [Unito](https://www.unito.io)

---------

Co-authored-by: GitHub Action <action@github.com>
2025-10-26 22:12:45 -07:00
Christian Byrne
ca45b2c4d6 [bugfix] use raw template ID for workflow_name in telemetry tracking (#6320)
## Summary

Fixes `trackTemplate` calls to use raw template ID instead of translated
workflow name for analytics tracking.

## Problem

When users load templates with non-English locale, the `workflow_name`
field was being tracked in their language (e.g., "默认工作流" for Chinese
users) instead of English. This makes statistical analysis difficult.

## Changes

- Updated both `trackTemplate` calls in `useTemplateWorkflows.ts` to use
raw `id` instead of translated `workflowName`
- The translated name is still used for display purposes in
`loadGraphData`

**Before:**
```typescript
useTelemetry()?.trackTemplate({
  workflow_name: workflowName, // Could be "默认工作流"
  template_source: sourceModule
})
```

**After:**
```typescript
useTelemetry()?.trackTemplate({
  workflow_name: id, // Always "default"
  template_source: sourceModule
})
```

## Testing

- [x] Type checking passes
- [x] Verified translated names still used for display

## Related

Part of comprehensive i18n audit for telemetry tracking. Related to
template metadata English tracking in PR #6304.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6320-bugfix-use-raw-template-ID-for-workflow_name-in-telemetry-tracking-2996d73d365081d8aef3fa2529a10247)
by [Unito](https://www.unito.io)
2025-10-26 22:02:27 -07:00
Christian Byrne
1453afad12 refactor: rename size report workflows to match naming pattern of other workflows (#6322)
## Summary

Changes gh workflow names and job names to match the unified naming
style of the other workflows.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6322-refactor-rename-size-report-workflows-to-match-naming-pattern-of-other-workflows-2996d73d365081c79cfcdfcb9013c3e1)
by [Unito](https://www.unito.io)
2025-10-26 21:51:38 -07:00
Christian Byrne
5de1a91f02 ci: fix "size report" (bundle size) markdown table comment formatting (#6318)
## Summary

Fixes https://github.com/Comfy-Org/ComfyUI_frontend/issues/6136 by
adding empty lines between html and markdown in the bundle size report
comments.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6318-ci-fix-size-report-bundle-size-markdown-table-comment-formatting-2996d73d36508104872be56c238339a8)
by [Unito](https://www.unito.io)
2025-10-26 21:06:59 -07:00
Christian Byrne
b3eee54abb fix: claude-review bails on cancelled and skipped checks (#6316)
## Summary

Expands the wait step in `pr-claude-review.yaml` so the
`lewagon/wait-on-check-action` accepts every terminal GitHub conclusion
instead of failing on outcomes like cancelled or failure. After the wait
completes, the `check-status` script still inspects those check runs and
only marks `should-proceed=true` when the tracked jobs finished
successfully.

Fixes what happened here:
https://github.com/Comfy-Org/ComfyUI_frontend/actions/runs/18828179272/job/53714488799?pr=6298
2025-10-26 20:39:18 -07:00
Christian Byrne
1ee33673ab [bugfix] fix survey properties mapping to match actual survey data (#6314)
## Summary

Updates `SurveyResponses` interface to match actual survey fields 1-to-1
based on analytics requirements.

## Changes

- Remove `team_size` property (no corresponding survey question exists)
- Change `use_case` to `useCase` to match actual survey field name
- Remove `intended_use` property (doesn't exist in actual survey)
- Add `making` field array for content generation type tracking (video,
image, etc.)

This fixes a Mixpanel analytics issue where survey properties weren't
mapping 1-to-1 with actual survey questions, making statistical analysis
difficult.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6314-bugfix-fix-survey-properties-mapping-to-match-actual-survey-data-2996d73d36508158a335e6b73e3e14ef)
by [Unito](https://www.unito.io)
2025-10-26 18:04:44 -07:00
Terry Jia
9f5245dc80 [refactor] Split mask editor into smaller files (#6308)
## Summary

Split mask editor structure into smaller files

## Changes

This PR is a prerequisite step for [the issue - refactoring the mask
editor using
Vue](https://github.com/Comfy-Org/ComfyUI_frontend/issues/5956). It
splits the current monolithic maskeditor.ts (about 5700 lines) into
separate files; otherwise, the original single file would be very
difficult to analyze or maintain.

This PR itself does not introduce any Vue, nor should it have any
functional changes or modifications. It's purely a code-level split,
with all related files placed in the maskeditor folder.

The original maskeditor.ts has been renamed to maskeditor.ts.backup for
future reference.

## Review Focus

Since this PR is only for splitting purposes, all logic remains
consistent with the original. Therefore, for any reviewer: any code
logic improvements should happen in the subsequent Vue-based
refactoring, not in this PR.

Following this PR, I will perform a Vue-based refactoring of the mask
editor to align with the frontend's overall Vue architecture and provide
better cloud-related support.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6308-refactor-Split-mask-editor-into-smaller-files-2986d73d36508131937dd43e465a47bd)
by [Unito](https://www.unito.io)
2025-10-26 17:34:36 -07:00
Comfy Org PR Bot
cacd7e3251 1.31.0 (#6313)
Minor version increment to 1.31.0

**Base branch:** `main`

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6313-1-31-0-2986d73d365081e9ac6ccb91b4ddad0a)
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>
2025-10-26 17:20:40 -07:00
Alexander Piskun
fca0ea72f7 feat(api-nodes): add pricing for new LTXV-2 models (#6307)
## Summary

For the upcoming LTXV API nodes.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6307-feat-api-nodes-add-pricing-for-new-LTXV-2-models-2986d73d365081db9994deffc219c6c4)
by [Unito](https://www.unito.io)
2025-10-26 11:45:55 -07:00
Christian Byrne
9f36158959 translate all analytics to English for template metadata (#6292)
## Summary

Track template metadata in English for analytics regardless of user's
locale to enable consistent statistical analysis.

## Changes

- **What**: Load English [template
index](https://github.com/Comfy-Org/ComfyUI_frontend/tree/main/public/templates)
alongside localized version (cloud builds only)
- **What**: Added `getEnglishMetadata()` method to
`workflowTemplatesStore` that returns English versions of template tags,
category, useCase, models, and license
- **What**: Updated `MixpanelTelemetryProvider` to prefer English
metadata for analytics events, falling back to localized values

## Review Focus

English template fetch only triggers in cloud builds via `isCloud` flag.
Non-cloud builds see no bundle size impact. Method returns null when
English templates unavailable, with fallback to localized data ensuring
analytics continue working in edge cases.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6292-translate-all-analytics-to-English-for-template-metadata-2986d73d365081fdbc21f372aa9bb41e)
by [Unito](https://www.unito.io)

---------

Co-authored-by: github-actions <github-actions@github.com>
2025-10-26 02:10:02 -07:00
Christian Byrne
857c13158b set Sentry config based on distribution (#6301)
Set the config based on compile-time DISTRIBUTION env var.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6301-set-Sentry-config-based-on-distribution-2986d73d3650815f9b7ef821bbdd745f)
by [Unito](https://www.unito.io)
2025-10-26 02:05:23 -07:00
Christian Byrne
cd50c54e61 add session cookie auth on cloud dist (#6295)
## Summary

Implemented cookie-based session authentication for cloud distribution,
replacing service worker approach with extension-based lifecycle hooks.

## Changes

- **What**: Added session cookie management via [extension
hooks](https://docs.comfy.org/comfyui/extensions) for login, token
refresh, and logout events
- **Architecture**: DDD-compliant structure with platform layer
(`src/platform/auth/session/`) and cloud-gated extension
- **New Extension Hooks**: `onAuthTokenRefreshed()` and
`onAuthUserLogout()` in [ComfyExtension
interface](src/types/comfy.ts:220-232)

```mermaid
sequenceDiagram
    participant User
    participant Firebase
    participant Extension
    participant Backend

    User->>Firebase: Login
    Firebase->>Extension: onAuthUserResolved
    Extension->>Backend: POST /auth/session (with JWT)
    Backend-->>Extension: Set-Cookie

    Firebase->>Firebase: Token Refresh
    Firebase->>Extension: onAuthTokenRefreshed
    Extension->>Backend: POST /auth/session (with new JWT)
    Backend-->>Extension: Update Cookie

    User->>Firebase: Logout
    Firebase->>Extension: onAuthUserLogout (user null)
    Extension->>Backend: DELETE /auth/session
    Backend-->>Extension: Clear Cookie
```

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6295-add-session-cookie-auth-on-cloud-dist-2986d73d365081868c56e5be1ad0d0d4)
by [Unito](https://www.unito.io)
2025-10-26 00:04:30 -07:00
Christian Byrne
3db1b153f3 make topbar badges responsive and fix server health badges showing on unrelated dialogs (#6291)
## Summary

Implemented responsive topbar badges with three display modes (full,
compact, icon-only) using Tailwind breakpoints and PrimeVue Popover
interactions.


https://github.com/user-attachments/assets/57912253-b1b5-4a68-953e-0be942ff09c4

## Changes

- **What**: Replaced hardcoded 880px breakpoint with [Tailwind
breakpoints](https://tailwindcss.com/docs/responsive-design) via
[@vueuse/core](https://vueuse.org/core/useBreakpoints/)
  - `xl (≥1280px)`: Full display (icon + label + text)
  - `lg (≥1024px)`: Compact (icon + label, click for popover)
  - `<lg (<1024px)`: Icon-only (icon/label/dot, click for popover)
- **What**: Added `CloudBadge` component to isolate static "Comfy Cloud
BETA" badge from runtime store badges
- **What**: Added `backgroundColor` prop to support different contexts
(topbar vs dialog backgrounds)

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6291-make-topbar-badges-responsive-and-fix-server-health-badges-showing-on-unrelated-dialogs-2986d73d365081d294e5c9a7af1aafb2)
by [Unito](https://www.unito.io)
2025-10-25 23:42:37 -07:00
Christian Byrne
d9e62985c6 remove all auth service work related code (#6294)
## Summary

Removes all service worker auth code, as it is being replaced by a more
robust standard solution for authenticating view and viewvideo requests
in https://github.com/Comfy-Org/ComfyUI_frontend/pull/6295.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6294-remove-all-auth-service-work-related-code-2986d73d36508170a24bf1c42cad401e)
by [Unito](https://www.unito.io)
2025-10-25 23:08:41 -07:00
Christian Byrne
e5d5c042c7 fix and enable skipped Vue nodes bypass test (#6290)
This test was failing on main for a few days so it was marked as
`fixme`. The failure was due to interaction with minimap. Just turn off
the minimap for now as it's not really related to what the test is
targeting (arguable). Better to at least have the coverage for now.

Context:

- https://github.com/Comfy-Org/ComfyUI_frontend/pull/6127

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6290-fix-and-enable-skipped-Vue-nodes-bypass-test-2986d73d3650819faeaaf414ce2b6e61)
by [Unito](https://www.unito.io)

---------

Co-authored-by: github-actions <github-actions@github.com>
2025-10-25 23:03:05 -07:00
Christian Byrne
9350c857a2 style: use "text-button-icon" token for dropdown icons on Vue nodes (#6284)
## Summary

Change these dropdown icons to match [the
design](https://www.figma.com/design/31uH3r4x3xbIctuRWYW6NM/V3---Vue-Nodes?node-id=6189-6820&m=dev).

**Before**

<img width="692" height="694" alt="Selection_2196"
src="https://github.com/user-attachments/assets/aa1fb135-0ad0-4ab6-8c79-7163349b5fae"
/>

**After**

<img width="600" height="602" alt="Selection_2195"
src="https://github.com/user-attachments/assets/742ddef4-4b51-48ba-98a0-9517267de0e1"
/>

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6284-style-use-text-button-icon-token-for-dropdown-icons-on-Vue-nodes-2976d73d365081d29d2aee51e836caaf)
by [Unito](https://www.unito.io)

---------

Co-authored-by: github-actions <github-actions@github.com>
2025-10-25 22:36:01 -07:00
Arjan Singh
c67c93ff4b feat(api): add history_v2 for cloud outputs (#6288)
## Summary

Backport outputs from new cloud history endpoint

Does:
1. Show history in the Queue
2. Show outputs from prompt execution

Does not:
1. Handle appending latest images generated to queue history
2. Making sure that workflow data from images is available from load
(requires additional API call to fetch)

Most of this PR is:
1. Test fixtures (truncated workflow to test).
2. The service worker so I could verify my changes locally.

## Changes

- Add `history_v2` to `history` adapter
- Add tests for mapping
- Do branded validation for promptIds (suggestion from @DrJKL)
- Create a dev environment service worker so we can view cloud hosted
images in development.

## Review Focus

1. Is the dev-only service work the right way to do it? It was the
easiest I could think of.
4. Are the validation changes too heavy? I can rip them out if needed.

## Screenshots 🎃 


https://github.com/user-attachments/assets/1787485a-8d27-4abe-abc8-cf133c1a52aa

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6288-Feat-history-v2-outputs-2976d73d365081a99864c40343449dcd)
by [Unito](https://www.unito.io)

---------

Co-authored-by: bymyself <cbyrne@comfy.org>
2025-10-25 22:16:38 -07:00
sno
e0e6d15cbb [refactor] remove downlevelIteration from tsconfig (#6279)
Remove downlevelIteration compiler option as it's no longer needed with
ES2023 target.

picked from - [\[Cleanup\] Remove TS migration lines from tsconfig by
webfiltered · Pull Request #5330 · Comfy-Org/ComfyUI_frontend](
https://github.com/Comfy-Org/ComfyUI_frontend/pull/5330 )

🤖 Generated with [Claude Code](https://claude.com/claude-code)

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6279-refactor-remove-downlevelIteration-from-tsconfig-2976d73d3650819a9d4be716006e8b85)
by [Unito](https://www.unito.io)

Co-authored-by: Claude <noreply@anthropic.com>
2025-10-26 13:30:13 +09:00
Christian Byrne
2ea6c92030 sort template workflows by required vram (#6285)
## Summary

Resolves https://github.com/Comfy-Org/ComfyUI_frontend/issues/6281 by
implementing the stubbed out vram sorting. Previously was waiting for it
to be added to the templates data and it now has
(https://github.com/Comfy-Org/workflow_templates/blob/main/templates/index.json)

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6285-sort-template-workflows-by-required-vram-2976d73d36508164a8f9fab438f53b21)
by [Unito](https://www.unito.io)
2025-10-25 15:52:24 -07:00
Christian Byrne
23e0d26ff8 add fuzzy searching to assets dialog (#6286)
## Summary

Add `useFuse` for assets searching to enable fuzzy searching with typo
tolerance.


https://github.com/user-attachments/assets/0c55bb77-3223-45ab-8c05-713f8ce4e58b

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6286-add-fuzzy-searching-to-assets-dialog-2976d73d3650813da63acadde2cf49c6)
by [Unito](https://www.unito.io)
2025-10-25 14:02:46 -07:00
Christian Byrne
95b3b509c7 [bugfix] add mode: no-cors to fix CORS error when following GCS redirects (#6277)
Fixes CORS error when service worker follows redirects to GCS by using
mode: 'no-cors' to allow cross-origin fetches without CORS headers.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6277-bugfix-add-mode-no-cors-to-fix-CORS-error-when-following-GCS-redirects-2976d73d36508101a4cbd7b59106dfc3)
by [Unito](https://www.unito.io)
2025-10-24 23:07:29 -07:00
Christian Byrne
936da14dbc [bugfix] fix service worker opaqueredirect error and ensure SW controls page before mount (#6275)
Fixes service worker network error by handling opaqueredirect responses
correctly and ensures SW registration completes before app mount to
prevent race conditions on first load.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6275-bugfix-fix-service-worker-opaqueredirect-error-and-ensure-SW-controls-page-before-mount-2976d73d36508106bc65dc82cdc62779)
by [Unito](https://www.unito.io)
2025-10-24 22:30:16 -07:00
Christian Byrne
bab47869c9 [bugfix] fix service worker registration timing to run after Pinia setup (#6272)
## Summary

Fixes Pinia initialization error that occurred when the auth service
worker tried to access stores before Pinia was set up.

## Problem

The auth service worker was being imported at the top of `main.ts`,
causing it to register immediately:

```typescript
import '@/platform/auth/serviceWorker'  // Runs immediately on import
```

This happened before Pinia was initialized, causing an error when the
service worker setup tried to use stores:

```
Error: [🍍]: "getActivePinia()" was called but there was no active Pinia.
```

## Solution

Moved the service worker registration to after Pinia is set up but
before mounting the app:

```typescript
  .use(pinia)
  .use(i18n)
  .use(VueFire, {
    firebaseApp,
    modules: [VueFireAuth()]
  })

// Register auth service worker after Pinia is initialized (cloud-only)
if (isCloud) {
  void import('@/platform/auth/serviceWorker')
}

app.mount('#vue-app')
```

## Benefits

-  Pinia stores are available when service worker setup runs
-  Still tree-shaken for non-cloud builds via dynamic import in `if
(isCloud)`
-  Registers before mounting, so service worker is active from the
start

## Testing

- Verified no Pinia errors in cloud builds
- Verified tree-shaking still works (service worker code not in
localhost builds)

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6272-bugfix-fix-service-worker-registration-timing-to-run-after-Pinia-setup-2976d73d365081b998dfd2eded782070)
by [Unito](https://www.unito.io)
2025-10-24 20:16:02 -07:00
Alexander Brown
3c28dd28cc Style: Remove currently non-functional collapsed slots (#6264)
## Summary

Address request to remove confusing hover behavior in the collapsed
state

## Review Focus

Confirmed with Alex that it's okay to remove these now and add them back
with the implementation instead of leaving them as decorative elements.

Also fixes a bug where the slots _all_ revealed when a node was hovered.

## Screenshots (if applicable)
<img width="769" height="376" alt="image"
src="https://github.com/user-attachments/assets/ec0037af-fd2e-4855-b0e4-f994f9ef9a0b"
/>

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6264-Style-Remove-currently-non-functional-collapsed-slots-2976d73d36508191914def8889491e8e)
by [Unito](https://www.unito.io)

---------

Co-authored-by: github-actions <github-actions@github.com>
2025-10-24 20:11:17 -07:00
Rizumu Ayaka
15c7c56f83 feat: deprecated API alert (#6090)
When an extension imports deprecated APIs, this log will appear:
```bash
[ComfyUI Deprecated] Importing from <file path> is deprecated. <guidance information>. This will be removed in <version>. See: <url>
```


┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6090-feat-deprecated-API-alert-28e6d73d365081898c5ddafcc025d6f8)
by [Unito](https://www.unito.io)
2025-10-24 19:26:11 -07:00
Comfy Org PR Bot
c7c513ae4a 1.30.3 (#6261)
Patch version increment to 1.30.3

**Base branch:** `main`

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6261-1-30-3-2966d73d365081799d90f8510c7e23dc)
by [Unito](https://www.unito.io)

---------

Co-authored-by: simula-r <18093452+simula-r@users.noreply.github.com>
Co-authored-by: github-actions <github-actions@github.com>
2025-10-24 19:22:54 -07:00
Christian Byrne
b1439be7f0 remove checkbox from sign up form (#6269)
## Summary

Removes the checkbox from the sign up form to simplify the user
experience. The "By clicking 'Next' or 'Sign Up'..." notice at the
bottom already covers terms and privacy.

## Changes

- Removed checkbox field from sign up schema
- Updated `SignUpForm.vue` component
- Removed unused `Checkbox` import

## Before/After

**Before:**
Sign up form included a checkbox that users had to check before
submitting

**After:**
Sign up form is cleaner without the checkbox. The existing notice text
covers the same information:
> "By clicking 'Next' or 'Sign Up', you agree to our Terms of Use and
Privacy Policy."

This notice appears on both sign in and sign up modals.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6269-remove-checkbox-from-sign-up-form-2976d73d3650819ab480e4db6685baee)
by [Unito](https://www.unito.io)
2025-10-24 19:21:16 -07:00
Christian Byrne
81fc65e59b [bugfix] fix auth service worker to handle cross-origin redirects to GCS (#6265)
## Summary

Fixes CORS errors in HTTPS environments where the auth service worker
blocked cross-origin redirects to Google Cloud Storage.

## Problem

The service worker was using `mode: 'same-origin'` which prevented
following redirects when `/api/view` returns a 302 redirect to GCS:

```
Unsafe attempt to load URL https://storage.googleapis.com/... 
from frame with URL https://testcloud.comfy.org/auth-sw.js. 
Domains, protocols and ports must match.
```

This only occurred in HTTPS/cloud environments where media is served
from GCS. Localhost/HTTP test environments serve files directly without
redirects, so the issue wasn't caught there.

## Solution

Changed redirect handling from automatic to manual:

1. **Initial request to `/api/view`**: Sends WITH auth headers
(validates user access)
2. **Detect redirect response**: Checks for 301/302/opaqueredirect 
3. **Follow redirect to GCS**: Fetches WITHOUT auth headers (signed URL
has built-in auth)

### Key Changes

- Removed `mode: 'same-origin'` (was blocking cross-origin redirects)
- Changed `redirect: event.request.redirect` to `redirect: 'manual'`
- Added manual redirect handling that follows to GCS without Firebase
auth headers

## Why This Works

The two requests have different authentication mechanisms:
- **`/api/view` request**: Uses Firebase auth header (backend validates
user access)
- **GCS request**: Uses signed URL with query params (`Signature=...`,
`GoogleAccessId=...`, `Expires=...`)

The security check still happens on the initial `/api/view` request, but
we allow the redirect to GCS to use its own authentication system.

## Testing

- Typecheck passed
- Should be tested in HTTPS cloud environment with media files stored in
GCS

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6265-bugfix-fix-auth-service-worker-to-handle-cross-origin-redirects-to-GCS-2976d73d365081d0b124db4918f8194e)
by [Unito](https://www.unito.io)
2025-10-24 18:31:38 -07:00
Alexander Brown
55c9cf7b57 Style: explicit font size for the badges (#6260)
## Summary

More consistent height regardless the text content.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6260-Style-explicit-font-size-for-the-badges-2966d73d365081bd90a0d01f7e104b9a)
by [Unito](https://www.unito.io)

---------

Co-authored-by: github-actions <github-actions@github.com>
2025-10-24 14:54:59 -07:00
AustinMroz
5897e00793 Fix disconnection of subgraphInput links (#6258)
`LLink.disconnect` is intended to cleanup only the link itself. #4800
mistakenly assumed that it would perform all required steps for
disconnection. Later, #5015 would partially resolve this by adding some
of the missing functionality into `LLink.disconnect`, but this still
left output cleanup unhandled and failed to call
`node.onConnectionsChanged`.

This PR instead moves the disconnection code to call the function that
already has robust handling for these items and removes the
no-longer-needed and potentially misleading workaround.

Resolves #6247

Also un-skipped several SubgraphIO tests. They appear to function fine.
I'm assuming the reasons for them being skipped have been resolved.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6258-Fix-disconnection-of-subgraphInput-links-2966d73d36508112ad1fe602cdcf461b)
by [Unito](https://www.unito.io)
2025-10-24 13:37:14 -07:00
Alexander Brown
233004c837 Feat: Add Badges for Vue Nodes (#6243)
## Summary

Adds the badges to the header for Vue nodes.


## Review Focus

Design, mostly. Any structures here I'm not handling but should be?

## Screenshots
<img width="1514" height="642" alt="image"
src="https://github.com/user-attachments/assets/387fd2f6-bb4b-4fee-b273-6166a52a3552"
/>

<!-- Add screenshots or video recording to help explain your changes -->

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6243-Feat-Add-Badges-for-Vue-Nodes-2956d73d36508184a250d67b127ed4b1)
by [Unito](https://www.unito.io)

---------

Co-authored-by: github-actions <github-actions@github.com>
2025-10-24 11:24:27 -07:00
Hendrik Wiese
7663517f54 Fix reference to .env_example in CONTRIBUTING.md (#6255)
## Summary

Found a broken link in the CONTRIBUTING.md, thought I'd quickly fix it.

// scoutsdeed

## Changes

- **What**: link in CONTRIBUTING.md
- **Breaking**: None
- **Dependencies**: None

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6255-Fix-reference-to-env_example-in-CONTRIBUTING-md-2966d73d365081a6bef2cc974a2ead9b)
by [Unito](https://www.unito.io)
2025-10-24 09:31:38 -07:00
AustinMroz
7f5efca00b Respect minimum node size on subgraph conversion (#6241)
Two small changes to improve sizing on subgraphs
- On conversion, automatically promoted widgets can increase the minimum
width of a node. When this occurs, the node is now automatically resized
to respect this new minimum. <img width="434" height="274" alt="image"
src="https://github.com/user-attachments/assets/8b642f12-24bf-439a-a07d-b392b1f406df"
/>

- On nodes with title_badges, titles now have greatly reduced empty
padding before being abbreviated. <img width="314" height="123"
alt="image"
src="https://github.com/user-attachments/assets/4d8fd899-a159-4c0d-b309-04844b6203fc"
/>


┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6241-Respect-minimum-node-size-on-subgraph-conversion-2956d73d3650817c867fe42d60afa28b)
by [Unito](https://www.unito.io)
2025-10-24 09:23:41 -07:00
AustinMroz
a108c52572 custom_node provided blueprints (#6172)
Frontend implementation for a system to allow custom_nodes to provide a
set of subgraph blueprints.

Requires comfyanonymous/ComfyUI#10438, but handles gracefully in unavailable.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6172-Early-POC-custom_node-provided-blueprints-2926d73d3650814982ecd43f12abd873)
by [Unito](https://www.unito.io)

---------

Co-authored-by: Christian Byrne <cbyrne@comfy.org>
2025-10-24 08:24:34 -07:00
Arjan Singh
8ed9be20a9 feat(useRemoteWidget): add cloud firebase auth (#6249)
## Summary

Add Firebase authentication for `useRemoteWidget` Cloud API calls.

## Changes

- Incorporate changes from
c27edb7e94
- Add tests

## Screenshots


<img width="849" height="552" alt="Screenshot 2025-10-23 at 8 41 00 PM"
src="https://github.com/user-attachments/assets/23e07aac-22ea-4222-a90c-00335937a011"
/>

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6249-feat-useRemoteWidget-add-cloud-firebase-auth-2966d73d36508121935efc9ed07c47d2)
by [Unito](https://www.unito.io)
2025-10-23 22:54:45 -07:00
Terry Jia
393f77e27a add widget-expands on Markdown widget (#6245)
## Summary
Markdown widget also needs widget-expands class

before adding  widget-expands
<img width="545" height="503" alt="image"
src="https://github.com/user-attachments/assets/db8fbf0b-5371-4d2b-9e81-0a37b5e07086"
/>

after added widget-expands
<img width="578" height="513" alt="image"
src="https://github.com/user-attachments/assets/9aa94807-167d-4ee1-bcc3-ee23d2673dfe"
/>

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6245-add-widget-expands-on-Markdown-widget-2966d73d365081c29ae4c4c82fdf4372)
by [Unito](https://www.unito.io)
2025-10-23 22:54:26 -07:00
AustinMroz
ec11d7825d Show default node list after clearing search input (#6231)
![searchbox-clear](https://github.com/user-attachments/assets/94737766-bf96-4f03-86a0-70057b3b0e81)
Resolves #4887 

<details>
<summary>The dirty details</summary>
There's really not a cleaner workaround here. Prime vue is hardcoded to
hide results when a query is received with length 0. With our search
box, we never want completions not to be shown and the sanest, if not
the only viable solution, is to simply block the hiding completely.

Future TODO:
- Completely remove the reFocusInput jank. If we can make the search box
a frame more responsive, we should.

```ts
onInput(event) {
    if (this.typeahead) {
        if (this.searchTimeout) {
            clearTimeout(this.searchTimeout);
        }

        let query = event.target.value;

        if (!this.multiple) {
            this.updateModel(event, query);
        }

        if (query.length === 0) {
            this.hide();
            this.$emit('clear');
        } else {
            if (query.length >= this.minLength) {
                this.focusedOptionIndex = -1;

                this.searchTimeout = setTimeout(() => {
                    this.search(event, query, 'input');
                }, this.delay);
            } else {
                this.hide();
            }
        }
    }
}
hide(isFocus) {
    const _hide = () => {
        this.$emit('before-hide');
        this.dirty = isFocus;
        this.overlayVisible = false;
        this.clicked = false;
        this.focusedOptionIndex = -1;

        isFocus && focus(this.multiple ? this.$refs.focusInput : this.$refs.focusInput?.$el);
    };

    setTimeout(() => {
        _hide();
    }, 0); // For ScreenReaders
}
```
</details>

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6231-Show-default-node-list-after-clearing-search-input-2956d73d36508134960df537c7409646)
by [Unito](https://www.unito.io)
2025-10-23 22:53:58 -07:00
Christian Byrne
93518f7f54 [chore] Regenerate browser test snapshots (#6253)
## Summary
Trigger snapshot regeneration via CI.

## Test plan
- CI will regenerate snapshots when the `New Browser Test Expectations`
label is added

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6253-chore-Regenerate-browser-test-snapshots-2966d73d365081009b0cdf1a924af7e6)
by [Unito](https://www.unito.io)

---------

Co-authored-by: github-actions <github-actions@github.com>
2025-10-23 22:34:08 -07:00
Johnpaul Chiwetelu
3fe74c7b1e Hotfix CI update test expectations (#6252)
This pull request updates the workflow for managing Playwright
expectation snapshots in
`.github/workflows/pr-update-playwright-expectations.yaml`. The main
focus is to improve how changed snapshot files are handled, ensuring
directory structures are correct and only necessary files are processed
and committed. The most important changes are grouped below:

**Snapshot file handling improvements:**

* When copying changed snapshot files, the workflow now strips the
`browser_tests/` prefix to avoid double nesting in the staging
directory. This ensures the directory structure remains correct when
files are merged back.
* During the merging step, the script clarifies that files are already
in the correct structure (without the `browser_tests/` prefix), so they
can be copied directly into `browser_tests/`.

**Workflow and commit logic enhancements:**

* The snapshot artifact download step is renamed for clarity, indicating
that only changed snapshot files are downloaded from shards.
* The commit step is improved to check for actual changes in
`browser_tests/` before attempting to commit and push. It sets an output
variable (`has-changes`) to control subsequent steps, adds more
informative logging, and only pushes when there are changes.
* The "Add Done Reaction" step is now conditional on both the event type
and whether there were changes to commit, preventing unnecessary
reactions.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6252-Hotfix-CI-update-test-expectations-2966d73d36508131968ee5a7f04ff787)
by [Unito](https://www.unito.io)

---------

Co-authored-by: github-actions <github-actions@github.com>
2025-10-23 21:58:10 -07:00
Johnpaul Chiwetelu
82e777bb7e Patch Update expectations CI (#6250)
This pull request updates the Playwright snapshot update workflow to
improve efficiency and clarity. The workflow now only uploads and merges
changed snapshot files from each shard, reducing unnecessary artifact
size and processing. It also adds better logging and summaries for
easier debugging and review.

**Snapshot handling improvements:**

* Only changed snapshot files are identified, staged, and uploaded as
artifacts per shard, instead of uploading all snapshot files. This
reduces artifact size and upload time.
* During the merge step, only the changed files from each shard are
merged back into the `browser_tests` directory, preserving directory
structure and avoiding redundant operations.

**Logging and debugging enhancements:**

* Added steps to list downloaded snapshot files and summarize the
changes after merging, making it easier to see what was updated and
debug any issues.
Sample run is here
https://github.com/Myestery/ComfyUI_frontend/actions/runs/18768857441

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6250-Patch-Update-expectations-CI-2966d73d365081b790a0fad66649a10b)
by [Unito](https://www.unito.io)
2025-10-23 21:19:21 -07:00
Christian Byrne
d7a58a7a9b change cloud feature flags to be loaded dynamically at runtime rather than set in build (#6246)
## Summary

Implements server-side remote configuration to decouple runtime behavior
from build artifacts, enabling dynamic configuration updates without
redeployment.

## Technical Changes

- **Replaced** build-time constants (`__MIXPANEL_TOKEN__`,
`__BUILD_FLAGS__`) with runtime configuration loaded from
`/api/features`
- Configuration now sourced from `window.__CONFIG__` (hydrated from
`/api/features` endpoint)
- **Added** `src/platform/remoteConfig/` service that polls server
configuration every 30 seconds
- **Modified** application bootstrap sequence in `main.ts` to load
remote config before module initialization (required for cloud builds)
- **Removed** global constants: `__BUILD_FLAGS__`, `__MIXPANEL_TOKEN__`.
Runtime subscription enforcement toggle via `subscription_required` flag
- Server health alerts with variant-based severity rendering
(info/warning/error) via topbar badges

## Rationale

- **Build-once-deploy-anywhere**: Single immutable artifact promoted
through environments (staging → production)
- **Zero-downtime configuration**: Update behavior without rebuilding or
redeploying the application
- **Incident response**: Disable features or display alerts dynamically
in response to outages or degraded service
- **Instant rollback**: Revert configuration changes server-side without
artifact redeployment
- **Progressive delivery**: Enable A/B testing, canary releases, and
user/region-based configuration
- **Environment parity**: Eliminate configuration drift between staging
and production builds
- Decouples deployment cadence from configuration changes
- Enables GitOps workflows for configuration management separate from
code deployments
- Supports real-time operational control of client behavior
- Reduces build matrix complexity (no per-environment builds)

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6246-change-cloud-feature-flags-to-be-loaded-dynamically-at-runtime-rather-than-set-in-build-2966d73d3650811cbb41c9093961037a)
by [Unito](https://www.unito.io)
2025-10-23 20:16:18 -07:00
Arjan Singh
a3bfc2e91a fix(TopMenuSection): show current user if logged in (#6239)
## Summary

`CurrentUserButton` was not showing at all. Now it shows when the user
is logged in.

## Changes

- Fix template logic
- Add test for `CurrentUserButton` and `LoginButton` display logic.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6239-fix-TopMenuSection-show-current-user-if-logged-in-2956d73d3650812bb9f8fcf5a3c01db5)
by [Unito](https://www.unito.io)
2025-10-23 20:10:06 -07:00
Christian Byrne
6d37834111 [chore] fix LOD test regressed by 4-way resize drag PR (#6238)
Trigger CI to regenerate Playwright snapshots affected by the
four-corner Vue node resize feature merged in #6187.

This empty commit will trigger the automatic snapshot update workflow.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6238-chore-update-Playwright-snapshots-2956d73d36508159b1f7e6e0e4d16d9f)
by [Unito](https://www.unito.io)
2025-10-23 17:53:52 -07:00
AustinMroz
671e33cf09 Fix asset browser on subgraph nodes (#6240)
When a widget is linked to a subgraph, the subgraph creates a copy of
the widget. The callback used by the asset browser to update the widget
still refers to the widget that lives inside the subgraph, but at time
of execution, this is overwritten by the unchanged value of the copy.

This is fixed by instead updating the value of the caller. It's a little
hacky, and may need future review.

See also #6237

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6240-Fix-asset-browser-on-subgraph-nodes-2956d73d365081b49bd1cd1a7a254763)
by [Unito](https://www.unito.io)
2025-10-23 15:28:08 -07:00
Christian Byrne
68f98e2624 style: change gaps between floating elements from 2 (8px) to 1 (4px) (#6226)
## Summary

Design request

**Before**

<img width="2643" height="2074" alt="Screenshot from 2025-10-23
11-16-26"
src="https://github.com/user-attachments/assets/1c93879e-bfc4-4054-b0dd-ee0cd296a7ca"
/>

**After**

<img width="2643" height="2074" alt="Screenshot from 2025-10-23
11-16-13"
src="https://github.com/user-attachments/assets/b9327dcc-9b97-424a-8b05-4f5d62beeefa"
/>

## Related

- https://github.com/Comfy-Org/ComfyUI_frontend/pull/6120

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6226-style-change-gaps-between-floating-elements-from-2-8px-to-1-4px-2956d73d3650815b8640c5f3891fe494)
by [Unito](https://www.unito.io)

---------

Co-authored-by: github-actions <github-actions@github.com>
2025-10-23 14:01:21 -07:00
Christian Byrne
8120ed9dfa load assets browser before fetch completes and show loading state (#6189)
## Summary

Moves the fetch and post-fetch logic associated with the asset browser
into the component and shows a loading state while fetching.

To test, use this branch:
https://github.com/comfyanonymous/ComfyUI/pull/10045



https://github.com/user-attachments/assets/718974d5-efc7-46a0-bcd6-e82596d4c389

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6189-load-assets-browser-before-fetch-completes-and-show-loading-state-2946d73d365081879d1bd05d86e8c036)
by [Unito](https://www.unito.io)

---------

Co-authored-by: GitHub Action <action@github.com>
2025-10-23 13:43:34 -07:00
Christian Byrne
89ff8255bd open markdown links in new window/tab (#6229)
## Summary

Changes links in markdown snippets (What's New popup, node info sidebar)
to open the link in a new tab/window rather than directly navigating and
potentially losing unsaved work.



https://github.com/user-attachments/assets/24331bba-e31a-484c-bc11-12cf61805c98



Fixes https://github.com/Comfy-Org/ComfyUI_frontend/issues/6223.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6229-open-markdown-links-in-new-window-tab-2956d73d365081edbb1efb21cd0e2ab2)
by [Unito](https://www.unito.io)

---------

Co-authored-by: Alexander Brown <drjkl@comfy.org>
Co-authored-by: GitHub Action <action@github.com>
2025-10-23 13:33:38 -07:00
Christian Byrne
f14a6beda5 allow Vue nodes to be resized from all 4 corners (#6187)
## Summary

Enables Vue nodes to resize from all four corners and consolidated the
interaction pipeline.

## Changes

- **What**: Added four-corner handles to `LGraphNode`, wired them
through the refactored `useNodeResize` composable, and centralized the
math/preset helpers under `interactions/resize/` with cleaner pure
functions and lint-compliant markup.

## Review Focus

Corner-to-corner resizing accuracy (position + size), pinned-node guard
preventing resize start, and snap-to-grid behavior at varied zoom
levels.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6187-allow-Vue-nodes-to-be-resized-from-all-4-corners-2936d73d365081c8bf14e944ab24c27f)
by [Unito](https://www.unito.io)

---------

Co-authored-by: DrJKL <DrJKL@users.noreply.github.com>
Co-authored-by: GitHub Action <action@github.com>
2025-10-23 13:24:28 -07:00
Christian Byrne
aeabc24bf2 make support URL dynamic based on distribution (#6205)
## Summary

Add query param to indicate whether support ticket is from cloud or OSS.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6205-make-support-URL-dynamic-based-on-distribution-2946d73d365081868093c52981021189)
by [Unito](https://www.unito.io)

---------

Co-authored-by: DrJKL <DrJKL@users.noreply.github.com>
Co-authored-by: GitHub Action <action@github.com>
2025-10-23 13:20:48 -07:00
Christian Byrne
56a6ad5660 fix: node hover previews overlapping with sidebar (#6232)
Fixes https://github.com/Comfy-Org/ComfyUI_frontend/issues/6130 -- node
previews are not positioned in correct location after
https://github.com/Comfy-Org/ComfyUI_frontend/pull/5980

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6232-fix-node-hover-previews-overlapping-with-sidebar-2956d73d365081b2a470f7fb399fda99)
by [Unito](https://www.unito.io)

---------

Co-authored-by: GitHub Action <action@github.com>
2025-10-23 13:11:08 -07:00
Christian Byrne
4e5eba6c54 refactor: centralize all download utils across app and apply special cloud-specific behavior (#6188)
## Summary

Centralized all download functionalities across app. Then changed
downloadFile on the cloud distribution to stream assets via blob fetches
while desktop/local retains direct anchor downloads. This fixes issue
where trying to download cross-origin resources opens them in the
window, potentially losing the user's unsaved changes.

## Changes

- **What**: Moved `downloadBlob` into `downloadUtil`, routed all callers
(3D exporter, recording manager, node template export, workflow/palette
export, Litegraph save, ~~`useDownload` consumers~~) through shared
helpers, and changed `downloadFile` to `fetch` first when `isCloud` so
cross-origin URLs download reliably
- `useDownload` is the exception since we simply cannot do model
downloads through blob (forcing user to transfer the entire model data
twice is bad). Fortunately on cloud, the user doesn't need to download
models locally anyway.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6188-refactor-centralize-all-download-utils-across-app-and-apply-special-cloud-specific-behav-2946d73d365081de9f27f0994950511d)
by [Unito](https://www.unito.io)
2025-10-23 12:08:30 -07:00
Christian Byrne
647e62d4b7 [ci] run stylelint as part of CI tests (#6143)
## Summary

Final PR continuing from

- https://github.com/Comfy-Org/ComfyUI_frontend/pull/5926
- https://github.com/Comfy-Org/ComfyUI_frontend/pull/5940

actually run stylelint in lint workflows.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6143-ci-run-stylelint-as-part-of-CI-and-pre-commit-hook-2916d73d3650811891bcc9f94379cc5f)
by [Unito](https://www.unito.io)

---------

Co-authored-by: DrJKL <DrJKL@users.noreply.github.com>
2025-10-23 12:06:52 -07:00
Arjan Singh
4555656bf2 feat(AssetCard): remove model size (#6227)
## Summary

Remove model file sizes.

It was confusing some users who thought that was they were being asked
to download them locally.

## Changes

- Remove `formattedSize` from `AssetDisplayItem`.
- Remove associated code.
- 

## Screenshots

<img width="1299" height="512" alt="Screenshot 2025-10-23 at 11 22
06 AM"
src="https://github.com/user-attachments/assets/625b588b-a605-49dd-97a2-16bcd604aef2"
/>

<!-- Add screenshots or video recording to help explain your changes -->

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6227-feat-AssetCard-remove-model-size-2956d73d36508155930fdb4d2db2522a)
by [Unito](https://www.unito.io)
2025-10-23 18:48:30 +00:00
Alexander Brown
1e85902242 fix: Remove old readonly and memos from LGraphNodePreview, default to empty string for widget contents. (#6217)
## Summary

Fixes the prop validation errors when the previews rendered.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6217-fix-Remove-old-readonly-and-memos-from-LGraphNodePreview-default-to-empty-string-for-wi-2956d73d3650812da482cf1fab400dfa)
by [Unito](https://www.unito.io)

---------

Co-authored-by: github-actions <github-actions@github.com>
2025-10-23 11:22:36 -07:00
ComfyUI Wiki
c436552ade Remove unnecessary Template Library translations. (#6222)
All the template translations are now maintained in the
[template](https://github.com/Comfy-Org/workflow_templates) repo, so
these translations in the FE repo are no longer needed.

<img width="696" height="762" alt="image"
src="https://github.com/user-attachments/assets/e27148c0-737d-4b8c-8513-50504e6f929c"
/>

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6222-Remove-unnecessary-Template-Library-translations-2956d73d36508194aa08fa968d8d627f)
by [Unito](https://www.unito.io)
2025-10-23 08:06:25 -07:00
AustinMroz
449a51b82d Move toasts downwards so they don't overlap run button (#6216)
<img width="431" height="127" alt="image"
src="https://github.com/user-attachments/assets/76fece3a-c001-40c7-9036-e12900869e1d"
/>

Could potentially be improved to not use magic constants.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6216-Move-toasts-downwards-so-they-don-t-overlap-run-button-2956d73d365081d0b361fa396962c806)
by [Unito](https://www.unito.io)

---------

Co-authored-by: github-actions <github-actions@github.com>
2025-10-23 07:51:39 -07:00
Jin Yi
bd3f02065a [Style] Add custom scrollbar styling for SelectBox components (#5879) 2025-10-23 23:10:07 +09:00
Johnpaul Chiwetelu
fb66637765 Don't remove labels if initial jobs were skipped (#6218)
This pull request makes a small change to the
`.github/workflows/pr-update-playwright-expectations.yaml` file,
removing the unconditional `if: always()` condition from the
`merge-and-commit` job. This means the job will now only run if its
dependencies succeed, rather than always running regardless of previous
job outcomes.
2025-10-23 06:43:07 +01:00
Johnpaul Chiwetelu
97f7c2149a Shard Update Test Expectations PR (#6100)
This pull request significantly refactors the Playwright expectations
update workflow to improve reliability, efficiency, and maintainability.
The workflow is now split into three coordinated jobs—setup, sharded
snapshot updates, and merge/commit—enabling parallel test execution and
artifact management. Key improvements include sharding Playwright
snapshot updates, robust caching and artifact handling, and more
reliable PR context handling.

**Workflow Restructuring and Sharding:**

* The workflow is split into three jobs: `setup` (prepares environment
and caches it), `update-snapshots-sharded` (runs Playwright snapshot
updates in four parallel shards), and `merge-and-commit` (merges results
and commits updates). This enables faster, more reliable snapshot
updates.
[[1]](diffhunk://#diff-0289f4b5962314fa2d58937651c3d2a0f2c6f76e26c95d6a04d43c18b3449917L15-R15)
[[2]](diffhunk://#diff-0289f4b5962314fa2d58937651c3d2a0f2c6f76e26c95d6a04d43c18b3449917R27-R175)

**Caching and Artifact Management:**

* The setup job builds and caches the entire workspace, which is then
restored by each shard for consistent environments. Each shard uploads
its updated snapshots and test reports as artifacts, which are later
downloaded and merged in the final job.

**Improved PR Context Handling:**

* PR number, branch, and comment IDs are now reliably extracted and
passed between jobs using outputs, ensuring correct association with the
PR throughout the workflow (e.g., for commenting, reactions, and pushing
updates).
[[1]](diffhunk://#diff-0289f4b5962314fa2d58937651c3d2a0f2c6f76e26c95d6a04d43c18b3449917R27-R175)
[[2]](diffhunk://#diff-0289f4b5962314fa2d58937651c3d2a0f2c6f76e26c95d6a04d43c18b3449917L92-R199)

**Job and Step Renaming/Cleanup:**

* The main job is renamed from `test` to `setup`, and redundant or
unnecessary steps (such as the old branch SHA extraction) are removed
for clarity and maintainability.
[[1]](diffhunk://#diff-0289f4b5962314fa2d58937651c3d2a0f2c6f76e26c95d6a04d43c18b3449917L15-R15)
[[2]](diffhunk://#diff-0289f4b5962314fa2d58937651c3d2a0f2c6f76e26c95d6a04d43c18b3449917R27-R175)

**Comment and Label Automation Improvements:**

* Automated GitHub comment reactions and label removals now use the
correct PR context, ensuring that feedback and status updates are
reliably posted to the right place.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6100-Shard-Update-Test-Expectations-PR-28f6d73d36508109bcd8d382c942d44d)
by [Unito](https://www.unito.io)

---------

Co-authored-by: sno <snomiao@gmail.com>
2025-10-23 06:22:44 +01:00
AustinMroz
f63d0f3289 Fix type on LoadClip being marked as asset (#6207)
Previously, asset conversion was performed on any combo widget on a
valid node. As the `type` widget on LoadClip was also a combo widget, it
was being incorrectly converted.

This PR changes the isAssetBrowserEligible check to also verify the
widget name is correct.
<img width="694" height="174" alt="image"
src="https://github.com/user-attachments/assets/a8523ade-7f59-4480-b5e6-8782fd680106"
/>


┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6207-Fix-type-on-LoadClip-being-marked-as-asset-2946d73d3650811690f8d3a221a5b76d)
by [Unito](https://www.unito.io)
2025-10-22 19:37:40 -07:00
Simula_r
e5a0466e40 feat: make login button show only on dekstop (#6213)
## Summary

Make login and current user button visible on desktop only. 

## Screenshots (if applicable)

<img width="519" height="162" alt="image"
src="https://github.com/user-attachments/assets/80d86145-2bb9-4396-a02d-dc577192cb17"
/>
<img width="482" height="130" alt="image"
src="https://github.com/user-attachments/assets/cd843611-ce23-4cd4-a864-78cac1c7101f"
/>

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6213-feat-make-login-button-show-only-on-dekstop-2956d73d365081eb93f1d5da92dc38ee)
by [Unito](https://www.unito.io)

---------

Co-authored-by: github-actions <github-actions@github.com>
2025-10-23 02:16:15 +00:00
Arjan Singh
c71ec35a5f refactor(vite.config): simplify cloud config (#6211)
## Summary

Simplify the `vite.config.mts` now that we know `addAuthHeaders` is not
necessary.

## Changes

- remove `addAuthHeaders`
- simplify cloud configuration objects accordingly.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6211-refactor-vite-config-simplify-cloud-config-2956d73d365081659cfade4e289a872f)
by [Unito](https://www.unito.io)
2025-10-22 18:48:12 -07:00
AustinMroz
7b213f135e Fix copying from multi-outputs with clipspace (#6210)
`copyToClipspace` sets the `paintedIndex` and `combinedIndex` regardless
of if any editing was actually performed on the image. Previously, they
were set to be simple increments above the currently selected image.

When attempting to copy from a node previewing multiple images, these
indexes may refer to images that already exist.
This is resolved by instead setting the indexes to be used for
paintedIndex and combinedIndex to always be beyond the range of current
images.
<img width="998" height="761" alt="image"
src="https://github.com/user-attachments/assets/dacd46b3-9029-45e3-a6b3-07e971125a14"
/>


Resolves #6202

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6210-Fix-copying-from-multi-outputs-with-clipspace-2956d73d365081c5a07be4ddb0b352a3)
by [Unito](https://www.unito.io)
2025-10-22 18:00:02 -07:00
Arjan Singh
8b7b580ed4 Cloud Auth Backport (#6195)
## Summary

Backports Firebase authentication with cloud environments.

Changes only work when developing for cloud environment locally.

## Changes

- Router guards to force unauthenticated users to sign in.
- Configure auth headers for REST and Websocket connections.
- Code implemented in a way that enables build tree-shaking based on
distribution
- Updates to build process to build cloud distribution and simplify
development workflow

## Review Focus

1. Idomatic Vue/codebase patterns.
2. Build logic (please double check that I integrated correctly with:
https://github.com/Comfy-Org/ComfyUI_frontend/blob/rh-test/vite.config.mts)

## Screenshots (if applicable)




https://github.com/user-attachments/assets/ee4ea3f7-afa6-4da0-ba43-d62ed8ba4e18





┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6195-Feat-cloud-auth-backport-2946d73d365081f395f5f2a89fb7d800)
by [Unito](https://www.unito.io)

---------

Co-authored-by: Alexander Brown <drjkl@comfy.org>
Co-authored-by: GitHub Action <action@github.com>
2025-10-23 00:06:37 +00:00
Comfy Org PR Bot
bfe53d7721 1.30.2 (#6171)
Patch version increment to 1.30.2

**Base branch:** `main`

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6171-1-30-2-2926d73d36508194b993d488cf43de6d)
by [Unito](https://www.unito.io)

---------

Co-authored-by: simula-r <18093452+simula-r@users.noreply.github.com>
Co-authored-by: github-actions <github-actions@github.com>
2025-10-22 15:42:41 -07:00
AustinMroz
6368647cde Fix empty padding on nodes with previews (#6208)
Followup to #6194. Fixes nodes with image previews having empty padding.
<img width="915" height="565" alt="image"
src="https://github.com/user-attachments/assets/852a5e95-10d5-4fde-a5f1-f2f72ae5ffb6"
/>

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6208-Fix-empty-padding-on-nodes-with-previews-2946d73d365081eca299e0cfae420be3)
by [Unito](https://www.unito.io)

---------

Co-authored-by: github-actions <github-actions@github.com>
2025-10-22 15:15:03 -07:00
sno
69d37e8949 feat: Add @prettier/plugin-oxc for faster formatting (#6088)
Note for reviewers: the code changes in src/* is because I've upgraded
prettier to latest.

the @prettier/plugin-oxc it self only improve performance and doesnt
affect format rules

## Summary

Integrates `@prettier/plugin-oxc` to improve Prettier performance by
~20%.

The oxc plugin provides a faster parser written in Rust, significantly
speeding up formatting operations across the codebase.

## Changes

- Added `@prettier/plugin-oxc` as dev dependency
- Updated `.prettierrc` to use oxc plugin alongside existing
sort-imports plugin
- Added `scripts/benchmark-prettier.js` to measure performance
improvements
- Updated `knip.config.ts` to ignore the oxc plugin
- Updated `eslint.config.ts` to ignore the benchmark script

## Benchmark Results

Ran 3 benchmarks comparing formatting performance on the entire
codebase:

**Without oxc:**
- Median: 32.76s
- Average: 32.89s
- Min: 32.49s
- Max: 33.43s

**With oxc:**
- Median: 26.13s
- Average: 26.35s
- Min: 25.24s
- Max: 27.69s

**Improvement: 20.26% faster (6.64s saved)**

## Testing

The benchmark script can be run with:
```bash
node scripts/benchmark-prettier.js
```

This will:
1. Test formatting performance without oxc plugin
2. Test formatting performance with oxc plugin
3. Display comparison results
4. Restore original configuration

🤖 Generated with [Claude Code](https://claude.com/claude-code)

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6088-feat-Add-prettier-plugin-oxc-for-faster-formatting-28e6d73d365081aabb24d3af98c11bb0)
by [Unito](https://www.unito.io)

---------

Co-authored-by: DrJKL <DrJKL0424@gmail.com>
Co-authored-by: GitHub Action <action@github.com>
2025-10-22 14:49:47 -07:00
Christian Byrne
dc5d41642d disable transform settling reflow when panning the graph (#6186)
## Summary

- disable pan tracking in `useTransformSettling` so we stop wiring
high-frequency pointer listeners during canvas drags
- the post-navigation-interaction forced reflow is only necessary when
zooming since it is for fixing pixel stretch that results from `scale`
(which doesn't happen during panning/`translate`)
- extend settle delay to 512ms to reduce unnecessary reflow while
preserving post-zoom pixel fix

After this PR, there should be 0 reflows when panning the graph.

First PR in series to address:

- https://github.com/Comfy-Org/ComfyUI_frontend/issues/6151

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6186-disable-transform-settling-reflow-when-panning-the-graph-2936d73d365081c2b357e3c72d711439)
by [Unito](https://www.unito.io)

---------

Co-authored-by: DrJKL <DrJKL@users.noreply.github.com>
2025-10-22 14:33:05 -07:00
AustinMroz
2f00893b27 Expand drop zone for docking run button (#6193)
Expand the drop zone for docking the run button to be a bit more
forgiving.
<img width="494" height="99" alt="image"
src="https://github.com/user-attachments/assets/97eb6948-211d-4ed6-b06c-d6fb57b45f0b"
/>

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6193-Expand-drop-zone-for-docking-run-button-2946d73d3650816d996fdc57022161db)
by [Unito](https://www.unito.io)
2025-10-22 14:05:53 -07:00
AustinMroz
a5c29a9826 Introduce grow-parent class for widgets (#6194)
Only some widgets actually want to grow. Flex makes this difficult. This
PR sets up a `widget-expands` class that widgets can use to indicate
that they want to dynamically grow and applies it to the textarea widget
<img width="709" height="860" alt="image"
src="https://github.com/user-attachments/assets/721d99e5-5939-4531-91b5-1cda69d4e8ed"
/>


There's a potential alternative avenue with using `flex-shrink` instead
of `flex-grow`, and using `min-content` for `calculateIntrinsicSize`,
but I've been poking around with that for a while with no success.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6194-Introduce-grow-parent-class-for-widgets-2946d73d3650812f9d03c305ab04e212)
by [Unito](https://www.unito.io)

---------

Co-authored-by: github-actions <github-actions@github.com>
2025-10-22 19:17:36 +00:00
Christian Byrne
b44a39569e style: remove pulsing animation on executing Vue nodes (#6206)
## Summary

Removes pulsing animation which wasn't part of the original design and
has performance overhead.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6206-style-remove-pulsing-animation-on-executing-Vue-nodes-2946d73d3650816ab877da8120ab4085)
by [Unito](https://www.unito.io)
2025-10-22 11:45:58 -07:00
sno
8e8a45c496 [fix] Update .gitignore to properly ignore Linux core dumps (#6201)
## Summary
Fixes the .gitignore pattern for Linux core dump files from `./core` to
`/core`.

## Problem
The pattern `./core` in .gitignore doesn't work as expected. Git
interprets the `./` prefix literally, looking for a path named `./core`
rather than matching `core` at the repository root.

## Solution
Change to `/core` which is the correct gitignore syntax to ignore
files/directories named `core` at the repository root only.

## Why This Matters
- Linux systems can generate core dump files named `core` when programs
crash
- These files shouldn't be tracked in version control
- The previous pattern wasn't actually ignoring these files

## Testing
The new pattern will properly ignore `core` files at the root while not
affecting subdirectories (e.g., `src/core/` would still be tracked).

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6201-fix-Update-gitignore-to-properly-ignore-Linux-core-dumps-2946d73d365081059e57d9919d03a501)
by [Unito](https://www.unito.io)
2025-10-22 18:40:14 +09:00
sno
187f59eed3 [fix] Remove pnpm cache from release-version-bump workflow (#6199)
## Summary
- Fixed the "Post Setup Node.js" failure in the release-version-bump
workflow
- Removed unnecessary pnpm cache configuration that was causing
validation errors

fixes this JOB
- [Release: Version Bump · Comfy-Org/ComfyUI_frontend@2e8e136](
https://github.com/Comfy-Org/ComfyUI_frontend/actions/runs/18695441150/job/53311521564
)
<img width="1361" height="229" alt="image"
src="https://github.com/user-attachments/assets/22f780f0-59b8-4e57-ad9b-540683289a10"
/>


## Problem
The workflow was failing with error: "Path(s) specified in the action
for caching do(es) not exist, hence no cache is being saved."

This occurred because `setup-node@v4` with `cache: 'pnpm'` expects the
pnpm store directory to exist, but the workflow never runs `pnpm
install`. The workflow only executes `pnpm version`, which doesn't
require dependencies to be installed.

## Solution
Removed the `cache: 'pnpm'` configuration from the Setup Node.js step
since:
1. The workflow doesn't install dependencies
2. The cache provides no benefit for this workflow
3. It was causing the post-setup cleanup step to fail

## Test Plan
- [ ] Verify workflow runs successfully without cache errors
- [ ] Confirm version bump functionality still works correctly

🤖 Generated with [Claude Code](https://claude.com/claude-code)

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6199-fix-Remove-pnpm-cache-from-release-version-bump-workflow-2946d73d3650813dae7cf987a800e28b)
by [Unito](https://www.unito.io)

Co-authored-by: Claude <noreply@anthropic.com>
2025-10-21 23:42:56 -07:00
Alexander Brown
9cd7d06a6d Fix: Make textarea fill the area available instead of being resizable. (#6190)
## Summary

Invert the sizing of textareas. They now grow based on the container
instead of being independently resizable.

## Review Focus

Tested the behavior in Note, Markdown Note, CLIP Text Encode, and
Subgrpahs with promoted mutliline text widgets.

Anything else that might break with this?

## Screenshots (if applicable)


https://github.com/user-attachments/assets/4e2da142-d0b7-4629-9814-b637566ac1d6


<!-- Add screenshots or video recording to help explain your changes -->

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6190-Fix-Make-textarea-fill-the-area-available-instead-of-being-resizable-2946d73d3650818a9f77c619deb93d0b)
by [Unito](https://www.unito.io)

---------

Co-authored-by: github-actions <github-actions@github.com>
2025-10-21 19:31:05 -07:00
Alexander Brown
4ad8ae2634 Fix: Make breadcrumbs non-draggable. (#6191)
## Summary

Prevents accidentally trying to load the current site as if it were a
workflow.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6191-Fix-Make-breadcrumbs-non-draggable-2946d73d365081bda833c7a413903e97)
by [Unito](https://www.unito.io)
2025-10-21 18:07:48 -07:00
Benjamin Lu
e4b52e5329 fix: surface knip hook output in vscode (#6183)
## Summary
- redirect pnpm knip output to stderr so VS Code surfaces failures

## Testing
<img width="1585" height="483" alt="image"
src="https://github.com/user-attachments/assets/d65af783-d168-45cf-b01e-2b727e429a91"
/>

before it would literally just say it failed and you'd have to manually
rerun pnpm knip in ur own terminal

------
https://chatgpt.com/codex/tasks/task_e_68f7d2f2f2548330a23ae74554f1a54a
2025-10-21 17:14:50 -07:00
Christian Byrne
2346ba1af0 fix Vue node number widget inc/dec buttons hover state style (#6121)
## Summary

Fix hover state on Vue node number widget incremenet/decrement buttons.
The problem was in `useNumberWidgetButtonPt.ts`, the button hover styles
were using `var(--color-node-component-surface-hovered)` which
references a Tailwind theme color created by the `@theme` inline
directive. This theme color doesn't properly inherit the `.dark-theme`
class overrides, so it was showing the light mode color (white) even in
dark mode.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6121-fix-Vue-node-number-widget-inc-dec-buttons-hover-state-style-2906d73d36508144b91aec3490e32d28)
by [Unito](https://www.unito.io)

---------

Co-authored-by: DrJKL <DrJKL0424@gmail.com>
2025-10-21 23:58:04 +00:00
Christian Byrne
45cefda6e1 [style] unify Vue widget/slot label colors (#6149)
## Summary

Change labels on all widgets and slots to the same value which matches
design spec.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6149-style-unify-Vue-widget-slot-label-colors-2916d73d3650810a98f3ee75e0b22da0)
by [Unito](https://www.unito.io)

---------

Co-authored-by: DrJKL <DrJKL0424@gmail.com>
2025-10-21 16:50:36 -07:00
AustinMroz
cc73c42f76 Fix circular dependency in setting registration (#6184)
`Comfy.Canvas.NavigationMode` and `Comfy.Canvas.LeftMouseClickBehavior`
introduce a circular dependency where setting the value of one will set
the value of the other.

This is solved by having `NavigationMode` skip changing other settings
when `oldValue` is undefined.
- Note that `oldValue` is only undefined during initial load. When a
user changes the value for the first time, oldValue will be the default
value.

In the unlikely event desync occurs (a user manually editing the backing
json?), the registration of the subsequent `LeftMouseClickBehavior` will
still correct `NavigationMode` back to custom

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6184-Fix-circular-dependency-in-setting-registration-2936d73d365081809aa5d8bff0bf2333)
by [Unito](https://www.unito.io)
2025-10-21 14:08:36 -07:00
AustinMroz
f8490d0939 Fix links to wrong slots in vue mode (#6181)
Error is divided into 2 parts
- A widget lacking slotMetadata would create a slot overwriting the
index 0 slot
- Slot data wasn't reactive. Any dynamic widgets would not have
slotMetadata
<img width="1065" height="436" alt="image"
src="https://github.com/user-attachments/assets/c83b04fb-3b3a-4abb-8b68-99b305336348"
/>

See #5705
- Describes incorrect links internal to subgraphs. Likely a different,
already solved issue

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6181-Fix-links-to-wrong-slots-in-vue-mode-2936d73d36508136ad43d8c818bf9fba)
by [Unito](https://www.unito.io)
2025-10-21 12:36:08 -07:00
filtered
2e8e1366bd Release desktop-ui v0.0.3 (#6182)
## What's Changed

### 🐛 Bug Fixes
- Remove broken installer terminal button (#6180)

### 🔧 Maintenance
- Remove redundant npm pack step from desktop-ui publish workflow
(#6176)
2025-10-21 11:41:01 -07:00
filtered
6ef5c2602f Remove broken installer terminal button (#6180)
## Summary

Removes broken terminal toggle button that causes UX issues during
desktop installation.

## Changes

- **What**: Removes "Show Terminal" button from ServerStartView during
install flow
- **Breaking**: None (removes broken functionality)

## Review Focus

Cherry-picked from feb3c078f (v1.27.9). The button causes broken UX when
clicked during install. The working "Show Logs" button remains and takes
users directly to the log directory. This patch was written directly for
the release, in a rush to fix issues. The fix was not PR'd into main as
well (due to timing), so the original issue has now resurfaced.
2025-10-21 11:23:21 -07:00
filtered
9cf3a318eb Remove redundant npm pack step from desktop-ui publish workflow (#6176)
## Summary

Removes duplicate tarball creation from desktop-ui publish workflow -
`pnpm publish` handles this internally.

## Changes

- **What**: Removes `npm pack` step and GitHub Actions artifact upload
- **Breaking**: None - workflow behavior unchanged, publish still works
identically

## Review Focus

The `npm pack` + artifact upload was creating a duplicate of what `pnpm
publish` generates and uploads to npm anyway. Verified
`publish-frontend-types.yaml` follows this same pattern (no pack step,
direct publish).
2025-10-21 10:39:18 -07:00
filtered
668e95501a Prepare desktop-ui 0.0.2 release (#6179)
## Summary

Bumps desktop-ui version for release.

## Changes

- **What**: Version bump from 0.0.1 to 0.0.2
2025-10-21 10:37:28 -07:00
filtered
d8860c87e8 Remove 'Desktop' suffix from desktop app title (#6177)
## Summary

Removes "Desktop" suffix from the desktop app window title.

## Changes

- **What**: Changes window title from "ComfyUI Desktop" to "ComfyUI"

## Review Focus

Fixes title regression introduced during desktop UI separation.
2025-10-21 10:34:20 -07:00
filtered
6f8789b9aa Fix asset path resolution in desktop GPU picker (#6178)
## Summary

Fixes regression where desktop UI GPU picker images failed to load due
to incorrect absolute path resolution.

## Changes

- **What**: Converts absolute image paths to relative paths with `./`
prefix in GpuPicker component
- **Breaking**: None

## Review Focus

ESLint rule incorrectly flagged relative paths as errors, leading to use
of absolute paths that don't resolve correctly in desktop app context.

The change is just adding `.` to the start of two lines. ESLint rules
reorganised the rest.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6178-Fix-asset-path-resolution-in-desktop-GPU-picker-2936d73d3650814e9d0df9faf8e28733)
by [Unito](https://www.unito.io)
2025-10-21 10:30:52 -07:00
AustinMroz
9ae66c778d Fix nodeDef resolution for virtual nodes. (#6175)
<img width="697" height="250" alt="image"
src="https://github.com/user-attachments/assets/71fe7d9b-0cd6-43c6-b0d5-7dcb64d385a6"
/>

Virtual nodes (like primitives) don't have a nodeData. As a result, the
existing call to attempt lookup from a node instance fails. This is
fixed by adding `node.type` as a fallback

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6175-Fix-nodeDef-resolution-for-virtual-nodes-2936d73d365081b0abfcfe8532a50f8e)
by [Unito](https://www.unito.io)
2025-10-20 20:22:30 -07:00
sno
80013bcd5c [bugfix] Fix i18n linting errors (#6170)
## Summary
- Fix i18n linting errors by adding missing locale keys to
`src/locales/en/main.json`
- Update all affected components to use `$t()` for internationalization

## Changes
Added the following locale keys:
- `comfyOrgLogoAlt`: "ComfyOrg Logo"
- `comfy`: "Comfy"
- `pressKeysForNewBinding`: "Press keys for new binding"
- `defaultBanner`: "default banner"
- `enableOrDisablePack`: "Enable or disable pack"
- `openManager`: "Open Manager"
- `graphNavigation`: "Graph navigation"

Updated components to use i18n keys:
- `ComfyOrgHeader.vue`
- `KeybindingPanel.vue`
- `PackBanner.vue`
- `PackIcon.vue`
- `PackEnableToggle.vue`
- `LoadWorkflowWarning.vue`
- `SubgraphBreadcrumb.vue`
- `SignInContent.vue`

## Test plan
- [x] Run `pnpm lint` - all i18n linting errors resolved
- [x] Pre-commit hooks pass

Aim to make #5625 CI/CD pass.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6170-bugfix-Fix-i18n-linting-errors-2926d73d365081c3b7fbcbbf4a8e03d6)
by [Unito](https://www.unito.io)

Co-authored-by: Claude <noreply@anthropic.com>
2025-10-20 13:11:22 -07:00
Copilot
aa943ac565 CI: Remove .cache caching from GitHub Actions workflows (#6097)
## Overview

Removes **all `actions/cache` steps** from GitHub Actions workflows
after empirical testing showed that they actually **slow down CI/CD by
11%** rather than speeding it up.

## Context

As discussed in #5988, the codebase has evolved with components moving
into the `/packages` directory structure. The review comment suggested
removing the entire `actions/cache` step rather than just the `.cache`
path to properly evaluate performance impact.

## Performance Benchmark Results

Empirical testing on this PR (commits 38695ae0b vs ab16635c5) revealed
that **removing cache steps improves CI performance across all
workflows**:

| Workflow | WITHOUT Cache | WITH Cache | Improvement |
|----------|---------------|------------|-------------|
| **CI: Lint Format** | 208s (3m 28s) | 226s (3m 46s) | **-18s (-8.7%)**
 |
| **CI: Tests Unit** | 160s (2m 40s) | 177s (2m 57s) | **-17s (-10.6%)**
 |
| **CI: Tests Storybook** | 65s (1m 5s) | 78s (1m 18s) | **-13s
(-20.0%)**  |
| **Total Pipeline** | **433s (7m 13s)** | **481s (8m 1s)** | **-48s
(-11.1%)**  |

### Why is caching slower?

1. **Cache overhead exceeds benefits**: Time spent saving/restoring
cache > time saved from cached content
2. **Complex cache key computation**: Hash calculations for file
patterns add processing time
3. **Network I/O cost**: Each cache step adds network round-trips
4. **Tools already optimize incrementally**: ESLint, Vitest, Prettier
handle their own incremental checks efficiently

## Changes

Removed the entire `actions/cache` step from 8 workflow files:

- `ci-lint-format.yaml` - Removed tool outputs cache (.eslintcache,
.prettierCache, .knip-cache, tsconfig.tsbuildinfo)
- `ci-tests-storybook.yaml` - Removed storybook-static and
tsconfig.tsbuildinfo cache (both jobs)
- `ci-tests-unit.yaml` - Removed coverage and .vitest-cache
- `api-update-electron-api-types.yaml` - Removed tsconfig.tsbuildinfo
cache
- `api-update-manager-api-types.yaml` - Removed tool cache and
ComfyUI-Manager repo cache
- `api-update-registry-api-types.yaml` - Removed tool cache and
comfy-api repo cache
- `release-draft-create.yaml` - Removed tsconfig.tsbuildinfo cache
- `release-pypi-dev.yaml` - Removed dist and tsconfig.tsbuildinfo cache

**What remains cached:**
-  pnpm packages via `cache: 'pnpm'` in setup-node actions (the most
valuable cache)
-  Tool-specific incremental caches generated fresh each run
-  Docker layer caching (where applicable)

## Testing

-  Empirical performance testing completed (see benchmark results
above)
-  All cache steps removed successfully
-  No structural changes to workflow logic
-  pnpm package caching remains active

## Conclusion

The benchmark data clearly shows that removing `actions/cache` steps
results in **faster, simpler CI workflows**. The overhead of cache
management exceeds any benefit, especially with pnpm package caching
already handling the most time-consuming dependency installations.

**Recommendation:  Proceed with this change**

## Test Methodology

1. **WITHOUT cache** (commit
[38695ae0b](https://github.com/Comfy-Org/ComfyUI_frontend/commit/38695ae0b)):
Removed all `actions/cache` steps → [Workflow
run](https://github.com/Comfy-Org/ComfyUI_frontend/actions/runs/18654024806)
2. **WITH cache** (commit
[ab16635c5](https://github.com/Comfy-Org/ComfyUI_frontend/commit/ab16635c5)):
Temporarily restored all `actions/cache` steps → [Workflow
run](https://github.com/Comfy-Org/ComfyUI_frontend/actions/runs/18654143363)
3. **Final state** (commit
[3ce876f87](https://github.com/Comfy-Org/ComfyUI_frontend/commit/3ce876f87)):
Restored no-cache version (current)

Fixes #5988

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: snomiao <7323030+snomiao@users.noreply.github.com>
Co-authored-by: snomiao <snomiao@gmail.com>
Co-authored-by: Claude <noreply@anthropic.com>
2025-10-20 11:15:56 -07:00
AustinMroz
8eac19d06e Support cross domain/application copy/paste (#6087)
![AnimateDiff_00001](https://github.com/user-attachments/assets/8ae88dc5-bba8-40c0-9cc2-5e81f579761d)


Browsers place very heavy restrictions on what can be copied and pasted.
See:
- https://alexharri.com/blog/clipboard
- https://www.w3.org/TR/clipboard-apis/#mandatory-data-types-x

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6087-Experimental-cross-domain-application-copy-paste-28e6d73d36508154a0a8deeb392f43a4)
by [Unito](https://www.unito.io)
2025-10-20 10:03:15 -07:00
AustinMroz
55d2b300a6 Fix link resolution of virtual nodes (#6135)
Some virtual nodes (like get/set nodes) perform link redirection at
prompt resolution. The prior implementation incorrectly tried to return
the source of the virtual link after resolution, but this causes things
to break when the source of the virtual link is a subgraph IO.

Instead, this PR changes the code section to restart resolution from the
destination of the virtual link so that the existing subgraph boundary
resolution code is applied.

Also fix a bug with reconnection of complex/any types on
conversion to subgraph.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6135-Fix-link-resolution-of-virtual-nodes-2916d73d36508183b891ef6eb39bad4c)
by [Unito](https://www.unito.io)
2025-10-20 10:02:41 -07:00
Jin Yi
32ed446285 [bugfix] Fix enable pack functionality to use proper API endpoint (#6157)
## Summary
- Fixed issue where enabling a disabled pack incorrectly triggered
installation instead of using the dedicated enable endpoint
- Added proper `enablePack` method in the manager service layer
- Updated store and component to use the correct API call

## Problem
When users toggled a disabled pack to enable it via `PackEnableToggle`
component, the system was incorrectly calling the install endpoint with
full installation parameters instead of the simpler enable endpoint that
only requires the pack ID.

## Solution
1. **Added dedicated `enablePack` method in `comfyManagerService.ts`**:
   - Uses the `'enable'` task kind with `EnablePackParams`
   - Only requires `cnr_id` parameter (simpler than install)

2. **Updated `comfyManagerStore.ts`**:
   - Created proper `enablePack` function that queues an enable task
- Removed the incorrect aliasing where `enablePack` was pointing to
`installPack`

3. **Simplified `PackEnableToggle.vue`**:
   - Now calls `enablePack` with only required parameters (id, version)
   - Removed unnecessary installation-specific parameters

## Test plan
- [x] Enable a disabled pack and verify it uses the enable endpoint (not
install)
- [x] Confirm the pack is properly enabled after the operation
- [x] Check that the task queue shows "Enabling" message (not
"Installing")
- [x] Verify existing install/uninstall functionality still works

Fixes #6154

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6157-bugfix-Fix-enable-pack-functionality-to-use-proper-API-endpoint-2926d73d3650819fa4caf1b848f99735)
by [Unito](https://www.unito.io)

---------

Co-authored-by: Claude <noreply@anthropic.com>
2025-10-19 23:55:21 -07:00
Christian Byrne
4066fbd2d7 disable instant queue mode on cloud (#6141)
## Summary

Remove the _Instant_ mode from the queue mode options if the
distribution target is cloud.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6141-disable-instant-queue-mode-on-cloud-2916d73d36508197920fc8e462f0be9f)
by [Unito](https://www.unito.io)
2025-10-19 23:34:44 -07:00
Christian Byrne
26f587c956 [auth] add service worker on cloud distribution to attach auth header to browser native /view requests (#6139)
## Summary

Added Service Worker to inject Firebase auth headers into browser-native
`/api/view` requests (img, video, audio tags) for cloud distribution.

## Changes

- **What**: Implemented [Service
Worker](https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API)
to intercept and authenticate media requests that cannot natively send
custom headers
- **Dependencies**: None (uses native Service Worker API)

## Implementation Details

**Tree-shaking**: Uses compile-time `isCloud` constant - completely
removed from localhost/desktop builds (verified via bundle analysis).
Verify yourself by building the app and `grep -r
"registerAuthServiceWorker\|setupAuth" dist/`
**Caching**: 50-minute auth header cache with automatic invalidation on
login/logout to prevent redundant token fetches.

**Message Flow**:
```mermaid
sequenceDiagram
    participant IMG as Browser
    participant SW as Service Worker
    participant MT as Main Thread
    participant FB as Firebase Auth

    IMG->>SW: GET /api/view/image.png
    SW->>SW: Check cache (50min TTL)
    alt Cache miss
        SW->>MT: REQUEST_AUTH_HEADER
        MT->>FB: getAuthHeader()
        FB-->>MT: Bearer token
        MT-->>SW: AUTH_HEADER_RESPONSE
        SW->>SW: Cache token
    end
    SW->>IMG: Fetch with Authorization header

    Note over SW,MT: On login/logout: INVALIDATE_AUTH_HEADER
```

## Review Focus

- **Same-origin mode**: Service Worker uses `mode: 'same-origin'` to
allow custom headers (browser-native requests default to `no-cors` which
strips headers)
- **Request deduplication**: Prevents concurrent auth header requests
from timing out
- **Build verification**: Confirm `register-*.js` absent in localhost
builds, present (~3.2KB) in cloud builds

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6139-auth-add-service-worker-on-cloud-distribution-to-attach-auth-header-to-browser-native--2916d73d3650812698dccd07d943ab3c)
by [Unito](https://www.unito.io)
2025-10-19 22:51:37 -07:00
Christian Byrne
7ad1112535 add telemetry provider for cloud distribution (#6154)
## Summary

This code is entirely excluded from open-source, local, and desktop
builds. During minification and dead-code elimination, the Mixpanel
library is fully tree-shaken -- meaning no telemetry code is ever
included or downloaded in those builds. Even the inline callsites are
removed during the build (because `isCloud` becomes false and the entire
block becomes dead code and is removed). The code not only has no
effect, is not even distributed in the first place. We’ve gone to great
lengths to ensure this behavior.

Verification proof:


https://github.com/user-attachments/assets/b66c35f7-e233-447f-93da-4d70c433908d

Telemetry is *enabled only in the ComfyUI Cloud environment*. Its goal
is to help us understand and improve onboarding and new-user adoption.
ComfyUI aims to be accessible to everyone, but we know the learning
curve can be steep. Anonymous usage insights will help us identify where
users struggle and guide us toward making the experience more intuitive
and welcoming.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6154-add-telemetry-provider-for-cloud-distribution-2926d73d3650813cb9ccfb3a2733848b)
by [Unito](https://www.unito.io)

---------

Co-authored-by: Claude <noreply@anthropic.com>
2025-10-19 19:47:35 -07:00
Christian Byrne
522656a2dc [style] remove hover effect on Vue node socket labels (#6150)
## Summary

Align with the design by removing hover state. Hover state should not be
applied if clicking doesn't actually do anything.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6150-style-remove-hover-effect-on-Vue-node-socket-labels-2916d73d365081158edef8065edc42e8)
by [Unito](https://www.unito.io)
2025-10-19 14:29:03 -07:00
Christian Byrne
15d223ef9b [style] adjust Vue widget hover state (#6146)
## Summary

Change to match the design (hover changes background color slightly and
doesn't affect outline/border).

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6146-style-adjust-Vue-widget-hover-state-2916d73d365081a19297e77208dc9613)
by [Unito](https://www.unito.io)

---------

Co-authored-by: github-actions <github-actions@github.com>
2025-10-19 12:55:29 -07:00
Christian Byrne
c8146ffc64 Revert "fix dragging video/image components on Vue nodes triggers node drag (#5922)" (#6148)
## Summary

This PR reverts #5922 which fixed pointer capture behavior on video and
image preview components to prevent unintended node dragging.

## Changes

- Removes `data-capture-node="true"` attribute from `VideoPreview.vue`
and `ImagePreview.vue` components
- Removes pointer event delegation logic from
`useNodePointerInteractions.ts` composable
- Restores previous drag behavior where dragging on preview components
triggers node drag

## Reason for Revert

This changes the behavior from original Litegraph and is generally
annoying. Users would rather be able to drag the node than be able to
drag an image/video out from a node.

Reverts #5922

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6148-Revert-fix-dragging-video-image-components-on-Vue-nodes-triggers-node-drag-5922-2916d73d365081398bb5c20384e26bb8)
by [Unito](https://www.unito.io)
2025-10-19 12:28:40 -07:00
Christian Byrne
c263f6da25 [style] prevent Vue node selection outline being obscured by image output (#6061)
## Summary

Adds `px-2` on image to prevent this issue (below) - I think there's a
better solution but I'm not really sure what it is. We use outline for
selection state and it's somewhat complex how our ring/border/outline
with many different node states and interactions works right now. It
will take some CSS skill to allow the images to be totally flush.

<img width="720" height="715" alt="image"
src="https://github.com/user-attachments/assets/0283e036-7a31-45ef-b5cc-af3ac73171c9"
/>

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6061-style-prevent-Vue-node-selection-outline-being-obscured-by-image-output-28c6d73d365081d59b34d8f91252de92)
by [Unito](https://www.unito.io)

---------

Co-authored-by: github-actions <github-actions@github.com>
2025-10-19 11:52:14 -07:00
Christian Byrne
1f5191847a make "require subscription" toggleable in build (#6144)
## Summary

Adds build time feature flags system starting with a flag that indicates
whether subscription is required to use the app. This is only used on
cloud.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6144-make-require-subscription-toggleable-in-build-2916d73d3650813bb140c5e96bcce1ce)
by [Unito](https://www.unito.io)
2025-10-19 10:30:14 -07:00
sno
fc69924c4a [feat] implement dynamic imports for locale code splitting (#6076)
## Summary
- Implement dynamic imports for internationalization (i18n) locale files
to reduce initial bundle size
- Only load English locale eagerly as default/fallback, load other
locales on-demand
- Apply code splitting to both main ComfyUI frontend and desktop-ui
applications

## Technical Details
- **Before**: All locale files (main.json, nodeDefs.json, commands.json,
settings.json) for all 9 languages were bundled in the initial
JavaScript bundle
- **After**: Only English locale files are included in initial bundle,
other locales are loaded dynamically when needed
- Implemented `loadLocale()` function that uses dynamic imports with
`Promise.all()` for efficient parallel loading
- Added locale tracking with `loadedLocales` Set to prevent duplicate
loading
- Updated both `src/i18n.ts` and `apps/desktop-ui/src/i18n.ts` with
consistent implementation

## Bundle Size Impact
This change significantly reduces the initial bundle size by removing ~8
languages worth of JSON locale data from the main bundle. Locale files
are now loaded on-demand only when users switch languages.

## Implementation
- Uses dynamic imports: `import('./locales/[locale]/[file].json')`
- Maintains backward compatibility with existing locale switching
mechanism
- Graceful error handling for unsupported locales
- No breaking changes to the public API

## Test plan
- [x] Verify initial load only includes English locale
- [x] Test dynamic locale loading when switching languages in settings
- [x] Confirm fallback behavior for unsupported locales
- [x] Validate both web and desktop-ui applications work correctly

🤖 Generated with [Claude Code](https://claude.ai/code)

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6076-feat-implement-dynamic-imports-for-locale-code-splitting-28d6d73d36508189ae0ef060804a5cee)
by [Unito](https://www.unito.io)

---------

Co-authored-by: Claude <noreply@anthropic.com>
2025-10-18 23:05:26 -07:00
Christian Byrne
5b5151f41f [perf] manually chunk vendored code (#6137)
## Summary

Added a `manualChunks` strategy in `vite.config.mts` that splits
primevue, tiptap, chart.js, three/@xterm, core Vue/Pinia, and the
remaining dependencies into dedicated vendor bundles. This reduces the
main application chunk size and allows browsers to cache heavy
third-party code across releases, improving load times when those
libraries stay unchanged.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6137-perf-manually-chunk-vendored-code-2916d73d36508140a44ec0de228ef9cc)
by [Unito](https://www.unito.io)
2025-10-18 22:49:11 -07:00
Christian Byrne
2018f1e671 [ci] drop console statements (except warn and error) when building app (#6123)
## Summary

Marks all the console methods besides `warn` and `error` as pure
functions so they can be dropped during DCE in build pipeline. It's
simpler to use `drop` but that would remove errors/warnings.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6123-ci-drop-console-statements-except-warn-and-error-when-building-app-2906d73d365081f28303c0d784352b12)
by [Unito](https://www.unito.io)
2025-10-18 22:43:38 -07:00
Christian Byrne
8822f186e0 [style] adjust appearance of "delete account" component to be text rather than button (#6126)
Adjusts style of "Delete Account" button.

**Before**:

<img width="731" height="925" alt="Screenshot from 2025-10-18 04-22-24"
src="https://github.com/user-attachments/assets/497de4ca-9359-41d8-b944-0d69835f43b1"
/>

**After**:

<img width="731" height="925" alt="Screenshot from 2025-10-18 04-22-14"
src="https://github.com/user-attachments/assets/f42a21fb-7d4d-43f5-b27b-1f85e4470dbd"
/>

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6126-style-adjust-appearance-of-delete-account-component-to-be-text-rather-than-button-2906d73d365081eab48ece171fa4fd8a)
by [Unito](https://www.unito.io)
2025-10-18 22:37:06 -07:00
Christian Byrne
0ff1837ced [ci] allow setting GENERATE_SOURCEMAP as env var (#6134)
## Summary

Introduces a `GENERATE_SOURCEMAP` environment flag in `vite.config.mts`
that defaults to enabled (`true` unless set to `'false'`). This keeps
source maps on by default, while allowing opt-out for lean production
artifacts.

Allows the choice to be made as part of the distribution pipeline and
changed for different targets.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6134-ci-allow-setting-GENERATE_SOURCEMAP-as-env-var-2916d73d3650815d91b9eff12b6e55fd)
by [Unito](https://www.unito.io)
2025-10-18 20:23:27 -07:00
Terry Jia
7e1e8e3b65 subscription page (#6064)
Summary

Implements cloud subscription management UI and flow for ComfyUI Cloud
users.

  Core Features:
- Subscription Status Tracking: Global reactive state management for
subscription status across all components
  using shared subscriptionStatus ref
- Subscribe to Run Button: Replaces the Run button in the actionbar with
a "Subscribe to Run" button for users
  without active subscriptions
- Subscription Required Dialog: Modal dialog with subscription benefits,
pricing, and checkout flow with video
  background
- Subscription Settings Panel: New settings panel showing subscription
status, renewal date, and quick access to
  billing management
- Auto-detection & Polling: Automatically polls subscription status
after checkout completion and syncs state
  across the application


https://github.com/user-attachments/assets/f41b8e6a-5845-48a7-8169-3a6fc0d2e5c8



┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6064-subscription-page-28d6d73d36508135a2a0fe7c94b40852)
by [Unito](https://www.unito.io)

---------

Co-authored-by: GitHub Action <action@github.com>
2025-10-18 20:21:30 -07:00
Christian Byrne
d83e34d0fc [ci] collapsible sections in ci size report comments (#6118)
## Summary

Adjust size reporting scripts to aggregate baseline/current metrics,
render collapsible GitHub-friendly tables using `<details>`, and
expanded bundle categorisation heuristics

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6118-ci-collapsible-sections-in-ci-size-report-comments-2906d73d365081d0b302c09fa1b98ccb)
by [Unito](https://www.unito.io)
2025-10-18 20:15:51 -07:00
Christian Byrne
c6b528b8be [ci] allow manual workflow dispatch to do version bumping on core branches (rather than just on main) (#6117)
## Summary

Added configurable base branch selection to version bump workflows,
enabling patch releases from `core/*` branches via GitHub Actions UI.

## Changes

- **What**: Extended [workflow_dispatch
inputs](https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#workflow_dispatch)
with `branch` parameter for both main frontend and desktop-ui version
bump workflows
- **Validation**: Added branch existence check that lists available
`core/*` branches on error
- **Workflow modifications**:
- `release-version-bump.yaml`: Checkout and create PRs targeting
user-specified branch
- `version-bump-desktop-ui.yaml`: Same behavior for desktop-ui releases

## Review Focus

Branch validation logic correctly handles both local (`refs/heads/`) and
remote (`refs/remotes/origin/`) refs. Default value preserves backward
compatibility for release sheriffs unfamiliar with new feature.

## Use Case

Previously, patch releases from `core/1.29` required manual version
bumping. Now maintainers can trigger from Actions UI with dropdown
selections.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6117-ci-allow-manual-workflow-dispatch-to-do-version-bumping-on-core-branches-rather-than-j-2906d73d365081cba3aff46471206a9e)
by [Unito](https://www.unito.io)
2025-10-18 13:04:49 -07:00
Christian Byrne
37d2f25d2f [style] ensure all floating menus have same gap from screen edges (#6120)
## Summary

Ensures all the floating shell app elements (graph canvas menu, minmap,
floating sidebar) all have .5rem gap between edge and viewport. Was not
proper distance for graph canvas menu and minimap due to splitter
overlay (parent) itself having 8px gap from viewport.

**After**:

<img width="605" height="535" alt="Screenshot from 2025-10-18 01-34-51"
src="https://github.com/user-attachments/assets/215efd5c-541f-441a-8b0d-1eb1f6fce672"
/>

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6120-style-ensure-all-floating-menus-have-same-gap-from-screen-edges-2906d73d365081598ae4d16f2380baf8)
by [Unito](https://www.unito.io)

---------

Co-authored-by: github-actions <github-actions@github.com>
2025-10-18 05:07:06 -07:00
Christian Byrne
3dcabaeb0a Mark failing vue nodes bypass test as fixme temporarily (#6127)
## Summary

Mark this test as `fixme` while I fix it. Don't want to disrupt CI
signal for other repo maintainers - the test has already been broken for
nearly 2 days now which is not acceptable.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6127-Mark-failing-vue-nodes-bypass-test-as-fixme-temporarily-2906d73d36508186a1def1d2dc7f85a5)
by [Unito](https://www.unito.io)

---------

Co-authored-by: GitHub Action <action@github.com>
2025-10-18 05:06:24 -07:00
Comfy Org PR Bot
eb85f99b10 1.30.1 (#6124)
Patch version increment to 1.30.1

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6124-1-30-1-2906d73d36508176a9a0dca7ecfbb40c)
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>
2025-10-18 04:24:04 -07:00
Christian Byrne
2e99c394a0 fix minimap not re-rendering when Vue nodes change mode (bypass, mute) (#6110)
## Summary

Fixes minimap not updating when node bypass/mute state changes in vue
nodes mode by adding `onTrigger` tracking of relevant instrumented
properties.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6110-fix-minimap-not-re-rendering-when-Vue-nodes-change-mode-bypass-mute-28f6d73d36508112bab1ce031705c6a9)
by [Unito](https://www.unito.io)

---------

Co-authored-by: github-actions <github-actions@github.com>
2025-10-18 03:59:19 -07:00
Johnpaul Chiwetelu
9ef6f94f92 [fix] Enable AUDIO_RECORD widget in both LiteGraph and Vue nodes modes (#6094)
Fixed the AUDIO_RECORD widget to display correctly in both rendering
modes:

- Removed conflicting AUDIO_RECORD registration from ComfyWidgets that
was blocking the custom widget implementation in uploadAudio.ts
extension
- Changed canvasOnly flags from true to false on both audioUIWidget and
recordWidget to enable Vue nodes rendering
- Added type override (recordWidget.type = 'audiorecord') after widget
creation to enable Vue component lookup while preserving LiteGraph
button rendering
- Removed unused IAudioRecordWidget type definition

The widget now works correctly:
- LiteGraph mode: Displays as a functional button
- Vue nodes mode: Displays full recording UI with waveform visualization

## 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 -->



https://github.com/user-attachments/assets/cf6513d4-0a4b-4210-88e2-a948855b5206


┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6094-fix-Enable-AUDIO_RECORD-widget-in-both-LiteGraph-and-Vue-nodes-modes-28e6d73d365081faa879f53e3e2dddad)
by [Unito](https://www.unito.io)

---------

Co-authored-by: bymyself <cbyrne@comfy.org>
2025-10-18 03:15:26 -07:00
Christian Byrne
598d170d10 [style] update dropdown button text class in Vue node upload widgets (#6062)
## Summary

Change colors to match Figma variables.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6062-style-update-dropdown-button-text-class-in-Vue-node-upload-widgets-28c6d73d365081ae8857ddcef06784ef)
by [Unito](https://www.unito.io)

---------

Co-authored-by: github-actions <github-actions@github.com>
2025-10-18 02:09:38 -07:00
Christian Byrne
2c221bdd7b [ci] enable minification in build workflows (#6116)
## Summary

Continuation of https://github.com/Comfy-Org/ComfyUI_frontend/pull/6068:
set `SHOULD_MINIFY` in build workflows to enable minification.

Our build plugin dictates what files are available for extensions to
import at runtime:
bfe083dcba/build/plugins/comfyAPIPlugin.ts (L52)

The shimming marks these files and their named exports and named memebrs
as side-effectful, so names should be preserved from the perspective of
3rd party extensions, making this safe in theory.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6116-ci-enable-minification-in-build-workflows-2906d73d36508141be57f63b619f9f16)
by [Unito](https://www.unito.io)
2025-10-18 01:47:43 -07:00
Christian Byrne
76b44d3de5 [perf] disable cache-busting param on cloud (#6105)
## Summary

Updates `ComfyApp.getRandParam()` to return an empty string on cloud
builds (detected via `isCloud`), letting hosted deployments rely on HTTP
caching for `/view` assets while local hosts retain the legacy cache
busting. The audio widget helpers now defer to the same method instead
of generating their own random suffix, so every frontend consumer shares
this logic.

This keeps the original overwrite protection for localhost workflows but
removes needless cache misses in the cloud stack, where GCS already
serve strong ETags and there is not really a concern about stale
filenames. Once the backend exposes a deterministic file-version field,
we can delete the remaining local-only randomness entirely.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6105-perf-disable-cache-busting-param-on-cloud-28f6d73d36508181b34afad406fabc59)
by [Unito](https://www.unito.io)
2025-10-18 01:26:40 -07:00
Christian Byrne
3fc8b49038 [refactor] simplify redundant check in cloud badge extension (#6106)
Simplifies code with tautological condition. The extension is only
loaded when `isCloud` is true due to
38f188759d/src/extensions/core/index.ts (L27-L29)

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6106-refactor-simplify-redundant-check-in-cloud-badge-extension-28f6d73d365081d69903f5a985fdc8be)
by [Unito](https://www.unito.io)
2025-10-18 01:25:59 -07:00
Christian Byrne
10748bdac9 [auth] handle auth/requires-recent-login for account deletion and password updates (#6109)
## Summary

Implemented error recovery system to handle Firebase
`auth/requires-recent-login` errors when deleting accounts or updating
passwords.


https://github.com/user-attachments/assets/92a79e2a-cff5-4b18-b529-dbaf5f2303f2

## Changes

- **What**: Added [ErrorRecoveryStrategy
pattern](https://firebase.google.com/docs/auth/web/manage-users#re-authenticate_a_user)
to `useErrorHandling` composable with automatic retry logic for
sensitive Firebase operations
- **Breaking**: None - recovery strategies are optional, all existing
code unchanged

## Technical Details

Firebase enforces
[reauthentication](https://firebase.google.com/docs/reference/js/auth#autherrorcodes)
for security-sensitive operations (account deletion, password changes)
after ~5 minutes of inactivity. Previously these operations failed with
cryptic error messages.

New flow:
1. Operation throws `auth/requires-recent-login`
2. Recovery strategy shows confirmation dialog
3. User logs out and re-authenticates
4. Operation automatically retries

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6109-auth-handle-auth-requires-recent-login-for-account-deletion-and-password-updates-28f6d73d36508119abf4ce30eecea976)
by [Unito](https://www.unito.io)
2025-10-18 01:24:52 -07:00
AustinMroz
bfe083dcba Move subgraph badge into node title (#6115)
<img width="1350" height="691" alt="image"
src="https://github.com/user-attachments/assets/4a1a909f-463e-4c77-8855-6fe0a177b659"
/>

Design docs show icon as being reversed in litegraph mode. If needed,
fixing this is basically just removing a negative sign.

LiteGraph implementation was not as clean as I hoped. I can spend some
more time divining some non-magic constants, and mayybe resolve the svg
data from css styles if desired.

Resolves #4647
Resolves #6107

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6115-Move-subgraph-badge-into-node-title-2906d73d365081e4ba82d321fc27afed)
by [Unito](https://www.unito.io)

---------

Co-authored-by: GitHub Action <action@github.com>
2025-10-18 00:32:54 -07:00
Rizumu Ayaka
917f8ae27d ci: size report (#6082)
## Summary

show bundle size info automatically in Pull Request

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6082-ci-size-report-28e6d73d365081c2bf73ce9919a7c01a)
by [Unito](https://www.unito.io)
2025-10-17 19:23:42 -07:00
Benjamin Lu
8dee207b8c WorkflowTabs: cleanup scroll/overflow handling and watcher disposal (#6080)
## Summary

Refactor and cleanup `WorkflowTabs` scroll/overflow handling to improve
stability, ensure proper watcher disposal, and keep the active tab in
view more reliably.

note: honestly a nit, can drop if review is too annoying

## Changes

- **What**: 
- Replace `ScrollPanel` ref with `containerRef` and query
`.p-scrollpanel-content` within the container.
- Add computed `scrollContent` to centralize access to the scrollable
element.
- Add `ensureActiveTabVisible({ waitForDom })` option to skip `nextTick`
when not needed.
- Rework watchers with explicit `WatchStopHandle`s and `onCleanup` to
stop previous watchers and dispose `overflowObserver` correctly when
`scrollContent` changes.
- Update arrow enable logic by watching `arrivedState.left/right`
together and setting `leftArrowEnabled`/`rightArrowEnabled` immediately.
- Only measure and re-ensure visibility when overflowing; call
`scrollState.measure()` and `ensureActiveTabVisible({ waitForDom: false
})` after arrows update.
- **Breaking**: None
- **Dependencies**: None

## Review Focus

- Correctness of watcher lifecycle and cleanup when `scrollContent`
changes
- Arrow enablement behavior at scroll boundaries
- Reliability of `ensureActiveTabVisible` across tab selection and
overflow changes
- Regressions in scroll performance and tab visibility

## Screenshots (if applicable)

N/A

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6080-WorkflowTabs-cleanup-scroll-overflow-handling-and-watcher-disposal-28d6d73d3650819cba6de70fb10354ad)
by [Unito](https://www.unito.io)
2025-10-17 15:12:03 -07:00
AustinMroz
a0d9cc7e33 Add subgraph management keybinds (#6114)
Adds a new keybind to attempt to toggle the promotion state of the
currently hovered widget

Adds a keybind to open the subgraph configuration panel

Neither of these options are given a default binding.

Cleans up some incorrect version and icon information that was forgotten
about.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6114-Add-subgraph-management-keybinds-28f6d73d365081e6b6f3c78f99afc094)
by [Unito](https://www.unito.io)

---------

Co-authored-by: Benjamin Lu <benceruleanlu@proton.me>
2025-10-17 15:09:37 -07:00
Christian Byrne
d1639c4377 [ci] extend backport workflow to work with arbitrary branches (#6108)
## Summary

Expands the PR backport workflow so maintainers can target any release
branch using labels, instead of being limited to the `core/x.y` release
lines. The workflow now collects labels formatted as plain version
numbers (`1.24`) as before, plus new prefixes like
`branch:release/hotfix` or `backport:partner/foo`, validates that each
referenced branch exists, and then cherry-picks the source merge commit
to every target.

All generated PRs and failure comments reference the actual branch name,
making it clear where the backport landed or why it failed. This keeps
the existing opt-in flow (`needs-backport`) but makes it flexible enough
for custom support and partner branches without extra manual work.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6108-ci-extend-backport-workflow-to-work-with-arbitrary-branches-28f6d73d365081bf85a3d4c40a23bb68)
by [Unito](https://www.unito.io)
2025-10-17 14:38:06 -07:00
Benjamin Lu
9f046a11ea Use scrollIntoView to keep active workflow tab visible (#6077)
> "The watcher that should keep the active workflow tab visible compares
the tab’s bounds against the scroll panel’s content element, whose
rectangle spans the entire scroll width instead of the visible viewport.
As a result, when the active tab starts off-screen, the computed offsets
stay ≤ 0 and no corrective scroll occurs, so the selected tab remains
hidden."

fixes #6057

------
https://chatgpt.com/codex/tasks/task_e_68efe2b5b4a08330940a20552c1db339
2025-10-17 00:37:43 -07:00
sno
e1cd88afe3 fix(ci): update claude review workflow to match renamed CI workflows (#6101)
## Summary
- Investigated Claude Review workflow failures after CI workflow
renaming
- Found and fixed outdated workflow references in `.claude/commands`
files
- Created comprehensive workflow evolution documentation

## Problem
Claude Review workflows were failing, initially thought to be due to
workflow name changes in commit 5a7ec8148. Investigation revealed the
real issues and uncovered additional documentation problems.

## Root Cause Analysis
After checking the [lewagon/wait-on-check-action
documentation](https://github.com/lewagon/wait-on-check-action):
- `check-regexp` matches against job names (`jobs.<job_id>.name`) 
- Job names like `lint-and-format`, `test`, `playwright-tests` were
never changed
- The real issue was Playwright test failures, not regex patterns

## Findings & Fixes

### 1. Claude Review Workflow 
- **Issue**: Failures were caused by actual test failures (e.g.,
`playwright-tests-chromium-sharded (8, 8): completed (failure)`)
- **Fix**: Reverted unnecessary regex changes since original patterns
were correct

### 2. Outdated Command References 
Found 5 outdated workflow references in `.claude/commands` files:

| File | Old Reference | New Reference |
|------|---------------|---------------|
| `create-frontend-release.md` | `release.yaml` |
`release-draft-create.yaml` |
| `create-frontend-release.md` | `create-release-candidate-branch.yaml`
| `release-branch-create.yaml` |
| `create-frontend-release.md` | `release.yaml` (in script) |
`release-draft-create.yaml` |
| `create-hotfix-release.md` | `release.yaml` |
`release-draft-create.yaml` |
| `create-frontend-release.md` | workflow text reference | updated to
match filename |

### 3. Historical Analysis & Documentation 📚
Created comprehensive workflow evolution timeline:

#### Documentation Added:
- `logs/workflow-renames.md`: Complete mapping of all workflow name
changes
- `logs/report.md`: Comprehensive audit report with findings and
recommendations
- `logs/track-workflow-renames.md`: **Exhaustive history of all 77
workflow files that ever existed**

#### Key Historical Insights:
- **77 unique workflow files** have existed since 2024-06-13
- **195 commits** analyzed affecting workflows
- **10 evolution phases** from ad-hoc naming to systematic
categorization
- **52 rename operations** and **10 file deletions**
- **68% reduction** through optimization (77 total → 25 active)

#### Timeline Highlights:
- **2024-06-13**: `test-ui.yaml` - first workflow ever created
- **2025-10-01**: First major reorganization (17 renames)
- **2025-10-02**: Category-based naming introduction (`ci-*`, `pr-*`,
`release-*`)
- **2025-10-16**: API category refinement (`api-update-*`)
- **2025-10-17**: Final consolidation (our current branch)

#### Evolution Patterns:
- **Era 1**: Descriptive names (`test-ui.yaml`, `format.yaml`)
- **Era 2**: Category prefixes (`ci-*`, `pr-*`, `release-*`)
- **Era 3**: Action-target patterns (`api-update-*-types.yaml`)
- **Era 4**: Semantic categorization (current state)

## Impact
-  Claude Review workflow now works correctly (job names were always
correct)
-  Command documentation now references actual workflow files
-  Complete archaeological record of workflow evolution created
-  Future workflow changes will be easier to track with comprehensive
documentation

## Test Plan
- [x] Workflow syntax validation passes
- [x] All `gh run list --workflow=<filename>` commands in docs now work
- [x] Documentation provides complete historical context
- [ ] Monitor if Claude Review works properly once underlying test
failures are resolved

🤖 Generated with [Claude Code](https://claude.ai/code)

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Alexander Brown <drjkl@comfy.org>
2025-10-17 00:25:43 -07:00
AustinMroz
38f188759d Fix mouse clicks being swallowed by new menu (#6103)
Moves around pointer events styling so that mouse events near the top
and side bar don't get swallowed.

Resolves #6102

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6103-Fix-mouse-clicks-being-swallowed-by-new-menu-28f6d73d3650819aa4a1dc1eacfd3adb)
by [Unito](https://www.unito.io)
2025-10-16 22:26:54 -07:00
Christian Byrne
ad5be8ec70 [style] update style of mask editor button on Vue node image preview (#6060)
Updates style of mask editor button on Vue nodes image preview overlay
to align with
[design](https://www.figma.com/design/31uH3r4x3xbIctuRWYW6NM/V3---Vue-Nodes?node-id=7744-84270&m=dev).
Also makes the custom mask have color be derived from text color class.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6060-style-update-style-of-mask-editor-button-on-Vue-node-image-preview-28c6d73d36508111a562c6a22c64f027)
by [Unito](https://www.unito.io)

---------

Co-authored-by: GitHub Action <action@github.com>
2025-10-16 21:02:55 -07:00
sno
653cf64e01 [chore] Upgrade Prettier from 3.3.2 to 3.6.2 (#6089)
## Summary
- Updated Prettier from 3.3.2 to 3.6.2 in pnpm-workspace.yaml
- Ran `pnpm install` to update dependencies
- Ran `pnpm format` to apply new formatting rules
- Verified typecheck passes

## Test plan
- [x] TypeScript typecheck passes
- [x] Prettier formatting applied successfully

🤖 Generated with [Claude Code](https://claude.com/claude-code)

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6089-chore-Upgrade-Prettier-from-3-3-2-to-3-6-2-28e6d73d3650816a888ff1129b14e0bc)
by [Unito](https://www.unito.io)

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Christian Byrne <cbyrne@comfy.org>
2025-10-16 20:46:59 -07:00
Comfy Org PR Bot
626fcff80b 1.30.0 (#6099)
Minor version increment to 1.30.0

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6099-1-30-0-28f6d73d3650817b9505d85b906047c2)
by [Unito](https://www.unito.io)

---------

Co-authored-by: arjansingh <1598641+arjansingh@users.noreply.github.com>
Co-authored-by: github-actions <github-actions@github.com>
2025-10-16 19:10:17 -07:00
AustinMroz
15b1b91b16 Implement a legacy canvas widget for vue mode (#6011)
![updated-legacy-widget](https://github.com/user-attachments/assets/3f0a1623-9445-4059-acbb-086baec54980)

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6011-Implement-a-legacy-canvas-widget-for-vue-mode-2896d73d36508127a5d1debcccb519a0)
by [Unito](https://www.unito.io)

---------

Co-authored-by: GitHub Action <action@github.com>
2025-10-16 18:50:19 -07:00
Christian Byrne
e48e11e434 fix LiteGraph capturing node pointer events if Vue and LG node positions become desynced (#6058)
## Summary

Added Vue mode guards to LiteGraph canvas to prevent dual event handling
between Vue node components and LiteGraph node event handlers. Fixes a
bug where the litegraph and vue positions become out of sync and then
there are effectively dead spots on the canvas (the user should still be
able to interact with the graph even if the positions desync - the only
real downside of desync should just be mispositioned nodes in the
serialized state).

Returns early only in `processNodeClick` and the node-related
(alt+click+drag node cloning) canvas handlers. In general, the LiteGraph
canvas still owns some events/interactions - but the node interactions
are fully owned by Vue when in Vue nodes mode.

## Changes

- **What**: Added `LiteGraph.vueNodesMode` checks in
[LGraphCanvas.ts](src/lib/litegraph/src/LGraphCanvas.ts) to skip native
event processing when Vue components handle interactions
- **Breaking**: This could have some side effects or break some
extensions if anything was relying on these. However, prior to this
change, things like `processNodeClick` should only have been getting the
click events in de-sync scenarios anyway (described in
[Summary](##Summary) section)

## Review Focus

Any possible side-effects you can think of.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6058-fix-LiteGraph-capturing-node-pointer-events-if-Vue-and-LG-node-positions-become-desynced-28c6d73d365081f389f8c72299c31b0b)
by [Unito](https://www.unito.io)
2025-10-16 18:46:37 -07:00
AustinMroz
09b1e1702c Skip rename operation on focus lost. (#6052)
When renaming a workflow through the breadcrumb bar. It is intended to
complete the rename operation if the user clicks away from the text
input. However, completing the text input by pressing enter can cause
the rename operation to occur twice: On pressing enter and on loss of
focus. When this occurs, the second rename causes, an error to be raised
because renaming a workflow to the same name is not allowed.

Worse, a user should be able to cancel a rename by pressing escape, but
this still causes the "loss of focus" event to fire and rename the
workflow anyways.

As a simple fix to both of these, this PR disables rename on loss of
focus. A rename only occurs when the user completes a rename operation
with the enter key.

Resolves #6051

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6052-Skip-rename-operation-on-focus-lost-28c6d73d3650815d9a5fecd8567d4299)
by [Unito](https://www.unito.io)
2025-10-16 18:45:47 -07:00
Rizumu Ayaka
64430708ea perf: tree shaking and minify (#6068)
## Summary

I believe tree-shaking does not affect [the
shim](https://github.com/Comfy-Org/ComfyUI_frontend/blob/rizumu/perf/cloud-minify/build/plugins/comfyAPIPlugin.ts#L73),
and it should safe to enable it.

maybe we need to find the extension that will broke in this case.


|   | Rendered | Gzip     | Brotli  |
|--------|-----------|----------|----------|
| Before | 16.69 MB  | 4.23 MB | 3.54 MB |
| After  | 12.07 MB  | 3 MB   | 2.53 MB |
2025-10-16 18:44:04 -07:00
Benjamin Lu
8396c9ae94 Rename ImagePreview spec to test (#6093)
## Summary
- rename the ImagePreview test file to use the .test.ts suffix required
outside of browser tests
- align with the eslint no-restricted-syntax rule that forbids .spec.ts
files in tests-ui

## Testing
- pnpm typecheck


------
https://chatgpt.com/codex/tasks/task_e_68f14718b0608330bd559182c9f7c62e

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6093-Rename-ImagePreview-spec-to-test-28e6d73d36508191b7e0c076fd96e4a3)
by [Unito](https://www.unito.io)
2025-10-16 18:16:02 -07:00
AustinMroz
1234e1c56d Support Right Click -> Save for animated webp (#6095)
Allow for Right Click -> Save Image to work for the "SaveAnimatedWEBP"
node.

Fixing this revealed other existing issues:
- Attempting to resize the node causes runaway scaling
- Right clicking on the image directly causes a browser context menu
without a save option.

Significant rewriting has been performed to resolve both of these
issues.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6095-Support-Right-Click-Save-for-animated-webp-28e6d73d3650818e85a2ec58c38c2aae)
by [Unito](https://www.unito.io)
2025-10-16 18:15:23 -07:00
pythongosssss
984ebef416 Floating Menus - UI rework (#5980)
## Summary

Enhancing and further modernizing the UI, giving users more usable area
whilst keeping farmiliar positioning and feel of elements.

## Changes

- **What**: Significant restructure of the UI elements, changing
elements from large blocks to floating elements, updating:
- Side toolbar menu (floating style, supports small/normal mode,
combines to scroll on height overflow)
- Bottom tabs panel (floating style, tabs redesigned)
- Action bar (support for docking/undocking menu)
    - Added login/user menu button to top right
- Restyled breadcrumbs (still collapse when overflows)
- Add litegraph support for fps info position (so it isn't covered by
the sidebar)

- **Breaking**: 
- Removed various elements and added new ones, I have tested custom
sidebars, custom actions, etc but if scripts are inserting elements into
"other" elements they may have been (re)moved.
- Remove support for bottom menu
- Remove support for 2nd-row tabs

## Screenshots 
<img width="1116" height="907" alt="ui"
src="https://github.com/user-attachments/assets/b040a215-67d3-4c88-8c4d-f402a16a34f6"
/>


https://github.com/user-attachments/assets/571dbda5-01ec-47e8-b235-ee1b88c93dd0

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5980-Floating-Menus-UI-rework-2866d73d3650810aac60cc1afe979b60)
by [Unito](https://www.unito.io)

---------

Co-authored-by: GitHub Action <action@github.com>
Co-authored-by: github-actions <github-actions@github.com>
2025-10-16 18:12:09 -07:00
snomiao
8cc5b52c64 refactor: reorganize GitHub workflows with consistent naming convention (#5891)
## Summary

This PR implements a systematic naming convention for all GitHub
workflows to improve organization and discoverability. All 22 workflows
have been renamed and grouped by logical categories with consistent
prefixes.

## Changes

### Naming Convention
- **`ci-*`**: Continuous Integration workflows (testing, linting,
validation)
- **`pr-*`**: PR-specific automation triggered by labels  
- **`release-*`**: Release management workflows
- **`types-*`**: TypeScript type generation workflows
- **`i18n-*`**: Internationalization workflows

### Key Renames
- `tests-ci.yaml` → `ci-tests-e2e.yaml`
- `vitest-tests.yaml` → `ci-tests-unit.yaml`
- `storybook-and-chromatic-ci.yaml` → `ci-tests-storybook.yaml`
- `auto-backport.yaml` → `pr-backport.yaml`
- `claude-pr-review.yml` → `pr-claude-review.yaml`
- `version-bump.yaml` → `release-version-bump.yaml`
- `publish-frontend-types.yaml` → `release-npm-types.yaml`
- `create-dev-pypi-package.yaml` → `release-pypi-dev.yaml`

### Test Workflow Improvements
- Grouped all test workflows under `ci-tests-*` pattern
- Fork-safe deployment workflows: `ci-tests-e2e-forks.yaml`,
`ci-tests-storybook-forks.yaml`
- Added comments explaining fork deployment security workarounds

### Documentation
- Added comprehensive `.github/workflows/README.md`
- Documents naming conventions, best practices, and workflow
organization
- Includes trigger patterns and external dependencies

## Benefits

1. **Better Organization**: Workflows are now grouped logically by
prefix
2. **Improved Discoverability**: Easy to find related workflows  
3. **Consistent Naming**: All workflows follow the same pattern
4. **Clear Purpose**: Workflow names immediately indicate their function
5. **Maintainable**: README provides guidelines for future workflows

## Test Plan

- [x] All workflow cross-references updated
- [x] Display names match new file names  
- [x] Fork deployment workflows properly reference main workflows
- [x] Release workflows reference correct npm types workflow
- [x] All workflows retain original functionality

🤖 Generated with [Claude Code](https://claude.ai/code)

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5891-refactor-reorganize-GitHub-workflows-with-consistent-naming-convention-2806d73d365081febe47c7511bf0507e)
by [Unito](https://www.unito.io)

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Alexander Brown <drjkl@comfy.org>
2025-10-17 08:32:33 +09:00
Arjan Singh
03681a12bd feat: AssetCard tweaks (#6085)
## Summary

1. fix `preview_url` logic
2. design tweaks for border radius, fallback gradient, and name line
clamping (2 lines)
3. handle image error states by showing gradient
4. misc. refactors

## Screenshots


<img width="1515" height="1087" alt="Screenshot 2025-10-15 at 8 13
41 PM"
src="https://github.com/user-attachments/assets/85642869-d8cb-4ee4-b23d-a381e33fe802"
/>

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6085-feat-AssetCard-tweaks-28e6d73d3650818da7a2f4148be48ff7)
by [Unito](https://www.unito.io)
2025-10-16 14:46:50 -07:00
AustinMroz
9bfc9b740d Add additional check when restoring widgets_values (#6054)
Prior to #6014, I had identified and started on #6015 to resolve what I
believed to be a larger class of issues. While the issue I was solving
produced the same error log on the same line, I misunderstood the
circumstances in which the reported issue would occur. As proxyWidgets
do not have their own value, only linked widgets should have their value
restored from `widgets_values`. This PR adds an additional check both
that the property entry is within bounds, and that the property entry is
for a linked widget.

See #6014
A potential additional issue is still being investigated.
- Additional issue seems unrelated to fix here. Will leave issue open,
but recommend merging this as is to not block v1.28.7

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6054-Add-additional-check-when-restoring-widgets_values-28c6d73d365081489d29fc9723132db0)
by [Unito](https://www.unito.io)
2025-10-15 19:37:41 -07:00
AustinMroz
7355a51282 In update-locales workflow, don't skip ci (#6081)
Adding `[skip ci]` causes release workflows to break and the added
safety of rerunning tests is beneficial for releases.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6081-In-update-locales-workflow-don-t-skip-ci-28e6d73d365081208332ef7b11ebbb52)
by [Unito](https://www.unito.io)
2025-10-15 18:50:23 -07:00
Arjan Singh
7ca8615947 feat(assetService): increase limit to 500 (#6078)
## Summary

Increase asset API limit to 500 to account for additional models being
added.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6078-feat-assetService-increase-limit-to-500-28d6d73d365081539760f986dd5bff23)
by [Unito](https://www.unito.io)
2025-10-15 14:38:10 -07:00
Jin Yi
4dab27a84e feat: Improve MediaAssetCard video controls and add gallery view (#6065)
## Summary
- Enhanced video control visibility logic for better UX
- Added fullscreen gallery view with zoom-in button  
- Fixed hover interaction issues with overlays

## Changes

### Video Controls
- **Before**: Controls hidden when not hovering
- **After**: Controls always visible when not playing, hover-based
during playback

### Overlay Behavior  
- **Before**: All overlays hidden during video playback
- **After**: All overlays (actions, tags, layers) show on hover even
during playback

### Gallery View
- Added zoom-in button to top-right corner (all media types except 3D)
- Integrated with existing ResultGallery component
- Gallery closes when clicking dimmed background area

### Bug Fixes
- Fixed hover flicker issue by proper event handling on overlay elements

## Test Plan
- [x] Test video controls visibility (paused vs playing)
- [x] Test overlay hover behavior during video playback
- [x] Test zoom-in button opens gallery view
- [x] Test gallery closes on background click
- [x] Test 3D assets don't show zoom button
- [x] Test in Storybook with various media types

🤖 Generated with [Claude Code](https://claude.ai/code)

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6065-feat-Improve-MediaAssetCard-video-controls-and-add-gallery-view-28d6d73d3650818c90cfc5d0d00e4826)
by [Unito](https://www.unito.io)

---------

Co-authored-by: Claude <noreply@anthropic.com>
2025-10-15 11:45:55 -07:00
Rizumu Ayaka
97417736be chore: rollup-plugin-visualizer (#6072)
## Summary

Using this tool, will generates dist/stats.html after build, we can see
the parts that are excessively large when packaged.

```bash
pnpm run build:analyze
```

<img width="2486" height="1780" alt="CleanShot 2025-10-15 at 17 25
17@2x"
src="https://github.com/user-attachments/assets/fd3ff5c9-2e7a-44a0-8b9f-4d87664c848c"
/>


┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6072-chore-rollup-plugin-visualizer-28d6d73d36508184800cf7a67141b782)
by [Unito](https://www.unito.io)
2025-10-15 11:39:12 -07:00
Alexander Piskun
86e6e7bf1f add pricing for new Veo3.1 model (#6074)
## Summary

The prices for Veo3.1 are the same as the existing Veo3, just added a
new model via `||`

## Screenshots (if applicable)

<img width="1125" height="1077" alt="Screenshot From 2025-10-15
20-39-37"
src="https://github.com/user-attachments/assets/441cba83-cdd7-4bae-af0a-259c2dd38a59"
/>

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6074-add-pricing-for-new-Veo3-1-model-28d6d73d3650817b8bb8ea7442ff3e1a)
by [Unito](https://www.unito.io)
2025-10-15 11:32:05 -07:00
Johnpaul Chiwetelu
fb3ab88f04 fix Cloudbadge (#6063)
This pull request refactors how top bar badges are handled in the
application, making badge rendering extensible and moving cloud badge
logic into the extension system. The main changes include replacing the
old `CloudBetaBadge` component with a new, generic badge system,
introducing a Pinia store for badge management, and updating the
extension API to support top bar badges.

**Badge System Refactor and Extensibility**

* Replaced the hardcoded `CloudBetaBadge` with a new `TopbarBadges`
component, which dynamically renders badges from the store instead of
relying on the `isCloud` flag in `TopMenubar.vue`.
[[1]](diffhunk://#diff-b7d7bf1028f09fb907c09edf27631214d005c93b80eaff7cf15cfd53671b1e8aL9-R9)
[[2]](diffhunk://#diff-b7d7bf1028f09fb907c09edf27631214d005c93b80eaff7cf15cfd53671b1e8aL43-R48)

* Renamed and refactored `CloudBetaBadge.vue` to `TopbarBadge.vue`,
making it accept a generic `badge` prop and removing i18n logic from the
component.

* Added a new `TopbarBadges.vue` component to render all badges from the
`topbarBadgeStore`.

**Badge Data Management**

* Introduced a new Pinia store `topbarBadgeStore` that aggregates top
bar badges from all extensions, enabling dynamic badge management.

**Extension System Integration**

* Updated the extension API (`ComfyExtension` interface) to support a
new `topbarBadges` property, allowing extensions to contribute badges to
the top bar.

* Added a core extension (`cloudBadge.ts`) that registers a "Comfy
Cloud" beta badge when running in a cloud environment, using the new
badge system.
[[1]](diffhunk://#diff-b7818ca9daae2411d56695777160b8132507f2a3ff4f700d2510453c8833ca75R1-R16)
[[2]](diffhunk://#diff-236993d9e4213efe96d267c75c3292d32b93aa4dd6c3318d26a397e0ae56bc87R2)

**Type Definitions**

* Added a new `TopbarBadge` type to `comfy.ts` to define the structure
for top bar badges, supporting optional labels.
2025-10-15 05:27:19 +01:00
Terry Jia
476d6df1ca fix mask editor bug under vueNodes (#5953)
## Summary

fix mask editor issues on vueNodes

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5953-fix-mask-editor-bug-under-vueNodes-2856d73d3650810aa8a2e1a94c4d97a6)
by [Unito](https://www.unito.io)
2025-10-14 17:42:36 -07:00
Johnpaul Chiwetelu
7caad10e93 Badge for cloud environment (#6048)
This pull request introduces a new system for displaying
environment-specific badges in the application's top bar, with a focus
on supporting a "Comfy Cloud" badge in production environments. The
changes include new badge types, extension support, UI components, and
environment detection logic to ensure badges are only shown in
appropriate contexts.

**Topbar Badge System**

* Added a new `TopbarBadge` type and support for topbar badges in the
`ComfyExtension` interface to allow extensions to specify badges for the
top bar.
[[1]](diffhunk://#diff-c29886a1b0c982c6fff3545af0ca8ec269876c2cf3948f867d08c14032c04d66R24-R31)
[[2]](diffhunk://#diff-c29886a1b0c982c6fff3545af0ca8ec269876c2cf3948f867d08c14032c04d66R85-R88)
* Created a Pinia store `topbarBadgeStore` to aggregate topbar badges
from all registered extensions for display.

**UI Integration**

* Added a new `TopbarBadges.vue` component to render topbar badges and
integrated it into the top menu bar UI.
[[1]](diffhunk://#diff-6f460b1398fd033a2059daca1f991c74ce572cef86046a3726d1b1a70a3a4325R1-R32)
[[2]](diffhunk://#diff-b7d7bf1028f09fb907c09edf27631214d005c93b80eaff7cf15cfd53671b1e8aL5-R14)
* Updated CSS variables and menu styling to support the new badge
visuals.
[[1]](diffhunk://#diff-71b6b57a56095b04e47c797a5016149b76b27971cab04b93f033f1f846e0f5a0R88-R89)
[[2]](diffhunk://#diff-b7d7bf1028f09fb907c09edf27631214d005c93b80eaff7cf15cfd53671b1e8aL5-R14)

**Environment Detection and Extension Registration**

* Added a runtime environment detection utility to determine if the app
is running in production or staging, replacing the previous build-time
constant approach.
* Registered a new `cloudBadge` extension that conditionally adds a
"Comfy Cloud" badge with a "BETA" label when running in production.
[[1]](diffhunk://#diff-b7818ca9daae2411d56695777160b8132507f2a3ff4f700d2510453c8833ca75R1-R15)
[[2]](diffhunk://#diff-236993d9e4213efe96d267c75c3292d32b93aa4dd6c3318d26a397e0ae56bc87R2)

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6048-Badge-for-cloud-environment-28c6d73d365081188050ece527c3c8f3)
by [Unito](https://www.unito.io)

<img width="996" height="897" alt="Screenshot 2025-10-14 at 20 02 40"
src="https://github.com/user-attachments/assets/5a3258c5-87fc-46ae-ad23-7669696cb8b6"
/>
2025-10-15 00:44:32 +01:00
Christian Byrne
6ea96f071e [style] update design of keybinding badges in menus (#6059)
## Summary

Updates the keybinding badges in the context menu (from selection
toolbox and right click on Vue node) to align with [the
design](https://www.figma.com/design/vALUV83vIdBzEsTJAhQgXq/Comfy-Design-System?node-id=3128-104039&m=dev)
exactly, including using the tokens from Figma variables.


https://github.com/user-attachments/assets/e37492f7-81a8-4598-bebb-56eb86b5dc56

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6059-style-update-design-of-keybinding-badges-in-menus-28c6d73d3650817784c8d8afac9ed8b8)
by [Unito](https://www.unito.io)
2025-10-14 16:02:04 -07:00
Christian Byrne
10af2300fa rework minimap, toolbox, and menu designs with unified theming (#6038)
## Summary

This PR redesigns the graph canvas interface components including
minimap, toolbox, and menu systems with updated spacing, colors, and
interaction patterns - using the design tokens directly from Figma,
which can be used elsewhere going forward.

There are some other changes to the designs, outlined
[here](https://www.notion.so/comfy-org/Update-Minimap-Menu-v2-2886d73d365080e88e12f8df027019c0):

- [x]  Update/standardize the padding between viewport and toolbox
- [x] Update toolbox component’s style to match the other floating menus
style (border radius, height, padding and follow theme colors)
- [x]  Expose the minimap button
- [x]  Remove the focus button and delete it’s keybinding
- [x]  Group the hand and the default cursor buttons


https://github.com/user-attachments/assets/92542e60-c32d-4a21-a6f6-e72837a70b17

## Review Focus

New CSS variables for cross-component theming consistency and
CanvasModeSelector component extraction for improved code organization.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6038-rework-minimap-toolbox-and-menu-designs-with-unified-theming-28b6d73d36508191a0c6cf8036d965c4)
by [Unito](https://www.unito.io)

---------

Co-authored-by: github-actions <github-actions@github.com>
2025-10-14 14:26:07 -07:00
Makki Shizu
2058967761 Fix Simplified Chinese Translation (#6039)
## Summary
Revise some traditional Chinese to simplified Chinese.
<!-- 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 -->

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6039-Fix-Simplified-Chinese-Translation-28b6d73d365081aebba5f60893b75cb3)
by [Unito](https://www.unito.io)
2025-10-14 13:33:55 -07:00
Christian Byrne
d1af7c8256 fix terminal style (#6056)
## Summary

After #5292, at certain browser zoom levels, the xterm terminal did not
fill horizontal space properly, showing a gap with mismatched background
colors (`#171717` viewport vs `black` terminal content).

Fixes https://github.com/Comfy-Org/ComfyUI_frontend/issues/6049

## Problem

The hardcoded `#171717` terminal theme background only affected the
xterm viewport, while terminal content remained black, causing a visible
color mismatch at low zoom levels where the gap was more apparent.

Fixed by keeping original theme when not on desktop distribution.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6056-fix-terminal-style-28c6d73d36508132b521e5767f41d540)
by [Unito](https://www.unito.io)
2025-10-14 13:26:35 -07:00
Christian Byrne
5bc7c8a5c2 add aria labels on vue node widgets (2/2) (#6037)
## Summary

Continuation of https://github.com/Comfy-Org/ComfyUI_frontend/pull/6032

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6037-add-aria-labels-on-vue-node-widgets-2-2-28b6d73d365081d68795f5dfaca0b89a)
by [Unito](https://www.unito.io)
2025-10-14 11:33:15 -07:00
Arjan Singh
094d6e65a2 Trigger release workflow for 1.29.2 (#6046)
Empty commit to trigger release workflow after [skip ci] blocked
automatic release.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6046-Trigger-release-workflow-for-1-29-2-28c6d73d365081119ef7d8bf570d2804)
by [Unito](https://www.unito.io)
2025-10-13 20:01:38 -07:00
Comfy Org PR Bot
2808e0a437 1.29.2 (#6045)
## What's Changed

### 🚀 Features
- Add MediaAssetCard presentation components (#5878)
- Make Vue nodes' outputs/previews responsively sized and work with node
resizing (#5970)
- Allow connection to subgraphIOs in vue mode (#6016)
- Add distribution detection pattern (#6028)
- Make nodeData.widgets reactive (#6019)

### 🐛 Bug Fixes
- Fix FLOAT widget incrementing broken & disabled state styles on widget
number input (Vue) (#6036)
- Fix Vue node border styles in different states (executing, error,
selected) (#6018)
- Fix Vue node opacity conditions (user node opacity, bypass state,
muted state) (#6022)
- Fix: emit layout change for batch node bounds (#5939)
- Safer restoration of widgets_values on subgraph nodes (#6015)
- Fix(execution): reset progress state after runs to unfreeze tab
title/favicon (main) (#6026)
- Use type check instead of cast (#6041)

### 🎨 Style & Design
- [style] match widget border/outline styles with designs (#6021)
- [style] make Vue widget/slot/label width and spacing align with
designs (#6023)

###  Accessibility
- Add aria labels on vue node widgets (#6032)

### 🔧 Maintenance
- [refactor] adjust Vue node fixtures to not be coupled to Litegraph
(#6033)
- [refactor] reorganize devtools test nodes into modules (#6020)

### 🧪 Testing
- [test] add browser test for control+a selection of Vue nodes (#6031)

### 🔄 CI/CD
- [ci] fix update locales workflow (#6017)

**Full Changelog**:
https://github.com/Comfy-Org/ComfyUI_frontend/compare/v1.29.1...v1.29.2

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6045-1-29-2-28c6d73d3650817a8c36fba944ce69a8)
by [Unito](https://www.unito.io)

---------

Co-authored-by: arjansingh <1598641+arjansingh@users.noreply.github.com>
Co-authored-by: github-actions <github-actions@github.com>
2025-10-13 19:15:14 -07:00
Christian Byrne
95c2732de4 fix ctrl+alt+click to remove link on Vue nodes (#6035)
## Summary

Implements Ctrl+Alt+click batch disconnect functionality for Vue node
output slots to match LiteGraph behavior.

## Changes

- **Feature**: Add Ctrl+Alt+click handler in `useSlotLinkInteraction.ts`
to disconnect all links from output slots
- **Test**: Add test case in `linkInteraction.spec.ts` to verify batch
disconnect behavior
- Follows existing pattern from input slot disconnect implementation

## Implementation Details

The implementation:
- Checks for Ctrl+Alt+click on output slots with existing links
- Calls `resolvedNode.disconnectOutput(index)` to batch disconnect all
links
- Marks canvas as dirty and prevents event propagation
- Matches LiteGraph canvas behavior (`LGraphCanvas.ts:2727-2731`)
- Follows same pattern as existing input slot disconnect (lines 591-611)

Note: Test currently uses `dispatchEvent` for pointerdown with modifiers
and is failing. The feature implementation is correct and matches the
existing codebase patterns, but the test interaction needs debugging.
2025-10-13 18:58:21 -07:00
AustinMroz
e59d2dd8df Use type check instead of cast (#6041)
Under some infrequent circumstances, `audioWidget.value` is not a
string. Presumably if a workflow is loaded with a saved file choice that
does not exist and the value is set to undefined instead.

Instead of a cast, a proper type guard is used and the widget is not
updated if the new value is not a string.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6041-Use-type-check-instead-of-cast-28b6d73d365081249353f4f905769f89)
by [Unito](https://www.unito.io)
2025-10-13 11:52:03 -07:00
Christian Byrne
d54923f766 fix FLOAT widget incrementing broken & disabled state styles on widget number input (Vue) (#6036)
## Summary

Align Vue node number widgets with Figma by centralising button styling
and surfacing disabled-state tokens in the design system.

## Changes

- **What**: Added shared
[`useNumberWidgetButtonPt`](src/renderer/extensions/vueNodes/widgets/composables/useNumberWidgetButtonPt.ts)
helper so both PrimeVue `InputNumber` widgets reuse the same Tailwind
token classes, and added the `color/tokens/alpha` values in
[`packages/design-system/src/css/style.css`](packages/design-system/src/css/style.css#L89-L212)
so semantic aliases remain token-driven ([PrimeVue passthrough
docs](https://www.primefaces.org/primevue/passthrough) w
[`color-mix`](https://www.w3.org/TR/css-color-5/#color-mix))

## Review Focus

Confirm hover/active/disabled colours match the design tokens in both
light and dark themes and that float precision still respects the
safe-range guard.

<img width="1377" height="1150" alt="Screenshot from 2025-10-12
17-53-23"
src="https://github.com/user-attachments/assets/c7d34870-5d07-4ce1-9272-7def7ae813b6"
/>

<img width="1377" height="1150" alt="Screenshot from 2025-10-12
17-53-32"
src="https://github.com/user-attachments/assets/86872ec8-979b-4586-879c-41a126a5f932"
/>

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6036-fix-disabled-state-styles-on-Vue-widget-number-input-INT-and-FLOAT-widgets-28b6d73d365081f8aef7fa860b641f7d)
by [Unito](https://www.unito.io)

---------

Co-authored-by: github-actions <github-actions@github.com>
2025-10-12 23:34:08 -07:00
Christian Byrne
c30f528d11 [refactor] adjust Vue node fixtures to not be coupled to Litegraph (#6033)
## Summary

Changes the Vue node test fixture to not rely on Litegraph internal
objects (which should eventually be fully decoupled from Vue nodes) and
instead interact with nodes using black-box approach that emulates user
actions (preferred appraoch for e2e tests).

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6033-refactor-adjust-Vue-node-fixtures-to-not-be-coupled-to-Litegraph-28a6d73d3650817b8152d27dc4fe0017)
by [Unito](https://www.unito.io)
2025-10-12 19:56:42 -07:00
Christian Byrne
0497421349 add aria labels on vue node widgets (#6032)
## Summary

Adds aria labels to buttons and widgets without pre-existing text
labels.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6032-add-aria-labels-on-vue-node-widgets-28a6d73d36508198a1c0ef7098ad24e8)
by [Unito](https://www.unito.io)
2025-10-12 17:44:03 -07:00
Christian Byrne
01b4ad0dbb [test] add browser test for control+a selection of Vue nodes (#6031)
## Summary

Adds test case ensuring you can select all Vue nodes with `Ctrl`+`a`,
mirroring coverage of similar behavior when using Litegraph nodes.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6031-test-add-browser-test-for-control-a-selection-of-Vue-nodes-28a6d73d365081079860c3a083a946ef)
by [Unito](https://www.unito.io)
2025-10-12 17:01:45 -07:00
Christian Byrne
31c85387ba [style] match widget border/outline styles with designs (#6021)
## Summary

Use semantic color variables from
https://github.com/Comfy-Org/ComfyUI_frontend/pull/6018 on widget
borders to match
[design](https://www.figma.com/design/vALUV83vIdBzEsTJAhQgXq/Comfy-Design-System?node-id=2-5739&m=dev)

The layouting of the widgets doesn't align yet, but it's somewhat
annoying to change the `WidgetSelect` height without using line height.
But, the gap should be 4 (16px) instead of 2, the height of the rows
should be 35px instead of 30px and the widgets should be 32px instead of
30px.

## Before

<img width="2061" height="1386" alt="Screenshot from 2025-10-11
12-23-24"
src="https://github.com/user-attachments/assets/5aa7ba1e-9309-4bd5-95b4-8d8e3d95b50b"
/>

<img width="2061" height="1386" alt="Screenshot from 2025-10-11
12-23-16"
src="https://github.com/user-attachments/assets/9dbabd1b-2174-4dfd-83c2-fef8178c7206"
/>

## After

<img width="2061" height="1386" alt="Screenshot from 2025-10-11
12-23-06"
src="https://github.com/user-attachments/assets/d0b0a611-e65b-462f-ad94-c42639502951"
/>

<img width="2061" height="1386" alt="Screenshot from 2025-10-11
12-22-57"
src="https://github.com/user-attachments/assets/64fb42c8-3d9a-4a2b-956f-482fcd63b64c"
/>

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6021-style-match-widget-border-outline-styles-with-designs-2896d73d365081d18dd9cca41cc2b95e)
by [Unito](https://www.unito.io)

---------

Co-authored-by: github-actions <github-actions@github.com>
2025-10-12 00:32:51 -07:00
AustinMroz
8108aaa2d4 Allow connection to subgraphIOs in vue mode (#6016)
Adds support for link connections from nodes to subgraphInputs and
subgraphOutputs when in vue mode.

![vue-subgraphio](https://github.com/user-attachments/assets/5b1ef66f-d45a-40c7-ace0-932aaf811e1d)

Resolves #5706

Known Issues
- Creating a connection from a widget does not trigger an update of the
widget to the disabled state

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6016-Allow-connection-to-subgraphIOs-in-vue-mode-2896d73d3650816cbd88f645dced87df)
by [Unito](https://www.unito.io)
2025-10-11 23:29:10 -07:00
Christian Byrne
9c245e9c23 Add distribution detection pattern (#6028)
## Summary

Establishes distribution-specific code pattern using compile-time
constants and dead code elimination. Demonstrates with Help Center by
hiding extension manager and update buttons in cloud distribution.

Below commentary makes assumption that minifcation and tree-shaking is
enabled (which isn't true yet, but will be eventually).

## Changes

- **What**: Added `src/platform/distribution/types.ts` with distribution
detection via `__DISTRIBUTION__` variable
- **Build**: Vite replaces `__DISTRIBUTION__` at build time using
environment variables
- **Tree-shaking**: All code not relevant to target distribution is
DCR'd and eliminated from bundle
- **Example**: Help Center hides "Manager Extension" menu item and
"Update" buttons in cloud builds

## Pattern

This PR defines a `__DISTRIBUTION__` variable which gets replaced at
build time by Vite using environment variables. All code not relevant to
the given distribution is then DCR'd and tree-shaken.

For simple cases (like this Help Center PR), import `isCloud` and use
compile-time conditionals:

```typescript
import { isCloud } from '@/platform/distribution/types'

if (!isCloud) {
  items.push({
    key: 'manager',
    action: async () => {
      await useManagerState().openManager({ ... })
    }
  })
}
```

The code is DCR'd at build time so there's zero runtime overhead - we
don't even incur the `if (isCloud)` cost because Terser eliminates it.

For complex services later, we'll add interfaces and use an index.ts
that exports different implementations under the same alias per
distribution. It will resemble a DI container but simpler since we don't
need runtime discovery like backend devs do. This guarantees types and
makes testing easier.

Example for services:
```typescript
// src/platform/storage/index.ts
import { isCloud } from '@/platform/distribution/types'

if (isCloud) {
  export { CloudStorage as StorageService } from './cloud'
} else {
  export { LocalStorage as StorageService } from './local'
}
```

Example for component variants:
```typescript
// src/components/downloads/index.ts
import { isCloud } from '@/platform/distribution/types'

if (isCloud) {
  export { default as DownloadButton } from './DownloadButton.cloud.vue'
} else {
  export { default as DownloadButton } from './DownloadButton.desktop.vue'
}
```

## Implementation Details

Distribution types (`src/platform/distribution/types.ts`):
```typescript
type Distribution = 'desktop' | 'localhost' | 'cloud'

declare global {
  const __DISTRIBUTION__: Distribution
}

const DISTRIBUTION: Distribution = __DISTRIBUTION__
export const isCloud = DISTRIBUTION === 'cloud'
```

Vite configuration adds the define:
```typescript
const DISTRIBUTION = (process.env.DISTRIBUTION || 'localhost') as
  | 'desktop'
  | 'localhost'
  | 'cloud'

export default defineConfig({
  define: {
    __DISTRIBUTION__: JSON.stringify(DISTRIBUTION)
  }
})
```

## Build Commands

```bash
pnpm build                      # localhost (default)
DISTRIBUTION=cloud pnpm build   # cloud
DISTRIBUTION=desktop pnpm build # desktop
```

## Future Applications

This pattern can be used with auth or telemetry services - which will
guarantee all the telemetry code, for example, is not even in the code
distributed in OSS Comfy whatsoever while still being able to develop
off `main`.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6028-Add-distribution-detection-pattern-28a6d73d365081b08767d395472cd1bc)
by [Unito](https://www.unito.io)
2025-10-11 23:10:15 -07:00
AustinMroz
cb40da612b Safer restoration of widgets_values on subgraph nodes (#6015)
Reordering linked widgets requires special attention on load to restore
widgets_values. The method which was merged was optimistic and
insufficient for some rarer edge cases.

Resolves #6014 

Fix was already included in #6009. Backport is not required.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6015-Safer-restoration-of-widgets_values-on-subgraph-nodes-2896d73d3650813a9162e8459e686981)
by [Unito](https://www.unito.io)
2025-10-11 23:09:29 -07:00
Christian Byrne
ddb3a0bfc6 fix Vue node opacity conditions (user node opacity, bypass state, muted state) (#6022)
## Summary

Fixed Vue node opacity calculation to properly combine global opacity
setting with muted/bypassed state opacity.

**Root Cause**: When global opacity setting was added as inline style
(481aa8252), it began overriding CSS `opacity-50` classes due to higher
specificity.

**Solution**: Modified `nodeOpacity` computed property to calculate
effective opacity as `globalOpacity * 0.5` for muted/bypassed states,
removing conflicting CSS classes.

## Changes

- **What**: Fixed [CSS specificity
conflict](https://developer.mozilla.org/en-US/docs/Web/CSS/Specificity)
where inline `opacity` style overrode `opacity-50` classes for
muted/bypassed nodes
- **Breaking**: None - restores intended opacity behavior

## Review Focus

Multiplicative opacity calculation ensuring muted/bypassed nodes apply
0.5 opacity on top of global opacity setting rather than being
overridden by it.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6022-fix-Vue-node-opacity-conditions-user-node-opacity-bypass-state-muted-state-2896d73d365081c290f1da37c195c2f5)
by [Unito](https://www.unito.io)

---------

Co-authored-by: github-actions <github-actions@github.com>
2025-10-11 21:36:50 -07:00
AustinMroz
5773df6ef7 Make nodeData.widgets reactive (#6019)
Makes the litegraph `node.widgets` array `shallowReactive` and makes the
`nodeData.widgets` a `reactiveComputed` derived from the litegraph
widget data.

![reactive-widgets](https://github.com/user-attachments/assets/8eb8d712-8586-4f34-b699-30fc3dc3340b)


Making changes to the structure of litegraph items is somewhat
dangerous, but code search verifies that there are no custom nodes using
`defineProperty` on `node.widgets`

This fixes display of promoted widgets on subgraph node and any custom
nodes that dynamically add or remove widgets.

TODO:
- Investigate occasional dropped widgets.
- Some of this was confusion with `canvasOnly` widgets and widgets not
implemented in vue. Will keep investigating, but I'm not terribly
concerned with actual test cases and it being an objective improvement.
  
Known Issue:
- Node does not grow/shrink to fit changed widgets

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6019-Make-nodeData-widgets-reactive-2896d73d3650815691b6ee370a86a22c)
by [Unito](https://www.unito.io)
2025-10-11 20:32:15 -07:00
Christian Byrne
bc281b2513 [style] make Vue widget/slot/label width and spacing align with designs (#6023)
Make the widths and spacing of the widgets/slots/labels match the
[design](https://www.figma.com/design/31uH3r4x3xbIctuRWYW6NM/V3---Vue-Nodes?node-id=6489-33817&m=dev)
which also better matches the interal layout of litegraph nodes.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6023-style-make-Vue-widget-slot-label-width-and-spacing-align-with-designs-2896d73d365081a1a831f396cb4eafc8)
by [Unito](https://www.unito.io)

---------

Co-authored-by: github-actions <github-actions@github.com>
2025-10-11 20:08:05 -07:00
Benjamin Lu
1d06b4d63b fix(execution): reset progress state after runs to unfreeze tab title/favicon (main) (#6026)
Cherry picked over from #6025, should've been made to target main to
begin with

## Summary
Fixes the browser tab progress and favicon remaining at ~14% after
workflow completion on `main` by resetting execution state when a run
ends (success, error, or interruption).

## Changes
- Add `execution_success` listener in the execution store
- Centralize terminal-state cleanup in `resetExecutionState()`
- Clear `nodeProgressStates`, queued prompt entry,
`_executingNodeProgress`, and set `activePromptId` to `null`
- Ensures `isIdle` becomes `true` post-run so tab title and favicon no
longer freeze mid-progress

resolves #6024

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6026-fix-execution-reset-progress-state-after-runs-to-unfreeze-tab-title-favicon-main-28a6d73d365081f188ebc2e69d936dd9)
by [Unito](https://www.unito.io)
2025-10-11 18:53:58 -07:00
Christian Byrne
14c07fd734 [refactor] reorganize devtools test nodes into modules (#6020)
## Summary

Refactored monolithic devtools node definitions into organized module
structure for better maintainability and separation of concerns.

## Changes

- **What**: Split 700+ line `dev_nodes.py` into modular structure under
`tools/devtools/nodes/` with categorized files: `errors.py`,
`inputs.py`, `models.py`, `remote.py`
- **Dependencies**: None

## Review Focus

Module import structure and ensure all node registrations are properly
preserved in the consolidated mappings.

**Before:**
```
tools/devtools/
├── __init__.py
└── dev_nodes.py (738 lines)
```

**After:**
```
tools/devtools/
├── __init__.py
├── dev_nodes.py (65 lines - imports only)
└── nodes/
    ├── __init__.py (consolidated mappings)
    ├── errors.py (error/debug nodes)
    ├── inputs.py (input/widget nodes)
    ├── models.py (model/patch nodes)
    └── remote.py (remote/combo nodes)
```

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6020-refactor-reorganize-devtools-test-nodes-into-modules-2896d73d365081e89efef7e88ca8fee3)
by [Unito](https://www.unito.io)
2025-10-11 15:29:29 -07:00
Christian Byrne
7cc08e8e35 [ci] fix update locales workflow (#6017)
Similar to https://github.com/Comfy-Org/ComfyUI_frontend/pull/6005,
fixing the update-locales workflow by setting up the frontend before
launching ComfyUI server.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6017-ci-fix-update-locales-workflow-2896d73d36508173aaf9e0eefe4f7660)
by [Unito](https://www.unito.io)
2025-10-11 15:21:34 -07:00
Jin Yi
9c0b3c4f7d Add MediaAssetCard presentation components (#5878)
## Summary

Implements a comprehensive media asset card component system for the
Asset Manager sidebar, enabling display and interaction with various
media types (images, videos, audio, and 3D models).

## Changes

### New Components
- **MediaAssetCard**: Main card component for displaying media assets
- **Media type-specific components**: Specialized display logic for each
media type
  - MediaImageTop/Bottom
  - MediaVideoTop/Bottom  
  - MediaAudioTop/Bottom
  - Media3DTop/Bottom
- **MediaAssetActions**: Top-left action buttons (delete, download, more
options)
- **MediaAssetMoreMenu**: Dropdown menu for additional actions
- **SquareChip**: Chip component for displaying duration and file format
with dark/light variants
- **MediaAssetButtonDivider**: Visual separator for button groups

### Features
- **Video playback**: Autoplay with native video controls
  - Dynamic duration chip positioning based on control visibility
  - Hides overlays when video is playing
- **Audio playback**: Audio icon with HTML5 audio element
  - Duration chip with consistent positioning
- **3D model support**: Icon display for 3D assets
- **Selection state**: Proper hover and selected state handling with CSS
priority fixes

### Architecture Improvements
- **Domain-Driven Design structure**: Organized under
`src/platform/mediaAsset/` following DDD principles
- **Provide/Inject pattern**: Eliminates props drilling with
MediaAssetKey InjectionKey
- **Composable pattern**: `useMediaAssetActions` manages all action
handlers
- **Type safety**: Comprehensive TypeScript types for media assets and
actions

### UI/UX Enhancements
- **CardTop component**: Added custom class props for slot positioning
- **SquareChip component**: Backdrop blur effects with variant system
- **Lazy loading**: Image optimization with LazyImage component
- **Responsive states**: Loading, selected, and hover states

### Utilities
- **formatDuration**: Converts milliseconds to human-readable format
(45s, 1m 23s, 1h 2m)

## Testing
- Comprehensive Storybook stories for all media types
- Grid layout examples
- Loading and selected state demonstrations

## File Structure
```
src/platform/assets/
├── components/
│   ├── MediaAssetCard.vue
│   ├── MediaAssetCard.stories.ts
│   ├── MediaAssetActions.vue
│   ├── MediaAssetMoreMenu.vue
│   ├── MediaAssetButtonDivider.vue
│   ├── MediaImageTop.vue
│   ├── MediaImageBottom.vue
│   ├── MediaVideoTop.vue
│   ├── MediaVideoBottom.vue
│   ├── MediaAudioTop.vue
│   ├── MediaAudioBottom.vue
│   ├── Media3DTop.vue
│   └── Media3DBottom.vue
├── composables/
│   └── useMediaAssetActions.ts
└── schemas/
    └── mediaAssetSchema.ts
```

## Screenshots

[media_asset_record.webm](https://github.com/user-attachments/assets/d13b5cc0-a262-4850-bb81-ca1daa0dd969)

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: GitHub Action <action@github.com>
Co-authored-by: github-actions <github-actions@github.com>
2025-10-11 14:39:04 -07:00
Christian Byrne
bb83b0107c fix Vue node border styles in different states (executing, error, selected) (#6018)
- Use exact tokens from Figma
- Fix issue in which node is stuck in `executing` state after it errors

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6018-fix-Vue-node-border-styles-in-different-states-executing-error-selected-2896d73d365081f39000fc3e42811f0d)
by [Unito](https://www.unito.io)
2025-10-11 12:20:06 -07:00
649 changed files with 33648 additions and 19978 deletions

View File

@@ -458,15 +458,15 @@ echo "Workflow triggered. Waiting for PR creation..."
3. **IMMEDIATELY CHECK**: Did release workflow trigger?
```bash
sleep 10
gh run list --workflow=release.yaml --limit=1
gh run list --workflow=release-draft-create.yaml --limit=1
```
4. **For Minor/Major Version Releases**: The create-release-candidate-branch workflow will automatically:
4. **For Minor/Major Version Releases**: The release-branch-create workflow will automatically:
- Create a `core/x.yy` branch for the PREVIOUS minor version
- Apply branch protection rules
- Document the feature freeze policy
```bash
# Monitor branch creation (for minor/major releases)
gh run list --workflow=create-release-candidate-branch.yaml --limit=1
gh run list --workflow=release-branch-create.yaml --limit=1
```
4. If workflow didn't trigger due to [skip ci]:
```bash
@@ -477,7 +477,7 @@ echo "Workflow triggered. Waiting for PR creation..."
```
5. If workflow triggered, monitor execution:
```bash
WORKFLOW_RUN_ID=$(gh run list --workflow=release.yaml --limit=1 --json databaseId --jq '.[0].databaseId')
WORKFLOW_RUN_ID=$(gh run list --workflow=release-draft-create.yaml --limit=1 --json databaseId --jq '.[0].databaseId')
gh run watch ${WORKFLOW_RUN_ID}
```

View File

@@ -246,7 +246,7 @@ For each commit:
3. Merge the PR: `gh pr merge --merge`
4. Monitor release workflow:
```bash
gh run list --workflow=release.yaml --limit=1
gh run list --workflow=release-draft-create.yaml --limit=1
gh run watch
```
5. Track progress:

View File

@@ -5,6 +5,10 @@ PLAYWRIGHT_TEST_URL=http://localhost:5173
# Proxy target of the local development server
# Note: localhost:8188 does not work.
# Cloud auto-detection: Setting this to any *.comfy.org URL automatically enables
# cloud mode (DISTRIBUTION=cloud) without needing to set DISTRIBUTION separately.
# Examples: https://testcloud.comfy.org/, https://stagingcloud.comfy.org/,
# https://pr-123.testenvs.comfy.org/, https://cloud.comfy.org/
DEV_SERVER_COMFYUI_URL=http://127.0.0.1:8188
# Allow dev server access from remote IP addresses.
@@ -19,10 +23,10 @@ TEST_COMFYUI_DIR=/home/ComfyUI
# Whether to enable minification of the frontend code.
ENABLE_MINIFY=true
# Whether to disable proxying the `/templates` route. If true, allows you to
# serve templates from the ComfyUI_frontend/public/templates folder (for
# locally testing changes to templates). When false or nonexistent, the
# templates are served via the normal method from the server's python site
# Whether to disable proxying the `/templates` route. If true, allows you to
# serve templates from the ComfyUI_frontend/public/templates folder (for
# locally testing changes to templates). When false or nonexistent, the
# templates are served via the normal method from the server's python site
# packages.
DISABLE_TEMPLATES_PROXY=false
@@ -33,3 +37,8 @@ DISABLE_VUE_PLUGINS=false
# Algolia credentials required for developing with the new custom node manager.
ALGOLIA_APP_ID=4E0RO38HS8
ALGOLIA_API_KEY=684d998c36b67a9a9fce8fc2d8860579
# Sentry ENV vars replace with real ones for debugging
# SENTRY_AUTH_TOKEN=private-token # get from sentry
# SENTRY_ORG=comfy-org
# SENTRY_PROJECT=cloud-frontend-staging

1
.gitattributes vendored
View File

@@ -7,6 +7,7 @@
*.json text eol=lf
*.mjs text eol=lf
*.mts text eol=lf
*.snap text eol=lf
*.ts text eol=lf
*.vue text eol=lf
*.yaml text eol=lf

21
.github/workflows/README.md vendored Normal file
View File

@@ -0,0 +1,21 @@
# GitHub Workflows
## Naming Convention
Workflow files follow a consistent naming pattern: `<prefix>-<descriptive-name>.yaml`
### Category Prefixes
| Prefix | Purpose | Example |
| ---------- | ----------------------------------- | ------------------------------------ |
| `ci-` | Testing, linting, validation | `ci-tests-e2e.yaml` |
| `release-` | Version management, publishing | `release-version-bump.yaml` |
| `pr-` | PR automation (triggered by labels) | `pr-claude-review.yaml` |
| `api-` | External Api type generation | `api-update-registry-api-types.yaml` |
| `i18n-` | Internationalization updates | `i18n-update-core.yaml` |
## Documentation
Each workflow file contains comments explaining its purpose, triggers, and behavior. For specific details about what each workflow does, refer to the comments at the top of each `.yaml` file.
For GitHub Actions documentation, see [Events that trigger workflows](https://docs.github.com/en/actions/writing-workflows/choosing-when-your-workflow-runs/events-that-trigger-workflows).

View File

@@ -1,4 +1,5 @@
name: Update Electron Types
name: 'Api: Update Electron API Types'
description: 'When upstream electron API is updated, click dispatch to update the TypeScript type definitions in this repo'
on:
workflow_dispatch:
@@ -25,15 +26,6 @@ jobs:
node-version: lts/*
cache: 'pnpm'
- name: Cache tool outputs
uses: actions/cache@v4
with:
path: |
.cache
key: electron-types-tools-cache-${{ runner.os }}-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
electron-types-tools-cache-${{ runner.os }}-
- name: Update electron types
run: pnpm install --workspace-root @comfyorg/comfyui-electron-types@latest

View File

@@ -1,4 +1,5 @@
name: Update ComfyUI-Manager API Types
name: 'Api: Update Manager API Types'
description: 'When upstream ComfyUI-Manager API is updated, click dispatch to update the TypeScript type definitions in this repo'
on:
# Manual trigger
@@ -30,26 +31,9 @@ jobs:
node-version: lts/*
cache: 'pnpm'
- name: Cache tool outputs
uses: actions/cache@v4
with:
path: |
.cache
key: update-manager-tools-cache-${{ runner.os }}-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
update-manager-tools-cache-${{ runner.os }}-
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Cache ComfyUI-Manager repository
uses: actions/cache@v4
with:
path: ComfyUI-Manager
key: comfyui-manager-repo-${{ runner.os }}-${{ github.run_id }}
restore-keys: |
comfyui-manager-repo-${{ runner.os }}-
- name: Checkout ComfyUI-Manager repository
uses: actions/checkout@v5
with:

View File

@@ -1,4 +1,5 @@
name: Update Comfy Registry API Types
name: 'Api: Update Registry API Types'
description: 'When upstream comfy-api is updated, click dispatch to update the TypeScript type definitions in this repo'
on:
# Manual trigger
@@ -29,26 +30,9 @@ jobs:
node-version: lts/*
cache: 'pnpm'
- name: Cache tool outputs
uses: actions/cache@v4
with:
path: |
.cache
key: update-registry-tools-cache-${{ runner.os }}-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
update-registry-tools-cache-${{ runner.os }}-
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Cache comfy-api repository
uses: actions/cache@v4
with:
path: comfy-api
key: comfy-api-repo-${{ runner.os }}-${{ github.run_id }}
restore-keys: |
comfy-api-repo-${{ runner.os }}-
- name: Checkout comfy-api repository
uses: actions/checkout@v5
with:

View File

@@ -1,4 +1,5 @@
name: Validate JSON
name: "CI: JSON Validation"
description: "Validates JSON syntax in all tracked .json files (excluding tsconfig*.json) using jq"
on:
push:

View File

@@ -1,4 +1,5 @@
name: Lint and Format
name: "CI: Lint Format"
description: "Linting and code formatting validation for pull requests"
on:
pull_request:
@@ -32,27 +33,15 @@ jobs:
node-version: 'lts/*'
cache: 'pnpm'
- name: Cache tool outputs
uses: actions/cache@v4
with:
path: |
.cache
.eslintcache
tsconfig.tsbuildinfo
.prettierCache
.knip-cache
key: lint-format-cache-${{ runner.os }}-${{ hashFiles('**/pnpm-lock.yaml') }}-${{ hashFiles('src/**/*.{ts,vue,js,mts}', '*.config.*', '.eslintrc.*', '.prettierrc.*', 'tsconfig.json') }}
restore-keys: |
lint-format-cache-${{ runner.os }}-${{ hashFiles('**/pnpm-lock.yaml') }}-
lint-format-cache-${{ runner.os }}-
ci-tools-cache-${{ runner.os }}-
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Run ESLint with auto-fix
run: pnpm lint:fix
- name: Run Stylelint with auto-fix
run: pnpm stylelint:fix
- name: Run Prettier with auto-format
run: pnpm format
@@ -77,6 +66,7 @@ jobs:
- name: Final validation
run: |
pnpm lint
pnpm stylelint
pnpm format:check
pnpm knip

View File

@@ -1,4 +1,5 @@
name: Devtools Python Check
name: "CI: Python Validation"
description: "Validates Python code in tools/devtools directory"
on:
pull_request:

52
.github/workflows/ci-size-data.yaml vendored Normal file
View File

@@ -0,0 +1,52 @@
name: "CI: Size Data"
on:
push:
branches:
- main
pull_request:
branches:
- main
permissions:
contents: read
jobs:
collect:
if: github.repository == 'Comfy-Org/ComfyUI_frontend'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- name: Install pnpm
uses: pnpm/action-setup@v4.1.0
with:
version: 10
- name: Install Node.js
uses: actions/setup-node@v5
with:
node-version: '24.x'
cache: pnpm
- name: Install dependencies
run: pnpm install
- name: Build project
run: pnpm build
- name: Collect size data
run: node scripts/size-collect.js
- name: Save PR number & base branch
if: ${{ github.event_name == 'pull_request' }}
run: |
echo ${{ github.event.number }} > ./temp/size/number.txt
echo ${{ github.base_ref }} > ./temp/size/base.txt
- name: Upload size data
uses: actions/upload-artifact@v4
with:
name: size-data
path: temp/size

View File

@@ -1,8 +1,9 @@
name: PR Playwright Deploy (Forks)
name: "CI: Tests E2E (Deploy for Forks)"
description: "Deploys test results from forked PRs (forks can't access deployment secrets)"
on:
workflow_run:
workflows: ["Tests CI"]
workflows: ["CI: Tests E2E"]
types: [requested, completed]
env:

View File

@@ -1,4 +1,5 @@
name: Tests CI
name: "CI: Tests E2E"
description: "End-to-end testing with Playwright across multiple browsers, deploys test reports to Cloudflare Pages"
on:
push:

View File

@@ -1,8 +1,9 @@
name: PR Storybook Deploy (Forks)
name: "CI: Tests Storybook (Deploy for Forks)"
description: "Deploys Storybook previews from forked PRs (forks can't access deployment secrets)"
on:
workflow_run:
workflows: ['Storybook and Chromatic CI']
workflows: ["CI: Tests Storybook"]
types: [requested, completed]
env:

View File

@@ -1,6 +1,5 @@
name: Storybook and Chromatic CI
# - [Automate Chromatic with GitHub Actions • Chromatic docs]( https://www.chromatic.com/docs/github-actions/ )
name: "CI: Tests Storybook"
description: "Builds Storybook and runs visual regression testing via Chromatic, deploys previews to Cloudflare Pages"
on:
workflow_dispatch: # Allow manual triggering
@@ -51,19 +50,6 @@ jobs:
node-version: '20'
cache: 'pnpm'
- name: Cache tool outputs
uses: actions/cache@v4
with:
path: |
.cache
storybook-static
tsconfig.tsbuildinfo
key: storybook-cache-${{ runner.os }}-${{ hashFiles('**/pnpm-lock.yaml') }}-${{ hashFiles('src/**/*.{ts,vue,js}', '*.config.*', '.storybook/**/*') }}
restore-keys: |
storybook-cache-${{ runner.os }}-${{ hashFiles('**/pnpm-lock.yaml') }}-
storybook-cache-${{ runner.os }}-
storybook-tools-cache-${{ runner.os }}-
- name: Install dependencies
run: pnpm install --frozen-lockfile
@@ -116,19 +102,6 @@ jobs:
node-version: '20'
cache: 'pnpm'
- name: Cache tool outputs
uses: actions/cache@v4
with:
path: |
.cache
storybook-static
tsconfig.tsbuildinfo
key: storybook-cache-${{ runner.os }}-${{ hashFiles('**/pnpm-lock.yaml') }}-${{ hashFiles('src/**/*.{ts,vue,js}', '*.config.*', '.storybook/**/*') }}
restore-keys: |
storybook-cache-${{ runner.os }}-${{ hashFiles('**/pnpm-lock.yaml') }}-
storybook-cache-${{ runner.os }}-
storybook-tools-cache-${{ runner.os }}-
- name: Install dependencies
run: pnpm install --frozen-lockfile

View File

@@ -1,4 +1,5 @@
name: Vitest Tests
name: "CI: Tests Unit"
description: "Unit and component testing with Vitest"
on:
push:
@@ -28,19 +29,6 @@ jobs:
node-version: "lts/*"
cache: "pnpm"
- name: Cache tool outputs
uses: actions/cache@v4
with:
path: |
.cache
coverage
.vitest-cache
key: vitest-cache-${{ runner.os }}-${{ hashFiles('**/pnpm-lock.yaml') }}-${{ hashFiles('src/**/*.{ts,vue,js}', 'vitest.config.*', 'tsconfig.json') }}
restore-keys: |
vitest-cache-${{ runner.os }}-${{ hashFiles('**/pnpm-lock.yaml') }}-
vitest-cache-${{ runner.os }}-
test-tools-cache-${{ runner.os }}-
- name: Install dependencies
run: pnpm install --frozen-lockfile

View File

@@ -1,4 +1,5 @@
name: Update Locales
name: "i18n: Update Core"
description: "Generates and updates translations for core ComfyUI components using OpenAI"
on:
# Manual dispatch for urgent translation updates
@@ -18,14 +19,14 @@ jobs:
uses: actions/checkout@v5
# Setup playwright environment
- name: Setup ComfyUI Server
uses: ./.github/actions/setup-comfyui-server
with:
launch_server: true
- name: Setup ComfyUI Frontend
uses: ./.github/actions/setup-frontend
with:
include_build_step: true
- name: Setup ComfyUI Server
uses: ./.github/actions/setup-comfyui-server
with:
launch_server: true
- name: Setup Playwright
uses: ./.github/actions/setup-playwright
@@ -54,5 +55,5 @@ jobs:
# Apply the stashed changes if any
git stash pop || true
git add src/locales/
git diff --staged --quiet || git commit -m "Update locales [skip ci]"
git diff --staged --quiet || git commit -m "Update locales"
git push origin HEAD:${{ github.head_ref }}

View File

@@ -1,4 +1,4 @@
name: Update Locales for given custom node repository
name: i18n Update Custom Nodes
on:
workflow_dispatch:

View File

@@ -1,4 +1,4 @@
name: Update Node Definitions Locales
name: i18n Update Nodes
on:
workflow_dispatch:

View File

@@ -1,4 +1,4 @@
name: Auto Backport
name: PR Backport
on:
pull_request_target:
@@ -69,70 +69,139 @@ jobs:
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
- name: Check if backports already exist
id: check-existing
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
PR_NUMBER: ${{ github.event_name == 'workflow_dispatch' && inputs.pr_number || github.event.pull_request.number }}
- name: Collect backport targets
id: targets
run: |
# Check for existing backport PRs for this PR number
EXISTING_BACKPORTS=$(gh pr list --state all --search "backport-${PR_NUMBER}-to" --json title,headRefName,baseRefName | jq -r '.[].headRefName')
if [ -z "$EXISTING_BACKPORTS" ]; then
echo "skip=false" >> $GITHUB_OUTPUT
exit 0
fi
# For manual triggers with force_rerun, proceed anyway
if [ "${{ github.event_name }}" = "workflow_dispatch" ] && [ "${{ inputs.force_rerun }}" = "true" ]; then
echo "skip=false" >> $GITHUB_OUTPUT
echo "::warning::Force rerun requested - existing backports will be updated"
exit 0
fi
echo "Found existing backport PRs:"
echo "$EXISTING_BACKPORTS"
echo "skip=true" >> $GITHUB_OUTPUT
echo "::warning::Backport PRs already exist for PR #${PR_NUMBER}, skipping to avoid duplicates"
TARGETS=()
declare -A SEEN=()
- name: Extract version labels
if: steps.check-existing.outputs.skip != 'true'
id: versions
run: |
# Extract version labels (e.g., "1.24", "1.22")
VERSIONS=""
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
# For manual triggers, get labels from the PR
LABELS=$(gh pr view ${{ inputs.pr_number }} --json labels | jq -r '.labels[].name')
else
# For automatic triggers, extract from PR event
LABELS='${{ toJSON(github.event.pull_request.labels) }}'
LABELS=$(echo "$LABELS" | jq -r '.[].name')
fi
for label in $LABELS; do
# Match version labels like "1.24" (major.minor only)
if [[ "$label" =~ ^[0-9]+\.[0-9]+$ ]]; then
# Validate the branch exists before adding to list
if git ls-remote --exit-code origin "core/${label}" >/dev/null 2>&1; then
VERSIONS="${VERSIONS}${label} "
else
echo "::warning::Label '${label}' found but branch 'core/${label}' does not exist"
fi
fi
done
if [ -z "$VERSIONS" ]; then
echo "::error::No version labels found (e.g., 1.24, 1.22)"
add_target() {
local label="$1"
local target="$2"
if [ -z "$target" ]; then
return
fi
target=$(echo "$target" | xargs)
if [ -z "$target" ] || [ -n "${SEEN[$target]}" ]; then
return
fi
if git ls-remote --exit-code origin "$target" >/dev/null 2>&1; then
TARGETS+=("$target")
SEEN["$target"]=1
else
echo "::warning::Label '${label}' references missing branch '${target}'"
fi
}
while IFS= read -r label; do
[ -z "$label" ] && continue
if [[ "$label" =~ ^branch:(.+)$ ]]; then
add_target "$label" "${BASH_REMATCH[1]}"
elif [[ "$label" =~ ^backport:(.+)$ ]]; then
add_target "$label" "${BASH_REMATCH[1]}"
elif [[ "$label" =~ ^core\/([0-9]+)\.([0-9]+)$ ]]; then
SAFE_MAJOR="${BASH_REMATCH[1]}"
SAFE_MINOR="${BASH_REMATCH[2]}"
add_target "$label" "core/${SAFE_MAJOR}.${SAFE_MINOR}"
elif [[ "$label" =~ ^cloud\/([0-9]+)\.([0-9]+)$ ]]; then
SAFE_MAJOR="${BASH_REMATCH[1]}"
SAFE_MINOR="${BASH_REMATCH[2]}"
add_target "$label" "cloud/${SAFE_MAJOR}.${SAFE_MINOR}"
elif [[ "$label" =~ ^[0-9]+\.[0-9]+$ ]]; then
add_target "$label" "core/${label}"
fi
done <<< "$LABELS"
if [ "${#TARGETS[@]}" -eq 0 ]; then
echo "::error::No backport targets found (use labels like '1.24' or 'branch:release/hotfix')"
exit 1
fi
echo "versions=${VERSIONS}" >> $GITHUB_OUTPUT
echo "Found version labels: ${VERSIONS}"
echo "targets=${TARGETS[*]}" >> $GITHUB_OUTPUT
echo "Found backport targets: ${TARGETS[*]}"
- name: Filter already backported targets
id: filter-targets
env:
EVENT_NAME: ${{ github.event_name }}
FORCE_RERUN_INPUT: >-
${{ github.event_name == 'workflow_dispatch' && inputs.force_rerun
|| 'false' }}
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
PR_NUMBER: >-
${{ github.event_name == 'workflow_dispatch' && inputs.pr_number
|| github.event.pull_request.number }}
run: |
set -euo pipefail
REQUESTED_TARGETS="${{ steps.targets.outputs.targets }}"
if [ -z "$REQUESTED_TARGETS" ]; then
echo "skip=true" >> $GITHUB_OUTPUT
echo "pending-targets=" >> $GITHUB_OUTPUT
exit 0
fi
FORCE_RERUN=false
if [ "$EVENT_NAME" = "workflow_dispatch" ] && [ "$FORCE_RERUN_INPUT" = "true" ]; then
FORCE_RERUN=true
fi
mapfile -t EXISTING_BRANCHES < <(
git ls-remote --heads origin "backport-${PR_NUMBER}-to-*" || true
)
PENDING=()
SKIPPED=()
for target in $REQUESTED_TARGETS; do
SAFE_TARGET=$(echo "$target" | tr '/' '-')
BACKPORT_BRANCH="backport-${PR_NUMBER}-to-${SAFE_TARGET}"
if [ "$FORCE_RERUN" = true ]; then
PENDING+=("$target")
continue
fi
if printf '%s\n' "${EXISTING_BRANCHES[@]:-}" |
grep -Fq "refs/heads/${BACKPORT_BRANCH}"; then
SKIPPED+=("$target")
else
PENDING+=("$target")
fi
done
SKIPPED_JOINED="${SKIPPED[*]:-}"
PENDING_JOINED="${PENDING[*]:-}"
echo "already-exists=${SKIPPED_JOINED}" >> $GITHUB_OUTPUT
echo "pending-targets=${PENDING_JOINED}" >> $GITHUB_OUTPUT
if [ -z "$PENDING_JOINED" ]; then
echo "skip=true" >> $GITHUB_OUTPUT
if [ -n "$SKIPPED_JOINED" ]; then
echo "::warning::Backport branches already exist for: ${SKIPPED_JOINED}"
fi
else
echo "skip=false" >> $GITHUB_OUTPUT
if [ -n "$SKIPPED_JOINED" ]; then
echo "::notice::Skipping already backported targets: ${SKIPPED_JOINED}"
fi
fi
- name: Backport commits
if: steps.check-existing.outputs.skip != 'true'
if: steps.filter-targets.outputs.skip != 'true'
id: backport
env:
PR_NUMBER: ${{ github.event_name == 'workflow_dispatch' && inputs.pr_number || github.event.pull_request.number }}
@@ -150,16 +219,25 @@ jobs:
MERGE_COMMIT="${{ github.event.pull_request.merge_commit_sha }}"
fi
for version in ${{ steps.versions.outputs.versions }}; do
echo "::group::Backporting to core/${version}"
for target in ${{ steps.filter-targets.outputs.pending-targets }}; do
TARGET_BRANCH="${target}"
SAFE_TARGET=$(echo "$TARGET_BRANCH" | tr '/' '-')
BACKPORT_BRANCH="backport-${PR_NUMBER}-to-${SAFE_TARGET}"
TARGET_BRANCH="core/${version}"
BACKPORT_BRANCH="backport-${PR_NUMBER}-to-${version}"
echo "::group::Backporting to ${TARGET_BRANCH}"
# Fetch target branch (fail if doesn't exist)
if ! git fetch origin "${TARGET_BRANCH}"; then
echo "::error::Target branch ${TARGET_BRANCH} does not exist"
FAILED="${FAILED}${version}:branch-missing "
FAILED="${FAILED}${TARGET_BRANCH}:branch-missing "
echo "::endgroup::"
continue
fi
# Check if commit already exists on target branch
if git branch -r --contains "${MERGE_COMMIT}" | grep -q "origin/${TARGET_BRANCH}"; then
echo "::notice::Commit ${MERGE_COMMIT} already exists on ${TARGET_BRANCH}, skipping backport"
FAILED="${FAILED}${TARGET_BRANCH}:already-exists "
echo "::endgroup::"
continue
fi
@@ -170,7 +248,7 @@ jobs:
# Try cherry-pick
if git cherry-pick "${MERGE_COMMIT}"; then
git push origin "${BACKPORT_BRANCH}"
SUCCESS="${SUCCESS}${version}:${BACKPORT_BRANCH} "
SUCCESS="${SUCCESS}${TARGET_BRANCH}:${BACKPORT_BRANCH} "
echo "Successfully created backport branch: ${BACKPORT_BRANCH}"
# Return to main (keep the branch, we need it for PR)
git checkout main
@@ -180,7 +258,7 @@ jobs:
git cherry-pick --abort
echo "::error::Cherry-pick failed due to conflicts"
FAILED="${FAILED}${version}:conflicts:${CONFLICTS} "
FAILED="${FAILED}${TARGET_BRANCH}:conflicts:${CONFLICTS} "
# Clean up the failed branch
git checkout main
@@ -198,7 +276,7 @@ jobs:
fi
- name: Create PR for each successful backport
if: steps.check-existing.outputs.skip != 'true' && steps.backport.outputs.success
if: steps.filter-targets.outputs.skip != 'true' && steps.backport.outputs.success
env:
GH_TOKEN: ${{ secrets.PR_GH_TOKEN }}
PR_NUMBER: ${{ github.event_name == 'workflow_dispatch' && inputs.pr_number || github.event.pull_request.number }}
@@ -214,13 +292,13 @@ jobs:
fi
for backport in ${{ steps.backport.outputs.success }}; do
IFS=':' read -r version branch <<< "${backport}"
IFS=':' read -r target branch <<< "${backport}"
if PR_URL=$(gh pr create \
--base "core/${version}" \
--base "${target}" \
--head "${branch}" \
--title "[backport ${version}] ${PR_TITLE}" \
--body "Backport of #${PR_NUMBER} to \`core/${version}\`"$'\n\n'"Automatically created by backport workflow." \
--title "[backport ${target}] ${PR_TITLE}" \
--body "Backport of #${PR_NUMBER} to \`${target}\`"$'\n\n'"Automatically created by backport workflow." \
--label "backport" 2>&1); then
# Extract PR number from URL
@@ -230,14 +308,14 @@ jobs:
gh pr comment "${PR_NUMBER}" --body "@${PR_AUTHOR} Successfully backported to #${PR_NUM}"
fi
else
echo "::error::Failed to create PR for ${version}: ${PR_URL}"
echo "::error::Failed to create PR for ${target}: ${PR_URL}"
# Still try to comment on the original PR about the failure
gh pr comment "${PR_NUMBER}" --body "@${PR_AUTHOR} Backport branch created but PR creation failed for \`core/${version}\`. Please create the PR manually from branch \`${branch}\`"
gh pr comment "${PR_NUMBER}" --body "@${PR_AUTHOR} Backport branch created but PR creation failed for \`${target}\`. Please create the PR manually from branch \`${branch}\`"
fi
done
- name: Comment on failures
if: steps.check-existing.outputs.skip != 'true' && failure() && steps.backport.outputs.failed
if: steps.filter-targets.outputs.skip != 'true' && failure() && steps.backport.outputs.failed
env:
GH_TOKEN: ${{ github.token }}
run: |
@@ -253,16 +331,25 @@ jobs:
fi
for failure in ${{ steps.backport.outputs.failed }}; do
IFS=':' read -r version reason conflicts <<< "${failure}"
IFS=':' read -r target reason conflicts <<< "${failure}"
if [ "${reason}" = "branch-missing" ]; then
gh pr comment "${PR_NUMBER}" --body "@${PR_AUTHOR} Backport failed: Branch \`core/${version}\` does not exist"
gh pr comment "${PR_NUMBER}" --body "@${PR_AUTHOR} Backport failed: Branch \`${target}\` does not exist"
elif [ "${reason}" = "already-exists" ]; then
gh pr comment "${PR_NUMBER}" --body "@${PR_AUTHOR} Commit \`${MERGE_COMMIT}\` already exists on branch \`${target}\`. No backport needed."
elif [ "${reason}" = "conflicts" ]; then
# Convert comma-separated conflicts back to newlines for display
CONFLICTS_LIST=$(echo "${conflicts}" | tr ',' '\n' | sed 's/^/- /')
COMMENT_BODY="@${PR_AUTHOR} Backport to \`core/${version}\` failed: Merge conflicts detected."$'\n\n'"Please manually cherry-pick commit \`${MERGE_COMMIT}\` to the \`core/${version}\` branch."$'\n\n'"<details><summary>Conflicting files</summary>"$'\n\n'"${CONFLICTS_LIST}"$'\n\n'"</details>"
COMMENT_BODY="@${PR_AUTHOR} Backport to \`${target}\` failed: Merge conflicts detected."$'\n\n'"Please manually cherry-pick commit \`${MERGE_COMMIT}\` to the \`${target}\` branch."$'\n\n'"<details><summary>Conflicting files</summary>"$'\n\n'"${CONFLICTS_LIST}"$'\n\n'"</details>"
gh pr comment "${PR_NUMBER}" --body "${COMMENT_BODY}"
fi
done
- name: Remove needs-backport label
if: steps.filter-targets.outputs.skip != 'true' && success()
run: gh pr edit ${{ github.event_name == 'workflow_dispatch' && inputs.pr_number || github.event.pull_request.number }} --remove-label "needs-backport"
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View File

@@ -1,4 +1,5 @@
name: Claude PR Review
name: "PR: Claude Review"
description: "AI-powered code review triggered by adding the 'claude-review' label to a PR"
permissions:
contents: read
@@ -27,6 +28,7 @@ jobs:
with:
ref: ${{ github.event.pull_request.head.sha }}
check-regexp: '^(lint-and-format|test|playwright-tests)'
allowed-conclusions: success,skipped,failure,cancelled,neutral,action_required,timed_out,stale
wait-interval: 30
repo-token: ${{ secrets.GITHUB_TOKEN }}

104
.github/workflows/pr-size-report.yaml vendored Normal file
View File

@@ -0,0 +1,104 @@
name: "PR: Size Report"
on:
workflow_run:
workflows: ['CI: Size Data']
types:
- completed
workflow_dispatch:
inputs:
pr_number:
description: 'PR number to report on'
required: true
type: number
run_id:
description: 'Size data workflow run ID'
required: true
type: string
permissions:
contents: read
pull-requests: write
issues: write
jobs:
comment:
runs-on: ubuntu-latest
if: >
github.repository == 'Comfy-Org/ComfyUI_frontend' &&
(
(github.event_name == 'workflow_run' &&
github.event.workflow_run.event == 'pull_request' &&
github.event.workflow_run.conclusion == 'success') ||
github.event_name == 'workflow_dispatch'
)
steps:
- uses: actions/checkout@v5
- name: Install pnpm
uses: pnpm/action-setup@v4.1.0
with:
version: 10
- name: Install Node.js
uses: actions/setup-node@v5
with:
node-version: '24.x'
cache: pnpm
- name: Install dependencies
run: pnpm install
- name: Download size data
uses: dawidd6/action-download-artifact@v11
with:
name: size-data
run_id: ${{ github.event_name == 'workflow_dispatch' && inputs.run_id || github.event.workflow_run.id }}
path: temp/size
- name: Set PR number
id: pr-number
run: |
if [ "${{ github.event_name }}" == "workflow_dispatch" ]; then
echo "content=${{ inputs.pr_number }}" >> $GITHUB_OUTPUT
else
echo "content=$(cat temp/size/number.txt)" >> $GITHUB_OUTPUT
fi
- name: Set base branch
id: pr-base
run: |
if [ "${{ github.event_name }}" == "workflow_dispatch" ]; then
echo "content=main" >> $GITHUB_OUTPUT
else
echo "content=$(cat temp/size/base.txt)" >> $GITHUB_OUTPUT
fi
- name: Download previous size data
uses: dawidd6/action-download-artifact@v11
with:
branch: ${{ steps.pr-base.outputs.content }}
workflow: ci-size-data.yaml
event: push
name: size-data
path: temp/size-prev
if_no_artifact_found: warn
- name: Generate size report
run: node scripts/size-report.js > size-report.md
- name: Read size report
id: size-report
uses: juliangruber/read-file-action@v1
with:
path: ./size-report.md
- name: Create or update PR comment
uses: actions-cool/maintain-one-comment@v3
with:
token: ${{ secrets.GITHUB_TOKEN }}
number: ${{ steps.pr-number.outputs.content }}
body: |
${{ steps.size-report.outputs.content }}
<!-- COMFYUI_FRONTEND_SIZE -->
body-include: '<!-- COMFYUI_FRONTEND_SIZE -->'

View File

@@ -0,0 +1,309 @@
# Setting test expectation screenshots for Playwright
name: "PR: Update Playwright Expectations"
on:
pull_request:
types: [labeled]
issue_comment:
types: [created]
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
setup:
runs-on: ubuntu-latest
if: >
( github.event_name == 'pull_request' && github.event.label.name == 'New Browser Test Expectations' ) ||
( github.event.issue.pull_request &&
github.event_name == 'issue_comment' &&
(
github.event.comment.author_association == 'OWNER' ||
github.event.comment.author_association == 'MEMBER' ||
github.event.comment.author_association == 'COLLABORATOR'
) &&
startsWith(github.event.comment.body, '/update-playwright') )
outputs:
cache-key: ${{ steps.cache-key.outputs.key }}
pr-number: ${{ steps.pr-info.outputs.pr-number }}
branch: ${{ steps.pr-info.outputs.branch }}
comment-id: ${{ steps.find-update-comment.outputs.comment-id }}
steps:
- name: Get PR info
id: pr-info
run: |
echo "pr-number=${{ github.event.number || github.event.issue.number }}" >> $GITHUB_OUTPUT
echo "branch=$(gh pr view ${{ github.event.number || github.event.issue.number }} --repo ${{ github.repository }} --json headRefName --jq '.headRefName')" >> $GITHUB_OUTPUT
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Find Update Comment
uses: peter-evans/find-comment@b30e6a3c0ed37e7c023ccd3f1db5c6c0b0c23aad
id: "find-update-comment"
with:
issue-number: ${{ steps.pr-info.outputs.pr-number }}
comment-author: "github-actions[bot]"
body-includes: "Updating Playwright Expectations"
- name: Add Starting Reaction
uses: peter-evans/create-or-update-comment@e8674b075228eee787fea43ef493e45ece1004c9
with:
comment-id: ${{ steps.find-update-comment.outputs.comment-id }}
issue-number: ${{ steps.pr-info.outputs.pr-number }}
body: |
Updating Playwright Expectations
edit-mode: replace
reactions: eyes
- name: Checkout repository
uses: actions/checkout@v5
with:
ref: ${{ steps.pr-info.outputs.branch }}
- name: Setup frontend
uses: ./.github/actions/setup-frontend
with:
include_build_step: true
# Save expensive build artifacts (Python env, built frontend, node_modules)
# Source code will be checked out fresh in sharded jobs
- name: Generate cache key
id: cache-key
run: echo "key=$(date +%s)" >> $GITHUB_OUTPUT
- name: Save cache
uses: actions/cache/save@5a3ec84eff668545956fd18022155c47e93e2684
with:
path: |
ComfyUI
dist
key: comfyui-setup-${{ steps.cache-key.outputs.key }}
# Sharded snapshot updates
update-snapshots-sharded:
needs: setup
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
shardIndex: [1, 2, 3, 4]
shardTotal: [4]
steps:
# Checkout source code fresh (not cached)
- name: Checkout repository
uses: actions/checkout@v5
with:
ref: ${{ needs.setup.outputs.branch }}
# Restore expensive build artifacts from setup job
- name: Restore cached artifacts
uses: actions/cache/restore@5a3ec84eff668545956fd18022155c47e93e2684
with:
fail-on-cache-miss: true
path: |
ComfyUI
dist
key: comfyui-setup-${{ needs.setup.outputs.cache-key }}
- name: Setup ComfyUI server (from cache)
uses: ./.github/actions/setup-comfyui-server
with:
launch_server: true
- name: Setup nodejs, pnpm, reuse built frontend
uses: ./.github/actions/setup-frontend
- name: Setup Playwright
uses: ./.github/actions/setup-playwright
# Run sharded tests with snapshot updates
- name: Update snapshots (Shard ${{ matrix.shardIndex }}/${{ matrix.shardTotal }})
id: playwright-tests
run: pnpm exec playwright test --update-snapshots --shard=${{ matrix.shardIndex }}/${{ matrix.shardTotal }}
continue-on-error: true
# Identify and stage only changed snapshot files
- name: Stage changed snapshot files
id: changed-snapshots
run: |
echo "=========================================="
echo "STAGING CHANGED SNAPSHOTS (Shard ${{ matrix.shardIndex }})"
echo "=========================================="
# Get list of changed snapshot files
changed_files=$(git diff --name-only browser_tests/ 2>/dev/null | grep -E '\-snapshots/' || echo "")
if [ -z "$changed_files" ]; then
echo "No snapshot changes in this shard"
echo "has-changes=false" >> $GITHUB_OUTPUT
exit 0
fi
echo "✓ Found changed files:"
echo "$changed_files"
file_count=$(echo "$changed_files" | wc -l)
echo "Count: $file_count"
echo "has-changes=true" >> $GITHUB_OUTPUT
echo ""
# Create staging directory
mkdir -p /tmp/changed_snapshots_shard
# Copy only changed files, preserving directory structure
# Strip 'browser_tests/' prefix to avoid double nesting
echo "Copying changed files to staging directory..."
while IFS= read -r file; do
# Remove 'browser_tests/' prefix
file_without_prefix="${file#browser_tests/}"
# Create parent directories
mkdir -p "/tmp/changed_snapshots_shard/$(dirname "$file_without_prefix")"
# Copy file
cp "$file" "/tmp/changed_snapshots_shard/$file_without_prefix"
echo " → $file_without_prefix"
done <<< "$changed_files"
echo ""
echo "Staged files for upload:"
find /tmp/changed_snapshots_shard -type f
# Upload ONLY the changed files from this shard
- name: Upload changed snapshots
uses: actions/upload-artifact@v4
if: steps.changed-snapshots.outputs.has-changes == 'true'
with:
name: snapshots-shard-${{ matrix.shardIndex }}
path: /tmp/changed_snapshots_shard/
retention-days: 1
- name: Upload test report
uses: actions/upload-artifact@v4
if: always()
with:
name: playwright-report-shard-${{ matrix.shardIndex }}
path: ./playwright-report/
retention-days: 30
# Merge snapshots and commit
merge-and-commit:
needs: [setup, update-snapshots-sharded]
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v5
with:
ref: ${{ needs.setup.outputs.branch }}
# Download all changed snapshot files from shards
- name: Download snapshot artifacts
uses: actions/download-artifact@v4
with:
pattern: snapshots-shard-*
path: ./downloaded-snapshots
merge-multiple: false
- name: List downloaded files
run: |
echo "=========================================="
echo "DOWNLOADED SNAPSHOT FILES"
echo "=========================================="
find ./downloaded-snapshots -type f
echo ""
echo "Total files: $(find ./downloaded-snapshots -type f | wc -l)"
# Merge only the changed files into browser_tests
- name: Merge changed snapshots
run: |
set -euo pipefail
echo "=========================================="
echo "MERGING CHANGED SNAPSHOTS"
echo "=========================================="
# Verify target directory exists
if [ ! -d "browser_tests" ]; then
echo "::error::Target directory 'browser_tests' does not exist"
exit 1
fi
merged_count=0
# For each shard's changed files, copy them directly
for shard_dir in ./downloaded-snapshots/snapshots-shard-*/; do
if [ ! -d "$shard_dir" ]; then
continue
fi
shard_name=$(basename "$shard_dir")
file_count=$(find "$shard_dir" -type f | wc -l)
if [ "$file_count" -eq 0 ]; then
echo " $shard_name: no files"
continue
fi
echo "Processing $shard_name ($file_count file(s))..."
# Copy files directly, preserving directory structure
# Since files are already in correct structure (no browser_tests/ prefix), just copy them all
cp -v -r "$shard_dir"* browser_tests/ 2>&1 | sed 's/^/ /'
merged_count=$((merged_count + 1))
echo " ✓ Merged"
echo ""
done
echo "=========================================="
echo "MERGE COMPLETE"
echo "=========================================="
echo "Shards merged: $merged_count"
- name: Show changes
run: |
echo "=========================================="
echo "CHANGES SUMMARY"
echo "=========================================="
echo ""
echo "Changed files in browser_tests:"
git diff --name-only browser_tests/ | head -20 || echo "No changes"
echo ""
echo "Total changes:"
git diff --name-only browser_tests/ | wc -l || echo "0"
- name: Commit updated expectations
id: commit
run: |
git config --global user.name 'github-actions'
git config --global user.email 'github-actions@github.com'
if git diff --quiet browser_tests/; then
echo "No changes to commit"
echo "has-changes=false" >> $GITHUB_OUTPUT
exit 0
fi
echo "=========================================="
echo "COMMITTING CHANGES"
echo "=========================================="
echo "has-changes=true" >> $GITHUB_OUTPUT
git add browser_tests/
git commit -m "[automated] Update test expectations"
echo "Pushing to ${{ needs.setup.outputs.branch }}..."
git push origin ${{ needs.setup.outputs.branch }}
echo "✓ Commit and push successful"
- name: Add Done Reaction
uses: peter-evans/create-or-update-comment@e8674b075228eee787fea43ef493e45ece1004c9
if: github.event_name == 'issue_comment' && steps.commit.outputs.has-changes == 'true'
with:
comment-id: ${{ needs.setup.outputs.comment-id }}
issue-number: ${{ needs.setup.outputs.pr-number }}
reactions: +1
reactions-edit-mode: replace
- name: Remove New Browser Test Expectations label
if: always() && github.event_name == 'pull_request'
run: gh pr edit ${{ needs.setup.outputs.pr-number }} --remove-label "New Browser Test Expectations"
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View File

@@ -44,6 +44,7 @@ jobs:
contents: read
env:
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: '1'
ENABLE_MINIFY: 'true'
steps:
- name: Validate inputs
env:
@@ -160,20 +161,6 @@ jobs:
echo "publish_dir=$PUBLISH_DIR" >> "$GITHUB_OUTPUT"
echo "name=$NAME" >> "$GITHUB_OUTPUT"
- name: Pack (preview only)
shell: bash
working-directory: ${{ steps.pkg.outputs.publish_dir }}
run: |
set -euo pipefail
npm pack --json | tee pack-result.json
- name: Upload package tarball artifact
uses: actions/upload-artifact@v4
with:
name: desktop-ui-npm-tarball-${{ inputs.version }}
path: ${{ steps.pkg.outputs.publish_dir }}/*.tgz
if-no-files-found: error
- name: Check if version already on npm
id: check_npm
env:

View File

@@ -1,4 +1,4 @@
name: Create Release Branch
name: Release Branch Create
on:
pull_request:
@@ -69,6 +69,9 @@ jobs:
echo "prev_version=$PREV_VERSION" >> $GITHUB_OUTPUT
echo "prev_minor=$PREV_MINOR" >> $GITHUB_OUTPUT
BASE_COMMIT=$(git rev-parse HEAD)
echo "base_commit=$BASE_COMMIT" >> $GITHUB_OUTPUT
# Get previous major version for comparison
PREV_MAJOR=$(echo $PREV_VERSION | cut -d. -f1)
@@ -87,13 +90,13 @@ jobs:
elif [[ "$MAJOR" -gt "$PREV_MAJOR" && "$MINOR" == "0" && "$PATCH" == "0" ]]; then
# Major version bump (e.g., 1.99.x → 2.0.0)
echo "is_minor_bump=true" >> $GITHUB_OUTPUT
BRANCH_NAME="core/${PREV_MAJOR}.${PREV_MINOR}"
echo "branch_name=$BRANCH_NAME" >> $GITHUB_OUTPUT
BRANCH_BASE="${PREV_MAJOR}.${PREV_MINOR}"
echo "branch_base=$BRANCH_BASE" >> $GITHUB_OUTPUT
elif [[ "$MAJOR" == "$PREV_MAJOR" && "$MINOR" -gt "$PREV_MINOR" && "$PATCH" == "0" ]]; then
# Minor version bump (e.g., 1.23.x → 1.24.0)
echo "is_minor_bump=true" >> $GITHUB_OUTPUT
BRANCH_NAME="core/${MAJOR}.${PREV_MINOR}"
echo "branch_name=$BRANCH_NAME" >> $GITHUB_OUTPUT
BRANCH_BASE="${MAJOR}.${PREV_MINOR}"
echo "branch_base=$BRANCH_BASE" >> $GITHUB_OUTPUT
else
echo "is_minor_bump=false" >> $GITHUB_OUTPUT
fi
@@ -101,64 +104,97 @@ jobs:
# Return to main branch
git checkout main
- name: Create release branch
- name: Create release branches
id: create_branches
if: steps.check_version.outputs.is_minor_bump == 'true'
run: |
BRANCH_NAME="${{ steps.check_version.outputs.branch_name }}"
BRANCH_BASE="${{ steps.check_version.outputs.branch_base }}"
PREV_VERSION="${{ steps.check_version.outputs.prev_version }}"
# Check if branch already exists
if git ls-remote --heads origin "$BRANCH_NAME" | grep -q "$BRANCH_NAME"; then
echo "⚠️ Branch $BRANCH_NAME already exists, skipping creation"
echo "branch_exists=true" >> $GITHUB_ENV
exit 0
else
echo "branch_exists=false" >> $GITHUB_ENV
if [[ -z "$BRANCH_BASE" ]]; then
echo "::error::Branch base not set; unable to determine release branches"
exit 1
fi
# Create branch from the commit BEFORE the version bump
# This ensures the release branch has the previous minor version
git checkout -b "$BRANCH_NAME" HEAD^1
BASE_COMMIT="${{ steps.check_version.outputs.base_commit }}"
# Push the new branch
git push origin "$BRANCH_NAME"
if [[ -z "$BASE_COMMIT" ]]; then
echo "::error::Base commit not provided; cannot create release branches"
exit 1
fi
echo "✅ Created release branch: $BRANCH_NAME"
echo "This branch is now in feature freeze and will only receive:"
echo "- Bug fixes"
echo "- Critical security patches"
echo "- Documentation updates"
RESULTS_FILE=$(mktemp)
trap 'rm -f "$RESULTS_FILE"' EXIT
for PREFIX in core cloud; do
BRANCH_NAME="${PREFIX}/${BRANCH_BASE}"
if git ls-remote --exit-code --heads origin \
"$BRANCH_NAME" >/dev/null 2>&1; then
echo "⚠️ Branch $BRANCH_NAME already exists"
echo " Skipping creation for $BRANCH_NAME"
STATUS="exists"
else
# Create branch from the commit BEFORE the version bump
if ! git push origin "$BASE_COMMIT:refs/heads/$BRANCH_NAME"; then
echo "::error::Failed to push release branch $BRANCH_NAME"
exit 1
fi
echo "✅ Created release branch: $BRANCH_NAME"
STATUS="created"
fi
echo "$BRANCH_NAME|$STATUS|$PREV_VERSION" >> "$RESULTS_FILE"
done
{
echo "results<<'EOF'"
cat "$RESULTS_FILE"
echo "EOF"
} >> $GITHUB_OUTPUT
- name: Post summary
if: steps.check_version.outputs.is_minor_bump == 'true'
run: |
BRANCH_NAME="${{ steps.check_version.outputs.branch_name }}"
PREV_VERSION="${{ steps.check_version.outputs.prev_version }}"
CURRENT_VERSION="${{ steps.check_version.outputs.current_version }}"
RESULTS="${{ steps.create_branches.outputs.results }}"
if [[ "${{ env.branch_exists }}" == "true" ]]; then
if [[ -z "$RESULTS" ]]; then
cat >> $GITHUB_STEP_SUMMARY << EOF
## 🌿 Release Branch Already Exists
## 🌿 Release Branch Summary
The release branch for the previous minor version already exists:
EOF
else
cat >> $GITHUB_STEP_SUMMARY << EOF
## 🌿 Release Branch Created
A new release branch has been created for the previous minor version:
Release branch creation skipped; no eligible branches were found.
EOF
exit 0
fi
cat >> $GITHUB_STEP_SUMMARY << EOF
## 🌿 Release Branch Summary
- **Branch**: \`$BRANCH_NAME\`
- **Version**: \`$PREV_VERSION\` (feature frozen)
- **Main branch**: \`$CURRENT_VERSION\` (active development)
### Branch Status
EOF
while IFS='|' read -r BRANCH STATUS PREV_VERSION; do
if [[ "$STATUS" == "created" ]]; then
cat >> $GITHUB_STEP_SUMMARY << EOF
- \`$BRANCH\` created from version \`$PREV_VERSION\`
EOF
else
cat >> $GITHUB_STEP_SUMMARY << EOF
- \`$BRANCH\` already existed (based on version \`$PREV_VERSION\`)
EOF
fi
done <<< "$RESULTS"
cat >> $GITHUB_STEP_SUMMARY << EOF
### Branch Policy
The \`$BRANCH_NAME\` branch is now in **feature freeze** and will only accept:
Release branches are feature-frozen and only accept:
- 🐛 Bug fixes
- 🔒 Security patches
- 📚 Documentation updates
@@ -167,9 +203,9 @@ jobs:
### Backporting Changes
To backport a fix to this release branch:
To backport a fix:
1. Create your fix on \`main\` first
2. Cherry-pick to \`$BRANCH_NAME\`
3. Create a PR targeting \`$BRANCH_NAME\`
4. Use the \`Release\` label on the PR
2. Cherry-pick to the target release branch
3. Create a PR targeting that branch
4. Apply the matching \`core/x.y\` or \`cloud/x.y\` label
EOF

View File

@@ -1,4 +1,4 @@
name: Create Release Draft
name: Release Draft Create
on:
pull_request:
@@ -28,16 +28,6 @@ jobs:
node-version: 'lts/*'
cache: 'pnpm'
- name: Cache tool outputs
uses: actions/cache@v4
with:
path: |
.cache
tsconfig.tsbuildinfo
key: release-tools-cache-${{ runner.os }}-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
release-tools-cache-${{ runner.os }}-
- name: Get current version
id: current_version
run: echo "version=$(node -p "require('./package.json').version")" >> $GITHUB_OUTPUT
@@ -55,6 +45,7 @@ jobs:
SENTRY_DSN: ${{ secrets.SENTRY_DSN }}
ALGOLIA_APP_ID: ${{ secrets.ALGOLIA_APP_ID }}
ALGOLIA_API_KEY: ${{ secrets.ALGOLIA_API_KEY }}
ENABLE_MINIFY: 'true'
USE_PROD_CONFIG: 'true'
run: |
pnpm install --frozen-lockfile
@@ -126,7 +117,7 @@ jobs:
publish_types:
needs: build
uses: ./.github/workflows/publish-frontend-types.yaml
uses: ./.github/workflows/release-npm-types.yaml
with:
version: ${{ needs.build.outputs.version }}
ref: ${{ github.event.pull_request.merge_commit_sha }}

View File

@@ -1,4 +1,4 @@
name: Publish Frontend Types
name: Release NPM Types
on:
workflow_dispatch:

View File

@@ -1,4 +1,4 @@
name: Create Dev PyPI Package
name: Release PyPI Dev
on:
workflow_dispatch:
@@ -25,17 +25,6 @@ jobs:
node-version: 'lts/*'
cache: 'pnpm'
- name: Cache tool outputs
uses: actions/cache@v4
with:
path: |
.cache
dist
tsconfig.tsbuildinfo
key: dev-release-tools-cache-${{ runner.os }}-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
dev-release-tools-cache-${{ runner.os }}-
- name: Get current version
id: current_version
run: echo "version=$(node -p "require('./package.json').version")" >> $GITHUB_OUTPUT
@@ -44,6 +33,7 @@ jobs:
SENTRY_DSN: ${{ secrets.SENTRY_DSN }}
ALGOLIA_APP_ID: ${{ secrets.ALGOLIA_APP_ID }}
ALGOLIA_API_KEY: ${{ secrets.ALGOLIA_API_KEY }}
ENABLE_MINIFY: 'true'
USE_PROD_CONFIG: 'true'
run: |
pnpm install --frozen-lockfile

View File

@@ -1,4 +1,5 @@
name: Version Bump
name: "Release: Version Bump"
description: "Manual workflow to increment package version with semantic versioning support"
on:
workflow_dispatch:
@@ -14,6 +15,11 @@ on:
required: false
default: ''
type: string
branch:
description: 'Base branch to bump (e.g., main, core/1.29, core/1.30)'
required: true
default: 'main'
type: string
jobs:
bump-version:
@@ -25,6 +31,24 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v5
with:
ref: ${{ github.event.inputs.branch }}
fetch-depth: 0
- name: Validate branch exists
run: |
BRANCH="${{ github.event.inputs.branch }}"
if ! git show-ref --verify --quiet "refs/heads/$BRANCH" && ! git show-ref --verify --quiet "refs/remotes/origin/$BRANCH"; then
echo "❌ Branch '$BRANCH' does not exist"
echo ""
echo "Available core branches:"
git branch -r | grep 'origin/core/' | sed 's/.*origin\// - /' || echo " (none found)"
echo ""
echo "Main branch:"
echo " - main"
exit 1
fi
echo "✅ Branch '$BRANCH' exists"
- name: Install pnpm
uses: pnpm/action-setup@v4
@@ -35,7 +59,6 @@ jobs:
uses: actions/setup-node@v4
with:
node-version: lts/*
cache: 'pnpm'
- name: Bump version
id: bump-version
@@ -58,7 +81,9 @@ jobs:
title: ${{ steps.bump-version.outputs.NEW_VERSION }}
body: |
${{ steps.capitalised.outputs.capitalised }} version increment to ${{ steps.bump-version.outputs.NEW_VERSION }}
**Base branch:** `${{ github.event.inputs.branch }}`
branch: version-bump-${{ steps.bump-version.outputs.NEW_VERSION }}
base: main
base: ${{ github.event.inputs.branch }}
labels: |
Release

View File

@@ -1,108 +0,0 @@
# Setting test expectation screenshots for Playwright
name: Update Playwright Expectations
on:
pull_request:
types: [labeled]
issue_comment:
types: [created]
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
test:
runs-on: ubuntu-latest
if: >
( github.event_name == 'pull_request' && github.event.label.name == 'New Browser Test Expectations' ) ||
( github.event.issue.pull_request &&
github.event_name == 'issue_comment' &&
(
github.event.comment.author_association == 'OWNER' ||
github.event.comment.author_association == 'MEMBER' ||
github.event.comment.author_association == 'COLLABORATOR'
) &&
startsWith(github.event.comment.body, '/update-playwright') )
steps:
- name: Find Update Comment
uses: peter-evans/find-comment@b30e6a3c0ed37e7c023ccd3f1db5c6c0b0c23aad
id: "find-update-comment"
with:
issue-number: ${{ github.event.number || github.event.issue.number }}
comment-author: "github-actions[bot]"
body-includes: "Updating Playwright Expectations"
- name: Add Starting Reaction
uses: peter-evans/create-or-update-comment@e8674b075228eee787fea43ef493e45ece1004c9
with:
comment-id: ${{ steps.find-update-comment.outputs.comment-id }}
issue-number: ${{ github.event.number || github.event.issue.number }}
body: |
Updating Playwright Expectations
edit-mode: replace
reactions: eyes
- name: Get Branch SHA
id: "get-branch"
run: echo ::set-output name=branch::$(gh pr view $PR_NO --repo $REPO --json headRefName --jq '.headRefName')
env:
REPO: ${{ github.repository }}
PR_NO: ${{ github.event.number || github.event.issue.number }}
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Initial Checkout
uses: actions/checkout@v5
with:
ref: ${{ steps.get-branch.outputs.branch }}
- name: Setup Frontend
uses: ./.github/actions/setup-frontend
with:
include_build_step: true
- name: Setup ComfyUI Server
uses: ./.github/actions/setup-comfyui-server
with:
launch_server: true
- name: Setup Playwright
uses: ./.github/actions/setup-playwright
- name: Run Playwright tests and update snapshots
id: playwright-tests
run: pnpm exec playwright test --update-snapshots
continue-on-error: true
- uses: actions/upload-artifact@v4
if: always()
with:
name: playwright-report
path: ./playwright-report/
retention-days: 30
- name: Debugging info
run: |
echo "PR: ${{ github.event.issue.number }}"
echo "Branch: ${{ steps.get-branch.outputs.branch }}"
git status
- name: Commit updated expectations
run: |
git config --global user.name 'github-actions'
git config --global user.email 'github-actions@github.com'
git add browser_tests
if git diff --cached --quiet; then
echo "No changes to commit"
else
git commit -m "[automated] Update test expectations"
git push origin ${{ steps.get-branch.outputs.branch }}
fi
- name: Add Done Reaction
uses: peter-evans/create-or-update-comment@e8674b075228eee787fea43ef493e45ece1004c9
if: github.event_name == 'issue_comment'
with:
comment-id: ${{ steps.find-update-comment.outputs.comment-id }}
issue-number: ${{ github.event.number || github.event.issue.number }}
reactions: +1
reactions-edit-mode: replace
- name: Remove New Browser Test Expectations label
if: always() && github.event_name == 'pull_request'
run: gh pr edit ${{ github.event.pull_request.number }} --remove-label "New Browser Test Expectations"
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View File

@@ -14,6 +14,11 @@ on:
required: false
default: ''
type: string
branch:
description: 'Base branch to bump (e.g., main, core/1.29, core/1.30)'
required: true
default: 'main'
type: string
jobs:
bump-version-desktop-ui:
@@ -26,8 +31,25 @@ jobs:
- name: Checkout repository
uses: actions/checkout@v5
with:
ref: ${{ github.event.inputs.branch }}
fetch-depth: 0
persist-credentials: false
- name: Validate branch exists
run: |
BRANCH="${{ github.event.inputs.branch }}"
if ! git show-ref --verify --quiet "refs/heads/$BRANCH" && ! git show-ref --verify --quiet "refs/remotes/origin/$BRANCH"; then
echo "❌ Branch '$BRANCH' does not exist"
echo ""
echo "Available core branches:"
git branch -r | grep 'origin/core/' | sed 's/.*origin\// - /' || echo " (none found)"
echo ""
echo "Main branch:"
echo " - main"
exit 1
fi
echo "✅ Branch '$BRANCH' exists"
- name: Install pnpm
uses: pnpm/action-setup@v4
with:
@@ -64,8 +86,10 @@ jobs:
title: desktop-ui ${{ steps.bump-version.outputs.NEW_VERSION }}
body: |
${{ steps.capitalised.outputs.capitalised }} version increment for @comfyorg/desktop-ui to ${{ steps.bump-version.outputs.NEW_VERSION }}
**Base branch:** `${{ github.event.inputs.branch }}`
branch: desktop-ui-version-bump-${{ steps.bump-version.outputs.NEW_VERSION }}
base: main
base: ${{ github.event.inputs.branch }}
labels: |
Release

144
.github/workflows/weekly-docs-check.yaml vendored Normal file
View File

@@ -0,0 +1,144 @@
name: "Weekly Documentation Check"
description: "Automated weekly documentation accuracy check and update via Claude"
permissions:
contents: write
pull-requests: write
id-token: write
on:
schedule:
# Run every Monday at 9 AM UTC
- cron: '0 9 * * 1'
workflow_dispatch:
concurrency:
group: ${{ github.workflow }}
cancel-in-progress: true
jobs:
docs-check:
runs-on: ubuntu-latest
timeout-minutes: 45
steps:
- name: Checkout repository
uses: actions/checkout@v5
with:
fetch-depth: 0
ref: main
- name: Install pnpm
uses: pnpm/action-setup@v4
with:
version: 10
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'pnpm'
- name: Install dependencies for analysis tools
run: |
# Check if packages are already available locally
if ! pnpm list typescript @vue/compiler-sfc >/dev/null 2>&1; then
echo "Installing TypeScript and Vue compiler globally..."
pnpm install -g typescript @vue/compiler-sfc
else
echo "TypeScript and Vue compiler already available locally"
fi
- name: Run Claude Documentation Review
uses: anthropics/claude-code-action@v1.0.6
with:
prompt: |
Is all documentation still 100% accurate?
INSTRUCTIONS:
1. Fact-check all documentation against the current codebase
2. Look for:
- Outdated API references
- Deprecated functions or components still documented
- Missing documentation for new features
- Incorrect code examples
- Broken internal references
- Configuration examples that no longer work
- Documentation that contradicts actual implementation
3. Update any inaccurate or outdated documentation
4. Add documentation for significant undocumented features
5. Ensure all code examples are valid and tested against current code
Focus on these key areas:
- docs/**/*.md (all documentation files)
- CLAUDE.md (project guidelines)
- README.md files throughout the repository
- .claude/commands/*.md (Claude command documentation)
Make changes directly to the documentation files as needed.
DO NOT modify any source code files unless absolutely necessary for documentation accuracy.
After making all changes, create a comprehensive PR message summary:
1. Write a detailed PR body to /tmp/pr-body-${{ github.run_id }}.md in markdown format
2. Include:
- ## Summary section with bullet points of what was changed
- ## Changes Made section with details organized by category
- ## Review Notes section with any important context
3. Be specific about which files were updated and why
4. If no changes were needed, write a brief message stating documentation is up to date
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
claude_args: "--max-turns 256 --allowedTools 'Bash(git status),Bash(git diff),Bash(git log),Bash(pnpm:*),Bash(npm:*),Bash(node:*),Bash(tsc:*),Bash(echo:*),Read,Write,Edit,Glob,Grep'"
continue-on-error: false
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Check for changes
id: check_changes
run: |
if git diff --quiet && git diff --cached --quiet; then
echo "has_changes=false" >> $GITHUB_OUTPUT
echo "No documentation changes needed"
else
echo "has_changes=true" >> $GITHUB_OUTPUT
echo "Documentation changes detected"
fi
- name: Create default PR body if not generated
if: steps.check_changes.outputs.has_changes == 'true'
run: |
if [ ! -f /tmp/pr-body-${{ github.run_id }}.md ]; then
cat > /tmp/pr-body-${{ github.run_id }}.md <<'EOF'
## Automated Documentation Review
This PR contains documentation updates identified by the weekly automated review.
### Review Process
- Automated fact-checking against current codebase
- Verification of code examples and API references
- Detection of outdated or missing documentation
### What was checked
- All markdown documentation in `docs/`
- Project guidelines in `CLAUDE.md`
- README files throughout the repository
- Claude command documentation in `.claude/commands/`
**Note**: This is an automated PR. Please review all changes carefully before merging.
🤖 Generated by weekly documentation check workflow
EOF
fi
- name: Create or Update Pull Request
if: steps.check_changes.outputs.has_changes == 'true'
uses: peter-evans/create-pull-request@v7
with:
token: ${{ secrets.PR_GH_TOKEN }}
commit-message: 'docs: weekly documentation accuracy update'
branch: docs/weekly-update
delete-branch: true
title: 'docs: Weekly Documentation Update'
body-path: /tmp/pr-body-${{ github.run_id }}.md
labels: |
documentation
automated
draft: true

5
.gitignore vendored
View File

@@ -78,7 +78,7 @@ templates_repo/
vite.config.mts.timestamp-*.mjs
# Linux core dumps
./core
/core
*storybook.log
storybook-static
@@ -92,3 +92,6 @@ storybook-static
.github/instructions/nx.instructions.md
vite.config.*.timestamp*
vitest.config.*.timestamp*
# Weekly docs check output
/output.txt

View File

@@ -1,5 +1,5 @@
#!/usr/bin/env bash
# Run Knip with cache via package script
pnpm knip
pnpm knip 1>&2

View File

@@ -7,12 +7,5 @@
"importOrder": ["^@core/(.*)$", "<THIRD_PARTY_MODULES>", "^@/(.*)$", "^[./]"],
"importOrderSeparation": true,
"importOrderSortSpecifiers": true,
"overrides": [
{
"files": "*.{js,cjs,mjs,ts,cts,mts,tsx,vue}",
"options": {
"plugins": ["@trivago/prettier-plugin-sort-imports"]
}
}
]
"plugins": ["@prettier/plugin-oxc", "@trivago/prettier-plugin-sort-imports"]
}

View File

@@ -62,6 +62,11 @@ Key Nx features:
## Project Philosophy
- Follow good software engineering principles
- YAGNI
- AHA
- DRY
- SOLID
- Clean, stable public APIs
- Domain-driven design
- Thousands of users and extensions

View File

@@ -43,7 +43,7 @@ Have another idea? Drop into Discord or open an issue, and let's chat!
```
3. Configure environment (optional):
Create a `.env` file in the project root based on the provided [.env.example](.env.example) file.
Create a `.env` file in the project root based on the provided [.env_example](.env_example) file.
**Note about ports**: By default, the dev server expects the ComfyUI backend at `localhost:8188`. If your ComfyUI instance runs on a different port, update this in your `.env` file.
@@ -325,4 +325,4 @@ If you have questions about contributing:
- Ask in our [Discord](https://discord.com/invite/comfyorg)
- Open a new issue for clarification
Thank you for contributing to ComfyUI Frontend!
Thank you for contributing to ComfyUI Frontend!

View File

@@ -2,7 +2,7 @@
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>ComfyUI Desktop</title>
<title>ComfyUI</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no" />
</head>
<body>

View File

@@ -1,6 +1,6 @@
{
"name": "@comfyorg/desktop-ui",
"version": "0.0.1",
"version": "0.0.3",
"type": "module",
"nx": {
"tags": [

View File

@@ -1,17 +1,17 @@
<template>
<div
class="grid grid-rows-[1fr_auto_auto_1fr] w-full max-w-3xl mx-auto h-[40rem] select-none"
class="mx-auto grid h-[40rem] w-full max-w-3xl grid-rows-[1fr_auto_auto_1fr] select-none"
>
<h2 class="font-inter font-bold text-3xl text-neutral-100 text-center">
<h2 class="text-center font-inter text-3xl font-bold text-neutral-100">
{{ $t('install.gpuPicker.title') }}
</h2>
<!-- GPU Selection buttons - takes up remaining space and centers content -->
<div class="flex-1 flex gap-8 justify-center items-center">
<div class="flex flex-1 items-center justify-center gap-8">
<!-- Apple Metal / NVIDIA -->
<HardwareOption
v-if="platform === 'darwin'"
:image-path="'/assets/images/apple-mps-logo.png'"
:image-path="'./assets/images/apple-mps-logo.png'"
placeholder-text="Apple Metal"
subtitle="Apple Metal"
:value="'mps'"
@@ -21,7 +21,7 @@
/>
<HardwareOption
v-else
:image-path="'/assets/images/nvidia-logo-square.jpg'"
:image-path="'./assets/images/nvidia-logo-square.jpg'"
placeholder-text="NVIDIA"
:subtitle="$t('install.gpuPicker.nvidiaSubtitle')"
:value="'nvidia'"
@@ -47,17 +47,17 @@
/>
</div>
<div class="pt-12 px-24 h-16">
<div class="h-16 px-24 pt-12">
<div v-show="showRecommendedBadge" class="flex items-center gap-2">
<Tag
:value="$t('install.gpuPicker.recommended')"
class="bg-neutral-300 text-neutral-900 rounded-full text-sm font-bold px-2 py-[1px]"
class="rounded-full bg-neutral-300 px-2 py-[1px] text-sm font-bold text-neutral-900"
/>
<i class="icon-[lucide--badge-check] text-neutral-300 text-lg" />
<i class="icon-[lucide--badge-check] text-lg text-neutral-300" />
</div>
</div>
<div class="text-neutral-300 px-24">
<div class="px-24 text-neutral-300">
<p v-show="descriptionText" class="leading-relaxed">
{{ descriptionText }}
</p>

View File

@@ -1,23 +1,23 @@
<template>
<div
class="task-div max-w-48 min-h-52 grid relative"
class="task-div relative grid min-h-52 max-w-48"
:class="{ 'opacity-75': isLoading }"
>
<Card
class="max-w-48 relative h-full overflow-hidden"
class="relative h-full max-w-48 overflow-hidden"
:class="{ 'opacity-65': runner.state !== 'error' }"
v-bind="(({ onClick, ...rest }) => rest)($attrs)"
>
<template #header>
<i
v-if="runner.state === 'error'"
class="pi pi-exclamation-triangle text-red-500 absolute m-2 top-0 -right-14 opacity-15"
class="pi pi-exclamation-triangle absolute top-0 -right-14 m-2 text-red-500 opacity-15"
style="font-size: 10rem"
/>
<img
v-if="task.headerImg"
:src="task.headerImg"
class="object-contain w-full h-full opacity-25 pt-4 px-4"
class="h-full w-full object-contain px-4 pt-4 opacity-25"
/>
</template>
<template #title>
@@ -27,7 +27,7 @@
{{ description }}
</template>
<template #footer>
<div class="flex gap-4 mt-1">
<div class="mt-1 flex gap-4">
<Button
:icon="task.button?.icon"
:label="task.button?.text"
@@ -73,7 +73,7 @@ defineEmits<{
// Bindings
const description = computed(() =>
runner.value.state === 'error'
? props.task.errorDescription ?? props.task.shortDescription
? (props.task.errorDescription ?? props.task.shortDescription)
: props.task.shortDescription
)

View File

@@ -1,67 +1,163 @@
import arCommands from '@frontend-locales/ar/commands.json' with { type: 'json' }
import ar from '@frontend-locales/ar/main.json' with { type: 'json' }
import arNodes from '@frontend-locales/ar/nodeDefs.json' with { type: 'json' }
import arSettings from '@frontend-locales/ar/settings.json' with { type: 'json' }
// Import only English locale eagerly as the default/fallback
// ESLint cannot statically resolve dynamic imports with path aliases (@frontend-locales/*),
// but these are properly configured in tsconfig.json and resolved by Vite at build time.
// eslint-disable-next-line import-x/no-unresolved
import enCommands from '@frontend-locales/en/commands.json' with { type: 'json' }
// eslint-disable-next-line import-x/no-unresolved
import en from '@frontend-locales/en/main.json' with { type: 'json' }
// eslint-disable-next-line import-x/no-unresolved
import enNodes from '@frontend-locales/en/nodeDefs.json' with { type: 'json' }
// eslint-disable-next-line import-x/no-unresolved
import enSettings from '@frontend-locales/en/settings.json' with { type: 'json' }
import esCommands from '@frontend-locales/es/commands.json' with { type: 'json' }
import es from '@frontend-locales/es/main.json' with { type: 'json' }
import esNodes from '@frontend-locales/es/nodeDefs.json' with { type: 'json' }
import esSettings from '@frontend-locales/es/settings.json' with { type: 'json' }
import frCommands from '@frontend-locales/fr/commands.json' with { type: 'json' }
import fr from '@frontend-locales/fr/main.json' with { type: 'json' }
import frNodes from '@frontend-locales/fr/nodeDefs.json' with { type: 'json' }
import frSettings from '@frontend-locales/fr/settings.json' with { type: 'json' }
import jaCommands from '@frontend-locales/ja/commands.json' with { type: 'json' }
import ja from '@frontend-locales/ja/main.json' with { type: 'json' }
import jaNodes from '@frontend-locales/ja/nodeDefs.json' with { type: 'json' }
import jaSettings from '@frontend-locales/ja/settings.json' with { type: 'json' }
import koCommands from '@frontend-locales/ko/commands.json' with { type: 'json' }
import ko from '@frontend-locales/ko/main.json' with { type: 'json' }
import koNodes from '@frontend-locales/ko/nodeDefs.json' with { type: 'json' }
import koSettings from '@frontend-locales/ko/settings.json' with { type: 'json' }
import ruCommands from '@frontend-locales/ru/commands.json' with { type: 'json' }
import ru from '@frontend-locales/ru/main.json' with { type: 'json' }
import ruNodes from '@frontend-locales/ru/nodeDefs.json' with { type: 'json' }
import ruSettings from '@frontend-locales/ru/settings.json' with { type: 'json' }
import trCommands from '@frontend-locales/tr/commands.json' with { type: 'json' }
import tr from '@frontend-locales/tr/main.json' with { type: 'json' }
import trNodes from '@frontend-locales/tr/nodeDefs.json' with { type: 'json' }
import trSettings from '@frontend-locales/tr/settings.json' with { type: 'json' }
import zhTWCommands from '@frontend-locales/zh-TW/commands.json' with { type: 'json' }
import zhTW from '@frontend-locales/zh-TW/main.json' with { type: 'json' }
import zhTWNodes from '@frontend-locales/zh-TW/nodeDefs.json' with { type: 'json' }
import zhTWSettings from '@frontend-locales/zh-TW/settings.json' with { type: 'json' }
import zhCommands from '@frontend-locales/zh/commands.json' with { type: 'json' }
import zh from '@frontend-locales/zh/main.json' with { type: 'json' }
import zhNodes from '@frontend-locales/zh/nodeDefs.json' with { type: 'json' }
import zhSettings from '@frontend-locales/zh/settings.json' with { type: 'json' }
import { createI18n } from 'vue-i18n'
function buildLocale<M, N, C, S>(main: M, nodes: N, commands: C, settings: S) {
function buildLocale<
M extends Record<string, unknown>,
N extends Record<string, unknown>,
C extends Record<string, unknown>,
S extends Record<string, unknown>
>(main: M, nodes: N, commands: C, settings: S) {
return {
...main,
nodeDefs: nodes,
commands: commands,
settings: settings
}
} as M & { nodeDefs: N; commands: C; settings: S }
}
const messages = {
en: buildLocale(en, enNodes, enCommands, enSettings),
zh: buildLocale(zh, zhNodes, zhCommands, zhSettings),
'zh-TW': buildLocale(zhTW, zhTWNodes, zhTWCommands, zhTWSettings),
ru: buildLocale(ru, ruNodes, ruCommands, ruSettings),
ja: buildLocale(ja, jaNodes, jaCommands, jaSettings),
ko: buildLocale(ko, koNodes, koCommands, koSettings),
fr: buildLocale(fr, frNodes, frCommands, frSettings),
es: buildLocale(es, esNodes, esCommands, esSettings),
ar: buildLocale(ar, arNodes, arCommands, arSettings),
tr: buildLocale(tr, trNodes, trCommands, trSettings)
// Locale loader map - dynamically import locales only when needed
// ESLint cannot statically resolve these dynamic imports, but they are valid at build time
/* eslint-disable import-x/no-unresolved */
const localeLoaders: Record<
string,
() => Promise<{ default: Record<string, unknown> }>
> = {
ar: () => import('@frontend-locales/ar/main.json'),
es: () => import('@frontend-locales/es/main.json'),
fr: () => import('@frontend-locales/fr/main.json'),
ja: () => import('@frontend-locales/ja/main.json'),
ko: () => import('@frontend-locales/ko/main.json'),
ru: () => import('@frontend-locales/ru/main.json'),
tr: () => import('@frontend-locales/tr/main.json'),
zh: () => import('@frontend-locales/zh/main.json'),
'zh-TW': () => import('@frontend-locales/zh-TW/main.json')
}
const nodeDefsLoaders: Record<
string,
() => Promise<{ default: Record<string, unknown> }>
> = {
ar: () => import('@frontend-locales/ar/nodeDefs.json'),
es: () => import('@frontend-locales/es/nodeDefs.json'),
fr: () => import('@frontend-locales/fr/nodeDefs.json'),
ja: () => import('@frontend-locales/ja/nodeDefs.json'),
ko: () => import('@frontend-locales/ko/nodeDefs.json'),
ru: () => import('@frontend-locales/ru/nodeDefs.json'),
tr: () => import('@frontend-locales/tr/nodeDefs.json'),
zh: () => import('@frontend-locales/zh/nodeDefs.json'),
'zh-TW': () => import('@frontend-locales/zh-TW/nodeDefs.json')
}
const commandsLoaders: Record<
string,
() => Promise<{ default: Record<string, unknown> }>
> = {
ar: () => import('@frontend-locales/ar/commands.json'),
es: () => import('@frontend-locales/es/commands.json'),
fr: () => import('@frontend-locales/fr/commands.json'),
ja: () => import('@frontend-locales/ja/commands.json'),
ko: () => import('@frontend-locales/ko/commands.json'),
ru: () => import('@frontend-locales/ru/commands.json'),
tr: () => import('@frontend-locales/tr/commands.json'),
zh: () => import('@frontend-locales/zh/commands.json'),
'zh-TW': () => import('@frontend-locales/zh-TW/commands.json')
}
const settingsLoaders: Record<
string,
() => Promise<{ default: Record<string, unknown> }>
> = {
ar: () => import('@frontend-locales/ar/settings.json'),
es: () => import('@frontend-locales/es/settings.json'),
fr: () => import('@frontend-locales/fr/settings.json'),
ja: () => import('@frontend-locales/ja/settings.json'),
ko: () => import('@frontend-locales/ko/settings.json'),
ru: () => import('@frontend-locales/ru/settings.json'),
tr: () => import('@frontend-locales/tr/settings.json'),
zh: () => import('@frontend-locales/zh/settings.json'),
'zh-TW': () => import('@frontend-locales/zh-TW/settings.json')
}
// Track which locales have been loaded
const loadedLocales = new Set<string>(['en'])
// Track locales currently being loaded to prevent race conditions
const loadingLocales = new Map<string, Promise<void>>()
/**
* Dynamically load a locale and its associated files (nodeDefs, commands, settings)
*/
export async function loadLocale(locale: string): Promise<void> {
if (loadedLocales.has(locale)) {
return
}
// If already loading, return the existing promise to prevent duplicate loads
const existingLoad = loadingLocales.get(locale)
if (existingLoad) {
return existingLoad
}
const loader = localeLoaders[locale]
const nodeDefsLoader = nodeDefsLoaders[locale]
const commandsLoader = commandsLoaders[locale]
const settingsLoader = settingsLoaders[locale]
if (!loader || !nodeDefsLoader || !commandsLoader || !settingsLoader) {
console.warn(`Locale "${locale}" is not supported`)
return
}
// Create and track the loading promise
const loadPromise = (async () => {
try {
const [main, nodes, commands, settings] = await Promise.all([
loader(),
nodeDefsLoader(),
commandsLoader(),
settingsLoader()
])
const messages = buildLocale(
main.default,
nodes.default,
commands.default,
settings.default
)
i18n.global.setLocaleMessage(locale, messages as LocaleMessages)
loadedLocales.add(locale)
} catch (error) {
console.error(`Failed to load locale "${locale}":`, error)
throw error
} finally {
// Clean up the loading promise once complete
loadingLocales.delete(locale)
}
})()
loadingLocales.set(locale, loadPromise)
return loadPromise
}
// Only include English in the initial bundle
const messages = {
en: buildLocale(en, enNodes, enCommands, enSettings)
}
// Type for locale messages - inferred from the English locale structure
type LocaleMessages = typeof messages.en
export const i18n = createI18n({
// Must set `false`, as Vue I18n Legacy API is for Vue 2
legacy: false,

View File

@@ -66,17 +66,6 @@
@click="troubleshoot"
/>
</div>
<div class="text-center">
<button
v-if="!terminalVisible"
class="text-sm text-neutral-500 hover:text-neutral-300 transition-colors flex items-center gap-2 mx-auto"
@click="terminalVisible = true"
>
<i class="pi pi-search"></i>
{{ $t('serverStart.showTerminal') }}
</button>
</div>
</div>
<!-- Terminal Output (positioned at bottom when manually toggled in error state) -->

Binary file not shown.

After

Width:  |  Height:  |  Size: 85 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 99 KiB

View File

@@ -1,6 +1,5 @@
import type { APIRequestContext, Locator, Page } from '@playwright/test'
import { expect } from '@playwright/test'
import { test as base } from '@playwright/test'
import { test as base, expect } from '@playwright/test'
import dotenv from 'dotenv'
import * as fs from 'fs'
@@ -47,6 +46,10 @@ class ComfyMenu {
.nth(0)
}
get buttons() {
return this.sideToolbar.locator('.side-bar-button')
}
get nodeLibraryTab() {
this._nodeLibraryTab ??= new NodeLibrarySidebarTab(this.page)
return this._nodeLibraryTab
@@ -130,7 +133,8 @@ export class ComfyPage {
// Buttons
public readonly resetViewButton: Locator
public readonly queueButton: Locator
public readonly queueButton: Locator // Run button in Legacy UI
public readonly runButton: Locator // Run button (renamed "Queue" -> "Run")
// Inputs
public readonly workflowUploadInput: Locator
@@ -165,6 +169,9 @@ export class ComfyPage {
this.widgetTextBox = page.getByPlaceholder('text').nth(1)
this.resetViewButton = page.getByRole('button', { name: 'Reset View' })
this.queueButton = page.getByRole('button', { name: 'Queue Prompt' })
this.runButton = page
.getByTestId('queue-button')
.getByRole('button', { name: 'Run' })
this.workflowUploadInput = page.locator('#comfy-file-input')
this.visibleToasts = page.locator('.p-toast-message:visible')
@@ -1086,12 +1093,6 @@ export class ComfyPage {
const targetPosition = await targetSlot.getPosition()
// Debug: Log the positions we're trying to use
console.log('Drag positions:', {
source: sourcePosition,
target: targetPosition
})
await this.dragAndDrop(sourcePosition, targetPosition)
await this.nextFrame()
}
@@ -1656,7 +1657,8 @@ export const comfyPageFixture = base.extend<{
'Comfy.userId': userId,
// Set tutorial completed to true to avoid loading the tutorial workflow.
'Comfy.TutorialCompleted': true,
'Comfy.SnapToGrid.GridSize': testComfySnapToGridGridSize
'Comfy.SnapToGrid.GridSize': testComfySnapToGridGridSize,
'Comfy.VueNodes.AutoScaleLayout': false
})
} catch (e) {
console.error(e)

View File

@@ -3,6 +3,8 @@
*/
import type { Locator, Page } from '@playwright/test'
import { VueNodeFixture } from './utils/vueNodeFixtures'
export class VueNodeHelpers {
constructor(private page: Page) {}
@@ -106,6 +108,24 @@ export class VueNodeHelpers {
await this.page.keyboard.press('Backspace')
}
/**
* Return a DOM-focused VueNodeFixture for the first node matching the title.
* Resolves the node id up front so subsequent interactions survive title changes.
*/
async getFixtureByTitle(title: string): Promise<VueNodeFixture> {
const node = this.getNodeByTitle(title).first()
await node.waitFor({ state: 'visible' })
const nodeId = await node.evaluate((el) => el.getAttribute('data-node-id'))
if (!nodeId) {
throw new Error(
`Vue node titled "${title}" is missing its data-node-id attribute`
)
}
return new VueNodeFixture(this.getNodeLocator(nodeId))
}
/**
* Wait for Vue nodes to be rendered
*/

View File

@@ -7,7 +7,7 @@ export class Topbar {
constructor(public readonly page: Page) {
this.menuLocator = page.locator('.comfy-command-menu')
this.menuTrigger = page.locator('.comfyui-logo-wrapper')
this.menuTrigger = page.locator('.comfy-menu-button-wrapper')
}
async getTabNames(): Promise<string[]> {
@@ -105,7 +105,7 @@ export class Topbar {
* Close the topbar menu by clicking outside
*/
async closeTopbarMenu() {
await this.page.locator('body').click({ position: { x: 10, y: 10 } })
await this.page.locator('body').click({ position: { x: 300, y: 10 } })
await expect(this.menuLocator).not.toBeVisible()
}

View File

@@ -503,7 +503,7 @@ export class NodeReference {
for (const position of clickPositions) {
// Clear any selection first
await this.comfyPage.canvas.click({
position: { x: 50, y: 50 },
position: { x: 250, y: 250 },
force: true
})
await this.comfyPage.nextFrame()

View File

@@ -1,131 +1,66 @@
import type { Locator, Page } from '@playwright/test'
import { expect } from '@playwright/test'
import type { Locator } from '@playwright/test'
import type { NodeReference } from './litegraphUtils'
/**
* VueNodeFixture provides Vue-specific testing utilities for interacting with
* Vue node components. It bridges the gap between litegraph node references
* and Vue UI components.
*/
/** DOM-centric helper for a single Vue-rendered node on the canvas. */
export class VueNodeFixture {
constructor(
private readonly nodeRef: NodeReference,
private readonly page: Page
) {}
constructor(private readonly locator: Locator) {}
/**
* Get the node's header element using data-testid
*/
async getHeader(): Promise<Locator> {
const nodeId = this.nodeRef.id
return this.page.locator(`[data-testid="node-header-${nodeId}"]`)
get header(): Locator {
return this.locator.locator('[data-testid^="node-header-"]')
}
/**
* Get the node's title element
*/
async getTitleElement(): Promise<Locator> {
const header = await this.getHeader()
return header.locator('[data-testid="node-title"]')
get title(): Locator {
return this.locator.locator('[data-testid="node-title"]')
}
get titleInput(): Locator {
return this.locator.locator('[data-testid="node-title-input"]')
}
get body(): Locator {
return this.locator.locator('[data-testid^="node-body-"]')
}
get collapseButton(): Locator {
return this.locator.locator('[data-testid="node-collapse-button"]')
}
get collapseIcon(): Locator {
return this.collapseButton.locator('i')
}
get root(): Locator {
return this.locator
}
/**
* Get the current title text
*/
async getTitle(): Promise<string> {
const titleElement = await this.getTitleElement()
return (await titleElement.textContent()) || ''
return (await this.title.textContent()) ?? ''
}
/**
* Set a new title by double-clicking and entering text
*/
async setTitle(newTitle: string): Promise<void> {
const titleElement = await this.getTitleElement()
await titleElement.dblclick()
const input = (await this.getHeader()).locator(
'[data-testid="node-title-input"]'
)
await input.fill(newTitle)
async setTitle(value: string): Promise<void> {
await this.header.dblclick()
const input = this.titleInput
await expect(input).toBeVisible()
await input.fill(value)
await input.press('Enter')
}
/**
* Cancel title editing
*/
async cancelTitleEdit(): Promise<void> {
const titleElement = await this.getTitleElement()
await titleElement.dblclick()
const input = (await this.getHeader()).locator(
'[data-testid="node-title-input"]'
)
await this.header.dblclick()
const input = this.titleInput
await expect(input).toBeVisible()
await input.press('Escape')
}
/**
* Check if the title is currently being edited
*/
async isEditingTitle(): Promise<boolean> {
const header = await this.getHeader()
const input = header.locator('[data-testid="node-title-input"]')
return await input.isVisible()
}
/**
* Get the collapse/expand button
*/
async getCollapseButton(): Promise<Locator> {
const header = await this.getHeader()
return header.locator('[data-testid="node-collapse-button"]')
}
/**
* Toggle the node's collapsed state
*/
async toggleCollapse(): Promise<void> {
const button = await this.getCollapseButton()
await button.click()
await this.collapseButton.click()
}
/**
* Get the collapse icon element
*/
async getCollapseIcon(): Promise<Locator> {
const button = await this.getCollapseButton()
return button.locator('i')
}
/**
* Get the collapse icon's CSS classes
*/
async getCollapseIconClass(): Promise<string> {
const icon = await this.getCollapseIcon()
return (await icon.getAttribute('class')) || ''
return (await this.collapseIcon.getAttribute('class')) ?? ''
}
/**
* Check if the collapse button is visible
*/
async isCollapseButtonVisible(): Promise<boolean> {
const button = await this.getCollapseButton()
return await button.isVisible()
}
/**
* Get the node's body/content element
*/
async getBody(): Promise<Locator> {
const nodeId = this.nodeRef.id
return this.page.locator(`[data-testid="node-body-${nodeId}"]`)
}
/**
* Check if the node body is visible (not collapsed)
*/
async isBodyVisible(): Promise<boolean> {
const body = await this.getBody()
return await body.isVisible()
boundingBox(): ReturnType<Locator['boundingBox']> {
return this.locator.boundingBox()
}
}

View File

@@ -13,7 +13,7 @@ export class ComfyActionbar {
async isDocked() {
const className = await this.root.getAttribute('class')
return className?.includes('is-docked') ?? false
return className?.includes('static') ?? false
}
}

View File

@@ -116,9 +116,10 @@ test.describe('Actionbar', () => {
test('Can dock actionbar into top menu', async ({ comfyPage }) => {
await comfyPage.page.dragAndDrop(
'.actionbar .drag-handle',
'.comfyui-menu',
'.actionbar-container',
{
targetPosition: { x: 0, y: 0 }
targetPosition: { x: 50, y: 20 },
force: true
}
)
expect(await comfyPage.actionbar.isDocked()).toBe(true)

View File

@@ -301,7 +301,9 @@ test.describe('Settings', () => {
})
test.describe('Support', () => {
test('Should open external zendesk link', async ({ comfyPage }) => {
test('Should open external zendesk link with OSS tag', async ({
comfyPage
}) => {
await comfyPage.setSetting('Comfy.UseNewMenu', 'Top')
const pagePromise = comfyPage.page.context().waitForEvent('page')
await comfyPage.menu.topbar.triggerTopbarCommand(['Help', 'Support'])
@@ -309,6 +311,10 @@ test.describe('Support', () => {
await newPage.waitForLoadState('networkidle')
await expect(newPage).toHaveURL(/.*support\.comfy\.org.*/)
const url = new URL(newPage.url())
expect(url.searchParams.get('tf_42243568391700')).toBe('oss')
await newPage.close()
})
})

Binary file not shown.

Before

Width:  |  Height:  |  Size: 88 KiB

After

Width:  |  Height:  |  Size: 82 KiB

View File

@@ -39,15 +39,15 @@ test.describe('Graph Canvas Menu', () => {
)
})
test('Focus mode button is clickable and has correct test id', async ({
test('Toggle minimap button is clickable and has correct test id', async ({
comfyPage
}) => {
const focusButton = comfyPage.page.getByTestId('focus-mode-button')
await expect(focusButton).toBeVisible()
await expect(focusButton).toBeEnabled()
const minimapButton = comfyPage.page.getByTestId('toggle-minimap-button')
await expect(minimapButton).toBeVisible()
await expect(minimapButton).toBeEnabled()
// Test that the button can be clicked without error
await focusButton.click()
await minimapButton.click()
await comfyPage.nextFrame()
})

Binary file not shown.

Before

Width:  |  Height:  |  Size: 86 KiB

After

Width:  |  Height:  |  Size: 85 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 100 KiB

After

Width:  |  Height:  |  Size: 99 KiB

View File

@@ -233,6 +233,7 @@ test.describe('Group Node', () => {
}
const isRegisteredNodeDefStore = async (comfyPage: ComfyPage) => {
await comfyPage.menu.nodeLibraryTab.open()
const groupNodesFolderCt = await comfyPage.menu.nodeLibraryTab
.getFolder(GROUP_NODE_CATEGORY)
.count()
@@ -253,8 +254,6 @@ test.describe('Group Node', () => {
test.beforeEach(async ({ comfyPage }) => {
await comfyPage.setSetting('Comfy.UseNewMenu', 'Top')
await comfyPage.loadWorkflow(WORKFLOW_NAME)
await comfyPage.menu.nodeLibraryTab.open()
groupNode = await comfyPage.getFirstNodeRef()
if (!groupNode)
throw new Error(`Group node not found in workflow ${WORKFLOW_NAME}`)

View File

@@ -3,10 +3,10 @@ import { expect } from '@playwright/test'
import type { Position } from '@vueuse/core'
import {
type ComfyPage,
comfyPageFixture as test,
testComfySnapToGridGridSize
} from '../fixtures/ComfyPage'
import type { ComfyPage } from '../fixtures/ComfyPage'
import type { NodeReference } from '../fixtures/utils/litegraphUtils'
test.beforeEach(async ({ comfyPage }) => {
@@ -786,24 +786,25 @@ test.describe('Viewport settings', () => {
// Screenshot the canvas element
await comfyPage.setSetting('Comfy.Graph.CanvasMenu', true)
// Open zoom controls dropdown first
const zoomControlsButton = comfyPage.page.getByTestId(
'zoom-controls-button'
)
await zoomControlsButton.click()
const toggleButton = comfyPage.page.getByTestId('toggle-minimap-button')
await toggleButton.click()
// close zoom menu
await zoomControlsButton.click()
await comfyPage.setSetting('Comfy.Graph.CanvasMenu', false)
await comfyPage.menu.topbar.saveWorkflow('Workflow A')
await comfyPage.nextFrame()
const screenshotA = (await comfyPage.canvas.screenshot()).toString('base64')
// Save workflow as a new file, then zoom out before screen shot
await comfyPage.menu.topbar.saveWorkflowAs('Workflow B')
await comfyPage.nextFrame()
const tabA = comfyPage.menu.topbar.getWorkflowTab('Workflow A')
await changeTab(tabA)
const screenshotA = (await comfyPage.canvas.screenshot()).toString('base64')
const tabB = comfyPage.menu.topbar.getWorkflowTab('Workflow B')
await changeTab(tabB)
await comfyMouse.move(comfyPage.emptySpace)
for (let i = 0; i < 4; i++) {
await comfyMouse.wheel(0, 60)
@@ -815,9 +816,6 @@ test.describe('Viewport settings', () => {
// Ensure that the screenshots are different due to zoom level
expect(screenshotB).not.toBe(screenshotA)
const tabA = comfyPage.menu.topbar.getWorkflowTab('Workflow A')
const tabB = comfyPage.menu.topbar.getWorkflowTab('Workflow B')
// Go back to Workflow A
await changeTab(tabA)
expect((await comfyPage.canvas.screenshot()).toString('base64')).toBe(

Binary file not shown.

Before

Width:  |  Height:  |  Size: 108 KiB

After

Width:  |  Height:  |  Size: 108 KiB

View File

@@ -8,9 +8,7 @@ test.describe('Menu', () => {
})
test('Can register sidebar tab', async ({ comfyPage }) => {
const initialChildrenCount = await comfyPage.menu.sideToolbar.evaluate(
(el) => el.children.length
)
const initialChildrenCount = await comfyPage.menu.buttons.count()
await comfyPage.page.evaluate(async () => {
window['app'].extensionManager.registerSidebarTab({
@@ -26,9 +24,7 @@ test.describe('Menu', () => {
})
await comfyPage.nextFrame()
const newChildrenCount = await comfyPage.menu.sideToolbar.evaluate(
(el) => el.children.length
)
const newChildrenCount = await comfyPage.menu.buttons.count()
expect(newChildrenCount).toBe(initialChildrenCount + 1)
})

View File

@@ -35,12 +35,6 @@ test.describe('Minimap', () => {
})
test('Validate minimap toggle button state', async ({ comfyPage }) => {
// Open zoom controls dropdown first
const zoomControlsButton = comfyPage.page.getByTestId(
'zoom-controls-button'
)
await zoomControlsButton.click()
const toggleButton = comfyPage.page.getByTestId('toggle-minimap-button')
await expect(toggleButton).toBeVisible()
@@ -51,13 +45,6 @@ test.describe('Minimap', () => {
test('Validate minimap can be toggled off and on', async ({ comfyPage }) => {
const minimapContainer = comfyPage.page.locator('.litegraph-minimap')
// Open zoom controls dropdown first
const zoomControlsButton = comfyPage.page.getByTestId(
'zoom-controls-button'
)
await zoomControlsButton.click()
const toggleButton = comfyPage.page.getByTestId('toggle-minimap-button')
await expect(minimapContainer).toBeVisible()
@@ -67,22 +54,10 @@ test.describe('Minimap', () => {
await expect(minimapContainer).not.toBeVisible()
// Open zoom controls dropdown again
await zoomControlsButton.click()
await comfyPage.nextFrame()
await expect(toggleButton).toContainText('Show Minimap')
await toggleButton.click()
await comfyPage.nextFrame()
await expect(minimapContainer).toBeVisible()
// Open zoom controls dropdown again to verify button text
await zoomControlsButton.click()
await comfyPage.nextFrame()
await expect(toggleButton).toContainText('Hide Minimap')
})
test('Validate minimap keyboard shortcut Alt+M', async ({ comfyPage }) => {

Binary file not shown.

Before

Width:  |  Height:  |  Size: 105 KiB

After

Width:  |  Height:  |  Size: 105 KiB

View File

@@ -0,0 +1,28 @@
import { expect } from '@playwright/test'
import { comfyPageFixture as test } from '../fixtures/ComfyPage'
test.beforeEach(async ({ comfyPage }) => {
await comfyPage.setSetting('Comfy.UseNewMenu', 'Disabled')
})
test.describe('Record Audio Node', () => {
test('should add a record audio node and take a screenshot', async ({
comfyPage
}) => {
// Open the search box by double clicking on the canvas
await comfyPage.doubleClickCanvas()
await expect(comfyPage.searchBox.input).toHaveCount(1)
// Search for and add the RecordAudio node
await comfyPage.searchBox.fillAndSelectFirstNode('RecordAudio')
await comfyPage.nextFrame()
// Verify the RecordAudio node was added
const recordAudioNodes = await comfyPage.getNodeRefsByType('RecordAudio')
expect(recordAudioNodes.length).toBe(1)
// Take a screenshot of the canvas with the RecordAudio node
await expect(comfyPage.canvas).toHaveScreenshot('record_audio_node.png')
})
})

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 105 KiB

After

Width:  |  Height:  |  Size: 104 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 107 KiB

After

Width:  |  Height:  |  Size: 106 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 105 KiB

After

Width:  |  Height:  |  Size: 104 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 102 KiB

After

Width:  |  Height:  |  Size: 102 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 104 KiB

After

Width:  |  Height:  |  Size: 104 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 99 KiB

After

Width:  |  Height:  |  Size: 99 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 68 KiB

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 80 KiB

After

Width:  |  Height:  |  Size: 108 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 47 KiB

After

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 34 KiB

View File

@@ -60,7 +60,6 @@ async function getInputLinkDetails(
)
}
// Test helpers to reduce repetition across cases
function slotLocator(
page: Page,
nodeId: NodeId,
@@ -789,6 +788,45 @@ test.describe('Vue Node Link Interaction', () => {
})
})
test('should batch disconnect all links with ctrl+alt+click on slot', async ({
comfyPage
}) => {
const clipNode = (await comfyPage.getNodeRefsByType('CLIPTextEncode'))[0]
const samplerNode = (await comfyPage.getNodeRefsByType('KSampler'))[0]
expect(clipNode && samplerNode).toBeTruthy()
await connectSlots(
comfyPage.page,
{ nodeId: clipNode.id, index: 0 },
{ nodeId: samplerNode.id, index: 1 },
() => comfyPage.nextFrame()
)
await connectSlots(
comfyPage.page,
{ nodeId: clipNode.id, index: 0 },
{ nodeId: samplerNode.id, index: 2 },
() => comfyPage.nextFrame()
)
const clipOutput = await clipNode.getOutput(0)
expect(await clipOutput.getLinkCount()).toBe(2)
const clipOutputSlot = slotLocator(comfyPage.page, clipNode.id, 0, false)
await clipOutputSlot.dispatchEvent('pointerdown', {
button: 0,
buttons: 1,
ctrlKey: true,
altKey: true,
shiftKey: false,
bubbles: true,
cancelable: true
})
await comfyPage.nextFrame()
expect(await clipOutput.getLinkCount()).toBe(0)
})
test.describe('Release actions (Shift-drop)', () => {
test('Context menu opens and endpoint is pinned on Shift-drop', async ({
comfyPage,

Binary file not shown.

Before

Width:  |  Height:  |  Size: 50 KiB

After

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

After

Width:  |  Height:  |  Size: 60 KiB

Some files were not shown because too many files have changed in this diff Show More