Skip to content

feat(cloud): yearly pricing#7572

Merged
simula-r merged 4 commits intomainfrom
feat(cloud)/yearly-pricing-dialog
Dec 17, 2025
Merged

feat(cloud): yearly pricing#7572
simula-r merged 4 commits intomainfrom
feat(cloud)/yearly-pricing-dialog

Conversation

@simula-r
Copy link
Contributor

@simula-r simula-r commented Dec 16, 2025

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

Screenshots (if applicable)

chrome_SNyoJ4butQ.mp4

┆Issue is synchronized with this Notion page by Unito

@dosubot dosubot bot added the size:L This PR changes 100-499 lines, ignoring generated files. label Dec 16, 2025
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 16, 2025

📝 Walkthrough

Walkthrough

Adds 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

Cohort / File(s) Summary
Design System Token Update
packages/design-system/src/css/style.css
Changed light-theme semantic variable --secondary-background-hover from var(--color-smoke-200) to var(--color-smoke-400)
Localization: Billing and Pricing
src/locales/en/main.json
Added billing cadence labels (billedMonthly, billedAnnually, monthly, yearly, yearlyDiscount); changed usdPerMonth label; migrated tier price values from scalar strings to objects with monthly, yearly, and annualTotal; adjusted viewEnterprise capitalization
Pricing Table Component
src/platform/cloud/subscription/components/PricingTable.vue
Added billing-cycle toggle (monthly/yearly); changed price shape to per-cycle object with annualTotal; introduced BillingCycle, CheckoutTier, and related types/constants; added getPrice accessor, cycle-aware price rendering (strike-through alternate), Video Estimate Help Popover, improved checkout error handling, and checkout tier key includes cycle (e.g., tierKey-yearly)
Subscription Dialog Layout
src/platform/cloud/subscription/components/SubscriptionRequiredDialogContent.vue
Replaced multi-column header with centered description and an absolutely positioned close button; adjusted "Have questions" and contact/action sections to centered/tighter layouts while preserving PricingTable inclusion
Subscription Dialog Styling
src/platform/cloud/subscription/composables/useSubscriptionDialog.ts
Increased dialog min-width from min(1200px,95vw) to min(1328px,95vw); updated wrapper and content classes to use rounded-2xl, border, semi-transparent background, backdrop blur, and shadow

Sequence Diagram

sequenceDiagram
    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
Loading

Possibly related PRs

  • #7359 — Replaces Stripe pricing table with custom PricingTable and updates subscription dialog and locales (strong overlap with pricing component changes).
  • #7324 — Earlier change to subscription dialog width (related to dialog min-width adjustments).
  • #7366 — Related theme/styling updates affecting pricing UI and CSS tokens.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat(cloud)/yearly-pricing-dialog

📜 Recent review details

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 75c4417 and 82ae07a.

📒 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

src/**/*.{vue,ts}: Avoid using ref and watch together - use computed instead if possible
Do not add ref if a prop would suffice; do not add computed if the ref/prop directly would work; use watch only when computed won't accomplish the goal
Leverage VueUse functions for performance-enhancing styles

Files:

  • src/platform/cloud/subscription/composables/useSubscriptionDialog.ts
  • src/platform/cloud/subscription/components/SubscriptionRequiredDialogContent.vue
  • src/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 safety

src/**/*.ts: Extract complex type definitions that are inlined in multiple related places - name them for reuse
Minimize the surface area (exported values) of each module and composable
Favor pure functions, especially testable ones

Files:

  • src/platform/cloud/subscription/composables/useSubscriptionDialog.ts
src/**/{services,composables}/**/*.{ts,tsx}

📄 CodeRabbit inference engine (src/CLAUDE.md)

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

Files:

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

src/**/*.{ts,tsx,vue}: ESLint rules: Vue + TS rules, no floating promises, unused imports disallowed, i18n raw text restrictions in templates
Use es-toolkit for utility functions
Avoid mutable state - prefer immutability and assignment at point of declaration
Never use any type - use proper TypeScript types
Never use as any type assertions - fix the underlying type issue

Files:

  • src/platform/cloud/subscription/composables/useSubscriptionDialog.ts
  • src/platform/cloud/subscription/components/SubscriptionRequiredDialogContent.vue
  • src/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.ts
  • src/platform/cloud/subscription/components/SubscriptionRequiredDialogContent.vue
  • src/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.ts
  • src/platform/cloud/subscription/components/SubscriptionRequiredDialogContent.vue
  • src/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.ts
  • src/platform/cloud/subscription/components/SubscriptionRequiredDialogContent.vue
  • src/platform/cloud/subscription/components/PricingTable.vue
src/**/*.{js,ts,tsx,vue}

📄 CodeRabbit inference engine (AGENTS.md)

Use TypeScript exclusively - no new JavaScript files

Files:

  • src/platform/cloud/subscription/composables/useSubscriptionDialog.ts
  • src/platform/cloud/subscription/components/SubscriptionRequiredDialogContent.vue
  • src/platform/cloud/subscription/components/PricingTable.vue
src/**/*.{ts,tsx,vue,js}

📄 CodeRabbit inference engine (AGENTS.md)

src/**/*.{ts,tsx,vue,js}: Indent with 2 spaces, use single quotes, no trailing semicolons, line width 80 - see .prettierrc
Sort and group imports by plugin, run pnpm format before committing

Files:

  • src/platform/cloud/subscription/composables/useSubscriptionDialog.ts
  • src/platform/cloud/subscription/components/SubscriptionRequiredDialogContent.vue
  • src/platform/cloud/subscription/components/PricingTable.vue
src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Do not use function expressions - use function declarations instead when possible

Files:

  • src/platform/cloud/subscription/composables/useSubscriptionDialog.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: Vue 3 SFCs must use Composition API only - no Options API
Use <script setup lang="ts"> for component logic
Use Tailwind 4 utility classes for styling - avoid <style> blocks
Use cn() utility from @/utils/tailwindUtil to merge Tailwind class names - never use :class="[]" syntax
Never use the dark: Tailwind variant - use semantic values from style.css theme instead (e.g., bg-node-component-surface)
Use Vue 3.5+ TypeScript style default prop declaration with reactive destructuring - prefer useModel to prop/emit pairs, do not use withDefaults
Do not import Vue macros unnecessarily
Use vue-i18n in composition API for string literals - place translation entries in src/locales/en/main.json
Implement proper props and emits definitions in Vue components
Utilize Vue 3's Teleport component when needed
Use Suspense for async components
Avoid new usage of PrimeVue components

Files:

  • src/platform/cloud/subscription/components/SubscriptionRequiredDialogContent.vue
  • src/platform/cloud/subscription/components/PricingTable.vue
🧠 Learnings (12)
📚 Learning: 2025-12-17T01:01:03.597Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-17T01:01:03.597Z
Learning: Applies to src/**/*.vue : Never use the `dark:` Tailwind variant - use semantic values from `style.css` theme instead (e.g., `bg-node-component-surface`)

Applied to files:

  • src/platform/cloud/subscription/composables/useSubscriptionDialog.ts
  • packages/design-system/src/css/style.css
📚 Learning: 2025-12-09T21:40:19.792Z
Learnt from: benceruleanlu
Repo: Comfy-Org/ComfyUI_frontend PR: 7297
File: src/components/actionbar/ComfyActionbar.vue:33-43
Timestamp: 2025-12-09T21:40:19.792Z
Learning: In the Comfy-Org/ComfyUI_frontend repository, inline Tailwind CSS class strings, even when long, are acceptable and preferred over extracting them to computed properties when the classes are static. This is a common Tailwind pattern and doesn't need to be flagged as a readability issue.

Applied to files:

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

Applied to files:

  • src/platform/cloud/subscription/composables/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.ts
  • src/platform/cloud/subscription/components/SubscriptionRequiredDialogContent.vue
  • src/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-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.vue
  • src/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.vue
  • src/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.vue
  • src/platform/cloud/subscription/components/PricingTable.vue
📚 Learning: 2025-12-05T20:35:28.330Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7137
File: src/locales/en/main.json:2257-2272
Timestamp: 2025-12-05T20:35:28.330Z
Learning: In the Comfy-Org/ComfyUI_frontend repository, vue-i18n pluralization uses a two-parameter pattern: `t('key', { count: value })` where the `count` property in the interpolation object automatically triggers plural selection from pipe-separated locale strings (e.g., "no items | 1 item | {count} items"). This is the correct pattern used throughout the codebase, as validated by Playwright tests.

Applied to files:

  • src/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,ts,js} : Use vue-i18n for ALL UI strings

Applied to files:

  • src/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: collect
  • GitHub Check: test
🔇 Additional comments (19)
src/platform/cloud/subscription/composables/useSubscriptionDialog.ts (1)

26-34: LGTM with existing note on shadow value.

The width increase to 1328px accommodates the expanded pricing table layout, and the updated styling (rounded-2xl, backdrop-blur, border) aligns with the new dialog design. The hardcoded shadow value on line 33 has already been noted in past review comments.

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

1910-1910: LGTM – USD label shortened for compact display.

The change from "USD / month" to "USD / mo" provides a more compact label suitable for the pricing UI.


1932-1942: LGTM – billing cycle and discount labels added.

The new keys (billedMonthly, billedAnnually, monthly, yearly, yearlyDiscount) properly support the billing cycle toggle and annual discount display in the pricing UI.


1946-2012: Breaking structure change: price migrated from scalar to object.

The price field structure changed from a scalar string (e.g., "20.00") to an object with cycle-specific fields:

{
  "monthly": "20",
  "yearly": "16",
  "annualTotal": "$192"
}

This is a breaking change for any code directly accessing the price. Verified that PricingTable.vue now uses getPrice() helper to access the correct cycle-aware price.


2025-2025: LGTM – capitalization fix.

Changed "view enterprise" to "View enterprise" for consistent button label capitalization.

src/platform/cloud/subscription/components/SubscriptionRequiredDialogContent.vue (4)

4-16: LGTM – close button with proper accessibility.

The absolutely positioned close button includes:

  • Proper aria-label for screen readers
  • Semantic hover state
  • Adequate spacing from edge (right-2.5 top-2.5)

The button size issue noted in past comments has been addressed.


17-21: LGTM – centered description header.

The centered description header uses i18n (subscription.description) and provides clear context for the pricing table below.


26-51: LGTM – improved contact/enterprise section layout.

The restructured section:

  • Centers content with proper vertical spacing
  • Uses semantic button components with i18n labels
  • Maintains proper hover states and icon positioning
  • Includes clear "or" separator between actions

86-86: LGTM – simplified text class in legacy branch.

Removed muted class while keeping text-text-primary for consistency.

src/platform/cloud/subscription/components/PricingTable.vue (9)

3-40: LGTM – billing cycle toggle with accessible SelectButton.

The SelectButton implementation:

  • Uses i18n for labels (subscription.yearly, subscription.monthly)
  • Provides clear visual feedback for active/inactive states
  • Includes proper ARIA semantics via PrimeVue
  • Note: The hardcoded "-20%" discount on lines 31-36 has been flagged in past review comments

42-99: LGTM – per-cycle pricing display with strike-through.

The pricing header correctly:

  • Shows strike-through for monthly price when yearly is selected (lines 73-77)
  • Displays current price via getPrice(tier) helper (line 78)
  • Uses i18n for USD label and billing frequency (lines 83, 88-94)
  • Handles dynamic billing message with billedAnnually/billedMonthly keys

100-178: LGTM – comprehensive tier features with i18n.

The tier details section:

  • Uses i18n for all labels (monthly credits, max duration, GPU, LoRAs, video estimate)
  • Provides visual indicators (checkmarks, icons) for feature availability
  • Includes conditional rendering for custom LoRAs based on tier
  • Adds help interaction for video estimate with proper hover states

180-202: LGTM – tier action button with cycle-aware styling.

The subscribe button:

  • Dynamically labels based on subscription state (getButtonLabel)
  • Disables when loading or for current plan
  • Applies tier-specific styling (creator tier has inverted colors)
  • Properly handles loading state with loadingTier tracking

205-234: LGTM – accessible help popover for video estimates.

The Popover component:

  • Uses proper z-index layering for body append
  • Provides dismissable behavior (click outside, escape key)
  • Contains clear explanatory text with i18n
  • Includes actionable link to try the template

262-289: LGTM – type-safe billing cycle implementation.

The type definitions and helpers:

  • BillingCycle type constrains to 'monthly' | 'yearly'
  • CheckoutTier type properly extends to include yearly variants
  • getCheckoutTier helper correctly appends '-yearly' suffix when needed
  • billingCycleOptions properly initialized with i18n labels

298-344: LGTM – tier data structure with per-cycle pricing.

The tiers array:

  • Properly typed as PricingTierConfig[]
  • Price structure matches locale data (monthly, yearly, annualTotal)
  • All user-facing strings use i18n
  • Popular flag correctly set for creator tier

354-387: LGTM – billing cycle state and price helper.

State management and helpers:

  • currentBillingCycle defaults to 'yearly' to promote annual savings
  • getPrice helper correctly indexes into tier.price record
  • getButtonLabel, getButtonSeverity, getButtonTextClass provide proper UI states

389-428: LGTM – improved error handling in checkout flow.

The initiateCheckout function:

  • Correctly computes checkoutTier with billing cycle via helper (line 395)
  • Uses cycle-aware endpoint path (line 397)
  • Enhanced error handling with multiple fallbacks:
    • Attempts to parse JSON error message
    • Falls back to plain text response
    • Falls back to HTTP status code
  • Wraps errors in FirebaseAuthStoreError with i18n message
packages/design-system/src/css/style.css (1)

238-238: The --secondary-background-hover color change (#e9e9e9 to #d9d9d9) does not create WCAG accessibility violations.

The token is confirmed to be used across 40+ components (buttons, menu items, filters, panels, dialogs, and form inputs). Per WCAG 1.4.11, hover states are not required to meet a 3:1 contrast ratio against default states—they need only maintain sufficient contrast with adjacent content (text/icons on top). Both the original and new colors are light backgrounds that maintain adequate contrast with the dark foreground text placed on them. The change is minor (~6% lightness difference) and does not present a perceivable accessibility concern.

Likely an incorrect or invalid review comment.


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 Dec 16, 2025

🎨 Storybook Build Status

Build completed successfully!

⏰ Completed at: 12/17/2025, 01:37:58 AM UTC

🔗 Links


🎉 Your Storybook is ready for review!

@github-actions
Copy link

github-actions bot commented Dec 16, 2025

🎭 Playwright Test Results

⚠️ Tests passed with flaky tests

⏰ Completed at: 12/17/2025, 01:46:46 AM UTC

📈 Summary

  • Total Tests: 505
  • Passed: 493 ✅
  • Failed: 0
  • Flaky: 3 ⚠️
  • Skipped: 9 ⏭️

📊 Test Reports by Browser

  • chromium: View Report • ✅ 481 / ❌ 0 / ⚠️ 3 / ⏭️ 9
  • 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 Dec 16, 2025

Bundle Size Report

Summary

  • Raw size: 16.6 MB baseline 16.6 MB — 🔴 +303 B
  • Gzip: 3.36 MB baseline 3.36 MB — 🔴 +41 B
  • Brotli: 2.59 MB baseline 2.59 MB — 🔴 +167 B
  • Bundles: 98 current • 98 baseline • 40 added / 40 removed

Category Glance
App Entry Points 🔴 +303 B (3.2 MB) · Vendor & Third-Party ⚪ 0 B (8.45 MB) · Other ⚪ 0 B (3.41 MB) · Graph Workspace ⚪ 0 B (992 kB) · Panels & Settings ⚪ 0 B (297 kB) · UI Components ⚪ 0 B (184 kB) · + 3 more

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

Main entry bundles and manifests

File Before After Δ Raw Δ Gzip Δ Brotli
assets/index-tgzluf8p.js (new) 2.98 MB 🔴 +2.98 MB 🔴 +627 kB 🔴 +477 kB
assets/index-BhRT9Z4X.js (removed) 2.98 MB 🟢 -2.98 MB 🟢 -627 kB 🟢 -477 kB
assets/index-Cfib0iwM.js (removed) 227 kB 🟢 -227 kB 🟢 -48.5 kB 🟢 -39.8 kB
assets/index-DPSF8kzC.js (new) 227 kB 🔴 +227 kB 🔴 +48.5 kB 🔴 +39.8 kB
assets/index-B8Txm3e5.js (removed) 345 B 🟢 -345 B 🟢 -245 B 🟢 -236 B
assets/index-Cfb0NpLU.js (new) 345 B 🔴 +345 B 🔴 +243 B 🔴 +228 B

Status: 3 added / 3 removed

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

Graph editor runtime, canvas, workflow orchestration

File Before After Δ Raw Δ Gzip Δ Brotli
assets/GraphView-DQIHD7A1.js (removed) 992 kB 🟢 -992 kB 🟢 -192 kB 🟢 -147 kB
assets/GraphView-sLyD653m.js (new) 992 kB 🔴 +992 kB 🔴 +192 kB 🔴 +147 kB

Status: 1 added / 1 removed

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

Top-level views, pages, and routed surfaces

File Before After Δ Raw Δ Gzip Δ Brotli
assets/UserSelectView-BSA8CbPk.js (removed) 6.54 kB 🟢 -6.54 kB 🟢 -2.14 kB 🟢 -1.89 kB
assets/UserSelectView-CwOruzSJ.js (new) 6.54 kB 🔴 +6.54 kB 🔴 +2.14 kB 🔴 +1.89 kB

Status: 1 added / 1 removed

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

Configuration panels, inspectors, and settings screens

File Before After Δ Raw Δ Gzip Δ Brotli
assets/LegacyCreditsPanel-CLqrM8fG.js (new) 21.4 kB 🔴 +21.4 kB 🔴 +5.15 kB 🔴 +4.5 kB
assets/LegacyCreditsPanel-CpxwAT0M.js (removed) 21.4 kB 🟢 -21.4 kB 🟢 -5.16 kB 🟢 -4.5 kB
assets/KeybindingPanel-BpYQrWGU.js (removed) 13.6 kB 🟢 -13.6 kB 🟢 -3.42 kB 🟢 -3.03 kB
assets/KeybindingPanel-DGWRPJeh.js (new) 13.6 kB 🔴 +13.6 kB 🔴 +3.42 kB 🔴 +3.03 kB
assets/ExtensionPanel-BEYh-XxU.js (new) 10.8 kB 🔴 +10.8 kB 🔴 +2.57 kB 🔴 +2.25 kB
assets/ExtensionPanel-BiyZT8E0.js (removed) 10.8 kB 🟢 -10.8 kB 🟢 -2.57 kB 🟢 -2.25 kB
assets/AboutPanel-BUq0fu50.js (removed) 9.16 kB 🟢 -9.16 kB 🟢 -2.46 kB 🟢 -2.2 kB
assets/AboutPanel-Dg1-cv5i.js (new) 9.16 kB 🔴 +9.16 kB 🔴 +2.46 kB 🔴 +2.21 kB
assets/ServerConfigPanel-BwNu8uZP.js (new) 7.07 kB 🔴 +7.07 kB 🔴 +1.98 kB 🔴 +1.76 kB
assets/ServerConfigPanel-C1XN3k5G.js (removed) 7.07 kB 🟢 -7.07 kB 🟢 -1.99 kB 🟢 -1.76 kB
assets/UserPanel-Ck-2Us7u.js (removed) 6.23 kB 🟢 -6.23 kB 🟢 -1.72 kB 🟢 -1.5 kB
assets/UserPanel-F6SyyTIh.js (new) 6.23 kB 🔴 +6.23 kB 🔴 +1.72 kB 🔴 +1.51 kB
assets/settings-BhbWhsRg.js 101 B 101 B ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-BvFz-TmQ.js 33.1 kB 33.1 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-BxYnTA2y.js 21.5 kB 21.5 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-C6bMjAne.js 27.1 kB 27.1 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-C7Yhp7H7.js 24 kB 24 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-CsnOn8G0.js 25.7 kB 25.7 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-D5BGNZ7M.js 24.8 kB 24.8 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-D7k3huHm.js 25 kB 25 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-DamkILzl.js 26.4 kB 26.4 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-s1k0gw1Y.js 20.9 kB 20.9 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B

Status: 6 added / 6 removed

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

Reusable component library chunks

File Before After Δ Raw Δ Gzip Δ Brotli
assets/Load3D.vue_vue_type_script_setup_true_lang-CXPA-MAx.js (new) 53.7 kB 🔴 +53.7 kB 🔴 +8.49 kB 🔴 +7.29 kB
assets/Load3D.vue_vue_type_script_setup_true_lang-Dh5jSbrS.js (removed) 53.7 kB 🟢 -53.7 kB 🟢 -8.49 kB 🟢 -7.29 kB
assets/WidgetSelect.vue_vue_type_script_setup_true_lang-lh7vQ3sT.js (removed) 48.1 kB 🟢 -48.1 kB 🟢 -10.4 kB 🟢 -8.99 kB
assets/WidgetSelect.vue_vue_type_script_setup_true_lang-pAEOJMeX.js (new) 48.1 kB 🔴 +48.1 kB 🔴 +10.4 kB 🔴 +8.99 kB
assets/LazyImage.vue_vue_type_script_setup_true_lang-B34IUs4X.js (new) 48 kB 🔴 +48 kB 🔴 +10.6 kB 🔴 +9.32 kB
assets/LazyImage.vue_vue_type_script_setup_true_lang-kKVkHc3k.js (removed) 48 kB 🟢 -48 kB 🟢 -10.6 kB 🟢 -9.32 kB
assets/WidgetInputNumber.vue_vue_type_script_setup_true_lang-DcwzuXgh.js (new) 19.5 kB 🔴 +19.5 kB 🔴 +5.06 kB 🔴 +4.49 kB
assets/WidgetInputNumber.vue_vue_type_script_setup_true_lang-DeylB7Ea.js (removed) 19.5 kB 🟢 -19.5 kB 🟢 -5.05 kB 🟢 -4.49 kB
assets/ComfyQueueButton-CSryIcAD.js (removed) 8.44 kB 🟢 -8.44 kB 🟢 -2.48 kB 🟢 -2.22 kB
assets/ComfyQueueButton-D6T64dV9.js (new) 8.44 kB 🔴 +8.44 kB 🔴 +2.48 kB 🔴 +2.22 kB
assets/WidgetLayoutField.vue_vue_type_script_setup_true_lang-2yoHCZ6K.js (removed) 2.14 kB 🟢 -2.14 kB 🟢 -891 B 🟢 -773 B
assets/WidgetLayoutField.vue_vue_type_script_setup_true_lang-923r41q7.js (new) 2.14 kB 🔴 +2.14 kB 🔴 +888 B 🔴 +770 B
assets/MediaTitle.vue_vue_type_script_setup_true_lang-Cwp3TEYU.js (removed) 897 B 🟢 -897 B 🟢 -504 B 🟢 -436 B
assets/MediaTitle.vue_vue_type_script_setup_true_lang-D5qk4bR3.js (new) 897 B 🔴 +897 B 🔴 +502 B 🔴 +430 B
assets/UserAvatar.vue_vue_type_script_setup_true_lang-ByQFYn-E.js 1.34 kB 1.34 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/WidgetButton-CEPQvIIQ.js 2.04 kB 2.04 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B

Status: 7 added / 7 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-CPw8jR-5.js (removed) 7.51 kB 🟢 -7.51 kB 🟢 -1.83 kB 🟢 -1.58 kB
assets/keybindingService-DS8YqSMA.js (new) 7.51 kB 🔴 +7.51 kB 🔴 +1.83 kB 🔴 +1.57 kB
assets/audioService-_392q8qH.js (removed) 2.2 kB 🟢 -2.2 kB 🟢 -964 B 🟢 -827 B
assets/audioService-BlirUkk2.js (new) 2.2 kB 🔴 +2.2 kB 🔴 +957 B 🔴 +826 B
assets/serverConfigStore-BP7nYmv7.js 2.83 kB 2.83 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B

Status: 2 added / 2 removed

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

Helpers, composables, and utility bundles

File Before After Δ Raw Δ Gzip Δ Brotli
assets/audioUtils-Bwjzel6R.js (removed) 1.41 kB 🟢 -1.41 kB 🟢 -652 B 🟢 -553 B
assets/audioUtils-DEIamuEV.js (new) 1.41 kB 🔴 +1.41 kB 🔴 +650 B 🔴 +552 B
assets/mathUtil-CD4DsosH.js 1.32 kB 1.32 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeFilterUtil-CXKCRJ-m.js 460 B 460 B ⚪ 0 B ⚪ 0 B ⚪ 0 B

Status: 1 added / 1 removed

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

External libraries and shared vendor chunks

File Before After Δ Raw Δ Gzip Δ Brotli
assets/vendor-chart-o7g89i4t.js 452 kB 452 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/vendor-other-DByaMi26.js 3.86 MB 3.86 MB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/vendor-primevue-DFKEqFSu.js 1.96 MB 1.96 MB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/vendor-three-C1LfRdh4.js 1.37 MB 1.37 MB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/vendor-tiptap-BIufkEM_.js 232 kB 232 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/vendor-vue-BVEjmwHo.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.41 MB (baseline 3.41 MB) • ⚪ 0 B

Bundles that do not match a named category

File Before After Δ Raw Δ Gzip Δ Brotli
assets/WidgetRecordAudio-BFUgPaCo.js (removed) 20.4 kB 🟢 -20.4 kB 🟢 -5.23 kB 🟢 -4.63 kB
assets/WidgetRecordAudio-DGWEnE2B.js (new) 20.4 kB 🔴 +20.4 kB 🔴 +5.23 kB 🔴 +4.64 kB
assets/AudioPreviewPlayer-B9fPLB2T.js (new) 13.4 kB 🔴 +13.4 kB 🔴 +3.37 kB 🔴 +3.01 kB
assets/AudioPreviewPlayer-DiMOFmOb.js (removed) 13.4 kB 🟢 -13.4 kB 🟢 -3.37 kB 🟢 -3.02 kB
assets/NumberControlPopover-B6-3B0O_.js (new) 6.31 kB 🔴 +6.31 kB 🔴 +1.93 kB 🔴 +1.71 kB
assets/NumberControlPopover-CTsyhXmI.js (removed) 6.31 kB 🟢 -6.31 kB 🟢 -1.93 kB 🟢 -1.71 kB
assets/WidgetGalleria-BL6CZXao.js (removed) 4.1 kB 🟢 -4.1 kB 🟢 -1.45 kB 🟢 -1.3 kB
assets/WidgetGalleria-BtNdW9VW.js (new) 4.1 kB 🔴 +4.1 kB 🔴 +1.44 kB 🔴 +1.3 kB
assets/WidgetColorPicker-BXD-nwK1.js (new) 3.41 kB 🔴 +3.41 kB 🔴 +1.38 kB 🔴 +1.23 kB
assets/WidgetColorPicker-CnpuKS6v.js (removed) 3.41 kB 🟢 -3.41 kB 🟢 -1.38 kB 🟢 -1.24 kB
assets/WidgetTextarea-Bm158LG2.js (new) 3.08 kB 🔴 +3.08 kB 🔴 +1.21 kB 🔴 +1.08 kB
assets/WidgetTextarea-BuONIXVi.js (removed) 3.08 kB 🟢 -3.08 kB 🟢 -1.21 kB 🟢 -1.08 kB
assets/WidgetMarkdown-B6RGgQbi.js (new) 3.08 kB 🔴 +3.08 kB 🔴 +1.28 kB 🔴 +1.13 kB
assets/WidgetMarkdown-BL8rdkBT.js (removed) 3.08 kB 🟢 -3.08 kB 🟢 -1.28 kB 🟢 -1.13 kB
assets/WidgetAudioUI-Chg0DXPX.js (removed) 2.86 kB 🟢 -2.86 kB 🟢 -1.17 kB 🟢 -1.05 kB
assets/WidgetAudioUI-dFg4iW5b.js (new) 2.86 kB 🔴 +2.86 kB 🔴 +1.17 kB 🔴 +1.05 kB
assets/WidgetInputText-DR_OtIwQ.js (removed) 1.99 kB 🟢 -1.99 kB 🟢 -919 B 🟢 -842 B
assets/WidgetInputText-UZ8CDNYj.js (new) 1.99 kB 🔴 +1.99 kB 🔴 +920 B 🔴 +850 B
assets/WidgetToggleSwitch-C7yh0N4z.js (removed) 1.76 kB 🟢 -1.76 kB 🟢 -835 B 🟢 -734 B
assets/WidgetToggleSwitch-CPscUOds.js (new) 1.76 kB 🔴 +1.76 kB 🔴 +833 B 🔴 +734 B
assets/MediaImageBottom-Buc8Bijg.js (removed) 1.55 kB 🟢 -1.55 kB 🟢 -735 B 🟢 -641 B
assets/MediaImageBottom-Cj-3dQJS.js (new) 1.55 kB 🔴 +1.55 kB 🔴 +734 B 🔴 +641 B
assets/MediaAudioBottom-DBxARJjU.js (new) 1.51 kB 🔴 +1.51 kB 🔴 +732 B 🔴 +645 B
assets/MediaAudioBottom-Vi7VGbRE.js (removed) 1.51 kB 🟢 -1.51 kB 🟢 -735 B 🟢 -646 B
assets/Media3DBottom-CHKjaKo7.js (new) 1.5 kB 🔴 +1.5 kB 🔴 +730 B 🔴 +643 B
assets/Media3DBottom-CUJLmeaA.js (removed) 1.5 kB 🟢 -1.5 kB 🟢 -731 B 🟢 -643 B
assets/MediaVideoBottom-BCGt3_Pm.js (new) 1.5 kB 🔴 +1.5 kB 🔴 +729 B 🔴 +643 B
assets/MediaVideoBottom-PsSQIRdk.js (removed) 1.5 kB 🟢 -1.5 kB 🟢 -732 B 🟢 -645 B
assets/Media3DTop-B31F3Mlb.js (new) 1.49 kB 🔴 +1.49 kB 🔴 +763 B 🔴 +650 B
assets/Media3DTop-DS2c1Paf.js (removed) 1.49 kB 🟢 -1.49 kB 🟢 -765 B 🟢 -650 B
assets/WidgetSelect-C8-UiSTV.js (removed) 655 B 🟢 -655 B 🟢 -343 B 🟢 -286 B
assets/WidgetSelect-ySrKTYEg.js (new) 655 B 🔴 +655 B 🔴 +344 B 🔴 +287 B
assets/WidgetInputNumber-B2IpIJJ2.js (removed) 595 B 🟢 -595 B 🟢 -329 B 🟢 -269 B
assets/WidgetInputNumber-sd5wfsSF.js (new) 595 B 🔴 +595 B 🔴 +329 B 🔴 +272 B
assets/Load3D-Cc5pmJOe.js (removed) 424 B 🟢 -424 B 🟢 -268 B 🟢 -223 B
assets/Load3D-ChARxnih.js (new) 424 B 🔴 +424 B 🔴 +265 B 🔴 +222 B
assets/WidgetLegacy-Nio6eOdP.js (new) 364 B 🔴 +364 B 🔴 +237 B 🔴 +192 B
assets/WidgetLegacy-O0pOmd9Q.js (removed) 364 B 🟢 -364 B 🟢 -238 B 🟢 -227 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/commands-Zxyx15Vd.js 12.8 kB 12.8 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-BDhxs-bB.js 79.9 kB 79.9 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-BtXVDFw6.js 84.7 kB 84.7 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-Buv6RhU4.js 82.4 kB 82.4 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-CgJ6zvJL.js 97.2 kB 97.2 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-CI9qb0E5.js 112 kB 112 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-Cu4LqtW0.js 81.6 kB 81.6 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-Cw9RZWRY.js 89 B 89 B ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-Dm19QY4N.js 92.2 kB 92.2 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-Tihe0h_r.js 71.3 kB 71.3 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-wPWMutMw.js 70.4 kB 70.4 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/MediaAudioTop-DSrVmTsv.js 1.46 kB 1.46 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/MediaImageTop-B7iuKaes.js 1.75 kB 1.75 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/MediaVideoTop-B1cvEWRN.js 2.65 kB 2.65 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-1Vh3MCrN.js 240 kB 240 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-aW9En70v.js 260 kB 260 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/WidgetChart-DwKqVDKB.js 2.48 kB 2.48 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/WidgetImageCompare-B4NspM7k.js 2.21 kB 2.21 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/widgetPropFilter-BIbGSUAt.js 1.28 kB 1.28 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B

Status: 19 added / 19 removed

@simula-r simula-r changed the title feat: yearly pricing feat(cloud): yearly pricing Dec 16, 2025
@github-actions
Copy link

🔧 Auto-fixes Applied

This PR has been automatically updated to fix linting and formatting issues.

⚠️ Important: Your local branch is now behind. Run git pull before making additional changes to avoid conflicts.

Changes made:

  • ESLint auto-fixes
  • Prettier formatting

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

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 with monthly and yearly fields (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

📥 Commits

Reviewing files that changed from the base of the PR and between e1294d6 and 4a76980.

📒 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.ts
  • src/platform/cloud/subscription/components/SubscriptionRequiredDialogContent.vue
  • src/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 safety

Minimize 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}: Use api.apiURL() for backend endpoints instead of constructing URLs directly
Use api.fileURL() for static file access instead of constructing URLs directly

Files:

  • src/platform/cloud/subscription/composables/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.ts
  • src/platform/cloud/subscription/components/SubscriptionRequiredDialogContent.vue
  • src/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.ts
  • src/platform/cloud/subscription/components/SubscriptionRequiredDialogContent.vue
  • src/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.ts
  • src/platform/cloud/subscription/components/SubscriptionRequiredDialogContent.vue
  • src/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.ts
  • src/platform/cloud/subscription/components/SubscriptionRequiredDialogContent.vue
  • src/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.ts
  • src/platform/cloud/subscription/components/SubscriptionRequiredDialogContent.vue
  • src/locales/en/main.json
  • src/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; run pnpm format before committing
Use TypeScript for type safety; never use any type - use proper TypeScript types
Never use as any type 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 nested if or for statements); 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.ts
  • src/platform/cloud/subscription/components/SubscriptionRequiredDialogContent.vue
  • src/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.vue
  • src/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 use withDefaults or runtime props declaration
Prefer useModel to separately defining a prop and emit
Use Tailwind 4 utility classes for styling; avoid using <style> blocks in Vue components
Use semantic Tailwind values from style.css theme instead of the dark: variant; for example, use bg-node-component-surface instead of dark: prefixes
Always use cn() utility from @/utils/tailwindUtil to merge Tailwind class names; do not use :class="[]" syntax
Use ref for reactive state in Vue Composition API components
Implement computed properties with computed() from Vue; avoid using a ref with a watch if a computed would work instead
Use watch and watchEffect for side effects in Vue components
Implement lifecycle hooks using onMounted, onUpdated, and other Vue lifecycle functions
Use provide/inject for 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 redundant computed, and prefer computed over watch
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 in src/locales/en/main.json
Avoid new usage of PrimeVue components; prefer shadcn/vue or Reka UI instead

Files:

  • src/platform/cloud/subscription/components/SubscriptionRequiredDialogContent.vue
  • src/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.ts
  • src/platform/cloud/subscription/components/SubscriptionRequiredDialogContent.vue
  • src/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.vue
  • src/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.vue
  • src/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-200 to --color-smoke-400 makes 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:

  1. The contrast ratio still meets WCAG AA standards for interactive elements
  2. The darker hover state provides clear visual feedback without being too harsh
  3. 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) to min(1328px, 95vw)). While the min() function with 95vw prevents 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] to rounded-2xl (equivalent in Tailwind 4)
  • Removed overflow-visible which 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 guidelines

The 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 SelectButton component 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.active provides 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:

  • CheckoutTier type correctly includes yearly variants (${TierKey}-yearly)
  • BillingCycle uses a union type for type safety
  • getCheckoutTier helper provides type-safe tier key computation
  • PricingTierConfig.price change from string to Record<BillingCycle, string> is a necessary breaking change that enables per-cycle pricing

All 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 monthly and yearly fields
  • All pricing values are sourced from i18n, maintaining a single source of truth
  • The isPopular flag is appropriately set for the Creator tier
  • New videoEstimate field added to all tiers for the benefit section

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

  • currentBillingCycle defaults to 'yearly', which makes sense as it highlights the discounted pricing
  • currentTierKey computed property correctly maps subscription tier to tier key
  • isCurrentPlan provides a clear, reusable check
  • togglePopover properly accesses the popover ref for programmatic control

364-385: LGTM: Clean and maintainable helper functions.

The helper functions are well-designed:

  • getButtonLabel properly handles all states (current plan, upgrade, change)
  • getButtonSeverity provides appropriate visual hierarchy
  • getButtonTextClass uses semantic token-based styling
  • getPrice is a simple, focused helper that retrieves the price for the current billing cycle

All functions are pure and follow single-responsibility principle.


386-446: LGTM: Robust checkout implementation with proper error handling.

The checkout logic is well-implemented:

  • getCheckoutTier correctly 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 isLoading and loadingTier refs
  • Checkout flow appropriately differentiates between new subscriptions and plan changes (billing portal)
  • handleSubscribe is wrapped with wrapWithErrorHandlingAsync for consistent error reporting

The 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-2xl adapts 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 guidelines

The 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-muted to text-text-primary improves readability (line 83)
  • Structure and functionality remain unchanged for backwards compatibility
  • Video element correctly uses autoplay, loop, muted, and playsinline for 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() and onBeforeUnmount() 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 closing

The 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 scoped attribute

@simula-r simula-r force-pushed the feat(cloud)/yearly-pricing-dialog branch from a85d8df to 75c4417 Compare December 16, 2025 23:00
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 (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's pt passthrough 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: annualTotal includes "$" while monthly/yearly do not.

The monthly and yearly values are plain numbers (e.g., "20", "16"), but annualTotal includes the currency symbol (e.g., "$192"). This inconsistency could cause formatting issues if the consuming code assumes uniform formatting.

Consider either:

  1. Making all values plain numbers and formatting consistently in the UI
  2. Making all values include the currency symbol

Current format works because billedAnnually interpolates {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

📥 Commits

Reviewing files that changed from the base of the PR and between a85d8df and 75c4417.

📒 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.vue
  • src/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.vue
  • src/platform/cloud/subscription/components/PricingTable.vue
  • 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/components/SubscriptionRequiredDialogContent.vue
  • src/platform/cloud/subscription/components/PricingTable.vue
  • src/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.vue
  • src/platform/cloud/subscription/components/PricingTable.vue
  • src/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.vue
  • src/platform/cloud/subscription/components/PricingTable.vue
  • src/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.vue
  • src/platform/cloud/subscription/components/PricingTable.vue
  • src/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 use withDefaults or runtime props declaration
Prefer useModel to separately defining a prop and emit
Use Tailwind 4 utility classes for styling; avoid using <style> blocks in Vue components
Use semantic Tailwind values from style.css theme instead of the dark: variant; for example, use bg-node-component-surface instead of dark: prefixes
Always use cn() utility from @/utils/tailwindUtil to merge Tailwind class names; do not use :class="[]" syntax
Use ref for reactive state in Vue Composition API components
Implement computed properties with computed() from Vue; avoid using a ref with a watch if a computed would work instead
Use watch and watchEffect for side effects in Vue components
Implement lifecycle hooks using onMounted, onUpdated, and other Vue lifecycle functions
Use provide/inject for 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 redundant computed, and prefer computed over watch
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 in src/locales/en/main.json
Avoid new usage of PrimeVue components; prefer shadcn/vue or Reka UI instead

Files:

  • src/platform/cloud/subscription/components/SubscriptionRequiredDialogContent.vue
  • src/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.vue
  • src/platform/cloud/subscription/components/PricingTable.vue
  • src/platform/cloud/subscription/composables/useSubscriptionDialog.ts
  • src/locales/en/main.json
**/*.{ts,tsx,vue}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx,vue}: Imports must be sorted and grouped by plugin; run pnpm format before committing
Use TypeScript for type safety; never use any type - use proper TypeScript types
Never use as any type 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 nested if or for statements); 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.vue
  • src/platform/cloud/subscription/components/PricingTable.vue
  • src/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 safety

Minimize 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}: Use api.apiURL() for backend endpoints instead of constructing URLs directly
Use api.fileURL() for static file access instead of constructing URLs directly

Files:

  • src/platform/cloud/subscription/composables/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.vue
  • src/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.vue
  • src/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.vue
  • src/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.vue
  • src/platform/cloud/subscription/components/PricingTable.vue
  • src/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-hover from --color-smoke-200 to --color-smoke-400 makes 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-auto on a flex container may cause issues.

Using !overflow-y-auto with flex flex-col can lead to unexpected scrolling behavior if child content exceeds the container height. The !important modifier 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: isCurrentPlan doesn't distinguish between monthly and yearly billing cycles.

If a user is on a monthly "creator" plan and views the yearly pricing, isCurrentPlan('creator') returns true, 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 getCheckoutTier function 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.

billingCycleOptions is 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" and auto-z-index="true". Ensure this doesn't conflict with the dialog's z-index since the popover is appended to body. The dismissable and close-on-escape props are good for accessibility.

@simula-r simula-r force-pushed the feat(cloud)/yearly-pricing-dialog branch from 75c4417 to 82ae07a Compare December 17, 2025 01:36
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 fa37112 into main Dec 17, 2025
28 checks passed
@simula-r simula-r deleted the feat(cloud)/yearly-pricing-dialog branch December 17, 2025 02:20
const checkoutTier = getCheckoutTier(tierKey, currentBillingCycle.value)
const response = await fetch(
`${getComfyApiBaseUrl()}/customers/cloud-subscription-checkout/${tierKey}`,
`${getComfyApiBaseUrl()}/customers/cloud-subscription-checkout/${checkoutTier}`,
Copy link
Contributor

Choose a reason for hiding this comment

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

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.

@simula-r simula-r added cloud/1.35 needs-backport Fix/change that needs to be cherry-picked to the current feature freeze branch labels Dec 17, 2025
github-actions bot pushed a commit that referenced this pull request Dec 17, 2025
## 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)
@comfy-pr-bot
Copy link
Member

@simula-r Successfully backported to #7581

@github-actions github-actions bot removed the needs-backport Fix/change that needs to be cherry-picked to the current feature freeze branch label Dec 17, 2025
simula-r added a commit that referenced this pull request Dec 17, 2025
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>
Enferlain pushed a commit to Enferlain/ComfyUI_frontend that referenced this pull request Dec 18, 2025
## 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)
Yourz pushed a commit that referenced this pull request Dec 24, 2025
## 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)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:L This PR changes 100-499 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants