Skip to content

Conversation

@thebeyondr
Copy link
Contributor

@thebeyondr thebeyondr commented Sep 24, 2025

Overview

Streamlines the onboarding flow, adds auto-complete features, and improves the plan selection interface.

Key Changes

Visual & Typography

  • Added Geist fonts
  • Improved stepper layout and spacing
  • Removed card wrapper for cleaner look
  • Added navigation header with branding

User Experience

Welcome Step

  • Auto-generates API keys for new users
  • Detects self-hosting vs hosted context
  • Shows existing API key status with copy button
  • Skip option for users with existing keys
  • Better error handling with retry

Referral Step

  • Auto-complete after selection (1s delay)
  • Changed copy to "How did you find us?"
  • Less pressure in messaging
  • 1.5s delay for typing in "other" field

Plan Choice

  • Complete redesign with 4-column grid
  • Added Free Plan as default option
  • Plan cards with icons, pricing, features
  • Context-aware messaging for self-hosting
  • Better Pro plan integration

Technical

Navigation & Flow

  • Simplified onboarding page structure
  • Dynamic button text based on selections
  • Better backward navigation
  • Removed separate API key step
  • Updated step indices

State Management

  • Better loading states and error handling
  • Improved authentication checks
  • Cache invalidation for API keys
  • Free plan selected by default

Code Quality

  • Better TypeScript usage
  • Cleaner component structure
  • Improved error boundaries
  • Better CSS organization

Files Changed

  • apps/ui/src/app/globals.css - Added Geist font variables
  • apps/ui/src/app/layout.tsx - Integrated Geist fonts
  • apps/ui/src/app/onboarding/onboarding-client.tsx - Better loading/error states
  • apps/ui/src/app/onboarding/page.tsx - Simplified page structure
  • apps/ui/src/components/onboarding/onboarding-wizard.tsx - Major UX improvements
  • apps/ui/src/components/onboarding/plan-choice-step.tsx - Complete redesign
  • apps/ui/src/components/onboarding/referral-step.tsx - Auto-complete functionality
  • apps/ui/src/components/onboarding/welcome-step.tsx - API key management integration
  • apps/ui/src/lib/components/stepper.tsx - Layout improvements

Testing

  • Manual testing of onboarding flow
  • Verified API key generation works correctly
  • Tested auto-complete functionality
  • Confirmed responsive design works on mobile/desktop
  • Validated self-hosting detection
  • Tested error states and retry mechanisms

Breaking Changes

None - this is purely additive and improves existing functionality.

Migration Notes

No migration required. All changes are backward compatible.


Commit History:

  • 907edb4 - Merge branch 'main' into fix/onboarding
  • 1505d20 - fix(ui): update onboarding wizard text
  • 9bdccfd - feat(ui): add auto-complete to referral step
  • 1dd3eda - feat(ui): add API key management to welcome step
  • a88f9b2 - fix(ui): improve stepper layout and styling
  • c784606 - feat(ui): improve plan choice step with new features
  • 3fc6bd8 - feat(ui): improve onboarding navigation and UX
  • d569bda - refactor(ui): simplify onboarding page by removing UserProvider
  • e9d5760 - feat(ui): enhance onboarding UX
  • 2a5ddd6 - feat(ui): add Geist fonts

Summary by CodeRabbit

  • New Features

    • Onboarding overhaul: header/logo, backward navigation, dynamic step text, loading spinner and error retry, streamlined flows with direct free-plan completion and removed separate API-key step.
    • Welcome step: API key status display, auto-creation when needed, copy-to-clipboard, skip-to-dashboard option.
    • Referral step: gentler copy, auto-advance after selection, improved "other" input behavior.
    • Plan selection: responsive card grid with contextual messaging, badges, and pro/upgrade flows.
  • Style

    • Updated typography to Geist fonts, tightened/centered stepper layout, and new marquee/accordion animations.
  • Chores

    • Improved commit/pre-commit hook reliability.

Updated the date format for the API key creation timestamp in the onboarding component to a more user-friendly format, enhancing readability.
Revised the development setup section to clarify the steps for starting required services, building packages, and setting up the database. Added notes for WSL2 users regarding Docker integration.
- Replace status column with inline badges for better space usage
- Remove redundant "Keys" card wrapper on mobile for cleaner design
- Fix mobile button placement to be full-width and below header
- Remove redundant copy and simplify card descriptions
- Use consistent badge styling across desktop and mobile views
- Updated layout to use flexbox for improved responsiveness
- Separated desktop and mobile views for API keys display
- Simplified card structure and descriptions for clarity
- Ensured full-width button on mobile for better usability
- Revised guidance to specify AI agents instead of Claude Code
- Added new commands for pushing and seeding the database
- Corrected "LLMGateway" to "LLM Gateway" for consistency
- Introduced folder structure section for better organization
- Added license information and details on enterprise features
- Changed badge variant from "outline" to "secondary" for better visual distinction
- Updated active status badge styles for improved readability and consistency across the UI
- Added StatusBadge component to standardize status display across API keys and provider keys lists
- Replaced existing badge implementations with StatusBadge for improved consistency and styling
- Updated the development setup section to combine dependency installation and environment setup into a single command.
- Clarified the steps for starting development servers and building for production.
- Removed redundant steps for starting services and setting up the database, streamlining the process for users.
- Implemented a status filter for API keys, allowing users to view all, active, or inactive keys.
- Added bulk activation functionality for inactive keys, enhancing user management capabilities.
- Introduced Tabs and Tooltip components for improved UI interaction and information display.
- Updated the API keys list to reflect the new filtering and activation features.
- Replaced the Plus icon with Orbit for a fresh look in the Create API Key button.
- Enhanced the description text to clarify the purpose of API keys.
- Adjusted the Card component layout for better spacing and visual appeal.
- Simplified header and description text for clarity.
- Enhanced error handling for project loading status.
- Improved layout for displaying existing API keys with mobile responsiveness.
- Added Tooltip for displaying creation dates in a user-friendly format.
- Updated card structure for newly created API keys to enhance visibility and organization.
- Added dynamic icons for different status states (active, inactive, default) using lucide-react.
- Improved status text formatting for better readability.
- Updated badge styling for a more consistent and visually appealing layout.
- Updated tooltip span in ApiKeysList and ApiKeyStep components to include a dotted underline and hover effect for improved user interaction and visual clarity.
- Added auto-switching between active and inactive tabs based on key availability.
- Enhanced loading state handling and ensured active tab displays results after activation.
- Implemented a mobile layout featuring a compact circular progress indicator.
- Improved accessibility and visual clarity with updated button and text styles.
- Ensured step titles and optional indicators are displayed correctly across devices.
- Added loading and error states with spinner and retry
- Redirect users to the login page only when not loading and no user data is available.
- Wrap the OnboardingWizard with UserProvider
- Removed UserProvider and server-side data fetching for user data.
- Updated OnboardingPage to directly render OnboardingClient, enhancing performance and reducing complexity.
- Replace Card with NavigationMenu for better layout
- Set default plan to "Free Plan" with updated logic
- Enhance step handling for backward navigation
- Update stepper with dynamic text based on user state
- Add Credits, BYOK, and Enterprise plans with descriptions
- Implement responsive grid layout for plan options
- Detect self-hosting and update messaging accordingly
- Add visual elements with icons and badges for plan states
- Improve button interactions based on plan availability
- Adjusted the stepper component to use full width and centered layout for better responsiveness.
- Removed optional label rendering for steps to simplify the UI.
- Ensured consistent styling for active and completed steps with updated class names.
- Integrate API key creation/management into onboarding
- Detect existing keys and handle authentication
- Add loading states and clipboard copy functionality
- Improve UI messaging for self-hosting status
- Auto-complete referral sources based on user selection
- Update UI messaging and remove unnecessary buttons
- Improve layout and styling for better responsiveness
@bunnyshell
Copy link

bunnyshell bot commented Sep 24, 2025

❌ Preview Environment deleted from Bunnyshell

Available commands (reply to this comment):

  • 🚀 /bns:deploy to deploy the environment

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Sep 24, 2025

Walkthrough

Refactors onboarding into a client-driven flow with a UserProvider (loading/error states), restructures wizard and steps (adds "free", removes ApiKey step, auto-advances referral, data-driven plan chooser), enhances WelcomeStep API-key logic, updates fonts/globals and layout, narrows Stepper, adds AppConfig.contactEmail, tweaks husky hooks, and removes TwitterComponents prop.

Changes

Cohort / File(s) Summary
Global styles
apps/ui/src/app/globals.css
Replaces system/code font stacks with Geist CSS-variable fonts, adds marquee and accordion keyframes, and extends @layer base rules.
Onboarding client & page
apps/ui/src/app/onboarding/onboarding-client.tsx, apps/ui/src/app/onboarding/page.tsx
Adds UserProvider usage, renders loading/error UIs, defers login redirect until auth resolved, and converts page to client-only onboarding client.
Onboarding wizard & steps
apps/ui/src/components/onboarding/onboarding-wizard.tsx, apps/ui/src/components/onboarding/plan-choice-step.tsx, apps/ui/src/components/onboarding/referral-step.tsx, apps/ui/src/components/onboarding/welcome-step.tsx
Large overhaul: header/navigation above Stepper, FlowType adds "free", removed ApiKeyStep, added selectedPlanName, plan choice is data-driven card grid with onSelectFreePlan, referral auto-advances via effects, WelcomeStep queries/creates API keys and supports skip/complete flows with cache invalidation and guarded auth flows.
Stepper layout
apps/ui/src/lib/components/stepper.tsx
Constrains desktop width (max-w-3xl), centers content, removes desktop "Optional" label, and centers children/action bar.
Tweet component API
apps/ui/src/lib/components/tweet-card.tsx
Removes TwitterComponents type/prop from MagicTweet and simplifies react-tweet imports.
Layout / fonts
apps/ui/src/app/layout.tsx
Moves font-related classes between html and body, redistributing DOM class usage for fonts and layout.
Config server
apps/ui/src/lib/config-server.ts
Adds AppConfig.contactEmail populated from NEXT_PUBLIC_CONTACT_EMAIL or default "[email protected]".
Git hooks
.husky/pre-commit, .husky/commit-msg
Adds shebangs and argument quoting to hook scripts; preserves pnpm lint-staged and pnpm commitlint invocations.
Docs
pr-description.md
Adds feature doc describing the onboarding/UI overhaul, migration notes, and commit history.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor U as User
  participant P as /onboarding Page
  participant C as OnboardingClient
  participant UP as UserProvider
  participant W as OnboardingWizard

  U->>P: Navigate to /onboarding
  P->>C: Mount client component
  C->>UP: Initialize user context
  UP-->>C: { user, isLoading, error }
  alt isLoading
    C-->>U: Show spinner + "Loading..."
  else error
    C-->>U: Show error + Retry button
  else no user
    C->>U: Redirect to /login
  else user
    C->>W: Render OnboardingWizard
  end
Loading
sequenceDiagram
  autonumber
  actor U as User
  participant W as OnboardingWizard
  participant R as ReferralStep
  participant P as PlanChoiceStep
  participant S as WelcomeStep
  participant API as Backend
  participant Nav as Router

  U->>W: Start onboarding
  W->>R: Render "How did you find us?"
  R-->>W: Auto-complete selection -> advance
  W->>P: Render plan chooser
  P-->>W: set selectedPlanName (free|credits|byok)
  W->>S: Render Welcome / API key status
  rect rgba(230,245,255)
    S->>API: Fetch existing API keys
    alt no keys & create chosen
      S->>API: Create API key
      API-->>S: Return key
      S->>API: Invalidate/refetch queries
      S->>API: Complete onboarding
      S->>Nav: Navigate to /dashboard
    else keys exist or skipped
      S->>API: Complete onboarding
      S->>Nav: Navigate to /dashboard
    end
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60–90 minutes

Possibly related PRs

Suggested labels

codex

Suggested reviewers

  • smakosh
  • steebchen

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The PR title "feat(ui): improve onboarding UX" is clearly related to the main changes in this pull request. The changeset comprehensively overhauls the onboarding flow with visual updates (Geist fonts, improved stepper, header branding), enhanced welcome step (API key auto-generation, host context detection), improved referral step (auto-complete, updated copy), and a redesigned plan choice interface (4-column grid, Free Plan default, new plan cards). The title accurately summarizes the primary focus of these changes, which is improving the user experience of the onboarding process. While the title doesn't capture every specific detail (such as the removal of the API key step or the addition of Geist fonts), it appropriately highlights the main intent from a developer's perspective.
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between dfc274f and db58dfc.

📒 Files selected for processing (2)
  • apps/ui/src/components/onboarding/onboarding-wizard.tsx (8 hunks)
  • apps/ui/src/components/onboarding/plan-choice-step.tsx (2 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx}: Never use any or as any in this pure TypeScript project unless absolutely necessary
Always use top-level import; never use require or dynamic imports

Files:

  • apps/ui/src/components/onboarding/onboarding-wizard.tsx
  • apps/ui/src/components/onboarding/plan-choice-step.tsx
apps/ui/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

apps/ui/**/*.{ts,tsx}: Use next/link for links and next/navigation's router for programmatic navigation in the UI
Use cookies for user settings not stored in the database to ensure SSR works

apps/ui/**/*.{ts,tsx}: Use next/link for links and Next.js next/navigation router for programmatic navigation
Use cookies for user settings that are not saved in the database to ensure SSR works

Files:

  • apps/ui/src/components/onboarding/onboarding-wizard.tsx
  • apps/ui/src/components/onboarding/plan-choice-step.tsx
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

Always use top-level import; never use require() or dynamic imports

Files:

  • apps/ui/src/components/onboarding/onboarding-wizard.tsx
  • apps/ui/src/components/onboarding/plan-choice-step.tsx
🧬 Code graph analysis (2)
apps/ui/src/components/onboarding/onboarding-wizard.tsx (2)
apps/ui/src/components/onboarding/plan-choice-step.tsx (1)
  • PlanChoiceStep (32-289)
apps/ui/src/lib/components/stepper.tsx (1)
  • Stepper (24-165)
apps/ui/src/components/onboarding/plan-choice-step.tsx (4)
apps/playground/src/lib/config.tsx (1)
  • useAppConfig (25-31)
apps/ui/src/hooks/useOrganization.ts (1)
  • useDefaultOrganization (9-21)
apps/ui/src/lib/components/stepper.tsx (1)
  • Step (172-174)
apps/ui/src/components/shared/upgrade-to-pro-dialog.tsx (1)
  • UpgradeToProDialog (24-38)
⏰ 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: build / run
  • GitHub Check: generate / run
  • GitHub Check: test / run
  • GitHub Check: lint / run
🔇 Additional comments (15)
apps/ui/src/components/onboarding/plan-choice-step.tsx (6)

1-24: LGTM! Clean imports and component structure.

All imports are top-level (no dynamic imports or require), TypeScript types are properly declared, and the component follows Next.js best practices.


26-30: Interface correctly declares all required props.

The onSelectFreePlan prop has been added to match the caller in OnboardingWizard. This resolves any potential prop-type mismatches.


32-41: Well-structured component initialization.

The use of useAppConfig and useDefaultOrganization hooks follows React patterns, and the localhost detection logic is clear and correct.


131-158: Color class helper provides clean abstraction.

The getColorClasses function centralizes styling logic for different plan states (current, pro, color variants) and returns consistent dot/card classes. This approach keeps the rendering code clean.


160-289: Responsive grid and card rendering looks solid.

The mobile-first responsive grid (grid-cols-1 lg:grid-cols-2 xl:grid-cols-4) properly adapts to screen sizes. The conditional rendering of badges, pro-requirement messaging, and button wrappers (UpgradeToProDialog vs direct Button) correctly implements the design requirements.

Icon usage from lucide-react is tree-shakable and follows best practices from the retrieved learnings.


42-129: Data-driven plans array is excellent refactoring.

The plans array consolidates all plan metadata (icons, pricing, features, actions) in one place, making the code more maintainable and easier to extend. The conditional logic for config.hosted and isProPlan properly adapts button states and text.

However, there's a fallback issue at line 127:

The Enterprise plan's onClick handler uses config.contactEmail without a fallback. If this value is undefined (misconfigured environment, self-hosted instances), the mailto link will be mailto:undefined, breaking the contact flow.

Based on past review comments, add a fallback:

-			onClick: () => window.open(`mailto:${config.contactEmail}`, "_blank"),
+			onClick: () =>
+				window.open(
+					`mailto:${config.contactEmail ?? "[email protected]"}`,
+					"_blank",
+				),
apps/ui/src/components/onboarding/onboarding-wizard.tsx (9)

1-24: Clean imports following guidelines.

All imports are top-level (no require or dynamic imports), and the use of next/navigation for routing aligns with the coding guidelines for Next.js apps.


25-46: FlowType expansion and step definitions are well-structured.

Adding "free" to FlowType and updating step titles ("How did you find us?" and "Choose plan") correctly reflects the UX changes described in the PR objectives.


48-56: State initialization is appropriate.

The addition of selectedPlanName provides the necessary tracking for dynamic button text in the stepper. Initializing hasSelectedPlan to false correctly avoids implicitly assuming a plan selection.


69-114: Backward navigation and completion flow are robust.

The early-return for backward navigation (lines 71-74) is clean. The plan-choice skip logic (lines 77-97) properly completes onboarding with error handling when no plan is selected, preventing users from being stuck on an empty step.


116-152: Plan selection handlers correctly route users.

handleSelectCredits and handleSelectBYOK advance to step 3 and set the appropriate flow type. handleSelectFreePlan (lines 130-152) completes onboarding directly, as recommended in past review comments, avoiding the dead-step issue.

Error handling in the free-plan handler logs failures and keeps the user on the current step, which is appropriate for UX resilience.


154-160: Referral completion advances to correct step.

Moving to step 2 (Plan Choice) after referral completion aligns with the updated step indices after removing the ApiKeyStep.


163-200: Step rendering logic correctly maps indices to components.

The conditional checks for activeStep === 2 and activeStep === 3 correctly render PlanChoiceStep, CreditsStep, and ProviderKeyStep based on flow type. The Stripe Elements wrapper for credits is properly applied.


203-227: Dynamic stepper button text enhances UX.

Customizing customNextText at each step (lines 207-219) provides clear guidance to users about what happens next. The conditional text for the plan-choice step ("Continue with ${selectedPlanName}" vs "Skip") aligns well with the user's selection state.


229-256: New header with branding improves onboarding presentation.

The NavigationMenu with Logo and branding text adds a professional touch to the onboarding flow. The Stepper integration remains clean, and the dynamic nextButtonDisabled logic for the credits flow ensures users can't proceed until payment succeeds.


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

Tip

🧪 Early access (models): enabled

We are currently testing Sonnet 4.5 code review models, which should lead to better review quality. However, this model may result in higher noise levels in the review comments. Please disable the early access features if the noise level causes any inconvenience.

Note:

  • Public repositories are always opted into early access features.
  • You can enable or disable early access features from the CodeRabbit UI or by updating the CodeRabbit configuration file.

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

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

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 98a81a3 and abc7b44.

📒 Files selected for processing (2)
  • .husky/commit-msg (1 hunks)
  • .husky/pre-commit (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • .husky/commit-msg
🧰 Additional context used
🧠 Learnings (5)
📚 Learning: 2025-09-25T15:37:36.675Z
Learnt from: CR
PR: theopenco/llmgateway#0
File: CLAUDE.md:0-0
Timestamp: 2025-09-25T15:37:36.675Z
Learning: Always run `pnpm format` before committing; use `pnpm lint` to check without fixing

Applied to files:

  • .husky/pre-commit
📚 Learning: 2025-09-25T15:39:12.723Z
Learnt from: CR
PR: theopenco/llmgateway#0
File: AGENTS.md:0-0
Timestamp: 2025-09-25T15:39:12.723Z
Learning: Run `pnpm test:unit` and `pnpm test:e2e` after adding features

Applied to files:

  • .husky/pre-commit
📚 Learning: 2025-09-25T15:39:12.723Z
Learnt from: CR
PR: theopenco/llmgateway#0
File: AGENTS.md:0-0
Timestamp: 2025-09-25T15:39:12.723Z
Learning: Run `pnpm format` after code changes

Applied to files:

  • .husky/pre-commit
📚 Learning: 2025-09-25T15:37:36.675Z
Learnt from: CR
PR: theopenco/llmgateway#0
File: CLAUDE.md:0-0
Timestamp: 2025-09-25T15:37:36.675Z
Learning: Always use pnpm for package management

Applied to files:

  • .husky/pre-commit
📚 Learning: 2025-09-25T15:39:12.723Z
Learnt from: CR
PR: theopenco/llmgateway#0
File: AGENTS.md:0-0
Timestamp: 2025-09-25T15:39:12.723Z
Learning: Run `pnpm build` to ensure production builds work

Applied to files:

  • .husky/pre-commit
⏰ 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: build / run
  • GitHub Check: lint / run
  • GitHub Check: test / run
  • GitHub Check: generate / run

- Moved fnm loading logic to the top of both commit-msg and pre-commit hooks for better clarity and consistency.
- Ensured that the check for pnpm availability remains intact and functional.
- Introduced `isSelfHostedHost` to check if a hostname indicates a self-hosted environment.
- Added `isSelfHostedEnvironment` to determine if the current environment is self-hosted based on `window.location.hostname`.
- Replaced manual localhost detection with `isSelfHostedEnvironment` utility for cleaner code.
- Updated user messaging to reflect the Pro plan status in the onboarding step.
- Wrapped onboarding completion logic in a try-catch block to log errors without disrupting the user flow.
- Ensured users remain on the current step if onboarding fails, improving overall user experience.
- Replaced manual localhost checks with `isSelfHostedEnvironment` utility for improved readability.
- Updated dependencies in effect hooks to ensure proper data fetching and state management.
- Cleaned up conditional rendering for API key status messages to enhance user experience.
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: 3

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between abc7b44 and 13ef60f.

📒 Files selected for processing (2)
  • .husky/commit-msg (1 hunks)
  • .husky/pre-commit (1 hunks)
🧰 Additional context used
🧠 Learnings (4)
📚 Learning: 2025-09-25T15:37:36.675Z
Learnt from: CR
PR: theopenco/llmgateway#0
File: CLAUDE.md:0-0
Timestamp: 2025-09-25T15:37:36.675Z
Learning: Always run `pnpm format` before committing; use `pnpm lint` to check without fixing

Applied to files:

  • .husky/commit-msg
  • .husky/pre-commit
📚 Learning: 2025-09-25T15:39:12.723Z
Learnt from: CR
PR: theopenco/llmgateway#0
File: AGENTS.md:0-0
Timestamp: 2025-09-25T15:39:12.723Z
Learning: Run `pnpm format` after code changes

Applied to files:

  • .husky/commit-msg
  • .husky/pre-commit
📚 Learning: 2025-09-25T15:39:12.723Z
Learnt from: CR
PR: theopenco/llmgateway#0
File: AGENTS.md:0-0
Timestamp: 2025-09-25T15:39:12.723Z
Learning: Run `pnpm build` to ensure production builds work

Applied to files:

  • .husky/pre-commit
📚 Learning: 2025-09-25T15:37:36.675Z
Learnt from: CR
PR: theopenco/llmgateway#0
File: CLAUDE.md:0-0
Timestamp: 2025-09-25T15:37:36.675Z
Learning: Always use pnpm for package management

Applied to files:

  • .husky/pre-commit
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: test / run
  • GitHub Check: generate / run
  • GitHub Check: build / run
  • GitHub Check: lint / run

- Updated fnm loading logic to check for command availability before attempting to load the environment.
- Ensured commit message file is properly quoted when passed to commitlint for better handling of filenames.
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

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

⚠️ Outside diff range comments (1)
apps/ui/src/components/onboarding/onboarding-wizard.tsx (1)

42-45: Credits step can be skipped without paying

When the user selects the Credits plan, the last step stays marked as optional, so the Stepper renders a “Skip” button. Clicking it jumps to completion and calls /complete-onboarding even though no payment succeeded. That lets users bypass the required purchase entirely. Make the credits step non-optional (and drop the optional override tied to isPaymentSuccessful) so the skip button never appears during the credits flow.

 	{
 		id: flowType === "credits" ? "credits" : "provider-key",
 		title: flowType === "credits" ? "Credits" : "Provider Key",
-		optional: true,
+		optional: flowType !== "credits",
 	},
@@
-			...(index === 3 &&
-				flowType === "credits" &&
-				isPaymentSuccessful && {
-					optional: false,
-				}),

Also applies to: 205-209

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 13ef60f and af656fb.

📒 Files selected for processing (6)
  • .husky/commit-msg (1 hunks)
  • .husky/pre-commit (1 hunks)
  • apps/ui/src/components/onboarding/onboarding-wizard.tsx (8 hunks)
  • apps/ui/src/components/onboarding/plan-choice-step.tsx (2 hunks)
  • apps/ui/src/components/onboarding/welcome-step.tsx (1 hunks)
  • apps/ui/src/lib/utils/self-host.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • .husky/pre-commit
  • .husky/commit-msg
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx}: Never use any or as any in this TypeScript project unless absolutely necessary
Always use top-level import; never use require or dynamic import()

Files:

  • apps/ui/src/lib/utils/self-host.ts
  • apps/ui/src/components/onboarding/plan-choice-step.tsx
  • apps/ui/src/components/onboarding/onboarding-wizard.tsx
  • apps/ui/src/components/onboarding/welcome-step.tsx
apps/ui/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

apps/ui/**/*.{ts,tsx}: In the Next.js UI, use next/link for links and next/navigation router for programmatic navigation
Use cookies for user settings not stored in the database to ensure SSR compatibility

Use cookies for user settings not stored in the database to ensure SSR works

Files:

  • apps/ui/src/lib/utils/self-host.ts
  • apps/ui/src/components/onboarding/plan-choice-step.tsx
  • apps/ui/src/components/onboarding/onboarding-wizard.tsx
  • apps/ui/src/components/onboarding/welcome-step.tsx
{apps,packages}/**/src/**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

Always use top-level import; never use require() or dynamic import()

Files:

  • apps/ui/src/lib/utils/self-host.ts
  • apps/ui/src/components/onboarding/plan-choice-step.tsx
  • apps/ui/src/components/onboarding/onboarding-wizard.tsx
  • apps/ui/src/components/onboarding/welcome-step.tsx
apps/ui/**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

Use next/link for links and next/navigation's router for programmatic navigation

Files:

  • apps/ui/src/lib/utils/self-host.ts
  • apps/ui/src/components/onboarding/plan-choice-step.tsx
  • apps/ui/src/components/onboarding/onboarding-wizard.tsx
  • apps/ui/src/components/onboarding/welcome-step.tsx
🧬 Code graph analysis (3)
apps/ui/src/components/onboarding/plan-choice-step.tsx (5)
apps/playground/src/lib/config.tsx (1)
  • useAppConfig (25-31)
apps/ui/src/hooks/useOrganization.ts (1)
  • useDefaultOrganization (9-21)
apps/ui/src/lib/utils/self-host.ts (1)
  • isSelfHostedEnvironment (25-30)
apps/ui/src/lib/components/stepper.tsx (1)
  • Step (172-174)
apps/ui/src/components/shared/upgrade-to-pro-dialog.tsx (1)
  • UpgradeToProDialog (24-38)
apps/ui/src/components/onboarding/onboarding-wizard.tsx (4)
apps/ui/src/components/onboarding/plan-choice-step.tsx (1)
  • PlanChoiceStep (33-295)
apps/ui/src/lib/components/navigation-menu.tsx (3)
  • NavigationMenu (159-159)
  • NavigationMenuList (160-160)
  • NavigationMenuItem (161-161)
apps/playground/src/components/ui/logo.tsx (1)
  • Logo (3-19)
apps/ui/src/lib/components/stepper.tsx (1)
  • Stepper (24-165)
apps/ui/src/components/onboarding/welcome-step.tsx (4)
apps/ui/src/lib/auth-client.ts (1)
  • useAuth (20-33)
apps/ui/src/hooks/useDefaultProject.ts (1)
  • useDefaultProject (3-35)
apps/ui/src/lib/fetch-client.ts (1)
  • useApi (22-28)
apps/ui/src/lib/utils/self-host.ts (1)
  • isSelfHostedEnvironment (25-30)
🔇 Additional comments (1)
apps/ui/src/components/onboarding/welcome-step.tsx (1)

283-285: Show skip CTA when at least one key exists

The skip button only renders when existingKeys.apiKeys.length > 1, so a returning project with exactly one key never sees the CTA and must redo onboarding. This regresses the intended “skip when you already have a key” behavior noted earlier. Please switch the condition to >= 1.

-				{existingKeys?.apiKeys &&
-					existingKeys.apiKeys.length > 1 &&
+				{existingKeys?.apiKeys &&
+					existingKeys.apiKeys.length >= 1 &&
 					!apiKey && (

- Eliminated fnm loading logic from commit-msg and pre-commit hooks for simplicity.
- Removed pnpm availability checks, streamlining the hooks to focus on their primary functions.
- Replaced manual localhost detection with a direct check on the app configuration for improved clarity.
- Cleaned up the onboarding components by removing the  utility and related state management.
- Enhanced conditional rendering for API key status messages to streamline user experience.
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

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

⚠️ Outside diff range comments (1)
apps/ui/src/components/onboarding/onboarding-wizard.tsx (1)

99-111: Handle onboarding completion failures to avoid crashing

When handleStepChange finishes the flow it awaits completeOnboarding.mutateAsync without guarding failures. If that request rejects, the async handler throws, React treats it as an unhandled error, and the onboarding UI crashes. Wrap the mutation in try/catch (and surface feedback) before navigating.

-		if (step >= STEPS.length) {
-			posthog.capture("onboarding_completed", {
-				completedSteps: STEPS.map((step) => step.id),
-				flowType,
-				referralSource: referralSource || "not_provided",
-				referralDetails: referralDetails || undefined,
-			});
-
-			await completeOnboarding.mutateAsync({});
-			const queryKey = api.queryOptions("get", "/user/me").queryKey;
-			await queryClient.invalidateQueries({ queryKey });
-			router.push("/dashboard");
+		if (step >= STEPS.length) {
+			posthog.capture("onboarding_completed", {
+				completedSteps: STEPS.map((step) => step.id),
+				flowType,
+				referralSource: referralSource || "not_provided",
+				referralDetails: referralDetails || undefined,
+			});
+
+			try {
+				await completeOnboarding.mutateAsync({});
+				const queryKey = api.queryOptions("get", "/user/me").queryKey;
+				await queryClient.invalidateQueries({ queryKey });
+				router.push("/dashboard");
+			} catch (error) {
+				console.error("Failed to complete onboarding:", error);
+				return;
+			}
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between af656fb and c0754ba.

📒 Files selected for processing (5)
  • .husky/commit-msg (1 hunks)
  • .husky/pre-commit (1 hunks)
  • apps/ui/src/components/onboarding/onboarding-wizard.tsx (8 hunks)
  • apps/ui/src/components/onboarding/plan-choice-step.tsx (2 hunks)
  • apps/ui/src/components/onboarding/welcome-step.tsx (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • .husky/commit-msg
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx}: Never use any or as any in this TypeScript project unless absolutely necessary
Always use top-level import; never use require or dynamic import()

Files:

  • apps/ui/src/components/onboarding/welcome-step.tsx
  • apps/ui/src/components/onboarding/onboarding-wizard.tsx
  • apps/ui/src/components/onboarding/plan-choice-step.tsx
apps/ui/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

apps/ui/**/*.{ts,tsx}: In the Next.js UI, use next/link for links and next/navigation router for programmatic navigation
Use cookies for user settings not stored in the database to ensure SSR compatibility

Use cookies for user settings not stored in the database to ensure SSR works

Files:

  • apps/ui/src/components/onboarding/welcome-step.tsx
  • apps/ui/src/components/onboarding/onboarding-wizard.tsx
  • apps/ui/src/components/onboarding/plan-choice-step.tsx
{apps,packages}/**/src/**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

Always use top-level import; never use require() or dynamic import()

Files:

  • apps/ui/src/components/onboarding/welcome-step.tsx
  • apps/ui/src/components/onboarding/onboarding-wizard.tsx
  • apps/ui/src/components/onboarding/plan-choice-step.tsx
apps/ui/**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

Use next/link for links and next/navigation's router for programmatic navigation

Files:

  • apps/ui/src/components/onboarding/welcome-step.tsx
  • apps/ui/src/components/onboarding/onboarding-wizard.tsx
  • apps/ui/src/components/onboarding/plan-choice-step.tsx
🧠 Learnings (4)
📚 Learning: 2025-09-25T15:37:36.675Z
Learnt from: CR
PR: theopenco/llmgateway#0
File: CLAUDE.md:0-0
Timestamp: 2025-09-25T15:37:36.675Z
Learning: Always run `pnpm format` before committing; use `pnpm lint` to check without fixing

Applied to files:

  • .husky/pre-commit
📚 Learning: 2025-09-25T15:39:12.723Z
Learnt from: CR
PR: theopenco/llmgateway#0
File: AGENTS.md:0-0
Timestamp: 2025-09-25T15:39:12.723Z
Learning: Run `pnpm build` to ensure production builds work

Applied to files:

  • .husky/pre-commit
📚 Learning: 2025-09-25T15:39:12.723Z
Learnt from: CR
PR: theopenco/llmgateway#0
File: AGENTS.md:0-0
Timestamp: 2025-09-25T15:39:12.723Z
Learning: Run `pnpm format` after code changes

Applied to files:

  • .husky/pre-commit
📚 Learning: 2025-09-25T15:39:12.723Z
Learnt from: CR
PR: theopenco/llmgateway#0
File: AGENTS.md:0-0
Timestamp: 2025-09-25T15:39:12.723Z
Learning: Always use pnpm for package management

Applied to files:

  • .husky/pre-commit
🧬 Code graph analysis (3)
apps/ui/src/components/onboarding/welcome-step.tsx (4)
apps/ui/src/lib/auth-client.ts (1)
  • useAuth (20-33)
apps/ui/src/hooks/useDefaultProject.ts (1)
  • useDefaultProject (3-35)
apps/ui/src/lib/fetch-client.ts (1)
  • useApi (22-28)
apps/playground/src/lib/config.tsx (1)
  • useAppConfig (25-31)
apps/ui/src/components/onboarding/onboarding-wizard.tsx (4)
apps/ui/src/components/onboarding/plan-choice-step.tsx (1)
  • PlanChoiceStep (32-289)
apps/ui/src/lib/components/navigation-menu.tsx (3)
  • NavigationMenu (159-159)
  • NavigationMenuList (160-160)
  • NavigationMenuItem (161-161)
apps/playground/src/components/ui/logo.tsx (1)
  • Logo (3-19)
apps/ui/src/lib/components/stepper.tsx (1)
  • Stepper (24-165)
apps/ui/src/components/onboarding/plan-choice-step.tsx (4)
apps/playground/src/lib/config.tsx (1)
  • useAppConfig (25-31)
apps/ui/src/hooks/useOrganization.ts (1)
  • useDefaultOrganization (9-21)
apps/ui/src/lib/components/stepper.tsx (1)
  • Step (172-174)
apps/ui/src/components/shared/upgrade-to-pro-dialog.tsx (1)
  • UpgradeToProDialog (24-38)
⏰ 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: build / run
  • GitHub Check: generate / run
  • GitHub Check: lint / run
  • GitHub Check: test / run
🔇 Additional comments (2)
.husky/pre-commit (1)

1-2: Shebang and hook command look solid.

Running pnpm lint-staged under an explicit Bash shebang restores a working pre-commit hook with no extraneous setup. Looks good.

apps/ui/src/components/onboarding/welcome-step.tsx (1)

279-281: Skip CTA should trigger for users with a single key

The guard existingKeys.apiKeys.length > 1 hides the “Skip setup” CTA unless the user has at least two keys. A user who already has exactly one key (the common case) can no longer exit onboarding, which is a regression. Please relax the condition to cover >= 1.

-				{existingKeys?.apiKeys &&
-					existingKeys.apiKeys.length > 1 &&
+				{existingKeys?.apiKeys &&
+					existingKeys.apiKeys.length >= 1 &&

Comment on lines +124 to +128
buttonText: "Contact Sales",
buttonVariant: "outline" as const,
buttonDisabled: false,
onClick: () => window.open(`mailto:${config.contactEmail}`, "_blank"),
},
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Fallback contact email is required

If config.contactEmail is undefined (misconfigured env, self-hosted tenants), the Enterprise CTA opens mailto:undefined, breaking the contact path. Add a sensible default to keep the link valid.

-			onClick: () => window.open(`mailto:${config.contactEmail}`, "_blank"),
+			onClick: () =>
+				window.open(
+					`mailto:${config.contactEmail ?? "[email protected]"}`,
+					"_blank",
+				),
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
buttonText: "Contact Sales",
buttonVariant: "outline" as const,
buttonDisabled: false,
onClick: () => window.open(`mailto:${config.contactEmail}`, "_blank"),
},
buttonText: "Contact Sales",
buttonVariant: "outline" as const,
buttonDisabled: false,
onClick: () =>
window.open(
`mailto:${config.contactEmail ?? "[email protected]"}`,
"_blank",
),
},
🤖 Prompt for AI Agents
In apps/ui/src/components/onboarding/plan-choice-step.tsx around lines 124 to
128, the Enterprise CTA builds a mailto link with config.contactEmail which can
be undefined; change the onClick handler to use a sensible fallback email (e.g.
'[email protected]' or a project-wide SUPPORT_EMAIL constant) so the mailto
URL is always valid, e.g. use (config.contactEmail || '[email protected]')
when constructing the link; ensure the fallback value is documented or pulled
from a central config constant rather than hard-coded inline.

- Removed unused  prop from  for clarity.
- Streamlined error handling in  by adjusting indentation for better readability.
- Added free option to FlowType for onboarding steps.
- Enhanced handleSelectFreePlan function to complete onboarding directly for the free plan.
- Integrated analytics tracking for onboarding completion with the free plan.
- Improved error handling during onboarding completion process.
- Reformatted font-family declarations in body and code for better clarity.
- Ensured consistent indentation and spacing throughout the CSS file.
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 (2)
apps/ui/src/components/onboarding/onboarding-wizard.tsx (1)

76-97: Good skip handling at plan choice; consider null-safe PostHog calls.

Skip flow correctly completes onboarding and routes to the dashboard. However, use optional chaining on PostHog to avoid a runtime crash when the provider isn’t initialized.

Apply this diff for null-safety:

-				posthog.capture("onboarding_skipped", {
+				posthog?.capture("onboarding_skipped", {
 					skippedAt: "plan_choice",
 					referralSource: referralSource || "not_provided",
 					referralDetails: referralDetails || undefined,
 				});

Also applies to: 86-93

apps/ui/src/lib/components/tweet-card.tsx (1)

228-249: Stop leaking non-DOM props to the DOM; type and filter forwarded props.

TweetCard forwards ...props (from TweetProps) into MagicTweet, which spreads them onto a <div>. Props like apiUrl/fetchOptions will end up as invalid DOM attributes, causing React warnings and noisy markup. Also, MagicTweet’s ...props isn’t typed for DOM attributes.

Fix by:

  • Typing MagicTweet to accept React.HTMLAttributes<HTMLDivElement>.
  • Narrowing what TweetCard forwards to MagicTweet (only DOM attrs + className), keeping react‑tweet props internal.

Apply this diff:

@@
 export const MagicTweet = ({
   tweet,
   className,
-  ...props
-}: {
-  tweet: Tweet;
-  className?: string;
-}) => {
+  ...divProps
+}: React.HTMLAttributes<HTMLDivElement> & {
+  tweet: Tweet;
+  className?: string;
+}) => {
   const enrichedTweet = enrichTweet(tweet);
   return (
     <div
       className={cn(
         "relative flex w-full h-64 max-w-lg flex-col gap-2 overflow-hidden rounded-lg border p-4 backdrop-blur-md",
         className,
       )}
-      {...props}
+      {...divProps}
     >
       <TweetHeader tweet={enrichedTweet} />
       <TweetBody tweet={enrichedTweet} />
     </div>
   );
 };
@@
-export const TweetCard = async ({
-  id,
-  components,
-  fallback = <TweetSkeleton />,
-  onError,
-  ...props
-}: TweetProps & {
-  className?: string;
-}) => {
+type TweetCardProps = (TweetProps & { className?: string }) &
+  React.HTMLAttributes<HTMLDivElement>;
+
+export const TweetCard = async ({
+  id,
+  components,
+  fallback = <TweetSkeleton />,
+  onError,
+  className,
+  ...divProps
+}: TweetCardProps) => {
@@
   if (!tweet) {
     const NotFound = components?.TweetNotFound || TweetNotFound;
-    return <NotFound {...props} />;
+    return <NotFound className={className} {...divProps} />;
   }
 
   return (
     <Suspense fallback={fallback}>
-      <MagicTweet tweet={tweet} {...props} />
+      <MagicTweet tweet={tweet} className={className} {...divProps} />
     </Suspense>
   );
 }

Also applies to: 279-281

🧹 Nitpick comments (6)
apps/ui/src/components/onboarding/onboarding-wizard.tsx (3)

99-112: Wrap final completion in try/catch to avoid dead-ends on failure.

If completeOnboarding rejects here, the user is stuck with no feedback. Align error handling with the skip and free flows.

Apply this diff:

-			posthog.capture("onboarding_completed", {
+			posthog?.capture("onboarding_completed", {
 				completedSteps: STEPS.map((step) => step.id),
 				flowType,
 				referralSource: referralSource || "not_provided",
 				referralDetails: referralDetails || undefined,
 			});
-
-			await completeOnboarding.mutateAsync({});
-			const queryKey = api.queryOptions("get", "/user/me").queryKey;
-			await queryClient.invalidateQueries({ queryKey });
-			router.push("/dashboard");
+			try {
+				await completeOnboarding.mutateAsync({});
+				const queryKey = api.queryOptions("get", "/user/me").queryKey;
+				await queryClient.invalidateQueries({ queryKey });
+				router.push("/dashboard");
+			} catch (err) {
+				console.error("Failed to complete onboarding:", err);
+			}

175-183: Guard against missing Stripe instance.

If stripeLoading is false but stripe is null, Elements will error. Add a fallback UI.

Apply this diff:

-		if (activeStep === 3 && flowType === "credits") {
-			return stripeLoading ? (
+		if (activeStep === 3 && flowType === "credits") {
+			return stripeLoading ? (
 				<div className="p-6 text-center">Loading payment form...</div>
-			) : (
-				<Elements stripe={stripe}>
-					<CreditsStep onPaymentSuccess={() => setIsPaymentSuccessful(true)} />
-				</Elements>
-			);
+			) : !stripe ? (
+				<div className="p-6 text-center">Unable to initialize payment. Please retry.</div>
+			) : (
+				<Elements stripe={stripe}>
+					<CreditsStep onPaymentSuccess={() => setIsPaymentSuccessful(true)} />
+				</Elements>
+			);

230-242: Optional: Make brand header clickable to home.

Wrap the logo/title in a Link to “/” with an aria-label for better UX/accessibility.

Proposed change:

+import Link from "next/link";
...
-					<NavigationMenuItem asChild>
-						<div className="flex items-center space-x-2">
-							<Logo className="h-8 w-8 rounded-full text-black dark:text-white" />
-							<span className="text-xl font-bold tracking-tight text-zinc-900 dark:text-white">
-								LLM Gateway
-							</span>
-						</div>
-					</NavigationMenuItem>
+					<NavigationMenuItem asChild>
+						<Link href="/" aria-label="Go to home" className="flex items-center space-x-2">
+							<Logo className="h-8 w-8 rounded-full text-black dark:text-white" />
+							<span className="text-xl font-bold tracking-tight text-zinc-900 dark:text-white">
+								LLM Gateway
+							</span>
+						</Link>
+					</NavigationMenuItem>
apps/ui/src/lib/components/tweet-card.tsx (3)

278-282: Suspense fallback is a no-op after awaiting getTweet.

Because getTweet(id) is awaited before render, there’s nothing inside Suspense that can suspend. Consider removing Suspense (or moving fetch inside a child that can suspend) to simplify.

-  return (
-    <Suspense fallback={fallback}>
-      <MagicTweet tweet={tweet} className={className} {...divProps} />
-    </Suspense>
-  );
+  return <MagicTweet tweet={tweet} className={className} {...divProps} />;

213-219: Avoid ts-ignore by narrowing optional card thumbnail access.

Replace the ts-ignore with safe optional chaining and a tiny extractor to satisfy TS without suppressions.

-            // @ts-ignore
-            tweet?.card?.binding_values?.thumbnail_image_large?.image_value.url && (
+            (() => {
+              const url =
+                (tweet as unknown as {
+                  card?: {
+                    binding_values?: {
+                      thumbnail_image_large?: {
+                        image_value?: { url?: string };
+                      };
+                    };
+                  };
+                })?.card?.binding_values?.thumbnail_image_large?.image_value?.url;
+              return url;
+            })() && (
               <img
-                src={
-                  // @ts-ignore
-                  tweet.card.binding_values.thumbnail_image_large.image_value.url
-                }
+                src={
+                  (tweet as any).card.binding_values.thumbnail_image_large
+                    .image_value.url
+                }
                 className="h-64 rounded-xl border object-cover shadow-sm"
                 alt={tweet.text}
               />
             )}

Note: If tolerating a single as any is unacceptable here, extract a typed helper function. Based on learnings.


104-111: Minor a11y: make avatar alt more descriptive.

Use the display name for clarity.

-          alt={tweet.user.screen_name}
+          alt={`Avatar of ${tweet.user.name}`}
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c0754ba and 9a4fcb0.

📒 Files selected for processing (3)
  • apps/ui/src/app/globals.css (4 hunks)
  • apps/ui/src/components/onboarding/onboarding-wizard.tsx (8 hunks)
  • apps/ui/src/lib/components/tweet-card.tsx (1 hunks)
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx}: Never use any or as any in this TypeScript project unless absolutely necessary
Always use top-level import; never use require or dynamic import()

Files:

  • apps/ui/src/components/onboarding/onboarding-wizard.tsx
  • apps/ui/src/lib/components/tweet-card.tsx
apps/ui/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

apps/ui/**/*.{ts,tsx}: In the Next.js UI, use next/link for links and next/navigation router for programmatic navigation
Use cookies for user settings not stored in the database to ensure SSR compatibility

Use cookies for user settings not stored in the database to ensure SSR works

Files:

  • apps/ui/src/components/onboarding/onboarding-wizard.tsx
  • apps/ui/src/lib/components/tweet-card.tsx
{apps,packages}/**/src/**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

Always use top-level import; never use require() or dynamic import()

Files:

  • apps/ui/src/components/onboarding/onboarding-wizard.tsx
  • apps/ui/src/lib/components/tweet-card.tsx
apps/ui/**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

Use next/link for links and next/navigation's router for programmatic navigation

Files:

  • apps/ui/src/components/onboarding/onboarding-wizard.tsx
  • apps/ui/src/lib/components/tweet-card.tsx
🧠 Learnings (2)
📚 Learning: 2025-09-22T18:29:26.374Z
Learnt from: smakosh
PR: theopenco/llmgateway#911
File: apps/ui/src/lib/components/tweet-card.tsx:244-255
Timestamp: 2025-09-22T18:29:26.374Z
Learning: In the tweet-card component at apps/ui/src/lib/components/tweet-card.tsx, the TweetMedia component is intentionally not used in the MagicTweet component. This is a deliberate design decision to keep testimonials text-focused without rendering images or videos from tweets.

Applied to files:

  • apps/ui/src/lib/components/tweet-card.tsx
📚 Learning: 2025-09-22T18:30:31.975Z
Learnt from: smakosh
PR: theopenco/llmgateway#911
File: apps/ui/src/lib/components/tweet-card.tsx:170-174
Timestamp: 2025-09-22T18:30:31.975Z
Learning: In the tweet-card component at apps/ui/src/lib/components/tweet-card.tsx, the use of dangerouslySetInnerHTML for rendering tweet entity text is acceptable. The react-tweet library content is considered safe to render as HTML in this context.

Applied to files:

  • apps/ui/src/lib/components/tweet-card.tsx
🧬 Code graph analysis (1)
apps/ui/src/components/onboarding/onboarding-wizard.tsx (4)
apps/api/src/posthog.ts (1)
  • posthog (3-6)
apps/ui/src/components/onboarding/plan-choice-step.tsx (1)
  • PlanChoiceStep (32-289)
apps/playground/src/components/ui/logo.tsx (1)
  • Logo (3-19)
apps/ui/src/lib/components/stepper.tsx (1)
  • Stepper (24-165)
⏰ 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: build / run
  • GitHub Check: test / run
  • GitHub Check: generate / run
  • GitHub Check: lint / run
🔇 Additional comments (16)
apps/ui/src/components/onboarding/onboarding-wizard.tsx (15)

9-13: LGTM: Header navigation import is appropriate for the new onboarding header.


25-25: LGTM: FlowType now includes "free".


34-34: LGTM: Updated step titles improve clarity.

Also applies to: 39-40


52-52: LGTM: Track selectedPlanName with a safe default.


70-75: LGTM: Backward navigation handled safely.


119-121: LGTM: Credits path sets plan state and advances to payment.


126-128: LGTM: BYOK path sets plan state and advances to provider key step.


130-152: Free plan now completes onboarding directly — resolved prior issue.

This addresses the earlier bug where selecting Free left users on an empty step. Good job finishing the flow and invalidating cache before redirect.

Apply the same PostHog null-safety here:

-		posthog.capture("onboarding_completed", {
+		posthog?.capture("onboarding_completed", {
 			completedSteps: ["welcome", "referral", "plan-choice"],
 			flowType: "free",
 			referralSource: referralSource || "not_provided",
 			referralDetails: referralDetails || undefined,
 		});

159-159: LGTM: Referral completion advances to plan choice.


164-171: LGTM: PlanChoiceStep wiring includes Free, Credits, and BYOK handlers.


186-187: LGTM: BYOK step renders ProviderKeyStep at the correct index.


206-219: LGTM: Dynamic next button labels improve guidance; good use of selectedPlanName.


221-226: LGTM: Making credits step non-optional after success prevents accidental skip.


244-254: LGTM: Stepper props and nextButtonDisabled logic are correct for credits flow.


16-16: Logo default export confirmed — no action required

apps/ui/src/lib/components/tweet-card.tsx (1)

2-2: Import change LGTM; confirm compatibility with remaining usage.

Removing TwitterComponents from imports is fine. TweetCard still consumes components from TweetProps only to override TweetNotFound, which is consistent. Verify no other local usage depended on TwitterComponents.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (1)
apps/ui/src/app/globals.css (1)

11-30: Add fallbacks to the Geist font variables

If --font-geist-sans or --font-geist-mono ever go missing (e.g., layout wiring regresses), the entire font-family declaration becomes invalid and the UI drops to the browser default serif. Wrapping the full stack in the variable fallback keeps the intended system stack as a safety net.

-	font-family:
-		var(--font-geist-sans),
-		-apple-system,
-		BlinkMacSystemFont,
-		"Segoe UI",
-		"Roboto",
-		"Oxygen",
-		"Ubuntu",
-		"Cantarell",
-		"Fira Sans",
-		"Droid Sans",
-		"Helvetica Neue",
-		sans-serif;
+	font-family:
+		var(
+			--font-geist-sans,
+			-apple-system,
+			BlinkMacSystemFont,
+			"Segoe UI",
+			"Roboto",
+			"Oxygen",
+			"Ubuntu",
+			"Cantarell",
+			"Fira Sans",
+			"Droid Sans",
+			"Helvetica Neue",
+			sans-serif
+		);
@@
-	font-family:
-		var(--font-geist-mono), source-code-pro, Menlo, Monaco, Consolas,
-		"Courier New", monospace;
+	font-family:
+		var(
+			--font-geist-mono,
+			source-code-pro,
+			Menlo,
+			Monaco,
+			Consolas,
+			"Courier New",
+			monospace
+		);
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9a4fcb0 and a3a531f.

📒 Files selected for processing (1)
  • apps/ui/src/app/globals.css (6 hunks)
⏰ 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: build / run
  • GitHub Check: generate / run
  • GitHub Check: test / run
  • GitHub Check: lint / run

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)
apps/ui/src/app/globals.css (1)

48-49: Light theme destructive-foreground equals background — contrast bug

--destructive and --destructive-foreground are identical, making text/icons invisible on destructive surfaces.

-	--destructive: oklch(0.577 0.245 27.325);
-	--destructive-foreground: oklch(0.577 0.245 27.325);
+	--destructive: oklch(0.577 0.245 27.325);
+	--destructive-foreground: oklch(0.985 0 0);
🧹 Nitpick comments (6)
apps/ui/src/app/globals.css (6)

29-30: Add fallback for var(--font-geist-mono)

Prevents property invalidation if the custom property isn’t set.

-		var(--font-geist-mono), source-code-pro, Menlo, Monaco, Consolas,
+		var(--font-geist-mono, ui-monospace), source-code-pro, Menlo, Monaco, Consolas,

141-143: Harden animation tokens with defaults

Provide defaults for --duration to avoid invalid animation values when unset.

-	--animate-marquee: marquee var(--duration) infinite linear;
-	--animate-marquee-vertical: marquee-vertical var(--duration) linear infinite;
+	--animate-marquee: marquee var(--duration, 30s) linear infinite;
+	--animate-marquee-vertical: marquee-vertical var(--duration, 30s) linear infinite;

144-152: Default --gap in marquee to avoid invalid transforms

Ensures keyframes remain valid even if --gap isn’t set.

-			transform: translateX(calc(-100% - var(--gap)));
+			transform: translateX(calc(-100% - var(--gap, 0px)));

154-162: Default --gap for vertical marquee as well

-			transform: translateY(calc(-100% - var(--gap)));
+			transform: translateY(calc(-100% - var(--gap, 0px)));

166-168: Extend base reset to ::before/::after (and box-sizing)

Covers pseudo-elements and sets border-box universally, matching common Tailwind/shadcn resets.

-* {
-		@apply border-border outline-ring/50;
-	}
+*,
+*::before,
+*::after {
+		@apply border-border outline-ring/50;
+		box-sizing: border-box;
+	}

174-192: Respect prefers-reduced-motion for marquee/accordion animations

Offer an accessible path for users who prefer reduced motion.

Add this block (placement near animations is fine):

@media (prefers-reduced-motion: reduce) {
  .animate-accordion-down,
  .animate-accordion-up {
    animation: none !important;
  }

  /* If you expose utilities using the --animate-* tokens, noop them */
  .animate-marquee,
  .animate-marquee-vertical {
    animation: none !important;
  }
}
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a3a531f and 937e40e.

📒 Files selected for processing (1)
  • apps/ui/src/app/globals.css (5 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: test / run
  • GitHub Check: build / run
  • GitHub Check: generate / run
  • GitHub Check: lint / run

- Added missing font options to the body font-family declaration for better typography.
- Refactored layout.tsx to improve HTML structure by moving className to the <html> tag and simplifying the <body> tag.
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (1)
apps/ui/src/app/globals.css (1)

31-34: Harden the code font fallback

Line 32 uses var(--font-geist-mono) without an inline fallback; if that custom property ever goes missing (bad deploy, alternate layout, etc.) the entire font-family declaration becomes invalid and we lose the curated monospace stack. Please include a fallback inside the var() so we gracefully degrade.

-	var(--font-geist-mono), source-code-pro, Menlo, Monaco, Consolas,
+	var(--font-geist-mono, source-code-pro), Menlo, Monaco, Consolas,
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 937e40e and dfc274f.

📒 Files selected for processing (2)
  • apps/ui/src/app/globals.css (5 hunks)
  • apps/ui/src/app/layout.tsx (1 hunks)
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx}: Never use any or as any in this TypeScript project unless absolutely necessary
Always use top-level import; never use require or dynamic import()

Files:

  • apps/ui/src/app/layout.tsx
apps/ui/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

apps/ui/**/*.{ts,tsx}: In the Next.js UI, use next/link for links and next/navigation router for programmatic navigation
Use cookies for user settings not stored in the database to ensure SSR compatibility

Use cookies for user settings not stored in the database to ensure SSR works

Files:

  • apps/ui/src/app/layout.tsx
{apps,packages}/**/src/**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

Always use top-level import; never use require() or dynamic import()

Files:

  • apps/ui/src/app/layout.tsx
apps/ui/**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

Use next/link for links and next/navigation's router for programmatic navigation

Files:

  • apps/ui/src/app/layout.tsx
⏰ 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: build / run
  • GitHub Check: lint / run
  • GitHub Check: test / run
  • GitHub Check: generate / run

{
id: "api-key",
title: "API Key",
},
Copy link
Member

Choose a reason for hiding this comment

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

why was this removed?

Copy link
Contributor Author

@thebeyondr thebeyondr Sep 29, 2025

Choose a reason for hiding this comment

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

Welcome step automatically makes the key or shows how many they have. Immediate reward and reduces the steps. Its something you'd have to try to see if it makes sense to you or not.

Welcome Step

  • Auto-generates API keys for new users
  • Detects self-hosting vs hosted context
  • Shows existing API key status with copy button
  • Skip option for users with existing keys
  • Better error handling with retry

Changed "Choose your approach" to "Choose plan" in both the onboarding wizard and plan choice step for consistency and clarity.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants