Skip to content

Layout v2#1097

Merged
brendan-kellam merged 23 commits intov5from
bkellam/layout_v2
Apr 10, 2026
Merged

Layout v2#1097
brendan-kellam merged 23 commits intov5from
bkellam/layout_v2

Conversation

@brendan-kellam
Copy link
Copy Markdown
Contributor

@brendan-kellam brendan-kellam commented Apr 7, 2026

image image

Permission sync banner:
image

Summary by CodeRabbit

Release Notes

  • New Features

    • Redesigned sidebar navigation with collapsible layout replacing top navigation bar.
    • Chat history panel in sidebar with recent chats and quick actions.
    • Dedicated chats page with search, sorting, and infinite scrolling.
    • Settings reorganized with sidebar navigation for easier access.
    • General settings page for theme and editor preferences.
  • Bug Fixes & Improvements

    • Enhanced dark mode color scheme.
    • Improved sticky search bar behavior in browse view.
    • Better tooltip positioning and animations.
  • Documentation

    • Added guidance on conditional className composition.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 7, 2026

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 4aa3ad48-16be-4151-a83a-70bb85c3af1f

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Walkthrough

This PR implements a major UI redesign, replacing the top navigation bar with a collapsible sidebar navigation system. It includes new database seeding tooling, API routes for chat listing with pagination, reorganized settings pages, removed legacy navigation components, and updated styling/theming tokens.

Changes

Cohort / File(s) Summary
Documentation & Config
CHANGELOG.md, CLAUDE.md, packages/web/package.json
Documented UI redesign in changelog; added conditional className guidance; bumped Radix UI and Lucide React dependencies.
Database Tooling
packages/db/tools/scriptRunner.ts, packages/db/tools/scripts/inject-chat-data.ts
Registered new inject-chat-data CLI script; changed argument parsing to tolerate unknown flags; script validates org, selects user, prompts confirmation, and creates chat records.
Sidebar Navigation System
packages/web/src/app/(app)/@sidebar/(routes)/(default)/page.tsx, packages/web/src/app/(app)/@sidebar/(routes)/(default)/[...slug]/page.tsx, packages/web/src/app/(app)/@sidebar/(routes)/settings/page.tsx, packages/web/src/app/(app)/@sidebar/(routes)/settings/[...slug]/page.tsx
Added new route modules rendering DefaultSidebar and SettingsSidebar components with authentication wrapper for settings routes.
Sidebar Components
packages/web/src/app/(app)/@sidebar/components/sidebarBase.tsx, packages/web/src/app/(app)/@sidebar/components/defaultSidebar/index.tsx, packages/web/src/app/(app)/@sidebar/components/defaultSidebar/nav.tsx, packages/web/src/app/(app)/@sidebar/components/defaultSidebar/chatHistory.tsx, packages/web/src/app/(app)/@sidebar/components/settingsSidebar/index.tsx, packages/web/src/app/(app)/@sidebar/components/settingsSidebar/nav.tsx, packages/web/src/app/(app)/@sidebar/components/whatsNewSidebarButton.tsx
Implemented new sidebar architecture: SidebarBase with collapsible rail and account controls, DefaultSidebar with home view switching and chat history, settings sidebar with grouped navigation, and "What's new" popover with read-item persistence.
Chat Features
packages/web/src/app/(app)/chat/page.tsx, packages/web/src/app/(app)/chat/chatLandingPage.tsx, packages/web/src/app/(app)/chats/page.tsx, packages/web/src/app/(app)/chats/chatsPage.tsx, packages/web/src/app/api/(server)/chats/route.ts, packages/web/src/app/api/(server)/chats/types.ts, packages/web/src/app/api/(client)/client.ts
Refactored chat landing page into dedicated async component; added /chats route with searchable, sortable infinite-scroll table; implemented GET /api/chats with cursor pagination, filtering, and sorting.
Removed Navigation Components
packages/web/src/app/(app)/components/topBar.tsx, packages/web/src/app/(app)/components/navigationMenu/index.tsx, packages/web/src/app/(app)/components/navigationMenu/navigationItems.tsx, packages/web/src/app/(app)/components/navigationMenu/progressIndicator.tsx, packages/web/src/app/(app)/chat/components/chatSidePanel.tsx
Deleted legacy top navigation bar, navigation menu, progress indicator, and chat side panel; functionality migrated to sidebar system.
Removed Dropdown Components
packages/web/src/app/(app)/components/appearanceDropdownMenu.tsx, packages/web/src/app/(app)/components/appearanceDropdownMenuGroup.tsx, packages/web/src/app/(app)/components/meControlDropdownMenu.tsx, packages/web/src/app/(app)/components/pageNotFound.tsx, packages/web/src/app/(app)/components/notFound.tsx, packages/web/src/app/(app)/components/whatsNewIndicator.tsx
Removed legacy dropdown menus and appearance controls; refactored into SidebarBase and WhatsNewSidebarButton; removed unused error page utilities.
Settings Pages & Layout
packages/web/src/app/(app)/settings/layout.tsx, packages/web/src/app/(app)/settings/page.tsx, packages/web/src/app/(app)/settings/general/page.tsx, packages/web/src/app/(app)/settings/general/generalPage.tsx, packages/web/src/app/(app)/settings/apiKeys/page.tsx, packages/web/src/app/(app)/settings/apiKeys/apiKeysPage.tsx, packages/web/src/app/(app)/settings/components/settingsCard.tsx, packages/web/src/app/(app)/settings/components/sidebar-nav.tsx
Refactored settings to single-column layout; extracted nav grouping logic (getSidebarNavGroups); added SettingsCard/SettingsCardGroup wrappers; migrated API keys UI from DataTable to list with inline delete; removed legacy SidebarNav.
Page Layout & Routing
packages/web/src/app/(app)/layout.tsx, packages/web/src/app/(app)/page.tsx, packages/web/src/app/(app)/browse/layout.tsx, packages/web/src/app/(app)/browse/layoutClient.tsx, packages/web/src/app/(app)/agents/page.tsx, packages/web/src/app/(app)/repos/layout.tsx, packages/web/src/app/(app)/chat/[id]/page.tsx, packages/web/src/app/(app)/chat/[id]/components/chatThreadPanel.tsx
Updated root layout to accept sidebar prop via SidebarProvider; removed anonymous chat claiming from chat detail page; replaced two-panel layout with sticky header; removed TopBar from browse/search; removed agents page; removed resizable panel wrapper from chat thread.
Search & Browse Components
packages/web/src/app/(app)/search/components/searchLandingPage.tsx, packages/web/src/app/(app)/search/components/searchResultsPage.tsx, packages/web/src/app/(app)/browse/components/bottomPanel.tsx, packages/web/src/app/(app)/chat/components/chatActionsDropdown.tsx
Removed NavigationMenu; replaced TopBar with sticky SearchBar header; made bottom panel separator conditional; set dropdown modal to false.
Styling & Theme
packages/web/src/app/globals.css, packages/web/tailwind.config.ts, packages/web/src/components/ui/sidebar.tsx, packages/web/src/components/ui/table.tsx, packages/web/src/components/ui/tooltip.tsx
Added --shell CSS variable; darkened dark theme backgrounds; updated sidebar keyboard shortcut to [; refined sidebar mobile Sheet structure; added wrapperClassName prop to Table; updated tooltip transform-origin.
Auth & Hooks
packages/web/src/auth.ts, packages/web/src/hooks/useHomeView.ts, packages/web/src/lib/constants.ts, packages/web/src/features/chat/actions.ts
Implemented anonymous-to-user chat claiming on sign-in; added useHomeView hook for search/ask view preference persistence; added HOME_VIEW_COOKIE_NAME constant; removed legacy getUserChatHistory action.
UI Component Updates
packages/web/src/components/ui/data-table.tsx, packages/web/src/ee/features/analytics/analyticsContent.tsx, packages/web/src/ee/features/analytics/analyticsEntitlementMessage.tsx, packages/web/src/ee/features/sso/components/connectAccountsCard.tsx, packages/web/src/ee/features/sso/components/linkedAccountProviderCard.tsx
Removed generic DataTable component; removed min-h-screen constraints from analytics UI; replaced regular Button with LoadingButton in SSO provider cards; refactored SSO card styling with SettingsCard.
Other Pages & Removals
packages/web/src/app/not-found.tsx, packages/web/src/app/onboard/page.tsx
Replaced PageNotFound import with inline JSX; updated onboarding footer email to team@sourcebot.dev.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes

Possibly related PRs

  • PR #874: Modifies the same top-right UI components (TopBar, MeControl/Appearance dropdowns) being restructured into the sidebar system.
  • PR #721: Both PRs refactor chat-history/sidebar UI and page composition with updated chat loading and sidebar layout.
  • PR #392: Adds the chat/"Ask" feature at the code level (DB models, server/client APIs, chat-related components and hooks).

Suggested reviewers

  • msukkari
🚥 Pre-merge checks | ✅ 1 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The PR title 'Layout v2' is vague and generic, using a non-descriptive term that does not convey meaningful information about the substantial architectural changes in this changeset. Consider a more descriptive title such as 'Migrate to collapsible sidebar navigation layout' or 'Refactor app layout with sidebar-based navigation v2' to better reflect the primary architectural changes.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ 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 bkellam/layout_v2

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.

@brendan-kellam brendan-kellam marked this pull request as ready for review April 10, 2026 04:42
@github-actions

This comment has been minimized.

@brendan-kellam
Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 10, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 9

🧹 Nitpick comments (11)
packages/web/src/app/onboard/page.tsx (1)

312-318: Remove unnecessary attributes from the mailto link.

The target="_blank" and rel="noopener" attributes are intended for external HTTP links that open in new browser tabs. For mailto: links, these attributes have no effect since email clients handle the protocol directly. Consider removing them for cleaner code.

♻️ Proposed cleanup
                        <a
                            href="mailto:team@sourcebot.dev"
                            className="text-primary hover:underline font-medium transition-colors"
-                            target="_blank"
-                            rel="noopener"
                        >
                            reach out
                        </a>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/web/src/app/onboard/page.tsx` around lines 312 - 318, The mailto
anchor in packages/web/src/app/onboard/page.tsx (the <a> element with
href="mailto:team@sourcebot.dev" and className="text-primary hover:underline
font-medium transition-colors") includes unnecessary target="_blank" and
rel="noopener" attributes; remove those two attributes from that anchor so the
mailto link is clean and only relies on href and className.
packages/web/src/app/(app)/repos/layout.tsx (1)

1-17: Move getReposStats() call from layout to specific page or API route.

This layout fetches repo statistics but never uses the value—only checks for errors. This blocks all routes under /repos unnecessarily. If stats are needed, fetch them in the page that consumes them or use an API route with client-side caching (react-query) instead.

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

In `@packages/web/src/app/`(app)/repos/layout.tsx around lines 1 - 17, The Layout
component is calling getReposStats and only using isServiceError to potentially
throw ServiceErrorException, which blocks all /repos child routes; remove the
await getReposStats() call and the associated imports (getReposStats,
isServiceError, ServiceErrorException) from the Layout function and instead
fetch repo stats where they are actually consumed—either inside the specific
page component that needs repoStats or via a new API route that the client can
call and cache with react-query; if you need to preserve the same error
behavior, perform the isServiceError check and throw ServiceErrorException in
the page or API handler that performs the fetch.
packages/db/tools/scriptRunner.ts (1)

10-10: Use camelCase for the new script filename/import.

Line 10 introduces inject-chat-data, which conflicts with the TS filename convention in this repo. Please rename to injectChatData.ts and update this import.

Proposed fix
-import { injectChatData } from "./scripts/inject-chat-data";
+import { injectChatData } from "./scripts/injectChatData";

As per coding guidelines, **/*.{ts,tsx,js,jsx}: Files should use camelCase starting with a lowercase letter (e.g., shareChatPopover.tsx, userAvatar.tsx, apiClient.ts).

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

In `@packages/db/tools/scriptRunner.ts` at line 10, The import uses a kebab-case
filename ("./scripts/inject-chat-data") which violates the repo's TS filename
convention; rename the script file to camelCase (injectChatData.ts) and update
the import in scriptRunner.ts to import { injectChatData } from
"./scripts/injectChatData"; ensure any exports inside the file remain named
injectChatData so references resolve.
packages/db/tools/scripts/inject-chat-data.ts (1)

73-82: Make chat injection atomic to avoid partial writes.

If one create fails, earlier inserts remain committed. Wrap the batch in a transaction.

Proposed refactor
-        for (const name of chatNames) {
-            await prisma.chat.create({
-                data: {
-                    name,
-                    orgId,
-                    createdById: user.id,
-                    messages: [],
-                }
-            });
-        }
+        await prisma.$transaction(
+            chatNames.map((name) =>
+                prisma.chat.create({
+                    data: {
+                        name,
+                        orgId,
+                        createdById: user.id,
+                        messages: [],
+                    },
+                })
+            )
+        );
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/db/tools/scripts/inject-chat-data.ts` around lines 73 - 82, The loop
that creates chats using prisma.chat.create over chatNames should be wrapped in
a single transaction so partial inserts can't be committed on failure; replace
the per-iteration creates with a single transactional operation (use
prisma.$transaction or a createMany within prisma.$transaction) that performs
all inserts for chatNames with the same orgId and createdById (user.id) and
messages=[], ensuring the operation is atomic and errors roll back all created
chat records.
packages/web/src/app/(app)/chat/chatLandingPage.tsx (1)

17-31: Avoid the server-side fetch waterfall on first render.

These calls are independent and currently serialized, which adds avoidable latency to the landing page.

♻️ Suggested refactor
 export async function ChatLandingPage() {
-    const languageModels = await getConfiguredLanguageModelsInfo();
-    const searchContexts = await getSearchContexts();
-    const allRepos = await getRepos();
-    const session = await auth();
-
-    const carouselRepos = await getRepos({
-        where: {
-            indexedAt: {
-                not: null,
-            },
-        },
-        take: 10,
-    });
-
-    const repoStats = await getReposStats();
+    const [
+        languageModels,
+        searchContexts,
+        allRepos,
+        session,
+        carouselRepos,
+        repoStats,
+    ] = await Promise.all([
+        getConfiguredLanguageModelsInfo(),
+        getSearchContexts(),
+        getRepos(),
+        auth(),
+        getRepos({
+            where: {
+                indexedAt: {
+                    not: null,
+                },
+            },
+            take: 10,
+        }),
+        getReposStats(),
+    ]);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/web/src/app/`(app)/chat/chatLandingPage.tsx around lines 17 - 31,
The server-side fetches in chatLandingPage.tsx (calls to
getConfiguredLanguageModelsInfo(), getSearchContexts(), getRepos(), auth(), the
carousel getRepos(...) and getReposStats()) are currently awaited serially; run
the independent calls in parallel using Promise.all (or Promise.allSettled if
you want partial results) so that languageModels, searchContexts, allRepos,
session, carouselRepos and repoStats are fetched concurrently and then assigned
from the resolved results instead of awaiting each call sequentially.
packages/web/src/auth.ts (1)

207-211: Consider fire-and-forget for analytics event.

Awaiting captureEvent could add latency to the sign-in flow if the analytics service is slow. Since this is a non-critical analytics event, consider not awaiting it:

♻️ Suggested change
                     if (result.count > 0) {
-                        await captureEvent('wa_anonymous_chats_claimed', {
+                        captureEvent('wa_anonymous_chats_claimed', {
                             claimedCount: result.count,
                         });
                     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/web/src/auth.ts` around lines 207 - 211, The analytics call to
captureEvent inside the result.count > 0 branch should be fire-and-forget to
avoid adding latency to sign-in: remove the await and invoke
captureEvent('wa_anonymous_chats_claimed', { claimedCount: result.count }) as a
non-blocking call (e.g., prefix with void or call and attach .catch to
swallow/report errors) so the sign-in flow doesn't wait for the analytics
service; update the code around the result.count check and captureEvent
invocation accordingly.
packages/web/src/app/(app)/layout.tsx (1)

176-176: Consider using Tailwind color tokens for border colors.

The hardcoded hex values border-[#e6e6e6] dark:border-[#1d1d1f] could be replaced with Tailwind's semantic color classes (e.g., border-border or a custom design token) for better maintainability and theme consistency. As per coding guidelines, prefer Tailwind color classes directly instead of arbitrary value syntax.

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

In `@packages/web/src/app/`(app)/layout.tsx at line 176, The border color uses
hardcoded hex classes in the layout div (the element in layout.tsx with
className containing "border-[`#e6e6e6`] dark:border-[`#1d1d1f`]"); replace those
arbitrary-value tokens with the appropriate Tailwind semantic tokens (for
example "border-border" and "dark:border-border-dark" or the project's chosen
tokens) to match design system tokens, update the className on that div
accordingly, and ensure the new token names exist in the Tailwind theme (add
them to theme.extend.colors if needed).
packages/web/src/app/(app)/@sidebar/components/defaultSidebar/nav.tsx (1)

80-96: Consider: /chat/* routes don't highlight "Ask" nav item.

The isActive logic for /chat uses exact match (pathname === "/chat"), so navigating to /chat/[chatId] won't show "Ask" as active. This may be intentional to distinguish the landing page from active chat threads, but worth confirming this is the desired UX.

If chat thread routes should highlight "Ask":

♻️ Optional change
         if (href === "/chat") {
-            return pathname === "/chat";
+            return pathname === "/chat" || pathname.startsWith("/chat/");
         }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/web/src/app/`(app)/@sidebar/components/defaultSidebar/nav.tsx around
lines 80 - 96, The isActive function currently treats "/chat" as active only on
exact match (pathname === "/chat"), so routes like "/chat/[chatId]" won't
highlight Ask; change the "/chat" branch in isActive to use
pathname.startsWith("/chat") (or a more specific check such as pathname ===
"/chat" || pathname.startsWith("/chat/")) so thread routes also mark the Ask nav
item active; update the isActive function accordingly (refer to the isActive
helper in nav.tsx).
packages/web/src/app/(app)/chats/chatsPage.tsx (1)

226-243: Consider increasing debounce delay for search.

The 100ms debounce is quite aggressive and will trigger URL updates rapidly as the user types. A more typical debounce of 300-500ms would reduce unnecessary navigation events while still feeling responsive.

♻️ Suggested change
     useEffect(() => {
         const timer = setTimeout(() => {
             const params = new URLSearchParams(searchParams.toString());
             if (searchValue) {
                 params.set("query", searchValue);
             } else {
                 params.delete("query");
             }
             isOwnUpdateRef.current = true;
             router.replace(`${pathname}?${params.toString()}`);
-        }, 100);
+        }, 300);

         return () => {
             clearTimeout(timer);
         };
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/web/src/app/`(app)/chats/chatsPage.tsx around lines 226 - 243, The
debounce delay inside the useEffect that updates the URL (the setTimeout in the
effect watching searchValue) is too short (100ms); increase it to a more typical
300–500ms to avoid rapid router.replace navigations. Update the timer duration
in the useEffect where searchValue, searchParams, pathname, isOwnUpdateRef, and
router.replace are used—prefer extracting a constant like DEBOUNCE_MS (set to
300 or 400) near the top of the module and use it in the setTimeout/clearTimeout
so the delay is easy to tweak.
packages/web/src/app/(app)/@sidebar/components/defaultSidebar/index.tsx (1)

40-43: Fetch the owner notification sources in parallel.

These calls are independent and they block sidebar render on every owner page load. Running them sequentially adds avoidable latency to a shared layout path.

⚡ Suggested change
-        const connectionStats = await getConnectionStats();
-        const joinRequests = await getOrgAccountRequests();
+        const [connectionStats, joinRequests] = await Promise.all([
+            getConnectionStats(),
+            getOrgAccountRequests(),
+        ]);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/web/src/app/`(app)/@sidebar/components/defaultSidebar/index.tsx
around lines 40 - 43, The two independent async calls getConnectionStats() and
getOrgAccountRequests() are awaited sequentially causing added latency; update
the logic in defaultSidebar (index.tsx) to run them in parallel using
Promise.all and destructure the results (e.g., const [connectionStats,
joinRequests] = await Promise.all([getConnectionStats(),
getOrgAccountRequests()])), then keep the existing checks for
hasConnectionNotification and hasJoinRequestNotification (using isServiceError
and the same property checks) so behavior is unchanged but fetches complete
concurrently.
packages/web/src/app/(app)/@sidebar/components/whatsNewSidebarButton.tsx (1)

159-170: Use cn() for the conditional classes in the news list.

These two interpolated class strings are the only style-composition regression in the component. Converting them to cn() will keep the conditional styling consistent with the rest of the repo.

♻️ Suggested cleanup
+import { cn } from "@/lib/utils"

- className={`relative rounded-md transition-colors ${item.read ? "opacity-60" : ""} ${index !== newsItemsWithReadState.length - 1 ? "border-b border-border/50" : ""}`}
+ className={cn(
+   "relative rounded-md transition-colors",
+   item.read && "opacity-60",
+   index !== newsItemsWithReadState.length - 1 && "border-b border-border/50",
+ )}

- <h4 className={`font-medium text-sm leading-tight group-hover:text-primary ${item.read ? "text-muted-foreground" : ""}`}>
+ <h4
+   className={cn(
+     "font-medium text-sm leading-tight group-hover:text-primary",
+     item.read && "text-muted-foreground",
+   )}
+ >

As per coding guidelines, "Use cn() from @/lib/utils for conditional classNames instead of template literal interpolation".

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

In `@packages/web/src/app/`(app)/@sidebar/components/whatsNewSidebarButton.tsx
around lines 159 - 170, Replace the template-literal conditional classNames with
the cn() helper from "@/lib/utils": import cn and update the outer wrapper div
(currently using className={`relative ... ${item.read ? "opacity-60" : ""}
${index !== newsItemsWithReadState.length - 1 ? "border-b border-border/50" :
""}`}) and the h4 (currently using className={`font-medium ... ${item.read ?
"text-muted-foreground" : ""}`}) to use cn("static classes", { "opacity-60":
item.read, "border-b border-border/50": index !== newsItemsWithReadState.length
- 1 }, { "text-muted-foreground": item.read }) so the conditional styling for
item.read and the last-item border matches the repo convention.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/db/tools/scripts/inject-chat-data.ts`:
- Around line 53-61: When resolving the target user, currently
prisma.user.findUnique({ where: { id: userIdArg } }) allows selecting a user by
ID regardless of org; change this to scope the lookup to the target org by
querying prisma.user.findFirst({ where: { id: userIdArg, orgs: { some: { orgId }
} } }) (or alternatively keep findUnique but add a subsequent check that
user.orgs contains orgId), and handle the case where no user is found (throw/log
and exit) so chats cannot be created for a user outside the specified org;
update the code that assigns to the variable user and references to userIdArg,
orgId, and prisma accordingly.

In
`@packages/web/src/app/`(app)/@sidebar/components/defaultSidebar/chatHistory.tsx:
- Around line 108-124: The chat actions trigger is only visible on hover which
hides it from keyboard users; update the SidebarMenuAction inside
ChatActionsDropdown to use its built-in showOnHover behavior (e.g., add the
showOnHover prop to SidebarMenuAction) instead of relying solely on the manual
"opacity-0 group-hover/chat:opacity-100" classes so it also becomes visible on
keyboard focus/focus-within; locate the SidebarMenuAction in the
ChatActionsDropdown block and add showOnHover and remove or replace the
opacity-only classes accordingly.

In `@packages/web/src/app/`(app)/chat/chatLandingPage.tsx:
- Around line 84-86: The fixed-width separator (<Separator className="mt-5
w-[700px]" />) can overflow on small screens; replace the hard-coded w-[700px]
with responsive/bounded classes such as using full width with a max width (e.g.
w-full max-w-[700px] or w-full sm:w-[700px]) so the Separator in
chatLandingPage.tsx scales down on small viewports while preserving the intended
max width on larger screens.

In `@packages/web/src/app/`(app)/search/components/searchResultsPage.tsx:
- Line 22: Remove the unused LogIn import from the module import list (the
import statement that currently imports AlertTriangleIcon, BugIcon, FilterIcon,
LogIn, RefreshCwIcon); update the import to only include the icons actually used
(e.g., AlertTriangleIcon, BugIcon, FilterIcon, RefreshCwIcon) so the unused
LogIn symbol is eliminated.

In `@packages/web/src/app/`(app)/settings/apiKeys/apiKeysPage.tsx:
- Around line 271-276: The delete Button inside AlertDialogTrigger is only
revealed on hover, making it inaccessible to keyboard users; update its
visibility classes so it also becomes visible on keyboard focus by adding
focus-visible:opacity-100 and focus:opacity-100 (and optionally
group-focus-within:opacity-100 on the parent 'group') to the Button's className,
ensuring AlertDialogTrigger/Button remain keyboard-focusable (they already are
via AlertDialogTrigger asChild) so tabbing will reveal the control for screen
and keyboard users.
- Around line 114-127: handleDeleteApiKey currently assumes deleteApiKey throws
on failure but deleteApiKey returns a ServiceError for expected failures; change
handleDeleteApiKey to inspect the result of deleteApiKey (check for
instanceOf/shape of ServiceError or a returned error flag) instead of relying on
catch: if the result is a ServiceError, call toast with a destructive error
message using the ServiceError details and do not call router.refresh(); if the
result indicates success, then call router.refresh() and show the "API key
deleted" toast. Ensure you reference deleteApiKey and the ServiceError
type/shape when implementing the check inside handleDeleteApiKey.

In `@packages/web/src/app/`(app)/settings/page.tsx:
- Around line 13-17: Add a defensive guard before using groups[0].items[0].href:
after calling getSidebarNavGroups() and the isServiceError check, verify groups
is a non-empty array and groups[0].items is a non-empty array (e.g. if
(!groups?.length || !groups[0]?.items?.length) { throw new
ServiceErrorException(groups) } or redirect to a safe fallback route) so you
avoid a TypeError when groups or items are empty; update the redirect call to
use the validated location only after these checks.

In `@packages/web/src/app/api/`(server)/chats/route.ts:
- Around line 37-44: The orderBy currently uses a single key [sortBy] which can
be non-unique (e.g., "name" or "updatedAt") and leads to unstable cursor
pagination; update the orderBy object used in the chats route to include a
secondary tiebreaker id with ascending order (i.e., keep orderBy: { [sortBy]:
sortOrder, id: "asc" }) so that queries using cursor/skip produce deterministic,
stable ordering; modify the orderBy construction where variables orderBy,
sortBy, sortOrder, take/limit, and cursor are used in route.ts to add this id
tiebreaker (id always "asc" regardless of sortOrder).

In `@packages/web/src/app/api/`(server)/chats/types.ts:
- Around line 3-8: The limit in listChatsQueryParamsSchema is user-controlled
and can be abused when passed directly into Prisma's findMany({ take: limit + 1
}); add a hard cap (e.g., MAX_LIMIT = 100) so the effective limit is
bounded—either update listChatsQueryParamsSchema to enforce .max(MAX_LIMIT) on
limit or, if you prefer keeping schema flexible, clamp the parsed limit before
calling findMany (use Math.min(parsedLimit, MAX_LIMIT) and then pass that
clamped value into take). Ensure the clamp is applied wherever findMany(...,
take: ...) is used so a client cannot request an arbitrarily large take.

---

Nitpick comments:
In `@packages/db/tools/scriptRunner.ts`:
- Line 10: The import uses a kebab-case filename ("./scripts/inject-chat-data")
which violates the repo's TS filename convention; rename the script file to
camelCase (injectChatData.ts) and update the import in scriptRunner.ts to import
{ injectChatData } from "./scripts/injectChatData"; ensure any exports inside
the file remain named injectChatData so references resolve.

In `@packages/db/tools/scripts/inject-chat-data.ts`:
- Around line 73-82: The loop that creates chats using prisma.chat.create over
chatNames should be wrapped in a single transaction so partial inserts can't be
committed on failure; replace the per-iteration creates with a single
transactional operation (use prisma.$transaction or a createMany within
prisma.$transaction) that performs all inserts for chatNames with the same orgId
and createdById (user.id) and messages=[], ensuring the operation is atomic and
errors roll back all created chat records.

In `@packages/web/src/app/`(app)/@sidebar/components/defaultSidebar/index.tsx:
- Around line 40-43: The two independent async calls getConnectionStats() and
getOrgAccountRequests() are awaited sequentially causing added latency; update
the logic in defaultSidebar (index.tsx) to run them in parallel using
Promise.all and destructure the results (e.g., const [connectionStats,
joinRequests] = await Promise.all([getConnectionStats(),
getOrgAccountRequests()])), then keep the existing checks for
hasConnectionNotification and hasJoinRequestNotification (using isServiceError
and the same property checks) so behavior is unchanged but fetches complete
concurrently.

In `@packages/web/src/app/`(app)/@sidebar/components/defaultSidebar/nav.tsx:
- Around line 80-96: The isActive function currently treats "/chat" as active
only on exact match (pathname === "/chat"), so routes like "/chat/[chatId]"
won't highlight Ask; change the "/chat" branch in isActive to use
pathname.startsWith("/chat") (or a more specific check such as pathname ===
"/chat" || pathname.startsWith("/chat/")) so thread routes also mark the Ask nav
item active; update the isActive function accordingly (refer to the isActive
helper in nav.tsx).

In `@packages/web/src/app/`(app)/@sidebar/components/whatsNewSidebarButton.tsx:
- Around line 159-170: Replace the template-literal conditional classNames with
the cn() helper from "@/lib/utils": import cn and update the outer wrapper div
(currently using className={`relative ... ${item.read ? "opacity-60" : ""}
${index !== newsItemsWithReadState.length - 1 ? "border-b border-border/50" :
""}`}) and the h4 (currently using className={`font-medium ... ${item.read ?
"text-muted-foreground" : ""}`}) to use cn("static classes", { "opacity-60":
item.read, "border-b border-border/50": index !== newsItemsWithReadState.length
- 1 }, { "text-muted-foreground": item.read }) so the conditional styling for
item.read and the last-item border matches the repo convention.

In `@packages/web/src/app/`(app)/chat/chatLandingPage.tsx:
- Around line 17-31: The server-side fetches in chatLandingPage.tsx (calls to
getConfiguredLanguageModelsInfo(), getSearchContexts(), getRepos(), auth(), the
carousel getRepos(...) and getReposStats()) are currently awaited serially; run
the independent calls in parallel using Promise.all (or Promise.allSettled if
you want partial results) so that languageModels, searchContexts, allRepos,
session, carouselRepos and repoStats are fetched concurrently and then assigned
from the resolved results instead of awaiting each call sequentially.

In `@packages/web/src/app/`(app)/chats/chatsPage.tsx:
- Around line 226-243: The debounce delay inside the useEffect that updates the
URL (the setTimeout in the effect watching searchValue) is too short (100ms);
increase it to a more typical 300–500ms to avoid rapid router.replace
navigations. Update the timer duration in the useEffect where searchValue,
searchParams, pathname, isOwnUpdateRef, and router.replace are used—prefer
extracting a constant like DEBOUNCE_MS (set to 300 or 400) near the top of the
module and use it in the setTimeout/clearTimeout so the delay is easy to tweak.

In `@packages/web/src/app/`(app)/layout.tsx:
- Line 176: The border color uses hardcoded hex classes in the layout div (the
element in layout.tsx with className containing "border-[`#e6e6e6`]
dark:border-[`#1d1d1f`]"); replace those arbitrary-value tokens with the
appropriate Tailwind semantic tokens (for example "border-border" and
"dark:border-border-dark" or the project's chosen tokens) to match design system
tokens, update the className on that div accordingly, and ensure the new token
names exist in the Tailwind theme (add them to theme.extend.colors if needed).

In `@packages/web/src/app/`(app)/repos/layout.tsx:
- Around line 1-17: The Layout component is calling getReposStats and only using
isServiceError to potentially throw ServiceErrorException, which blocks all
/repos child routes; remove the await getReposStats() call and the associated
imports (getReposStats, isServiceError, ServiceErrorException) from the Layout
function and instead fetch repo stats where they are actually consumed—either
inside the specific page component that needs repoStats or via a new API route
that the client can call and cache with react-query; if you need to preserve the
same error behavior, perform the isServiceError check and throw
ServiceErrorException in the page or API handler that performs the fetch.

In `@packages/web/src/app/onboard/page.tsx`:
- Around line 312-318: The mailto anchor in
packages/web/src/app/onboard/page.tsx (the <a> element with
href="mailto:team@sourcebot.dev" and className="text-primary hover:underline
font-medium transition-colors") includes unnecessary target="_blank" and
rel="noopener" attributes; remove those two attributes from that anchor so the
mailto link is clean and only relies on href and className.

In `@packages/web/src/auth.ts`:
- Around line 207-211: The analytics call to captureEvent inside the
result.count > 0 branch should be fire-and-forget to avoid adding latency to
sign-in: remove the await and invoke captureEvent('wa_anonymous_chats_claimed',
{ claimedCount: result.count }) as a non-blocking call (e.g., prefix with void
or call and attach .catch to swallow/report errors) so the sign-in flow doesn't
wait for the analytics service; update the code around the result.count check
and captureEvent invocation accordingly.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

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

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 6f35e5b0-9134-4283-b6ff-f6ca2fed3e90

📥 Commits

Reviewing files that changed from the base of the PR and between 251f5b5 and 4ead8ad.

⛔ Files ignored due to path filters (1)
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (73)
  • CHANGELOG.md
  • CLAUDE.md
  • packages/db/tools/scriptRunner.ts
  • packages/db/tools/scripts/inject-chat-data.ts
  • packages/web/package.json
  • packages/web/src/app/(app)/@sidebar/(routes)/(default)/[...slug]/page.tsx
  • packages/web/src/app/(app)/@sidebar/(routes)/(default)/page.tsx
  • packages/web/src/app/(app)/@sidebar/(routes)/settings/[...slug]/page.tsx
  • packages/web/src/app/(app)/@sidebar/(routes)/settings/page.tsx
  • packages/web/src/app/(app)/@sidebar/components/defaultSidebar/chatHistory.tsx
  • packages/web/src/app/(app)/@sidebar/components/defaultSidebar/index.tsx
  • packages/web/src/app/(app)/@sidebar/components/defaultSidebar/nav.tsx
  • packages/web/src/app/(app)/@sidebar/components/settingsSidebar/index.tsx
  • packages/web/src/app/(app)/@sidebar/components/settingsSidebar/nav.tsx
  • packages/web/src/app/(app)/@sidebar/components/sidebarBase.tsx
  • packages/web/src/app/(app)/@sidebar/components/whatsNewSidebarButton.tsx
  • packages/web/src/app/(app)/agents/page.tsx
  • packages/web/src/app/(app)/browse/components/bottomPanel.tsx
  • packages/web/src/app/(app)/browse/layout.tsx
  • packages/web/src/app/(app)/browse/layoutClient.tsx
  • packages/web/src/app/(app)/chat/[id]/components/chatThreadPanel.tsx
  • packages/web/src/app/(app)/chat/[id]/page.tsx
  • packages/web/src/app/(app)/chat/chatLandingPage.tsx
  • packages/web/src/app/(app)/chat/components/chatActionsDropdown.tsx
  • packages/web/src/app/(app)/chat/components/chatSidePanel.tsx
  • packages/web/src/app/(app)/chat/page.tsx
  • packages/web/src/app/(app)/chats/chatsPage.tsx
  • packages/web/src/app/(app)/chats/page.tsx
  • packages/web/src/app/(app)/components/appearanceDropdownMenu.tsx
  • packages/web/src/app/(app)/components/appearanceDropdownMenuGroup.tsx
  • packages/web/src/app/(app)/components/meControlDropdownMenu.tsx
  • packages/web/src/app/(app)/components/navigationMenu/index.tsx
  • packages/web/src/app/(app)/components/navigationMenu/navigationItems.tsx
  • packages/web/src/app/(app)/components/navigationMenu/progressIndicator.tsx
  • packages/web/src/app/(app)/components/notFound.tsx
  • packages/web/src/app/(app)/components/pageNotFound.tsx
  • packages/web/src/app/(app)/components/topBar.tsx
  • packages/web/src/app/(app)/components/whatsNewIndicator.tsx
  • packages/web/src/app/(app)/layout.tsx
  • packages/web/src/app/(app)/page.tsx
  • packages/web/src/app/(app)/repos/layout.tsx
  • packages/web/src/app/(app)/search/components/searchLandingPage.tsx
  • packages/web/src/app/(app)/search/components/searchResultsPage.tsx
  • packages/web/src/app/(app)/settings/apiKeys/apiKeysPage.tsx
  • packages/web/src/app/(app)/settings/apiKeys/columns.tsx
  • packages/web/src/app/(app)/settings/apiKeys/page.tsx
  • packages/web/src/app/(app)/settings/components/settingsCard.tsx
  • packages/web/src/app/(app)/settings/components/sidebar-nav.tsx
  • packages/web/src/app/(app)/settings/general/generalPage.tsx
  • packages/web/src/app/(app)/settings/general/page.tsx
  • packages/web/src/app/(app)/settings/layout.tsx
  • packages/web/src/app/(app)/settings/linked-accounts/page.tsx
  • packages/web/src/app/(app)/settings/page.tsx
  • packages/web/src/app/api/(client)/client.ts
  • packages/web/src/app/api/(server)/chats/route.ts
  • packages/web/src/app/api/(server)/chats/types.ts
  • packages/web/src/app/globals.css
  • packages/web/src/app/not-found.tsx
  • packages/web/src/app/onboard/page.tsx
  • packages/web/src/auth.ts
  • packages/web/src/components/ui/data-table.tsx
  • packages/web/src/components/ui/sidebar.tsx
  • packages/web/src/components/ui/table.tsx
  • packages/web/src/components/ui/tooltip.tsx
  • packages/web/src/ee/features/analytics/analyticsContent.tsx
  • packages/web/src/ee/features/analytics/analyticsEntitlementMessage.tsx
  • packages/web/src/ee/features/sso/components/connectAccountsCard.tsx
  • packages/web/src/ee/features/sso/components/linkedAccountProviderCard.tsx
  • packages/web/src/features/chat/actions.ts
  • packages/web/src/hooks/use-mobile.tsx
  • packages/web/src/hooks/useHomeView.ts
  • packages/web/src/lib/constants.ts
  • packages/web/tailwind.config.ts
💤 Files with no reviewable changes (16)
  • packages/web/src/app/(app)/components/pageNotFound.tsx
  • packages/web/src/app/(app)/components/appearanceDropdownMenu.tsx
  • packages/web/src/app/(app)/components/notFound.tsx
  • packages/web/src/app/(app)/components/meControlDropdownMenu.tsx
  • packages/web/src/app/(app)/agents/page.tsx
  • packages/web/src/app/(app)/components/appearanceDropdownMenuGroup.tsx
  • packages/web/src/features/chat/actions.ts
  • packages/web/src/app/(app)/components/navigationMenu/index.tsx
  • packages/web/src/app/(app)/components/navigationMenu/progressIndicator.tsx
  • packages/web/src/app/(app)/chat/components/chatSidePanel.tsx
  • packages/web/src/app/(app)/components/navigationMenu/navigationItems.tsx
  • packages/web/src/components/ui/data-table.tsx
  • packages/web/src/app/(app)/components/topBar.tsx
  • packages/web/src/app/(app)/settings/components/sidebar-nav.tsx
  • packages/web/src/app/(app)/components/whatsNewIndicator.tsx
  • packages/web/src/app/(app)/settings/apiKeys/columns.tsx

@brendan-kellam brendan-kellam merged commit 92cf88f into v5 Apr 10, 2026
3 checks passed
@brendan-kellam brendan-kellam deleted the bkellam/layout_v2 branch April 10, 2026 17:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant