Skip to content

Conversation

@christian-byrne
Copy link
Contributor

@christian-byrne christian-byrne commented Nov 30, 2025

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

@christian-byrne christian-byrne requested a review from a team as a code owner November 30, 2025 22:02
@dosubot dosubot bot added the size:L This PR changes 100-499 lines, ignoring generated files. label Nov 30, 2025
@github-actions
Copy link

github-actions bot commented Nov 30, 2025

🎨 Storybook Build Status

Build completed successfully!

⏰ Completed at: 12/04/2025, 08:21:52 PM UTC

🔗 Links


🎉 Your Storybook is ready for review!

@github-actions
Copy link

github-actions bot commented Nov 30, 2025

🎭 Playwright Test Results

⚠️ Tests passed with flaky tests

⏰ Completed at: 12/04/2025, 08:30:00 PM UTC

📈 Summary

  • Total Tests: 496
  • Passed: 484 ✅
  • Failed: 0
  • Flaky: 2 ⚠️
  • Skipped: 10 ⏭️

📊 Test Reports by Browser

  • chromium: View Report • ✅ 475 / ❌ 0 / ⚠️ 2 / ⏭️ 10
  • chromium-2x: View Report • ✅ 2 / ❌ 0 / ⚠️ 0 / ⏭️ 0
  • chromium-0.5x: View Report • ✅ 1 / ❌ 0 / ⚠️ 0 / ⏭️ 0
  • mobile-chrome: View Report • ✅ 6 / ❌ 0 / ⚠️ 0 / ⏭️ 0

🎉 Click on the links above to view detailed test results for each browser configuration.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 30, 2025

📝 Walkthrough

Walkthrough

Adds 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

Cohort / File(s) Summary
Credits utility + tests
src/base/credits/comfyCredits.ts, tests-ui/tests/base/credits/comfyCredits.test.ts
New module exporting COMFY_CREDIT_RATE_CENTS, COMFY_CREDIT_RATE_USD, conversion helpers (usd↔cents, cents↔credits, usd↔credits), formatting helpers (formatCredits*, formatUsd*), and FormatOptions type. Unit tests added to validate rates, conversions, round-trips, and formatting.
UI: user balance display
src/components/common/UserCredit.vue
Replaced dollar icon with wallet; switched balance rendering to use formatCreditsFromCents (defaults to 0 cents) and i18n translation for the credits unit.
UI: top-up option + tests
src/components/dialog/content/credit/CreditTopUpOption.vue, tests-ui/tests/components/dialog/content/credit/CreditTopUpOption.test.ts
Tag icon changed to wallet; refactored editable vs non-editable rendering to show USD input with inline credits when editable and stacked credits/USD when read-only; added computed formatting using comfyCredits helpers and i18n. Tests added/updated to assert credit label rendering and dynamic updates.
Subscription composable + tests + panel test
src/platform/cloud/subscription/composables/useSubscriptionCredits.ts, tests-ui/tests/platform/cloud/subscription/composables/useSubscriptionCredits.test.ts, tests-ui/tests/platform/cloud/subscription/components/SubscriptionPanel.test.ts
Replaced previous currency formatting with formatCreditsFromCents for total, monthlyBonus, and prepaid values; unified error/empty handling to formatted zero-credit string; added isLoadingBalance to the composable's returned API and updated tests/mocks to expect credit-formatted strings.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch comfy-credits-domain

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

@github-actions
Copy link

github-actions bot commented Nov 30, 2025

Bundle Size Report

Summary

  • Raw size: 17 MB baseline 17 MB — 🔴 +3.03 kB
  • Gzip: 3.38 MB baseline 3.38 MB — 🔴 +615 B
  • Brotli: 2.59 MB baseline 2.59 MB — 🔴 +96 B
  • Bundles: 97 current • 97 baseline • 37 added / 37 removed

Category Glance
App Entry Points 🔴 +3.03 kB (3.2 MB) · Vendor & Third-Party ⚪ 0 B (8.56 MB) · Other ⚪ 0 B (3.81 MB) · Graph Workspace ⚪ 0 B (972 kB) · Panels & Settings ⚪ 0 B (298 kB) · UI Components ⚪ 0 B (173 kB) · + 3 more

Per-category breakdown
App Entry Points — 3.2 MB (baseline 3.2 MB) • 🔴 +3.03 kB

Main entry bundles and manifests

File Before After Δ Raw Δ Gzip Δ Brotli
assets/index-It-djjVf.js (new) 2.98 MB 🔴 +2.98 MB 🔴 +620 kB 🔴 +471 kB
assets/index-CV8hwdrX.js (removed) 2.97 MB 🟢 -2.97 MB 🟢 -619 kB 🟢 -471 kB
assets/index-DC2GnKmY.js (removed) 223 kB 🟢 -223 kB 🟢 -47.6 kB 🟢 -39.3 kB
assets/index-ietF0x9h.js (new) 223 kB 🔴 +223 kB 🔴 +47.6 kB 🔴 +39.3 kB
assets/index-Czs58fyr.js (new) 345 B 🔴 +345 B 🔴 +243 B 🔴 +200 B
assets/index-FfkQvRqM.js (removed) 345 B 🟢 -345 B 🟢 -246 B 🟢 -229 B

Status: 3 added / 3 removed

Graph Workspace — 972 kB (baseline 972 kB) • ⚪ 0 B

Graph editor runtime, canvas, workflow orchestration

File Before After Δ Raw Δ Gzip Δ Brotli
assets/GraphView-C1EAuk-3.js (removed) 972 kB 🟢 -972 kB 🟢 -188 kB 🟢 -143 kB
assets/GraphView-jLSJa-FO.js (new) 972 kB 🔴 +972 kB 🔴 +188 kB 🔴 +143 kB

Status: 1 added / 1 removed

Views & Navigation — 6.54 kB (baseline 6.54 kB) • ⚪ 0 B

Top-level views, pages, and routed surfaces

File Before After Δ Raw Δ Gzip Δ Brotli
assets/UserSelectView-BWfxpef5.js (removed) 6.54 kB 🟢 -6.54 kB 🟢 -2.14 kB 🟢 -1.9 kB
assets/UserSelectView-CDaQY2W4.js (new) 6.54 kB 🔴 +6.54 kB 🔴 +2.14 kB 🔴 +1.9 kB

Status: 1 added / 1 removed

Panels & Settings — 298 kB (baseline 298 kB) • ⚪ 0 B

Configuration panels, inspectors, and settings screens

File Before After Δ Raw Δ Gzip Δ Brotli
assets/CreditsPanel-CF1lqSmM.js (new) 21.4 kB 🔴 +21.4 kB 🔴 +5.15 kB 🔴 +4.49 kB
assets/CreditsPanel-lcPn9-MB.js (removed) 21.4 kB 🟢 -21.4 kB 🟢 -5.15 kB 🟢 -4.49 kB
assets/KeybindingPanel-ByIC4nIT.js (new) 13.6 kB 🔴 +13.6 kB 🔴 +3.42 kB 🔴 +3.01 kB
assets/KeybindingPanel-CGJJ2y6_.js (removed) 13.6 kB 🟢 -13.6 kB 🟢 -3.42 kB 🟢 -3.01 kB
assets/ExtensionPanel-D2AWRKM5.js (removed) 10.8 kB 🟢 -10.8 kB 🟢 -2.58 kB 🟢 -2.26 kB
assets/ExtensionPanel-M-1uLj3m.js (new) 10.8 kB 🔴 +10.8 kB 🔴 +2.57 kB 🔴 +2.26 kB
assets/AboutPanel-aX2iYYVj.js (new) 9.16 kB 🔴 +9.16 kB 🔴 +2.46 kB 🔴 +2.21 kB
assets/AboutPanel-ntqODLhs.js (removed) 9.16 kB 🟢 -9.16 kB 🟢 -2.46 kB 🟢 -2.21 kB
assets/ServerConfigPanel-D7RtUVzD.js (new) 6.56 kB 🔴 +6.56 kB 🔴 +1.83 kB 🔴 +1.63 kB
assets/ServerConfigPanel-DikSDtY-.js (removed) 6.56 kB 🟢 -6.56 kB 🟢 -1.83 kB 🟢 -1.63 kB
assets/UserPanel-BkGCWL1Z.js (removed) 6.23 kB 🟢 -6.23 kB 🟢 -1.72 kB 🟢 -1.5 kB
assets/UserPanel-DrO6qw-b.js (new) 6.23 kB 🔴 +6.23 kB 🔴 +1.72 kB 🔴 +1.5 kB
assets/settings-BhbWhsRg.js 101 B 101 B ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-BXTtSH4O.js 33.3 kB 33.3 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-C9Pzn-NG.js 25.2 kB 25.2 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-CCy2fA_h.js 27.3 kB 27.3 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-CQpqEFfl.js 26.6 kB 26.6 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-DHcnxypw.js 21.7 kB 21.7 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-DhFTK9fY.js 25.1 kB 25.1 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-DlT4t_ui.js 25.9 kB 25.9 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-DRgSrIdD.js 24.2 kB 24.2 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-tjkeqiZq.js 21.1 kB 21.1 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B

Status: 6 added / 6 removed

UI Components — 173 kB (baseline 173 kB) • ⚪ 0 B

Reusable component library chunks

File Before After Δ Raw Δ Gzip Δ Brotli
assets/Load3D.vue_vue_type_script_setup_true_lang-B2Xjo6t3.js (removed) 53.9 kB 🟢 -53.9 kB 🟢 -8.52 kB 🟢 -7.31 kB
assets/Load3D.vue_vue_type_script_setup_true_lang-C-2zftO4.js (new) 53.9 kB 🔴 +53.9 kB 🔴 +8.52 kB 🔴 +7.3 kB
assets/WidgetSelect.vue_vue_type_script_setup_true_lang-8ZA4CTww.js (new) 47.6 kB 🔴 +47.6 kB 🔴 +10.3 kB 🔴 +8.91 kB
assets/WidgetSelect.vue_vue_type_script_setup_true_lang-BaZfwzB-.js (removed) 47.6 kB 🟢 -47.6 kB 🟢 -10.3 kB 🟢 -8.9 kB
assets/LazyImage.vue_vue_type_script_setup_true_lang-DVoAUMWg.js (new) 43.3 kB 🔴 +43.3 kB 🔴 +9.68 kB 🔴 +8.45 kB
assets/LazyImage.vue_vue_type_script_setup_true_lang-DvQiHvku.js (removed) 43.3 kB 🟢 -43.3 kB 🟢 -9.68 kB 🟢 -8.45 kB
assets/WidgetInputNumber.vue_vue_type_script_setup_true_lang-CiUiS57R.js (new) 12.9 kB 🔴 +12.9 kB 🔴 +3.37 kB 🔴 +2.97 kB
assets/WidgetInputNumber.vue_vue_type_script_setup_true_lang-CRngITh1.js (removed) 12.9 kB 🟢 -12.9 kB 🟢 -3.37 kB 🟢 -2.96 kB
assets/ComfyQueueButton-0pmMBHW3.js (new) 8.44 kB 🔴 +8.44 kB 🔴 +2.48 kB 🔴 +2.21 kB
assets/ComfyQueueButton-D1NUtjbp.js (removed) 8.44 kB 🟢 -8.44 kB 🟢 -2.47 kB 🟢 -2.21 kB
assets/MediaTitle.vue_vue_type_script_setup_true_lang-DgMICJGf.js (removed) 897 B 🟢 -897 B 🟢 -503 B 🟢 -436 B
assets/MediaTitle.vue_vue_type_script_setup_true_lang-LDyEDKmF.js (new) 897 B 🔴 +897 B 🔴 +501 B 🔴 +444 B
assets/UserAvatar.vue_vue_type_script_setup_true_lang-DRxvgpU7.js 1.34 kB 1.34 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/WidgetButton-DBkWxjh1.js 2.04 kB 2.04 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/WidgetLayoutField.vue_vue_type_script_setup_true_lang-VhqMWCTd.js 2.16 kB 2.16 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B

Status: 6 added / 6 removed

Data & Services — 12.5 kB (baseline 12.5 kB) • ⚪ 0 B

Stores, services, APIs, and repositories

File Before After Δ Raw Δ Gzip Δ Brotli
assets/keybindingService-CkJuX_wT.js (new) 7.51 kB 🔴 +7.51 kB 🔴 +1.83 kB 🔴 +1.57 kB
assets/keybindingService-D3pP8hgC.js (removed) 7.51 kB 🟢 -7.51 kB 🟢 -1.83 kB 🟢 -1.58 kB
assets/audioService-CfmW5Qmm.js (new) 2.2 kB 🔴 +2.2 kB 🔴 +959 B 🔴 +819 B
assets/audioService-QQdxKfQB.js (removed) 2.2 kB 🟢 -2.2 kB 🟢 -960 B 🟢 -823 B
assets/serverConfigStore-BIb40oOr.js 2.83 kB 2.83 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B

Status: 2 added / 2 removed

Utilities & Hooks — 2.94 kB (baseline 2.94 kB) • ⚪ 0 B

Helpers, composables, and utility bundles

File Before After Δ Raw Δ Gzip Δ Brotli
assets/audioUtils-BMIoD1Rd.js (removed) 1.41 kB 🟢 -1.41 kB 🟢 -651 B 🟢 -547 B
assets/audioUtils-CKEMDMcA.js (new) 1.41 kB 🔴 +1.41 kB 🔴 +650 B 🔴 +547 B
assets/mathUtil-CTARWQ-l.js 1.07 kB 1.07 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeFilterUtil-CXKCRJ-m.js 460 B 460 B ⚪ 0 B ⚪ 0 B ⚪ 0 B

Status: 1 added / 1 removed

Vendor & Third-Party — 8.56 MB (baseline 8.56 MB) • ⚪ 0 B

External libraries and shared vendor chunks

File Before After Δ Raw Δ Gzip Δ Brotli
assets/vendor-chart-DWRkrNy1.js 452 kB 452 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/vendor-other-BZzgsWym.js 3.98 MB 3.98 MB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/vendor-primevue-3gHexYD_.js 1.96 MB 1.96 MB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/vendor-three-aR6ntw5X.js 1.37 MB 1.37 MB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/vendor-tiptap-Dw558Ulu.js 232 kB 232 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/vendor-vue-CoO6TOrn.js 160 kB 160 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/vendor-xterm-BZLod3g9.js 407 kB 407 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
Other — 3.81 MB (baseline 3.81 MB) • ⚪ 0 B

Bundles that do not match a named category

File Before After Δ Raw Δ Gzip Δ Brotli
assets/WidgetRecordAudio-DhdF_rCB.js (new) 20.4 kB 🔴 +20.4 kB 🔴 +5.24 kB 🔴 +4.63 kB
assets/WidgetRecordAudio-DiNYll9A.js (removed) 20.4 kB 🟢 -20.4 kB 🟢 -5.24 kB 🟢 -4.63 kB
assets/AudioPreviewPlayer-BFzI79L7.js (new) 13.5 kB 🔴 +13.5 kB 🔴 +3.4 kB 🔴 +3.04 kB
assets/AudioPreviewPlayer-DVhbDmhK.js (removed) 13.5 kB 🟢 -13.5 kB 🟢 -3.4 kB 🟢 -3.05 kB
assets/WidgetGalleria-BB6rhuh1.js (removed) 4.1 kB 🟢 -4.1 kB 🟢 -1.44 kB 🟢 -1.3 kB
assets/WidgetGalleria-D4rXwKgW.js (new) 4.1 kB 🔴 +4.1 kB 🔴 +1.45 kB 🔴 +1.3 kB
assets/WidgetColorPicker-Eel-a338.js (removed) 3.41 kB 🟢 -3.41 kB 🟢 -1.38 kB 🟢 -1.23 kB
assets/WidgetColorPicker-RyrXNCfL.js (new) 3.41 kB 🔴 +3.41 kB 🔴 +1.38 kB 🔴 +1.23 kB
assets/WidgetMarkdown-BG72AEVD.js (removed) 3.08 kB 🟢 -3.08 kB 🟢 -1.28 kB 🟢 -1.12 kB
assets/WidgetMarkdown-E7TSB55t.js (new) 3.08 kB 🔴 +3.08 kB 🔴 +1.28 kB 🔴 +1.12 kB
assets/WidgetAudioUI-gCAvGk9p.js (new) 2.82 kB 🔴 +2.82 kB 🔴 +1.12 kB 🔴 +1.02 kB
assets/WidgetAudioUI-kFBE0tVD.js (removed) 2.82 kB 🟢 -2.82 kB 🟢 -1.12 kB 🟢 -1.02 kB
assets/WidgetTextarea-Av23y74c.js (new) 2.79 kB 🔴 +2.79 kB 🔴 +1.1 kB 🔴 +1 kB
assets/WidgetTextarea-Ct-aT0Ua.js (removed) 2.79 kB 🟢 -2.79 kB 🟢 -1.1 kB 🟢 -992 B
assets/WidgetInputText-JtE0-Vm8.js (removed) 1.99 kB 🟢 -1.99 kB 🟢 -917 B 🟢 -848 B
assets/WidgetInputText-uTfFwl67.js (new) 1.99 kB 🔴 +1.99 kB 🔴 +916 B 🔴 +846 B
assets/MediaImageBottom-D5RdEwiT.js (removed) 1.57 kB 🟢 -1.57 kB 🟢 -744 B 🟢 -649 B
assets/MediaImageBottom-DJnaqiZk.js (new) 1.57 kB 🔴 +1.57 kB 🔴 +743 B 🔴 +647 B
assets/MediaAudioBottom-CcnOrhW_.js (new) 1.52 kB 🔴 +1.52 kB 🔴 +739 B 🔴 +657 B
assets/MediaAudioBottom-Clj1E9MY.js (removed) 1.52 kB 🟢 -1.52 kB 🟢 -742 B 🟢 -659 B
assets/MediaVideoBottom-aJDy-00X.js (new) 1.52 kB 🔴 +1.52 kB 🔴 +741 B 🔴 +655 B
assets/MediaVideoBottom-CigxZi05.js (removed) 1.52 kB 🟢 -1.52 kB 🟢 -739 B 🟢 -659 B
assets/Media3DBottom-Be16FVoa.js (new) 1.5 kB 🔴 +1.5 kB 🔴 +731 B 🔴 +650 B
assets/Media3DBottom-CSO9wB8R.js (removed) 1.5 kB 🟢 -1.5 kB 🟢 -733 B 🟢 -652 B
assets/Media3DTop-BCx3tNEX.js (removed) 1.49 kB 🟢 -1.49 kB 🟢 -762 B 🟢 -650 B
assets/Media3DTop-DEqDsPq7.js (new) 1.49 kB 🔴 +1.49 kB 🔴 +765 B 🔴 +651 B
assets/WidgetSelect-COfPvUCX.js (new) 655 B 🔴 +655 B 🔴 +341 B 🔴 +289 B
assets/WidgetSelect-DSCWFXed.js (removed) 655 B 🟢 -655 B 🟢 -342 B 🟢 -290 B
assets/WidgetInputNumber-CqLniKY1.js (removed) 595 B 🟢 -595 B 🟢 -329 B 🟢 -275 B
assets/WidgetInputNumber-ZbR2Y8Hn.js (new) 595 B 🔴 +595 B 🔴 +328 B 🔴 +276 B
assets/Load3D-6kFdk2EL.js (new) 424 B 🔴 +424 B 🔴 +267 B 🔴 +224 B
assets/Load3D-EdGPsx__.js (removed) 424 B 🟢 -424 B 🟢 -265 B 🟢 -226 B
assets/WidgetLegacy--n4iEA8A.js (removed) 364 B 🟢 -364 B 🟢 -236 B 🟢 -194 B
assets/WidgetLegacy-CKDEjVWr.js (new) 364 B 🔴 +364 B 🔴 +236 B 🔴 +231 B
assets/commands-_s-RvhJR.js 13.6 kB 13.6 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-BuUILW6P.js 13 kB 13 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-BV4R6fLx.js 14.9 kB 14.9 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-BWp4HdfU.js 101 B 101 B ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-CLwPdnT6.js 14.2 kB 14.2 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-CWMchBmd.js 15.9 kB 15.9 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-DazTQhtc.js 12.9 kB 12.9 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-DmWrOe93.js 13.7 kB 13.7 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-DwiH7Kr6.js 13.8 kB 13.8 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-mS3LCNPn.js 14.5 kB 14.5 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-B1JflQcI.js 72.2 kB 72.2 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-B2lyXe48.js 114 kB 114 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-B9XEQ-pc.js 94 kB 94 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-BErKFzc-.js 73.1 kB 73.1 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-Bf7Tze-u.js 83.4 kB 83.4 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-BhGMcO4Q.js 84.3 kB 84.3 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-CPZUloNQ.js 99 kB 99 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-Cw9RZWRY.js 89 B 89 B ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-Dva0z-T2.js 86.5 kB 86.5 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-un0K9wDS.js 81.8 kB 81.8 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/MediaAudioTop-DX0t5RN3.js 1.46 kB 1.46 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/MediaImageTop-BN0aEMtg.js 1.75 kB 1.75 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/MediaVideoTop-BpvqEylZ.js 2.76 kB 2.76 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-8e6QYQW0.js 283 kB 283 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-A_9dx4yn.js 304 kB 304 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-BbD3HDi7.js 307 kB 307 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-BOJhIPft.js 369 kB 369 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-Bw_Jitw_.js 101 B 101 B ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-C-Pw33mW.js 317 kB 317 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-ChLyG0UJ.js 285 kB 285 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-CUVPxA4l.js 342 kB 342 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-Dx5Y4xrW.js 310 kB 310 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-JqO5mNmW.js 306 kB 306 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/WidgetChart-DsbKXC7D.js 2.48 kB 2.48 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/WidgetImageCompare-nVNi67CU.js 2.21 kB 2.21 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/widgetPropFilter-BIbGSUAt.js 1.28 kB 1.28 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/WidgetToggleSwitch-DkvdXJVp.js 1.58 kB 1.58 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B

Status: 17 added / 17 removed

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (2)
src/base/credits/comfyCredits.ts (1)

6-25: Consider adding input validation for edge cases.

The formatNumber function doesn't validate inputs for edge cases like NaN, Infinity, or negative values. While Intl.NumberFormat handles 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 formatNumber

Consider 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

📥 Commits

Reviewing files that changed from the base of the PR and between b50b34a and 47d2e14.

📒 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.ts
  • tests-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.ts
  • tests-ui/tests/base/credits/comfyCredits.test.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursorrules)

Use TypeScript for type safety

**/*.{ts,tsx}: Never use any type - use proper TypeScript types
Never use as any type assertions - fix the underlying type issue

Files:

  • src/base/credits/comfyCredits.ts
  • 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 run pnpm format before committing

Files:

  • src/base/credits/comfyCredits.ts
  • tests-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.ts
  • tests-ui/tests/base/credits/comfyCredits.test.ts
**/*.{ts,tsx,vue}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx,vue}: Use const settingStore = useSettingStore() and settingStore.get('Comfy.SomeSetting') to retrieve settings in TypeScript/Vue files
Use await settingStore.set('Comfy.SomeSetting', newValue) to update settings in TypeScript/Vue files
Check server capabilities using api.serverSupportsFeature('feature_name') before using enhanced features
Use api.getServerFeature('config_name', defaultValue) to retrieve server feature configuration

Enforce ESLint rules for Vue + TypeScript including: no floating promises, no unused imports, and i18n raw text restrictions in templates

Files:

  • src/base/credits/comfyCredits.ts
  • 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
Use defaultsByInstallVersion property for gradual feature rollout based on version in settings configuration

Files:

  • src/base/credits/comfyCredits.ts
  • tests-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 as src/components/**/*.{test,spec}.ts; E2E tests should be in browser_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.

centsToCredits returns an unrounded result, while creditsToCents rounds. 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.

Comment on lines 41 to 44
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')
Copy link
Contributor

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?

Copy link
Contributor

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

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 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

📥 Commits

Reviewing files that changed from the base of the PR and between 47d2e14 and e1a6407.

📒 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 use any type - use proper TypeScript types
Never use as any type 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 run pnpm format before 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}: Use const settingStore = useSettingStore() and settingStore.get('Comfy.SomeSetting') to retrieve settings in TypeScript/Vue files
Use await settingStore.set('Comfy.SomeSetting', newValue) to update settings in TypeScript/Vue files
Check server capabilities using api.serverSupportsFeature('feature_name') before using enhanced features
Use api.getServerFeature('config_name', defaultValue) to retrieve server feature configuration

Enforce 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
Use defaultsByInstallVersion property 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 as src/components/**/*.{test,spec}.ts; E2E tests should be in browser_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 test from 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 toBeCloseTo for 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.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (4)
src/base/credits/comfyCredits.ts (1)

1-29: formatNumber guard may surprise callers when minFractionDigits > default max

The min/max guard prevents Intl from throwing, which is good, but in the case where a caller sets minimumFractionDigits higher than the default maximumFractionDigits, the current fix (minimum = maximum) silently drops the requested precision. You may want to instead raise maximumFractionDigits to minimumFractionDigits (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 for amount_micros and consider localizing the "credits" suffix

This computed assumes authStore.balance?.amount_micros is 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 for amount_micros and, if it’s micros, convert to cents before calling formatCreditsFromCents.

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 like credits.balanceLabel with 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 labeling

The editable/non‑editable display logic is generally solid, but a few details are worth tightening up:

  • displayUsdAmount uses Math.max(1, editable ? customAmount.value : amount). In edge cases where amount or customAmount could be 0 or negative (e.g., transient InputNumber states), the displayed USD/credits would be clamped to 1 while handleBuyNow still uses the raw amount/customAmount. If that’s a realistic scenario, consider aligning the purchase amount with displayUsdAmount or clamping earlier (e.g., in the ref) so display and behavior always match.
  • formattedUsd manually builds a $${...toFixed(2)} string using usdToCents. Since you already have formatUsd/formatUsdFromCents in comfyCredits.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 in formattedCredits. 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

formatBalance assumes the *_micros fields from authStore.balance are already in cents (maybeCents). If those fields are still micros, this will vastly overstate the user’s credit amounts. Please confirm the unit of amount_micros, cloud_credit_balance_micros, and prepaid_balance_micros and, if needed, convert micros → cents before calling formatCreditsFromCents.

Since formatBalance produces 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

📥 Commits

Reviewing files that changed from the base of the PR and between e1a6407 and ca13f55.

📒 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 use import { cn } from '@/utils/tailwindUtil' for class merging in Vue templates

**/*.vue: Use TypeScript with Vue 3 Single File Components (.vue files)
Name Vue components in PascalCase (e.g., MenuHamburger.vue)

Files:

  • src/components/dialog/content/credit/CreditTopUpOption.vue
  • src/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.json

Files:

  • src/components/dialog/content/credit/CreditTopUpOption.vue
  • src/components/common/UserCredit.vue
  • src/platform/cloud/subscription/composables/useSubscriptionCredits.ts
  • src/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 run pnpm format before committing

Files:

  • src/components/dialog/content/credit/CreditTopUpOption.vue
  • src/components/common/UserCredit.vue
  • src/platform/cloud/subscription/composables/useSubscriptionCredits.ts
  • src/base/credits/comfyCredits.ts
src/**/*.vue

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

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

Files:

  • src/components/dialog/content/credit/CreditTopUpOption.vue
  • src/components/common/UserCredit.vue
src/**/*.{vue,ts}

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

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

Files:

  • src/components/dialog/content/credit/CreditTopUpOption.vue
  • src/components/common/UserCredit.vue
  • src/platform/cloud/subscription/composables/useSubscriptionCredits.ts
  • 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/components/dialog/content/credit/CreditTopUpOption.vue
  • src/components/common/UserCredit.vue
  • src/platform/cloud/subscription/composables/useSubscriptionCredits.ts
  • src/base/credits/comfyCredits.ts
**/*.{vue,html}

📄 CodeRabbit inference engine (CLAUDE.md)

Never use dark: or dark-theme: Tailwind variants - instead use semantic values from style.css theme, e.g. bg-node-component-surface

Files:

  • src/components/dialog/content/credit/CreditTopUpOption.vue
  • src/components/common/UserCredit.vue
**/*.{ts,tsx,vue}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx,vue}: Use const settingStore = useSettingStore() and settingStore.get('Comfy.SomeSetting') to retrieve settings in TypeScript/Vue files
Use await settingStore.set('Comfy.SomeSetting', newValue) to update settings in TypeScript/Vue files
Check server capabilities using api.serverSupportsFeature('feature_name') before using enhanced features
Use api.getServerFeature('config_name', defaultValue) to retrieve server feature configuration

Enforce 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.vue
  • src/components/common/UserCredit.vue
  • src/platform/cloud/subscription/composables/useSubscriptionCredits.ts
  • src/base/credits/comfyCredits.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/components/dialog/content/credit/CreditTopUpOption.vue
  • src/components/common/UserCredit.vue
  • src/platform/cloud/subscription/composables/useSubscriptionCredits.ts
  • src/base/credits/comfyCredits.ts
src/**/{composables,components}/**/*.{ts,tsx,vue}

📄 CodeRabbit inference engine (src/CLAUDE.md)

Clean up subscriptions in state management to prevent memory leaks

Files:

  • src/components/dialog/content/credit/CreditTopUpOption.vue
  • src/components/common/UserCredit.vue
  • src/platform/cloud/subscription/composables/useSubscriptionCredits.ts
src/**/*.{vue,ts,tsx}

📄 CodeRabbit inference engine (src/CLAUDE.md)

Follow Vue 3 composition API style guide

Files:

  • src/components/dialog/content/credit/CreditTopUpOption.vue
  • src/components/common/UserCredit.vue
  • src/platform/cloud/subscription/composables/useSubscriptionCredits.ts
  • src/base/credits/comfyCredits.ts
src/**/{components,composables}/**/*.{ts,tsx,vue}

📄 CodeRabbit inference engine (src/CLAUDE.md)

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

Files:

  • src/components/dialog/content/credit/CreditTopUpOption.vue
  • src/components/common/UserCredit.vue
  • src/platform/cloud/subscription/composables/useSubscriptionCredits.ts
src/components/**/*.vue

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

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

Files:

  • src/components/dialog/content/credit/CreditTopUpOption.vue
  • src/components/common/UserCredit.vue
src/components/**/*.{vue,css}

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

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

Files:

  • src/components/dialog/content/credit/CreditTopUpOption.vue
  • src/components/common/UserCredit.vue
src/components/**/*.{vue,ts,js}

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

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

Files:

  • src/components/dialog/content/credit/CreditTopUpOption.vue
  • src/components/common/UserCredit.vue
**/*.{ts,tsx,js}

📄 CodeRabbit inference engine (.cursorrules)

Use es-toolkit for utility functions

Files:

  • src/platform/cloud/subscription/composables/useSubscriptionCredits.ts
  • src/base/credits/comfyCredits.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursorrules)

Use TypeScript for type safety

**/*.{ts,tsx}: Never use any type - use proper TypeScript types
Never use as any type assertions - fix the underlying type issue

Files:

  • src/platform/cloud/subscription/composables/useSubscriptionCredits.ts
  • 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/platform/cloud/subscription/composables/useSubscriptionCredits.ts
  • src/base/credits/comfyCredits.ts
**/*.ts

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.ts: Define dynamic setting defaults using runtime context with functions in settings configuration
Use defaultsByInstallVersion property for gradual feature rollout based on version in settings configuration

Files:

  • src/platform/cloud/subscription/composables/useSubscriptionCredits.ts
  • src/base/credits/comfyCredits.ts
src/**/{services,composables}/**/*.{ts,tsx}

📄 CodeRabbit inference engine (src/CLAUDE.md)

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

Files:

  • src/platform/cloud/subscription/composables/useSubscriptionCredits.ts
**/composables/use*.ts

📄 CodeRabbit inference engine (AGENTS.md)

Name composables in the format useXyz.ts

Files:

  • 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 ToggleSwitch

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 Calendar component with DatePicker

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

Applied to files:

  • src/components/dialog/content/credit/CreditTopUpOption.vue
  • src/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 3

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 utilities

Applied to files:

  • src/components/dialog/content/credit/CreditTopUpOption.vue
  • src/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 state

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 : Use ref/reactive for state management in Vue 3 Composition API

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 Steps component with Stepper without panels

Applied 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 styles

Applied to files:

  • src/components/dialog/content/credit/CreditTopUpOption.vue
  • src/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() function

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 : Implement computed() for derived state in Vue 3 Composition API

Applied 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 sane

The 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

FormatOptions plus the formatCredits* / formatUsd* functions are nicely typed and keep formatting concerns (locale, options) decoupled from conversion concerns. The shared formatNumber core 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 semantics

Switching the Tag icon to pi pi-wallet better 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 components

The Tag icon update to pi pi-wallet keeps 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 good

The three computed values (totalCredits, monthlyBonusCredits, prepaidCredits) consistently use formatBalance, catch formatting errors, log a descriptive message, and fall back to formatting 0 credits. This should make the UI resilient to missing/invalid balance data while still surfacing issues in the console.

@christian-byrne christian-byrne added needs-backport Fix/change that needs to be cherry-picked to the current feature freeze branch core/1.33 Backport PRs for core 1.33 cloud/1.33 Backport PRs for cloud 1.33 labels Dec 2, 2025
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (2)
tests-ui/tests/components/dialog/content/credit/CreditTopUpOption.test.ts (1)

1-3: Prefer test over it in new Vitest suites

Project unit-test guidelines call for test instead of it. For consistency with other updated specs (e.g., comfyCredits tests), consider importing test from vitest and renaming these cases accordingly.

Also applies to: 53-66

tests-ui/tests/platform/cloud/subscription/composables/useSubscriptionCredits.test.ts (1)

2-2: Align on test instead of it for Vitest cases

This spec still imports and uses it even though the project test rules prefer test. For consistency with comfyCredits.test.ts and 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( to test(.

Also applies to: 78-147

🧹 Nitpick comments (1)
tests-ui/tests/platform/cloud/subscription/composables/useSubscriptionCredits.test.ts (1)

80-96: Avoid as any on mocked balances where possible

Several 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 of as any to 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

📥 Commits

Reviewing files that changed from the base of the PR and between ca13f55 and d1006dd.

📒 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.ts
  • tests-ui/tests/components/dialog/content/credit/CreditTopUpOption.test.ts
  • src/components/dialog/content/credit/CreditTopUpOption.vue
  • tests-ui/tests/base/credits/comfyCredits.test.ts
  • tests-ui/tests/platform/cloud/subscription/composables/useSubscriptionCredits.test.ts
  • src/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.ts
  • tests-ui/tests/components/dialog/content/credit/CreditTopUpOption.test.ts
  • tests-ui/tests/base/credits/comfyCredits.test.ts
  • tests-ui/tests/platform/cloud/subscription/composables/useSubscriptionCredits.test.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursorrules)

Use TypeScript for type safety

**/*.{ts,tsx}: Never use any type - use proper TypeScript types
Never use as any type assertions - fix the underlying type issue

Files:

  • tests-ui/tests/platform/cloud/subscription/components/SubscriptionPanel.test.ts
  • tests-ui/tests/components/dialog/content/credit/CreditTopUpOption.test.ts
  • tests-ui/tests/base/credits/comfyCredits.test.ts
  • tests-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 run pnpm format before committing

Files:

  • tests-ui/tests/platform/cloud/subscription/components/SubscriptionPanel.test.ts
  • tests-ui/tests/components/dialog/content/credit/CreditTopUpOption.test.ts
  • src/components/dialog/content/credit/CreditTopUpOption.vue
  • tests-ui/tests/base/credits/comfyCredits.test.ts
  • tests-ui/tests/platform/cloud/subscription/composables/useSubscriptionCredits.test.ts
  • src/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.ts
  • tests-ui/tests/components/dialog/content/credit/CreditTopUpOption.test.ts
  • src/components/dialog/content/credit/CreditTopUpOption.vue
  • tests-ui/tests/base/credits/comfyCredits.test.ts
  • tests-ui/tests/platform/cloud/subscription/composables/useSubscriptionCredits.test.ts
  • src/components/common/UserCredit.vue
**/*.{ts,tsx,vue}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx,vue}: Use const settingStore = useSettingStore() and settingStore.get('Comfy.SomeSetting') to retrieve settings in TypeScript/Vue files
Use await settingStore.set('Comfy.SomeSetting', newValue) to update settings in TypeScript/Vue files
Check server capabilities using api.serverSupportsFeature('feature_name') before using enhanced features
Use api.getServerFeature('config_name', defaultValue) to retrieve server feature configuration

Enforce 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.ts
  • tests-ui/tests/components/dialog/content/credit/CreditTopUpOption.test.ts
  • src/components/dialog/content/credit/CreditTopUpOption.vue
  • tests-ui/tests/base/credits/comfyCredits.test.ts
  • tests-ui/tests/platform/cloud/subscription/composables/useSubscriptionCredits.test.ts
  • src/components/common/UserCredit.vue
**/*.ts

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.ts: Define dynamic setting defaults using runtime context with functions in settings configuration
Use defaultsByInstallVersion property for gradual feature rollout based on version in settings configuration

Files:

  • tests-ui/tests/platform/cloud/subscription/components/SubscriptionPanel.test.ts
  • tests-ui/tests/components/dialog/content/credit/CreditTopUpOption.test.ts
  • tests-ui/tests/base/credits/comfyCredits.test.ts
  • tests-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.ts
  • tests-ui/tests/components/dialog/content/credit/CreditTopUpOption.test.ts
  • tests-ui/tests/base/credits/comfyCredits.test.ts
  • tests-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 as src/components/**/*.{test,spec}.ts; E2E tests should be in browser_tests/

Files:

  • tests-ui/tests/platform/cloud/subscription/components/SubscriptionPanel.test.ts
  • tests-ui/tests/components/dialog/content/credit/CreditTopUpOption.test.ts
  • tests-ui/tests/base/credits/comfyCredits.test.ts
  • tests-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 use import { cn } from '@/utils/tailwindUtil' for class merging in Vue templates

**/*.vue: Use TypeScript with Vue 3 Single File Components (.vue files)
Name Vue components in PascalCase (e.g., MenuHamburger.vue)

Files:

  • src/components/dialog/content/credit/CreditTopUpOption.vue
  • src/components/common/UserCredit.vue
src/**/*.vue

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

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

Files:

  • src/components/dialog/content/credit/CreditTopUpOption.vue
  • src/components/common/UserCredit.vue
src/**/*.{vue,ts}

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

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

Files:

  • src/components/dialog/content/credit/CreditTopUpOption.vue
  • src/components/common/UserCredit.vue
**/*.{vue,html}

📄 CodeRabbit inference engine (CLAUDE.md)

Never use dark: or dark-theme: Tailwind variants - instead use semantic values from style.css theme, e.g. bg-node-component-surface

Files:

  • src/components/dialog/content/credit/CreditTopUpOption.vue
  • src/components/common/UserCredit.vue
src/**/*.{ts,tsx,vue}

📄 CodeRabbit inference engine (src/CLAUDE.md)

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

Files:

  • src/components/dialog/content/credit/CreditTopUpOption.vue
  • src/components/common/UserCredit.vue
src/**/{composables,components}/**/*.{ts,tsx,vue}

📄 CodeRabbit inference engine (src/CLAUDE.md)

Clean up subscriptions in state management to prevent memory leaks

Files:

  • src/components/dialog/content/credit/CreditTopUpOption.vue
  • src/components/common/UserCredit.vue
src/**/*.{vue,ts,tsx}

📄 CodeRabbit inference engine (src/CLAUDE.md)

Follow Vue 3 composition API style guide

Files:

  • src/components/dialog/content/credit/CreditTopUpOption.vue
  • src/components/common/UserCredit.vue
src/**/{components,composables}/**/*.{ts,tsx,vue}

📄 CodeRabbit inference engine (src/CLAUDE.md)

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

Files:

  • src/components/dialog/content/credit/CreditTopUpOption.vue
  • src/components/common/UserCredit.vue
src/components/**/*.vue

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

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

Files:

  • src/components/dialog/content/credit/CreditTopUpOption.vue
  • src/components/common/UserCredit.vue
src/components/**/*.{vue,css}

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

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

Files:

  • src/components/dialog/content/credit/CreditTopUpOption.vue
  • src/components/common/UserCredit.vue
src/components/**/*.{vue,ts,js}

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

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

Files:

  • src/components/dialog/content/credit/CreditTopUpOption.vue
  • src/components/common/UserCredit.vue
**/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.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 features

Applied to files:

  • tests-ui/tests/platform/cloud/subscription/components/SubscriptionPanel.test.ts
  • tests-ui/tests/components/dialog/content/credit/CreditTopUpOption.test.ts
  • tests-ui/tests/base/credits/comfyCredits.test.ts
  • tests-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 codebase

Applied to files:

  • tests-ui/tests/components/dialog/content/credit/CreditTopUpOption.test.ts
  • 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/components/dialog/content/credit/CreditTopUpOption.test.ts
  • tests-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 utilities

Applied to files:

  • tests-ui/tests/components/dialog/content/credit/CreditTopUpOption.test.ts
  • tests-ui/tests/base/credits/comfyCredits.test.ts
  • tests-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 mocks

Applied to files:

  • tests-ui/tests/components/dialog/content/credit/CreditTopUpOption.test.ts
  • tests-ui/tests/base/credits/comfyCredits.test.ts
  • tests-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 guidelines

Applied to files:

  • tests-ui/tests/components/dialog/content/credit/CreditTopUpOption.test.ts
  • 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: 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 project

Applied to files:

  • tests-ui/tests/components/dialog/content/credit/CreditTopUpOption.test.ts
  • tests-ui/tests/base/credits/comfyCredits.test.ts
  • 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 possible

Applied to files:

  • tests-ui/tests/components/dialog/content/credit/CreditTopUpOption.test.ts
  • tests-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 tests

Applied to files:

  • tests-ui/tests/components/dialog/content/credit/CreditTopUpOption.test.ts
  • tests-ui/tests/base/credits/comfyCredits.test.ts
  • tests-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.json

Applied 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 ToggleSwitch

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

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 : 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 utilities

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 3

Applied 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 state

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 Calendar component with DatePicker

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 Steps component with Stepper without panels

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 : Use ref/reactive for state management in Vue 3 Composition API

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 Dropdown component with Select

Applied 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.ts
  • 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} : Use `test` instead of `it` for defining test cases in vitest

Applied to files:

  • tests-ui/tests/base/credits/comfyCredits.test.ts
  • tests-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 @.cursor

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: 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 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} : 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: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
  • 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/**/{composables,components}/**/*.{ts,tsx,vue} : Clean up subscriptions in state management to prevent memory leaks

Applied 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 strings

Applied 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 solid

Using formatCreditsFromCents with a safe ?? 0 default and appending t('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 formatting

Updating mockCreditsData and 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 good

The tests exercise both preset and editable flows, verifying "1.00 Credits" / "$2.10" as well as the recomputed "2.00 Credits" label when customAmount changes, which matches the comfyCredits helpers’ behavior.

tests-ui/tests/base/credits/comfyCredits.test.ts (1)

1-46: Comprehensive, locale-stable coverage for comfyCredits

These 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 behavior

The new cases around "0.00 Credits" defaults, specific cent values (210/420/630), and the formatCreditsFromCents error spy all line up with useSubscriptionCredits’s formatBalance and try/catch fallback, giving good regression coverage for the new credits-domain logic.

Also applies to: 78-107, 111-121, 125-135

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 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: Guard purchaseCredits with try/finally so loading state can’t get stuck

If authActions.purchaseCredits rejects, loading.value stays true, the spinner never clears, and didClickBuyNow is 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 opportunity

The 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 from FormatOptions for 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: Use test instead of it and be mindful of locale‑dependent formatting

The behavior under test looks correct, but per project test guidelines you should prefer test over it, 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.NumberFormat in 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 into formatCreditsFromCents for consistent formatting

The balance calculation itself is sound, but formatCreditsFromCents currently 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

formatBalance correctly handles undefined balances and error fallbacks, but like UserCredit.vue it lets formatCreditsFromCents pick 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

formattedCredits and formattedUsd use 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.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 6

📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7c43bf4 and b23ab6b.

📒 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.ts
  • tests-ui/tests/platform/cloud/subscription/composables/useSubscriptionCredits.test.ts
  • src/components/common/UserCredit.vue
  • tests-ui/tests/platform/cloud/subscription/components/SubscriptionPanel.test.ts
  • src/components/dialog/content/credit/CreditTopUpOption.vue
  • src/platform/cloud/subscription/composables/useSubscriptionCredits.ts
  • tests-ui/tests/components/dialog/content/credit/CreditTopUpOption.test.ts
  • src/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.ts
  • tests-ui/tests/platform/cloud/subscription/composables/useSubscriptionCredits.test.ts
  • tests-ui/tests/platform/cloud/subscription/components/SubscriptionPanel.test.ts
  • src/platform/cloud/subscription/composables/useSubscriptionCredits.ts
  • tests-ui/tests/components/dialog/content/credit/CreditTopUpOption.test.ts
  • src/base/credits/comfyCredits.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursorrules)

Use TypeScript for type safety

**/*.{ts,tsx}: Never use any type - use proper TypeScript types
Never use as any type assertions - fix the underlying type issue

Files:

  • tests-ui/tests/base/credits/comfyCredits.test.ts
  • tests-ui/tests/platform/cloud/subscription/composables/useSubscriptionCredits.test.ts
  • tests-ui/tests/platform/cloud/subscription/components/SubscriptionPanel.test.ts
  • src/platform/cloud/subscription/composables/useSubscriptionCredits.ts
  • tests-ui/tests/components/dialog/content/credit/CreditTopUpOption.test.ts
  • src/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 run pnpm format before committing

Files:

  • tests-ui/tests/base/credits/comfyCredits.test.ts
  • tests-ui/tests/platform/cloud/subscription/composables/useSubscriptionCredits.test.ts
  • src/components/common/UserCredit.vue
  • tests-ui/tests/platform/cloud/subscription/components/SubscriptionPanel.test.ts
  • src/components/dialog/content/credit/CreditTopUpOption.vue
  • src/platform/cloud/subscription/composables/useSubscriptionCredits.ts
  • tests-ui/tests/components/dialog/content/credit/CreditTopUpOption.test.ts
  • 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:

  • tests-ui/tests/base/credits/comfyCredits.test.ts
  • tests-ui/tests/platform/cloud/subscription/composables/useSubscriptionCredits.test.ts
  • src/components/common/UserCredit.vue
  • tests-ui/tests/platform/cloud/subscription/components/SubscriptionPanel.test.ts
  • src/components/dialog/content/credit/CreditTopUpOption.vue
  • src/platform/cloud/subscription/composables/useSubscriptionCredits.ts
  • tests-ui/tests/components/dialog/content/credit/CreditTopUpOption.test.ts
  • src/base/credits/comfyCredits.ts
**/*.{ts,tsx,vue}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx,vue}: Use const settingStore = useSettingStore() and settingStore.get('Comfy.SomeSetting') to retrieve settings in TypeScript/Vue files
Use await settingStore.set('Comfy.SomeSetting', newValue) to update settings in TypeScript/Vue files
Check server capabilities using api.serverSupportsFeature('feature_name') before using enhanced features
Use api.getServerFeature('config_name', defaultValue) to retrieve server feature configuration

Enforce 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
  • tests-ui/tests/platform/cloud/subscription/composables/useSubscriptionCredits.test.ts
  • src/components/common/UserCredit.vue
  • tests-ui/tests/platform/cloud/subscription/components/SubscriptionPanel.test.ts
  • src/components/dialog/content/credit/CreditTopUpOption.vue
  • src/platform/cloud/subscription/composables/useSubscriptionCredits.ts
  • tests-ui/tests/components/dialog/content/credit/CreditTopUpOption.test.ts
  • src/base/credits/comfyCredits.ts
**/*.ts

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.ts: Define dynamic setting defaults using runtime context with functions in settings configuration
Use defaultsByInstallVersion property for gradual feature rollout based on version in settings configuration

Files:

  • tests-ui/tests/base/credits/comfyCredits.test.ts
  • tests-ui/tests/platform/cloud/subscription/composables/useSubscriptionCredits.test.ts
  • tests-ui/tests/platform/cloud/subscription/components/SubscriptionPanel.test.ts
  • src/platform/cloud/subscription/composables/useSubscriptionCredits.ts
  • tests-ui/tests/components/dialog/content/credit/CreditTopUpOption.test.ts
  • 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
  • tests-ui/tests/platform/cloud/subscription/composables/useSubscriptionCredits.test.ts
  • tests-ui/tests/platform/cloud/subscription/components/SubscriptionPanel.test.ts
  • tests-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 as src/components/**/*.{test,spec}.ts; E2E tests should be in browser_tests/

Files:

  • tests-ui/tests/base/credits/comfyCredits.test.ts
  • tests-ui/tests/platform/cloud/subscription/composables/useSubscriptionCredits.test.ts
  • tests-ui/tests/platform/cloud/subscription/components/SubscriptionPanel.test.ts
  • tests-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.ts
  • src/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 use import { cn } from '@/utils/tailwindUtil' for class merging in Vue templates

**/*.vue: Use TypeScript with Vue 3 Single File Components (.vue files)
Name Vue components in PascalCase (e.g., MenuHamburger.vue)

Files:

  • src/components/common/UserCredit.vue
  • src/components/dialog/content/credit/CreditTopUpOption.vue
src/**/*.vue

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

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

Files:

  • src/components/common/UserCredit.vue
  • src/components/dialog/content/credit/CreditTopUpOption.vue
src/**/*.{vue,ts}

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

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

Files:

  • src/components/common/UserCredit.vue
  • src/components/dialog/content/credit/CreditTopUpOption.vue
  • src/platform/cloud/subscription/composables/useSubscriptionCredits.ts
  • src/base/credits/comfyCredits.ts
**/*.{vue,html}

📄 CodeRabbit inference engine (CLAUDE.md)

Never use dark: or dark-theme: Tailwind variants - instead use semantic values from style.css theme, e.g. bg-node-component-surface

Files:

  • src/components/common/UserCredit.vue
  • src/components/dialog/content/credit/CreditTopUpOption.vue
src/**/*.{ts,tsx,vue}

📄 CodeRabbit inference engine (src/CLAUDE.md)

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

Files:

  • src/components/common/UserCredit.vue
  • src/components/dialog/content/credit/CreditTopUpOption.vue
  • src/platform/cloud/subscription/composables/useSubscriptionCredits.ts
  • src/base/credits/comfyCredits.ts
src/**/{composables,components}/**/*.{ts,tsx,vue}

📄 CodeRabbit inference engine (src/CLAUDE.md)

Clean up subscriptions in state management to prevent memory leaks

Files:

  • src/components/common/UserCredit.vue
  • src/components/dialog/content/credit/CreditTopUpOption.vue
  • src/platform/cloud/subscription/composables/useSubscriptionCredits.ts
src/**/*.{vue,ts,tsx}

📄 CodeRabbit inference engine (src/CLAUDE.md)

Follow Vue 3 composition API style guide

Files:

  • src/components/common/UserCredit.vue
  • src/components/dialog/content/credit/CreditTopUpOption.vue
  • src/platform/cloud/subscription/composables/useSubscriptionCredits.ts
  • src/base/credits/comfyCredits.ts
src/**/{components,composables}/**/*.{ts,tsx,vue}

📄 CodeRabbit inference engine (src/CLAUDE.md)

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

Files:

  • src/components/common/UserCredit.vue
  • src/components/dialog/content/credit/CreditTopUpOption.vue
  • src/platform/cloud/subscription/composables/useSubscriptionCredits.ts
src/components/**/*.vue

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

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

Files:

  • src/components/common/UserCredit.vue
  • src/components/dialog/content/credit/CreditTopUpOption.vue
src/components/**/*.{vue,css}

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

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

Files:

  • src/components/common/UserCredit.vue
  • src/components/dialog/content/credit/CreditTopUpOption.vue
src/components/**/*.{vue,ts,js}

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

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

Files:

  • src/components/common/UserCredit.vue
  • src/components/dialog/content/credit/CreditTopUpOption.vue
src/**/*.ts

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

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

Files:

  • src/platform/cloud/subscription/composables/useSubscriptionCredits.ts
  • src/base/credits/comfyCredits.ts
src/**/{services,composables}/**/*.{ts,tsx}

📄 CodeRabbit inference engine (src/CLAUDE.md)

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

Files:

  • src/platform/cloud/subscription/composables/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 features

Applied to files:

  • tests-ui/tests/base/credits/comfyCredits.test.ts
  • tests-ui/tests/platform/cloud/subscription/composables/useSubscriptionCredits.test.ts
  • tests-ui/tests/platform/cloud/subscription/components/SubscriptionPanel.test.ts
  • tests-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 codebase

Applied to files:

  • tests-ui/tests/base/credits/comfyCredits.test.ts
  • tests-ui/tests/platform/cloud/subscription/composables/useSubscriptionCredits.test.ts
  • 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 project

Applied to files:

  • tests-ui/tests/base/credits/comfyCredits.test.ts
  • tests-ui/tests/platform/cloud/subscription/composables/useSubscriptionCredits.test.ts
  • tests-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 utilities

Applied to files:

  • tests-ui/tests/base/credits/comfyCredits.test.ts
  • tests-ui/tests/platform/cloud/subscription/composables/useSubscriptionCredits.test.ts
  • 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} : 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
  • 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} : Use `test` instead of `it` for defining test cases in vitest

Applied to files:

  • tests-ui/tests/base/credits/comfyCredits.test.ts
  • tests-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 mocks

Applied to files:

  • tests-ui/tests/base/credits/comfyCredits.test.ts
  • tests-ui/tests/platform/cloud/subscription/composables/useSubscriptionCredits.test.ts
  • tests-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 guidelines

Applied to files:

  • tests-ui/tests/base/credits/comfyCredits.test.ts
  • tests-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 @.cursor

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
  • 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} : 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
  • tests-ui/tests/platform/cloud/subscription/composables/useSubscriptionCredits.test.ts
  • tests-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 setup

Applied to files:

  • tests-ui/tests/base/credits/comfyCredits.test.ts
  • tests-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 tests

Applied to files:

  • tests-ui/tests/platform/cloud/subscription/composables/useSubscriptionCredits.test.ts
  • tests-ui/tests/platform/cloud/subscription/components/SubscriptionPanel.test.ts
  • tests-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 leaks

Applied 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 possible

Applied to files:

  • tests-ui/tests/platform/cloud/subscription/composables/useSubscriptionCredits.test.ts
  • tests-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.json

Applied 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 strings

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 : Replace PrimeVue InputSwitch component with ToggleSwitch

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

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 : 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 utilities

Applied to files:

  • src/components/dialog/content/credit/CreditTopUpOption.vue
  • 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 : Utilize ref and reactive for reactive state in Vue 3

Applied 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 state

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 Calendar component with DatePicker

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 Steps component with Stepper without panels

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 : Use ref/reactive for state management in Vue 3 Composition API

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 Dropdown component with Select

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 : Implement computed() for derived state in Vue 3 Composition API

Applied 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 files

Applied 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() function

Applied 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-dollar to pi-wallet better reflects the shift from USD to credits-based display.


37-45: LGTM: Proper i18n integration for credit formatting.

The implementation correctly uses formatCreditsFromCents with locale-aware formatting and appends the translated credits label via t('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 of amount_micros with 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 whether amount_micros actually 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 test instead of it per 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 (toBe for exact matches, toBeCloseTo for 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: 210 which, 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 formatBalance helper 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 isLoadingBalance to 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-dollar to pi-wallet aligns with the shift to credits-based display and matches the icon change in UserCredit.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 displayUsdAmount to 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.NumberFormat from throwing when maximumFractionDigits < minimumFractionDigits by adjusting minimumFractionDigits down 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_CENTS clarifying whether this rate is final, approved, or subject to change—this helps future maintainers understand the intended pricing model.

Comment on lines +54 to +115
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
})
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Consider simplifying 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 options

However, 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.

Comment on lines +14 to +22
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')}`
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 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 5

Repository: 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 -20

Repository: 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 3

Repository: 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 -10

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 164


🏁 Script executed:

# Find formatCreditsFromCents implementation
rg -n "formatCreditsFromCents" --type ts -B 5 -A 15

Repository: 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 -100

Repository: 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 -60

Repository: 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.

Comment on lines +1 to +67
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')
})
})
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

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.

Suggested change
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.

Comment on lines +60 to +66
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')
})
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Consider 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'.

Comment on lines 19 to 24
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
}
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

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'
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

cloud/1.33 Backport PRs for cloud 1.33 core/1.33 Backport PRs for core 1.33 needs-backport Fix/change that needs to be cherry-picked to the current feature freeze branch size:L This PR changes 100-499 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants