Skip to content

Restore app shell and add ltx-2 support#50

Merged
Simplereally merged 3 commits intomainfrom
codex/remote-main-20260412-005248
Apr 11, 2026
Merged

Restore app shell and add ltx-2 support#50
Simplereally merged 3 commits intomainfrom
codex/remote-main-20260412-005248

Conversation

@Simplereally
Copy link
Copy Markdown
Owner

@Simplereally Simplereally commented Apr 11, 2026

Summary

  • remove the temporary maintenance-mode shell and restore the full app layout/landing page routes
  • replace dirtberry model support with LTX-2 video model metadata, constraints, pricing, schemas, and SEO/test coverage
  • keep Pollinations callback save flow gated on Convex auth readiness

Verification

  • bun run lint
  • bun run test
  • bun run build

Note: build passes; Clerk emits its expected warning about a test publishable key being present in the production build environment.

Summary by Sourcery

Restore the full authenticated app shell and landing page while adding first-class support for the LTX-2 video model across config, pricing, schemas, SEO, and tests.

New Features:

  • Reintroduce the full marketing landing page with hero, feature, model, and CTA sections atop the standard app shell.
  • Add LTX-2 as a supported video generation model with defined constraints, pricing, and SEO metadata.

Bug Fixes:

  • Ensure the Pollinations OAuth callback only attempts to save API keys once Convex auth is initialized and the user is authenticated.

Enhancements:

  • Include LTX-2 in video model registries, active model lists, and studio model selection, and adjust related tests accordingly.
  • Refine Pollinations video URL construction so interpolation parameters are applied only to supported models.
  • Update upgrade modal copy to use the dynamic active model count rather than a hard-coded value.

Build:

  • Add development scripts for running Convex locally and in cloud mode.

Tests:

  • Expand unit tests for model registry, pricing, Pollinations URL building, model SEO slugs, Pollinations schemas, and hooks to cover LTX-2 and the new behaviors.
  • Adjust Pollinations callback tests to account for Convex auth gating of the save flow.

Summary by CodeRabbit

  • New Features

    • Added ltx-2 video model (1–10s, $0.005/s) and updated landing page with interactive sections + WebGL background
  • Improvements

    • Pollinations callback now requires user to click “Finish Connection”, clears URL hash immediately, and displays “Invalid API Key” when applicable
    • Video controls: reference-frame picker hidden when model doesn’t support reference images; generation no longer sends unsupported reference inputs
  • Chores

    • Removed public maintenance-mode branch
    • Added Convex development scripts

@vercel
Copy link
Copy Markdown
Contributor

vercel Bot commented Apr 11, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
bloomstudio Ignored Ignored Preview Apr 11, 2026 3:42pm

@sourcery-ai
Copy link
Copy Markdown

sourcery-ai Bot commented Apr 11, 2026

Reviewer's Guide

Restores the full app shell and landing page experience, introduces first-class support for the LTX-2 video model across configuration, pricing, schemas, SEO, and tests, and hardens the Pollinations OAuth callback to depend on Convex auth readiness while cleaning up temporary maintenance-mode plumbing and adding Convex dev helpers.

Sequence diagram for Pollinations OAuth callback with Convex auth gating

sequenceDiagram
    actor User
    participant Browser
    participant PollinationsCallbackPage
    participant ConvexAuth
    participant ConvexMutation
    participant ConvexBackend

    User->>Browser: Navigate to /auth/pollinations/callback
    Browser->>PollinationsCallbackPage: Render React component
    PollinationsCallbackPage->>ConvexAuth: useConvexAuth()
    ConvexAuth-->>PollinationsCallbackPage: isAuthLoading, isAuthenticated

    PollinationsCallbackPage->>PollinationsCallbackPage: useEffect watches isAuthLoading, isAuthenticated, processCallback
    PollinationsCallbackPage->>PollinationsCallbackPage: if isAuthLoading is true, return early
    PollinationsCallbackPage->>PollinationsCallbackPage: setTimeout 100ms
    PollinationsCallbackPage->>PollinationsCallbackPage: after 100ms, check isAuthenticated
    alt User not authenticated
        PollinationsCallbackPage->>PollinationsCallbackPage: setState error_save_failed
    else User authenticated
        PollinationsCallbackPage->>ConvexMutation: setPollinationsApiKey(extractedKey)
        ConvexMutation->>ConvexBackend: save Pollinations API key
        ConvexBackend-->>ConvexMutation: success
        ConvexMutation-->>PollinationsCallbackPage: success
        PollinationsCallbackPage->>PollinationsCallbackPage: setState success
    end

    PollinationsCallbackPage->>Browser: Render success or error UI
    Browser-->>User: Show result and redirect countdown
Loading

Updated class diagram for LTX-2 model configuration and pricing

classDiagram
    class ModelDefinition {
        +string id
        +string displayName
        +string type
        +string icon
        +string description
        +Constraints constraints
        +VideoDurationConstraints durationConstraints
        +string[] aspectRatios
        +boolean supportsNegativePrompt
        +boolean supportsReferenceImage
        +ModelPricingDefinition modelPricing
    }

    class Constraints {
        +number maxPixels
        +number minPixels
        +number minDimension
        +number maxDimension
        +number step
        +Dimensions defaultDimensions
        +boolean dimensionsEnabled
        +boolean supportsSeed
        +string[] supportedTiers
        +string outputCertainty
        +string dimensionWarning
    }

    class Dimensions {
        +number width
        +number height
    }

    class VideoDurationConstraints {
        +number min
        +number max
        +number defaultDuration
    }

    class ModelPricingDefinition {
        +string modelId
        +string type
        +number approximatePerPollen
        +boolean supportsReferenceImage
        +boolean isAlpha
        +VideoPricing videoPricing
    }

    class VideoPricing {
        +number perSecond
    }

    class MODEL_REGISTRY {
        +Record~string, ModelDefinition~ entries
    }

    class VIDEO_MODEL_PRICING {
        +Record~string, ModelPricingDefinition~ entries
    }

    class VideoModelSchema {
        +veo
        +seedance
        +seedance_pro
        +wan
        +ltx_2
        +grok_video
    }

    class VideoGenerationParamsSchema {
        +string prompt
        +string model
        +number duration
        +string aspectRatio
        +boolean audio
        +string lastFrameImage
    }

    class PollinationsUrlParams {
        +string prompt
        +string model
        +string image
        +string lastFrameImage
        +number duration
        +string aspectRatio
        +boolean audio
    }

    class MODEL_SEO_SLUGS {
        +ModelSlugEntry[] entries
    }

    class ModelSlugEntry {
        +string modelId
        +string slug
        +string displayName
        +string type
        +string[] categories
    }

    MODEL_REGISTRY --> ModelDefinition : contains
    ModelDefinition --> Constraints
    Constraints --> Dimensions
    ModelDefinition --> VideoDurationConstraints
    ModelDefinition --> ModelPricingDefinition

    VIDEO_MODEL_PRICING --> ModelPricingDefinition : contains
    ModelPricingDefinition --> VideoPricing

    VideoGenerationParamsSchema --> VideoModelSchema : uses
    PollinationsUrlParams --> VideoModelSchema : uses

    MODEL_SEO_SLUGS --> ModelSlugEntry : contains

    class LTX2ModelDefinition {
        +id = ltx-2
        +displayName = LTX-2
        +type = video
        +icon = video
        +description
        +constraints
        +durationConstraints
        +aspectRatios
        +supportsNegativePrompt = false
        +supportsReferenceImage = false
        +modelPricing
    }

    class LTX2Constraints {
        +maxPixels = 1048576
        +minPixels = 65536
        +minDimension = 256
        +maxDimension = 1024
        +step = 32
        +defaultDimensions = 1024x576
        +dimensionsEnabled = false
        +supportsSeed = false
        +supportedTiers = sd
        +outputCertainty = exact
        +dimensionWarning
    }

    class LTX2DurationConstraints {
        +min = 1
        +max = 10
        +defaultDuration = 5
    }

    class LTX2PricingDefinition {
        +modelId = ltx-2
        +type = video
        +approximatePerPollen = 40
        +supportsReferenceImage = false
        +isAlpha = true
        +videoPricing
    }

    class LTX2VideoPricing {
        +perSecond = 0.005
    }

    MODEL_REGISTRY --> LTX2ModelDefinition : entry ltx-2
    LTX2ModelDefinition --> LTX2Constraints
    LTX2ModelDefinition --> LTX2DurationConstraints
    LTX2ModelDefinition --> LTX2PricingDefinition
    LTX2PricingDefinition --> LTX2VideoPricing
    VIDEO_MODEL_PRICING --> LTX2PricingDefinition : entry ltx-2

    class LTX2SeoEntry {
        +modelId = ltx-2
        +slug = ltx-2
        +displayName = LTX-2
        +type = video
        +categories = create,features
    }

    MODEL_SEO_SLUGS --> LTX2SeoEntry : includes ltx-2
Loading

Flow diagram for Pollinations URL building with LTX-2 and video models

flowchart TD
    A[Start buildPollinationsUrl] --> B[Receive PollinationsUrlParams]
    B --> C{Has model and model is in VIDEO_MODELS}
    C -->|No| Z[Build image URL params]
    C -->|Yes| D[Video model branch]

    D --> E{Model is veo and has image and lastFrameImage}
    E -->|Yes| F[Append image param with image and lastFrameImage joined by pipe]
    E -->|No| G{Has image}
    G -->|Yes| H[Append image param with image only]
    G -->|No| I[Skip image param]

    F --> J
    H --> J
    I --> J

    J{Has duration and duration > 0} -->|Yes| K[Append duration param]
    J -->|No| L[Skip duration param]

    K --> M
    L --> M

    M{Has aspectRatio} -->|Yes| N[Append aspect_ratio param]
    M -->|No| O[Skip aspect_ratio]

    N --> P[Handle other common params]
    O --> P

    P --> Q[Construct final URL]
    Z --> Q
    Q --> R[Return URL string]
Loading

File-Level Changes

Change Details Files
Restore full landing page and always wrap app in Clerk/Convex shell
  • Replace temporary maintenance-only landing page with the full multi-section marketing layout wired through server-rendered content plus client components for header and WebGL background
  • Remove the request-header–based maintenance shell toggle from the root layout and always render children inside the Clerk/Convex/Pollen app shell with header, toasts, and analytics
  • Delete the edge middleware header injection for the maintenance shell on the root route
app/page.tsx
app/layout.tsx
proxy.ts
Add LTX-2 as a first-class video model with constraints, pricing, and SEO coverage
  • Register ltx-2 in the model registry with video-specific constraints (1MP, fixed dimensions, duration 1–10s), no seed or reference image support, and link it to shared video aspect ratios
  • Extend model tests to assert ltx-2 presence in MODEL_REGISTRY, ACTIVE_VIDEO_MODEL_IDS, and model IDs/SEO slug lists, plus validate its constraints, pricing, and aspect ratios
  • Add ltx-2 to video-related API constraints, Pollinations video model enums/schemas, and ensure cost-estimation utilities treat it as an alpha video model without reference-image support
  • Update Pollinations URL builder and tests so only Veo encodes interpolation start/end frames while ltx-2 ignores lastFrameImage and only sends a single image parameter
lib/config/models.ts
lib/config/models.test.ts
lib/config/api.config.ts
lib/models/model-seo-slugs.ts
lib/models/model-seo-slugs.test.ts
lib/schemas/pollinations.schema.ts
lib/schemas/pollinations.schema.test.ts
lib/schemas/pollinations-pricing.schema.ts
lib/schemas/pollinations-pricing.schema.test.ts
convex/lib/pollinations.ts
convex/lib/pollinations.test.ts
hooks/queries/use-image-models.test.ts
Gate Pollinations callback key-saving flow on Convex auth readiness
  • Use useConvexAuth in the Pollinations callback page to read auth loading and authentication state before invoking the Convex mutation to save the API key
  • Change the callback processing effect to wait until Convex auth is ready, bail out with an error state when the user is not authenticated, and still retain the 100ms timeout for hash parsing quirks
  • Adjust the callback test suite to mock useConvexAuth so tests assume an authenticated, ready Convex client
app/auth/pollinations/callback/page.tsx
app/auth/pollinations/callback/page.test.tsx
Minor tooling and UX test adjustments
  • Add dev scripts for running Convex locally and in cloud mode via bunx convex dev
  • Update the studio upgrade modal test to expect the dynamic ACTIVE_MODEL_COUNT instead of a hard-coded model count, aligning with the new ltx-2 model
  • Tidy minor comments and docstrings around video model params (audio and interpolation) to reflect that they are only supported on some models
package.json
components/studio/upgrade-modal.test.tsx
lib/schemas/pollinations.schema.ts

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 11, 2026

Warning

Rate limit exceeded

@Simplereally has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 3 minutes and 17 seconds before requesting another review.

Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 3 minutes and 17 seconds.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: a4836394-67a7-47c2-b492-4383c5b78170

📥 Commits

Reviewing files that changed from the base of the PR and between 92fa3cb and 0670b04.

📒 Files selected for processing (2)
  • convex/batchGeneration.ts
  • convex/singleGeneration.ts
📝 Walkthrough

Walkthrough

Callback handling now separates client-side hash parsing/clearing from a user-confirmed server action that persists the Pollinations API key; layout and middleware conditional rendering removed; landing page refactored into composed sections; a new ltx-2 video model is added across registry, pricing, schemas, and URL logic; assorted tests and Convex dev scripts added.

Changes

Cohort / File(s) Summary
Pollinations callback UI + action
app/auth/pollinations/callback/page.tsx, app/auth/pollinations/callback/page.test.tsx, app/auth/pollinations/callback/actions.ts, app/auth/pollinations/callback/actions.test.ts
Split callback into client-side hash parsing/clear and a form-driven server action savePollinationsApiKey; tests updated to mock the server action, assert URL-hash clearing timing, and verify server action invocation and error states.
Root layout & middleware
app/layout.tsx, proxy.ts
Removed async header inspection and public-maintenance branch from RootLayout; always render providers. Deleted special-case root-path header mutation in middleware.
Landing page refactor
app/page.tsx
Replaced hardcoded maintenance UI with composed landing sections and client components (GLBackground, LandingHeader, multiple server-rendered sections).
ltx-2 model addition
lib/config/models.ts, lib/config/api.config.ts, lib/schemas/pollinations.schema.ts, lib/schemas/pollinations-pricing.schema.ts, lib/models/model-seo-slugs.ts, lib/models/model-seo-slugs.test.ts, lib/config/models.test.ts, lib/schemas/pollinations-pricing.schema.test.ts, lib/schemas/pollinations.schema.test.ts
Added ltx-2 to MODEL_REGISTRY, API constraints, VideoModelSchema, pricing registry, SEO metadata, and tests (duration, pricing, features, aspect ratios, slug inclusion).
Pollinations URL / video model logic
convex/lib/pollinations.ts, convex/lib/pollinations.test.ts
Expanded VIDEO_MODELS to include ltx-2, introduced VIDEO_MODELS_WITH_REFERENCE_IMAGE, and adjusted query-parameter emission: combined `image
Generation settings & reference-frame flow
hooks/use-generation-settings.ts, hooks/use-generation-settings.test.ts, hooks/queries/use-image-models.test.ts, components/studio/features/generation/controls-feature.tsx, components/studio/features/generation/controls-view.tsx, components/studio/features/generation/controls-view.test.tsx, components/studio/layout/studio-shell.tsx, components/studio/layout/studio-shell.test.tsx
Made max reference-frames capability-aware and video-type gated; prevented UI frame picker when maxReferenceFrames === 0; generation param construction now conditionally includes image/lastFrameImage only if model supports them; tests updated to cover ltx-2 behavior and UI gating.
Misc tests & small updates
components/studio/upgrade-modal.test.tsx, app/auth/pollinations/callback/page.test.tsx (additional), other test files updated
Minor test assertion updates to use constants or assert new behaviors (e.g., preserved query string, server action invocation).
Convex dev scripts
package.json
Added dev:convex, dev:convex:once, and dev:convex:cloud npm scripts for local/cloud Convex development.
Other
proxy.ts (middleware removed behavior)
Removed NextResponse import and root-path header injection logic; middleware now only performs auth protection checks.

Sequence Diagram(s)

sequenceDiagram
  participant Browser as Browser (client)
  participant UI as Callback UI
  participant Action as Server Action (savePollinationsApiKey)
  participant Auth as Clerk/Convex Auth
  participant Convex as Convex Mutation

  Browser->>UI: mount with URL hash (`#api_key`=...)
  UI->>UI: parse hash, validate format
  UI->>Browser: clear URL hash (replaceState)
  UI-->>Browser: show "Finish Connection" (ready)
  Browser->>UI: user clicks "Finish Connection"
  UI->>Action: call savePollinationsApiKey(apiKey)
  Action->>Auth: getConvexClerkToken()
  Auth-->>Action: token (or null)
  alt token available
    Action->>Convex: fetchMutation(api.users.setPollinationsApiKey, {apiKey}, {token})
    Convex-->>Action: mutation result
    Action-->>UI: { status: "success" } or error
  else no token / error
    Action-->>UI: { status: "error_save_failed" } or validation error
  end
  UI->>Browser: render success or error UI
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~40 minutes

Possibly related PRs

Poem

🐰 I hopped in with a tiny key in paw,

Cleared the hash and waited—patient, in awe,
A button to finish, a server to call,
Models grew a sibling, ltx-2 stands tall,
Hooray! I nibble bugs and leap through the hall.

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely summarizes the two main changes: restoring the app shell (removing maintenance mode) and adding LTX-2 video model support, which aligns with the substantial refactoring across layout, landing page, model registries, and schemas.
Docstring Coverage ✅ Passed Docstring coverage is 85.71% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/remote-main-20260412-005248

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.

@qodo-code-review
Copy link
Copy Markdown

Review Summary by Qodo

Restore app shell and add LTX-2 video model support

✨ Enhancement 🧪 Tests

Grey Divider

Walkthroughs

Description
• Add LTX-2 video model support with full metadata and constraints
• Restore full app shell and landing page, removing maintenance mode
• Update Pollinations callback to wait for Convex auth readiness
• Refine video model reference image handling for interpolation-only models
Diagram
flowchart LR
  A["LTX-2 Model Definition"] -->|"metadata, constraints, pricing"| B["Model Registry"]
  B -->|"per-second pricing"| C["Video Pricing Schema"]
  B -->|"1MP, 1-10s duration"| D["API Constraints"]
  E["Pollinations URL Builder"] -->|"reference image logic"| F["Video Model Handling"]
  F -->|"interpolation only for Veo"| G["URL Parameters"]
  H["App Layout"] -->|"remove maintenance mode"| I["Full App Shell"]
  I -->|"restore providers"| J["Landing Page + Studio"]
  K["Pollinations Callback"] -->|"wait for auth"| L["Convex Auth Ready"]
  L -->|"then save API key"| M["User Profile"]
Loading

Grey Divider

File Changes

1. lib/config/models.ts ✨ Enhancement +40/-0

Add LTX-2 model definition with constraints

lib/config/models.ts


2. lib/config/models.test.ts 🧪 Tests +50/-2

Add LTX-2 model registry and property tests

lib/config/models.test.ts


3. lib/schemas/pollinations-pricing.schema.ts ✨ Enhancement +16/-0

Add LTX-2 video pricing at $0.005/s

lib/schemas/pollinations-pricing.schema.ts


View more (16)
4. lib/schemas/pollinations-pricing.schema.test.ts 🧪 Tests +10/-0

Test LTX-2 cost calculation and reference image support

lib/schemas/pollinations-pricing.schema.test.ts


5. lib/schemas/pollinations.schema.ts ✨ Enhancement +3/-3

Add LTX-2 to VideoModelSchema enum

lib/schemas/pollinations.schema.ts


6. lib/schemas/pollinations.schema.test.ts 🧪 Tests +1/-0

Test LTX-2 video model schema validation

lib/schemas/pollinations.schema.test.ts


7. lib/models/model-seo-slugs.ts ✨ Enhancement +7/-0

Add LTX-2 SEO slug and metadata entry

lib/models/model-seo-slugs.ts


8. lib/models/model-seo-slugs.test.ts 🧪 Tests +4/-3

Update video model count and test LTX-2 slug

lib/models/model-seo-slugs.test.ts


9. convex/lib/pollinations.ts ✨ Enhancement +7/-16

Refine video model reference image handling logic

convex/lib/pollinations.ts


10. convex/lib/pollinations.test.ts 🧪 Tests +15/-0

Test LTX-2 reference image parameter handling

convex/lib/pollinations.test.ts


11. hooks/queries/use-image-models.test.ts 🧪 Tests +1/-0

Verify LTX-2 in active video models list

hooks/queries/use-image-models.test.ts


12. app/layout.tsx ✨ Enhancement +13/-27

Remove maintenance mode shell and restore full app layout

app/layout.tsx


13. app/page.tsx ✨ Enhancement +44/-63

Restore full landing page with hero and features sections

app/page.tsx


14. proxy.ts ✨ Enhancement +1/-13

Remove maintenance mode header injection logic

proxy.ts


15. app/auth/pollinations/callback/page.tsx ✨ Enhancement +16/-12

Gate callback on Convex auth readiness before mutation

app/auth/pollinations/callback/page.tsx


16. app/auth/pollinations/callback/page.test.tsx 🧪 Tests +1/-0

Mock useConvexAuth hook for callback page tests

app/auth/pollinations/callback/page.test.tsx


17. components/studio/upgrade-modal.test.tsx 🧪 Tests +2/-1

Use ACTIVE_MODEL_COUNT constant in upgrade modal test

components/studio/upgrade-modal.test.tsx


18. package.json ⚙️ Configuration changes +3/-0

Add Convex dev scripts for local and cloud modes

package.json


19. lib/config/api.config.ts Additional files +1/-0

...

lib/config/api.config.ts


Grey Divider

Qodo Logo

@qodo-code-review
Copy link
Copy Markdown

qodo-code-review Bot commented Apr 11, 2026

Code Review by Qodo

🐞 Bugs (1)   📘 Rule violations (1)   📎 Requirement gaps (0)   🎨 UX Issues (0)
🐞\ ≡ Correctness (1)
📘\ ⚙ Maintainability (1)

Grey Divider


Action required

1. processCallback() called in useEffect📘
Description
useEffect triggers the setApiKey Convex mutation via processCallback(), which is application
data I/O rather than a browser-only side effect. This violates the constraint that useEffect must
not be used for application data fetching/mutations.
Code

app/auth/pollinations/callback/page.tsx[R180-192]

useEffect(() => {
+    if (isAuthLoading) return;
+
 const timer = setTimeout(() => {
+      if (!isAuthenticated) {
+        setState("error_save_failed");
+        return;
+      }
+
   void processCallback();
 }, 100);
 return () => clearTimeout(timer);
-  }, [processCallback]);
+  }, [isAuthLoading, isAuthenticated, processCallback]);
Evidence
PR Compliance ID 76493 disallows using useEffect for application data fetching/I/O; the effect
here calls processCallback(), which performs the Convex mutation setApiKey(...) as part of the
mount flow.

Rule 76493: Restrict React useEffect to browser-only side effects (no application data fetching)
app/auth/pollinations/callback/page.tsx[180-192]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`useEffect` is being used to initiate application I/O (a Convex mutation) by calling `processCallback()` on mount.
## Issue Context
The compliance rule requires `useEffect` be limited to browser-only side effects (DOM/browser APIs) and not used to kick off application data fetching/mutations.
## Fix Focus Areas
- app/auth/pollinations/callback/page.tsx[180-192]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. VIDEO_MODELS uses as const 📘
Description
The updated VIDEO_MODELS definition uses a TypeScript as const assertion on a changed line,
which is disallowed by the TypeScript assertion compliance rule. This reduces type-safety guarantees
by allowing assertion-based typing rather than narrowing/validation.
Code

convex/lib/pollinations.ts[R17-18]

+/** Video model IDs - these are the only models that accept video-specific query params */
+const VIDEO_MODELS = ["veo", "seedance", "seedance-pro", "wan", "ltx-2", "grok-video"] as const
Evidence
PR Compliance ID 76498 forbids TypeScript as assertions in changed code; the modified
VIDEO_MODELS declaration includes an as const assertion.

Rule 76498: Avoid as type assertions in TypeScript
convex/lib/pollinations.ts[17-18]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
A changed line uses `as const` (`VIDEO_MODELS = [...] as const`), which violates the rule to avoid `as` type assertions.
## Issue Context
This constant can be typed without assertions (e.g., as a `readonly string[]`) since it is only used for membership checks.
## Fix Focus Areas
- convex/lib/pollinations.ts[17-18]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. OAuth key hash persists🐞
Description
PollinationsCallbackPage skips processCallback() when Convex auth is unauthenticated, so the URL
hash fragment containing the API key is never cleared. This can leave the API key in the address bar
(and in copied URLs/screenshots) even though the save flow is blocked.
Code

app/auth/pollinations/callback/page.tsx[R181-187]

+    if (isAuthLoading) return;
+
 const timer = setTimeout(() => {
+      if (!isAuthenticated) {
+        setState("error_save_failed");
+        return;
+      }
Evidence
The hash-clearing logic (window.history.replaceState) only runs inside processCallback(), but the
new auth gating returns early on !isAuthenticated and never calls processCallback(), leaving the
hash untouched in that branch.

app/auth/pollinations/callback/page.tsx[151-159]
app/auth/pollinations/callback/page.tsx[174-192]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Pollinations callback can leave `#api_key=...` in `window.location.hash` when `!isAuthenticated`, because the code returns before calling `processCallback()` where the hash is cleared.
## Issue Context
This is a credential-leak risk (copy/paste, screenshots, bug reports). Even if we cannot save without auth, we should still remove the hash ASAP.
## Fix Focus Areas
- app/auth/pollinations/callback/page.tsx[151-159]
- app/auth/pollinations/callback/page.tsx[174-192]
## Expected fix
- Extract+clear the hash fragment as soon as possible (e.g., in the effect before checking `isAuthenticated`, or always call a small `clearHash()` helper even on error paths).
- Optionally: if unauthenticated, redirect to sign-in with a safe returnTo and show an explicit auth-required error state (instead of "internet connection").

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


View more (2)
4. LTX-2 ratio/constraint mismatch 🐞
Description
The ltx-2 model sets maxDimension=1024/maxPixels=1,048,576 but uses VIDEO_ASPECT_RATIOS (HD
1920×1080 / 1080×1920). Because the settings hook treats single-tier fixed-size models as exact,
ltx-2 will be initialized with out-of-constraint dimensions that users cannot adjust
(dimensionsEnabled=false), likely breaking generation or forcing upstream clamping.
Code

lib/config/models.ts[R883-898]

+    constraints: {
+      maxPixels: 1_048_576,
+      minPixels: 65_536,
+      minDimension: 256,
+      maxDimension: 1024,
+      step: 32,
+      defaultDimensions: { width: 1024, height: 576 },
+      dimensionsEnabled: false,
+      supportsSeed: false,
+      supportedTiers: ["sd"],
+      outputCertainty: "exact",
+      dimensionWarning: "Uses fixed 16:9 or 9:16 frames rounded to multiples of 32",
+    },
+    aspectRatios: VIDEO_ASPECT_RATIOS,
+    supportsNegativePrompt: false,
+    supportsReferenceImage: false,
Evidence
ltx-2 declares a 1024px max dimension and 1MP cap but references VIDEO_ASPECT_RATIOS.
VIDEO_ASPECT_RATIOS are explicitly HD and resolve to 1920×1080 / 1080×1920.
useGenerationSettings.handleModelChange sets width/height directly from ratios[0] for fixed-size
models when supportedTiers length is 1 (ltx-2 uses ["sd"]).

lib/config/models.ts[867-905]
lib/config/models.ts[266-272]
lib/config/standard-resolutions.ts[104-110]
hooks/use-generation-settings.ts[328-337]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The new `ltx-2` model uses `VIDEO_ASPECT_RATIOS` (HD dimensions), but its constraints cap dimensions to 1024 and pixels to 1MP. The generation settings hook will pick the first ratio's width/height for single-tier fixed models, resulting in invalid/unadjustable dimensions.
## Issue Context
`dimensionsEnabled: false` prevents the user from correcting dimensions. This can lead to provider errors or silent clamping and incorrect UX.
## Fix Focus Areas
- lib/config/models.ts[867-905]
- lib/config/models.ts[266-272]
- hooks/use-generation-settings.ts[328-337]
## Expected fix
- Define a dedicated `LTX2_ASPECT_RATIOS` with exact sizes that satisfy constraints (e.g., 1024×576 and 576×1024, step 32) and use it for `ltx-2`.
- Alternatively/additionally, change the fixed-size-model branch in `use-generation-settings` to prefer `newConstraints.defaultDimensions` (or clamp ratio dims to constraints) instead of blindly using `ratios[0].width/height`.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


5. LTX-2 still sends image🐞
Description
Although ltx-2 is documented as text-only and sets supportsReferenceImage=false, the Studio
generation payload still maps videoReferenceImages[0] into the image parameter and
buildPollinationsUrl will append image= for ltx-2. This can cause failed generations if the
provider rejects reference-image inputs for ltx-2.
Code

convex/lib/pollinations.ts[R105-112]

 const isVideoModel = params.model && VIDEO_MODELS.includes(params.model as typeof VIDEO_MODELS[number])
 if (isVideoModel) {
-        // Reference image(s): handle different formats for different video models
-        if (params.model === "grok-video") {
-            if (params.image) {
-                queryParams.append("image", params.image)
-            }
-        } else {
-            // Other video models: for models that support interpolation (two reference images),
-            // join both URLs with "|" in a single `image` param so the Pollinations gateway
-            // splits them into the upstream `image_urls` array.
-            if (params.image && params.lastFrameImage) {
-                queryParams.append("image", `${params.image}|${params.lastFrameImage}`)
-            } else if (params.image) {
-                queryParams.append("image", params.image)
-            }
+        // Reference image(s): only Veo currently supports interpolation with start+end frames.
+        if (params.model === "veo" && params.image && params.lastFrameImage) {
+            queryParams.append("image", `${params.image}|${params.lastFrameImage}`)
+        } else if (params.image) {
+            queryParams.append("image", params.image)
     }
Evidence
The ltx-2 model is explicitly described as having no reference image support and sets
supportsReferenceImage=false, but the Studio UI/control plumbing does not gate video reference
frames on supportsReferenceImage and the generation request builder always includes image for any
video model when provided. Since ltx-2 is included in VIDEO_MODELS, buildPollinationsUrl will emit
image= for it.

lib/config/models.ts[867-905]
components/studio/features/generation/controls-feature.tsx[167-180]
components/studio/layout/studio-shell.tsx[340-349]
convex/lib/pollinations.ts[17-18]
convex/lib/pollinations.ts[104-112]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`ltx-2` is configured as text-only (`supportsReferenceImage: false`), but the Studio can still send `image` for it (via `videoReferenceImages[0]`), and `buildPollinationsUrl` appends `image=` for all video models.
## Issue Context
This is likely to cause provider-side validation errors or unexpected behavior for ltx-2.
## Fix Focus Areas
- lib/config/models.ts[867-905]
- components/studio/features/generation/controls-feature.tsx[167-180]
- components/studio/layout/studio-shell.tsx[340-349]
- convex/lib/pollinations.ts[17-18]
- convex/lib/pollinations.ts[104-112]
## Expected fix (defense-in-depth)
- UI: hide/disable the Video Frames section for models where `supportsReferenceImage === false` (or set `referenceFrameCount: 0` and also avoid rendering when maxFrames===0).
- Request building: in `studio-shell`, only include `image` for video models that support reference images.
- URL building: in `buildPollinationsUrl`, do not append `image` for `model === "ltx-2"` even if `params.image` is present (protect against stale client state / API callers).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

ⓘ The new review experience is currently in Beta. Learn more

Grey Divider

Qodo Logo

Copy link
Copy Markdown

@sourcery-ai sourcery-ai Bot left a comment

Choose a reason for hiding this comment

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

Hey - I've reviewed your changes and they look great!


Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread app/auth/pollinations/callback/page.tsx Outdated
Comment on lines +17 to +18
/** Video model IDs - these are the only models that accept video-specific query params */
const VIDEO_MODELS = ["veo", "seedance", "seedance-pro", "wan", "ltx-2", "grok-video"] as const
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Action required

2. video_models uses as const 📘 Rule violation ⚙ Maintainability

The updated VIDEO_MODELS definition uses a TypeScript as const assertion on a changed line,
which is disallowed by the TypeScript assertion compliance rule. This reduces type-safety guarantees
by allowing assertion-based typing rather than narrowing/validation.
Agent Prompt
## Issue description
A changed line uses `as const` (`VIDEO_MODELS = [...] as const`), which violates the rule to avoid `as` type assertions.

## Issue Context
This constant can be typed without assertions (e.g., as a `readonly string[]`) since it is only used for membership checks.

## Fix Focus Areas
- convex/lib/pollinations.ts[17-18]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment thread app/auth/pollinations/callback/page.tsx Outdated
Comment thread lib/config/models.ts
Comment on lines +883 to +898
constraints: {
maxPixels: 1_048_576,
minPixels: 65_536,
minDimension: 256,
maxDimension: 1024,
step: 32,
defaultDimensions: { width: 1024, height: 576 },
dimensionsEnabled: false,
supportsSeed: false,
supportedTiers: ["sd"],
outputCertainty: "exact",
dimensionWarning: "Uses fixed 16:9 or 9:16 frames rounded to multiples of 32",
},
aspectRatios: VIDEO_ASPECT_RATIOS,
supportsNegativePrompt: false,
supportsReferenceImage: false,
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Action required

4. Ltx-2 ratio/constraint mismatch 🐞 Bug ≡ Correctness

The ltx-2 model sets maxDimension=1024/maxPixels=1,048,576 but uses VIDEO_ASPECT_RATIOS (HD
1920×1080 / 1080×1920). Because the settings hook treats single-tier fixed-size models as exact,
ltx-2 will be initialized with out-of-constraint dimensions that users cannot adjust
(dimensionsEnabled=false), likely breaking generation or forcing upstream clamping.
Agent Prompt
## Issue description
The new `ltx-2` model uses `VIDEO_ASPECT_RATIOS` (HD dimensions), but its constraints cap dimensions to 1024 and pixels to 1MP. The generation settings hook will pick the first ratio's width/height for single-tier fixed models, resulting in invalid/unadjustable dimensions.

## Issue Context
`dimensionsEnabled: false` prevents the user from correcting dimensions. This can lead to provider errors or silent clamping and incorrect UX.

## Fix Focus Areas
- lib/config/models.ts[867-905]
- lib/config/models.ts[266-272]
- hooks/use-generation-settings.ts[328-337]

## Expected fix
- Define a dedicated `LTX2_ASPECT_RATIOS` with exact sizes that satisfy constraints (e.g., 1024×576 and 576×1024, step 32) and use it for `ltx-2`.
- Alternatively/additionally, change the fixed-size-model branch in `use-generation-settings` to prefer `newConstraints.defaultDimensions` (or clamp ratio dims to constraints) instead of blindly using `ratios[0].width/height`.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment thread convex/lib/pollinations.ts
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ebd56c6cfe

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread app/auth/pollinations/callback/page.tsx Outdated
Comment on lines +184 to +186
if (!isAuthenticated) {
setState("error_save_failed");
return;
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Clear OAuth hash before unauthenticated early return

When isAuthenticated is false, this branch sets error_save_failed and returns before processCallback() runs, but processCallback() is the only place that removes #api_key=... from the URL (app/auth/pollinations/callback/page.tsx:151-159). If a user’s Clerk/Convex auth is not ready after redirect (or their session expired), the API key remains in the address bar and can be leaked via copy/paste, browser history, or screenshots; the hash should be cleared even on this error path.

Useful? React with 👍 / 👎.

Comment thread lib/config/models.ts
},
aspectRatios: VIDEO_ASPECT_RATIOS,
supportsNegativePrompt: false,
supportsReferenceImage: false,
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Gate ltx-2 from reference-frame request fields

This commit introduces ltx-2 as supportsReferenceImage: false, but the generation flow still treats all video models as frame-capable: the frames UI is shown for any isVideoModel (components/studio/features/generation/controls-view.tsx:359-363) and request assembly always forwards videoReferenceImages[0]/[1] for video models (components/studio/layout/studio-shell.tsx:342-349 and 374-395). As a result, users can send stale or newly selected frame images with ltx-2, which contradicts its text-only contract and can cause failed generations.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🧹 Nitpick comments (1)
lib/models/model-seo-slugs.test.ts (1)

27-30: Consider deriving expected video count instead of hardcoding 2.

This test will churn every time active video models change; deriving expected count from the active registry/filter would make it more resilient.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lib/models/model-seo-slugs.test.ts` around lines 27 - 30, Replace the
hardcoded expected value (2) with a derived count from the authoritative
active-model source: compute expectedVideoCount by querying the active
registry/collection (e.g., MODEL_REGISTRY.getActiveModels() or the source list
used to build MODEL_SEO_SLUGS) and filter for type === "video", then assert
expect(videoEntries).toHaveLength(expectedVideoCount); reference MODEL_SEO_SLUGS
and the registry method/collection you use to derive the expected count.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@app/auth/pollinations/callback/page.test.tsx`:
- Around line 39-42: The current test hardcodes useConvexAuth to {
isAuthenticated: true, isLoading: false } so it never exercises the new
loading/unauthenticated gate; change the mock exported from
vi.mock("convex/react") to allow configuring the return value (e.g., expose a
configurable get/set or a mock implementation that reads a variable) instead of
always returning the same object, and update tests that use mockSetApiKey to add
two new cases: one where useConvexAuth returns { isLoading: true } to assert the
component remains in processing and does not call useMutation/mockSetApiKey, and
one where it returns { isAuthenticated: false, isLoading: false } to assert the
failure UI is shown and the callback-hash stripping still occurs.

In `@app/auth/pollinations/callback/page.tsx`:
- Around line 174-192: The effect that waits on isAuthLoading may leave the
Pollinations API key in window.location.hash during auth startup or when
unauthenticated; before returning or bailing out in the useEffect (the callback
that references isAuthLoading, isAuthenticated, and processCallback), clear the
hash so the secret isn't leaked. Add a line at the top of that useEffect to
remove the fragment (e.g., history.replaceState(null, '',
window.location.pathname + window.location.search) or window.location.hash = '')
and also clear it right before the early return when !isAuthenticated, ensuring
processCallback (which already strips the hash) still functions as expected.

In `@app/layout.tsx`:
- Around line 152-158: The root layout currently always renders Header, causing
duplicate navbars because app/page.tsx mounts LandingHeader; update the layout
to avoid rendering Header for landing routes by either moving Header out of
app/layout.tsx into a route-specific layout (e.g., an "app" sublayout) or add a
route check that suppresses Header for the landing path. Locate the Header usage
in app/layout.tsx and either relocate it to the appropriate sublayout (so
LandingHeader remains in app/page.tsx) or wrap the Header render in a
pathname/route conditional that excludes the root (“/”) and any marketing
routes; ensure LandingHeader (referenced in app/page.tsx) remains unaffected.

In `@convex/lib/pollinations.test.ts`:
- Around line 38-51: The test for buildPollinationsUrl currently only asserts
the primary "image" param; add a negative assertion that the
last-frame/interpolation query params are not present so the test truly verifies
"does not encode last frame image" behavior—after creating parsed (new URL(url))
add assertions like expect(parsed.searchParams.get("lastFrameImage")).toBeNull()
and/or expect(parsed.searchParams.has("last_frame")).toBe(false) (and similarly
for any interpolation-related keys your buildPollinationsUrl might emit) to
ensure no last-frame or interpolation params are encoded.

---

Nitpick comments:
In `@lib/models/model-seo-slugs.test.ts`:
- Around line 27-30: Replace the hardcoded expected value (2) with a derived
count from the authoritative active-model source: compute expectedVideoCount by
querying the active registry/collection (e.g., MODEL_REGISTRY.getActiveModels()
or the source list used to build MODEL_SEO_SLUGS) and filter for type ===
"video", then assert expect(videoEntries).toHaveLength(expectedVideoCount);
reference MODEL_SEO_SLUGS and the registry method/collection you use to derive
the expected count.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: d13df503-d0bc-429f-89d8-b45f4eca4718

📥 Commits

Reviewing files that changed from the base of the PR and between 39bcee1 and ebd56c6.

📒 Files selected for processing (19)
  • app/auth/pollinations/callback/page.test.tsx
  • app/auth/pollinations/callback/page.tsx
  • app/layout.tsx
  • app/page.tsx
  • components/studio/upgrade-modal.test.tsx
  • convex/lib/pollinations.test.ts
  • convex/lib/pollinations.ts
  • hooks/queries/use-image-models.test.ts
  • lib/config/api.config.ts
  • lib/config/models.test.ts
  • lib/config/models.ts
  • lib/models/model-seo-slugs.test.ts
  • lib/models/model-seo-slugs.ts
  • lib/schemas/pollinations-pricing.schema.test.ts
  • lib/schemas/pollinations-pricing.schema.ts
  • lib/schemas/pollinations.schema.test.ts
  • lib/schemas/pollinations.schema.ts
  • package.json
  • proxy.ts
👮 Files not reviewed due to content moderation or server errors (5)
  • lib/schemas/pollinations.schema.ts
  • lib/schemas/pollinations-pricing.schema.ts
  • lib/config/models.ts
  • convex/lib/pollinations.ts
  • lib/config/models.test.ts

Comment on lines 39 to 42
vi.mock("convex/react", () => ({
useMutation: () => mockSetApiKey,
useConvexAuth: () => ({ isAuthenticated: true, isLoading: false }),
}));
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Make the Convex auth mock configurable and cover the new gate.

Hardcoding useConvexAuth() to { isAuthenticated: true, isLoading: false } means this suite never exercises the newly added isAuthLoading and unauthenticated branches. That leaves the security-sensitive callback gating untested.

💡 Small test setup tweak
 const mockSetApiKey = vi.fn().mockResolvedValue({ success: true });
+const mockUseConvexAuth = vi.fn(() => ({
+  isAuthenticated: true,
+  isLoading: false,
+}));

 vi.mock("convex/react", () => ({
   useMutation: () => mockSetApiKey,
-  useConvexAuth: () => ({ isAuthenticated: true, isLoading: false }),
+  useConvexAuth: () => mockUseConvexAuth(),
 }));

Then add explicit cases for:

  • isLoading: true → stays in processing and does not call the mutation yet.
  • isAuthenticated: false → shows the failure path and still strips the callback hash.
📝 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
vi.mock("convex/react", () => ({
useMutation: () => mockSetApiKey,
useConvexAuth: () => ({ isAuthenticated: true, isLoading: false }),
}));
const mockSetApiKey = vi.fn().mockResolvedValue({ success: true });
const mockUseConvexAuth = vi.fn(() => ({
isAuthenticated: true,
isLoading: false,
}));
vi.mock("convex/react", () => ({
useMutation: () => mockSetApiKey,
useConvexAuth: () => mockUseConvexAuth(),
}));
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/auth/pollinations/callback/page.test.tsx` around lines 39 - 42, The
current test hardcodes useConvexAuth to { isAuthenticated: true, isLoading:
false } so it never exercises the new loading/unauthenticated gate; change the
mock exported from vi.mock("convex/react") to allow configuring the return value
(e.g., expose a configurable get/set or a mock implementation that reads a
variable) instead of always returning the same object, and update tests that use
mockSetApiKey to add two new cases: one where useConvexAuth returns { isLoading:
true } to assert the component remains in processing and does not call
useMutation/mockSetApiKey, and one where it returns { isAuthenticated: false,
isLoading: false } to assert the failure UI is shown and the callback-hash
stripping still occurs.

Comment thread app/auth/pollinations/callback/page.tsx Outdated
Comment thread app/layout.tsx
Comment on lines +152 to +158
<ClerkThemeProvider>
<ConvexClientProvider>
<QueryProvider>
<PollenAuthProvider>
<Header />
{children}
<Toaster position="bottom-right" richColors closeButton />
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Avoid rendering both app and landing headers on /.

app/page.tsx Line 55 already mounts LandingHeader, so the unconditional Header here gives the home page two top nav bars / auth entry points. Please move the app header into a route-specific layout (for example, app vs. marketing groups) or otherwise suppress it for landing routes.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/layout.tsx` around lines 152 - 158, The root layout currently always
renders Header, causing duplicate navbars because app/page.tsx mounts
LandingHeader; update the layout to avoid rendering Header for landing routes by
either moving Header out of app/layout.tsx into a route-specific layout (e.g.,
an "app" sublayout) or add a route check that suppresses Header for the landing
path. Locate the Header usage in app/layout.tsx and either relocate it to the
appropriate sublayout (so LandingHeader remains in app/page.tsx) or wrap the
Header render in a pathname/route conditional that excludes the root (“/”) and
any marketing routes; ensure LandingHeader (referenced in app/page.tsx) remains
unaffected.

Comment thread convex/lib/pollinations.test.ts Outdated
Comment on lines +38 to +51
it("does not encode last frame image for non-interpolation video models like ltx-2", () => {
const url = buildPollinationsUrl({
prompt: "test prompt",
model: "ltx-2",
image: "https://example.com/first.jpg",
lastFrameImage: "https://example.com/second.jpg",
duration: 5,
aspectRatio: "16:9",
})

const parsed = new URL(url)

expect(parsed.searchParams.get("image")).toBe("https://example.com/first.jpg")
})
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Test misses a negative assertion for last-frame encoding.

The test title says last-frame data is not encoded, but it only checks image. Add an explicit check that interpolation params are absent (Line 50 onward), otherwise this can pass on a partial regression.

✅ Tighten the test
     const parsed = new URL(url)

     expect(parsed.searchParams.get("image")).toBe("https://example.com/first.jpg")
+    expect(parsed.searchParams.get("image_urls")).toBeNull()
📝 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
it("does not encode last frame image for non-interpolation video models like ltx-2", () => {
const url = buildPollinationsUrl({
prompt: "test prompt",
model: "ltx-2",
image: "https://example.com/first.jpg",
lastFrameImage: "https://example.com/second.jpg",
duration: 5,
aspectRatio: "16:9",
})
const parsed = new URL(url)
expect(parsed.searchParams.get("image")).toBe("https://example.com/first.jpg")
})
it("does not encode last frame image for non-interpolation video models like ltx-2", () => {
const url = buildPollinationsUrl({
prompt: "test prompt",
model: "ltx-2",
image: "https://example.com/first.jpg",
lastFrameImage: "https://example.com/second.jpg",
duration: 5,
aspectRatio: "16:9",
})
const parsed = new URL(url)
expect(parsed.searchParams.get("image")).toBe("https://example.com/first.jpg")
expect(parsed.searchParams.get("image_urls")).toBeNull()
})
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@convex/lib/pollinations.test.ts` around lines 38 - 51, The test for
buildPollinationsUrl currently only asserts the primary "image" param; add a
negative assertion that the last-frame/interpolation query params are not
present so the test truly verifies "does not encode last frame image"
behavior—after creating parsed (new URL(url)) add assertions like
expect(parsed.searchParams.get("lastFrameImage")).toBeNull() and/or
expect(parsed.searchParams.has("last_frame")).toBe(false) (and similarly for any
interpolation-related keys your buildPollinationsUrl might emit) to ensure no
last-frame or interpolation params are encoded.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (1)
components/studio/features/generation/controls-feature.tsx (1)

86-89: Centralize the reference-frame limit calculation.

useGenerationSettings() now computes the same limit, and the two versions already differ for non-video / unknown models (undefined there vs 0 here). Pull this into a shared helper or expose the computed max from the hook so the UI gate, local-storage clamp, and request payload stay in sync.

Also applies to: 183-183

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@components/studio/features/generation/controls-feature.tsx` around lines 86 -
89, The UI computes maxReferenceFrames locally (using
currentModelDef.supportsReferenceImage, supportsInterpolation, and
referenceFrameCount) while useGenerationSettings() already computes this value
differently; centralize the logic by either moving the calculation into a shared
helper (e.g., getMaxReferenceFrames(modelDef)) or expose the computed max from
useGenerationSettings() so the UI can consume it directly; update references to
maxReferenceFrames in controls-feature.tsx (and the other occurrence) to use the
shared helper or hook-provided value to ensure the UI gate, local-storage clamp,
and request payload all use the same source of truth.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@app/auth/pollinations/callback/actions.ts`:
- Around line 47-49: The catch block in
app/auth/pollinations/callback/actions.ts currently logs the raw thrown object
(console.error("[PollinationsCallback] Error saving API key:", error)), which
may expose secrets; change this to log a sanitized identifier instead—replace
the raw error log with a minimal safe message and non-sensitive metadata (e.g.,
console.error("[PollinationsCallback] Error saving API key:", { errorName:
error?.name ?? "UnknownError" }) or log a redacted message like "{ message:
'save failed', errorName: error?.name }"), and ensure the thrown error object or
full stack is not included; keep the return { status: "error_save_failed" }
behavior intact.

In `@app/auth/pollinations/callback/page.tsx`:
- Around line 170-199: The effect currently advances to "ready" purely on a
valid apiKey; change it to gate the "ready" state on the authenticated
user/session state: still call clearUrlHash() early as shown, but inside the
timeout (the block using callbackHash, extractKeyFromHash, isValidApiKeyFormat,
setApiKey, setCallbackState) validate the app's auth indicator (e.g.,
currentUser, isAuthenticated, or session object) before calling
setCallbackState("ready") — if auth is unknown wait or set an auth-specific
error state (e.g., "error_not_authenticated") and do not call setApiKey or
"ready"; ensure any auth check is synchronous or handled via the same
timeout/async flow so the URL-hash clearing behavior is unchanged.

---

Nitpick comments:
In `@components/studio/features/generation/controls-feature.tsx`:
- Around line 86-89: The UI computes maxReferenceFrames locally (using
currentModelDef.supportsReferenceImage, supportsInterpolation, and
referenceFrameCount) while useGenerationSettings() already computes this value
differently; centralize the logic by either moving the calculation into a shared
helper (e.g., getMaxReferenceFrames(modelDef)) or expose the computed max from
useGenerationSettings() so the UI can consume it directly; update references to
maxReferenceFrames in controls-feature.tsx (and the other occurrence) to use the
shared helper or hook-provided value to ensure the UI gate, local-storage clamp,
and request payload all use the same source of truth.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 3d1c3449-9ebf-4091-9359-95cbf8b397a5

📥 Commits

Reviewing files that changed from the base of the PR and between ebd56c6 and 92fa3cb.

📒 Files selected for processing (13)
  • app/auth/pollinations/callback/actions.test.ts
  • app/auth/pollinations/callback/actions.ts
  • app/auth/pollinations/callback/page.test.tsx
  • app/auth/pollinations/callback/page.tsx
  • components/studio/features/generation/controls-feature.tsx
  • components/studio/features/generation/controls-view.test.tsx
  • components/studio/features/generation/controls-view.tsx
  • components/studio/layout/studio-shell.test.tsx
  • components/studio/layout/studio-shell.tsx
  • convex/lib/pollinations.test.ts
  • convex/lib/pollinations.ts
  • hooks/use-generation-settings.test.ts
  • hooks/use-generation-settings.ts
✅ Files skipped from review due to trivial changes (1)
  • hooks/use-generation-settings.test.ts
🚧 Files skipped from review as they are similar to previous changes (3)
  • convex/lib/pollinations.test.ts
  • app/auth/pollinations/callback/page.test.tsx
  • convex/lib/pollinations.ts

Comment on lines +47 to +49
} catch (error) {
console.error("[PollinationsCallback] Error saving API key:", error);
return { status: "error_save_failed" };
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Avoid logging the raw exception in the API-key save path.

This handler processes a secret, and dumping the full thrown object to server logs can expose the key or request metadata from downstream failures. Log a sanitized identifier instead.

Suggested fix
   } catch (error) {
-    console.error("[PollinationsCallback] Error saving API key:", error);
+    console.error("[PollinationsCallback] Error saving API key", {
+      errorName: error instanceof Error ? error.name : "unknown",
+    });
     return { status: "error_save_failed" };
   }
📝 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
} catch (error) {
console.error("[PollinationsCallback] Error saving API key:", error);
return { status: "error_save_failed" };
} catch (error) {
console.error("[PollinationsCallback] Error saving API key", {
errorName: error instanceof Error ? error.name : "unknown",
});
return { status: "error_save_failed" };
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/auth/pollinations/callback/actions.ts` around lines 47 - 49, The catch
block in app/auth/pollinations/callback/actions.ts currently logs the raw thrown
object (console.error("[PollinationsCallback] Error saving API key:", error)),
which may expose secrets; change this to log a sanitized identifier
instead—replace the raw error log with a minimal safe message and non-sensitive
metadata (e.g., console.error("[PollinationsCallback] Error saving API key:", {
errorName: error?.name ?? "UnknownError" }) or log a redacted message like "{
message: 'save failed', errorName: error?.name }"), and ensure the thrown error
object or full stack is not included; keep the return { status:
"error_save_failed" } behavior intact.

Comment on lines +170 to +199
useEffect(() => {
let callbackHash = window.location.hash;

if (callbackHash) {
// Clear the hash from the URL for security (prevent accidental sharing)
// before waiting for browser redirect timing quirks to settle.
clearUrlHash();
}

const timer = window.setTimeout(() => {
if (!callbackHash && window.location.hash) {
callbackHash = window.location.hash;
clearUrlHash();
}

const apiKey = extractKeyFromHash(callbackHash);

if (!apiKey) {
setState("error_missing_key");
setCallbackState("error_missing_key");
return;
}

// Validate key format
if (!isValidApiKeyFormat(apiKey)) {
setState("error_invalid_key");
setCallbackState("error_invalid_key");
return;
}

// Clear the hash from the URL for security (prevent accidental sharing)
// Do this before the async call to minimize exposure time
if (typeof window !== "undefined") {
window.history.replaceState(
null,
"",
window.location.pathname + window.location.search,
);
}
setApiKey(apiKey);
setCallbackState("ready");
}, PROCESSING_DELAY_MS);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Gate the "ready" state on authenticated user state.

A syntactically valid hash always advances to "ready" here, even if the user's session expired during the OAuth round-trip. In that case the hash is already stripped, the user clicks “Finish Connection”, and only then learns the save can never succeed. Keep clearing the hash early, but don't offer the confirm step until auth is known/valid.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/auth/pollinations/callback/page.tsx` around lines 170 - 199, The effect
currently advances to "ready" purely on a valid apiKey; change it to gate the
"ready" state on the authenticated user/session state: still call clearUrlHash()
early as shown, but inside the timeout (the block using callbackHash,
extractKeyFromHash, isValidApiKeyFormat, setApiKey, setCallbackState) validate
the app's auth indicator (e.g., currentUser, isAuthenticated, or session object)
before calling setCallbackState("ready") — if auth is unknown wait or set an
auth-specific error state (e.g., "error_not_authenticated") and do not call
setApiKey or "ready"; ensure any auth check is synchronous or handled via the
same timeout/async flow so the URL-hash clearing behavior is unchanged.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 92fa3cb109

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread lib/config/models.ts
outputCertainty: "exact",
dimensionWarning: "Uses fixed 16:9 or 9:16 frames rounded to multiples of 32",
},
aspectRatios: VIDEO_ASPECT_RATIOS,
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Define LTX-2 aspect-ratio presets within model limits

The ltx-2 model points to VIDEO_ASPECT_RATIOS, which uses HD presets (1920×1080 / 1080×1920) that exceed this model’s own maxDimension (1024) and 1MP budget declared in the same block. Because useGenerationSettings.handleModelChange() applies aspect-ratio preset dimensions for single-tier fixed-size models, selecting LTX-2 initializes oversized dimensions and StudioShell forwards them in generation requests. That makes this model prone to invalid requests (or provider-side clamping) instead of the intended 1024×576 / 576×1024 behavior.

Useful? React with 👍 / 👎.

@Simplereally Simplereally merged commit 0a28ae7 into main Apr 11, 2026
5 checks passed
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 0670b04591

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +316 to +320
const [pendingDispatch, dispatched, processingDispatch] = await Promise.all([
ctx.db
.query("pendingGenerations")
.withIndex("by_status", (q) => q.eq("status", "pending"))
.collect(),
.withIndex("by_dispatch_status", (q) =>
q.eq("dispatchStatus", "pending").lt("updatedAt", cutoff)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Handle undefined dispatchStatus in stuck-generation cleanup

cleanupStuckGenerations now only queries by_dispatch_status for pending, dispatched, and processing, so pending/processing rows with dispatchStatus unset are never considered for timeout failure. That state is still explicitly supported elsewhere (claimGenerationForWorker accepts dispatchStatus === undefined), so legacy or partially migrated records can remain permanently active instead of being cleaned up. Add a fallback for undefined dispatch status (or a status-based fallback query) so old stuck jobs are still retired.

Useful? React with 👍 / 👎.

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.

1 participant