Skip to content

Conversation

@simula-r
Copy link
Contributor

@simula-r simula-r commented Jan 8, 2026

Summary

  • Implement the new add credits (top up) dialog.
  • Refactor the subscription dialog to make different credit types easier to understand

Changes

  • What: TopUpCreditsDialogContent.vue, SubscriptionPanel.vue, /en/main.json
  • Breaking:
  • Dependencies:

Review Focus

chrome_MDCFy5uJ3d-cut-merged-1767855064217.mp4

Relevant notion links:
https://www.notion.so/comfy-org/Implement-New-Top-Up-Dialog-with-Custom-Amount-Input-2df6d73d36508142b901fc0edb0d1fc1?source=copy_link
https://www.notion.so/comfy-org/Implement-Update-confusing-credits-remaining-this-month-message-2df6d73d36508168b7e5ed46754cec60?source=copy_link

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 8, 2026

📝 Walkthrough

Walkthrough

Replaces the TopUp credits UI with a two-column "You Pay / You Get" flow (preset amounts, synchronized USD↔credits steppers, validation, inline pricing link), updates purchase/close flow and telemetry, adds locale keys, updates SubscriptionPanel (refillsDate, focus-based refresh) and minor dialog styling.

Changes

Cohort / File(s) Summary
Top-up Dialog Rewrite
src/components/dialog/content/TopUpCreditsDialogContent.vue
Full rewrite: new two-column "You Pay / You Get" UI with preset amount buttons, FormattedNumberStepper usage, client-side validation (min/max/ceiling warnings), pricing link, telemetry, authActions.purchaseCredits(payAmount) purchase flow, toast/error handling, and cleanup/close logic.
Numeric Stepper Component
src/components/ui/stepper/FormattedNumberStepper.vue
New component: formatted numeric stepper with v-model, min/max, step (number
Localization Additions
src/locales/en/main.json
Adds multiple credits.topUp.* keys (e.g., usdAmount, viewPricing, youPay, youGet, buyCredits, minimumPurchase, maximumAmount, creditsPerDollar, amountToPayLabel, creditsToReceiveLabel, selectAmount, needMore, contactUs), adjusts several credits/subscription labels and videosEstimate.
Subscription Panel Updates
src/platform/cloud/subscription/components/SubscriptionPanel.vue,
src/platform/cloud/subscription/components/SubscriptionPanel.test.ts
Layout/styling changes to a responsive flex layout; added subscriptionStatus usage and refillsDate; new planTotalCredits/includedCreditsDisplay; window focus listener to refresh balance after Stripe checkout (uses pending_topup_timestamp expiry); test mock augmented with subscriptionStatus.
Dialog Service Styling
src/services/dialogService.ts
Styling-only: added rounded-2xl classes to Top Up dialog content/root presentation props.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant TopUpDialog as TopUp Dialog
    participant Auth as Auth Service
    participant Dialog as Dialog Service
    participant SubscriptionPanel as Subscription Panel
    participant Storage as LocalStorage

    User->>TopUpDialog: Enter amount & click "Buy Credits"
    TopUpDialog->>TopUpDialog: Validate amount, track telemetry
    TopUpDialog->>Auth: purchaseCredits(payAmount)
    Auth->>Auth: Process payment (Stripe)

    alt Purchase Success
        Auth-->>TopUpDialog: Success
        TopUpDialog->>Storage: Set pending_topup_timestamp
        TopUpDialog->>Dialog: Close top-up dialog
        TopUpDialog->>Dialog: Open subscription panel
        User->>SubscriptionPanel: Window focus
        SubscriptionPanel->>Storage: Read pending_topup_timestamp
        SubscriptionPanel->>SubscriptionPanel: Refresh balance if pending and not expired
        SubscriptionPanel-->>User: Show updated credits
    else Purchase Failure
        Auth-->>TopUpDialog: Error
        TopUpDialog-->>User: Show error toast
    end
Loading

Possibly related PRs

  • PR 7061: Adds/overlaps USD↔credits conversion and formatting helpers used by the new top-up steppers.
  • PR 7305: Overlaps TopUpCreditsDialogContent.vue changes and dialog styling/behavior; likely touches the same UI and dialog service wiring.
  • PR 7596: Also modifies TopUpCreditsDialogContent.vue and top-up UI logic; probable overlap with purchase/UX changes.
✨ Finishing touches
  • 📝 Generate docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@github-actions
Copy link

github-actions bot commented Jan 8, 2026

🎨 Storybook Build Status

Build completed successfully!

⏰ Completed at: 01/09/2026, 02:27:11 AM UTC

🔗 Links


🎉 Your Storybook is ready for review!

@github-actions
Copy link

github-actions bot commented Jan 8, 2026

🎭 Playwright Test Results

All tests passed!

⏰ Completed at: 01/09/2026, 02:32:26 AM UTC

📈 Summary

  • Total Tests: 514
  • Passed: 506 ✅
  • Failed: 0
  • Flaky: 0
  • Skipped: 8 ⏭️

📊 Test Reports by Browser

  • chromium: View Report • ✅ 494 / ❌ 0 / ⚠️ 0 / ⏭️ 8
  • chromium-2x: View Report • ✅ 2 / ❌ 0 / ⚠️ 0 / ⏭️ 0
  • chromium-0.5x: View Report • ✅ 1 / ❌ 0 / ⚠️ 0 / ⏭️ 0
  • mobile-chrome: View Report • ✅ 9 / ❌ 0 / ⚠️ 0 / ⏭️ 0

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

@github-actions
Copy link

github-actions bot commented Jan 8, 2026

Bundle Size Report

Summary

  • Raw size: 17.6 MB baseline 17.6 MB — 🔴 +9.07 kB
  • Gzip: 3.6 MB baseline 3.6 MB — 🔴 +2.07 kB
  • Brotli: 2.75 MB baseline 2.74 MB — 🔴 +1.66 kB
  • Bundles: 94 current • 94 baseline • 38 added / 38 removed

Category Glance
App Entry Points 🔴 +6.64 kB (3.24 MB) · Panels & Settings 🔴 +2.43 kB (302 kB) · Vendor & Third-Party ⚪ 0 B (9.19 MB) · Other ⚪ 0 B (3.6 MB) · Graph Workspace ⚪ 0 B (1.05 MB) · UI Components ⚪ 0 B (198 kB) · + 3 more

Per-category breakdown
App Entry Points — 3.24 MB (baseline 3.23 MB) • 🔴 +6.64 kB

Main entry bundles and manifests

File Before After Δ Raw Δ Gzip Δ Brotli
assets/index-B5EVhuz_.js (new) 3.04 MB 🔴 +3.04 MB 🔴 +639 kB 🔴 +485 kB
assets/index-D5CiEnXH.js (removed) 3.04 MB 🟢 -3.04 MB 🟢 -637 kB 🟢 -484 kB
assets/index-Bga3iMRk.js (removed) 194 kB 🟢 -194 kB 🟢 -42.3 kB 🟢 -35.2 kB
assets/index-DpOiJ-oN.js (new) 194 kB 🔴 +194 kB 🔴 +42.3 kB 🔴 +35.2 kB
assets/index-BfXS3QhJ.js (new) 345 B 🔴 +345 B 🔴 +246 B 🔴 +237 B
assets/index-DmutjGr7.js (removed) 345 B 🟢 -345 B 🟢 -244 B 🟢 -232 B

Status: 3 added / 3 removed

Graph Workspace — 1.05 MB (baseline 1.05 MB) • ⚪ 0 B

Graph editor runtime, canvas, workflow orchestration

File Before After Δ Raw Δ Gzip Δ Brotli
assets/GraphView-CBw-IdUI.js (new) 1.05 MB 🔴 +1.05 MB 🔴 +203 kB 🔴 +154 kB
assets/GraphView-CCbI6JUF.js (removed) 1.05 MB 🟢 -1.05 MB 🟢 -203 kB 🟢 -154 kB

Status: 1 added / 1 removed

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

Top-level views, pages, and routed surfaces

File Before After Δ Raw Δ Gzip Δ Brotli
assets/UserSelectView--JtBR1TP.js (removed) 6.63 kB 🟢 -6.63 kB 🟢 -2.14 kB 🟢 -1.9 kB
assets/UserSelectView-DkC_dv24.js (new) 6.63 kB 🔴 +6.63 kB 🔴 +2.14 kB 🔴 +1.89 kB

Status: 1 added / 1 removed

Panels & Settings — 302 kB (baseline 300 kB) • 🔴 +2.43 kB

Configuration panels, inspectors, and settings screens

File Before After Δ Raw Δ Gzip Δ Brotli
assets/LegacyCreditsPanel-DdSdLxTr.js (new) 25.1 kB 🔴 +25.1 kB 🔴 +5.74 kB 🔴 +5 kB
assets/LegacyCreditsPanel-CCPBXq65.js (removed) 22.7 kB 🟢 -22.7 kB 🟢 -5.25 kB 🟢 -4.6 kB
assets/KeybindingPanel-Bm3XQyEp.js (removed) 14.8 kB 🟢 -14.8 kB 🟢 -3.57 kB 🟢 -3.13 kB
assets/KeybindingPanel-xU2aYsbp.js (new) 14.8 kB 🔴 +14.8 kB 🔴 +3.57 kB 🔴 +3.12 kB
assets/ExtensionPanel-6b-4DIA-.js (removed) 11.1 kB 🟢 -11.1 kB 🟢 -2.61 kB 🟢 -2.29 kB
assets/ExtensionPanel-BCZHsm68.js (new) 11.1 kB 🔴 +11.1 kB 🔴 +2.61 kB 🔴 +2.29 kB
assets/AboutPanel-DiXAkJih.js (removed) 9.16 kB 🟢 -9.16 kB 🟢 -2.46 kB 🟢 -2.21 kB
assets/AboutPanel-DVaKImQB.js (new) 9.16 kB 🔴 +9.16 kB 🔴 +2.46 kB 🔴 +2.21 kB
assets/ServerConfigPanel-C6N5TV0t.js (new) 7.51 kB 🔴 +7.51 kB 🔴 +2.04 kB 🔴 +1.8 kB
assets/ServerConfigPanel-oIfrf0Ly.js (removed) 7.51 kB 🟢 -7.51 kB 🟢 -2.04 kB 🟢 -1.81 kB
assets/UserPanel-Bno6jmzh.js (new) 6.88 kB 🔴 +6.88 kB 🔴 +1.79 kB 🔴 +1.56 kB
assets/UserPanel-mMRCYSEN.js (removed) 6.88 kB 🟢 -6.88 kB 🟢 -1.79 kB 🟢 -1.56 kB
assets/settings-BhbWhsRg.js 101 B 101 B ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-BIdKi-OT.js 26.2 kB 26.2 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-Bu3OR-lX.js 24.6 kB 24.6 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-ByL6gy5c.js 25.4 kB 25.4 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-CjlRFMdL.js 32.8 kB 32.8 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-DkGwvylK.js 26.9 kB 26.9 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-Dyd027Dx.js 24.7 kB 24.7 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-MzsBgiwB.js 21.7 kB 21.7 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-wwBxqLH5.js 21.3 kB 21.3 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-xx2Yb6R2.js 23.8 kB 23.8 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B

Status: 6 added / 6 removed

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

Reusable component library chunks

File Before After Δ Raw Δ Gzip Δ Brotli
assets/LazyImage.vue_vue_type_script_setup_true_lang-CcbFxVsi.js (removed) 63 kB 🟢 -63 kB 🟢 -12.9 kB 🟢 -11.2 kB
assets/LazyImage.vue_vue_type_script_setup_true_lang-CjWQjFxz.js (new) 63 kB 🔴 +63 kB 🔴 +12.9 kB 🔴 +11.2 kB
assets/Load3D.vue_vue_type_script_setup_true_lang-D9QkOVy9.js (new) 56.4 kB 🔴 +56.4 kB 🔴 +8.78 kB 🔴 +7.53 kB
assets/Load3D.vue_vue_type_script_setup_true_lang-DjUQJmma.js (removed) 56.4 kB 🟢 -56.4 kB 🟢 -8.78 kB 🟢 -7.53 kB
assets/WidgetSelect.vue_vue_type_script_setup_true_lang-B5pwqaef.js (new) 49 kB 🔴 +49 kB 🔴 +10.5 kB 🔴 +9.15 kB
assets/WidgetSelect.vue_vue_type_script_setup_true_lang-BmvvUhtb.js (removed) 49 kB 🟢 -49 kB 🟢 -10.5 kB 🟢 -9.15 kB
assets/WidgetInputNumber.vue_vue_type_script_setup_true_lang-DmvCmoZs.js (new) 10.9 kB 🔴 +10.9 kB 🔴 +2.89 kB 🔴 +2.57 kB
assets/WidgetInputNumber.vue_vue_type_script_setup_true_lang-ePdpvxmj.js (removed) 10.9 kB 🟢 -10.9 kB 🟢 -2.89 kB 🟢 -2.55 kB
assets/ComfyQueueButton-ClhFJrhY.js (removed) 8.83 kB 🟢 -8.83 kB 🟢 -2.58 kB 🟢 -2.3 kB
assets/ComfyQueueButton-DBcC9a27.js (new) 8.83 kB 🔴 +8.83 kB 🔴 +2.58 kB 🔴 +2.29 kB
assets/WidgetWithControl.vue_vue_type_script_setup_true_lang-b_begn3N.js (removed) 3.72 kB 🟢 -3.72 kB 🟢 -1.45 kB 🟢 -1.32 kB
assets/WidgetWithControl.vue_vue_type_script_setup_true_lang-BojZqBLE.js (new) 3.72 kB 🔴 +3.72 kB 🔴 +1.45 kB 🔴 +1.32 kB
assets/WidgetButton-DcWHmsNi.js (new) 2.21 kB 🔴 +2.21 kB 🔴 +993 B 🔴 +896 B
assets/WidgetButton-FrZcsvBV.js (removed) 2.21 kB 🟢 -2.21 kB 🟢 -994 B 🟢 -897 B
assets/WidgetLayoutField.vue_vue_type_script_setup_true_lang-B_bq-1WM.js (removed) 2.14 kB 🟢 -2.14 kB 🟢 -889 B 🟢 -773 B
assets/WidgetLayoutField.vue_vue_type_script_setup_true_lang-DM7cGJQg.js (new) 2.14 kB 🔴 +2.14 kB 🔴 +890 B 🔴 +766 B
assets/UserAvatar.vue_vue_type_script_setup_true_lang-9XQOozXG.js 1.34 kB 1.34 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B

Status: 8 added / 8 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-BFHYUe0U.js (removed) 7.51 kB 🟢 -7.51 kB 🟢 -1.83 kB 🟢 -1.58 kB
assets/keybindingService-Dsqdzh-g.js (new) 7.51 kB 🔴 +7.51 kB 🔴 +1.83 kB 🔴 +1.57 kB
assets/audioService-DjElrpAp.js (removed) 2.2 kB 🟢 -2.2 kB 🟢 -958 B 🟢 -819 B
assets/audioService-DRwKpFzL.js (new) 2.2 kB 🔴 +2.2 kB 🔴 +962 B 🔴 +824 B
assets/serverConfigStore-C7L6OKtS.js 2.83 kB 2.83 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B

Status: 2 added / 2 removed

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

Helpers, composables, and utility bundles

File Before After Δ Raw Δ Gzip Δ Brotli
assets/audioUtils-BrpEOMyy.js (removed) 1.41 kB 🟢 -1.41 kB 🟢 -651 B 🟢 -549 B
assets/audioUtils-DH93Pthh.js (new) 1.41 kB 🔴 +1.41 kB 🔴 +652 B 🔴 +541 B

Status: 1 added / 1 removed

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

External libraries and shared vendor chunks

File Before After Δ Raw Δ Gzip Δ Brotli
assets/vendor-chart-CAFSFyru.js 452 kB 452 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/vendor-other-CtqoBnhc.js 3.9 MB 3.9 MB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/vendor-primevue-uBII0nI5.js 1.95 MB 1.95 MB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/vendor-three-gKB3jfCi.js 2.08 MB 2.08 MB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/vendor-tiptap-DxCePpNw.js 232 kB 232 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/vendor-vue-Btw3Hy_q.js 160 kB 160 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/vendor-xterm-BF8peZ5_.js 420 kB 420 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
Other — 3.6 MB (baseline 3.6 MB) • ⚪ 0 B

Bundles that do not match a named category

File Before After Δ Raw Δ Gzip Δ Brotli
assets/SubscriptionRequiredDialogContent-B1J_AMB8.js (removed) 29.3 kB 🟢 -29.3 kB 🟢 -6.51 kB 🟢 -5.65 kB
assets/SubscriptionRequiredDialogContent-DjyDnMi2.js (new) 29.3 kB 🔴 +29.3 kB 🔴 +6.51 kB 🔴 +5.66 kB
assets/WidgetRecordAudio-BCotCbYy.js (removed) 20.4 kB 🟢 -20.4 kB 🟢 -5.23 kB 🟢 -4.62 kB
assets/WidgetRecordAudio-lh-28fcI.js (new) 20.4 kB 🔴 +20.4 kB 🔴 +5.23 kB 🔴 +4.63 kB
assets/AudioPreviewPlayer-DG6ciqQO.js (removed) 13.3 kB 🟢 -13.3 kB 🟢 -3.35 kB 🟢 -3 kB
assets/AudioPreviewPlayer-DxSAfDLY.js (new) 13.3 kB 🔴 +13.3 kB 🔴 +3.35 kB 🔴 +3 kB
assets/ValueControlPopover-BJ9adKao.js (removed) 5.49 kB 🟢 -5.49 kB 🟢 -1.71 kB 🟢 -1.52 kB
assets/ValueControlPopover-DSTT_k0G.js (new) 5.49 kB 🔴 +5.49 kB 🔴 +1.7 kB 🔴 +1.52 kB
assets/WidgetGalleria-BoBKXFN_.js (new) 4.1 kB 🔴 +4.1 kB 🔴 +1.44 kB 🔴 +1.31 kB
assets/WidgetGalleria-DPVwkfNL.js (removed) 4.1 kB 🟢 -4.1 kB 🟢 -1.45 kB 🟢 -1.3 kB
assets/WidgetColorPicker-Bfqqacjv.js (removed) 3.41 kB 🟢 -3.41 kB 🟢 -1.38 kB 🟢 -1.23 kB
assets/WidgetColorPicker-D_nWNRkb.js (new) 3.41 kB 🔴 +3.41 kB 🔴 +1.38 kB 🔴 +1.23 kB
assets/WidgetTextarea-BhX62vHD.js (removed) 3.08 kB 🟢 -3.08 kB 🟢 -1.21 kB 🟢 -1.08 kB
assets/WidgetTextarea-Bsejqc8T.js (new) 3.08 kB 🔴 +3.08 kB 🔴 +1.21 kB 🔴 +1.07 kB
assets/WidgetMarkdown-2E1dCciJ.js (removed) 3.08 kB 🟢 -3.08 kB 🟢 -1.28 kB 🟢 -1.13 kB
assets/WidgetMarkdown-CVdRjiXM.js (new) 3.08 kB 🔴 +3.08 kB 🔴 +1.28 kB 🔴 +1.13 kB
assets/WidgetAudioUI-CGdoPJ57.js (new) 2.89 kB 🔴 +2.89 kB 🔴 +1.17 kB 🔴 +1.06 kB
assets/WidgetAudioUI-CSCu7t52.js (removed) 2.89 kB 🟢 -2.89 kB 🟢 -1.16 kB 🟢 -1.06 kB
assets/WidgetToggleSwitch-7AjgFXxf.js (new) 2.66 kB 🔴 +2.66 kB 🔴 +1.13 kB 🔴 +1 kB
assets/WidgetToggleSwitch-B9jnH501.js (removed) 2.66 kB 🟢 -2.66 kB 🟢 -1.13 kB 🟢 -1.01 kB
assets/WidgetInputText-B6OW6joL.js (new) 1.99 kB 🔴 +1.99 kB 🔴 +921 B 🔴 +854 B
assets/WidgetInputText-BlysDJWW.js (removed) 1.99 kB 🟢 -1.99 kB 🟢 -918 B 🟢 -851 B
assets/Media3DTop-CPvQ-xs1.js (removed) 1.49 kB 🟢 -1.49 kB 🟢 -764 B 🟢 -654 B
assets/Media3DTop-xnI4IJ6J.js (new) 1.49 kB 🔴 +1.49 kB 🔴 +766 B 🔴 +653 B
assets/WidgetSelect-CTK5v6b7.js (new) 733 B 🔴 +733 B 🔴 +361 B 🔴 +325 B
assets/WidgetSelect-D5RW3Rnk.js (removed) 733 B 🟢 -733 B 🟢 -358 B 🟢 -325 B
assets/WidgetInputNumber-_6_k9UBz.js (removed) 673 B 🟢 -673 B 🟢 -344 B 🟢 -286 B
assets/WidgetInputNumber-FCNa5TLa.js (new) 673 B 🔴 +673 B 🔴 +344 B 🔴 +295 B
assets/Load3D-B0q6cuh5.js (new) 424 B 🔴 +424 B 🔴 +264 B 🔴 +224 B
assets/Load3D-BaA3G8Eg.js (removed) 424 B 🟢 -424 B 🟢 -265 B 🟢 -222 B
assets/WidgetLegacy-D7KsUuCS.js (removed) 364 B 🟢 -364 B 🟢 -236 B 🟢 -195 B
assets/WidgetLegacy-D9Z5zWWu.js (new) 364 B 🔴 +364 B 🔴 +236 B 🔴 +196 B
assets/commands-bTEY9Mp6.js 13.8 kB 13.8 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-BWp4HdfU.js 101 B 101 B ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-CcfGaui5.js 14.4 kB 14.4 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-CisfgZf5.js 13.7 kB 13.7 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-CkU12Foh.js 13 kB 13 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-CoH2DJa6.js 14.2 kB 14.2 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-COSt-Bjx.js 14.9 kB 14.9 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-DalfIW5f.js 15.9 kB 15.9 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-DfTl0eCm.js 13.5 kB 13.5 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-DwSJL865.js 13.7 kB 13.7 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-Bdc58rJq.js 97.1 kB 97.1 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-C9ZJBRdI.js 81.5 kB 81.5 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-CAL83XT3.js 84.6 kB 84.6 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-CHLLfvpG.js 82.4 kB 82.4 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-Cw9RZWRY.js 89 B 89 B ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-DDqR5EuX.js 71.3 kB 71.3 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-DLHyaEcz.js 92.1 kB 92.1 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-JzWMbEh0.js 91.2 kB 91.2 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-O7KfJeMO.js 79.9 kB 79.9 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-OzGsrlqJ.js 112 kB 112 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/MediaAudioTop-Cig_KuEt.js 1.46 kB 1.46 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/MediaImageTop-CceP_k47.js 1.75 kB 1.75 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/MediaVideoTop-Bm4APiyx.js 2.65 kB 2.65 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-aW9En70v.js 260 kB 260 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-BDbge6_f.js 279 kB 279 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-BIckSVgU.js 273 kB 273 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-BiYpVi7D.js 263 kB 263 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-Bw_Jitw_.js 101 B 101 B ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-CCEXtYfM.js 243 kB 243 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-CvmVDWYd.js 323 kB 323 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-D_wreoPJ.js 267 kB 267 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-Dz-0ZIBN.js 297 kB 297 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-VZsNmhG7.js 264 kB 264 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/OBJLoader2WorkerModule-DTMpvldF.js 109 kB 109 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/WidgetChart-d_e6D31f.js 2.48 kB 2.48 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/WidgetImageCompare-gJXsKU9X.js 3.18 kB 3.18 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/widgetPropFilter-BIbGSUAt.js 1.28 kB 1.28 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B

Status: 16 added / 16 removed

@simula-r simula-r marked this pull request as ready for review January 8, 2026 07:01
@dosubot dosubot bot added the size:XL This PR changes 500-999 lines, ignoring generated files. label Jan 8, 2026
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 6

🤖 Fix all issues with AI agents
In @src/components/dialog/content/TopUpCreditsDialogContent.vue:
- Around line 51-83: Replace the plain <button> step controls with the shared
Button component to ensure consistent styling and behavior: swap the two +/-
buttons around the pay input to use Button with variant="ghost" and
size="icon-sm", preserve the :disabled bindings (:disabled="payAmount <= 0" and
:disabled="payAmount >= MAX_AMOUNT"), keep the @click handlers calling
handlePayStep(-1) and handlePayStep(1) and retain the inner icons
(icon-[lucide--minus] / icon-[lucide--plus]); ensure props and classes that
affect layout remain (or move needed classes to the Button) so payAmount,
MAX_AMOUNT and handlePayStep continue to work unchanged.
- Around line 10-15: Replace the raw <button> used for closing with the repo's
shared Button component to follow theming and accessibility conventions: import
the Button component in TopUpCreditsDialogContent.vue, swap the plain button
markup with Button (keeping the same classes/props semantics—use the
ghost/transparent variant or equivalent and preserve cursor, icon, and hover
behavior), and wire its click to the existing handleClose method (call
handleClose directly rather than wrapping in an arrow). Ensure the icon element
(<i class="icon-[lucide--x] size-6" />) remains inside the Button and remove the
anonymous arrow in the @click handler.
- Around line 200-202: The prop isInsufficientCredits is destructured (aliased
as _isInsufficientCredits) from defineProps in TopUpCreditsDialogContent.vue but
never used; remove this dead prop by deleting the isInsufficientCredits entry
from the defineProps call (and the _isInsufficientCredits alias) so no unused
variables remain, ensuring any dependent logic is unaffected and updating the
component signature accordingly.
- Around line 213-216: Replace the locally defined CREDITS_PER_DOLLAR constant
in TopUpCreditsDialogContent.vue with an import of the centralized
CREDITS_PER_USD (or equivalent) from the comfyCredits module and update all
places that multiply/divide by CREDITS_PER_DOLLAR to use the module's utility
functions (usdToCredits() and creditsToUsd()) instead; specifically remove the
local CREDITS_PER_DOLLAR = 211, add an import for CREDITS_PER_USD and the
utilities from src/base/credits/comfyCredits.ts, then refactor usages in methods
and computed properties that convert between USD and credits to call
usdToCredits(amount) or creditsToUsd(credits) to keep a single source of truth.

In @src/platform/cloud/subscription/components/SubscriptionPanel.vue:
- Around line 318-325: The computed refillsDate currently builds MM/DD/YY
manually; replace it to use a locale-aware formatter or the existing
formattedRenewalDate from useSubscription() to ensure consistency and respect
user locale. Update the refillsDate computed to either call formattedRenewalDate
(if it already represents the same renewal_date) or format
subscriptionStatus.value.renewal_date with Intl.DateTimeFormat configured for
short date (two-digit year) so the output matches other date displays and user
locale settings.
📜 Review details

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b3d8767 and 7b0662c.

📒 Files selected for processing (5)
  • src/components/dialog/content/TopUpCreditsDialogContent.vue
  • src/locales/en/main.json
  • src/platform/cloud/subscription/components/SubscriptionPanel.test.ts
  • src/platform/cloud/subscription/components/SubscriptionPanel.vue
  • src/services/dialogService.ts
🧰 Additional context used
📓 Path-based instructions (16)
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/services/dialogService.ts
  • src/platform/cloud/subscription/components/SubscriptionPanel.vue
  • src/components/dialog/content/TopUpCreditsDialogContent.vue
  • src/platform/cloud/subscription/components/SubscriptionPanel.test.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/services/dialogService.ts
  • src/platform/cloud/subscription/components/SubscriptionPanel.test.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/services/dialogService.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/services/dialogService.ts
  • src/platform/cloud/subscription/components/SubscriptionPanel.vue
  • src/components/dialog/content/TopUpCreditsDialogContent.vue
  • src/platform/cloud/subscription/components/SubscriptionPanel.test.ts
src/**/*.{vue,ts,tsx}

📄 CodeRabbit inference engine (src/CLAUDE.md)

Follow Vue 3 composition API style guide

Files:

  • src/services/dialogService.ts
  • src/platform/cloud/subscription/components/SubscriptionPanel.vue
  • src/components/dialog/content/TopUpCreditsDialogContent.vue
  • src/platform/cloud/subscription/components/SubscriptionPanel.test.ts
**/*.{ts,tsx,vue}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx,vue}: Use TypeScript exclusively; do not write new JavaScript code
Use sorted and grouped imports organized by plugin/source
Enforce ESLint rules including Vue + TypeScript rules, disallow floating promises, disallow unused imports, and restrict i18n raw text in templates
Do not use any type or as any type assertions; fix the underlying type issue instead
Write code that is expressive and self-documenting; avoid redundant comments and clean as you go
Keep functions short and functional; minimize nesting and follow the arrow anti-pattern
Avoid mutable state; prefer immutability and assignment at point of declaration
Use function declarations instead of function expressions when possible
Use es-toolkit for utility functions
Implement proper error handling in code

Files:

  • src/services/dialogService.ts
  • src/platform/cloud/subscription/components/SubscriptionPanel.vue
  • src/components/dialog/content/TopUpCreditsDialogContent.vue
  • src/platform/cloud/subscription/components/SubscriptionPanel.test.ts
**/*.{ts,tsx,vue,js,jsx,json,css}

📄 CodeRabbit inference engine (AGENTS.md)

Apply Prettier formatting with 2-space indentation, single quotes, no trailing semicolons, and 80-character line width

Files:

  • src/services/dialogService.ts
  • src/platform/cloud/subscription/components/SubscriptionPanel.vue
  • src/components/dialog/content/TopUpCreditsDialogContent.vue
  • src/platform/cloud/subscription/components/SubscriptionPanel.test.ts
  • src/locales/en/main.json
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Minimize the surface area (exported values) of each module and composable

Files:

  • src/services/dialogService.ts
  • src/platform/cloud/subscription/components/SubscriptionPanel.test.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/platform/cloud/subscription/components/SubscriptionPanel.vue
  • src/components/dialog/content/TopUpCreditsDialogContent.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/platform/cloud/subscription/components/SubscriptionPanel.vue
  • src/components/dialog/content/TopUpCreditsDialogContent.vue
  • src/platform/cloud/subscription/components/SubscriptionPanel.test.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/platform/cloud/subscription/components/SubscriptionPanel.vue
  • src/components/dialog/content/TopUpCreditsDialogContent.vue
  • src/platform/cloud/subscription/components/SubscriptionPanel.test.ts
**/*.vue

📄 CodeRabbit inference engine (AGENTS.md)

**/*.vue: Use Vue 3.5+ with TypeScript in .vue files, exclusively using Composition API with <script setup lang="ts"> syntax
Use Tailwind 4 for styling in Vue components; avoid <style> blocks
Name Vue components using PascalCase (e.g., MenuHamburger.vue)
Use Vue 3.5 TypeScript-style default prop declaration with reactive props destructuring; do not use withDefaults or runtime props declaration
Prefer computed() over ref with watch when deriving values
Prefer useModel over separately defining prop and emit for two-way binding
Use vue-i18n in composition API for string literals; place new translation entries in src/locales/en/main.json
Use cn() utility function from @/utils/tailwindUtil for merging Tailwind class names; do not use :class="[]" syntax
Do not use the dark: Tailwind variant; use semantic values from the style.css theme instead (e.g., bg-node-component-surface)
Do not use !important or the ! important prefix for Tailwind classes; find and correct interfering !important classes instead
Avoid new usage of PrimeVue components; use VueUse, shadcn/vue, or Reka UI instead
Leverage VueUse functions for performance-enhancing styles in Vue components
Implement proper props and emits definitions in Vue components
Utilize Vue 3's Teleport component when needed
Use Suspense for async components
Follow Vue 3 style guide and naming conventions

Files:

  • src/platform/cloud/subscription/components/SubscriptionPanel.vue
  • src/components/dialog/content/TopUpCreditsDialogContent.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/TopUpCreditsDialogContent.vue
src/components/**/*.{vue,css}

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

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

Files:

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

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

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

Files:

  • src/components/dialog/content/TopUpCreditsDialogContent.vue
**/*.test.ts

📄 CodeRabbit inference engine (AGENTS.md)

**/*.test.ts: Use unit/component tests in tests-ui/ or src/**/*.test.ts with Vitest framework
For mocking in tests, leverage Vitest utilities; keep module mocks contained and avoid global mutable state within test files
Do not write change detector tests or tests dependent on non-behavioral features like utility classes or styles
Aim for behavioral coverage of critical and new features in unit tests

Files:

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

Applied to files:

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

Applied to files:

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

Applied to files:

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

Applied to files:

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

Applied to files:

  • src/services/dialogService.ts
  • src/platform/cloud/subscription/components/SubscriptionPanel.vue
  • src/components/dialog/content/TopUpCreditsDialogContent.vue
  • src/platform/cloud/subscription/components/SubscriptionPanel.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:

  • src/platform/cloud/subscription/components/SubscriptionPanel.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 TabMenu component with Tabs without panels

Applied to files:

  • src/platform/cloud/subscription/components/SubscriptionPanel.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/platform/cloud/subscription/components/SubscriptionPanel.vue
📚 Learning: 2025-12-06T02:11:00.385Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7137
File: src/components/rightSidePanel/RightSidePanel.vue:174-180
Timestamp: 2025-12-06T02:11:00.385Z
Learning: PrimeVue components have poor TypeScript typing, so type assertions (like `as RightSidePanelTab`) may be necessary when handling emitted events or prop values from PrimeVue components like TabList.

Applied to files:

  • src/platform/cloud/subscription/components/SubscriptionPanel.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 setup() function in Vue 3 Composition API

Applied to files:

  • src/platform/cloud/subscription/components/SubscriptionPanel.vue
📚 Learning: 2025-12-21T06:04:12.562Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-21T06:04:12.562Z
Learning: Applies to **/*.vue : Utilize Vue 3's Teleport component when needed

Applied to files:

  • src/platform/cloud/subscription/components/SubscriptionPanel.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 Vue 3's Teleport component when needed

Applied to files:

  • src/platform/cloud/subscription/components/SubscriptionPanel.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 Sidebar component with Drawer

Applied to files:

  • src/platform/cloud/subscription/components/SubscriptionPanel.vue
📚 Learning: 2025-12-21T06:04:12.562Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-21T06:04:12.562Z
Learning: Applies to **/*.vue : Use Vue 3.5+ with TypeScript in `.vue` files, exclusively using Composition API with `<script setup lang="ts">` syntax

Applied to files:

  • src/platform/cloud/subscription/components/SubscriptionPanel.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 lifecycle hooks: onMounted, onUpdated in Vue 3 Composition API

Applied to files:

  • src/platform/cloud/subscription/components/SubscriptionPanel.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 OverlayPanel component with Popover

Applied to files:

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

Applied to files:

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

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

Applied to files:

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

Applied to files:

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

Applied to files:

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

Applied to files:

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

Applied to files:

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

Applied to files:

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

Applied to files:

  • src/platform/cloud/subscription/components/SubscriptionPanel.vue
  • src/components/dialog/content/TopUpCreditsDialogContent.vue
📚 Learning: 2025-12-18T16:03:02.066Z
Learnt from: henrikvilhelmberglund
Repo: Comfy-Org/ComfyUI_frontend PR: 7617
File: src/components/actionbar/ComfyActionbar.vue:301-308
Timestamp: 2025-12-18T16:03:02.066Z
Learning: In the ComfyUI frontend queue system, useQueuePendingTaskCountStore().count indicates the number of tasks in the queue, where count = 1 means a single active/running task and count > 1 means there are pending tasks in addition to the active task. Therefore, in src/components/actionbar/ComfyActionbar.vue, enable the 'Clear Pending Tasks' button only when count > 1 to avoid clearing the currently running task. The active task should be canceled using the 'Cancel current run' button instead. This rule should be enforced via a conditional check on the queue count, with appropriate disabled/aria-disabled states for accessibility, and tests should verify behavior for count = 1 and count > 1.

Applied to files:

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

Applied to files:

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

Applied to files:

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

Applied to files:

  • src/platform/cloud/subscription/components/SubscriptionPanel.test.ts
⏰ 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). (12)
  • GitHub Check: playwright-tests-chromium-sharded (5, 8)
  • GitHub Check: playwright-tests-chromium-sharded (7, 8)
  • GitHub Check: playwright-tests-chromium-sharded (2, 8)
  • GitHub Check: playwright-tests-chromium-sharded (3, 8)
  • GitHub Check: playwright-tests-chromium-sharded (6, 8)
  • GitHub Check: playwright-tests-chromium-sharded (1, 8)
  • GitHub Check: playwright-tests-chromium-sharded (8, 8)
  • GitHub Check: playwright-tests-chromium-sharded (4, 8)
  • GitHub Check: playwright-tests (mobile-chrome)
  • GitHub Check: playwright-tests (chromium-0.5x)
  • GitHub Check: playwright-tests (chromium-2x)
  • GitHub Check: test
🔇 Additional comments (7)
src/services/dialogService.ts (1)

397-398: LGTM! Consistent dialog styling.

The rounded-2xl class addition aligns with the styling used in showLayoutDialog and matches the new TopUpCreditsDialogContent.vue container styling.

src/platform/cloud/subscription/components/SubscriptionPanel.test.ts (1)

36-38: LGTM! Test mock correctly extended.

The subscriptionStatus mock appropriately mirrors the updated useSubscription() return value, enabling tests to validate the component's new refillsDate and related computed properties.

src/platform/cloud/subscription/components/SubscriptionPanel.vue (1)

123-155: LGTM! Semantic table layout with proper loading states.

The table-based credit breakdown is accessible and appropriately uses skeleton placeholders during loading. The structure clearly presents the "included" vs "additional" credits distinction.

src/locales/en/main.json (2)

1921-1939: LGTM! Comprehensive locale additions for the new top-up dialog.

The new localization keys (usdAmount, youPay, youGet, buyCredits, minimumPurchase, maximumAmount, etc.) properly support the redesigned UI with preset amounts and the You Pay/You Get editor layout.


1976-1978: Interpolation alignment confirmed.

All usages of creditsRemainingThisMonth and creditsRemainingThisYear in SubscriptionPanel.vue (lines 329-334) correctly pass the required date parameter via refillsDate.value.

src/components/dialog/content/TopUpCreditsDialogContent.vue (2)

423-449: LGTM! Well-structured purchase flow.

The handleBuy implementation correctly:

  • Guards against double-clicks with loading.value check
  • Tracks telemetry before the purchase
  • Handles errors gracefully with localized toast messages
  • Transitions to the subscription panel after success to show updated credits
  • Preserves top-up tracking for the focus-based balance refresh

272-303: Thoughtful cursor management for formatted inputs.

The formatWithCursor function properly maintains cursor position when formatting numbers with thousands separators. This significantly improves UX for direct input editing. The digit-counting approach correctly handles the position mapping between raw and formatted strings.

@simula-r simula-r marked this pull request as draft January 8, 2026 07:16
@simula-r simula-r marked this pull request as ready for review January 8, 2026 07:32
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: 9

🤖 Fix all issues with AI agents
In @src/components/dialog/content/TopUpCreditsDialogContent.vue:
- Around line 163-168: The anchor uses a hardcoded enterprise URL; switch it to
the shared external link helper by importing and using useExternalLink in
TopUpCreditsDialogContent.vue and replace the href with
docsPaths.cloudEnterprise (or add docsPaths.cloudEnterprise to the external link
config if missing) so the link is managed consistently like the existing pricing
link; ensure target and class remain the same and that useExternalLink is used
where other external links are handled.
- Around line 129-135: The plus button lacks an accessible label; update the
button element that calls handleCreditsStep(1) and uses MAX_AMOUNT to include an
aria-label (e.g., aria-label binding to a translated string) so screen readers
describe its action, and add the matching translation key/value to
src/locales/en/main.json so the aria-label can be localized.
- Around line 61-67: Add an accessible aria-label to the minus step button by
adding an aria-label bound to your i18n key (e.g.
:aria-label="$t('topUp.decrease')") on the button that calls handlePayStep(-1)
and ensure the button remains :disabled="payAmount <= 0"; then add the
corresponding "topUp.decrease" entry to the English locale file (main.json) with
an appropriate string like "Decrease amount" so screen readers get a descriptive
label.
- Around line 105-111: The minus step button in TopUpCreditsDialogContent.vue
lacks an accessible label; add an aria-label binding like
:aria-label="$t('topUpCredits.decrease')" to the <button> that calls
handleCreditsStep(-1), and add the corresponding translation key
"topUpCredits.decrease" (e.g. "Decrease credits" or "Decrease by one") to
src/locales/en/main.json so screen readers get a descriptive label.
- Around line 14-19: The close button currently uses a plain <button>; replace
it with the shared IconButton component (IconButton) and wire its click to the
existing handleClose method (use @click="handleClose", not an inline arrow).
Import IconButton in the component, replace the button block with IconButton
using the appropriate props (size/variant/class) or slot to render the <i
class="icon-[lucide--x] size-6" /> icon, and adjust styling props to match the
project's IconButton API; if IconButton lacks a required prop, document why a
plain button is retained.
- Around line 87-93: The plus-step button lacks an accessible label—add an
aria-label that uses a translation key (e.g. aria-label="$t('topup.add_step')")
on the button next to the existing :disabled and @click bindings (the button
that calls handlePayStep and checks payAmount and MAX_AMOUNT); then add the
corresponding English translation key ("topup.add_step": "Add one step" or
similar) to the English locale file so screen readers get a meaningful
description.

In @src/platform/cloud/subscription/components/SubscriptionPanel.vue:
- Around line 318-325: The refillsDate computed property currently builds a
US-centric MM/DD/YY string manually; update refillsDate to use
Intl.DateTimeFormat with the user's locale (e.g., navigator.language) and
options to produce a short two-digit year format so the formatting is
locale-aware; locate the computed refillsDate and replace the manual date
construction that reads subscriptionStatus.value.renewal_date with parsing to a
Date and formatting via new Intl.DateTimeFormat(locale, { month: '2-digit', day:
'2-digit', year: '2-digit' }).format(date), preserving the early-return when
renewal_date is missing.
- Around line 401-428: The focus-based refresh in handleWindowFocus silently
suppresses errors and uses localStorage calls that can throw; wrap all
localStorage operations (getItem/removeItem for PENDING_TOPUP_KEY) in try/catch
to prevent private-mode/storage-quota exceptions from crashing the component,
and change the void handleRefresh() call to an awaited call inside a try/catch
so refresh failures are caught; in the catch blocks log the error (using
console.error or processLogger) and optionally surface a user-facing toast if
available, while preserving the existing TOPUP_EXPIRY_MS logic and ensuring the
event listener added in onMounted is still removed in onBeforeUnmount.
📜 Review details

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7b0662c and 63b2c47.

📒 Files selected for processing (2)
  • src/components/dialog/content/TopUpCreditsDialogContent.vue
  • src/platform/cloud/subscription/components/SubscriptionPanel.vue
🧰 Additional context used
📓 Path-based instructions (12)
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/TopUpCreditsDialogContent.vue
  • src/platform/cloud/subscription/components/SubscriptionPanel.vue
src/**/*.{vue,ts}

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

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

Files:

  • src/components/dialog/content/TopUpCreditsDialogContent.vue
  • src/platform/cloud/subscription/components/SubscriptionPanel.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/TopUpCreditsDialogContent.vue
  • src/platform/cloud/subscription/components/SubscriptionPanel.vue
src/**/{composables,components}/**/*.{ts,tsx,vue}

📄 CodeRabbit inference engine (src/CLAUDE.md)

Clean up subscriptions in state management to prevent memory leaks

Files:

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

📄 CodeRabbit inference engine (src/CLAUDE.md)

Follow Vue 3 composition API style guide

Files:

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

📄 CodeRabbit inference engine (src/CLAUDE.md)

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

Files:

  • src/components/dialog/content/TopUpCreditsDialogContent.vue
  • src/platform/cloud/subscription/components/SubscriptionPanel.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/TopUpCreditsDialogContent.vue
src/components/**/*.{vue,css}

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

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

Files:

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

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

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

Files:

  • src/components/dialog/content/TopUpCreditsDialogContent.vue
**/*.vue

📄 CodeRabbit inference engine (AGENTS.md)

**/*.vue: Use Vue 3.5+ with TypeScript in .vue files, exclusively using Composition API with <script setup lang="ts"> syntax
Use Tailwind 4 for styling in Vue components; avoid <style> blocks
Name Vue components using PascalCase (e.g., MenuHamburger.vue)
Use Vue 3.5 TypeScript-style default prop declaration with reactive props destructuring; do not use withDefaults or runtime props declaration
Prefer computed() over ref with watch when deriving values
Prefer useModel over separately defining prop and emit for two-way binding
Use vue-i18n in composition API for string literals; place new translation entries in src/locales/en/main.json
Use cn() utility function from @/utils/tailwindUtil for merging Tailwind class names; do not use :class="[]" syntax
Do not use the dark: Tailwind variant; use semantic values from the style.css theme instead (e.g., bg-node-component-surface)
Do not use !important or the ! important prefix for Tailwind classes; find and correct interfering !important classes instead
Avoid new usage of PrimeVue components; use VueUse, shadcn/vue, or Reka UI instead
Leverage VueUse functions for performance-enhancing styles in Vue components
Implement proper props and emits definitions in Vue components
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/TopUpCreditsDialogContent.vue
  • src/platform/cloud/subscription/components/SubscriptionPanel.vue
**/*.{ts,tsx,vue}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx,vue}: Use TypeScript exclusively; do not write new JavaScript code
Use sorted and grouped imports organized by plugin/source
Enforce ESLint rules including Vue + TypeScript rules, disallow floating promises, disallow unused imports, and restrict i18n raw text in templates
Do not use any type or as any type assertions; fix the underlying type issue instead
Write code that is expressive and self-documenting; avoid redundant comments and clean as you go
Keep functions short and functional; minimize nesting and follow the arrow anti-pattern
Avoid mutable state; prefer immutability and assignment at point of declaration
Use function declarations instead of function expressions when possible
Use es-toolkit for utility functions
Implement proper error handling in code

Files:

  • src/components/dialog/content/TopUpCreditsDialogContent.vue
  • src/platform/cloud/subscription/components/SubscriptionPanel.vue
**/*.{ts,tsx,vue,js,jsx,json,css}

📄 CodeRabbit inference engine (AGENTS.md)

Apply Prettier formatting with 2-space indentation, single quotes, no trailing semicolons, and 80-character line width

Files:

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

Applied to files:

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

Applied to files:

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

Applied to files:

  • src/components/dialog/content/TopUpCreditsDialogContent.vue
  • src/platform/cloud/subscription/components/SubscriptionPanel.vue
📚 Learning: 2025-12-05T06:11:09.383Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7177
File: src/platform/assets/components/UploadModelFooter.vue:72-78
Timestamp: 2025-12-05T06:11:09.383Z
Learning: For the ComfyUI_frontend repository, avoid suggesting comments that would be redundant when the code is already self-explanatory through descriptive naming (e.g., filenames, prop names, aria-labels). The project prefers clean code without unnecessary documentation comments.

Applied to files:

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

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

Applied to files:

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

Applied to files:

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

Applied to files:

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

Applied to files:

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

Applied to files:

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

Applied to files:

  • src/components/dialog/content/TopUpCreditsDialogContent.vue
  • src/platform/cloud/subscription/components/SubscriptionPanel.vue
📚 Learning: 2025-12-18T16:03:02.066Z
Learnt from: henrikvilhelmberglund
Repo: Comfy-Org/ComfyUI_frontend PR: 7617
File: src/components/actionbar/ComfyActionbar.vue:301-308
Timestamp: 2025-12-18T16:03:02.066Z
Learning: In the ComfyUI frontend queue system, useQueuePendingTaskCountStore().count indicates the number of tasks in the queue, where count = 1 means a single active/running task and count > 1 means there are pending tasks in addition to the active task. Therefore, in src/components/actionbar/ComfyActionbar.vue, enable the 'Clear Pending Tasks' button only when count > 1 to avoid clearing the currently running task. The active task should be canceled using the 'Cancel current run' button instead. This rule should be enforced via a conditional check on the queue count, with appropriate disabled/aria-disabled states for accessibility, and tests should verify behavior for count = 1 and count > 1.

Applied to files:

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

Applied to files:

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

Applied to files:

  • src/platform/cloud/subscription/components/SubscriptionPanel.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 TabMenu component with Tabs without panels

Applied to files:

  • src/platform/cloud/subscription/components/SubscriptionPanel.vue
📚 Learning: 2025-12-06T02:11:00.385Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7137
File: src/components/rightSidePanel/RightSidePanel.vue:174-180
Timestamp: 2025-12-06T02:11:00.385Z
Learning: PrimeVue components have poor TypeScript typing, so type assertions (like `as RightSidePanelTab`) may be necessary when handling emitted events or prop values from PrimeVue components like TabList.

Applied to files:

  • src/platform/cloud/subscription/components/SubscriptionPanel.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 setup() function in Vue 3 Composition API

Applied to files:

  • src/platform/cloud/subscription/components/SubscriptionPanel.vue
📚 Learning: 2025-12-21T06:04:12.562Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-21T06:04:12.562Z
Learning: Applies to **/*.vue : Utilize Vue 3's Teleport component when needed

Applied to files:

  • src/platform/cloud/subscription/components/SubscriptionPanel.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 Vue 3's Teleport component when needed

Applied to files:

  • src/platform/cloud/subscription/components/SubscriptionPanel.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 Sidebar component with Drawer

Applied to files:

  • src/platform/cloud/subscription/components/SubscriptionPanel.vue
📚 Learning: 2025-12-21T06:04:12.562Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-21T06:04:12.562Z
Learning: Applies to **/*.vue : Use Vue 3.5+ with TypeScript in `.vue` files, exclusively using Composition API with `<script setup lang="ts">` syntax

Applied to files:

  • src/platform/cloud/subscription/components/SubscriptionPanel.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 lifecycle hooks: onMounted, onUpdated in Vue 3 Composition API

Applied to files:

  • src/platform/cloud/subscription/components/SubscriptionPanel.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 OverlayPanel component with Popover

Applied to files:

  • src/platform/cloud/subscription/components/SubscriptionPanel.vue
⏰ 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: lint-and-format
  • GitHub Check: setup
  • GitHub Check: test
  • GitHub Check: collect
🔇 Additional comments (7)
src/platform/cloud/subscription/components/SubscriptionPanel.vue (6)

87-88: LGTM! Responsive layout improvement.

The flex-based responsive layout with shrink-0 on the credit panel prevents unwanted shrinking and provides better control over the layout behavior across screen sizes.


101-105: LGTM! Improved button positioning and icon visibility.

The increased spacing (top-4 right-4) and larger icon size (text-sm) improve visual hierarchy and touch target accessibility.


179-179: LGTM! Consistent spacing applied.

The flex-col gap-2 wrapper provides consistent spacing for the benefits section.


270-270: LGTM! Correct lifecycle hook imports.

The onMounted and onBeforeUnmount imports are properly added for managing the focus event listener lifecycle.


302-302: LGTM! Required for renewal date display.

The subscriptionStatus is properly destructured to access the renewal date for the refills label.


327-345: LGTM! Proper i18n usage for labels and number formatting.

The computed properties correctly use t() for translations with date parameters and n() for locale-aware number formatting. The credit display logic is clear and well-structured.

src/components/dialog/content/TopUpCreditsDialogContent.vue (1)

433-459: No changes needed. The tracking cleanup is properly handled.

The design is intentional and correct: handleClose(false) preserves tracking so that when the subscription panel loads and UsageLogsTable fetches audit events, checkForCompletedTopup() can detect when the credit_added event appears and automatically clear the tracking (line 48 in topupTracker.ts). Additionally, tracking has a 24-hour auto-expiration as a safety net. No stale tracking persists indefinitely.

Likely an incorrect or invalid review comment.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🤖 Fix all issues with AI agents
In @src/components/dialog/content/TopUpCreditsDialogContent.vue:
- Around line 14-19: The icon-only close button in TopUpCreditsDialogContent.vue
(the <button> with @click="() => handleClose()") lacks an accessible name; add
an aria-label (for example aria-label="Close" or a localized string) to that
button element so screen readers can announce its purpose, and keep the click
handler calling handleClose (or simplify to @click="handleClose") to preserve
behavior.
- Line 3: Replace the hard-coded shadow utility in the TopUpCreditsDialogContent
component's root element: find the class string containing
shadow-[1px_1px_8px_0_rgba(0,0,0,0.4)] in TopUpCreditsDialogContent.vue and swap
it to shadow-interface so the component uses the design system token (which maps
to --palette-interface-panel-box-shadow) for consistent panel shadows.
- Around line 2-4: The dialog's root div in TopUpCreditsDialogContent.vue uses a
fixed min-w-[460px] which forces horizontal scrolling on small viewports;
replace that constraint with responsive sizing such as w-full max-w-[460px] (or
max-w-[460px] 2xl:max-w-full w-full) on the same div so the dialog fills small
screens while capping width on larger screens, keeping the existing rounded,
border and shadow classes intact.
📜 Review details

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 63b2c47 and 19d35eb.

📒 Files selected for processing (1)
  • src/components/dialog/content/TopUpCreditsDialogContent.vue
🧰 Additional context used
📓 Path-based instructions (12)
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/TopUpCreditsDialogContent.vue
src/**/*.{vue,ts}

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

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

Files:

  • src/components/dialog/content/TopUpCreditsDialogContent.vue
src/**/*.{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/TopUpCreditsDialogContent.vue
src/**/{composables,components}/**/*.{ts,tsx,vue}

📄 CodeRabbit inference engine (src/CLAUDE.md)

Clean up subscriptions in state management to prevent memory leaks

Files:

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

📄 CodeRabbit inference engine (src/CLAUDE.md)

Follow Vue 3 composition API style guide

Files:

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

📄 CodeRabbit inference engine (src/CLAUDE.md)

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

Files:

  • src/components/dialog/content/TopUpCreditsDialogContent.vue
src/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/TopUpCreditsDialogContent.vue
src/components/**/*.{vue,css}

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

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

Files:

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

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

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

Files:

  • src/components/dialog/content/TopUpCreditsDialogContent.vue
**/*.vue

📄 CodeRabbit inference engine (AGENTS.md)

**/*.vue: Use Vue 3.5+ with TypeScript in .vue files, exclusively using Composition API with <script setup lang="ts"> syntax
Use Tailwind 4 for styling in Vue components; avoid <style> blocks
Name Vue components using PascalCase (e.g., MenuHamburger.vue)
Use Vue 3.5 TypeScript-style default prop declaration with reactive props destructuring; do not use withDefaults or runtime props declaration
Prefer computed() over ref with watch when deriving values
Prefer useModel over separately defining prop and emit for two-way binding
Use vue-i18n in composition API for string literals; place new translation entries in src/locales/en/main.json
Use cn() utility function from @/utils/tailwindUtil for merging Tailwind class names; do not use :class="[]" syntax
Do not use the dark: Tailwind variant; use semantic values from the style.css theme instead (e.g., bg-node-component-surface)
Do not use !important or the ! important prefix for Tailwind classes; find and correct interfering !important classes instead
Avoid new usage of PrimeVue components; use VueUse, shadcn/vue, or Reka UI instead
Leverage VueUse functions for performance-enhancing styles in Vue components
Implement proper props and emits definitions in Vue components
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/TopUpCreditsDialogContent.vue
**/*.{ts,tsx,vue}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx,vue}: Use TypeScript exclusively; do not write new JavaScript code
Use sorted and grouped imports organized by plugin/source
Enforce ESLint rules including Vue + TypeScript rules, disallow floating promises, disallow unused imports, and restrict i18n raw text in templates
Do not use any type or as any type assertions; fix the underlying type issue instead
Write code that is expressive and self-documenting; avoid redundant comments and clean as you go
Keep functions short and functional; minimize nesting and follow the arrow anti-pattern
Avoid mutable state; prefer immutability and assignment at point of declaration
Use function declarations instead of function expressions when possible
Use es-toolkit for utility functions
Implement proper error handling in code

Files:

  • src/components/dialog/content/TopUpCreditsDialogContent.vue
**/*.{ts,tsx,vue,js,jsx,json,css}

📄 CodeRabbit inference engine (AGENTS.md)

Apply Prettier formatting with 2-space indentation, single quotes, no trailing semicolons, and 80-character line width

Files:

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

Applied to files:

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

Applied to files:

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

Applied to files:

  • src/components/dialog/content/TopUpCreditsDialogContent.vue
📚 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/TopUpCreditsDialogContent.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/TopUpCreditsDialogContent.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 OverlayPanel component with Popover

Applied to files:

  • src/components/dialog/content/TopUpCreditsDialogContent.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 InlineMessage component with Message

Applied to files:

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

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

Applied to files:

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

Applied to files:

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

Applied to files:

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

Applied to files:

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

Applied to files:

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

Applied to files:

  • src/components/dialog/content/TopUpCreditsDialogContent.vue
📚 Learning: 2025-12-18T16:03:02.066Z
Learnt from: henrikvilhelmberglund
Repo: Comfy-Org/ComfyUI_frontend PR: 7617
File: src/components/actionbar/ComfyActionbar.vue:301-308
Timestamp: 2025-12-18T16:03:02.066Z
Learning: In the ComfyUI frontend queue system, useQueuePendingTaskCountStore().count indicates the number of tasks in the queue, where count = 1 means a single active/running task and count > 1 means there are pending tasks in addition to the active task. Therefore, in src/components/actionbar/ComfyActionbar.vue, enable the 'Clear Pending Tasks' button only when count > 1 to avoid clearing the currently running task. The active task should be canceled using the 'Cancel current run' button instead. This rule should be enforced via a conditional check on the queue count, with appropriate disabled/aria-disabled states for accessibility, and tests should verify behavior for count = 1 and count > 1.

Applied to files:

  • src/components/dialog/content/TopUpCreditsDialogContent.vue
⏰ 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). (3)
  • GitHub Check: lint-and-format
  • GitHub Check: test
  • GitHub Check: collect
🔇 Additional comments (5)
src/components/dialog/content/TopUpCreditsDialogContent.vue (5)

79-79: Dynamic inline styles are appropriate for auto-sizing inputs.

The inline :style bindings for dynamic input width are necessary and correctly implemented. While the coding guidelines prefer Tailwind CSS, dynamic width based on reactive content length cannot be achieved with static Tailwind classes. This is a valid exception for providing smooth auto-sizing UX.

Also applies to: 121-121


262-266: LGTM: Watch pattern correctly avoids input conflicts.

The watcher updates creditsInputValue only when the credits input is not focused, preventing conflicts with user typing. This is the correct pattern for synchronized inputs.


282-313: LGTM: Cursor preservation logic is well-implemented.

The formatWithCursor function and its usage in input handlers correctly preserve cursor position during real-time number formatting. The logic:

  1. Counts digits before the cursor in the unformatted input
  2. Finds the equivalent position in the formatted output
  3. Uses requestAnimationFrame to restore the cursor after Vue's DOM update

This sophisticated approach is necessary for smooth UX when typing into formatted number inputs with thousand separators.

Also applies to: 330-356, 358-386


433-459: LGTM: Purchase flow is well-structured with proper error handling.

The handleBuy function correctly:

  • Prevents double-clicks with loading state and validation guard
  • Tracks telemetry before purchase
  • Handles both success and error cases
  • Opens the subscription panel after successful purchase to show updated credits
  • Intentionally preserves tracking data by passing false to handleClose

The error handling provides clear feedback to users via toast notifications.


315-319: LGTM: Adaptive step logic provides excellent UX.

The adaptive step amounts (5 for <$100, 50 for <$1,000, 100 for ≥$1,000) scale appropriately with the purchase amount. Both step handlers correctly:

  • Clear the ceiling warning
  • Clamp to valid ranges
  • Synchronize between USD and credits
  • Reset the preset selection when manually stepping

The implementation provides smooth incrementing/decrementing at all scales.

Also applies to: 406-424

@simula-r simula-r added needs-backport Fix/change that needs to be cherry-picked to the current feature freeze branch cloud/1.36 Backport PRs for cloud 1.36 labels Jan 8, 2026
@simula-r simula-r force-pushed the feat(cloud)/new-top-up-dialog branch from 8174534 to 3b409c6 Compare January 9, 2026 02:25
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

Caution

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

⚠️ Outside diff range comments (1)
src/locales/en/main.json (1)

929-929: Remove trailing comma from error message.

The error message has an unusual trailing comma: "An unknown error occurred,". This appears to be a typo and should be removed for proper grammar in user-facing text.

📝 Proposed fix
-      "unknownError": "An unknown error occurred",
+      "unknownError": "An unknown error occurred"
🤖 Fix all issues with AI agents
In @src/components/dialog/content/TopUpCreditsDialogContent.vue:
- Around line 14-19: The close button in TopUpCreditsDialogContent.vue should
use the repo's common IconButton component and include an accessible name;
import IconButton from src/components/button/IconButton and replace the raw
<button> that calls handleClose() with the IconButton, passing the same click
handler (handleClose) and adding an aria-label like "Close" (or a localized
equivalent) while keeping the icon element (icon-[lucide--x]) as the child so
styling and behavior remain consistent with the design system.
- Around line 117-122: TopUpCreditsDialogContent.vue currently hardcodes the
enterprise URL; add a new entry (e.g. enterprise:
'https://www.comfy.org/cloud/enterprise') to the staticUrls object in the
useExternalLink composable and expose it via the composable's API, then replace
the hardcoded href in TopUpCreditsDialogContent.vue with the composable lookup
(e.g. externalLink('enterprise') or the returned enterpriseUrl) so the component
uses the centralized staticUrls value.
📜 Review details

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 19d35eb and 3b409c6.

📒 Files selected for processing (6)
  • src/components/dialog/content/TopUpCreditsDialogContent.vue
  • src/components/ui/stepper/FormattedNumberStepper.vue
  • src/locales/en/main.json
  • src/platform/cloud/subscription/components/SubscriptionPanel.test.ts
  • src/platform/cloud/subscription/components/SubscriptionPanel.vue
  • src/services/dialogService.ts
🧰 Additional context used
📓 Path-based instructions (14)
src/**/*.{vue,ts}

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

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

Leverage VueUse functions for performance-enhancing utilities

Files:

  • src/services/dialogService.ts
  • src/platform/cloud/subscription/components/SubscriptionPanel.test.ts
  • src/components/dialog/content/TopUpCreditsDialogContent.vue
  • src/components/ui/stepper/FormattedNumberStepper.vue
  • src/platform/cloud/subscription/components/SubscriptionPanel.vue
src/**/*.ts

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

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

src/**/*.ts: Minimize the surface area (exported values) of each module and composable
Use es-toolkit for utility functions

Files:

  • src/services/dialogService.ts
  • src/platform/cloud/subscription/components/SubscriptionPanel.test.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/services/dialogService.ts
src/**/*.{ts,tsx,vue}

📄 CodeRabbit inference engine (src/CLAUDE.md)

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

src/**/*.{ts,tsx,vue}: Use separate import type statements; do not mix inline type imports in the same statement
Sort and group imports by plugin; run pnpm format before committing
Derive component types using vue-component-type-helpers (ComponentProps, ComponentSlots) instead of separate type files
Code should be well-designed with clear names for everything; write code that is expressive and self-documenting
Avoid redundant comments and clean up code as you go; comments should explain why, not what
Ask if there is a simpler way to implement functionality; refactor complex code to simplify it
Minimize nesting depth (e.g., if () { ... } or for () { ... }); watch for arrow anti-pattern
Watch out for code smells and refactor to avoid them
Never use any type; use proper TypeScript types
Never use as any type assertions; fix the underlying type issue instead
Indent with 2 spaces; use single quotes; no trailing semicolons; max line width 80 (per .prettierrc)
Complex type definitions used in multiple related places should be extracted and named for reuse
Implement proper error handling

Files:

  • src/services/dialogService.ts
  • src/platform/cloud/subscription/components/SubscriptionPanel.test.ts
  • src/components/dialog/content/TopUpCreditsDialogContent.vue
  • src/components/ui/stepper/FormattedNumberStepper.vue
  • src/platform/cloud/subscription/components/SubscriptionPanel.vue
src/**/*.{vue,ts,tsx}

📄 CodeRabbit inference engine (src/CLAUDE.md)

Follow Vue 3 composition API style guide

Files:

  • src/services/dialogService.ts
  • src/platform/cloud/subscription/components/SubscriptionPanel.test.ts
  • src/components/dialog/content/TopUpCreditsDialogContent.vue
  • src/components/ui/stepper/FormattedNumberStepper.vue
  • src/platform/cloud/subscription/components/SubscriptionPanel.vue
src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

src/**/*.{ts,tsx}: Keep functions short and functional; use function declarations instead of function expressions when possible
Avoid mutable state; prefer immutability and assignment at point of declaration
Favor pure functions, especially testable ones

Files:

  • src/services/dialogService.ts
  • src/platform/cloud/subscription/components/SubscriptionPanel.test.ts
src/**/{composables,components}/**/*.{ts,tsx,vue}

📄 CodeRabbit inference engine (src/CLAUDE.md)

Clean up subscriptions in state management to prevent memory leaks

Files:

  • src/platform/cloud/subscription/components/SubscriptionPanel.test.ts
  • src/components/dialog/content/TopUpCreditsDialogContent.vue
  • src/components/ui/stepper/FormattedNumberStepper.vue
  • src/platform/cloud/subscription/components/SubscriptionPanel.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/platform/cloud/subscription/components/SubscriptionPanel.test.ts
  • src/components/dialog/content/TopUpCreditsDialogContent.vue
  • src/components/ui/stepper/FormattedNumberStepper.vue
  • src/platform/cloud/subscription/components/SubscriptionPanel.vue
src/**/*.test.ts

📄 CodeRabbit inference engine (AGENTS.md)

src/**/*.test.ts: Write unit tests in src/**/*.test.ts; use Vitest with happy-dom
Do not write tests that only test the mocks; ensure tests fail when code behaves unexpectedly
Use Vitest's utilities for mocking where possible; keep module mocks contained and avoid global mutable state
Use Vue Test Utils for component testing; follow advice about making components easy to test
Use vi.hoisted() in tests to allow per-test Arrange phase manipulation of mock state
Aim for behavioral coverage of critical and new features in unit tests

Files:

  • src/platform/cloud/subscription/components/SubscriptionPanel.test.ts
**/*.{test,spec}.ts

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{test,spec}.ts: Do not write change detector tests that only assert default values
Do not write tests dependent on non-behavioral features like utility classes or styles
Write tests for all changes, especially bug fixes to catch future regressions
Be parsimonious in testing; do not write redundant tests (see Composable Tests pattern)
Do not mock what you don't own; avoid mocking external dependencies

Files:

  • src/platform/cloud/subscription/components/SubscriptionPanel.test.ts
src/**/*.vue

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

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

src/**/*.vue: Use Vue 3 Single File Components (SFCs) with Composition API only; never use Options API
Use <script setup lang="ts"> syntax for component logic
Use Tailwind 4 utility classes for styling; avoid <style> blocks in Vue components
Do not use the dark: Tailwind variant; use semantic values from style.css theme instead (e.g., bg-node-component-surface)
Use cn() utility from @/utils/tailwindUtil for merging class names; never use :class="[]" syntax
Never use !important or the ! prefix for Tailwind classes; find and fix interfering classes instead
Use Tailwind fraction utilities instead of arbitrary percentages (e.g., w-4/5 instead of w-[80%], w-1/2 instead of w-[50%])
Use Vue 3.5 TypeScript style default prop declaration with destructuring; avoid withDefaults and runtime props
Use defineModel for v-model bindings instead of separately defining props and emits
Prefer reactive props destructuring over const props = defineProps<...>
Define slots via template usage, not defineSlots
Use same-name shorthand for slot prop bindings (e.g., :isExpanded instead of :is-expanded="isExpanded")
Use ref for reactive state, computed() for computed properties, and watch/watchEffect for side effects
Avoid using ref and watch t...

Files:

  • src/components/dialog/content/TopUpCreditsDialogContent.vue
  • src/components/ui/stepper/FormattedNumberStepper.vue
  • src/platform/cloud/subscription/components/SubscriptionPanel.vue
src/components/**/*.vue

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

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

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

Files:

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

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

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

Files:

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

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

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

Files:

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

Applied to files:

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

Applied to files:

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

Applied to files:

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

Applied to files:

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

Applied to files:

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

Applied to files:

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

Applied to files:

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

Applied to files:

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

Applied to files:

  • src/platform/cloud/subscription/components/SubscriptionPanel.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 : Replace PrimeVue Steps component with Stepper without panels

Applied to files:

  • src/components/dialog/content/TopUpCreditsDialogContent.vue
  • src/components/ui/stepper/FormattedNumberStepper.vue
  • src/platform/cloud/subscription/components/SubscriptionPanel.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 OverlayPanel component with Popover

Applied to files:

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

Applied to files:

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

Applied to files:

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

Applied to files:

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

Applied to files:

  • src/components/dialog/content/TopUpCreditsDialogContent.vue
  • src/components/ui/stepper/FormattedNumberStepper.vue
  • src/platform/cloud/subscription/components/SubscriptionPanel.vue
📚 Learning: 2026-01-09T00:50:57.103Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-09T00:50:57.103Z
Learning: Applies to src/**/*.vue : Use Tailwind fraction utilities instead of arbitrary percentages (e.g., `w-4/5` instead of `w-[80%]`, `w-1/2` instead of `w-[50%]`)

Applied to files:

  • src/components/dialog/content/TopUpCreditsDialogContent.vue
📚 Learning: 2026-01-09T00:50:57.103Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-09T00:50:57.103Z
Learning: Applies to src/**/*.vue : Do not use the `dark:` Tailwind variant; use semantic values from `style.css` theme instead (e.g., `bg-node-component-surface`)

Applied to files:

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

Applied to files:

  • src/components/dialog/content/TopUpCreditsDialogContent.vue
📚 Learning: 2025-12-16T17:30:29.719Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7559
File: .storybook/preview.ts:61-61
Timestamp: 2025-12-16T17:30:29.719Z
Learning: In .storybook/preview.ts for the Comfy-Org/ComfyUI_frontend repository, using `document.body.classList.add('[&_*]:!font-inter')` is the correct approach for applying the Inter font to all Storybook story elements. The simpler `font-inter` class alone does not work in this context. This runtime arbitrary variant pattern is valid and should not be flagged as an issue.

Applied to files:

  • src/components/dialog/content/TopUpCreditsDialogContent.vue
📚 Learning: 2026-01-09T00:50:57.103Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-09T00:50:57.103Z
Learning: Applies to src/**/*.vue : Use Tailwind 4 utility classes for styling; avoid `<style>` blocks in Vue components

Applied to files:

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

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

Applied to files:

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

Applied to files:

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

Applied to files:

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

Applied to files:

  • src/components/dialog/content/TopUpCreditsDialogContent.vue
  • src/components/ui/stepper/FormattedNumberStepper.vue
  • src/platform/cloud/subscription/components/SubscriptionPanel.vue
📚 Learning: 2025-12-18T16:03:02.066Z
Learnt from: henrikvilhelmberglund
Repo: Comfy-Org/ComfyUI_frontend PR: 7617
File: src/components/actionbar/ComfyActionbar.vue:301-308
Timestamp: 2025-12-18T16:03:02.066Z
Learning: In the ComfyUI frontend queue system, useQueuePendingTaskCountStore().count indicates the number of tasks in the queue, where count = 1 means a single active/running task and count > 1 means there are pending tasks in addition to the active task. Therefore, in src/components/actionbar/ComfyActionbar.vue, enable the 'Clear Pending Tasks' button only when count > 1 to avoid clearing the currently running task. The active task should be canceled using the 'Cancel current run' button instead. This rule should be enforced via a conditional check on the queue count, with appropriate disabled/aria-disabled states for accessibility, and tests should verify behavior for count = 1 and count > 1.

Applied to files:

  • src/components/dialog/content/TopUpCreditsDialogContent.vue
  • src/components/ui/stepper/FormattedNumberStepper.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/**/*.{vue,ts,tsx} : Follow Vue 3 composition API style guide

Applied to files:

  • src/components/ui/stepper/FormattedNumberStepper.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 : Follow Vue 3 style guide and naming conventions

Applied to files:

  • src/components/ui/stepper/FormattedNumberStepper.vue
📚 Learning: 2026-01-09T00:50:57.103Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-09T00:50:57.103Z
Learning: Follow Vue 3 style guide and naming conventions

Applied to files:

  • src/components/ui/stepper/FormattedNumberStepper.vue
📚 Learning: 2026-01-09T00:50:57.103Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-09T00:50:57.103Z
Learning: Applies to src/**/*.vue : Use Vue 3 Single File Components (SFCs) with Composition API only; never use Options API

Applied to files:

  • src/components/ui/stepper/FormattedNumberStepper.vue
📚 Learning: 2026-01-09T00:50:57.103Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-09T00:50:57.103Z
Learning: Applies to src/**/*.vue : Implement proper props and emits definitions in Vue components

Applied to files:

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

Applied to files:

  • src/platform/cloud/subscription/components/SubscriptionPanel.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 TabMenu component with Tabs without panels

Applied to files:

  • src/platform/cloud/subscription/components/SubscriptionPanel.vue
📚 Learning: 2025-12-06T02:11:00.385Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7137
File: src/components/rightSidePanel/RightSidePanel.vue:174-180
Timestamp: 2025-12-06T02:11:00.385Z
Learning: PrimeVue components have poor TypeScript typing, so type assertions (like `as RightSidePanelTab`) may be necessary when handling emitted events or prop values from PrimeVue components like TabList.

Applied to files:

  • src/platform/cloud/subscription/components/SubscriptionPanel.vue
📚 Learning: 2026-01-09T00:50:57.103Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-09T00:50:57.103Z
Learning: Applies to src/**/*.vue : Avoid new usage of PrimeVue components

Applied to files:

  • src/platform/cloud/subscription/components/SubscriptionPanel.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 setup() function in Vue 3 Composition API

Applied to files:

  • src/platform/cloud/subscription/components/SubscriptionPanel.vue
📚 Learning: 2026-01-09T00:50:57.103Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-09T00:50:57.103Z
Learning: Applies to src/**/*.vue : Use `<script setup lang="ts">` syntax for component logic

Applied to files:

  • src/platform/cloud/subscription/components/SubscriptionPanel.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 Vue 3's Teleport component when needed

Applied to files:

  • src/platform/cloud/subscription/components/SubscriptionPanel.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 Sidebar component with Drawer

Applied to files:

  • src/platform/cloud/subscription/components/SubscriptionPanel.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 lifecycle hooks: onMounted, onUpdated in Vue 3 Composition API

Applied to files:

  • src/platform/cloud/subscription/components/SubscriptionPanel.vue
⏰ 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: lint-and-format
  • GitHub Check: setup
  • GitHub Check: collect
  • GitHub Check: test
🔇 Additional comments (23)
src/services/dialogService.ts (1)

371-372: LGTM: Styling update aligns with new design.

The addition of rounded-2xl to the dialog styling is consistent with the card-like appearance introduced in the Top Up Credits dialog redesign.

src/platform/cloud/subscription/components/SubscriptionPanel.test.ts (1)

36-38: LGTM: Test mock correctly reflects production API.

The subscriptionStatus mock addition aligns with the production component changes in SubscriptionPanel.vue, which now consumes subscriptionStatus.renewal_date to derive the refills date display.

src/components/dialog/content/TopUpCreditsDialogContent.vue (8)

28-51: LGTM: Preset amount buttons are well-implemented.

The preset amount buttons use the common Button component with proper variant and size, conditional styling via cn(), and autofocus on the default amount. The implementation aligns with coding guidelines and provides good UX.


52-73: LGTM: "You Pay" stepper is correctly wired.

The FormattedNumberStepper component is properly bound with explicit :model-value and @update:model-value to handle side effects (clearing preset selection and warnings). The min/max/step configuration is appropriate, and the dollar sign prefix enhances clarity.


75-92: LGTM: "You Get" stepper correctly synchronizes credits and USD.

The creditsModel computed property with get/set provides bidirectional synchronization between the USD amount and credits, using the usdToCredits and creditsToUsd conversion utilities. The stepper is properly bound with v-model, and the gold icon prefix visually distinguishes credits from USD.


125-147: LGTM: Buy button and pricing link are well-implemented.

The buy button correctly disables when the amount is invalid or a purchase is in progress. The pricing link uses the external link composable for consistency and includes locale support. The UX flow is clear and user-friendly.


150-187: LGTM: Imports, constants, and state are well-organized.

The script setup follows coding guidelines with proper import organization, clear constant naming, and appropriate default values for state. The use of composition API patterns (ref, computed) is correct.


189-206: LGTM: Computed properties are correctly implemented.

The computed properties provide clean, reactive derivations: pricingUrl uses the external link utility, creditsModel provides bidirectional USD↔credits sync with appropriate rounding, and the validation flags (isValidAmount, isBelowMin) correctly guard user actions.


208-223: LGTM: Utility and step functions are well-designed.

The tiered step amounts (5, 50, 100) based on current value provide a good UX for incrementing/decrementing across different magnitude ranges. The conversion from USD step to credits step is correctly implemented.


225-271: LGTM: Event handlers are robust and well-structured.

The event handlers implement a clean purchase flow:

  • handleBuy includes double-click prevention, telemetry tracking, error handling with user-friendly toast messages, and proper loading state management.
  • The selective tracking clear in handleClose(false) is intentional, allowing the subscription panel to refresh balance on focus after a successful purchase.
  • Validation guards and finally blocks ensure consistent state.
src/components/ui/stepper/FormattedNumberStepper.vue (5)

1-47: LGTM: Template structure is accessible and well-designed.

The component template demonstrates excellent practices:

  • Proper aria-label attributes on icon-only increment/decrement buttons for accessibility.
  • inputmode="numeric" optimizes mobile keyboard input.
  • Dynamic input width using ch units adapts smoothly to content length.
  • Disabled state is correctly propagated to all interactive elements.
  • Slot-based prefix/suffix design provides flexible customization.

49-72: LGTM: Props and emits are properly defined.

The component uses Vue 3.5 style props destructuring with sensible defaults. The defineModel binding and typed emits follow best practices for Vue Composition API. The step prop accepting either a number or function provides flexible UX customization.


74-85: LGTM: State management and synchronization are well-implemented.

The component correctly separates display (inputValue string) from model value (number). The watch on modelValue only updates the display when the input is not focused, preventing interference with user typing. The dynamic inputWidth calculation provides smooth visual adaptation.


87-132: LGTM: Utility functions are well-designed for formatted input.

The cursor position preservation logic in formatWithCursor is sophisticated and necessary for good UX. By counting digits before the cursor and mapping to the formatted string, it maintains user expectations during real-time formatting. The other utilities (parseFormattedNumber, clamp, getStepAmount) are clear and correct.


134-175: LGTM: Event handlers provide excellent UX for formatted input.

The input handling is thoughtfully designed:

  • handleInputChange clamps to max in real-time (emitting max-reached) while allowing temporary values below min during typing.
  • handleInputBlur enforces full min/max constraints when the user finishes editing.
  • Cursor position is preserved during formatting via requestAnimationFrame for smooth UX.
  • Focus behavior (select all) aids quick editing.
src/platform/cloud/subscription/components/SubscriptionPanel.vue (5)

87-88: LGTM: Responsive layout improvements.

The addition of responsive flex direction (flex-col lg:flex-row) and shrink-0 on the credit panel improves the layout across different screen sizes while preserving the intended panel proportions.


123-156: LGTM: Credit display refactored for clarity.

The table structure provides better semantic HTML and accessibility. The use of includedCreditsDisplay (showing "included / total" format) and creditsRemainingLabel with refills date clarifies the different credit types for users, addressing the PR's goal to reduce confusion about credit messages.


270-270: LGTM: Imports and destructuring support new lifecycle logic.

The addition of lifecycle hooks (onMounted, onBeforeUnmount) and subscriptionStatus from useSubscription correctly support the new focus-based balance refresh feature.

Also applies to: 302-302


318-325: Verify timezone handling for renewal date display.

The refillsDate computed property parses the ISO 8601 renewal_date string and extracts date components using local timezone methods (getDate(), getMonth(), getFullYear()). This could cause off-by-one date display issues for users in timezones behind UTC when the renewal date is midnight UTC.

Confirm whether renewal dates should be displayed in UTC or the user's local timezone. If UTC is intended, use UTC date methods instead:

♻️ Proposed fix for UTC display
 const refillsDate = computed(() => {
   if (!subscriptionStatus.value?.renewal_date) return ''
   const date = new Date(subscriptionStatus.value.renewal_date)
-  const day = String(date.getDate()).padStart(2, '0')
-  const month = String(date.getMonth() + 1).padStart(2, '0')
-  const year = String(date.getFullYear()).slice(-2)
+  const day = String(date.getUTCDate()).padStart(2, '0')
+  const month = String(date.getUTCMonth() + 1).padStart(2, '0')
+  const year = String(date.getUTCFullYear()).slice(-2)
   return `${month}/${day}/${year}`
 })

401-429: LGTM: Focus-based balance refresh is well-implemented.

The window focus listener correctly handles the post-purchase balance refresh flow:

  • Uses localStorage with a 5-minute expiry to track pending top-ups.
  • Removes expired tracking to prevent stale refreshes.
  • Properly cleans up the event listener in onBeforeUnmount to prevent memory leaks.
  • Removes the timestamp immediately after refresh to prevent duplicate calls on rapid tab switching.

The integration with clearTopupTracking (called from TopUpCreditsDialogContent.handleClose(false)) ensures the timestamp is set when needed and cleared appropriately.

src/locales/en/main.json (3)

1978-1980: LGTM! Improved clarity for credit types.

The revised labels make it much clearer to users which credits are included with their subscription versus which are additionally purchased. The "Refills {date}" wording is particularly helpful for setting expectations.


2035-2035: LGTM! Concise label improvement.

The shortened label "Max run duration" is clearer and more scannable than the previous "Max duration of each workflow run" while maintaining the same meaning.


13-13: Well-structured locale additions for the new top-up dialog.

The new locale keys are properly organized and follow existing conventions. The interpolation syntax is correct throughout ({amount}, {count}, {credits}, {date}), and the keys comprehensively support the new payment flow UI.

Minor observations:

  • The dual labels like youPay vs amountToPayLabel serve different purposes (short UI label vs full accessibility label), which is appropriate.
  • The asterisk addition to videosEstimate (line 924) correctly pairs with the templateNote footnote.

Also applies to: 16-16, 923-941

Comment on lines +14 to +19
<button
class="cursor-pointer rounded border-none bg-transparent p-0 text-muted-foreground transition-colors hover:text-base-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-secondary-foreground"
@click="() => handleClose()"
>
<i class="icon-[lucide--x] size-6" />
</button>
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Use common button component and add aria-label for accessibility.

The close button is implemented as a plain <button> element. Per coding guidelines, prefer the repo's common button components (e.g., IconButton from src/components/button/) for consistency with the design system.

Additionally, icon-only buttons require an aria-label to provide an accessible name for screen readers.

♻️ Proposed refactor using IconButton

Import IconButton at the top of the script:

+import IconButton from '@/components/ui/button/IconButton.vue'

Then replace the button in the template:

-      <button
-        class="cursor-pointer rounded border-none bg-transparent p-0 text-muted-foreground transition-colors hover:text-base-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-secondary-foreground"
-        @click="() => handleClose()"
-      >
-        <i class="icon-[lucide--x] size-6" />
-      </button>
+      <IconButton
+        icon="lucide--x"
+        variant="muted-textonly"
+        size="icon-sm"
+        :aria-label="$t('g.close')"
+        @click="handleClose"
+      />

Based on learnings and coding guidelines.

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In @src/components/dialog/content/TopUpCreditsDialogContent.vue around lines 14
- 19, The close button in TopUpCreditsDialogContent.vue should use the repo's
common IconButton component and include an accessible name; import IconButton
from src/components/button/IconButton and replace the raw <button> that calls
handleClose() with the IconButton, passing the same click handler (handleClose)
and adding an aria-label like "Close" (or a localized equivalent) while keeping
the icon element (icon-[lucide--x]) as the child so styling and behavior remain
consistent with the design system.

Comment on lines +117 to +122
<a
href="https://www.comfy.org/cloud/enterprise"
target="_blank"
class="ml-1 text-inherit"
>{{ $t('credits.topUp.contactUs') }}</a
>
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Find and examine the useExternalLink composable
fd -t f "useExternalLink" src/

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 144


🏁 Script executed:

#!/bin/bash
# Examine the useExternalLink composable
cat -n src/composables/useExternalLink.ts

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 3958


🏁 Script executed:

#!/bin/bash
# Also check the component file around the mentioned lines
sed -n '110,125p' src/components/dialog/content/TopUpCreditsDialogContent.vue

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 485


Consider centralizing the enterprise link URL in the external link composable.

The enterprise contact URL is hardcoded. To maintain consistency with other external links in the component and the codebase, add this URL to the staticUrls object in the useExternalLink composable (alongside discord, github, forum, etc.), then reference it here.

🤖 Prompt for AI Agents
In @src/components/dialog/content/TopUpCreditsDialogContent.vue around lines 117
- 122, TopUpCreditsDialogContent.vue currently hardcodes the enterprise URL; add
a new entry (e.g. enterprise: 'https://www.comfy.org/cloud/enterprise') to the
staticUrls object in the useExternalLink composable and expose it via the
composable's API, then replace the hardcoded href in
TopUpCreditsDialogContent.vue with the composable lookup (e.g.
externalLink('enterprise') or the returned enterpriseUrl) so the component uses
the centralized staticUrls value.

Copy link
Contributor

@christian-byrne christian-byrne left a comment

Choose a reason for hiding this comment

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

LGTM!

@simula-r simula-r merged commit 1bf5b53 into main Jan 9, 2026
28 checks passed
@simula-r simula-r deleted the feat(cloud)/new-top-up-dialog branch January 9, 2026 03:22
@github-actions
Copy link

github-actions bot commented Jan 9, 2026

⚠️ Backport to cloud/1.36 failed

Reason: Merge conflicts detected during cherry-pick of 1bf5b53

📄 Conflicting files
src/components/dialog/content/TopUpCreditsDialogContent.vue
src/locales/en/main.json
🤖 Prompt for AI Agents
Backport PR #7899 (https://github.com/Comfy-Org/ComfyUI_frontend/pull/7899) to cloud/1.36.
Cherry-pick merge commit 1bf5b5397dd1fdc177bd8a801500e66a9cc549a7 onto new branch
backport-7899-to-cloud-1.36 from origin/cloud/1.36.
Resolve conflicts in: src/components/dialog/content/TopUpCreditsDialogContent.vue src/locales/en/main.json .
For test snapshots (browser_tests/**/*-snapshots/), accept PR version if
changed in original PR, else keep target. For package.json versions, keep
target branch. For pnpm-lock.yaml, regenerate with pnpm install.
Ask user for non-obvious conflicts.
Create PR titled "[backport cloud/1.36] <original title>" with label "backport".
See .github/workflows/pr-backport.yaml for workflow details.

cc @simula-r

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

Labels

cloud/1.36 Backport PRs for cloud 1.36 needs-backport Fix/change that needs to be cherry-picked to the current feature freeze branch size:XL This PR changes 500-999 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants