Skip to content

Conversation

@nedhmn
Copy link
Owner

@nedhmn nedhmn commented May 17, 2025

Summary by CodeRabbit

  • New Features

    • Introduced a Yu-Gi-Oh! Ruling AI Chatbot web app with interactive chat powered by AI, including a chat interface, message display, and input controls.
    • Added theme selection (Light, Dark, System) and a navigation bar for improved user experience.
    • Implemented custom-styled Markdown rendering for chat messages and enhanced UI components.
  • Improvements

    • Updated project titles and documentation for clarity and consistency.
    • Enhanced global styling, including custom scrollbars and improved layout with a global UI provider.
    • Added support for module path aliases and updated package configurations for better maintainability.
    • Improved environment variable management with typed validation and secure handling.
  • Bug Fixes

    • Improved environment variable loading for reliability across environments.
  • Chores

    • Updated dependencies and internal configurations for better performance and compatibility.
    • Streamlined workflow and build processes.

@coderabbitai
Copy link

coderabbitai bot commented May 17, 2025

Caution

Review failed

The pull request is closed.

## Walkthrough

This update introduces a new Yu-Gi-Oh! Ruling AI Chatbot web application, replacing previous placeholder content with a fully functional chat interface. It adds complete chat UI components, API routes for OpenAI-based chat, and theme support. The update also restructures package configurations, environment handling, and UI library exports, while cleaning up obsolete files and documentation.

## Changes

| File(s) / Path(s)                                                     | Change Summary |
|-----------------------------------------------------------------------|---------------|
| `.github/workflows/lint.yml`                                          | Consolidated build and type-check steps; removed separate build step and replaced type-check with build command; added environment variables from secrets. |
| `README.md`, `apps/web/README.md`                                     | Updated project title in main README; replaced web app README content with a single title line. |
| `apps/web/app/(home)/page.tsx`, `apps/web/app/api/chat/route.ts`      | Added new home page client component rendering the chat UI; introduced POST API route for chat using OpenAI streaming. |
| `apps/web/app/globals.css`                                            | Removed root color variables and dark mode query; added custom scrollbar and paragraph margin styles. |
| `apps/web/app/layout.tsx`, `apps/web/components/navbar.tsx`           | Switched font to Inter, added global Provider and Navbar components with theme toggle. |
| `apps/web/app/page.tsx`, `packages/ui/src/components/card.tsx`, `packages/ui/src/components/gradient.tsx`, `packages/ui/src/components/turborepo-logo.tsx`, `apps/web/next.config.ts` | Deleted old landing page, UI card, gradient, and logo components, and old Next.js config. |
| `apps/web/components/chat-input-area.tsx`, `apps/web/components/chat-input-controls.tsx`, `apps/web/components/chat-input.tsx`, `apps/web/components/chat-messages.tsx`, `apps/web/components/chat.tsx` | Added new chat UI components for input, controls, message display, and chat logic. |
| `apps/web/hooks/use-send-message.ts`                                  | Added custom hook for message sending and cancellation logic. |
| `apps/web/next.config.mjs`                                            | Added Next.js config to transpile `@repo/ui` package. |
| `apps/web/package.json`, `apps/web/tsconfig.json`                     | Added dependencies for chat, markdown, theme, icons; updated TypeScript paths and includes. |
| `packages/embeddings/package.json`, `packages/pinecone/package.json`  | Changed internal import paths from `src` to `dist`, removed `@repo/utils`, added `dotenv` dependency. |
| `packages/embeddings/src/config.ts`, `packages/pinecone/src/config.ts`| Switched environment loading from custom utility to `dotenv` for `.env.local`. |
| `packages/seeder/src/index.ts`, `packages/seeder/src/scraper.ts`, `packages/seeder/src/utils.ts` | Minor formatting: adjusted import order and added trailing commas. |
| `packages/ui/components.json`, `packages/ui/tsconfig.json`            | Enabled React Server Components, updated path aliases from `@repo/ui/*` to `#*`. |
| `packages/ui/package.json`                                            | Marked package as private/module, revised exports/imports fields, added dependencies for dropdown, theme, markdown, and toasts. |
| `packages/ui/src/components/markdown-components.tsx`                  | Added custom Markdown component mapping for consistent rendering. |
| `packages/ui/src/components/mode-toggle.tsx`                          | Added theme toggle dropdown with sun/moon icons and theme selection. |
| `packages/ui/src/components/ui/button.tsx`                            | Changed utility import path, added pointer cursor class. |
| `packages/ui/src/components/ui/dropdown-menu.tsx`                     | Added comprehensive dropdown menu component suite using Radix primitives. |
| `packages/ui/src/components/ui/input.tsx`, `packages/ui/src/components/ui/textarea.tsx` | Added styled Input and Textarea components. |
| `packages/ui/src/components/ui/sonner.tsx`                            | Added Toaster component for theme-aware toast notifications. |
| `packages/ui/src/index.tsx`, `packages/ui/src/providers/theme.tsx`    | Added Provider and ThemeProvider components for theme context and toasts. |
| `apps/web/app/env.ts`                                                 | Added typed environment configuration with server-only variables validated and separated from client variables. |
| `turbo.json`                                                         | Added environment variables to build task configuration. |

## Sequence Diagram(s)

```mermaid
sequenceDiagram
    participant User
    participant ChatInput
    participant Chat
    participant API_Route
    participant OpenAI

    User->>ChatInput: Enter message and send
    ChatInput->>Chat: handleSendMessage (append user message)
    Chat->>API_Route: POST /api/chat with messages
    API_Route->>OpenAI: Stream response (gpt-4o-mini)
    OpenAI-->>API_Route: Streamed completion
    API_Route-->>Chat: Streamed response
    Chat->>ChatMessages: Update message list
    ChatMessages->>User: Render chat history

Possibly related PRs

  • nedhmn/ygo-ruling-ai-chatbot#3: Both PRs modify the .github/workflows/lint.yml workflow, with this PR consolidating steps and the related PR adding new lint workflow steps, indicating a close connection in CI configuration changes.
  • nedhmn/ygo-ruling-ai-chatbot#4: Contains identical changes including chat UI components, API routes, environment handling, and workflow updates, likely a duplicate or closely related PR.

<!-- walkthrough_end -->
<!-- internal state start -->


<!-- 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 -->

<!-- internal state end -->
<!-- announcements_start -->

> [!NOTE]
> <details>
> <summary>⚡️ AI Code Reviews for VS Code, Cursor, Windsurf</summary>
> 
> CodeRabbit now has a plugin for VS Code, Cursor and Windsurf. This brings AI code reviews directly in the code editor. Each commit is reviewed immediately, finding bugs before the PR is raised. Seamless context handoff to your AI code agent ensures that you can easily incorporate review feedback.
> Learn more [here](http://coderabbit.ai/ide).
> 
> </details>

---

> [!NOTE]
> <details>
> <summary>⚡️ Faster reviews with caching</summary>
> 
> CodeRabbit now supports caching for code and dependencies, helping speed up reviews. This means quicker feedback, reduced wait times, and a smoother review experience overall. Cached data is encrypted and stored securely. This feature will be automatically enabled for all accounts on May 16th. To opt out, configure `Review - Disable Cache` at either the organization or repository level. If you prefer to disable all data retention across your organization, simply turn off the `Data Retention` setting under your Organization Settings.
> Enjoy the performance boost—your workflow just got faster.
> 
> </details>

<!-- announcements_end -->

---

<details>
<summary>📜 Recent review details</summary>

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


<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between 88ee0418eab5da5ea231902ed631e25f7bea22eb and e03135b2ef81bffc61c739d57b661ebd6585382a.

</details>

<details>
<summary>⛔ Files ignored due to path filters (1)</summary>

* `pnpm-lock.yaml` is excluded by `!**/pnpm-lock.yaml`

</details>

<details>
<summary>📒 Files selected for processing (7)</summary>

* `.github/workflows/lint.yml` (1 hunks)
* `apps/web/app/env.ts` (1 hunks)
* `apps/web/components/chat-input-controls.tsx` (1 hunks)
* `apps/web/components/chat-input.tsx` (1 hunks)
* `apps/web/package.json` (2 hunks)
* `packages/ui/src/components/markdown-components.tsx` (1 hunks)
* `turbo.json` (1 hunks)

</details>

</details>
<!-- tips_start -->

---

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.

<details>
<summary>❤️ Share</summary>

- [X](https://twitter.com/intent/tweet?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A&url=https%3A//coderabbit.ai)
- [Mastodon](https://mastodon.social/share?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A%20https%3A%2F%2Fcoderabbit.ai)
- [Reddit](https://www.reddit.com/submit?title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&text=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code.%20Check%20it%20out%3A%20https%3A//coderabbit.ai)
- [LinkedIn](https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Fcoderabbit.ai&mini=true&title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&summary=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code)

</details>

<details>
<summary>🪧 Tips</summary>

### Chat

There are 3 ways to chat with [CodeRabbit](https://coderabbit.ai?utm_source=oss&utm_medium=github&utm_campaign=nedhmn/ygo-ruling-ai-chatbot&utm_content=4):

> ‼️ **IMPORTANT**
> Auto-reply has been disabled for this repository in the CodeRabbit settings. The CodeRabbit bot will not respond to your replies unless it is explicitly tagged.
- 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.`
  -	`@coderabbitai modularize this function.`
- 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.`
  - `@coderabbitai help me debug CodeRabbit configuration file.`

### Support

Need help? Create a ticket on our [support page](https://www.coderabbit.ai/contact-us/support) for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

### 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 sequence diagram` to generate a sequence diagram of the changes in 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](https://docs.coderabbit.ai/guides/configure-coderabbit) 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](https://docs.coderabbit.ai) for detailed information on how to use CodeRabbit.
- Join our [Discord Community](http://discord.gg/coderabbit) to get help, request features, and share feedback.
- Follow us on [X/Twitter](https://twitter.com/coderabbitai) for updates and announcements.

</details>

<!-- tips_end -->

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

🔭 Outside diff range comments (1)
apps/web/app/layout.tsx (1)

9-12: 🛠️ Refactor suggestion

Update metadata to reflect actual application.

The metadata still contains generic Turborepo placeholder content.

export const metadata: Metadata = {
-  title: "Create Turborepo",
-  description: "Generated by create turbo",
+  title: "Yu-Gi-Oh! Ruling Chatbot",
+  description: "AI-powered Yu-Gi-Oh! card ruling assistant",
};
🧹 Nitpick comments (18)
apps/web/app/(home)/page.tsx (1)

1-11: Simple and clean component structure.

The Page component is well-structured, rendering the Chat component within a minimal wrapper.

Consider whether the wrapping div is necessary if it's not providing any layout, styling or semantic function:

export default function Page() {
  return (
-   <div>
      <Chat />
-   </div>
  );
}
apps/web/app/globals.css (1)

5-21: Custom scrollbar styling improves UI consistency.

The scrollbar styling enhances the visual experience across WebKit browsers.

Consider adding Firefox scrollbar styling for cross-browser consistency:

/* Custom scrollbar styles */
::-webkit-scrollbar {
  width: 8px;
}

::-webkit-scrollbar-track {
  background: transparent;
}

::-webkit-scrollbar-thumb {
  background: #888;
  border-radius: 4px;
}

::-webkit-scrollbar-thumb:hover {
  background: #555;
}

+/* Firefox scrollbar styles */
+* {
+  scrollbar-width: thin;
+  scrollbar-color: #888 transparent;
+}
apps/web/components/navbar.tsx (2)

1-16: Clean Navbar implementation with good accessibility and responsive design

The Navbar component is well-structured with appropriate positioning, z-index, and responsive layout. The component correctly integrates the ModeToggle and provides a clear title.

One potential enhancement would be to make the title a link to the home page:

- <span>Yu-Gi-Oh! Ruling Chatbot</span>
+ <a href="/" className="hover:opacity-80 transition-opacity">Yu-Gi-Oh! Ruling Chatbot</a>

5-5: Consider adding aria-label for better accessibility

For better accessibility, consider adding an aria-label to the header element.

- <header className="fixed top-0 left-0 right-0 border-b w-full bg-background z-10">
+ <header className="fixed top-0 left-0 right-0 border-b w-full bg-background z-10" aria-label="Main navigation">
apps/web/app/api/chat/route.ts (1)

4-4: Unused constant value.

The maxDuration constant is defined but not utilized within the API route handler. This may be intended for future use with Next.js Edge Runtime configurations, but currently serves no purpose.

Either remove the unused constant or add a comment explaining its intended future use.

apps/web/components/chat.tsx (3)

9-14: Consider enhancing error handling in chat hook.

The current error handler only shows a generic toast message without providing specific error details or recovery options.

Improve the error handling to provide more context and potential recovery options:

 const chatHelpers = useChat({
   id: "primary",
-  onError: () => {
-    toast.error("An error occurred, please try again!");
+  onError: (error) => {
+    console.error("Chat error:", error);
+    toast.error(
+      error.message || "An error occurred, please try again!",
+      {
+        description: "You can try refreshing the page or sending a different message.",
+        action: {
+          label: "Retry",
+          onClick: () => chatHelpers.reload(),
+        },
+      }
+    );
   },
 });

19-19: Consider container constraints for better layout.

The chat container uses min-h-screen without width constraints, which might lead to excessively wide content on large screens.

Add width constraints for better readability on larger screens:

-  <div className="min-h-screen bg-background pb-24">
+  <div className="min-h-screen bg-background pb-24 max-w-4xl mx-auto">

20-21: Remove unnecessary closing tag syntax.

The ChatMessages component uses a redundant self-closing tag syntax with an explicit closing tag.

Standardize the component usage style:

-  <ChatMessages messages={messages}></ChatMessages>
+  <ChatMessages messages={messages} />
packages/ui/src/components/ui/input.tsx (2)

5-19: Consider adding forward ref for better component integration.

The Input component doesn't use forwardRef, which limits its usability when ref forwarding is needed in form libraries or focus management.

Implement forwardRef for better component interoperability:

-function Input({ className, type, ...props }: React.ComponentProps<"input">) {
+const Input = React.forwardRef<
+  HTMLInputElement,
+  React.ComponentProps<"input">
+>(({ className, type, ...props }, ref) => {
   return (
     <input
       type={type}
       data-slot="input"
       className={cn(
         "file:text-foreground placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground dark:bg-input/30 border-input flex h-9 w-full min-w-0 rounded-md border bg-transparent px-3 py-1 text-base shadow-xs transition-[color,box-shadow] outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
         "focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]",
         "aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
         className,
       )}
+      ref={ref}
       {...props}
     />
   );
-}
+});
+
+Input.displayName = "Input";

10-15: Consider extracting long className string to a variable.

The className string is very long and combines multiple styles, making it harder to read and maintain.

Extract the base styles to variables for better readability:

+const baseStyles = "file:text-foreground placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground dark:bg-input/30 border-input flex h-9 w-full min-w-0 rounded-md border bg-transparent px-3 py-1 text-base shadow-xs transition-[color,box-shadow] outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm";
+const focusStyles = "focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]";
+const invalidStyles = "aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive";

 // In the component render
       className={cn(
-        "file:text-foreground placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground dark:bg-input/30 border-input flex h-9 w-full min-w-0 rounded-md border bg-transparent px-3 py-1 text-base shadow-xs transition-[color,box-shadow] outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
-        "focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]",
-        "aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
+        baseStyles,
+        focusStyles,
+        invalidStyles,
         className,
       )}
packages/ui/src/components/ui/textarea.tsx (2)

8-13: Consider breaking down the lengthy className for better readability

While the implementation is solid, the extremely long className string makes the code harder to read and maintain. Consider breaking it into logical groups using template literals or creating specific utility classes in your Tailwind config.

-      className={cn(
-        "border-input placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:bg-input/30 flex field-sizing-content min-h-16 w-full rounded-md border bg-transparent px-3 py-2 text-base shadow-xs transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
-        className,
-      )}
+      className={cn(
+        // Base appearance
+        "flex field-sizing-content min-h-16 w-full rounded-md border bg-transparent",
+        // Typography and spacing
+        "px-3 py-2 text-base md:text-sm",
+        // Theming
+        "border-input placeholder:text-muted-foreground dark:bg-input/30",
+        // States
+        "focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]",
+        "aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40",
+        "disabled:cursor-not-allowed disabled:opacity-50",
+        // Effects
+        "shadow-xs transition-[color,box-shadow] outline-none",
+        className,
+      )}

8-8: Document the purpose of the data-slot attribute

The data-slot="textarea" attribute isn't standard HTML. Consider adding a comment explaining its purpose or if it's part of a design system.

apps/web/hooks/use-send-message.ts (1)

20-25: Consider adding error handling

The handleSendMessage function should include error handling for potential failures when calling stop() or append().

  const handleSendMessage = () => {
    // Cancel message generation
    if (isGeneratingResponse) {
-     stop();
+     try {
+       stop();
+     } catch (error) {
+       console.error("Error stopping message generation:", error);
+     }
      return;
    }
apps/web/components/chat-messages.tsx (2)

11-13: Optimize rendering performance with memoization

For better performance with larger message lists, consider memoizing the component and its callback functions.

- const ChatMessages = ({ messages }: ChatMessagesProps) => {
+ const ChatMessages = React.memo(({ messages }: ChatMessagesProps) => {
  const messagesRef = useRef<HTMLDivElement>(null);

  // Component implementation...
- };
+ });

35-53: Enhance accessibility for chat messages

The message elements would benefit from additional accessibility attributes to improve screen reader support.

  <div
    key={message.id}
    className={cn(
      "flex",
      message.role === "user" ? "justify-end" : "justify-start",
    )}
  >
    <div
+     role="log"
+     aria-label={`${message.role} message`}
      className={cn(
        "rounded-lg px-4 py-2 max-w-[80%]",
        message.role === "user"
          ? "bg-primary text-primary-foreground"
          : "bg-secondary text-foreground system-message-styles",
      )}
    >
apps/web/components/chat-input-controls.tsx (1)

8-12: Rename interface to follow naming conventions

The interface name should be suffixed with "Props" to follow standard React naming conventions.

- interface ChatInputControls {
+ interface ChatInputControlsProps {
  chatHelpers: UseChatHelpers;
  input: string;
  setInput: (value: string) => void;
}
apps/web/components/chat-input-area.tsx (1)

36-48: Consider adding aria attributes for improved accessibility.

While the component has good functionality, adding aria attributes would enhance accessibility for screen readers.

 <Textarea
   value={input}
   placeholder="How can I help with Yu-Gi-Oh! rulings?"
   className="pr-12 py-3 min-h-[60px] max-h-[120px] focus-visible:ring-0 focus-visible:ring-offset-0 focus-visible:outline-none border-0 resize-none"
   disabled={isGeneratingResponse}
   autoFocus
+  aria-label="Chat message input"
+  aria-disabled={isGeneratingResponse}
   onChange={(event) => {
     setInput(event.currentTarget.value);
   }}
   onKeyDown={handleKeyDown}
 />
packages/ui/src/components/ui/dropdown-menu.tsx (1)

62-83: Improve variant implementation with composition over inheritance.

The DropdownMenuItem component uses data attributes for variant styling, but a more maintainable approach would be to use composition with separate components.

Consider creating separate components for destructive menu items to make the API more explicit:

function DropdownMenuDestructiveItem({
  className,
  ...props
}: React.ComponentProps<typeof DropdownMenuItem>) {
  return (
    <DropdownMenuItem
      variant="destructive"
      className={className}
      {...props}
    />
  );
}

// Then export it:
export {
  // ...other exports
  DropdownMenuDestructiveItem,
}

This would allow consumers to use a more explicit API:

<DropdownMenuDestructiveItem>Delete</DropdownMenuDestructiveItem>

instead of:

<DropdownMenuItem variant="destructive">Delete</DropdownMenuItem>
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between a77cc30 and f48bb1e.

⛔ Files ignored due to path filters (6)
  • apps/web/public/circles.svg is excluded by !**/*.svg
  • apps/web/public/favicon.ico is excluded by !**/*.ico
  • apps/web/public/next.svg is excluded by !**/*.svg
  • apps/web/public/turborepo.svg is excluded by !**/*.svg
  • apps/web/public/vercel.svg is excluded by !**/*.svg
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (41)
  • .github/workflows/lint.yml (1 hunks)
  • README.md (1 hunks)
  • apps/web/README.md (1 hunks)
  • apps/web/app/(home)/page.tsx (1 hunks)
  • apps/web/app/api/chat/route.ts (1 hunks)
  • apps/web/app/globals.css (1 hunks)
  • apps/web/app/layout.tsx (2 hunks)
  • apps/web/app/page.tsx (0 hunks)
  • apps/web/components/chat-input-area.tsx (1 hunks)
  • apps/web/components/chat-input-controls.tsx (1 hunks)
  • apps/web/components/chat-input.tsx (1 hunks)
  • apps/web/components/chat-messages.tsx (1 hunks)
  • apps/web/components/chat.tsx (1 hunks)
  • apps/web/components/navbar.tsx (1 hunks)
  • apps/web/hooks/use-send-message.ts (1 hunks)
  • apps/web/next.config.mjs (1 hunks)
  • apps/web/next.config.ts (0 hunks)
  • apps/web/package.json (2 hunks)
  • apps/web/tsconfig.json (1 hunks)
  • packages/embeddings/package.json (2 hunks)
  • packages/embeddings/src/config.ts (1 hunks)
  • packages/pinecone/package.json (2 hunks)
  • packages/pinecone/src/config.ts (2 hunks)
  • packages/seeder/src/index.ts (2 hunks)
  • packages/seeder/src/scraper.ts (2 hunks)
  • packages/seeder/src/utils.ts (1 hunks)
  • packages/ui/components.json (2 hunks)
  • packages/ui/package.json (2 hunks)
  • packages/ui/src/components/card.tsx (0 hunks)
  • packages/ui/src/components/gradient.tsx (0 hunks)
  • packages/ui/src/components/markdown-components.tsx (1 hunks)
  • packages/ui/src/components/mode-toggle.tsx (1 hunks)
  • packages/ui/src/components/turborepo-logo.tsx (0 hunks)
  • packages/ui/src/components/ui/button.tsx (2 hunks)
  • packages/ui/src/components/ui/dropdown-menu.tsx (1 hunks)
  • packages/ui/src/components/ui/input.tsx (1 hunks)
  • packages/ui/src/components/ui/sonner.tsx (1 hunks)
  • packages/ui/src/components/ui/textarea.tsx (1 hunks)
  • packages/ui/src/index.tsx (1 hunks)
  • packages/ui/src/providers/theme.tsx (1 hunks)
  • packages/ui/tsconfig.json (1 hunks)
💤 Files with no reviewable changes (5)
  • apps/web/next.config.ts
  • packages/ui/src/components/gradient.tsx
  • packages/ui/src/components/turborepo-logo.tsx
  • packages/ui/src/components/card.tsx
  • apps/web/app/page.tsx
🧰 Additional context used
🧬 Code Graph Analysis (9)
packages/seeder/src/index.ts (1)
packages/embeddings/src/embeddings.ts (1)
  • generateEmbeddings (33-42)
apps/web/app/layout.tsx (2)
packages/ui/src/index.tsx (1)
  • Provider (5-10)
apps/web/components/navbar.tsx (1)
  • Navbar (3-16)
apps/web/components/navbar.tsx (1)
packages/ui/src/components/mode-toggle.tsx (1)
  • ModeToggle (19-44)
packages/ui/src/components/ui/input.tsx (1)
packages/ui/src/lib/utils.ts (1)
  • cn (4-6)
packages/ui/src/components/ui/textarea.tsx (1)
packages/ui/src/lib/utils.ts (1)
  • cn (4-6)
packages/ui/src/index.tsx (2)
packages/ui/src/providers/theme.tsx (1)
  • ThemeProvider (4-17)
packages/ui/src/components/ui/sonner.tsx (1)
  • Toaster (25-25)
packages/ui/src/components/mode-toggle.tsx (2)
packages/ui/src/components/ui/dropdown-menu.tsx (4)
  • DropdownMenu (242-242)
  • DropdownMenuTrigger (244-244)
  • DropdownMenuContent (245-245)
  • DropdownMenuItem (248-248)
packages/ui/src/components/ui/button.tsx (1)
  • Button (59-59)
apps/web/components/chat-input-controls.tsx (2)
apps/web/hooks/use-send-message.ts (1)
  • useSendMessage (12-48)
packages/ui/src/components/ui/button.tsx (1)
  • Button (59-59)
packages/ui/src/components/ui/dropdown-menu.tsx (1)
packages/ui/src/lib/utils.ts (1)
  • cn (4-6)
🔇 Additional comments (50)
packages/seeder/src/utils.ts (1)

24-26: Code style improvement with trailing comma

The addition of a trailing comma to the error message string follows consistent code style practices, making future code modifications cleaner and reducing the likelihood of merge conflicts.

packages/seeder/src/scraper.ts (2)

13-15: Code style improvement with trailing comma

The addition of a trailing comma after the callback function in the urls.map() improves code style consistency and follows modern JavaScript/TypeScript conventions.


56-58: Code style improvement with trailing comma

The addition of a trailing comma after the regular expression literal in the text.match() call maintains consistent formatting throughout the codebase.

packages/embeddings/src/config.ts (1)

1-6: Improved environment configuration approach

The change from a custom utility to directly using the dotenv package simplifies the environment variable loading process and reduces dependencies. This is a good architectural improvement that maintains the same functionality while reducing complexity.

README.md (1)

1-2: Project title update

The project title has been updated to use the full "Yu-Gi-Oh!" name instead of the abbreviated "YGO," which provides better clarity and consistency with the branding throughout the project.

.github/workflows/lint.yml (1)

48-49: Verify type-checking coverage in build step.

The "Check types" step now runs pnpm build, which may not perform an explicit type-check (tsc --noEmit). Ensure your build script includes type validation or consider restoring a separate pnpm check-types step to catch type errors before merge.

packages/ui/tsconfig.json (1)

7-8: Confirm updated path alias prefix.

The alias #* now maps to ./src/*, replacing the previous @repo/ui/*. Verify that all internal imports and any downstream consumers (e.g., Next.js, other packages) have been updated to use the new #-based alias and resolve correctly.

packages/seeder/src/index.ts (4)

3-5: Reordered the import of getPineconeIndex above upsertVectors. This change is purely organizational and has no functional impact.


36-39: Console log formatting updated to multiline with trailing comma. No change to functionality.


44-46: Adjusted the processing summary log for rulings batching; purely formatting.


57-59: String template formatting in the augmentedRulings map was tweaked. Functionality remains unchanged.

packages/ui/components.json (2)

4-4: Enabling React Server Components support.

The "rsc": true flag is set to enable React Server Components. Ensure your Next.js version and SSR setup are compatible and that you’ve tested the hydration boundaries accordingly.


14-15: Validate component and utils alias updates.

Aliases for "components" and "utils" have been updated to "#components" and "#lib/utils". Confirm these align with your TypeScript and runtime module resolution settings so imports resolve as expected.

apps/web/tsconfig.json (3)

4-7: Improved import organization with path aliases

Adding baseUrl and path aliases is a great improvement for developer experience. The @/* alias pattern is a standard practice in Next.js applications that makes imports cleaner and more maintainable, especially for deeply nested components.


18-18: Updated Next.js config reference

Changing from next.config.ts to next.config.mjs reflects the move to a JavaScript module for the Next.js configuration. According to the summary, this new config adds transpilePackages for @repo/ui, which is necessary for proper integration of the UI components.


21-21: Simplified exclude configuration

The exclude array has been reformatted to a more concise single-line format, which is a minor but nice improvement to the file's readability.

apps/web/next.config.mjs (1)

1-8: Configuration looks good for transpiling the UI package.

The next.config.mjs file properly configures Next.js to transpile the @repo/ui package, which is necessary for integrating shared UI components from your monorepo. The reference to the Turborepo example is helpful for future contributors.

apps/web/app/globals.css (1)

23-26: Good spacing for system message paragraphs.

Adding top margin to sequential paragraphs improves readability in system messages.

apps/web/app/layout.tsx (2)

3-7: Good font choice and component imports.

Switching to Inter font and importing the Provider and Navbar components aligns well with modern UI practices.


20-26: Good implementation of app layout structure with theme provider.

The layout properly integrates the Provider component for theming and toast notifications, and includes the Navbar for consistent navigation.

The suppressHydrationWarning attribute is used on the HTML element, which can mask potential hydration issues. Verify if this is necessary or if there's an underlying hydration mismatch that should be properly addressed instead.

packages/ui/src/providers/theme.tsx (1)

1-17: Good implementation of ThemeProvider with sensible defaults

The implementation is clean and follows good practices with a wrapper component providing sensible defaults. The component correctly forwards additional properties while maintaining the core theme configuration.

packages/pinecone/src/config.ts (2)

2-2: Good migration from custom utility to direct dotenv usage

The change from a custom environment loading utility to directly using dotenv simplifies the codebase and reduces dependencies. Make sure the dotenv package is properly added to package.json.

Also applies to: 5-5


16-17: Fixed missing comma in describe documentation

Good catch on fixing the missing comma in the description for PINECONE_INDEX_DIMENSION.

packages/ui/src/index.tsx (1)

1-10: Well-structured Provider component composition

The Provider component elegantly composes the ThemeProvider with the Toaster component, creating a clean abstraction for the application. The pattern of spreading properties to the ThemeProvider ensures flexibility while maintaining a clean API.

apps/web/package.json (2)

14-26: LGTM: Well-structured dependencies for the chat application.

The package dependencies are correctly added to support the new chat functionality, including AI SDK, streaming, UI components, and markdown rendering.


5-5: LGTM: Proper module type definition.

The repositioning of the "type": "module" field is correct and follows best practices.

packages/ui/src/components/ui/input.tsx (1)

3-3:

❓ Verification inconclusive

Update import path for better compatibility.

The import uses a #lib prefix which may require special module resolution configuration.

Check if the import path is correctly configured in your tsconfig.json and build tools:


🏁 Script executed:

#!/bin/bash
# Check the project configuration for path aliases
cat tsconfig.json | grep -A 10 "paths"

# Check if there are any other imports using the same pattern
find . -type f -name "*.tsx" -o -name "*.ts" | xargs grep -l "#lib" | grep -v "input.tsx"

Length of output: 312


🏁 Script executed:

#!/bin/bash
# Locate all TypeScript config files and inspect their path mappings
find . -type f \( -name "tsconfig.json" -o -name "tsconfig.*.json" \) | while read cfg; do
  echo "=== $cfg ==="
  grep -R '"paths"' -A5 "$cfg" || echo "  No \"paths\" section in $cfg"
done

# Check for alias definitions in common build configs (Vite, Webpack, Babel)
grep -R "alias" -n --include="vite.config.*" --include="webpack.config.*" --include=".babelrc*" || echo "No alias entries found in Vite/Webpack/Babel configs"

Length of output: 2100


Verify #lib alias support in your build pipeline

We’ve confirmed that in packages/ui/tsconfig.json you have:

"paths": {
  "#*": ["./src/*"]
}

but there’s no matching alias in any Vite, Webpack or Babel config. Without a runtime/bundler alias or a path‐transform plugin, imports like:

import { cn } from "#lib/utils.js";

will compile in TypeScript but fail at build or runtime.

• Add a corresponding alias to your bundler (e.g. in vite.config.ts:
{ resolve: { alias: { '#': path.resolve(__dirname, 'src') } } })
• Or update the import to a relative path:

- import { cn } from "#lib/utils.js";
+ import { cn } from "../../lib/utils.js";
packages/ui/src/components/ui/sonner.tsx (1)

1-25: Good implementation with theme integration!

The component effectively wraps the Sonner toaster library with theme awareness, properly passing the current theme and providing theme-specific styling through CSS variables. The component follows best practices for client components and prop forwarding.

packages/ui/src/components/mode-toggle.tsx (1)

1-44: Well-structured theme toggle with good accessibility

This component implements a theme toggle with excellent UX considerations:

  • Proper accessibility with screen reader text
  • Visual feedback with animated icon transitions
  • Clean dropdown implementation with all standard theme options
  • Efficient state management with the useTheme hook

The code follows modern React patterns and best practices.

apps/web/hooks/use-send-message.ts (1)

12-48: Good implementation of the message sending hook

The hook provides a clean abstraction for handling chat message submission logic, with proper handling of edge cases like ongoing message generation and empty inputs.

apps/web/components/chat-messages.tsx (1)

11-63: Well-structured messages component with auto-scrolling

The component is well-designed, with good separation of concerns, proper handling of empty states, and efficient auto-scrolling to the latest messages.

apps/web/components/chat-input.tsx (1)

12-37: Well-composed input component with clean responsibility separation

Good component composition with clear separation of responsibilities between parent and child components. The state management is handled at the appropriate level.

apps/web/components/chat-input-area.tsx (4)

3-6: Clean import organization and segregation.

The imports are well-organized, categorized by local hooks, external packages, and React types.


8-12: Well-defined prop types.

The component has clear TypeScript interfaces with self-explanatory property names that make the component API easy to understand.


18-27: Good use of custom hooks for separation of concerns.

The component correctly extracts chat status and creates a derived state variable isGeneratingResponse for better readability. The use of a custom hook useSendMessage isolates the message sending logic, making this component more focused on UI rendering.


29-34: Appropriate keyboard event handling.

The implementation for handling Enter key (without Shift) to send messages is a standard pattern for chat interfaces and is implemented correctly with proper event prevention.

packages/ui/package.json (4)

4-5: Appropriate package configuration.

Marking the package as private and specifying it as an ES module are good practices for internal packages in a monorepo setup.


9-11: Well-defined import mapping.

The imports field mapping #* to ./dist/* aligns with modern JavaScript module practices and TypeScript path aliasing.


14-17: Explicit exports configuration improves package resolution.

Explicitly defining the package root export with separate import and types entries enhances compatibility with both TypeScript and JavaScript consumers.


19-19: Fixed exports field by removing .js extension.

Correctly removing the .js extension from import paths in the wildcard export pattern supports better interoperability with modern bundlers and runtimes.

packages/ui/src/components/ui/dropdown-menu.tsx (4)

3-7: Well-structured imports with clear dependencies.

The component imports only what's needed from React, Radix UI, and Lucide icons, along with the utility function for class name management.


9-13: Clean implementation of the root component.

The root component follows the pattern of forwarding props to the Radix primitive and adds a data-slot attribute for styling and testing hooks.


241-257: Good API surface with comprehensive exports.

The component exports a complete set of dropdown menu primitives, allowing consumers to compose complex dropdown interfaces with fine-grained control.


77-78:

❓ Verification inconclusive

Verify CSS classes are compatible with Tailwind configuration.

The component uses several CSS utility classes that might need to be included in your Tailwind safelist if they're dynamically composed.


🏁 Script executed:

#!/bin/bash
# Check if the Tailwind configuration includes relevant safelist entries for dynamically composed classes
cat packages/ui/tailwind.config.js 2>/dev/null || cat packages/ui/tailwind.config.ts 2>/dev/null || echo "Tailwind config not found in expected location"

Length of output: 176


I want to locate the Tailwind configuration for the repo (likely at the monorepo root) and check for any safelist entries.


🏁 Script executed:

#!/bin/bash
# Find and inspect Tailwind config for any safelist definitions
file=$(find . -maxdepth 2 -type f -name "tailwind.config.*" | head -n1)
if [[ -n "$file" ]]; then
  echo "Config file: $file"
  grep -R "safelist" "$file" || echo "No safelist configured"
else
  echo "No Tailwind config found"
fi

Length of output: 148


Let’s search the entire repo for any Tailwind config and check for safelist entries:


🏁 Script executed:

#!/bin/bash
# Locate all Tailwind config files
echo "🔍 Finding Tailwind config files..."
find . -type f -iname "tailwind.config.*"

# Search for any safelist usage across the repo
echo -e "\n🔍 Searching for 'safelist' declarations..."
grep -R --color=never -n "safelist" .

Length of output: 271


Ensure Tailwind safelist includes dynamic CSS classes

I wasn’t able to locate a tailwind.config.* file in the repo. Please verify that the following utility classes—dynamically composed in your dropdown menu—are whitelisted in your Tailwind purge (safelist) or otherwise included in your configuration to prevent them from being removed in production builds:

• File: packages/ui/src/components/ui/dropdown-menu.tsx (lines 77–78)

"focus:bg-accent focus:text-accent-foreground
 data-[variant=destructive]:text-destructive
 data-[variant=destructive]:focus:bg-destructive/10
 dark:data-[variant=destructive]:focus:bg-destructive/20
 data-[variant=destructive]:focus:text-destructive
 data-[variant=destructive]:*:[svg]:!text-destructive
 [&_svg:not([class*='text-'])]:text-muted-foreground
 …
",
className,
packages/pinecone/package.json (2)

10-10: Import path mapping change improves build structure.

Updating import paths to use ./dist/* instead of source files is a good practice as it ensures consumers use compiled code rather than source files.


40-40: Standardized environment handling with dotenv.

Replacing the custom utility from @repo/utils with the standard dotenv package reduces coupling between packages and standardizes environment variable handling.

packages/ui/src/components/ui/button.tsx (2)

5-5: Updated import path aligns with package configuration.

The new import path using the # prefix matches the updated import mapping configuration in package.json.


8-8: Enhanced button styling with cursor-pointer.

Adding the cursor-pointer class improves the interactive styling of the button component, providing visual feedback to users.

packages/embeddings/package.json (2)

10-10: Consistent import path mapping across packages.

The update to use ./dist/* aligns with the same change in the pinecone package, maintaining consistency across the monorepo.


37-37: Standardized dependency management.

Adding dotenv as a direct dependency while removing the internal utils dependency follows the same pattern as in other packages, creating a more maintainable approach to environment configuration.


export async function POST(req: Request) {
try {
const { messages } = await req.json();
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Missing input validation.

The code extracts messages from the request body without validating its structure or content, which could lead to runtime errors if malformed data is received.

Add validation to ensure messages is an array of the expected format before passing it to the OpenAI client:

-    const { messages } = await req.json();
+    const body = await req.json();
+    const messages = Array.isArray(body.messages) ? body.messages : [];
+    
+    if (messages.length === 0) {
+      return new Response(
+        JSON.stringify({ error: "Invalid or empty messages array" }),
+        {
+          status: 400,
+          headers: { "Content-Type": "application/json" },
+        }
+      );
+    }
🤖 Prompt for AI Agents
In apps/web/app/api/chat/route.ts at line 8, the code extracts messages from the
request body without validating its structure. Add validation to check that
messages exists, is an array, and each item matches the expected format before
using it. If validation fails, return an appropriate error response to prevent
runtime errors from malformed input.

Comment on lines +6 to +27
export async function POST(req: Request) {
try {
const { messages } = await req.json();
const openai = getOpenAIClient();

const result = streamText({
model: openai.responses("gpt-4o-mini"),
messages,
});

return result.toDataStreamResponse();
} catch (error) {
console.error("Error in chat API:", error);
return new Response(
JSON.stringify({ error: "Failed to process chat request" }),
{
status: 500,
headers: { "Content-Type": "application/json" },
},
);
}
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add authentication and rate limiting.

The API route lacks authentication and rate limiting, which could lead to abuse and excessive costs for OpenAI API usage.

Consider implementing authentication and rate limiting mechanisms to protect your API:

+import { getSession } from "@/lib/auth";
+import { rateLimit } from "@/lib/rate-limit";

 export async function POST(req: Request) {
   try {
+    // Check authentication
+    const session = await getSession();
+    if (!session?.user) {
+      return new Response(
+        JSON.stringify({ error: "Unauthorized" }),
+        {
+          status: 401,
+          headers: { "Content-Type": "application/json" },
+        }
+      );
+    }
+
+    // Apply rate limiting
+    const { success } = await rateLimit.limit(session.user.id);
+    if (!success) {
+      return new Response(
+        JSON.stringify({ error: "Too many requests, please try again later" }),
+        {
+          status: 429,
+          headers: { "Content-Type": "application/json" },
+        }
+      );
+    }

     const { messages } = await req.json();
     // Rest of the function...
📝 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
export async function POST(req: Request) {
try {
const { messages } = await req.json();
const openai = getOpenAIClient();
const result = streamText({
model: openai.responses("gpt-4o-mini"),
messages,
});
return result.toDataStreamResponse();
} catch (error) {
console.error("Error in chat API:", error);
return new Response(
JSON.stringify({ error: "Failed to process chat request" }),
{
status: 500,
headers: { "Content-Type": "application/json" },
},
);
}
}
import { getSession } from "@/lib/auth";
import { rateLimit } from "@/lib/rate-limit";
export async function POST(req: Request) {
try {
// Check authentication
const session = await getSession();
if (!session?.user) {
return new Response(
JSON.stringify({ error: "Unauthorized" }),
{
status: 401,
headers: { "Content-Type": "application/json" },
}
);
}
// Apply rate limiting
const { success } = await rateLimit.limit(session.user.id);
if (!success) {
return new Response(
JSON.stringify({ error: "Too many requests, please try again later" }),
{
status: 429,
headers: { "Content-Type": "application/json" },
}
);
}
const { messages } = await req.json();
const openai = getOpenAIClient();
const result = streamText({
model: openai.responses("gpt-4o-mini"),
messages,
});
return result.toDataStreamResponse();
} catch (error) {
console.error("Error in chat API:", error);
return new Response(
JSON.stringify({ error: "Failed to process chat request" }),
{
status: 500,
headers: { "Content-Type": "application/json" },
},
);
}
}
🤖 Prompt for AI Agents
In apps/web/app/api/chat/route.ts between lines 6 and 27, the POST API handler
currently lacks authentication and rate limiting, exposing it to potential abuse
and high costs. To fix this, implement an authentication check at the start of
the handler to verify the user's identity or API key before processing the
request. Additionally, integrate a rate limiting mechanism to restrict the
number of requests per user or IP within a time window. Use middleware or inline
logic to enforce these protections and return appropriate error responses when
limits are exceeded or authentication fails.

const openai = getOpenAIClient();

const result = streamText({
model: openai.responses("gpt-4o-mini"),
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Hardcoded model name.

The OpenAI model name is hardcoded, making it difficult to switch models in different environments or update the model without code changes.

Extract the model name to an environment variable for better flexibility:

+// Import environment configuration
+import { env } from "@/lib/env";

// In the POST handler
-      model: openai.responses("gpt-4o-mini"),
+      model: openai.responses(env.OPENAI_MODEL || "gpt-4o-mini"),
📝 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
model: openai.responses("gpt-4o-mini"),
// Import environment configuration
import { env } from "@/lib/env";
// … inside your POST handler …
model: openai.responses(env.OPENAI_MODEL || "gpt-4o-mini"),
🤖 Prompt for AI Agents
In apps/web/app/api/chat/route.ts at line 12, the OpenAI model name is hardcoded
as "gpt-4o-mini". To improve flexibility and allow changing the model without
code modifications, replace the hardcoded string with a reference to an
environment variable. Update the code to read the model name from process.env
(or the appropriate environment config) and use that variable instead of the
fixed string.

Comment on lines 16 to 35
<div className="fixed bottom-0 left-0 right-0 bg-background py-4">
<div className="max-w-3xl mx-auto w-full px-4">
<div className="flex flex-col bg-secondary rounded-[10px]">
<ChatInputArea
chatHelpers={chatHelpers}
input={input}
setInput={setInput}
/>
<ChatInputControls
chatHelpers={chatHelpers}
input={input}
setInput={setInput}
/>
</div>
<div className="text-muted-foreground text-xs mt-2">
This is an AI chatbot using RAG. It is trained on GOAT Format
Yu-Gi-Oh! rulings only.
</div>
</div>
</div>
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

Consider mobile and keyboard accessibility issues

The fixed positioning might cause issues on mobile devices when the virtual keyboard appears. Consider testing and adjusting the UI for mobile scenarios.


🏁 Script executed:

#!/bin/bash
# Check if there are any media queries or mobile-specific adjustments in CSS files

echo "Checking for mobile-specific styles for the chat input:"
rg -i "(mobile|media\s+query|@media|screen and \(|max-width)" --type css

Length of output: 195


To verify if responsive Tailwind utilities are applied to handle mobile layouts, let’s inspect the chat input component for any breakpoint prefixes:


🏁 Script executed:

#!/bin/bash
# Locate and inspect the chat-input component for responsive classes
echo "Checking for responsive Tailwind prefixes in apps/web/components/chat-input.tsx:"
rg -n "(sm:|md:|lg:|xl:)" apps/web/components/chat-input.tsx || true

echo -e "\nShowing the first 100 lines of the file for manual review:"
sed -n '1,100p' apps/web/components/chat-input.tsx

Length of output: 1547


Improve mobile keyboard & safe-area handling for the chat input

The fixed bottom-0 container has no responsive or safe-area padding, so on mobile—especially when the virtual keyboard is open—it can get covered or overlap system UI. Let’s add support for the safe-area inset and consider increasing bottom padding when the keyboard is active.

• File: apps/web/components/chat-input.tsx
• Around lines 14–22: replace the fixed container’s padding with safe-area–aware values

Suggested diff snippet:

-   <div className="fixed bottom-0 left-0 right-0 bg-background py-4">
+   <div
+     className="
+       fixed inset-x-0 bottom-0
+       bg-background
+       pt-4
+       pb-[calc(1rem+env(safe-area-inset-bottom))]  /* adds safe-area inset */
+     "
+   >

• Optional enhancements:
– Listen for input focus to add a larger bottom padding or switch to position: sticky.
– Test on iOS/Android devices with virtual keyboards to ensure visibility.

🤖 Prompt for AI Agents
In apps/web/components/chat-input.tsx around lines 16 to 35, the fixed bottom
container uses static padding that does not account for mobile safe-area insets
or virtual keyboard presence, which can cause the chat input to be obscured on
mobile devices. Update the container's padding to use safe-area-aware CSS
variables (e.g., padding-bottom with env(safe-area-inset-bottom)) and consider
adding responsive padding adjustments for when the keyboard is active.
Optionally, implement event listeners on the input focus to increase bottom
padding or switch the container's position to sticky for better mobile keyboard
handling. Test these changes on iOS and Android devices to ensure the chat input
remains visible above the keyboard.

Comment on lines +48 to +55
"@radix-ui/react-dropdown-menu": "^2.1.14",
"@radix-ui/react-slot": "^1.2.2",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"lucide-react": "^0.509.0",
"next-themes": "^0.4.6",
"react-markdown": "^10.1.0",
"sonner": "^2.0.3",
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

Verify dependency compatibility with React 19.

The package has react@^19 as a peer dependency, but some of the UI libraries added might not have been fully tested with this version yet.


🌐 Web query:

Are @radix-ui/react-dropdown-menu, next-themes, react-markdown, and sonner compatible with React 19?

💡 Result:

Based on the search results, I can provide information about the compatibility of these packages with React 19.

Radix UI Components

@radix-ui/react-dropdown-menu is compatible with React 19. Radix UI has released updates for full React 19 compatibility as mentioned in their releases documentation[3]. The latest version of @radix-ui/react-dropdown-menu (2.1.14) was published 11 days ago on May 6, 2025[2], and Radix UI has specifically addressed React 19 compatibility in their updates.

next-themes

next-themes currently has compatibility issues with React 19. There was an issue reported on GitHub where a user encountered problems when trying to use next-themes with React 19.0.0-rc version[6]. The package worked fine with React 18.3.1, but after upgrading to the React 19 release candidate, it failed to function as expected. As of the reported issue, there does not appear to be an official fix or update for next-themes to work with React 19.

react-markdown

There's no specific information in the search results about react-markdown's compatibility with React 19. The search results include general information about react-markdown as a component for rendering markdown in React[7], but don't mention its compatibility with React 19 specifically.

sonner

Sonner is compatible with React 19 according to the Shadcn UI documentation. In their upgrade status table for React 19 compatibility, sonner is marked with a ✅ status, indicating it "Works with React 19 using npm, pnpm, and bun"[10].

Additional Information

For developers working with React 19, there are compatibility solutions available:

  1. Ant Design provides a compatibility package (@ant-design/v5-patch-for-react-19) to make their components work with React 19[8].

  2. Shadcn UI has added full support for React 19 in their "canary" release and provides a compatibility status table for various dependencies[10].

  3. For React 19 projects using npm, you might need to use special flags when installing some packages due to peer dependency issues. The Shadcn UI documentation mentions that their CLI will prompt you to select appropriate flags[10].

Citations:


Investigate React 19 compatibility for next-themes and react-markdown

Based on available release notes and issue trackers:

  • @radix-ui/react-dropdown-menu@2.1.14 · ✅ Confirmed React 19 support
  • sonner@2.0.3 · ✅ Shadcn UI docs list it as fully React 19–compatible
  • next-themes@0.4.6 · 🚧 Known to break under React 19 RC (see next-themes not working with React 19.0.0-rc-66855b96-20241106 facebook/react#31576); no fix released yet
  • react-markdown@10.1.0 · ⚠️ No explicit React 19 compatibility statements found—recommend manual testing

Please address the following before merging:

  • Replace or patch next-themes (pin to React 18 or apply a workaround)
  • Verify react-markdown renders correctly under React 19 (upgrade or swap if issues arise)
🤖 Prompt for AI Agents
In packages/ui/package.json lines 48 to 55, next-themes is known to be
incompatible with React 19 and may cause breakage. To fix this, either downgrade
next-themes to a version compatible with React 18 or apply a workaround to avoid
using it with React 19. Additionally, manually test react-markdown under React
19 for rendering issues and consider upgrading or replacing it if problems
occur.

Copy link
Owner Author

Choose a reason for hiding this comment

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

not useful

Comment on lines +34 to +52
function DropdownMenuContent({
className,
sideOffset = 4,
...props
}: React.ComponentProps<typeof DropdownMenuPrimitive.Content>) {
return (
<DropdownMenuPrimitive.Portal>
<DropdownMenuPrimitive.Content
data-slot="dropdown-menu-content"
sideOffset={sideOffset}
className={cn(
"bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 max-h-(--radix-dropdown-menu-content-available-height) min-w-[8rem] origin-(--radix-dropdown-menu-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border p-1 shadow-md",
className,
)}
{...props}
/>
</DropdownMenuPrimitive.Portal>
);
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix CSS variable syntax in class names.

The class names in the className prop contain CSS variable references with incorrect syntax.

-          "bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 max-h-(--radix-dropdown-menu-content-available-height) min-w-[8rem] origin-(--radix-dropdown-menu-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border p-1 shadow-md",
+          "bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 max-h-[var(--radix-dropdown-menu-content-available-height)] min-w-[8rem] origin-[var(--radix-dropdown-menu-content-transform-origin)] overflow-x-hidden overflow-y-auto rounded-md border p-1 shadow-md",

This also appears in DropdownMenuSubContent:

#!/bin/bash
# Check for other instances of this pattern
rg "origin-\(--" --type=tsx
🤖 Prompt for AI Agents
In packages/ui/src/components/ui/dropdown-menu.tsx around lines 34 to 52, the
CSS variable references in the className prop use incorrect syntax with
parentheses instead of square brackets. Fix this by replacing occurrences like
max-h-(--radix-dropdown-menu-content-available-height) and
origin-(--radix-dropdown-menu-content-transform-origin) with the correct syntax
max-h-[var(--radix-dropdown-menu-content-available-height)] and
origin-[var(--radix-dropdown-menu-content-transform-origin)]. Also, apply the
same fix to DropdownMenuSubContent if it uses similar CSS variable syntax.

Comment on lines +232 to +235
className={cn(
"bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 min-w-[8rem] origin-(--radix-dropdown-menu-content-transform-origin) overflow-hidden rounded-md border p-1 shadow-lg",
className,
)}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix CSS variable syntax in SubContent class.

Similar to the DropdownMenuContent component, there's an issue with the CSS variable syntax here.

-        "bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 min-w-[8rem] origin-(--radix-dropdown-menu-content-transform-origin) overflow-hidden rounded-md border p-1 shadow-lg",
+        "bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 min-w-[8rem] origin-[var(--radix-dropdown-menu-content-transform-origin)] overflow-hidden rounded-md border p-1 shadow-lg",
🤖 Prompt for AI Agents
In packages/ui/src/components/ui/dropdown-menu.tsx around lines 232 to 235, the
CSS variable syntax used in the origin property is incorrect. Replace the
parentheses around the CSS variable with square brackets and use the correct
var() function syntax, changing
origin-(--radix-dropdown-menu-content-transform-origin) to
origin-[var(--radix-dropdown-menu-content-transform-origin)] to fix the CSS
variable usage.

Copy link
Owner Author

Choose a reason for hiding this comment

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

ignore suggestions from packages/ui/components/ui/**
they are generated from shadcn

@nedhmn nedhmn merged commit 6a12751 into main May 17, 2025
1 check passed
@coderabbitai coderabbitai bot mentioned this pull request May 17, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants