Cloud/1.36 - cloud release branch [DO NOT MERGE]#7967
Cloud/1.36 - cloud release branch [DO NOT MERGE]#7967luke-mino-altherr wants to merge 39 commits intomainfrom
Conversation
…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]>
📝 WalkthroughWalkthroughAdds 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
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
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
Possibly related PRs
Suggested reviewers
✨ Finishing touches
Comment |
…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>
There was a problem hiding this comment.
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
📒 Files selected for processing (8)
AGENTS.mdsrc/components/honeyToast/HoneyToast.stories.tssrc/components/toast/ProgressToastItem.stories.tssrc/platform/tasks/services/taskService.tssrc/schemas/apiSchema.tssrc/stores/assetDownloadStore.test.tssrc/stores/assetDownloadStore.tstests-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.tssrc/stores/assetDownloadStore.test.tssrc/components/honeyToast/HoneyToast.stories.tssrc/schemas/apiSchema.tssrc/components/toast/ProgressToastItem.stories.tssrc/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 usingvue-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.tssrc/stores/assetDownloadStore.test.tssrc/components/honeyToast/HoneyToast.stories.tssrc/schemas/apiSchema.tssrc/components/toast/ProgressToastItem.stories.tssrc/stores/assetDownloadStore.ts
src/**/{services,composables}/**/*.{ts,tsx}
📄 CodeRabbit inference engine (src/CLAUDE.md)
src/**/{services,composables}/**/*.{ts,tsx}: Useapi.apiURL()for backend endpoints instead of constructing URLs directly
Useapi.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 separateimport typestatements instead of inlinetypein mixed imports
Apply Prettier formatting with 2-space indentation, single quotes, no trailing semicolons, 80-character width
Sort and group imports by plugin, runpnpm formatbefore committing
Never useanytype - use proper TypeScript types
Never useas anytype 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.tssrc/stores/assetDownloadStore.test.tssrc/components/honeyToast/HoneyToast.stories.tssrc/schemas/apiSchema.tssrc/components/toast/ProgressToastItem.stories.tssrc/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.tssrc/stores/assetDownloadStore.test.tssrc/components/honeyToast/HoneyToast.stories.tssrc/schemas/apiSchema.tssrc/components/toast/ProgressToastItem.stories.tssrc/stores/assetDownloadStore.ts
src/**/*.{ts,vue}
📄 CodeRabbit inference engine (AGENTS.md)
src/**/*.{ts,vue}: Usereffor reactive state,computed()for derived values, andwatch/watchEffectfor side effects in Composition API
Avoid usingrefwithwatchif acomputedwould suffice - minimize refs and derived state
Useprovide/injectfor 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.tssrc/stores/assetDownloadStore.test.tssrc/components/honeyToast/HoneyToast.stories.tssrc/schemas/apiSchema.tssrc/components/toast/ProgressToastItem.stories.tssrc/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.tssrc/stores/assetDownloadStore.test.tssrc/components/honeyToast/HoneyToast.stories.tssrc/schemas/apiSchema.tssrc/components/toast/ProgressToastItem.stories.tssrc/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.tssrc/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.tssrc/stores/assetDownloadStore.ts
+(tests-ui|src)/**/*.test.ts
📄 CodeRabbit inference engine (AGENTS.md)
+(tests-ui|src)/**/*.test.ts: Unit and component tests belong intests-ui/orsrc/**/*.test.tsusing 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; usevi.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.tssrc/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.tssrc/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.tssrc/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.tssrc/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.tssrc/stores/assetDownloadStore.test.tssrc/components/honeyToast/HoneyToast.stories.tssrc/schemas/apiSchema.tssrc/components/toast/ProgressToastItem.stories.tssrc/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.tssrc/stores/assetDownloadStore.test.tssrc/components/honeyToast/HoneyToast.stories.tssrc/schemas/apiSchema.tssrc/components/toast/ProgressToastItem.stories.tssrc/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.tssrc/stores/assetDownloadStore.test.tssrc/components/honeyToast/HoneyToast.stories.tssrc/schemas/apiSchema.tssrc/components/toast/ProgressToastItem.stories.tssrc/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.tssrc/stores/assetDownloadStore.test.tssrc/components/honeyToast/HoneyToast.stories.tssrc/schemas/apiSchema.tssrc/components/toast/ProgressToastItem.stories.tssrc/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.tssrc/stores/assetDownloadStore.test.tssrc/components/honeyToast/HoneyToast.stories.tssrc/schemas/apiSchema.tssrc/components/toast/ProgressToastItem.stories.tssrc/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.tssrc/stores/assetDownloadStore.test.tssrc/components/honeyToast/HoneyToast.stories.tssrc/schemas/apiSchema.tssrc/components/toast/ProgressToastItem.stories.tssrc/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.tssrc/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.tstests-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.tssrc/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.tsAGENTS.mdtests-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.tstests-ui/vitest-patterns.mdsrc/components/honeyToast/HoneyToast.stories.tssrc/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.tssrc/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.tsAGENTS.mdtests-ui/vitest-patterns.mdsrc/components/honeyToast/HoneyToast.stories.tssrc/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.tstests-ui/vitest-patterns.mdsrc/components/honeyToast/HoneyToast.stories.tssrc/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.mdtests-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.mdtests-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.mdtests-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.mdtests-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.mdsrc/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.mdsrc/components/honeyToast/HoneyToast.stories.tssrc/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.tssrc/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:
defineModelsimplifies 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/*.mdpath is incorrect on two fronts: the@prefix is non-standard for markdown cross-references (used only for code import aliases intsconfig.jsonandpackage.json) and thedocs/testing/directory does not exist in the codebase. Other documentation cross-references in the codebase use relative paths likedocs/FEATURE_FLAGS.mdor absolute paths like/docs/extensions/core.md. Either remove the@prefix and ensure thedocs/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 compatibilityLearnt 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 regressionsLearnt 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 testsLearnt 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 unexpectedlyLearnt 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 testLearnt 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 possibleLearnt 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 possibleLearnt 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 testsLearnt 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 stylesLearnt 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 teststests-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.tsfor browser/e2e tests inbrowser_tests/and.test.tsfor unit/component tests insrc/andtests-ui/. The documentation appropriately includes both patterns.src/schemas/apiSchema.ts (2)
138-148: LGTM!The new
zAssetDownloadWsMessageschema is well-structured with appropriate field types. Usingz.string()fortask_idis 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()andz.string().datetime()(string refinements) is correct for Zod 3.x compatibility.
46-68: LGTM!The
getTaskfunction follows best practices: usessafeParse()for validation (avoiding ZodError throws with internal details), provides user-friendly error messages viafromZodError, and handles HTTP errors appropriately. Based on learnings, this pattern is preferred.
70-70: LGTM!Singleton export follows the established pattern used by
apiin 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, andcreateTestingPiniawithstubActions: falseallows 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 typestatement per repository lint rules.
9-29: LGTM!
AssetDownloadinterface is appropriately exported for use in components/stories.CompletedDownloadis kept internal. Constants have reasonable defaults for polling behavior.
31-48: LGTM!State structure is well-designed:
ref<Map>for reactive downloads, non-reactiveMapfor internalpendingModelTypes, andcomputedproperties 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
completedDownloadslist. ThelastUpdatetimestamp enables stale download detection.
93-129: LGTM!Polling logic correctly detects stale downloads and fetches updated task state. Replaying updates through
handleAssetDownloadensures 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
useIntervalFnwith reactive pause/resume based onhasActiveDownloads. 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.
handleAssetDownloadis 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 typestatement. 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 typestatements 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.
createMockJobis duplicated in bothProgressToastItem.stories.tsand 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 possibleLearnt 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 possibleLearnt 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 necessaryLearnt 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 OwnLearnt 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
| ```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() | ||
| }) | ||
| }) | ||
| ``` |
There was a problem hiding this comment.
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.
| ```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.
| ```typescript | ||
| import { api } from '@/scripts/api' | ||
| import { myService } from '@/services/myService' | ||
|
|
||
| it('handles success', () => { | ||
| vi.mocked(myService.doThing).mockResolvedValue({ data: 'test' }) | ||
| // ... test code | ||
| }) |
There was a problem hiding this comment.
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.
| ```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.
| ```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) | ||
| }) |
There was a problem hiding this comment.
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.
| ```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.
| ```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() | ||
| }) |
There was a problem hiding this comment.
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.
… 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]>
There was a problem hiding this comment.
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
📒 Files selected for processing (12)
src/composables/graph/useGraphNodeManager.tssrc/platform/assets/components/AssetBrowserModal.vuesrc/platform/assets/components/AssetCard.vuesrc/platform/assets/components/AssetGrid.vuesrc/platform/assets/composables/useUploadModelWizard.tssrc/renderer/extensions/vueNodes/components/NodeWidgets.vuesrc/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberInput.vuesrc/renderer/extensions/vueNodes/widgets/components/WidgetLegacy.vuesrc/renderer/extensions/vueNodes/widgets/composables/useNumberStepCalculation.tssrc/stores/assetDownloadStore.test.tssrc/stores/assetDownloadStore.tssrc/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 insrc/locales/en/main.json
Use Tailwind utility classes instead ofdark:variant - use semantic values fromstyle.csstheme (e.g.,bg-node-component-surface)
Usecn()utility from@/utils/tailwindUtilfor merging Tailwind class names instead of:class="[]"or hardcoding
Never use!importantor!Tailwind prefix - fix interfering classes instead
Use Tailwind fraction utilities instead of arbitrary percentage values (e.g.,w-4/5instead ofw-[80%])
Use TypeScript Vue 3.5 style default prop declaration with reactive props destructuring - avoidwithDefaultsor runtime props
PreferdefineModelover separately defining a prop and emit for v-model bindings
Define slots via template usage, not viadefineSlots
Use same-name shorthand for slot prop bindings (e.g.,:isExpandedinstead 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.vuesrc/platform/assets/components/AssetCard.vuesrc/platform/assets/components/AssetBrowserModal.vuesrc/renderer/extensions/vueNodes/widgets/components/WidgetLegacy.vuesrc/renderer/extensions/vueNodes/components/NodeWidgets.vuesrc/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.vuesrc/composables/graph/useGraphNodeManager.tssrc/stores/assetsStore.tssrc/platform/assets/components/AssetCard.vuesrc/platform/assets/components/AssetBrowserModal.vuesrc/renderer/extensions/vueNodes/widgets/components/WidgetLegacy.vuesrc/stores/assetDownloadStore.test.tssrc/stores/assetDownloadStore.tssrc/platform/assets/composables/useUploadModelWizard.tssrc/renderer/extensions/vueNodes/components/NodeWidgets.vuesrc/renderer/extensions/vueNodes/widgets/composables/useNumberStepCalculation.tssrc/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 separateimport typestatements instead of inlinetypein mixed imports
Apply Prettier formatting with 2-space indentation, single quotes, no trailing semicolons, 80-character width
Sort and group imports by plugin, runpnpm formatbefore committing
Never useanytype - use proper TypeScript types
Never useas anytype 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.vuesrc/composables/graph/useGraphNodeManager.tssrc/stores/assetsStore.tssrc/platform/assets/components/AssetCard.vuesrc/platform/assets/components/AssetBrowserModal.vuesrc/renderer/extensions/vueNodes/widgets/components/WidgetLegacy.vuesrc/stores/assetDownloadStore.test.tssrc/stores/assetDownloadStore.tssrc/platform/assets/composables/useUploadModelWizard.tssrc/renderer/extensions/vueNodes/components/NodeWidgets.vuesrc/renderer/extensions/vueNodes/widgets/composables/useNumberStepCalculation.tssrc/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.vuesrc/composables/graph/useGraphNodeManager.tssrc/platform/assets/components/AssetCard.vuesrc/platform/assets/components/AssetBrowserModal.vuesrc/renderer/extensions/vueNodes/widgets/components/WidgetLegacy.vuesrc/platform/assets/composables/useUploadModelWizard.tssrc/renderer/extensions/vueNodes/components/NodeWidgets.vuesrc/renderer/extensions/vueNodes/widgets/composables/useNumberStepCalculation.tssrc/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.vuesrc/composables/graph/useGraphNodeManager.tssrc/stores/assetsStore.tssrc/platform/assets/components/AssetCard.vuesrc/platform/assets/components/AssetBrowserModal.vuesrc/renderer/extensions/vueNodes/widgets/components/WidgetLegacy.vuesrc/stores/assetDownloadStore.test.tssrc/stores/assetDownloadStore.tssrc/platform/assets/composables/useUploadModelWizard.tssrc/renderer/extensions/vueNodes/components/NodeWidgets.vuesrc/renderer/extensions/vueNodes/widgets/composables/useNumberStepCalculation.tssrc/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.vuesrc/composables/graph/useGraphNodeManager.tssrc/platform/assets/components/AssetCard.vuesrc/platform/assets/components/AssetBrowserModal.vuesrc/renderer/extensions/vueNodes/widgets/components/WidgetLegacy.vuesrc/platform/assets/composables/useUploadModelWizard.tssrc/renderer/extensions/vueNodes/components/NodeWidgets.vuesrc/renderer/extensions/vueNodes/widgets/composables/useNumberStepCalculation.tssrc/platform/assets/components/AssetGrid.vue
src/**/*.{ts,vue}
📄 CodeRabbit inference engine (AGENTS.md)
src/**/*.{ts,vue}: Usereffor reactive state,computed()for derived values, andwatch/watchEffectfor side effects in Composition API
Avoid usingrefwithwatchif acomputedwould suffice - minimize refs and derived state
Useprovide/injectfor 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.vuesrc/composables/graph/useGraphNodeManager.tssrc/stores/assetsStore.tssrc/platform/assets/components/AssetCard.vuesrc/platform/assets/components/AssetBrowserModal.vuesrc/renderer/extensions/vueNodes/widgets/components/WidgetLegacy.vuesrc/stores/assetDownloadStore.test.tssrc/stores/assetDownloadStore.tssrc/platform/assets/composables/useUploadModelWizard.tssrc/renderer/extensions/vueNodes/components/NodeWidgets.vuesrc/renderer/extensions/vueNodes/widgets/composables/useNumberStepCalculation.tssrc/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 usingvue-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.tssrc/stores/assetsStore.tssrc/stores/assetDownloadStore.test.tssrc/stores/assetDownloadStore.tssrc/platform/assets/composables/useUploadModelWizard.tssrc/renderer/extensions/vueNodes/widgets/composables/useNumberStepCalculation.ts
src/**/{services,composables}/**/*.{ts,tsx}
📄 CodeRabbit inference engine (src/CLAUDE.md)
src/**/{services,composables}/**/*.{ts,tsx}: Useapi.apiURL()for backend endpoints instead of constructing URLs directly
Useapi.fileURL()for static file access instead of constructing URLs directly
Files:
src/composables/graph/useGraphNodeManager.tssrc/platform/assets/composables/useUploadModelWizard.tssrc/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.tssrc/stores/assetsStore.tssrc/stores/assetDownloadStore.test.tssrc/stores/assetDownloadStore.tssrc/platform/assets/composables/useUploadModelWizard.tssrc/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.tssrc/stores/assetDownloadStore.test.tssrc/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.tssrc/stores/assetDownloadStore.test.tssrc/stores/assetDownloadStore.ts
+(tests-ui|src)/**/*.test.ts
📄 CodeRabbit inference engine (AGENTS.md)
+(tests-ui|src)/**/*.test.ts: Unit and component tests belong intests-ui/orsrc/**/*.test.tsusing 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; usevi.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.vuesrc/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.vuesrc/platform/assets/components/AssetCard.vuesrc/platform/assets/components/AssetBrowserModal.vuesrc/renderer/extensions/vueNodes/widgets/components/WidgetLegacy.vuesrc/renderer/extensions/vueNodes/components/NodeWidgets.vuesrc/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.vuesrc/platform/assets/components/AssetCard.vuesrc/platform/assets/components/AssetBrowserModal.vuesrc/renderer/extensions/vueNodes/widgets/components/WidgetLegacy.vuesrc/renderer/extensions/vueNodes/components/NodeWidgets.vuesrc/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.vuesrc/platform/assets/components/AssetCard.vuesrc/platform/assets/components/AssetBrowserModal.vuesrc/renderer/extensions/vueNodes/widgets/components/WidgetLegacy.vuesrc/renderer/extensions/vueNodes/components/NodeWidgets.vuesrc/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.vuesrc/platform/assets/components/AssetCard.vuesrc/platform/assets/components/AssetBrowserModal.vuesrc/renderer/extensions/vueNodes/widgets/components/WidgetLegacy.vuesrc/renderer/extensions/vueNodes/components/NodeWidgets.vuesrc/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.vuesrc/platform/assets/components/AssetCard.vuesrc/platform/assets/components/AssetBrowserModal.vuesrc/renderer/extensions/vueNodes/widgets/components/WidgetLegacy.vuesrc/renderer/extensions/vueNodes/components/NodeWidgets.vuesrc/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.vuesrc/renderer/extensions/vueNodes/widgets/components/WidgetLegacy.vuesrc/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.vuesrc/composables/graph/useGraphNodeManager.tssrc/stores/assetsStore.tssrc/platform/assets/components/AssetCard.vuesrc/platform/assets/components/AssetBrowserModal.vuesrc/renderer/extensions/vueNodes/widgets/components/WidgetLegacy.vuesrc/stores/assetDownloadStore.test.tssrc/stores/assetDownloadStore.tssrc/platform/assets/composables/useUploadModelWizard.tssrc/renderer/extensions/vueNodes/components/NodeWidgets.vuesrc/renderer/extensions/vueNodes/widgets/composables/useNumberStepCalculation.tssrc/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.vuesrc/platform/assets/components/AssetCard.vuesrc/platform/assets/components/AssetBrowserModal.vuesrc/renderer/extensions/vueNodes/widgets/components/WidgetLegacy.vuesrc/renderer/extensions/vueNodes/components/NodeWidgets.vuesrc/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.vuesrc/platform/assets/components/AssetCard.vuesrc/platform/assets/components/AssetBrowserModal.vuesrc/renderer/extensions/vueNodes/widgets/components/WidgetLegacy.vuesrc/renderer/extensions/vueNodes/components/NodeWidgets.vuesrc/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.vuesrc/platform/assets/components/AssetCard.vuesrc/platform/assets/components/AssetBrowserModal.vuesrc/renderer/extensions/vueNodes/widgets/components/WidgetLegacy.vuesrc/renderer/extensions/vueNodes/components/NodeWidgets.vuesrc/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.vuesrc/composables/graph/useGraphNodeManager.tssrc/stores/assetsStore.tssrc/platform/assets/components/AssetCard.vuesrc/platform/assets/components/AssetBrowserModal.vuesrc/renderer/extensions/vueNodes/widgets/components/WidgetLegacy.vuesrc/stores/assetDownloadStore.test.tssrc/stores/assetDownloadStore.tssrc/platform/assets/composables/useUploadModelWizard.tssrc/renderer/extensions/vueNodes/components/NodeWidgets.vuesrc/renderer/extensions/vueNodes/widgets/composables/useNumberStepCalculation.tssrc/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.tssrc/stores/assetsStore.tssrc/stores/assetDownloadStore.test.tssrc/stores/assetDownloadStore.tssrc/platform/assets/composables/useUploadModelWizard.tssrc/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.tssrc/stores/assetsStore.tssrc/stores/assetDownloadStore.test.tssrc/stores/assetDownloadStore.tssrc/platform/assets/composables/useUploadModelWizard.tssrc/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.tssrc/stores/assetsStore.tssrc/stores/assetDownloadStore.test.tssrc/stores/assetDownloadStore.tssrc/platform/assets/composables/useUploadModelWizard.tssrc/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.tssrc/stores/assetsStore.tssrc/stores/assetDownloadStore.test.tssrc/stores/assetDownloadStore.tssrc/platform/assets/composables/useUploadModelWizard.tssrc/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.tssrc/stores/assetDownloadStore.test.tssrc/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.tssrc/stores/assetDownloadStore.test.tssrc/stores/assetDownloadStore.tssrc/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.tssrc/platform/assets/components/AssetBrowserModal.vuesrc/stores/assetDownloadStore.tssrc/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.tssrc/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.tssrc/platform/assets/components/AssetBrowserModal.vuesrc/stores/assetDownloadStore.test.tssrc/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.tssrc/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.vuesrc/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.vuesrc/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.tssrc/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)
| async function uploadModel(): Promise<boolean> { | ||
| if (!canUploadModel.value) { | ||
| return false | ||
| } | ||
|
|
There was a problem hiding this comment.
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.
| 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.
| 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 | ||
| } |
There was a problem hiding this comment.
🧹 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.
| 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.
| 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 | ||
| } |
There was a problem hiding this comment.
🧹 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.
| 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.
| const providers = modelToNodeStore | ||
| .getAllNodeProviders(modelType) | ||
| .filter((provider) => provider.nodeDef?.name) | ||
|
|
||
| const nodeTypeUpdates = providers.map((provider) => | ||
| updateModelsForNodeType(provider.nodeDef.name).then( | ||
| () => provider.nodeDef.name | ||
| ) | ||
| ) |
There was a problem hiding this comment.
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.
| 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.
…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]>
…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]>
There was a problem hiding this comment.
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 forWanReferenceVideoApinode.The implementation adds
WanReferenceVideoApipricing 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
WanReferenceVideoApisimilar to the existingWanTextToVideoApiandWanImageToVideoApitests?
🤖 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
📒 Files selected for processing (4)
src/composables/node/useNodePricing.tssrc/extensions/core/load3d/Load3d.tssrc/renderer/extensions/vueNodes/widgets/composables/useImagePreviewWidget.tstests-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.tssrc/composables/node/useNodePricing.tssrc/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 usingvue-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.tssrc/composables/node/useNodePricing.tssrc/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 separateimport typestatements instead of inlinetypein mixed imports
Apply Prettier formatting with 2-space indentation, single quotes, no trailing semicolons, 80-character width
Sort and group imports by plugin, runpnpm formatbefore committing
Never useanytype - use proper TypeScript types
Never useas anytype 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.tssrc/composables/node/useNodePricing.tssrc/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.tssrc/composables/node/useNodePricing.tssrc/renderer/extensions/vueNodes/widgets/composables/useImagePreviewWidget.ts
src/**/*.{ts,vue}
📄 CodeRabbit inference engine (AGENTS.md)
src/**/*.{ts,vue}: Usereffor reactive state,computed()for derived values, andwatch/watchEffectfor side effects in Composition API
Avoid usingrefwithwatchif acomputedwould suffice - minimize refs and derived state
Useprovide/injectfor 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.tssrc/composables/node/useNodePricing.tssrc/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.tssrc/composables/node/useNodePricing.tssrc/renderer/extensions/vueNodes/widgets/composables/useImagePreviewWidget.ts
src/**/{services,composables}/**/*.{ts,tsx}
📄 CodeRabbit inference engine (src/CLAUDE.md)
src/**/{services,composables}/**/*.{ts,tsx}: Useapi.apiURL()for backend endpoints instead of constructing URLs directly
Useapi.fileURL()for static file access instead of constructing URLs directly
Files:
src/composables/node/useNodePricing.tssrc/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.tssrc/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.tssrc/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 intests-ui/orsrc/**/*.test.tsusing 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; usevi.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.tssrc/composables/node/useNodePricing.tstests-ui/tests/composables/node/useNodePricing.test.tssrc/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.tssrc/composables/node/useNodePricing.tstests-ui/tests/composables/node/useNodePricing.test.tssrc/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.tssrc/composables/node/useNodePricing.tstests-ui/tests/composables/node/useNodePricing.test.tssrc/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.tssrc/composables/node/useNodePricing.tstests-ui/tests/composables/node/useNodePricing.test.tssrc/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.tssrc/composables/node/useNodePricing.tstests-ui/tests/composables/node/useNodePricing.test.tssrc/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.tssrc/composables/node/useNodePricing.tssrc/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.isAnimationPlayingensures 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: trueandseparator: '-'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: trueandseparator: '-'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! ThecanvasOnly: trueoption correctly prevents Vue rendering of the image preview widget.The addition of
canvasOnly: trueto the widget options is a valid and supported property (defined inIWidgetOptions) 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]>
There was a problem hiding this comment.
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
📒 Files selected for processing (2)
src/composables/graph/useGraphNodeManager.tssrc/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.tssrc/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 usingvue-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}: Useapi.apiURL()for backend endpoints instead of constructing URLs directly
Useapi.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 separateimport typestatements instead of inlinetypein mixed imports
Apply Prettier formatting with 2-space indentation, single quotes, no trailing semicolons, 80-character width
Sort and group imports by plugin, runpnpm formatbefore committing
Never useanytype - use proper TypeScript types
Never useas anytype 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.tssrc/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.tssrc/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.tssrc/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.tssrc/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}: Usereffor reactive state,computed()for derived values, andwatch/watchEffectfor side effects in Composition API
Avoid usingrefwithwatchif acomputedwould suffice - minimize refs and derived state
Useprovide/injectfor 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.tssrc/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 insrc/locales/en/main.json
Use Tailwind utility classes instead ofdark:variant - use semantic values fromstyle.csstheme (e.g.,bg-node-component-surface)
Usecn()utility from@/utils/tailwindUtilfor merging Tailwind class names instead of:class="[]"or hardcoding
Never use!importantor!Tailwind prefix - fix interfering classes instead
Use Tailwind fraction utilities instead of arbitrary percentage values (e.g.,w-4/5instead ofw-[80%])
Use TypeScript Vue 3.5 style default prop declaration with reactive props destructuring - avoidwithDefaultsor runtime props
PreferdefineModelover separately defining a prop and emit for v-model bindings
Define slots via template usage, not viadefineSlots
Use same-name shorthand for slot prop bindings (e.g.,:isExpandedinstead 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.tssrc/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.tssrc/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
appfrom@/scripts/appcorrectly accesses the singleton ComfyApp instance needed for the canvas reference in widget callbacks.
51-51: LGTM - Interface extensions are well-typed.Adding optional
hasLayoutSizeandresizableproperties 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.resizableto 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 !== falsecorrectly preserves the default behavior (resizable whenundefined) 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.
…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]>
There was a problem hiding this comment.
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
📒 Files selected for processing (2)
.i18nrc.cjssrc/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 insrc/locales/en/main.json
Use Tailwind utility classes instead ofdark:variant - use semantic values fromstyle.csstheme (e.g.,bg-node-component-surface)
Usecn()utility from@/utils/tailwindUtilfor merging Tailwind class names instead of:class="[]"or hardcoding
Never use!importantor!Tailwind prefix - fix interfering classes instead
Use Tailwind fraction utilities instead of arbitrary percentage values (e.g.,w-4/5instead ofw-[80%])
Use TypeScript Vue 3.5 style default prop declaration with reactive props destructuring - avoidwithDefaultsor runtime props
PreferdefineModelover separately defining a prop and emit for v-model bindings
Define slots via template usage, not viadefineSlots
Use same-name shorthand for slot prop bindings (e.g.,:isExpandedinstead 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 separateimport typestatements instead of inlinetypein mixed imports
Apply Prettier formatting with 2-space indentation, single quotes, no trailing semicolons, 80-character width
Sort and group imports by plugin, runpnpm formatbefore committing
Never useanytype - use proper TypeScript types
Never useas anytype 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}: Usereffor reactive state,computed()for derived values, andwatch/watchEffectfor side effects in Composition API
Avoid usingrefwithwatchif acomputedwould suffice - minimize refs and derived state
Useprovide/injectfor 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
containerHeightref initialization of 20 is consistent with the fallback value in the height computation logic.
101-107: LGTM!The dynamic
minHeightbinding tocontainerHeightensures proper layout sizing based on the widget's computed dimensions. The styling approach withrelativepositioning on the wrapper andabsoluteon 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: trueoption 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.
| // 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 |
There was a problem hiding this comment.
🛠️ 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]>
There was a problem hiding this comment.
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
📒 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 usingvue-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}: Useapi.apiURL()for backend endpoints instead of constructing URLs directly
Useapi.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 separateimport typestatements instead of inlinetypein mixed imports
Apply Prettier formatting with 2-space indentation, single quotes, no trailing semicolons, 80-character width
Sort and group imports by plugin, runpnpm formatbefore committing
Never useanytype - use proper TypeScript types
Never useas anytype 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}: Usereffor reactive state,computed()for derived values, andwatch/watchEffectfor side effects in Composition API
Avoid usingrefwithwatchif acomputedwould suffice - minimize refs and derived state
Useprovide/injectfor 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: trueandseparator: '-'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 inVidu2ImageToVideoNode.
| 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) | ||
| } |
There was a problem hiding this comment.
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.
| // 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 |
There was a problem hiding this comment.
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.
| // 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'], |
There was a problem hiding this comment.
🧹 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.
| 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.
There was a problem hiding this comment.
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
📒 Files selected for processing (2)
src/composables/graph/useGraphNodeManager.tstests-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 usingvue-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}: Useapi.apiURL()for backend endpoints instead of constructing URLs directly
Useapi.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 separateimport typestatements instead of inlinetypein mixed imports
Apply Prettier formatting with 2-space indentation, single quotes, no trailing semicolons, 80-character width
Sort and group imports by plugin, runpnpm formatbefore committing
Never useanytype - use proper TypeScript types
Never useas anytype 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}: Usereffor reactive state,computed()for derived values, andwatch/watchEffectfor side effects in Composition API
Avoid usingrefwithwatchif acomputedwould suffice - minimize refs and derived state
Useprovide/injectfor 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 intests-ui/orsrc/**/*.test.tsusing 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; usevi.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.tstests-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.tstests-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.tstests-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.tstests-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.tstests-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.canvasneeded for the litegraph callback signature.
51-51: LGTM - New optional properties for layout capabilities.Adding
hasLayoutSizeandresizableto 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."
| 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?.()) | ||
| } |
There was a problem hiding this comment.
🧩 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.tsRepository: 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 -A2Repository: 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:
- Line 176 (
widget.value = value ?? undefined) triggers the DOMWidget's setter, which callsthis.callback?.(this.value)with 1 argument - 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()) |
There was a problem hiding this comment.
🧹 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.
| graph.trigger('node:slot-links:changed', { | ||
| nodeId: '1', | ||
| slotType: NodeSlotType.INPUT | ||
| }) |
There was a problem hiding this comment.
🧹 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.
| 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.
…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]>
There was a problem hiding this comment.
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
📒 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 usingvue-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 separateimport typestatements instead of inlinetypein mixed imports
Apply Prettier formatting with 2-space indentation, single quotes, no trailing semicolons, 80-character width
Sort and group imports by plugin, runpnpm formatbefore committing
Never useanytype - use proper TypeScript types
Never useas anytype 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}: Usereffor reactive state,computed()for derived values, andwatch/watchEffectfor side effects in Composition API
Avoid usingrefwithwatchif acomputedwould suffice - minimize refs and derived state
Useprovide/injectfor 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 ofes-toolkit'sremovefunction is appropriate per coding guidelines.
491-493: LGTM!Consistent with the cleanup pattern added to
dynamicComboWidget. Properly invokesonRemove?.()on each removed widget to ensure lifecycle cleanup is performed.
| requestAnimationFrame(() => { | ||
| const input = node.inputs.at(index)! | ||
| node.onConnectionsChange?.( | ||
| LiteGraph.INPUT, | ||
| index, | ||
| !!input.link, | ||
| input.link ? node.graph?.links?.[input.link] : undefined, | ||
| input | ||
| ) | ||
| }) |
There was a problem hiding this comment.
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.
| 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]>
There was a problem hiding this comment.
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
📒 Files selected for processing (3)
src/components/dialog/content/TopUpCreditsDialogContent.vuesrc/platform/cloud/subscription/components/SubscriptionRequiredDialogContent.vuesrc/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 insrc/locales/en/main.json
Use Tailwind utility classes instead ofdark:variant - use semantic values fromstyle.csstheme (e.g.,bg-node-component-surface)
Usecn()utility from@/utils/tailwindUtilfor merging Tailwind class names instead of:class="[]"or hardcoding
Never use!importantor!Tailwind prefix - fix interfering classes instead
Use Tailwind fraction utilities instead of arbitrary percentage values (e.g.,w-4/5instead ofw-[80%])
Use TypeScript Vue 3.5 style default prop declaration with reactive props destructuring - avoidwithDefaultsor runtime props
PreferdefineModelover separately defining a prop and emit for v-model bindings
Define slots via template usage, not viadefineSlots
Use same-name shorthand for slot prop bindings (e.g.,:isExpandedinstead 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.vuesrc/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.vuesrc/platform/cloud/subscription/composables/useSubscription.tssrc/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 separateimport typestatements instead of inlinetypein mixed imports
Apply Prettier formatting with 2-space indentation, single quotes, no trailing semicolons, 80-character width
Sort and group imports by plugin, runpnpm formatbefore committing
Never useanytype - use proper TypeScript types
Never useas anytype 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.vuesrc/platform/cloud/subscription/composables/useSubscription.tssrc/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.vuesrc/platform/cloud/subscription/composables/useSubscription.tssrc/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.vuesrc/platform/cloud/subscription/composables/useSubscription.tssrc/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.vuesrc/platform/cloud/subscription/composables/useSubscription.tssrc/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 componentsName 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}: Usereffor reactive state,computed()for derived values, andwatch/watchEffectfor side effects in Composition API
Avoid usingrefwithwatchif acomputedwould suffice - minimize refs and derived state
Useprovide/injectfor 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.vuesrc/platform/cloud/subscription/composables/useSubscription.tssrc/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 usingvue-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}: Useapi.apiURL()for backend endpoints instead of constructing URLs directly
Useapi.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.vuesrc/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.vuesrc/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.vuesrc/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.vuesrc/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.vuesrc/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.vuesrc/platform/cloud/subscription/composables/useSubscription.tssrc/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.vuesrc/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.vuesrc/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.vuesrc/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.vuesrc/platform/cloud/subscription/composables/useSubscription.tssrc/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.tssrc/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
isSubscriptionEnabledas theshouldWatchCancellationcallback cleanly encapsulates the subscription-enabled check, making the cancellation watcher reusable and decoupled from the specific condition logic.
244-268: LGTM!Exposing
isSubscriptionEnabledin 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
isSubscriptionEnabledfrom the shared composable properly centralizes the subscription mode check logic, eliminating duplication across components.
255-258: LGTM!Proper cleanup in
onBeforeUnmountprevents 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 belowMIN_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
handleBuyfunction 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
| <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> |
There was a problem hiding this comment.
🧹 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.
| <a | ||
| href="https://www.comfy.org/cloud/enterprise" | ||
| target="_blank" | ||
| class="ml-1 text-inherit" | ||
| >{{ $t('credits.topUp.contactUs') }}</a | ||
| > |
There was a problem hiding this comment.
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.
| <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.
| <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> |
There was a problem hiding this comment.
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.
| <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.
| function formatNumber(num: number): string { | ||
| return num.toLocaleString('en-US') | ||
| } |
There was a problem hiding this comment.
🧹 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.
| // Always show custom pricing table for cloud subscriptions | ||
| const showCustomPricingTable = computed( | ||
| () => isCloud && window.__CONFIG__?.subscription_required | ||
| ) | ||
| const showCustomPricingTable = computed(() => isSubscriptionEnabled()) |
There was a problem hiding this comment.
🧹 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.
| // 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.
| /** | ||
| * 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) |
There was a problem hiding this comment.
🧹 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.
| /** | |
| * 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.
|
Think it's safe to close as cloud is now on 1.37 |
Summary
Changes
Review Focus
Screenshots (if applicable)
┆Issue is synchronized with this Notion page by Unito