Skip to content

feat: Dashboard UI, login redesign, and landing page - #68

Merged
HrushiBorhade merged 6 commits into
mainfrom
step-7/dashboard-ui
Mar 23, 2026
Merged

HrushiBorhade merged 6 commits into
mainfrom
step-7/dashboard-ui

Conversation

@HrushiBorhade

@HrushiBorhade HrushiBorhade commented Mar 23, 2026 •

Copy link
Copy Markdown
Owner

Summary

Complete Phase 1 frontend — dashboard with sidebar navigation, login page redesign, and landing page.

Dashboard (Commit 1)

  • Sidebar layout with app branding, navigation (Dashboard, Search, Settings), user dropdown
  • Repo cards grid with status badges (pending/indexing/ready/error), branch, file/chunk counts, last indexed
  • Empty state with GitHub App install CTA
  • Server component fetches repos via DAL + DB singleton
  • shadcn components: sidebar, dropdown-menu, tooltip, dialog, progress, chart, tabs, sheet, input, scroll-area

Login Redesign (Commit 2)

  • Split layout: terminal demo panel (left) + sign-in form (right)
  • Terminal panel shows fake search results showcasing the product
  • Feature badges: Semantic Search, AST Chunking, Hybrid Search
  • Responsive: single column on mobile, split on desktop

Landing Page (Commit 3)

  • Hero: "Search your codebase by meaning" + CTA buttons
  • Terminal preview with animated cursor showing search results
  • 4-card features grid: Semantic Search, AST Chunking, Hybrid Search, Incremental Sync
  • Nav bar, footer, all Phosphor icons

Fixes (Commit 4)

  • Fixed sidebar hydration mismatch (client component)
  • Added page metadata for SEO

Test plan

  • pnpm format:check passes
  • pnpm typecheck passes (web + db)
  • pnpm lint passes
  • 17 tests pass
  • react-doctor: 97/100
  • Landing page renders (Playwright verified)
  • Login page renders with split layout (Playwright verified)
  • Dashboard redirects to login when unauthenticated (proxy.ts)

Closes #60

Summary by CodeRabbit

  • New Features

    • Dashboard: repository list with status, metadata, and empty-state CTA
    • New sidebar, user menu (with sign-out), and repo cards
    • Charting and motion components for analytics and animated hero/feature UI
    • New UI primitives: dialogs, dropdowns, tooltips, tabs, sheets, progress, scroll areas, inputs, skeletons
  • UI/UX Improvements

    • Redesigned landing and login pages with hero, features, and CTAs
    • Responsive behavior and mobile-aware navigation; theme toggle placement adjusted
  • Chores

    • Added runtime deps for motion and charting libraries

- Sidebar with navigation, app branding, and user dropdown (sign-out, profile, settings)
- Dashboard page fetches repos via DAL + DB singleton, renders repo cards grid
- Repo cards show status badge, branch, file/chunk counts, last indexed time
- Empty state with GitHub App install CTA when no repos
- Status badges: pending (yellow), indexing (blue), ready (green), error (red)
- Added shadcn components: sidebar, dropdown-menu, tooltip, dialog, progress, chart, tabs, sheet, input, scroll-area
- TooltipProvider added to root providers
- Theme toggle moved from fixed position to dashboard header
- Split layout: terminal demo panel (left) + sign-in form (right)
- Terminal panel shows fake search results to showcase the product
- Feature badges: Semantic Search, AST Chunking, Hybrid Search
- Responsive: single column on mobile, split on desktop
- Consistent with design system (monospace, neutral, dark mode)
- Hero section: "Search your codebase by meaning" with CTA buttons
- Terminal preview showing fake search results with blinking cursor
- 4-card features grid: Semantic Search, AST Chunking, Hybrid Search, Incremental Sync
- Consistent nav bar with CodeIndexer branding, Sign in + Get Started
- Footer with GitHub link
- All Phosphor icons, shadcn components, design system tokens
- Made app-sidebar a client component to fix Link + asChild hydration mismatch
- Added metadata export to landing page for SEO
@vercel

vercel Bot commented Mar 23, 2026 •

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
code-indexer-web Ready Ready Preview, Comment Mar 23, 2026 1:18pm

@greptile-apps greptile-apps Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Your free trial has ended. If you'd like to continue receiving code reviews, you can add a payment method here.

@coderabbitai

coderabbitai Bot commented Mar 23, 2026 •

Copy link
Copy Markdown

Warning

Rate limit exceeded

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

⌛ 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: f0b94492-6779-424b-87cc-6018b8689573

📥 Commits

Reviewing files that changed from the base of the PR and between 5cedaa4 and 176b13c.

📒 Files selected for processing (1)
  • apps/web/src/app/api/webhooks/github/route.ts
📝 Walkthrough

Walkthrough

Adds a dashboard layout and page that fetches user session and repositories, new dashboard UI components (sidebar, repo cards, empty state), numerous UI primitives (sidebar, chart, dialog, dropdown, sheet, tabs, tooltip, input, progress, scroll-area), landing/login redesigns, and package dependency additions for motion and recharts.

Changes

Cohort / File(s) Summary
Package changes
apps/web/package.json
Added runtime dependencies: motion@^12.38.0, recharts@^3.8.0.
Dashboard Layout & Page
apps/web/src/app/dashboard/layout.tsx, apps/web/src/app/dashboard/page.tsx
New server-side DashboardLayout using getSession(); DashboardPage queries DB for user repos and renders conditional EmptyState or responsive RepoCard grid.
Root & Providers
apps/web/src/app/layout.tsx, apps/web/src/components/providers.tsx, apps/web/src/components/theme-toggle.tsx
Removed ThemeToggle from root layout; Providers now wrap content with TooltipProvider; ThemeToggle class adjusted (fixed → size-8).
Landing & Login
apps/web/src/app/page.tsx, apps/web/src/app/login/page.tsx
Home page replaced with marketing hero, features, terminal preview and metadata export; login page converted to two-column layout with branding panel and updated sign-in flow/UI.
Dashboard components
apps/web/src/components/dashboard/app-sidebar.tsx, .../nav-user.tsx, .../repo-card.tsx, .../repo-status-badge.tsx, .../empty-state.tsx
New AppSidebar, NavUser (sign-out via BroadcastChannel), RepoCard (relative time formatter), RepoStatusBadge (status→style mapping), and EmptyState with GitHub App install CTA.
Sidebar system
apps/web/src/components/ui/sidebar.tsx
Large new sidebar suite: SidebarProvider, responsive/collapsible variants, cookie persistence, Ctrl/Cmd+B shortcut, mobile sheet integration, and many subcomponents (menu, triggers, groups, badges, skeletons).
Charts (recharts)
apps/web/src/components/ui/chart.tsx
New chart wrapper using recharts: ChartContainer with context/config, ChartStyle, ChartTooltipContent and ChartLegendContent, helpers for payload/config resolution and themed CSS variables.
Radix-based UI primitives
apps/web/src/components/ui/dialog.tsx, .../dropdown-menu.tsx, .../sheet.tsx
New dialog, dropdown-menu, and sheet component suites that wrap Radix primitives with Tailwind styling and layout helpers (headers, footers, close buttons, submenus).
Other UI primitives
apps/web/src/components/ui/tabs.tsx, .../tooltip.tsx, .../input.tsx, .../progress.tsx, .../scroll-area.tsx, .../skeleton.tsx
Added Tabs (CVA variants), Tooltip provider/content, basic Input, Progress indicator, ScrollArea/ScrollBar, and removed unused React import in Skeleton.
Motion & hooks
apps/web/src/components/landing/hero-animations.tsx, apps/web/src/components/motion/fade-in.tsx, apps/web/src/hooks/use-mobile.ts
New Framer Motion-based hero animation components and FadeIn; added useIsMobile hook using matchMedia.

Sequence Diagram

sequenceDiagram
    actor User
    participant Browser
    participant Server as Next.js Server
    participant Auth as getSession()
    participant DB as Database
    participant ClientUI as Client Components

    User->>Browser: Request /dashboard
    Browser->>Server: Server-side render DashboardLayout
    Server->>Auth: getSession()
    Auth-->>Server: session (user)
    Server->>ClientUI: Render AppSidebar with user
    Server->>Server: Render DashboardPage (server)
    Server->>DB: query repos where userId = session.user.id
    DB-->>Server: repos[]
    Server->>ClientUI: Render RepoCard grid or EmptyState
    User->>ClientUI: Click "Add Repository" or Sign Out
    alt Add Repository
        ClientUI->>Browser: Open GitHub App install URL
    else Sign Out
        ClientUI->>Auth: authClient.signOut()
        ClientUI->>Browser: BroadcastChannel message + navigate to /login
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related PRs

Poem

🐇 I hop through code with eager paws,

New sidebars, charts, and repo laws.
Cards align in tidy rows,
Empty states where springtime grows.
A rabbit cheers — the dashboard glows!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 1.14% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main changes: dashboard UI, login redesign, and landing page enhancements are all primary modifications in this changeset.
Linked Issues check ✅ Passed The PR fulfills all core requirements from #60: DB singleton, dashboard with repo list, status badges, empty state, sign-out functionality, authentication redirect, and GitHub App install CTA are all implemented.
Out of Scope Changes check ✅ Passed All file changes align with stated objectives. Dashboard components, login redesign, landing page updates, and UI library additions directly support the Phase 1 frontend completion goals.

✏️ 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 step-7/dashboard-ui

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 8

🧹 Nitpick comments (7)
apps/web/src/app/login/page.tsx (1)

18-20: Consider surfacing sign-in failure feedback to the user.

Right now failure only resets loading; adding a toast/message would avoid a silent no-op experience.

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

In `@apps/web/src/app/login/page.tsx` around lines 18 - 20, The catch block that
currently only calls setLoading(false) should surface sign-in failure to the
user: modify the catch to accept the error (catch (error)), keep
setLoading(false), and then show a user-facing message (e.g., call your
toast/error UI or set an error state) with a friendly message and optional error
details; locate the async sign-in handler where setLoading is used (the function
containing the shown catch) and add a toast/error display (e.g., toast.error or
setError) so failures are not silent.
apps/web/src/components/ui/chart.tsx (1)

80-107: Note: dangerouslySetInnerHTML usage for CSS injection.

The static analysis tool flagged this as a potential XSS risk. While this is a common pattern for injecting dynamic CSS variables, ensure that config values (particularly color strings) are never derived from untrusted user input. Malicious color values like </style><script>alert(1)</script> could escape the style block.

The current implementation is acceptable since ChartConfig is typically defined by developers, but consider adding a simple sanitization function if config values might come from external sources in the future:

🛡️ Optional: Add color value sanitization
+const sanitizeColor = (color: string) => color.replace(/[<>"'&]/g, '');
+
 const ChartStyle = ({ id, config }: { id: string; config: ChartConfig }) => {
   const colorConfig = Object.entries(config).filter(([, config]) => config.theme ?? config.color);
 
   if (!colorConfig.length) {
     return null;
   }
 
   return (
     <style
       dangerouslySetInnerHTML={{
         __html: Object.entries(THEMES)
           .map(
             ([theme, prefix]) => `
 ${prefix} [data-chart=${id}] {
 ${colorConfig
   .map(([key, itemConfig]) => {
     const color = itemConfig.theme?.[theme as keyof typeof itemConfig.theme] ?? itemConfig.color;
-    return color ? `  --color-${key}: ${color};` : null;
+    return color ? `  --color-${key}: ${sanitizeColor(color)};` : null;
   })
   .join('\n')}
 }
 `,
           )
           .join('\n'),
       }}
     />
   );
 };
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/src/components/ui/chart.tsx` around lines 80 - 107, The ChartStyle
component uses dangerouslySetInnerHTML to inject CSS from config values
(ChartConfig and THEMES) which can be an XSS risk; implement a sanitizer (e.g.,
sanitizeColor or validateColor) and use it when deriving color in the map inside
ChartStyle (replace usages of itemConfig.theme?.[...] ?? itemConfig.color) so
only safe color tokens are emitted (allow only hex, rgba(), hsla(), CSS variable
names, or a whitelist) and fall back to null/empty if validation fails before
building the innerHTML.
apps/web/src/components/dashboard/repo-card.tsx (1)

38-38: Implicit assumption on fullName format.

The destructuring assumes fullName follows the owner/repo format. This is correct for GitHub repositories, but if fullName ever contains additional slashes (e.g., nested paths) or no slash, the parsing would produce unexpected results.

This is acceptable for the current GitHub-specific use case, but consider documenting this expectation in the type definition or adding a comment.

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

In `@apps/web/src/components/dashboard/repo-card.tsx` at line 38, The code in
repo-card.tsx assumes fullName is "owner/repo" via the line const [owner, name]
= fullName.split('/'); — make this explicit: either validate/safely parse
fullName (e.g., check for a single slash and fallback or throw a clear error) or
document the invariant on the prop/type that fullName must be "owner/repo" (add
a comment and update the related prop/type definition). Reference the fullName
variable and the destructuring expression so the change is applied where const
[owner, name] = fullName.split('/') is defined.
apps/web/src/app/dashboard/page.tsx (1)

10-10: Duplicate constant with empty-state.tsx.

As noted earlier, this URL is duplicated. Consider a shared constants file.

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

In `@apps/web/src/app/dashboard/page.tsx` at line 10, The constant
GITHUB_APP_INSTALL_URL is duplicated; extract it into a shared constant and
update usages to import that single source of truth. Create a new exported
constant (e.g., GITHUB_APP_INSTALL_URL) in a shared module (constants or urls)
and replace the inline declaration in the dashboard's GITHUB_APP_INSTALL_URL and
the one in empty-state (or any other duplicates) with imports from that module
so both consume the same value.
apps/web/src/components/dashboard/nav-user.tsx (1)

75-82: Profile and Settings menu items are non-functional.

These DropdownMenuItem components have no onClick handlers or navigation. Consider adding navigation to the respective pages or removing them until implemented.

♻️ Option: Add navigation or disable until ready
-            <DropdownMenuItem>
+            <DropdownMenuItem asChild>
+              <Link href="/profile">
               <User className="mr-2 size-4" />
               Profile
+              </Link>
             </DropdownMenuItem>
-            <DropdownMenuItem>
+            <DropdownMenuItem asChild>
+              <Link href="/settings">
               <Gear className="mr-2 size-4" />
               Settings
+              </Link>
             </DropdownMenuItem>

Note: This requires importing Link from next/link.

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

In `@apps/web/src/components/dashboard/nav-user.tsx` around lines 75 - 82, The
Profile and Settings menu items in nav-user.tsx are non-functional because the
DropdownMenuItem elements (containing User and Gear icons) have no navigation or
click handlers; fix by wiring them to the proper routes or disabling them:
either import Link from next/link and wrap each DropdownMenuItem (or use the
menu component's asChild pattern) to point to the intended routes (e.g.,
"/profile" and "/settings"), or use Next's useRouter and add onClick handlers
that call router.push("/profile") and router.push("/settings"); alternatively,
if those pages aren’t ready, add a disabled prop to the DropdownMenuItem to
prevent interaction until implemented.
apps/web/src/components/ui/sidebar.tsx (1)

80-81: Cookie missing SameSite attribute.

The sidebar state cookie is set without a SameSite attribute. While browsers default to Lax, explicitly setting it improves clarity and cross-browser consistency.

♻️ Proposed fix to add SameSite
-      document.cookie = `${SIDEBAR_COOKIE_NAME}=${openState}; path=/; max-age=${SIDEBAR_COOKIE_MAX_AGE}`;
+      document.cookie = `${SIDEBAR_COOKIE_NAME}=${openState}; path=/; max-age=${SIDEBAR_COOKIE_MAX_AGE}; SameSite=Lax`;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/src/components/ui/sidebar.tsx` around lines 80 - 81, The cookie
write for the sidebar state (where document.cookie is set using
SIDEBAR_COOKIE_NAME, openState and SIDEBAR_COOKIE_MAX_AGE in sidebar.tsx) should
include an explicit SameSite attribute for clarity and cross-browser
consistency; update the document.cookie assignment that uses
SIDEBAR_COOKIE_NAME, openState and SIDEBAR_COOKIE_MAX_AGE to append an explicit
`SameSite=Lax` (or `SameSite=Strict` if you want stricter behavior) along with
the existing path and max-age attributes.
apps/web/src/components/dashboard/empty-state.tsx (1)

5-5: Duplicated GitHub App install URL constant.

This URL is also defined in apps/web/src/app/dashboard/page.tsx (line 10). Consider extracting it to a shared constants file (e.g., @/lib/constants.ts) to avoid duplication and ensure consistency if the URL changes.

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

In `@apps/web/src/components/dashboard/empty-state.tsx` at line 5, The
GITHUB_APP_INSTALL_URL constant is duplicated; extract it into a shared
constants module (e.g., create and export from "@/lib/constants.ts" as
GITHUB_APP_INSTALL_URL) and update both usages to import that exported constant
(replace the local GITHUB_APP_INSTALL_URL in empty-state.tsx and the other file
to import from the shared module); ensure the constant is exported (export const
GITHUB_APP_INSTALL_URL =
'https://github.com/apps/codeindexer-dev/installations/new') and update any
references to use the imported symbol so there is a single 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 `@apps/web/src/components/dashboard/app-sidebar.tsx`:
- Around line 19-23: The navItems array contains entries for non-existent routes
('/search' and '/settings') which will produce 404s; either remove or comment
out the offending entries in navItems (the objects with title 'Search' and
'Settings' referencing MagnifyingGlass and Gear) or implement corresponding
route files under app/ to match those hrefs; if you remove them, ensure any
references to those icons (MagnifyingGlass, Gear) are also removed to avoid
unused-import warnings, otherwise add pages at /search and /settings that export
the expected React components so the links resolve.

In `@apps/web/src/components/dashboard/nav-user.tsx`:
- Around line 34-36: In nav-user.tsx the BroadcastChannel sends { type:
'signed-out' } which doesn't match the listener expecting { type: 'n' }; update
the bc.postMessage call in the nav user sign-out flow to send { type: 'n' }
(keep bc initialization and bc.close() as-is) so it matches the message format
used by use-session-sync and sign-out-button and other tabs will recognize the
logout.
- Around line 43-48: The initials computation using user.name can break when
user.name is empty; update the logic that defines initials to first trim and
guard the name (e.g., const name = (user.name || '').trim()), split on
whitespace, filter out empty segments, then map to first characters, join,
toUpperCase and slice(0,2); if that results in an empty string, fall back to a
safe default such as the first two chars of user.email (or a constant like '?')
so the initials variable always yields a valid 1–2 character string.

In `@apps/web/src/components/ui/progress.tsx`:
- Line 25: Clamp the progress value before using it in the inline transform to
avoid out-of-range translations: compute a clampedValue (e.g. const clampedValue
= Math.max(0, Math.min(100, value || 0))) inside the Progress component and
replace the style usage style={{ transform: `translateX(-${100 - (value ||
0)}%)` }} with style={{ transform: `translateX(-${100 - clampedValue}%)` }} so
the indicator never moves beyond 0–100%.

In `@apps/web/src/components/ui/scroll-area.tsx`:
- Line 42: The Tailwind variants in the scroll-area component's class string
currently use boolean-style variants like "data-horizontal:..." and
"data-vertical:..." which don't match the component's
data-orientation="horizontal|vertical"; update those to attribute-value variants
"data-[orientation=horizontal]:..." and "data-[orientation=vertical]:..." in the
same class entry so the rules (e.g., h-2.5, flex-col, border-t, border-l, etc.)
apply correctly when the element's data-orientation equals "horizontal" or
"vertical".

In `@apps/web/src/components/ui/sidebar.tsx`:
- Around line 567-570: The random width initializer for the width state (const
[width] = React.useState(() => ...)) causes server/client hydration mismatches;
change to a deterministic SSR-safe initial value (e.g., null or a fixed
percentage) and set the random width only on the client inside a useEffect (use
React.useState for width and React.useEffect to call Math.random and update
width on mount), ensuring you reference and update the same width state variable
and remove direct Math.random usage from the initial React.useState initializer.

In `@apps/web/src/components/ui/tabs.tsx`:
- Around line 9-22: The Tabs component destructures orientation but doesn't
forward it to TabsPrimitive.Root, breaking ARIA/keyboard behavior; update the
Tabs function to pass orientation={orientation} into TabsPrimitive.Root (in
addition to data-orientation) so Radix receives the prop and keyboard
navigation/ARIA are correct; keep the existing spread {...props} and other
attributes (className, data-slot) unchanged and ensure the prop type remains
React.ComponentProps<typeof TabsPrimitive.Root>.
- Around line 54-68: The Tailwind selectors in TabsTrigger's className are using
non-existent Radix attributes; update the selectors in the TabsTrigger component
(where TabsPrimitive.Trigger is rendered) to use Radix's emitted attributes:
replace all occurrences of `data-active:` with `data-[state=active]:`,
`data-horizontal:` with `data-[orientation=horizontal]:`, and `data-vertical:`
with `data-[orientation=vertical]:` (also update any grouped selectors like
`group-data-horizontal/tabs:` and `group-data-vertical/tabs:` accordingly) so
state- and orientation-based styles match Radix Tabs' `data-state` and
`data-orientation` attributes.

---

Nitpick comments:
In `@apps/web/src/app/dashboard/page.tsx`:
- Line 10: The constant GITHUB_APP_INSTALL_URL is duplicated; extract it into a
shared constant and update usages to import that single source of truth. Create
a new exported constant (e.g., GITHUB_APP_INSTALL_URL) in a shared module
(constants or urls) and replace the inline declaration in the dashboard's
GITHUB_APP_INSTALL_URL and the one in empty-state (or any other duplicates) with
imports from that module so both consume the same value.

In `@apps/web/src/app/login/page.tsx`:
- Around line 18-20: The catch block that currently only calls setLoading(false)
should surface sign-in failure to the user: modify the catch to accept the error
(catch (error)), keep setLoading(false), and then show a user-facing message
(e.g., call your toast/error UI or set an error state) with a friendly message
and optional error details; locate the async sign-in handler where setLoading is
used (the function containing the shown catch) and add a toast/error display
(e.g., toast.error or setError) so failures are not silent.

In `@apps/web/src/components/dashboard/empty-state.tsx`:
- Line 5: The GITHUB_APP_INSTALL_URL constant is duplicated; extract it into a
shared constants module (e.g., create and export from "@/lib/constants.ts" as
GITHUB_APP_INSTALL_URL) and update both usages to import that exported constant
(replace the local GITHUB_APP_INSTALL_URL in empty-state.tsx and the other file
to import from the shared module); ensure the constant is exported (export const
GITHUB_APP_INSTALL_URL =
'https://github.com/apps/codeindexer-dev/installations/new') and update any
references to use the imported symbol so there is a single source of truth.

In `@apps/web/src/components/dashboard/nav-user.tsx`:
- Around line 75-82: The Profile and Settings menu items in nav-user.tsx are
non-functional because the DropdownMenuItem elements (containing User and Gear
icons) have no navigation or click handlers; fix by wiring them to the proper
routes or disabling them: either import Link from next/link and wrap each
DropdownMenuItem (or use the menu component's asChild pattern) to point to the
intended routes (e.g., "/profile" and "/settings"), or use Next's useRouter and
add onClick handlers that call router.push("/profile") and
router.push("/settings"); alternatively, if those pages aren’t ready, add a
disabled prop to the DropdownMenuItem to prevent interaction until implemented.

In `@apps/web/src/components/dashboard/repo-card.tsx`:
- Line 38: The code in repo-card.tsx assumes fullName is "owner/repo" via the
line const [owner, name] = fullName.split('/'); — make this explicit: either
validate/safely parse fullName (e.g., check for a single slash and fallback or
throw a clear error) or document the invariant on the prop/type that fullName
must be "owner/repo" (add a comment and update the related prop/type
definition). Reference the fullName variable and the destructuring expression so
the change is applied where const [owner, name] = fullName.split('/') is
defined.

In `@apps/web/src/components/ui/chart.tsx`:
- Around line 80-107: The ChartStyle component uses dangerouslySetInnerHTML to
inject CSS from config values (ChartConfig and THEMES) which can be an XSS risk;
implement a sanitizer (e.g., sanitizeColor or validateColor) and use it when
deriving color in the map inside ChartStyle (replace usages of
itemConfig.theme?.[...] ?? itemConfig.color) so only safe color tokens are
emitted (allow only hex, rgba(), hsla(), CSS variable names, or a whitelist) and
fall back to null/empty if validation fails before building the innerHTML.

In `@apps/web/src/components/ui/sidebar.tsx`:
- Around line 80-81: The cookie write for the sidebar state (where
document.cookie is set using SIDEBAR_COOKIE_NAME, openState and
SIDEBAR_COOKIE_MAX_AGE in sidebar.tsx) should include an explicit SameSite
attribute for clarity and cross-browser consistency; update the document.cookie
assignment that uses SIDEBAR_COOKIE_NAME, openState and SIDEBAR_COOKIE_MAX_AGE
to append an explicit `SameSite=Lax` (or `SameSite=Strict` if you want stricter
behavior) along with the existing path and max-age attributes.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: cd6dad84-3c40-4922-9ea0-7d01f6898281

📥 Commits

Reviewing files that changed from the base of the PR and between 058fd41 and cbd5057.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (25)
  • apps/web/package.json
  • apps/web/src/app/dashboard/layout.tsx
  • apps/web/src/app/dashboard/page.tsx
  • apps/web/src/app/layout.tsx
  • apps/web/src/app/login/page.tsx
  • apps/web/src/app/page.tsx
  • apps/web/src/components/dashboard/app-sidebar.tsx
  • apps/web/src/components/dashboard/empty-state.tsx
  • apps/web/src/components/dashboard/nav-user.tsx
  • apps/web/src/components/dashboard/repo-card.tsx
  • apps/web/src/components/dashboard/repo-status-badge.tsx
  • apps/web/src/components/providers.tsx
  • apps/web/src/components/theme-toggle.tsx
  • apps/web/src/components/ui/chart.tsx
  • apps/web/src/components/ui/dialog.tsx
  • apps/web/src/components/ui/dropdown-menu.tsx
  • apps/web/src/components/ui/input.tsx
  • apps/web/src/components/ui/progress.tsx
  • apps/web/src/components/ui/scroll-area.tsx
  • apps/web/src/components/ui/sheet.tsx
  • apps/web/src/components/ui/sidebar.tsx
  • apps/web/src/components/ui/skeleton.tsx
  • apps/web/src/components/ui/tabs.tsx
  • apps/web/src/components/ui/tooltip.tsx
  • apps/web/src/hooks/use-mobile.ts
💤 Files with no reviewable changes (1)
  • apps/web/src/components/ui/skeleton.tsx

Comment on lines +19 to +23
const navItems = [
{ title: 'Dashboard', href: '/dashboard', icon: House },
{ title: 'Search', href: '/search', icon: MagnifyingGlass },
{ title: 'Settings', href: '/settings', icon: Gear },
];

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check if search and settings routes exist
fd -t d "search|settings" apps/web/src/app/

# Also check for any page files
fd "page.tsx" apps/web/src/app/ --exec grep -l "search\|settings" {} \; 2>/dev/null || echo "Checking app directory structure..."
tree apps/web/src/app/ -I node_modules 2>/dev/null || ls -la apps/web/src/app/

Repository: HrushiBorhade/code-indexer

Length of output: 503


Routes /search and /settings do not exist and will cause 404 errors.

The sidebar navigation links to /search and /settings (lines 20-21), but these routes are not implemented in apps/web/src/app/. Either implement these routes or remove/disable the navigation items until they are ready.

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

In `@apps/web/src/components/dashboard/app-sidebar.tsx` around lines 19 - 23, The
navItems array contains entries for non-existent routes ('/search' and
'/settings') which will produce 404s; either remove or comment out the offending
entries in navItems (the objects with title 'Search' and 'Settings' referencing
MagnifyingGlass and Gear) or implement corresponding route files under app/ to
match those hrefs; if you remove them, ensure any references to those icons
(MagnifyingGlass, Gear) are also removed to avoid unused-import warnings,
otherwise add pages at /search and /settings that export the expected React
components so the links resolve.

Comment on lines +34 to +36
const bc = new BroadcastChannel('codeindexer-session');
bc.postMessage({ type: 'signed-out' });
bc.close();

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

BroadcastChannel message type mismatch — other tabs won't recognize this logout.

The message { type: 'signed-out' } doesn't match the expected format. From apps/web/src/hooks/use-session-sync.ts, the listener expects { type: 'n' }, and apps/web/src/components/sign-out-button.tsx sends { type: 'n' }. This inconsistency means sign-out from the sidebar won't trigger logout in other tabs.

🐛 Proposed fix to use consistent message type
       await authClient.signOut();
       const bc = new BroadcastChannel('codeindexer-session');
-      bc.postMessage({ type: 'signed-out' });
+      bc.postMessage({ type: 'n' });
       bc.close();
📝 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
const bc = new BroadcastChannel('codeindexer-session');
bc.postMessage({ type: 'signed-out' });
bc.close();
const bc = new BroadcastChannel('codeindexer-session');
bc.postMessage({ type: 'n' });
bc.close();
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/src/components/dashboard/nav-user.tsx` around lines 34 - 36, In
nav-user.tsx the BroadcastChannel sends { type: 'signed-out' } which doesn't
match the listener expecting { type: 'n' }; update the bc.postMessage call in
the nav user sign-out flow to send { type: 'n' } (keep bc initialization and
bc.close() as-is) so it matches the message format used by use-session-sync and
sign-out-button and other tabs will recognize the logout.

Comment on lines +43 to +48
const initials = user.name
.split(' ')
.map((n) => n[0])
.join('')
.toUpperCase()
.slice(0, 2);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Handle edge case where user.name is empty.

If user.name is an empty string, split(' ') returns [''], and n[0] would be undefined, causing initials to be empty or potentially throwing on .toUpperCase().

🛡️ Proposed fix with fallback
   const initials = user.name
     .split(' ')
-    .map((n) => n[0])
+    .map((n) => n[0] ?? '')
+    .filter(Boolean)
     .join('')
     .toUpperCase()
-    .slice(0, 2);
+    .slice(0, 2) || '?';
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/src/components/dashboard/nav-user.tsx` around lines 43 - 48, The
initials computation using user.name can break when user.name is empty; update
the logic that defines initials to first trim and guard the name (e.g., const
name = (user.name || '').trim()), split on whitespace, filter out empty
segments, then map to first characters, join, toUpperCase and slice(0,2); if
that results in an empty string, fall back to a safe default such as the first
two chars of user.email (or a constant like '?') so the initials variable always
yields a valid 1–2 character string.

<ProgressPrimitive.Indicator
data-slot="progress-indicator"
className="size-full flex-1 bg-primary transition-all"
style={{ transform: `translateX(-${100 - (value || 0)}%)` }}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Clamp progress value to prevent invalid indicator transforms.

Line 25 uses value directly; out-of-range values can push the indicator beyond expected bounds.

💡 Proposed fix
 function Progress({
   className,
   value,
   ...props
 }: React.ComponentProps<typeof ProgressPrimitive.Root>) {
+  const safeValue = Math.min(100, Math.max(0, value ?? 0));
+
   return (
@@
       <ProgressPrimitive.Indicator
         data-slot="progress-indicator"
         className="size-full flex-1 bg-primary transition-all"
-        style={{ transform: `translateX(-${100 - (value || 0)}%)` }}
+        style={{ transform: `translateX(-${100 - safeValue}%)` }}
       />
📝 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
style={{ transform: `translateX(-${100 - (value || 0)}%)` }}
function Progress({
className,
value,
...props
}: React.ComponentProps<typeof ProgressPrimitive.Root>) {
const safeValue = Math.min(100, Math.max(0, value ?? 0));
return (
<ProgressPrimitive.Root
className={cn('relative h-4 w-full overflow-hidden rounded-full bg-secondary', className)}
{...props}
>
<ProgressPrimitive.Indicator
data-slot="progress-indicator"
className="size-full flex-1 bg-primary transition-all"
style={{ transform: `translateX(-${100 - safeValue}%)` }}
/>
</ProgressPrimitive.Root>
);
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/src/components/ui/progress.tsx` at line 25, Clamp the progress value
before using it in the inline transform to avoid out-of-range translations:
compute a clampedValue (e.g. const clampedValue = Math.max(0, Math.min(100,
value || 0))) inside the Progress component and replace the style usage style={{
transform: `translateX(-${100 - (value || 0)}%)` }} with style={{ transform:
`translateX(-${100 - clampedValue}%)` }} so the indicator never moves beyond
0–100%.

data-orientation={orientation}
orientation={orientation}
className={cn(
'flex touch-none p-px transition-colors select-none data-horizontal:h-2.5 data-horizontal:flex-col data-horizontal:border-t data-horizontal:border-t-transparent data-vertical:h-full data-vertical:w-2.5 data-vertical:border-l data-vertical:border-l-transparent',

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

In Tailwind CSS v4, for an element with data-orientation="horizontal" or "vertical", what is the correct variant syntax? Is data-horizontal:h-2.5valid, or should it bedata-[orientation=horizontal]:h-2.5?

💡 Result:

In Tailwind CSS v4, the correct variant syntax for an element with data-orientation="horizontal" or data-orientation="vertical" is data-[orientation=horizontal]:h-2.5 or data-[orientation=vertical]:h-2.5. The syntax data-horizontal:h-2.5 is not valid for matching a specific value of a data attribute; it would only match the presence of a data-horizontal attribute (boolean-like, e.g., data-horizontal without a value). This is confirmed by multiple sources: - Official Tailwind CSS v4 announcement shows boolean data attributes use bare syntax like data-current:opacity-100 for data-current (no value), but for valued attributes, the bracketed arbitrary syntax is required. - shadcn/ui Separator component explicitly uses data-[orientation=horizontal]: and data-[orientation=vertical]: in Tailwind v4 to fix rendering issues, replacing custom data-horizontal: variants. - GitHub issues and discussions consistently use data-[attr=value]: for data attributes with specific values in v3 and v4. - Stack Overflow and Tailwind GitHub confirm data-[size=large]:p-8 syntax. data-horizontal: requires defining a @custom-variant or relies on shadcn-specific imports, which fail without them in plain Tailwind v4, treating it as boolean check that doesn't match data-orientation="horizontal". Use data-[orientation=horizontal]:h-2.5 on the child element when the parent has data-orientation="horizontal". If targeting ancestors, use in-data-[orientation=horizontal]:h-2.5.

Citations:


🏁 Script executed:

#!/bin/bash
# Check if the file exists and view the relevant lines around line 42
if [ -f "apps/web/src/components/ui/scroll-area.tsx" ]; then
  echo "=== File exists ===" 
  wc -l apps/web/src/components/ui/scroll-area.tsx
  echo -e "\n=== Content around line 42 (lines 38-50) ==="
  sed -n '38,50p' apps/web/src/components/ui/scroll-area.tsx
else
  echo "File not found"
  find . -name "scroll-area.tsx" -type f 2>/dev/null | head -20
fi

Repository: HrushiBorhade/code-indexer

Length of output: 781


Fix Tailwind variant syntax for data-orientation attribute.

Line 42 uses data-horizontal:* and data-vertical:*, but the component provides data-orientation="horizontal|vertical". These variants are invalid in Tailwind CSS v4 for matching specific attribute values—they only match a boolean data-horizontal attribute. The correct syntax is data-[orientation=horizontal]:* and data-[orientation=vertical]:*.

💡 Proposed fix
       className={cn(
-        'flex touch-none p-px transition-colors select-none data-horizontal:h-2.5 data-horizontal:flex-col data-horizontal:border-t data-horizontal:border-t-transparent data-vertical:h-full data-vertical:w-2.5 data-vertical:border-l data-vertical:border-l-transparent',
+        'flex touch-none select-none p-px transition-colors data-[orientation=horizontal]:h-2.5 data-[orientation=horizontal]:flex-col data-[orientation=horizontal]:border-t data-[orientation=horizontal]:border-t-transparent data-[orientation=vertical]:h-full data-[orientation=vertical]:w-2.5 data-[orientation=vertical]:border-l data-[orientation=vertical]:border-l-transparent',
         className,
       )}
📝 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
'flex touch-none p-px transition-colors select-none data-horizontal:h-2.5 data-horizontal:flex-col data-horizontal:border-t data-horizontal:border-t-transparent data-vertical:h-full data-vertical:w-2.5 data-vertical:border-l data-vertical:border-l-transparent',
'flex touch-none select-none p-px transition-colors data-[orientation=horizontal]:h-2.5 data-[orientation=horizontal]:flex-col data-[orientation=horizontal]:border-t data-[orientation=horizontal]:border-t-transparent data-[orientation=vertical]:h-full data-[orientation=vertical]:w-2.5 data-[orientation=vertical]:border-l data-[orientation=vertical]:border-l-transparent',
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/src/components/ui/scroll-area.tsx` at line 42, The Tailwind variants
in the scroll-area component's class string currently use boolean-style variants
like "data-horizontal:..." and "data-vertical:..." which don't match the
component's data-orientation="horizontal|vertical"; update those to
attribute-value variants "data-[orientation=horizontal]:..." and
"data-[orientation=vertical]:..." in the same class entry so the rules (e.g.,
h-2.5, flex-col, border-t, border-l, etc.) apply correctly when the element's
data-orientation equals "horizontal" or "vertical".

Comment on lines +567 to +570
// Random width between 50 to 90%.
const [width] = React.useState(() => {
return `${Math.floor(Math.random() * 40) + 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.

⚠️ Potential issue | 🟡 Minor

Potential hydration mismatch with random width.

useState with a random initializer (Math.random()) will produce different values on server vs client, causing a hydration mismatch. Consider using a deterministic value or generating the random width only on the client.

🛡️ Proposed fix using useEffect for client-only randomization
 function SidebarMenuSkeleton({
   className,
   showIcon = false,
   ...props
 }: React.ComponentProps<'div'> & {
   showIcon?: boolean;
 }) {
-  // Random width between 50 to 90%.
-  const [width] = React.useState(() => {
-    return `${Math.floor(Math.random() * 40) + 50}%`;
-  });
+  // Random width between 50 to 90%, set after hydration to avoid mismatch
+  const [width, setWidth] = React.useState('70%');
+  
+  React.useEffect(() => {
+    setWidth(`${Math.floor(Math.random() * 40) + 50}%`);
+  }, []);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/src/components/ui/sidebar.tsx` around lines 567 - 570, The random
width initializer for the width state (const [width] = React.useState(() =>
...)) causes server/client hydration mismatches; change to a deterministic
SSR-safe initial value (e.g., null or a fixed percentage) and set the random
width only on the client inside a useEffect (use React.useState for width and
React.useEffect to call Math.random and update width on mount), ensuring you
reference and update the same width state variable and remove direct Math.random
usage from the initial React.useState initializer.

Comment on lines +9 to +22
function Tabs({
className,
orientation = 'horizontal',
...props
}: React.ComponentProps<typeof TabsPrimitive.Root>) {
return (
<TabsPrimitive.Root
data-slot="tabs"
data-orientation={orientation}
className={cn('group/tabs flex gap-2 data-horizontal:flex-col', className)}
{...props}
/>
);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

orientation prop is not passed to TabsPrimitive.Root, breaking accessibility.

The orientation is destructured and used only for the data-orientation attribute (styling), but it's excluded from ...props. Radix's TabsPrimitive.Root requires the orientation prop directly to set proper ARIA attributes and enable correct keyboard navigation (horizontal uses left/right arrows, vertical uses up/down arrows).

🛠️ Proposed fix
 function Tabs({
   className,
   orientation = 'horizontal',
   ...props
 }: React.ComponentProps<typeof TabsPrimitive.Root>) {
   return (
     <TabsPrimitive.Root
       data-slot="tabs"
+      orientation={orientation}
       data-orientation={orientation}
       className={cn('group/tabs flex gap-2 data-horizontal:flex-col', className)}
       {...props}
     />
   );
 }
📝 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
function Tabs({
className,
orientation = 'horizontal',
...props
}: React.ComponentProps<typeof TabsPrimitive.Root>) {
return (
<TabsPrimitive.Root
data-slot="tabs"
data-orientation={orientation}
className={cn('group/tabs flex gap-2 data-horizontal:flex-col', className)}
{...props}
/>
);
}
function Tabs({
className,
orientation = 'horizontal',
...props
}: React.ComponentProps<typeof TabsPrimitive.Root>) {
return (
<TabsPrimitive.Root
data-slot="tabs"
orientation={orientation}
data-orientation={orientation}
className={cn('group/tabs flex gap-2 data-horizontal:flex-col', className)}
{...props}
/>
);
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/src/components/ui/tabs.tsx` around lines 9 - 22, The Tabs component
destructures orientation but doesn't forward it to TabsPrimitive.Root, breaking
ARIA/keyboard behavior; update the Tabs function to pass
orientation={orientation} into TabsPrimitive.Root (in addition to
data-orientation) so Radix receives the prop and keyboard navigation/ARIA are
correct; keep the existing spread {...props} and other attributes (className,
data-slot) unchanged and ensure the prop type remains
React.ComponentProps<typeof TabsPrimitive.Root>.

Comment on lines +54 to +68
function TabsTrigger({ className, ...props }: React.ComponentProps<typeof TabsPrimitive.Trigger>) {
return (
<TabsPrimitive.Trigger
data-slot="tabs-trigger"
className={cn(
"relative inline-flex h-[calc(100%-1px)] flex-1 items-center justify-center gap-1.5 rounded-none border border-transparent px-1.5 py-0.5 text-xs font-medium whitespace-nowrap text-foreground/60 transition-all group-data-vertical/tabs:w-full group-data-vertical/tabs:justify-start group-data-vertical/tabs:py-[calc(--spacing(1.25))] hover:text-foreground focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 focus-visible:outline-1 focus-visible:outline-ring disabled:pointer-events-none disabled:opacity-50 dark:text-muted-foreground dark:hover:text-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
'group-data-[variant=line]/tabs-list:bg-transparent group-data-[variant=line]/tabs-list:data-active:bg-transparent dark:group-data-[variant=line]/tabs-list:data-active:border-transparent dark:group-data-[variant=line]/tabs-list:data-active:bg-transparent',
'data-active:bg-background data-active:text-foreground dark:data-active:border-input dark:data-active:bg-input/30 dark:data-active:text-foreground',
'after:absolute after:bg-foreground after:opacity-0 after:transition-opacity group-data-horizontal/tabs:after:inset-x-0 group-data-horizontal/tabs:after:bottom-[-5px] group-data-horizontal/tabs:after:h-0.5 group-data-vertical/tabs:after:inset-y-0 group-data-vertical/tabs:after:-right-1 group-data-vertical/tabs:after:w-0.5 group-data-[variant=line]/tabs-list:data-active:after:opacity-100',
className,
)}
{...props}
/>
);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check Tailwind config for custom data attribute shortcuts
fd -e ts -e js -e mjs "tailwind.config" --exec cat {}

# Check if similar data attribute patterns are used elsewhere
rg -n "data-active:" --type ts --type tsx -g '*.tsx' | head -20

Repository: HrushiBorhade/code-indexer

Length of output: 97


🏁 Script executed:

#!/bin/bash
# Search for tailwind config at root and common locations
find . -maxdepth 2 -name "tailwind*" -o -name "*.config.*" | grep -E "(tailwind|config)" | head -20

# Check package.json for Tailwind version and config location
cat package.json 2>/dev/null | grep -A 5 -B 5 "tailwind" || echo "Not found in root package.json"

# Search for data-active in tsx files with correct rg syntax
rg "data-active:" apps/web --type ts --type js | head -20

# Check the actual tabs.tsx file
cat apps/web/src/components/ui/tabs.tsx | head -100

Repository: HrushiBorhade/code-indexer

Length of output: 6005


🏁 Script executed:

#!/bin/bash
# Search for tailwind config in apps/web and root
find . -maxdepth 3 -type f \( -name "tailwind.config.*" -o -name "tailwind.json" \)

# Check apps/web for any config files
ls -la apps/web/ 2>/dev/null | grep -E "(tailwind|config)"

# Search for where data-orientation is being used and what's actually emitted
rg "data-orientation\|data-state\|data-active" apps/web --type ts --type js -B 2 -A 2 | head -40

# Check if there are any custom Tailwind plugins or config in the codebase
rg "theme:|extend:" . --type js --type ts | grep -i tailwind | head -10

Repository: HrushiBorhade/code-indexer

Length of output: 331


🏁 Script executed:

#!/bin/bash
# Check postcss.config for Tailwind configuration
cat apps/web/postcss.config.mjs 2>/dev/null || cat postcss.config.mjs

# Check if there's a globals or tailwind CSS file with custom config
find apps/web -name "*.css" -o -name "globals.*" | head -10

# Search for any code that might be adding custom data attributes
rg "setAttribute.*data-\|dataset\[" apps/web --type ts --type tsx -B 2 -A 2 | head -40

# Check Radix UI version in package.json or lock file
rg "radix-ui.*version|@radix-ui" . --type json | head -5

Repository: HrushiBorhade/code-indexer

Length of output: 237


🏁 Script executed:

#!/bin/bash
# Read globals.css to see Tailwind configuration
cat apps/web/src/app/globals.css | head -100

# Search for any custom attribute mappings or data-active assignments
rg "data-active|data-horizontal|data-vertical" apps/web --type ts --type js -B 3 -A 1 | head -50

# Check if Radix UI is being wrapped or customized anywhere
rg "TabsPrimitive\|from 'radix-ui'" apps/web --type ts --type js -B 2 -A 5 | head -40

Repository: HrushiBorhade/code-indexer

Length of output: 10003


🏁 Script executed:

#!/bin/bash
# Search for shadcn/tailwind.css 
find . -name "tailwind.css" -path "*shadcn*" 2>/dev/null

# Check if shadcn is imported from node_modules or local
rg "shadcn/tailwind.css" . --type css

# Look for any custom data attribute definitions in CSS
rg "@supports.*data-\|@theme.*data-" . --type css

# Search for where tabs' data-state might be used (Radix UI's actual attribute)
rg "data-state" apps/web --type ts --type js | head -10

Repository: HrushiBorhade/code-indexer

Length of output: 479


🌐 Web query:

Radix UI Tabs component data attributes emitted

💡 Result:

The Radix UI Tabs component emits the following data attributes on its sub-components, as documented in the official API reference: Root: - [data-orientation] = "vertical" | "horizontal" List: - [data-orientation] = "vertical" | "horizontal" Trigger: - [data-state] = "active" | "inactive" - [data-disabled] = present when disabled - [data-orientation] = "vertical" | "horizontal" Content: - [data-state] = "active" | "inactive" - [data-orientation] = "vertical" | "horizontal" These attributes allow for state-based styling via CSS selectors like [data-state="active"]. No other data attributes are listed in the official docs.

Citations:


Fix Tailwind selectors to match Radix UI Tabs attribute emissions.

The component uses selectors like data-active:, data-horizontal:, and data-vertical: that don't correspond to any attributes Radix UI Tabs actually emits. According to Radix UI's official API, Tabs emits data-state="active"|"inactive" and data-orientation="vertical"|"horizontal", not data-active, data-horizontal, or data-vertical.

Update the selectors:

  • Replace data-active: with data-[state=active]:
  • Replace data-horizontal: with data-[orientation=horizontal]:
  • Replace data-vertical: with data-[orientation=vertical]:

Without these changes, state and orientation-based styling won't apply to the tabs.

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

In `@apps/web/src/components/ui/tabs.tsx` around lines 54 - 68, The Tailwind
selectors in TabsTrigger's className are using non-existent Radix attributes;
update the selectors in the TabsTrigger component (where TabsPrimitive.Trigger
is rendered) to use Radix's emitted attributes: replace all occurrences of
`data-active:` with `data-[state=active]:`, `data-horizontal:` with
`data-[orientation=horizontal]:`, and `data-vertical:` with
`data-[orientation=vertical]:` (also update any grouped selectors like
`group-data-horizontal/tabs:` and `group-data-vertical/tabs:` accordingly) so
state- and orientation-based styles match Radix Tabs' `data-state` and
`data-orientation` attributes.

- Staggered hero entrance: badge → title → description → CTA → terminal
- Feature cards fade in on scroll with staggered delay
- Feature cards lift on hover (y: -2)
- Emil Kowalski easing: cubic-bezier(0.16, 1, 0.3, 1) for all entrances
- Reusable FadeIn component for future use
- motion library (formerly framer-motion) added

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
apps/web/src/components/landing/hero-animations.tsx (1)

72-84: Consider reduced motion accessibility.

The scroll-triggered and hover animations work well. For users who prefer reduced motion, you could wrap the app with <MotionConfig reducedMotion="user"> at a higher level (e.g., in a layout or provider), which respects prefers-reduced-motion system settings automatically.

This is optional since motion v12 provides this capability at the provider level rather than per-component.

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

In `@apps/web/src/components/landing/hero-animations.tsx` around lines 72 - 84,
Wrap application-level layout or provider with Framer Motion's MotionConfig and
set reducedMotion="user" so components like FeatureCard (which uses motion.div
and transition props) automatically respect prefers-reduced-motion; update the
top-level layout/provider (not FeatureCard itself) to import MotionConfig and
add reducedMotion="user" so scroll-triggered and hover animations in FeatureCard
and other motion components honor user system settings.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/web/src/components/motion/fade-in.tsx`:
- Line 44: The FadeIn component is always setting whileInView={{ opacity: 1, y:
0, x: 0 }}, which forces an inline transform even when direction === "none";
update the logic that builds the motion props (the initial and whileInView
objects used on the motion.div in FadeIn) to only include x and y when the
direction prop is not "none" (e.g., conditionally add x:0/y:0 and the matching
initial offsets), otherwise omit x/y entirely so no transform is injected and
existing wrapper transform classes are preserved.

---

Nitpick comments:
In `@apps/web/src/components/landing/hero-animations.tsx`:
- Around line 72-84: Wrap application-level layout or provider with Framer
Motion's MotionConfig and set reducedMotion="user" so components like
FeatureCard (which uses motion.div and transition props) automatically respect
prefers-reduced-motion; update the top-level layout/provider (not FeatureCard
itself) to import MotionConfig and add reducedMotion="user" so scroll-triggered
and hover animations in FeatureCard and other motion components honor user
system settings.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: b599e1ea-7235-46f4-b72d-41b97c5679ea

📥 Commits

Reviewing files that changed from the base of the PR and between cbd5057 and 5cedaa4.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (4)
  • apps/web/package.json
  • apps/web/src/app/page.tsx
  • apps/web/src/components/landing/hero-animations.tsx
  • apps/web/src/components/motion/fade-in.tsx
✅ Files skipped from review due to trivial changes (1)
  • apps/web/package.json
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/web/src/app/page.tsx

<motion.div
className={className}
initial={initial}
whileInView={{ opacity: 1, y: 0, x: 0 }}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Avoid forcing transform when direction is none.

Line 44 always sets x and y, which adds inline transform even when no directional motion is requested. This can unintentionally override transform classes on the wrapper.

Proposed fix
 export function FadeIn({
@@
 }: FadeInProps) {
   const dir = directionMap[direction];
   const initial = {
     opacity: 0,
     ...('y' in dir ? { y: dir.y * distance } : {}),
     ...('x' in dir ? { x: dir.x * distance } : {}),
   };
+  const whileInView =
+    direction === 'none' ? { opacity: 1 } : { opacity: 1, y: 0, x: 0 };
 
   return (
     <motion.div
       className={className}
       initial={initial}
-      whileInView={{ opacity: 1, y: 0, x: 0 }}
+      whileInView={whileInView}
       viewport={{ once }}
       transition={{
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/src/components/motion/fade-in.tsx` at line 44, The FadeIn component
is always setting whileInView={{ opacity: 1, y: 0, x: 0 }}, which forces an
inline transform even when direction === "none"; update the logic that builds
the motion props (the initial and whileInView objects used on the motion.div in
FadeIn) to only include x and y when the direction prop is not "none" (e.g.,
conditionally add x:0/y:0 and the matching initial offsets), otherwise omit x/y
entirely so no transform is injected and existing wrapper transform classes are
preserved.

Sequential inserts caused 46s timeouts on accounts with many repos.
Promise.all sends all upserts in parallel over Neon HTTP.
@HrushiBorhade
HrushiBorhade merged commit d172d99 into main Mar 23, 2026
4 of 5 checks passed

This branch was successfully deployed

1 active deployment
Preview — 176b13c2 Deployed Mar 23, 2026 by vercel[bot]
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.

Phase 1, Step 7: Dashboard UI — repo list + empty state

1 participant