Repository: triggerdotdev/trigger.dev
Pull request #1962: Allow empty payloads in test runs
Author: login: myftija
Created: 2025-04-22T00:22:54Z | Merged: 2025-04-22T15:23:53Z

Changes in this PR:

- adapted the code editor component to allow empty inputs
- exposed basic linter configs in the code editor component
- applied a couple of light touch ups to the error display elements in favor of consistency

## Screenshots

Previously:
<img width="1197" alt="Screenshot 2025-04-21 at 17 20 37" src="https://github.com/user-attachments/assets/9cdea079-dac3-498a-9339-33ae22f3204c" />

Now:
<img width="1197" alt="Screenshot 2025-04-21 at 17 21 17" src="https://github.com/user-attachments/assets/b074ab84-2bb4-4f01-8c79-dc154616929f" />


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

## Summary by CodeRabbit

- **New Features**
  - Added configurable linting options to the JSON editor, allowing users to enable or disable linting and permit empty content without errors.

- **Style**
  - Improved the visual appearance of linting errors and diagnostic messages in the editor for better readability.

- **Bug Fixes**
  - Enhanced validation error messages for JSON fields, providing clearer feedback when invalid JSON is entered.

- **Chores**
  - Removed placeholder text from the JSON editor in the task form for a cleaner interface.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->


Diff:
diff --git a/apps/webapp/app/components/code/JSONEditor.tsx b/apps/webapp/app/components/code/JSONEditor.tsx
index 0313120b140..db85a9cd9f5 100644
--- a/apps/webapp/app/components/code/JSONEditor.tsx
+++ b/apps/webapp/app/components/code/JSONEditor.tsx
@@ -1,5 +1,5 @@
-import { json as jsonLang } from "@codemirror/lang-json";
-import type { ViewUpdate } from "@codemirror/view";
+import { json as jsonLang, jsonParseLinter } from "@codemirror/lang-json";
+import type { EditorView, ViewUpdate } from "@codemirror/view";
 import { CheckIcon, ClipboardIcon, TrashIcon } from "@heroicons/react/20/solid";
 import type { ReactCodeMirrorProps, UseCodeMirror } from "@uiw/react-codemirror";
 import { useCodeMirror } from "@uiw/react-codemirror";
@@ -8,6 +8,7 @@ import { cn } from "~/utils/cn";
 import { Button } from "../primitives/Buttons";
 import { getEditorSetup } from "./codeMirrorSetup";
 import { darkTheme } from "./codeMirrorTheme";
+import { linter, lintGutter, type Diagnostic } from "@codemirror/lint";
 
 export interface JSONEditorProps extends Omit<ReactCodeMirrorProps, "onBlur"> {
   defaultValue?: string;
@@ -18,18 +19,35 @@ export interface JSONEditorProps extends Omit<ReactCodeMirrorProps, "onBlur"> {
   onBlur?: (code: string) => void;
   showCopyButton?: boolean;
   showClearButton?: boolean;
+  linterEnabled?: boolean;
+  allowEmpty?: boolean;
 }
 
 const languages = {
   json: jsonLang,
 };
 
+function emptyAwareJsonLinter() {
+  return (view: EditorView): Diagnostic[] => {
+    const content = view.state.doc.toString().trim();
+
+    // return no errors if content is empty
+    if (!content) {
+      return [];
+    }
+
+    return jsonParseLinter()(view);
+  };
+}
+
 type JSONEditorDefaultProps = Partial<JSONEditorProps>;
 
 const defaultProps: JSONEditorDefaultProps = {
   language: "json",
   readOnly: true,
   basicSetup: false,
+  linterEnabled: true,
+  allowEmpty: true,
 };
 
 export function JSONEditor(opts: JSONEditorProps) {
@@ -44,6 +62,8 @@ export function JSONEditor(opts: JSONEditorProps) {
     autoFocus,
     showCopyButton = true,
     showClearButton = true,
+    linterEnabled,
+    allowEmpty,
   } = {
     ...defaultProps,
     ...opts,
@@ -56,6 +76,19 @@ export function JSONEditor(opts: JSONEditorProps) {
 
   extensions.push(languageExtension());
 
+  if (linterEnabled) {
+    extensions.push(lintGutter());
+
+    switch (language) {
+      case "json": {
+        extensions.push(allowEmpty ? linter(emptyAwareJsonLinter()) : linter(jsonParseLinter()));
+        break;
+      }
+      default:
+        language satisfies never;
+    }
+  }
+
   const editor = useRef<HTMLDivElement>(null);
   const settings: Omit<UseCodeMirror, "onBlur"> = {
     ...opts,
diff --git a/apps/webapp/app/components/code/codeMirrorSetup.ts b/apps/webapp/app/components/code/codeMirrorSetup.ts
index 89988c3d9bc..811a6ebc298 100644
--- a/apps/webapp/app/components/code/codeMirrorSetup.ts
+++ b/apps/webapp/app/components/code/codeMirrorSetup.ts
@@ -1,8 +1,7 @@
 import { closeBrackets } from "@codemirror/autocomplete";
 import { indentWithTab } from "@codemirror/commands";
-import { jsonParseLinter } from "@codemirror/lang-json";
 import { bracketMatching } from "@codemirror/language";
-import { lintGutter, lintKeymap, linter } from "@codemirror/lint";
+import { lintKeymap } from "@codemirror/lint";
 import { highlightSelectionMatches } from "@codemirror/search";
 import { Prec, type Extension } from "@codemirror/state";
 import {
@@ -21,8 +20,6 @@ export function getEditorSetup(showLineNumbers = true, showHighlights = true): A
     dropCursor(),
     bracketMatching(),
     closeBrackets(),
-    lintGutter(),
-    linter(jsonParseLinter()),
     Prec.highest(
       keymap.of([
         {
diff --git a/apps/webapp/app/components/code/codeMirrorTheme.ts b/apps/webapp/app/components/code/codeMirrorTheme.ts
index 74d074b3cd7..25167051340 100644
--- a/apps/webapp/app/components/code/codeMirrorTheme.ts
+++ b/apps/webapp/app/components/code/codeMirrorTheme.ts
@@ -39,6 +39,32 @@ export function darkTheme(): Extension {
         fontSize: "14px",
       },
 
+      ".cm-tooltip.cm-tooltip-lint": {
+        backgroundColor: tooltipBackground,
+      },
+
+      ".cm-diagnostic": {
+        padding: "4px 8px",
+        color: ivory,
+        fontFamily: "Geist Mono Variable",
+        fontSize: "12px",
+      },
+
+      ".cm-diagnostic-error": {
+        borderLeft: "2px solid #e11d48",
+      },
+
+      ".cm-lint-marker-error": {
+        content: "none",
+        backgroundColor: "#e11d48",
+        height: "100%",
+        width: "2px",
+      },
+
+      ".cm-lintPoint:after": {
+        borderBottom: "4px solid #e11d48",
+      },
+
       ".cm-cursor, .cm-dropCursor": { borderLeftColor: cursor },
       "&.cm-focused .cm-selectionBackground, .cm-selectionBackground, .cm-content ::selection": {
         backgroundColor: selection,
@@ -82,6 +108,7 @@ export function darkTheme(): Extension {
 
       ".cm-tooltip": {
         border: "none",
+        marginTop: "6px",
         backgroundColor: tooltipBackground,
       },
       ".cm-tooltip .cm-tooltip-arrow:before": {
diff --git a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.test.tasks.$taskParam/route.tsx b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.test.tasks.$taskParam/route.tsx
index 6205336d75b..abdfcda4c38 100644
--- a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.test.tasks.$taskParam/route.tsx
+++ b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.test.tasks.$taskParam/route.tsx
@@ -291,7 +291,6 @@ function StandardTaskForm({ task, runs }: { task: TestTask["task"]; runs: Standa
                 }}
                 height="100%"
                 autoFocus={!tab || tab === "payload"}
-                placeholder="{ }"
                 className={cn("h-full overflow-auto", tab === "metadata" && "hidden")}
               />
               <JSONEditor
diff --git a/apps/webapp/app/v3/testTask.ts b/apps/webapp/app/v3/testTask.ts
index 289d519524a..764f6b09f71 100644
--- a/apps/webapp/app/v3/testTask.ts
+++ b/apps/webapp/app/v3/testTask.ts
@@ -4,46 +4,42 @@ export const TestTaskData = z
   .discriminatedUnion("triggerSource", [
     z.object({
       triggerSource: z.literal("STANDARD"),
-      payload: z.string().transform((payload, ctx) => {
-        try {
-          const data = JSON.parse(payload);
-          return data as any;
-        } catch (e) {
-          console.log("parsing error", e);
+      payload: z
+        .string()
+        .optional()
+        .transform((val, ctx) => {
+          if (!val) {
+            return {};
+          }
 
-          if (e instanceof Error) {
-            ctx.addIssue({
-              code: z.ZodIssueCode.custom,
-              message: e.message,
-            });
-          } else {
+          try {
+            return JSON.parse(val);
+          } catch {
             ctx.addIssue({
               code: z.ZodIssueCode.custom,
-              message: "This is invalid JSON",
+              message: "Payload must be a valid JSON string",
             });
+            return z.NEVER;
+          }
+        }),
+      metadata: z
+        .string()
+        .optional()
+        .transform((val, ctx) => {
+          if (!val) {
+            return {};
           }
-        }
-      }),
-      metadata: z.string().transform((metadata, ctx) => {
-        try {
-          const data = JSON.parse(metadata);
-          return data as any;
-        } catch (e) {
-          console.log("parsing error", e);
 
-          if (e instanceof Error) {
-            ctx.addIssue({
-              code: z.ZodIssueCode.custom,
-              message: e.message,
-            });
-          } else {
+          try {
+            return JSON.parse(val);
+          } catch {
             ctx.addIssue({
               code: z.ZodIssueCode.custom,
-              message: "This is invalid JSON",
+              message: "Metadata must be a valid JSON string",
             });
+            return z.NEVER;
           }
-        }
-      }),
+        }),
     }),
     z.object({
       triggerSource: z.literal("SCHEDULED"),


Reviews:
user: login: myftija
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-22T00:29:56Z
user: login: matt-aitken
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-22T06:55:38Z
user: login: matt-aitken
state: APPROVED
author_association: MEMBER
submitted_at: 2025-04-22T15:10:48Z

Review comments:
diff_hunk: @@ -4,46 +4,42 @@ export const TestTaskData = z
   .discriminatedUnion("triggerSource", [
     z.object({
       triggerSource: z.literal("STANDARD"),
-      payload: z.string().transform((payload, ctx) => {
-        try {
-          const data = JSON.parse(payload);
-          return data as any;
-        } catch (e) {
-          console.log("parsing error", e);
+      payload: z
+        .string()
+        .optional()
+        .transform((val, ctx) => {
+          if (!val) {
+            return {};
path: apps/webapp/app/v3/testTask.ts
user: login: myftija
body: Returning `undefined` here would be more fitting, but it breaks the replay flow, which doesn't handle this well. The fix seemed a bit involved, so leaving this for a separate PR. In the meantime, the behavior stays the same, as users would still pass `{ }` as empty payload for tests. 

created_at: 2025-04-22T00:29:56Z
author_association: COLLABORATOR
diff_hunk: @@ -4,46 +4,42 @@ export const TestTaskData = z
   .discriminatedUnion("triggerSource", [
     z.object({
       triggerSource: z.literal("STANDARD"),
-      payload: z.string().transform((payload, ctx) => {
-        try {
-          const data = JSON.parse(payload);
-          return data as any;
-        } catch (e) {
-          console.log("parsing error", e);
+      payload: z
+        .string()
+        .optional()
+        .transform((val, ctx) => {
+          if (!val) {
+            return {};
path: apps/webapp/app/v3/testTask.ts
user: login: matt-aitken
body: Good idea
created_at: 2025-04-22T06:55:38Z
author_association: MEMBER

PR comments:
user: login: changeset-bot[bot]
created_at: 2025-04-22T00:22:58Z
body: ###  ⚠️  No Changeset found

Latest commit: 134e486fe3ab1676147de1b2b219cf15073067b2

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. **If these changes should result in a version bump, you need to add a changeset.**



[Click here to learn what changesets are, and how to add one](https://github.com/changesets/changesets/blob/main/docs/adding-a-changeset.md).

[Click here if you're a maintainer who wants to add a changeset to this PR](https://github.com/myftija/trigger.dev/new/allow-empty-payload-json-editor?filename=.changeset/fifty-carrots-thank.md&value=---%0A%22%40fake-scope%2Ffake-pkg%22%3A%20patch%0A---%0A%0AAllow%20empty%20payloads%20in%20test%20runs%0A)


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

## Walkthrough

The changes introduce configurable linting behavior to the JSONEditor component, allowing users to enable or disable linting and to specify whether empty content should be considered valid. Supporting logic was added to handle linting based on these new props. Editor theme styles were updated to improve the appearance of linting diagnostics. The default editor setup was simplified by removing built-in linting extensions, delegating linting responsibility to the JSONEditor component. Additionally, JSON parsing and validation logic was improved in the test task schema for clearer error reporting, and a placeholder attribute was removed from a JSONEditor instance.

## Changes

| File(s)                                                                                          | Change Summary                                                                                                       |
|--------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------|
| `apps/webapp/app/components/code/JSONEditor.tsx`                                                 | Added `linterEnabled` and `allowEmpty` props, implemented empty-aware linter logic, updated default props, and adjusted editor setup to use new linting behavior. |
| `apps/webapp/app/components/code/codeMirrorSetup.ts`                                             | Removed linting-related imports and extensions; editor setup no longer includes built-in linting.                   |
| `apps/webapp/app/components/code/codeMirrorTheme.ts`                                             | Added and updated styles for linting and diagnostic UI elements in the dark theme.                                  |
| `apps/webapp/app/routes/_app.orgs...test.tasks.$taskParam/route.tsx`                             | Removed the placeholder attribute from a JSONEditor component in the StandardTaskForm.                              |
| `apps/webapp/app/v3/testTask.ts`                                                                 | Refactored JSON parsing for `payload` and `metadata` fields to improve error handling and reporting in schema logic. |

## Sequence Diagram(s)

```mermaid
sequenceDiagram
    participant User
    participant JSONEditor
    participant CodeMirror
    participant Linter

    User->>JSONEditor: Edit JSON content
    JSONEditor->>CodeMirror: Update editor state
    alt Linting enabled
        JSONEditor->>Linter: Run linter (empty-aware if allowEmpty)
        Linter-->>JSONEditor: Return diagnostics (or none if empty)
        JSONEditor->>CodeMirror: Display diagnostics with custom styling
    else Linting disabled
        JSONEditor->>CodeMirror: No diagnostics shown
    end
```

## Poem

> In the warren where code runs deep,  
> JSON now gets a careful sweep.  
> Linting toggled, errors bright,  
> Empty fields? That’s now all right!  
> With colors clear and checks anew,  
> Our editor’s smarter—thanks to this crew.  
> 🐇✨

<!-- walkthrough_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 2c19693cfc31aae5154402de49aec5e08f50aadb and 134e486fe3ab1676147de1b2b219cf15073067b2.

</details>

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

* `apps/webapp/app/components/code/JSONEditor.tsx` (5 hunks)
* `apps/webapp/app/components/code/codeMirrorSetup.ts` (1 hunks)
* `apps/webapp/app/components/code/codeMirrorTheme.ts` (2 hunks)
* `apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.test.tasks.$taskParam/route.tsx` (0 hunks)
* `apps/webapp/app/v3/testTask.ts` (1 hunks)

</details>

<details>
<summary>💤 Files with no reviewable changes (1)</summary>

* apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.test.tasks.$taskParam/route.tsx

</details>

<details>
<summary>⏰ Context from checks skipped due to timeout of 90000ms (5)</summary>

* GitHub Check: e2e / 🧪 CLI v3 tests (windows-latest - pnpm)
* GitHub Check: e2e / 🧪 CLI v3 tests (windows-latest - npm)
* GitHub Check: e2e / 🧪 CLI v3 tests (ubuntu-latest - pnpm)
* GitHub Check: e2e / 🧪 CLI v3 tests (ubuntu-latest - npm)
* GitHub Check: typecheck / typecheck

</details>

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

<details>
<summary>apps/webapp/app/components/code/codeMirrorSetup.ts (2)</summary>

`4-4`: **Good modularization of linting functionality.**

The import changes here are part of a larger refactoring that moves linting responsibility from the global editor setup to the JSONEditor component. This allows for more flexible and configurable linting behavior.

---

`34-34`: **LGTM: Keymap integration preserved correctly.**

You've appropriately maintained the lintKeymap integration in the editor setup while removing the automatic inclusion of linting extensions. This preserves keyboard shortcuts for linting without forcing linting behavior.

</details>
<details>
<summary>apps/webapp/app/components/code/codeMirrorTheme.ts (2)</summary>

`42-67`: **Well-structured styling for linting UI elements.**

The added styles for linting tooltips, diagnostics, and markers provide a cohesive visual experience that fits with the overall theme. The error styling with red borders and indicators clearly highlights validation issues for users.

---

`111-111`: **Maintain consistent spacing in the theme.**

The marginTop adjustment for tooltips helps maintain consistent spacing and improves the overall appearance of the editor.

</details>
<details>
<summary>apps/webapp/app/components/code/JSONEditor.tsx (6)</summary>

`1-2`: **LGTM: Necessary imports for linting functionality.**

The updated imports correctly bring in the required linting functionality that was previously imported in the codeMirrorSetup file.



Also applies to: 11-11

---

`22-23`: **Good configuration options for linting flexibility.**

Adding the `linterEnabled` and `allowEmpty` props provides excellent flexibility for consumers of the JSONEditor component, aligning with the PR objective to allow empty payloads.

---

`30-41`: **Elegant solution for handling empty content.**

The `emptyAwareJsonLinter` function is a smart approach that allows empty content to be valid while still applying linting rules to non-empty content. The implementation is clean and focused.

---

`49-50`: **Sensible default configuration.**

Setting `linterEnabled: true` and `allowEmpty: true` as defaults provides a good balance of syntax validation while supporting the PR objective of allowing empty payloads.

---

`65-66`: **Proper prop destructuring.**

The new props are correctly destructured from the combined default and user-provided props.

---

`79-90`: **Well-structured conditional linting setup.**

The approach to conditionally add linting extensions is well-organized:
1. Only adds linting if enabled
2. Uses a switch statement for language-specific linting, making it easy to extend
3. Applies the appropriate linter based on the `allowEmpty` setting

The `language satisfies never` in the default case is a good TypeScript pattern to ensure exhaustive handling of all language types.

</details>
<details>
<summary>apps/webapp/app/v3/testTask.ts (3)</summary>

`7-14`: **Improved payload handling with optional support.**

The schema changes properly handle optional payloads, returning an empty object for falsy values. This aligns with the UI changes to allow empty payloads in the editor.

---

`15-24`: **Cleaner error handling for JSON parsing.**

The updated transformation logic provides clearer error messages when JSON parsing fails, improving the user experience while maintaining validation integrity.

---

`25-42`: **Consistent approach for metadata handling.**

The metadata field uses the same pattern as payload, maintaining consistency in the codebase for similar types of data. The error message is appropriately specific to metadata.

</details>

</blockquote></details>

</details>
<!-- internal state start -->


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

<!-- internal state end -->
<!-- finishing_touch_checkbox_start -->

<details>
<summary>✨ Finishing Touches</summary>

- [ ] <!-- {"checkboxId": "7962f53c-55bc-4827-bfbf-6a18da830691"} --> 📝 Generate Docstrings

</details>

<!-- finishing_touch_checkbox_end -->
<!-- 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=triggerdotdev/trigger.dev&utm_content=1962):

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