Skip to content

Cloud/1.36 - cloud release branch [DO NOT MERGE]#7967

Closed
luke-mino-altherr wants to merge 39 commits intomainfrom
cloud/1.36
Closed

Cloud/1.36 - cloud release branch [DO NOT MERGE]#7967
luke-mino-altherr wants to merge 39 commits intomainfrom
cloud/1.36

Conversation

@luke-mino-altherr
Copy link
Contributor

@luke-mino-altherr luke-mino-altherr commented Jan 12, 2026

Summary

Changes

  • What:
  • Breaking:
  • Dependencies:

Review Focus

Screenshots (if applicable)

┆Issue is synchronized with this Notion page by Unito

comfy-pr-bot and others added 17 commits December 30, 2025 12:54
…act-Pack plugin (#7801)

Backport of #7762 to `cloud/1.36`

Automatically created by backport workflow.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-7801-backport-cloud-1-36-fix-restore-mask-editor-compatibility-with-Impact-Pack-plugin-2d96d73d36508190b073e683e4f08f2b)
by [Unito](https://www.unito.io)

Co-authored-by: Terry Jia <[email protected]>
Backport of #7813 to `cloud/1.36`

Automatically created by backport workflow.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-7820-backport-cloud-1-36-Guard-downgrades-via-billing-portal-2db6d73d3650814fb2fbd73697ed070c)
by [Unito](https://www.unito.io)

Co-authored-by: Christian Byrne <[email protected]>
…del flow (#7886)

Backport of #7793 to `cloud/1.36`

Automatically created by backport workflow.

Co-authored-by: Luke Mino-Altherr <[email protected]>
Co-authored-by: Claude <[email protected]>
…ogress tracking (#7887)

Backport of #7746 to `cloud/1.36`

Automatically created by backport workflow.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-7887-backport-cloud-1-36-feat-Add-async-model-upload-with-WebSocket-progress-tracking-2e26d73d365081c4a060e6e88f33016d)
by [Unito](https://www.unito.io)

Co-authored-by: Luke Mino-Altherr <[email protected]>
Co-authored-by: Claude <[email protected]>
Co-authored-by: GitHub Action <[email protected]>
…eparate deletion and rename flags (#7888)

Backport of #7864 to `cloud/1.36`

Automatically created by backport workflow.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-7888-backport-cloud-1-36-feat-split-asset_update_options_enabled-into-separate-deletion-and-2e26d73d365081edb755ede860c53f97)
by [Unito](https://www.unito.io)

Co-authored-by: Luke Mino-Altherr <[email protected]>
Co-authored-by: Amp <[email protected]>
…rowserModal (#7889)

Backport of #7880 to `cloud/1.36`

Automatically created by backport workflow.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-7889-backport-cloud-1-36-feat-Stale-while-revalidate-pattern-for-AssetBrowserModal-2e26d73d365081fb854bfe4189a94bef)
by [Unito](https://www.unito.io)

Co-authored-by: Alexander Brown <[email protected]>
Co-authored-by: Amp <[email protected]>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Backport of #7869 to `cloud/1.36`

Automatically created by backport workflow.

Co-authored-by: Jin Yi <[email protected]>
Backport of #7912 to `cloud/1.36`

Automatically created by backport workflow.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-7915-backport-cloud-1-36-Fix-run-badge-anchoring-2e36d73d36508160a874dea0a732a9af)
by [Unito](https://www.unito.io)

Co-authored-by: Christian Byrne <[email protected]>
Co-authored-by: github-actions <[email protected]>
Backport of #7892 to `cloud/1.36`

Automatically created by backport workflow.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-7916-backport-cloud-1-36-feature-model-browser-folder-grouping-2e36d73d365081279a6bf032c6e0893d)
by [Unito](https://www.unito.io)

Co-authored-by: Jin Yi <[email protected]>
Backport of #7897 to `cloud/1.36`

Automatically created by backport workflow.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-7917-backport-cloud-1-36-feat-add-model-download-progress-dialog-2e36d73d365081b18bddeb4835f4d706)
by [Unito](https://www.unito.io)

Co-authored-by: Alexander Brown <[email protected]>
Co-authored-by: Amp <[email protected]>
…rogress notifications (#7918)

Backport of #7902 to cloud/1.36

Original PR: #7902

Cherry-picked merge commit e26e1f0.

## Conflicts resolved
- **pnpm-lock.yaml**: Regenerated with `pnpm install`
-
**tests-ui/tests/components/dialog/footer/ManagerProgressFooter.test.ts**:
Removed (PR deletes this file along with the component it tested)

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-7918-backport-cloud-1-36-feat-add-HoneyToast-component-for-persistent-progress-notification-2e36d73d3650811a9f57f26c56b84c97)
by [Unito](https://www.unito.io)

Co-authored-by: Alexander Brown <[email protected]>
Co-authored-by: Amp <[email protected]>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: sno <[email protected]>
Co-authored-by: github-actions <[email protected]>
Co-authored-by: GitHub Action <[email protected]>
…7921)

Backport of #7920 to `cloud/1.36`

Automatically created by backport workflow.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-7921-backport-cloud-1-36-fix-Button-sizing-in-modals-and-asset-browser-2e36d73d365081b9a52af74dc2b5e94c)
by [Unito](https://www.unito.io)

Co-authored-by: Alexander Brown <[email protected]>
…to reduce network requests (#7922)

Backport of #7919 to `cloud/1.36`

Automatically created by backport workflow.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-7922-backport-cloud-1-36-perf-AssetBrowserModal-virtualize-asset-grid-to-reduce-network-re-2e36d73d3650812ca602d496f4decec4)
by [Unito](https://www.unito.io)

Co-authored-by: Alexander Brown <[email protected]>
Co-authored-by: Amp <[email protected]>
Backport of #7899 to `cloud/1.36`

## Summary
- Implement the new add credits (top up) dialog
- Refactor the subscription dialog to make different credit types easier
to understand

## Conflicts Resolved
- `src/components/dialog/content/TopUpCreditsDialogContent.vue` - Took
PR version (new UI)
- `src/locales/en/main.json` - Added missing `usdAmount` translation key

Automatically created by manual backport process.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-7932-backport-cloud-1-36-Feat-cloud-new-top-up-dialog-2e36d73d3650812dba2cd7edcb43259b)
by [Unito](https://www.unito.io)
Backport of #7933 to `cloud/1.36`

Automatically created by backport workflow.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-7936-backport-cloud-1-36-fix-UX-nits-and-styles-2e36d73d365081ce89add4b9857df1db)
by [Unito](https://www.unito.io)

Co-authored-by: Simula_r <[email protected]>
Backport of #7938 to `cloud/1.36`

Automatically created by backport workflow.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-7943-backport-cloud-1-36-fix-Model-upload-UI-improvements-2e46d73d365081fcadd1f71e7fa012d6)
by [Unito](https://www.unito.io)

Co-authored-by: Alexander Brown <[email protected]>
Co-authored-by: Amp <[email protected]>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
…zard (#7944)

Backport of #7939 to `cloud/1.36`

Automatically created by backport workflow.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-7944-backport-cloud-1-36-fix-UploadModel-truncate-long-filenames-in-wizard-2e46d73d365081518cf3f78545ca5ecd)
by [Unito](https://www.unito.io)

Co-authored-by: Alexander Brown <[email protected]>
@dosubot dosubot bot added the size:XXL This PR changes 1000+ lines, ignoring generated files. label Jan 12, 2026
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 12, 2026

📝 Walkthrough

Walkthrough

Adds async model upload and asset-download tracking (WebSocket + polling) with a new Pinia store and task service, replaces the manager progress dialog with a persistent toast, refactors asset caching/store flows and upload wizard for async paths, updates feature flags/locales/CI/Storybook, and adds multiple UI components and tests.

Changes

Cohort / File(s) Summary
Async upload & download core
src/platform/assets/schemas/assetSchema.ts, src/platform/assets/services/assetService.ts, src/schemas/apiSchema.ts, src/scripts/api.ts, src/platform/tasks/services/taskService.ts, src/stores/assetDownloadStore.ts
Add async upload Zod schemas/types and uploadAssetAsync; add WS schema/type for asset_download and wire into BackendApiCalls; add taskService.getTask; new assetDownloadStore for tracking, dedupe, polling and public API.
Progress UI & toast components
src/workbench/extensions/manager/components/ManagerProgressToast.vue, src/components/honeyToast/*, src/components/toast/ProgressToastItem.vue, src/components/common/StatusBadge.vue, src/components/ui/stepper/FormattedNumberStepper.vue, src/components/honeyToast/*.{test,stories}.ts, src/components/toast/*.stories.ts
New ManagerProgressToast plus supporting toast components (ProgressToastItem, StatusBadge), a formatted number stepper, and related stories/tests for toast UI.
Manager dialog removal & wiring
src/services/dialogService.ts, src/workbench/extensions/manager/components/* (removed), src/workbench/extensions/manager/stores/* (removed), src/workbench/extensions/manager/composables/*, src/views/GraphView.vue
Remove manager progress dialog/header/footer components and dialog-store wiring; delete show/toggle dialog APIs; mount new ManagerProgressToast in GraphView.
Asset browser / store refactor
src/stores/assetsStore.ts, src/platform/assets/components/AssetBrowserModal.vue, src/platform/assets/components/AssetGrid.vue, src/platform/assets/components/AssetCard.vue, src/platform/assets/components/AssetFilterBar.vue, src/platform/assets/components/AssetBadgeGroup.vue, src/platform/assets/composables/*, tests-ui/platform/assets/*
Key-based cache helper and updateModelsForTag; AssetBrowserModal reads from store (new assetType prop), background refresh and upload wiring; AssetGrid uses VirtualGrid; AssetCard respects feature flags and emits deleted; tests updated to mock stores.
Upload UI / wizard & async flow
src/platform/assets/components/UploadModelDialog.vue, src/platform/assets/components/UploadModelFooter.vue, src/platform/assets/components/UploadModelProgress.vue, src/platform/assets/components/UploadModelUrlInput*.vue, src/platform/assets/components/UploadModelConfirmation.vue, src/platform/assets/composables/useUploadModelWizard.ts, src/platform/assets/composables/useModelUpload.ts
Add preview upload helper and async upload path using uploadAssetAsync (processing vs success); rename statusresult; UploadModelFooter adds importAnother; useUploadModelWizard exposes uploadModel(): Promise<boolean>, uploadPreviewImage, refreshModelCaches, resetWizard; useModelUpload signature changed to accept onUploadSuccess.
Feature flags & remote config types
src/composables/useFeatureFlags.ts, src/platform/remoteConfig/types.ts
Split asset_update_options_enabled into asset_deletion_enabled and asset_rename_enabled; add async_model_upload_enabled; update enum, getters, and RemoteConfig fields.
Top-up / subscription / pricing
src/components/dialog/content/TopUpCreditsDialogContent.vue, src/locales/en/main.json, src/platform/cloud/subscription/components/{PricingTable.vue,SubscriptionPanel.vue}, src/platform/cloud/subscription/utils/subscriptionTierRank.ts, src/platform/cloud/subscription/utils/subscriptionTierRank.test.ts
Rework TopUp dialog (USD↔credits two-way binding, presets, validation, buy flow); add credits/pricing locale keys; add getPlanRank/isPlanDowngrade utilities and tests; PricingTable handles downgrade detection; SubscriptionPanel layout/refresh/display updates.
Asset UI / UX tweaks
src/platform/assets/components/* (various)
Styling/layout tweaks: URL validation icons, badge break-all, scrollable modal content, preview sizing/truncation, icon sizing/spacing, upload button sizing, and related class adjustments.
Small UI / component tweaks
src/components/ui/button/button.variants.ts, src/components/widget/layout/BaseModalLayout.vue, src/components/widget/nav/*, src/renderer/extensions/vueNodes/widgets/components/WidgetSelect.vue, src/components/load3d/Load3DScene.vue
Minor class/behavior changes: prepend relative to button base classes, enlarge close button and add scrollbar class in layout, nav icon spacing tweaks, add v-model in WidgetSelect control branch, add min-height to 3D viewer.
Composables / mask editor integration
src/composables/maskeditor/useMaskEditorLoader.ts, src/extensions/core/maskeditor.ts
Skip widgetFilename values starting with $ in loader to avoid internal refs; add openMaskEditorFromClipspace and set ComfyApp.open_maskeditor shim for compatibility.
Settings default change
src/platform/settings/constants/coreSettings.ts
Set Comfy.Validation.Workflows default to constant false (removed cloud-dependent default).
Storybook / workspace deps
.storybook/main.ts, package.json, pnpm-workspace.yaml
Add @storybook/addon-mcp to Storybook addons, devDependencies, and workspace catalog.
CI / GitHub Actions
.github/actions/start-comfyui-server/action.yml, .github/workflows/ci-tests-e2e.yaml, .github/workflows/pr-update-playwright-expectations.yaml
Add composite action to start ComfyUI server; switch CI to containerized runners with artifact upload/download of built frontend-dist; replace cache steps with artifact flows; add report merge and snapshot staging adjustments.
Tests & test infra
tests-ui/**/*, src/stores/assetDownloadStore.test.ts
Add comprehensive tests for assetDownloadStore; adapt many UI tests to mocked Pinia stores; remove manager-progress dialog tests; add Vitest patterns doc and expand test mocks (API event listeners, subscription mocks).
Graph / widget changes
src/composables/graph/useGraphNodeManager.ts, src/renderer/extensions/vueNodes/*, src/lib/litegraph/src/*
Add hasLayoutSize and resizable to widget/node data; change safeWidgetMapper to pass node and app.canvas to widget callbacks and trigger widget redraws; add widget nodeType pass-through and prefer it in WidgetSelectDropdown.
Misc. behavior fixes
src/extensions/core/load3d/LoaderManager.ts, src/extensions/core/load3d/Load3d.ts, src/composables/node/useNodePricing.ts, other small files
Disable frustum culling on SkinnedMesh children; keep load3d active while animations play; add WanReferenceVideoApi pricing and various pricing/widget mapping changes; assorted widget/input behavior fixes.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant UploadUI as Upload UI
    participant AssetSvc as Asset Service
    participant Backend as Backend API
    participant WS as WebSocket
    participant DownloadStore as AssetDownloadStore
    participant TaskSvc as Task Service
    participant ProgressUI as Progress UI (Toast)

    User->>UploadUI: submit model upload (file or URL)
    UploadUI->>AssetSvc: call uploadAssetAsync(params)
    AssetSvc->>Backend: POST /assets/download
    Backend-->>AssetSvc: 202 Accepted with task_id OR 200 with asset
    AssetSvc-->>UploadUI: return async response (task_id) or sync asset
    alt async (task_id)
      UploadUI->>DownloadStore: trackDownload(task_id, modelType, assetName)
      DownloadStore->>ProgressUI: show processing toast
      loop progress updates
        Backend->>WS: emit asset_download events
        WS->>DownloadStore: handleAssetDownload(event)
        DownloadStore-->>ProgressUI: reactive updates (progress/completed/failed)
      end
      DownloadStore->>TaskSvc: poll getTask(task_id) for stale tasks
      TaskSvc-->>DownloadStore: current task state -> handleAssetDownload
    else sync
      UploadUI->>ProgressUI: show success / refresh caches
    end
Loading
sequenceDiagram
    participant User
    participant TopUpUI as TopUp Credits Dialog
    participant Conversion as USD↔Credits
    participant Billing as Billing/Checkout
    participant DialogSvc as Dialog Service

    User->>TopUpUI: enter amount or pick preset
    TopUpUI->>Conversion: usdToCredits / creditsToUsd
    Conversion-->>TopUpUI: update credits / USD display
    TopUpUI->>TopUpUI: validate min/max, toggle Buy button
    User->>TopUpUI: click Buy
    TopUpUI->>Billing: open checkout (pricingUrl)
    TopUpUI->>DialogSvc: close dialog, open subscription panel
    DialogSvc-->>User: navigate to subscription/checkout
Loading

Possibly related PRs

Suggested reviewers

  • jtydhr88
  • shinshin86
  • bigcat88
  • trsommer
  • KarryCharon
  • viva-jinyi
  • Yorha4D
  • jojodecayz
  • ltdrdata
  • brucew4yn3rp
✨ Finishing touches
  • 📝 Generate docstrings

Comment @coderabbitai help to get the list of available commands and usage tips.

…loads (#7981)

Backport of #7926 to cloud/1.36

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-7981-backport-cloud-1-36-feat-add-polling-fallback-for-stale-asset-downloads-2e76d73d365081a983a4e5a8683ae2c9)
by [Unito](https://www.unito.io)

Co-authored-by: Amp <[email protected]>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🤖 Fix all issues with AI agents
In @tests-ui/vitest-patterns.md:
- Around line 100-116: Replace the usage of the Jest alias `it` with the
project-preferred `test` in the polling spec: change the test declaration
`it('polls after delay', async () => { ... })` to `test('polls after delay',
async () => { ... })`, leaving the surrounding hooks (`beforeEach`, `afterEach`)
and assertions unchanged so the behavior and timers logic (vi.useFakeTimers,
vi.advanceTimersByTimeAsync, expect(mockService.fetch)) remain identical.
- Around line 13-29: The example imports and uses "it" which violates the
project's vitest convention; update the import list to replace "it" with "test"
and ensure any example test declarations (e.g., the placeholder test block in
the describe) use "test" instead of "it" so the symbol names in the imports and
usages (the import line with it, and any subsequent test declarations) are
consistent with the project's preferred pattern.
- Around line 76-93: Replace the `it('handles events', ...)` call with
`test('handles events', ...)` to match project conventions; update the test
declaration where `dispatch` and `getEventHandler` are used so the test harness
invokes `test` instead of `it`, leaving the test body (store setup, dispatch({
field: 'value' }), and expect(store.items).toHaveLength(1)) unchanged.
- Around line 62-69: Replace the use of the BDD alias "it" with the project's
preferred "test" function for the test case that currently reads like
"it('handles success', ...)" so the file uses "test('handles success', ...)"
instead; locate the instance around the test that mocks myService.doThing
(vi.mocked(myService.doThing).mockResolvedValue(...)) and change only the test
function name to maintain consistency with project conventions.
📜 Review details

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2d04cf4 and 7f83af3.

📒 Files selected for processing (8)
  • AGENTS.md
  • src/components/honeyToast/HoneyToast.stories.ts
  • src/components/toast/ProgressToastItem.stories.ts
  • src/platform/tasks/services/taskService.ts
  • src/schemas/apiSchema.ts
  • src/stores/assetDownloadStore.test.ts
  • src/stores/assetDownloadStore.ts
  • tests-ui/vitest-patterns.md
🧰 Additional context used
📓 Path-based instructions (14)
src/**/*.{vue,ts}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

src/**/*.{vue,ts}: Leverage VueUse functions for performance-enhancing styles
Implement proper error handling
Use vue-i18n in composition API for any string literals. Place new translation entries in src/locales/en/main.json

Files:

  • src/platform/tasks/services/taskService.ts
  • src/stores/assetDownloadStore.test.ts
  • src/components/honeyToast/HoneyToast.stories.ts
  • src/schemas/apiSchema.ts
  • src/components/toast/ProgressToastItem.stories.ts
  • src/stores/assetDownloadStore.ts
src/**/*.ts

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

src/**/*.ts: Use es-toolkit for utility functions
Use TypeScript for type safety

src/**/*.ts: Derive component types using vue-component-type-helpers (ComponentProps, ComponentSlots) instead of separate type files
Use es-toolkit for utility functions
Minimize the surface area (exported values) of each module and composable
Favor pure functions, especially testable ones

Files:

  • src/platform/tasks/services/taskService.ts
  • src/stores/assetDownloadStore.test.ts
  • src/components/honeyToast/HoneyToast.stories.ts
  • src/schemas/apiSchema.ts
  • src/components/toast/ProgressToastItem.stories.ts
  • src/stores/assetDownloadStore.ts
src/**/{services,composables}/**/*.{ts,tsx}

📄 CodeRabbit inference engine (src/CLAUDE.md)

src/**/{services,composables}/**/*.{ts,tsx}: Use api.apiURL() for backend endpoints instead of constructing URLs directly
Use api.fileURL() for static file access instead of constructing URLs directly

Files:

  • src/platform/tasks/services/taskService.ts
src/**/*.{ts,tsx,vue}

📄 CodeRabbit inference engine (src/CLAUDE.md)

src/**/*.{ts,tsx,vue}: Sanitize HTML with DOMPurify to prevent XSS attacks
Avoid using @ts-expect-error; use proper TypeScript types instead
Use es-toolkit for utility functions instead of other utility libraries
Implement proper TypeScript types throughout the codebase

src/**/*.{ts,tsx,vue}: Use separate import type statements instead of inline type in mixed imports
Apply Prettier formatting with 2-space indentation, single quotes, no trailing semicolons, 80-character width
Sort and group imports by plugin, run pnpm format before committing
Never use any type - use proper TypeScript types
Never use as any type assertions - fix the underlying type issue
Write code that is expressive and self-documenting - avoid unnecessary comments
Do not add or retain redundant comments - clean as you go
Avoid mutable state - prefer immutability and assignment at point of declaration
Watch out for Code Smells and refactor to avoid them

Files:

  • src/platform/tasks/services/taskService.ts
  • src/stores/assetDownloadStore.test.ts
  • src/components/honeyToast/HoneyToast.stories.ts
  • src/schemas/apiSchema.ts
  • src/components/toast/ProgressToastItem.stories.ts
  • src/stores/assetDownloadStore.ts
src/**/*.{vue,ts,tsx}

📄 CodeRabbit inference engine (src/CLAUDE.md)

Follow Vue 3 composition API style guide

Files:

  • src/platform/tasks/services/taskService.ts
  • src/stores/assetDownloadStore.test.ts
  • src/components/honeyToast/HoneyToast.stories.ts
  • src/schemas/apiSchema.ts
  • src/components/toast/ProgressToastItem.stories.ts
  • src/stores/assetDownloadStore.ts
src/**/*.{ts,vue}

📄 CodeRabbit inference engine (AGENTS.md)

src/**/*.{ts,vue}: Use ref for reactive state, computed() for derived values, and watch/watchEffect for side effects in Composition API
Avoid using ref with watch if a computed would suffice - minimize refs and derived state
Use provide/inject for dependency injection only when simpler alternatives (Store or shared composable) won't work
Leverage VueUse functions for performance-enhancing composables
Use VueUse function for useI18n in composition API for string literals

Files:

  • src/platform/tasks/services/taskService.ts
  • src/stores/assetDownloadStore.test.ts
  • src/components/honeyToast/HoneyToast.stories.ts
  • src/schemas/apiSchema.ts
  • src/components/toast/ProgressToastItem.stories.ts
  • src/stores/assetDownloadStore.ts
src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

src/**/*.{ts,tsx}: Keep functions short and functional
Minimize nesting (if statements, for loops, etc.)
Use function declarations instead of function expressions when possible

Files:

  • src/platform/tasks/services/taskService.ts
  • src/stores/assetDownloadStore.test.ts
  • src/components/honeyToast/HoneyToast.stories.ts
  • src/schemas/apiSchema.ts
  • src/components/toast/ProgressToastItem.stories.ts
  • src/stores/assetDownloadStore.ts
src/**/stores/**/*.{ts,tsx}

📄 CodeRabbit inference engine (src/CLAUDE.md)

src/**/stores/**/*.{ts,tsx}: Maintain clear public interfaces and restrict extension access in stores
Use TypeScript for type safety in state management stores

Files:

  • src/stores/assetDownloadStore.test.ts
  • src/stores/assetDownloadStore.ts
src/stores/**/*.ts

📄 CodeRabbit inference engine (AGENTS.md)

Name Pinia stores as *Store.ts (e.g., nodeStore.ts)

Files:

  • src/stores/assetDownloadStore.test.ts
  • src/stores/assetDownloadStore.ts
+(tests-ui|src)/**/*.test.ts

📄 CodeRabbit inference engine (AGENTS.md)

+(tests-ui|src)/**/*.test.ts: Unit and component tests belong in tests-ui/ or src/**/*.test.ts using Vitest
Write tests for all changes, especially bug fixes to catch future regressions
Do not write tests dependent on non-behavioral features like utility classes or styles
Do not write tests that just test the mocks - ensure tests fail when code behaves unexpectedly
Leverage Vitest's utilities for mocking where possible
Keep module mocks contained - do not use global mutable state within test files; use vi.hoisted() if necessary
Use Vue Test Utils for Component testing and follow best practices for making components easy to test
Aim for behavioral coverage of critical and new features in unit tests

Files:

  • src/stores/assetDownloadStore.test.ts
+(tests-ui|src|browser_tests)/**/*.+(test.ts|spec.ts)

📄 CodeRabbit inference engine (AGENTS.md)

+(tests-ui|src|browser_tests)/**/*.+(test.ts|spec.ts): Do not write change detector tests - avoid tests that only assert default values
Be parsimonious in testing - do not write redundant tests
Don't Mock What You Don't Own

Files:

  • src/stores/assetDownloadStore.test.ts
src/**/{composables,components}/**/*.{ts,tsx,vue}

📄 CodeRabbit inference engine (src/CLAUDE.md)

Clean up subscriptions in state management to prevent memory leaks

Files:

  • src/components/honeyToast/HoneyToast.stories.ts
  • src/components/toast/ProgressToastItem.stories.ts
src/**/{components,composables}/**/*.{ts,tsx,vue}

📄 CodeRabbit inference engine (src/CLAUDE.md)

Use vue-i18n for ALL user-facing strings by adding them to src/locales/en/main.json

Files:

  • src/components/honeyToast/HoneyToast.stories.ts
  • src/components/toast/ProgressToastItem.stories.ts
src/components/**/*.{vue,ts,js}

📄 CodeRabbit inference engine (src/components/CLAUDE.md)

src/components/**/*.{vue,ts,js}: Use existing VueUse composables (such as useElementHover) instead of manually managing event listeners
Use useIntersectionObserver for visibility detection instead of custom scroll handlers
Use vue-i18n for ALL UI strings

Files:

  • src/components/honeyToast/HoneyToast.stories.ts
  • src/components/toast/ProgressToastItem.stories.ts
🧠 Learnings (80)
📓 Common learnings
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7897
File: src/stores/assetDownloadStore.ts:117-117
Timestamp: 2026-01-08T21:44:05.435Z
Learning: In src/stores/assetDownloadStore.ts, the api.addEventListener('asset_download', handleAssetDownload) call at module level is intentional and should persist for the application lifetime since it's a singleton Pinia store managing global WebSocket events.
📚 Learning: 2026-01-06T19:20:56.167Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7746
File: src/platform/assets/services/assetService.ts:484-491
Timestamp: 2026-01-06T19:20:56.167Z
Learning: In `src/platform/assets/services/assetService.ts`, prefer using `schema.safeParse()` over `schema.parse()` for validating API responses to avoid throwing ZodError with internal schema details; follow the existing pattern: call `safeParse()`, check `result.success`, and use `fromZodError(result.error)` to format error messages for logging while throwing user-friendly errors.

Applied to files:

  • src/platform/tasks/services/taskService.ts
  • src/schemas/apiSchema.ts
📚 Learning: 2025-12-09T03:39:54.501Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7169
File: src/platform/remote/comfyui/jobs/jobTypes.ts:1-107
Timestamp: 2025-12-09T03:39:54.501Z
Learning: In the ComfyUI_frontend project, Zod is on v3.x. Do not suggest Zod v4 standalone validators (z.uuid, z.ulid, z.cuid2, z.nanoid) until an upgrade to Zod 4 is performed. When reviewing TypeScript files (e.g., src/platform/remote/comfyui/jobs/jobTypes.ts) validate against Zod 3 capabilities and avoid introducing v4-specific features; flag any proposal to upgrade or incorporate v4-only validators and propose staying with compatible 3.x patterns.

Applied to files:

  • src/platform/tasks/services/taskService.ts
  • src/stores/assetDownloadStore.test.ts
  • src/components/honeyToast/HoneyToast.stories.ts
  • src/schemas/apiSchema.ts
  • src/components/toast/ProgressToastItem.stories.ts
  • src/stores/assetDownloadStore.ts
📚 Learning: 2025-12-13T11:03:11.264Z
Learnt from: christian-byrne
Repo: Comfy-Org/ComfyUI_frontend PR: 7416
File: src/stores/imagePreviewStore.ts:5-7
Timestamp: 2025-12-13T11:03:11.264Z
Learning: In the ComfyUI_frontend repository, lint rules require keeping 'import type' statements separate from non-type imports, even if importing from the same module. Do not suggest consolidating them into a single import statement. Ensure type imports remain on their own line (import type { ... } from 'module') and regular imports stay on separate lines.

Applied to files:

  • src/platform/tasks/services/taskService.ts
  • src/stores/assetDownloadStore.test.ts
  • src/components/honeyToast/HoneyToast.stories.ts
  • src/schemas/apiSchema.ts
  • src/components/toast/ProgressToastItem.stories.ts
  • src/stores/assetDownloadStore.ts
📚 Learning: 2025-12-17T00:40:09.635Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7537
File: src/components/ui/button/Button.stories.ts:45-55
Timestamp: 2025-12-17T00:40:09.635Z
Learning: Prefer pure function declarations over function expressions (e.g., use function foo() { ... } instead of const foo = () => { ... }) for pure functions in the repository. Function declarations are more functional-leaning, offer better hoisting clarity, and can improve readability and tooling consistency. Apply this guideline across TypeScript files in Comfy-Org/ComfyUI_frontend, including story and UI component code, except where a function expression is semantically required (e.g., callbacks, higher-order functions with closures).

Applied to files:

  • src/platform/tasks/services/taskService.ts
  • src/stores/assetDownloadStore.test.ts
  • src/components/honeyToast/HoneyToast.stories.ts
  • src/schemas/apiSchema.ts
  • src/components/toast/ProgressToastItem.stories.ts
  • src/stores/assetDownloadStore.ts
📚 Learning: 2025-12-30T22:22:33.836Z
Learnt from: kaili-yang
Repo: Comfy-Org/ComfyUI_frontend PR: 7805
File: src/composables/useCoreCommands.ts:439-439
Timestamp: 2025-12-30T22:22:33.836Z
Learning: When accessing reactive properties from Pinia stores in TypeScript files, avoid using .value on direct property access (e.g., useStore().isOverlayExpanded). Pinia auto-wraps refs when accessed directly, returning the primitive value. The .value accessor is only needed when destructuring store properties or when using storeToRefs().

Applied to files:

  • src/platform/tasks/services/taskService.ts
  • src/stores/assetDownloadStore.test.ts
  • src/components/honeyToast/HoneyToast.stories.ts
  • src/schemas/apiSchema.ts
  • src/components/toast/ProgressToastItem.stories.ts
  • src/stores/assetDownloadStore.ts
📚 Learning: 2025-12-11T12:25:15.470Z
Learnt from: christian-byrne
Repo: Comfy-Org/ComfyUI_frontend PR: 7358
File: src/components/dialog/content/signin/SignUpForm.vue:45-54
Timestamp: 2025-12-11T12:25:15.470Z
Learning: This repository uses CI automation to format code (pnpm format). Do not include manual formatting suggestions in code reviews for Comfy-Org/ComfyUI_frontend. If formatting issues are detected, rely on the CI formatter or re-run pnpm format. Focus reviews on correctness, readability, performance, accessibility, and maintainability rather than style formatting.

Applied to files:

  • src/platform/tasks/services/taskService.ts
  • src/stores/assetDownloadStore.test.ts
  • src/components/honeyToast/HoneyToast.stories.ts
  • src/schemas/apiSchema.ts
  • src/components/toast/ProgressToastItem.stories.ts
  • src/stores/assetDownloadStore.ts
📚 Learning: 2026-01-12T17:39:27.738Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7906
File: src/components/sidebar/tabs/AssetsSidebarTab.vue:545-552
Timestamp: 2026-01-12T17:39:27.738Z
Learning: In Vue/TypeScript files (src/**/*.{ts,tsx,vue}), prefer if/else statements over ternary operators when performing side effects or actions (e.g., mutating state, calling methods with side effects). Ternaries should be reserved for computing and returning values.

Applied to files:

  • src/platform/tasks/services/taskService.ts
  • src/stores/assetDownloadStore.test.ts
  • src/components/honeyToast/HoneyToast.stories.ts
  • src/schemas/apiSchema.ts
  • src/components/toast/ProgressToastItem.stories.ts
  • src/stores/assetDownloadStore.ts
📚 Learning: 2026-01-08T21:44:05.435Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7897
File: src/stores/assetDownloadStore.ts:117-117
Timestamp: 2026-01-08T21:44:05.435Z
Learning: In src/stores/assetDownloadStore.ts, the api.addEventListener('asset_download', handleAssetDownload) call at module level is intentional and should persist for the application lifetime since it's a singleton Pinia store managing global WebSocket events.

Applied to files:

  • src/stores/assetDownloadStore.test.ts
  • src/stores/assetDownloadStore.ts
📚 Learning: 2026-01-09T23:59:06.942Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7926
File: src/stores/assetDownloadStore.ts:101-126
Timestamp: 2026-01-09T23:59:06.942Z
Learning: In src/stores/assetDownloadStore.ts, the empty catch block in pollSingleDownload within pollStaleDownloads is intentional. Do not suggest adding error logging or handling for polling failures in this function.

Applied to files:

  • src/stores/assetDownloadStore.test.ts
📚 Learning: 2025-11-24T19:47:22.909Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: browser_tests/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:22.909Z
Learning: Applies to browser_tests/**/*.{e2e,spec}.{ts,tsx,js,jsx} : Check assets/ directory for test data when writing tests

Applied to files:

  • src/stores/assetDownloadStore.test.ts
📚 Learning: 2026-01-10T00:24:17.695Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-10T00:24:17.695Z
Learning: Applies to +(tests-ui|src)/**/*.test.ts : Write tests for all changes, especially bug fixes to catch future regressions

Applied to files:

  • src/stores/assetDownloadStore.test.ts
  • tests-ui/vitest-patterns.md
📚 Learning: 2026-01-10T00:24:17.695Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-10T00:24:17.695Z
Learning: Applies to src/stores/**/*.ts : Name Pinia stores as `*Store.ts` (e.g., `nodeStore.ts`)

Applied to files:

  • src/stores/assetDownloadStore.test.ts
  • src/stores/assetDownloadStore.ts
📚 Learning: 2026-01-10T00:24:17.695Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-10T00:24:17.695Z
Learning: Applies to +(tests-ui|src)/**/*.test.ts : Aim for behavioral coverage of critical and new features in unit tests

Applied to files:

  • src/stores/assetDownloadStore.test.ts
  • AGENTS.md
  • tests-ui/vitest-patterns.md
📚 Learning: 2026-01-10T00:24:17.695Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-10T00:24:17.695Z
Learning: Applies to +(tests-ui|src)/**/*.test.ts : Keep module mocks contained - do not use global mutable state within test files; use `vi.hoisted()` if necessary

Applied to files:

  • src/stores/assetDownloadStore.test.ts
  • tests-ui/vitest-patterns.md
  • src/components/honeyToast/HoneyToast.stories.ts
  • src/components/toast/ProgressToastItem.stories.ts
📚 Learning: 2025-11-24T19:47:34.324Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:34.324Z
Learning: Applies to src/**/stores/**/*.{ts,tsx} : Maintain clear public interfaces and restrict extension access in stores

Applied to files:

  • src/stores/assetDownloadStore.test.ts
  • src/stores/assetDownloadStore.ts
📚 Learning: 2026-01-10T00:24:17.695Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-10T00:24:17.695Z
Learning: Applies to +(tests-ui|src)/**/*.test.ts : Leverage Vitest's utilities for mocking where possible

Applied to files:

  • src/stores/assetDownloadStore.test.ts
  • AGENTS.md
  • tests-ui/vitest-patterns.md
  • src/components/honeyToast/HoneyToast.stories.ts
  • src/components/toast/ProgressToastItem.stories.ts
📚 Learning: 2026-01-10T00:24:17.695Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-10T00:24:17.695Z
Learning: Applies to +(tests-ui|src)/**/*.test.ts : Do not write tests that just test the mocks - ensure tests fail when code behaves unexpectedly

Applied to files:

  • src/stores/assetDownloadStore.test.ts
  • tests-ui/vitest-patterns.md
  • src/components/honeyToast/HoneyToast.stories.ts
  • src/components/toast/ProgressToastItem.stories.ts
📚 Learning: 2025-12-10T03:09:13.807Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7303
File: src/components/topbar/CurrentUserPopover.test.ts:199-205
Timestamp: 2025-12-10T03:09:13.807Z
Learning: In test files, prefer selecting or asserting on accessible properties (text content, aria-label, role, accessible name) over data-testid attributes. This ensures tests validate actual user-facing behavior and accessibility, reducing reliance on implementation details like test IDs.

Applied to files:

  • src/stores/assetDownloadStore.test.ts
📚 Learning: 2025-12-30T01:31:04.927Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7797
File: tests-ui/tests/lib/litegraph/src/widgets/ComboWidget.test.ts:648-648
Timestamp: 2025-12-30T01:31:04.927Z
Learning: In Vitest v4, when mocking functions that may be called as constructors (using new), the mock implementation must use function() or class syntax rather than an arrow function. Arrow mocks can cause '<anonymous> is not a constructor' errors. This is a breaking change from Vitest v3 where mocks could use an arrow function. Apply this guideline to test files that mock constructor-like calls (e.g., in tests under tests-ui, such as ComboWidget.test.ts) and ensure mock implementations are defined with function() { ... } or class { ... } to preserve constructor behavior.

Applied to files:

  • src/stores/assetDownloadStore.test.ts
📚 Learning: 2026-01-08T02:40:15.482Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7894
File: src/renderer/extensions/vueNodes/widgets/components/WidgetToggleSwitch.test.ts:11-14
Timestamp: 2026-01-08T02:40:15.482Z
Learning: In TypeScript test files (e.g., any test under src), avoid duplicating interface/type definitions. Import real type definitions from the component modules under test and reference them directly, so there is a single source of truth and to prevent type drift. This improves maintainability and consistency across tests.

Applied to files:

  • src/stores/assetDownloadStore.test.ts
📚 Learning: 2026-01-09T02:07:54.558Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7898
File: src/composables/usePaste.test.ts:248-248
Timestamp: 2026-01-09T02:07:54.558Z
Learning: In test files (e.g., any .test.ts or .test.tsx under src/...), when you create mock objects that partially implement an interface (such as LGraphNode), prefer casting with as Partial<InterfaceType> as InterfaceType rather than as any or as unknown as InterfaceType. This makes the incomplete implementation explicit while preserving type safety, improving readability and maintainability of tests.

Applied to files:

  • src/stores/assetDownloadStore.test.ts
📚 Learning: 2025-11-24T19:47:34.324Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:34.324Z
Learning: Applies to src/**/*.{vue,ts,tsx} : Follow Vue 3 composition API style guide

Applied to files:

  • AGENTS.md
📚 Learning: 2025-11-24T19:47:02.860Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-24T19:47:02.860Z
Learning: Applies to src/**/*.vue : Follow Vue 3 style guide and naming conventions

Applied to files:

  • AGENTS.md
📚 Learning: 2025-11-24T19:47:02.860Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-24T19:47:02.860Z
Learning: Applies to src/**/*.vue : Use the Vue 3 Composition API instead of the Options API when writing Vue components (exception: when overriding or extending PrimeVue components for compatibility)

Applied to files:

  • AGENTS.md
📚 Learning: 2025-11-24T19:47:34.324Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:34.324Z
Learning: Applies to src/**/{components,composables}/**/*.{ts,tsx,vue} : Use vue-i18n for ALL user-facing strings by adding them to `src/locales/en/main.json`

Applied to files:

  • AGENTS.md
📚 Learning: 2026-01-10T00:24:17.695Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-10T00:24:17.695Z
Learning: Applies to src/**/*.{ts,tsx,vue} : Use separate `import type` statements instead of inline `type` in mixed imports

Applied to files:

  • AGENTS.md
📚 Learning: 2025-11-24T19:47:02.860Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-24T19:47:02.860Z
Learning: Applies to src/**/*.{vue,ts} : Use vue-i18n in composition API for any string literals. Place new translation entries in src/locales/en/main.json

Applied to files:

  • AGENTS.md
📚 Learning: 2026-01-10T00:24:17.695Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-10T00:24:17.695Z
Learning: Applies to src/**/*.{ts,vue} : Use `ref` for reactive state, `computed()` for derived values, and `watch`/`watchEffect` for side effects in Composition API

Applied to files:

  • AGENTS.md
📚 Learning: 2026-01-10T00:24:17.695Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-10T00:24:17.695Z
Learning: Applies to +(tests-ui|src)/**/*.test.ts : Use Vue Test Utils for Component testing and follow best practices for making components easy to test

Applied to files:

  • AGENTS.md
  • tests-ui/vitest-patterns.md
📚 Learning: 2026-01-10T00:24:17.695Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-10T00:24:17.695Z
Learning: Applies to src/**/*.{ts,tsx,vue} : Sort and group imports by plugin, run `pnpm format` before committing

Applied to files:

  • AGENTS.md
📚 Learning: 2026-01-10T00:24:17.695Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-10T00:24:17.695Z
Learning: Applies to src/**/*.{ts,tsx,vue} : Apply Prettier formatting with 2-space indentation, single quotes, no trailing semicolons, 80-character width

Applied to files:

  • AGENTS.md
📚 Learning: 2025-12-13T11:03:21.073Z
Learnt from: christian-byrne
Repo: Comfy-Org/ComfyUI_frontend PR: 7416
File: src/stores/imagePreviewStore.ts:5-7
Timestamp: 2025-12-13T11:03:21.073Z
Learning: In the Comfy-Org/ComfyUI_frontend repository, linting rules enforce keeping `import type` statements separate from regular `import` statements, even when importing from the same module. Do not suggest consolidating them into a single import statement.

Applied to files:

  • AGENTS.md
📚 Learning: 2025-11-24T19:47:56.371Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/lib/litegraph/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:56.371Z
Learning: Be sure to typecheck when you're done making a series of code changes using `pnpm typecheck`

Applied to files:

  • AGENTS.md
📚 Learning: 2025-11-24T19:47:56.371Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/lib/litegraph/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:56.371Z
Learning: Applies to src/lib/litegraph/**/*.{js,ts,jsx,tsx} : Run ESLint instead of manually figuring out whitespace fixes or other trivial style concerns using the `pnpm lint:fix` command

Applied to files:

  • AGENTS.md
📚 Learning: 2026-01-10T00:24:17.695Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-10T00:24:17.695Z
Learning: Run quality gates before PR: `pnpm lint`, `pnpm typecheck`, `pnpm knip`, and ensure relevant tests pass

Applied to files:

  • AGENTS.md
📚 Learning: 2025-11-24T19:47:34.324Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:34.324Z
Learning: Applies to src/**/*.{ts,tsx,vue} : Implement proper TypeScript types throughout the codebase

Applied to files:

  • AGENTS.md
📚 Learning: 2026-01-10T00:24:17.695Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-10T00:24:17.695Z
Learning: Applies to src/**/*.{ts,tsx,vue} : Watch out for Code Smells and refactor to avoid them

Applied to files:

  • AGENTS.md
📚 Learning: 2025-11-24T19:47:45.616Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/components/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:45.616Z
Learning: Applies to src/components/**/*.vue : Define proper props and emits definitions in Vue components

Applied to files:

  • AGENTS.md
📚 Learning: 2026-01-10T00:24:17.695Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-10T00:24:17.695Z
Learning: Applies to src/**/*.vue : Prefer `defineModel` over separately defining a prop and emit for v-model bindings

Applied to files:

  • AGENTS.md
📚 Learning: 2026-01-10T00:24:17.695Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-10T00:24:17.695Z
Learning: Applies to src/**/*.vue : Use TypeScript Vue 3.5 style default prop declaration with reactive props destructuring - avoid `withDefaults` or runtime props

Applied to files:

  • AGENTS.md
📚 Learning: 2026-01-10T00:24:17.695Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-10T00:24:17.695Z
Learning: Applies to src/**/*.vue : Use same-name shorthand for slot prop bindings (e.g., `:isExpanded` instead of `:is-expanded="isExpanded"`)

Applied to files:

  • AGENTS.md
📚 Learning: 2025-11-24T19:47:02.860Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-24T19:47:02.860Z
Learning: Applies to src/**/*.vue : Implement proper props and emits definitions

Applied to files:

  • AGENTS.md
📚 Learning: 2025-11-24T19:47:45.616Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/components/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:45.616Z
Learning: Applies to src/components/**/*.vue : Destructure props using Vue 3.5 style in Vue components

Applied to files:

  • AGENTS.md
📚 Learning: 2026-01-10T00:24:17.695Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-10T00:24:17.695Z
Learning: Applies to src/**/*.{ts,vue} : Avoid using `ref` with `watch` if a `computed` would suffice - minimize refs and derived state

Applied to files:

  • AGENTS.md
📚 Learning: 2025-11-24T19:47:02.860Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-24T19:47:02.860Z
Learning: Applies to src/**/*.vue : Utilize ref and reactive for reactive state

Applied to files:

  • AGENTS.md
📚 Learning: 2026-01-10T00:24:17.695Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-10T00:24:17.695Z
Learning: Applies to src/**/*.vue : Use vue-i18n for all string literals in Vue components - place translation entries in `src/locales/en/main.json`

Applied to files:

  • AGENTS.md
📚 Learning: 2025-11-24T19:47:45.616Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/components/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:45.616Z
Learning: Applies to src/components/**/*.{vue,ts,js} : Use vue-i18n for ALL UI strings

Applied to files:

  • AGENTS.md
📚 Learning: 2026-01-10T00:24:17.695Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-10T00:24:17.695Z
Learning: Applies to src/**/*.vue : Avoid new usage of PrimeVue components

Applied to files:

  • AGENTS.md
📚 Learning: 2026-01-10T00:24:17.695Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-10T00:24:17.695Z
Learning: Applies to src/**/*.{ts,vue} : Use VueUse function for useI18n in composition API for string literals

Applied to files:

  • AGENTS.md
📚 Learning: 2026-01-10T00:24:17.695Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-10T00:24:17.695Z
Learning: Applies to src/**/*.vue : Use Tailwind's plurals system via i18n instead of hardcoding pluralization in templates

Applied to files:

  • AGENTS.md
📚 Learning: 2026-01-10T00:24:17.695Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-10T00:24:17.695Z
Learning: Applies to browser_tests/**/*.spec.ts : Follow Playwright best practices for E2E testing

Applied to files:

  • AGENTS.md
📚 Learning: 2025-11-24T19:48:09.318Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: .cursor/rules/unit-test.mdc:0-0
Timestamp: 2025-11-24T19:48:09.318Z
Learning: Applies to test/**/*.{test,spec}.{js,ts,jsx,tsx} : Use `vitest` for unit testing in this project

Applied to files:

  • AGENTS.md
  • tests-ui/vitest-patterns.md
📚 Learning: 2026-01-10T00:24:17.695Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-10T00:24:17.695Z
Learning: Applies to +(tests-ui|src)/**/*.test.ts : Unit and component tests belong in `tests-ui/` or `src/**/*.test.ts` using Vitest

Applied to files:

  • AGENTS.md
  • tests-ui/vitest-patterns.md
📚 Learning: 2025-11-24T19:47:22.909Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: browser_tests/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:22.909Z
Learning: Applies to browser_tests/**/*.{e2e,spec}.{ts,tsx,js,jsx} : Test user workflows in browser tests

Applied to files:

  • AGENTS.md
📚 Learning: 2025-11-24T19:47:22.909Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: browser_tests/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:22.909Z
Learning: Applies to browser_tests/**/*.{e2e,spec}.{ts,tsx,js,jsx} : Use Playwright fixtures for browser tests

Applied to files:

  • AGENTS.md
📚 Learning: 2025-11-24T19:47:22.909Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: browser_tests/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:22.909Z
Learning: Applies to browser_tests/**/*.{e2e,spec}.{ts,tsx,js,jsx} : Follow naming conventions for browser tests

Applied to files:

  • AGENTS.md
📚 Learning: 2025-11-24T19:48:09.318Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: .cursor/rules/unit-test.mdc:0-0
Timestamp: 2025-11-24T19:48:09.318Z
Learning: Applies to test/**/*.{test,spec}.{js,ts,jsx,tsx} : Use `test` instead of `it` for defining test cases in vitest

Applied to files:

  • AGENTS.md
  • tests-ui/vitest-patterns.md
📚 Learning: 2026-01-10T00:24:17.695Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-10T00:24:17.695Z
Learning: Applies to src/**/*.vue : Never use `!important` or `!` Tailwind prefix - fix interfering classes instead

Applied to files:

  • AGENTS.md
📚 Learning: 2026-01-10T00:24:17.695Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-10T00:24:17.695Z
Learning: Applies to src/**/*.vue : Use Tailwind fraction utilities instead of arbitrary percentage values (e.g., `w-4/5` instead of `w-[80%]`)

Applied to files:

  • AGENTS.md
📚 Learning: 2025-12-09T21:40:19.792Z
Learnt from: benceruleanlu
Repo: Comfy-Org/ComfyUI_frontend PR: 7297
File: src/components/actionbar/ComfyActionbar.vue:33-43
Timestamp: 2025-12-09T21:40:19.792Z
Learning: In the Comfy-Org/ComfyUI_frontend repository, inline Tailwind CSS class strings, even when long, are acceptable and preferred over extracting them to computed properties when the classes are static. This is a common Tailwind pattern and doesn't need to be flagged as a readability issue.

Applied to files:

  • AGENTS.md
📚 Learning: 2026-01-10T00:24:17.695Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-10T00:24:17.695Z
Learning: Applies to src/**/*.vue : Avoid `<style>` blocks in Vue components - use Tailwind 4 styling instead

Applied to files:

  • AGENTS.md
📚 Learning: 2025-12-18T02:07:44.374Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7598
File: src/components/sidebar/tabs/AssetsSidebarTab.vue:131-131
Timestamp: 2025-12-18T02:07:44.374Z
Learning: Comfy-Org/ComfyUI_frontend uses Tailwind CSS v4 utilities, including the new “safe” overflow-alignment classes. Do not flag items-center-safe, justify-*-safe, or place-*-safe utilities as invalid in src/**/*.vue or stories.

Applied to files:

  • AGENTS.md
📚 Learning: 2026-01-08T02:26:27.225Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7893
File: src/components/button/IconGroup.vue:5-6
Timestamp: 2026-01-08T02:26:27.225Z
Learning: In Comfy-Org/ComfyUI_frontend, the `cn` utility function from `@/utils/tailwindUtil` uses `tailwind-merge`, which intelligently resolves conflicting Tailwind classes by keeping the last one. When a component uses `cn('base-classes bg-default', propClass)`, if `propClass` contains a conflicting background class, `tailwind-merge` will correctly override `bg-default` with the value from `propClass`. This additive pattern is correct and intentional, following the shadcn-ui convention.
</learning]

Applied to files:

  • AGENTS.md
📚 Learning: 2026-01-10T00:24:17.695Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-10T00:24:17.695Z
Learning: Applies to src/**/*.vue : Use `cn()` utility from `@/utils/tailwindUtil` for merging Tailwind class names instead of `:class="[]"` or hardcoding

Applied to files:

  • AGENTS.md
📚 Learning: 2026-01-10T00:24:17.695Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-10T00:24:17.695Z
Learning: Applies to src/**/*.vue : Use Tailwind utility classes instead of `dark:` variant - use semantic values from `style.css` theme (e.g., `bg-node-component-surface`)

Applied to files:

  • AGENTS.md
📚 Learning: 2025-12-18T20:39:30.137Z
Learnt from: jtydhr88
Repo: Comfy-Org/ComfyUI_frontend PR: 7621
File: src/components/load3d/Load3DScene.vue:4-4
Timestamp: 2025-12-18T20:39:30.137Z
Learning: In src/components/load3d/Load3DScene.vue, the scoped `<style>` block with `!important` declarations for the canvas element is necessary because Three.js dynamically creates the canvas with inline styles, preventing direct application of Tailwind classes. This is a valid exception to the Tailwind-only styling guideline.

Applied to files:

  • AGENTS.md
📚 Learning: 2025-11-24T19:47:45.616Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/components/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:45.616Z
Learning: Applies to src/components/**/*.{vue,css} : Use Tailwind CSS only for styling (no custom CSS)

Applied to files:

  • AGENTS.md
📚 Learning: 2026-01-10T00:24:17.695Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-10T00:24:17.695Z
Learning: Applies to +(tests-ui|src|browser_tests)/**/*.+(test.ts|spec.ts) : Do not write change detector tests - avoid tests that only assert default values

Applied to files:

  • tests-ui/vitest-patterns.md
📚 Learning: 2025-11-24T19:48:09.318Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: .cursor/rules/unit-test.mdc:0-0
Timestamp: 2025-11-24T19:48:09.318Z
Learning: Applies to test/**/*.{test,spec}.{js,ts,jsx,tsx} : Prefer the use of `test.extend` over loose variables; import `test as baseTest` from `vitest`

Applied to files:

  • tests-ui/vitest-patterns.md
📚 Learning: 2026-01-10T00:24:17.695Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-10T00:24:17.695Z
Learning: Applies to +(tests-ui|src|browser_tests)/**/*.+(test.ts|spec.ts) : Be parsimonious in testing - do not write redundant tests

Applied to files:

  • tests-ui/vitest-patterns.md
  • src/components/honeyToast/HoneyToast.stories.ts
📚 Learning: 2026-01-10T00:24:17.695Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-10T00:24:17.695Z
Learning: Applies to +(tests-ui|src|browser_tests)/**/*.+(test.ts|spec.ts) : Don't Mock What You Don't Own

Applied to files:

  • tests-ui/vitest-patterns.md
  • src/components/honeyToast/HoneyToast.stories.ts
  • src/components/toast/ProgressToastItem.stories.ts
📚 Learning: 2025-11-24T19:48:09.318Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: .cursor/rules/unit-test.mdc:0-0
Timestamp: 2025-11-24T19:48:09.318Z
Learning: Applies to test/**/*.{test,spec}.{js,ts,jsx,tsx} : Mocks should be cleanly written and easy to understand, with reusable mocks where possible

Applied to files:

  • src/components/honeyToast/HoneyToast.stories.ts
  • src/components/toast/ProgressToastItem.stories.ts
📚 Learning: 2025-11-24T19:47:56.371Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/lib/litegraph/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:56.371Z
Learning: Applies to src/lib/litegraph/**/*.{test,spec}.{ts,tsx} : Use provided test helpers `createTestSubgraph` and `createTestSubgraphNode` from `./fixtures/subgraphHelpers` for consistent subgraph test setup

Applied to files:

  • src/components/honeyToast/HoneyToast.stories.ts
📚 Learning: 2026-01-08T02:40:22.621Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7894
File: src/renderer/extensions/vueNodes/widgets/components/WidgetToggleSwitch.test.ts:11-14
Timestamp: 2026-01-08T02:40:22.621Z
Learning: In the Comfy-Org/ComfyUI_frontend repository test files: When testing components, import the real type definitions from the component files instead of duplicating interface definitions in the test files. This prevents type drift and maintains consistency.

Applied to files:

  • src/components/honeyToast/HoneyToast.stories.ts
📚 Learning: 2026-01-10T00:24:17.695Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-10T00:24:17.695Z
Learning: Applies to src/**/*.{ts,tsx} : Minimize nesting (if statements, for loops, etc.)

Applied to files:

  • src/components/honeyToast/HoneyToast.stories.ts
📚 Learning: 2026-01-09T23:59:04.777Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7926
File: src/stores/assetDownloadStore.ts:101-126
Timestamp: 2026-01-09T23:59:04.777Z
Learning: In src/stores/assetDownloadStore.ts, the empty catch block in pollSingleDownload within pollStaleDownloads is intentional. Do not suggest adding error logging or error handling for polling failures in this function; treat it as a deliberate swallow of errors to avoid affecting poll logic or user-visible behavior. Reviewers should refrain from proposing fixes to this catch block for this function.

Applied to files:

  • src/stores/assetDownloadStore.ts
📚 Learning: 2026-01-06T19:17:12.355Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7746
File: src/platform/assets/composables/useUploadModelWizard.ts:207-219
Timestamp: 2026-01-06T19:17:12.355Z
Learning: In `src/platform/assets/composables/useUploadModelWizard.ts`, the `uploadModel()` function should have a concurrency guard at the beginning that checks if `isUploading.value` is already true and returns false early to prevent race conditions from multiple rapid invocations.

Applied to files:

  • src/stores/assetDownloadStore.ts
📚 Learning: 2025-12-18T16:03:09.642Z
Learnt from: henrikvilhelmberglund
Repo: Comfy-Org/ComfyUI_frontend PR: 7617
File: src/components/actionbar/ComfyActionbar.vue:301-308
Timestamp: 2025-12-18T16:03:09.642Z
Learning: In the ComfyUI frontend queue system (src/stores/queueStore.ts), the `useQueuePendingTaskCountStore().count` includes the currently executing task. When count = 1, there is only the active/running task with no pending tasks. When count > 1, there is an active task plus pending tasks waiting in the queue. The "Clear Pending Tasks" button should only be enabled when count > 1 to avoid clearing the currently running task, which should be handled by the "Cancel current run" button instead.

Applied to files:

  • src/stores/assetDownloadStore.ts
📚 Learning: 2026-01-10T00:24:17.695Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-10T00:24:17.695Z
Learning: Applies to src/**/*.{ts,tsx,vue} : Never use `as any` type assertions - fix the underlying type issue

Applied to files:

  • src/stores/assetDownloadStore.ts
🧬 Code graph analysis (4)
src/platform/tasks/services/taskService.ts (1)
src/scripts/api.ts (1)
  • api (1309-1309)
src/components/honeyToast/HoneyToast.stories.ts (3)
src/stores/assetDownloadStore.ts (1)
  • AssetDownload (9-19)
src/components/input/SingleSelect.stories.ts (1)
  • Story (40-40)
src/components/toast/ProgressToastItem.stories.ts (1)
  • Completed (66-74)
src/components/toast/ProgressToastItem.stories.ts (2)
src/stores/assetDownloadStore.ts (1)
  • AssetDownload (9-19)
src/components/honeyToast/HoneyToast.stories.ts (1)
  • Completed (161-218)
src/stores/assetDownloadStore.ts (3)
src/schemas/apiSchema.ts (1)
  • AssetDownloadWsMessage (168-168)
src/platform/tasks/services/taskService.ts (1)
  • taskService (70-70)
src/scripts/api.ts (1)
  • api (1309-1309)
🔇 Additional comments (35)
AGENTS.md (5)

66-68: LGTM: Clear import type separation guidance.

The explicit guidance with examples clarifies the expected pattern for import statements, consistent with the project's linting rules.


125-128: LGTM: Excellent Vue 3.5 best practices.

These additions provide clear, actionable guidance on modern Vue composition patterns:

  • defineModel simplifies two-way bindings
  • Template-based slot definitions reduce boilerplate
  • Shorthand syntax improves readability
  • Type helper usage reduces maintenance burden

146-146: LGTM: Important i18n pluralization guidance.

This addition promotes consistent, translatable pluralization patterns across the application.


279-280: LGTM: Clear Tailwind fraction utility guidance.

This addition promotes the use of built-in Tailwind utilities over arbitrary values, improving consistency and maintainability across the codebase.


164-165: Fix broken documentation reference.

The @docs/testing/*.md path is incorrect on two fronts: the @ prefix is non-standard for markdown cross-references (used only for code import aliases in tsconfig.json and package.json) and the docs/testing/ directory does not exist in the codebase. Other documentation cross-references in the codebase use relative paths like docs/FEATURE_FLAGS.md or absolute paths like /docs/extensions/core.md. Either remove the @ prefix and ensure the docs/testing/ directory and files are added, or update the reference to point to existing documentation.

⛔ Skipped due to learnings
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: .cursor/rules/unit-test.mdc:0-0
Timestamp: 2025-11-24T19:48:09.318Z
Learning: Applies to test/**/*.{test,spec}.{js,ts,jsx,tsx} : Tests should be cross-platform compatible using `path.resolve`, `path.join`, and `path.sep` for Windows, macOS, and Linux compatibility
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-10T00:24:17.695Z
Learning: Applies to +(tests-ui|src)/**/*.test.ts : Write tests for all changes, especially bug fixes to catch future regressions
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-10T00:24:17.695Z
Learning: Applies to +(tests-ui|src)/**/*.test.ts : Aim for behavioral coverage of critical and new features in unit tests
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-10T00:24:17.695Z
Learning: Applies to +(tests-ui|src)/**/*.test.ts : Do not write tests that just test the mocks - ensure tests fail when code behaves unexpectedly
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-10T00:24:17.695Z
Learning: Applies to +(tests-ui|src)/**/*.test.ts : Use Vue Test Utils for Component testing and follow best practices for making components easy to test
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-10T00:24:17.695Z
Learning: Applies to +(tests-ui|src)/**/*.test.ts : Leverage Vitest's utilities for mocking where possible
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: .cursor/rules/unit-test.mdc:0-0
Timestamp: 2025-11-24T19:48:09.318Z
Learning: Applies to test/**/*.{test,spec}.{js,ts,jsx,tsx} : Mocks should be cleanly written and easy to understand, with reusable mocks where possible
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-10T00:24:17.695Z
Learning: Applies to +(tests-ui|src|browser_tests)/**/*.+(test.ts|spec.ts) : Be parsimonious in testing - do not write redundant tests
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-10T00:24:17.695Z
Learning: Applies to +(tests-ui|src)/**/*.test.ts : Do not write tests dependent on non-behavioral features like utility classes or styles
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: browser_tests/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:22.909Z
Learning: Applies to browser_tests/**/*.{e2e,spec}.{ts,tsx,js,jsx} : Follow naming conventions for browser tests
tests-ui/vitest-patterns.md (2)

119-138: Good assertion style recommendations.

The recommendations for .toHaveLength() and .toMatchObject() are solid best practices that improve test readability and provide clearer error messages. These patterns will help maintain consistent, high-quality tests across the codebase.


2-4: No changes needed—both glob patterns are correct. The codebase actively uses .spec.ts for browser/e2e tests in browser_tests/ and .test.ts for unit/component tests in src/ and tests-ui/. The documentation appropriately includes both patterns.

src/schemas/apiSchema.ts (2)

138-148: LGTM!

The new zAssetDownloadWsMessage schema is well-structured with appropriate field types. Using z.string() for task_id is correct for Zod 3.x compatibility. The status enum covers all expected lifecycle states, and optional fields are properly marked.


168-168: LGTM!

Type export follows the established pattern in this file.

src/platform/tasks/services/taskService.ts (4)

1-8: LGTM!

Clear documentation explaining the scope limitation of the payload/result schemas. This is good practice for maintainability.


16-42: LGTM!

Schemas are well-defined. Using z.string().uuid() and z.string().datetime() (string refinements) is correct for Zod 3.x compatibility.


46-68: LGTM!

The getTask function follows best practices: uses safeParse() for validation (avoiding ZodError throws with internal details), provides user-friendly error messages via fromZodError, and handles HTTP errors appropriately. Based on learnings, this pattern is preferred.


70-70: LGTM!

Singleton export follows the established pattern used by api in this codebase.

src/stores/assetDownloadStore.test.ts (6)

1-24: LGTM!

Test setup follows best practices: uses vi.hoisted() for shared mock state (per learnings), mocks are cleanly contained, and createTestingPinia with stubActions: false allows testing real store behavior.


32-54: LGTM!

Helper functions are clean, reusable, and use function declarations per guidelines.


68-114: LGTM!

Tests cover key behavioral scenarios: tracking active downloads, transitioning to finished states, and idempotent handling of duplicates. Tests verify actual store behavior rather than just mocking.


116-129: LGTM!

Test verifies the intended behavior of trackDownload - associating a task with a model type for completion tracking.


131-211: LGTM!

Comprehensive polling tests cover success, failure, still-running, and error scenarios. The "continues tracking on polling error" test (lines 201-210) correctly verifies the intentional error-swallowing behavior in pollSingleDownload. Based on learnings, this empty catch is deliberate.


213-224: LGTM!

Test verifies the cleanup functionality.

src/stores/assetDownloadStore.ts (9)

1-8: LGTM!

Imports are properly organized with separate import type statement per repository lint rules.


9-29: LGTM!

AssetDownload interface is appropriately exported for use in components/stories. CompletedDownload is kept internal. Constants have reasonable defaults for polling behavior.


31-48: LGTM!

State structure is well-designed: ref<Map> for reactive downloads, non-reactive Map for internal pendingModelTypes, and computed properties for derived values per Vue 3 best practices.


50-52: LGTM!

Simple function that associates a task with a model type for completion tracking.


54-91: LGTM!

Well-implemented event handler with idempotent terminal state handling, proper state mapping, and bounded completedDownloads list. The lastUpdate timestamp enables stale download detection.


93-129: LGTM!

Polling logic correctly detects stale downloads and fetches updated task state. Replaying updates through handleAssetDownload ensures consistent state management. Based on learnings, the empty catch block (lines 123-125) is intentional to avoid affecting poll logic or user-visible behavior.


131-144: LGTM!

Efficient polling control using VueUse's useIntervalFn with reactive pause/resume based on hasActiveDownloads. Uses if/else for side effects per guidelines.


146-146: LGTM!

Based on learnings, this module-level event listener is intentional and should persist for the application lifetime since it's a singleton Pinia store managing global WebSocket events.


148-164: LGTM!

Clean public interface with appropriate encapsulation. handleAssetDownload is correctly kept internal since it's wired via the global event listener.

src/components/toast/ProgressToastItem.stories.ts (3)

1-21: LGTM!

Proper imports with separate import type statement. Meta configuration with padded layout and decorator wrapper is appropriate for component isolation.


23-35: LGTM!

Clean helper function using function declaration per guidelines. Provides sensible defaults with override capability.


37-95: LGTM!

Stories provide comprehensive coverage of all status states (created, running, completed, failed) plus edge cases (almost complete, long filename). This enables visual verification of the component across its lifecycle.

src/components/honeyToast/HoneyToast.stories.ts (4)

1-9: LGTM!

Imports properly organized with separate import type statements per repository lint rules.


25-39: LGTM!

Meta configuration with fullscreen layout and page framing decorator is appropriate for testing the toast's fixed positioning behavior.


41-293: LGTM!

Stories provide comprehensive coverage of HoneyToast states: collapsed/expanded, completed, error, and hidden. Using render functions with setup() is appropriate here since stories need reactive state (isExpanded).


11-23: Consider extracting shared mock helper.

createMockJob is duplicated in both ProgressToastItem.stories.ts and here. Consider extracting to a shared story utility (e.g., src/stories/mocks/assetDownloadMocks.ts) to reduce duplication and ensure consistency.

♻️ Proposed shared mock location
// src/stories/mocks/assetDownloadMocks.ts
import type { AssetDownload } from '@/stores/assetDownloadStore'

export function createMockAssetDownload(
  overrides: Partial<AssetDownload> = {}
): AssetDownload {
  return {
    taskId: 'task-1',
    assetId: 'asset-1',
    assetName: 'model-v1.safetensors',
    bytesTotal: 1000000,
    bytesDownloaded: 0,
    progress: 0,
    status: 'created',
    lastUpdate: Date.now(),
    ...overrides
  }
}

Then import in both story files:

import { createMockAssetDownload } from '@/stories/mocks/assetDownloadMocks'
⛔ Skipped due to learnings
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: .cursor/rules/unit-test.mdc:0-0
Timestamp: 2025-11-24T19:48:09.318Z
Learning: Applies to test/**/*.{test,spec}.{js,ts,jsx,tsx} : Mocks should be cleanly written and easy to understand, with reusable mocks where possible
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-10T00:24:17.695Z
Learning: Applies to +(tests-ui|src)/**/*.test.ts : Leverage Vitest's utilities for mocking where possible
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-10T00:24:17.695Z
Learning: Applies to +(tests-ui|src)/**/*.test.ts : Keep module mocks contained - do not use global mutable state within test files; use `vi.hoisted()` if necessary
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-10T00:24:17.695Z
Learning: Applies to +(tests-ui|src|browser_tests)/**/*.+(test.ts|spec.ts) : Don't Mock What You Don't Own
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-10T00:24:17.695Z
Learning: Applies to +(tests-ui|src)/**/*.test.ts : Do not write tests that just test the mocks - ensure tests fail when code behaves unexpectedly

Comment on lines +13 to +29
```typescript
import { createTestingPinia } from '@pinia/testing'
import { setActivePinia } from 'pinia'
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'

describe('MyStore', () => {
beforeEach(() => {
setActivePinia(createTestingPinia({ stubActions: false }))
vi.useFakeTimers()
vi.resetAllMocks()
})

afterEach(() => {
vi.useRealTimers()
})
})
```
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Use test instead of it for consistency with project conventions.

The example imports and demonstrates it for test cases, which contradicts the project's established convention. Based on learnings, "Use test instead of it for defining test cases in vitest" is the preferred pattern in this codebase.

📝 Proposed fix to use `test` instead of `it`
-import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
+import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest'
 
 describe('MyStore', () => {
   beforeEach(() => {
     setActivePinia(createTestingPinia({ stubActions: false }))
     vi.useFakeTimers()
     vi.resetAllMocks()
   })
 
   afterEach(() => {
     vi.useRealTimers()
   })
 })

Note: While there's no explicit test case shown in this example, the import should be corrected to match the pattern used in subsequent examples.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
```typescript
import { createTestingPinia } from '@pinia/testing'
import { setActivePinia } from 'pinia'
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
describe('MyStore', () => {
beforeEach(() => {
setActivePinia(createTestingPinia({ stubActions: false }))
vi.useFakeTimers()
vi.resetAllMocks()
})
afterEach(() => {
vi.useRealTimers()
})
})
```
import { createTestingPinia } from '@pinia/testing'
import { setActivePinia } from 'pinia'
import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest'
describe('MyStore', () => {
beforeEach(() => {
setActivePinia(createTestingPinia({ stubActions: false }))
vi.useFakeTimers()
vi.resetAllMocks()
})
afterEach(() => {
vi.useRealTimers()
})
})
🤖 Prompt for AI Agents
In @tests-ui/vitest-patterns.md around lines 13 - 29, The example imports and
uses "it" which violates the project's vitest convention; update the import list
to replace "it" with "test" and ensure any example test declarations (e.g., the
placeholder test block in the describe) use "test" instead of "it" so the symbol
names in the imports and usages (the import line with it, and any subsequent
test declarations) are consistent with the project's preferred pattern.

Comment on lines +62 to +69
```typescript
import { api } from '@/scripts/api'
import { myService } from '@/services/myService'

it('handles success', () => {
vi.mocked(myService.doThing).mockResolvedValue({ data: 'test' })
// ... test code
})
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Use test instead of it for consistency.

Line 66 uses it for the test case, which should be test according to project conventions.

📝 Proposed fix
-it('handles success', () => {
+test('handles success', () => {
   vi.mocked(myService.doThing).mockResolvedValue({ data: 'test' })
   // ... test code
 })
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
```typescript
import { api } from '@/scripts/api'
import { myService } from '@/services/myService'
it('handles success', () => {
vi.mocked(myService.doThing).mockResolvedValue({ data: 'test' })
// ... test code
})
🤖 Prompt for AI Agents
In @tests-ui/vitest-patterns.md around lines 62 - 69, Replace the use of the BDD
alias "it" with the project's preferred "test" function for the test case that
currently reads like "it('handles success', ...)" so the file uses
"test('handles success', ...)" instead; locate the instance around the test that
mocks myService.doThing (vi.mocked(myService.doThing).mockResolvedValue(...))
and change only the test function name to maintain consistency with project
conventions.

Comment on lines +76 to +93
```typescript
function getEventHandler() {
const call = vi.mocked(api.addEventListener).mock.calls.find(
([event]) => event === 'my_event'
)
return call?.[1] as (e: CustomEvent<MyEventType>) => void
}

function dispatch(data: MyEventType) {
const handler = getEventHandler()
handler(new CustomEvent('my_event', { detail: data }))
}

it('handles events', () => {
const store = useMyStore()
dispatch({ field: 'value' })
expect(store.items).toHaveLength(1)
})
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Use test instead of it for consistency.

Line 89 uses it for the test case, which should be test according to project conventions.

📝 Proposed fix
-it('handles events', () => {
+test('handles events', () => {
   const store = useMyStore()
   dispatch({ field: 'value' })
   expect(store.items).toHaveLength(1)
 })
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
```typescript
function getEventHandler() {
const call = vi.mocked(api.addEventListener).mock.calls.find(
([event]) => event === 'my_event'
)
return call?.[1] as (e: CustomEvent<MyEventType>) => void
}
function dispatch(data: MyEventType) {
const handler = getEventHandler()
handler(new CustomEvent('my_event', { detail: data }))
}
it('handles events', () => {
const store = useMyStore()
dispatch({ field: 'value' })
expect(store.items).toHaveLength(1)
})
function getEventHandler() {
const call = vi.mocked(api.addEventListener).mock.calls.find(
([event]) => event === 'my_event'
)
return call?.[1] as (e: CustomEvent<MyEventType>) => void
}
function dispatch(data: MyEventType) {
const handler = getEventHandler()
handler(new CustomEvent('my_event', { detail: data }))
}
test('handles events', () => {
const store = useMyStore()
dispatch({ field: 'value' })
expect(store.items).toHaveLength(1)
})
🤖 Prompt for AI Agents
In @tests-ui/vitest-patterns.md around lines 76 - 93, Replace the `it('handles
events', ...)` call with `test('handles events', ...)` to match project
conventions; update the test declaration where `dispatch` and `getEventHandler`
are used so the test harness invokes `test` instead of `it`, leaving the test
body (store setup, dispatch({ field: 'value' }), and
expect(store.items).toHaveLength(1)) unchanged.

Comment on lines +100 to +116
```typescript
beforeEach(() => {
vi.useFakeTimers()
})

afterEach(() => {
vi.useRealTimers()
})

it('polls after delay', async () => {
const store = useMyStore()
store.startPolling()

await vi.advanceTimersByTimeAsync(30000)

expect(mockService.fetch).toHaveBeenCalled()
})
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Use test instead of it for consistency.

Line 109 uses it for the test case, which should be test according to project conventions.

📝 Proposed fix
-it('polls after delay', async () => {
+test('polls after delay', async () => {
   const store = useMyStore()
   store.startPolling()
 
   await vi.advanceTimersByTimeAsync(30000)
 
   expect(mockService.fetch).toHaveBeenCalled()
 })
🤖 Prompt for AI Agents
In @tests-ui/vitest-patterns.md around lines 100 - 116, Replace the usage of the
Jest alias `it` with the project-preferred `test` in the polling spec: change
the test declaration `it('polls after delay', async () => { ... })` to
`test('polls after delay', async () => { ... })`, leaving the surrounding hooks
(`beforeEach`, `afterEach`) and assertions unchanged so the behavior and timers
logic (vi.useFakeTimers, vi.advanceTimersByTimeAsync, expect(mockService.fetch))
remain identical.

comfy-pr-bot and others added 3 commits January 12, 2026 19:59
… deletion UI (#7991)

Backport of #7974 to `cloud/1.36`

Automatically created by backport workflow.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-7991-backport-cloud-1-36-refactor-simplify-asset-download-state-and-fix-deletion-UI-2e76d73d365081ac8a99c9d4c849325f)
by [Unito](https://www.unito.io)

Co-authored-by: Alexander Brown <[email protected]>
Co-authored-by: Amp <[email protected]>
…Nodes mode (#7987)

Backport of #7766 to `cloud/1.36`

Automatically created by backport workflow.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-7987-backport-cloud-1-36-fix-Improve-legacy-widget-compatibility-in-vueNodes-mode-2e76d73d365081c7a2e0db7aa23748f5)
by [Unito](https://www.unito.io)

Co-authored-by: Terry Jia <[email protected]>
… prevent clipping during animation (#7989)

Backport of #7856 to `cloud/1.36`

Automatically created by backport workflow.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-7989-backport-cloud-1-36-fix-disable-frustum-culling-for-SkinnedMesh-to-prevent-clipping-du-2e76d73d365081e9b323d462a60cf7dc)
by [Unito](https://www.unito.io)

Co-authored-by: Terry Jia <[email protected]>
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🤖 Fix all issues with AI agents
In @src/platform/assets/composables/useUploadModelWizard.ts:
- Around line 207-211: The uploadModel function lacks a concurrency guard: at
the top of uploadModel() check isUploading.value and if it's true return false
immediately, otherwise set isUploading.value = true before proceeding and ensure
you reset isUploading.value = false on all exit paths (success, early returns,
and error paths) to prevent race conditions from multiple rapid invocations;
reference the uploadModel function and the isUploading and canUploadModel
reactive values when implementing the guard.

In @src/renderer/extensions/vueNodes/widgets/components/WidgetLegacy.vue:
- Around line 66-68: The code uses @ts-expect-error to assign node.canvasHeight;
instead declare an extended type (e.g., interface LGraphNodeWithCanvasHeight
extends LGraphNode { canvasHeight?: number }) and replace the suppression by
casting node to that extended type before assigning canvasHeight. Update the top
of WidgetLegacy.vue (or a shared types file) with the LGraphNodeWithCanvasHeight
declaration and change the assignment to use (node as
LGraphNodeWithCanvasHeight).canvasHeight = height so the property is properly
typed without suppressing TypeScript errors.

In
@src/renderer/extensions/vueNodes/widgets/composables/useNumberStepCalculation.ts:
- Around line 19-31: Remove redundant Number() conversions in the computed
callback of useNumberStepCalculation: when returning options.step2 and when
returning options.step / 10 simply return the numeric values directly (use
options.step2 and options.step / 10) since step2 and step are already typed as
number; keep the existing conditional order (options.step2 → large step handling
→ precision-based) and ensure the returned type remains number.

In @src/stores/assetDownloadStore.ts:
- Around line 9-20: Extract the inline union for the status field into a named
exported type (e.g., export type AssetDownloadStatus = 'created' | 'running' |
'completed' | 'failed') and update the AssetDownload interface to use that type
for the status property; ensure the new type is exported so other modules can
import and reuse it where they currently reference AssetDownload.status.

In @src/stores/assetsStore.ts:
- Around line 384-392: The mapping later accesses provider.nodeDef.name without
TypeScript knowing nodeDef is present; narrow the type or assert non-null before
mapping. For example, make the filter a type guard so
modelToNodeStore.getAllNodeProviders(modelType).filter((p): p is /* concrete
type */ => !!p.nodeDef?.name) or, if you prefer minimal change, use a non-null
assertion when calling updateModelsForNodeType (provider.nodeDef!.name); update
the providers declaration or the map that calls updateModelsForNodeType to
ensure TypeScript knows nodeDef.name cannot be undefined.
📜 Review details

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7f83af3 and 19db196.

📒 Files selected for processing (12)
  • src/composables/graph/useGraphNodeManager.ts
  • src/platform/assets/components/AssetBrowserModal.vue
  • src/platform/assets/components/AssetCard.vue
  • src/platform/assets/components/AssetGrid.vue
  • src/platform/assets/composables/useUploadModelWizard.ts
  • src/renderer/extensions/vueNodes/components/NodeWidgets.vue
  • src/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberInput.vue
  • src/renderer/extensions/vueNodes/widgets/components/WidgetLegacy.vue
  • src/renderer/extensions/vueNodes/widgets/composables/useNumberStepCalculation.ts
  • src/stores/assetDownloadStore.test.ts
  • src/stores/assetDownloadStore.ts
  • src/stores/assetsStore.ts
🧰 Additional context used
📓 Path-based instructions (15)
src/**/*.vue

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

src/**/*.vue: Use the Vue 3 Composition API instead of the Options API when writing Vue components (exception: when overriding or extending PrimeVue components for compatibility)
Use setup() function for component logic
Utilize ref and reactive for reactive state
Implement computed properties with computed()
Use watch and watchEffect for side effects
Implement lifecycle hooks with onMounted, onUpdated, etc.
Utilize provide/inject for dependency injection
Use vue 3.5 style of default prop declaration
Use Tailwind CSS for styling
Implement proper props and emits definitions
Utilize Vue 3's Teleport component when needed
Use Suspense for async components
Follow Vue 3 style guide and naming conventions

src/**/*.vue: Use Vue 3 Single File Components (SFCs) with Composition API only
Use <script setup lang="ts"> for component logic in Vue SFCs
Avoid <style> blocks in Vue components - use Tailwind 4 styling instead
Use vue-i18n for all string literals in Vue components - place translation entries in src/locales/en/main.json
Use Tailwind utility classes instead of dark: variant - use semantic values from style.css theme (e.g., bg-node-component-surface)
Use cn() utility from @/utils/tailwindUtil for merging Tailwind class names instead of :class="[]" or hardcoding
Never use !important or ! Tailwind prefix - fix interfering classes instead
Use Tailwind fraction utilities instead of arbitrary percentage values (e.g., w-4/5 instead of w-[80%])
Use TypeScript Vue 3.5 style default prop declaration with reactive props destructuring - avoid withDefaults or runtime props
Prefer defineModel over separately defining a prop and emit for v-model bindings
Define slots via template usage, not via defineSlots
Use same-name shorthand for slot prop bindings (e.g., :isExpanded instead of :is-expanded="isExpanded")
Do not import Vue macros unnecessarily
Avoid new usage of PrimeVue components
Use Tailwind's plurals system via i18n instead of hardcoding ...

Files:

  • src/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberInput.vue
  • src/platform/assets/components/AssetCard.vue
  • src/platform/assets/components/AssetBrowserModal.vue
  • src/renderer/extensions/vueNodes/widgets/components/WidgetLegacy.vue
  • src/renderer/extensions/vueNodes/components/NodeWidgets.vue
  • src/platform/assets/components/AssetGrid.vue
src/**/*.{vue,ts}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

src/**/*.{vue,ts}: Leverage VueUse functions for performance-enhancing styles
Implement proper error handling
Use vue-i18n in composition API for any string literals. Place new translation entries in src/locales/en/main.json

Files:

  • src/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberInput.vue
  • src/composables/graph/useGraphNodeManager.ts
  • src/stores/assetsStore.ts
  • src/platform/assets/components/AssetCard.vue
  • src/platform/assets/components/AssetBrowserModal.vue
  • src/renderer/extensions/vueNodes/widgets/components/WidgetLegacy.vue
  • src/stores/assetDownloadStore.test.ts
  • src/stores/assetDownloadStore.ts
  • src/platform/assets/composables/useUploadModelWizard.ts
  • src/renderer/extensions/vueNodes/components/NodeWidgets.vue
  • src/renderer/extensions/vueNodes/widgets/composables/useNumberStepCalculation.ts
  • src/platform/assets/components/AssetGrid.vue
src/**/*.{ts,tsx,vue}

📄 CodeRabbit inference engine (src/CLAUDE.md)

src/**/*.{ts,tsx,vue}: Sanitize HTML with DOMPurify to prevent XSS attacks
Avoid using @ts-expect-error; use proper TypeScript types instead
Use es-toolkit for utility functions instead of other utility libraries
Implement proper TypeScript types throughout the codebase

src/**/*.{ts,tsx,vue}: Use separate import type statements instead of inline type in mixed imports
Apply Prettier formatting with 2-space indentation, single quotes, no trailing semicolons, 80-character width
Sort and group imports by plugin, run pnpm format before committing
Never use any type - use proper TypeScript types
Never use as any type assertions - fix the underlying type issue
Write code that is expressive and self-documenting - avoid unnecessary comments
Do not add or retain redundant comments - clean as you go
Avoid mutable state - prefer immutability and assignment at point of declaration
Watch out for Code Smells and refactor to avoid them

Files:

  • src/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberInput.vue
  • src/composables/graph/useGraphNodeManager.ts
  • src/stores/assetsStore.ts
  • src/platform/assets/components/AssetCard.vue
  • src/platform/assets/components/AssetBrowserModal.vue
  • src/renderer/extensions/vueNodes/widgets/components/WidgetLegacy.vue
  • src/stores/assetDownloadStore.test.ts
  • src/stores/assetDownloadStore.ts
  • src/platform/assets/composables/useUploadModelWizard.ts
  • src/renderer/extensions/vueNodes/components/NodeWidgets.vue
  • src/renderer/extensions/vueNodes/widgets/composables/useNumberStepCalculation.ts
  • src/platform/assets/components/AssetGrid.vue
src/**/{composables,components}/**/*.{ts,tsx,vue}

📄 CodeRabbit inference engine (src/CLAUDE.md)

Clean up subscriptions in state management to prevent memory leaks

Files:

  • src/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberInput.vue
  • src/composables/graph/useGraphNodeManager.ts
  • src/platform/assets/components/AssetCard.vue
  • src/platform/assets/components/AssetBrowserModal.vue
  • src/renderer/extensions/vueNodes/widgets/components/WidgetLegacy.vue
  • src/platform/assets/composables/useUploadModelWizard.ts
  • src/renderer/extensions/vueNodes/components/NodeWidgets.vue
  • src/renderer/extensions/vueNodes/widgets/composables/useNumberStepCalculation.ts
  • src/platform/assets/components/AssetGrid.vue
src/**/*.{vue,ts,tsx}

📄 CodeRabbit inference engine (src/CLAUDE.md)

Follow Vue 3 composition API style guide

Files:

  • src/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberInput.vue
  • src/composables/graph/useGraphNodeManager.ts
  • src/stores/assetsStore.ts
  • src/platform/assets/components/AssetCard.vue
  • src/platform/assets/components/AssetBrowserModal.vue
  • src/renderer/extensions/vueNodes/widgets/components/WidgetLegacy.vue
  • src/stores/assetDownloadStore.test.ts
  • src/stores/assetDownloadStore.ts
  • src/platform/assets/composables/useUploadModelWizard.ts
  • src/renderer/extensions/vueNodes/components/NodeWidgets.vue
  • src/renderer/extensions/vueNodes/widgets/composables/useNumberStepCalculation.ts
  • src/platform/assets/components/AssetGrid.vue
src/**/{components,composables}/**/*.{ts,tsx,vue}

📄 CodeRabbit inference engine (src/CLAUDE.md)

Use vue-i18n for ALL user-facing strings by adding them to src/locales/en/main.json

Files:

  • src/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberInput.vue
  • src/composables/graph/useGraphNodeManager.ts
  • src/platform/assets/components/AssetCard.vue
  • src/platform/assets/components/AssetBrowserModal.vue
  • src/renderer/extensions/vueNodes/widgets/components/WidgetLegacy.vue
  • src/platform/assets/composables/useUploadModelWizard.ts
  • src/renderer/extensions/vueNodes/components/NodeWidgets.vue
  • src/renderer/extensions/vueNodes/widgets/composables/useNumberStepCalculation.ts
  • src/platform/assets/components/AssetGrid.vue
src/**/*.{ts,vue}

📄 CodeRabbit inference engine (AGENTS.md)

src/**/*.{ts,vue}: Use ref for reactive state, computed() for derived values, and watch/watchEffect for side effects in Composition API
Avoid using ref with watch if a computed would suffice - minimize refs and derived state
Use provide/inject for dependency injection only when simpler alternatives (Store or shared composable) won't work
Leverage VueUse functions for performance-enhancing composables
Use VueUse function for useI18n in composition API for string literals

Files:

  • src/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberInput.vue
  • src/composables/graph/useGraphNodeManager.ts
  • src/stores/assetsStore.ts
  • src/platform/assets/components/AssetCard.vue
  • src/platform/assets/components/AssetBrowserModal.vue
  • src/renderer/extensions/vueNodes/widgets/components/WidgetLegacy.vue
  • src/stores/assetDownloadStore.test.ts
  • src/stores/assetDownloadStore.ts
  • src/platform/assets/composables/useUploadModelWizard.ts
  • src/renderer/extensions/vueNodes/components/NodeWidgets.vue
  • src/renderer/extensions/vueNodes/widgets/composables/useNumberStepCalculation.ts
  • src/platform/assets/components/AssetGrid.vue
src/**/*.ts

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

src/**/*.ts: Use es-toolkit for utility functions
Use TypeScript for type safety

src/**/*.ts: Derive component types using vue-component-type-helpers (ComponentProps, ComponentSlots) instead of separate type files
Use es-toolkit for utility functions
Minimize the surface area (exported values) of each module and composable
Favor pure functions, especially testable ones

Files:

  • src/composables/graph/useGraphNodeManager.ts
  • src/stores/assetsStore.ts
  • src/stores/assetDownloadStore.test.ts
  • src/stores/assetDownloadStore.ts
  • src/platform/assets/composables/useUploadModelWizard.ts
  • src/renderer/extensions/vueNodes/widgets/composables/useNumberStepCalculation.ts
src/**/{services,composables}/**/*.{ts,tsx}

📄 CodeRabbit inference engine (src/CLAUDE.md)

src/**/{services,composables}/**/*.{ts,tsx}: Use api.apiURL() for backend endpoints instead of constructing URLs directly
Use api.fileURL() for static file access instead of constructing URLs directly

Files:

  • src/composables/graph/useGraphNodeManager.ts
  • src/platform/assets/composables/useUploadModelWizard.ts
  • src/renderer/extensions/vueNodes/widgets/composables/useNumberStepCalculation.ts
src/composables/**/*.ts

📄 CodeRabbit inference engine (AGENTS.md)

Name composables as useXyz.ts (e.g., useForm.ts)

Files:

  • src/composables/graph/useGraphNodeManager.ts
src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

src/**/*.{ts,tsx}: Keep functions short and functional
Minimize nesting (if statements, for loops, etc.)
Use function declarations instead of function expressions when possible

Files:

  • src/composables/graph/useGraphNodeManager.ts
  • src/stores/assetsStore.ts
  • src/stores/assetDownloadStore.test.ts
  • src/stores/assetDownloadStore.ts
  • src/platform/assets/composables/useUploadModelWizard.ts
  • src/renderer/extensions/vueNodes/widgets/composables/useNumberStepCalculation.ts
src/**/stores/**/*.{ts,tsx}

📄 CodeRabbit inference engine (src/CLAUDE.md)

src/**/stores/**/*.{ts,tsx}: Maintain clear public interfaces and restrict extension access in stores
Use TypeScript for type safety in state management stores

Files:

  • src/stores/assetsStore.ts
  • src/stores/assetDownloadStore.test.ts
  • src/stores/assetDownloadStore.ts
src/stores/**/*.ts

📄 CodeRabbit inference engine (AGENTS.md)

Name Pinia stores as *Store.ts (e.g., nodeStore.ts)

Files:

  • src/stores/assetsStore.ts
  • src/stores/assetDownloadStore.test.ts
  • src/stores/assetDownloadStore.ts
+(tests-ui|src)/**/*.test.ts

📄 CodeRabbit inference engine (AGENTS.md)

+(tests-ui|src)/**/*.test.ts: Unit and component tests belong in tests-ui/ or src/**/*.test.ts using Vitest
Write tests for all changes, especially bug fixes to catch future regressions
Do not write tests dependent on non-behavioral features like utility classes or styles
Do not write tests that just test the mocks - ensure tests fail when code behaves unexpectedly
Leverage Vitest's utilities for mocking where possible
Keep module mocks contained - do not use global mutable state within test files; use vi.hoisted() if necessary
Use Vue Test Utils for Component testing and follow best practices for making components easy to test
Aim for behavioral coverage of critical and new features in unit tests

Files:

  • src/stores/assetDownloadStore.test.ts
+(tests-ui|src|browser_tests)/**/*.+(test.ts|spec.ts)

📄 CodeRabbit inference engine (AGENTS.md)

+(tests-ui|src|browser_tests)/**/*.+(test.ts|spec.ts): Do not write change detector tests - avoid tests that only assert default values
Be parsimonious in testing - do not write redundant tests
Don't Mock What You Don't Own

Files:

  • src/stores/assetDownloadStore.test.ts
🧠 Learnings (63)
📓 Common learnings
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7897
File: src/stores/assetDownloadStore.ts:117-117
Timestamp: 2026-01-08T21:44:05.435Z
Learning: In src/stores/assetDownloadStore.ts, the api.addEventListener('asset_download', handleAssetDownload) call at module level is intentional and should persist for the application lifetime since it's a singleton Pinia store managing global WebSocket events.
📚 Learning: 2025-11-24T19:47:45.616Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/components/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:45.616Z
Learning: Applies to src/components/**/*.vue : Replace PrimeVue Steps component with Stepper without panels

Applied to files:

  • src/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberInput.vue
  • src/renderer/extensions/vueNodes/widgets/composables/useNumberStepCalculation.ts
📚 Learning: 2025-12-09T03:49:52.828Z
Learnt from: christian-byrne
Repo: Comfy-Org/ComfyUI_frontend PR: 6300
File: src/platform/updates/components/WhatsNewPopup.vue:5-13
Timestamp: 2025-12-09T03:49:52.828Z
Learning: In Vue files across the ComfyUI_frontend repo, when a button is needed, prefer the repo's common button components from src/components/button/ (IconButton.vue, TextButton.vue, IconTextButton.vue) over plain HTML <button> elements. These components wrap PrimeVue with the project’s design system styling. Use only the common button components for consistency and theming, and import them from src/components/button/ as needed.

Applied to files:

  • src/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberInput.vue
  • src/platform/assets/components/AssetCard.vue
  • src/platform/assets/components/AssetBrowserModal.vue
  • src/renderer/extensions/vueNodes/widgets/components/WidgetLegacy.vue
  • src/renderer/extensions/vueNodes/components/NodeWidgets.vue
  • src/platform/assets/components/AssetGrid.vue
📚 Learning: 2025-12-09T21:40:12.361Z
Learnt from: benceruleanlu
Repo: Comfy-Org/ComfyUI_frontend PR: 7297
File: src/components/actionbar/ComfyActionbar.vue:33-43
Timestamp: 2025-12-09T21:40:12.361Z
Learning: In Vue single-file components, allow inline Tailwind CSS class strings for static classes and avoid extracting them into computed properties solely for readability. Prefer keeping static class names inline for simplicity and performance. For dynamic or conditional classes, use Vue bindings (e.g., :class) to compose classes.

Applies to all Vue files in the repository (e.g., src/**/*.vue) where Tailwind utilities are used for static styling.

Applied to files:

  • src/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberInput.vue
  • src/platform/assets/components/AssetCard.vue
  • src/platform/assets/components/AssetBrowserModal.vue
  • src/renderer/extensions/vueNodes/widgets/components/WidgetLegacy.vue
  • src/renderer/extensions/vueNodes/components/NodeWidgets.vue
  • src/platform/assets/components/AssetGrid.vue
📚 Learning: 2025-12-16T22:26:49.463Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7537
File: src/components/ui/button/Button.vue:17-17
Timestamp: 2025-12-16T22:26:49.463Z
Learning: In Vue 3.5+ with <script setup>, when using defineProps<Props>() with partial destructuring (e.g., const { as = 'button', class: customClass = '' } = defineProps<Props>() ), props that are not destructured (e.g., variant, size) stay accessible by name in the template scope. This pattern is valid: you can destructure only a subset of props for convenience while referencing the remaining props directly in template expressions. Apply this guideline to Vue components across the codebase (all .vue files).

Applied to files:

  • src/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberInput.vue
  • src/platform/assets/components/AssetCard.vue
  • src/platform/assets/components/AssetBrowserModal.vue
  • src/renderer/extensions/vueNodes/widgets/components/WidgetLegacy.vue
  • src/renderer/extensions/vueNodes/components/NodeWidgets.vue
  • src/platform/assets/components/AssetGrid.vue
📚 Learning: 2025-12-22T21:36:08.369Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7649
File: src/platform/cloud/subscription/components/PricingTable.vue:185-201
Timestamp: 2025-12-22T21:36:08.369Z
Learning: In Vue components, avoid creating single-use variants for common UI components (e.g., Button and other shared components). Aim for reusable variants that cover multiple use cases. It’s acceptable to temporarily mix variant props with inline Tailwind classes when a styling need is unique to one place, but plan and consolidate into shared, reusable variants as patterns emerge across the codebase.

Applied to files:

  • src/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberInput.vue
  • src/platform/assets/components/AssetCard.vue
  • src/platform/assets/components/AssetBrowserModal.vue
  • src/renderer/extensions/vueNodes/widgets/components/WidgetLegacy.vue
  • src/renderer/extensions/vueNodes/components/NodeWidgets.vue
  • src/platform/assets/components/AssetGrid.vue
📚 Learning: 2026-01-08T02:26:18.357Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7893
File: src/components/button/IconGroup.vue:5-6
Timestamp: 2026-01-08T02:26:18.357Z
Learning: In components that use the cn utility from '@/utils/tailwindUtil' with tailwind-merge, rely on the behavior that conflicting Tailwind classes are resolved by keeping the last one. For example, cn('base-classes bg-default', propClass) will have any conflicting background class from propClass override bg-default. This additive pattern is intentional and aligns with the shadcn-ui convention; ensure you document or review expectations accordingly in Vue components.

Applied to files:

  • src/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberInput.vue
  • src/platform/assets/components/AssetCard.vue
  • src/platform/assets/components/AssetBrowserModal.vue
  • src/renderer/extensions/vueNodes/widgets/components/WidgetLegacy.vue
  • src/renderer/extensions/vueNodes/components/NodeWidgets.vue
  • src/platform/assets/components/AssetGrid.vue
📚 Learning: 2025-12-11T03:55:51.755Z
Learnt from: simula-r
Repo: Comfy-Org/ComfyUI_frontend PR: 7252
File: src/renderer/extensions/vueNodes/components/ImagePreview.vue:151-158
Timestamp: 2025-12-11T03:55:51.755Z
Learning: In Vue components under src/renderer/extensions/vueNodes (e.g., ImagePreview.vue and LGraphNode.vue), implement image gallery keyboard navigation so that it responds to the node's focus state rather than requiring focus inside the image preview wrapper. Achieve this by wiring keyEvent handling at the node focus level and injecting or propagating key events (arrow keys) to the gallery when the node is focused/selected. This improves accessibility and aligns navigation with node-level focus behavior.

Applied to files:

  • src/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberInput.vue
  • src/renderer/extensions/vueNodes/widgets/components/WidgetLegacy.vue
  • src/renderer/extensions/vueNodes/components/NodeWidgets.vue
📚 Learning: 2025-12-11T12:25:15.470Z
Learnt from: christian-byrne
Repo: Comfy-Org/ComfyUI_frontend PR: 7358
File: src/components/dialog/content/signin/SignUpForm.vue:45-54
Timestamp: 2025-12-11T12:25:15.470Z
Learning: This repository uses CI automation to format code (pnpm format). Do not include manual formatting suggestions in code reviews for Comfy-Org/ComfyUI_frontend. If formatting issues are detected, rely on the CI formatter or re-run pnpm format. Focus reviews on correctness, readability, performance, accessibility, and maintainability rather than style formatting.

Applied to files:

  • src/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberInput.vue
  • src/composables/graph/useGraphNodeManager.ts
  • src/stores/assetsStore.ts
  • src/platform/assets/components/AssetCard.vue
  • src/platform/assets/components/AssetBrowserModal.vue
  • src/renderer/extensions/vueNodes/widgets/components/WidgetLegacy.vue
  • src/stores/assetDownloadStore.test.ts
  • src/stores/assetDownloadStore.ts
  • src/platform/assets/composables/useUploadModelWizard.ts
  • src/renderer/extensions/vueNodes/components/NodeWidgets.vue
  • src/renderer/extensions/vueNodes/widgets/composables/useNumberStepCalculation.ts
  • src/platform/assets/components/AssetGrid.vue
📚 Learning: 2025-12-18T02:07:38.870Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7598
File: src/components/sidebar/tabs/AssetsSidebarTab.vue:131-131
Timestamp: 2025-12-18T02:07:38.870Z
Learning: Tailwind CSS v4 safe utilities (e.g., items-center-safe, justify-*-safe, place-*-safe) are allowed in Vue components under src/ and in story files. Do not flag these specific safe variants as invalid when reviewing code in src/**/*.vue or related stories.

Applied to files:

  • src/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberInput.vue
  • src/platform/assets/components/AssetCard.vue
  • src/platform/assets/components/AssetBrowserModal.vue
  • src/renderer/extensions/vueNodes/widgets/components/WidgetLegacy.vue
  • src/renderer/extensions/vueNodes/components/NodeWidgets.vue
  • src/platform/assets/components/AssetGrid.vue
📚 Learning: 2025-12-18T21:15:46.862Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7603
File: src/components/queue/QueueOverlayHeader.vue:49-59
Timestamp: 2025-12-18T21:15:46.862Z
Learning: In the ComfyUI_frontend repository, for Vue components, do not add aria-label to buttons that have visible text content (e.g., buttons containing <span> text). The visible text provides the accessible name. Use aria-label only for elements without visible labels (e.g., icon-only buttons). If a button has no visible label, provide a clear aria-label or associate with an aria-labelledby describing its action.

Applied to files:

  • src/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberInput.vue
  • src/platform/assets/components/AssetCard.vue
  • src/platform/assets/components/AssetBrowserModal.vue
  • src/renderer/extensions/vueNodes/widgets/components/WidgetLegacy.vue
  • src/renderer/extensions/vueNodes/components/NodeWidgets.vue
  • src/platform/assets/components/AssetGrid.vue
📚 Learning: 2025-12-21T01:06:02.786Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7649
File: src/components/graph/selectionToolbox/ColorPickerButton.vue:15-18
Timestamp: 2025-12-21T01:06:02.786Z
Learning: In Comfy-Org/ComfyUI_frontend, in Vue component files, when a filled icon is required (e.g., 'pi pi-circle-fill'), you may mix PrimeIcons with Lucide icons since Lucide lacks filled variants. This mixed usage is acceptable when one icon library does not provide an equivalent filled icon. Apply consistently across Vue components in the src directory where icons are used, and document the rationale when a mixed approach is chosen.

Applied to files:

  • src/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberInput.vue
  • src/platform/assets/components/AssetCard.vue
  • src/platform/assets/components/AssetBrowserModal.vue
  • src/renderer/extensions/vueNodes/widgets/components/WidgetLegacy.vue
  • src/renderer/extensions/vueNodes/components/NodeWidgets.vue
  • src/platform/assets/components/AssetGrid.vue
📚 Learning: 2026-01-12T17:39:27.738Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7906
File: src/components/sidebar/tabs/AssetsSidebarTab.vue:545-552
Timestamp: 2026-01-12T17:39:27.738Z
Learning: In Vue/TypeScript files (src/**/*.{ts,tsx,vue}), prefer if/else statements over ternary operators when performing side effects or actions (e.g., mutating state, calling methods with side effects). Ternaries should be reserved for computing and returning values.

Applied to files:

  • src/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberInput.vue
  • src/composables/graph/useGraphNodeManager.ts
  • src/stores/assetsStore.ts
  • src/platform/assets/components/AssetCard.vue
  • src/platform/assets/components/AssetBrowserModal.vue
  • src/renderer/extensions/vueNodes/widgets/components/WidgetLegacy.vue
  • src/stores/assetDownloadStore.test.ts
  • src/stores/assetDownloadStore.ts
  • src/platform/assets/composables/useUploadModelWizard.ts
  • src/renderer/extensions/vueNodes/components/NodeWidgets.vue
  • src/renderer/extensions/vueNodes/widgets/composables/useNumberStepCalculation.ts
  • src/platform/assets/components/AssetGrid.vue
📚 Learning: 2025-11-24T19:47:56.371Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/lib/litegraph/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:56.371Z
Learning: Applies to src/lib/litegraph/**/*.{test,spec}.{ts,tsx} : Use provided test helpers `createTestSubgraph` and `createTestSubgraphNode` from `./fixtures/subgraphHelpers` for consistent subgraph test setup

Applied to files:

  • src/composables/graph/useGraphNodeManager.ts
📚 Learning: 2025-12-09T03:39:54.501Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7169
File: src/platform/remote/comfyui/jobs/jobTypes.ts:1-107
Timestamp: 2025-12-09T03:39:54.501Z
Learning: In the ComfyUI_frontend project, Zod is on v3.x. Do not suggest Zod v4 standalone validators (z.uuid, z.ulid, z.cuid2, z.nanoid) until an upgrade to Zod 4 is performed. When reviewing TypeScript files (e.g., src/platform/remote/comfyui/jobs/jobTypes.ts) validate against Zod 3 capabilities and avoid introducing v4-specific features; flag any proposal to upgrade or incorporate v4-only validators and propose staying with compatible 3.x patterns.

Applied to files:

  • src/composables/graph/useGraphNodeManager.ts
  • src/stores/assetsStore.ts
  • src/stores/assetDownloadStore.test.ts
  • src/stores/assetDownloadStore.ts
  • src/platform/assets/composables/useUploadModelWizard.ts
  • src/renderer/extensions/vueNodes/widgets/composables/useNumberStepCalculation.ts
📚 Learning: 2025-12-13T11:03:11.264Z
Learnt from: christian-byrne
Repo: Comfy-Org/ComfyUI_frontend PR: 7416
File: src/stores/imagePreviewStore.ts:5-7
Timestamp: 2025-12-13T11:03:11.264Z
Learning: In the ComfyUI_frontend repository, lint rules require keeping 'import type' statements separate from non-type imports, even if importing from the same module. Do not suggest consolidating them into a single import statement. Ensure type imports remain on their own line (import type { ... } from 'module') and regular imports stay on separate lines.

Applied to files:

  • src/composables/graph/useGraphNodeManager.ts
  • src/stores/assetsStore.ts
  • src/stores/assetDownloadStore.test.ts
  • src/stores/assetDownloadStore.ts
  • src/platform/assets/composables/useUploadModelWizard.ts
  • src/renderer/extensions/vueNodes/widgets/composables/useNumberStepCalculation.ts
📚 Learning: 2025-12-17T00:40:09.635Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7537
File: src/components/ui/button/Button.stories.ts:45-55
Timestamp: 2025-12-17T00:40:09.635Z
Learning: Prefer pure function declarations over function expressions (e.g., use function foo() { ... } instead of const foo = () => { ... }) for pure functions in the repository. Function declarations are more functional-leaning, offer better hoisting clarity, and can improve readability and tooling consistency. Apply this guideline across TypeScript files in Comfy-Org/ComfyUI_frontend, including story and UI component code, except where a function expression is semantically required (e.g., callbacks, higher-order functions with closures).

Applied to files:

  • src/composables/graph/useGraphNodeManager.ts
  • src/stores/assetsStore.ts
  • src/stores/assetDownloadStore.test.ts
  • src/stores/assetDownloadStore.ts
  • src/platform/assets/composables/useUploadModelWizard.ts
  • src/renderer/extensions/vueNodes/widgets/composables/useNumberStepCalculation.ts
📚 Learning: 2025-12-30T22:22:33.836Z
Learnt from: kaili-yang
Repo: Comfy-Org/ComfyUI_frontend PR: 7805
File: src/composables/useCoreCommands.ts:439-439
Timestamp: 2025-12-30T22:22:33.836Z
Learning: When accessing reactive properties from Pinia stores in TypeScript files, avoid using .value on direct property access (e.g., useStore().isOverlayExpanded). Pinia auto-wraps refs when accessed directly, returning the primitive value. The .value accessor is only needed when destructuring store properties or when using storeToRefs().

Applied to files:

  • src/composables/graph/useGraphNodeManager.ts
  • src/stores/assetsStore.ts
  • src/stores/assetDownloadStore.test.ts
  • src/stores/assetDownloadStore.ts
  • src/platform/assets/composables/useUploadModelWizard.ts
  • src/renderer/extensions/vueNodes/widgets/composables/useNumberStepCalculation.ts
📚 Learning: 2026-01-10T00:24:17.695Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-10T00:24:17.695Z
Learning: Applies to src/stores/**/*.ts : Name Pinia stores as `*Store.ts` (e.g., `nodeStore.ts`)

Applied to files:

  • src/stores/assetsStore.ts
  • src/stores/assetDownloadStore.test.ts
  • src/stores/assetDownloadStore.ts
📚 Learning: 2026-01-08T21:44:05.435Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7897
File: src/stores/assetDownloadStore.ts:117-117
Timestamp: 2026-01-08T21:44:05.435Z
Learning: In src/stores/assetDownloadStore.ts, the api.addEventListener('asset_download', handleAssetDownload) call at module level is intentional and should persist for the application lifetime since it's a singleton Pinia store managing global WebSocket events.

Applied to files:

  • src/stores/assetsStore.ts
  • src/stores/assetDownloadStore.test.ts
  • src/stores/assetDownloadStore.ts
  • src/platform/assets/composables/useUploadModelWizard.ts
📚 Learning: 2026-01-06T19:17:12.355Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7746
File: src/platform/assets/composables/useUploadModelWizard.ts:207-219
Timestamp: 2026-01-06T19:17:12.355Z
Learning: In `src/platform/assets/composables/useUploadModelWizard.ts`, the `uploadModel()` function should have a concurrency guard at the beginning that checks if `isUploading.value` is already true and returns false early to prevent race conditions from multiple rapid invocations.

Applied to files:

  • src/stores/assetsStore.ts
  • src/platform/assets/components/AssetBrowserModal.vue
  • src/stores/assetDownloadStore.ts
  • src/platform/assets/composables/useUploadModelWizard.ts
📚 Learning: 2026-01-10T00:24:17.695Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-10T00:24:17.695Z
Learning: Applies to src/**/*.{ts,vue} : Avoid using `ref` with `watch` if a `computed` would suffice - minimize refs and derived state

Applied to files:

  • src/stores/assetsStore.ts
📚 Learning: 2026-01-10T00:24:17.695Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-10T00:24:17.695Z
Learning: Applies to src/**/*.{ts,tsx,vue} : Watch out for Code Smells and refactor to avoid them

Applied to files:

  • src/stores/assetsStore.ts
  • src/platform/assets/components/AssetBrowserModal.vue
📚 Learning: 2025-11-24T19:47:34.324Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:34.324Z
Learning: Applies to src/**/{composables,components}/**/*.{ts,tsx,vue} : Clean up subscriptions in state management to prevent memory leaks

Applied to files:

  • src/stores/assetsStore.ts
📚 Learning: 2026-01-10T00:24:17.695Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-10T00:24:17.695Z
Learning: Applies to src/**/*.{ts,vue} : Use `ref` for reactive state, `computed()` for derived values, and `watch`/`watchEffect` for side effects in Composition API

Applied to files:

  • src/stores/assetsStore.ts
📚 Learning: 2026-01-09T23:59:06.942Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7926
File: src/stores/assetDownloadStore.ts:101-126
Timestamp: 2026-01-09T23:59:06.942Z
Learning: In src/stores/assetDownloadStore.ts, the empty catch block in pollSingleDownload within pollStaleDownloads is intentional. Do not suggest adding error logging or handling for polling failures in this function.

Applied to files:

  • src/stores/assetsStore.ts
  • src/platform/assets/components/AssetBrowserModal.vue
  • src/stores/assetDownloadStore.test.ts
  • src/platform/assets/composables/useUploadModelWizard.ts
📚 Learning: 2025-11-24T19:47:02.860Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-24T19:47:02.860Z
Learning: Applies to src/**/*.vue : Utilize ref and reactive for reactive state

Applied to files:

  • src/stores/assetsStore.ts
📚 Learning: 2025-11-24T19:47:45.616Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/components/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:45.616Z
Learning: Applies to src/components/**/*.vue : Use ref/reactive for state management in Vue 3 Composition API

Applied to files:

  • src/stores/assetsStore.ts
📚 Learning: 2026-01-10T00:24:17.695Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-10T00:24:17.695Z
Learning: Applies to src/**/*.{ts,vue} : Leverage VueUse functions for performance-enhancing composables

Applied to files:

  • src/stores/assetsStore.ts
📚 Learning: 2025-11-24T19:47:45.616Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/components/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:45.616Z
Learning: Applies to src/components/**/*.vue : Implement computed() for derived state in Vue 3 Composition API

Applied to files:

  • src/stores/assetsStore.ts
📚 Learning: 2025-11-24T19:47:02.860Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-24T19:47:02.860Z
Learning: Applies to src/**/*.{vue,ts} : Leverage VueUse functions for performance-enhancing styles

Applied to files:

  • src/stores/assetsStore.ts
  • src/platform/assets/components/AssetBrowserModal.vue
📚 Learning: 2025-11-24T19:47:02.860Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-24T19:47:02.860Z
Learning: Applies to src/**/*.vue : Use Suspense for async components

Applied to files:

  • src/stores/assetsStore.ts
📚 Learning: 2025-11-24T19:47:45.616Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/components/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:45.616Z
Learning: Applies to src/components/**/*.vue : Use setup() function in Vue 3 Composition API

Applied to files:

  • src/stores/assetsStore.ts
📚 Learning: 2025-11-24T19:47:45.616Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/components/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:45.616Z
Learning: Applies to src/components/**/*.vue : Define proper props and emits definitions in Vue components

Applied to files:

  • src/platform/assets/components/AssetCard.vue
📚 Learning: 2025-11-24T19:47:02.860Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-24T19:47:02.860Z
Learning: Applies to src/**/*.vue : Implement proper props and emits definitions

Applied to files:

  • src/platform/assets/components/AssetCard.vue
📚 Learning: 2026-01-10T00:24:17.695Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-10T00:24:17.695Z
Learning: Applies to src/**/*.vue : Use Tailwind fraction utilities instead of arbitrary percentage values (e.g., `w-4/5` instead of `w-[80%]`)

Applied to files:

  • src/platform/assets/components/AssetBrowserModal.vue
📚 Learning: 2026-01-10T00:24:17.695Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-10T00:24:17.695Z
Learning: Applies to src/**/*.vue : Never use `!important` or `!` Tailwind prefix - fix interfering classes instead

Applied to files:

  • src/platform/assets/components/AssetBrowserModal.vue
📚 Learning: 2026-01-10T00:24:17.695Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-10T00:24:17.695Z
Learning: Applies to src/**/*.vue : Avoid `<style>` blocks in Vue components - use Tailwind 4 styling instead

Applied to files:

  • src/platform/assets/components/AssetBrowserModal.vue
📚 Learning: 2025-11-24T19:47:45.616Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/components/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:45.616Z
Learning: Applies to src/components/**/*.{vue,css} : Use the correct tokens from style.css in the design system package

Applied to files:

  • src/platform/assets/components/AssetBrowserModal.vue
📚 Learning: 2025-11-24T19:47:02.860Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-24T19:47:02.860Z
Learning: Applies to src/**/*.{vue,ts} : Implement proper error handling

Applied to files:

  • src/platform/assets/components/AssetBrowserModal.vue
  • src/platform/assets/composables/useUploadModelWizard.ts
📚 Learning: 2026-01-06T19:20:56.167Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7746
File: src/platform/assets/services/assetService.ts:484-491
Timestamp: 2026-01-06T19:20:56.167Z
Learning: In `src/platform/assets/services/assetService.ts`, prefer using `schema.safeParse()` over `schema.parse()` for validating API responses to avoid throwing ZodError with internal schema details; follow the existing pattern: call `safeParse()`, check `result.success`, and use `fromZodError(result.error)` to format error messages for logging while throwing user-friendly errors.

Applied to files:

  • src/platform/assets/components/AssetBrowserModal.vue
  • src/platform/assets/composables/useUploadModelWizard.ts
📚 Learning: 2025-12-18T20:39:30.137Z
Learnt from: jtydhr88
Repo: Comfy-Org/ComfyUI_frontend PR: 7621
File: src/components/load3d/Load3DScene.vue:4-4
Timestamp: 2025-12-18T20:39:30.137Z
Learning: In src/components/load3d/Load3DScene.vue, the scoped `<style>` block with `!important` declarations for the canvas element is necessary because Three.js dynamically creates the canvas with inline styles, preventing direct application of Tailwind classes. This is a valid exception to the Tailwind-only styling guideline.

Applied to files:

  • src/renderer/extensions/vueNodes/widgets/components/WidgetLegacy.vue
📚 Learning: 2025-11-24T19:47:22.909Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: browser_tests/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:22.909Z
Learning: Applies to browser_tests/**/*.{e2e,spec}.{ts,tsx,js,jsx} : Check assets/ directory for test data when writing tests

Applied to files:

  • src/stores/assetDownloadStore.test.ts
📚 Learning: 2026-01-10T00:24:17.695Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-10T00:24:17.695Z
Learning: Applies to +(tests-ui|src)/**/*.test.ts : Write tests for all changes, especially bug fixes to catch future regressions

Applied to files:

  • src/stores/assetDownloadStore.test.ts
📚 Learning: 2026-01-10T00:24:17.695Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-10T00:24:17.695Z
Learning: Applies to +(tests-ui|src)/**/*.test.ts : Aim for behavioral coverage of critical and new features in unit tests

Applied to files:

  • src/stores/assetDownloadStore.test.ts
📚 Learning: 2025-11-24T19:47:34.324Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:34.324Z
Learning: Applies to src/**/stores/**/*.{ts,tsx} : Maintain clear public interfaces and restrict extension access in stores

Applied to files:

  • src/stores/assetDownloadStore.test.ts
  • src/stores/assetDownloadStore.ts
📚 Learning: 2026-01-10T00:24:17.695Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-10T00:24:17.695Z
Learning: Applies to +(tests-ui|src)/**/*.test.ts : Do not write tests that just test the mocks - ensure tests fail when code behaves unexpectedly

Applied to files:

  • src/stores/assetDownloadStore.test.ts
📚 Learning: 2026-01-10T00:24:17.695Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-10T00:24:17.695Z
Learning: Applies to +(tests-ui|src|browser_tests)/**/*.+(test.ts|spec.ts) : Be parsimonious in testing - do not write redundant tests

Applied to files:

  • src/stores/assetDownloadStore.test.ts
📚 Learning: 2026-01-10T00:24:17.695Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-10T00:24:17.695Z
Learning: Applies to +(tests-ui|src)/**/*.test.ts : Leverage Vitest's utilities for mocking where possible

Applied to files:

  • src/stores/assetDownloadStore.test.ts
📚 Learning: 2026-01-10T00:24:17.695Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-10T00:24:17.695Z
Learning: Applies to +(tests-ui|src)/**/*.test.ts : Keep module mocks contained - do not use global mutable state within test files; use `vi.hoisted()` if necessary

Applied to files:

  • src/stores/assetDownloadStore.test.ts
📚 Learning: 2025-12-10T03:09:13.807Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7303
File: src/components/topbar/CurrentUserPopover.test.ts:199-205
Timestamp: 2025-12-10T03:09:13.807Z
Learning: In test files, prefer selecting or asserting on accessible properties (text content, aria-label, role, accessible name) over data-testid attributes. This ensures tests validate actual user-facing behavior and accessibility, reducing reliance on implementation details like test IDs.

Applied to files:

  • src/stores/assetDownloadStore.test.ts
📚 Learning: 2025-12-30T01:31:04.927Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7797
File: tests-ui/tests/lib/litegraph/src/widgets/ComboWidget.test.ts:648-648
Timestamp: 2025-12-30T01:31:04.927Z
Learning: In Vitest v4, when mocking functions that may be called as constructors (using new), the mock implementation must use function() or class syntax rather than an arrow function. Arrow mocks can cause '<anonymous> is not a constructor' errors. This is a breaking change from Vitest v3 where mocks could use an arrow function. Apply this guideline to test files that mock constructor-like calls (e.g., in tests under tests-ui, such as ComboWidget.test.ts) and ensure mock implementations are defined with function() { ... } or class { ... } to preserve constructor behavior.

Applied to files:

  • src/stores/assetDownloadStore.test.ts
📚 Learning: 2026-01-08T02:40:15.482Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7894
File: src/renderer/extensions/vueNodes/widgets/components/WidgetToggleSwitch.test.ts:11-14
Timestamp: 2026-01-08T02:40:15.482Z
Learning: In TypeScript test files (e.g., any test under src), avoid duplicating interface/type definitions. Import real type definitions from the component modules under test and reference them directly, so there is a single source of truth and to prevent type drift. This improves maintainability and consistency across tests.

Applied to files:

  • src/stores/assetDownloadStore.test.ts
📚 Learning: 2026-01-09T02:07:54.558Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7898
File: src/composables/usePaste.test.ts:248-248
Timestamp: 2026-01-09T02:07:54.558Z
Learning: In test files (e.g., any .test.ts or .test.tsx under src/...), when you create mock objects that partially implement an interface (such as LGraphNode), prefer casting with as Partial<InterfaceType> as InterfaceType rather than as any or as unknown as InterfaceType. This makes the incomplete implementation explicit while preserving type safety, improving readability and maintainability of tests.

Applied to files:

  • src/stores/assetDownloadStore.test.ts
📚 Learning: 2026-01-09T23:59:04.777Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7926
File: src/stores/assetDownloadStore.ts:101-126
Timestamp: 2026-01-09T23:59:04.777Z
Learning: In src/stores/assetDownloadStore.ts, the empty catch block in pollSingleDownload within pollStaleDownloads is intentional. Do not suggest adding error logging or error handling for polling failures in this function; treat it as a deliberate swallow of errors to avoid affecting poll logic or user-visible behavior. Reviewers should refrain from proposing fixes to this catch block for this function.

Applied to files:

  • src/stores/assetDownloadStore.ts
📚 Learning: 2025-12-18T16:03:09.642Z
Learnt from: henrikvilhelmberglund
Repo: Comfy-Org/ComfyUI_frontend PR: 7617
File: src/components/actionbar/ComfyActionbar.vue:301-308
Timestamp: 2025-12-18T16:03:09.642Z
Learning: In the ComfyUI frontend queue system (src/stores/queueStore.ts), the `useQueuePendingTaskCountStore().count` includes the currently executing task. When count = 1, there is only the active/running task with no pending tasks. When count > 1, there is an active task plus pending tasks waiting in the queue. The "Clear Pending Tasks" button should only be enabled when count > 1 to avoid clearing the currently running task, which should be handled by the "Cancel current run" button instead.

Applied to files:

  • src/stores/assetDownloadStore.ts
📚 Learning: 2026-01-10T00:24:17.695Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-10T00:24:17.695Z
Learning: Applies to src/**/*.{ts,tsx,vue} : Never use `as any` type assertions - fix the underlying type issue

Applied to files:

  • src/stores/assetDownloadStore.ts
📚 Learning: 2025-11-24T19:47:34.324Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:34.324Z
Learning: Applies to src/**/{components,composables}/**/*.{ts,tsx,vue} : Use vue-i18n for ALL user-facing strings by adding them to `src/locales/en/main.json`

Applied to files:

  • src/platform/assets/composables/useUploadModelWizard.ts
📚 Learning: 2025-11-24T19:47:34.324Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:34.324Z
Learning: Applies to src/**/*.{ts,tsx,vue} : Avoid using ts-expect-error; use proper TypeScript types instead

Applied to files:

  • src/platform/assets/composables/useUploadModelWizard.ts
📚 Learning: 2025-11-24T19:47:02.860Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-24T19:47:02.860Z
Learning: Applies to src/**/*.{vue,ts} : Use vue-i18n in composition API for any string literals. Place new translation entries in src/locales/en/main.json

Applied to files:

  • src/platform/assets/composables/useUploadModelWizard.ts
📚 Learning: 2025-11-24T19:47:45.616Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/components/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:45.616Z
Learning: Applies to src/components/**/*.{vue,ts,js} : Use vue-i18n for ALL UI strings

Applied to files:

  • src/platform/assets/composables/useUploadModelWizard.ts
📚 Learning: 2026-01-10T00:24:17.695Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-10T00:24:17.695Z
Learning: Applies to src/**/*.{ts,vue} : Use VueUse function for useI18n in composition API for string literals

Applied to files:

  • src/platform/assets/composables/useUploadModelWizard.ts
📚 Learning: 2025-11-24T19:47:34.324Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:34.324Z
Learning: Applies to src/**/stores/**/*.{ts,tsx} : Use TypeScript for type safety in state management stores

Applied to files:

  • src/platform/assets/composables/useUploadModelWizard.ts
🧬 Code graph analysis (4)
src/composables/graph/useGraphNodeManager.ts (1)
src/scripts/app.ts (1)
  • app (1772-1772)
src/stores/assetsStore.ts (4)
src/stores/assetDownloadStore.ts (1)
  • useAssetDownloadStore (47-178)
src/stores/modelToNodeStore.ts (1)
  • useModelToNodeStore (22-168)
src/platform/assets/schemas/assetSchema.ts (1)
  • AssetItem (86-86)
src/platform/assets/services/assetService.ts (1)
  • assetService (533-533)
src/stores/assetDownloadStore.test.ts (3)
src/schemas/apiSchema.ts (1)
  • AssetDownloadWsMessage (168-168)
src/stores/assetDownloadStore.ts (1)
  • useAssetDownloadStore (47-178)
src/platform/tasks/services/taskService.ts (2)
  • TaskResponse (44-44)
  • taskService (70-70)
src/stores/assetDownloadStore.ts (3)
src/schemas/apiSchema.ts (1)
  • AssetDownloadWsMessage (168-168)
src/platform/tasks/services/taskService.ts (1)
  • taskService (70-70)
src/scripts/api.ts (1)
  • api (1309-1309)

Comment on lines +207 to 211
async function uploadModel(): Promise<boolean> {
if (!canUploadModel.value) {
return false
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Missing concurrency guard for uploadModel.

Based on learnings, uploadModel() should have a concurrency guard at the beginning that checks if isUploading.value is already true and returns false early to prevent race conditions from multiple rapid invocations.

🔒 Recommended fix: Add concurrency guard
 async function uploadModel(): Promise<boolean> {
+  if (isUploading.value) {
+    return false
+  }
+
   if (!canUploadModel.value) {
     return false
   }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
async function uploadModel(): Promise<boolean> {
if (!canUploadModel.value) {
return false
}
async function uploadModel(): Promise<boolean> {
if (isUploading.value) {
return false
}
if (!canUploadModel.value) {
return false
}
🤖 Prompt for AI Agents
In @src/platform/assets/composables/useUploadModelWizard.ts around lines 207 -
211, The uploadModel function lacks a concurrency guard: at the top of
uploadModel() check isUploading.value and if it's true return false immediately,
otherwise set isUploading.value = true before proceeding and ensure you reset
isUploading.value = false on all exit paths (success, early returns, and error
paths) to prevent race conditions from multiple rapid invocations; reference the
uploadModel function and the isUploading and canUploadModel reactive values when
implementing the guard.

Comment on lines 19 to +31
return computed(() => {
const precision = toValue(precisionArg)
// Use step2 (correct input spec value) instead of step (legacy 10x value)
// Use step2 (correct input spec value) if available
if (options?.step2 !== undefined) {
return Number(options.step2)
}
// Use step / 10 for custom large step values (> 10) to match litegraph behavior
// This is important for extensions like Impact Pack that use custom step values (e.g., 640)
// We skip default step values (1, 10) to avoid affecting normal widgets
const step = options?.step
if (step !== undefined && step > 10) {
return Number(step) / 10
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

LGTM! Logic for handling large step values is well-documented.

The priority order (step2 → large step handling → precision-based) is clear and the inline comments provide good context for the litegraph compatibility behavior.

Minor nitpick: The Number() wrappers on lines 23 and 30 are redundant since step2 and step are already typed as number. You could simplify to:

-      return Number(options.step2)
+      return options.step2
-      return Number(step) / 10
+      return step / 10
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
return computed(() => {
const precision = toValue(precisionArg)
// Use step2 (correct input spec value) instead of step (legacy 10x value)
// Use step2 (correct input spec value) if available
if (options?.step2 !== undefined) {
return Number(options.step2)
}
// Use step / 10 for custom large step values (> 10) to match litegraph behavior
// This is important for extensions like Impact Pack that use custom step values (e.g., 640)
// We skip default step values (1, 10) to avoid affecting normal widgets
const step = options?.step
if (step !== undefined && step > 10) {
return Number(step) / 10
}
return computed(() => {
const precision = toValue(precisionArg)
// Use step2 (correct input spec value) if available
if (options?.step2 !== undefined) {
return options.step2
}
// Use step / 10 for custom large step values (> 10) to match litegraph behavior
// This is important for extensions like Impact Pack that use custom step values (e.g., 640)
// We skip default step values (1, 10) to avoid affecting normal widgets
const step = options?.step
if (step !== undefined && step > 10) {
return step / 10
🤖 Prompt for AI Agents
In
@src/renderer/extensions/vueNodes/widgets/composables/useNumberStepCalculation.ts
around lines 19 - 31, Remove redundant Number() conversions in the computed
callback of useNumberStepCalculation: when returning options.step2 and when
returning options.step / 10 simply return the numeric values directly (use
options.step2 and options.step / 10) since step2 and step are already typed as
number; keep the existing conditional order (options.step2 → large step handling
→ precision-based) and ensure the returned type remains number.

Comment on lines +9 to +20
export interface AssetDownload {
taskId: string
assetName: string
bytesTotal: number
bytesDownloaded: number
progress: number
status: 'created' | 'running' | 'completed' | 'failed'
lastUpdate: number
assetId?: string
error?: string
modelType?: string
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Consider making the status type a union literal export.

The status field uses inline union type 'created' | 'running' | 'completed' | 'failed'. Consider extracting this as a named type for reusability and consistency across components that consume this store.

♻️ Optional: Extract status type
+export type AssetDownloadStatus = 'created' | 'running' | 'completed' | 'failed'
+
 export interface AssetDownload {
   taskId: string
   assetName: string
   bytesTotal: number
   bytesDownloaded: number
   progress: number
-  status: 'created' | 'running' | 'completed' | 'failed'
+  status: AssetDownloadStatus
   lastUpdate: number
   assetId?: string
   error?: string
   modelType?: string
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export interface AssetDownload {
taskId: string
assetName: string
bytesTotal: number
bytesDownloaded: number
progress: number
status: 'created' | 'running' | 'completed' | 'failed'
lastUpdate: number
assetId?: string
error?: string
modelType?: string
}
export type AssetDownloadStatus = 'created' | 'running' | 'completed' | 'failed'
export interface AssetDownload {
taskId: string
assetName: string
bytesTotal: number
bytesDownloaded: number
progress: number
status: AssetDownloadStatus
lastUpdate: number
assetId?: string
error?: string
modelType?: string
}
🤖 Prompt for AI Agents
In @src/stores/assetDownloadStore.ts around lines 9 - 20, Extract the inline
union for the status field into a named exported type (e.g., export type
AssetDownloadStatus = 'created' | 'running' | 'completed' | 'failed') and update
the AssetDownload interface to use that type for the status property; ensure the
new type is exported so other modules can import and reuse it where they
currently reference AssetDownload.status.

Comment on lines +384 to +392
const providers = modelToNodeStore
.getAllNodeProviders(modelType)
.filter((provider) => provider.nodeDef?.name)

const nodeTypeUpdates = providers.map((provider) =>
updateModelsForNodeType(provider.nodeDef.name).then(
() => provider.nodeDef.name
)
)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Potential undefined access on provider.nodeDef.name.

The filter checks provider.nodeDef?.name but the subsequent map accesses provider.nodeDef.name without optional chaining. While the filter should exclude undefined cases, TypeScript may not narrow the type correctly.

🔧 Suggested fix for type safety
-      const providers = modelToNodeStore
-        .getAllNodeProviders(modelType)
-        .filter((provider) => provider.nodeDef?.name)
-
-      const nodeTypeUpdates = providers.map((provider) =>
-        updateModelsForNodeType(provider.nodeDef.name).then(
+      const providers = modelToNodeStore
+        .getAllNodeProviders(modelType)
+        .filter(
+          (provider): provider is typeof provider & { nodeDef: { name: string } } =>
+            !!provider.nodeDef?.name
+        )
+
+      const nodeTypeUpdates = providers.map((provider) =>
+        updateModelsForNodeType(provider.nodeDef.name).then(
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const providers = modelToNodeStore
.getAllNodeProviders(modelType)
.filter((provider) => provider.nodeDef?.name)
const nodeTypeUpdates = providers.map((provider) =>
updateModelsForNodeType(provider.nodeDef.name).then(
() => provider.nodeDef.name
)
)
const providers = modelToNodeStore
.getAllNodeProviders(modelType)
.filter(
(provider): provider is typeof provider & { nodeDef: { name: string } } =>
!!provider.nodeDef?.name
)
const nodeTypeUpdates = providers.map((provider) =>
updateModelsForNodeType(provider.nodeDef.name).then(
() => provider.nodeDef.name
)
)
🤖 Prompt for AI Agents
In @src/stores/assetsStore.ts around lines 384 - 392, The mapping later accesses
provider.nodeDef.name without TypeScript knowing nodeDef is present; narrow the
type or assert non-null before mapping. For example, make the filter a type
guard so modelToNodeStore.getAllNodeProviders(modelType).filter((p): p is /*
concrete type */ => !!p.nodeDef?.name) or, if you prefer minimal change, use a
non-null assertion when calling updateModelsForNodeType
(provider.nodeDef!.name); update the providers declaration or the map that calls
updateModelsForNodeType to ensure TypeScript knows nodeDef.name cannot be
undefined.

comfy-pr-bot and others added 3 commits January 12, 2026 22:02
…aying (#7995)

Backport of #7836 to `cloud/1.36`

Automatically created by backport workflow.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-7995-backport-cloud-1-36-fix-continue-rendering-when-3D-animation-is-playing-2e76d73d3650811fb287cc0f31c5301c)
by [Unito](https://www.unito.io)

Co-authored-by: Terry Jia <[email protected]>
…witching to vueNodes mode (#7997)

Backport of #7868 to `cloud/1.36`

Automatically created by backport workflow.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-7997-backport-cloud-1-36-fix-prevent-image-preview-resize-issues-when-switching-to-vueNodes-2e76d73d36508164be55fceba35aa0d8)
by [Unito](https://www.unito.io)

Co-authored-by: Terry Jia <[email protected]>
…at nodes (#7999)

Backport of #7900 to `cloud/1.36`

Automatically created by backport workflow.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-7999-backport-cloud-1-36-fix-price-badges-improve-Gemini-and-OpenAI-chat-nodes-2e76d73d36508169a65cce327fd7bdaf)
by [Unito](https://www.unito.io)

Co-authored-by: Alexander Piskun <[email protected]>
comfy-pr-bot and others added 2 commits January 12, 2026 22:07
…erenceVideoApi node (#8001)

Backport of #7901 to `cloud/1.36`

Automatically created by backport workflow.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8001-backport-cloud-1-36-fix-price-badges-add-missing-badge-for-WanReferenceVideoApi-node-2e76d73d3650814c9939e8f27c4e48db)
by [Unito](https://www.unito.io)

Co-authored-by: Alexander Piskun <[email protected]>
Backport of #7895 to `cloud/1.36`

Automatically created by backport workflow.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8003-backport-cloud-1-36-Fix-linked-asset-widget-promotion-in-vue-2e76d73d365081758120de9d88ee632a)
by [Unito](https://www.unito.io)

Co-authored-by: AustinMroz <[email protected]>
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
tests-ui/tests/composables/node/useNodePricing.test.ts (1)

2420-2422: Missing test coverage for WanReferenceVideoApi node.

The implementation adds WanReferenceVideoApi pricing logic and widget relevance mapping, but no corresponding tests exist for this new node. Based on learnings, tests should be written for all changes.

Consider adding tests covering:

  • Valid pricing for 1080p and non-1080p sizes
  • Edge cases (missing widgets, invalid duration)
  • Widget relevance mapping verification

Would you like me to generate test cases for WanReferenceVideoApi similar to the existing WanTextToVideoApi and WanImageToVideoApi tests?

🤖 Fix all issues with AI agents
In @src/composables/node/useNodePricing.ts:
- Around line 2136-2163: The displayPrice implementation for
WanReferenceVideoApi must validate the parsed seconds value (from durationWidget
→ seconds) and return the same fallback range used by
WanTextToVideoApi/WanImageToVideoApi when seconds is NaN; add an isNaN(seconds)
check immediately after const seconds = parseFloat(String(durationWidget.value))
and, if true, return the existing formatCreditsRangeLabel fallback (including
the '(varies with size & duration)' note) instead of proceeding with the NaN
arithmetic.
📜 Review details

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between fe48900 and bc4b3d0.

📒 Files selected for processing (4)
  • src/composables/node/useNodePricing.ts
  • src/extensions/core/load3d/Load3d.ts
  • src/renderer/extensions/vueNodes/widgets/composables/useImagePreviewWidget.ts
  • tests-ui/tests/composables/node/useNodePricing.test.ts
🧰 Additional context used
📓 Path-based instructions (12)
src/**/*.{vue,ts}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

src/**/*.{vue,ts}: Leverage VueUse functions for performance-enhancing styles
Implement proper error handling
Use vue-i18n in composition API for any string literals. Place new translation entries in src/locales/en/main.json

Files:

  • src/extensions/core/load3d/Load3d.ts
  • src/composables/node/useNodePricing.ts
  • src/renderer/extensions/vueNodes/widgets/composables/useImagePreviewWidget.ts
src/**/*.ts

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

src/**/*.ts: Use es-toolkit for utility functions
Use TypeScript for type safety

src/**/*.ts: Derive component types using vue-component-type-helpers (ComponentProps, ComponentSlots) instead of separate type files
Use es-toolkit for utility functions
Minimize the surface area (exported values) of each module and composable
Favor pure functions, especially testable ones

Files:

  • src/extensions/core/load3d/Load3d.ts
  • src/composables/node/useNodePricing.ts
  • src/renderer/extensions/vueNodes/widgets/composables/useImagePreviewWidget.ts
src/**/*.{ts,tsx,vue}

📄 CodeRabbit inference engine (src/CLAUDE.md)

src/**/*.{ts,tsx,vue}: Sanitize HTML with DOMPurify to prevent XSS attacks
Avoid using @ts-expect-error; use proper TypeScript types instead
Use es-toolkit for utility functions instead of other utility libraries
Implement proper TypeScript types throughout the codebase

src/**/*.{ts,tsx,vue}: Use separate import type statements instead of inline type in mixed imports
Apply Prettier formatting with 2-space indentation, single quotes, no trailing semicolons, 80-character width
Sort and group imports by plugin, run pnpm format before committing
Never use any type - use proper TypeScript types
Never use as any type assertions - fix the underlying type issue
Write code that is expressive and self-documenting - avoid unnecessary comments
Do not add or retain redundant comments - clean as you go
Avoid mutable state - prefer immutability and assignment at point of declaration
Watch out for Code Smells and refactor to avoid them

Files:

  • src/extensions/core/load3d/Load3d.ts
  • src/composables/node/useNodePricing.ts
  • src/renderer/extensions/vueNodes/widgets/composables/useImagePreviewWidget.ts
src/**/*.{vue,ts,tsx}

📄 CodeRabbit inference engine (src/CLAUDE.md)

Follow Vue 3 composition API style guide

Files:

  • src/extensions/core/load3d/Load3d.ts
  • src/composables/node/useNodePricing.ts
  • src/renderer/extensions/vueNodes/widgets/composables/useImagePreviewWidget.ts
src/**/*.{ts,vue}

📄 CodeRabbit inference engine (AGENTS.md)

src/**/*.{ts,vue}: Use ref for reactive state, computed() for derived values, and watch/watchEffect for side effects in Composition API
Avoid using ref with watch if a computed would suffice - minimize refs and derived state
Use provide/inject for dependency injection only when simpler alternatives (Store or shared composable) won't work
Leverage VueUse functions for performance-enhancing composables
Use VueUse function for useI18n in composition API for string literals

Files:

  • src/extensions/core/load3d/Load3d.ts
  • src/composables/node/useNodePricing.ts
  • src/renderer/extensions/vueNodes/widgets/composables/useImagePreviewWidget.ts
src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

src/**/*.{ts,tsx}: Keep functions short and functional
Minimize nesting (if statements, for loops, etc.)
Use function declarations instead of function expressions when possible

Files:

  • src/extensions/core/load3d/Load3d.ts
  • src/composables/node/useNodePricing.ts
  • src/renderer/extensions/vueNodes/widgets/composables/useImagePreviewWidget.ts
src/**/{services,composables}/**/*.{ts,tsx}

📄 CodeRabbit inference engine (src/CLAUDE.md)

src/**/{services,composables}/**/*.{ts,tsx}: Use api.apiURL() for backend endpoints instead of constructing URLs directly
Use api.fileURL() for static file access instead of constructing URLs directly

Files:

  • src/composables/node/useNodePricing.ts
  • src/renderer/extensions/vueNodes/widgets/composables/useImagePreviewWidget.ts
src/**/{composables,components}/**/*.{ts,tsx,vue}

📄 CodeRabbit inference engine (src/CLAUDE.md)

Clean up subscriptions in state management to prevent memory leaks

Files:

  • src/composables/node/useNodePricing.ts
  • src/renderer/extensions/vueNodes/widgets/composables/useImagePreviewWidget.ts
src/**/{components,composables}/**/*.{ts,tsx,vue}

📄 CodeRabbit inference engine (src/CLAUDE.md)

Use vue-i18n for ALL user-facing strings by adding them to src/locales/en/main.json

Files:

  • src/composables/node/useNodePricing.ts
  • src/renderer/extensions/vueNodes/widgets/composables/useImagePreviewWidget.ts
src/composables/**/*.ts

📄 CodeRabbit inference engine (AGENTS.md)

Name composables as useXyz.ts (e.g., useForm.ts)

Files:

  • src/composables/node/useNodePricing.ts
+(tests-ui|src)/**/*.test.ts

📄 CodeRabbit inference engine (AGENTS.md)

+(tests-ui|src)/**/*.test.ts: Unit and component tests belong in tests-ui/ or src/**/*.test.ts using Vitest
Write tests for all changes, especially bug fixes to catch future regressions
Do not write tests dependent on non-behavioral features like utility classes or styles
Do not write tests that just test the mocks - ensure tests fail when code behaves unexpectedly
Leverage Vitest's utilities for mocking where possible
Keep module mocks contained - do not use global mutable state within test files; use vi.hoisted() if necessary
Use Vue Test Utils for Component testing and follow best practices for making components easy to test
Aim for behavioral coverage of critical and new features in unit tests

Files:

  • tests-ui/tests/composables/node/useNodePricing.test.ts
+(tests-ui|src|browser_tests)/**/*.+(test.ts|spec.ts)

📄 CodeRabbit inference engine (AGENTS.md)

+(tests-ui|src|browser_tests)/**/*.+(test.ts|spec.ts): Do not write change detector tests - avoid tests that only assert default values
Be parsimonious in testing - do not write redundant tests
Don't Mock What You Don't Own

Files:

  • tests-ui/tests/composables/node/useNodePricing.test.ts
🧠 Learnings (14)
📓 Common learnings
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7897
File: src/stores/assetDownloadStore.ts:117-117
Timestamp: 2026-01-08T21:44:05.435Z
Learning: In src/stores/assetDownloadStore.ts, the api.addEventListener('asset_download', handleAssetDownload) call at module level is intentional and should persist for the application lifetime since it's a singleton Pinia store managing global WebSocket events.
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7746
File: src/platform/assets/composables/useUploadModelWizard.ts:207-219
Timestamp: 2026-01-06T19:17:12.355Z
Learning: In `src/platform/assets/composables/useUploadModelWizard.ts`, the `uploadModel()` function should have a concurrency guard at the beginning that checks if `isUploading.value` is already true and returns false early to prevent race conditions from multiple rapid invocations.
📚 Learning: 2025-12-09T03:39:54.501Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7169
File: src/platform/remote/comfyui/jobs/jobTypes.ts:1-107
Timestamp: 2025-12-09T03:39:54.501Z
Learning: In the ComfyUI_frontend project, Zod is on v3.x. Do not suggest Zod v4 standalone validators (z.uuid, z.ulid, z.cuid2, z.nanoid) until an upgrade to Zod 4 is performed. When reviewing TypeScript files (e.g., src/platform/remote/comfyui/jobs/jobTypes.ts) validate against Zod 3 capabilities and avoid introducing v4-specific features; flag any proposal to upgrade or incorporate v4-only validators and propose staying with compatible 3.x patterns.

Applied to files:

  • src/extensions/core/load3d/Load3d.ts
  • src/composables/node/useNodePricing.ts
  • tests-ui/tests/composables/node/useNodePricing.test.ts
  • src/renderer/extensions/vueNodes/widgets/composables/useImagePreviewWidget.ts
📚 Learning: 2025-12-13T11:03:11.264Z
Learnt from: christian-byrne
Repo: Comfy-Org/ComfyUI_frontend PR: 7416
File: src/stores/imagePreviewStore.ts:5-7
Timestamp: 2025-12-13T11:03:11.264Z
Learning: In the ComfyUI_frontend repository, lint rules require keeping 'import type' statements separate from non-type imports, even if importing from the same module. Do not suggest consolidating them into a single import statement. Ensure type imports remain on their own line (import type { ... } from 'module') and regular imports stay on separate lines.

Applied to files:

  • src/extensions/core/load3d/Load3d.ts
  • src/composables/node/useNodePricing.ts
  • tests-ui/tests/composables/node/useNodePricing.test.ts
  • src/renderer/extensions/vueNodes/widgets/composables/useImagePreviewWidget.ts
📚 Learning: 2025-12-17T00:40:09.635Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7537
File: src/components/ui/button/Button.stories.ts:45-55
Timestamp: 2025-12-17T00:40:09.635Z
Learning: Prefer pure function declarations over function expressions (e.g., use function foo() { ... } instead of const foo = () => { ... }) for pure functions in the repository. Function declarations are more functional-leaning, offer better hoisting clarity, and can improve readability and tooling consistency. Apply this guideline across TypeScript files in Comfy-Org/ComfyUI_frontend, including story and UI component code, except where a function expression is semantically required (e.g., callbacks, higher-order functions with closures).

Applied to files:

  • src/extensions/core/load3d/Load3d.ts
  • src/composables/node/useNodePricing.ts
  • tests-ui/tests/composables/node/useNodePricing.test.ts
  • src/renderer/extensions/vueNodes/widgets/composables/useImagePreviewWidget.ts
📚 Learning: 2025-12-30T22:22:33.836Z
Learnt from: kaili-yang
Repo: Comfy-Org/ComfyUI_frontend PR: 7805
File: src/composables/useCoreCommands.ts:439-439
Timestamp: 2025-12-30T22:22:33.836Z
Learning: When accessing reactive properties from Pinia stores in TypeScript files, avoid using .value on direct property access (e.g., useStore().isOverlayExpanded). Pinia auto-wraps refs when accessed directly, returning the primitive value. The .value accessor is only needed when destructuring store properties or when using storeToRefs().

Applied to files:

  • src/extensions/core/load3d/Load3d.ts
  • src/composables/node/useNodePricing.ts
  • tests-ui/tests/composables/node/useNodePricing.test.ts
  • src/renderer/extensions/vueNodes/widgets/composables/useImagePreviewWidget.ts
📚 Learning: 2025-12-11T12:25:15.470Z
Learnt from: christian-byrne
Repo: Comfy-Org/ComfyUI_frontend PR: 7358
File: src/components/dialog/content/signin/SignUpForm.vue:45-54
Timestamp: 2025-12-11T12:25:15.470Z
Learning: This repository uses CI automation to format code (pnpm format). Do not include manual formatting suggestions in code reviews for Comfy-Org/ComfyUI_frontend. If formatting issues are detected, rely on the CI formatter or re-run pnpm format. Focus reviews on correctness, readability, performance, accessibility, and maintainability rather than style formatting.

Applied to files:

  • src/extensions/core/load3d/Load3d.ts
  • src/composables/node/useNodePricing.ts
  • tests-ui/tests/composables/node/useNodePricing.test.ts
  • src/renderer/extensions/vueNodes/widgets/composables/useImagePreviewWidget.ts
📚 Learning: 2026-01-12T17:39:27.738Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7906
File: src/components/sidebar/tabs/AssetsSidebarTab.vue:545-552
Timestamp: 2026-01-12T17:39:27.738Z
Learning: In Vue/TypeScript files (src/**/*.{ts,tsx,vue}), prefer if/else statements over ternary operators when performing side effects or actions (e.g., mutating state, calling methods with side effects). Ternaries should be reserved for computing and returning values.

Applied to files:

  • src/extensions/core/load3d/Load3d.ts
  • src/composables/node/useNodePricing.ts
  • src/renderer/extensions/vueNodes/widgets/composables/useImagePreviewWidget.ts
📚 Learning: 2026-01-10T00:24:17.695Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-10T00:24:17.695Z
Learning: Applies to +(tests-ui|src)/**/*.test.ts : Write tests for all changes, especially bug fixes to catch future regressions

Applied to files:

  • tests-ui/tests/composables/node/useNodePricing.test.ts
📚 Learning: 2026-01-10T00:24:17.695Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-10T00:24:17.695Z
Learning: Applies to +(tests-ui|src|browser_tests)/**/*.+(test.ts|spec.ts) : Do not write change detector tests - avoid tests that only assert default values

Applied to files:

  • tests-ui/tests/composables/node/useNodePricing.test.ts
📚 Learning: 2025-12-10T03:09:13.807Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7303
File: src/components/topbar/CurrentUserPopover.test.ts:199-205
Timestamp: 2025-12-10T03:09:13.807Z
Learning: In test files, prefer selecting or asserting on accessible properties (text content, aria-label, role, accessible name) over data-testid attributes. This ensures tests validate actual user-facing behavior and accessibility, reducing reliance on implementation details like test IDs.

Applied to files:

  • tests-ui/tests/composables/node/useNodePricing.test.ts
📚 Learning: 2025-12-30T01:31:04.927Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7797
File: tests-ui/tests/lib/litegraph/src/widgets/ComboWidget.test.ts:648-648
Timestamp: 2025-12-30T01:31:04.927Z
Learning: In Vitest v4, when mocking functions that may be called as constructors (using new), the mock implementation must use function() or class syntax rather than an arrow function. Arrow mocks can cause '<anonymous> is not a constructor' errors. This is a breaking change from Vitest v3 where mocks could use an arrow function. Apply this guideline to test files that mock constructor-like calls (e.g., in tests under tests-ui, such as ComboWidget.test.ts) and ensure mock implementations are defined with function() { ... } or class { ... } to preserve constructor behavior.

Applied to files:

  • tests-ui/tests/composables/node/useNodePricing.test.ts
📚 Learning: 2026-01-08T02:40:15.482Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7894
File: src/renderer/extensions/vueNodes/widgets/components/WidgetToggleSwitch.test.ts:11-14
Timestamp: 2026-01-08T02:40:15.482Z
Learning: In TypeScript test files (e.g., any test under src), avoid duplicating interface/type definitions. Import real type definitions from the component modules under test and reference them directly, so there is a single source of truth and to prevent type drift. This improves maintainability and consistency across tests.

Applied to files:

  • tests-ui/tests/composables/node/useNodePricing.test.ts
📚 Learning: 2026-01-09T02:07:54.558Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7898
File: src/composables/usePaste.test.ts:248-248
Timestamp: 2026-01-09T02:07:54.558Z
Learning: In test files (e.g., any .test.ts or .test.tsx under src/...), when you create mock objects that partially implement an interface (such as LGraphNode), prefer casting with as Partial<InterfaceType> as InterfaceType rather than as any or as unknown as InterfaceType. This makes the incomplete implementation explicit while preserving type safety, improving readability and maintainability of tests.

Applied to files:

  • tests-ui/tests/composables/node/useNodePricing.test.ts
📚 Learning: 2025-12-22T21:36:38.905Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7649
File: tests-ui/tests/platform/cloud/subscription/components/SubscriptionPanel.test.ts:189-194
Timestamp: 2025-12-22T21:36:38.905Z
Learning: In tests-ui feature tests, do not stub primitive UI components or customized primitives (e.g., Button). Instead, import and register the real components in the test setup so tests reflect production behavior and component API usage. This improves test reliability and fidelity. If a test must avoid rendering the full component, justify the approach and consider higher-level mocks or integration tests rather than replacing the primitive itself.

Applied to files:

  • tests-ui/tests/composables/node/useNodePricing.test.ts
🧬 Code graph analysis (1)
src/composables/node/useNodePricing.ts (1)
src/lib/litegraph/src/types/widgets.ts (1)
  • IComboWidget (133-140)
🔇 Additional comments (7)
src/extensions/core/load3d/Load3d.ts (1)

389-398: LGTM! Correct fix for animation rendering.

The addition of this.animationManager.isAnimationPlaying ensures the render loop continues when a 3D animation is playing, regardless of mouse position. This prevents animations from freezing when the user moves their cursor away from the 3D viewport.

src/composables/node/useNodePricing.ts (3)

1826-1856: LGTM - Gemini pricing formatting updates are consistent.

The addition of approximate: true and separator: '-' across all Gemini model pricing branches is consistent and correctly reflects the token-based pricing semantics.


1907-1978: LGTM - OpenAI Chat pricing formatting updates are consistent.

The formatting options are applied uniformly across all OpenAI model branches.


2317-2317: LGTM - Widget relevance map entry is correct.

The WanReferenceVideoApi: ['duration', 'size'] entry correctly matches the widgets used in the pricing function.

tests-ui/tests/composables/node/useNodePricing.test.ts (2)

1667-1702: LGTM - Gemini test expectations correctly updated.

The test expectations now include approximate: true and separator: '-' options, matching the implementation changes.


1740-1897: LGTM - OpenAI test expectations correctly updated.

The test expectations are consistent with the implementation changes for all OpenAI model pricing branches.

src/renderer/extensions/vueNodes/widgets/composables/useImagePreviewWidget.ts (1)

369-375: LGTM! The canvasOnly: true option correctly prevents Vue rendering of the image preview widget.

The addition of canvasOnly: true to the widget options is a valid and supported property (defined in IWidgetOptions) that ensures the image preview renders on canvas only, preventing resize issues when switching to vueNodes mode. This pattern is consistent with other widgets in the codebase (e.g., useImageUploadWidget.ts).

…s mode (#8011)

Backport of #7934 to `cloud/1.36`

Automatically created by backport workflow.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8011-backport-cloud-1-36-fix-respect-node-resizable-property-in-vueNodes-mode-2e76d73d365081509c9bfe6603052452)
by [Unito](https://www.unito.io)

Co-authored-by: Terry Jia <[email protected]>
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In @src/composables/graph/useGraphNodeManager.ts:
- Around line 174-183: The callback currently calls widget.callback with
app.canvas which can be undefined during initialization; change the control flow
so you still compute and assign value (using normalizeWidgetValue and
widget.value) and still trigger node.widgets.forEach(w => w.triggerDraw?.()),
but guard the widget.callback invocation by checking app.canvas first (e.g., if
(!app.canvas) skip calling widget.callback(value, app.canvas, node)); ensure the
check references the same callback function and app.canvas symbol so extensions
only receive a callback when a valid canvas exists.
📜 Review details

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 74ebfec and dc0d859.

📒 Files selected for processing (2)
  • src/composables/graph/useGraphNodeManager.ts
  • src/renderer/extensions/vueNodes/components/LGraphNode.vue
🧰 Additional context used
📓 Path-based instructions (11)
src/**/*.{vue,ts}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

src/**/*.{vue,ts}: Leverage VueUse functions for performance-enhancing styles
Implement proper error handling
Use vue-i18n in composition API for any string literals. Place new translation entries in src/locales/en/main.json

Files:

  • src/composables/graph/useGraphNodeManager.ts
  • src/renderer/extensions/vueNodes/components/LGraphNode.vue
src/**/*.ts

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

src/**/*.ts: Use es-toolkit for utility functions
Use TypeScript for type safety

src/**/*.ts: Derive component types using vue-component-type-helpers (ComponentProps, ComponentSlots) instead of separate type files
Use es-toolkit for utility functions
Minimize the surface area (exported values) of each module and composable
Favor pure functions, especially testable ones

Files:

  • src/composables/graph/useGraphNodeManager.ts
src/**/{services,composables}/**/*.{ts,tsx}

📄 CodeRabbit inference engine (src/CLAUDE.md)

src/**/{services,composables}/**/*.{ts,tsx}: Use api.apiURL() for backend endpoints instead of constructing URLs directly
Use api.fileURL() for static file access instead of constructing URLs directly

Files:

  • src/composables/graph/useGraphNodeManager.ts
src/**/*.{ts,tsx,vue}

📄 CodeRabbit inference engine (src/CLAUDE.md)

src/**/*.{ts,tsx,vue}: Sanitize HTML with DOMPurify to prevent XSS attacks
Avoid using @ts-expect-error; use proper TypeScript types instead
Use es-toolkit for utility functions instead of other utility libraries
Implement proper TypeScript types throughout the codebase

src/**/*.{ts,tsx,vue}: Use separate import type statements instead of inline type in mixed imports
Apply Prettier formatting with 2-space indentation, single quotes, no trailing semicolons, 80-character width
Sort and group imports by plugin, run pnpm format before committing
Never use any type - use proper TypeScript types
Never use as any type assertions - fix the underlying type issue
Write code that is expressive and self-documenting - avoid unnecessary comments
Do not add or retain redundant comments - clean as you go
Avoid mutable state - prefer immutability and assignment at point of declaration
Watch out for Code Smells and refactor to avoid them

Files:

  • src/composables/graph/useGraphNodeManager.ts
  • src/renderer/extensions/vueNodes/components/LGraphNode.vue
src/**/{composables,components}/**/*.{ts,tsx,vue}

📄 CodeRabbit inference engine (src/CLAUDE.md)

Clean up subscriptions in state management to prevent memory leaks

Files:

  • src/composables/graph/useGraphNodeManager.ts
  • src/renderer/extensions/vueNodes/components/LGraphNode.vue
src/**/*.{vue,ts,tsx}

📄 CodeRabbit inference engine (src/CLAUDE.md)

Follow Vue 3 composition API style guide

Files:

  • src/composables/graph/useGraphNodeManager.ts
  • src/renderer/extensions/vueNodes/components/LGraphNode.vue
src/**/{components,composables}/**/*.{ts,tsx,vue}

📄 CodeRabbit inference engine (src/CLAUDE.md)

Use vue-i18n for ALL user-facing strings by adding them to src/locales/en/main.json

Files:

  • src/composables/graph/useGraphNodeManager.ts
  • src/renderer/extensions/vueNodes/components/LGraphNode.vue
src/composables/**/*.ts

📄 CodeRabbit inference engine (AGENTS.md)

Name composables as useXyz.ts (e.g., useForm.ts)

Files:

  • src/composables/graph/useGraphNodeManager.ts
src/**/*.{ts,vue}

📄 CodeRabbit inference engine (AGENTS.md)

src/**/*.{ts,vue}: Use ref for reactive state, computed() for derived values, and watch/watchEffect for side effects in Composition API
Avoid using ref with watch if a computed would suffice - minimize refs and derived state
Use provide/inject for dependency injection only when simpler alternatives (Store or shared composable) won't work
Leverage VueUse functions for performance-enhancing composables
Use VueUse function for useI18n in composition API for string literals

Files:

  • src/composables/graph/useGraphNodeManager.ts
  • src/renderer/extensions/vueNodes/components/LGraphNode.vue
src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

src/**/*.{ts,tsx}: Keep functions short and functional
Minimize nesting (if statements, for loops, etc.)
Use function declarations instead of function expressions when possible

Files:

  • src/composables/graph/useGraphNodeManager.ts
src/**/*.vue

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

src/**/*.vue: Use the Vue 3 Composition API instead of the Options API when writing Vue components (exception: when overriding or extending PrimeVue components for compatibility)
Use setup() function for component logic
Utilize ref and reactive for reactive state
Implement computed properties with computed()
Use watch and watchEffect for side effects
Implement lifecycle hooks with onMounted, onUpdated, etc.
Utilize provide/inject for dependency injection
Use vue 3.5 style of default prop declaration
Use Tailwind CSS for styling
Implement proper props and emits definitions
Utilize Vue 3's Teleport component when needed
Use Suspense for async components
Follow Vue 3 style guide and naming conventions

src/**/*.vue: Use Vue 3 Single File Components (SFCs) with Composition API only
Use <script setup lang="ts"> for component logic in Vue SFCs
Avoid <style> blocks in Vue components - use Tailwind 4 styling instead
Use vue-i18n for all string literals in Vue components - place translation entries in src/locales/en/main.json
Use Tailwind utility classes instead of dark: variant - use semantic values from style.css theme (e.g., bg-node-component-surface)
Use cn() utility from @/utils/tailwindUtil for merging Tailwind class names instead of :class="[]" or hardcoding
Never use !important or ! Tailwind prefix - fix interfering classes instead
Use Tailwind fraction utilities instead of arbitrary percentage values (e.g., w-4/5 instead of w-[80%])
Use TypeScript Vue 3.5 style default prop declaration with reactive props destructuring - avoid withDefaults or runtime props
Prefer defineModel over separately defining a prop and emit for v-model bindings
Define slots via template usage, not via defineSlots
Use same-name shorthand for slot prop bindings (e.g., :isExpanded instead of :is-expanded="isExpanded")
Do not import Vue macros unnecessarily
Avoid new usage of PrimeVue components
Use Tailwind's plurals system via i18n instead of hardcoding ...

Files:

  • src/renderer/extensions/vueNodes/components/LGraphNode.vue
🧠 Learnings (17)
📓 Common learnings
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7897
File: src/stores/assetDownloadStore.ts:117-117
Timestamp: 2026-01-08T21:44:05.435Z
Learning: In src/stores/assetDownloadStore.ts, the api.addEventListener('asset_download', handleAssetDownload) call at module level is intentional and should persist for the application lifetime since it's a singleton Pinia store managing global WebSocket events.
📚 Learning: 2025-11-24T19:47:56.371Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/lib/litegraph/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:56.371Z
Learning: Applies to src/lib/litegraph/**/*.{test,spec}.{ts,tsx} : Use provided test helpers `createTestSubgraph` and `createTestSubgraphNode` from `./fixtures/subgraphHelpers` for consistent subgraph test setup

Applied to files:

  • src/composables/graph/useGraphNodeManager.ts
📚 Learning: 2025-12-09T03:39:54.501Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7169
File: src/platform/remote/comfyui/jobs/jobTypes.ts:1-107
Timestamp: 2025-12-09T03:39:54.501Z
Learning: In the ComfyUI_frontend project, Zod is on v3.x. Do not suggest Zod v4 standalone validators (z.uuid, z.ulid, z.cuid2, z.nanoid) until an upgrade to Zod 4 is performed. When reviewing TypeScript files (e.g., src/platform/remote/comfyui/jobs/jobTypes.ts) validate against Zod 3 capabilities and avoid introducing v4-specific features; flag any proposal to upgrade or incorporate v4-only validators and propose staying with compatible 3.x patterns.

Applied to files:

  • src/composables/graph/useGraphNodeManager.ts
📚 Learning: 2025-12-13T11:03:11.264Z
Learnt from: christian-byrne
Repo: Comfy-Org/ComfyUI_frontend PR: 7416
File: src/stores/imagePreviewStore.ts:5-7
Timestamp: 2025-12-13T11:03:11.264Z
Learning: In the ComfyUI_frontend repository, lint rules require keeping 'import type' statements separate from non-type imports, even if importing from the same module. Do not suggest consolidating them into a single import statement. Ensure type imports remain on their own line (import type { ... } from 'module') and regular imports stay on separate lines.

Applied to files:

  • src/composables/graph/useGraphNodeManager.ts
📚 Learning: 2025-12-17T00:40:09.635Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7537
File: src/components/ui/button/Button.stories.ts:45-55
Timestamp: 2025-12-17T00:40:09.635Z
Learning: Prefer pure function declarations over function expressions (e.g., use function foo() { ... } instead of const foo = () => { ... }) for pure functions in the repository. Function declarations are more functional-leaning, offer better hoisting clarity, and can improve readability and tooling consistency. Apply this guideline across TypeScript files in Comfy-Org/ComfyUI_frontend, including story and UI component code, except where a function expression is semantically required (e.g., callbacks, higher-order functions with closures).

Applied to files:

  • src/composables/graph/useGraphNodeManager.ts
📚 Learning: 2025-12-30T22:22:33.836Z
Learnt from: kaili-yang
Repo: Comfy-Org/ComfyUI_frontend PR: 7805
File: src/composables/useCoreCommands.ts:439-439
Timestamp: 2025-12-30T22:22:33.836Z
Learning: When accessing reactive properties from Pinia stores in TypeScript files, avoid using .value on direct property access (e.g., useStore().isOverlayExpanded). Pinia auto-wraps refs when accessed directly, returning the primitive value. The .value accessor is only needed when destructuring store properties or when using storeToRefs().

Applied to files:

  • src/composables/graph/useGraphNodeManager.ts
📚 Learning: 2025-12-11T12:25:15.470Z
Learnt from: christian-byrne
Repo: Comfy-Org/ComfyUI_frontend PR: 7358
File: src/components/dialog/content/signin/SignUpForm.vue:45-54
Timestamp: 2025-12-11T12:25:15.470Z
Learning: This repository uses CI automation to format code (pnpm format). Do not include manual formatting suggestions in code reviews for Comfy-Org/ComfyUI_frontend. If formatting issues are detected, rely on the CI formatter or re-run pnpm format. Focus reviews on correctness, readability, performance, accessibility, and maintainability rather than style formatting.

Applied to files:

  • src/composables/graph/useGraphNodeManager.ts
  • src/renderer/extensions/vueNodes/components/LGraphNode.vue
📚 Learning: 2026-01-12T17:39:27.738Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7906
File: src/components/sidebar/tabs/AssetsSidebarTab.vue:545-552
Timestamp: 2026-01-12T17:39:27.738Z
Learning: In Vue/TypeScript files (src/**/*.{ts,tsx,vue}), prefer if/else statements over ternary operators when performing side effects or actions (e.g., mutating state, calling methods with side effects). Ternaries should be reserved for computing and returning values.

Applied to files:

  • src/composables/graph/useGraphNodeManager.ts
  • src/renderer/extensions/vueNodes/components/LGraphNode.vue
📚 Learning: 2025-12-11T03:55:51.755Z
Learnt from: simula-r
Repo: Comfy-Org/ComfyUI_frontend PR: 7252
File: src/renderer/extensions/vueNodes/components/ImagePreview.vue:151-158
Timestamp: 2025-12-11T03:55:51.755Z
Learning: In Vue components under src/renderer/extensions/vueNodes (e.g., ImagePreview.vue and LGraphNode.vue), implement image gallery keyboard navigation so that it responds to the node's focus state rather than requiring focus inside the image preview wrapper. Achieve this by wiring keyEvent handling at the node focus level and injecting or propagating key events (arrow keys) to the gallery when the node is focused/selected. This improves accessibility and aligns navigation with node-level focus behavior.

Applied to files:

  • src/renderer/extensions/vueNodes/components/LGraphNode.vue
📚 Learning: 2025-12-09T03:49:52.828Z
Learnt from: christian-byrne
Repo: Comfy-Org/ComfyUI_frontend PR: 6300
File: src/platform/updates/components/WhatsNewPopup.vue:5-13
Timestamp: 2025-12-09T03:49:52.828Z
Learning: In Vue files across the ComfyUI_frontend repo, when a button is needed, prefer the repo's common button components from src/components/button/ (IconButton.vue, TextButton.vue, IconTextButton.vue) over plain HTML <button> elements. These components wrap PrimeVue with the project’s design system styling. Use only the common button components for consistency and theming, and import them from src/components/button/ as needed.

Applied to files:

  • src/renderer/extensions/vueNodes/components/LGraphNode.vue
📚 Learning: 2025-12-09T21:40:12.361Z
Learnt from: benceruleanlu
Repo: Comfy-Org/ComfyUI_frontend PR: 7297
File: src/components/actionbar/ComfyActionbar.vue:33-43
Timestamp: 2025-12-09T21:40:12.361Z
Learning: In Vue single-file components, allow inline Tailwind CSS class strings for static classes and avoid extracting them into computed properties solely for readability. Prefer keeping static class names inline for simplicity and performance. For dynamic or conditional classes, use Vue bindings (e.g., :class) to compose classes.

Applies to all Vue files in the repository (e.g., src/**/*.vue) where Tailwind utilities are used for static styling.

Applied to files:

  • src/renderer/extensions/vueNodes/components/LGraphNode.vue
📚 Learning: 2025-12-16T22:26:49.463Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7537
File: src/components/ui/button/Button.vue:17-17
Timestamp: 2025-12-16T22:26:49.463Z
Learning: In Vue 3.5+ with <script setup>, when using defineProps<Props>() with partial destructuring (e.g., const { as = 'button', class: customClass = '' } = defineProps<Props>() ), props that are not destructured (e.g., variant, size) stay accessible by name in the template scope. This pattern is valid: you can destructure only a subset of props for convenience while referencing the remaining props directly in template expressions. Apply this guideline to Vue components across the codebase (all .vue files).

Applied to files:

  • src/renderer/extensions/vueNodes/components/LGraphNode.vue
📚 Learning: 2025-12-22T21:36:08.369Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7649
File: src/platform/cloud/subscription/components/PricingTable.vue:185-201
Timestamp: 2025-12-22T21:36:08.369Z
Learning: In Vue components, avoid creating single-use variants for common UI components (e.g., Button and other shared components). Aim for reusable variants that cover multiple use cases. It’s acceptable to temporarily mix variant props with inline Tailwind classes when a styling need is unique to one place, but plan and consolidate into shared, reusable variants as patterns emerge across the codebase.

Applied to files:

  • src/renderer/extensions/vueNodes/components/LGraphNode.vue
📚 Learning: 2026-01-08T02:26:18.357Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7893
File: src/components/button/IconGroup.vue:5-6
Timestamp: 2026-01-08T02:26:18.357Z
Learning: In components that use the cn utility from '@/utils/tailwindUtil' with tailwind-merge, rely on the behavior that conflicting Tailwind classes are resolved by keeping the last one. For example, cn('base-classes bg-default', propClass) will have any conflicting background class from propClass override bg-default. This additive pattern is intentional and aligns with the shadcn-ui convention; ensure you document or review expectations accordingly in Vue components.

Applied to files:

  • src/renderer/extensions/vueNodes/components/LGraphNode.vue
📚 Learning: 2025-12-18T02:07:38.870Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7598
File: src/components/sidebar/tabs/AssetsSidebarTab.vue:131-131
Timestamp: 2025-12-18T02:07:38.870Z
Learning: Tailwind CSS v4 safe utilities (e.g., items-center-safe, justify-*-safe, place-*-safe) are allowed in Vue components under src/ and in story files. Do not flag these specific safe variants as invalid when reviewing code in src/**/*.vue or related stories.

Applied to files:

  • src/renderer/extensions/vueNodes/components/LGraphNode.vue
📚 Learning: 2025-12-18T21:15:46.862Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7603
File: src/components/queue/QueueOverlayHeader.vue:49-59
Timestamp: 2025-12-18T21:15:46.862Z
Learning: In the ComfyUI_frontend repository, for Vue components, do not add aria-label to buttons that have visible text content (e.g., buttons containing <span> text). The visible text provides the accessible name. Use aria-label only for elements without visible labels (e.g., icon-only buttons). If a button has no visible label, provide a clear aria-label or associate with an aria-labelledby describing its action.

Applied to files:

  • src/renderer/extensions/vueNodes/components/LGraphNode.vue
📚 Learning: 2025-12-21T01:06:02.786Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7649
File: src/components/graph/selectionToolbox/ColorPickerButton.vue:15-18
Timestamp: 2025-12-21T01:06:02.786Z
Learning: In Comfy-Org/ComfyUI_frontend, in Vue component files, when a filled icon is required (e.g., 'pi pi-circle-fill'), you may mix PrimeIcons with Lucide icons since Lucide lacks filled variants. This mixed usage is acceptable when one icon library does not provide an equivalent filled icon. Apply consistently across Vue components in the src directory where icons are used, and document the rationale when a mixed approach is chosen.

Applied to files:

  • src/renderer/extensions/vueNodes/components/LGraphNode.vue
🧬 Code graph analysis (1)
src/composables/graph/useGraphNodeManager.ts (1)
src/scripts/app.ts (1)
  • app (1772-1772)
🔇 Additional comments (6)
src/composables/graph/useGraphNodeManager.ts (4)

37-37: LGTM - Import follows project conventions.

The import of app from @/scripts/app correctly accesses the singleton ComfyApp instance needed for the canvas reference in widget callbacks.


51-51: LGTM - Interface extensions are well-typed.

Adding optional hasLayoutSize and resizable properties to the respective interfaces maintains backward compatibility and clearly documents the new capabilities.

Also applies to: 78-78


192-192: LGTM - Runtime type check is appropriate.

Using typeof widget.computeLayoutSize === 'function' is a safe runtime check that correctly identifies layout-aware widgets.


329-329: LGTM - Property propagation is straightforward.

Passing through node.resizable to the Vue data maintains the node's resizable state for the UI layer.

src/renderer/extensions/vueNodes/components/LGraphNode.vue (2)

127-132: LGTM - Resize handle respects node resizable property.

Using nodeData.resizable !== false correctly preserves the default behavior (resizable when undefined) while allowing explicit opt-out. The condition is consistent with the handler logic at line 344.


340-346: LGTM - Defense in depth for resize handling.

The early return at line 344 complements the template guard at line 127, ensuring resize behavior is blocked even if the DOM element were somehow rendered. This is a good defensive pattern.

comfy-pr-bot and others added 3 commits January 12, 2026 22:24
…canvas (#8013)

Backport of #7925 to `cloud/1.36`

Automatically created by backport workflow.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8013-backport-cloud-1-36-fix-remove-negative-margin-from-legacy-widget-canvas-2e76d73d3650818a983bf2ed6095e599)
by [Unito](https://www.unito.io)

Co-authored-by: Terry Jia <[email protected]>
…slations (#8015)

Backport of #7785 to `cloud/1.36`

Automatically created by backport workflow.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8015-backport-cloud-1-36-fix-enable-immediate-file-saving-for-i18n-translations-2e76d73d365081f6b6aae8ab76c677c5)
by [Unito](https://www.unito.io)

Co-authored-by: sno <[email protected]>
Co-authored-by: github-actions <[email protected]>
Co-authored-by: Alexander Brown <[email protected]>
…nodes (#8017)

Backport of #7927 to `cloud/1.36`

Automatically created by backport workflow.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8017-backport-cloud-1-36-feat-price-badges-add-price-badges-for-Vidu2-nodes-2e76d73d3650810ea3b1d37744fe0523)
by [Unito](https://www.unito.io)

Co-authored-by: Alexander Piskun <[email protected]>
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In @src/renderer/extensions/vueNodes/widgets/components/WidgetLegacy.vue:
- Around line 66-68: Add a proper type augmentation for LGraphNode instead of
suppressing TS errors: declare an interface extension for LGraphNode that adds
an optional canvasHeight?: number (using module augmentation for the
'@/lib/litegraph/src/LGraphNode' module or a shared .d.ts) and then remove the
@ts-expect-error above the assignment to node.canvasHeight = height so the
assignment is type-safe; reference LGraphNode and the node.canvasHeight property
when adding the declaration.
📜 Review details

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between dc0d859 and eeb0fd3.

📒 Files selected for processing (2)
  • .i18nrc.cjs
  • src/renderer/extensions/vueNodes/widgets/components/WidgetLegacy.vue
🧰 Additional context used
📓 Path-based instructions (7)
src/**/*.vue

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

src/**/*.vue: Use the Vue 3 Composition API instead of the Options API when writing Vue components (exception: when overriding or extending PrimeVue components for compatibility)
Use setup() function for component logic
Utilize ref and reactive for reactive state
Implement computed properties with computed()
Use watch and watchEffect for side effects
Implement lifecycle hooks with onMounted, onUpdated, etc.
Utilize provide/inject for dependency injection
Use vue 3.5 style of default prop declaration
Use Tailwind CSS for styling
Implement proper props and emits definitions
Utilize Vue 3's Teleport component when needed
Use Suspense for async components
Follow Vue 3 style guide and naming conventions

src/**/*.vue: Use Vue 3 Single File Components (SFCs) with Composition API only
Use <script setup lang="ts"> for component logic in Vue SFCs
Avoid <style> blocks in Vue components - use Tailwind 4 styling instead
Use vue-i18n for all string literals in Vue components - place translation entries in src/locales/en/main.json
Use Tailwind utility classes instead of dark: variant - use semantic values from style.css theme (e.g., bg-node-component-surface)
Use cn() utility from @/utils/tailwindUtil for merging Tailwind class names instead of :class="[]" or hardcoding
Never use !important or ! Tailwind prefix - fix interfering classes instead
Use Tailwind fraction utilities instead of arbitrary percentage values (e.g., w-4/5 instead of w-[80%])
Use TypeScript Vue 3.5 style default prop declaration with reactive props destructuring - avoid withDefaults or runtime props
Prefer defineModel over separately defining a prop and emit for v-model bindings
Define slots via template usage, not via defineSlots
Use same-name shorthand for slot prop bindings (e.g., :isExpanded instead of :is-expanded="isExpanded")
Do not import Vue macros unnecessarily
Avoid new usage of PrimeVue components
Use Tailwind's plurals system via i18n instead of hardcoding ...

Files:

  • src/renderer/extensions/vueNodes/widgets/components/WidgetLegacy.vue
src/**/*.{vue,ts}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

src/**/*.{vue,ts}: Leverage VueUse functions for performance-enhancing styles
Implement proper error handling
Use vue-i18n in composition API for any string literals. Place new translation entries in src/locales/en/main.json

Files:

  • src/renderer/extensions/vueNodes/widgets/components/WidgetLegacy.vue
src/**/*.{ts,tsx,vue}

📄 CodeRabbit inference engine (src/CLAUDE.md)

src/**/*.{ts,tsx,vue}: Sanitize HTML with DOMPurify to prevent XSS attacks
Avoid using @ts-expect-error; use proper TypeScript types instead
Use es-toolkit for utility functions instead of other utility libraries
Implement proper TypeScript types throughout the codebase

src/**/*.{ts,tsx,vue}: Use separate import type statements instead of inline type in mixed imports
Apply Prettier formatting with 2-space indentation, single quotes, no trailing semicolons, 80-character width
Sort and group imports by plugin, run pnpm format before committing
Never use any type - use proper TypeScript types
Never use as any type assertions - fix the underlying type issue
Write code that is expressive and self-documenting - avoid unnecessary comments
Do not add or retain redundant comments - clean as you go
Avoid mutable state - prefer immutability and assignment at point of declaration
Watch out for Code Smells and refactor to avoid them

Files:

  • src/renderer/extensions/vueNodes/widgets/components/WidgetLegacy.vue
src/**/{composables,components}/**/*.{ts,tsx,vue}

📄 CodeRabbit inference engine (src/CLAUDE.md)

Clean up subscriptions in state management to prevent memory leaks

Files:

  • src/renderer/extensions/vueNodes/widgets/components/WidgetLegacy.vue
src/**/*.{vue,ts,tsx}

📄 CodeRabbit inference engine (src/CLAUDE.md)

Follow Vue 3 composition API style guide

Files:

  • src/renderer/extensions/vueNodes/widgets/components/WidgetLegacy.vue
src/**/{components,composables}/**/*.{ts,tsx,vue}

📄 CodeRabbit inference engine (src/CLAUDE.md)

Use vue-i18n for ALL user-facing strings by adding them to src/locales/en/main.json

Files:

  • src/renderer/extensions/vueNodes/widgets/components/WidgetLegacy.vue
src/**/*.{ts,vue}

📄 CodeRabbit inference engine (AGENTS.md)

src/**/*.{ts,vue}: Use ref for reactive state, computed() for derived values, and watch/watchEffect for side effects in Composition API
Avoid using ref with watch if a computed would suffice - minimize refs and derived state
Use provide/inject for dependency injection only when simpler alternatives (Store or shared composable) won't work
Leverage VueUse functions for performance-enhancing composables
Use VueUse function for useI18n in composition API for string literals

Files:

  • src/renderer/extensions/vueNodes/widgets/components/WidgetLegacy.vue
🧠 Learnings (19)
📓 Common learnings
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7897
File: src/stores/assetDownloadStore.ts:117-117
Timestamp: 2026-01-08T21:44:05.435Z
Learning: In src/stores/assetDownloadStore.ts, the api.addEventListener('asset_download', handleAssetDownload) call at module level is intentional and should persist for the application lifetime since it's a singleton Pinia store managing global WebSocket events.
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7746
File: src/platform/assets/composables/useUploadModelWizard.ts:207-219
Timestamp: 2026-01-06T19:17:12.355Z
Learning: In `src/platform/assets/composables/useUploadModelWizard.ts`, the `uploadModel()` function should have a concurrency guard at the beginning that checks if `isUploading.value` is already true and returns false early to prevent race conditions from multiple rapid invocations.
📚 Learning: 2025-11-24T19:47:34.324Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:34.324Z
Learning: Applies to src/**/*.{ts,tsx,vue} : Avoid using ts-expect-error; use proper TypeScript types instead

Applied to files:

  • src/renderer/extensions/vueNodes/widgets/components/WidgetLegacy.vue
📚 Learning: 2026-01-09T02:07:59.035Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7898
File: src/composables/usePaste.test.ts:248-248
Timestamp: 2026-01-09T02:07:59.035Z
Learning: In test files at src/**/*.test.ts, when creating mock objects that partially implement an interface (e.g., LGraphNode), use `as Partial<InterfaceType> as InterfaceType` instead of `as any` or `as unknown as InterfaceType` to explicitly acknowledge the incomplete implementation while maintaining type safety.

Applied to files:

  • src/renderer/extensions/vueNodes/widgets/components/WidgetLegacy.vue
📚 Learning: 2025-11-24T19:47:56.371Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/lib/litegraph/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:56.371Z
Learning: Applies to src/lib/litegraph/**/*.{ts,tsx} : Type assertions are an absolute last resort. In almost all cases, they are a crutch that leads to brittle code

Applied to files:

  • src/renderer/extensions/vueNodes/widgets/components/WidgetLegacy.vue
📚 Learning: 2026-01-10T00:24:17.695Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-10T00:24:17.695Z
Learning: Applies to src/**/*.{ts,tsx,vue} : Never use `as any` type assertions - fix the underlying type issue

Applied to files:

  • src/renderer/extensions/vueNodes/widgets/components/WidgetLegacy.vue
📚 Learning: 2025-12-11T03:55:51.755Z
Learnt from: simula-r
Repo: Comfy-Org/ComfyUI_frontend PR: 7252
File: src/renderer/extensions/vueNodes/components/ImagePreview.vue:151-158
Timestamp: 2025-12-11T03:55:51.755Z
Learning: In Vue components under src/renderer/extensions/vueNodes (e.g., ImagePreview.vue and LGraphNode.vue), implement image gallery keyboard navigation so that it responds to the node's focus state rather than requiring focus inside the image preview wrapper. Achieve this by wiring keyEvent handling at the node focus level and injecting or propagating key events (arrow keys) to the gallery when the node is focused/selected. This improves accessibility and aligns navigation with node-level focus behavior.

Applied to files:

  • src/renderer/extensions/vueNodes/widgets/components/WidgetLegacy.vue
📚 Learning: 2025-11-24T19:47:56.371Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/lib/litegraph/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:56.371Z
Learning: Applies to src/lib/litegraph/**/*.{js,ts,jsx,tsx} : Do not replace `&&=` or `||=` with `=` when there is no reason to do so. If you do find a reason to remove either `&&=` or `||=`, leave a comment explaining why the removal occurred

Applied to files:

  • src/renderer/extensions/vueNodes/widgets/components/WidgetLegacy.vue
📚 Learning: 2025-11-24T19:47:56.371Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/lib/litegraph/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:56.371Z
Learning: Applies to src/lib/litegraph/**/*.{test,spec}.{ts,tsx} : Use provided test helpers `createTestSubgraph` and `createTestSubgraphNode` from `./fixtures/subgraphHelpers` for consistent subgraph test setup

Applied to files:

  • src/renderer/extensions/vueNodes/widgets/components/WidgetLegacy.vue
📚 Learning: 2025-12-18T20:39:30.137Z
Learnt from: jtydhr88
Repo: Comfy-Org/ComfyUI_frontend PR: 7621
File: src/components/load3d/Load3DScene.vue:4-4
Timestamp: 2025-12-18T20:39:30.137Z
Learning: In src/components/load3d/Load3DScene.vue, the scoped `<style>` block with `!important` declarations for the canvas element is necessary because Three.js dynamically creates the canvas with inline styles, preventing direct application of Tailwind classes. This is a valid exception to the Tailwind-only styling guideline.

Applied to files:

  • src/renderer/extensions/vueNodes/widgets/components/WidgetLegacy.vue
📚 Learning: 2025-12-09T03:49:52.828Z
Learnt from: christian-byrne
Repo: Comfy-Org/ComfyUI_frontend PR: 6300
File: src/platform/updates/components/WhatsNewPopup.vue:5-13
Timestamp: 2025-12-09T03:49:52.828Z
Learning: In Vue files across the ComfyUI_frontend repo, when a button is needed, prefer the repo's common button components from src/components/button/ (IconButton.vue, TextButton.vue, IconTextButton.vue) over plain HTML <button> elements. These components wrap PrimeVue with the project’s design system styling. Use only the common button components for consistency and theming, and import them from src/components/button/ as needed.

Applied to files:

  • src/renderer/extensions/vueNodes/widgets/components/WidgetLegacy.vue
📚 Learning: 2025-12-09T21:40:12.361Z
Learnt from: benceruleanlu
Repo: Comfy-Org/ComfyUI_frontend PR: 7297
File: src/components/actionbar/ComfyActionbar.vue:33-43
Timestamp: 2025-12-09T21:40:12.361Z
Learning: In Vue single-file components, allow inline Tailwind CSS class strings for static classes and avoid extracting them into computed properties solely for readability. Prefer keeping static class names inline for simplicity and performance. For dynamic or conditional classes, use Vue bindings (e.g., :class) to compose classes.

Applies to all Vue files in the repository (e.g., src/**/*.vue) where Tailwind utilities are used for static styling.

Applied to files:

  • src/renderer/extensions/vueNodes/widgets/components/WidgetLegacy.vue
📚 Learning: 2025-12-16T22:26:49.463Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7537
File: src/components/ui/button/Button.vue:17-17
Timestamp: 2025-12-16T22:26:49.463Z
Learning: In Vue 3.5+ with <script setup>, when using defineProps<Props>() with partial destructuring (e.g., const { as = 'button', class: customClass = '' } = defineProps<Props>() ), props that are not destructured (e.g., variant, size) stay accessible by name in the template scope. This pattern is valid: you can destructure only a subset of props for convenience while referencing the remaining props directly in template expressions. Apply this guideline to Vue components across the codebase (all .vue files).

Applied to files:

  • src/renderer/extensions/vueNodes/widgets/components/WidgetLegacy.vue
📚 Learning: 2025-12-22T21:36:08.369Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7649
File: src/platform/cloud/subscription/components/PricingTable.vue:185-201
Timestamp: 2025-12-22T21:36:08.369Z
Learning: In Vue components, avoid creating single-use variants for common UI components (e.g., Button and other shared components). Aim for reusable variants that cover multiple use cases. It’s acceptable to temporarily mix variant props with inline Tailwind classes when a styling need is unique to one place, but plan and consolidate into shared, reusable variants as patterns emerge across the codebase.

Applied to files:

  • src/renderer/extensions/vueNodes/widgets/components/WidgetLegacy.vue
📚 Learning: 2026-01-08T02:26:18.357Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7893
File: src/components/button/IconGroup.vue:5-6
Timestamp: 2026-01-08T02:26:18.357Z
Learning: In components that use the cn utility from '@/utils/tailwindUtil' with tailwind-merge, rely on the behavior that conflicting Tailwind classes are resolved by keeping the last one. For example, cn('base-classes bg-default', propClass) will have any conflicting background class from propClass override bg-default. This additive pattern is intentional and aligns with the shadcn-ui convention; ensure you document or review expectations accordingly in Vue components.

Applied to files:

  • src/renderer/extensions/vueNodes/widgets/components/WidgetLegacy.vue
📚 Learning: 2025-12-11T12:25:15.470Z
Learnt from: christian-byrne
Repo: Comfy-Org/ComfyUI_frontend PR: 7358
File: src/components/dialog/content/signin/SignUpForm.vue:45-54
Timestamp: 2025-12-11T12:25:15.470Z
Learning: This repository uses CI automation to format code (pnpm format). Do not include manual formatting suggestions in code reviews for Comfy-Org/ComfyUI_frontend. If formatting issues are detected, rely on the CI formatter or re-run pnpm format. Focus reviews on correctness, readability, performance, accessibility, and maintainability rather than style formatting.

Applied to files:

  • src/renderer/extensions/vueNodes/widgets/components/WidgetLegacy.vue
📚 Learning: 2025-12-18T02:07:38.870Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7598
File: src/components/sidebar/tabs/AssetsSidebarTab.vue:131-131
Timestamp: 2025-12-18T02:07:38.870Z
Learning: Tailwind CSS v4 safe utilities (e.g., items-center-safe, justify-*-safe, place-*-safe) are allowed in Vue components under src/ and in story files. Do not flag these specific safe variants as invalid when reviewing code in src/**/*.vue or related stories.

Applied to files:

  • src/renderer/extensions/vueNodes/widgets/components/WidgetLegacy.vue
📚 Learning: 2025-12-18T21:15:46.862Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7603
File: src/components/queue/QueueOverlayHeader.vue:49-59
Timestamp: 2025-12-18T21:15:46.862Z
Learning: In the ComfyUI_frontend repository, for Vue components, do not add aria-label to buttons that have visible text content (e.g., buttons containing <span> text). The visible text provides the accessible name. Use aria-label only for elements without visible labels (e.g., icon-only buttons). If a button has no visible label, provide a clear aria-label or associate with an aria-labelledby describing its action.

Applied to files:

  • src/renderer/extensions/vueNodes/widgets/components/WidgetLegacy.vue
📚 Learning: 2025-12-21T01:06:02.786Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7649
File: src/components/graph/selectionToolbox/ColorPickerButton.vue:15-18
Timestamp: 2025-12-21T01:06:02.786Z
Learning: In Comfy-Org/ComfyUI_frontend, in Vue component files, when a filled icon is required (e.g., 'pi pi-circle-fill'), you may mix PrimeIcons with Lucide icons since Lucide lacks filled variants. This mixed usage is acceptable when one icon library does not provide an equivalent filled icon. Apply consistently across Vue components in the src directory where icons are used, and document the rationale when a mixed approach is chosen.

Applied to files:

  • src/renderer/extensions/vueNodes/widgets/components/WidgetLegacy.vue
📚 Learning: 2026-01-12T17:39:27.738Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7906
File: src/components/sidebar/tabs/AssetsSidebarTab.vue:545-552
Timestamp: 2026-01-12T17:39:27.738Z
Learning: In Vue/TypeScript files (src/**/*.{ts,tsx,vue}), prefer if/else statements over ternary operators when performing side effects or actions (e.g., mutating state, calling methods with side effects). Ternaries should be reserved for computing and returning values.

Applied to files:

  • src/renderer/extensions/vueNodes/widgets/components/WidgetLegacy.vue
🔇 Additional comments (3)
src/renderer/extensions/vueNodes/widgets/components/WidgetLegacy.vue (2)

20-20: LGTM!

The containerHeight ref initialization of 20 is consistent with the fallback value in the height computation logic.


101-107: LGTM!

The dynamic minHeight binding to containerHeight ensures proper layout sizing based on the widget's computed dimensions. The styling approach with relative positioning on the wrapper and absolute on the canvas is appropriate for this overlay pattern.

.i18nrc.cjs (1)

9-9: LGTM — immediate save behavior enabled for i18n translations.

The configuration change is syntactically correct and properly placed. The saveImmediately: true option is a documented feature of @lobehub/i18n-cli that saves translation results immediately after each chunk completes, providing real-time progress visibility and preventing data loss if the process is interrupted.

Comment on lines +66 to +68
// Set node.canvasHeight for legacy widgets that use it (e.g., Impact Pack)
// @ts-expect-error canvasHeight is a custom property used by some extensions
node.canvasHeight = height
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

Avoid @ts-expect-error; extend the type instead.

Per coding guidelines, use proper TypeScript types rather than suppressing errors. Since canvasHeight is a custom property used by extensions, consider augmenting the LGraphNode type.

♻️ Suggested approach using module augmentation

Add a type declaration (either in this file or a shared .d.ts):

declare module '@/lib/litegraph/src/LGraphNode' {
  interface LGraphNode {
    /** Custom property used by legacy widgets (e.g., Impact Pack) */
    canvasHeight?: number
  }
}

Then remove the @ts-expect-error:

   containerHeight.value = height
   // Set node.canvasHeight for legacy widgets that use it (e.g., Impact Pack)
-  // @ts-expect-error canvasHeight is a custom property used by some extensions
   node.canvasHeight = height
🤖 Prompt for AI Agents
In @src/renderer/extensions/vueNodes/widgets/components/WidgetLegacy.vue around
lines 66 - 68, Add a proper type augmentation for LGraphNode instead of
suppressing TS errors: declare an interface extension for LGraphNode that adds
an optional canvasHeight?: number (using module augmentation for the
'@/lib/litegraph/src/LGraphNode' module or a shared .d.ts) and then remove the
@ts-expect-error above the assignment to node.canvasHeight = height so the
assignment is type-safe; reference LGraphNode and the node.canvasHeight property
when adding the declaration.

Backport of #7802 to `cloud/1.36`

Automatically created by backport workflow.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8019-backport-cloud-1-36-Fix-reactivity-washing-in-refreshNodeSlots-2e76d73d36508113b3eec5aab31165a7)
by [Unito](https://www.unito.io)

Co-authored-by: AustinMroz <[email protected]>
Co-authored-by: Alexander Brown <[email protected]>
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Fix all issues with AI agents
In @src/composables/node/useNodePricing.ts:
- Line 2552: Remove 'model' from the relevance array for Vidu2TextToVideoNode so
widget changes to model no longer trigger price recalculations; update the
Vidu2TextToVideoNode relevance entry (currently Vidu2TextToVideoNode: ['model',
'duration', 'resolution']) to only include the widgets actually used by its
pricing logic (e.g., ['duration', 'resolution']), and adjust any nearby comments
if they reference model to reflect that Text-to-Video uses the fixed Q2 model.
- Around line 2249-2257: The Q2-turbo 720p branch lacks NaN/finite validation so
when duration is NaN the math yields NaN; add a guard like the one used in
Vidu2StartEndToVideoNode: check Number.isFinite(duration) (or
!Number.isFinite(duration)) at the start of this branch and return a safe
formatted value (e.g., formatCreditsLabel(0) or the same fallback used
elsewhere) before performing comparisons and arithmetic, so formatCreditsLabel
never receives NaN.
- Around line 2296-2327: The comment and code disagree on the audio surcharge;
in the block that defines audioValue/hasAudio and computes cost (look for
audioWidget, hasAudio, basePrice, pricePerSecond, is1080p, duration, and cost),
either update the numeric surcharge to match the comment by changing the added
value from 0.075 to 0.75, or if 0.075 is correct, update the comment to reflect
"$0.075" so the code and comment agree; make the single change where cost is
incremented inside the hasAudio check.
📜 Review details

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between eeb0fd3 and fca4931.

📒 Files selected for processing (1)
  • src/composables/node/useNodePricing.ts
🧰 Additional context used
📓 Path-based instructions (10)
src/**/*.{vue,ts}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

src/**/*.{vue,ts}: Leverage VueUse functions for performance-enhancing styles
Implement proper error handling
Use vue-i18n in composition API for any string literals. Place new translation entries in src/locales/en/main.json

Files:

  • src/composables/node/useNodePricing.ts
src/**/*.ts

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

src/**/*.ts: Use es-toolkit for utility functions
Use TypeScript for type safety

src/**/*.ts: Derive component types using vue-component-type-helpers (ComponentProps, ComponentSlots) instead of separate type files
Use es-toolkit for utility functions
Minimize the surface area (exported values) of each module and composable
Favor pure functions, especially testable ones

Files:

  • src/composables/node/useNodePricing.ts
src/**/{services,composables}/**/*.{ts,tsx}

📄 CodeRabbit inference engine (src/CLAUDE.md)

src/**/{services,composables}/**/*.{ts,tsx}: Use api.apiURL() for backend endpoints instead of constructing URLs directly
Use api.fileURL() for static file access instead of constructing URLs directly

Files:

  • src/composables/node/useNodePricing.ts
src/**/*.{ts,tsx,vue}

📄 CodeRabbit inference engine (src/CLAUDE.md)

src/**/*.{ts,tsx,vue}: Sanitize HTML with DOMPurify to prevent XSS attacks
Avoid using @ts-expect-error; use proper TypeScript types instead
Use es-toolkit for utility functions instead of other utility libraries
Implement proper TypeScript types throughout the codebase

src/**/*.{ts,tsx,vue}: Use separate import type statements instead of inline type in mixed imports
Apply Prettier formatting with 2-space indentation, single quotes, no trailing semicolons, 80-character width
Sort and group imports by plugin, run pnpm format before committing
Never use any type - use proper TypeScript types
Never use as any type assertions - fix the underlying type issue
Write code that is expressive and self-documenting - avoid unnecessary comments
Do not add or retain redundant comments - clean as you go
Avoid mutable state - prefer immutability and assignment at point of declaration
Watch out for Code Smells and refactor to avoid them

Files:

  • src/composables/node/useNodePricing.ts
src/**/{composables,components}/**/*.{ts,tsx,vue}

📄 CodeRabbit inference engine (src/CLAUDE.md)

Clean up subscriptions in state management to prevent memory leaks

Files:

  • src/composables/node/useNodePricing.ts
src/**/*.{vue,ts,tsx}

📄 CodeRabbit inference engine (src/CLAUDE.md)

Follow Vue 3 composition API style guide

Files:

  • src/composables/node/useNodePricing.ts
src/**/{components,composables}/**/*.{ts,tsx,vue}

📄 CodeRabbit inference engine (src/CLAUDE.md)

Use vue-i18n for ALL user-facing strings by adding them to src/locales/en/main.json

Files:

  • src/composables/node/useNodePricing.ts
src/composables/**/*.ts

📄 CodeRabbit inference engine (AGENTS.md)

Name composables as useXyz.ts (e.g., useForm.ts)

Files:

  • src/composables/node/useNodePricing.ts
src/**/*.{ts,vue}

📄 CodeRabbit inference engine (AGENTS.md)

src/**/*.{ts,vue}: Use ref for reactive state, computed() for derived values, and watch/watchEffect for side effects in Composition API
Avoid using ref with watch if a computed would suffice - minimize refs and derived state
Use provide/inject for dependency injection only when simpler alternatives (Store or shared composable) won't work
Leverage VueUse functions for performance-enhancing composables
Use VueUse function for useI18n in composition API for string literals

Files:

  • src/composables/node/useNodePricing.ts
src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

src/**/*.{ts,tsx}: Keep functions short and functional
Minimize nesting (if statements, for loops, etc.)
Use function declarations instead of function expressions when possible

Files:

  • src/composables/node/useNodePricing.ts
🧠 Learnings (7)
📓 Common learnings
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7897
File: src/stores/assetDownloadStore.ts:117-117
Timestamp: 2026-01-08T21:44:05.435Z
Learning: In src/stores/assetDownloadStore.ts, the api.addEventListener('asset_download', handleAssetDownload) call at module level is intentional and should persist for the application lifetime since it's a singleton Pinia store managing global WebSocket events.
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7746
File: src/platform/assets/composables/useUploadModelWizard.ts:207-219
Timestamp: 2026-01-06T19:17:12.355Z
Learning: In `src/platform/assets/composables/useUploadModelWizard.ts`, the `uploadModel()` function should have a concurrency guard at the beginning that checks if `isUploading.value` is already true and returns false early to prevent race conditions from multiple rapid invocations.
📚 Learning: 2025-12-09T03:39:54.501Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7169
File: src/platform/remote/comfyui/jobs/jobTypes.ts:1-107
Timestamp: 2025-12-09T03:39:54.501Z
Learning: In the ComfyUI_frontend project, Zod is on v3.x. Do not suggest Zod v4 standalone validators (z.uuid, z.ulid, z.cuid2, z.nanoid) until an upgrade to Zod 4 is performed. When reviewing TypeScript files (e.g., src/platform/remote/comfyui/jobs/jobTypes.ts) validate against Zod 3 capabilities and avoid introducing v4-specific features; flag any proposal to upgrade or incorporate v4-only validators and propose staying with compatible 3.x patterns.

Applied to files:

  • src/composables/node/useNodePricing.ts
📚 Learning: 2025-12-13T11:03:11.264Z
Learnt from: christian-byrne
Repo: Comfy-Org/ComfyUI_frontend PR: 7416
File: src/stores/imagePreviewStore.ts:5-7
Timestamp: 2025-12-13T11:03:11.264Z
Learning: In the ComfyUI_frontend repository, lint rules require keeping 'import type' statements separate from non-type imports, even if importing from the same module. Do not suggest consolidating them into a single import statement. Ensure type imports remain on their own line (import type { ... } from 'module') and regular imports stay on separate lines.

Applied to files:

  • src/composables/node/useNodePricing.ts
📚 Learning: 2025-12-17T00:40:09.635Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7537
File: src/components/ui/button/Button.stories.ts:45-55
Timestamp: 2025-12-17T00:40:09.635Z
Learning: Prefer pure function declarations over function expressions (e.g., use function foo() { ... } instead of const foo = () => { ... }) for pure functions in the repository. Function declarations are more functional-leaning, offer better hoisting clarity, and can improve readability and tooling consistency. Apply this guideline across TypeScript files in Comfy-Org/ComfyUI_frontend, including story and UI component code, except where a function expression is semantically required (e.g., callbacks, higher-order functions with closures).

Applied to files:

  • src/composables/node/useNodePricing.ts
📚 Learning: 2025-12-30T22:22:33.836Z
Learnt from: kaili-yang
Repo: Comfy-Org/ComfyUI_frontend PR: 7805
File: src/composables/useCoreCommands.ts:439-439
Timestamp: 2025-12-30T22:22:33.836Z
Learning: When accessing reactive properties from Pinia stores in TypeScript files, avoid using .value on direct property access (e.g., useStore().isOverlayExpanded). Pinia auto-wraps refs when accessed directly, returning the primitive value. The .value accessor is only needed when destructuring store properties or when using storeToRefs().

Applied to files:

  • src/composables/node/useNodePricing.ts
📚 Learning: 2025-12-11T12:25:15.470Z
Learnt from: christian-byrne
Repo: Comfy-Org/ComfyUI_frontend PR: 7358
File: src/components/dialog/content/signin/SignUpForm.vue:45-54
Timestamp: 2025-12-11T12:25:15.470Z
Learning: This repository uses CI automation to format code (pnpm format). Do not include manual formatting suggestions in code reviews for Comfy-Org/ComfyUI_frontend. If formatting issues are detected, rely on the CI formatter or re-run pnpm format. Focus reviews on correctness, readability, performance, accessibility, and maintainability rather than style formatting.

Applied to files:

  • src/composables/node/useNodePricing.ts
📚 Learning: 2026-01-12T17:39:27.738Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7906
File: src/components/sidebar/tabs/AssetsSidebarTab.vue:545-552
Timestamp: 2026-01-12T17:39:27.738Z
Learning: In Vue/TypeScript files (src/**/*.{ts,tsx,vue}), prefer if/else statements over ternary operators when performing side effects or actions (e.g., mutating state, calling methods with side effects). Ternaries should be reserved for computing and returning values.

Applied to files:

  • src/composables/node/useNodePricing.ts
🧬 Code graph analysis (1)
src/composables/node/useNodePricing.ts (1)
src/lib/litegraph/src/types/widgets.ts (1)
  • IComboWidget (135-142)
🔇 Additional comments (7)
src/composables/node/useNodePricing.ts (7)

1826-1856: LGTM! Consistent formatting update for token-based pricing.

The standardization to use approximate: true and separator: '-' improves readability for price ranges and correctly indicates these are estimates.


1907-1978: LGTM! Consistent with GeminiNode formatting changes.


2136-2164: LGTM! Pricing logic is sound.

The calculation correctly handles input and output pricing components based on resolution. The fallback range appropriately covers expected scenarios when widgets are missing.


2165-2207: LGTM! Pricing logic is correct.

The pricing formula correctly applies base price plus per-second rate for additional seconds. The validation at lines 2198-2202 properly handles invalid duration values.


2258-2273: LGTM! Fallback handling for unknown models is appropriate.


2298-2303: Audio detection logic is thorough.

The check correctly handles various falsy representations (undefined, null, 'false', 'none', empty string).


2333-2396: LGTM! Proper NaN handling in Q2-turbo 720p branch.

The !Number.isFinite(duration) check at line 2374 correctly prevents NaN from causing calculation errors, unlike the similar code in Vidu2ImageToVideoNode.

Comment on lines +2249 to +2257
if (duration <= 1) {
return formatCreditsLabel(0.04)
}
if (duration <= 2) {
return formatCreditsLabel(0.05)
}
const cost = 0.05 + 0.05 * (duration - 2)
return formatCreditsLabel(cost)
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Missing NaN validation in Q2-turbo 720p branch.

When duration is NaN (invalid input), the comparisons duration <= 1 and duration <= 2 both return false, causing the code to fall through to line 2255 where 0.05 + 0.05 * (NaN - 2) produces NaN. This NaN is then passed to formatCreditsLabel, resulting in malformed output.

Compare to Vidu2StartEndToVideoNode (line 2374) which correctly handles this case with !Number.isFinite(duration).

Proposed fix
-            if (duration <= 1) {
+            if (!Number.isFinite(duration) || duration <= 1) {
               return formatCreditsLabel(0.04)
             }
🤖 Prompt for AI Agents
In @src/composables/node/useNodePricing.ts around lines 2249 - 2257, The
Q2-turbo 720p branch lacks NaN/finite validation so when duration is NaN the
math yields NaN; add a guard like the one used in Vidu2StartEndToVideoNode:
check Number.isFinite(duration) (or !Number.isFinite(duration)) at the start of
this branch and return a safe formatted value (e.g., formatCreditsLabel(0) or
the same fallback used elsewhere) before performing comparisons and arithmetic,
so formatCreditsLabel never receives NaN.

Comment on lines +2296 to +2327
// Check if audio is enabled (adds $0.75)
const audioValue = audioWidget?.value
const hasAudio =
audioValue !== undefined &&
audioValue !== null &&
String(audioValue).toLowerCase() !== 'false' &&
String(audioValue).toLowerCase() !== 'none' &&
audioValue !== ''

// Reference-to-Video uses Q2 model
// 720P: Starts at $0.125, +$0.025/sec
// 1080P: Starts at $0.375, +$0.05/sec
let basePrice: number
let pricePerSecond: number

if (is1080p) {
basePrice = 0.375
pricePerSecond = 0.05
} else {
// 720P default
basePrice = 0.125
pricePerSecond = 0.025
}

let cost = basePrice
if (Number.isFinite(duration) && duration > 0) {
cost = basePrice + pricePerSecond * (duration - 1)
}

// Audio adds $0.75 on top
if (hasAudio) {
cost += 0.075
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Comment and code disagree on audio pricing: $0.75 vs $0.075.

Line 2296 comment states audio "adds $0.75" but line 2327 adds 0.075 (ten times less). This is a significant discrepancy that affects displayed pricing.

Proposed fix (if $0.75 is correct)
         // Audio adds $0.75 on top
         if (hasAudio) {
-          cost += 0.075
+          cost += 0.75
         }

Or update the comment if 0.075 is the intended value.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// Check if audio is enabled (adds $0.75)
const audioValue = audioWidget?.value
const hasAudio =
audioValue !== undefined &&
audioValue !== null &&
String(audioValue).toLowerCase() !== 'false' &&
String(audioValue).toLowerCase() !== 'none' &&
audioValue !== ''
// Reference-to-Video uses Q2 model
// 720P: Starts at $0.125, +$0.025/sec
// 1080P: Starts at $0.375, +$0.05/sec
let basePrice: number
let pricePerSecond: number
if (is1080p) {
basePrice = 0.375
pricePerSecond = 0.05
} else {
// 720P default
basePrice = 0.125
pricePerSecond = 0.025
}
let cost = basePrice
if (Number.isFinite(duration) && duration > 0) {
cost = basePrice + pricePerSecond * (duration - 1)
}
// Audio adds $0.75 on top
if (hasAudio) {
cost += 0.075
// Check if audio is enabled (adds $0.75)
const audioValue = audioWidget?.value
const hasAudio =
audioValue !== undefined &&
audioValue !== null &&
String(audioValue).toLowerCase() !== 'false' &&
String(audioValue).toLowerCase() !== 'none' &&
audioValue !== ''
// Reference-to-Video uses Q2 model
// 720P: Starts at $0.125, +$0.025/sec
// 1080P: Starts at $0.375, +$0.05/sec
let basePrice: number
let pricePerSecond: number
if (is1080p) {
basePrice = 0.375
pricePerSecond = 0.05
} else {
// 720P default
basePrice = 0.125
pricePerSecond = 0.025
}
let cost = basePrice
if (Number.isFinite(duration) && duration > 0) {
cost = basePrice + pricePerSecond * (duration - 1)
}
// Audio adds $0.75 on top
if (hasAudio) {
cost += 0.75
}
🤖 Prompt for AI Agents
In @src/composables/node/useNodePricing.ts around lines 2296 - 2327, The comment
and code disagree on the audio surcharge; in the block that defines
audioValue/hasAudio and computes cost (look for audioWidget, hasAudio,
basePrice, pricePerSecond, is1080p, duration, and cost), either update the
numeric surcharge to match the comment by changing the added value from 0.075 to
0.75, or if 0.075 is correct, update the comment to reflect "$0.075" so the code
and comment agree; make the single change where cost is incremented inside the
hasAudio check.

LtxvApiTextToVideo: ['model', 'duration', 'resolution'],
LtxvApiImageToVideo: ['model', 'duration', 'resolution']
LtxvApiImageToVideo: ['model', 'duration', 'resolution'],
Vidu2TextToVideoNode: ['model', 'duration', 'resolution'],
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Consider removing 'model' from Vidu2TextToVideoNode widget relevance.

The pricing function for Vidu2TextToVideoNode (line 2166) doesn't use the model widget—the comment states "Text-to-Video uses Q2 model only." Including 'model' in the relevance array may trigger unnecessary price recalculations when the model widget changes.

Proposed fix
-      Vidu2TextToVideoNode: ['model', 'duration', 'resolution'],
+      Vidu2TextToVideoNode: ['duration', 'resolution'],
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
Vidu2TextToVideoNode: ['model', 'duration', 'resolution'],
Vidu2TextToVideoNode: ['duration', 'resolution'],
🤖 Prompt for AI Agents
In @src/composables/node/useNodePricing.ts at line 2552, Remove 'model' from the
relevance array for Vidu2TextToVideoNode so widget changes to model no longer
trigger price recalculations; update the Vidu2TextToVideoNode relevance entry
(currently Vidu2TextToVideoNode: ['model', 'duration', 'resolution']) to only
include the widgets actually used by its pricing logic (e.g., ['duration',
'resolution']), and adjust any nearby comments if they reference model to
reflect that Text-to-Video uses the fixed Q2 model.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Fix all issues with AI agents
In @src/composables/graph/useGraphNodeManager.ts:
- Around line 174-183: The callback wrapper in useGraphNodeManager.ts calls
widget.value = ... which for DOM widgets triggers their setter and invokes
widget.callback, then unconditionally calls widget.callback again; prevent
double invocation by detecting DOM widgets (e.g., using isDOMWidget(widget) or
DOMWidget instanceof check) and only calling widget.callback(value, app.canvas,
node) for non-DOM widgets (or alternatively assign the internal value field for
DOM widgets instead of using the setter); keep the normalizeWidgetValue call and
ensure node.widgets?.forEach((w) => w.triggerDraw?.()) still runs.

In @tests-ui/tests/composables/graph/useGraphNodeManager.test.ts:
- Around line 38-41: Replace the hardcoded nodeId '1' in the graph.trigger call
with the actual node object's ID to avoid relying on LGraph internals; locate
the test where graph.trigger('node:slot-links:changed', { nodeId: '1', slotType:
NodeSlotType.INPUT }) is used and change the payload to use the node instance
(e.g., node.id or the variable holding the created node's id) so the event uses
the real node id returned when the node was created.
- Line 10: The test currently calls setActivePinia(createTestingPinia()) at
module scope which shares Pinia state across tests; move this call into a
beforeEach hook so each test gets a fresh store instance (use beforeEach(() =>
setActivePinia(createTestingPinia()))), and optionally clear/reset after each
test (afterEach(() => setActivePinia(undefined) or stub reset) to ensure
isolation; update any imports or setup referencing
setActivePinia/createTestingPinia accordingly so tests use the per-test Pinia
instance.
📜 Review details

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between fca4931 and 8e62359.

📒 Files selected for processing (2)
  • src/composables/graph/useGraphNodeManager.ts
  • tests-ui/tests/composables/graph/useGraphNodeManager.test.ts
🧰 Additional context used
📓 Path-based instructions (12)
src/**/*.{vue,ts}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

src/**/*.{vue,ts}: Leverage VueUse functions for performance-enhancing styles
Implement proper error handling
Use vue-i18n in composition API for any string literals. Place new translation entries in src/locales/en/main.json

Files:

  • src/composables/graph/useGraphNodeManager.ts
src/**/*.ts

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

src/**/*.ts: Use es-toolkit for utility functions
Use TypeScript for type safety

src/**/*.ts: Derive component types using vue-component-type-helpers (ComponentProps, ComponentSlots) instead of separate type files
Use es-toolkit for utility functions
Minimize the surface area (exported values) of each module and composable
Favor pure functions, especially testable ones

Files:

  • src/composables/graph/useGraphNodeManager.ts
src/**/{services,composables}/**/*.{ts,tsx}

📄 CodeRabbit inference engine (src/CLAUDE.md)

src/**/{services,composables}/**/*.{ts,tsx}: Use api.apiURL() for backend endpoints instead of constructing URLs directly
Use api.fileURL() for static file access instead of constructing URLs directly

Files:

  • src/composables/graph/useGraphNodeManager.ts
src/**/*.{ts,tsx,vue}

📄 CodeRabbit inference engine (src/CLAUDE.md)

src/**/*.{ts,tsx,vue}: Sanitize HTML with DOMPurify to prevent XSS attacks
Avoid using @ts-expect-error; use proper TypeScript types instead
Use es-toolkit for utility functions instead of other utility libraries
Implement proper TypeScript types throughout the codebase

src/**/*.{ts,tsx,vue}: Use separate import type statements instead of inline type in mixed imports
Apply Prettier formatting with 2-space indentation, single quotes, no trailing semicolons, 80-character width
Sort and group imports by plugin, run pnpm format before committing
Never use any type - use proper TypeScript types
Never use as any type assertions - fix the underlying type issue
Write code that is expressive and self-documenting - avoid unnecessary comments
Do not add or retain redundant comments - clean as you go
Avoid mutable state - prefer immutability and assignment at point of declaration
Watch out for Code Smells and refactor to avoid them

Files:

  • src/composables/graph/useGraphNodeManager.ts
src/**/{composables,components}/**/*.{ts,tsx,vue}

📄 CodeRabbit inference engine (src/CLAUDE.md)

Clean up subscriptions in state management to prevent memory leaks

Files:

  • src/composables/graph/useGraphNodeManager.ts
src/**/*.{vue,ts,tsx}

📄 CodeRabbit inference engine (src/CLAUDE.md)

Follow Vue 3 composition API style guide

Files:

  • src/composables/graph/useGraphNodeManager.ts
src/**/{components,composables}/**/*.{ts,tsx,vue}

📄 CodeRabbit inference engine (src/CLAUDE.md)

Use vue-i18n for ALL user-facing strings by adding them to src/locales/en/main.json

Files:

  • src/composables/graph/useGraphNodeManager.ts
src/composables/**/*.ts

📄 CodeRabbit inference engine (AGENTS.md)

Name composables as useXyz.ts (e.g., useForm.ts)

Files:

  • src/composables/graph/useGraphNodeManager.ts
src/**/*.{ts,vue}

📄 CodeRabbit inference engine (AGENTS.md)

src/**/*.{ts,vue}: Use ref for reactive state, computed() for derived values, and watch/watchEffect for side effects in Composition API
Avoid using ref with watch if a computed would suffice - minimize refs and derived state
Use provide/inject for dependency injection only when simpler alternatives (Store or shared composable) won't work
Leverage VueUse functions for performance-enhancing composables
Use VueUse function for useI18n in composition API for string literals

Files:

  • src/composables/graph/useGraphNodeManager.ts
src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

src/**/*.{ts,tsx}: Keep functions short and functional
Minimize nesting (if statements, for loops, etc.)
Use function declarations instead of function expressions when possible

Files:

  • src/composables/graph/useGraphNodeManager.ts
+(tests-ui|src)/**/*.test.ts

📄 CodeRabbit inference engine (AGENTS.md)

+(tests-ui|src)/**/*.test.ts: Unit and component tests belong in tests-ui/ or src/**/*.test.ts using Vitest
Write tests for all changes, especially bug fixes to catch future regressions
Do not write tests dependent on non-behavioral features like utility classes or styles
Do not write tests that just test the mocks - ensure tests fail when code behaves unexpectedly
Leverage Vitest's utilities for mocking where possible
Keep module mocks contained - do not use global mutable state within test files; use vi.hoisted() if necessary
Use Vue Test Utils for Component testing and follow best practices for making components easy to test
Aim for behavioral coverage of critical and new features in unit tests

Files:

  • tests-ui/tests/composables/graph/useGraphNodeManager.test.ts
+(tests-ui|src|browser_tests)/**/*.+(test.ts|spec.ts)

📄 CodeRabbit inference engine (AGENTS.md)

+(tests-ui|src|browser_tests)/**/*.+(test.ts|spec.ts): Do not write change detector tests - avoid tests that only assert default values
Be parsimonious in testing - do not write redundant tests
Don't Mock What You Don't Own

Files:

  • tests-ui/tests/composables/graph/useGraphNodeManager.test.ts
🧠 Learnings (24)
📓 Common learnings
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7897
File: src/stores/assetDownloadStore.ts:117-117
Timestamp: 2026-01-08T21:44:05.435Z
Learning: In src/stores/assetDownloadStore.ts, the api.addEventListener('asset_download', handleAssetDownload) call at module level is intentional and should persist for the application lifetime since it's a singleton Pinia store managing global WebSocket events.
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7746
File: src/platform/assets/composables/useUploadModelWizard.ts:207-219
Timestamp: 2026-01-06T19:17:12.355Z
Learning: In `src/platform/assets/composables/useUploadModelWizard.ts`, the `uploadModel()` function should have a concurrency guard at the beginning that checks if `isUploading.value` is already true and returns false early to prevent race conditions from multiple rapid invocations.
📚 Learning: 2025-12-09T03:39:54.501Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7169
File: src/platform/remote/comfyui/jobs/jobTypes.ts:1-107
Timestamp: 2025-12-09T03:39:54.501Z
Learning: In the ComfyUI_frontend project, Zod is on v3.x. Do not suggest Zod v4 standalone validators (z.uuid, z.ulid, z.cuid2, z.nanoid) until an upgrade to Zod 4 is performed. When reviewing TypeScript files (e.g., src/platform/remote/comfyui/jobs/jobTypes.ts) validate against Zod 3 capabilities and avoid introducing v4-specific features; flag any proposal to upgrade or incorporate v4-only validators and propose staying with compatible 3.x patterns.

Applied to files:

  • src/composables/graph/useGraphNodeManager.ts
  • tests-ui/tests/composables/graph/useGraphNodeManager.test.ts
📚 Learning: 2025-12-13T11:03:11.264Z
Learnt from: christian-byrne
Repo: Comfy-Org/ComfyUI_frontend PR: 7416
File: src/stores/imagePreviewStore.ts:5-7
Timestamp: 2025-12-13T11:03:11.264Z
Learning: In the ComfyUI_frontend repository, lint rules require keeping 'import type' statements separate from non-type imports, even if importing from the same module. Do not suggest consolidating them into a single import statement. Ensure type imports remain on their own line (import type { ... } from 'module') and regular imports stay on separate lines.

Applied to files:

  • src/composables/graph/useGraphNodeManager.ts
  • tests-ui/tests/composables/graph/useGraphNodeManager.test.ts
📚 Learning: 2025-12-17T00:40:09.635Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7537
File: src/components/ui/button/Button.stories.ts:45-55
Timestamp: 2025-12-17T00:40:09.635Z
Learning: Prefer pure function declarations over function expressions (e.g., use function foo() { ... } instead of const foo = () => { ... }) for pure functions in the repository. Function declarations are more functional-leaning, offer better hoisting clarity, and can improve readability and tooling consistency. Apply this guideline across TypeScript files in Comfy-Org/ComfyUI_frontend, including story and UI component code, except where a function expression is semantically required (e.g., callbacks, higher-order functions with closures).

Applied to files:

  • src/composables/graph/useGraphNodeManager.ts
  • tests-ui/tests/composables/graph/useGraphNodeManager.test.ts
📚 Learning: 2025-12-30T22:22:33.836Z
Learnt from: kaili-yang
Repo: Comfy-Org/ComfyUI_frontend PR: 7805
File: src/composables/useCoreCommands.ts:439-439
Timestamp: 2025-12-30T22:22:33.836Z
Learning: When accessing reactive properties from Pinia stores in TypeScript files, avoid using .value on direct property access (e.g., useStore().isOverlayExpanded). Pinia auto-wraps refs when accessed directly, returning the primitive value. The .value accessor is only needed when destructuring store properties or when using storeToRefs().

Applied to files:

  • src/composables/graph/useGraphNodeManager.ts
  • tests-ui/tests/composables/graph/useGraphNodeManager.test.ts
📚 Learning: 2025-12-11T12:25:15.470Z
Learnt from: christian-byrne
Repo: Comfy-Org/ComfyUI_frontend PR: 7358
File: src/components/dialog/content/signin/SignUpForm.vue:45-54
Timestamp: 2025-12-11T12:25:15.470Z
Learning: This repository uses CI automation to format code (pnpm format). Do not include manual formatting suggestions in code reviews for Comfy-Org/ComfyUI_frontend. If formatting issues are detected, rely on the CI formatter or re-run pnpm format. Focus reviews on correctness, readability, performance, accessibility, and maintainability rather than style formatting.

Applied to files:

  • src/composables/graph/useGraphNodeManager.ts
  • tests-ui/tests/composables/graph/useGraphNodeManager.test.ts
📚 Learning: 2026-01-12T17:39:27.738Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7906
File: src/components/sidebar/tabs/AssetsSidebarTab.vue:545-552
Timestamp: 2026-01-12T17:39:27.738Z
Learning: In Vue/TypeScript files (src/**/*.{ts,tsx,vue}), prefer if/else statements over ternary operators when performing side effects or actions (e.g., mutating state, calling methods with side effects). Ternaries should be reserved for computing and returning values.

Applied to files:

  • src/composables/graph/useGraphNodeManager.ts
📚 Learning: 2025-11-24T19:47:56.371Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/lib/litegraph/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:56.371Z
Learning: Applies to src/lib/litegraph/**/*.{test,spec}.{ts,tsx} : Use provided test helpers `createTestSubgraph` and `createTestSubgraphNode` from `./fixtures/subgraphHelpers` for consistent subgraph test setup

Applied to files:

  • tests-ui/tests/composables/graph/useGraphNodeManager.test.ts
📚 Learning: 2025-11-24T19:47:56.371Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/lib/litegraph/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:56.371Z
Learning: Applies to src/lib/litegraph/**/*.{test,spec}.{ts,tsx} : When writing tests for subgraph-related code, always import from the barrel export at `@/lib/litegraph/src/litegraph` to avoid circular dependency issues

Applied to files:

  • tests-ui/tests/composables/graph/useGraphNodeManager.test.ts
📚 Learning: 2026-01-10T00:24:17.695Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-10T00:24:17.695Z
Learning: Applies to +(tests-ui|src)/**/*.test.ts : Use Vue Test Utils for Component testing and follow best practices for making components easy to test

Applied to files:

  • tests-ui/tests/composables/graph/useGraphNodeManager.test.ts
📚 Learning: 2025-11-24T19:47:56.371Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/lib/litegraph/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:56.371Z
Learning: Applies to src/lib/litegraph/**/*.{test,spec}.{js,ts,jsx,tsx} : When adding features, always write vitest unit tests using cursor rules in @.cursor

Applied to files:

  • tests-ui/tests/composables/graph/useGraphNodeManager.test.ts
📚 Learning: 2026-01-10T00:24:17.695Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-10T00:24:17.695Z
Learning: Applies to +(tests-ui|src)/**/*.test.ts : Write tests for all changes, especially bug fixes to catch future regressions

Applied to files:

  • tests-ui/tests/composables/graph/useGraphNodeManager.test.ts
📚 Learning: 2026-01-09T02:07:54.558Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7898
File: src/composables/usePaste.test.ts:248-248
Timestamp: 2026-01-09T02:07:54.558Z
Learning: In test files (e.g., any .test.ts or .test.tsx under src/...), when you create mock objects that partially implement an interface (such as LGraphNode), prefer casting with as Partial<InterfaceType> as InterfaceType rather than as any or as unknown as InterfaceType. This makes the incomplete implementation explicit while preserving type safety, improving readability and maintainability of tests.

Applied to files:

  • tests-ui/tests/composables/graph/useGraphNodeManager.test.ts
📚 Learning: 2026-01-10T00:24:17.695Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-10T00:24:17.695Z
Learning: Applies to +(tests-ui|src)/**/*.test.ts : Leverage Vitest's utilities for mocking where possible

Applied to files:

  • tests-ui/tests/composables/graph/useGraphNodeManager.test.ts
📚 Learning: 2026-01-10T00:24:17.695Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-10T00:24:17.695Z
Learning: Applies to +(tests-ui|src)/**/*.test.ts : Aim for behavioral coverage of critical and new features in unit tests

Applied to files:

  • tests-ui/tests/composables/graph/useGraphNodeManager.test.ts
📚 Learning: 2026-01-10T00:24:17.695Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-10T00:24:17.695Z
Learning: Applies to +(tests-ui|src)/**/*.test.ts : Keep module mocks contained - do not use global mutable state within test files; use `vi.hoisted()` if necessary

Applied to files:

  • tests-ui/tests/composables/graph/useGraphNodeManager.test.ts
📚 Learning: 2026-01-10T00:24:17.695Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-10T00:24:17.695Z
Learning: Applies to +(tests-ui|src|browser_tests)/**/*.+(test.ts|spec.ts) : Do not write change detector tests - avoid tests that only assert default values

Applied to files:

  • tests-ui/tests/composables/graph/useGraphNodeManager.test.ts
📚 Learning: 2025-12-04T21:43:49.363Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7137
File: src/components/rightSidePanel/parameters/TabParameters.vue:10-0
Timestamp: 2025-12-04T21:43:49.363Z
Learning: Vue 3.5+ supports reactive props destructure in <script setup>. Destructuring props directly (e.g., `const { nodes } = defineProps<{ nodes: LGraphNode[] }>()`) maintains reactivity through compiler transformation. This is the recommended modern approach and does not require using `props.x` or `toRef`/`toRefs`.

Applied to files:

  • tests-ui/tests/composables/graph/useGraphNodeManager.test.ts
📚 Learning: 2025-11-24T19:47:02.860Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-24T19:47:02.860Z
Learning: Applies to src/**/*.vue : Utilize ref and reactive for reactive state

Applied to files:

  • tests-ui/tests/composables/graph/useGraphNodeManager.test.ts
📚 Learning: 2025-12-11T03:55:57.926Z
Learnt from: simula-r
Repo: Comfy-Org/ComfyUI_frontend PR: 7252
File: src/renderer/extensions/vueNodes/components/ImagePreview.vue:151-158
Timestamp: 2025-12-11T03:55:57.926Z
Learning: In src/renderer/extensions/vueNodes/components/ImagePreview.vue and LGraphNode.vue, keyboard navigation for image galleries should respond to node-level focus (via keyEvent injection from LGraphNode), not require focus within the image preview wrapper itself. This allows users to navigate the gallery with arrow keys immediately when the node is focused/selected.

Applied to files:

  • tests-ui/tests/composables/graph/useGraphNodeManager.test.ts
📚 Learning: 2025-12-10T03:09:13.807Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7303
File: src/components/topbar/CurrentUserPopover.test.ts:199-205
Timestamp: 2025-12-10T03:09:13.807Z
Learning: In test files, prefer selecting or asserting on accessible properties (text content, aria-label, role, accessible name) over data-testid attributes. This ensures tests validate actual user-facing behavior and accessibility, reducing reliance on implementation details like test IDs.

Applied to files:

  • tests-ui/tests/composables/graph/useGraphNodeManager.test.ts
📚 Learning: 2025-12-30T01:31:04.927Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7797
File: tests-ui/tests/lib/litegraph/src/widgets/ComboWidget.test.ts:648-648
Timestamp: 2025-12-30T01:31:04.927Z
Learning: In Vitest v4, when mocking functions that may be called as constructors (using new), the mock implementation must use function() or class syntax rather than an arrow function. Arrow mocks can cause '<anonymous> is not a constructor' errors. This is a breaking change from Vitest v3 where mocks could use an arrow function. Apply this guideline to test files that mock constructor-like calls (e.g., in tests under tests-ui, such as ComboWidget.test.ts) and ensure mock implementations are defined with function() { ... } or class { ... } to preserve constructor behavior.

Applied to files:

  • tests-ui/tests/composables/graph/useGraphNodeManager.test.ts
📚 Learning: 2026-01-08T02:40:15.482Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7894
File: src/renderer/extensions/vueNodes/widgets/components/WidgetToggleSwitch.test.ts:11-14
Timestamp: 2026-01-08T02:40:15.482Z
Learning: In TypeScript test files (e.g., any test under src), avoid duplicating interface/type definitions. Import real type definitions from the component modules under test and reference them directly, so there is a single source of truth and to prevent type drift. This improves maintainability and consistency across tests.

Applied to files:

  • tests-ui/tests/composables/graph/useGraphNodeManager.test.ts
📚 Learning: 2025-12-22T21:36:38.905Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7649
File: tests-ui/tests/platform/cloud/subscription/components/SubscriptionPanel.test.ts:189-194
Timestamp: 2025-12-22T21:36:38.905Z
Learning: In tests-ui feature tests, do not stub primitive UI components or customized primitives (e.g., Button). Instead, import and register the real components in the test setup so tests reflect production behavior and component API usage. This improves test reliability and fidelity. If a test must avoid rendering the full component, justify the approach and consider higher-level mocks or integration tests rather than replacing the primitive itself.

Applied to files:

  • tests-ui/tests/composables/graph/useGraphNodeManager.test.ts
🧬 Code graph analysis (2)
src/composables/graph/useGraphNodeManager.ts (3)
src/lib/litegraph/src/widgets/BaseWidget.ts (2)
  • value (100-102)
  • value (104-106)
src/scripts/domWidget.ts (2)
  • value (142-144)
  • value (146-149)
src/scripts/app.ts (1)
  • app (1772-1772)
tests-ui/tests/composables/graph/useGraphNodeManager.test.ts (1)
src/composables/graph/useGraphNodeManager.ts (1)
  • useGraphNodeManager (221-599)
🔇 Additional comments (7)
src/composables/graph/useGraphNodeManager.ts (6)

37-37: LGTM - Import of app singleton for canvas access.

This correctly imports the app singleton which provides access to app.canvas needed for the litegraph callback signature.


51-51: LGTM - New optional properties for layout capabilities.

Adding hasLayoutSize and resizable to the safe data interfaces properly exposes layout-related node capabilities to Vue components.

Also applies to: 78-78


180-182: Verify performance impact of triggering redraw on all widgets.

Calling triggerDraw?.() on every widget of the node on each callback invocation could be expensive for nodes with many widgets. Consider whether this should be throttled or if only dependent widgets need redrawing.


192-192: LGTM - Correctly detects layout sizing capability.

Using typeof widget.computeLayoutSize === 'function' is a proper runtime check for this capability.


248-251: Good fix for reactivity washing.

Mutating existing widget objects in place instead of rebuilding the array preserves Vue reactivity correctly. This aligns with the backported fix mentioned in the PR ("Fix reactivity washing in refreshNodeSlots").


322-322: LGTM - Exposes resizable state to Vue.

Correctly propagates the node's resizable property to the Vue-safe data structure.

tests-ui/tests/composables/graph/useGraphNodeManager.test.ts (1)

26-48: Good test coverage for reactivity after slot changes.

These tests validate the critical behavior that widget reactivity is preserved after connection events. This aligns with the backported fix "Fix reactivity washing in refreshNodeSlots."

Comment on lines 174 to 183
const callback = (v: unknown) => {
const value = normalizeWidgetValue(v)
widget.value = value ?? undefined
widget.callback?.(value)
// Match litegraph callback signature: (value, canvas, node, pos, event)
// Some extensions (e.g., Impact Pack) expect node as the 3rd parameter
widget.callback?.(value, app.canvas, node)
// Trigger redraw for all legacy widgets on this node (e.g., mask preview)
// This ensures widgets that depend on other widget values get updated
node.widgets?.forEach((w) => w.triggerDraw?.())
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# First, let's examine the DOMWidget's value setter implementation
cat -n src/scripts/domWidget.ts | sed -n '140,155p'

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 488


🏁 Script executed:

# Check the specific code in useGraphNodeManager.ts around lines 174-183
cat -n src/composables/graph/useGraphNodeManager.ts | sed -n '170,185p'

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 920


🏁 Script executed:

# Search for isDOMWidget usage in the file
rg -n 'isDOMWidget' src/composables/graph/useGraphNodeManager.ts

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 195


🏁 Script executed:

# Check for any callback invocation patterns in domWidget.ts
rg -n 'callback' src/scripts/domWidget.ts -B2 -A2

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 849


Double callback invocation for DOM widgets must be prevented.

For DOM widgets, callbacks are invoked twice in this flow:

  1. Line 176 (widget.value = value ?? undefined) triggers the DOMWidget's setter, which calls this.callback?.(this.value) with 1 argument
  2. Line 179 explicitly calls widget.callback?.(value, app.canvas, node) with 3 arguments

Non-DOM widgets (using BaseWidget) are not affected since their setters don't invoke callbacks. Add an isDOMWidget() check to skip the explicit callback invocation for DOM widgets, or adjust the flow to avoid triggering the setter for them.

🤖 Prompt for AI Agents
In @src/composables/graph/useGraphNodeManager.ts around lines 174 - 183, The
callback wrapper in useGraphNodeManager.ts calls widget.value = ... which for
DOM widgets triggers their setter and invokes widget.callback, then
unconditionally calls widget.callback again; prevent double invocation by
detecting DOM widgets (e.g., using isDOMWidget(widget) or DOMWidget instanceof
check) and only calling widget.callback(value, app.canvas, node) for non-DOM
widgets (or alternatively assign the internal value field for DOM widgets
instead of using the setter); keep the normalizeWidgetValue call and ensure
node.widgets?.forEach((w) => w.triggerDraw?.()) still runs.

import { LGraph, LGraphNode } from '@/lib/litegraph/src/litegraph'
import { NodeSlotType } from '@/lib/litegraph/src/types/globalEnums'

setActivePinia(createTestingPinia())
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Move Pinia setup into beforeEach for test isolation.

Module-level setActivePinia shares state across all tests, which can lead to flaky tests or pollution between test cases. Per coding guidelines: "Keep module mocks contained - do not use global mutable state within test files."

♻️ Suggested refactor
-setActivePinia(createTestingPinia())
+import { beforeEach } from 'vitest'
+
+beforeEach(() => {
+  setActivePinia(createTestingPinia())
+})
🤖 Prompt for AI Agents
In @tests-ui/tests/composables/graph/useGraphNodeManager.test.ts at line 10, The
test currently calls setActivePinia(createTestingPinia()) at module scope which
shares Pinia state across tests; move this call into a beforeEach hook so each
test gets a fresh store instance (use beforeEach(() =>
setActivePinia(createTestingPinia()))), and optionally clear/reset after each
test (afterEach(() => setActivePinia(undefined) or stub reset) to ensure
isolation; update any imports or setup referencing
setActivePinia/createTestingPinia accordingly so tests use the per-test Pinia
instance.

Comment on lines +38 to +41
graph.trigger('node:slot-links:changed', {
nodeId: '1',
slotType: NodeSlotType.INPUT
})
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Avoid hardcoded nodeId: '1'.

The test assumes the node will have ID '1', but this depends on LGraph's internal ID assignment. Use the actual node's ID for robustness.

♻️ Suggested fix
     graph.trigger('node:slot-links:changed', {
-      nodeId: '1',
+      nodeId: String(node.id),
       slotType: NodeSlotType.INPUT
     })
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
graph.trigger('node:slot-links:changed', {
nodeId: '1',
slotType: NodeSlotType.INPUT
})
graph.trigger('node:slot-links:changed', {
nodeId: String(node.id),
slotType: NodeSlotType.INPUT
})
🤖 Prompt for AI Agents
In @tests-ui/tests/composables/graph/useGraphNodeManager.test.ts around lines 38
- 41, Replace the hardcoded nodeId '1' in the graph.trigger call with the actual
node object's ID to avoid relying on LGraph internals; locate the test where
graph.trigger('node:slot-links:changed', { nodeId: '1', slotType:
NodeSlotType.INPUT }) is used and change the payload to use the node instance
(e.g., node.id or the variable holding the created node's id) so the event uses
the real node id returned when the node was created.

comfy-pr-bot and others added 2 commits January 12, 2026 22:41
…erflowing node width (#8006)

Backport of #7891 to `cloud/1.36`

Automatically created by backport workflow.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8006-backport-cloud-1-36-fix-wrap-image-preview-navigation-dots-when-overflowing-node-width-2e76d73d3650814ead44ebf4a66d4765)
by [Unito](https://www.unito.io)

Co-authored-by: Terry Jia <[email protected]>
Backport of #7837 to `cloud/1.36`

Automatically created by backport workflow.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8008-backport-cloud-1-36-Dynamic-input-fixes-2e76d73d3650811186daca4b7e20b83a)
by [Unito](https://www.unito.io)

Co-authored-by: AustinMroz <[email protected]>
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In @src/core/graph/widgets/dynamicWidgets.ts:
- Around line 344-353: The requestAnimationFrame callback uses a non-null
assertion on node.inputs.at(index) which can be undefined if inputs change
before the RAF runs; replace the assertion with a guard: retrieve const input =
node.inputs.at(index) and if (!input) return early, then call
node.onConnectionsChange only when input is defined (still pass LiteGraph.INPUT,
index, !!input.link, input.link ? node.graph?.links?.[input.link] : undefined,
input). Ensure the guard prevents dereferencing undefined and avoids stale-index
runtime errors.
📜 Review details

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 15955bd and 2da8338.

📒 Files selected for processing (1)
  • src/core/graph/widgets/dynamicWidgets.ts
🧰 Additional context used
📓 Path-based instructions (6)
src/**/*.{vue,ts}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

src/**/*.{vue,ts}: Leverage VueUse functions for performance-enhancing styles
Implement proper error handling
Use vue-i18n in composition API for any string literals. Place new translation entries in src/locales/en/main.json

Files:

  • src/core/graph/widgets/dynamicWidgets.ts
src/**/*.ts

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

src/**/*.ts: Use es-toolkit for utility functions
Use TypeScript for type safety

src/**/*.ts: Derive component types using vue-component-type-helpers (ComponentProps, ComponentSlots) instead of separate type files
Use es-toolkit for utility functions
Minimize the surface area (exported values) of each module and composable
Favor pure functions, especially testable ones

Files:

  • src/core/graph/widgets/dynamicWidgets.ts
src/**/*.{ts,tsx,vue}

📄 CodeRabbit inference engine (src/CLAUDE.md)

src/**/*.{ts,tsx,vue}: Sanitize HTML with DOMPurify to prevent XSS attacks
Avoid using @ts-expect-error; use proper TypeScript types instead
Use es-toolkit for utility functions instead of other utility libraries
Implement proper TypeScript types throughout the codebase

src/**/*.{ts,tsx,vue}: Use separate import type statements instead of inline type in mixed imports
Apply Prettier formatting with 2-space indentation, single quotes, no trailing semicolons, 80-character width
Sort and group imports by plugin, run pnpm format before committing
Never use any type - use proper TypeScript types
Never use as any type assertions - fix the underlying type issue
Write code that is expressive and self-documenting - avoid unnecessary comments
Do not add or retain redundant comments - clean as you go
Avoid mutable state - prefer immutability and assignment at point of declaration
Watch out for Code Smells and refactor to avoid them

Files:

  • src/core/graph/widgets/dynamicWidgets.ts
src/**/*.{vue,ts,tsx}

📄 CodeRabbit inference engine (src/CLAUDE.md)

Follow Vue 3 composition API style guide

Files:

  • src/core/graph/widgets/dynamicWidgets.ts
src/**/*.{ts,vue}

📄 CodeRabbit inference engine (AGENTS.md)

src/**/*.{ts,vue}: Use ref for reactive state, computed() for derived values, and watch/watchEffect for side effects in Composition API
Avoid using ref with watch if a computed would suffice - minimize refs and derived state
Use provide/inject for dependency injection only when simpler alternatives (Store or shared composable) won't work
Leverage VueUse functions for performance-enhancing composables
Use VueUse function for useI18n in composition API for string literals

Files:

  • src/core/graph/widgets/dynamicWidgets.ts
src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

src/**/*.{ts,tsx}: Keep functions short and functional
Minimize nesting (if statements, for loops, etc.)
Use function declarations instead of function expressions when possible

Files:

  • src/core/graph/widgets/dynamicWidgets.ts
🧠 Learnings (7)
📓 Common learnings
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7897
File: src/stores/assetDownloadStore.ts:117-117
Timestamp: 2026-01-08T21:44:05.435Z
Learning: In src/stores/assetDownloadStore.ts, the api.addEventListener('asset_download', handleAssetDownload) call at module level is intentional and should persist for the application lifetime since it's a singleton Pinia store managing global WebSocket events.
📚 Learning: 2025-12-09T03:39:54.501Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7169
File: src/platform/remote/comfyui/jobs/jobTypes.ts:1-107
Timestamp: 2025-12-09T03:39:54.501Z
Learning: In the ComfyUI_frontend project, Zod is on v3.x. Do not suggest Zod v4 standalone validators (z.uuid, z.ulid, z.cuid2, z.nanoid) until an upgrade to Zod 4 is performed. When reviewing TypeScript files (e.g., src/platform/remote/comfyui/jobs/jobTypes.ts) validate against Zod 3 capabilities and avoid introducing v4-specific features; flag any proposal to upgrade or incorporate v4-only validators and propose staying with compatible 3.x patterns.

Applied to files:

  • src/core/graph/widgets/dynamicWidgets.ts
📚 Learning: 2025-12-13T11:03:11.264Z
Learnt from: christian-byrne
Repo: Comfy-Org/ComfyUI_frontend PR: 7416
File: src/stores/imagePreviewStore.ts:5-7
Timestamp: 2025-12-13T11:03:11.264Z
Learning: In the ComfyUI_frontend repository, lint rules require keeping 'import type' statements separate from non-type imports, even if importing from the same module. Do not suggest consolidating them into a single import statement. Ensure type imports remain on their own line (import type { ... } from 'module') and regular imports stay on separate lines.

Applied to files:

  • src/core/graph/widgets/dynamicWidgets.ts
📚 Learning: 2025-12-17T00:40:09.635Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7537
File: src/components/ui/button/Button.stories.ts:45-55
Timestamp: 2025-12-17T00:40:09.635Z
Learning: Prefer pure function declarations over function expressions (e.g., use function foo() { ... } instead of const foo = () => { ... }) for pure functions in the repository. Function declarations are more functional-leaning, offer better hoisting clarity, and can improve readability and tooling consistency. Apply this guideline across TypeScript files in Comfy-Org/ComfyUI_frontend, including story and UI component code, except where a function expression is semantically required (e.g., callbacks, higher-order functions with closures).

Applied to files:

  • src/core/graph/widgets/dynamicWidgets.ts
📚 Learning: 2025-12-30T22:22:33.836Z
Learnt from: kaili-yang
Repo: Comfy-Org/ComfyUI_frontend PR: 7805
File: src/composables/useCoreCommands.ts:439-439
Timestamp: 2025-12-30T22:22:33.836Z
Learning: When accessing reactive properties from Pinia stores in TypeScript files, avoid using .value on direct property access (e.g., useStore().isOverlayExpanded). Pinia auto-wraps refs when accessed directly, returning the primitive value. The .value accessor is only needed when destructuring store properties or when using storeToRefs().

Applied to files:

  • src/core/graph/widgets/dynamicWidgets.ts
📚 Learning: 2025-12-11T12:25:15.470Z
Learnt from: christian-byrne
Repo: Comfy-Org/ComfyUI_frontend PR: 7358
File: src/components/dialog/content/signin/SignUpForm.vue:45-54
Timestamp: 2025-12-11T12:25:15.470Z
Learning: This repository uses CI automation to format code (pnpm format). Do not include manual formatting suggestions in code reviews for Comfy-Org/ComfyUI_frontend. If formatting issues are detected, rely on the CI formatter or re-run pnpm format. Focus reviews on correctness, readability, performance, accessibility, and maintainability rather than style formatting.

Applied to files:

  • src/core/graph/widgets/dynamicWidgets.ts
📚 Learning: 2026-01-12T17:39:27.738Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7906
File: src/components/sidebar/tabs/AssetsSidebarTab.vue:545-552
Timestamp: 2026-01-12T17:39:27.738Z
Learning: In Vue/TypeScript files (src/**/*.{ts,tsx,vue}), prefer if/else statements over ternary operators when performing side effects or actions (e.g., mutating state, calling methods with side effects). Ternaries should be reserved for computing and returning values.

Applied to files:

  • src/core/graph/widgets/dynamicWidgets.ts
🧬 Code graph analysis (1)
src/core/graph/widgets/dynamicWidgets.ts (3)
src/lib/litegraph/src/LGraphNode.ts (1)
  • widget (3968-3971)
src/extensions/core/widgetInputs.ts (1)
  • input (355-368)
src/lib/litegraph/src/litegraph.ts (1)
  • LiteGraph (17-17)
🔇 Additional comments (2)
src/core/graph/widgets/dynamicWidgets.ts (2)

96-97: LGTM!

Good improvement to invoke onRemove?.() on each removed widget, ensuring proper cleanup callbacks are triggered during dynamic widget updates. The use of es-toolkit's remove function is appropriate per coding guidelines.


491-493: LGTM!

Consistent with the cleanup pattern added to dynamicComboWidget. Properly invokes onRemove?.() on each removed widget to ensure lifecycle cleanup is performed.

Comment on lines +344 to +353
requestAnimationFrame(() => {
const input = node.inputs.at(index)!
node.onConnectionsChange?.(
LiteGraph.INPUT,
index,
!!input.link,
input.link ? node.graph?.links?.[input.link] : undefined,
input
)
})
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Guard against undefined input in the RAF callback.

The index captured on line 343 may become stale if inputs are modified before the requestAnimationFrame callback executes. The non-null assertion ! on line 345 will cause a runtime error if node.inputs.at(index) returns undefined.

🛡️ Suggested fix: Add early return guard
  requestAnimationFrame(() => {
-   const input = node.inputs.at(index)!
+   const input = node.inputs.at(index)
+   if (!input) return
    node.onConnectionsChange?.(
      LiteGraph.INPUT,
      index,
      !!input.link,
      input.link ? node.graph?.links?.[input.link] : undefined,
      input
    )
  })
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
requestAnimationFrame(() => {
const input = node.inputs.at(index)!
node.onConnectionsChange?.(
LiteGraph.INPUT,
index,
!!input.link,
input.link ? node.graph?.links?.[input.link] : undefined,
input
)
})
requestAnimationFrame(() => {
const input = node.inputs.at(index)
if (!input) return
node.onConnectionsChange?.(
LiteGraph.INPUT,
index,
!!input.link,
input.link ? node.graph?.links?.[input.link] : undefined,
input
)
})
🤖 Prompt for AI Agents
In @src/core/graph/widgets/dynamicWidgets.ts around lines 344 - 353, The
requestAnimationFrame callback uses a non-null assertion on
node.inputs.at(index) which can be undefined if inputs change before the RAF
runs; replace the assertion with a guard: retrieve const input =
node.inputs.at(index) and if (!input) return early, then call
node.onConnectionsChange only when input is defined (still pass LiteGraph.INPUT,
index, !!input.link, input.link ? node.graph?.links?.[input.link] : undefined,
input). Ensure the guard prevents dereferencing undefined and avoids stale-index
runtime errors.

Backport of #7990 to `cloud/1.36`

Automatically created by backport workflow.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8004-backport-cloud-1-36-feat-local-legacy-settings-dialog-fix-2e76d73d3650814398aff135890ee1cb)
by [Unito](https://www.unito.io)

Co-authored-by: Simula_r <[email protected]>
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

🤖 Fix all issues with AI agents
In @src/components/dialog/content/TopUpCreditsDialogContent.vue:
- Around line 138-145: The external anchor rendering the pricing link in
TopUpCreditsDialogContent.vue (the <a> that uses :href="pricingUrl" and displays
$t('credits.topUp.viewPricing')) is missing rel="noopener noreferrer"; update
that anchor to include rel="noopener noreferrer" (alongside target="_blank") to
prevent tab-napping/security issues.
- Around line 118-123: The external anchor in TopUpCreditsDialogContent.vue that
uses target="_blank" (the <a> rendering {{ $t('credits.topUp.contactUs') }})
must include rel="noopener noreferrer" to prevent reverse tabnapping; update
that anchor tag to add rel="noopener noreferrer" alongside the existing href,
target, and class attributes so the new page cannot access window.opener.
- Around line 14-19: Replace the plain <button> in TopUpCreditsDialogContent.vue
with the shared Button component (e.g., Button or BaseButton) and pass the same
click handler (handleClose) directly as @click; ensure the icon element stays as
the button child and add an aria-label like aria-label="Close" to make the
icon-only control accessible; maintain existing styling via Button props
(variant/size/class) or by mapping the current classes to the Button so visual
behavior remains unchanged.
- Around line 212-214: The formatNumber function currently hardcodes 'en-US';
instead use the i18n locale from useI18n(): destructure locale (e.g. const {
locale } = useI18n()) and update formatNumber to call
num.toLocaleString(locale.value || undefined) (or use new
Intl.NumberFormat(locale.value).format(num)); keep the function name
formatNumber and ensure it reads locale.value so formatting follows the user's
locale.

In
@src/platform/cloud/subscription/components/SubscriptionRequiredDialogContent.vue:
- Around line 158-159: The computed showCustomPricingTable wraps
isSubscriptionEnabled() but its inputs are static at app init, so replace the
computed with a plain constant: change showCustomPricingTable = computed(() =>
isSubscriptionEnabled()) to a direct const showCustomPricingTable =
isSubscriptionEnabled(); then update any template or watcher usage that
currently accesses showCustomPricingTable.value to use showCustomPricingTable
instead; keep computed only if you intentionally want the current computed
pattern for consistency.

In @src/platform/cloud/subscription/composables/useSubscription.ts:
- Around line 124-129: Replace the arrow function expression
isSubscriptionEnabled with a function declaration to follow the coding guideline
for pure functions; keep the same boolean return type and logic that returns
Boolean(isCloud && window.__CONFIG__?.subscription_required), referencing the
same identifiers (isSubscriptionEnabled, isCloud,
window.__CONFIG__?.subscription_required), and ensure no other behavior or
closure semantics change when performing the refactor.
📜 Review details

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2da8338 and 99f5b36.

📒 Files selected for processing (3)
  • src/components/dialog/content/TopUpCreditsDialogContent.vue
  • src/platform/cloud/subscription/components/SubscriptionRequiredDialogContent.vue
  • src/platform/cloud/subscription/composables/useSubscription.ts
🧰 Additional context used
📓 Path-based instructions (13)
src/**/*.vue

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

src/**/*.vue: Use the Vue 3 Composition API instead of the Options API when writing Vue components (exception: when overriding or extending PrimeVue components for compatibility)
Use setup() function for component logic
Utilize ref and reactive for reactive state
Implement computed properties with computed()
Use watch and watchEffect for side effects
Implement lifecycle hooks with onMounted, onUpdated, etc.
Utilize provide/inject for dependency injection
Use vue 3.5 style of default prop declaration
Use Tailwind CSS for styling
Implement proper props and emits definitions
Utilize Vue 3's Teleport component when needed
Use Suspense for async components
Follow Vue 3 style guide and naming conventions

src/**/*.vue: Use Vue 3 Single File Components (SFCs) with Composition API only
Use <script setup lang="ts"> for component logic in Vue SFCs
Avoid <style> blocks in Vue components - use Tailwind 4 styling instead
Use vue-i18n for all string literals in Vue components - place translation entries in src/locales/en/main.json
Use Tailwind utility classes instead of dark: variant - use semantic values from style.css theme (e.g., bg-node-component-surface)
Use cn() utility from @/utils/tailwindUtil for merging Tailwind class names instead of :class="[]" or hardcoding
Never use !important or ! Tailwind prefix - fix interfering classes instead
Use Tailwind fraction utilities instead of arbitrary percentage values (e.g., w-4/5 instead of w-[80%])
Use TypeScript Vue 3.5 style default prop declaration with reactive props destructuring - avoid withDefaults or runtime props
Prefer defineModel over separately defining a prop and emit for v-model bindings
Define slots via template usage, not via defineSlots
Use same-name shorthand for slot prop bindings (e.g., :isExpanded instead of :is-expanded="isExpanded")
Do not import Vue macros unnecessarily
Avoid new usage of PrimeVue components
Use Tailwind's plurals system via i18n instead of hardcoding ...

Files:

  • src/components/dialog/content/TopUpCreditsDialogContent.vue
  • src/platform/cloud/subscription/components/SubscriptionRequiredDialogContent.vue
src/**/*.{vue,ts}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

src/**/*.{vue,ts}: Leverage VueUse functions for performance-enhancing styles
Implement proper error handling
Use vue-i18n in composition API for any string literals. Place new translation entries in src/locales/en/main.json

Files:

  • src/components/dialog/content/TopUpCreditsDialogContent.vue
  • src/platform/cloud/subscription/composables/useSubscription.ts
  • src/platform/cloud/subscription/components/SubscriptionRequiredDialogContent.vue
src/**/*.{ts,tsx,vue}

📄 CodeRabbit inference engine (src/CLAUDE.md)

src/**/*.{ts,tsx,vue}: Sanitize HTML with DOMPurify to prevent XSS attacks
Avoid using @ts-expect-error; use proper TypeScript types instead
Use es-toolkit for utility functions instead of other utility libraries
Implement proper TypeScript types throughout the codebase

src/**/*.{ts,tsx,vue}: Use separate import type statements instead of inline type in mixed imports
Apply Prettier formatting with 2-space indentation, single quotes, no trailing semicolons, 80-character width
Sort and group imports by plugin, run pnpm format before committing
Never use any type - use proper TypeScript types
Never use as any type assertions - fix the underlying type issue
Write code that is expressive and self-documenting - avoid unnecessary comments
Do not add or retain redundant comments - clean as you go
Avoid mutable state - prefer immutability and assignment at point of declaration
Watch out for Code Smells and refactor to avoid them

Files:

  • src/components/dialog/content/TopUpCreditsDialogContent.vue
  • src/platform/cloud/subscription/composables/useSubscription.ts
  • src/platform/cloud/subscription/components/SubscriptionRequiredDialogContent.vue
src/**/{composables,components}/**/*.{ts,tsx,vue}

📄 CodeRabbit inference engine (src/CLAUDE.md)

Clean up subscriptions in state management to prevent memory leaks

Files:

  • src/components/dialog/content/TopUpCreditsDialogContent.vue
  • src/platform/cloud/subscription/composables/useSubscription.ts
  • src/platform/cloud/subscription/components/SubscriptionRequiredDialogContent.vue
src/**/*.{vue,ts,tsx}

📄 CodeRabbit inference engine (src/CLAUDE.md)

Follow Vue 3 composition API style guide

Files:

  • src/components/dialog/content/TopUpCreditsDialogContent.vue
  • src/platform/cloud/subscription/composables/useSubscription.ts
  • src/platform/cloud/subscription/components/SubscriptionRequiredDialogContent.vue
src/**/{components,composables}/**/*.{ts,tsx,vue}

📄 CodeRabbit inference engine (src/CLAUDE.md)

Use vue-i18n for ALL user-facing strings by adding them to src/locales/en/main.json

Files:

  • src/components/dialog/content/TopUpCreditsDialogContent.vue
  • src/platform/cloud/subscription/composables/useSubscription.ts
  • src/platform/cloud/subscription/components/SubscriptionRequiredDialogContent.vue
src/components/**/*.vue

📄 CodeRabbit inference engine (src/components/CLAUDE.md)

src/components/**/*.vue: Use setup() function in Vue 3 Composition API
Destructure props using Vue 3.5 style in Vue components
Use ref/reactive for state management in Vue 3 Composition API
Implement computed() for derived state in Vue 3 Composition API
Use provide/inject for dependency injection in Vue components
Prefer emit/@event-name for state changes over other communication patterns
Use defineExpose only for imperative operations (such as form.validate(), modal.open())
Replace PrimeVue Dropdown component with Select
Replace PrimeVue OverlayPanel component with Popover
Replace PrimeVue Calendar component with DatePicker
Replace PrimeVue InputSwitch component with ToggleSwitch
Replace PrimeVue Sidebar component with Drawer
Replace PrimeVue Chips component with AutoComplete with multiple enabled
Replace PrimeVue TabMenu component with Tabs without panels
Replace PrimeVue Steps component with Stepper without panels
Replace PrimeVue InlineMessage component with Message
Extract complex conditionals to computed properties
Implement cleanup for async operations in Vue components
Use lifecycle hooks: onMounted, onUpdated in Vue 3 Composition API
Use Teleport/Suspense when needed for component rendering
Define proper props and emits definitions in Vue components

Name Vue components in PascalCase (e.g., MenuHamburger.vue)

Files:

  • src/components/dialog/content/TopUpCreditsDialogContent.vue
src/components/**/*.{vue,css}

📄 CodeRabbit inference engine (src/components/CLAUDE.md)

src/components/**/*.{vue,css}: Use Tailwind CSS only for styling (no custom CSS)
Use the correct tokens from style.css in the design system package

Files:

  • src/components/dialog/content/TopUpCreditsDialogContent.vue
src/components/**/*.{vue,ts,js}

📄 CodeRabbit inference engine (src/components/CLAUDE.md)

src/components/**/*.{vue,ts,js}: Use existing VueUse composables (such as useElementHover) instead of manually managing event listeners
Use useIntersectionObserver for visibility detection instead of custom scroll handlers
Use vue-i18n for ALL UI strings

Files:

  • src/components/dialog/content/TopUpCreditsDialogContent.vue
src/**/*.{ts,vue}

📄 CodeRabbit inference engine (AGENTS.md)

src/**/*.{ts,vue}: Use ref for reactive state, computed() for derived values, and watch/watchEffect for side effects in Composition API
Avoid using ref with watch if a computed would suffice - minimize refs and derived state
Use provide/inject for dependency injection only when simpler alternatives (Store or shared composable) won't work
Leverage VueUse functions for performance-enhancing composables
Use VueUse function for useI18n in composition API for string literals

Files:

  • src/components/dialog/content/TopUpCreditsDialogContent.vue
  • src/platform/cloud/subscription/composables/useSubscription.ts
  • src/platform/cloud/subscription/components/SubscriptionRequiredDialogContent.vue
src/**/*.ts

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

src/**/*.ts: Use es-toolkit for utility functions
Use TypeScript for type safety

src/**/*.ts: Derive component types using vue-component-type-helpers (ComponentProps, ComponentSlots) instead of separate type files
Use es-toolkit for utility functions
Minimize the surface area (exported values) of each module and composable
Favor pure functions, especially testable ones

Files:

  • src/platform/cloud/subscription/composables/useSubscription.ts
src/**/{services,composables}/**/*.{ts,tsx}

📄 CodeRabbit inference engine (src/CLAUDE.md)

src/**/{services,composables}/**/*.{ts,tsx}: Use api.apiURL() for backend endpoints instead of constructing URLs directly
Use api.fileURL() for static file access instead of constructing URLs directly

Files:

  • src/platform/cloud/subscription/composables/useSubscription.ts
src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

src/**/*.{ts,tsx}: Keep functions short and functional
Minimize nesting (if statements, for loops, etc.)
Use function declarations instead of function expressions when possible

Files:

  • src/platform/cloud/subscription/composables/useSubscription.ts
🧠 Learnings (19)
📓 Common learnings
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7897
File: src/stores/assetDownloadStore.ts:117-117
Timestamp: 2026-01-08T21:44:05.435Z
Learning: In src/stores/assetDownloadStore.ts, the api.addEventListener('asset_download', handleAssetDownload) call at module level is intentional and should persist for the application lifetime since it's a singleton Pinia store managing global WebSocket events.
📚 Learning: 2025-12-05T20:35:28.330Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7137
File: src/locales/en/main.json:2257-2272
Timestamp: 2025-12-05T20:35:28.330Z
Learning: In the Comfy-Org/ComfyUI_frontend repository, vue-i18n pluralization uses a two-parameter pattern: `t('key', { count: value })` where the `count` property in the interpolation object automatically triggers plural selection from pipe-separated locale strings (e.g., "no items | 1 item | {count} items"). This is the correct pattern used throughout the codebase, as validated by Playwright tests.

Applied to files:

  • src/components/dialog/content/TopUpCreditsDialogContent.vue
📚 Learning: 2025-12-09T03:49:52.828Z
Learnt from: christian-byrne
Repo: Comfy-Org/ComfyUI_frontend PR: 6300
File: src/platform/updates/components/WhatsNewPopup.vue:5-13
Timestamp: 2025-12-09T03:49:52.828Z
Learning: In Vue files across the ComfyUI_frontend repo, when a button is needed, prefer the repo's common button components from src/components/button/ (IconButton.vue, TextButton.vue, IconTextButton.vue) over plain HTML <button> elements. These components wrap PrimeVue with the project’s design system styling. Use only the common button components for consistency and theming, and import them from src/components/button/ as needed.

Applied to files:

  • src/components/dialog/content/TopUpCreditsDialogContent.vue
  • src/platform/cloud/subscription/components/SubscriptionRequiredDialogContent.vue
📚 Learning: 2025-12-09T21:40:12.361Z
Learnt from: benceruleanlu
Repo: Comfy-Org/ComfyUI_frontend PR: 7297
File: src/components/actionbar/ComfyActionbar.vue:33-43
Timestamp: 2025-12-09T21:40:12.361Z
Learning: In Vue single-file components, allow inline Tailwind CSS class strings for static classes and avoid extracting them into computed properties solely for readability. Prefer keeping static class names inline for simplicity and performance. For dynamic or conditional classes, use Vue bindings (e.g., :class) to compose classes.

Applies to all Vue files in the repository (e.g., src/**/*.vue) where Tailwind utilities are used for static styling.

Applied to files:

  • src/components/dialog/content/TopUpCreditsDialogContent.vue
  • src/platform/cloud/subscription/components/SubscriptionRequiredDialogContent.vue
📚 Learning: 2025-12-16T22:26:49.463Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7537
File: src/components/ui/button/Button.vue:17-17
Timestamp: 2025-12-16T22:26:49.463Z
Learning: In Vue 3.5+ with <script setup>, when using defineProps<Props>() with partial destructuring (e.g., const { as = 'button', class: customClass = '' } = defineProps<Props>() ), props that are not destructured (e.g., variant, size) stay accessible by name in the template scope. This pattern is valid: you can destructure only a subset of props for convenience while referencing the remaining props directly in template expressions. Apply this guideline to Vue components across the codebase (all .vue files).

Applied to files:

  • src/components/dialog/content/TopUpCreditsDialogContent.vue
  • src/platform/cloud/subscription/components/SubscriptionRequiredDialogContent.vue
📚 Learning: 2025-12-22T21:36:08.369Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7649
File: src/platform/cloud/subscription/components/PricingTable.vue:185-201
Timestamp: 2025-12-22T21:36:08.369Z
Learning: In Vue components, avoid creating single-use variants for common UI components (e.g., Button and other shared components). Aim for reusable variants that cover multiple use cases. It’s acceptable to temporarily mix variant props with inline Tailwind classes when a styling need is unique to one place, but plan and consolidate into shared, reusable variants as patterns emerge across the codebase.

Applied to files:

  • src/components/dialog/content/TopUpCreditsDialogContent.vue
  • src/platform/cloud/subscription/components/SubscriptionRequiredDialogContent.vue
📚 Learning: 2026-01-08T02:26:18.357Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7893
File: src/components/button/IconGroup.vue:5-6
Timestamp: 2026-01-08T02:26:18.357Z
Learning: In components that use the cn utility from '@/utils/tailwindUtil' with tailwind-merge, rely on the behavior that conflicting Tailwind classes are resolved by keeping the last one. For example, cn('base-classes bg-default', propClass) will have any conflicting background class from propClass override bg-default. This additive pattern is intentional and aligns with the shadcn-ui convention; ensure you document or review expectations accordingly in Vue components.

Applied to files:

  • src/components/dialog/content/TopUpCreditsDialogContent.vue
  • src/platform/cloud/subscription/components/SubscriptionRequiredDialogContent.vue
📚 Learning: 2025-12-11T12:25:15.470Z
Learnt from: christian-byrne
Repo: Comfy-Org/ComfyUI_frontend PR: 7358
File: src/components/dialog/content/signin/SignUpForm.vue:45-54
Timestamp: 2025-12-11T12:25:15.470Z
Learning: This repository uses CI automation to format code (pnpm format). Do not include manual formatting suggestions in code reviews for Comfy-Org/ComfyUI_frontend. If formatting issues are detected, rely on the CI formatter or re-run pnpm format. Focus reviews on correctness, readability, performance, accessibility, and maintainability rather than style formatting.

Applied to files:

  • src/components/dialog/content/TopUpCreditsDialogContent.vue
  • src/platform/cloud/subscription/composables/useSubscription.ts
  • src/platform/cloud/subscription/components/SubscriptionRequiredDialogContent.vue
📚 Learning: 2025-12-18T02:07:38.870Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7598
File: src/components/sidebar/tabs/AssetsSidebarTab.vue:131-131
Timestamp: 2025-12-18T02:07:38.870Z
Learning: Tailwind CSS v4 safe utilities (e.g., items-center-safe, justify-*-safe, place-*-safe) are allowed in Vue components under src/ and in story files. Do not flag these specific safe variants as invalid when reviewing code in src/**/*.vue or related stories.

Applied to files:

  • src/components/dialog/content/TopUpCreditsDialogContent.vue
  • src/platform/cloud/subscription/components/SubscriptionRequiredDialogContent.vue
📚 Learning: 2025-12-18T21:15:46.862Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7603
File: src/components/queue/QueueOverlayHeader.vue:49-59
Timestamp: 2025-12-18T21:15:46.862Z
Learning: In the ComfyUI_frontend repository, for Vue components, do not add aria-label to buttons that have visible text content (e.g., buttons containing <span> text). The visible text provides the accessible name. Use aria-label only for elements without visible labels (e.g., icon-only buttons). If a button has no visible label, provide a clear aria-label or associate with an aria-labelledby describing its action.

Applied to files:

  • src/components/dialog/content/TopUpCreditsDialogContent.vue
  • src/platform/cloud/subscription/components/SubscriptionRequiredDialogContent.vue
📚 Learning: 2025-12-21T01:06:02.786Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7649
File: src/components/graph/selectionToolbox/ColorPickerButton.vue:15-18
Timestamp: 2025-12-21T01:06:02.786Z
Learning: In Comfy-Org/ComfyUI_frontend, in Vue component files, when a filled icon is required (e.g., 'pi pi-circle-fill'), you may mix PrimeIcons with Lucide icons since Lucide lacks filled variants. This mixed usage is acceptable when one icon library does not provide an equivalent filled icon. Apply consistently across Vue components in the src directory where icons are used, and document the rationale when a mixed approach is chosen.

Applied to files:

  • src/components/dialog/content/TopUpCreditsDialogContent.vue
  • src/platform/cloud/subscription/components/SubscriptionRequiredDialogContent.vue
📚 Learning: 2025-12-18T16:03:02.066Z
Learnt from: henrikvilhelmberglund
Repo: Comfy-Org/ComfyUI_frontend PR: 7617
File: src/components/actionbar/ComfyActionbar.vue:301-308
Timestamp: 2025-12-18T16:03:02.066Z
Learning: In the ComfyUI frontend queue system, useQueuePendingTaskCountStore().count indicates the number of tasks in the queue, where count = 1 means a single active/running task and count > 1 means there are pending tasks in addition to the active task. Therefore, in src/components/actionbar/ComfyActionbar.vue, enable the 'Clear Pending Tasks' button only when count > 1 to avoid clearing the currently running task. The active task should be canceled using the 'Cancel current run' button instead. This rule should be enforced via a conditional check on the queue count, with appropriate disabled/aria-disabled states for accessibility, and tests should verify behavior for count = 1 and count > 1.

Applied to files:

  • src/components/dialog/content/TopUpCreditsDialogContent.vue
📚 Learning: 2026-01-12T17:39:27.738Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7906
File: src/components/sidebar/tabs/AssetsSidebarTab.vue:545-552
Timestamp: 2026-01-12T17:39:27.738Z
Learning: In Vue/TypeScript files (src/**/*.{ts,tsx,vue}), prefer if/else statements over ternary operators when performing side effects or actions (e.g., mutating state, calling methods with side effects). Ternaries should be reserved for computing and returning values.

Applied to files:

  • src/components/dialog/content/TopUpCreditsDialogContent.vue
  • src/platform/cloud/subscription/composables/useSubscription.ts
  • src/platform/cloud/subscription/components/SubscriptionRequiredDialogContent.vue
📚 Learning: 2025-11-24T19:47:34.324Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:34.324Z
Learning: Applies to src/**/{composables,components}/**/*.{ts,tsx,vue} : Clean up subscriptions in state management to prevent memory leaks

Applied to files:

  • src/platform/cloud/subscription/composables/useSubscription.ts
  • src/platform/cloud/subscription/components/SubscriptionRequiredDialogContent.vue
📚 Learning: 2025-12-09T03:39:54.501Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7169
File: src/platform/remote/comfyui/jobs/jobTypes.ts:1-107
Timestamp: 2025-12-09T03:39:54.501Z
Learning: In the ComfyUI_frontend project, Zod is on v3.x. Do not suggest Zod v4 standalone validators (z.uuid, z.ulid, z.cuid2, z.nanoid) until an upgrade to Zod 4 is performed. When reviewing TypeScript files (e.g., src/platform/remote/comfyui/jobs/jobTypes.ts) validate against Zod 3 capabilities and avoid introducing v4-specific features; flag any proposal to upgrade or incorporate v4-only validators and propose staying with compatible 3.x patterns.

Applied to files:

  • src/platform/cloud/subscription/composables/useSubscription.ts
📚 Learning: 2025-12-13T11:03:11.264Z
Learnt from: christian-byrne
Repo: Comfy-Org/ComfyUI_frontend PR: 7416
File: src/stores/imagePreviewStore.ts:5-7
Timestamp: 2025-12-13T11:03:11.264Z
Learning: In the ComfyUI_frontend repository, lint rules require keeping 'import type' statements separate from non-type imports, even if importing from the same module. Do not suggest consolidating them into a single import statement. Ensure type imports remain on their own line (import type { ... } from 'module') and regular imports stay on separate lines.

Applied to files:

  • src/platform/cloud/subscription/composables/useSubscription.ts
📚 Learning: 2025-12-17T00:40:09.635Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7537
File: src/components/ui/button/Button.stories.ts:45-55
Timestamp: 2025-12-17T00:40:09.635Z
Learning: Prefer pure function declarations over function expressions (e.g., use function foo() { ... } instead of const foo = () => { ... }) for pure functions in the repository. Function declarations are more functional-leaning, offer better hoisting clarity, and can improve readability and tooling consistency. Apply this guideline across TypeScript files in Comfy-Org/ComfyUI_frontend, including story and UI component code, except where a function expression is semantically required (e.g., callbacks, higher-order functions with closures).

Applied to files:

  • src/platform/cloud/subscription/composables/useSubscription.ts
📚 Learning: 2025-12-30T22:22:33.836Z
Learnt from: kaili-yang
Repo: Comfy-Org/ComfyUI_frontend PR: 7805
File: src/composables/useCoreCommands.ts:439-439
Timestamp: 2025-12-30T22:22:33.836Z
Learning: When accessing reactive properties from Pinia stores in TypeScript files, avoid using .value on direct property access (e.g., useStore().isOverlayExpanded). Pinia auto-wraps refs when accessed directly, returning the primitive value. The .value accessor is only needed when destructuring store properties or when using storeToRefs().

Applied to files:

  • src/platform/cloud/subscription/composables/useSubscription.ts
📚 Learning: 2026-01-10T00:24:17.695Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-10T00:24:17.695Z
Learning: Applies to src/**/*.{ts,vue} : Leverage VueUse functions for performance-enhancing composables

Applied to files:

  • src/platform/cloud/subscription/components/SubscriptionRequiredDialogContent.vue
🔇 Additional comments (7)
src/platform/cloud/subscription/composables/useSubscription.ts (2)

131-138: LGTM!

Passing isSubscriptionEnabled as the shouldWatchCancellation callback cleanly encapsulates the subscription-enabled check, making the cancellation watcher reusable and decoupled from the specific condition logic.


244-268: LGTM!

Exposing isSubscriptionEnabled in the return object provides a clean public API for consumers to determine subscription mode status without duplicating the logic.

src/platform/cloud/subscription/components/SubscriptionRequiredDialogContent.vue (2)

142-143: LGTM!

Destructuring isSubscriptionEnabled from the shared composable properly centralizes the subscription mode check logic, eliminating duplication across components.


255-258: LGTM!

Proper cleanup in onBeforeUnmount prevents memory leaks by stopping the polling interval and removing the window focus event listener.

src/components/dialog/content/TopUpCreditsDialogContent.vue (3)

34-49: LGTM!

Good use of the Button component with proper variant/size props, cn() utility for conditional styling, and autofocus on the default selection.


59-72: Consider aligning stepper min with MIN_AMOUNT for better UX.

The stepper's :min="0" allows values below MIN_AMOUNT (5), which triggers the validation error message. While this works, setting :min="MIN_AMOUNT" would prevent invalid input entirely and provide clearer feedback through the stepper's own constraints.

If the current behavior (allowing 0 to show the warning) is intentional for UX reasons, this can be disregarded.

♻️ Optional fix if prevention is preferred
         <FormattedNumberStepper
           :model-value="payAmount"
-          :min="0"
+          :min="MIN_AMOUNT"
           :max="MAX_AMOUNT"

248-276: LGTM!

The handleBuy function properly implements:

  • Double-click prevention with loading guard
  • Telemetry tracking before purchase
  • Appropriate error handling with user-friendly toast messages
  • Correct navigation flow after successful purchase

Comment on lines +14 to +19
<button
class="cursor-pointer rounded border-none bg-transparent p-0 text-muted-foreground transition-colors hover:text-base-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-secondary-foreground"
@click="() => handleClose()"
>
<i class="icon-[lucide--x] size-6" />
</button>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Use the common Button component and add accessibility label for icon-only button.

Per repository conventions, prefer common button components over plain HTML <button> elements. Additionally, icon-only buttons require an aria-label for screen reader accessibility.

♻️ Suggested fix
-      <button
-        class="cursor-pointer rounded border-none bg-transparent p-0 text-muted-foreground transition-colors hover:text-base-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-secondary-foreground"
-        @click="() => handleClose()"
+      <Button
+        variant="ghost"
+        size="sm"
+        aria-label="Close dialog"
+        class="p-0 text-muted-foreground hover:text-base-foreground"
+        @click="handleClose()"
       >
         <i class="icon-[lucide--x] size-6" />
-      </button>
+      </Button>
🤖 Prompt for AI Agents
In @src/components/dialog/content/TopUpCreditsDialogContent.vue around lines 14
- 19, Replace the plain <button> in TopUpCreditsDialogContent.vue with the
shared Button component (e.g., Button or BaseButton) and pass the same click
handler (handleClose) directly as @click; ensure the icon element stays as the
button child and add an aria-label like aria-label="Close" to make the icon-only
control accessible; maintain existing styling via Button props
(variant/size/class) or by mapping the current classes to the Button so visual
behavior remains unchanged.

Comment on lines +118 to +123
<a
href="https://www.comfy.org/cloud/enterprise"
target="_blank"
class="ml-1 text-inherit"
>{{ $t('credits.topUp.contactUs') }}</a
>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Add rel="noopener noreferrer" to external link for security.

Links with target="_blank" should include rel="noopener noreferrer" to prevent potential reverse tabnapping attacks and ensure the new page doesn't have access to window.opener.

🔒 Suggested fix
       <a
         href="https://www.comfy.org/cloud/enterprise"
         target="_blank"
-        class="ml-1 text-inherit"
+        class="ml-1 text-inherit"
+        rel="noopener noreferrer"
         >{{ $t('credits.topUp.contactUs') }}</a
       >
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<a
href="https://www.comfy.org/cloud/enterprise"
target="_blank"
class="ml-1 text-inherit"
>{{ $t('credits.topUp.contactUs') }}</a
>
<a
href="https://www.comfy.org/cloud/enterprise"
target="_blank"
class="ml-1 text-inherit"
rel="noopener noreferrer"
>{{ $t('credits.topUp.contactUs') }}</a
>
🤖 Prompt for AI Agents
In @src/components/dialog/content/TopUpCreditsDialogContent.vue around lines 118
- 123, The external anchor in TopUpCreditsDialogContent.vue that uses
target="_blank" (the <a> rendering {{ $t('credits.topUp.contactUs') }}) must
include rel="noopener noreferrer" to prevent reverse tabnapping; update that
anchor tag to add rel="noopener noreferrer" alongside the existing href, target,
and class attributes so the new page cannot access window.opener.

Comment on lines +138 to +145
<a
:href="pricingUrl"
target="_blank"
class="flex items-center gap-1 text-sm text-muted-foreground no-underline transition-colors hover:text-base-foreground"
>
{{ $t('credits.topUp.viewPricing') }}
<i class="icon-[lucide--external-link] size-4" />
</a>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Add rel="noopener noreferrer" to external link.

Same security consideration as the enterprise link above.

🔒 Suggested fix
       <a
         :href="pricingUrl"
         target="_blank"
+        rel="noopener noreferrer"
         class="flex items-center gap-1 text-sm text-muted-foreground no-underline transition-colors hover:text-base-foreground"
       >
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<a
:href="pricingUrl"
target="_blank"
class="flex items-center gap-1 text-sm text-muted-foreground no-underline transition-colors hover:text-base-foreground"
>
{{ $t('credits.topUp.viewPricing') }}
<i class="icon-[lucide--external-link] size-4" />
</a>
<a
:href="pricingUrl"
target="_blank"
rel="noopener noreferrer"
class="flex items-center gap-1 text-sm text-muted-foreground no-underline transition-colors hover:text-base-foreground"
>
{{ $t('credits.topUp.viewPricing') }}
<i class="icon-[lucide--external-link] size-4" />
</a>
🤖 Prompt for AI Agents
In @src/components/dialog/content/TopUpCreditsDialogContent.vue around lines 138
- 145, The external anchor rendering the pricing link in
TopUpCreditsDialogContent.vue (the <a> that uses :href="pricingUrl" and displays
$t('credits.topUp.viewPricing')) is missing rel="noopener noreferrer"; update
that anchor to include rel="noopener noreferrer" (alongside target="_blank") to
prevent tab-napping/security issues.

Comment on lines +212 to +214
function formatNumber(num: number): string {
return num.toLocaleString('en-US')
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Consider using the user's locale for number formatting.

The formatNumber function hardcodes 'en-US' locale, but per coding guidelines, i18n should be used for localization. Consider using the i18n locale for consistent formatting.

♻️ Suggested improvement
+import { useI18n } from 'vue-i18n'
+
+const { locale } = useI18n()

 function formatNumber(num: number): string {
-  return num.toLocaleString('en-US')
+  return num.toLocaleString(locale.value)
 }

Note: locale is already available from the existing useI18n() call - just destructure it:

-const { t } = useI18n()
+const { t, locale } = useI18n()
🤖 Prompt for AI Agents
In @src/components/dialog/content/TopUpCreditsDialogContent.vue around lines 212
- 214, The formatNumber function currently hardcodes 'en-US'; instead use the
i18n locale from useI18n(): destructure locale (e.g. const { locale } =
useI18n()) and update formatNumber to call num.toLocaleString(locale.value ||
undefined) (or use new Intl.NumberFormat(locale.value).format(num)); keep the
function name formatNumber and ensure it reads locale.value so formatting
follows the user's locale.

Comment on lines 158 to +159
// Always show custom pricing table for cloud subscriptions
const showCustomPricingTable = computed(
() => isCloud && window.__CONFIG__?.subscription_required
)
const showCustomPricingTable = computed(() => isSubscriptionEnabled())
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Consider simplifying to a constant since the value is static.

isSubscriptionEnabled() checks isCloud and window.__CONFIG__?.subscription_required, both of which are static values set at app initialization. Wrapping this in a computed provides no reactivity benefit since there are no reactive dependencies to track.

If the value won't change during the component's lifecycle, a simple constant would be clearer:

♻️ Suggested refactor
-// Always show custom pricing table for cloud subscriptions
-const showCustomPricingTable = computed(() => isSubscriptionEnabled())
+// Always show custom pricing table for cloud subscriptions
+const showCustomPricingTable = isSubscriptionEnabled()

Then update template usage from showCustomPricingTable.value references (if any exist in watchers) to just showCustomPricingTable.

Note: If keeping computed for consistency with the existing pattern in watchers (lines 206-217, 219-226), this is acceptable as-is.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// Always show custom pricing table for cloud subscriptions
const showCustomPricingTable = computed(
() => isCloud && window.__CONFIG__?.subscription_required
)
const showCustomPricingTable = computed(() => isSubscriptionEnabled())
// Always show custom pricing table for cloud subscriptions
const showCustomPricingTable = isSubscriptionEnabled()
🤖 Prompt for AI Agents
In
@src/platform/cloud/subscription/components/SubscriptionRequiredDialogContent.vue
around lines 158 - 159, The computed showCustomPricingTable wraps
isSubscriptionEnabled() but its inputs are static at app init, so replace the
computed with a plain constant: change showCustomPricingTable = computed(() =>
isSubscriptionEnabled()) to a direct const showCustomPricingTable =
isSubscriptionEnabled(); then update any template or watcher usage that
currently accesses showCustomPricingTable.value to use showCustomPricingTable
instead; keep computed only if you intentionally want the current computed
pattern for consistency.

Comment on lines +124 to 129
/**
* Whether cloud subscription mode is enabled (cloud distribution with subscription_required config).
* Use to determine which UI to show (SubscriptionPanel vs LegacyCreditsPanel).
*/
const isSubscriptionEnabled = (): boolean =>
Boolean(isCloud && window.__CONFIG__?.subscription_required)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Prefer function declaration for pure functions.

Per coding guidelines, prefer function declarations over function expressions for pure functions. Since this is a simple pure function without closure requirements that would necessitate an arrow function, consider refactoring:

♻️ Suggested refactor
-  /**
-   * Whether cloud subscription mode is enabled (cloud distribution with subscription_required config).
-   * Use to determine which UI to show (SubscriptionPanel vs LegacyCreditsPanel).
-   */
-  const isSubscriptionEnabled = (): boolean =>
-    Boolean(isCloud && window.__CONFIG__?.subscription_required)
+  /**
+   * Whether cloud subscription mode is enabled (cloud distribution with subscription_required config).
+   * Use to determine which UI to show (SubscriptionPanel vs LegacyCreditsPanel).
+   */
+  function isSubscriptionEnabled(): boolean {
+    return Boolean(isCloud && window.__CONFIG__?.subscription_required)
+  }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
/**
* Whether cloud subscription mode is enabled (cloud distribution with subscription_required config).
* Use to determine which UI to show (SubscriptionPanel vs LegacyCreditsPanel).
*/
const isSubscriptionEnabled = (): boolean =>
Boolean(isCloud && window.__CONFIG__?.subscription_required)
/**
* Whether cloud subscription mode is enabled (cloud distribution with subscription_required config).
* Use to determine which UI to show (SubscriptionPanel vs LegacyCreditsPanel).
*/
function isSubscriptionEnabled(): boolean {
return Boolean(isCloud && window.__CONFIG__?.subscription_required)
}
🤖 Prompt for AI Agents
In @src/platform/cloud/subscription/composables/useSubscription.ts around lines
124 - 129, Replace the arrow function expression isSubscriptionEnabled with a
function declaration to follow the coding guideline for pure functions; keep the
same boolean return type and logic that returns Boolean(isCloud &&
window.__CONFIG__?.subscription_required), referencing the same identifiers
(isSubscriptionEnabled, isCloud, window.__CONFIG__?.subscription_required), and
ensure no other behavior or closure semantics change when performing the
refactor.

@christian-byrne
Copy link
Contributor

Think it's safe to close as cloud is now on 1.37

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

preview size:XXL This PR changes 1000+ lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants