Skip to content

Feat: Auth , Landing page , Api Key insertion functionality#7

Open
Abhishek-Sonje wants to merge 3 commits into
jaydxxp:mainfrom
Abhishek-Sonje:main
Open

Feat: Auth , Landing page , Api Key insertion functionality#7
Abhishek-Sonje wants to merge 3 commits into
jaydxxp:mainfrom
Abhishek-Sonje:main

Conversation

@Abhishek-Sonje
Copy link
Copy Markdown
Collaborator

No description provided.

Copilot AI review requested due to automatic review settings May 19, 2026 06:39
@qodo-code-review
Copy link
Copy Markdown

Review Summary by Qodo

Implement authentication, API key management, and comprehensive landing page with Chalk AI design system

✨ Enhancement

Grey Divider

Walkthroughs

Description
• **Authentication & Authorization**: Implemented Clerk authentication middleware protecting
  /board route, with public landing page and authenticated board access
• **API Key Management**: Added Gemini API key validation endpoint, user-provided key support in
  diagram completion, and persistent localStorage management via context provider
• **Landing Page**: Converted home page to comprehensive landing page with Hero, Features, How It
  Works, Testimonials, Founders, CTA, and Footer sections with sketched design aesthetic
• **Board Refactoring**: Extracted board functionality into dedicated BoardClient component with
  integrated API key modal, voice input, and fullscreen controls
• **Design System**: Implemented "Chalk AI" design tokens with purple/gray color palette, custom
  fonts (Kalam for sketch style, Inter for body), and marquee animations
• **UI Components**: Added reusable components including API key modal with validation, video modal,
  fade-in animations, decorative icons, and responsive header with auth integration
• **Dependencies**: Added @clerk/nextjs for authentication infrastructure
Diagram
flowchart LR
  A["Landing Page<br/>Hero, Features,<br/>Testimonials"] -->|"Authenticated"| B["Board Page<br/>BoardClient<br/>Component"]
  A -->|"Sign In"| C["Clerk Auth<br/>Middleware"]
  C -->|"Protected"| B
  B -->|"API Key Input"| D["API Key Modal<br/>& Provider"]
  D -->|"Validate"| E["Validate Key<br/>Endpoint"]
  E -->|"User Key"| F["Complete Diagram<br/>Endpoint"]
  F -->|"Generate"| G["Gemini API<br/>Response"]
  H["Design System<br/>Chalk AI Tokens"] -.->|"Styles"| A
  H -.->|"Styles"| B
Loading

Grey Divider

File Changes

1. frontend/app/api/validate-key/route.ts ✨ Enhancement +73/-0

Add Gemini API key validation endpoint

• New API endpoint for validating Gemini API keys with comprehensive error handling
• Performs real API calls to Google's Generative Language API to verify key validity
• Returns specific error codes for invalid keys, permission issues, and rate limiting
• Includes validation for key format (must start with 'AIza')

frontend/app/api/validate-key/route.ts


2. frontend/app/api/complete-diagram/route.ts ✨ Enhancement +9/-4

Support user-provided Gemini API keys in diagram completion

• Modified to accept user-provided API key via x-gemini-api-key header
• Falls back to server environment variable if no user key provided
• Updated error response to use NO_API_KEY error code with 401 status
• Injects user key into environment for the AI SDK provider

frontend/app/api/complete-diagram/route.ts


3. frontend/proxy.ts Authentication +21/-0

Add Clerk authentication middleware

• New middleware file implementing Clerk authentication
• Defines public routes (only / is public, /board is protected)
• Protects all non-public routes requiring authentication
• Configures matcher to skip Next.js internals and static files

frontend/proxy.ts


View more (24)
4. frontend/app/globals.css Design +38/-12

Implement Chalk AI design system and color tokens

• Replaced minimal monochrome palette with "Chalk AI" design tokens
• Added comprehensive color variables for purple, gray, borders, and backgrounds
• Introduced chalk-* color variables for consistent branding
• Added marquee animation keyframes for scrolling testimonials

frontend/app/globals.css


5. frontend/components/board-client.tsx ✨ Enhancement +492/-0

Create board client component with API key integration

• New client component extracted from main page with full board functionality
• Integrates API key management with modal and provider
• Implements voice input with idle detection and auto-generation
• Displays setup screen when API key is missing, with link to get free key
• Includes fullscreen toggle, mic controls, and API key settings button

frontend/components/board-client.tsx


6. frontend/app/page.tsx ✨ Enhancement +18/-463

Convert home page to landing page with section components

• Refactored from full board implementation to landing page
• Now imports and renders landing page sections (Hero, Features, etc.)
• Removed all whiteboard and AI suggestion logic
• Simplified to composition of reusable landing components

frontend/app/page.tsx


7. frontend/components/landing/founders.tsx ✨ Enhancement +227/-0

Add founders section with sketched card profiles

• New component showcasing co-founders with sketched card design
• Displays founder profiles with images, bios, and social links
• Implements hand-drawn frame SVG backgrounds with paper texture overlay
• Includes LinkedIn, Twitter, and email contact buttons with hover effects

frontend/components/landing/founders.tsx


8. frontend/components/landing/hero.tsx ✨ Enhancement +285/-0

Add hero section with CTA and demo video

• New hero section with tagline "Teach. Draw. Improve with AI"
• Includes sign-up/sign-in buttons with Clerk integration
• Features video modal trigger for live demo
• Displays triangle sketch diagram in frame with AI assistant badge
• Implements decorative icons and sketch marks for visual interest

frontend/components/landing/hero.tsx


9. frontend/components/landing/testimonials.tsx ✨ Enhancement +221/-0

Add testimonials section with marquee carousel

• New testimonials section with featured review and infinite marquee carousel
• Implements sketched paper frame design for each testimonial card
• Features 5 tutor testimonials with rotating display
• Includes fade-in animations and hover effects on cards

frontend/components/landing/testimonials.tsx


10. frontend/components/api-key-modal.tsx ✨ Enhancement +236/-0

Add API key modal with validation and feedback

• New modal component for API key input and validation
• Integrates with useApiKey provider for state management
• Shows/hide toggle for password field and real-time validation feedback
• Displays success/error messages with appropriate styling
• Can be set as required to prevent dismissal without key

frontend/components/api-key-modal.tsx


11. frontend/components/landing/features.tsx ✨ Enhancement +183/-0

Add features section with sketched card design

• New features section displaying 4 core capabilities
• Implements sketched card frames with hand-drawn SVG backgrounds
• Features Smart Drawing, AI Suggestions, Interactive Lessons, and Save & Share
• Includes decorative icons and hover animations on feature cards

frontend/components/landing/features.tsx


12. frontend/components/landing/doodles.tsx ✨ Enhancement +27/-0

Add sparkle doodle icon component

• New utility component exporting Sparkle SVG icon
• Provides reusable decorative sparkle element for landing page

frontend/components/landing/doodles.tsx


13. frontend/components/landing/icons.tsx ✨ Enhancement +86/-0

Add landing page icon library

• New icon library with 12 SVG icon exports
• Includes Star, Pencil, Bulb, Users, Cloud, Sparkle, Play, ArrowRight, Brain, Draw, Improve, Teach,
 and Logo icons
• All icons use consistent stroke widths and sizing

frontend/components/landing/icons.tsx


14. frontend/components/landing/fade-in.tsx ✨ Enhancement +35/-0

Add fade-in animation component

• New reusable fade-in animation component using Intersection Observer
• Triggers animation when element becomes visible in viewport
• Supports configurable delay and custom className
• Uses cubic-bezier easing for smooth transitions

frontend/components/landing/fade-in.tsx


15. frontend/components/landing/footer.tsx ✨ Enhancement +142/-0

Landing page footer with social links and branding

• New footer component with brand signature, social media links, and copyright information
• Includes GitHub repository link and individual LinkedIn/X profiles for team members
• Features decorative SVG grid pattern overlay and gradient divider
• Responsive layout with mobile and desktop optimizations

frontend/components/landing/footer.tsx


16. frontend/components/landing/how-it-works.tsx ✨ Enhancement +118/-0

Process flow section explaining Chalk AI workflow

• New "How It Works" section component displaying 4-step process flow
• Steps include: API key setup, drawing, AI recognition, and teaching enhancement
• Features animated step cards with icons, desktop connector lines, and arrow indicators
• Includes decorative hourglass icon and fade-in animations

frontend/components/landing/how-it-works.tsx


17. frontend/components/header.tsx ✨ Enhancement +92/-0

Navigation header with auth integration and fullscreen toggle

• New header component with responsive navigation and branding
• Includes fullscreen toggle button for board page and home navigation link
• Supports conditional rendering for board vs. landing pages
• Integrates auth UI slots for sign-in/sign-up buttons via children prop

frontend/components/header.tsx


18. frontend/components/landing/video-modal.tsx ✨ Enhancement +69/-0

Video modal component with placeholder state

• New modal component for video playback with Framer Motion animations
• Displays placeholder with loading spinner when no video URL provided
• Includes close button and backdrop with blur effect
• Responsive design with spring animation transitions

frontend/components/landing/video-modal.tsx


19. frontend/app/layout.tsx ✨ Enhancement +65/-2

Root layout with Clerk auth and provider setup

• Integrated Clerk authentication provider wrapping entire application
• Added custom font imports (Kalam for sketch style, Inter for body text)
• Wrapped layout with UIProvider, ApiKeyProvider, Header, and LayoutWrapper
• Added conditional auth UI with sign-in/sign-up buttons and user menu

frontend/app/layout.tsx


20. frontend/components/landing/cta.tsx ✨ Enhancement +62/-0

Call-to-action section with auth-aware button routing

• New call-to-action section component with conditional rendering based on auth state
• Shows "Get Started" button for signed-out users and "Go to My Board" for signed-in users
• Includes decorative cloud icon and fade-in animations
• Features dark background with decorative circles and responsive layout

frontend/components/landing/cta.tsx


21. frontend/components/api-key-provider.tsx ✨ Enhancement +66/-0

API key context provider with localStorage persistence

• New context provider for managing Gemini API key storage and retrieval
• Implements localStorage persistence with error handling
• Provides useApiKey hook for accessing API key state across components
• Includes loading state to handle async localStorage initialization

frontend/components/api-key-provider.tsx


22. frontend/components/landing/decorative-icon.tsx ✨ Enhancement +48/-0

Decorative icon component for landing page embellishments

• New reusable component for positioning decorative images on landing pages
• Supports rotation, sizing, opacity, and delay animations
• Uses Next.js Image component and FadeIn animation wrapper
• Hidden on mobile devices, visible only on large screens

frontend/components/landing/decorative-icon.tsx


23. frontend/components/ui-provider.tsx ✨ Enhancement +31/-0

UI state context provider for fullscreen management

• New context provider for managing UI state (fullscreen mode)
• Provides useUI hook with fullscreen toggle functionality
• Includes error handling for hook usage outside provider

frontend/components/ui-provider.tsx


24. frontend/package.json Dependencies +1/-0

Added Clerk authentication dependency

• Added @clerk/nextjs dependency version ^7.3.5 for authentication

frontend/package.json


25. frontend/components/clerk-helpers.tsx ✨ Enhancement +26/-0

Clerk authentication helper component

• New helper component Show for conditional rendering based on authentication state
• Uses server-side Clerk auth() API to check user authentication
• Supports "signed-in" and "signed-out" conditional rendering

frontend/components/clerk-helpers.tsx


26. frontend/app/board/page.tsx ✨ Enhancement +20/-0

Protected board page with auth redirect

• New board page with server-side authentication check
• Redirects unauthenticated users to landing page
• Renders BoardClient component for authenticated users

frontend/app/board/page.tsx


27. frontend/components/layout-wrapper.tsx ✨ Enhancement +20/-0

Layout wrapper with responsive padding management

• New wrapper component managing layout padding based on fullscreen and page state
• Removes top padding on board page or when in fullscreen mode
• Uses smooth transitions for padding changes

frontend/components/layout-wrapper.tsx


Grey Divider

Qodo Logo

@qodo-code-review
Copy link
Copy Markdown

qodo-code-review Bot commented May 19, 2026

Code Review by Qodo

🐞 Bugs (6) 📘 Rule violations (0)

Grey Divider


Action required

1. Env key race leak 🐞 Bug ⛨ Security
Description
POST /api/complete-diagram assigns process.env.GOOGLE_GENERATIVE_AI_API_KEY from a request
header, which is global and shared across concurrent requests. This can cause one user’s Gemini key
to be used for another user’s request and can persist beyond the request when the header is absent.
Code

frontend/app/api/complete-diagram/route.ts[R10-23]

+    // Prefer user-provided key from header, fall back to server env var
+    const userKey = request.headers.get("x-gemini-api-key");
+    const apiKey = userKey?.trim() || process.env.GOOGLE_GENERATIVE_AI_API_KEY;
+
    if (!apiKey) {
      return NextResponse.json(
-        { error: "Missing GOOGLE_GENERATIVE_AI_API_KEY" },
-        { status: 500 }
+        { error: "NO_API_KEY", message: "No Gemini API key configured. Please add your API key in the board settings." },
+        { status: 401 }
      );
    }

+    // Inject the key into the environment for the @ai-sdk/google provider
+    process.env.GOOGLE_GENERATIVE_AI_API_KEY = apiKey;
+
Evidence
The route reads a user-provided key from x-gemini-api-key, then overwrites
process.env.GOOGLE_GENERATIVE_AI_API_KEY before invoking the Gemini model via @ai-sdk/google,
making the key effectively global for concurrent and subsequent requests.

frontend/app/api/complete-diagram/route.ts[8-23]
frontend/app/api/complete-diagram/route.ts[79-83]

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

## Issue description
`frontend/app/api/complete-diagram/route.ts` sets `process.env.GOOGLE_GENERATIVE_AI_API_KEY` per request based on `x-gemini-api-key`. Because `process.env` is process-global, concurrent requests can race and later requests can unintentionally reuse a previous user's key.

## Issue Context
The code calls `generateText({ model: google("gemini-2.5-flash-image"), ... })`, and the provider reads credentials from environment/global config.

## Fix Focus Areas
- Prefer a per-request API key injection mechanism (provider option / client instance / explicit header) rather than global env.
- If the SDK truly only supports env-based configuration, do **not** use a shared mutable global; refactor to a direct HTTP call where the key can be set per request.

## Fix Focus Areas (references)
- frontend/app/api/complete-diagram/route.ts[8-23]
- frontend/app/api/complete-diagram/route.ts[79-83]

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


2. Middleware file misnamed 🐞 Bug ⛨ Security
Description
Clerk route protection is implemented in frontend/proxy.ts, but Next.js only executes middleware
from middleware.ts/middleware.js at the app root. As a result, /board and /api/* protections
may not run, leaving API routes unauthenticated.
Code

frontend/proxy.ts[R1-21]

+import { clerkMiddleware, createRouteMatcher } from "@clerk/nextjs/server";
+
+// Define which routes are public. All other routes will require authentication.
+// / is public, /board is protected.
+const isPublicRoute = createRouteMatcher(["/"]);
+
+export default clerkMiddleware(async (auth, request) => {
+  if (!isPublicRoute(request)) {
+    // Protect all non-public routes
+    await auth.protect();
+  }
+});
+
+export const config = {
+  matcher: [
+    // Skip Next.js internals and all static files, unless found in search params
+    "/((?!_next|[^?]*\\.(?:html?|css|js(?!on)|jpe?g|webp|png|gif|svg|ttf|woff2?|ico|csv|docx?|xlsx?|zip|webmanifest)).*)",
+    // Always run for API routes
+    "/(api|trpc)(.*)",
+  ],
+};
Evidence
The protection logic is present but placed in proxy.ts, while BoardPage indicates the project
expects middleware.ts to handle auth. If the middleware is not in the recognized
filename/location, it won’t execute and API routes remain unguarded.

frontend/proxy.ts[1-21]
frontend/app/board/page.tsx[9-17]

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 Clerk middleware is defined in `frontend/proxy.ts`, which Next.js will not automatically run as middleware. This likely disables intended auth protection for non-public routes and API endpoints.

## Issue Context
`BoardPage` explicitly references `middleware.ts` as the primary guard, but the repo contains `proxy.ts` instead.

## Fix Focus Areas
- Rename/move `frontend/proxy.ts` to `frontend/middleware.ts` (or the correct root per your Next.js layout).
- Ensure the matcher config remains intact and that the middleware actually runs for `/board` and `/api/*`.
- Update any comments/docs to reflect the real filename.

## Fix Focus Areas (references)
- frontend/proxy.ts[1-21]
- frontend/app/board/page.tsx[9-17]

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


3. Invalid SVG JSX props 🐞 Bug ≡ Correctness
Description
frontend/components/landing/doodles.tsx uses invalid TSX/SVG props (e.g., style="...",
fill-rule, clip-rule) that do not type-check in React/TypeScript. This will break next build
type-checking even if the component is not imported.
Code

frontend/components/landing/doodles.tsx[R1-26]

+export const Sparkle = () => (
+  <svg
+    xmlns="http://www.w3.org/2000/svg"
+    viewBox="0 0 87 87"
+    fill="currentColor"
+    style="color:#0a0000;"
+    width="64"
+    height="64"
+    preserveAspectRatio="xMidYMid meet"
+  >
+    <mask id="path-1-inside-1_103_6790" fill="currentColor">
+      <path
+        fill-rule="evenodd"
+        clip-rule="evenodd"
+        d="M48.956 32.7515L43.007 0.632324L37.0728 32.6721L23.227 24.0134L31.7425 37.7602L0 43.6394L31.9899 49.5643L23.1425 63.712L37.1519 55.0337L43.007 86.6464L48.8587 55.0524L62.8411 63.7964L54.0248 49.5642L86.0141 43.6394L54.3703 37.7785L62.9256 24.0979L48.956 32.7515Z"
+        fill="#000000"
+        stroke="#000000"
+      />
+    </mask>
+    <path
+      d="M43.007 0.632324L45.3499 0.198402H40.6642L43.007 0.632324ZM48.956 32.7515L46.6131 33.1854L47.2479 36.6123L50.2107 34.777L48.956 32.7515ZM37.0728 32.6721L35.8095 34.6922L38.778 36.5486L39.4156 33.106L37.0728 32.6721ZM23.227 24.0134L24.4903 21.9932L21.2015 25.2681L23.227 24.0134ZM31.7425 37.7602L32.1765 40.103L35.6034 39.4683L33.7681 36.5055L31.7425 37.7602ZM0 43.6394L-0.433923 41.2966L-0.433923 45.9822L0 43.6394ZM31.9899 49.5643L34.0101 50.8277L35.8665 47.8592L32.4238 47.2215L31.9899 49.5643ZM23.1425 63.712L21.1224 62.4486L24.3972 65.7375L23.1425 63.712ZM37.1519 55.0337L39.4947 54.5998L38.86 51.1729L35.8972 53.0082L37.1519 55.0337ZM43.007 86.6464L40.6642 87.0803H45.3499L43.007 86.6464ZM48.8587 55.0524L50.122 53.0322L47.1535 51.1758L46.5159 54.6185L48.8587 55.0524ZM62.8411 63.7964L61.5777 65.8166L64.8666 62.5417L62.8411 63.7964ZM54.0248 49.5642L53.5909 47.2214L50.164 47.8561L51.9993 50.819L54.0248 49.5642ZM86.0141 43.6394L86.448 45.9822V41.2966L86.0141 43.6394ZM54.3703 37.7785L52.3501 36.5151L50.4937 39.4837L53.9363 40.1213L54.3703 37.7785ZM62.9256 24.0979L64.9457 25.3612L61.6708 22.0724L62.9256 24.0979ZM40.6642 1.06625L46.6131 33.1854L51.2988 32.3175L45.3499 0.198402L40.6642 1.06625ZM39.4156 33.106L45.3499 1.06625L40.6642 0.198402L34.73 32.2381L39.4156 33.106ZM21.9637 26.0336L35.8095 34.6922L38.3362 30.6519L24.4903 21.9932L21.9637 26.0336ZM33.7681 36.5055L25.2525 22.7587L21.2015 25.2681L29.717 39.0149L33.7681 36.5055ZM0.433923 45.9822L32.1765 40.103L31.3086 35.4174L-0.433923 41.2966L0.433923 45.9822ZM32.4238 47.2215L0.433923 41.2966L-0.433923 45.9822L31.556 51.9072L32.4238 47.2215ZM25.1627 64.9753L34.0101 50.8277L29.9697 48.301L21.1224 62.4486L25.1627 64.9753ZM35.8972 53.0082L21.8878 61.6864L24.3972 65.7375L38.4067 57.0593L35.8972 53.0082ZM45.3499 86.2125L39.4947 54.5998L34.8091 55.4677L40.6642 87.0803L45.3499 86.2125ZM46.5159 54.6185L40.6642 86.2125L45.3499 87.0803L51.2015 55.4863L46.5159 54.6185ZM64.1044 61.7763L50.122 53.0322L47.5954 57.0726L61.5777 65.8166L64.1044 61.7763ZM51.9993 50.819L60.8155 65.0512L64.8666 62.5417L56.0504 48.3095L51.9993 50.819ZM85.5802 41.2966L53.5909 47.2214L54.4588 51.907L86.448 45.9822L85.5802 41.2966ZM53.9363 40.1213L85.5802 45.9822L86.448 41.2966L54.8042 35.4357L53.9363 40.1213ZM60.9054 22.8346L52.3501 36.5151L56.3904 39.0418L64.9457 25.3612L60.9054 22.8346ZM50.2107 34.777L64.1803 26.1234L61.6708 22.0724L47.7012 30.7259L50.2107 34.777Z"
+      fill="#000000"
+      mask="url(#path-1-inside-1_103_6790)"
+      stroke="#000000"
+    />
+  </svg>
Evidence
The file uses a string-valued style prop and kebab-cased SVG attributes; both are rejected by
React’s TSX typings and will cause type-check errors during build.

frontend/components/landing/doodles.tsx[1-26]

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

## Issue description
`doodles.tsx` is TSX and is included in TypeScript checking. It uses invalid JSX attribute names (`fill-rule`, `clip-rule`) and assigns `style` as a string, which violates React/TS types.

## Issue Context
TypeScript checking in Next.js covers `**/*.ts` and `**/*.tsx` (per tsconfig include), so this can fail CI/build even if unused.

## Fix Focus Areas
- Convert SVG attributes to React’s camelCase equivalents (`fillRule`, `clipRule`, etc.).
- Replace `style="color:#0a0000;"` with `style={{ color: "#0a0000" }}`.
- If the component is not needed, delete the file instead.

## Fix Focus Areas (references)
- frontend/components/landing/doodles.tsx[1-26]

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


View more (1)
4. Broken theme CSS tokens 🐞 Bug ≡ Correctness
Description
globals.css defines @keyframes inside @theme inline and maps theme variables to undefined
custom properties like --muted/--primary, making utilities such as
bg-primary/text-muted-foreground resolve to invalid values. This can break global styling and
animations across the app.
Code

frontend/app/globals.css[R35-59]

  --color-primary-foreground: var(--primary-foreground);
  --color-ring: var(--ring);
  --radius-lg: var(--radius);
-  --font-sans: var(--font-geist-sans), system-ui, sans-serif;
+  --color-chalk-ink: var(--chalk-ink);
+  --color-chalk-ink-soft: var(--chalk-ink-soft);
+  --color-chalk-gray: var(--chalk-gray);
+  --color-chalk-bg: var(--chalk-bg);
+  --color-chalk-white: var(--chalk-white);
+  --color-chalk-purple: var(--chalk-purple);
+  --color-chalk-purple-light: var(--chalk-purple-light);
+  --color-chalk-blue-soft: var(--chalk-blue-soft);
+  --color-chalk-border: var(--chalk-border);
+  --color-chalk-border-soft: var(--chalk-border-soft);
+
+  --font-sans: var(--font-body), system-ui, sans-serif;
  --font-mono: var(--font-geist-mono), monospace;
+  --font-sketch: var(--font-sketch);
+  
+  --animate-marquee: marquee 28s linear infinite;
+  
+  @keyframes marquee {
+    from { transform: translateX(0); }
+    to { transform: translateX(-50%); }
+  }
}
Evidence
The theme mapping references multiple base tokens (--muted, --primary, etc.) that are not
defined in :root, and it nests @keyframes inside @theme inline, which is not valid in standard
CSS/Tailwind theme blocks.

frontend/app/globals.css[5-25]
frontend/app/globals.css[27-59]

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

## Issue description
In `globals.css`, the Tailwind `@theme inline` block contains a nested `@keyframes` rule and references CSS variables (`--muted`, `--primary`, etc.) that are not defined anywhere. This can break global token resolution and utility styles.

## Issue Context
Many components use classes like `bg-primary`, `text-muted-foreground`, etc. With `--primary`/`--muted` undefined, the mapped `--color-*` variables become invalid.

## Fix Focus Areas
- Move `@keyframes marquee` to the top level (outside `@theme`).
- Define the missing base tokens in `:root` (e.g., `--muted`, `--primary`, `--border`, `--ring`, `--primary-foreground`, etc.), or remove the mappings if you no longer use those utilities.

## Fix Focus Areas (references)
- frontend/app/globals.css[5-25]
- frontend/app/globals.css[27-59]

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



Remediation recommended

5. API key in URL 🐞 Bug ⛨ Security
Description
POST /api/validate-key sends the Gemini API key as a ?key= query parameter, which can leak via
server logs/telemetry and intermediaries. It also returns HTTP 200 for obviously-invalid keys
(non-AIza prefix), making client/server error handling inconsistent.
Code

frontend/app/api/validate-key/route.ts[R16-33]

+    if (!api_key.startsWith("AIza")) {
+      return NextResponse.json({
+        valid: false,
+        reason: "That doesn't look like a valid Gemini API key. Keys start with 'AIza'.",
+      });
+    }
+
+    // Make a real but minimal call to Gemini to validate the key
+    const response = await fetch(
+      `https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent?key=${api_key}`,
+      {
+        method: "POST",
+        headers: { "Content-Type": "application/json" },
+        body: JSON.stringify({
+          contents: [{ parts: [{ text: "Hi" }] }],
+        }),
+      }
+    );
Evidence
The route constructs the outbound fetch URL with ?key=${api_key}, directly placing the secret in
the URL. The invalid-prefix branch uses NextResponse.json(...) without a status override, which
defaults to 200.

frontend/app/api/validate-key/route.ts[16-33]
frontend/app/api/validate-key/route.ts[16-21]

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 key validation route embeds the API key in the outbound request URL (`...generateContent?key=${api_key}`), which can leak secrets through URL logging. Additionally, the invalid-prefix branch returns a 200 by default.

## Issue Context
Google APIs typically accept an API key in a header (e.g., `x-goog-api-key`) which avoids putting secrets in URLs.

## Fix Focus Areas
- Send the API key via request header instead of querystring.
- Return an explicit 4xx status for invalid prefix (e.g., 400).
- (Optional reliability) Add an AbortController timeout so validation can’t hang indefinitely.

## Fix Focus Areas (references)
- frontend/app/api/validate-key/route.ts[16-33]
- frontend/app/api/validate-key/route.ts[16-21]

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


6. Stale apiKey in callback 🐞 Bug ≡ Correctness
Description
BoardClient.handleRequestSuggestion uses apiKey when building request headers but omits apiKey
from the useCallback dependency list. After the user saves/updates a key, the memoized callback
can keep using the previous key (or no key) until intent changes.
Code

frontend/components/board-client.tsx[R56-124]

+  const handleRequestSuggestion = useCallback(async (overridePrompt?: string) => {
+    if (!exportFnRef.current) {
+      setError("Canvas not ready. Please wait a moment and try again.");
+      return;
+    }
+
+    const promptText = typeof overridePrompt === "string" ? overridePrompt : intent;
+
+    if (!promptText.trim()) {
+      setError("Please provide an intent description");
+      return;
+    }
+
+    setIsLoading(true);
+    setError(null);
+
+    try {
+      const imageDataUrl = await exportFnRef.current();
+      let base64Image: string | undefined;
+
+      if (imageDataUrl) {
+        if (imageDataUrl.startsWith("data:image/png;base64,")) {
+          base64Image = imageDataUrl.replace("data:image/png;base64,", "");
+        } else {
+          const response = await fetch(imageDataUrl);
+          const blob = await response.blob();
+          const reader = new FileReader();
+          await new Promise((resolve, reject) => {
+            reader.onloadend = resolve;
+            reader.onerror = reject;
+            reader.readAsDataURL(blob);
+          });
+          const dataUrl = reader.result as string;
+          base64Image = dataUrl.replace("data:image/png;base64,", "");
+        }
+      }
+
+      const apiResponse = await fetch("/api/complete-diagram", {
+        method: "POST",
+        headers: {
+          "Content-Type": "application/json",
+          ...(apiKey ? { "x-gemini-api-key": apiKey } : {}),
+        },
+        body: JSON.stringify({
+          prompt: promptText,
+          image_data: base64Image,
+        }),
+      });
+
+      if (!apiResponse.ok) {
+        const errorData = await apiResponse.json();
+        if (errorData.error === "NO_API_KEY") {
+          setShowApiKeyModal(true);
+          throw new Error("Please set up your Gemini API key to use AI features.");
+        }
+        throw new Error(errorData.error || "Failed to get AI suggestion");
+      }
+
+      const data = await apiResponse.json();
+      setGeneratedImage(data.image_data);
+    } catch (err) {
+      console.error("Error:", err);
+      setError(
+        err instanceof Error ? err.message : "Failed to get AI suggestion"
+      );
+    } finally {
+      setIsLoading(false);
+    }
+  }, [intent]);
Evidence
The callback builds headers using ...(apiKey ? { "x-gemini-api-key": apiKey } : {}), but the hook
is memoized only on [intent], so the function instance can remain bound to an older apiKey value
across key updates.

frontend/components/board-client.tsx[56-124]

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

## Issue description
`handleRequestSuggestion` uses `apiKey` inside a `useCallback`, but the dependency array is `[intent]` only. When `apiKey` changes (e.g., user saves key), React will keep returning the old callback instance, which still closes over the previous `apiKey` value.

## Issue Context
This affects calls like the “Improvise (Quick Enhance)” button that may run without changing `intent`, leading to requests missing the header even though the user has already configured a key.

## Fix Focus Areas
- Add `apiKey` to the `useCallback` dependency list (and any other reactive values referenced).
- Alternatively, remove the `useCallback` if memoization is not required.

## Fix Focus Areas (references)
- frontend/components/board-client.tsx[56-124]

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


Grey Divider

Qodo Logo

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR layers a Clerk-based auth flow, a marketing landing page, and an in-app "Bring Your Own Gemini Key" experience onto the existing whiteboard app. The board UI (formerly app/page.tsx) is moved behind /board, the public root now renders a multi-section landing page, and a new modal + provider lets users save a Gemini key in localStorage that is forwarded to the diagram API via header.

Changes:

  • Add Clerk auth (provider in root layout, sign-in/up buttons, Show helper, proxy.ts matcher config, and a server-side guard on /board).
  • Replace the home page with a landing experience (hero, features, how-it-works, testimonials, founders, cta, footer, decorative-icon, fade-in) and introduce Header, UIProvider, LayoutWrapper shells; refactor board logic into BoardClient.
  • Introduce user-supplied Gemini key handling: ApiKeyProvider/ApiKeyModal, a /api/validate-key route, and header-based key forwarding in /api/complete-diagram.

Reviewed changes

Copilot reviewed 27 out of 38 changed files in this pull request and generated 10 comments.

Show a summary per file
File Description
frontend/proxy.ts New Clerk middleware — but placed at a non-recognized path so it doesn't run.
frontend/package.json / package-lock.json Adds @clerk/nextjs dependency.
frontend/bun.lock Adds a Windows-only lightningcss native binary as a direct dep.
frontend/app/layout.tsx Wraps app in ClerkProvider/ApiKeyProvider/UIProvider, adds header + fonts.
frontend/app/page.tsx Replaces whiteboard with the new landing-page composition.
frontend/app/board/page.tsx New server-side auth-guarded entry that renders BoardClient.
frontend/app/api/complete-diagram/route.ts Accepts per-request Gemini key via header; mutates process.env to forward it.
frontend/app/api/validate-key/route.ts New unauthenticated endpoint that probes Gemini to validate a user key.
frontend/app/globals.css Adds chalk-themed CSS variables, marquee keyframes, font tokens.
frontend/components/board-client.tsx Extracts whiteboard UI; integrates API-key gating and modal.
frontend/components/api-key-provider.tsx / api-key-modal.tsx New context + modal for managing Gemini key in localStorage.
frontend/components/header.tsx / layout-wrapper.tsx / ui-provider.tsx New chrome/layout primitives shared across pages.
frontend/components/clerk-helpers.tsx Async Show helper to conditionally render by auth state.
frontend/components/landing/*.tsx New landing sections, icons, doodles, decorative icon, fade-in animator.
Files not reviewed (1)
  • frontend/package-lock.json: Language not supported

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread frontend/proxy.ts
Comment on lines +1 to +21
import { clerkMiddleware, createRouteMatcher } from "@clerk/nextjs/server";

// Define which routes are public. All other routes will require authentication.
// / is public, /board is protected.
const isPublicRoute = createRouteMatcher(["/"]);

export default clerkMiddleware(async (auth, request) => {
if (!isPublicRoute(request)) {
// Protect all non-public routes
await auth.protect();
}
});

export const config = {
matcher: [
// Skip Next.js internals and all static files, unless found in search params
"/((?!_next|[^?]*\\.(?:html?|css|js(?!on)|jpe?g|webp|png|gif|svg|ttf|woff2?|ico|csv|docx?|xlsx?|zip|webmanifest)).*)",
// Always run for API routes
"/(api|trpc)(.*)",
],
};
Comment on lines +21 to +22
// Inject the key into the environment for the @ai-sdk/google provider
process.env.GOOGLE_GENERATIVE_AI_API_KEY = apiKey;
Comment on lines +6 to +14
style="color:#0a0000;"
width="64"
height="64"
preserveAspectRatio="xMidYMid meet"
>
<mask id="path-1-inside-1_103_6790" fill="currentColor">
<path
fill-rule="evenodd"
clip-rule="evenodd"
Comment on lines +38 to +42
export const PlayIcon = () => (
<svg width="12" height="12" viewBox="0 0 12 12" fill="currentColor">
<path d="M3 2l7 4-7 4z" />
</svg>
);
import { PlayIcon, SparkleIcon } from "./icons";
import { ArrowRight, Sparkles } from "lucide-react";
import { VideoModal } from "./video-modal";
import { SignInButton, SignUpButton, useAuth } from "@clerk/nextjs";
import Link from "next/link";
import { FadeIn } from "./fade-in";
import { LogoIcon, ArrowRightIcon } from "./icons";
import { SignInButton, SignUpButton, useAuth } from "@clerk/nextjs";
Comment thread frontend/app/layout.tsx
Comment on lines 2 to +10
import { Geist, Geist_Mono } from "next/font/google";
import Link from "next/link";
import { ClerkProvider, SignInButton, SignUpButton, UserButton } from "@clerk/nextjs";
import { Show } from "@/components/clerk-helpers";
import { UIProvider } from "@/components/ui-provider";
import { ApiKeyProvider } from "@/components/api-key-provider";
import { Header } from "@/components/header";
import { LayoutWrapper } from "@/components/layout-wrapper";
import { Caveat, Nunito, Kalam, Inter } from "next/font/google";
Comment on lines +5 to +33
export async function POST(request: NextRequest) {
try {
const { api_key } = (await request.json()) as { api_key: string };

if (!api_key?.trim()) {
return NextResponse.json(
{ valid: false, reason: "API key is required." },
{ status: 400 }
);
}

if (!api_key.startsWith("AIza")) {
return NextResponse.json({
valid: false,
reason: "That doesn't look like a valid Gemini API key. Keys start with 'AIza'.",
});
}

// Make a real but minimal call to Gemini to validate the key
const response = await fetch(
`https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent?key=${api_key}`,
{
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
contents: [{ parts: [{ text: "Hi" }] }],
}),
}
);
Comment on lines 9 to 19
try {
// Server-side only API key (no NEXT_PUBLIC prefix)
const apiKey = process.env.GOOGLE_GENERATIVE_AI_API_KEY;
// Prefer user-provided key from header, fall back to server env var
const userKey = request.headers.get("x-gemini-api-key");
const apiKey = userKey?.trim() || process.env.GOOGLE_GENERATIVE_AI_API_KEY;

if (!apiKey) {
return NextResponse.json(
{ error: "Missing GOOGLE_GENERATIVE_AI_API_KEY" },
{ status: 500 }
{ error: "NO_API_KEY", message: "No Gemini API key configured. Please add your API key in the board settings." },
{ status: 401 }
);
}
Comment on lines +27 to +40
useEffect(() => {
try {
const stored = localStorage.getItem(STORAGE_KEY);
if (stored) setApiKeyState(stored);
} catch {}
setIsLoaded(true);
}, []);

const setApiKey = useCallback((key: string) => {
try {
localStorage.setItem(STORAGE_KEY, key);
} catch {}
setApiKeyState(key);
}, []);
Comment on lines +10 to +23
// Prefer user-provided key from header, fall back to server env var
const userKey = request.headers.get("x-gemini-api-key");
const apiKey = userKey?.trim() || process.env.GOOGLE_GENERATIVE_AI_API_KEY;

if (!apiKey) {
return NextResponse.json(
{ error: "Missing GOOGLE_GENERATIVE_AI_API_KEY" },
{ status: 500 }
{ error: "NO_API_KEY", message: "No Gemini API key configured. Please add your API key in the board settings." },
{ status: 401 }
);
}

// Inject the key into the environment for the @ai-sdk/google provider
process.env.GOOGLE_GENERATIVE_AI_API_KEY = apiKey;

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

1. Env key race leak 🐞 Bug ⛨ Security

POST /api/complete-diagram assigns process.env.GOOGLE_GENERATIVE_AI_API_KEY from a request
header, which is global and shared across concurrent requests. This can cause one user’s Gemini key
to be used for another user’s request and can persist beyond the request when the header is absent.
Agent Prompt
## Issue description
`frontend/app/api/complete-diagram/route.ts` sets `process.env.GOOGLE_GENERATIVE_AI_API_KEY` per request based on `x-gemini-api-key`. Because `process.env` is process-global, concurrent requests can race and later requests can unintentionally reuse a previous user's key.

## Issue Context
The code calls `generateText({ model: google("gemini-2.5-flash-image"), ... })`, and the provider reads credentials from environment/global config.

## Fix Focus Areas
- Prefer a per-request API key injection mechanism (provider option / client instance / explicit header) rather than global env.
- If the SDK truly only supports env-based configuration, do **not** use a shared mutable global; refactor to a direct HTTP call where the key can be set per request.

## Fix Focus Areas (references)
- frontend/app/api/complete-diagram/route.ts[8-23]
- frontend/app/api/complete-diagram/route.ts[79-83]

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

Comment thread frontend/proxy.ts
Comment on lines +1 to +21
import { clerkMiddleware, createRouteMatcher } from "@clerk/nextjs/server";

// Define which routes are public. All other routes will require authentication.
// / is public, /board is protected.
const isPublicRoute = createRouteMatcher(["/"]);

export default clerkMiddleware(async (auth, request) => {
if (!isPublicRoute(request)) {
// Protect all non-public routes
await auth.protect();
}
});

export const config = {
matcher: [
// Skip Next.js internals and all static files, unless found in search params
"/((?!_next|[^?]*\\.(?:html?|css|js(?!on)|jpe?g|webp|png|gif|svg|ttf|woff2?|ico|csv|docx?|xlsx?|zip|webmanifest)).*)",
// Always run for API routes
"/(api|trpc)(.*)",
],
};
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. Middleware file misnamed 🐞 Bug ⛨ Security

Clerk route protection is implemented in frontend/proxy.ts, but Next.js only executes middleware
from middleware.ts/middleware.js at the app root. As a result, /board and /api/* protections
may not run, leaving API routes unauthenticated.
Agent Prompt
## Issue description
The Clerk middleware is defined in `frontend/proxy.ts`, which Next.js will not automatically run as middleware. This likely disables intended auth protection for non-public routes and API endpoints.

## Issue Context
`BoardPage` explicitly references `middleware.ts` as the primary guard, but the repo contains `proxy.ts` instead.

## Fix Focus Areas
- Rename/move `frontend/proxy.ts` to `frontend/middleware.ts` (or the correct root per your Next.js layout).
- Ensure the matcher config remains intact and that the middleware actually runs for `/board` and `/api/*`.
- Update any comments/docs to reflect the real filename.

## Fix Focus Areas (references)
- frontend/proxy.ts[1-21]
- frontend/app/board/page.tsx[9-17]

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

Comment on lines +1 to +26
export const Sparkle = () => (
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 87 87"
fill="currentColor"
style="color:#0a0000;"
width="64"
height="64"
preserveAspectRatio="xMidYMid meet"
>
<mask id="path-1-inside-1_103_6790" fill="currentColor">
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M48.956 32.7515L43.007 0.632324L37.0728 32.6721L23.227 24.0134L31.7425 37.7602L0 43.6394L31.9899 49.5643L23.1425 63.712L37.1519 55.0337L43.007 86.6464L48.8587 55.0524L62.8411 63.7964L54.0248 49.5642L86.0141 43.6394L54.3703 37.7785L62.9256 24.0979L48.956 32.7515Z"
fill="#000000"
stroke="#000000"
/>
</mask>
<path
d="M43.007 0.632324L45.3499 0.198402H40.6642L43.007 0.632324ZM48.956 32.7515L46.6131 33.1854L47.2479 36.6123L50.2107 34.777L48.956 32.7515ZM37.0728 32.6721L35.8095 34.6922L38.778 36.5486L39.4156 33.106L37.0728 32.6721ZM23.227 24.0134L24.4903 21.9932L21.2015 25.2681L23.227 24.0134ZM31.7425 37.7602L32.1765 40.103L35.6034 39.4683L33.7681 36.5055L31.7425 37.7602ZM0 43.6394L-0.433923 41.2966L-0.433923 45.9822L0 43.6394ZM31.9899 49.5643L34.0101 50.8277L35.8665 47.8592L32.4238 47.2215L31.9899 49.5643ZM23.1425 63.712L21.1224 62.4486L24.3972 65.7375L23.1425 63.712ZM37.1519 55.0337L39.4947 54.5998L38.86 51.1729L35.8972 53.0082L37.1519 55.0337ZM43.007 86.6464L40.6642 87.0803H45.3499L43.007 86.6464ZM48.8587 55.0524L50.122 53.0322L47.1535 51.1758L46.5159 54.6185L48.8587 55.0524ZM62.8411 63.7964L61.5777 65.8166L64.8666 62.5417L62.8411 63.7964ZM54.0248 49.5642L53.5909 47.2214L50.164 47.8561L51.9993 50.819L54.0248 49.5642ZM86.0141 43.6394L86.448 45.9822V41.2966L86.0141 43.6394ZM54.3703 37.7785L52.3501 36.5151L50.4937 39.4837L53.9363 40.1213L54.3703 37.7785ZM62.9256 24.0979L64.9457 25.3612L61.6708 22.0724L62.9256 24.0979ZM40.6642 1.06625L46.6131 33.1854L51.2988 32.3175L45.3499 0.198402L40.6642 1.06625ZM39.4156 33.106L45.3499 1.06625L40.6642 0.198402L34.73 32.2381L39.4156 33.106ZM21.9637 26.0336L35.8095 34.6922L38.3362 30.6519L24.4903 21.9932L21.9637 26.0336ZM33.7681 36.5055L25.2525 22.7587L21.2015 25.2681L29.717 39.0149L33.7681 36.5055ZM0.433923 45.9822L32.1765 40.103L31.3086 35.4174L-0.433923 41.2966L0.433923 45.9822ZM32.4238 47.2215L0.433923 41.2966L-0.433923 45.9822L31.556 51.9072L32.4238 47.2215ZM25.1627 64.9753L34.0101 50.8277L29.9697 48.301L21.1224 62.4486L25.1627 64.9753ZM35.8972 53.0082L21.8878 61.6864L24.3972 65.7375L38.4067 57.0593L35.8972 53.0082ZM45.3499 86.2125L39.4947 54.5998L34.8091 55.4677L40.6642 87.0803L45.3499 86.2125ZM46.5159 54.6185L40.6642 86.2125L45.3499 87.0803L51.2015 55.4863L46.5159 54.6185ZM64.1044 61.7763L50.122 53.0322L47.5954 57.0726L61.5777 65.8166L64.1044 61.7763ZM51.9993 50.819L60.8155 65.0512L64.8666 62.5417L56.0504 48.3095L51.9993 50.819ZM85.5802 41.2966L53.5909 47.2214L54.4588 51.907L86.448 45.9822L85.5802 41.2966ZM53.9363 40.1213L85.5802 45.9822L86.448 41.2966L54.8042 35.4357L53.9363 40.1213ZM60.9054 22.8346L52.3501 36.5151L56.3904 39.0418L64.9457 25.3612L60.9054 22.8346ZM50.2107 34.777L64.1803 26.1234L61.6708 22.0724L47.7012 30.7259L50.2107 34.777Z"
fill="#000000"
mask="url(#path-1-inside-1_103_6790)"
stroke="#000000"
/>
</svg>
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

3. Invalid svg jsx props 🐞 Bug ≡ Correctness

frontend/components/landing/doodles.tsx uses invalid TSX/SVG props (e.g., style="...",
fill-rule, clip-rule) that do not type-check in React/TypeScript. This will break next build
type-checking even if the component is not imported.
Agent Prompt
## Issue description
`doodles.tsx` is TSX and is included in TypeScript checking. It uses invalid JSX attribute names (`fill-rule`, `clip-rule`) and assigns `style` as a string, which violates React/TS types.

## Issue Context
TypeScript checking in Next.js covers `**/*.ts` and `**/*.tsx` (per tsconfig include), so this can fail CI/build even if unused.

## Fix Focus Areas
- Convert SVG attributes to React’s camelCase equivalents (`fillRule`, `clipRule`, etc.).
- Replace `style="color:#0a0000;"` with `style={{ color: "#0a0000" }}`.
- If the component is not needed, delete the file instead.

## Fix Focus Areas (references)
- frontend/components/landing/doodles.tsx[1-26]

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

Comment thread frontend/app/globals.css
Comment on lines 35 to 59
--color-primary-foreground: var(--primary-foreground);
--color-ring: var(--ring);
--radius-lg: var(--radius);
--font-sans: var(--font-geist-sans), system-ui, sans-serif;
--color-chalk-ink: var(--chalk-ink);
--color-chalk-ink-soft: var(--chalk-ink-soft);
--color-chalk-gray: var(--chalk-gray);
--color-chalk-bg: var(--chalk-bg);
--color-chalk-white: var(--chalk-white);
--color-chalk-purple: var(--chalk-purple);
--color-chalk-purple-light: var(--chalk-purple-light);
--color-chalk-blue-soft: var(--chalk-blue-soft);
--color-chalk-border: var(--chalk-border);
--color-chalk-border-soft: var(--chalk-border-soft);

--font-sans: var(--font-body), system-ui, sans-serif;
--font-mono: var(--font-geist-mono), monospace;
--font-sketch: var(--font-sketch);

--animate-marquee: marquee 28s linear infinite;

@keyframes marquee {
from { transform: translateX(0); }
to { transform: translateX(-50%); }
}
}
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. Broken theme css tokens 🐞 Bug ≡ Correctness

globals.css defines @keyframes inside @theme inline and maps theme variables to undefined
custom properties like --muted/--primary, making utilities such as
bg-primary/text-muted-foreground resolve to invalid values. This can break global styling and
animations across the app.
Agent Prompt
## Issue description
In `globals.css`, the Tailwind `@theme inline` block contains a nested `@keyframes` rule and references CSS variables (`--muted`, `--primary`, etc.) that are not defined anywhere. This can break global token resolution and utility styles.

## Issue Context
Many components use classes like `bg-primary`, `text-muted-foreground`, etc. With `--primary`/`--muted` undefined, the mapped `--color-*` variables become invalid.

## Fix Focus Areas
- Move `@keyframes marquee` to the top level (outside `@theme`).
- Define the missing base tokens in `:root` (e.g., `--muted`, `--primary`, `--border`, `--ring`, `--primary-foreground`, etc.), or remove the mappings if you no longer use those utilities.

## Fix Focus Areas (references)
- frontend/app/globals.css[5-25]
- frontend/app/globals.css[27-59]

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

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.

2 participants