Repository: MODSetter/SurfSense
Pull request #34: Add hot reloading + minor UX updates
Author: login: Adamsmith6300
Created: 2025-04-12T00:01:24Z | Merged: 2025-04-12T01:40:42Z

Changes:
- Added hot-reloading to backend server startup (main.py), enabled via arguments
- Updated oauth client setup in README for extra clarity
- Updated typo in README
- Updated Search Cards, removed 'search button' made whole card clickable, also updated hover text color on delete button

Testing:
- pass `--reload` to backed startup...`uv run main.py --reload`, test making changes and verify server restarts automatically
- in search space dashboard, click to open search spaces, test deleting search space and verify it doesnt open the search space instead

<!-- This is an auto-generated comment: release notes by coderabbit.ai -->
## Summary by CodeRabbit

- **Documentation**
	- Updated authentication details and installation instructions with new requirements for Google OAuth and revised commands for dependency installation and frontend startup.
- **New Features**
	- Introduced an optional hot reloading configuration for improved runtime flexibility.
	- Enhanced the dashboard’s search space interactions by integrating navigation links and streamlining deletion actions for a more intuitive user experience.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->


Diff:
diff --git a/README.md b/README.md
index 8b09551ad..c890b2de3 100644
--- a/README.md
+++ b/README.md
@@ -78,7 +78,7 @@ Make sure pgvector extension is installed on your machine. Setup Guide https://g
 For File uploading you need Unstructured.io API key. You can get it at http://platform.unstructured.io/
 
 #### Auth
-SurfSense now only works with Google OAuth. Make sure to set your OAuth Client at https://developers.google.com/identity/protocols/oauth2 . We need client id and client secret for backend.
+SurfSense now only works with Google OAuth. Make sure to set your OAuth Client at https://developers.google.com/identity/protocols/oauth2 . We need client id and client secret for backend. Make sure to enable people api and add the required scopes under data access (openid, userinfo.email, userinfo.profile)
 
 ![gauth](https://github.com/user-attachments/assets/80d60fe5-889b-48a6-b947-200fdaf544c1)
 
@@ -130,7 +130,7 @@ You can also integrate any LLM just follow this https://docs.litellm.ai/docs/pro
 Now once you have everything let's proceed to run SurfSense. 
 1. Install `uv` : https://docs.astral.sh/uv/getting-started/installation/
 2. Now just run this command to install dependencies i.e `uv sync`
-3. That's it. Now just run the `main.py` file using `uv run main.py`.
+3. That's it. Now just run the `main.py` file using `uv run main.py`. You can also optionally pass `--reload` as an argument to enable hot reloading.
 4. If everything worked fine you should see screen like this.
 
 ![backend](https://i.ibb.co/542Vhqw/backendrunning.png)
diff --git a/surfsense_backend/main.py b/surfsense_backend/main.py
index 78a33d4c9..76d478b4f 100644
--- a/surfsense_backend/main.py
+++ b/surfsense_backend/main.py
@@ -1,4 +1,15 @@
 import uvicorn
+import argparse
 
 if __name__ == "__main__":
-    uvicorn.run("app.app:app", host="0.0.0.0", log_level="info", loop="asyncio")
+    parser = argparse.ArgumentParser(description='Run the SurfSense application')
+    parser.add_argument('--reload', action='store_true', help='Enable hot reloading')
+    args = parser.parse_args()
+
+    uvicorn.run(
+        "app.app:app",
+        host="0.0.0.0",
+        log_level="info",
+        reload=args.reload,
+        reload_dirs=["app"]
+    )
diff --git a/surfsense_web/app/dashboard/page.tsx b/surfsense_web/app/dashboard/page.tsx
index 4e19545a4..1418b8096 100644
--- a/surfsense_web/app/dashboard/page.tsx
+++ b/surfsense_web/app/dashboard/page.tsx
@@ -211,6 +211,7 @@ const DashboardPage = () => {
 
           <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
             {searchSpaces && searchSpaces.map((space) => (
+              <Link href={`/dashboard/${space.id}/documents`}>
               <motion.div
                 key={space.id}
                 variants={itemVariants}
@@ -243,44 +244,39 @@ const DashboardPage = () => {
                         alt={space.name}
                         className="h-full w-full object-cover grayscale duration-700 group-hover:grayscale-0"
                       />
-                      <div className="absolute inset-0 bg-gradient-to-t from-background/80 to-transparent" />
-                      <div className="absolute bottom-2 left-3 flex items-center gap-2">
-                        <Link href={`/dashboard/${space.id}/documents`}>
-                          <span className="flex h-8 w-8 items-center justify-center rounded-full bg-blue-100/80 dark:bg-blue-950/80">
-                            <Search className="h-4 w-4 text-blue-500" />
-                          </span>
-                        </Link>
-                      </div>
+                        <div className="absolute inset-0 bg-gradient-to-t from-background/80 to-transparent" />
                       <div className="absolute top-2 right-2">
-                        <AlertDialog>
-                          <AlertDialogTrigger asChild>
-                            <Button
-                              variant="ghost"
-                              size="icon"
-                              className="h-8 w-8 rounded-full bg-background/50 backdrop-blur-sm hover:bg-destructive/90 hover:text-destructive-foreground"
-                            >
-                              <Trash2 className="h-4 w-4" />
-                            </Button>
-                          </AlertDialogTrigger>
-                          <AlertDialogContent>
-                            <AlertDialogHeader>
-                              <AlertDialogTitle>Delete Search Space</AlertDialogTitle>
-                              <AlertDialogDescription>
-                                Are you sure you want to delete "{space.name}"? This action cannot be undone.
-                                All documents, chats, and podcasts in this search space will be permanently deleted.
-                              </AlertDialogDescription>
-                            </AlertDialogHeader>
-                            <AlertDialogFooter>
-                              <AlertDialogCancel>Cancel</AlertDialogCancel>
-                              <AlertDialogAction
-                                onClick={() => handleDeleteSearchSpace(space.id)}
-                                className="bg-destructive hover:bg-destructive/90"
-                              >
-                                Delete
-                              </AlertDialogAction>
-                            </AlertDialogFooter>
-                          </AlertDialogContent>
-                        </AlertDialog>
+                          <div onClick={(e) => e.preventDefault()}>
+                            <AlertDialog>
+                              <AlertDialogTrigger asChild>
+                                <Button
+                                  variant="ghost"
+                                  size="icon"
+                                  className="h-8 w-8 rounded-full bg-background/50 backdrop-blur-sm hover:bg-destructive/90 cursor-pointer"
+                                >
+                                  <Trash2 className="h-4 w-4" />
+                                </Button>
+                              </AlertDialogTrigger>
+                              <AlertDialogContent>
+                                <AlertDialogHeader>
+                                  <AlertDialogTitle>Delete Search Space</AlertDialogTitle>
+                                  <AlertDialogDescription>
+                                    Are you sure you want to delete &quot;{space.name}&quot;? This action cannot be undone.
+                                    All documents, chats, and podcasts in this search space will be permanently deleted.
+                                  </AlertDialogDescription>
+                                </AlertDialogHeader>
+                                <AlertDialogFooter>
+                                  <AlertDialogCancel>Cancel</AlertDialogCancel>
+                                  <AlertDialogAction
+                                    onClick={() => handleDeleteSearchSpace(space.id)}
+                                    className="bg-destructive hover:bg-destructive/90"
+                                  >
+                                    Delete
+                                  </AlertDialogAction>
+                                </AlertDialogFooter>
+                              </AlertDialogContent>
+                            </AlertDialog>
+                          </div>
                       </div>
                     </div>
              
@@ -298,6 +294,7 @@ const DashboardPage = () => {
                 </Tilt>
 
               </motion.div>
+              </Link>
             ))}
 
             {searchSpaces.length === 0 && (
@@ -350,4 +347,4 @@ const DashboardPage = () => {
   )
 }
 
-export default DashboardPage
\ No newline at end of file
+export default DashboardPage


Reviews:
user: login: coderabbitai[bot]
body: **Actionable comments posted: 2**

<details>
<summary>🧹 Nitpick comments (1)</summary><blockquote>

<details>
<summary>README.md (1)</summary><blockquote>

`81-81`: **Fix the bare URL in the instructions.**

The URL for Google OAuth should be formatted using proper Markdown syntax for links.

```diff
-SurfSense now only works with Google OAuth. Make sure to set your OAuth Client at https://developers.google.com/identity/protocols/oauth2 . We need client id and client secret for backend. Make sure to enable people api and add the required scopes under data access (openid, userinfo.email, userinfo.profile)
+SurfSense now only works with Google OAuth. Make sure to set your OAuth Client at [https://developers.google.com/identity/protocols/oauth2](https://developers.google.com/identity/protocols/oauth2). We need client id and client secret for backend. Make sure to enable people api and add the required scopes under data access (openid, userinfo.email, userinfo.profile)
```

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 markdownlint-cli2 (0.17.2)</summary>

81-81: Bare URL used
null

(MD034, no-bare-urls)

</details>

</details>

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 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 a5c6cf246d4ff06fe03869932e3b7b8183b0da12 and cb0c65b15e7ec1448dde7812b576a47c8029c561.

</details>

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

* `README.md` (3 hunks)
* `surfsense_backend/main.py` (1 hunks)
* `surfsense_web/app/dashboard/page.tsx` (4 hunks)

</details>

<details>
<summary>🧰 Additional context used</summary>

<details>
<summary>🧬 Code Graph Analysis (1)</summary>

<details>
<summary>surfsense_web/app/dashboard/page.tsx (1)</summary><blockquote>

<details>
<summary>surfsense_web/components/ui/alert-dialog.tsx (9)</summary>

* `AlertDialog` (146-146)
* `AlertDialogTrigger` (149-149)
* `AlertDialogContent` (150-150)
* `AlertDialogHeader` (151-151)
* `AlertDialogTitle` (153-153)
* `AlertDialogDescription` (154-154)
* `AlertDialogFooter` (152-152)
* `AlertDialogCancel` (156-156)
* `AlertDialogAction` (155-155)

</details>

</blockquote></details>

</details><details>
<summary>🪛 markdownlint-cli2 (0.17.2)</summary>

<details>
<summary>README.md</summary>

81-81: Bare URL used
null

(MD034, no-bare-urls)

</details>

</details>
<details>
<summary>🪛 Biome (1.9.4)</summary>

<details>
<summary>surfsense_web/app/dashboard/page.tsx</summary>

[error] 214-214: Missing key property for this element in iterable.

The order of the items may change, and having a key can help React identify which item was moved.
Check the React documentation. 

(lint/correctness/useJsxKeyInIterable)

</details>

</details>

</details>

<details>
<summary>🔇 Additional comments (4)</summary><blockquote>

<details>
<summary>surfsense_backend/main.py (2)</summary>

`2-7`: **Clean addition of command-line argument parsing for hot reloading.**

The implementation of the argument parser follows best practices for Python CLI applications. The description and help text are clear and informative.

---

`9-15`: **Enhanced server configuration with hot reloading support.**

The uvicorn.run call now properly uses the command-line argument to enable hot reloading. The addition of the reload_dirs parameter is a good practice as it restricts monitoring to only the "app" directory, which prevents unnecessary restarts when other files change.

</details>
<details>
<summary>README.md (1)</summary>

`133-133`: **Documentation updated to match new hot reload feature.**

The instructions now correctly include information about the new `--reload` argument, which aligns with the implementation changes in main.py.

</details>
<details>
<summary>surfsense_web/app/dashboard/page.tsx (1)</summary>

`247-279`: **Improved delete functionality with confirmation dialog.**

The implementation of the AlertDialog for deletion is a good UX improvement as it prevents accidental deletions. The event.preventDefault() call correctly stops the Link navigation when clicking the delete button.

The hover styling for the delete button (`hover:bg-destructive/90`) provides clear visual feedback to users.

</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: NONE
submitted_at: 2025-04-12T00:04:41Z
user: login: coderabbitai[bot]
body: **Actionable comments posted: 0**

<details>
<summary>🧹 Nitpick comments (1)</summary><blockquote>

<details>
<summary>README.md (1)</summary><blockquote>

`81-81`: **Minor Language and Consistency Improvements in OAuth Setup Instructions**

Consider refining the phrasing for clarity and consistency. For example, instead of:
  
```
We need client id and client secret for backend. Make sure to enable people api and add the required scopes under data access (openid, userinfo.email, userinfo.profile)
```

you might use:

```diff
-We need client id and client secret for backend. Make sure to enable people api and add the required scopes under data access (openid, userinfo.email, userinfo.profile)
+We need the client ID and client secret for the backend. Make sure to enable the People API and add the required scopes under data access (openid, userinfo.email, userinfo.profile).
```

This adjustment enhances readability and aligns with industry-standard terminology.

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 LanguageTool</summary>

[uncategorized] ~81-~81: You might be missing the article “the” here.
Context: ...We need client id and client secret for backend. Make sure to enable people api and add...

(AI_EN_LECTOR_MISSING_DETERMINER_THE)

</details>
<details>
<summary>🪛 markdownlint-cli2 (0.17.2)</summary>

81-81: Bare URL used
null

(MD034, no-bare-urls)

</details>

</details>

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 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 cb0c65b15e7ec1448dde7812b576a47c8029c561 and 9867dbd536e3bd93012fcbe11ea32930cab42b7b.

</details>

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

* `README.md` (2 hunks)

</details>

<details>
<summary>🧰 Additional context used</summary>

<details>
<summary>🪛 LanguageTool</summary>

<details>
<summary>README.md</summary>

[uncategorized] ~81-~81: You might be missing the article “the” here.
Context: ...We need client id and client secret for backend. Make sure to enable people api and add...

(AI_EN_LECTOR_MISSING_DETERMINER_THE)

</details>

</details>
<details>
<summary>🪛 markdownlint-cli2 (0.17.2)</summary>

<details>
<summary>README.md</summary>

81-81: Bare URL used
null

(MD034, no-bare-urls)

</details>

</details>

</details>

<details>
<summary>🔇 Additional comments (1)</summary><blockquote>

<details>
<summary>README.md (1)</summary>

`133-133`: **Clear Hot Reloading Instruction Added**

The updated instruction clearly explains how to run the backend with hot reloading by passing the `--reload` flag (i.e., using `uv run main.py --reload`). This addition meets the PR objective of enhancing hot reloading functionality and improves developer usability.

</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: NONE
submitted_at: 2025-04-12T00:12:39Z
user: login: MODSetter
state: COMMENTED
author_association: OWNER
submitted_at: 2025-04-12T00:26:41Z
user: login: coderabbitai[bot]
state: COMMENTED
author_association: NONE
submitted_at: 2025-04-12T00:26:46Z
user: login: MODSetter
state: COMMENTED
author_association: OWNER
submitted_at: 2025-04-12T01:40:33Z

Review comments:
diff_hunk: @@ -145,8 +145,8 @@ For local frontend setup just fill out the `.env` file of frontend.
 |--|--|
 | NEXT_PUBLIC_FASTAPI_BACKEND_URL | Give hosted backend url here. Eg. `http://localhost:8000`|
 
-1. Now install dependencies using `pnpm install`
-2. Run it using `pnpm run dev`
+1. Now install dependencies using `npm install`
+2. Run it using `npm run dev`
path: README.md
user: login: coderabbitai[bot]
body: _💡 Verification agent_

<details>
<summary>🧩 Analysis chain</summary>

**Package manager change from pnpm to npm.**

The instructions have been updated to use npm instead of pnpm for installing dependencies and running the frontend. Ensure this change was intentional and aligns with project standards.

---

🏁 Script executed:

```shell
#!/bin/bash
# Check if there are any pnpm-related files in the frontend directory
echo "Checking for pnpm-related files:"
find surfsense_web -name "pnpm*" -type f
```

Length of output: 146

---



</details>

**Action Required: Ensure Package Manager Consistency**

The README now instructs using npm (`npm install` and `npm run dev`), but a pnpm file (`surfsense_web/pnpm-lock.yaml`) still exists in the repository. Please verify if the migration from pnpm to npm has been fully adopted. If npm is now the standard, consider removing pnpm-specific files (like the lock file) to avoid confusion. Otherwise, update the documentation to reflect any dual usage or remaining dependencies on pnpm.

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-04-12T00:04:40Z
author_association: NONE
diff_hunk: @@ -211,6 +211,7 @@ const DashboardPage = () => {
 
           <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
             {searchSpaces && searchSpaces.map((space) => (
+              <Link href={`/dashboard/${space.id}/documents`}>
path: surfsense_web/app/dashboard/page.tsx
user: login: coderabbitai[bot]
body: _⚠️ Potential issue_

**Missing key prop in the Link component.**

The Link component wrapping the motion.div lacks a key property, which is required when rendering elements in an array. React needs this key to efficiently update the DOM when the list changes.

```diff
-<Link href={`/dashboard/${space.id}/documents`}>
+<Link href={`/dashboard/${space.id}/documents`} key={space.id}>
```

Note: The key should be moved from the motion.div (line 216) to the Link component since it's now the outermost element in the iteration.


Also applies to: 297-297

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 Biome (1.9.4)</summary>

[error] 214-214: Missing key property for this element in iterable.

The order of the items may change, and having a key can help React identify which item was moved.
Check the React documentation. 

(lint/correctness/useJsxKeyInIterable)

</details>

</details>

<!-- This is an auto-generated reply by CodeRabbit -->
created_at: 2025-04-12T00:04:40Z
author_association: NONE
diff_hunk: @@ -211,6 +211,7 @@ const DashboardPage = () => {
 
           <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
             {searchSpaces && searchSpaces.map((space) => (
+              <Link href={`/dashboard/${space.id}/documents`}>
path: surfsense_web/app/dashboard/page.tsx
user: login: MODSetter
body: @Adamsmith6300 Thanks for the PR. Can you add the key prop. I am getting issues in turbopack console.
![KEY](https://github.com/user-attachments/assets/939f8853-ff61-4ccb-8e09-c2a5ee53cd38)



created_at: 2025-04-12T00:26:41Z
author_association: OWNER
diff_hunk: @@ -211,6 +211,7 @@ const DashboardPage = () => {
 
           <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
             {searchSpaces && searchSpaces.map((space) => (
+              <Link href={`/dashboard/${space.id}/documents`}>
path: surfsense_web/app/dashboard/page.tsx
user: login: coderabbitai[bot]
body: > Seems like the humans are having a chat. I'll hop back into my burrow for now. If you need me again, just tag @coderabbitai in a new comment, and I'll come hopping out!

<!-- This is an auto-generated mute comment by CodeRabbit -->
created_at: 2025-04-12T00:26:46Z
author_association: NONE
diff_hunk: @@ -211,6 +211,7 @@ const DashboardPage = () => {
 
           <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
             {searchSpaces && searchSpaces.map((space) => (
+              <Link href={`/dashboard/${space.id}/documents`}>
path: surfsense_web/app/dashboard/page.tsx
user: login: MODSetter
body: Will go ahead with merge. Will add the fix in next commit.
created_at: 2025-04-12T01:40:33Z
author_association: OWNER

PR comments:
user: login: vercel[bot]
created_at: 2025-04-12T00:01:27Z
body: @Adamsmith6300 is attempting to deploy a commit to the **Rohan Verma's projects** Team on [Vercel](https://vercel.com).

A member of the Team first needs to [authorize it](https://vercel.com/git/authorize?team=Rohan%20Verma's%20projects&type=github&job=%7B%22headInfo%22%3A%7B%22sha%22%3A%22cb0c65b15e7ec1448dde7812b576a47c8029c561%22%7D%2C%22id%22%3A%22QmUo2aTg59BqHsPGkuMC8QzjUmhzUtQcA9WJ8JAEWxSwJ4%22%2C%22org%22%3A%22MODSetter%22%2C%22prId%22%3A34%2C%22repo%22%3A%22SurfSense%22%7D).


author_association: NONE
user: login: coderabbitai[bot]
created_at: 2025-04-12T00:01:33Z
body: <!-- This is an auto-generated comment: summarize by coderabbit.ai -->
<!-- walkthrough_start -->

## Walkthrough

This update refines documentation and enhances both backend and frontend behavior. The README has been revised with updated authentication and installation instructions for Google OAuth and expanded backend command-line options. The backend now accepts a command-line argument for hot reloading, dynamically adjusting its behavior. Additionally, the dashboard page changes improve UI interactivity by wrapping search space elements in navigation links and streamlining the deletion interaction.

## Changes

| File(s)                                           | Summary                                                                                                                                             |
| ------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- |
| `README.md`                                       | Updated authentication instructions for Google OAuth (People API, scopes) and revised installation commands to include the `--reload` option.    |
| `surfsense_backend/main.py`                       | Introduced an `ArgumentParser` to allow a `--reload` flag and set `reload_dirs` to `["app"]` for dynamic hot reloading with `uvicorn`.            |
| `surfsense_web/app/dashboard/page.tsx`            | Wrapped search space elements in a `<Link>` for navigation, and modified the deletion flow by integrating the trash icon with an alert dialog.      |

## Sequence Diagram(s)

```mermaid
sequenceDiagram
    participant U as User
    participant CLI as Command Line
    participant AP as ArgumentParser
    participant UV as uvicorn.run
    participant App as Application

    U->>CLI: Execute command with --reload flag
    CLI->>AP: Parse command-line arguments
    AP->>UV: Forward reload flag & set reload_dirs ["app"]
    UV->>App: Start application with hot reloading enabled
```

```mermaid
sequenceDiagram
    participant U as User
    participant DP as DashboardPage
    participant L as Navigation Link
    participant AD as AlertDialog

    U->>DP: Click on search space (motion.div)
    DP->>L: Trigger navigation to documents
    Note right of L: Page navigation occurs

    U->>DP: Click on trash icon
    DP->>AD: Open alert dialog for deletion confirmation
```

## Poem

> I hop through lines of refined code,  
> Where flags and links in harmony explode.  
> With a CLI carrot, reload's a delight,  
> And UI paths guide my joyful flight.  
> In a garden of changes, I hop and cheer—  
> A tech bunny's tune, crisp and clear!  
> 🥕🐇

<!-- walkthrough_end -->

<!-- announcements_start -->

> [!TIP]
> <details>
> <summary>⚡💬 Agentic Chat (Pro Plan, General Availability)</summary>
> 
> - We're introducing multi-step agentic chat in review comments and issue comments, within and outside of PR's. This feature enhances review and issue discussions with the CodeRabbit agentic chat by enabling advanced interactions, including the ability to create pull requests directly from comments and add commits to existing pull requests.
> 
> </details>

<!-- announcements_end -->
<!-- internal state start -->


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

<!-- internal state end -->
<!-- tips_start -->

---



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

### Chat

There are 3 ways to chat with [CodeRabbit](https://coderabbit.ai?utm_source=oss&utm_medium=github&utm_campaign=MODSetter/SurfSense&utm_content=34):

- Review comments: Directly reply to a review comment made by CodeRabbit. Example:
  - `I pushed a fix in commit <commit_id>, please review it.`
  - `Generate unit testing code for this file.`
  - `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 generate unit testing code for this file.`
  -	`@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 generate unit testing code.`
  - `@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.`

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 docstrings` to [generate docstrings](https://docs.coderabbit.ai/finishing-touches/docstrings) for this PR.
- `@coderabbitai resolve` resolve all the CodeRabbit review comments.
- `@coderabbitai plan` to trigger planning for file edits and PR creation.
- `@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 -->
author_association: NONE