Skip to content

Conversation

maamokun
Copy link
Contributor

@maamokun maamokun commented Aug 10, 2025

Summary by CodeRabbit

  • New Features

    • Introduced automated bundle size analysis for pull requests and main branch updates, with results posted as comments.
  • Refactor

    • Replaced the previous sidebar and navigation system with a streamlined drawer and navbar layout for chat pages.
    • Simplified the chat sidebar to display only a basic list of chat links.
    • Removed the sidebar layout from public and settings pages.
  • Chores

    • Updated several dependencies to newer versions.
    • Added configuration for bundle size analysis.
    • Reformatted Renovate configuration for consistency.
    • Configured Next.js to ignore ESLint errors during builds.
    • Adjusted TypeScript configuration to disable strict mode while keeping strict null checks.
  • Removals

    • Deleted numerous UI component files, including sidebar, dropdown menu, sheet, tooltip, avatar, button, input, label, separator, and skeleton components.
    • Removed advanced sidebar, dropdown, and animation features, resulting in a simpler overall UI.

Copy link

coderabbitai bot commented Aug 10, 2025

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Walkthrough

This update removes a comprehensive set of custom UI component files, including advanced sidebar, dropdown, sheet, tooltip, and highlight systems, and replaces the chat sidebar with a minimal version. Layout components are refactored to remove dependencies on the deleted UI modules. Additional changes include dependency updates, a new Next.js bundle analysis workflow, and minor configuration tweaks.

Changes

Cohort / File(s) Change Summary
Workflow and Configuration
.github/workflows/nextjs_bundle_analysis.yml, package.json, renovate.json
Added a GitHub Actions workflow for Next.js bundle analysis; updated dependencies and added a new bundle analysis config in package.json; reformatted renovate.json.
Next.js Configuration
next.config.ts
Added eslint.ignoreDuringBuilds set to true to ignore ESLint errors during builds.
TypeScript Configuration
tsconfig.json
Disabled global strict mode while keeping strict null checks enabled.
Chat Layout Refactor
src/app/(chat)/template.tsx
Replaced sidebar-based layout with a new drawer and navbar system, removing all sidebar component dependencies and updating the children prop type.
Public Layout Simplification
src/app/(public)/template.tsx
Removed sidebar context and inset wrappers, simplifying the layout to directly render children.
Settings Layout Removal
src/app/(settings)/template.tsx
Deleted the entire settings layout component, which previously handled user authentication, data fetching, and sidebar rendering.
Sidebar Component Simplification
src/components/sidebar.tsx
Replaced the complex, feature-rich chat sidebar with a minimal version that only lists chat links.
Animate UI: Effects and Radix Systems Removal
src/components/animate-ui/effects/motion-highlight.tsx, src/components/animate-ui/radix/dropdown-menu.tsx, src/components/animate-ui/radix/sheet.tsx, src/components/animate-ui/radix/sidebar.tsx, src/components/animate-ui/radix/tooltip.tsx
Deleted all advanced UI systems for motion highlights, dropdown menus, sheets, sidebars, and tooltips, including their context, animation, and accessibility logic.
UI Primitives Removal
src/components/ui/avatar.tsx, src/components/ui/button.tsx, src/components/ui/input.tsx, src/components/ui/label.tsx, src/components/ui/separator.tsx, src/components/ui/skeleton.tsx
Deleted all shared UI primitive components (avatar, button, input, label, separator, skeleton) and their styling logic.
Minor Code Cleanups and Refactoring
src/actions/chat.ts, src/actions/image.ts, src/actions/ollama.ts, src/app/(chat)/chat/[id]/page.tsx, src/app/(chat)/chat/page.tsx, src/app/(chat)/image/page.tsx, src/app/(chat)/settings/models/page.tsx, src/app/(public)/share/[id]/page.tsx, src/app/api/chat/route.ts, src/components/OllamaModelList.tsx, src/components/OpenRouterConfigButton.tsx, src/components/OpenRouterModelList.tsx, src/components/chat.tsx, src/components/chatContainer.tsx, src/components/chatInput.tsx, src/components/chatMeta.tsx, src/components/header.tsx, src/components/imageGenerator.tsx, src/components/modelCard.tsx
Renamed unused variables with underscore prefixes; removed unused imports; simplified conditional expressions; removed console logging of caught errors; restructured JSX for clarity; standardized early return blocks; and other minor formatting and code clarity improvements.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant Navbar/Drawer
    participant ChatSidebar (Minimal)
    participant MainContent

    User->>Navbar/Drawer: Clicks hamburger menu
    Navbar/Drawer->>ChatSidebar (Minimal): Opens sidebar drawer
    ChatSidebar (Minimal)-->>User: Displays chat links
    User->>ChatSidebar (Minimal): Selects chat
    ChatSidebar (Minimal)->>MainContent: Loads selected chat
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

A rabbit hopped through fields of code,
And swept away the UI load.
Sidebars, sheets, and tooltips—gone!
Now layouts simpler carry on.
With menus light and chat links neat,
This bundle hops with nimble feet.
🐇✨

Note

🔌 MCP (Model Context Protocol) integration is now available in Early Access!

Pro users can now connect to remote MCP servers under the Integrations page to get reviews and chat conversations that understand additional development context.

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch Feature/aisdk-5

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
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai generate unit tests to generate unit tests for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Summary of Changes

Hello @maamokun, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

I've implemented a new feature that overhauls the application's primary navigation and layout by integrating the daisyUI component library. This involved a substantial refactoring effort, replacing a suite of custom-built UI components with daisyUI's pre-styled and functional alternatives, particularly for the sidebar and main application shell. The goal is to simplify UI development, improve consistency, and reduce the overall maintenance burden of custom UI elements. Additionally, I've updated several core dependencies to their latest versions, ensuring the project benefits from the newest features and stability improvements.

Highlights

  • UI Framework Migration: I've undertaken a significant refactor of the application's UI, specifically migrating the custom sidebar and related components to utilize daisyUI's drawer component. This change simplifies the UI codebase by leveraging an existing, robust component library.
  • Removal of Custom UI Components: Several custom UI components, including MotionHighlight, DropdownMenu, Sheet, Sidebar, Tooltip, Avatar, Button, Input, Label, Separator, and Skeleton, have been removed. This streamlines the project by reducing custom component maintenance and relying more on external libraries or simpler native elements.
  • Chat Layout and Sidebar Redesign: The chat layout (src/app/(chat)/template.tsx) has been completely revamped to integrate the new daisyUI drawer, including a new navigation bar with integrated links and user information. The ChatSidebar component itself has been simplified to only render the chat list.
  • Dependency Updates: I've updated several @ai-sdk/* dependencies to their latest versions, including major version bumps for @ai-sdk/anthropic and @ai-sdk/groq. This ensures we are using the most recent features and bug fixes from the AI SDK.
  • Build Analysis Configuration: I've added nextBundleAnalysis configuration to package.json, which will help monitor and analyze the size of the Next.js application bundles, aiding in performance optimization.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point in your pull request via creating an issue comment (i.e. comment on the pull request page) using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in issue comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments or fill out our survey to provide feedback.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request is a significant refactoring that replaces a complex, custom sidebar and UI component library with a simpler implementation using daisyUI. This greatly reduces the amount of custom code and dependencies, which is a positive change. However, the new implementation introduces some issues related to semantic HTML, accessibility, and Next.js best practices, such as using <a> tags for internal navigation instead of <Link>, which will cause unnecessary page reloads. I've provided suggestions to address these points.

Comment on lines 90 to 115
<ul className="menu menu-horizontal">
<Link href={'/settings/models'}>
<button className={'btn btn-ghost'}>
<LuBot className="inline-block h-6 w-6" />
Models
</button>
</Link>
<Link href={'/settings/files'}>
<button className={'btn btn-ghost'}>
<LuFile className="inline-block h-6 w-6" />
Files
</button>
</Link>
<Link href={'/settings/shared'}>
<button className={'btn btn-ghost'}>
<LuShare className="inline-block h-6 w-6" />
Shared chats
</button>
</Link>
<Link href={'/logout'} prefetch={false}>
<button className={'btn btn-ghost'}>
<LuLogOut className="inline-block h-6 w-6" />
Logout
</button>
</Link>
</ul>

Choose a reason for hiding this comment

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

high

The <ul> element has <Link> components as its direct children, which is invalid HTML. <ul> elements should only contain <li> elements as direct children. This can cause issues with accessibility and rendering. Additionally, wrapping a <button> inside a <Link> is an anti-pattern. The <Link> component can be styled directly to look like a button.

              <ul className="menu menu-horizontal">
                <li>
                  <Link href={'/settings/models'} className={'btn btn-ghost'}>
                    <LuBot className="inline-block h-6 w-6" />
                    Models
                  </Link>
                </li>
                <li>
                  <Link href={'/settings/files'} className={'btn btn-ghost'}>
                    <LuFile className="inline-block h-6 w-6" />
                    Files
                  </Link>
                </li>
                <li>
                  <Link href={'/settings/shared'} className={'btn btn-ghost'}>
                    <LuShare className="inline-block h-6 w-6" />
                    Shared chats
                  </Link>
                </li>
                <li>
                  <Link href={'/logout'} prefetch={false} className={'btn btn-ghost'}>
                    <LuLogOut className="inline-block h-6 w-6" />
                    Logout
                  </Link>
                </li>
              </ul>

<ul>
{data.chats?.map((chat) => (
<li key={chat.id}>
<a href={`/chat/${chat.id}`}>{chat.name}</a>

Choose a reason for hiding this comment

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

high

You are using a standard <a> tag for navigation within your Next.js application. This will trigger a full page reload every time a user clicks on a chat, which is inefficient and provides a poor user experience in a single-page application. You should use the Link component from next/link for client-side navigation. Remember to add import Link from 'next/link'; at the top of the file.

                <Link href={`/chat/${chat.id}`}>{chat.name}</Link>

Comment on lines 80 to 87
<a className="flex space-x-3 font-bold text-xl normal-case">
<img
alt="MikanDev Logo"
className="mr-3 h-8 w-auto"
src={KawaiiLogo.src}
/>
Chat
</a>

Choose a reason for hiding this comment

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

medium

The <a> tag is used here but doesn't have an href attribute, making it non-functional as a link and semantically incorrect. Since this element appears to be a title/branding element and not a link, using a <div> would be more appropriate for semantic HTML.

              <div className="flex space-x-3 font-bold text-xl normal-case">
                <img
                  alt="MikanDev Logo"
                  className="mr-3 h-8 w-auto"
                  src={KawaiiLogo.src}
                />
                Chat
              </div>

Comment on lines 133 to 136
<img
alt={data.user.name}
src={data.user.avatar || '/default-avatar.png'}
/>

Choose a reason for hiding this comment

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

medium

The <img> tag for the user avatar is missing width and height attributes. This can lead to Cumulative Layout Shift (CLS) as the browser has to reflow the page once the image is loaded. It's a best practice to always include these attributes to improve performance and user experience.

                      <img
                        alt={data.user.name}
                        src={data.user.avatar || '/default-avatar.png'}
                        width={48}
                        height={48}
                      />

Copy link

@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: 7

🔭 Outside diff range comments (1)
src/app/(chat)/template.tsx (1)

33-43: Race condition on initial user creation — use upsert

Two concurrent requests can both pass findUnique and race on create, causing a unique constraint error. Use upsert or create with conflict handling.

Example:

await prisma.user.upsert({
  where: { id: claims!.sub },
  create: { id: claims!.sub },
  update: {},
});
♻️ Duplicate comments (4)
src/app/(chat)/template.tsx (3)

80-88: Brand element should not be an anchor without href

This is a non-link brand/title. Use a non-interactive element.

-              <a className="flex space-x-3 font-bold text-xl normal-case">
+              <div className="flex space-x-3 font-bold text-xl normal-case">
                 <img
                   alt="MikanDev Logo"
                   className="mr-3 h-8 w-auto"
                   src={KawaiiLogo.src}
                 />
                 Chat
-              </a>
+              </div>

90-115: Fix invalid

    structure and button-in-link anti-pattern

      must contain
    • as direct children; avoid wrapping inside . Style the directly.
      -              <ul className="menu menu-horizontal">
      -                <Link href={'/settings/models'}>
      -                  <button className={'btn btn-ghost'}>
      -                    <LuBot className="inline-block h-6 w-6" />
      -                    Models
      -                  </button>
      -                </Link>
      -                <Link href={'/settings/files'}>
      -                  <button className={'btn btn-ghost'}>
      -                    <LuFile className="inline-block h-6 w-6" />
      -                    Files
      -                  </button>
      -                </Link>
      -                <Link href={'/settings/shared'}>
      -                  <button className={'btn btn-ghost'}>
      -                    <LuShare className="inline-block h-6 w-6" />
      -                    Shared chats
      -                  </button>
      -                </Link>
      -                <Link href={'/logout'} prefetch={false}>
      -                  <button className={'btn btn-ghost'}>
      -                    <LuLogOut className="inline-block h-6 w-6" />
      -                    Logout
      -                  </button>
      -                </Link>
      -              </ul>
      +              <ul className="menu menu-horizontal">
      +                <li>
      +                  <Link href="/settings/models" className="btn btn-ghost">
      +                    <LuBot className="inline-block h-6 w-6" />
      +                    <span className="ml-2">Models</span>
      +                  </Link>
      +                </li>
      +                <li>
      +                  <Link href="/settings/files" className="btn btn-ghost">
      +                    <LuFile className="inline-block h-6 w-6" />
      +                    <span className="ml-2">Files</span>
      +                  </Link>
      +                </li>
      +                <li>
      +                  <Link href="/settings/shared" className="btn btn-ghost">
      +                    <LuShare className="inline-block h-6 w-6" />
      +                    <span className="ml-2">Shared chats</span>
      +                  </Link>
      +                </li>
      +                <li>
      +                  <Link href="/logout" prefetch={false} className="btn btn-ghost">
      +                    <LuLogOut className="inline-block h-6 w-6" />
      +                    <span className="ml-2">Logout</span>
      +                  </Link>
      +                </li>
      +              </ul>

      133-136: Add image dimensions (or use next/image) to prevent CLS

      The avatar <img> lacks width/height. Add them or switch to next/image for optimisation. Also ensure remote domains are configured if using external avatars.

      -                      <img
      +                      <img
                               alt={data.user.name}
                               src={data.user.avatar || '/default-avatar.png'}
      +                        width={48}
      +                        height={48}
                             />

      Alternatively:

      import Image from 'next/image';
      
      <Image
        alt={data.user.name}
        src={data.user.avatar || '/default-avatar.png'}
        width={48}
        height={48}
        className="rounded-full"
      />
src/components/sidebar.tsx (1)

25-26: Use Next.js Link for client-side navigation

Using <a> will trigger full page reloads. Use next/link and style it directly.

-              <li key={chat.id}>
-                <a href={`/chat/${chat.id}`}>{chat.name}</a>
+              <li key={chat.id}>
+                <Link href={`/chat/${chat.id}`}>{chat.name}</Link>
               </li>

Add the import:

import Link from 'next/link';
🧹 Nitpick comments (6)
renovate.json (1)

3-3: Cosmetic reformat; no functional impact — LGTM

Single-line array is equivalent to the previous multi-line form. If the repo enforces JSON formatting via a formatter, ensure this aligns to avoid churn in future diffs.

package.json (1)

13-20: Surface and sanity-check all imports from the bumped @AI-SDK packages

I’ve listed every import and core API call for the upgraded packages—major bumps landed on:

All import paths and function names remain unchanged in:

  • src/components/chatContainer.tsx (useChat)
  • src/components/chatInput.tsx (startChat)
  • src/actions/image.ts (openai, xai, generateImage)
  • src/app/api/chat/route.ts (createAnthropic, createGoogleGenerativeAI, createGroq, createOpenAI, createXai, streamText)

No missing or renamed exports detected, but because these are new major releases, please manually confirm:

  • The signature and options for useChat still match v2.0.9
  • Provider-factory calls (e.g. createAnthropic, createGroq) conform to their updated APIs
  • Any breaking changes in React hooks or client factory args
.github/workflows/nextjs_bundle_analysis.yml (2)

1-2: Licence header introduces MPL-2.0 into the repo – verify compatibility
This file carries a HashiCorp copyright and MPL-2.0 SPDX tag. Ensure your project’s licence allows inclusion of MPL-2.0 files, and that attribution/notice requirements are met.

If incompatible, replace with your project’s licence header or add proper NOTICE/ATTRIBUTION per MPL guidelines. I can prepare an updated header if you confirm the repo’s licence.


62-69: Handle missing base branch artifact more gracefully

If the base artifact doesn’t exist (for example in a new repo or after expiry), the download step will error and the compare step will be skipped or fail. To avoid this:

  • Explicitly target your artifact by name
  • Use the if_no_artifact_found: warn (or if-no-artifact-found: warn) input to prevent failure when it’s absent
  • Wrap the compare step in a guard that checks for the presence of the base stats file

Proposed diff in .github/workflows/nextjs_bundle_analysis.yml (around lines 62–69):

      - name: Download base branch bundle stats
-       uses: dawidd6/action-download-artifact@v2
+       uses: dawidd6/action-download-artifact@v2  # or bump to a valid later tag
        if: success() && github.event.number
        with:
          workflow: nextjs_bundle_analysis.yml
          branch: ${{ github.event.pull_request.base.ref }}
          path: .next/analyze/base
+         name: bundle                # adjust to your artifact’s name
+         if_no_artifact_found: warn  # or if-no-artifact-found: warn

And wrap the compare step (around lines 84–86):

      - name: Compare with base branch bundle
-       if: success() && github.event.number
-       run: ls -laR .next/analyze/base && bunx -p nextjs-bundle-analysis compare
+       if: success() && github.event.number
+       run: |
+         ls -laR .next/analyze/base || true
+         if [ -f ".next/analyze/base/__bundle_analysis.json" ]; then
+           bunx -p nextjs-bundle-analysis compare
+         else
+           echo "No base bundle stats found; skipping compare."
+         fi

Please confirm:

  • the correct action tag (v2 vs. any newer release)
  • the exact input key syntax (if_no_artifact_found or if-no-artifact-found)
  • the JSON stats filename and path you’re using
src/app/(chat)/template.tsx (1)

45-50: Query only required fields and consider a cap

You only use id and name, but fetch entire rows. Reduce payload and optionally cap the list.

const chats = await prisma.chat.findMany({
  where: { userId: claims!.sub },
  orderBy: { createdAt: 'desc' },
  select: { id: true, name: true },
  // take: 100, // optional
});
src/components/sidebar.tsx (1)

22-28: Align with DaisyUI menu semantics and handle empty state

Add menu classes to the list and show a small fallback when there are no chats.

-          <ul>
-            {data.chats?.map((chat) => (
+          <ul className="menu menu-vertical">
+            {data.chats?.length ? data.chats.map((chat) => (
               <li key={chat.id}>
-                <a href={`/chat/${chat.id}`}>{chat.name}</a>
+                <Link href={`/chat/${chat.id}`} className="truncate">{chat.name}</Link>
               </li>
-            ))}
+            )) : (
+              <li className="opacity-60">No chats yet</li>
+            )}
           </ul>

Note: ensure import Link from 'next/link' is added at the top.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 06af8d8 and 15daba8.

⛔ Files ignored due to path filters (1)
  • bun.lock is excluded by !**/*.lock
📒 Files selected for processing (18)
  • .github/workflows/nextjs_bundle_analysis.yml (1 hunks)
  • package.json (4 hunks)
  • renovate.json (1 hunks)
  • src/app/(chat)/template.tsx (2 hunks)
  • src/app/(public)/template.tsx (1 hunks)
  • src/app/(settings)/template.tsx (0 hunks)
  • src/components/animate-ui/effects/motion-highlight.tsx (0 hunks)
  • src/components/animate-ui/radix/dropdown-menu.tsx (0 hunks)
  • src/components/animate-ui/radix/sheet.tsx (0 hunks)
  • src/components/animate-ui/radix/sidebar.tsx (0 hunks)
  • src/components/animate-ui/radix/tooltip.tsx (0 hunks)
  • src/components/sidebar.tsx (1 hunks)
  • src/components/ui/avatar.tsx (0 hunks)
  • src/components/ui/button.tsx (0 hunks)
  • src/components/ui/input.tsx (0 hunks)
  • src/components/ui/label.tsx (0 hunks)
  • src/components/ui/separator.tsx (0 hunks)
  • src/components/ui/skeleton.tsx (0 hunks)
💤 Files with no reviewable changes (12)
  • src/components/ui/skeleton.tsx
  • src/components/ui/avatar.tsx
  • src/components/ui/label.tsx
  • src/components/ui/input.tsx
  • src/components/ui/button.tsx
  • src/components/ui/separator.tsx
  • src/components/animate-ui/radix/tooltip.tsx
  • src/components/animate-ui/radix/sheet.tsx
  • src/app/(settings)/template.tsx
  • src/components/animate-ui/effects/motion-highlight.tsx
  • src/components/animate-ui/radix/dropdown-menu.tsx
  • src/components/animate-ui/radix/sidebar.tsx
🧰 Additional context used
🪛 actionlint (1.7.7)
.github/workflows/nextjs_bundle_analysis.yml

27-27: the runner of "actions/checkout@v3" action is too old to run on GitHub Actions. update the action's version to fix this issue

(action)


36-36: the runner of "actions/cache@v3" action is too old to run on GitHub Actions. update the action's version to fix this issue

(action)


63-63: the runner of "dawidd6/action-download-artifact@v2" action is too old to run on GitHub Actions. update the action's version to fix this issue

(action)

🔇 Additional comments (1)
src/app/(public)/template.tsx (1)

8-8: Sidebar provider removal validated – no context consumers remain

I’ve searched for any SidebarProvider, SidebarInset, SidebarTrigger, or useSidebar usages and found none. The only remaining “sidebar” reference is the ChatSidebar component (src/components/sidebar.tsx), which purely renders passed-in data and does not consume any context or hooks. Removal of the old sidebar providers will not break any descendants.

analyze:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Update GitHub Actions to supported major versions (actionlint)
The workflow uses older action majors flagged by actionlint. Upgrade to current majors compatible with hosted runners.

Apply:

-      - uses: actions/checkout@v3
+      - uses: actions/checkout@v4
...
-      - name: Restore next build
-        uses: actions/cache@v3
+      - name: Restore next build
+        uses: actions/cache@v4
...
-      - name: Download base branch bundle stats
-        uses: dawidd6/action-download-artifact@v2
+      - name: Download base branch bundle stats
+        uses: dawidd6/action-download-artifact@v6

Also applies to: 36-36, 63-63

🧰 Tools
🪛 actionlint (1.7.7)

27-27: the runner of "actions/checkout@v3" action is too old to run on GitHub Actions. update the action's version to fix this issue

(action)

🤖 Prompt for AI Agents
In .github/workflows/nextjs_bundle_analysis.yml at lines 27, 36, and 63, the
GitHub Actions use older major versions flagged by actionlint. Update the action
versions to their latest supported major versions compatible with hosted runners
by changing the version tags (e.g., from v2 to v3 or the latest stable major)
for all actions used at these lines.

Comment on lines +32 to +34
- name: Install dependencies
run: bun install

Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Make dependency installation reproducible in CI
Use Bun’s lockfile-enforced mode to avoid drift across runs.

-      - name: Install dependencies
-        run: bun install
+      - name: Install dependencies
+        run: bun install --frozen-lockfile --no-progress

Optional: cache Bun’s global package data to speed up installs. I can add that if desired.

📝 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
- name: Install dependencies
run: bun install
- name: Install dependencies
run: bun install --frozen-lockfile --no-progress
🤖 Prompt for AI Agents
In .github/workflows/nextjs_bundle_analysis.yml at lines 32 to 34, the
dependency installation step uses "bun install" without enforcing the lockfile,
which can cause non-reproducible installs. Modify the command to "bun install
--frozen-lockfile" to ensure the lockfile is strictly respected during
installation. Optionally, consider adding caching for Bun’s global package data
to speed up subsequent installs.

Comment on lines +35 to +46
- name: Restore next build
uses: actions/cache@v3
id: restore-build-cache
env:
cache-name: cache-next-build
with:
# if you use a custom build directory, replace all instances of `.next` in this file with your build directory
# ex: if your app builds to `dist`, replace `.next` with `dist`
path: .next/cache
# change this if you prefer a more strict cache
key: ${{ runner.os }}-build-${{ env.cache-name }}

Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Harden Next.js build cache key to prevent stale caches
Current key is constant, risking incorrect cache hits. Include lockfile hash and os.

       - name: Restore next build
         uses: actions/cache@v4
         id: restore-build-cache
         env:
           cache-name: cache-next-build
         with:
           # if you use a custom build directory, replace all instances of `.next` in this file with your build directory
           # ex: if your app builds to `dist`, replace `.next` with `dist`
           path: .next/cache
           # change this if you prefer a more strict cache
-          key: ${{ runner.os }}-build-${{ env.cache-name }}
+          key: ${{ runner.os }}-${{ env.cache-name }}-${{ hashFiles('**/bun.lockb', '**/package-lock.json', '**/pnpm-lock.yaml', '**/yarn.lock') }}
+          restore-keys: |
+            ${{ runner.os }}-${{ env.cache-name }}-
📝 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
- name: Restore next build
uses: actions/cache@v3
id: restore-build-cache
env:
cache-name: cache-next-build
with:
# if you use a custom build directory, replace all instances of `.next` in this file with your build directory
# ex: if your app builds to `dist`, replace `.next` with `dist`
path: .next/cache
# change this if you prefer a more strict cache
key: ${{ runner.os }}-build-${{ env.cache-name }}
- name: Restore next build
uses: actions/cache@v4
id: restore-build-cache
env:
cache-name: cache-next-build
with:
# if you use a custom build directory, replace all instances of `.next` in this file with your build directory
# ex: if your app builds to `dist`, replace `.next` with `dist`
path: .next/cache
# change this if you prefer a more strict cache
key: ${{ runner.os }}-${{ env.cache-name }}-${{ hashFiles('**/bun.lockb', '**/package-lock.json', '**/pnpm-lock.yaml', '**/yarn.lock') }}
restore-keys: |
${{ runner.os }}-${{ env.cache-name }}-
🧰 Tools
🪛 actionlint (1.7.7)

36-36: the runner of "actions/cache@v3" action is too old to run on GitHub Actions. update the action's version to fix this issue

(action)

🤖 Prompt for AI Agents
In .github/workflows/nextjs_bundle_analysis.yml around lines 35 to 46, the cache
key for restoring the Next.js build is too generic and can cause stale cache
hits. Update the cache key to include a hash of the lockfile (e.g.,
package-lock.json or yarn.lock) and the runner OS to ensure the cache is
invalidated when dependencies change. This will make the cache key unique per
dependency state and OS, preventing incorrect cache usage.

Comment on lines +54 to +55
run: bunx -p nextjs-bundle-analysis report

Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Pin nextjs-bundle-analysis tool version for deterministic results
Ephemeral bunx pulls latest, which can change behaviour. Pin the version (or add as devDependency).

-        run: bunx -p nextjs-bundle-analysis report
+        run: bunx -p [email protected] report

Same for compare below.

📝 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
run: bunx -p nextjs-bundle-analysis report
run: bunx -p nextjs-bundle-analysis@0.7.0 report
🤖 Prompt for AI Agents
In .github/workflows/nextjs_bundle_analysis.yml at lines 54-55, the command uses
bunx to run nextjs-bundle-analysis without specifying a version, causing
non-deterministic results due to ephemeral latest pulls. Fix this by pinning the
nextjs-bundle-analysis tool version explicitly in the command or by adding it as
a devDependency in package.json and referencing that fixed version in the
workflow. Apply the same version pinning approach to the compare command
mentioned below in the file.

Comment on lines +63 to 68
"nextBundleAnalysis": {
"budget": null,
"budgetPercentIncreaseRed": 20,
"minimumChangeThreshold": 0,
"showDetails": true
}
Copy link

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Confirm nextjs-bundle-analysis config keys; avoid budget: null if unsupported

Ensure the tool reads these exact keys from package.json. If budget is optional, omit it rather than setting to null to prevent parsing issues.

Please check the tool’s expected package.json schema:

If budget should be omitted, apply:

   "nextBundleAnalysis": {
-    "budget": null,
     "budgetPercentIncreaseRed": 20,
     "minimumChangeThreshold": 0,
     "showDetails": true
   }

🌐 Web query:

What configuration keys does "nextjs-bundle-analysis" read from package.json, and are "budgetPercentIncreaseRed", "minimumChangeThreshold", and "showDetails" valid? Should "budget" be omitted or set to null?

💡 Result:

The "nextjs-bundle-analysis" GitHub Action reads configuration from the "nextBundleAnalysis" key in your project's package.json[1]. It accepts only specific configuration keys, and their validity is determined by the documented options. Based on available documentation, here are the answers to your key questions:

Supported package.json Configuration Keys

The action specifically mentions the following keys under "nextBundleAnalysis"[1]:

  • showDetails (boolean): Valid; controls whether detailed sections are rendered in the bundle analysis comment (default: true)[1].
  • buildOutputDirectory (string): Valid; for specifying the custom Next.js build output directory. Default is .next[1].

Validity of Specific Keys

Key Valid Notes
showDetails Yes Directly documented as an option controlling UI detail[1].
budgetPercentIncreaseRed No Not mentioned or documented as a supported key[1].
minimumChangeThreshold No Not mentioned or documented as a supported key[1].

No current documentation supports budgetPercentIncreaseRed or minimumChangeThreshold as valid configuration keys for "nextjs-bundle-analysis" in package.json[1]. If these are present, they may be ignored or unsupported by the tool.

Handling the budget Key

The documentation refers only to performance budgets in general but does not specify a budget property in the package.json configuration section[1]. The workflow might contain details relating to budgets, but there is no mention that setting "budget": null in package.json is required or supported. If a budget key is present without clear documentation, it's safest to omit it rather than set it to null[1].

Summary

  • You should only include well-documented keys (showDetails, buildOutputDirectory) in "nextBundleAnalysis" within package.json[1].
  • The keys budgetPercentIncreaseRed, minimumChangeThreshold, and a plain budget key are not valid according to current GitHub and npm documentation[1]. They should be omitted unless updated documentation specifically adds support.
  • Use "showDetails" for controlling display detail, and "buildOutputDirectory" to set your build output location[1].

If you need advanced budgeting features, you may need to configure these within the GitHub workflow file itself, not in the package.json[1].

Citations:


Update unsupported nextBundleAnalysis keys in package.json

Only the documented options (showDetails and buildOutputDirectory) are read from nextBundleAnalysis. Remove the unsupported keys (budget, budgetPercentIncreaseRed, minimumChangeThreshold) to avoid them being ignored or causing parsing issues.

• In package.json under "nextBundleAnalysis":
– Keep: showDetails (boolean)
– Optionally add: buildOutputDirectory (string, default: .next)
– Remove: budget, budgetPercentIncreaseRed, minimumChangeThreshold

Suggested diff:

 "nextBundleAnalysis": {
-  "budget": null,
-  "budgetPercentIncreaseRed": 20,
-  "minimumChangeThreshold": 0,
   "showDetails": true
 }

If you require performance budgets, configure them within your GitHub workflow rather than in the package.json.

📝 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
"nextBundleAnalysis": {
"budget": null,
"budgetPercentIncreaseRed": 20,
"minimumChangeThreshold": 0,
"showDetails": true
}
"nextBundleAnalysis": {
"showDetails": true
}
🤖 Prompt for AI Agents
In package.json lines 63 to 68, the nextBundleAnalysis section contains
unsupported keys budget, budgetPercentIncreaseRed, and minimumChangeThreshold
that should be removed because only showDetails and buildOutputDirectory are
valid. Remove these unsupported keys and optionally add buildOutputDirectory if
needed, keeping showDetails as is. This will prevent parsing issues and ensure
only recognized options are present.

</div>
{children}
</div>
<div className="drawer-side z-60">
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Invalid Tailwind class z-60

Tailwind defaults don’t include z-60. Use z-50 or an arbitrary value z-[60].

-        <div className="drawer-side z-60">
+        <div className="drawer-side z-50">
📝 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
<div className="drawer-side z-60">
<div className="drawer-side z-50">
🤖 Prompt for AI Agents
In src/app/(chat)/template.tsx at line 120, the Tailwind class `z-60` is invalid
because Tailwind's default scale does not include `z-60`. Replace `z-60` with
either the closest valid default class `z-50` or use an arbitrary value syntax
like `z-[60]` to apply the desired z-index.

Comment on lines 126 to 147
<ul className="menu flex min-h-screen w-80 flex-col bg-base-200 p-4">
<ChatSidebar data={data} />
<div className="card mt-auto shadow-xl">
<div className="card-body">
<div className="flex items-center">
<div className="avatar">
<div className="w-12 rounded-full">
<img
alt={data.user.name}
src={data.user.avatar || '/default-avatar.png'}
/>
</div>
</div>
<div className="ml-3">
<h2 className="card-title">{data.user.name}</h2>
<p className="text-gray-500 text-sm">UID {data.user.id}</p>
</div>
</div>
</div>
</div>
</ul>
</div>
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

⚠️ Potential issue

Invalid

    children: ChatSidebar renders a
    , not
  • Your outer <ul> has a direct child <ChatSidebar/> which renders a <div>. That’s invalid HTML and breaks accessibility. Wrap with a non-list container and let ChatSidebar own its list.

    -          <ul className="menu flex min-h-screen w-80 flex-col bg-base-200 p-4">
    -            <ChatSidebar data={data} />
    -            <div className="card mt-auto shadow-xl">
    +          <div className="flex min-h-screen w-80 flex-col bg-base-200 p-4">
    +            <ChatSidebar data={data} />
    +            <div className="card mt-auto shadow-xl">
                   <div className="card-body">
                     <div className="flex items-center">
                       <div className="avatar">
                         <div className="w-12 rounded-full">
                           <img
                             alt={data.user.name}
                             src={data.user.avatar || '/default-avatar.png'}
                           />
                         </div>
                       </div>
                       <div className="ml-3">
                         <h2 className="card-title">{data.user.name}</h2>
                         <p className="text-gray-500 text-sm">UID {data.user.id}</p>
                       </div>
                     </div>
                   </div>
                 </div>
    -          </ul>
    +          </div>
    📝 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
    <ul className="menu flex min-h-screen w-80 flex-col bg-base-200 p-4">
    <ChatSidebar data={data} />
    <div className="card mt-auto shadow-xl">
    <div className="card-body">
    <div className="flex items-center">
    <div className="avatar">
    <div className="w-12 rounded-full">
    <img
    alt={data.user.name}
    src={data.user.avatar || '/default-avatar.png'}
    />
    </div>
    </div>
    <div className="ml-3">
    <h2 className="card-title">{data.user.name}</h2>
    <p className="text-gray-500 text-sm">UID {data.user.id}</p>
    </div>
    </div>
    </div>
    </div>
    </ul>
    </div>
    <div className="flex min-h-screen w-80 flex-col bg-base-200 p-4">
    <ChatSidebar data={data} />
    <div className="card mt-auto shadow-xl">
    <div className="card-body">
    <div className="flex items-center">
    <div className="avatar">
    <div className="w-12 rounded-full">
    <img
    alt={data.user.name}
    src={data.user.avatar || '/default-avatar.png'}
    />
    </div>
    </div>
    <div className="ml-3">
    <h2 className="card-title">{data.user.name}</h2>
    <p className="text-gray-500 text-sm">UID {data.user.id}</p>
    </div>
    </div>
    </div>
    </div>
    </div>
    </div>
    🤖 Prompt for AI Agents
    In src/app/(chat)/template.tsx around lines 126 to 147, the <ul> element has a
    direct child <ChatSidebar> component that renders a <div>, which is invalid HTML
    because <ul> should only have <li> children. To fix this, replace the outer <ul>
    with a non-list container element like a <div> or <nav> that can contain
    <ChatSidebar> and other div elements, and ensure that ChatSidebar manages its
    own list structure internally.
    

Copy link

@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: 6

🔭 Outside diff range comments (12)
src/components/header.tsx (3)

373-401: Avoid rendering “0” when there are no buttons

Using buttons?.length && (…) will render 0 when the array is empty. Guard with an explicit length check.

-              {buttons?.length && (
+              {(buttons?.length ?? 0) > 0 && (
                 <motion.div
                   className="mt-auto flex flex-col gap-2"
                   variants={mobileMenuButtonsVariants}
                 >
                   {buttons?.map((buttonProps) => {
                     return (
-                      <button
-                        key={buttonProps.title}
-                        {...buttonProps}
-                        className={'btn btn-neutral'}
-                      >
-                        {buttonProps.href ? (
-                          <Link
-                            href={buttonProps.href}
-                            target={buttonProps.target ?? '_self'}
-                          >
-                            {buttonProps.title}
-                          </Link>
-                        ) : (
-                          buttonProps.title
-                        )}
-                      </button>
+                      // see next comment for improved link/button semantics
+                      <></>
                     );
                   })}
                 </motion.div>
               )}

Follow-up: see the next comment for fixing the actual button/link rendering.


380-395: Fix invalid button/link semantics and stray props

Currently a <button> receives href/target via spread and wraps a <Link>, which is invalid HTML and can cause confusing behaviour. Render a Link (anchor) when href exists; otherwise render a button.

-                      <button
-                        key={buttonProps.title}
-                        {...buttonProps}
-                        className={'btn btn-neutral'}
-                      >
-                        {buttonProps.href ? (
-                          <Link
-                            href={buttonProps.href}
-                            target={buttonProps.target ?? '_self'}
-                          >
-                            {buttonProps.title}
-                          </Link>
-                        ) : (
-                          buttonProps.title
-                        )}
-                      </button>
+                      <>
+                        {buttonProps.href ? (
+                          <Link
+                            key={buttonProps.title}
+                            href={buttonProps.href}
+                            target={buttonProps.target ?? '_self'}
+                            rel={
+                              (buttonProps.target ?? '_self') === '_blank'
+                                ? 'noopener noreferrer'
+                                : undefined
+                            }
+                            className="btn btn-neutral"
+                          >
+                            {buttonProps.title}
+                          </Link>
+                        ) : (
+                          <button key={buttonProps.title} className="btn btn-neutral">
+                            {buttonProps.title}
+                          </button>
+                        )}
+                      </>

450-466: Apply the same link/button semantics fix on desktop buttons

Mirror the mobile fix: render a Link (anchor) with button styling when href exists; otherwise a real button. Also add rel="noopener noreferrer" for _blank.

-                <button
-                  key={buttonProps.title}
-                  {...buttonProps}
-                  className={'btn btn-secondary'}
-                >
-                  {buttonProps.href ? (
-                    <Link
-                      href={buttonProps.href}
-                      target={buttonProps.target ?? '_self'}
-                    >
-                      {buttonProps.title}
-                    </Link>
-                  ) : (
-                    buttonProps.title
-                  )}
-                </button>
+                <>
+                  {buttonProps.href ? (
+                    <Link
+                      key={buttonProps.title}
+                      href={buttonProps.href}
+                      target={buttonProps.target ?? '_self'}
+                      rel={
+                        (buttonProps.target ?? '_self') === '_blank'
+                          ? 'noopener noreferrer'
+                          : undefined
+                      }
+                      className="btn btn-secondary"
+                    >
+                      {buttonProps.title}
+                    </Link>
+                  ) : (
+                    <button key={buttonProps.title} className="btn btn-secondary">
+                      {buttonProps.title}
+                    </button>
+                  )}
+                </>
src/actions/ollama.ts (1)

14-21: SSRF risk and missing timeout when validating user-provided host

fetch(${host}/v1/models) uses a user-supplied URL without validation and with no timeout. This enables server-side request forgery (e.g. hitting internal services like 169.254.169.254) and can hang threads.

Add protocol/host validation, reject localhost/private networks, and apply a timeout:

   try {
-    const response = await fetch(`${host}/v1/models`);
+    const url = new URL(host);
+    if (!['http:', 'https:'].includes(url.protocol)) {
+      throw new Error('Invalid protocol for Ollama host');
+    }
+    // Basic guard against local/private targets
+    const hostname = url.hostname.toLowerCase();
+    const privateV4 =
+      /^(10\.\d{1,3}\.\d{1,3}\.\d{1,3}|192\.168\.\d{1,3}\.\d{1,3}|172\.(1[6-9]|2\d|3[0-1])\.\d{1,3}\.\d{1,3})$/.test(
+        hostname
+      ) || ['localhost', '127.0.0.1', '0.0.0.0'].includes(hostname);
+    const loopbackV6 = hostname === '::1';
+    if (privateV4 || loopbackV6) {
+      throw new Error('Forbidden Ollama host');
+    }
+    const ac = new AbortController();
+    const t = setTimeout(() => ac.abort(), 7000);
+    const response = await fetch(`${url.origin}/v1/models`, { signal: ac.signal });
+    clearTimeout(t);
     if (!response.ok) {
       throw new Error('Invalid Ollama host');
     }

Optionally, consider DNS resolution checks against private ranges for a stronger guarantee.

src/app/api/chat/route.ts (3)

89-93: Guard against malformed Ollama modelId

Splitting by ' - ' without validation can yield undefined parts and 500s later. Validate and return 400 on bad input.

-        const endpoint = modelId.split(' - ')[0];
-        const modelName = modelId.split(' - ')[1];
-        const provider = createOllama({ baseURL: `${endpoint}/api` });
+        const [endpoint, modelName] = modelId.split(' - ');
+        if (!endpoint || !modelName) {
+          return new Response(
+            'Invalid Ollama model id format. Expected "<endpoint> - <modelName>".',
+            { status: 400 }
+          );
+        }
+        const provider = createOllama({ baseURL: `${endpoint}/api` });

103-115: Ensure stream record cleanup even if addMessage fails

prisma.stream.deleteMany is only called after addMessage. If that throws, stream rows linger. Use try/finally.

-          onFinish: async (message) => {
-            await addMessage({
-              message: {
-                content: message.text,
-                role: 'assistant',
-              },
-              id,
-            });
-            await prisma.stream.deleteMany({
-              where: { streamId },
-            });
-          },
+          onFinish: async (message) => {
+            try {
+              await addMessage({
+                message: {
+                  content: message.text,
+                  role: 'assistant',
+                },
+                id,
+              });
+            } finally {
+              await prisma.stream.deleteMany({
+                where: { streamId },
+              });
+            }
+          },

Apply similarly in the other two blocks above.

Also applies to: 144-156, 189-200


206-209: Avoid leaking internal error details to clients

Returning error.message can expose internals. Prefer a generic error response in production.

-      JSON.stringify({
-        error: 'An error occurred while processing your request.',
-        details: error instanceof Error ? error.message : 'Unknown error',
-      }),
+      JSON.stringify({
+        error: 'An error occurred while processing your request.',
+      }),
src/components/chat.tsx (1)

31-39: Regression: chat no longer auto-scrolls on new messages

Changing deps to [] runs the scroll only on mount. Users won’t see later messages without manual scrolling.

-  useEffect(() => {
+  useEffect(() => {
     if (bottomRef.current) {
       bottomRef.current.scrollIntoView({
         behavior: 'smooth',
         block: 'end',
       });
     }
-  }, []);
+  }, [msg.length, status]);

This triggers on new messages and status transitions, without over-firing.

src/components/chatContainer.tsx (1)

83-88: Align addMessage call to its defined signature

The addMessage action expects an object with:

  • message: { content: string; role: 'user' | 'assistant' | 'system' | 'data'; attachment?: string }
  • id: string
  • optional top-level attachment?: string

Instead of suppressing the error, reshape your payload:

// src/components/chatContainer.tsx ~83–88
await addMessage({
  message: {
    content: messageData.content,
    role: messageData.role,
    // only include if defined
    ...(messageData.attachment && { attachment: messageData.attachment }),
  },
  id: id!,                            // ensure id is non-empty
  attachment: messageData.attachment,  // optional top-level field
});

This removes the need for // @ts-expect-error and aligns exactly with the addMessage type.

src/components/chatInput.tsx (3)

171-181: Invalid Tailwind class justify-left

Tailwind uses justify-start, not justify-left. This will be ignored by the compiler.

-                      <div className={'card-body justify-left w-full p-3'}>
+                      <div className="card-body justify-start w-full p-3">-                              <div
-                                className={'card-body justify-left w-full p-3'}
-                              >
+                              <div className="card-body justify-start w-full p-3">-                                      <div className="card-body justify-left w-full p-3">
+                                      <div className="card-body justify-start w-full p-3">

Also applies to: 179-181, 232-236, 304-306


191-196: Invalid Tailwind class text-md

text-md isn’t part of Tailwind’s default scale. Use text-base/text-sm or an arbitrary value like text-[15px].

-                              'font-semibold text-base-content text-md'
+                              'font-semibold text-base-content text-base'-                                      'font-semibold text-base-content text-md'
+                                      'font-semibold text-base-content text-base'-                                          <span className="font-semibold text-base-content text-md">
+                                          <span className="font-semibold text-base-content text-base">

Also applies to: 240-245, 306-310


364-377: Make the close “X” accessible

Wrap the icon in a button with an accessible name and size the hit area.

-              <X onClick={() => setAttachment(null)} />
+              <button
+                type="button"
+                className="btn btn-ghost btn-sm"
+                aria-label="Remove attachment"
+                onClick={() => setAttachment(null)}
+              >
+                <X />
+              </button>
♻️ Duplicate comments (6)
src/components/sidebar.tsx (1)

20-23: Use Next.js Link for client-side navigation

Anchor tags trigger full reloads; use next/link for SPA navigation.

-          {data.chats?.map((chat) => (
-            <li key={chat.id}>
-              <a href={`/chat/${chat.id}`}>{chat.name}</a>
-            </li>
-          ))}
+          {data.chats?.map((chat) => (
+            <li key={chat.id}>
+              <Link href={`/chat/${chat.id}`}>{chat.name}</Link>
+            </li>
+          ))}

Add outside this hunk:

import Link from 'next/link';
src/app/(chat)/template.tsx (5)

68-76: Branding element should not be an anchor without href

Use a semantic container or link to home. Prior comment noted this.

-          <div className="mx-2 flex-1 px-2">
-            <a className="flex space-x-3 font-bold text-xl normal-case">
+          <div className="mx-2 flex-1 px-2">
+            <Link href="/" className="flex space-x-3 font-bold text-xl normal-case">
               <img
                 alt="MikanDev Logo"
                 className="mr-3 h-8 w-auto"
                 src={KawaiiLogo.src}
               />
               Chat
-            </a>
+            </Link>
           </div>

78-104: Invalid

    children and nested button inside Link

    <ul> must contain <li>. Avoid wrapping <button> inside <Link>. Apply button classes to <Link> directly. This was raised before.

    -          <div className="hidden flex-none lg:block">
    -            <ul className="menu menu-horizontal">
    -              <Link href={'/settings/models'}>
    -                <button className={'btn btn-ghost'}>
    +          <div className="hidden flex-none lg:block">
    +            <ul className="menu menu-horizontal">
    +              <li>
    +                <Link href="/settings/models" className="btn btn-ghost">
                       <LuBot className="inline-block h-6 w-6" />
                       Models
    -                </button>
    -              </Link>
    -              <Link href={'/settings/files'}>
    -                <button className={'btn btn-ghost'}>
    +                </Link>
    +              </li>
    +              <li>
    +                <Link href="/settings/files" className="btn btn-ghost">
                       <LuFile className="inline-block h-6 w-6" />
                       Files
    -                </button>
    -              </Link>
    -              <Link href={'/settings/shared'}>
    -                <button className={'btn btn-ghost'}>
    +                </Link>
    +              </li>
    +              <li>
    +                <Link href="/settings/shared" className="btn btn-ghost">
                       <LuShare className="inline-block h-6 w-6" />
                       Shared chats
    -                </button>
    -              </Link>
    -              <Link href={'/logout'} prefetch={false}>
    -                <button className={'btn btn-ghost'}>
    +                </Link>
    +              </li>
    +              <li>
    +                <Link href="/logout" prefetch={false} className="btn btn-ghost">
                       <LuLogOut className="inline-block h-6 w-6" />
                       Logout
    -                </button>
    -              </Link>
    +                </Link>
    +              </li>
                 </ul>
               </div>

    108-108: Invalid Tailwind class z-60

    Tailwind default scale excludes z-60. Use z-50 or z-[60]. Previously raised.

    -      <div className="drawer-side z-60">
    +      <div className="drawer-side z-50">

    114-135: Invalid

      children: <ChatSidebar/> renders a div and there’s a sibling div

      The outer container should be a non-list element. Prior comment covered this.

      -        <ul className="menu flex min-h-screen w-80 flex-col bg-base-200 p-4">
      -          <ChatSidebar data={data} />
      -          <div className="card mt-auto shadow-xl">
      +        <div className="flex min-h-screen w-80 flex-col bg-base-200 p-4">
      +          <ChatSidebar data={data} />
      +          <div className="card mt-auto shadow-xl">-          </div>
      -        </ul>
      +          </div>
      +        </div>

      121-124: Add width/height to avatar image to prevent CLS

      Specify intrinsic dimensions or switch to next/image.

      -                      <img
      -                        alt={data.user.name}
      -                        src={data.user.avatar || '/default-avatar.png'}
      -                      />
      +                      <img
      +                        alt={data.user.name}
      +                        src={data.user.avatar || '/default-avatar.png'}
      +                        width={48}
      +                        height={48}
      +                        loading="eager"
      +                        decoding="async"
      +                      />
🧹 Nitpick comments (19)
src/components/imageGenerator.tsx (2)

67-71: Disable Generate when no model is selected (and minor a11y)

Prevent pointless requests and improve UX by disabling the button unless both prompt and model are set; also surface aria-disabled.

-          <button
-            className={'btn btn-secondary'}
-            disabled={loading || !prompt}
-            onClick={handleGenerate}
-          >
+          <button
+            className="btn btn-secondary"
+            disabled={loading || !(prompt && selectedModel)}
+            aria-disabled={loading}
+            onClick={handleGenerate}
+          >

57-64: Clamp the image count on input to enforce min/max

HTML min/max can be bypassed. Clamp the value in onChange and add step/inputMode for better mobile UX.

-            <input
-              className={'input w-24'}
-              max={9}
-              min={1}
-              onChange={(e) => setCount(Number(e.target.value))}
-              placeholder="Count"
-              type="number"
-              value={count}
-            />
+            <input
+              className="input w-24"
+              max={9}
+              min={1}
+              step={1}
+              inputMode="numeric"
+              aria-label="Image count"
+              onChange={(e) => {
+                const v = Number(e.target.value);
+                const next = Number.isFinite(v) ? Math.min(9, Math.max(1, v)) : 1;
+                setCount(next);
+              }}
+              placeholder="Count"
+              type="number"
+              value={count}
+            />
src/actions/image.ts (1)

43-60: Action: confirm XAI client factory name before per-request instantiation

I’ve verified that @ai-sdk/openai exposes a createOpenAI factory for per-request clients—but there’s no definitive documentation for a similar createXAI export in @ai-sdk/xai. Please check the Node.js package’s public API (e.g. in node_modules/@ai-sdk/xai/index.d.ts or its README) for the correct factory function name.

Once confirmed, refactor src/actions/image.ts (lines 43–60) to remove the process.env.*_API_KEY mutations and instantiate both providers per-request. For example:

 case 'openai': {
-  process.env.OPENAI_API_KEY = apiKey;
-  return await generateImage({ model: openai.image(model), prompt, n: count });
+  const { createOpenAI } = await import('@ai-sdk/openai');
+  const openaiClient = createOpenAI({ apiKey });
+  return await generateImage({
+    model: openaiClient.image(model),
+    prompt,
+    n: count,
+  });
 }

 case 'xai': {
-  process.env.XAI_API_KEY = apiKey;
-  return await generateImage({ model: xai.image(model), prompt, n: count });
+  // replace with the actual factory export from @ai-sdk/xai
+  const { createXAI } = await import('@ai-sdk/xai');
+  const xaiClient = createXAI({ apiKey });
+  return await generateImage({
+    model: xaiClient.image(model),
+    prompt,
+    n: count,
+  });
 }

Please confirm the exact name of the XAI factory (e.g. createXAI or otherwise) before applying this refactor.

src/actions/ollama.ts (1)

28-34: Silent catch: consider routing to your error pipeline

If you’re standardising on no console output, consider forwarding errors to your monitoring sink (e.g. Sentry) to retain observability.

src/app/api/chat/route.ts (3)

64-65: Unnecessary await on headers()

headers() is synchronous in Next.js. Drop the await for clarity.

-    const headersList = await headers();
+    const headersList = headers();

117-121: Consider structured logging instead of silent catch

Catching as _error and returning 500 without any server-side logging makes incidents hard to debug. Route logs to your observability tool (e.g. Sentry) or at least a server logger. Avoid console logs if they’re intentionally removed elsewhere.


92-99: DRY: factor out stream record creation

You create a streamId and insert a stream row in three places. Consider a helper to reduce duplication and centralise error handling.

Example helper (outside this file):

  • createStream(prisma, chatId): returns streamId
  • deleteStream(prisma, streamId)
    This also makes it easier to add metrics/retries later.

Also applies to: 134-140, 178-184

src/app/(public)/share/[id]/page.tsx (2)

15-16: Good: underscore unused destructured var

Renaming to _providerKey clarifies intent. Alternatively, avoid destructuring the key if it’s unused by iterating Object.values(Models). Both are fine.


152-152: Tailwind class conflict: remove duplicate text size

First <p> already uses text-xl. The second <p> includes both text-sm and text-xl; the latter wins. Drop text-sm for consistency.

-            <p
-              className={'ml-2 font-bold text-base-content/70 text-sm text-xl'}
-            >
+            <p className={'ml-2 font-bold text-base-content/70 text-xl'}>

Also applies to: 163-165

src/components/chatMeta.tsx (3)

112-120: Add aria-labels to icon-only buttons

Icon-only buttons aren’t keyboard reader friendly. Provide labels.

-            <button
+            <button aria-label="Rename chat"
               className="btn btn-ghost btn-sm ml-2"
               onClick={() => {
                 setOpen(true);
               }}
             >
               <Edit className="h-4 w-4" />
             </button>
-            <button
+            <button aria-label="Delete chat"
               className="btn btn-ghost btn-sm ml-2"
               onClick={() => {
                 handleDelete();
               }}
             >
               <Trash className="h-4 w-4" />
             </button>
-            {isShared && (
+            {isShared && (
               <button
+                aria-label="Copy share link"
                 className="btn btn-ghost btn-sm ml-2"
                 onClick={() => {
                   navigator.clipboard.writeText(
                     `${window.location.origin}/share/${id}`
                   );
                   toast.success('Share link copied to clipboard!');
                 }}
               >
                 <Link className="h-4 w-4" />
               </button>
             )}

Also applies to: 121-127, 129-139


68-78: Prefer router navigation over window.location + timeout

Use Next’s useRouter().replace('/chat') after delete. Avoid arbitrary delays and full reloads.

   const handleDelete = async () => {
     try {
       await deleteChat(id);
       toast.success('Chat deleted successfully!');
-      setTimeout(() => {
-        window.location.href = '/chat';
-      }, 1000);
+      // Navigate immediately after showing the toast
+      router.replace('/chat');
     } catch (_error) {
       toast.error('Failed to delete chat. Please try again.');
     }
   };

Additions outside this hunk:

// at top imports
import { useRouter } from 'next/navigation';

// inside component (near other state)
const router = useRouter();

45-47: Silent catches: consider structured logging

Keeping UI clean is fine, but route errors to your logging/monitoring stack so you have breadcrumbs when things go wrong.

Also applies to: 61-63, 75-77

src/components/sidebar.tsx (1)

17-27: Optional: empty state

Consider a simple empty state when there are no chats to guide users.

Example:

  • Render “No chats yet” with a CTA to create a new chat when data.chats?.length === 0.
src/components/chatContainer.tsx (4)

62-66: Call preventDefault before early return to avoid accidental form submits

If this handler is ever bound while isPublic is true, default browser submit could fire. Move e.preventDefault() above the guard.

-  if (isPublic) {
-    return;
-  }
-  e.preventDefault();
+  e.preventDefault();
+  if (isPublic) {
+    return;
+  }

68-71: Avoid shadowing input from useChat

Rename the local variable to improve readability and prevent confusion with useChat’s input prop.

-  const input = form.querySelector('input') as HTMLInputElement;
-  const message = input.value.trim();
+  const inputEl = form.querySelector('input') as HTMLInputElement;
+  const message = inputEl.value.trim();-  if (input) {
-    input.value = '';
+  if (inputEl) {
+    inputEl.value = '';

Also applies to: 97-102


170-187: Use the incremented retry count when constructing the ID

setRetryCount is async; using the stale retryCount can duplicate IDs. Compute the next count first.

- setRetryCount((prev) => prev + 1);
- setInternalStatus('ready');
+ const next = retryCount + 1;
+ setRetryCount(next);
+ setInternalStatus('ready');- append({
-   id: `retry-${retryCount}-${userMsg.id || Date.now()}`,
+ append({
+   id: `retry-${next}-${userMsg.id || Date.now()}`,

103-105: Consider logging errors to an observer (Sentry, console in dev)

Swallowing errors hampers diagnosis. Keep the toast, but also log in dev or send to telemetry.

src/app/(chat)/template.tsx (1)

18-20: Unnecessary await on redirect

redirect() throws and never resolves in Next.js. Drop await.

-  if (!claims) {
-    await redirect('/login');
-  }
+  if (!claims) {
+    redirect('/login');
+  }
src/components/chatInput.tsx (1)

137-141: Check Tailwind config for custom fractions

I didn’t find any tailwind.config.* file in the repo to confirm that a 10/13 fraction has been added. Please verify whether you’ve extended the theme with that custom width. If not, replace the invalid utility with an arbitrary value or a standard class. For example:

- <div className="fixed bottom-5 flex w-full max-w-10/13 items-center justify-center rounded-lg bg-neutral p-4 shadow-lg">
+ <div className="fixed bottom-5 flex w-full max-w-[77%] items-center justify-center rounded-lg bg-neutral p-4 shadow-lg">
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 15daba8 and 96cac8d.

⛔ Files ignored due to path filters (1)
  • bun.lock is excluded by !**/*.lock
📒 Files selected for processing (24)
  • next.config.ts (1 hunks)
  • package.json (3 hunks)
  • src/actions/chat.ts (1 hunks)
  • src/actions/image.ts (1 hunks)
  • src/actions/ollama.ts (1 hunks)
  • src/app/(chat)/chat/[id]/page.tsx (1 hunks)
  • src/app/(chat)/chat/page.tsx (1 hunks)
  • src/app/(chat)/image/page.tsx (0 hunks)
  • src/app/(chat)/settings/models/page.tsx (1 hunks)
  • src/app/(chat)/template.tsx (2 hunks)
  • src/app/(public)/share/[id]/page.tsx (4 hunks)
  • src/app/api/chat/route.ts (5 hunks)
  • src/components/OllamaModelList.tsx (5 hunks)
  • src/components/OpenRouterConfigButton.tsx (2 hunks)
  • src/components/OpenRouterModelList.tsx (6 hunks)
  • src/components/chat.tsx (4 hunks)
  • src/components/chatContainer.tsx (3 hunks)
  • src/components/chatInput.tsx (4 hunks)
  • src/components/chatMeta.tsx (4 hunks)
  • src/components/header.tsx (1 hunks)
  • src/components/imageGenerator.tsx (1 hunks)
  • src/components/modelCard.tsx (2 hunks)
  • src/components/sidebar.tsx (1 hunks)
  • tsconfig.json (1 hunks)
💤 Files with no reviewable changes (1)
  • src/app/(chat)/image/page.tsx
✅ Files skipped from review due to trivial changes (8)
  • src/app/(chat)/chat/[id]/page.tsx
  • src/app/(chat)/settings/models/page.tsx
  • src/components/modelCard.tsx
  • tsconfig.json
  • src/components/OpenRouterConfigButton.tsx
  • src/app/(chat)/chat/page.tsx
  • src/components/OpenRouterModelList.tsx
  • src/components/OllamaModelList.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
  • package.json
🧰 Additional context used
🧬 Code Graph Analysis (2)
src/app/api/chat/route.ts (1)
src/lib/prisma.ts (1)
  • prisma (3-3)
src/app/(chat)/template.tsx (1)
src/components/sidebar.tsx (1)
  • ChatSidebar (15-29)
🔇 Additional comments (10)
src/components/imageGenerator.tsx (1)

27-29: Catch variable rename is fine

Renaming to _error clearly signals it’s intentionally unused. No functional change.

src/components/header.tsx (1)

88-90: Style-only early-return change looks good

Block form improves readability; logic unchanged.

src/actions/chat.ts (1)

9-12: LGTM – clearer intent for unused variable

Renaming the unused destructured key to _providerKey improves clarity with no functional change.

src/actions/image.ts (1)

64-71: LGTM – clearer intent for unused variable

The rename to _providerKey is sound; behaviour unchanged.

src/actions/ollama.ts (1)

20-22: Style-only guard improvement is OK

Optional chaining is cleaner; behaviour unchanged.

src/app/(public)/share/[id]/page.tsx (2)

48-53: Good: simpler public check

!chat?.public is clearer and correctly handles null chat.


89-91: Good: strict equality for userId

Using === avoids coercion pitfalls.

src/components/chat.tsx (1)

40-41: Readability improvements look good

Using .at(-1) and optional chaining keep the code concise and safe.

Also applies to: 82-83, 92-94, 263-265

src/components/chatContainer.tsx (1)

149-150: Ensure Array.prototype.at(–1) Is Supported at Runtime

While TypeScript’s "lib": ["dom", "dom.iterable", "esnext"] gives you compile-time typings for .at(), your target: "ES2017" build will emit calls to Array.prototype.at un-downleveled. You should therefore:

• Confirm your browsers/environments actually support ES2022’s Array.prototype.at.
• Add or update a browserslist config (e.g. in package.json or a .browserslistrc) so downstream tooling can warn or polyfill as needed.
• Alternatively, bump your TS target to "ES2022" (or higher) or explicitly include a polyfill (e.g. via core-js) to cover older runtimes.

This applies at lines 149–150, 156–157 and 168–169 in src/components/chatContainer.tsx.

src/components/chatInput.tsx (1)

59-61: LGTM – clearer guard clause

The explicit block improves readability without changing behaviour.

Comment on lines +5 to +7
eslint: {
ignoreDuringBuilds: true,
},
Copy link

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Scope ESLint suppression; don’t disable it globally

Hard-coding ignoreDuringBuilds: true disables lint checks for all builds, reducing code-quality guardrails. Make it conditional via an environment flag and keep a separate lint job gating PRs.

Apply this diff to only disable ESLint where explicitly intended (e.g., bundle analysis job):

-  eslint: {
-    ignoreDuringBuilds: true,
-  },
+  eslint: {
+    ignoreDuringBuilds: process.env.NEXT_IGNORE_ESLINT === 'true',
+  },

Example: enable only in the bundle analysis workflow step/job:

# .github/workflows/nextjs_bundle_analysis.yml (snippet)
jobs:
  bundle-analysis:
    steps:
      - name: Build for analysis
        env:
          NEXT_IGNORE_ESLINT: 'true'
        run: npm run build

Optionally, enforce lint in a dedicated job:

jobs:
  lint:
    steps:
      - run: npm ci
      - run: npm run lint

Verification script to confirm a separate lint gate exists:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Searching for ESLint usage in workflows..."
fd -t f -H .github/workflows || true
rg -n -S -A2 -B2 "(next lint|eslint)" .github/workflows || echo "No explicit ESLint steps found in workflows."

echo "Checking for a lint script in package.json..."
rg -n -S '"lint"\s*:' package.json || echo "No 'lint' script found in package.json."

echo "If neither a workflow lint step nor a package.json lint script exists, consider adding them to keep linting enforced."

Length of output: 1093


Ensure ESLint is only skipped when explicitly requested and enforce linting in CI

We’ve confirmed there’s a "lint": "next lint" script in package.json but no dedicated lint job in your workflows. To keep build guardrails:

  1. Make ignoreDuringBuilds conditional on an env var:
    // next.config.ts
    eslint: {
    -  ignoreDuringBuilds: true,
    +  ignoreDuringBuilds: process.env.NEXT_IGNORE_ESLINT === 'true',
    },
  2. In your bundle‐analysis workflow (or wherever you need to skip ESLint), set the flag:
    # .github/workflows/nextjs_bundle_analysis.yml
    jobs:
      bundle-analysis:
        steps:
          - name: Build for analysis
            env:
              NEXT_IGNORE_ESLINT: 'true'
            run: npm run build
  3. Add a dedicated lint job to your CI workflows to run npm run lint, for example:
    # .github/workflows/lint.yml
    name: Lint
    on: [pull_request, push]
    jobs:
      eslint:
        runs-on: ubuntu-latest
        steps:
          - uses: actions/checkout@v3
          - run: npm ci
          - run: npm run lint

This ensures linting remains enforced on every PR while still allowing you to bypass it when explicitly needed.

🤖 Prompt for AI Agents
In next.config.ts around lines 5 to 7, the ESLint configuration currently always
ignores linting during builds. Modify the ignoreDuringBuilds setting to be
conditional based on an environment variable, such as NEXT_IGNORE_ESLINT, so
ESLint is only skipped when explicitly requested. Then, update your GitHub
workflows by adding the NEXT_IGNORE_ESLINT environment variable set to 'true' in
jobs where you want to skip linting, and create a dedicated lint job that runs
npm run lint on every pull request and push to enforce linting in CI.

Comment on lines +20 to 22
const _streamContext = createResumableStreamContext({
waitUntil: after,
});
Copy link

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Unused resumable stream context — remove or wire it up

_streamContext is created but never used. Either:

  • remove it (and the after/createResumableStreamContext imports) to avoid overhead, or
  • pass it into your streaming response to enable resume support.

At the moment it’s dead code.

Do you actually use resumable streams anywhere else? Run this to check:


🏁 Script executed:

#!/bin/bash
rg -n "createResumableStreamContext|toDataStreamResponse\\(" -A 2 -B 2

Length of output: 1507


Remove unused resumable‐stream code

_streamContext is created but never used in any toDataStreamResponse() call. Either wire it into your streaming responses to enable resume support or strip it out entirely to avoid dead code.

Locations to address:

  • src/app/api/chat/route.ts:10 – remove the after import from next/server
  • src/app/api/chat/route.ts:12 – remove the createResumableStreamContext import
  • src/app/api/chat/route.ts:20–22 – remove the _streamContext instantiation

Suggested diff:

-import { after, type NextRequest } from 'next/server';
+import { type NextRequest } from 'next/server';
-import { createResumableStreamContext } from 'resumable-stream';

-const _streamContext = createResumableStreamContext({
-  waitUntil: after,
-});

If you do intend to support resumable streams, pass _streamContext into your response calls, e.g.

return result.toDataStreamResponse(_streamContext);
🤖 Prompt for AI Agents
In src/app/api/chat/route.ts around lines 10, 12, and 20 to 22, the variable
_streamContext is created but never used, and the imports 'after' from
'next/server' and 'createResumableStreamContext' are also unused. To fix this,
either remove the import of 'after' at line 10, remove the import of
'createResumableStreamContext' at line 12, and delete the _streamContext
instantiation at lines 20 to 22, or if resumable streams are intended, pass
_streamContext into the toDataStreamResponse() call where the streaming response
is returned.

Comment on lines +149 to 157
messages.at(-1).role === 'user' &&
internalStatus !== 'streaming' &&
internalStatus !== 'submitted'
) {
const timer = setTimeout(() => {
if (
messages.length > 0 &&
messages[messages.length - 1].role === 'user' &&
messages.at(-1).role === 'user' &&
//@ts-expect-error
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Safer last-message checks; drop TypeScript suppressions

Use a local last with optional chaining to avoid //@ts-expect-error and potential undefined access. This also reads cleaner.

-  if (
-    messages.length > 0 &&
-    messages.at(-1).role === 'user' &&
-    internalStatus !== 'streaming' &&
-    internalStatus !== 'submitted'
-  ) {
+  const last = messages.length > 0 ? messages.at(-1) : undefined;
+  if (
+    last?.role === 'user' &&
+    internalStatus !== 'streaming' &&
+    internalStatus !== 'submitted'
+  ) {-        if (
-          messages.length > 0 &&
-          //@ts-expect-error
-          messages.at(-1).role === 'user' &&
-          //@ts-expect-error
-          internalStatus !== 'streaming' &&
-          //@ts-expect-error
-          internalStatus !== 'submitted'
-        ) {
+        const last2 = messages.length > 0 ? messages.at(-1) : undefined;
+        if (
+          last2?.role === 'user' &&
+          internalStatus !== 'streaming' &&
+          internalStatus !== 'submitted'
+        ) {
           setInternalStatus('error');
         }

Also applies to: 153-164

🤖 Prompt for AI Agents
In src/components/chatContainer.tsx around lines 149 to 157 and 153 to 164,
replace direct access to the last message using messages.at(-1) with a local
variable assigned via optional chaining (e.g., const last = messages.at(-1)) to
safely check its properties. Remove the //@ts-expect-error comment by ensuring
the code handles the possibility that last may be undefined before accessing its
role, improving type safety and code clarity.

Comment on lines +219 to 231
openRouterModels && openRouterModels.length > 0 ? (
<div className="grid w-full grid-cols-2 gap-2">
{openRouterModels.map((model) => (
<div
className={`card flex w-full items-center justify-start gap-3 bg-primary ${selectedModel === model.name ? 'btn-active' : ''}`}
key={model.id}
onClick={() => {
setSelectedModel(model.name);
setModelType('openrouter');
setOpen(false);
}}
>
<div
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Clickable cards should be interactive elements

Using <div onClick> harms accessibility and keyboard navigation. Use <button type="button"> with proper semantics.

-                            <div
-                              className={`card flex w-full items-center justify-start gap-3 bg-primary ${selectedModel === model.name ? 'btn-active' : ''}`}
-                              key={model.id}
-                              onClick={() => {
+                            <button
+                              type="button"
+                              className={`card flex w-full items-center justify-start gap-3 bg-primary ${selectedModel === model.name ? 'ring-2 ring-offset-2 ring-primary' : ''}`}
+                              key={model.id}
+                              onClick={() => {
                                 setSelectedModel(model.name);
                                 setModelType('openrouter');
                                 setOpen(false);
                               }}
-                            >
+                            >-                            </div>
+                            </button>

Apply similarly to the “providers” and “ollama” cards.

Also applies to: 239-245

🤖 Prompt for AI Agents
In src/components/chatInput.tsx around lines 219 to 231 and also lines 239 to
245, the clickable cards are implemented using <div> elements with onClick
handlers, which is not accessible for keyboard users. Replace these <div>
elements with <button type="button"> elements to provide proper semantics and
keyboard interactivity. Ensure the styling and event handlers are preserved on
the <button> elements to maintain the same visual appearance and behavior.

Comment on lines +121 to +127
className="btn btn-ghost btn-sm ml-2"
onClick={() => {
handleDelete();
}}
>
<Trash className="h-4 w-4" />
</button>
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Protect destructive action with a confirm

Deleting immediately is risky. Add a confirmation to prevent accidental loss.

-            <button
+            <button
               className="btn btn-ghost btn-sm ml-2"
-              onClick={() => {
-                handleDelete();
-              }}
+              onClick={() => {
+                if (confirm('Delete this chat permanently?')) {
+                  handleDelete();
+                }
+              }}
             >
               <Trash className="h-4 w-4" />
             </button>
🤖 Prompt for AI Agents
In src/components/chatMeta.tsx around lines 121 to 127, the delete button
triggers handleDelete immediately without user confirmation. Wrap the
handleDelete call inside a confirmation dialog by using window.confirm or a
similar method. Only call handleDelete if the user confirms the action,
preventing accidental deletions.

Comment on lines 129 to 139
<button
className="btn btn-ghost btn-sm ml-2"
onClick={() => {
handleDelete();
navigator.clipboard.writeText(
`${window.location.origin}/share/${id}`
);
toast.success('Share link copied to clipboard!');
}}
>
<Trash className="h-4 w-4" />
<Link className="h-4 w-4" />
</button>
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Clipboard operation should handle failures

Clipboard API can fail (permissions, HTTP context). Add try/catch to surface errors.

-              onClick={() => {
-                navigator.clipboard.writeText(
-                  `${window.location.origin}/share/${id}`
-                );
-                toast.success('Share link copied to clipboard!');
-              }}
+              onClick={async () => {
+                try {
+                  await navigator.clipboard.writeText(
+                    `${window.location.origin}/share/${id}`
+                  );
+                  toast.success('Share link copied to clipboard!');
+                } catch (_e) {
+                  toast.error('Failed to copy link. Please copy it manually.');
+                }
+              }}
📝 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
<button
className="btn btn-ghost btn-sm ml-2"
onClick={() => {
handleDelete();
navigator.clipboard.writeText(
`${window.location.origin}/share/${id}`
);
toast.success('Share link copied to clipboard!');
}}
>
<Trash className="h-4 w-4" />
<Link className="h-4 w-4" />
</button>
<button
className="btn btn-ghost btn-sm ml-2"
onClick={async () => {
try {
await navigator.clipboard.writeText(
`${window.location.origin}/share/${id}`
);
toast.success('Share link copied to clipboard!');
} catch (_e) {
toast.error('Failed to copy link. Please copy it manually.');
}
}}
>
<Link className="h-4 w-4" />
</button>
🤖 Prompt for AI Agents
In src/components/chatMeta.tsx around lines 129 to 139, the clipboard write
operation does not handle potential failures such as permission issues or
unsupported contexts. Wrap the navigator.clipboard.writeText call inside a
try/catch block, and in the catch block, show an error toast or log the error to
inform the user that copying failed.

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