Conversation
📝 WalkthroughWalkthroughAdds annual billing support to the pricing UI: localization now contains per-cycle prices, the PricingTable component gained a billing-cycle toggle and cycle-aware checkout, subscription dialog layout and styling were adjusted, and a design-system CSS token for secondary background hover was changed. Changes
Sequence DiagramsequenceDiagram
actor User
participant UI as PricingTable (UI)
participant Locale as Pricing Data (locales)
participant Checkout as Checkout API
User->>UI: Toggle billing cycle (monthly/yearly)
UI->>Locale: Read per-cycle prices for tiers
Locale-->>UI: Return monthly/yearly + annualTotal
UI->>UI: Render prices, strike-through alternates, update labels
User->>UI: Click "Subscribe" on a tier
UI->>UI: Compute CheckoutTier (e.g., tierKey or tierKey-yearly)
UI->>Checkout: Initiate checkout with cycle-aware tier payload
Checkout-->>User: Redirect / return checkout result or error
Possibly related PRs
✨ Finishing touches
🧪 Generate unit tests (beta)
📜 Recent review detailsConfiguration used: Organization UI Review profile: ASSERTIVE Plan: Pro 📒 Files selected for processing (5)
🧰 Additional context used📓 Path-based instructions (11)src/**/*.{vue,ts}📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Files:
src/**/*.ts📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Files:
src/**/{services,composables}/**/*.{ts,tsx}📄 CodeRabbit inference engine (src/CLAUDE.md)
Files:
src/**/*.{ts,tsx,vue}📄 CodeRabbit inference engine (src/CLAUDE.md)
Files:
src/**/{composables,components}/**/*.{ts,tsx,vue}📄 CodeRabbit inference engine (src/CLAUDE.md)
Files:
src/**/*.{vue,ts,tsx}📄 CodeRabbit inference engine (src/CLAUDE.md)
Files:
src/**/{components,composables}/**/*.{ts,tsx,vue}📄 CodeRabbit inference engine (src/CLAUDE.md)
Files:
src/**/*.{js,ts,tsx,vue}📄 CodeRabbit inference engine (AGENTS.md)
Files:
src/**/*.{ts,tsx,vue,js}📄 CodeRabbit inference engine (AGENTS.md)
Files:
src/**/*.{ts,tsx}📄 CodeRabbit inference engine (AGENTS.md)
Files:
src/**/*.vue📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Files:
🧠 Learnings (12)📚 Learning: 2025-12-17T01:01:03.597ZApplied to files:
📚 Learning: 2025-12-09T21:40:19.792ZApplied to files:
📚 Learning: 2025-12-09T03:39:54.501ZApplied to files:
📚 Learning: 2025-12-13T11:03:11.264ZApplied to files:
📚 Learning: 2025-12-17T00:40:09.635ZApplied to files:
📚 Learning: 2025-12-11T12:25:15.470ZApplied to files:
📚 Learning: 2025-11-24T19:47:45.616ZApplied to files:
📚 Learning: 2025-12-09T03:49:52.828ZApplied to files:
📚 Learning: 2025-12-09T21:40:12.361ZApplied to files:
📚 Learning: 2025-12-16T22:26:49.463ZApplied to files:
📚 Learning: 2025-12-05T20:35:28.330ZApplied to files:
📚 Learning: 2025-11-24T19:47:45.616ZApplied to files:
⏰ 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)
🔇 Additional comments (19)
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. Comment |
🎨 Storybook Build Status✅ Build completed successfully! ⏰ Completed at: 12/17/2025, 01:37:58 AM UTC 🔗 Links🎉 Your Storybook is ready for review! |
🎭 Playwright Test Results⏰ Completed at: 12/17/2025, 01:46:46 AM UTC 📈 Summary
📊 Test Reports by Browser
🎉 Click on the links above to view detailed test results for each browser configuration. |
Bundle Size ReportSummary
Category Glance Per-category breakdownApp Entry Points — 3.2 MB (baseline 3.2 MB) • 🔴 +303 BMain entry bundles and manifests
Status: 3 added / 3 removed Graph Workspace — 992 kB (baseline 992 kB) • ⚪ 0 BGraph editor runtime, canvas, workflow orchestration
Status: 1 added / 1 removed Views & Navigation — 6.54 kB (baseline 6.54 kB) • ⚪ 0 BTop-level views, pages, and routed surfaces
Status: 1 added / 1 removed Panels & Settings — 297 kB (baseline 297 kB) • ⚪ 0 BConfiguration panels, inspectors, and settings screens
Status: 6 added / 6 removed UI Components — 184 kB (baseline 184 kB) • ⚪ 0 BReusable component library chunks
Status: 7 added / 7 removed Data & Services — 12.5 kB (baseline 12.5 kB) • ⚪ 0 BStores, services, APIs, and repositories
Status: 2 added / 2 removed Utilities & Hooks — 3.18 kB (baseline 3.18 kB) • ⚪ 0 BHelpers, composables, and utility bundles
Status: 1 added / 1 removed Vendor & Third-Party — 8.45 MB (baseline 8.45 MB) • ⚪ 0 BExternal libraries and shared vendor chunks
Other — 3.41 MB (baseline 3.41 MB) • ⚪ 0 BBundles that do not match a named category
Status: 19 added / 19 removed |
🔧 Auto-fixes AppliedThis PR has been automatically updated to fix linting and formatting issues.
Changes made:
|
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/locales/en/main.json (1)
1945-2007: Verify Founder tier price structure consistency.The Founder tier price remains a simple string
"20"(line 1945), while Standard, Creator, and Pro tiers now use the object structure withmonthlyandyearlyfields (lines 1959-1996). This inconsistency could lead to runtime errors when the UI expects all tiers to have the same price structure.Run the following script to verify how the Founder tier is handled in the codebase:
#!/bin/bash # Description: Check if Founder tier is handled differently in the pricing logic # Search for references to founder/founders in pricing-related files rg -n -C3 "founder" --type=ts --type=vue -g "!*.json" src/platform/cloud/subscription/
📜 Review details
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
📒 Files selected for processing (5)
packages/design-system/src/css/style.css(1 hunks)src/locales/en/main.json(6 hunks)src/platform/cloud/subscription/components/PricingTable.vue(7 hunks)src/platform/cloud/subscription/components/SubscriptionRequiredDialogContent.vue(2 hunks)src/platform/cloud/subscription/composables/useSubscriptionDialog.ts(1 hunks)
🧰 Additional context used
📓 Path-based instructions (11)
src/**/*.{vue,ts}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
src/**/*.{vue,ts}: Leverage VueUse functions for performance-enhancing styles
Implement proper error handling
Use vue-i18n in composition API for any string literals. Place new translation entries in src/locales/en/main.json
Files:
src/platform/cloud/subscription/composables/useSubscriptionDialog.tssrc/platform/cloud/subscription/components/SubscriptionRequiredDialogContent.vuesrc/platform/cloud/subscription/components/PricingTable.vue
src/**/*.ts
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
src/**/*.ts: Use es-toolkit for utility functions
Use TypeScript for type safetyMinimize the surface area (exported values) of each module and composable
Files:
src/platform/cloud/subscription/composables/useSubscriptionDialog.ts
src/**/{services,composables}/**/*.{ts,tsx}
📄 CodeRabbit inference engine (src/CLAUDE.md)
src/**/{services,composables}/**/*.{ts,tsx}: Useapi.apiURL()for backend endpoints instead of constructing URLs directly
Useapi.fileURL()for static file access instead of constructing URLs directly
Files:
src/platform/cloud/subscription/composables/useSubscriptionDialog.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/platform/cloud/subscription/composables/useSubscriptionDialog.tssrc/platform/cloud/subscription/components/SubscriptionRequiredDialogContent.vuesrc/platform/cloud/subscription/components/PricingTable.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/composables/useSubscriptionDialog.tssrc/platform/cloud/subscription/components/SubscriptionRequiredDialogContent.vuesrc/platform/cloud/subscription/components/PricingTable.vue
src/**/*.{vue,ts,tsx}
📄 CodeRabbit inference engine (src/CLAUDE.md)
Follow Vue 3 composition API style guide
Files:
src/platform/cloud/subscription/composables/useSubscriptionDialog.tssrc/platform/cloud/subscription/components/SubscriptionRequiredDialogContent.vuesrc/platform/cloud/subscription/components/PricingTable.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/composables/useSubscriptionDialog.tssrc/platform/cloud/subscription/components/SubscriptionRequiredDialogContent.vuesrc/platform/cloud/subscription/components/PricingTable.vue
**/*.{ts,tsx,js,jsx,vue,json}
📄 CodeRabbit inference engine (AGENTS.md)
Code style: Use 2-space indentation, single quotes, no trailing semicolons, and 80-character line width (see
.prettierrc)
Files:
src/platform/cloud/subscription/composables/useSubscriptionDialog.tssrc/platform/cloud/subscription/components/SubscriptionRequiredDialogContent.vuesrc/locales/en/main.jsonsrc/platform/cloud/subscription/components/PricingTable.vue
**/*.{ts,tsx,vue}
📄 CodeRabbit inference engine (AGENTS.md)
**/*.{ts,tsx,vue}: Imports must be sorted and grouped by plugin; runpnpm formatbefore committing
Use TypeScript for type safety; never useanytype - use proper TypeScript types
Never useas anytype assertions; fix the underlying type issue instead
Use es-toolkit for utility functions
Write code that is expressive and self-documenting; avoid comments unless absolutely necessary; do not add or retain redundant comments
Keep functions short and functional
Minimize nesting in code (e.g., deeply nestediforforstatements); apply the Arrow Anti-Pattern principle
Avoid mutable state; prefer immutability and assignment at point of declaration
Favor pure functions, especially testable ones
Files:
src/platform/cloud/subscription/composables/useSubscriptionDialog.tssrc/platform/cloud/subscription/components/SubscriptionRequiredDialogContent.vuesrc/platform/cloud/subscription/components/PricingTable.vue
src/**/*.vue
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
src/**/*.vue: Use the Vue 3 Composition API instead of the Options API when writing Vue components (exception: when overriding or extending PrimeVue components for compatibility)
Use setup() function for component logic
Utilize ref and reactive for reactive state
Implement computed properties with computed()
Use watch and watchEffect for side effects
Implement lifecycle hooks with onMounted, onUpdated, etc.
Utilize provide/inject for dependency injection
Use vue 3.5 style of default prop declaration
Use Tailwind CSS for styling
Implement proper props and emits definitions
Utilize Vue 3's Teleport component when needed
Use Suspense for async components
Follow Vue 3 style guide and naming conventions
Files:
src/platform/cloud/subscription/components/SubscriptionRequiredDialogContent.vuesrc/platform/cloud/subscription/components/PricingTable.vue
**/*.vue
📄 CodeRabbit inference engine (AGENTS.md)
**/*.vue: Use Vue 3 SFCs (Single File Components) with Composition API only; do not use Options API
Vue components must use<script setup lang="ts">for component logic
Use Vue 3.5 TypeScript style for default prop declaration with reactive props destructuring; do not usewithDefaultsor runtime props declaration
PreferuseModelto separately defining a prop and emit
Use Tailwind 4 utility classes for styling; avoid using<style>blocks in Vue components
Use semantic Tailwind values fromstyle.csstheme instead of thedark:variant; for example, usebg-node-component-surfaceinstead ofdark:prefixes
Always usecn()utility from@/utils/tailwindUtilto merge Tailwind class names; do not use:class="[]"syntax
Usereffor reactive state in Vue Composition API components
Implement computed properties withcomputed()from Vue; avoid using arefwith awatchif acomputedwould work instead
UsewatchandwatchEffectfor side effects in Vue components
Implement lifecycle hooks usingonMounted,onUpdated, and other Vue lifecycle functions
Useprovide/injectfor dependency injection; do not use dependency injection if a Store or shared composable would be simpler
Do not import Vue macros unnecessarily; only use when needed
Be judicious with addition of new refs or other state: prefer props, avoid redundantcomputed, and prefercomputedoverwatch
Use VueUse functions for performance-enhancing styles
In Vue Components, implement proper props and emits definitions
Utilize Vue 3's Teleport component when needed
Use Suspense for async components
Implement proper error handling in Vue components
Follow Vue 3 style guide and naming conventions
Use vue-i18n in composition API for any string literals; place new translation entries insrc/locales/en/main.json
Avoid new usage of PrimeVue components; prefer shadcn/vue or Reka UI instead
Files:
src/platform/cloud/subscription/components/SubscriptionRequiredDialogContent.vuesrc/platform/cloud/subscription/components/PricingTable.vue
🧠 Learnings (7)
📚 Learning: 2025-12-09T03:39:54.501Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7169
File: src/platform/remote/comfyui/jobs/jobTypes.ts:1-107
Timestamp: 2025-12-09T03:39:54.501Z
Learning: In the ComfyUI_frontend project, Zod is on v3.x. Do not suggest Zod v4 standalone validators (z.uuid, z.ulid, z.cuid2, z.nanoid) until an upgrade to Zod 4 is performed. When reviewing TypeScript files (e.g., src/platform/remote/comfyui/jobs/jobTypes.ts) validate against Zod 3 capabilities and avoid introducing v4-specific features; flag any proposal to upgrade or incorporate v4-only validators and propose staying with compatible 3.x patterns.
Applied to files:
src/platform/cloud/subscription/composables/useSubscriptionDialog.ts
📚 Learning: 2025-12-13T11:03:11.264Z
Learnt from: christian-byrne
Repo: Comfy-Org/ComfyUI_frontend PR: 7416
File: src/stores/imagePreviewStore.ts:5-7
Timestamp: 2025-12-13T11:03:11.264Z
Learning: In the ComfyUI_frontend repository, lint rules require keeping 'import type' statements separate from non-type imports, even if importing from the same module. Do not suggest consolidating them into a single import statement. Ensure type imports remain on their own line (import type { ... } from 'module') and regular imports stay on separate lines.
Applied to files:
src/platform/cloud/subscription/composables/useSubscriptionDialog.ts
📚 Learning: 2025-12-11T12:25:15.470Z
Learnt from: christian-byrne
Repo: Comfy-Org/ComfyUI_frontend PR: 7358
File: src/components/dialog/content/signin/SignUpForm.vue:45-54
Timestamp: 2025-12-11T12:25:15.470Z
Learning: This repository uses CI automation to format code (pnpm format). Do not include manual formatting suggestions in code reviews for Comfy-Org/ComfyUI_frontend. If formatting issues are detected, rely on the CI formatter or re-run pnpm format. Focus reviews on correctness, readability, performance, accessibility, and maintainability rather than style formatting.
Applied to files:
src/platform/cloud/subscription/composables/useSubscriptionDialog.tssrc/platform/cloud/subscription/components/SubscriptionRequiredDialogContent.vuesrc/platform/cloud/subscription/components/PricingTable.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:
packages/design-system/src/css/style.css
📚 Learning: 2025-12-09T20:22:23.620Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-09T20:22:23.620Z
Learning: Applies to **/*.vue : Use semantic Tailwind values from `style.css` theme instead of the `dark:` variant; for example, use `bg-node-component-surface` instead of `dark:` prefixes
Applied to files:
packages/design-system/src/css/style.css
📚 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/SubscriptionRequiredDialogContent.vuesrc/platform/cloud/subscription/components/PricingTable.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/SubscriptionRequiredDialogContent.vuesrc/platform/cloud/subscription/components/PricingTable.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: setup
- GitHub Check: lint-and-format
- GitHub Check: test
- GitHub Check: collect
🔇 Additional comments (15)
src/locales/en/main.json (2)
1909-1909: LGTM: Abbreviation update is standard and appropriate.The change from "USD / month" to "USD / mo" follows common UI patterns for space-constrained displays.
1931-1941: LGTM: New billing cycle keys support the yearly pricing feature.The addition of billing-related keys ("billed", "monthly", "annually", "yearly", "yearlyDiscount") aligns with the PR's goal of introducing yearly/annual billing.
packages/design-system/src/css/style.css (1)
238-238: Verify the visual impact of the darker hover state.The change from
--color-smoke-200to--color-smoke-400makes the secondary background hover state significantly darker in light mode. This affects all components using--secondary-background-hover, including the billing cycle selector buttons in the new pricing table.Please verify:
- The contrast ratio still meets WCAG AA standards for interactive elements
- The darker hover state provides clear visual feedback without being too harsh
- All components using this token have been visually tested in light mode
Based on learnings, this is the correct approach using semantic tokens from the design system.
src/platform/cloud/subscription/composables/useSubscriptionDialog.ts (2)
26-26: Verify dialog width on smaller viewports.The dialog width increased by 128px (from
min(1200px, 95vw)tomin(1328px, 95vw)). While themin()function with95vwprevents overflow on small screens, please verify that the pricing table content displays properly on tablets and smaller laptops (e.g., 1024px, 1280px viewports).
29-34: LGTM: Improved dialog styling with semantic tokens.The styling updates provide a more polished modal appearance:
- Simplified
rounded-[32px]torounded-2xl(equivalent in Tailwind 4)- Removed
overflow-visiblewhich could cause layout issues- Added proper border, background, and shadow to the content wrapper
- Uses semantic design tokens (
border-border-default,bg-base-background) as recommended in the coding guidelinesThe shadow value
0_25px_80px_rgba(5,6,12,0.45)creates an appropriate depth for a modal dialog.Based on learnings, using semantic tokens is the correct approach for this codebase.
src/platform/cloud/subscription/components/PricingTable.vue (6)
4-40: LGTM: Well-implemented billing cycle selector with good accessibility.The
SelectButtoncomponent is properly configured:
allow-empty="false"prevents deselection, ensuring a billing cycle is always chosen- Unstyled mode with custom pass-through props provides full design control
- Dynamic styling based on
context.activeprovides clear visual feedback- The -20% discount badge is prominently displayed for the yearly option
- Keyboard navigation should work out of the box with PrimeVue
The semantic token usage (
bg-base-foreground,text-base-background, etc.) follows the coding guidelines.Based on learnings, semantic tokens from the design system are the correct approach.
258-284: LGTM: Type-safe billing cycle implementation.The type definitions properly model the billing cycle feature:
CheckoutTiertype correctly includes yearly variants (${TierKey}-yearly)BillingCycleuses a union type for type safetygetCheckoutTierhelper provides type-safe tier key computationPricingTierConfig.pricechange fromstringtoRecord<BillingCycle, string>is a necessary breaking change that enables per-cycle pricingAll types follow TypeScript best practices.
286-341: LGTM: Consistent tier configuration with proper i18n usage.The tier configurations are well-structured:
- All three tiers (Standard, Creator, Pro) use consistent price structure with
monthlyandyearlyfields- All pricing values are sourced from i18n, maintaining a single source of truth
- The
isPopularflag is appropriately set for the Creator tier- New
videoEstimatefield added to all tiers for the benefit sectionThe use of
t()for all user-facing strings follows the coding guidelines for i18n.
343-362: LGTM: Well-structured reactive state and helpers.The reactive state and helper functions are properly implemented:
currentBillingCycledefaults to'yearly', which makes sense as it highlights the discounted pricingcurrentTierKeycomputed property correctly maps subscription tier to tier keyisCurrentPlanprovides a clear, reusable checktogglePopoverproperly accesses the popover ref for programmatic control
364-385: LGTM: Clean and maintainable helper functions.The helper functions are well-designed:
getButtonLabelproperly handles all states (current plan, upgrade, change)getButtonSeverityprovides appropriate visual hierarchygetButtonTextClassuses semantic token-based stylinggetPriceis a simple, focused helper that retrieves the price for the current billing cycleAll functions are pure and follow single-responsibility principle.
386-446: LGTM: Robust checkout implementation with proper error handling.The checkout logic is well-implemented:
getCheckoutTiercorrectly constructs tier keys based on billing cycle (e.g.,"standard-yearly")- API endpoint dynamically includes the checkout tier:
/customers/cloud-subscription-checkout/${checkoutTier}- Error handling is comprehensive with nested try-catch blocks to parse JSON, text, or HTTP status
- Loading states are properly managed with
isLoadingandloadingTierrefs- Checkout flow appropriately differentiates between new subscriptions and plan changes (billing portal)
handleSubscribeis wrapped withwrapWithErrorHandlingAsyncfor consistent error reportingThe implementation follows best practices for async operations and error handling.
src/platform/cloud/subscription/components/SubscriptionRequiredDialogContent.vue (4)
14-48: LGTM: Clean, centered layout with proper i18n and semantic tokens.The restructured layout provides a better user experience:
- Responsive text sizing with
text-xl lg:text-2xladapts to screen size- Centered alignment creates visual hierarchy
- Compact button heights (
h-6) with appropriate padding maintain a clean look- Contact/enterprise section is logically grouped with clear actions
- All user-facing strings use
$t()for i18n- Semantic tokens (
text-text-secondary,text-base-foreground, etc.) follow coding guidelinesThe layout is well-structured and maintainable.
Based on learnings, semantic tokens from the design system are the correct approach.
50-118: LGTM: Legacy dialog preserved with minor styling update.The legacy dialog branch is properly maintained:
- Minor text color change from
text-mutedtotext-text-primaryimproves readability (line 83)- Structure and functionality remain unchanged for backwards compatibility
- Video element correctly uses
autoplay,loop,muted, andplaysinlinefor optimal UX- All styling uses semantic tokens as per coding guidelines
121-261: LGTM: Well-structured component logic with proper cleanup.The script section demonstrates good practices:
- Polling logic is properly implemented with reasonable intervals (3s) and max attempts (3)
- Window focus handler ensures subscription status is refreshed when user returns to the tab
- Proper cleanup in
stopPolling()andonBeforeUnmount()prevents memory leaks- Watchers correctly manage event listeners based on
showCustomPricingTable- Event handlers emit appropriate events and track telemetry
handleClose()ensures polling is stopped before closingThe implementation follows Vue 3 composition API best practices and the coding guidelines.
263-271: LGTM: Minimal scoped styles for legacy compatibility.The scoped styles are appropriately minimal:
- Only target legacy dialog elements using
:deep()selector- Ensure proper visual appearance for legacy branch
- No style pollution due to
scopedattribute
src/platform/cloud/subscription/components/SubscriptionRequiredDialogContent.vue
Show resolved
Hide resolved
a85d8df to
75c4417
Compare
There was a problem hiding this comment.
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 (2)
src/platform/cloud/subscription/components/SubscriptionRequiredDialogContent.vue (1)
266-274: Consider migrating scoped styles to Tailwind utilities.Per coding guidelines, prefer Tailwind utility classes over
<style>blocks. These legacy dialog overrides could potentially be handled via Tailwind's arbitrary selectors or by adjusting the component'sptpassthrough props. However, given this is for a legacy code path, keeping these scoped styles is acceptable for now.src/locales/en/main.json (1)
1958-1999: Price format inconsistency:annualTotalincludes "$" whilemonthly/yearlydo not.The
monthlyandyearlyvalues are plain numbers (e.g.,"20","16"), butannualTotalincludes the currency symbol (e.g.,"$192"). This inconsistency could cause formatting issues if the consuming code assumes uniform formatting.Consider either:
- Making all values plain numbers and formatting consistently in the UI
- Making all values include the currency symbol
Current format works because
billedAnnuallyinterpolates{total}directly, but this couples the locale structure to specific UI expectations.
♻️ Duplicate comments (1)
src/platform/cloud/subscription/components/SubscriptionRequiredDialogContent.vue (1)
6-16: Close button lacks explicit size dimensions.The close button doesn't specify explicit width/height classes. The previous iteration had
h-10 w-10(40×40px). Consider adding size classes to ensure consistent tap target size. Per WCAG 2.5.5 (AAA), 44×44px is recommended for touch targets.
📜 Review details
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
📒 Files selected for processing (5)
packages/design-system/src/css/style.css(1 hunks)src/locales/en/main.json(6 hunks)src/platform/cloud/subscription/components/PricingTable.vue(7 hunks)src/platform/cloud/subscription/components/SubscriptionRequiredDialogContent.vue(2 hunks)src/platform/cloud/subscription/composables/useSubscriptionDialog.ts(1 hunks)
🧰 Additional context used
📓 Path-based instructions (11)
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/SubscriptionRequiredDialogContent.vuesrc/platform/cloud/subscription/components/PricingTable.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/platform/cloud/subscription/components/SubscriptionRequiredDialogContent.vuesrc/platform/cloud/subscription/components/PricingTable.vuesrc/platform/cloud/subscription/composables/useSubscriptionDialog.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/platform/cloud/subscription/components/SubscriptionRequiredDialogContent.vuesrc/platform/cloud/subscription/components/PricingTable.vuesrc/platform/cloud/subscription/composables/useSubscriptionDialog.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/SubscriptionRequiredDialogContent.vuesrc/platform/cloud/subscription/components/PricingTable.vuesrc/platform/cloud/subscription/composables/useSubscriptionDialog.ts
src/**/*.{vue,ts,tsx}
📄 CodeRabbit inference engine (src/CLAUDE.md)
Follow Vue 3 composition API style guide
Files:
src/platform/cloud/subscription/components/SubscriptionRequiredDialogContent.vuesrc/platform/cloud/subscription/components/PricingTable.vuesrc/platform/cloud/subscription/composables/useSubscriptionDialog.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/SubscriptionRequiredDialogContent.vuesrc/platform/cloud/subscription/components/PricingTable.vuesrc/platform/cloud/subscription/composables/useSubscriptionDialog.ts
**/*.vue
📄 CodeRabbit inference engine (AGENTS.md)
**/*.vue: Use Vue 3 SFCs (Single File Components) with Composition API only; do not use Options API
Vue components must use<script setup lang="ts">for component logic
Use Vue 3.5 TypeScript style for default prop declaration with reactive props destructuring; do not usewithDefaultsor runtime props declaration
PreferuseModelto separately defining a prop and emit
Use Tailwind 4 utility classes for styling; avoid using<style>blocks in Vue components
Use semantic Tailwind values fromstyle.csstheme instead of thedark:variant; for example, usebg-node-component-surfaceinstead ofdark:prefixes
Always usecn()utility from@/utils/tailwindUtilto merge Tailwind class names; do not use:class="[]"syntax
Usereffor reactive state in Vue Composition API components
Implement computed properties withcomputed()from Vue; avoid using arefwith awatchif acomputedwould work instead
UsewatchandwatchEffectfor side effects in Vue components
Implement lifecycle hooks usingonMounted,onUpdated, and other Vue lifecycle functions
Useprovide/injectfor dependency injection; do not use dependency injection if a Store or shared composable would be simpler
Do not import Vue macros unnecessarily; only use when needed
Be judicious with addition of new refs or other state: prefer props, avoid redundantcomputed, and prefercomputedoverwatch
Use VueUse functions for performance-enhancing styles
In Vue Components, implement proper props and emits definitions
Utilize Vue 3's Teleport component when needed
Use Suspense for async components
Implement proper error handling in Vue components
Follow Vue 3 style guide and naming conventions
Use vue-i18n in composition API for any string literals; place new translation entries insrc/locales/en/main.json
Avoid new usage of PrimeVue components; prefer shadcn/vue or Reka UI instead
Files:
src/platform/cloud/subscription/components/SubscriptionRequiredDialogContent.vuesrc/platform/cloud/subscription/components/PricingTable.vue
**/*.{ts,tsx,js,jsx,vue,json}
📄 CodeRabbit inference engine (AGENTS.md)
Code style: Use 2-space indentation, single quotes, no trailing semicolons, and 80-character line width (see
.prettierrc)
Files:
src/platform/cloud/subscription/components/SubscriptionRequiredDialogContent.vuesrc/platform/cloud/subscription/components/PricingTable.vuesrc/platform/cloud/subscription/composables/useSubscriptionDialog.tssrc/locales/en/main.json
**/*.{ts,tsx,vue}
📄 CodeRabbit inference engine (AGENTS.md)
**/*.{ts,tsx,vue}: Imports must be sorted and grouped by plugin; runpnpm formatbefore committing
Use TypeScript for type safety; never useanytype - use proper TypeScript types
Never useas anytype assertions; fix the underlying type issue instead
Use es-toolkit for utility functions
Write code that is expressive and self-documenting; avoid comments unless absolutely necessary; do not add or retain redundant comments
Keep functions short and functional
Minimize nesting in code (e.g., deeply nestediforforstatements); apply the Arrow Anti-Pattern principle
Avoid mutable state; prefer immutability and assignment at point of declaration
Favor pure functions, especially testable ones
Files:
src/platform/cloud/subscription/components/SubscriptionRequiredDialogContent.vuesrc/platform/cloud/subscription/components/PricingTable.vuesrc/platform/cloud/subscription/composables/useSubscriptionDialog.ts
src/**/*.ts
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
src/**/*.ts: Use es-toolkit for utility functions
Use TypeScript for type safetyMinimize the surface area (exported values) of each module and composable
Files:
src/platform/cloud/subscription/composables/useSubscriptionDialog.ts
src/**/{services,composables}/**/*.{ts,tsx}
📄 CodeRabbit inference engine (src/CLAUDE.md)
src/**/{services,composables}/**/*.{ts,tsx}: Useapi.apiURL()for backend endpoints instead of constructing URLs directly
Useapi.fileURL()for static file access instead of constructing URLs directly
Files:
src/platform/cloud/subscription/composables/useSubscriptionDialog.ts
🧠 Learnings (8)
📚 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/SubscriptionRequiredDialogContent.vuesrc/platform/cloud/subscription/components/PricingTable.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/SubscriptionRequiredDialogContent.vuesrc/platform/cloud/subscription/components/PricingTable.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/SubscriptionRequiredDialogContent.vuesrc/platform/cloud/subscription/components/PricingTable.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/platform/cloud/subscription/components/SubscriptionRequiredDialogContent.vuesrc/platform/cloud/subscription/components/PricingTable.vuesrc/platform/cloud/subscription/composables/useSubscriptionDialog.ts
📚 Learning: 2025-12-09T03:39:54.501Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7169
File: src/platform/remote/comfyui/jobs/jobTypes.ts:1-107
Timestamp: 2025-12-09T03:39:54.501Z
Learning: In the ComfyUI_frontend project, Zod is on v3.x. Do not suggest Zod v4 standalone validators (z.uuid, z.ulid, z.cuid2, z.nanoid) until an upgrade to Zod 4 is performed. When reviewing TypeScript files (e.g., src/platform/remote/comfyui/jobs/jobTypes.ts) validate against Zod 3 capabilities and avoid introducing v4-specific features; flag any proposal to upgrade or incorporate v4-only validators and propose staying with compatible 3.x patterns.
Applied to files:
src/platform/cloud/subscription/composables/useSubscriptionDialog.ts
📚 Learning: 2025-12-13T11:03:11.264Z
Learnt from: christian-byrne
Repo: Comfy-Org/ComfyUI_frontend PR: 7416
File: src/stores/imagePreviewStore.ts:5-7
Timestamp: 2025-12-13T11:03:11.264Z
Learning: In the ComfyUI_frontend repository, lint rules require keeping 'import type' statements separate from non-type imports, even if importing from the same module. Do not suggest consolidating them into a single import statement. Ensure type imports remain on their own line (import type { ... } from 'module') and regular imports stay on separate lines.
Applied to files:
src/platform/cloud/subscription/composables/useSubscriptionDialog.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,css} : Use the correct tokens from style.css in the design system package
Applied to files:
packages/design-system/src/css/style.css
📚 Learning: 2025-12-09T20:22:23.620Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-09T20:22:23.620Z
Learning: Applies to **/*.vue : Use semantic Tailwind values from `style.css` theme instead of the `dark:` variant; for example, use `bg-node-component-surface` instead of `dark:` prefixes
Applied to files:
packages/design-system/src/css/style.css
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
- GitHub Check: deploy-and-comment
- GitHub Check: setup
- GitHub Check: lint-and-format
- GitHub Check: collect
- GitHub Check: test
🔇 Additional comments (9)
packages/design-system/src/css/style.css (1)
238-238: Design token change increases light theme hover contrast.Changing
--secondary-background-hoverfrom--color-smoke-200to--color-smoke-400makes hover states more pronounced in light mode. This affects all components using this token.Verify this change visually across affected components (buttons, cards, pricing table) in light theme to ensure the increased contrast meets design requirements.
src/platform/cloud/subscription/components/SubscriptionRequiredDialogContent.vue (2)
4-4: The!overflow-y-autoon a flex container may cause issues.Using
!overflow-y-autowithflex flex-colcan lead to unexpected scrolling behavior if child content exceeds the container height. The!importantmodifier suggests this may be overriding conflicting styles—verify this is necessary and that the dialog scrolls correctly when content overflows.
30-50: LGTM! Contact section layout is clean and accessible.The vertical stack with centered alignment and compact button group using semantic classes is well-structured. The button heights and spacing are consistent.
src/locales/en/main.json (1)
1931-1934: LGTM! Billing cadence labels added correctly.The new keys for billing cycle UI (
billedMonthly,billedAnnually,monthly,yearly) follow the existing naming conventions and provide proper i18n support for the yearly pricing feature.src/platform/cloud/subscription/components/PricingTable.vue (5)
360-361:isCurrentPlandoesn't distinguish between monthly and yearly billing cycles.If a user is on a monthly "creator" plan and views the yearly pricing,
isCurrentPlan('creator')returnstrue, disabling the button and showing "Current Plan" even though they could upgrade to yearly billing. This may prevent users from switching billing cycles within the same tier.Consider whether the billing cycle should be factored into the current plan check, or if this behavior is intentional (requiring users to go through the billing portal to change cycles).
264-267: LGTM! Checkout tier construction is clean.The
getCheckoutTierfunction correctly maps the billing cycle to the API endpoint format (tierKey-yearly). This is a simple, testable pure function.
404-425: Error handling is thorough with proper fallback chain.The nested try-catch for JSON parsing with text fallback and HTTP status fallback provides robust error message extraction. This is good defensive coding.
286-289: Static options array initialized at module level is correct.
billingCycleOptionsis static data that doesn't need reactivity. Initializing it at module scope is appropriate and performant.
206-234: Popover implementation looks good, but verify z-index stacking context.The popover uses
base-z-index="1000"andauto-z-index="true". Ensure this doesn't conflict with the dialog's z-index since the popover is appended to body. Thedismissableandclose-on-escapeprops are good for accessibility.
75c4417 to
82ae07a
Compare
| const checkoutTier = getCheckoutTier(tierKey, currentBillingCycle.value) | ||
| const response = await fetch( | ||
| `${getComfyApiBaseUrl()}/customers/cloud-subscription-checkout/${tierKey}`, | ||
| `${getComfyApiBaseUrl()}/customers/cloud-subscription-checkout/${checkoutTier}`, |
There was a problem hiding this comment.
Oh, I never even typed this method to begin with haha. In the future, I suppose we should use the generated registry types for the request body and response types here.
## Summary Add support for yearly/annual billing. Implement new Figma design. Add new yearly tier params to api. ## Changes - **What**: Mostly PricingTable.vue, SubscriptionRequiredDialogContent.vue, - **Breaking**: <!-- Any breaking changes (if none, remove this line) --> - **Dependencies**: <!-- New dependencies (if none, remove this line) --> ## Screenshots (if applicable) https://github.com/user-attachments/assets/06545dca-95a4-43ce-a128-2e45bb44f132 ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-7572-feat-yearly-pricing-2cb6d73d365081c68802f2beb47a312e) by [Unito](https://www.unito.io)
Backport of #7572 to `cloud/1.35` Automatically created by backport workflow. ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-7581-backport-cloud-1-35-feat-cloud-yearly-pricing-2cc6d73d3650814296f1c41377746400) by [Unito](https://www.unito.io) Co-authored-by: Simula_r <18093452+simula-r@users.noreply.github.com>
## Summary Add support for yearly/annual billing. Implement new Figma design. Add new yearly tier params to api. ## Changes - **What**: Mostly PricingTable.vue, SubscriptionRequiredDialogContent.vue, - **Breaking**: <!-- Any breaking changes (if none, remove this line) --> - **Dependencies**: <!-- New dependencies (if none, remove this line) --> ## Screenshots (if applicable) https://github.com/user-attachments/assets/06545dca-95a4-43ce-a128-2e45bb44f132 ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-7572-feat-yearly-pricing-2cb6d73d365081c68802f2beb47a312e) by [Unito](https://www.unito.io)
## Summary Add support for yearly/annual billing. Implement new Figma design. Add new yearly tier params to api. ## Changes - **What**: Mostly PricingTable.vue, SubscriptionRequiredDialogContent.vue, - **Breaking**: <!-- Any breaking changes (if none, remove this line) --> - **Dependencies**: <!-- New dependencies (if none, remove this line) --> ## Screenshots (if applicable) https://github.com/user-attachments/assets/06545dca-95a4-43ce-a128-2e45bb44f132 ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-7572-feat-yearly-pricing-2cb6d73d365081c68802f2beb47a312e) by [Unito](https://www.unito.io)
Summary
Add support for yearly/annual billing. Implement new Figma design. Add new yearly tier params to api.
Changes
Screenshots (if applicable)
chrome_SNyoJ4butQ.mp4
┆Issue is synchronized with this Notion page by Unito