-
Notifications
You must be signed in to change notification settings - Fork 433
add shared comfy credit conversion helpers #7061
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
🎨 Storybook Build Status✅ Build completed successfully! ⏰ Completed at: 12/04/2025, 08:21:52 PM UTC 🔗 Links🎉 Your Storybook is ready for review! |
🎭 Playwright Test Results⏰ Completed at: 12/04/2025, 08:30:00 PM UTC 📈 Summary
📊 Test Reports by Browser
🎉 Click on the links above to view detailed test results for each browser configuration. |
📝 WalkthroughWalkthroughAdds a new TypeScript module for comfy-credit conversions/formatting, new unit tests, updates UI components to display balances as credits, and adjusts a subscription composable to format balances as credits (also adding an isLoadingBalance flag). Changes
✨ Finishing touches
🧪 Generate unit tests (beta)
Comment |
Bundle Size ReportSummary
Category Glance Per-category breakdownApp Entry Points — 3.2 MB (baseline 3.2 MB) • 🔴 +3.03 kBMain entry bundles and manifests
Status: 3 added / 3 removed Graph Workspace — 972 kB (baseline 972 kB) • ⚪ 0 BGraph editor runtime, canvas, workflow orchestration
Status: 1 added / 1 removed Views & Navigation — 6.54 kB (baseline 6.54 kB) • ⚪ 0 BTop-level views, pages, and routed surfaces
Status: 1 added / 1 removed Panels & Settings — 298 kB (baseline 298 kB) • ⚪ 0 BConfiguration panels, inspectors, and settings screens
Status: 6 added / 6 removed UI Components — 173 kB (baseline 173 kB) • ⚪ 0 BReusable component library chunks
Status: 6 added / 6 removed Data & Services — 12.5 kB (baseline 12.5 kB) • ⚪ 0 BStores, services, APIs, and repositories
Status: 2 added / 2 removed Utilities & Hooks — 2.94 kB (baseline 2.94 kB) • ⚪ 0 BHelpers, composables, and utility bundles
Status: 1 added / 1 removed Vendor & Third-Party — 8.56 MB (baseline 8.56 MB) • ⚪ 0 BExternal libraries and shared vendor chunks
Other — 3.81 MB (baseline 3.81 MB) • ⚪ 0 BBundles that do not match a named category
Status: 17 added / 17 removed |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
🧹 Nitpick comments (2)
src/base/credits/comfyCredits.ts (1)
6-25: Consider adding input validation for edge cases.The
formatNumberfunction doesn't validate inputs for edge cases likeNaN,Infinity, or negative values. WhileIntl.NumberFormathandles these gracefully, explicit validation and error handling would make the API more robust and prevent unexpected formatting outputs.Consider adding validation:
const formatNumber = ( value: number, options: Intl.NumberFormatOptions = DEFAULT_NUMBER_FORMAT, locale?: string ) => { + if (!Number.isFinite(value)) { + throw new TypeError(`Expected a finite number, got ${value}`) + } + const merged: Intl.NumberFormatOptions = { ...DEFAULT_NUMBER_FORMAT, ...options }tests-ui/tests/base/credits/comfyCredits.test.ts (1)
18-44: Consider adding edge case tests.The current test suite covers happy path scenarios but lacks coverage for edge cases such as:
- Zero values
- Negative numbers (if supported)
- Very large numbers
- Custom formatting options (e.g., different decimal places)
- The min/max fraction digits correction logic in
formatNumberConsider adding tests like:
test('handles zero values', () => { expect(usdToCents(0)).toBe(0) expect(formatCredits(0, undefined, 'en-US')).toBe('0.00') }) test('handles large numbers', () => { expect(formatCredits(1000000, undefined, 'en-US')).toBe('1,000,000.00') }) test('respects custom formatting options', () => { expect(formatCredits(1.23456, { maximumFractionDigits: 4 }, 'en-US')).toBe('1.2346') }) test('corrects invalid min/max fraction digits', () => { // When max < min, min should be clamped to max expect(formatCredits(1.23456, { minimumFractionDigits: 5, maximumFractionDigits: 2 }, 'en-US')).toBe('1.23') })
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
src/base/credits/comfyCredits.ts(1 hunks)tests-ui/tests/base/credits/comfyCredits.test.ts(1 hunks)
🧰 Additional context used
📓 Path-based instructions (13)
**/*.{vue,ts,tsx}
📄 CodeRabbit inference engine (.cursorrules)
**/*.{vue,ts,tsx}: Leverage VueUse functions for performance-enhancing utilities
Use vue-i18n in Composition API for any string literals and place new translation entries in src/locales/en/main.json
Files:
src/base/credits/comfyCredits.tstests-ui/tests/base/credits/comfyCredits.test.ts
**/*.{ts,tsx,js}
📄 CodeRabbit inference engine (.cursorrules)
Use es-toolkit for utility functions
Files:
src/base/credits/comfyCredits.tstests-ui/tests/base/credits/comfyCredits.test.ts
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursorrules)
Use TypeScript for type safety
**/*.{ts,tsx}: Never useanytype - use proper TypeScript types
Never useas anytype assertions - fix the underlying type issue
Files:
src/base/credits/comfyCredits.tstests-ui/tests/base/credits/comfyCredits.test.ts
**/*.{ts,tsx,js,vue}
📄 CodeRabbit inference engine (.cursorrules)
Implement proper error handling in components and services
**/*.{ts,tsx,js,vue}: Use 2-space indentation, single quotes, no semicolons, and maintain 80-character line width as configured in.prettierrc
Organize imports by sorting and grouping by plugin, and runpnpm formatbefore committing
Files:
src/base/credits/comfyCredits.tstests-ui/tests/base/credits/comfyCredits.test.ts
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/base/credits/comfyCredits.ts
src/**/*.ts
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
src/**/*.ts: Use es-toolkit for utility functions
Use TypeScript for type safety
Files:
src/base/credits/comfyCredits.ts
**/*.{ts,tsx,js,jsx,vue}
📄 CodeRabbit inference engine (CLAUDE.md)
Use camelCase for variable and setting names in TypeScript/Vue files
Files:
src/base/credits/comfyCredits.tstests-ui/tests/base/credits/comfyCredits.test.ts
**/*.{ts,tsx,vue}
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.{ts,tsx,vue}: Useconst settingStore = useSettingStore()andsettingStore.get('Comfy.SomeSetting')to retrieve settings in TypeScript/Vue files
Useawait settingStore.set('Comfy.SomeSetting', newValue)to update settings in TypeScript/Vue files
Check server capabilities usingapi.serverSupportsFeature('feature_name')before using enhanced features
Useapi.getServerFeature('config_name', defaultValue)to retrieve server feature configurationEnforce ESLint rules for Vue + TypeScript including: no floating promises, no unused imports, and i18n raw text restrictions in templates
Files:
src/base/credits/comfyCredits.tstests-ui/tests/base/credits/comfyCredits.test.ts
**/*.ts
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.ts: Define dynamic setting defaults using runtime context with functions in settings configuration
UsedefaultsByInstallVersionproperty for gradual feature rollout based on version in settings configuration
Files:
src/base/credits/comfyCredits.tstests-ui/tests/base/credits/comfyCredits.test.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
Files:
src/base/credits/comfyCredits.ts
src/**/*.{vue,ts,tsx}
📄 CodeRabbit inference engine (src/CLAUDE.md)
Follow Vue 3 composition API style guide
Files:
src/base/credits/comfyCredits.ts
tests-ui/**/*.test.{js,ts,jsx,tsx}
📄 CodeRabbit inference engine (tests-ui/CLAUDE.md)
tests-ui/**/*.test.{js,ts,jsx,tsx}: Write tests for new features
Follow existing test patterns in the codebase
Use existing test utilities rather than writing custom utilities
Mock external dependencies in tests
Always prefer vitest mock functions over writing verbose manual mocks
Files:
tests-ui/tests/base/credits/comfyCredits.test.ts
**/*.{test,spec}.{ts,tsx,js}
📄 CodeRabbit inference engine (AGENTS.md)
Unit and component tests should be located in
tests-ui/or co-located with components assrc/components/**/*.{test,spec}.ts; E2E tests should be inbrowser_tests/
Files:
tests-ui/tests/base/credits/comfyCredits.test.ts
🧠 Learnings (14)
📓 Common learnings
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: tests-ui/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:48:03.270Z
Learning: Applies to tests-ui/**/*.test.{js,ts,jsx,tsx} : Write tests for new features
📚 Learning: 2025-11-24T19:48:03.270Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: tests-ui/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:48:03.270Z
Learning: Applies to tests-ui/**/*.test.{js,ts,jsx,tsx} : Write tests for new features
Applied to files:
tests-ui/tests/base/credits/comfyCredits.test.ts
📚 Learning: 2025-11-24T19:48:03.270Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: tests-ui/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:48:03.270Z
Learning: Applies to tests-ui/**/*.test.{js,ts,jsx,tsx} : Follow existing test patterns in the codebase
Applied to files:
tests-ui/tests/base/credits/comfyCredits.test.ts
📚 Learning: 2025-11-24T19:48:03.270Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: tests-ui/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:48:03.270Z
Learning: Applies to tests-ui/**/*.test.{js,ts,jsx,tsx} : Use existing test utilities rather than writing custom utilities
Applied to files:
tests-ui/tests/base/credits/comfyCredits.test.ts
📚 Learning: 2025-11-24T19:48:03.270Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: tests-ui/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:48:03.270Z
Learning: Applies to tests-ui/**/*.test.{js,ts,jsx,tsx} : Mock external dependencies in tests
Applied to files:
tests-ui/tests/base/credits/comfyCredits.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} : Follow naming conventions for browser tests
Applied to files:
tests-ui/tests/base/credits/comfyCredits.test.ts
📚 Learning: 2025-11-24T19:48:03.270Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: tests-ui/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:48:03.270Z
Learning: Check tests-ui/README.md for test guidelines
Applied to files:
tests-ui/tests/base/credits/comfyCredits.test.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} : Tests should be cross-platform compatible using `path.resolve`, `path.join`, and `path.sep` for Windows, macOS, and Linux compatibility
Applied to files:
tests-ui/tests/base/credits/comfyCredits.test.ts
📚 Learning: 2025-11-24T19:48:23.088Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-24T19:48:23.088Z
Learning: Use Vitest (with happy-dom) for unit and component tests, and Playwright for E2E tests
Applied to files:
tests-ui/tests/base/credits/comfyCredits.test.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} : Use `vitest` for unit testing in this project
Applied to files:
tests-ui/tests/base/credits/comfyCredits.test.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} : Prefer the use of `test.extend` over loose variables; import `test as baseTest` from `vitest`
Applied to files:
tests-ui/tests/base/credits/comfyCredits.test.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} : Use `test` instead of `it` for defining test cases in vitest
Applied to files:
tests-ui/tests/base/credits/comfyCredits.test.ts
📚 Learning: 2025-11-24T19:48:03.270Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: tests-ui/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:48:03.270Z
Learning: Applies to tests-ui/**/*.test.{js,ts,jsx,tsx} : Always prefer vitest mock functions over writing verbose manual mocks
Applied to files:
tests-ui/tests/base/credits/comfyCredits.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} : Use provided test helpers `createTestSubgraph` and `createTestSubgraphNode` from `./fixtures/subgraphHelpers` for consistent subgraph test setup
Applied to files:
tests-ui/tests/base/credits/comfyCredits.test.ts
🧬 Code graph analysis (1)
tests-ui/tests/base/credits/comfyCredits.test.ts (1)
src/base/credits/comfyCredits.ts (12)
COMFY_CREDIT_RATE_CENTS(27-27)COMFY_CREDIT_RATE_USD(28-28)usdToCents(30-30)formatUsdFromCents(63-67)centsToCredits(33-34)creditsToCents(36-37)usdToCredits(39-40)creditsToUsd(42-43)formatCredits(45-49)formatCreditsFromCents(51-55)formatCreditsFromUsd(57-61)formatUsd(69-73)
🪛 ESLint
tests-ui/tests/base/credits/comfyCredits.test.ts
[error] 1-1: Resolve error: EACCES: permission denied, open '/zHNWSHCQkF'
at Object.writeFileSync (node:fs:2409:20)
at l (file:///home/jailuser/git/node_modules/.pnpm/[email protected]/node_modules/get-tsconfig/dist/index.mjs:7:13670)
at createFilesMatcher (file:///home/jailuser/git/node_modules/.pnpm/[email protected]/node_modules/get-tsconfig/dist/index.mjs:7:14422)
at resolve (file:///home/jailuser/git/node_modules/.pnpm/[email protected][email protected]_@typescript-eslin_da4796079dab5a32abf73f9910d12370/node_modules/eslint-import-resolver-typescript/lib/index.js:70:65)
at Object.resolve (file:///home/jailuser/git/node_modules/.pnpm/[email protected][email protected]_@typescript-eslin_da4796079dab5a32abf73f9910d12370/node_modules/eslint-import-resolver-typescript/lib/index.js:147:20)
at file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint[email protected]_eslint@[email protected]__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/utils/resolve.js:170:69
at setRuleContext (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint-import-context/lib/index.js:23:20)
at fullResolve (file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint[email protected]_eslint@[email protected]__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/utils/resolve.js:170:30)
at relative (file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint[email protected]_eslint@[email protected]__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/utils/resolve.js:215:12)
at resolve (file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint[email protected]_eslint@[email protected]__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/utils/resolve.js:220:16)
at ExportMap.get (file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint[email protected]_eslint@[email protected]__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/utils/export-map.js:88:22)
at processBodyStatement (file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint[email protected]_eslint@[email protected]__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/rules/namespace.js:9:31)
at Program (file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint[email protected]_eslint@[email protected]__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/rules/namespace.js:100:21)
at ruleErrorHandler (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/linter.js:1173:33)
at /home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/source-code-visitor.js:76:46
at Array.forEach ()
at SourceCodeVisitor.callSync (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/source-code-visitor.js:76:30)
at /home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/source-code-traverser.js:291:18
at Array.forEach ()
at SourceCodeTraverser.traverseSync (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/source-code-traverser.js:290:10)
at runRules (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/linter.js:1214:12)
at #flatVerifyWithoutProcessors (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/linter.js:2101:4)
at Linter._verifyWithFlatConfigArrayAndWithoutProcessors (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/linter.js:2189:43)
at Linter._verifyWithFlatConfigArray (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/linter.js:2292:15)
at Linter.verify (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/linter.js:1677:10)
at Linter.verifyAndFix (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/linter.js:2557:20)
at verifyText (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/eslint/eslint-helpers.js:1179:45)
at readAndVerifyFile (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/eslint/eslint-helpers.js:1320:10)
(import-x/namespace)
[error] 1-1: Resolve error: EACCES: permission denied, open '/RgdkawmjpG'
at Object.writeFileSync (node:fs:2409:20)
at l (file:///home/jailuser/git/node_modules/.pnpm/[email protected]/node_modules/get-tsconfig/dist/index.mjs:7:13670)
at createFilesMatcher (file:///home/jailuser/git/node_modules/.pnpm/[email protected]/node_modules/get-tsconfig/dist/index.mjs:7:14422)
at resolve (file:///home/jailuser/git/node_modules/.pnpm/[email protected][email protected]_@typescript-eslin_da4796079dab5a32abf73f9910d12370/node_modules/eslint-import-resolver-typescript/lib/index.js:70:65)
at Object.resolve (file:///home/jailuser/git/node_modules/.pnpm/[email protected][email protected]_@typescript-eslin_da4796079dab5a32abf73f9910d12370/node_modules/eslint-import-resolver-typescript/lib/index.js:147:20)
at file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint[email protected]_eslint@[email protected]__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/utils/resolve.js:170:69
at setRuleContext (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint-import-context/lib/index.js:23:20)
at fullResolve (file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint[email protected]_eslint@[email protected]__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/utils/resolve.js:170:30)
at relative (file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint[email protected]_eslint@[email protected]__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/utils/resolve.js:215:12)
at resolve (file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint[email protected]_eslint@[email protected]__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/utils/resolve.js:220:16)
at checkSourceValue (file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint[email protected]_eslint@[email protected]__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/rules/no-unresolved.js:31:34)
at checkSourceValue (file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint[email protected]_eslint@[email protected]__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/utils/module-visitor.js:14:9)
at checkSource (file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint[email protected]_eslint@[email protected]__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/utils/module-visitor.js:17:9)
at ruleErrorHandler (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/linter.js:1173:33)
at /home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/source-code-visitor.js:76:46
at Array.forEach ()
at SourceCodeVisitor.callSync (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/source-code-visitor.js:76:30)
at /home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/source-code-traverser.js:291:18
at Array.forEach ()
at SourceCodeTraverser.traverseSync (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/source-code-traverser.js:290:10)
at runRules (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/linter.js:1214:12)
at #flatVerifyWithoutProcessors (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/linter.js:2101:4)
at Linter._verifyWithFlatConfigArrayAndWithoutProcessors (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/linter.js:2189:43)
at Linter._verifyWithFlatConfigArray (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/linter.js:2292:15)
at Linter.verify (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/linter.js:1677:10)
at Linter.verifyAndFix (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/linter.js:2557:20)
at verifyText (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/eslint/eslint-helpers.js:1179:45)
at readAndVerifyFile (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/eslint/eslint-helpers.js:1320:10)
(import-x/no-unresolved)
[error] 1-1: Resolve error: EACCES: permission denied, open '/VbxmqDgBZW'
at Object.writeFileSync (node:fs:2409:20)
at l (file:///home/jailuser/git/node_modules/.pnpm/[email protected]/node_modules/get-tsconfig/dist/index.mjs:7:13670)
at createFilesMatcher (file:///home/jailuser/git/node_modules/.pnpm/[email protected]/node_modules/get-tsconfig/dist/index.mjs:7:14422)
at resolve (file:///home/jailuser/git/node_modules/.pnpm/[email protected][email protected]_@typescript-eslin_da4796079dab5a32abf73f9910d12370/node_modules/eslint-import-resolver-typescript/lib/index.js:70:65)
at Object.resolve (file:///home/jailuser/git/node_modules/.pnpm/[email protected][email protected]_@typescript-eslin_da4796079dab5a32abf73f9910d12370/node_modules/eslint-import-resolver-typescript/lib/index.js:147:20)
at file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint[email protected]_eslint@[email protected]__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/utils/resolve.js:170:69
at setRuleContext (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint-import-context/lib/index.js:23:20)
at fullResolve (file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint[email protected]_eslint@[email protected]__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/utils/resolve.js:170:30)
at relative (file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint[email protected]_eslint@[email protected]__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/utils/resolve.js:215:12)
at resolve (file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint[email protected]_eslint@[email protected]__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/utils/resolve.js:220:16)
at importType (file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint[email protected]_eslint@[email protected]__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/utils/import-type.js:126:63)
at checkImportForRelativePackage (file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint[email protected]_eslint@[email protected]__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/rules/no-relative-packages.js:15:38)
at file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint[email protected]_eslint@[email protected]__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/rules/no-relative-packages.js:59:40
at checkSourceValue (file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint[email protected]_eslint@[email protected]__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/utils/module-visitor.js:14:9)
at checkSource (file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint[email protected]_eslint@[email protected]__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/utils/module-visitor.js:17:9)
at ruleErrorHandler (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/linter.js:1173:33)
at /home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/source-code-visitor.js:76:46
at Array.forEach ()
at SourceCodeVisitor.callSync (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/source-code-visitor.js:76:30)
at /home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/source-code-traverser.js:291:18
at Array.forEach ()
at SourceCodeTraverser.traverseSync (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/source-code-traverser.js:290:10)
at runRules (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/linter.js:1214:12)
at #flatVerifyWithoutProcessors (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/linter.js:2101:4)
at Linter._verifyWithFlatConfigArrayAndWithoutProcessors (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/linter.js:2189:43)
at Linter._verifyWithFlatConfigArray (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/linter.js:2292:15)
at Linter.verify (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/linter.js:1677:10)
at Linter.verifyAndFix (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/linter.js:2557:20)
at verifyText (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/eslint/eslint-helpers.js:1179:45)
at readAndVerifyFile (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/eslint/eslint-helpers.js:1320:10)
(import-x/no-relative-packages)
[error] 1-1: Unable to resolve path to module 'vitest'.
(import-x/no-unresolved)
[error] 16-16: Unable to resolve path to module '@/base/credits/comfyCredits'.
(import-x/no-unresolved)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
- GitHub Check: setup
- GitHub Check: test
- GitHub Check: lint-and-format
- GitHub Check: collect
🔇 Additional comments (2)
src/base/credits/comfyCredits.ts (2)
33-37: Asymmetric rounding may cause precision loss in round-trip conversions.
centsToCreditsreturns an unrounded result, whilecreditsToCentsrounds. This asymmetry means converting cents→credits→cents may not return the original value.Example:
const original = 211 const credits = centsToCredits(211) // 1.004761904... const backToCents = creditsToCents(credits) // Math.round(211.00...) = 211 ✓However, small floating-point errors could accumulate in chains of conversions. Consider whether this is acceptable for your use case or if both functions should round consistently.
39-44: Formatting tests assume en-US locale.The test assertions use en-US number formatting (e.g.,
'1,234.57'). These tests will fail in environments with different default locales. Consider explicitly passing a locale to ensure consistent, locale-independent test results.Wait, I need to review the test file separately. Let me skip this comment here.
47d2e14 to
ac457c6
Compare
| expect(formatCredits(1234.567, undefined, locale)).toBe('1,234.57') | ||
| expect(formatCreditsFromCents(210, undefined, locale)).toBe('1.00') | ||
| expect(formatCreditsFromUsd(4.2, undefined, locale)).toBe('2.00') | ||
| expect(formatUsd(4.2, undefined, locale)).toBe('4.20') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Having to put undefined in here makes me think we should just have an object param, wdyt?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Or, we could flip the order which would also serve to match the order in Intl.NumberFormat
ac457c6 to
e1a6407
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🧹 Nitpick comments (1)
tests-ui/tests/base/credits/comfyCredits.test.ts (1)
18-46: Consider adding edge case tests.While the current test coverage is adequate, consider adding tests for edge cases to improve robustness:
- Zero values (0 credits, 0 USD, 0 cents)
- Very large numbers (to verify no overflow issues)
- Rounding behavior at boundaries (e.g., values that round up vs down)
- Negative values (if applicable to your domain)
Example additions:
test('handles edge cases correctly', () => { const locale = 'en-US' expect(usdToCents(0)).toBe(0) expect(centsToCredits(0)).toBe(0) expect(formatUsd(0, locale)).toBe('0.00') // Test rounding behavior expect(usdToCents(1.235)).toBe(124) // rounds to nearest cent expect(usdToCents(1.234)).toBe(123) })
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
src/base/credits/comfyCredits.ts(1 hunks)tests-ui/tests/base/credits/comfyCredits.test.ts(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- src/base/credits/comfyCredits.ts
🧰 Additional context used
📓 Path-based instructions (9)
**/*.{vue,ts,tsx}
📄 CodeRabbit inference engine (.cursorrules)
**/*.{vue,ts,tsx}: Leverage VueUse functions for performance-enhancing utilities
Use vue-i18n in Composition API for any string literals and place new translation entries in src/locales/en/main.json
Files:
tests-ui/tests/base/credits/comfyCredits.test.ts
**/*.{ts,tsx,js}
📄 CodeRabbit inference engine (.cursorrules)
Use es-toolkit for utility functions
Files:
tests-ui/tests/base/credits/comfyCredits.test.ts
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursorrules)
Use TypeScript for type safety
**/*.{ts,tsx}: Never useanytype - use proper TypeScript types
Never useas anytype assertions - fix the underlying type issue
Files:
tests-ui/tests/base/credits/comfyCredits.test.ts
**/*.{ts,tsx,js,vue}
📄 CodeRabbit inference engine (.cursorrules)
Implement proper error handling in components and services
**/*.{ts,tsx,js,vue}: Use 2-space indentation, single quotes, no semicolons, and maintain 80-character line width as configured in.prettierrc
Organize imports by sorting and grouping by plugin, and runpnpm formatbefore committing
Files:
tests-ui/tests/base/credits/comfyCredits.test.ts
**/*.{ts,tsx,js,jsx,vue}
📄 CodeRabbit inference engine (CLAUDE.md)
Use camelCase for variable and setting names in TypeScript/Vue files
Files:
tests-ui/tests/base/credits/comfyCredits.test.ts
**/*.{ts,tsx,vue}
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.{ts,tsx,vue}: Useconst settingStore = useSettingStore()andsettingStore.get('Comfy.SomeSetting')to retrieve settings in TypeScript/Vue files
Useawait settingStore.set('Comfy.SomeSetting', newValue)to update settings in TypeScript/Vue files
Check server capabilities usingapi.serverSupportsFeature('feature_name')before using enhanced features
Useapi.getServerFeature('config_name', defaultValue)to retrieve server feature configurationEnforce ESLint rules for Vue + TypeScript including: no floating promises, no unused imports, and i18n raw text restrictions in templates
Files:
tests-ui/tests/base/credits/comfyCredits.test.ts
**/*.ts
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.ts: Define dynamic setting defaults using runtime context with functions in settings configuration
UsedefaultsByInstallVersionproperty for gradual feature rollout based on version in settings configuration
Files:
tests-ui/tests/base/credits/comfyCredits.test.ts
tests-ui/**/*.test.{js,ts,jsx,tsx}
📄 CodeRabbit inference engine (tests-ui/CLAUDE.md)
tests-ui/**/*.test.{js,ts,jsx,tsx}: Write tests for new features
Follow existing test patterns in the codebase
Use existing test utilities rather than writing custom utilities
Mock external dependencies in tests
Always prefer vitest mock functions over writing verbose manual mocks
Files:
tests-ui/tests/base/credits/comfyCredits.test.ts
**/*.{test,spec}.{ts,tsx,js}
📄 CodeRabbit inference engine (AGENTS.md)
Unit and component tests should be located in
tests-ui/or co-located with components assrc/components/**/*.{test,spec}.ts; E2E tests should be inbrowser_tests/
Files:
tests-ui/tests/base/credits/comfyCredits.test.ts
🧠 Learnings (14)
📓 Common learnings
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: .cursorrules:0-0
Timestamp: 2025-11-24T19:46:52.279Z
Learning: Applies to **/*.{ts,tsx,js} : Use es-toolkit for utility functions
📚 Learning: 2025-11-24T19:48:03.270Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: tests-ui/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:48:03.270Z
Learning: Applies to tests-ui/**/*.test.{js,ts,jsx,tsx} : Write tests for new features
Applied to files:
tests-ui/tests/base/credits/comfyCredits.test.ts
📚 Learning: 2025-11-24T19:48:03.270Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: tests-ui/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:48:03.270Z
Learning: Applies to tests-ui/**/*.test.{js,ts,jsx,tsx} : Follow existing test patterns in the codebase
Applied to files:
tests-ui/tests/base/credits/comfyCredits.test.ts
📚 Learning: 2025-11-24T19:48:03.270Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: tests-ui/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:48:03.270Z
Learning: Applies to tests-ui/**/*.test.{js,ts,jsx,tsx} : Use existing test utilities rather than writing custom utilities
Applied to files:
tests-ui/tests/base/credits/comfyCredits.test.ts
📚 Learning: 2025-11-24T19:48:03.270Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: tests-ui/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:48:03.270Z
Learning: Applies to tests-ui/**/*.test.{js,ts,jsx,tsx} : Mock external dependencies in tests
Applied to files:
tests-ui/tests/base/credits/comfyCredits.test.ts
📚 Learning: 2025-11-24T19:48:03.270Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: tests-ui/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:48:03.270Z
Learning: Check tests-ui/README.md for test guidelines
Applied to files:
tests-ui/tests/base/credits/comfyCredits.test.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} : Tests should be cross-platform compatible using `path.resolve`, `path.join`, and `path.sep` for Windows, macOS, and Linux compatibility
Applied to files:
tests-ui/tests/base/credits/comfyCredits.test.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} : Use `test` instead of `it` for defining test cases in vitest
Applied to files:
tests-ui/tests/base/credits/comfyCredits.test.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} : Use `vitest` for unit testing in this project
Applied to files:
tests-ui/tests/base/credits/comfyCredits.test.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} : Prefer the use of `test.extend` over loose variables; import `test as baseTest` from `vitest`
Applied to files:
tests-ui/tests/base/credits/comfyCredits.test.ts
📚 Learning: 2025-11-24T19:48:03.270Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: tests-ui/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:48:03.270Z
Learning: Applies to tests-ui/**/*.test.{js,ts,jsx,tsx} : Always prefer vitest mock functions over writing verbose manual mocks
Applied to files:
tests-ui/tests/base/credits/comfyCredits.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} : Follow naming conventions for browser tests
Applied to files:
tests-ui/tests/base/credits/comfyCredits.test.ts
📚 Learning: 2025-11-24T19:48:23.088Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-24T19:48:23.088Z
Learning: Use Vitest (with happy-dom) for unit and component tests, and Playwright for E2E tests
Applied to files:
tests-ui/tests/base/credits/comfyCredits.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} : Use provided test helpers `createTestSubgraph` and `createTestSubgraphNode` from `./fixtures/subgraphHelpers` for consistent subgraph test setup
Applied to files:
tests-ui/tests/base/credits/comfyCredits.test.ts
🧬 Code graph analysis (1)
tests-ui/tests/base/credits/comfyCredits.test.ts (1)
src/base/credits/comfyCredits.ts (12)
COMFY_CREDIT_RATE_CENTS(27-27)COMFY_CREDIT_RATE_USD(28-28)usdToCents(30-30)formatUsdFromCents(63-67)centsToCredits(33-34)creditsToCents(36-37)usdToCredits(39-40)creditsToUsd(42-43)formatCredits(45-49)formatCreditsFromCents(51-55)formatCreditsFromUsd(57-61)formatUsd(69-73)
🪛 ESLint
tests-ui/tests/base/credits/comfyCredits.test.ts
[error] 1-1: Resolve error: EACCES: permission denied, open '/XSWPeiGcoH'
at Object.writeFileSync (node:fs:2409:20)
at l (file:///home/jailuser/git/node_modules/.pnpm/[email protected]/node_modules/get-tsconfig/dist/index.mjs:7:13670)
at createFilesMatcher (file:///home/jailuser/git/node_modules/.pnpm/[email protected]/node_modules/get-tsconfig/dist/index.mjs:7:14422)
at resolve (file:///home/jailuser/git/node_modules/.pnpm/[email protected][email protected]_@typescript-eslin_da4796079dab5a32abf73f9910d12370/node_modules/eslint-import-resolver-typescript/lib/index.js:70:65)
at Object.resolve (file:///home/jailuser/git/node_modules/.pnpm/[email protected][email protected]_@typescript-eslin_da4796079dab5a32abf73f9910d12370/node_modules/eslint-import-resolver-typescript/lib/index.js:147:20)
at file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint[email protected]_eslint@[email protected]__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/utils/resolve.js:170:69
at setRuleContext (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint-import-context/lib/index.js:23:20)
at fullResolve (file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint[email protected]_eslint@[email protected]__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/utils/resolve.js:170:30)
at relative (file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint[email protected]_eslint@[email protected]__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/utils/resolve.js:215:12)
at resolve (file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint[email protected]_eslint@[email protected]__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/utils/resolve.js:220:16)
at ExportMap.get (file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint[email protected]_eslint@[email protected]__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/utils/export-map.js:88:22)
at processBodyStatement (file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint[email protected]_eslint@[email protected]__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/rules/namespace.js:9:31)
at Program (file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint[email protected]_eslint@[email protected]__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/rules/namespace.js:100:21)
at ruleErrorHandler (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/linter.js:1173:33)
at /home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/source-code-visitor.js:76:46
at Array.forEach ()
at SourceCodeVisitor.callSync (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/source-code-visitor.js:76:30)
at /home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/source-code-traverser.js:291:18
at Array.forEach ()
at SourceCodeTraverser.traverseSync (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/source-code-traverser.js:290:10)
at runRules (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/linter.js:1214:12)
at #flatVerifyWithoutProcessors (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/linter.js:2101:4)
at Linter._verifyWithFlatConfigArrayAndWithoutProcessors (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/linter.js:2189:43)
at Linter._verifyWithFlatConfigArray (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/linter.js:2292:15)
at Linter.verify (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/linter.js:1677:10)
at Linter.verifyAndFix (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/linter.js:2557:20)
at verifyText (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/eslint/eslint-helpers.js:1179:45)
at readAndVerifyFile (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/eslint/eslint-helpers.js:1320:10)
(import-x/namespace)
[error] 1-1: Resolve error: EACCES: permission denied, open '/bQNEyktcgu'
at Object.writeFileSync (node:fs:2409:20)
at l (file:///home/jailuser/git/node_modules/.pnpm/[email protected]/node_modules/get-tsconfig/dist/index.mjs:7:13670)
at createFilesMatcher (file:///home/jailuser/git/node_modules/.pnpm/[email protected]/node_modules/get-tsconfig/dist/index.mjs:7:14422)
at resolve (file:///home/jailuser/git/node_modules/.pnpm/[email protected][email protected]_@typescript-eslin_da4796079dab5a32abf73f9910d12370/node_modules/eslint-import-resolver-typescript/lib/index.js:70:65)
at Object.resolve (file:///home/jailuser/git/node_modules/.pnpm/[email protected][email protected]_@typescript-eslin_da4796079dab5a32abf73f9910d12370/node_modules/eslint-import-resolver-typescript/lib/index.js:147:20)
at file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint[email protected]_eslint@[email protected]__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/utils/resolve.js:170:69
at setRuleContext (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint-import-context/lib/index.js:23:20)
at fullResolve (file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint[email protected]_eslint@[email protected]__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/utils/resolve.js:170:30)
at relative (file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint[email protected]_eslint@[email protected]__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/utils/resolve.js:215:12)
at resolve (file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint[email protected]_eslint@[email protected]__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/utils/resolve.js:220:16)
at checkSourceValue (file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint[email protected]_eslint@[email protected]__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/rules/no-unresolved.js:31:34)
at checkSourceValue (file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint[email protected]_eslint@[email protected]__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/utils/module-visitor.js:14:9)
at checkSource (file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint[email protected]_eslint@[email protected]__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/utils/module-visitor.js:17:9)
at ruleErrorHandler (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/linter.js:1173:33)
at /home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/source-code-visitor.js:76:46
at Array.forEach ()
at SourceCodeVisitor.callSync (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/source-code-visitor.js:76:30)
at /home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/source-code-traverser.js:291:18
at Array.forEach ()
at SourceCodeTraverser.traverseSync (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/source-code-traverser.js:290:10)
at runRules (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/linter.js:1214:12)
at #flatVerifyWithoutProcessors (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/linter.js:2101:4)
at Linter._verifyWithFlatConfigArrayAndWithoutProcessors (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/linter.js:2189:43)
at Linter._verifyWithFlatConfigArray (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/linter.js:2292:15)
at Linter.verify (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/linter.js:1677:10)
at Linter.verifyAndFix (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/linter.js:2557:20)
at verifyText (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/eslint/eslint-helpers.js:1179:45)
at readAndVerifyFile (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/eslint/eslint-helpers.js:1320:10)
(import-x/no-unresolved)
[error] 1-1: Resolve error: EACCES: permission denied, open '/bmbDktuJVY'
at Object.writeFileSync (node:fs:2409:20)
at l (file:///home/jailuser/git/node_modules/.pnpm/[email protected]/node_modules/get-tsconfig/dist/index.mjs:7:13670)
at createFilesMatcher (file:///home/jailuser/git/node_modules/.pnpm/[email protected]/node_modules/get-tsconfig/dist/index.mjs:7:14422)
at resolve (file:///home/jailuser/git/node_modules/.pnpm/[email protected][email protected]_@typescript-eslin_da4796079dab5a32abf73f9910d12370/node_modules/eslint-import-resolver-typescript/lib/index.js:70:65)
at Object.resolve (file:///home/jailuser/git/node_modules/.pnpm/[email protected][email protected]_@typescript-eslin_da4796079dab5a32abf73f9910d12370/node_modules/eslint-import-resolver-typescript/lib/index.js:147:20)
at file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint[email protected]_eslint@[email protected]__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/utils/resolve.js:170:69
at setRuleContext (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint-import-context/lib/index.js:23:20)
at fullResolve (file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint[email protected]_eslint@[email protected]__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/utils/resolve.js:170:30)
at relative (file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint[email protected]_eslint@[email protected]__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/utils/resolve.js:215:12)
at resolve (file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint[email protected]_eslint@[email protected]__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/utils/resolve.js:220:16)
at importType (file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint[email protected]_eslint@[email protected]__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/utils/import-type.js:126:63)
at checkImportForRelativePackage (file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint[email protected]_eslint@[email protected]__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/rules/no-relative-packages.js:15:38)
at file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint[email protected]_eslint@[email protected]__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/rules/no-relative-packages.js:59:40
at checkSourceValue (file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint[email protected]_eslint@[email protected]__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/utils/module-visitor.js:14:9)
at checkSource (file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint[email protected]_eslint@[email protected]__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/utils/module-visitor.js:17:9)
at ruleErrorHandler (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/linter.js:1173:33)
at /home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/source-code-visitor.js:76:46
at Array.forEach ()
at SourceCodeVisitor.callSync (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/source-code-visitor.js:76:30)
at /home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/source-code-traverser.js:291:18
at Array.forEach ()
at SourceCodeTraverser.traverseSync (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/source-code-traverser.js:290:10)
at runRules (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/linter.js:1214:12)
at #flatVerifyWithoutProcessors (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/linter.js:2101:4)
at Linter._verifyWithFlatConfigArrayAndWithoutProcessors (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/linter.js:2189:43)
at Linter._verifyWithFlatConfigArray (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/linter.js:2292:15)
at Linter.verify (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/linter.js:1677:10)
at Linter.verifyAndFix (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/linter.js:2557:20)
at verifyText (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/eslint/eslint-helpers.js:1179:45)
at readAndVerifyFile (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/eslint/eslint-helpers.js:1320:10)
(import-x/no-relative-packages)
[error] 1-1: Unable to resolve path to module 'vitest'.
(import-x/no-unresolved)
[error] 16-16: Unable to resolve path to module '@/base/credits/comfyCredits'.
(import-x/no-unresolved)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
- GitHub Check: deploy-and-comment
- GitHub Check: test
- GitHub Check: setup
- GitHub Check: collect
- GitHub Check: lint-and-format
🔇 Additional comments (5)
tests-ui/tests/base/credits/comfyCredits.test.ts (5)
1-16: Imports look good.The imports follow project guidelines by using
testfrom vitest and correctly import all necessary functions from the comfyCredits module.
19-22: Constant verification is correct.The test properly verifies both conversion rate constants and uses
toBeCloseTofor floating-point comparison.
29-32: Conversion logic is verified correctly.The tests properly verify bidirectional conversion between cents and credits with appropriate assertions.
34-37: USD/credits conversions are correct.The bidirectional conversion tests properly verify the math: 2.1 USD = 210 cents = 1 credit, and 3.5 credits = 735 cents = 7.35 USD.
39-45: Formatting tests with explicit locale look good.The explicit
'en-US'locale parameter ensures consistent test results across different environments, addressing previous review feedback.
e1a6407 to
ca13f55
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (4)
src/base/credits/comfyCredits.ts (1)
1-29: formatNumber guard may surprise callers when minFractionDigits > default maxThe min/max guard prevents Intl from throwing, which is good, but in the case where a caller sets
minimumFractionDigitshigher than the defaultmaximumFractionDigits, the current fix (minimum = maximum) silently drops the requested precision. You may want to instead raisemaximumFractionDigitstominimumFractionDigits(or let Intl throw) so that explicit caller intent for more decimals is preserved.- if ( - typeof merged.maximumFractionDigits === 'number' && - typeof merged.minimumFractionDigits === 'number' && - merged.maximumFractionDigits < merged.minimumFractionDigits - ) { - merged.minimumFractionDigits = merged.maximumFractionDigits - } + if ( + typeof merged.maximumFractionDigits === 'number' && + typeof merged.minimumFractionDigits === 'number' && + merged.maximumFractionDigits < merged.minimumFractionDigits + ) { + merged.maximumFractionDigits = merged.minimumFractionDigits + }src/components/common/UserCredit.vue (1)
35-38: Confirm units foramount_microsand consider localizing the"credits"suffixThis computed assumes
authStore.balance?.amount_microsis already in cents. If it is still in micros (as the name suggests), this will inflate displayed credits by 10,000×. Please double‑check the backend/SDK type foramount_microsand, if it’s micros, convert to cents before callingformatCreditsFromCents.Also, per the project i18n guidelines, the
'credits'suffix should ideally come from vue‑i18n rather than being hard‑coded in the computed string (e.g., use a translation likecredits.balanceLabelwith the formatted number as an interpolation parameter).src/components/dialog/content/credit/CreditTopUpOption.vue (1)
9-28: Tighten display amount handling and centralize USD/credits labelingThe editable/non‑editable display logic is generally solid, but a few details are worth tightening up:
displayUsdAmountusesMath.max(1, editable ? customAmount.value : amount). In edge cases whereamountorcustomAmountcould be0or negative (e.g., transient InputNumber states), the displayed USD/credits would be clamped to1whilehandleBuyNowstill uses the rawamount/customAmount. If that’s a realistic scenario, consider aligning the purchase amount withdisplayUsdAmountor clamping earlier (e.g., in the ref) so display and behavior always match.formattedUsdmanually builds a$${...toFixed(2)}string usingusdToCents. Since you already haveformatUsd/formatUsdFromCentsincomfyCredits.ts, it may be cleaner and more consistent to use those helpers for USD formatting instead of re‑implementing the pattern here.- As in other files, the
"credits"suffix is hard‑coded informattedCredits. To follow the i18n guidance, consider moving this to a vue‑i18n key (e.g.,credits.topUp.optionLabel) and interpolating the formatted numeric part.Also applies to: 72-83
src/platform/cloud/subscription/composables/useSubscriptionCredits.ts (1)
3-14: Verify balance units and consider i18n for the"credits"suffix
formatBalanceassumes the*_microsfields fromauthStore.balanceare already in cents (maybeCents). If those fields are still micros, this will vastly overstate the user’s credit amounts. Please confirm the unit ofamount_micros,cloud_credit_balance_micros, andprepaid_balance_microsand, if needed, convert micros → cents before callingformatCreditsFromCents.Since
formatBalanceproduces user-facing strings (including' credits'), it would also be more in line with the i18n guidelines to move the suffix into a vue‑i18n translation (e.g.,credits.subscription.balance) and let components or composables interpolate the numeric portion.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (5)
src/base/credits/comfyCredits.ts(1 hunks)src/components/common/UserCredit.vue(3 hunks)src/components/dialog/content/credit/CreditTopUpOption.vue(3 hunks)src/platform/cloud/subscription/composables/useSubscriptionCredits.ts(1 hunks)tests-ui/tests/base/credits/comfyCredits.test.ts(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- tests-ui/tests/base/credits/comfyCredits.test.ts
🧰 Additional context used
📓 Path-based instructions (21)
**/*.vue
📄 CodeRabbit inference engine (.cursorrules)
**/*.vue: Use setup() function for component logic in Vue 3 Composition API
Utilize ref and reactive for reactive state in Vue 3
Implement computed properties with computed() function
Use watch and watchEffect for side effects in Vue 3
Implement lifecycle hooks with onMounted, onUpdated, etc.
Utilize provide/inject for dependency injection in Vue 3
Use Vue 3.5 style of default prop declaration with defineProps()
Organize Vue components in <script> <style> order
Use Tailwind CSS for styling Vue components
Implement responsive design with Tailwind CSS
Do not use deprecated PrimeVue components (Dropdown, OverlayPanel, Calendar, InputSwitch, Sidebar, Chips, TabMenu, Steps, InlineMessage). Use replacements: Select, Popover, DatePicker, ToggleSwitch, Drawer, AutoComplete, Tabs, Stepper, Message respectively
Implement proper props and emits definitions in Vue components
Utilize Vue 3's Teleport component when needed
Use Suspense for async components in Vue 3
Follow Vue 3 style guide and naming conventions
Never use deprecated PrimeVue components (Dropdown, OverlayPanel, Calendar, InputSwitch, Sidebar, Chips, TabMenu, Steps, InlineMessage)Never use
:class="[]"to merge class names - always useimport { cn } from '@/utils/tailwindUtil'for class merging in Vue templates
**/*.vue: Use TypeScript with Vue 3 Single File Components (.vuefiles)
Name Vue components in PascalCase (e.g.,MenuHamburger.vue)Files:
src/components/dialog/content/credit/CreditTopUpOption.vuesrc/components/common/UserCredit.vue**/*.{vue,ts,tsx}
📄 CodeRabbit inference engine (.cursorrules)
**/*.{vue,ts,tsx}: Leverage VueUse functions for performance-enhancing utilities
Use vue-i18n in Composition API for any string literals and place new translation entries in src/locales/en/main.jsonFiles:
src/components/dialog/content/credit/CreditTopUpOption.vuesrc/components/common/UserCredit.vuesrc/platform/cloud/subscription/composables/useSubscriptionCredits.tssrc/base/credits/comfyCredits.ts**/*.{ts,tsx,js,vue}
📄 CodeRabbit inference engine (.cursorrules)
Implement proper error handling in components and services
**/*.{ts,tsx,js,vue}: Use 2-space indentation, single quotes, no semicolons, and maintain 80-character line width as configured in.prettierrc
Organize imports by sorting and grouping by plugin, and runpnpm formatbefore committingFiles:
src/components/dialog/content/credit/CreditTopUpOption.vuesrc/components/common/UserCredit.vuesrc/platform/cloud/subscription/composables/useSubscriptionCredits.tssrc/base/credits/comfyCredits.tssrc/**/*.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 conventionsFiles:
src/components/dialog/content/credit/CreditTopUpOption.vuesrc/components/common/UserCredit.vuesrc/**/*.{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.jsonFiles:
src/components/dialog/content/credit/CreditTopUpOption.vuesrc/components/common/UserCredit.vuesrc/platform/cloud/subscription/composables/useSubscriptionCredits.tssrc/base/credits/comfyCredits.ts**/*.{ts,tsx,js,jsx,vue}
📄 CodeRabbit inference engine (CLAUDE.md)
Use camelCase for variable and setting names in TypeScript/Vue files
Files:
src/components/dialog/content/credit/CreditTopUpOption.vuesrc/components/common/UserCredit.vuesrc/platform/cloud/subscription/composables/useSubscriptionCredits.tssrc/base/credits/comfyCredits.ts**/*.{vue,html}
📄 CodeRabbit inference engine (CLAUDE.md)
Never use
dark:ordark-theme:Tailwind variants - instead use semantic values fromstyle.csstheme, e.g.bg-node-component-surfaceFiles:
src/components/dialog/content/credit/CreditTopUpOption.vuesrc/components/common/UserCredit.vue**/*.{ts,tsx,vue}
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.{ts,tsx,vue}: Useconst settingStore = useSettingStore()andsettingStore.get('Comfy.SomeSetting')to retrieve settings in TypeScript/Vue files
Useawait settingStore.set('Comfy.SomeSetting', newValue)to update settings in TypeScript/Vue files
Check server capabilities usingapi.serverSupportsFeature('feature_name')before using enhanced features
Useapi.getServerFeature('config_name', defaultValue)to retrieve server feature configurationEnforce ESLint rules for Vue + TypeScript including: no floating promises, no unused imports, and i18n raw text restrictions in templates
Files:
src/components/dialog/content/credit/CreditTopUpOption.vuesrc/components/common/UserCredit.vuesrc/platform/cloud/subscription/composables/useSubscriptionCredits.tssrc/base/credits/comfyCredits.tssrc/**/*.{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 codebaseFiles:
src/components/dialog/content/credit/CreditTopUpOption.vuesrc/components/common/UserCredit.vuesrc/platform/cloud/subscription/composables/useSubscriptionCredits.tssrc/base/credits/comfyCredits.tssrc/**/{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/credit/CreditTopUpOption.vuesrc/components/common/UserCredit.vuesrc/platform/cloud/subscription/composables/useSubscriptionCredits.tssrc/**/*.{vue,ts,tsx}
📄 CodeRabbit inference engine (src/CLAUDE.md)
Follow Vue 3 composition API style guide
Files:
src/components/dialog/content/credit/CreditTopUpOption.vuesrc/components/common/UserCredit.vuesrc/platform/cloud/subscription/composables/useSubscriptionCredits.tssrc/base/credits/comfyCredits.tssrc/**/{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.jsonFiles:
src/components/dialog/content/credit/CreditTopUpOption.vuesrc/components/common/UserCredit.vuesrc/platform/cloud/subscription/composables/useSubscriptionCredits.tssrc/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 componentsFiles:
src/components/dialog/content/credit/CreditTopUpOption.vuesrc/components/common/UserCredit.vuesrc/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 packageFiles:
src/components/dialog/content/credit/CreditTopUpOption.vuesrc/components/common/UserCredit.vuesrc/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 stringsFiles:
src/components/dialog/content/credit/CreditTopUpOption.vuesrc/components/common/UserCredit.vue**/*.{ts,tsx,js}
📄 CodeRabbit inference engine (.cursorrules)
Use es-toolkit for utility functions
Files:
src/platform/cloud/subscription/composables/useSubscriptionCredits.tssrc/base/credits/comfyCredits.ts**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursorrules)
Use TypeScript for type safety
**/*.{ts,tsx}: Never useanytype - use proper TypeScript types
Never useas anytype assertions - fix the underlying type issueFiles:
src/platform/cloud/subscription/composables/useSubscriptionCredits.tssrc/base/credits/comfyCredits.tssrc/**/*.ts
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
src/**/*.ts: Use es-toolkit for utility functions
Use TypeScript for type safetyFiles:
src/platform/cloud/subscription/composables/useSubscriptionCredits.tssrc/base/credits/comfyCredits.ts**/*.ts
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.ts: Define dynamic setting defaults using runtime context with functions in settings configuration
UsedefaultsByInstallVersionproperty for gradual feature rollout based on version in settings configurationFiles:
src/platform/cloud/subscription/composables/useSubscriptionCredits.tssrc/base/credits/comfyCredits.tssrc/**/{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 directlyFiles:
src/platform/cloud/subscription/composables/useSubscriptionCredits.ts**/composables/use*.ts
📄 CodeRabbit inference engine (AGENTS.md)
Name composables in the format
useXyz.tsFiles:
src/platform/cloud/subscription/composables/useSubscriptionCredits.ts🧠 Learnings (12)
📚 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 InputSwitch component with ToggleSwitchApplied to files:
src/components/dialog/content/credit/CreditTopUpOption.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 : Replace PrimeVue Calendar component with DatePickerApplied to files:
src/components/dialog/content/credit/CreditTopUpOption.vue📚 Learning: 2025-11-24T19:46:52.279Z
Learnt from: CR Repo: Comfy-Org/ComfyUI_frontend PR: 0 File: .cursorrules:0-0 Timestamp: 2025-11-24T19:46:52.279Z Learning: Applies to **/*.vue : Do not use deprecated PrimeVue components (Dropdown, OverlayPanel, Calendar, InputSwitch, Sidebar, Chips, TabMenu, Steps, InlineMessage). Use replacements: Select, Popover, DatePicker, ToggleSwitch, Drawer, AutoComplete, Tabs, Stepper, Message respectivelyApplied to files:
src/components/dialog/content/credit/CreditTopUpOption.vuesrc/components/common/UserCredit.vue📚 Learning: 2025-11-24T19:46:52.279Z
Learnt from: CR Repo: Comfy-Org/ComfyUI_frontend PR: 0 File: .cursorrules:0-0 Timestamp: 2025-11-24T19:46:52.279Z Learning: Applies to **/*.vue : Never use deprecated PrimeVue components (Dropdown, OverlayPanel, Calendar, InputSwitch, Sidebar, Chips, TabMenu, Steps, InlineMessage)Applied to files:
src/components/dialog/content/credit/CreditTopUpOption.vue📚 Learning: 2025-11-24T19:46:52.279Z
Learnt from: CR Repo: Comfy-Org/ComfyUI_frontend PR: 0 File: .cursorrules:0-0 Timestamp: 2025-11-24T19:46:52.279Z Learning: Applies to **/*.vue : Utilize ref and reactive for reactive state in Vue 3Applied to files:
src/components/dialog/content/credit/CreditTopUpOption.vue📚 Learning: 2025-11-24T19:46:52.279Z
Learnt from: CR Repo: Comfy-Org/ComfyUI_frontend PR: 0 File: .cursorrules:0-0 Timestamp: 2025-11-24T19:46:52.279Z Learning: Applies to **/*.{vue,ts,tsx} : Leverage VueUse functions for performance-enhancing utilitiesApplied to files:
src/components/dialog/content/credit/CreditTopUpOption.vuesrc/components/common/UserCredit.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 : Utilize ref and reactive for reactive stateApplied to files:
src/components/dialog/content/credit/CreditTopUpOption.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 : Use ref/reactive for state management in Vue 3 Composition APIApplied to files:
src/components/dialog/content/credit/CreditTopUpOption.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 : Replace PrimeVue Steps component with Stepper without panelsApplied to files:
src/components/dialog/content/credit/CreditTopUpOption.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} : Leverage VueUse functions for performance-enhancing stylesApplied to files:
src/components/dialog/content/credit/CreditTopUpOption.vuesrc/components/common/UserCredit.vue📚 Learning: 2025-11-24T19:46:52.279Z
Learnt from: CR Repo: Comfy-Org/ComfyUI_frontend PR: 0 File: .cursorrules:0-0 Timestamp: 2025-11-24T19:46:52.279Z Learning: Applies to **/*.vue : Implement computed properties with computed() functionApplied to files:
src/components/common/UserCredit.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 : Implement computed() for derived state in Vue 3 Composition APIApplied to files:
src/platform/cloud/subscription/composables/useSubscriptionCredits.ts🧬 Code graph analysis (1)
src/platform/cloud/subscription/composables/useSubscriptionCredits.ts (2)
src/stores/firebaseAuthStore.ts (1)
useFirebaseAuthStore(53-473)src/base/credits/comfyCredits.ts (1)
formatCreditsFromCents(61-74)⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
- GitHub Check: setup
- GitHub Check: lint-and-format
- GitHub Check: collect
- GitHub Check: test
🔇 Additional comments (5)
src/base/credits/comfyCredits.ts (2)
31-47: Conversion helpers look consistent and numerically saneThe cents↔USD↔credits helpers (
usdToCents,centsToCredits,creditsToCents,usdToCredits,creditsToUsd) are internally consistent, use integer cents as the base, and correctly apply rounding where needed. This should give stable round‑trips at cent precision for realistic values.
48-115: Formatting wrappers provide a clean, typed surface over Intl
FormatOptionsplus theformatCredits*/formatUsd*functions are nicely typed and keep formatting concerns (locale, options) decoupled from conversion concerns. The sharedformatNumbercore should make future tweaks to default fraction digits or locales straightforward.src/components/common/UserCredit.vue (1)
10-15: Wallet icon change is consistent with credit semanticsSwitching the Tag icon to
pi pi-walletbetter matches the credit/balance concept and keeps visuals aligned with the other updated credit UIs.src/components/dialog/content/credit/CreditTopUpOption.vue (1)
3-8: Wallet icon matches other credit-related componentsThe Tag icon update to
pi pi-walletkeeps this top-up option visually in sync with the rest of the credit UI.src/platform/cloud/subscription/composables/useSubscriptionCredits.ts (1)
15-49: Computed credit strings with fallback behavior look goodThe three computed values (
totalCredits,monthlyBonusCredits,prepaidCredits) consistently useformatBalance, catch formatting errors, log a descriptive message, and fall back to formatting0credits. This should make the UI resilient to missing/invalid balance data while still surfacing issues in the console.
ca13f55 to
d1006dd
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
♻️ Duplicate comments (2)
tests-ui/tests/components/dialog/content/credit/CreditTopUpOption.test.ts (1)
1-3: Prefertestoveritin new Vitest suitesProject unit-test guidelines call for
testinstead ofit. For consistency with other updated specs (e.g., comfyCredits tests), consider importingtestfromvitestand renaming these cases accordingly.Also applies to: 53-66
tests-ui/tests/platform/cloud/subscription/composables/useSubscriptionCredits.test.ts (1)
2-2: Align ontestinstead ofitfor Vitest casesThis spec still imports and uses
iteven though the project test rules prefertest. For consistency withcomfyCredits.test.tsand future maintenance, consider switching to:-import { beforeEach, describe, expect, it, vi } from 'vitest' +import { beforeEach, describe, expect, test, vi } from 'vitest'and renaming each
it(totest(.Also applies to: 78-147
🧹 Nitpick comments (1)
tests-ui/tests/platform/cloud/subscription/composables/useSubscriptionCredits.test.ts (1)
80-96: Avoidas anyon mocked balances where possibleSeveral expectations rely on
authStore.balance = { ... } as any. If there’s a balance type exported from the store (or a shared interface), consider using a more specific type (e.g.,Partial<BalanceType>) instead ofas anyto keep these tests type-safe while still minimal.Also applies to: 111-121, 125-135
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (8)
src/base/credits/comfyCredits.ts(1 hunks)src/components/common/UserCredit.vue(2 hunks)src/components/dialog/content/credit/CreditTopUpOption.vue(3 hunks)src/platform/cloud/subscription/composables/useSubscriptionCredits.ts(1 hunks)tests-ui/tests/base/credits/comfyCredits.test.ts(1 hunks)tests-ui/tests/components/dialog/content/credit/CreditTopUpOption.test.ts(1 hunks)tests-ui/tests/platform/cloud/subscription/components/SubscriptionPanel.test.ts(2 hunks)tests-ui/tests/platform/cloud/subscription/composables/useSubscriptionCredits.test.ts(2 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
- src/base/credits/comfyCredits.ts
- src/platform/cloud/subscription/composables/useSubscriptionCredits.ts
🧰 Additional context used
📓 Path-based instructions (21)
**/*.{vue,ts,tsx}
📄 CodeRabbit inference engine (.cursorrules)
**/*.{vue,ts,tsx}: Leverage VueUse functions for performance-enhancing utilities
Use vue-i18n in Composition API for any string literals and place new translation entries in src/locales/en/main.json
Files:
tests-ui/tests/platform/cloud/subscription/components/SubscriptionPanel.test.tstests-ui/tests/components/dialog/content/credit/CreditTopUpOption.test.tssrc/components/dialog/content/credit/CreditTopUpOption.vuetests-ui/tests/base/credits/comfyCredits.test.tstests-ui/tests/platform/cloud/subscription/composables/useSubscriptionCredits.test.tssrc/components/common/UserCredit.vue
**/*.{ts,tsx,js}
📄 CodeRabbit inference engine (.cursorrules)
Use es-toolkit for utility functions
Files:
tests-ui/tests/platform/cloud/subscription/components/SubscriptionPanel.test.tstests-ui/tests/components/dialog/content/credit/CreditTopUpOption.test.tstests-ui/tests/base/credits/comfyCredits.test.tstests-ui/tests/platform/cloud/subscription/composables/useSubscriptionCredits.test.ts
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursorrules)
Use TypeScript for type safety
**/*.{ts,tsx}: Never useanytype - use proper TypeScript types
Never useas anytype assertions - fix the underlying type issue
Files:
tests-ui/tests/platform/cloud/subscription/components/SubscriptionPanel.test.tstests-ui/tests/components/dialog/content/credit/CreditTopUpOption.test.tstests-ui/tests/base/credits/comfyCredits.test.tstests-ui/tests/platform/cloud/subscription/composables/useSubscriptionCredits.test.ts
**/*.{ts,tsx,js,vue}
📄 CodeRabbit inference engine (.cursorrules)
Implement proper error handling in components and services
**/*.{ts,tsx,js,vue}: Use 2-space indentation, single quotes, no semicolons, and maintain 80-character line width as configured in.prettierrc
Organize imports by sorting and grouping by plugin, and runpnpm formatbefore committing
Files:
tests-ui/tests/platform/cloud/subscription/components/SubscriptionPanel.test.tstests-ui/tests/components/dialog/content/credit/CreditTopUpOption.test.tssrc/components/dialog/content/credit/CreditTopUpOption.vuetests-ui/tests/base/credits/comfyCredits.test.tstests-ui/tests/platform/cloud/subscription/composables/useSubscriptionCredits.test.tssrc/components/common/UserCredit.vue
**/*.{ts,tsx,js,jsx,vue}
📄 CodeRabbit inference engine (CLAUDE.md)
Use camelCase for variable and setting names in TypeScript/Vue files
Files:
tests-ui/tests/platform/cloud/subscription/components/SubscriptionPanel.test.tstests-ui/tests/components/dialog/content/credit/CreditTopUpOption.test.tssrc/components/dialog/content/credit/CreditTopUpOption.vuetests-ui/tests/base/credits/comfyCredits.test.tstests-ui/tests/platform/cloud/subscription/composables/useSubscriptionCredits.test.tssrc/components/common/UserCredit.vue
**/*.{ts,tsx,vue}
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.{ts,tsx,vue}: Useconst settingStore = useSettingStore()andsettingStore.get('Comfy.SomeSetting')to retrieve settings in TypeScript/Vue files
Useawait settingStore.set('Comfy.SomeSetting', newValue)to update settings in TypeScript/Vue files
Check server capabilities usingapi.serverSupportsFeature('feature_name')before using enhanced features
Useapi.getServerFeature('config_name', defaultValue)to retrieve server feature configurationEnforce ESLint rules for Vue + TypeScript including: no floating promises, no unused imports, and i18n raw text restrictions in templates
Files:
tests-ui/tests/platform/cloud/subscription/components/SubscriptionPanel.test.tstests-ui/tests/components/dialog/content/credit/CreditTopUpOption.test.tssrc/components/dialog/content/credit/CreditTopUpOption.vuetests-ui/tests/base/credits/comfyCredits.test.tstests-ui/tests/platform/cloud/subscription/composables/useSubscriptionCredits.test.tssrc/components/common/UserCredit.vue
**/*.ts
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.ts: Define dynamic setting defaults using runtime context with functions in settings configuration
UsedefaultsByInstallVersionproperty for gradual feature rollout based on version in settings configuration
Files:
tests-ui/tests/platform/cloud/subscription/components/SubscriptionPanel.test.tstests-ui/tests/components/dialog/content/credit/CreditTopUpOption.test.tstests-ui/tests/base/credits/comfyCredits.test.tstests-ui/tests/platform/cloud/subscription/composables/useSubscriptionCredits.test.ts
tests-ui/**/*.test.{js,ts,jsx,tsx}
📄 CodeRabbit inference engine (tests-ui/CLAUDE.md)
tests-ui/**/*.test.{js,ts,jsx,tsx}: Write tests for new features
Follow existing test patterns in the codebase
Use existing test utilities rather than writing custom utilities
Mock external dependencies in tests
Always prefer vitest mock functions over writing verbose manual mocks
Files:
tests-ui/tests/platform/cloud/subscription/components/SubscriptionPanel.test.tstests-ui/tests/components/dialog/content/credit/CreditTopUpOption.test.tstests-ui/tests/base/credits/comfyCredits.test.tstests-ui/tests/platform/cloud/subscription/composables/useSubscriptionCredits.test.ts
**/*.{test,spec}.{ts,tsx,js}
📄 CodeRabbit inference engine (AGENTS.md)
Unit and component tests should be located in
tests-ui/or co-located with components assrc/components/**/*.{test,spec}.ts; E2E tests should be inbrowser_tests/
Files:
tests-ui/tests/platform/cloud/subscription/components/SubscriptionPanel.test.tstests-ui/tests/components/dialog/content/credit/CreditTopUpOption.test.tstests-ui/tests/base/credits/comfyCredits.test.tstests-ui/tests/platform/cloud/subscription/composables/useSubscriptionCredits.test.ts
**/*.vue
📄 CodeRabbit inference engine (.cursorrules)
**/*.vue: Use setup() function for component logic in Vue 3 Composition API
Utilize ref and reactive for reactive state in Vue 3
Implement computed properties with computed() function
Use watch and watchEffect for side effects in Vue 3
Implement lifecycle hooks with onMounted, onUpdated, etc.
Utilize provide/inject for dependency injection in Vue 3
Use Vue 3.5 style of default prop declaration with defineProps()
Organize Vue components in <script> <style> order
Use Tailwind CSS for styling Vue components
Implement responsive design with Tailwind CSS
Do not use deprecated PrimeVue components (Dropdown, OverlayPanel, Calendar, InputSwitch, Sidebar, Chips, TabMenu, Steps, InlineMessage). Use replacements: Select, Popover, DatePicker, ToggleSwitch, Drawer, AutoComplete, Tabs, Stepper, Message respectively
Implement proper props and emits definitions in Vue components
Utilize Vue 3's Teleport component when needed
Use Suspense for async components in Vue 3
Follow Vue 3 style guide and naming conventions
Never use deprecated PrimeVue components (Dropdown, OverlayPanel, Calendar, InputSwitch, Sidebar, Chips, TabMenu, Steps, InlineMessage)Never use
:class="[]"to merge class names - always useimport { cn } from '@/utils/tailwindUtil'for class merging in Vue templates
**/*.vue: Use TypeScript with Vue 3 Single File Components (.vuefiles)
Name Vue components in PascalCase (e.g.,MenuHamburger.vue)Files:
src/components/dialog/content/credit/CreditTopUpOption.vuesrc/components/common/UserCredit.vuesrc/**/*.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 conventionsFiles:
src/components/dialog/content/credit/CreditTopUpOption.vuesrc/components/common/UserCredit.vuesrc/**/*.{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.jsonFiles:
src/components/dialog/content/credit/CreditTopUpOption.vuesrc/components/common/UserCredit.vue**/*.{vue,html}
📄 CodeRabbit inference engine (CLAUDE.md)
Never use
dark:ordark-theme:Tailwind variants - instead use semantic values fromstyle.csstheme, e.g.bg-node-component-surfaceFiles:
src/components/dialog/content/credit/CreditTopUpOption.vuesrc/components/common/UserCredit.vuesrc/**/*.{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 codebaseFiles:
src/components/dialog/content/credit/CreditTopUpOption.vuesrc/components/common/UserCredit.vuesrc/**/{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/credit/CreditTopUpOption.vuesrc/components/common/UserCredit.vuesrc/**/*.{vue,ts,tsx}
📄 CodeRabbit inference engine (src/CLAUDE.md)
Follow Vue 3 composition API style guide
Files:
src/components/dialog/content/credit/CreditTopUpOption.vuesrc/components/common/UserCredit.vuesrc/**/{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.jsonFiles:
src/components/dialog/content/credit/CreditTopUpOption.vuesrc/components/common/UserCredit.vuesrc/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 componentsFiles:
src/components/dialog/content/credit/CreditTopUpOption.vuesrc/components/common/UserCredit.vuesrc/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 packageFiles:
src/components/dialog/content/credit/CreditTopUpOption.vuesrc/components/common/UserCredit.vuesrc/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 stringsFiles:
src/components/dialog/content/credit/CreditTopUpOption.vuesrc/components/common/UserCredit.vue**/composables/use*.ts
📄 CodeRabbit inference engine (AGENTS.md)
Name composables in the format
useXyz.tsFiles:
tests-ui/tests/platform/cloud/subscription/composables/useSubscriptionCredits.test.ts🧠 Learnings (32)
📓 Common learnings
Learnt from: CR Repo: Comfy-Org/ComfyUI_frontend PR: 0 File: tests-ui/CLAUDE.md:0-0 Timestamp: 2025-11-24T19:48:03.270Z Learning: Applies to tests-ui/**/*.test.{js,ts,jsx,tsx} : Write tests for new features📚 Learning: 2025-11-24T19:48:03.270Z
Learnt from: CR Repo: Comfy-Org/ComfyUI_frontend PR: 0 File: tests-ui/CLAUDE.md:0-0 Timestamp: 2025-11-24T19:48:03.270Z Learning: Applies to tests-ui/**/*.test.{js,ts,jsx,tsx} : Write tests for new featuresApplied to files:
tests-ui/tests/platform/cloud/subscription/components/SubscriptionPanel.test.tstests-ui/tests/components/dialog/content/credit/CreditTopUpOption.test.tstests-ui/tests/base/credits/comfyCredits.test.tstests-ui/tests/platform/cloud/subscription/composables/useSubscriptionCredits.test.ts📚 Learning: 2025-11-24T19:48:03.270Z
Learnt from: CR Repo: Comfy-Org/ComfyUI_frontend PR: 0 File: tests-ui/CLAUDE.md:0-0 Timestamp: 2025-11-24T19:48:03.270Z Learning: Applies to tests-ui/**/*.test.{js,ts,jsx,tsx} : Follow existing test patterns in the codebaseApplied to files:
tests-ui/tests/components/dialog/content/credit/CreditTopUpOption.test.tstests-ui/tests/base/credits/comfyCredits.test.ts📚 Learning: 2025-11-24T19:48:03.270Z
Learnt from: CR Repo: Comfy-Org/ComfyUI_frontend PR: 0 File: tests-ui/CLAUDE.md:0-0 Timestamp: 2025-11-24T19:48:03.270Z Learning: Applies to tests-ui/**/*.test.{js,ts,jsx,tsx} : Mock external dependencies in testsApplied to files:
tests-ui/tests/components/dialog/content/credit/CreditTopUpOption.test.tstests-ui/tests/platform/cloud/subscription/composables/useSubscriptionCredits.test.ts📚 Learning: 2025-11-24T19:48:03.270Z
Learnt from: CR Repo: Comfy-Org/ComfyUI_frontend PR: 0 File: tests-ui/CLAUDE.md:0-0 Timestamp: 2025-11-24T19:48:03.270Z Learning: Applies to tests-ui/**/*.test.{js,ts,jsx,tsx} : Use existing test utilities rather than writing custom utilitiesApplied to files:
tests-ui/tests/components/dialog/content/credit/CreditTopUpOption.test.tstests-ui/tests/base/credits/comfyCredits.test.tstests-ui/tests/platform/cloud/subscription/composables/useSubscriptionCredits.test.ts📚 Learning: 2025-11-24T19:48:03.270Z
Learnt from: CR Repo: Comfy-Org/ComfyUI_frontend PR: 0 File: tests-ui/CLAUDE.md:0-0 Timestamp: 2025-11-24T19:48:03.270Z Learning: Applies to tests-ui/**/*.test.{js,ts,jsx,tsx} : Always prefer vitest mock functions over writing verbose manual mocksApplied to files:
tests-ui/tests/components/dialog/content/credit/CreditTopUpOption.test.tstests-ui/tests/base/credits/comfyCredits.test.tstests-ui/tests/platform/cloud/subscription/composables/useSubscriptionCredits.test.ts📚 Learning: 2025-11-24T19:48:03.270Z
Learnt from: CR Repo: Comfy-Org/ComfyUI_frontend PR: 0 File: tests-ui/CLAUDE.md:0-0 Timestamp: 2025-11-24T19:48:03.270Z Learning: Check tests-ui/README.md for test guidelinesApplied to files:
tests-ui/tests/components/dialog/content/credit/CreditTopUpOption.test.tstests-ui/tests/base/credits/comfyCredits.test.ts📚 Learning: 2025-11-24T19:48:23.088Z
Learnt from: CR Repo: Comfy-Org/ComfyUI_frontend PR: 0 File: AGENTS.md:0-0 Timestamp: 2025-11-24T19:48:23.088Z Learning: Applies to **/*.{test,spec}.{ts,tsx,js} : Unit and component tests should be located in `tests-ui/` or co-located with components as `src/components/**/*.{test,spec}.ts`; E2E tests should be in `browser_tests/`Applied to files:
tests-ui/tests/components/dialog/content/credit/CreditTopUpOption.test.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} : Use `vitest` for unit testing in this projectApplied to files:
tests-ui/tests/components/dialog/content/credit/CreditTopUpOption.test.tstests-ui/tests/base/credits/comfyCredits.test.tstests-ui/tests/platform/cloud/subscription/composables/useSubscriptionCredits.test.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 possibleApplied to files:
tests-ui/tests/components/dialog/content/credit/CreditTopUpOption.test.tstests-ui/tests/platform/cloud/subscription/composables/useSubscriptionCredits.test.ts📚 Learning: 2025-11-24T19:48:23.088Z
Learnt from: CR Repo: Comfy-Org/ComfyUI_frontend PR: 0 File: AGENTS.md:0-0 Timestamp: 2025-11-24T19:48:23.088Z Learning: Use Vitest (with happy-dom) for unit and component tests, and Playwright for E2E testsApplied to files:
tests-ui/tests/components/dialog/content/credit/CreditTopUpOption.test.tstests-ui/tests/base/credits/comfyCredits.test.tstests-ui/tests/platform/cloud/subscription/composables/useSubscriptionCredits.test.ts📚 Learning: 2025-11-24T19:46:52.279Z
Learnt from: CR Repo: Comfy-Org/ComfyUI_frontend PR: 0 File: .cursorrules:0-0 Timestamp: 2025-11-24T19:46:52.279Z Learning: Applies to **/*.{vue,ts,tsx} : Use vue-i18n in Composition API for any string literals and place new translation entries in src/locales/en/main.jsonApplied to files:
src/components/dialog/content/credit/CreditTopUpOption.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/**/{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/components/dialog/content/credit/CreditTopUpOption.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 : Replace PrimeVue InputSwitch component with ToggleSwitchApplied to files:
src/components/dialog/content/credit/CreditTopUpOption.vue📚 Learning: 2025-11-24T19:46:52.279Z
Learnt from: CR Repo: Comfy-Org/ComfyUI_frontend PR: 0 File: .cursorrules:0-0 Timestamp: 2025-11-24T19:46:52.279Z Learning: Applies to **/*.vue : Do not use deprecated PrimeVue components (Dropdown, OverlayPanel, Calendar, InputSwitch, Sidebar, Chips, TabMenu, Steps, InlineMessage). Use replacements: Select, Popover, DatePicker, ToggleSwitch, Drawer, AutoComplete, Tabs, Stepper, Message respectivelyApplied to files:
src/components/dialog/content/credit/CreditTopUpOption.vue📚 Learning: 2025-11-24T19:46:52.279Z
Learnt from: CR Repo: Comfy-Org/ComfyUI_frontend PR: 0 File: .cursorrules:0-0 Timestamp: 2025-11-24T19:46:52.279Z Learning: Applies to **/*.vue : Never use deprecated PrimeVue components (Dropdown, OverlayPanel, Calendar, InputSwitch, Sidebar, Chips, TabMenu, Steps, InlineMessage)Applied to files:
src/components/dialog/content/credit/CreditTopUpOption.vue📚 Learning: 2025-11-24T19:46:52.279Z
Learnt from: CR Repo: Comfy-Org/ComfyUI_frontend PR: 0 File: .cursorrules:0-0 Timestamp: 2025-11-24T19:46:52.279Z Learning: Applies to **/*.{vue,ts,tsx} : Leverage VueUse functions for performance-enhancing utilitiesApplied to files:
src/components/dialog/content/credit/CreditTopUpOption.vue📚 Learning: 2025-11-24T19:46:52.279Z
Learnt from: CR Repo: Comfy-Org/ComfyUI_frontend PR: 0 File: .cursorrules:0-0 Timestamp: 2025-11-24T19:46:52.279Z Learning: Applies to **/*.vue : Utilize ref and reactive for reactive state in Vue 3Applied to files:
src/components/dialog/content/credit/CreditTopUpOption.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 : Utilize ref and reactive for reactive stateApplied to files:
src/components/dialog/content/credit/CreditTopUpOption.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 : Replace PrimeVue Calendar component with DatePickerApplied to files:
src/components/dialog/content/credit/CreditTopUpOption.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 : Replace PrimeVue Steps component with Stepper without panelsApplied to files:
src/components/dialog/content/credit/CreditTopUpOption.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 : Use ref/reactive for state management in Vue 3 Composition APIApplied to files:
src/components/dialog/content/credit/CreditTopUpOption.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 : Replace PrimeVue Dropdown component with SelectApplied to files:
src/components/dialog/content/credit/CreditTopUpOption.vue📚 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/tests/base/credits/comfyCredits.test.tstests-ui/tests/platform/cloud/subscription/composables/useSubscriptionCredits.test.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} : Use `test` instead of `it` for defining test cases in vitestApplied to files:
tests-ui/tests/base/credits/comfyCredits.test.tstests-ui/tests/platform/cloud/subscription/composables/useSubscriptionCredits.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 @.cursorApplied to files:
tests-ui/tests/base/credits/comfyCredits.test.ts📚 Learning: 2025-11-24T19:48:23.088Z
Learnt from: CR Repo: Comfy-Org/ComfyUI_frontend PR: 0 File: AGENTS.md:0-0 Timestamp: 2025-11-24T19:48:23.088Z Learning: Vitest test files should use coverage enabled with text, json, and html reporters as configured in `vitest.config.ts`Applied to files:
tests-ui/tests/base/credits/comfyCredits.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} : Follow naming conventions for browser testsApplied to files:
tests-ui/tests/base/credits/comfyCredits.test.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} : Tests should be cross-platform compatible using `path.resolve`, `path.join`, and `path.sep` for Windows, macOS, and Linux compatibilityApplied to files:
tests-ui/tests/base/credits/comfyCredits.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} : Use provided test helpers `createTestSubgraph` and `createTestSubgraphNode` from `./fixtures/subgraphHelpers` for consistent subgraph test setupApplied to files:
tests-ui/tests/base/credits/comfyCredits.test.tstests-ui/tests/platform/cloud/subscription/composables/useSubscriptionCredits.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/**/{composables,components}/**/*.{ts,tsx,vue} : Clean up subscriptions in state management to prevent memory leaksApplied to files:
tests-ui/tests/platform/cloud/subscription/composables/useSubscriptionCredits.test.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 stringsApplied to files:
src/components/common/UserCredit.vue🧬 Code graph analysis (2)
tests-ui/tests/base/credits/comfyCredits.test.ts (1)
src/base/credits/comfyCredits.ts (12)
COMFY_CREDIT_RATE_CENTS(31-31)COMFY_CREDIT_RATE_USD(32-32)usdToCents(34-34)formatUsdFromCents(102-115)centsToCredits(36-37)creditsToCents(39-40)usdToCredits(42-43)creditsToUsd(45-46)formatCredits(54-59)formatCreditsFromCents(61-74)formatCreditsFromUsd(76-89)formatUsd(91-100)tests-ui/tests/platform/cloud/subscription/composables/useSubscriptionCredits.test.ts (1)
src/platform/cloud/subscription/composables/useSubscriptionCredits.ts (1)
useSubscriptionCredits(10-65)⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
- GitHub Check: deploy-and-comment
- GitHub Check: setup
- GitHub Check: test
- GitHub Check: collect
- GitHub Check: lint-and-format
🔇 Additional comments (5)
src/components/common/UserCredit.vue (1)
12-17: Credits formatting + i18n usage looks solidUsing
formatCreditsFromCentswith a safe?? 0default and appendingt('credits.credits')gives consistent, localized"0.00 Credits"-style output and avoids balance null/undefined pitfalls. The wallet icon change also matches the new credits-focused UX.Also applies to: 24-27, 35-42
tests-ui/tests/platform/cloud/subscription/components/SubscriptionPanel.test.ts (1)
20-24: Mocks and expectations aligned with new credits formattingUpdating
mockCreditsDataand assertions to"10.00 Credits"/"5.00 Credits"keeps this suite consistent with the centralized credits formatter and suffix semantics.Also applies to: 155-159
tests-ui/tests/components/dialog/content/credit/CreditTopUpOption.test.ts (1)
53-66: Coverage for credit/USD display behavior looks goodThe tests exercise both preset and editable flows, verifying
"1.00 Credits"/"$2.10"as well as the recomputed"2.00 Credits"label whencustomAmountchanges, which matches the comfyCredits helpers’ behavior.tests-ui/tests/base/credits/comfyCredits.test.ts (1)
1-46: Comprehensive, locale-stable coverage for comfyCreditsThese tests thoroughly cover conversion helpers and formatting, and the explicit
'en-US'locale on formatting calls prevents locale-dependent flakiness. This gives strong confidence in the new credits utilities.tests-ui/tests/platform/cloud/subscription/composables/useSubscriptionCredits.test.ts (1)
4-16: Tests accurately mirror credits formatting and fallback behaviorThe new cases around
"0.00 Credits"defaults, specific cent values (210/420/630), and theformatCreditsFromCentserror spy all line up withuseSubscriptionCredits’sformatBalanceand try/catch fallback, giving good regression coverage for the new credits-domain logic.Also applies to: 78-107, 111-121, 125-135
d1006dd to
7c43bf4
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/components/dialog/content/credit/CreditTopUpOption.vue (1)
98-106: GuardpurchaseCreditswith try/finally so loading state can’t get stuckIf
authActions.purchaseCreditsrejects,loading.valuestaystrue, the spinner never clears, anddidClickBuyNowis never set (so no balance refresh on unmount). Wrapping the call in try/finally (and optionally logging) keeps the UI resilient:-const handleBuyNow = async () => { - const creditAmount = displayUsdAmount.value - telemetry?.trackApiCreditTopupButtonPurchaseClicked(creditAmount) - - loading.value = true - await authActions.purchaseCredits(creditAmount) - loading.value = false - didClickBuyNow.value = true -} +const handleBuyNow = async () => { + const creditAmount = displayUsdAmount.value + telemetry?.trackApiCreditTopupButtonPurchaseClicked(creditAmount) + + loading.value = true + try { + await authActions.purchaseCredits(creditAmount) + didClickBuyNow.value = true + } catch (error) { + console.error('[CreditTopUpOption] Failed to purchase credits:', error) + // Optionally surface a toast here if desired. + } finally { + loading.value = false + } +}
🧹 Nitpick comments (5)
src/base/credits/comfyCredits.ts (1)
1-115: Credit conversion and formatting helpers look consistent; minor DRY opportunityThe conversion + formatting pipeline is coherent (usd↔cents↔credits, shared
DEFAULT_NUMBER_FORMAT, and the min/max-fraction guard). If you want to pare things down further, you could derive small helper types fromFormatOptionsfor the “from cents/usd” helpers instead of repeating inline{ cents|usd; locale?; numberOptions? }shapes, but that’s purely optional.tests-ui/tests/components/dialog/content/credit/CreditTopUpOption.test.ts (1)
1-67: Usetestinstead ofitand be mindful of locale‑dependent formattingThe behavior under test looks correct, but per project test guidelines you should prefer
testoverit, and the numeric assertions assume an en‑US number format.You can address both by:
- Switching to
test:-import { describe, expect, it, vi } from 'vitest' +import { describe, expect, test, vi } from 'vitest' @@ -describe('CreditTopUpOption', () => { - it('renders converted credit price for preset amounts', () => { +describe('CreditTopUpOption', () => { + test('renders converted credit price for preset amounts', () => { @@ - it('updates credit label when editable amount changes', async () => { + test('updates credit label when editable amount changes', async () => {
- If these tests ever need to run under non‑en‑US locales, consider either passing an explicit locale into the comfyCredits helpers (via vue‑i18n’s current locale) or mocking
Intl.NumberFormatin the test setup so'1.00'/'$2.10'remain stable across environments.Based on learnings, the vitest style guidance applies here.
src/components/common/UserCredit.vue (1)
12-42: Pass vue‑i18n locale intoformatCreditsFromCentsfor consistent formattingThe balance calculation itself is sound, but
formatCreditsFromCentscurrently relies on the environment default locale, while labels come from vue‑i18n. To keep digits/grouping aligned with the active app locale, consider:-const { t } = useI18n() +const { t, locale } = useI18n() @@ - const cents = authStore.balance?.amount_micros ?? 0 - const amount = formatCreditsFromCents({ cents }) + const cents = authStore.balance?.amount_micros ?? 0 + const amount = formatCreditsFromCents({ cents, locale: locale.value }) return `${amount} ${t('credits.credits')}`This makes the numeric format follow the same locale as your translations.
As per coding guidelines, vue‑i18n should drive user‑visible formatting.
src/platform/cloud/subscription/composables/useSubscriptionCredits.ts (1)
2-5: Use vue‑i18n locale when formatting subscription credit balances
formatBalancecorrectly handles undefined balances and error fallbacks, but likeUserCredit.vueit letsformatCreditsFromCentspick the environment locale while labels come from vue‑i18n.To keep number formatting aligned with your active app locale, you could do:
-export function useSubscriptionCredits() { - const authStore = useFirebaseAuthStore() - const { t } = useI18n() +export function useSubscriptionCredits() { + const authStore = useFirebaseAuthStore() + const { t, locale } = useI18n() @@ - const cents = maybeCents ?? 0 - const amount = formatCreditsFromCents({ cents }) + const cents = maybeCents ?? 0 + const amount = formatCreditsFromCents({ + cents, + locale: locale.value + })That ensures balances render with the same locale settings as the rest of the subscription UI.
As per coding guidelines, vue‑i18n should be the source of truth for user‑visible formatting.
Also applies to: 14-19, 21-55
src/components/dialog/content/credit/CreditTopUpOption.vue (1)
79-96: Wire vue‑i18n locale into credits/USD formatting for consistent output
formattedCreditsandformattedUsduse comfyCredits helpers but don’t pass a locale, so formatting may not follow the current vue‑i18n locale.You can align them with the rest of the app like this:
-const { t } = useI18n() +const { t, locale } = useI18n() @@ -const formattedCredits = computed( - () => - `${formatCreditsFromUsd({ usd: displayUsdAmount.value })} ${t('credits.credits')}` -) +const formattedCredits = computed( + () => + `${formatCreditsFromUsd({ + usd: displayUsdAmount.value, + locale: locale.value + })} ${t('credits.credits')}` +) @@ -const formattedUsd = computed( - () => `$${formatUsd({ value: displayUsdAmount.value })}` -) +const formattedUsd = computed( + () => `$${formatUsd({ value: displayUsdAmount.value, locale: locale.value })}` +)This keeps the number formatting (decimal separator, grouping, etc.) in sync with the active translation locale.
As per coding guidelines, vue‑i18n should drive localized formatting in components.
7c43bf4 to
b23ab6b
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 6
📜 Review details
Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro
📒 Files selected for processing (8)
src/base/credits/comfyCredits.ts(1 hunks)src/components/common/UserCredit.vue(2 hunks)src/components/dialog/content/credit/CreditTopUpOption.vue(3 hunks)src/platform/cloud/subscription/composables/useSubscriptionCredits.ts(1 hunks)tests-ui/tests/base/credits/comfyCredits.test.ts(1 hunks)tests-ui/tests/components/dialog/content/credit/CreditTopUpOption.test.ts(1 hunks)tests-ui/tests/platform/cloud/subscription/components/SubscriptionPanel.test.ts(2 hunks)tests-ui/tests/platform/cloud/subscription/composables/useSubscriptionCredits.test.ts(2 hunks)
🧰 Additional context used
📓 Path-based instructions (23)
**/*.{vue,ts,tsx}
📄 CodeRabbit inference engine (.cursorrules)
**/*.{vue,ts,tsx}: Leverage VueUse functions for performance-enhancing utilities
Use vue-i18n in Composition API for any string literals and place new translation entries in src/locales/en/main.json
Files:
tests-ui/tests/base/credits/comfyCredits.test.tstests-ui/tests/platform/cloud/subscription/composables/useSubscriptionCredits.test.tssrc/components/common/UserCredit.vuetests-ui/tests/platform/cloud/subscription/components/SubscriptionPanel.test.tssrc/components/dialog/content/credit/CreditTopUpOption.vuesrc/platform/cloud/subscription/composables/useSubscriptionCredits.tstests-ui/tests/components/dialog/content/credit/CreditTopUpOption.test.tssrc/base/credits/comfyCredits.ts
**/*.{ts,tsx,js}
📄 CodeRabbit inference engine (.cursorrules)
Use es-toolkit for utility functions
Files:
tests-ui/tests/base/credits/comfyCredits.test.tstests-ui/tests/platform/cloud/subscription/composables/useSubscriptionCredits.test.tstests-ui/tests/platform/cloud/subscription/components/SubscriptionPanel.test.tssrc/platform/cloud/subscription/composables/useSubscriptionCredits.tstests-ui/tests/components/dialog/content/credit/CreditTopUpOption.test.tssrc/base/credits/comfyCredits.ts
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursorrules)
Use TypeScript for type safety
**/*.{ts,tsx}: Never useanytype - use proper TypeScript types
Never useas anytype assertions - fix the underlying type issue
Files:
tests-ui/tests/base/credits/comfyCredits.test.tstests-ui/tests/platform/cloud/subscription/composables/useSubscriptionCredits.test.tstests-ui/tests/platform/cloud/subscription/components/SubscriptionPanel.test.tssrc/platform/cloud/subscription/composables/useSubscriptionCredits.tstests-ui/tests/components/dialog/content/credit/CreditTopUpOption.test.tssrc/base/credits/comfyCredits.ts
**/*.{ts,tsx,js,vue}
📄 CodeRabbit inference engine (.cursorrules)
Implement proper error handling in components and services
**/*.{ts,tsx,js,vue}: Use 2-space indentation, single quotes, no semicolons, and maintain 80-character line width as configured in.prettierrc
Organize imports by sorting and grouping by plugin, and runpnpm formatbefore committing
Files:
tests-ui/tests/base/credits/comfyCredits.test.tstests-ui/tests/platform/cloud/subscription/composables/useSubscriptionCredits.test.tssrc/components/common/UserCredit.vuetests-ui/tests/platform/cloud/subscription/components/SubscriptionPanel.test.tssrc/components/dialog/content/credit/CreditTopUpOption.vuesrc/platform/cloud/subscription/composables/useSubscriptionCredits.tstests-ui/tests/components/dialog/content/credit/CreditTopUpOption.test.tssrc/base/credits/comfyCredits.ts
**/*.{ts,tsx,js,jsx,vue}
📄 CodeRabbit inference engine (CLAUDE.md)
Use camelCase for variable and setting names in TypeScript/Vue files
Files:
tests-ui/tests/base/credits/comfyCredits.test.tstests-ui/tests/platform/cloud/subscription/composables/useSubscriptionCredits.test.tssrc/components/common/UserCredit.vuetests-ui/tests/platform/cloud/subscription/components/SubscriptionPanel.test.tssrc/components/dialog/content/credit/CreditTopUpOption.vuesrc/platform/cloud/subscription/composables/useSubscriptionCredits.tstests-ui/tests/components/dialog/content/credit/CreditTopUpOption.test.tssrc/base/credits/comfyCredits.ts
**/*.{ts,tsx,vue}
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.{ts,tsx,vue}: Useconst settingStore = useSettingStore()andsettingStore.get('Comfy.SomeSetting')to retrieve settings in TypeScript/Vue files
Useawait settingStore.set('Comfy.SomeSetting', newValue)to update settings in TypeScript/Vue files
Check server capabilities usingapi.serverSupportsFeature('feature_name')before using enhanced features
Useapi.getServerFeature('config_name', defaultValue)to retrieve server feature configurationEnforce ESLint rules for Vue + TypeScript including: no floating promises, no unused imports, and i18n raw text restrictions in templates
Files:
tests-ui/tests/base/credits/comfyCredits.test.tstests-ui/tests/platform/cloud/subscription/composables/useSubscriptionCredits.test.tssrc/components/common/UserCredit.vuetests-ui/tests/platform/cloud/subscription/components/SubscriptionPanel.test.tssrc/components/dialog/content/credit/CreditTopUpOption.vuesrc/platform/cloud/subscription/composables/useSubscriptionCredits.tstests-ui/tests/components/dialog/content/credit/CreditTopUpOption.test.tssrc/base/credits/comfyCredits.ts
**/*.ts
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.ts: Define dynamic setting defaults using runtime context with functions in settings configuration
UsedefaultsByInstallVersionproperty for gradual feature rollout based on version in settings configuration
Files:
tests-ui/tests/base/credits/comfyCredits.test.tstests-ui/tests/platform/cloud/subscription/composables/useSubscriptionCredits.test.tstests-ui/tests/platform/cloud/subscription/components/SubscriptionPanel.test.tssrc/platform/cloud/subscription/composables/useSubscriptionCredits.tstests-ui/tests/components/dialog/content/credit/CreditTopUpOption.test.tssrc/base/credits/comfyCredits.ts
tests-ui/**/*.test.{js,ts,jsx,tsx}
📄 CodeRabbit inference engine (tests-ui/CLAUDE.md)
tests-ui/**/*.test.{js,ts,jsx,tsx}: Write tests for new features
Follow existing test patterns in the codebase
Use existing test utilities rather than writing custom utilities
Mock external dependencies in tests
Always prefer vitest mock functions over writing verbose manual mocks
Files:
tests-ui/tests/base/credits/comfyCredits.test.tstests-ui/tests/platform/cloud/subscription/composables/useSubscriptionCredits.test.tstests-ui/tests/platform/cloud/subscription/components/SubscriptionPanel.test.tstests-ui/tests/components/dialog/content/credit/CreditTopUpOption.test.ts
**/*.{test,spec}.{ts,tsx,js}
📄 CodeRabbit inference engine (AGENTS.md)
Unit and component tests should be located in
tests-ui/or co-located with components assrc/components/**/*.{test,spec}.ts; E2E tests should be inbrowser_tests/
Files:
tests-ui/tests/base/credits/comfyCredits.test.tstests-ui/tests/platform/cloud/subscription/composables/useSubscriptionCredits.test.tstests-ui/tests/platform/cloud/subscription/components/SubscriptionPanel.test.tstests-ui/tests/components/dialog/content/credit/CreditTopUpOption.test.ts
**/composables/use*.ts
📄 CodeRabbit inference engine (AGENTS.md)
Name composables in the format
useXyz.ts
Files:
tests-ui/tests/platform/cloud/subscription/composables/useSubscriptionCredits.test.tssrc/platform/cloud/subscription/composables/useSubscriptionCredits.ts
**/*.vue
📄 CodeRabbit inference engine (.cursorrules)
**/*.vue: Use setup() function for component logic in Vue 3 Composition API
Utilize ref and reactive for reactive state in Vue 3
Implement computed properties with computed() function
Use watch and watchEffect for side effects in Vue 3
Implement lifecycle hooks with onMounted, onUpdated, etc.
Utilize provide/inject for dependency injection in Vue 3
Use Vue 3.5 style of default prop declaration with defineProps()
Organize Vue components in <script> <style> order
Use Tailwind CSS for styling Vue components
Implement responsive design with Tailwind CSS
Do not use deprecated PrimeVue components (Dropdown, OverlayPanel, Calendar, InputSwitch, Sidebar, Chips, TabMenu, Steps, InlineMessage). Use replacements: Select, Popover, DatePicker, ToggleSwitch, Drawer, AutoComplete, Tabs, Stepper, Message respectively
Implement proper props and emits definitions in Vue components
Utilize Vue 3's Teleport component when needed
Use Suspense for async components in Vue 3
Follow Vue 3 style guide and naming conventions
Never use deprecated PrimeVue components (Dropdown, OverlayPanel, Calendar, InputSwitch, Sidebar, Chips, TabMenu, Steps, InlineMessage)Never use
:class="[]"to merge class names - always useimport { cn } from '@/utils/tailwindUtil'for class merging in Vue templates
**/*.vue: Use TypeScript with Vue 3 Single File Components (.vuefiles)
Name Vue components in PascalCase (e.g.,MenuHamburger.vue)Files:
src/components/common/UserCredit.vuesrc/components/dialog/content/credit/CreditTopUpOption.vuesrc/**/*.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 conventionsFiles:
src/components/common/UserCredit.vuesrc/components/dialog/content/credit/CreditTopUpOption.vuesrc/**/*.{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.jsonFiles:
src/components/common/UserCredit.vuesrc/components/dialog/content/credit/CreditTopUpOption.vuesrc/platform/cloud/subscription/composables/useSubscriptionCredits.tssrc/base/credits/comfyCredits.ts**/*.{vue,html}
📄 CodeRabbit inference engine (CLAUDE.md)
Never use
dark:ordark-theme:Tailwind variants - instead use semantic values fromstyle.csstheme, e.g.bg-node-component-surfaceFiles:
src/components/common/UserCredit.vuesrc/components/dialog/content/credit/CreditTopUpOption.vuesrc/**/*.{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 codebaseFiles:
src/components/common/UserCredit.vuesrc/components/dialog/content/credit/CreditTopUpOption.vuesrc/platform/cloud/subscription/composables/useSubscriptionCredits.tssrc/base/credits/comfyCredits.tssrc/**/{composables,components}/**/*.{ts,tsx,vue}
📄 CodeRabbit inference engine (src/CLAUDE.md)
Clean up subscriptions in state management to prevent memory leaks
Files:
src/components/common/UserCredit.vuesrc/components/dialog/content/credit/CreditTopUpOption.vuesrc/platform/cloud/subscription/composables/useSubscriptionCredits.tssrc/**/*.{vue,ts,tsx}
📄 CodeRabbit inference engine (src/CLAUDE.md)
Follow Vue 3 composition API style guide
Files:
src/components/common/UserCredit.vuesrc/components/dialog/content/credit/CreditTopUpOption.vuesrc/platform/cloud/subscription/composables/useSubscriptionCredits.tssrc/base/credits/comfyCredits.tssrc/**/{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.jsonFiles:
src/components/common/UserCredit.vuesrc/components/dialog/content/credit/CreditTopUpOption.vuesrc/platform/cloud/subscription/composables/useSubscriptionCredits.tssrc/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 componentsFiles:
src/components/common/UserCredit.vuesrc/components/dialog/content/credit/CreditTopUpOption.vuesrc/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 packageFiles:
src/components/common/UserCredit.vuesrc/components/dialog/content/credit/CreditTopUpOption.vuesrc/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 stringsFiles:
src/components/common/UserCredit.vuesrc/components/dialog/content/credit/CreditTopUpOption.vuesrc/**/*.ts
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
src/**/*.ts: Use es-toolkit for utility functions
Use TypeScript for type safetyFiles:
src/platform/cloud/subscription/composables/useSubscriptionCredits.tssrc/base/credits/comfyCredits.tssrc/**/{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 directlyFiles:
src/platform/cloud/subscription/composables/useSubscriptionCredits.ts🧠 Learnings (35)
📓 Common learnings
Learnt from: CR Repo: Comfy-Org/ComfyUI_frontend PR: 0 File: tests-ui/CLAUDE.md:0-0 Timestamp: 2025-11-24T19:48:03.270Z Learning: Applies to tests-ui/**/*.test.{js,ts,jsx,tsx} : Write tests for new features📚 Learning: 2025-11-24T19:48:03.270Z
Learnt from: CR Repo: Comfy-Org/ComfyUI_frontend PR: 0 File: tests-ui/CLAUDE.md:0-0 Timestamp: 2025-11-24T19:48:03.270Z Learning: Applies to tests-ui/**/*.test.{js,ts,jsx,tsx} : Write tests for new featuresApplied to files:
tests-ui/tests/base/credits/comfyCredits.test.tstests-ui/tests/platform/cloud/subscription/composables/useSubscriptionCredits.test.tstests-ui/tests/platform/cloud/subscription/components/SubscriptionPanel.test.tstests-ui/tests/components/dialog/content/credit/CreditTopUpOption.test.ts📚 Learning: 2025-11-24T19:48:03.270Z
Learnt from: CR Repo: Comfy-Org/ComfyUI_frontend PR: 0 File: tests-ui/CLAUDE.md:0-0 Timestamp: 2025-11-24T19:48:03.270Z Learning: Applies to tests-ui/**/*.test.{js,ts,jsx,tsx} : Follow existing test patterns in the codebaseApplied to files:
tests-ui/tests/base/credits/comfyCredits.test.tstests-ui/tests/platform/cloud/subscription/composables/useSubscriptionCredits.test.tstests-ui/tests/components/dialog/content/credit/CreditTopUpOption.test.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} : Use `vitest` for unit testing in this projectApplied to files:
tests-ui/tests/base/credits/comfyCredits.test.tstests-ui/tests/platform/cloud/subscription/composables/useSubscriptionCredits.test.tstests-ui/tests/components/dialog/content/credit/CreditTopUpOption.test.ts📚 Learning: 2025-11-24T19:48:03.270Z
Learnt from: CR Repo: Comfy-Org/ComfyUI_frontend PR: 0 File: tests-ui/CLAUDE.md:0-0 Timestamp: 2025-11-24T19:48:03.270Z Learning: Applies to tests-ui/**/*.test.{js,ts,jsx,tsx} : Use existing test utilities rather than writing custom utilitiesApplied to files:
tests-ui/tests/base/credits/comfyCredits.test.tstests-ui/tests/platform/cloud/subscription/composables/useSubscriptionCredits.test.tstests-ui/tests/components/dialog/content/credit/CreditTopUpOption.test.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} : Prefer the use of `test.extend` over loose variables; import `test as baseTest` from `vitest`Applied to files:
tests-ui/tests/base/credits/comfyCredits.test.tstests-ui/tests/platform/cloud/subscription/composables/useSubscriptionCredits.test.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} : Use `test` instead of `it` for defining test cases in vitestApplied to files:
tests-ui/tests/base/credits/comfyCredits.test.tstests-ui/tests/platform/cloud/subscription/composables/useSubscriptionCredits.test.ts📚 Learning: 2025-11-24T19:48:03.270Z
Learnt from: CR Repo: Comfy-Org/ComfyUI_frontend PR: 0 File: tests-ui/CLAUDE.md:0-0 Timestamp: 2025-11-24T19:48:03.270Z Learning: Applies to tests-ui/**/*.test.{js,ts,jsx,tsx} : Always prefer vitest mock functions over writing verbose manual mocksApplied to files:
tests-ui/tests/base/credits/comfyCredits.test.tstests-ui/tests/platform/cloud/subscription/composables/useSubscriptionCredits.test.tstests-ui/tests/components/dialog/content/credit/CreditTopUpOption.test.ts📚 Learning: 2025-11-24T19:48:03.270Z
Learnt from: CR Repo: Comfy-Org/ComfyUI_frontend PR: 0 File: tests-ui/CLAUDE.md:0-0 Timestamp: 2025-11-24T19:48:03.270Z Learning: Check tests-ui/README.md for test guidelinesApplied to files:
tests-ui/tests/base/credits/comfyCredits.test.tstests-ui/tests/components/dialog/content/credit/CreditTopUpOption.test.ts📚 Learning: 2025-11-24T19:48:23.088Z
Learnt from: CR Repo: Comfy-Org/ComfyUI_frontend PR: 0 File: AGENTS.md:0-0 Timestamp: 2025-11-24T19:48:23.088Z Learning: Vitest test files should use coverage enabled with text, json, and html reporters as configured in `vitest.config.ts`Applied to files:
tests-ui/tests/base/credits/comfyCredits.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 @.cursorApplied to files:
tests-ui/tests/base/credits/comfyCredits.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} : Follow naming conventions for browser testsApplied to files:
tests-ui/tests/base/credits/comfyCredits.test.tstests-ui/tests/components/dialog/content/credit/CreditTopUpOption.test.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} : Tests should be cross-platform compatible using `path.resolve`, `path.join`, and `path.sep` for Windows, macOS, and Linux compatibilityApplied to files:
tests-ui/tests/base/credits/comfyCredits.test.ts📚 Learning: 2025-11-24T19:48:23.088Z
Learnt from: CR Repo: Comfy-Org/ComfyUI_frontend PR: 0 File: AGENTS.md:0-0 Timestamp: 2025-11-24T19:48:23.088Z Learning: Use Vitest (with happy-dom) for unit and component tests, and Playwright for E2E testsApplied to files:
tests-ui/tests/base/credits/comfyCredits.test.tstests-ui/tests/platform/cloud/subscription/composables/useSubscriptionCredits.test.tstests-ui/tests/components/dialog/content/credit/CreditTopUpOption.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} : Use provided test helpers `createTestSubgraph` and `createTestSubgraphNode` from `./fixtures/subgraphHelpers` for consistent subgraph test setupApplied to files:
tests-ui/tests/base/credits/comfyCredits.test.tstests-ui/tests/platform/cloud/subscription/composables/useSubscriptionCredits.test.ts📚 Learning: 2025-11-24T19:48:03.270Z
Learnt from: CR Repo: Comfy-Org/ComfyUI_frontend PR: 0 File: tests-ui/CLAUDE.md:0-0 Timestamp: 2025-11-24T19:48:03.270Z Learning: Applies to tests-ui/**/*.test.{js,ts,jsx,tsx} : Mock external dependencies in testsApplied to files:
tests-ui/tests/platform/cloud/subscription/composables/useSubscriptionCredits.test.tstests-ui/tests/platform/cloud/subscription/components/SubscriptionPanel.test.tstests-ui/tests/components/dialog/content/credit/CreditTopUpOption.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/**/{composables,components}/**/*.{ts,tsx,vue} : Clean up subscriptions in state management to prevent memory leaksApplied to files:
tests-ui/tests/platform/cloud/subscription/composables/useSubscriptionCredits.test.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 possibleApplied to files:
tests-ui/tests/platform/cloud/subscription/composables/useSubscriptionCredits.test.tstests-ui/tests/platform/cloud/subscription/components/SubscriptionPanel.test.ts📚 Learning: 2025-11-24T19:46:52.279Z
Learnt from: CR Repo: Comfy-Org/ComfyUI_frontend PR: 0 File: .cursorrules:0-0 Timestamp: 2025-11-24T19:46:52.279Z Learning: Applies to **/*.{vue,ts,tsx} : Use vue-i18n in Composition API for any string literals and place new translation entries in src/locales/en/main.jsonApplied to files:
tests-ui/tests/platform/cloud/subscription/composables/useSubscriptionCredits.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/**/{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/components/common/UserCredit.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,ts,js} : Use vue-i18n for ALL UI stringsApplied to files:
src/components/common/UserCredit.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 : Replace PrimeVue InputSwitch component with ToggleSwitchApplied to files:
src/components/dialog/content/credit/CreditTopUpOption.vue📚 Learning: 2025-11-24T19:46:52.279Z
Learnt from: CR Repo: Comfy-Org/ComfyUI_frontend PR: 0 File: .cursorrules:0-0 Timestamp: 2025-11-24T19:46:52.279Z Learning: Applies to **/*.vue : Do not use deprecated PrimeVue components (Dropdown, OverlayPanel, Calendar, InputSwitch, Sidebar, Chips, TabMenu, Steps, InlineMessage). Use replacements: Select, Popover, DatePicker, ToggleSwitch, Drawer, AutoComplete, Tabs, Stepper, Message respectivelyApplied to files:
src/components/dialog/content/credit/CreditTopUpOption.vue📚 Learning: 2025-11-24T19:46:52.279Z
Learnt from: CR Repo: Comfy-Org/ComfyUI_frontend PR: 0 File: .cursorrules:0-0 Timestamp: 2025-11-24T19:46:52.279Z Learning: Applies to **/*.vue : Never use deprecated PrimeVue components (Dropdown, OverlayPanel, Calendar, InputSwitch, Sidebar, Chips, TabMenu, Steps, InlineMessage)Applied to files:
src/components/dialog/content/credit/CreditTopUpOption.vue📚 Learning: 2025-11-24T19:46:52.279Z
Learnt from: CR Repo: Comfy-Org/ComfyUI_frontend PR: 0 File: .cursorrules:0-0 Timestamp: 2025-11-24T19:46:52.279Z Learning: Applies to **/*.{vue,ts,tsx} : Leverage VueUse functions for performance-enhancing utilitiesApplied to files:
src/components/dialog/content/credit/CreditTopUpOption.vuesrc/platform/cloud/subscription/composables/useSubscriptionCredits.ts📚 Learning: 2025-11-24T19:46:52.279Z
Learnt from: CR Repo: Comfy-Org/ComfyUI_frontend PR: 0 File: .cursorrules:0-0 Timestamp: 2025-11-24T19:46:52.279Z Learning: Applies to **/*.vue : Utilize ref and reactive for reactive state in Vue 3Applied to files:
src/components/dialog/content/credit/CreditTopUpOption.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 : Utilize ref and reactive for reactive stateApplied to files:
src/components/dialog/content/credit/CreditTopUpOption.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 : Replace PrimeVue Calendar component with DatePickerApplied to files:
src/components/dialog/content/credit/CreditTopUpOption.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 : Replace PrimeVue Steps component with Stepper without panelsApplied to files:
src/components/dialog/content/credit/CreditTopUpOption.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 : Use ref/reactive for state management in Vue 3 Composition APIApplied to files:
src/components/dialog/content/credit/CreditTopUpOption.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 : Replace PrimeVue Dropdown component with SelectApplied to files:
src/components/dialog/content/credit/CreditTopUpOption.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 : Implement computed() for derived state in Vue 3 Composition APIApplied to files:
src/platform/cloud/subscription/composables/useSubscriptionCredits.ts📚 Learning: 2025-11-24T19:47:14.779Z
Learnt from: CR Repo: Comfy-Org/ComfyUI_frontend PR: 0 File: CLAUDE.md:0-0 Timestamp: 2025-11-24T19:47:14.779Z Learning: Applies to **/*.{ts,tsx,vue} : Use `const settingStore = useSettingStore()` and `settingStore.get('Comfy.SomeSetting')` to retrieve settings in TypeScript/Vue filesApplied to files:
src/platform/cloud/subscription/composables/useSubscriptionCredits.ts📚 Learning: 2025-11-24T19:46:52.279Z
Learnt from: CR Repo: Comfy-Org/ComfyUI_frontend PR: 0 File: .cursorrules:0-0 Timestamp: 2025-11-24T19:46:52.279Z Learning: Applies to **/*.vue : Implement computed properties with computed() functionApplied to files:
src/platform/cloud/subscription/composables/useSubscriptionCredits.ts📚 Learning: 2025-11-24T19:48:23.088Z
Learnt from: CR Repo: Comfy-Org/ComfyUI_frontend PR: 0 File: AGENTS.md:0-0 Timestamp: 2025-11-24T19:48:23.088Z Learning: Applies to **/*.{test,spec}.{ts,tsx,js} : Unit and component tests should be located in `tests-ui/` or co-located with components as `src/components/**/*.{test,spec}.ts`; E2E tests should be in `browser_tests/`Applied to files:
tests-ui/tests/components/dialog/content/credit/CreditTopUpOption.test.ts🧬 Code graph analysis (3)
tests-ui/tests/base/credits/comfyCredits.test.ts (1)
src/base/credits/comfyCredits.ts (11)
COMFY_CREDIT_RATE_CENTS(31-31)COMFY_CREDIT_RATE_USD(32-32)usdToCents(34-34)centsToCredits(36-37)creditsToCents(39-40)usdToCredits(42-43)creditsToUsd(45-46)formatCredits(54-59)formatCreditsFromCents(61-74)formatCreditsFromUsd(76-89)formatUsd(91-100)tests-ui/tests/platform/cloud/subscription/composables/useSubscriptionCredits.test.ts (1)
src/platform/cloud/subscription/composables/useSubscriptionCredits.ts (1)
useSubscriptionCredits(10-68)src/platform/cloud/subscription/composables/useSubscriptionCredits.ts (2)
src/stores/firebaseAuthStore.ts (1)
useFirebaseAuthStore(53-473)src/base/credits/comfyCredits.ts (1)
formatCreditsFromCents(61-74)⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
- GitHub Check: test
- GitHub Check: setup
- GitHub Check: lint-and-format
- GitHub Check: collect
🔇 Additional comments (18)
tests-ui/tests/platform/cloud/subscription/components/SubscriptionPanel.test.ts (1)
154-159: LGTM: Tests correctly validate formatted credit display.The expectations properly verify that the component displays formatted credit values with the localized suffix.
src/components/common/UserCredit.vue (3)
12-12: LGTM: Icon updated to better represent credits.The change from
pi-dollartopi-walletbetter reflects the shift from USD to credits-based display.
37-45: LGTM: Proper i18n integration for credit formatting.The implementation correctly uses
formatCreditsFromCentswith locale-aware formatting and appends the translated credits label viat('credits.credits'). The fallback to 0 when balance is undefined is appropriate.As per coding guidelines, using vue-i18n in Composition API for user-facing strings.
37-45: Clarify the units ofamount_microswith the backend team.The field is documented as microamount (1/1,000,000 of currency unit) in the API schema, yet the frontend code treats it as cents by passing it directly to
formatCreditsFromCents()which divides by 210 (the cent-to-credit rate). The test case confirms 210 units map to 1 credit, consistent with cent units. Verify with the backend team whetheramount_microsactually contains cents (1/100) or true micros (1/1,000,000), or if the field should be renamed for clarity.tests-ui/tests/components/dialog/content/credit/CreditTopUpOption.test.ts (1)
5-25: LGTM: Clean mock setup following project patterns.The mocks for Firebase auth actions and telemetry are concise and use vitest mock functions appropriately. The i18n setup is minimal but sufficient for testing credit formatting.
Based on learnings, using existing test utilities and vitest mock functions.
tests-ui/tests/base/credits/comfyCredits.test.ts (1)
1-46: LGTM: Comprehensive test coverage with proper locale handling.The test suite correctly:
- Uses
testinstead ofitper project guidelines- Passes explicit locale (
'en-US') to all formatting functions, preventing flakiness- Covers all core conversion functions (USD↔cents↔credits)
- Validates both conversion rates and formatting output
- Uses appropriate assertions (
toBefor exact matches,toBeCloseTofor floating-point comparisons)Based on learnings, following existing test patterns and addressing locale-dependent formatting concerns from past reviews.
tests-ui/tests/platform/cloud/subscription/composables/useSubscriptionCredits.test.ts (3)
8-17: LGTM: Proper i18n mock setup.The vue-i18n mock correctly returns the translation token and locale, allowing tests to verify the complete formatted output including the translated suffix.
98-108: LGTM: Excellent error handling test.The test properly uses a spy to simulate formatting errors and verifies that the composable falls back gracefully to formatting
0. The spy is restored after the test, preventing interference with other tests.Based on learnings, using vitest mock functions appropriately.
92-96: Test values correctly demonstrate credit conversion.The test uses
amount_micros: 210which, given the conversion rate of 210 cents per credit, correctly results in'1.00 Credits'. The test values (210, 420, 630) are well-chosen multiples that produce clean decimal outputs (1.00, 2.00, 3.00).src/platform/cloud/subscription/composables/useSubscriptionCredits.ts (3)
14-22: LGTM: Clean formatting helper with proper i18n.The
formatBalancehelper effectively centralizes the formatting logic:
- Handles undefined values gracefully with nullish coalescing
- Uses locale-aware formatting via
formatCreditsFromCents- Appends translated credits suffix using
t('credits.credits')As per coding guidelines, using vue-i18n for user-facing strings.
24-58: LGTM: Robust error handling with consistent fallback.All three credit computeds (
totalCredits,monthlyBonusCredits,prepaidCredits) follow the same pattern:
- Try to format the balance value
- Catch any formatting errors
- Log the error for debugging
- Return a safe fallback (
formatBalance(0))This ensures the UI never breaks due to formatting errors.
60-67: LGTM: Added loading state to public API.The addition of
isLoadingBalanceto the return value allows consumers to show appropriate loading states while balance data is being fetched.src/components/dialog/content/credit/CreditTopUpOption.vue (3)
5-5: LGTM: Icon updated for consistency.The change from
pi-dollartopi-walletaligns with the shift to credits-based display and matches the icon change inUserCredit.vue.
9-34: LGTM: Clear separation of editable and non-editable modes.The restructured template provides distinct UX for each mode:
- Editable: InputNumber with USD prefix and inline credits label
- Non-editable: Two-line display with emphasized credits and supporting USD
The layout is clear and appropriate for each use case.
80-99: LGTM: Robust clamping and locale-aware formatting.The implementation correctly:
- Clamps values between $1 and $1000, handling NaN cases
- Uses
displayUsdAmountto ensure editable and non-editable paths use the same clamped value- Formats credits and USD with locale-aware helpers
- Appends translated credits label
The clamping on input/blur (lines 20-27) addresses the past review comment about keeping input, labels, and purchase amount in sync.
As per coding guidelines, using vue-i18n for user-facing strings and proper computed properties for derived state.
src/base/credits/comfyCredits.ts (3)
20-26: LGTM: Defensive handling of conflicting fraction digit options.The logic prevents
Intl.NumberFormatfrom throwing whenmaximumFractionDigits < minimumFractionDigitsby adjustingminimumFractionDigitsdown to match. This ensures formatting never fails due to misconfigured options.
1-115: LGTM: Well-structured credit conversion and formatting module.The module provides:
- Clear separation between conversion (USD↔cents↔credits) and formatting
- Type-safe public API with exported types
- Consistent 2-decimal formatting via
DEFAULT_NUMBER_FORMAT- Locale-aware formatting through
Intl.NumberFormat- Defensive option handling to prevent formatting errors
The structure is clean, testable, and provides all necessary utilities for the credit system.
As per coding guidelines, using proper TypeScript types and avoiding
any.
31-46: Add documentation explaining the 210-cent credit rate.The conversion rate ($2.10 per 1 credit) is mathematically correct and well-tested, but lacks any comment explaining the business rationale. This unusual rate (roughly 0.476 credits per $1 USD) differs from typical SaaS platforms that use larger, rounder scales. Add a comment above
COMFY_CREDIT_RATE_CENTSclarifying whether this rate is final, approved, or subject to change—this helps future maintainers understand the intended pricing model.
| export const formatCredits = ({ | ||
| value, | ||
| locale, | ||
| numberOptions | ||
| }: FormatOptions): string => | ||
| formatNumber({ value, locale, options: numberOptions }) | ||
|
|
||
| export const formatCreditsFromCents = ({ | ||
| cents, | ||
| locale, | ||
| numberOptions | ||
| }: { | ||
| cents: number | ||
| locale?: string | ||
| numberOptions?: Intl.NumberFormatOptions | ||
| }): string => | ||
| formatCredits({ | ||
| value: centsToCredits(cents), | ||
| locale, | ||
| numberOptions | ||
| }) | ||
|
|
||
| export const formatCreditsFromUsd = ({ | ||
| usd, | ||
| locale, | ||
| numberOptions | ||
| }: { | ||
| usd: number | ||
| locale?: string | ||
| numberOptions?: Intl.NumberFormatOptions | ||
| }): string => | ||
| formatCredits({ | ||
| value: usdToCredits(usd), | ||
| locale, | ||
| numberOptions | ||
| }) | ||
|
|
||
| export const formatUsd = ({ | ||
| value, | ||
| locale, | ||
| numberOptions | ||
| }: FormatOptions): string => | ||
| formatNumber({ | ||
| value, | ||
| locale, | ||
| options: numberOptions | ||
| }) | ||
|
|
||
| export const formatUsdFromCents = ({ | ||
| cents, | ||
| locale, | ||
| numberOptions | ||
| }: { | ||
| cents: number | ||
| locale?: string | ||
| numberOptions?: Intl.NumberFormatOptions | ||
| }): string => | ||
| formatUsd({ | ||
| value: cents / 100, | ||
| locale, | ||
| numberOptions | ||
| }) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧹 Nitpick | 🔵 Trivial
Consider simplifying function signatures with parameter reordering.
Past review comments suggest that having undefined as a placeholder for locale (e.g., formatCredits(value, undefined, options)) is awkward. The current object-based parameters avoid this, but another approach would be to match Intl.NumberFormat's parameter order: (value, locale?, options?).
Example alternative signature:
export const formatCredits = (
value: number,
locale?: string,
numberOptions?: Intl.NumberFormatOptions
): string => formatNumber({ value, locale, options: numberOptions })This would allow calls like:
formatCredits(100) // default locale
formatCredits(100, 'en-US') // explicit locale
formatCredits(100, 'en-US', options) // with optionsHowever, the current object-based approach is also valid and provides better clarity at call sites, especially when only some parameters are needed. Consider team preferences and consistency with other utility functions in the codebase.
Based on past review discussion about parameter ergonomics.
| const formatBalance = (maybeCents?: number) => { | ||
| // Backend returns cents despite the *_micros naming convention. | ||
| const cents = maybeCents ?? 0 | ||
| const amount = formatCreditsFromCents({ | ||
| cents, | ||
| locale: locale.value | ||
| }) | ||
| return `${amount} ${t('credits.credits')}` | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
# Search for backend API response types related to subscription/credits
rg -n "GetCustomerBalance|cloud_credit_balance|prepaid_balance" --type ts -C 5Repository: Comfy-Org/ComfyUI_frontend
Length of output: 16028
🏁 Script executed:
# Look for type definitions and API responses
fd -e ts -e tsx | xargs rg -l "micros" | head -20Repository: Comfy-Org/ComfyUI_frontend
Length of output: 390
🏁 Script executed:
# Search for any conversion or formatting logic around these fields
rg -n "micros|formatCredits" --type ts --type tsx -A 3 -B 3Repository: Comfy-Org/ComfyUI_frontend
Length of output: 97
🏁 Script executed:
# Check for API schema or backend type definitions
fd -e ts -e tsx | xargs rg -l "backend.*return|api.*response" | head -10Repository: Comfy-Org/ComfyUI_frontend
Length of output: 164
🏁 Script executed:
# Find formatCreditsFromCents implementation
rg -n "formatCreditsFromCents" --type ts -B 5 -A 15Repository: Comfy-Org/ComfyUI_frontend
Length of output: 9756
🏁 Script executed:
# Look for any constants or documentation about the 210 division factor
rg -n "210|micros|cents" --type ts -C 3 | head -100Repository: Comfy-Org/ComfyUI_frontend
Length of output: 6997
🏁 Script executed:
# Check the exact API documentation comment about microamount
rg -n "microamount|micros" packages/registry-types/src/comfyRegistryTypes.ts -B 2 -A 2 | head -60Repository: Comfy-Org/ComfyUI_frontend
Length of output: 3006
Backend API contract violation: field names and documentation don't match unit values.
The backend API schema documents amount_micros, cloud_credit_balance_micros, and prepaid_balance_micros as containing "microamount (1/1,000,000 of the currency unit)". However, the code divides these values by 210 (COMFY_CREDIT_RATE_CENTS), treating them as cents (1/100). This is a unit conversion mismatch:
- Backend claims: microamount (1/1,000,000)
- Code assumes: cents (1/100)
The comment on line 15 is misleading—it's not a naming issue; the fields ARE correctly named with _micros suffix. The actual problem is a discrepancy between the backend schema documentation and what it actually returns. Either the schema is incorrect (should say "cents" not "microamount"), or the code applies the wrong conversion factor.
This needs clarification from the backend team or verification through actual API testing to ensure the unit conversion is correct.
🤖 Prompt for AI Agents
In src/platform/cloud/subscription/composables/useSubscriptionCredits.ts around
lines 14 to 22, the inline comment and conversion assume the backend returns
cents while the API field names and docs use microamounts; fix this by verifying
the actual unit returned (ask backend or inspect API responses), then update the
conversion to use the correct factor (1,000,000 -> currency units or 100 ->
cents) and remove or replace the misleading comment; if the backend truly
returns cents, change the field docs or add a clear code comment stating that
mismatch, and add a unit-focused test asserting the expected numeric conversion.
| import { mount } from '@vue/test-utils' | ||
| import { createI18n } from 'vue-i18n' | ||
| import { describe, expect, it, vi } from 'vitest' | ||
|
|
||
| vi.mock('@/composables/auth/useFirebaseAuthActions', () => ({ | ||
| useFirebaseAuthActions: () => ({ | ||
| purchaseCredits: vi.fn(), | ||
| fetchBalance: vi.fn(), | ||
| accessBillingPortal: vi.fn() | ||
| }) | ||
| })) | ||
|
|
||
| vi.mock('@/platform/telemetry', () => ({ | ||
| useTelemetry: () => ({ | ||
| trackApiCreditTopupButtonPurchaseClicked: vi.fn() | ||
| }) | ||
| })) | ||
|
|
||
| import CreditTopUpOption from '@/components/dialog/content/credit/CreditTopUpOption.vue' | ||
|
|
||
| const i18n = createI18n({ | ||
| legacy: false, | ||
| locale: 'en', | ||
| messages: { en: { credits: { credits: 'Credits' } } } | ||
| }) | ||
|
|
||
| const mountOption = ( | ||
| props?: Partial<{ amount: number; preselected: boolean; editable: boolean }> | ||
| ) => | ||
| mount(CreditTopUpOption, { | ||
| props: { | ||
| amount: 10, | ||
| preselected: false, | ||
| editable: false, | ||
| ...props | ||
| }, | ||
| global: { | ||
| plugins: [i18n], | ||
| stubs: { | ||
| Tag: { template: '<span><slot /></span>' }, | ||
| Button: { template: '<button><slot /></button>' }, | ||
| InputNumber: { | ||
| props: ['modelValue'], | ||
| emits: ['update:modelValue'], | ||
| template: | ||
| '<input type="number" :value="modelValue" @input="$emit(\'update:modelValue\',$event.target.value)" />' | ||
| }, | ||
| ProgressSpinner: { template: '<div class="spinner" />' } | ||
| } | ||
| } | ||
| }) | ||
|
|
||
| describe('CreditTopUpOption', () => { | ||
| it('renders converted credit price for preset amounts', () => { | ||
| const wrapper = mountOption({ amount: 2.1 }) | ||
| expect(wrapper.text()).toContain('1.00 Credits') | ||
| expect(wrapper.text()).toContain('$2.10') | ||
| }) | ||
|
|
||
| it('updates credit label when editable amount changes', async () => { | ||
| const wrapper = mountOption({ editable: true }) | ||
| const vm = wrapper.vm as unknown as { customAmount: number } | ||
| vm.customAmount = 4.2 | ||
| await wrapper.vm.$nextTick() | ||
| expect(wrapper.text()).toContain('2.00 Credits') | ||
| }) | ||
| }) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion | 🟠 Major
Use test instead of it per project guidelines.
Based on the coding guidelines, test cases should use test instead of it in Vitest.
Apply this diff:
-import { describe, expect, it, vi } from 'vitest'
+import { describe, expect, test, vi } from 'vitest'
describe('CreditTopUpOption', () => {
- it('renders converted credit price for preset amounts', () => {
+ test('renders converted credit price for preset amounts', () => {
const wrapper = mountOption({ amount: 2.1 })
expect(wrapper.text()).toContain('1.00 Credits')
expect(wrapper.text()).toContain('$2.10')
})
- it('updates credit label when editable amount changes', async () => {
+ test('updates credit label when editable amount changes', async () => {
const wrapper = mountOption({ editable: true })
const vm = wrapper.vm as unknown as { customAmount: number }
vm.customAmount = 4.2
await wrapper.vm.$nextTick()
expect(wrapper.text()).toContain('2.00 Credits')
})
})Based on learnings and coding guidelines for tests-ui/**/*.test.{js,ts,jsx,tsx}.
📝 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.
| import { mount } from '@vue/test-utils' | |
| import { createI18n } from 'vue-i18n' | |
| import { describe, expect, it, vi } from 'vitest' | |
| vi.mock('@/composables/auth/useFirebaseAuthActions', () => ({ | |
| useFirebaseAuthActions: () => ({ | |
| purchaseCredits: vi.fn(), | |
| fetchBalance: vi.fn(), | |
| accessBillingPortal: vi.fn() | |
| }) | |
| })) | |
| vi.mock('@/platform/telemetry', () => ({ | |
| useTelemetry: () => ({ | |
| trackApiCreditTopupButtonPurchaseClicked: vi.fn() | |
| }) | |
| })) | |
| import CreditTopUpOption from '@/components/dialog/content/credit/CreditTopUpOption.vue' | |
| const i18n = createI18n({ | |
| legacy: false, | |
| locale: 'en', | |
| messages: { en: { credits: { credits: 'Credits' } } } | |
| }) | |
| const mountOption = ( | |
| props?: Partial<{ amount: number; preselected: boolean; editable: boolean }> | |
| ) => | |
| mount(CreditTopUpOption, { | |
| props: { | |
| amount: 10, | |
| preselected: false, | |
| editable: false, | |
| ...props | |
| }, | |
| global: { | |
| plugins: [i18n], | |
| stubs: { | |
| Tag: { template: '<span><slot /></span>' }, | |
| Button: { template: '<button><slot /></button>' }, | |
| InputNumber: { | |
| props: ['modelValue'], | |
| emits: ['update:modelValue'], | |
| template: | |
| '<input type="number" :value="modelValue" @input="$emit(\'update:modelValue\',$event.target.value)" />' | |
| }, | |
| ProgressSpinner: { template: '<div class="spinner" />' } | |
| } | |
| } | |
| }) | |
| describe('CreditTopUpOption', () => { | |
| it('renders converted credit price for preset amounts', () => { | |
| const wrapper = mountOption({ amount: 2.1 }) | |
| expect(wrapper.text()).toContain('1.00 Credits') | |
| expect(wrapper.text()).toContain('$2.10') | |
| }) | |
| it('updates credit label when editable amount changes', async () => { | |
| const wrapper = mountOption({ editable: true }) | |
| const vm = wrapper.vm as unknown as { customAmount: number } | |
| vm.customAmount = 4.2 | |
| await wrapper.vm.$nextTick() | |
| expect(wrapper.text()).toContain('2.00 Credits') | |
| }) | |
| }) | |
| import { mount } from '@vue/test-utils' | |
| import { createI18n } from 'vue-i18n' | |
| import { describe, expect, test, vi } from 'vitest' | |
| vi.mock('@/composables/auth/useFirebaseAuthActions', () => ({ | |
| useFirebaseAuthActions: () => ({ | |
| purchaseCredits: vi.fn(), | |
| fetchBalance: vi.fn(), | |
| accessBillingPortal: vi.fn() | |
| }) | |
| })) | |
| vi.mock('@/platform/telemetry', () => ({ | |
| useTelemetry: () => ({ | |
| trackApiCreditTopupButtonPurchaseClicked: vi.fn() | |
| }) | |
| })) | |
| import CreditTopUpOption from '@/components/dialog/content/credit/CreditTopUpOption.vue' | |
| const i18n = createI18n({ | |
| legacy: false, | |
| locale: 'en', | |
| messages: { en: { credits: { credits: 'Credits' } } } | |
| }) | |
| const mountOption = ( | |
| props?: Partial<{ amount: number; preselected: boolean; editable: boolean }> | |
| ) => | |
| mount(CreditTopUpOption, { | |
| props: { | |
| amount: 10, | |
| preselected: false, | |
| editable: false, | |
| ...props | |
| }, | |
| global: { | |
| plugins: [i18n], | |
| stubs: { | |
| Tag: { template: '<span><slot /></span>' }, | |
| Button: { template: '<button><slot /></button>' }, | |
| InputNumber: { | |
| props: ['modelValue'], | |
| emits: ['update:modelValue'], | |
| template: | |
| '<input type="number" :value="modelValue" @input="$emit(\'update:modelValue\',$event.target.value)" />' | |
| }, | |
| ProgressSpinner: { template: '<div class="spinner" />' } | |
| } | |
| } | |
| }) | |
| describe('CreditTopUpOption', () => { | |
| test('renders converted credit price for preset amounts', () => { | |
| const wrapper = mountOption({ amount: 2.1 }) | |
| expect(wrapper.text()).toContain('1.00 Credits') | |
| expect(wrapper.text()).toContain('$2.10') | |
| }) | |
| test('updates credit label when editable amount changes', async () => { | |
| const wrapper = mountOption({ editable: true }) | |
| const vm = wrapper.vm as unknown as { customAmount: number } | |
| vm.customAmount = 4.2 | |
| await wrapper.vm.$nextTick() | |
| expect(wrapper.text()).toContain('2.00 Credits') | |
| }) | |
| }) |
🤖 Prompt for AI Agents
In tests-ui/tests/components/dialog/content/credit/CreditTopUpOption.test.ts
around lines 1 to 67, the test cases use "it" but project guidelines require
"test"; update the Vitest import to import "test" instead of "it" (i.e., replace
it with test in the import list) and replace the two "it('...')" blocks with
"test('...')" while keeping the same bodies and async behavior so the assertions
remain unchanged.
| it('updates credit label when editable amount changes', async () => { | ||
| const wrapper = mountOption({ editable: true }) | ||
| const vm = wrapper.vm as unknown as { customAmount: number } | ||
| vm.customAmount = 4.2 | ||
| await wrapper.vm.$nextTick() | ||
| expect(wrapper.text()).toContain('2.00 Credits') | ||
| }) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧹 Nitpick | 🔵 Trivial
Consider testing dynamic updates through user interaction.
The test directly mutates vm.customAmount to test dynamic updates. While this works, it bypasses the actual input event handling that users would trigger.
Consider using wrapper.find('input') and triggering actual input events for more realistic testing:
test('updates credit label when editable amount changes', async () => {
const wrapper = mountOption({ editable: true })
const input = wrapper.find('input[type="number"]')
await input.setValue(4.2)
await wrapper.vm.$nextTick()
expect(wrapper.text()).toContain('2.00 Credits')
})This approach tests the actual user interaction flow rather than internal state mutation.
🤖 Prompt for AI Agents
tests-ui/tests/components/dialog/content/credit/CreditTopUpOption.test.ts around
lines 60-66: the test mutates vm.customAmount directly which bypasses user
interaction; modify the test to locate the numeric input (e.g.,
wrapper.find('input[type="number"]')), use wrapper.find(...).setValue(4.2) or
trigger an input event to simulate user typing, await the setValue and
wrapper.vm.$nextTick(), then assert the updated label contains '2.00 Credits'.
| const mockCreditsData = { | ||
| totalCredits: '10.00', | ||
| monthlyBonusCredits: '5.00', | ||
| prepaidCredits: '5.00', | ||
| totalCredits: '10.00 Credits', | ||
| monthlyBonusCredits: '5.00 Credits', | ||
| prepaidCredits: '5.00 Credits', | ||
| isLoadingBalance: false | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion | 🟠 Major
Mock data hardcodes formatted strings instead of raw values.
The mock now returns fully formatted strings like '10.00 Credits' rather than raw numeric values. This tightly couples the test to the formatting implementation and duplicates the formatting logic that useSubscriptionCredits performs. If the i18n key changes or the format changes, both the composable and these mocks must be updated.
Consider mocking the underlying data (raw cents/micros) and letting the component/composable handle formatting:
const mockCreditsData = {
- totalCredits: '10.00 Credits',
- monthlyBonusCredits: '5.00 Credits',
- prepaidCredits: '5.00 Credits',
+ totalCredits: computed(() => /* actual formatting logic */),
+ monthlyBonusCredits: computed(() => /* actual formatting logic */),
+ prepaidCredits: computed(() => /* actual formatting logic */),
isLoadingBalance: false
}Alternatively, if you must mock the composable output, ensure the mock values match the exact output format of useSubscriptionCredits and document that these are pre-formatted strings.
Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In
tests-ui/tests/platform/cloud/subscription/components/SubscriptionPanel.test.ts
around lines 19-24, the mockCreditsData uses pre-formatted strings ('10.00
Credits') which duplicates formatting logic; change the mock to provide raw
numeric values (e.g., cents or micros and booleans) that match the underlying
data shape consumed by useSubscriptionCredits so the composable/component does
the formatting, or if you must mock the composable output, replace the values
with the exact strings returned by useSubscriptionCredits and add a comment
documenting that these are pre-formatted outputs; update any assertions
accordingly.
| @@ -1,9 +1,21 @@ | |||
| import { createPinia, setActivePinia } from 'pinia' | |||
| import { beforeEach, describe, expect, it, vi } from 'vitest' | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion | 🟠 Major
Use test instead of it per project guidelines.
The coding guidelines specify using test instead of it for defining test cases in Vitest.
Apply this diff:
-import { beforeEach, describe, expect, it, vi } from 'vitest'
+import { beforeEach, describe, expect, test, vi } from 'vitest'Then replace all it( with test( throughout the file (lines 80, 86, 92, 98, 112, 118, 126, 132, 140).
Based on coding guidelines for tests-ui/**/*.test.{js,ts,jsx,tsx}.
Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In
tests-ui/tests/platform/cloud/subscription/composables/useSubscriptionCredits.test.ts
around line 2 and at lines 80, 86, 92, 98, 112, 118, 126, 132, 140, change
usages of Vitest's "it" to "test": update the import on line 2 to import "test"
instead of "it", then replace every occurrence of "it(" at the specified lines
with "test(". Ensure the file compiles by keeping the rest of each test case
unchanged and run the test suite to verify no other references to "it" remain.
Introduces cents<->usd<->credit converters plus basic formatters and adds test. Lays groundwork to start converting UI components into displaying comfy credits.
┆Issue is synchronized with this Notion page by Unito