Conversation
WalkthroughProvider-aware AI rule schema introduced; prompt-to-rules split into new (create-only) and legacy (old) implementations; new RulesPrompt workflow, RulesTab, and CreatedRulesModal added; Examples and Rules UI updated; createRulesAction/createRulesBody and CreateRuleResult type added; RuleForm/RuleDialog APIs adjusted; tests updated and version bumped to v2.6.0. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
actor User
participant RulesPrompt as RulesPromptNew (UI)
participant Editor as SimpleRichTextEditor
participant Persona as PersonaDialog
participant Examples as ExamplesList
participant Server as createRulesAction
participant Toast as toast.promise
User->>RulesPrompt: Open Rules tab
RulesPrompt->>Editor: Initialize editorRef
User->>Persona: Choose persona
Persona-->>RulesPrompt: Selected persona
RulesPrompt->>Editor: Append persona prompt
User->>Examples: Click example
Examples-->>RulesPrompt: Example text
RulesPrompt->>Editor: Append example text
User->>RulesPrompt: Submit form
RulesPrompt->>Toast: Show pending
RulesPrompt->>Server: createRulesAction({ prompt })
Server-->>RulesPrompt: { rules }
RulesPrompt->>Toast: Show success
sequenceDiagram
autonumber
actor User
participant Rules as Rules.tsx
participant Chat as useChat
participant Sidebar as useSidebar
User->>Rules: Open rule action menu
alt Edit via AI selected
Rules->>Chat: setInput("Edit rule ‘<name>’ ...")
Rules->>Sidebar: setOpen(prev => [...prev, "chat-sidebar"])
else Edit manually selected
Rules->>Rules: Open manual edit form
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Suggested reviewers
Poem
Tip 🔌 Remote MCP (Model Context Protocol) integration is now available!Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats. 📜 Recent review detailsConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro 💡 Knowledge Base configuration:
You can enable these sources in your CodeRabbit configuration. 📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
✨ Finishing Touches
🧪 Generate unit tests
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. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR/Issue comments)Type Other keywords and placeholders
Status, Documentation and Community
|
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (3)
apps/web/app/(app)/[emailAccountId]/assistant/FixWithChat.tsx (1)
1-1: Missing "use client" directive – this component uses hooks and event handlers.This module calls React hooks (useState, custom hooks) and attaches onClick handlers, so it must be a client component per our project rule for apps/web/app/**/*.tsx. Add the directive to avoid Next.js RSC/runtime issues.
Apply:
+ "use client"; + import { MessageCircleIcon } from "lucide-react"; import { useState } from "react";apps/web/app/(app)/[emailAccountId]/assistant/ExamplesList.tsx (1)
1-1: Add "use client" – this component attaches onClick handlers.Per our guideline for apps/web/app/**/*.tsx, components with onClick must be client components. Add the directive.
+ "use client"; + import { memo } from "react";apps/web/app/(app)/[emailAccountId]/assistant/ProcessingPromptFileDialog.tsx (1)
1-1: Missing "use client" — this component uses React hooks and onClick.This file renders interactive UI and calls hooks; it must be a Client Component.
Apply at the very top:
+ "use client"; + import { useCallback, useEffect, useState } from "react";
🧹 Nitpick comments (20)
apps/web/app/(app)/[emailAccountId]/assistant/FixWithChat.tsx (1)
77-77: Prevent duplicate sidebar entries when opening the chat.Appending blindly can introduce repeated "chat-sidebar" keys. Deduplicate in the updater.
- setOpen((arr) => [...arr, "chat-sidebar"]); + setOpen((arr) => + arr.includes("chat-sidebar") ? arr : [...arr, "chat-sidebar"], + );apps/web/utils/actions/rule.validation.ts (1)
176-178: Tighten validation for prompt input and align naming with existing schemas.
- Consider enforcing a non-empty prompt after trim to prevent accidental empty submissions.
- Optional: For consistency with saveRulesPromptBody, either document the difference (prompt vs rulesPrompt) or harmonize the field name to reduce mental overhead.
-export const createRulesBody = z.object({ prompt: z.string().trim() }); +export const createRulesBody = z.object({ + prompt: z + .string() + .trim() + .min(1, "Please enter a prompt"), +}); export type CreateRulesBody = z.infer<typeof createRulesBody>;If you prefer harmonizing the property name:
-export const createRulesBody = z.object({ prompt: z.string().trim() }); +export const createRulesBody = z.object({ rulesPrompt: z.string().trim().min(1, "Please enter a prompt") });I can update the corresponding action and callers if you want to standardize.
apps/web/app/(app)/[emailAccountId]/assistant/ExamplesList.tsx (1)
11-13: Prop API change looks good; minor DX suggestion.The provider + className props make the component self-contained and configurable. Consider documenting the expected provider values (e.g., "gmail" | "outlook") via a union type to catch typos at compile time.
- provider: string; + provider: "gmail" | "outlook" | string; // refine if you have a Provider typeapps/web/app/(app)/[emailAccountId]/assistant/ProcessingPromptFileDialog.tsx (2)
141-147: Add a responsive sizes attribute to Next/Image to avoid downloading a 800×600 image on mobile.<Image src="/images/assistant/rules.png" alt="Analyzing prompt file" width={800} height={600} + sizes="(min-width: 768px) 800px, 100vw" className="rounded-lg shadow" />
231-256: Provide a friendly fallback when no changes were made.If no rules were created/edited/removed,
getDescription()returns an empty string. Consider a default message to avoid an empty description block.function getDescription() { let message = ""; @@ - return message; + return message || "No changes were needed this time."; }apps/web/app/(app)/[emailAccountId]/assistant/AIChatButton.tsx (1)
14-15: Prevent duplicate sidebar entries when clicking multiple times.- onClick={() => setOpen((arr) => [...arr, "chat-sidebar"])} + onClick={() => + setOpen((arr) => + arr.includes("chat-sidebar") ? arr : [...arr, "chat-sidebar"], + ) + }apps/web/app/(app)/[emailAccountId]/assistant/Rules.tsx (1)
350-361: Avoid pushing duplicate "chat-sidebar" entries.- <DropdownMenuItem - onClick={() => { - setInput( - `I'd like to edit the "${rule.name}" rule:\n`, - ); - setOpen((arr) => [...arr, "chat-sidebar"]); - }} - > + <DropdownMenuItem + onClick={() => { + setInput( + `I'd like to edit the "${rule.name}" rule:\n`, + ); + setOpen((arr) => + arr.includes("chat-sidebar") + ? arr + : [...arr, "chat-sidebar"], + ); + }} + >apps/web/__tests__/ai-prompt-to-rules.test.ts (2)
32-32: Use console.debug with a descriptive prefix for AI test artifacts.Project guidelines prefer descriptive debug statements in AI tests. Switch from
console.logand add context.- console.log(JSON.stringify(result, null, 2)); + console.debug("[ai-prompt-to-rules] Result:", JSON.stringify(result, null, 2));
2-2: Add tests for the newprompt-to-rulesimplementationWe currently only exercise the legacy
aiPromptToRulesOldin
apps/web/__tests__/ai-prompt-to-rules.test.ts. The neweraiPromptToRulesfrom
@/utils/ai/rule/prompt-to-rules.tsis already wired up in production code
(apps/web/utils/actions/ai-rule.ts:23) but has no dedicated tests. To avoid regressions when switching providers or flows, please add a parallel test suite covering the new implementation.• Legacy test coverage:
– File:apps/web/__tests__/ai-prompt-to-rules.test.ts
– Imports onlyaiPromptToRulesOld
• Production usage:
– File:apps/web/utils/actions/ai-rule.ts(line 23)
– ImportsaiPromptToRulesfrom@/utils/ai/rule/prompt-to-rules
• Suggested action:
– Create a new test file (e.g.apps/web/__tests__/ai-prompt-to-rules-new.test.ts)
– Mirror the existing test cases against the new function signature and expected outputsapps/web/utils/actions/ai-rule.ts (3)
36-36: Explicit legacy path import is fine — just keep drift under controlBringing in aiPromptToRulesOld for the “diff/edit” flows makes sense since it supports ruleId for updates. To avoid long-term drift between old/new convertors, consider extracting the shared system prompt and schema selection into a small utility used by both modules.
345-353: Using aiPromptToRulesOld for added rules in edit flow is appropriateThis ensures the schema can include categories and remains compatible with the edited-rules path. Minor type clarity suggestion: addedRules is typed as Awaited<ReturnType>; since this branch resolves to aiPromptToRulesOld, widening the variable to CreateOrUpdateRuleSchemaWithCategories[] would be clearer for future readers.
471-475: New-prompt branch correctly routes through the old converter for bulk additionsConsistent with the diff/edit path. Same note as above about clarifying addedRules’ type in this scope.
apps/web/app/(app)/[emailAccountId]/assistant/RulesPrompt.tsx (1)
139-156: Remove duplicate submission state toggles; simplify toast.promise usageisSubmitting is set in both onSubmit and inside saveRulesPromise. The inner setIsSubmitting calls are redundant and can lead to flicker. Suggest trimming state changes to the outer scope only.
- const saveRulesPromise = async (data: SaveRulesPromptBody) => { - setIsSubmitting(true); + const saveRulesPromise = async (data: SaveRulesPromptBody) => { const result = await saveRulesPromptAction(emailAccountId, data); if (result?.serverError) { - setIsSubmitting(false); throw new Error(result.serverError); } @@ - setIsSubmitting(false); - return result; }; @@ - toast.promise(() => saveRulesPromise({ rulesPrompt: markdown }), { + toast.promise(saveRulesPromise({ rulesPrompt: markdown }), { loading: "Saving rules... This may take a while to process...", success: (result) => { const { createdRules = 0, editedRules = 0, removedRules = 0, } = result?.data || {}; setResult({ createdRules, editedRules, removedRules }); @@ return `Rules saved successfully! ${message}`; }, error: (err) => { return `Error saving rules: ${err.message}`; }, });Also, passing the Promise directly to toast.promise avoids an extra wrapper function.
Also applies to: 163-187
apps/web/utils/ai/rule/prompt-to-rules-old.ts (3)
93-99: Fix typo in system prompt“conver them into rules” → “convert them into rules”. Small, but worth correcting to avoid ambiguity in model instructions.
- return `You are an AI assistant that converts email management rules into a structured format. Parse the given prompt file and conver them into rules. + return `You are an AI assistant that converts email management rules into a structured format. Parse the given prompt file and convert them into rules.
51-76: Add retry around the LLM call and cap prompt length to control token usagePer the AI utils guidelines, add withRetry for transient failures and truncate overly long inputs to a safe limit before sending to the model. Consider centralizing truncation logic in a helper to keep this module and the new one in sync.
I can wire in withRetry(createGenerateObject(...)) and a shared truncateForModel helper used by both prompt-to-rules modules.
88-221: De-duplicate the system prompt with the new converter to prevent driftThe examples and instruction text are nearly identical to prompt-to-rules.ts but already have small wording differences. Extract getSystemPrompt to a shared utility (e.g., utils/ai/rule/prompts.ts) and pass flags like hasSmartCategories to tailor sections.
apps/web/app/(app)/[emailAccountId]/assistant/RulesPromptNew.tsx (2)
9-11: Remove unused type importCreateRulesBody is imported but not used. Drop it to satisfy linters and reduce bundle size.
-import type { CreateRulesBody } from "@/utils/actions/rule.validation";
95-135: Submit flow looks solid; add an empty-prompt guard for UX parity with serverMirror the server-side guard to short-circuit when the editor is empty and provide immediate feedback:
const onSubmit = useCallback(async () => { const markdown = editorRef.current?.getMarkdown(); - if (typeof markdown !== "string") return; + if (typeof markdown !== "string") return; + if (!markdown.trim()) { + toast.error("Please write at least one rule or example."); + return; + }Also consider moving mutate() and navigation into the success handler only; they are already inside the toast.promise closure, so you’re fine, but keeping all state transitions in one place reduces race conditions.
apps/web/utils/ai/rule/prompt-to-rules.ts (2)
41-46: Minor copy consistency and shared prompt extraction
- The prompt header still says “prompt file”; elsewhere (system prompt) you call it “prompt”. Consider picking one and using it across both modules. Purely cosmetic.
- As noted on the legacy module, extract getSystemPrompt to a shared helper to avoid drift.
- const prompt = `Convert the following prompt file into rules: + const prompt = `Convert the following prompt into rules:Also applies to: 77-205
55-60: Add retry/error classification around the model callWrapping generateObject in a retry helper with backoff and classifying provider timeouts/transient errors would make this path more robust. Optional, but recommended per the AI utils guidelines.
I can add withRetry(...) here and wire provider-specific retryable error checks.
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (15)
apps/web/__tests__/ai-prompt-to-rules.test.ts(2 hunks)apps/web/app/(app)/[emailAccountId]/assistant/AIChatButton.tsx(1 hunks)apps/web/app/(app)/[emailAccountId]/assistant/AssistantOnboarding.tsx(2 hunks)apps/web/app/(app)/[emailAccountId]/assistant/ExamplesList.tsx(1 hunks)apps/web/app/(app)/[emailAccountId]/assistant/FixWithChat.tsx(1 hunks)apps/web/app/(app)/[emailAccountId]/assistant/ProcessingPromptFileDialog.tsx(7 hunks)apps/web/app/(app)/[emailAccountId]/assistant/RuleForm.tsx(1 hunks)apps/web/app/(app)/[emailAccountId]/assistant/Rules.tsx(4 hunks)apps/web/app/(app)/[emailAccountId]/assistant/RulesPrompt.tsx(4 hunks)apps/web/app/(app)/[emailAccountId]/assistant/RulesPromptNew.tsx(1 hunks)apps/web/app/(app)/[emailAccountId]/assistant/RulesTabNew.tsx(1 hunks)apps/web/utils/actions/ai-rule.ts(7 hunks)apps/web/utils/actions/rule.validation.ts(1 hunks)apps/web/utils/ai/rule/prompt-to-rules-old.ts(1 hunks)apps/web/utils/ai/rule/prompt-to-rules.ts(3 hunks)
🧰 Additional context used
📓 Path-based instructions (28)
apps/web/**/*.{ts,tsx}
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
apps/web/**/*.{ts,tsx}: Use TypeScript with strict null checks
Path aliases: Use@/for imports from project root
Use proper error handling with try/catch blocks
Format code with Prettier
Leverage TypeScript inference for better DX
Files:
apps/web/app/(app)/[emailAccountId]/assistant/FixWithChat.tsxapps/web/app/(app)/[emailAccountId]/assistant/AIChatButton.tsxapps/web/app/(app)/[emailAccountId]/assistant/AssistantOnboarding.tsxapps/web/utils/actions/rule.validation.tsapps/web/app/(app)/[emailAccountId]/assistant/RuleForm.tsxapps/web/app/(app)/[emailAccountId]/assistant/RulesTabNew.tsxapps/web/app/(app)/[emailAccountId]/assistant/RulesPromptNew.tsxapps/web/app/(app)/[emailAccountId]/assistant/ExamplesList.tsxapps/web/utils/ai/rule/prompt-to-rules-old.tsapps/web/__tests__/ai-prompt-to-rules.test.tsapps/web/app/(app)/[emailAccountId]/assistant/Rules.tsxapps/web/utils/ai/rule/prompt-to-rules.tsapps/web/utils/actions/ai-rule.tsapps/web/app/(app)/[emailAccountId]/assistant/RulesPrompt.tsxapps/web/app/(app)/[emailAccountId]/assistant/ProcessingPromptFileDialog.tsx
apps/web/app/**
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
NextJS app router structure with (app) directory
Files:
apps/web/app/(app)/[emailAccountId]/assistant/FixWithChat.tsxapps/web/app/(app)/[emailAccountId]/assistant/AIChatButton.tsxapps/web/app/(app)/[emailAccountId]/assistant/AssistantOnboarding.tsxapps/web/app/(app)/[emailAccountId]/assistant/RuleForm.tsxapps/web/app/(app)/[emailAccountId]/assistant/RulesTabNew.tsxapps/web/app/(app)/[emailAccountId]/assistant/RulesPromptNew.tsxapps/web/app/(app)/[emailAccountId]/assistant/ExamplesList.tsxapps/web/app/(app)/[emailAccountId]/assistant/Rules.tsxapps/web/app/(app)/[emailAccountId]/assistant/RulesPrompt.tsxapps/web/app/(app)/[emailAccountId]/assistant/ProcessingPromptFileDialog.tsx
apps/web/**/*.tsx
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
apps/web/**/*.tsx: Follow tailwindcss patterns with prettier-plugin-tailwindcss
Prefer functional components with hooks
Use shadcn/ui components when available
Ensure responsive design with mobile-first approach
Follow consistent naming conventions (PascalCase for components)
Use LoadingContent component for async data
Useresult?.serverErrorwithtoastErrorandtoastSuccess
UseLoadingContentcomponent to handle loading and error states consistently
Passloading,error, and children props toLoadingContent
Files:
apps/web/app/(app)/[emailAccountId]/assistant/FixWithChat.tsxapps/web/app/(app)/[emailAccountId]/assistant/AIChatButton.tsxapps/web/app/(app)/[emailAccountId]/assistant/AssistantOnboarding.tsxapps/web/app/(app)/[emailAccountId]/assistant/RuleForm.tsxapps/web/app/(app)/[emailAccountId]/assistant/RulesTabNew.tsxapps/web/app/(app)/[emailAccountId]/assistant/RulesPromptNew.tsxapps/web/app/(app)/[emailAccountId]/assistant/ExamplesList.tsxapps/web/app/(app)/[emailAccountId]/assistant/Rules.tsxapps/web/app/(app)/[emailAccountId]/assistant/RulesPrompt.tsxapps/web/app/(app)/[emailAccountId]/assistant/ProcessingPromptFileDialog.tsx
!{.cursor/rules/*.mdc}
📄 CodeRabbit inference engine (.cursor/rules/cursor-rules.mdc)
Never place rule files in the project root, in subdirectories outside .cursor/rules, or in any other location
Files:
apps/web/app/(app)/[emailAccountId]/assistant/FixWithChat.tsxapps/web/app/(app)/[emailAccountId]/assistant/AIChatButton.tsxapps/web/app/(app)/[emailAccountId]/assistant/AssistantOnboarding.tsxapps/web/utils/actions/rule.validation.tsapps/web/app/(app)/[emailAccountId]/assistant/RuleForm.tsxapps/web/app/(app)/[emailAccountId]/assistant/RulesTabNew.tsxapps/web/app/(app)/[emailAccountId]/assistant/RulesPromptNew.tsxapps/web/app/(app)/[emailAccountId]/assistant/ExamplesList.tsxapps/web/utils/ai/rule/prompt-to-rules-old.tsapps/web/__tests__/ai-prompt-to-rules.test.tsapps/web/app/(app)/[emailAccountId]/assistant/Rules.tsxapps/web/utils/ai/rule/prompt-to-rules.tsapps/web/utils/actions/ai-rule.tsapps/web/app/(app)/[emailAccountId]/assistant/RulesPrompt.tsxapps/web/app/(app)/[emailAccountId]/assistant/ProcessingPromptFileDialog.tsx
**/*.tsx
📄 CodeRabbit inference engine (.cursor/rules/form-handling.mdc)
**/*.tsx: Use React Hook Form with Zod for validation
Validate form inputs before submission
Show validation errors inline next to form fields
Files:
apps/web/app/(app)/[emailAccountId]/assistant/FixWithChat.tsxapps/web/app/(app)/[emailAccountId]/assistant/AIChatButton.tsxapps/web/app/(app)/[emailAccountId]/assistant/AssistantOnboarding.tsxapps/web/app/(app)/[emailAccountId]/assistant/RuleForm.tsxapps/web/app/(app)/[emailAccountId]/assistant/RulesTabNew.tsxapps/web/app/(app)/[emailAccountId]/assistant/RulesPromptNew.tsxapps/web/app/(app)/[emailAccountId]/assistant/ExamplesList.tsxapps/web/app/(app)/[emailAccountId]/assistant/Rules.tsxapps/web/app/(app)/[emailAccountId]/assistant/RulesPrompt.tsxapps/web/app/(app)/[emailAccountId]/assistant/ProcessingPromptFileDialog.tsx
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/logging.mdc)
**/*.{ts,tsx}: UsecreateScopedLoggerfor logging in backend TypeScript files
Typically add the logger initialization at the top of the file when usingcreateScopedLogger
Only use.with()on a logger instance within a specific function, not for a global loggerImport Prisma in the project using
import prisma from "@/utils/prisma";
**/*.{ts,tsx}: Don't use TypeScript enums.
Don't use TypeScript const enum.
Don't use the TypeScript directive @ts-ignore.
Don't use primitive type aliases or misleading types.
Don't use empty type parameters in type aliases and interfaces.
Don't use any or unknown as type constraints.
Don't use implicit any type on variable declarations.
Don't let variables evolve into any type through reassignments.
Don't use non-null assertions with the ! postfix operator.
Don't misuse the non-null assertion operator (!) in TypeScript files.
Don't use user-defined types.
Use as const instead of literal types and type annotations.
Use export type for types.
Use import type for types.
Don't declare empty interfaces.
Don't merge interfaces and classes unsafely.
Don't use overload signatures that aren't next to each other.
Use the namespace keyword instead of the module keyword to declare TypeScript namespaces.
Don't use TypeScript namespaces.
Don't export imported variables.
Don't add type annotations to variables, parameters, and class properties that are initialized with literal expressions.
Don't use parameter properties in class constructors.
Use either T[] or Array consistently.
Initialize each enum member value explicitly.
Make sure all enum members are literal values.
Files:
apps/web/app/(app)/[emailAccountId]/assistant/FixWithChat.tsxapps/web/app/(app)/[emailAccountId]/assistant/AIChatButton.tsxapps/web/app/(app)/[emailAccountId]/assistant/AssistantOnboarding.tsxapps/web/utils/actions/rule.validation.tsapps/web/app/(app)/[emailAccountId]/assistant/RuleForm.tsxapps/web/app/(app)/[emailAccountId]/assistant/RulesTabNew.tsxapps/web/app/(app)/[emailAccountId]/assistant/RulesPromptNew.tsxapps/web/app/(app)/[emailAccountId]/assistant/ExamplesList.tsxapps/web/utils/ai/rule/prompt-to-rules-old.tsapps/web/__tests__/ai-prompt-to-rules.test.tsapps/web/app/(app)/[emailAccountId]/assistant/Rules.tsxapps/web/utils/ai/rule/prompt-to-rules.tsapps/web/utils/actions/ai-rule.tsapps/web/app/(app)/[emailAccountId]/assistant/RulesPrompt.tsxapps/web/app/(app)/[emailAccountId]/assistant/ProcessingPromptFileDialog.tsx
apps/web/app/(app)/*/**
📄 CodeRabbit inference engine (.cursor/rules/page-structure.mdc)
Components for the page are either put in page.tsx, or in the apps/web/app/(app)/PAGE_NAME folder
Files:
apps/web/app/(app)/[emailAccountId]/assistant/FixWithChat.tsxapps/web/app/(app)/[emailAccountId]/assistant/AIChatButton.tsxapps/web/app/(app)/[emailAccountId]/assistant/AssistantOnboarding.tsxapps/web/app/(app)/[emailAccountId]/assistant/RuleForm.tsxapps/web/app/(app)/[emailAccountId]/assistant/RulesTabNew.tsxapps/web/app/(app)/[emailAccountId]/assistant/RulesPromptNew.tsxapps/web/app/(app)/[emailAccountId]/assistant/ExamplesList.tsxapps/web/app/(app)/[emailAccountId]/assistant/Rules.tsxapps/web/app/(app)/[emailAccountId]/assistant/RulesPrompt.tsxapps/web/app/(app)/[emailAccountId]/assistant/ProcessingPromptFileDialog.tsx
apps/web/app/(app)/*/**/*.tsx
📄 CodeRabbit inference engine (.cursor/rules/page-structure.mdc)
If you need to use onClick in a component, that component is a client component and file must start with 'use client'
Files:
apps/web/app/(app)/[emailAccountId]/assistant/FixWithChat.tsxapps/web/app/(app)/[emailAccountId]/assistant/AIChatButton.tsxapps/web/app/(app)/[emailAccountId]/assistant/AssistantOnboarding.tsxapps/web/app/(app)/[emailAccountId]/assistant/RuleForm.tsxapps/web/app/(app)/[emailAccountId]/assistant/RulesTabNew.tsxapps/web/app/(app)/[emailAccountId]/assistant/RulesPromptNew.tsxapps/web/app/(app)/[emailAccountId]/assistant/ExamplesList.tsxapps/web/app/(app)/[emailAccountId]/assistant/Rules.tsxapps/web/app/(app)/[emailAccountId]/assistant/RulesPrompt.tsxapps/web/app/(app)/[emailAccountId]/assistant/ProcessingPromptFileDialog.tsx
apps/web/app/(app)/*/**/**/*.tsx
📄 CodeRabbit inference engine (.cursor/rules/page-structure.mdc)
If we're in a deeply nested component we will use swr to fetch via API
Files:
apps/web/app/(app)/[emailAccountId]/assistant/FixWithChat.tsxapps/web/app/(app)/[emailAccountId]/assistant/AIChatButton.tsxapps/web/app/(app)/[emailAccountId]/assistant/AssistantOnboarding.tsxapps/web/app/(app)/[emailAccountId]/assistant/RuleForm.tsxapps/web/app/(app)/[emailAccountId]/assistant/RulesTabNew.tsxapps/web/app/(app)/[emailAccountId]/assistant/RulesPromptNew.tsxapps/web/app/(app)/[emailAccountId]/assistant/ExamplesList.tsxapps/web/app/(app)/[emailAccountId]/assistant/Rules.tsxapps/web/app/(app)/[emailAccountId]/assistant/RulesPrompt.tsxapps/web/app/(app)/[emailAccountId]/assistant/ProcessingPromptFileDialog.tsx
apps/web/app/**/*.tsx
📄 CodeRabbit inference engine (.cursor/rules/project-structure.mdc)
Components with
onClickmust be client components withuse clientdirective
Files:
apps/web/app/(app)/[emailAccountId]/assistant/FixWithChat.tsxapps/web/app/(app)/[emailAccountId]/assistant/AIChatButton.tsxapps/web/app/(app)/[emailAccountId]/assistant/AssistantOnboarding.tsxapps/web/app/(app)/[emailAccountId]/assistant/RuleForm.tsxapps/web/app/(app)/[emailAccountId]/assistant/RulesTabNew.tsxapps/web/app/(app)/[emailAccountId]/assistant/RulesPromptNew.tsxapps/web/app/(app)/[emailAccountId]/assistant/ExamplesList.tsxapps/web/app/(app)/[emailAccountId]/assistant/Rules.tsxapps/web/app/(app)/[emailAccountId]/assistant/RulesPrompt.tsxapps/web/app/(app)/[emailAccountId]/assistant/ProcessingPromptFileDialog.tsx
**/*.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)
**/*.{js,jsx,ts,tsx}: Don't useelements in Next.js projects.
Don't use elements in Next.js projects.
Don't use namespace imports.
Don't access namespace imports dynamically.
Don't use global eval().
Don't use console.
Don't use debugger.
Don't use var.
Don't use with statements in non-strict contexts.
Don't use the arguments object.
Don't use consecutive spaces in regular expression literals.
Don't use the comma operator.
Don't use unnecessary boolean casts.
Don't use unnecessary callbacks with flatMap.
Use for...of statements instead of Array.forEach.
Don't create classes that only have static members (like a static namespace).
Don't use this and super in static contexts.
Don't use unnecessary catch clauses.
Don't use unnecessary constructors.
Don't use unnecessary continue statements.
Don't export empty modules that don't change anything.
Don't use unnecessary escape sequences in regular expression literals.
Don't use unnecessary labels.
Don't use unnecessary nested block statements.
Don't rename imports, exports, and destructured assignments to the same name.
Don't use unnecessary string or template literal concatenation.
Don't use String.raw in template literals when there are no escape sequences.
Don't use useless case statements in switch statements.
Don't use ternary operators when simpler alternatives exist.
Don't use useless this aliasing.
Don't initialize variables to undefined.
Don't use the void operators (they're not familiar).
Use arrow functions instead of function expressions.
Use Date.now() to get milliseconds since the Unix Epoch.
Use .flatMap() instead of map().flat() when possible.
Use literal property access instead of computed property access.
Don't use parseInt() or Number.parseInt() when binary, octal, or hexadecimal literals work.
Use concise optional chaining instead of chained logical expressions.
Use regular expression literals instead of the RegExp constructor when possible.
Don't use number literal object member names th...
Files:
apps/web/app/(app)/[emailAccountId]/assistant/FixWithChat.tsxapps/web/app/(app)/[emailAccountId]/assistant/AIChatButton.tsxapps/web/app/(app)/[emailAccountId]/assistant/AssistantOnboarding.tsxapps/web/utils/actions/rule.validation.tsapps/web/app/(app)/[emailAccountId]/assistant/RuleForm.tsxapps/web/app/(app)/[emailAccountId]/assistant/RulesTabNew.tsxapps/web/app/(app)/[emailAccountId]/assistant/RulesPromptNew.tsxapps/web/app/(app)/[emailAccountId]/assistant/ExamplesList.tsxapps/web/utils/ai/rule/prompt-to-rules-old.tsapps/web/__tests__/ai-prompt-to-rules.test.tsapps/web/app/(app)/[emailAccountId]/assistant/Rules.tsxapps/web/utils/ai/rule/prompt-to-rules.tsapps/web/utils/actions/ai-rule.tsapps/web/app/(app)/[emailAccountId]/assistant/RulesPrompt.tsxapps/web/app/(app)/[emailAccountId]/assistant/ProcessingPromptFileDialog.tsx
!pages/_document.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)
!pages/_document.{js,jsx,ts,tsx}: Don't import next/document outside of pages/_document.jsx in Next.js projects.
Don't import next/document outside of pages/_document.jsx in Next.js projects.
Files:
apps/web/app/(app)/[emailAccountId]/assistant/FixWithChat.tsxapps/web/app/(app)/[emailAccountId]/assistant/AIChatButton.tsxapps/web/app/(app)/[emailAccountId]/assistant/AssistantOnboarding.tsxapps/web/utils/actions/rule.validation.tsapps/web/app/(app)/[emailAccountId]/assistant/RuleForm.tsxapps/web/app/(app)/[emailAccountId]/assistant/RulesTabNew.tsxapps/web/app/(app)/[emailAccountId]/assistant/RulesPromptNew.tsxapps/web/app/(app)/[emailAccountId]/assistant/ExamplesList.tsxapps/web/utils/ai/rule/prompt-to-rules-old.tsapps/web/__tests__/ai-prompt-to-rules.test.tsapps/web/app/(app)/[emailAccountId]/assistant/Rules.tsxapps/web/utils/ai/rule/prompt-to-rules.tsapps/web/utils/actions/ai-rule.tsapps/web/app/(app)/[emailAccountId]/assistant/RulesPrompt.tsxapps/web/app/(app)/[emailAccountId]/assistant/ProcessingPromptFileDialog.tsx
**/*.{jsx,tsx}
📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)
**/*.{jsx,tsx}: Don't destructure props inside JSX components in Solid projects.
Don't use both children and dangerouslySetInnerHTML props on the same element.
Don't use Array index in keys.
Don't assign to React component props.
Don't define React components inside other components.
Don't use event handlers on non-interactive elements.
Don't assign JSX properties multiple times.
Don't add extra closing tags for components without children.
Use <>...</> instead of ....
Don't insert comments as text nodes.
Don't use the return value of React.render.
Make sure all dependencies are correctly specified in React hooks.
Make sure all React hooks are called from the top level of component functions.
Don't use unnecessary fragments.
Don't pass children as props.
Use semantic elements instead of role attributes in JSX.
Files:
apps/web/app/(app)/[emailAccountId]/assistant/FixWithChat.tsxapps/web/app/(app)/[emailAccountId]/assistant/AIChatButton.tsxapps/web/app/(app)/[emailAccountId]/assistant/AssistantOnboarding.tsxapps/web/app/(app)/[emailAccountId]/assistant/RuleForm.tsxapps/web/app/(app)/[emailAccountId]/assistant/RulesTabNew.tsxapps/web/app/(app)/[emailAccountId]/assistant/RulesPromptNew.tsxapps/web/app/(app)/[emailAccountId]/assistant/ExamplesList.tsxapps/web/app/(app)/[emailAccountId]/assistant/Rules.tsxapps/web/app/(app)/[emailAccountId]/assistant/RulesPrompt.tsxapps/web/app/(app)/[emailAccountId]/assistant/ProcessingPromptFileDialog.tsx
**/*.{html,jsx,tsx}
📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)
**/*.{html,jsx,tsx}: Don't use or elements.
Don't use accessKey attribute on any HTML element.
Don't set aria-hidden="true" on focusable elements.
Don't add ARIA roles, states, and properties to elements that don't support them.
Only use the scope prop on elements.
Don't assign non-interactive ARIA roles to interactive HTML elements.
Make sure label elements have text content and are associated with an input.
Don't assign interactive ARIA roles to non-interactive HTML elements.
Don't assign tabIndex to non-interactive HTML elements.
Don't use positive integers for tabIndex property.
Don't include "image", "picture", or "photo" in img alt prop.
Don't use explicit role property that's the same as the implicit/default role.
Make static elements with click handlers use a valid role attribute.
Always include a title element for SVG elements.
Give all elements requiring alt text meaningful information for screen readers.
Make sure anchors have content that's accessible to screen readers.
Assign tabIndex to non-interactive HTML elements with aria-activedescendant.
Include all required ARIA attributes for elements with ARIA roles.
Make sure ARIA properties are valid for the element's supported roles.
Always include a type attribute for button elements.
Make elements with interactive roles and handlers focusable.
Give heading elements content that's accessible to screen readers (not hidden with aria-hidden).
Always include a lang attribute on the html element.
Always include a title attribute for iframe elements.
Accompany onClick with at least one of: onKeyUp, onKeyDown, or onKeyPress.
Accompany onMouseOver/onMouseOut with onFocus/onBlur.
Include caption tracks for audio and video elements.
Make sure all anchors are valid and navigable.
Ensure all ARIA properties (aria-*) are valid.
Use valid, non-abstract ARIA roles for elements with ARIA roles.
Use valid ARIA state and property values.
Use valid values for the autocomplete attribute on input eleme...Files:
apps/web/app/(app)/[emailAccountId]/assistant/FixWithChat.tsxapps/web/app/(app)/[emailAccountId]/assistant/AIChatButton.tsxapps/web/app/(app)/[emailAccountId]/assistant/AssistantOnboarding.tsxapps/web/app/(app)/[emailAccountId]/assistant/RuleForm.tsxapps/web/app/(app)/[emailAccountId]/assistant/RulesTabNew.tsxapps/web/app/(app)/[emailAccountId]/assistant/RulesPromptNew.tsxapps/web/app/(app)/[emailAccountId]/assistant/ExamplesList.tsxapps/web/app/(app)/[emailAccountId]/assistant/Rules.tsxapps/web/app/(app)/[emailAccountId]/assistant/RulesPrompt.tsxapps/web/app/(app)/[emailAccountId]/assistant/ProcessingPromptFileDialog.tsxapps/web/utils/actions/**/*.ts
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
apps/web/utils/actions/**/*.ts: Use server actions for all mutations (create/update/delete operations)
next-safe-actionprovides centralized error handling
Use Zod schemas for validation on both client and server
UserevalidatePathin server actions for cache invalidation
apps/web/utils/actions/**/*.ts: Use server actions (withnext-safe-action) for all mutations (create/update/delete operations); do NOT use POST API routes for mutations.
UserevalidatePathin server actions to invalidate cache after mutations.Files:
apps/web/utils/actions/rule.validation.tsapps/web/utils/actions/ai-rule.ts**/*.ts
📄 CodeRabbit inference engine (.cursor/rules/form-handling.mdc)
**/*.ts: The same validation should be done in the server action too
Define validation schemas using ZodFiles:
apps/web/utils/actions/rule.validation.tsapps/web/utils/ai/rule/prompt-to-rules-old.tsapps/web/__tests__/ai-prompt-to-rules.test.tsapps/web/utils/ai/rule/prompt-to-rules.tsapps/web/utils/actions/ai-rule.tsapps/web/utils/actions/*.validation.ts
📄 CodeRabbit inference engine (.cursor/rules/fullstack-workflow.mdc)
Define Zod schemas for validation in dedicated files and use them for both client and server validation.
Define input validation schemas using Zod in the corresponding
.validation.tsfile. These schemas are used bynext-safe-action(.schema()) and can also be reused on the client for form validation.Files:
apps/web/utils/actions/rule.validation.tsapps/web/utils/actions/*.ts
📄 CodeRabbit inference engine (.cursor/rules/server-actions.mdc)
apps/web/utils/actions/*.ts: Implement all server actions using thenext-safe-actionlibrary for type safety, input validation, context management, and error handling. Refer toapps/web/utils/actions/safe-action.tsfor client definitions (actionClient,actionClientUser,adminActionClient).
UseactionClientUserwhen only authenticated user context (userId) is needed.
UseactionClientwhen both authenticated user context and a specificemailAccountIdare needed. TheemailAccountIdmust be bound when calling the action from the client.
UseadminActionClientfor actions restricted to admin users.
Access necessary context (likeuserId,emailAccountId, etc.) provided by the safe action client via thectxobject in the.action()handler.
Server Actions are strictly for mutations (operations that change data, e.g., creating, updating, deleting). Do NOT use Server Actions for data fetching (GET operations). For data fetching, use dedicated GET API Routes combined with SWR Hooks.
UseSafeErrorfor expected/handled errors within actions if needed.next-safe-actionprovides centralized error handling.
Use the.metadata({ name: "actionName" })method to provide a meaningful name for monitoring. Sentry instrumentation is automatically applied viawithServerActionInstrumentationwithin the safe action clients.
If an action modifies data displayed elsewhere, userevalidatePathorrevalidateTagfromnext/cachewithin the action handler as needed.Server action files must start with
use serverFiles:
apps/web/utils/actions/rule.validation.tsapps/web/utils/actions/ai-rule.tsapps/web/utils/**
📄 CodeRabbit inference engine (.cursor/rules/project-structure.mdc)
Create utility functions in
utils/folder for reusable logicFiles:
apps/web/utils/actions/rule.validation.tsapps/web/utils/ai/rule/prompt-to-rules-old.tsapps/web/utils/ai/rule/prompt-to-rules.tsapps/web/utils/actions/ai-rule.tsapps/web/utils/**/*.ts
📄 CodeRabbit inference engine (.cursor/rules/project-structure.mdc)
apps/web/utils/**/*.ts: Use lodash utilities for common operations (arrays, objects, strings)
Import specific lodash functions to minimize bundle sizeFiles:
apps/web/utils/actions/rule.validation.tsapps/web/utils/ai/rule/prompt-to-rules-old.tsapps/web/utils/ai/rule/prompt-to-rules.tsapps/web/utils/actions/ai-rule.tsapps/web/utils/{ai,llms}/**/*.ts
📄 CodeRabbit inference engine (.cursor/rules/llm.mdc)
apps/web/utils/{ai,llms}/**/*.ts: Place LLM-related implementation code under apps/web/utils/ai or apps/web/utils/llms
Keep system and user prompts separate; system defines role/task, user contains data/context
Always validate LLM responses with a specific Zod schema
Use descriptive scoped loggers per feature and log inputs/outputs with appropriate levels and context
Implement early returns for invalid inputs and use proper error types with logging
Add fallbacks for AI failures and include retry logic for transient errors using withRetry
Format prompts with XML-like tags; remove excessive whitespace; truncate overly long inputs; keep formatting consistent
Use TypeScript types for all parameters/returns and define interfaces for complex IO structures
Keep related AI functions co-located; extract shared logic into utilities; document complex AI logic with clear comments
Call LLMs via createGenerateObject; pass system, prompt, and a Zod schema; return the validated result.object
Derive model options using getModel(...) and pass them to createGenerateObject and the generate callFiles:
apps/web/utils/ai/rule/prompt-to-rules-old.tsapps/web/utils/ai/rule/prompt-to-rules.tsapps/web/__tests__/**/*
📄 CodeRabbit inference engine (.cursor/rules/llm-test.mdc)
Place all LLM-related tests in apps/web/tests/
Files:
apps/web/__tests__/ai-prompt-to-rules.test.tsapps/web/__tests__/**/*.test.ts
📄 CodeRabbit inference engine (.cursor/rules/llm-test.mdc)
apps/web/__tests__/**/*.test.ts: Always create helper functions for common test data in LLM-related tests
Include standard test cases: happy path, error handling, edge cases (empty input, null values), different user configurations, and various input formats in LLM-related tests
Set appropriate timeouts for LLM calls in tests (e.g., 15,000ms for long-running operations)
Use descriptive console.debug statements for generated content in LLM-related tests
Do not mock the LLM call in LLM-related tests; call the actual LLM
Test both AI and non-AI paths in LLM-related testsFiles:
apps/web/__tests__/ai-prompt-to-rules.test.ts**/*.test.{ts,js}
📄 CodeRabbit inference engine (.cursor/rules/security.mdc)
Include security tests in your test suites to verify authentication, authorization, and error handling.
Files:
apps/web/__tests__/ai-prompt-to-rules.test.ts**/*.test.{ts,js,tsx,jsx}
📄 CodeRabbit inference engine (.cursor/rules/testing.mdc)
**/*.test.{ts,js,tsx,jsx}: Tests are colocated next to the tested file (e.g.,dir/format.tsanddir/format.test.ts)
Usevi.mock("server-only", () => ({}));to mock theserver-onlymodule in tests
Mock@/utils/prismain tests usingvi.mock("@/utils/prisma")and use the provided prisma mock
Mock external dependencies in tests
Clean up mocks between tests
Do not mock the LoggerFiles:
apps/web/__tests__/ai-prompt-to-rules.test.ts**/__tests__/**/*.{ts,js,tsx,jsx}
📄 CodeRabbit inference engine (.cursor/rules/testing.mdc)
AI tests are placed in the
__tests__directory and are not run by default (they use a real LLM)Files:
apps/web/__tests__/ai-prompt-to-rules.test.ts**/*.{test,spec}.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)
**/*.{test,spec}.{js,jsx,ts,tsx}: Don't use export or module.exports in test files.
Don't use focused tests.
Don't use disabled tests.
Make sure the assertion function, like expect, is placed inside an it() function call.
Don't nest describe() blocks too deeply in test files.
Don't use focused tests.
Don't use disabled tests.
Don't use export or module.exports in test files.Files:
apps/web/__tests__/ai-prompt-to-rules.test.tsapps/web/__tests__/**/*.ts
📄 CodeRabbit inference engine (.cursor/rules/llm.mdc)
Place LLM-specific tests under apps/web/tests
Files:
apps/web/__tests__/ai-prompt-to-rules.test.ts🧠 Learnings (14)
📚 Learning: 2025-07-18T15:05:16.146Z
Learnt from: CR PR: elie222/inbox-zero#0 File: .cursor/rules/fullstack-workflow.mdc:0-0 Timestamp: 2025-07-18T15:05:16.146Z Learning: Applies to apps/web/utils/actions/*.validation.ts : Define Zod schemas for validation in dedicated files and use them for both client and server validation.Applied to files:
apps/web/utils/actions/rule.validation.ts📚 Learning: 2025-07-18T17:27:58.249Z
Learnt from: CR PR: elie222/inbox-zero#0 File: .cursor/rules/server-actions.mdc:0-0 Timestamp: 2025-07-18T17:27:58.249Z Learning: Applies to apps/web/utils/actions/*.validation.ts : Define input validation schemas using Zod in the corresponding `.validation.ts` file. These schemas are used by `next-safe-action` (`.schema()`) and can also be reused on the client for form validation.Applied to files:
apps/web/utils/actions/rule.validation.ts📚 Learning: 2025-08-17T16:57:25.834Z
Learnt from: CR PR: elie222/inbox-zero#0 File: .cursor/rules/llm.mdc:0-0 Timestamp: 2025-08-17T16:57:25.834Z Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Call LLMs via createGenerateObject; pass system, prompt, and a Zod schema; return the validated result.objectApplied to files:
apps/web/utils/actions/rule.validation.tsapps/web/utils/ai/rule/prompt-to-rules-old.tsapps/web/utils/ai/rule/prompt-to-rules.ts📚 Learning: 2025-07-18T15:04:30.467Z
Learnt from: CR PR: elie222/inbox-zero#0 File: apps/web/CLAUDE.md:0-0 Timestamp: 2025-07-18T15:04:30.467Z Learning: Applies to apps/web/utils/actions/**/*.ts : Use Zod schemas for validation on both client and serverApplied to files:
apps/web/utils/actions/rule.validation.ts📚 Learning: 2025-07-20T09:00:41.968Z
Learnt from: CR PR: elie222/inbox-zero#0 File: .cursor/rules/security-audit.mdc:0-0 Timestamp: 2025-07-20T09:00:41.968Z Learning: Applies to apps/web/app/api/**/*.{ts,js} : Request bodies in API routes should use Zod schemas for validation.Applied to files:
apps/web/utils/actions/rule.validation.ts📚 Learning: 2025-07-18T15:04:57.115Z
Learnt from: CR PR: elie222/inbox-zero#0 File: .cursor/rules/form-handling.mdc:0-0 Timestamp: 2025-07-18T15:04:57.115Z Learning: Applies to **/*.ts : Define validation schemas using ZodApplied to files:
apps/web/utils/actions/rule.validation.ts📚 Learning: 2025-08-17T16:57:25.834Z
Learnt from: CR PR: elie222/inbox-zero#0 File: .cursor/rules/llm.mdc:0-0 Timestamp: 2025-08-17T16:57:25.834Z Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Keep system and user prompts separate; system defines role/task, user contains data/contextApplied to files:
apps/web/utils/ai/rule/prompt-to-rules-old.tsapps/web/utils/ai/rule/prompt-to-rules.tsapps/web/utils/actions/ai-rule.ts📚 Learning: 2025-08-17T16:57:25.834Z
Learnt from: CR PR: elie222/inbox-zero#0 File: .cursor/rules/llm.mdc:0-0 Timestamp: 2025-08-17T16:57:25.834Z Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Format prompts with XML-like tags; remove excessive whitespace; truncate overly long inputs; keep formatting consistentApplied to files:
apps/web/utils/ai/rule/prompt-to-rules-old.tsapps/web/utils/ai/rule/prompt-to-rules.tsapps/web/app/(app)/[emailAccountId]/assistant/RulesPrompt.tsx📚 Learning: 2025-08-17T16:57:25.834Z
Learnt from: CR PR: elie222/inbox-zero#0 File: .cursor/rules/llm.mdc:0-0 Timestamp: 2025-08-17T16:57:25.834Z Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Keep related AI functions co-located; extract shared logic into utilities; document complex AI logic with clear commentsApplied to files:
apps/web/utils/ai/rule/prompt-to-rules-old.tsapps/web/utils/actions/ai-rule.ts📚 Learning: 2025-07-18T15:06:10.570Z
Learnt from: CR PR: elie222/inbox-zero#0 File: .cursor/rules/llm-test.mdc:0-0 Timestamp: 2025-07-18T15:06:10.570Z Learning: Applies to apps/web/__tests__/**/*.test.ts : Test both AI and non-AI paths in LLM-related testsApplied to files:
apps/web/__tests__/ai-prompt-to-rules.test.ts📚 Learning: 2025-06-23T12:27:30.570Z
Learnt from: CR PR: elie222/inbox-zero#0 File: .cursor/rules/testing.mdc:0-0 Timestamp: 2025-06-23T12:27:30.570Z Learning: When mocking Prisma in Vitest, import the Prisma mock from '@/utils/__mocks__/prisma', mock '@/utils/prisma', and clear all mocks in a beforeEach hook to ensure test isolation.Applied to files:
apps/web/__tests__/ai-prompt-to-rules.test.ts📚 Learning: 2025-06-23T12:26:53.882Z
Learnt from: CR PR: elie222/inbox-zero#0 File: .cursor/rules/prisma.mdc:0-0 Timestamp: 2025-06-23T12:26:53.882Z Learning: In this project, Prisma should be imported using 'import prisma from "@/utils/prisma";' in TypeScript files.Applied to files:
apps/web/__tests__/ai-prompt-to-rules.test.ts📚 Learning: 2025-07-20T09:03:06.318Z
Learnt from: CR PR: elie222/inbox-zero#0 File: .cursor/rules/ultracite.mdc:0-0 Timestamp: 2025-07-20T09:03:06.318Z Learning: Applies to **/*.{test,spec}.{js,jsx,ts,tsx} : Make sure the assertion function, like expect, is placed inside an it() function call.Applied to files:
apps/web/__tests__/ai-prompt-to-rules.test.ts📚 Learning: 2025-08-17T16:57:25.834Z
Learnt from: CR PR: elie222/inbox-zero#0 File: .cursor/rules/llm.mdc:0-0 Timestamp: 2025-08-17T16:57:25.834Z Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Derive model options using getModel(...) and pass them to createGenerateObject and the generate callApplied to files:
apps/web/utils/ai/rule/prompt-to-rules.ts🧬 Code graph analysis (11)
apps/web/app/(app)/[emailAccountId]/assistant/RuleForm.tsx (1)
apps/web/components/Button.tsx (1)
Button(60-87)apps/web/app/(app)/[emailAccountId]/assistant/RulesTabNew.tsx (3)
apps/web/app/(app)/[emailAccountId]/assistant/RulesPromptNew.tsx (1)
RulesPrompt(31-63)apps/web/app/(app)/[emailAccountId]/assistant/Rules.tsx (1)
Rules(66-493)apps/web/app/(app)/[emailAccountId]/assistant/RulesTab.tsx (1)
RulesTab(5-24)apps/web/app/(app)/[emailAccountId]/assistant/RulesPromptNew.tsx (13)
apps/web/providers/EmailAccountProvider.tsx (1)
useAccount(72-82)apps/web/hooks/useModal.tsx (1)
useModal(3-9)apps/web/app/(app)/[emailAccountId]/assistant/examples.ts (1)
getPersonas(85-348)apps/web/app/(app)/[emailAccountId]/assistant/PersonaDialog.tsx (1)
PersonaDialog(7-40)apps/web/app/(app)/[emailAccountId]/assistant/AssistantOnboarding.tsx (1)
AssistantOnboarding(17-77)apps/web/hooks/useRules.tsx (1)
useRules(4-8)apps/web/hooks/useLabels.ts (1)
useLabels(62-88)apps/web/hooks/useDialogState.ts (1)
useDialogState(8-32)apps/web/components/editor/SimpleRichTextEditor.tsx (2)
SimpleRichTextEditorRef(22-25)SimpleRichTextEditor(27-153)apps/web/utils/actions/ai-rule.ts (1)
createRulesAction(515-578)apps/web/utils/path.ts (1)
prefixPath(1-4)apps/web/app/(app)/[emailAccountId]/assistant/ProcessingPromptFileDialog.tsx (1)
ProcessingPromptFileDialog(35-83)apps/web/app/(app)/[emailAccountId]/assistant/RuleDialog.tsx (1)
RuleDialog(23-79)apps/web/app/(app)/[emailAccountId]/assistant/ExamplesList.tsx (1)
apps/web/app/(app)/[emailAccountId]/assistant/examples.ts (1)
getExamplePrompts(70-72)apps/web/utils/ai/rule/prompt-to-rules-old.ts (6)
apps/web/utils/logger.ts (1)
createScopedLogger(17-65)apps/web/utils/ai/rule/create-rule-schema.ts (3)
createRuleSchema(100-107)CreateOrUpdateRuleSchemaWithCategories(147-150)getCreateRuleSchemaWithCategories(109-136)apps/web/utils/llms/types.ts (1)
EmailAccountWithAI(10-29)apps/web/utils/mention.ts (1)
convertMentionsToLabels(6-8)apps/web/utils/llms/model.ts (1)
getModel(27-41)apps/web/utils/llms/index.ts (1)
createGenerateObject(118-166)apps/web/__tests__/ai-prompt-to-rules.test.ts (1)
apps/web/utils/ai/rule/create-rule-schema.ts (1)
createRuleSchema(100-107)apps/web/app/(app)/[emailAccountId]/assistant/Rules.tsx (3)
apps/web/hooks/useRules.tsx (1)
useRules(4-8)apps/web/components/ui/sidebar.tsx (1)
useSidebar(821-821)apps/web/providers/ChatProvider.tsx (1)
useChat(116-122)apps/web/utils/ai/rule/prompt-to-rules.ts (1)
apps/web/utils/ai/rule/create-rule-schema.ts (3)
CreateRuleSchemaWithCategories(139-146)getCreateRuleSchemaWithCategories(109-136)createRuleSchema(100-107)apps/web/utils/actions/ai-rule.ts (6)
apps/web/utils/ai/rule/prompt-to-rules-old.ts (1)
aiPromptToRulesOld(20-86)apps/web/utils/actions/safe-action.ts (1)
actionClient(46-86)apps/web/utils/actions/rule.validation.ts (1)
createRulesBody(176-176)apps/web/utils/error.ts (1)
SafeError(86-96)apps/web/utils/ai/rule/prompt-to-rules.ts (1)
aiPromptToRules(15-70)apps/web/utils/rule/rule.ts (1)
safeCreateRule(34-100)apps/web/app/(app)/[emailAccountId]/assistant/RulesPrompt.tsx (1)
apps/web/app/(app)/[emailAccountId]/assistant/ExamplesList.tsx (1)
Examples(53-53)apps/web/app/(app)/[emailAccountId]/assistant/ProcessingPromptFileDialog.tsx (1)
apps/web/utils/string.ts (1)
pluralize(40-46)🔇 Additional comments (22)
apps/web/app/(app)/[emailAccountId]/assistant/RuleForm.tsx (1)
954-961: LGTM: remove redundant disabled prop (Button already disables when loading).Consistent with Button component behavior (disabled={loading || props.disabled}). Keeps UX identical while simplifying props.
apps/web/app/(app)/[emailAccountId]/assistant/AssistantOnboarding.tsx (1)
20-20:onCompleteprop can safely remain optional
A search for any<AssistantOnboardingusages across apps/web returned no call sites supplying—or depending on—a non-optional onComplete callback. Since no consumers expect a mandatory callback, making it optional is safe and ergonomic.apps/web/app/(app)/[emailAccountId]/assistant/ExamplesList.tsx (2)
18-19: LGTM: derive prompts internally based on provider.Localizing data derivation reduces prop surface and keeps Examples aligned with provider terminology.
24-24: LGTM: expose scroll container size via className.Good flexibility while preserving previous defaults.
apps/web/app/(app)/[emailAccountId]/assistant/ProcessingPromptFileDialog.tsx (4)
64-64: DialogContent width tweak looks good.Using
max-w-xlimproves readability on small/medium screens without affecting larger viewports.
101-106: Header/layout adjustments improve readability.
mx-autowith left-aligned description is a sensible refinement for content-heavy steps.
241-253: Nice defensive guards for optional counts.The
editedRules/removedRuleschecks prevent undefined from leaking into the UI.
271-275: Copy change LGTM.“Try it out!” is concise and consistent with the rest of the flow.
apps/web/app/(app)/[emailAccountId]/assistant/RulesTabNew.tsx (1)
4-13: LGTM — clear composition of prompt + rules list, hides Add button in this context.This keeps the “create via prompt” flow prominent while still surfacing the rules table. No client hooks/onClick here, so leaving it as a Server Component is fine.
apps/web/app/(app)/[emailAccountId]/assistant/Rules.tsx (3)
61-63: Imports for chat/sidebar look correct and localized to this component.
66-72: Public API extension (showAddRuleButton) is clean and backward compatible.Default remains true; callers can opt-out where needed.
230-235: Conditional rendering of Add Rule button is straightforward and readable.apps/web/__tests__/ai-prompt-to-rules.test.ts (1)
103-106: Provider-aware validation is a solid improvement.Parsing with
createRuleSchema(emailAccount.account.provider)ensures action validation respects provider capabilities.apps/web/utils/actions/ai-rule.ts (3)
19-22: Good: centralizing schemas under rule.validationSwitching to import createRulesBody/saveRulesPromptBody from rule.validation aligns server actions with a single validation source. Keeps client/server schemas consistent.
426-435: Edit-flow conversion uses the legacy path correctlyPassing isEditing: true guarantees the presence of optional ruleId for targeted updates. Good call on threading availableCategories to improve structured outputs.
482-493: Concise creation loggingLogging ruleName only reduces noise while keeping traceability. Looks good.
apps/web/app/(app)/[emailAccountId]/assistant/RulesPrompt.tsx (3)
69-69: Passing provider down is the right movePropagating provider into RulesPromptForm enables provider-aware examples and personas.
97-107: Prop type addition looks goodAdding provider: string to RulesPromptForm props keeps types explicit and readable.
346-347: Examples now provider-driven — goodSwitching Examples to resolve its own prompts via provider removes coupling to RulesPrompt and keeps that logic localized.
apps/web/app/(app)/[emailAccountId]/assistant/RulesPromptNew.tsx (2)
146-155: Processing dialog result shape — verify compatibilityProcessingPromptFileDialog previously displayed created/edited/removed counts. Here you pass only { createdRules }. Ensure the dialog’s ResultProps accepts this shape (or handle missing edited/removed gracefully). If not, update the dialog’s types to a union.
263-271: Examples panel integration looks goodProvider-based Examples with a bounded height keeps the layout neat. Toggling via isExamplesOpen avoids unnecessary editor re-renders.
apps/web/utils/ai/rule/prompt-to-rules.ts (1)
23-33: Schema selection logic is clean and provider-awareGood use of getCreateRuleSchemaWithCategories when categories are available; otherwise falling back to createRuleSchema(provider).
| export const createRulesAction = actionClient | ||
| .metadata({ name: "createRules" }) | ||
| .schema(createRulesBody) | ||
| .action(async ({ ctx: { emailAccountId }, parsedInput: { prompt } }) => { | ||
| const emailAccount = await prisma.emailAccount.findUnique({ | ||
| where: { id: emailAccountId }, | ||
| select: { | ||
| id: true, | ||
| email: true, | ||
| userId: true, | ||
| about: true, | ||
| rulesPrompt: true, | ||
| categories: { select: { id: true, name: true } }, | ||
| user: { | ||
| select: { | ||
| aiProvider: true, | ||
| aiModel: true, | ||
| aiApiKey: true, | ||
| }, | ||
| }, | ||
| account: { | ||
| select: { | ||
| provider: true, | ||
| }, | ||
| }, | ||
| }, | ||
| }); | ||
|
|
||
| if (!emailAccount) { | ||
| logger.error("Email account not found"); | ||
| throw new SafeError("Email account not found"); | ||
| } | ||
|
|
||
| const addedRules = await aiPromptToRules({ | ||
| emailAccount, | ||
| promptFile: prompt, | ||
| availableCategories: emailAccount.categories.map((c) => c.name), | ||
| }); | ||
|
|
||
| logger.info("Rules to be added", { | ||
| emailAccountId, | ||
| count: addedRules?.length || 0, | ||
| }); | ||
|
|
||
| // add new rules | ||
| for (const rule of addedRules || []) { | ||
| logger.info("Creating rule", { emailAccountId, ruleName: rule.name }); | ||
|
|
||
| await safeCreateRule({ | ||
| result: rule, | ||
| emailAccountId, | ||
| categoryNames: rule.condition.categories?.categoryFilters || [], | ||
| shouldCreateIfDuplicate: false, | ||
| provider: emailAccount.account.provider, | ||
| }); | ||
| } | ||
|
|
||
| logger.info("Completed", { | ||
| emailAccountId, | ||
| createdRules: addedRules?.length || 0, | ||
| }); | ||
|
|
||
| return { createdRules: addedRules?.length || 0 }; | ||
| }); | ||
|
|
There was a problem hiding this comment.
💡 Verification agent
🧩 Analysis chain
Harden createRulesAction: validate input, handle AI failures, and revalidate cache
The action works, but a few upgrades will improve resilience and UX:
- Validate non-empty prompt on the server (z.string().min(1)) and/or guard in the action.
- Wrap the AI call in try/catch and surface SafeError for expected failures.
- Revalidate any paths/tags that list rules to keep SSR/ISR views in sync.
Proposed patch:
export const createRulesAction = actionClient
.metadata({ name: "createRules" })
.schema(createRulesBody)
.action(async ({ ctx: { emailAccountId }, parsedInput: { prompt } }) => {
+ if (!prompt?.trim()) {
+ // Defensive guard until schema is tightened to .min(1)
+ throw new SafeError("Prompt is empty");
+ }
const emailAccount = await prisma.emailAccount.findUnique({
@@
- const addedRules = await aiPromptToRules({
- emailAccount,
- promptFile: prompt,
- availableCategories: emailAccount.categories.map((c) => c.name),
- });
+ let addedRules;
+ try {
+ addedRules = await aiPromptToRules({
+ emailAccount,
+ promptFile: prompt,
+ availableCategories: emailAccount.categories.map((c) => c.name),
+ });
+ } catch (err) {
+ logger.error("AI error converting prompt to rules", {
+ emailAccountId,
+ error: err instanceof Error ? { message: err.message, name: err.name } : err,
+ });
+ throw new SafeError("Could not convert prompt to rules. Please try again.");
+ }
@@
- logger.info("Completed", {
+ logger.info("Completed", {
emailAccountId,
createdRules: addedRules?.length || 0,
});
- return { createdRules: addedRules?.length || 0 };
+ // Optional: keep pages/lists fresh if you rely on ISR/SSR caches.
+ // revalidatePath(`/[emailAccountId]/automation?tab=rules`);
+ return { createdRules: addedRules?.length || 0 };
});If you are using route tags for rules listing, prefer revalidateTag("rules:") over revalidatePath.
Would you like me to also tighten createRulesBody to z.string().trim().min(1, "Prompt is empty") in rule.validation.ts?
🏁 Script executed:
#!/bin/bash
set -e
# Locate createRulesBody definition
rg -n "createRulesBody" -C3
# Locate aiPromptToRules definition
rg -n "aiPromptToRules" -C3Length of output: 10669
🏁 Script executed:
#!/bin/bash
set -e
# Search for any existing revalidation calls
rg -n "revalidatePath" -C3
rg -n "revalidateTag" -C3Length of output: 10969
Harden createRulesAction: validate prompt, handle AI errors, and revalidate cache
Please address the following mandatory refactors to improve resilience, UX, and cache consistency:
-
Enforce non-empty prompts both at the schema level and in-action
• Inapps/web/utils/actions/rule.validation.ts, changeexport const createRulesBody = z.object({ prompt: z.string().trim() });
to
export const createRulesBody = z.object({ prompt: z.string().trim().min(1, "Prompt is empty"), });
• In
createRulesAction(apps/web/utils/actions/ai-rule.ts), add at the top of the action:if (!prompt?.trim()) { throw new SafeError("Prompt is empty"); }
-
Wrap the AI invocation in try/catch and convert failures into user-friendly SafeErrors
let addedRules; try { addedRules = await aiPromptToRules({ emailAccount, promptFile: prompt, availableCategories }); } catch (err) { logger.error("AI error converting prompt to rules", { emailAccountId, error: err instanceof Error ? { name: err.name, message: err.message } : err, }); throw new SafeError("Could not convert prompt to rules. Please try again."); } -
Invalidate your ISR/SSR cache so new rules appear immediately
• Import at the top ofai-rule.ts:import { revalidatePath } from "next/cache"; import { prefixPath } from "@/utils/actions/helpers"; // or wherever this helper lives
• After creating rules, call:
revalidatePath(prefixPath(emailAccountId, "/automation?tab=rules"));
These changes align with existing patterns for server-action validation, error handling, and cache invalidation across the codebase.
🤖 Prompt for AI Agents
In apps/web/utils/actions/ai-rule.ts around lines 515 to 579, the action does
not validate empty prompts, does not handle AI invocation errors, and does not
revalidate the ISR/SSR cache after creating rules; update rule.validation.ts to
make prompt required (trim().min(1,"Prompt is empty")), add a top-of-action
guard that throws SafeError when prompt is empty/blank, wrap the
aiPromptToRules(...) call in try/catch and convert any thrown error into a
user-friendly SafeError, import revalidatePath and prefixPath at the top of
ai-rule.ts, and after the loop that creates rules call
revalidatePath(prefixPath(emailAccountId, "/automation?tab=rules")) so new rules
are visible immediately.
There was a problem hiding this comment.
Actionable comments posted: 3
♻️ Duplicate comments (1)
apps/web/utils/actions/ai-rule.ts (1)
516-586: Harden createRulesAction: validate prompt, handle AI errors, and revalidate cacheThese were previously requested and still apply. Please:
- Enforce non-empty prompt.
- Wrap
aiPromptToRulesin try/catch and convert failures into a user-facingSafeError.- Revalidate relevant paths/tags so UI reflects new rules immediately.
export const createRulesAction = actionClient .metadata({ name: "createRules" }) .schema(createRulesBody) .action(async ({ ctx: { emailAccountId }, parsedInput: { prompt } }) => { + if (!prompt?.trim()) { + throw new SafeError("Prompt is empty"); + } const emailAccount = await prisma.emailAccount.findUnique({ @@ - const addedRules = await aiPromptToRules({ - emailAccount, - promptFile: prompt, - availableCategories: emailAccount.categories.map((c) => c.name), - }); + let addedRules; + try { + addedRules = await aiPromptToRules({ + emailAccount, + promptFile: prompt, + availableCategories: emailAccount.categories.map((c) => c.name), + }); + } catch (err) { + logger.error("AI error converting prompt to rules", { + emailAccountId, + error: + err instanceof Error + ? { name: err.name, message: err.message } + : err, + }); + throw new SafeError("Could not convert prompt to rules. Please try again."); + } @@ - return { - rules: createdRules, - }; + // Optional: keep any ISR/SSR views in sync if applicable + // import { revalidatePath } from "next/cache"; + // import { prefixPath } from "@/utils/path"; + // revalidatePath(prefixPath(emailAccountId, "/automation")); + return { rules: createdRules }; });Also tighten the schema in
apps/web/utils/actions/rule.validation.ts:-export const createRulesBody = z.object({ prompt: z.string().trim() }); +export const createRulesBody = z.object({ + prompt: z.string().trim().min(1, "Prompt is empty"), +});
🧹 Nitpick comments (5)
apps/web/app/(app)/[emailAccountId]/assistant/RulesPromptNew.tsx (3)
94-101: Guard against empty prompts on the clientEarly-return if the editor contains only whitespace. This avoids a server round-trip and aligns with server-side validation you’ll add.
const onSubmit = useCallback(async () => { - const markdown = editorRef.current?.getMarkdown(); - if (typeof markdown !== "string") return; + const markdown = editorRef.current?.getMarkdown(); + if (typeof markdown !== "string") return; + if (!markdown.trim()) { + toast.error("Please write a prompt before creating rules."); + return; + }
59-61: Restore onboarding → persona handoffIn the old flow, onboarding completed by auto-opening the persona picker when no prompt existed. Consider parity here for a smoother first-run:
- <AssistantOnboarding /> + <AssistantOnboarding + onComplete={() => { + onOpenPersonaDialog(); + }} + />This keeps the progressive guidance intact.
Also applies to: 33-41
179-185: Nit: avoid passingundefinedasdefaultValueThe editor treats
defaultValueas optional. Passingundefinedis redundant.- <SimpleRichTextEditor - ref={editorRef} - defaultValue={undefined} + <SimpleRichTextEditor + ref={editorRef}apps/web/app/(app)/[emailAccountId]/assistant/CreatedRulesModal.tsx (1)
47-50: Decorative SVG should be hidden from screen readersAdd
aria-hiddenfor decorative icons or provide an accessible title. Since the heading already conveys success, hide the icon:- <CheckCircle2 className="size-5 text-green-600" /> + <CheckCircle2 aria-hidden className="size-5 text-green-600" />apps/web/utils/actions/ai-rule.ts (1)
520-542: Avoid duplicating selection logic: reuse getEmailAccountWithAiYou already have
getEmailAccountWithAi({ emailAccountId })above. Reuse it for consistency and to centralize selection changes.- const emailAccount = await prisma.emailAccount.findUnique({ - where: { id: emailAccountId }, - select: { - id: true, - email: true, - userId: true, - about: true, - rulesPrompt: true, - categories: { select: { id: true, name: true } }, - user: { - select: { - aiProvider: true, - aiModel: true, - aiApiKey: true, - }, - }, - account: { - select: { - provider: true, - }, - }, - }, - }); + const emailAccount = await getEmailAccountWithAi({ emailAccountId });
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (4)
apps/web/app/(app)/[emailAccountId]/assistant/CreatedRulesModal.tsx(1 hunks)apps/web/app/(app)/[emailAccountId]/assistant/RulesPromptNew.tsx(1 hunks)apps/web/utils/actions/ai-rule.ts(7 hunks)apps/web/utils/rule/types.ts(1 hunks)
🧰 Additional context used
📓 Path-based instructions (19)
apps/web/**/*.{ts,tsx}
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
apps/web/**/*.{ts,tsx}: Use TypeScript with strict null checks
Path aliases: Use@/for imports from project root
Use proper error handling with try/catch blocks
Format code with Prettier
Leverage TypeScript inference for better DX
Files:
apps/web/app/(app)/[emailAccountId]/assistant/CreatedRulesModal.tsxapps/web/utils/rule/types.tsapps/web/app/(app)/[emailAccountId]/assistant/RulesPromptNew.tsxapps/web/utils/actions/ai-rule.ts
apps/web/app/**
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
NextJS app router structure with (app) directory
Files:
apps/web/app/(app)/[emailAccountId]/assistant/CreatedRulesModal.tsxapps/web/app/(app)/[emailAccountId]/assistant/RulesPromptNew.tsx
apps/web/**/*.tsx
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
apps/web/**/*.tsx: Follow tailwindcss patterns with prettier-plugin-tailwindcss
Prefer functional components with hooks
Use shadcn/ui components when available
Ensure responsive design with mobile-first approach
Follow consistent naming conventions (PascalCase for components)
Use LoadingContent component for async data
Useresult?.serverErrorwithtoastErrorandtoastSuccess
UseLoadingContentcomponent to handle loading and error states consistently
Passloading,error, and children props toLoadingContent
Files:
apps/web/app/(app)/[emailAccountId]/assistant/CreatedRulesModal.tsxapps/web/app/(app)/[emailAccountId]/assistant/RulesPromptNew.tsx
!{.cursor/rules/*.mdc}
📄 CodeRabbit inference engine (.cursor/rules/cursor-rules.mdc)
Never place rule files in the project root, in subdirectories outside .cursor/rules, or in any other location
Files:
apps/web/app/(app)/[emailAccountId]/assistant/CreatedRulesModal.tsxapps/web/utils/rule/types.tsapps/web/app/(app)/[emailAccountId]/assistant/RulesPromptNew.tsxapps/web/utils/actions/ai-rule.ts
**/*.tsx
📄 CodeRabbit inference engine (.cursor/rules/form-handling.mdc)
**/*.tsx: Use React Hook Form with Zod for validation
Validate form inputs before submission
Show validation errors inline next to form fields
Files:
apps/web/app/(app)/[emailAccountId]/assistant/CreatedRulesModal.tsxapps/web/app/(app)/[emailAccountId]/assistant/RulesPromptNew.tsx
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/logging.mdc)
**/*.{ts,tsx}: UsecreateScopedLoggerfor logging in backend TypeScript files
Typically add the logger initialization at the top of the file when usingcreateScopedLogger
Only use.with()on a logger instance within a specific function, not for a global loggerImport Prisma in the project using
import prisma from "@/utils/prisma";
**/*.{ts,tsx}: Don't use TypeScript enums.
Don't use TypeScript const enum.
Don't use the TypeScript directive @ts-ignore.
Don't use primitive type aliases or misleading types.
Don't use empty type parameters in type aliases and interfaces.
Don't use any or unknown as type constraints.
Don't use implicit any type on variable declarations.
Don't let variables evolve into any type through reassignments.
Don't use non-null assertions with the ! postfix operator.
Don't misuse the non-null assertion operator (!) in TypeScript files.
Don't use user-defined types.
Use as const instead of literal types and type annotations.
Use export type for types.
Use import type for types.
Don't declare empty interfaces.
Don't merge interfaces and classes unsafely.
Don't use overload signatures that aren't next to each other.
Use the namespace keyword instead of the module keyword to declare TypeScript namespaces.
Don't use TypeScript namespaces.
Don't export imported variables.
Don't add type annotations to variables, parameters, and class properties that are initialized with literal expressions.
Don't use parameter properties in class constructors.
Use either T[] or Array consistently.
Initialize each enum member value explicitly.
Make sure all enum members are literal values.
Files:
apps/web/app/(app)/[emailAccountId]/assistant/CreatedRulesModal.tsxapps/web/utils/rule/types.tsapps/web/app/(app)/[emailAccountId]/assistant/RulesPromptNew.tsxapps/web/utils/actions/ai-rule.ts
apps/web/app/(app)/*/**
📄 CodeRabbit inference engine (.cursor/rules/page-structure.mdc)
Components for the page are either put in page.tsx, or in the apps/web/app/(app)/PAGE_NAME folder
Files:
apps/web/app/(app)/[emailAccountId]/assistant/CreatedRulesModal.tsxapps/web/app/(app)/[emailAccountId]/assistant/RulesPromptNew.tsx
apps/web/app/(app)/*/**/*.tsx
📄 CodeRabbit inference engine (.cursor/rules/page-structure.mdc)
If you need to use onClick in a component, that component is a client component and file must start with 'use client'
Files:
apps/web/app/(app)/[emailAccountId]/assistant/CreatedRulesModal.tsxapps/web/app/(app)/[emailAccountId]/assistant/RulesPromptNew.tsx
apps/web/app/(app)/*/**/**/*.tsx
📄 CodeRabbit inference engine (.cursor/rules/page-structure.mdc)
If we're in a deeply nested component we will use swr to fetch via API
Files:
apps/web/app/(app)/[emailAccountId]/assistant/CreatedRulesModal.tsxapps/web/app/(app)/[emailAccountId]/assistant/RulesPromptNew.tsx
apps/web/app/**/*.tsx
📄 CodeRabbit inference engine (.cursor/rules/project-structure.mdc)
Components with
onClickmust be client components withuse clientdirective
Files:
apps/web/app/(app)/[emailAccountId]/assistant/CreatedRulesModal.tsxapps/web/app/(app)/[emailAccountId]/assistant/RulesPromptNew.tsx
**/*.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)
**/*.{js,jsx,ts,tsx}: Don't useelements in Next.js projects.
Don't use elements in Next.js projects.
Don't use namespace imports.
Don't access namespace imports dynamically.
Don't use global eval().
Don't use console.
Don't use debugger.
Don't use var.
Don't use with statements in non-strict contexts.
Don't use the arguments object.
Don't use consecutive spaces in regular expression literals.
Don't use the comma operator.
Don't use unnecessary boolean casts.
Don't use unnecessary callbacks with flatMap.
Use for...of statements instead of Array.forEach.
Don't create classes that only have static members (like a static namespace).
Don't use this and super in static contexts.
Don't use unnecessary catch clauses.
Don't use unnecessary constructors.
Don't use unnecessary continue statements.
Don't export empty modules that don't change anything.
Don't use unnecessary escape sequences in regular expression literals.
Don't use unnecessary labels.
Don't use unnecessary nested block statements.
Don't rename imports, exports, and destructured assignments to the same name.
Don't use unnecessary string or template literal concatenation.
Don't use String.raw in template literals when there are no escape sequences.
Don't use useless case statements in switch statements.
Don't use ternary operators when simpler alternatives exist.
Don't use useless this aliasing.
Don't initialize variables to undefined.
Don't use the void operators (they're not familiar).
Use arrow functions instead of function expressions.
Use Date.now() to get milliseconds since the Unix Epoch.
Use .flatMap() instead of map().flat() when possible.
Use literal property access instead of computed property access.
Don't use parseInt() or Number.parseInt() when binary, octal, or hexadecimal literals work.
Use concise optional chaining instead of chained logical expressions.
Use regular expression literals instead of the RegExp constructor when possible.
Don't use number literal object member names th...
Files:
apps/web/app/(app)/[emailAccountId]/assistant/CreatedRulesModal.tsxapps/web/utils/rule/types.tsapps/web/app/(app)/[emailAccountId]/assistant/RulesPromptNew.tsxapps/web/utils/actions/ai-rule.ts
!pages/_document.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)
!pages/_document.{js,jsx,ts,tsx}: Don't import next/document outside of pages/_document.jsx in Next.js projects.
Don't import next/document outside of pages/_document.jsx in Next.js projects.
Files:
apps/web/app/(app)/[emailAccountId]/assistant/CreatedRulesModal.tsxapps/web/utils/rule/types.tsapps/web/app/(app)/[emailAccountId]/assistant/RulesPromptNew.tsxapps/web/utils/actions/ai-rule.ts
**/*.{jsx,tsx}
📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)
**/*.{jsx,tsx}: Don't destructure props inside JSX components in Solid projects.
Don't use both children and dangerouslySetInnerHTML props on the same element.
Don't use Array index in keys.
Don't assign to React component props.
Don't define React components inside other components.
Don't use event handlers on non-interactive elements.
Don't assign JSX properties multiple times.
Don't add extra closing tags for components without children.
Use <>...</> instead of ....
Don't insert comments as text nodes.
Don't use the return value of React.render.
Make sure all dependencies are correctly specified in React hooks.
Make sure all React hooks are called from the top level of component functions.
Don't use unnecessary fragments.
Don't pass children as props.
Use semantic elements instead of role attributes in JSX.
Files:
apps/web/app/(app)/[emailAccountId]/assistant/CreatedRulesModal.tsxapps/web/app/(app)/[emailAccountId]/assistant/RulesPromptNew.tsx
**/*.{html,jsx,tsx}
📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)
**/*.{html,jsx,tsx}: Don't use or elements.
Don't use accessKey attribute on any HTML element.
Don't set aria-hidden="true" on focusable elements.
Don't add ARIA roles, states, and properties to elements that don't support them.
Only use the scope prop on elements.
Don't assign non-interactive ARIA roles to interactive HTML elements.
Make sure label elements have text content and are associated with an input.
Don't assign interactive ARIA roles to non-interactive HTML elements.
Don't assign tabIndex to non-interactive HTML elements.
Don't use positive integers for tabIndex property.
Don't include "image", "picture", or "photo" in img alt prop.
Don't use explicit role property that's the same as the implicit/default role.
Make static elements with click handlers use a valid role attribute.
Always include a title element for SVG elements.
Give all elements requiring alt text meaningful information for screen readers.
Make sure anchors have content that's accessible to screen readers.
Assign tabIndex to non-interactive HTML elements with aria-activedescendant.
Include all required ARIA attributes for elements with ARIA roles.
Make sure ARIA properties are valid for the element's supported roles.
Always include a type attribute for button elements.
Make elements with interactive roles and handlers focusable.
Give heading elements content that's accessible to screen readers (not hidden with aria-hidden).
Always include a lang attribute on the html element.
Always include a title attribute for iframe elements.
Accompany onClick with at least one of: onKeyUp, onKeyDown, or onKeyPress.
Accompany onMouseOver/onMouseOut with onFocus/onBlur.
Include caption tracks for audio and video elements.
Make sure all anchors are valid and navigable.
Ensure all ARIA properties (aria-*) are valid.
Use valid, non-abstract ARIA roles for elements with ARIA roles.
Use valid ARIA state and property values.
Use valid values for the autocomplete attribute on input eleme...Files:
apps/web/app/(app)/[emailAccountId]/assistant/CreatedRulesModal.tsxapps/web/app/(app)/[emailAccountId]/assistant/RulesPromptNew.tsx**/*.ts
📄 CodeRabbit inference engine (.cursor/rules/form-handling.mdc)
**/*.ts: The same validation should be done in the server action too
Define validation schemas using ZodFiles:
apps/web/utils/rule/types.tsapps/web/utils/actions/ai-rule.tsapps/web/utils/**
📄 CodeRabbit inference engine (.cursor/rules/project-structure.mdc)
Create utility functions in
utils/folder for reusable logicFiles:
apps/web/utils/rule/types.tsapps/web/utils/actions/ai-rule.tsapps/web/utils/**/*.ts
📄 CodeRabbit inference engine (.cursor/rules/project-structure.mdc)
apps/web/utils/**/*.ts: Use lodash utilities for common operations (arrays, objects, strings)
Import specific lodash functions to minimize bundle sizeFiles:
apps/web/utils/rule/types.tsapps/web/utils/actions/ai-rule.tsapps/web/utils/actions/**/*.ts
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
apps/web/utils/actions/**/*.ts: Use server actions for all mutations (create/update/delete operations)
next-safe-actionprovides centralized error handling
Use Zod schemas for validation on both client and server
UserevalidatePathin server actions for cache invalidation
apps/web/utils/actions/**/*.ts: Use server actions (withnext-safe-action) for all mutations (create/update/delete operations); do NOT use POST API routes for mutations.
UserevalidatePathin server actions to invalidate cache after mutations.Files:
apps/web/utils/actions/ai-rule.tsapps/web/utils/actions/*.ts
📄 CodeRabbit inference engine (.cursor/rules/server-actions.mdc)
apps/web/utils/actions/*.ts: Implement all server actions using thenext-safe-actionlibrary for type safety, input validation, context management, and error handling. Refer toapps/web/utils/actions/safe-action.tsfor client definitions (actionClient,actionClientUser,adminActionClient).
UseactionClientUserwhen only authenticated user context (userId) is needed.
UseactionClientwhen both authenticated user context and a specificemailAccountIdare needed. TheemailAccountIdmust be bound when calling the action from the client.
UseadminActionClientfor actions restricted to admin users.
Access necessary context (likeuserId,emailAccountId, etc.) provided by the safe action client via thectxobject in the.action()handler.
Server Actions are strictly for mutations (operations that change data, e.g., creating, updating, deleting). Do NOT use Server Actions for data fetching (GET operations). For data fetching, use dedicated GET API Routes combined with SWR Hooks.
UseSafeErrorfor expected/handled errors within actions if needed.next-safe-actionprovides centralized error handling.
Use the.metadata({ name: "actionName" })method to provide a meaningful name for monitoring. Sentry instrumentation is automatically applied viawithServerActionInstrumentationwithin the safe action clients.
If an action modifies data displayed elsewhere, userevalidatePathorrevalidateTagfromnext/cachewithin the action handler as needed.Server action files must start with
use serverFiles:
apps/web/utils/actions/ai-rule.ts🧠 Learnings (3)
📚 Learning: 2025-08-17T16:57:25.834Z
Learnt from: CR PR: elie222/inbox-zero#0 File: .cursor/rules/llm.mdc:0-0 Timestamp: 2025-08-17T16:57:25.834Z Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Keep related AI functions co-located; extract shared logic into utilities; document complex AI logic with clear commentsApplied to files:
apps/web/utils/actions/ai-rule.ts📚 Learning: 2025-07-18T17:27:58.249Z
Learnt from: CR PR: elie222/inbox-zero#0 File: .cursor/rules/server-actions.mdc:0-0 Timestamp: 2025-07-18T17:27:58.249Z Learning: Applies to apps/web/utils/actions/*.ts : Implement all server actions using the `next-safe-action` library for type safety, input validation, context management, and error handling. Refer to `apps/web/utils/actions/safe-action.ts` for client definitions (`actionClient`, `actionClientUser`, `adminActionClient`).Applied to files:
apps/web/utils/actions/ai-rule.ts📚 Learning: 2025-07-18T17:27:58.249Z
Learnt from: CR PR: elie222/inbox-zero#0 File: .cursor/rules/server-actions.mdc:0-0 Timestamp: 2025-07-18T17:27:58.249Z Learning: Applies to apps/web/utils/actions/*.ts : If an action modifies data displayed elsewhere, use `revalidatePath` or `revalidateTag` from `next/cache` within the action handler as needed.Applied to files:
apps/web/utils/actions/ai-rule.ts🧬 Code graph analysis (4)
apps/web/app/(app)/[emailAccountId]/assistant/CreatedRulesModal.tsx (7)
apps/web/utils/rule/types.ts (1)
CreateRuleResult(3-5)apps/web/providers/EmailAccountProvider.tsx (1)
useAccount(72-82)apps/web/hooks/useDialogState.ts (1)
useDialogState(8-32)apps/web/utils/path.ts (1)
prefixPath(1-4)apps/web/utils/condition.ts (1)
conditionsToString(194-227)apps/web/app/(app)/[emailAccountId]/assistant/Rules.tsx (1)
ActionBadges(495-525)apps/web/app/(app)/[emailAccountId]/assistant/RuleDialog.tsx (1)
RuleDialog(23-79)apps/web/utils/rule/types.ts (1)
apps/web/utils/rule/rule.ts (1)
safeCreateRule(34-100)apps/web/app/(app)/[emailAccountId]/assistant/RulesPromptNew.tsx (13)
apps/web/app/(app)/[emailAccountId]/assistant/RulesPrompt.tsx (2)
RulesPrompt(39-93)RulesPromptForm(99-367)apps/web/providers/EmailAccountProvider.tsx (1)
useAccount(72-82)apps/web/hooks/useModal.tsx (1)
useModal(3-9)apps/web/app/(app)/[emailAccountId]/assistant/examples.ts (1)
getPersonas(85-348)apps/web/app/(app)/[emailAccountId]/assistant/PersonaDialog.tsx (1)
PersonaDialog(7-40)apps/web/app/(app)/[emailAccountId]/assistant/AssistantOnboarding.tsx (1)
AssistantOnboarding(17-77)apps/web/hooks/useRules.tsx (1)
useRules(4-8)apps/web/hooks/useLabels.ts (1)
useLabels(62-88)apps/web/hooks/useDialogState.ts (1)
useDialogState(8-32)apps/web/components/editor/SimpleRichTextEditor.tsx (2)
SimpleRichTextEditorRef(22-25)SimpleRichTextEditor(27-153)apps/web/utils/actions/ai-rule.ts (1)
createRulesAction(516-586)apps/web/app/(app)/[emailAccountId]/assistant/ProcessingPromptFileDialog.tsx (1)
ProcessingPromptFileDialog(35-83)apps/web/app/(app)/[emailAccountId]/assistant/CreatedRulesModal.tsx (1)
CreatedRulesModal(24-109)apps/web/utils/actions/ai-rule.ts (7)
apps/web/utils/ai/rule/prompt-to-rules-old.ts (1)
aiPromptToRulesOld(20-86)apps/web/utils/actions/safe-action.ts (1)
actionClient(46-86)apps/web/utils/actions/rule.validation.ts (1)
createRulesBody(176-176)apps/web/utils/error.ts (1)
SafeError(86-96)apps/web/utils/ai/rule/prompt-to-rules.ts (1)
aiPromptToRules(15-70)apps/web/utils/rule/types.ts (1)
CreateRuleResult(3-5)apps/web/utils/rule/rule.ts (1)
safeCreateRule(34-100)🔇 Additional comments (2)
apps/web/app/(app)/[emailAccountId]/assistant/CreatedRulesModal.tsx (1)
74-77: Type safety check: conditionsToString expects RuleConditionsConfirm that
CreateRuleResultincludes the propertiesconditionalOperator,instructions,categoryFilterType, andcategoryFiltersat the top level. If not, pass the correct shape or map fields before callingconditionsToString(rule).apps/web/utils/rule/types.ts (1)
1-5: LGTM: Stable type alias and import hygiene
import typeprevents runtime dependency.NonNullable<Awaited<ReturnType<...>>>matches how you filter falsy results before pushing.No changes needed.
| {rules.map((rule) => ( | ||
| <Card | ||
| key={rule.id} | ||
| className="p-4 hover:shadow-md transition-shadow cursor-pointer" | ||
| onClick={() => ruleDialog.open({ ruleId: rule.id })} | ||
| > | ||
| <div className="space-y-2"> |
There was a problem hiding this comment.
🛠️ Refactor suggestion
Make the clickable Card keyboard-accessible
Cards are clickable divs with onClick only. Per guidelines, accompany onClick with keyboard handlers and ensure focusability/role.
- <Card
+ <Card
key={rule.id}
- className="p-4 hover:shadow-md transition-shadow cursor-pointer"
- onClick={() => ruleDialog.open({ ruleId: rule.id })}
+ className="p-4 hover:shadow-md transition-shadow cursor-pointer"
+ role="button"
+ tabIndex={0}
+ onClick={() => ruleDialog.open({ ruleId: rule.id })}
+ onKeyDown={(e) => {
+ if (e.key === "Enter" || e.key === " ") {
+ e.preventDefault();
+ ruleDialog.open({ ruleId: rule.id });
+ }
+ }}
>Also applies to: 92-99
🤖 Prompt for AI Agents
In apps/web/app/(app)/[emailAccountId]/assistant/CreatedRulesModal.tsx around
lines 60-66 (and similarly 92-99), the Card elements are only clickable via
onClick which is not keyboard-accessible; make them focusable and handle
keyboard activation by adding tabIndex={0}, role="button" (or use a native
button element), and an onKeyDown handler that triggers the same action when
Enter or Space is pressed; ensure you also preserve the onClick handler and
include an accessible label/aria-label if the card content is not descriptive
enough.
apps/web/app/(app)/[emailAccountId]/assistant/RulesPromptNew.tsx
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Actionable comments posted: 3
♻️ Duplicate comments (1)
apps/web/app/(app)/[emailAccountId]/assistant/RulesPromptNew.tsx (1)
78-87: Fix: Created rules modal never opens and Processing dialog never reaches “final” state
resultnever gets populated with counts, soProcessingPromptFileDialogremains stuck on its waiting step.showCreatedRulesModalis never set totrue, so the CreatedRulesModal never opens.- This matches an earlier review; re-raising with a concrete patch for this file.
Apply this diff to:
- Type
resultto include all counts.- Populate
resulton success.- Auto-open CreatedRulesModal either immediately (if the processing walkthrough was already viewed) or right after the dialog is closed on first-time flow.
@@ - const [isDialogOpen, setIsDialogOpen] = useState(false); - const [result, setResult] = useState<{ createdRules: number }>(); + const [isDialogOpen, setIsDialogOpen] = useState(false); + const [result, setResult] = useState<{ + createdRules: number; + editedRules: number; + removedRules: number; + }>(); const [createdRules, setCreatedRules] = useState<CreateRuleResult[]>([]); const [showCreatedRulesModal, setShowCreatedRulesModal] = useState(false); @@ { loading: "Creating rules...", success: (result) => { const { rules = [] } = result?.data || {}; setCreatedRules(rules); + setResult({ + createdRules: rules.length, + editedRules: 0, + removedRules: 0, + }); + // If the user already viewed the processing dialog, show the modal immediately. + if (viewedProcessingPromptFileDialog) { + setShowCreatedRulesModal(true); + } return `${rules.length} rules created!`; }, @@ /> + + {/* + After first-time users complete the processing dialog (it closes), + automatically open the CreatedRulesModal once. + */} + { /* effect intentionally placed near dialog wiring for locality */ } + {(() => { + // Inline effect pattern to keep the diff localized to this file section. + // If your lint rules disallow IIFEs for effects, move this to the hooks block above. + // eslint-disable-next-line react-hooks/rules-of-hooks + useEffect(() => { + if (!isDialogOpen && createdRules.length > 0 && !showCreatedRulesModal) { + setShowCreatedRulesModal(true); + } + }, [isDialogOpen, createdRules.length, showCreatedRulesModal]); + return null; + })()}Also applies to: 142-151, 281-286
🧹 Nitpick comments (2)
apps/web/app/(app)/[emailAccountId]/assistant/RulesPromptNew.tsx (2)
75-77: Pass error to LoadingContent for consistent error handlingGuidelines request using
LoadingContentwithloading,error, and children. You already provide a skeleton for loading; pass through theerrorfromuseLabels()as well.- const { userLabels, isLoading: isLoadingLabels } = useLabels(); + const { + userLabels, + isLoading: isLoadingLabels, + error: labelsError, + } = useLabels(); @@ - <LoadingContent - loading={isLoadingLabels} - loadingComponent={<Skeleton className="min-h-[70px] w/full" />} - > + <LoadingContent + loading={isLoadingLabels} + error={labelsError} + loadingComponent={<Skeleton className="min-h-[70px] w-full" />} + >Also applies to: 175-178
30-62: Export name collides with existing RulesPrompt componentThis file exports
RulesPrompt, and there is already aRulesPromptin apps/web/app/(app)/[emailAccountId]/assistant/RulesPrompt.tsx. Even as named exports, this can cause confusion and import mistakes.
- Ensure all import sites reference the intended module.
- Optionally rename this export to
RulesPromptNewto match the filename.-export function RulesPrompt() { +export function RulesPromptNew() {If you take this route, update the import where this is consumed.
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (2)
apps/web/app/(app)/[emailAccountId]/assistant/RulesPromptNew.tsx(1 hunks)version.txt(1 hunks)
✅ Files skipped from review due to trivial changes (1)
- version.txt
🧰 Additional context used
📓 Path-based instructions (14)
apps/web/**/*.{ts,tsx}
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
apps/web/**/*.{ts,tsx}: Use TypeScript with strict null checks
Path aliases: Use@/for imports from project root
Use proper error handling with try/catch blocks
Format code with Prettier
Leverage TypeScript inference for better DX
Files:
apps/web/app/(app)/[emailAccountId]/assistant/RulesPromptNew.tsx
apps/web/app/**
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
NextJS app router structure with (app) directory
Files:
apps/web/app/(app)/[emailAccountId]/assistant/RulesPromptNew.tsx
apps/web/**/*.tsx
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
apps/web/**/*.tsx: Follow tailwindcss patterns with prettier-plugin-tailwindcss
Prefer functional components with hooks
Use shadcn/ui components when available
Ensure responsive design with mobile-first approach
Follow consistent naming conventions (PascalCase for components)
Use LoadingContent component for async data
Useresult?.serverErrorwithtoastErrorandtoastSuccess
UseLoadingContentcomponent to handle loading and error states consistently
Passloading,error, and children props toLoadingContent
Files:
apps/web/app/(app)/[emailAccountId]/assistant/RulesPromptNew.tsx
!{.cursor/rules/*.mdc}
📄 CodeRabbit inference engine (.cursor/rules/cursor-rules.mdc)
Never place rule files in the project root, in subdirectories outside .cursor/rules, or in any other location
Files:
apps/web/app/(app)/[emailAccountId]/assistant/RulesPromptNew.tsx
**/*.tsx
📄 CodeRabbit inference engine (.cursor/rules/form-handling.mdc)
**/*.tsx: Use React Hook Form with Zod for validation
Validate form inputs before submission
Show validation errors inline next to form fields
Files:
apps/web/app/(app)/[emailAccountId]/assistant/RulesPromptNew.tsx
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/logging.mdc)
**/*.{ts,tsx}: UsecreateScopedLoggerfor logging in backend TypeScript files
Typically add the logger initialization at the top of the file when usingcreateScopedLogger
Only use.with()on a logger instance within a specific function, not for a global loggerImport Prisma in the project using
import prisma from "@/utils/prisma";
**/*.{ts,tsx}: Don't use TypeScript enums.
Don't use TypeScript const enum.
Don't use the TypeScript directive @ts-ignore.
Don't use primitive type aliases or misleading types.
Don't use empty type parameters in type aliases and interfaces.
Don't use any or unknown as type constraints.
Don't use implicit any type on variable declarations.
Don't let variables evolve into any type through reassignments.
Don't use non-null assertions with the ! postfix operator.
Don't misuse the non-null assertion operator (!) in TypeScript files.
Don't use user-defined types.
Use as const instead of literal types and type annotations.
Use export type for types.
Use import type for types.
Don't declare empty interfaces.
Don't merge interfaces and classes unsafely.
Don't use overload signatures that aren't next to each other.
Use the namespace keyword instead of the module keyword to declare TypeScript namespaces.
Don't use TypeScript namespaces.
Don't export imported variables.
Don't add type annotations to variables, parameters, and class properties that are initialized with literal expressions.
Don't use parameter properties in class constructors.
Use either T[] or Array consistently.
Initialize each enum member value explicitly.
Make sure all enum members are literal values.
Files:
apps/web/app/(app)/[emailAccountId]/assistant/RulesPromptNew.tsx
apps/web/app/(app)/*/**
📄 CodeRabbit inference engine (.cursor/rules/page-structure.mdc)
Components for the page are either put in page.tsx, or in the apps/web/app/(app)/PAGE_NAME folder
Files:
apps/web/app/(app)/[emailAccountId]/assistant/RulesPromptNew.tsx
apps/web/app/(app)/*/**/*.tsx
📄 CodeRabbit inference engine (.cursor/rules/page-structure.mdc)
If you need to use onClick in a component, that component is a client component and file must start with 'use client'
Files:
apps/web/app/(app)/[emailAccountId]/assistant/RulesPromptNew.tsx
apps/web/app/(app)/*/**/**/*.tsx
📄 CodeRabbit inference engine (.cursor/rules/page-structure.mdc)
If we're in a deeply nested component we will use swr to fetch via API
Files:
apps/web/app/(app)/[emailAccountId]/assistant/RulesPromptNew.tsx
apps/web/app/**/*.tsx
📄 CodeRabbit inference engine (.cursor/rules/project-structure.mdc)
Components with
onClickmust be client components withuse clientdirective
Files:
apps/web/app/(app)/[emailAccountId]/assistant/RulesPromptNew.tsx
**/*.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)
**/*.{js,jsx,ts,tsx}: Don't useelements in Next.js projects.
Don't use elements in Next.js projects.
Don't use namespace imports.
Don't access namespace imports dynamically.
Don't use global eval().
Don't use console.
Don't use debugger.
Don't use var.
Don't use with statements in non-strict contexts.
Don't use the arguments object.
Don't use consecutive spaces in regular expression literals.
Don't use the comma operator.
Don't use unnecessary boolean casts.
Don't use unnecessary callbacks with flatMap.
Use for...of statements instead of Array.forEach.
Don't create classes that only have static members (like a static namespace).
Don't use this and super in static contexts.
Don't use unnecessary catch clauses.
Don't use unnecessary constructors.
Don't use unnecessary continue statements.
Don't export empty modules that don't change anything.
Don't use unnecessary escape sequences in regular expression literals.
Don't use unnecessary labels.
Don't use unnecessary nested block statements.
Don't rename imports, exports, and destructured assignments to the same name.
Don't use unnecessary string or template literal concatenation.
Don't use String.raw in template literals when there are no escape sequences.
Don't use useless case statements in switch statements.
Don't use ternary operators when simpler alternatives exist.
Don't use useless this aliasing.
Don't initialize variables to undefined.
Don't use the void operators (they're not familiar).
Use arrow functions instead of function expressions.
Use Date.now() to get milliseconds since the Unix Epoch.
Use .flatMap() instead of map().flat() when possible.
Use literal property access instead of computed property access.
Don't use parseInt() or Number.parseInt() when binary, octal, or hexadecimal literals work.
Use concise optional chaining instead of chained logical expressions.
Use regular expression literals instead of the RegExp constructor when possible.
Don't use number literal object member names th...
Files:
apps/web/app/(app)/[emailAccountId]/assistant/RulesPromptNew.tsx
!pages/_document.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)
!pages/_document.{js,jsx,ts,tsx}: Don't import next/document outside of pages/_document.jsx in Next.js projects.
Don't import next/document outside of pages/_document.jsx in Next.js projects.
Files:
apps/web/app/(app)/[emailAccountId]/assistant/RulesPromptNew.tsx
**/*.{jsx,tsx}
📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)
**/*.{jsx,tsx}: Don't destructure props inside JSX components in Solid projects.
Don't use both children and dangerouslySetInnerHTML props on the same element.
Don't use Array index in keys.
Don't assign to React component props.
Don't define React components inside other components.
Don't use event handlers on non-interactive elements.
Don't assign JSX properties multiple times.
Don't add extra closing tags for components without children.
Use <>...</> instead of ....
Don't insert comments as text nodes.
Don't use the return value of React.render.
Make sure all dependencies are correctly specified in React hooks.
Make sure all React hooks are called from the top level of component functions.
Don't use unnecessary fragments.
Don't pass children as props.
Use semantic elements instead of role attributes in JSX.
Files:
apps/web/app/(app)/[emailAccountId]/assistant/RulesPromptNew.tsx
**/*.{html,jsx,tsx}
📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)
**/*.{html,jsx,tsx}: Don't use or elements.
Don't use accessKey attribute on any HTML element.
Don't set aria-hidden="true" on focusable elements.
Don't add ARIA roles, states, and properties to elements that don't support them.
Only use the scope prop on elements.
Don't assign non-interactive ARIA roles to interactive HTML elements.
Make sure label elements have text content and are associated with an input.
Don't assign interactive ARIA roles to non-interactive HTML elements.
Don't assign tabIndex to non-interactive HTML elements.
Don't use positive integers for tabIndex property.
Don't include "image", "picture", or "photo" in img alt prop.
Don't use explicit role property that's the same as the implicit/default role.
Make static elements with click handlers use a valid role attribute.
Always include a title element for SVG elements.
Give all elements requiring alt text meaningful information for screen readers.
Make sure anchors have content that's accessible to screen readers.
Assign tabIndex to non-interactive HTML elements with aria-activedescendant.
Include all required ARIA attributes for elements with ARIA roles.
Make sure ARIA properties are valid for the element's supported roles.
Always include a type attribute for button elements.
Make elements with interactive roles and handlers focusable.
Give heading elements content that's accessible to screen readers (not hidden with aria-hidden).
Always include a lang attribute on the html element.
Always include a title attribute for iframe elements.
Accompany onClick with at least one of: onKeyUp, onKeyDown, or onKeyPress.
Accompany onMouseOver/onMouseOut with onFocus/onBlur.
Include caption tracks for audio and video elements.
Make sure all anchors are valid and navigable.
Ensure all ARIA properties (aria-*) are valid.
Use valid, non-abstract ARIA roles for elements with ARIA roles.
Use valid ARIA state and property values.
Use valid values for the autocomplete attribute on input eleme...Files:
apps/web/app/(app)/[emailAccountId]/assistant/RulesPromptNew.tsx🧬 Code graph analysis (1)
apps/web/app/(app)/[emailAccountId]/assistant/RulesPromptNew.tsx (13)
apps/web/app/(app)/[emailAccountId]/assistant/RulesPrompt.tsx (2)
RulesPrompt(39-93)RulesPromptForm(99-367)apps/web/providers/EmailAccountProvider.tsx (1)
useAccount(72-82)apps/web/hooks/useModal.tsx (1)
useModal(3-9)apps/web/app/(app)/[emailAccountId]/assistant/examples.ts (1)
getPersonas(85-348)apps/web/app/(app)/[emailAccountId]/assistant/PersonaDialog.tsx (1)
PersonaDialog(7-40)apps/web/hooks/useRules.tsx (1)
useRules(4-8)apps/web/hooks/useLabels.ts (1)
useLabels(62-88)apps/web/hooks/useDialogState.ts (1)
useDialogState(8-32)apps/web/components/editor/SimpleRichTextEditor.tsx (2)
SimpleRichTextEditorRef(22-25)SimpleRichTextEditor(27-153)apps/web/utils/actions/ai-rule.ts (1)
createRulesAction(516-586)apps/web/app/(app)/[emailAccountId]/assistant/ProcessingPromptFileDialog.tsx (1)
ProcessingPromptFileDialog(35-83)apps/web/app/(app)/[emailAccountId]/assistant/ExamplesList.tsx (1)
Examples(53-53)apps/web/app/(app)/[emailAccountId]/assistant/CreatedRulesModal.tsx (1)
CreatedRulesModal(24-109)🔇 Additional comments (2)
apps/web/app/(app)/[emailAccountId]/assistant/RulesPromptNew.tsx (2)
84-86: Verify the localStorage key name is intentionalYou use the key "viewedProcessingPromptFileDialog3", while the existing flow elsewhere uses "viewedProcessingPromptFileDialog". If unintentional, users who already completed onboarding will see the walkthrough again.
If you meant to reuse the existing flag, change it to:
- ] = useLocalStorage("viewedProcessingPromptFileDialog3", false); + ] = useLocalStorage("viewedProcessingPromptFileDialog", false);
142-151: LGTM: Processing dialog wiring and persona appendDialog visibility/init flow and the persona-append effect are wired cleanly. Once
resultis set (see earlier comment), the final step will render correctly.
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
apps/web/app/(app)/[emailAccountId]/assistant/RuleForm.tsx (1)
236-239: Remove console.error in UI code; rely on toasts and scoped logging policiesThe coding guidelines forbid console usage. These console.error calls are redundant given toastError and don’t add value in production.
- if (res?.serverError) { - console.error(res); + if (res?.serverError) { toastError({ description: res.serverError }); if (mutate) mutate(); } else if (!res?.data?.rule) {- if (res?.serverError) { - console.error(res); + if (res?.serverError) { toastError({ description: res.serverError }); } else if (!res?.data?.rule) {If you need diagnostics during development, consider temporary scoped logging with whatever client-side logging facility you use, but keep it out of production builds.
Also applies to: 265-266
🧹 Nitpick comments (1)
apps/web/app/(app)/[emailAccountId]/assistant/RuleForm.tsx (1)
925-959: Delete button should track its own loading; don’t couple to form submit stateUsing loading={isSubmitting} ties deletion UX to form submission and doesn’t prevent double-deletes. Track deletion state locally, disable while in-flight, and optionally close the dialog after success.
- {rule.id && ( - <Button - size="sm" - variant="outline" - loading={isSubmitting} - Icon={TrashIcon} - onClick={async () => { + {rule.id && ( + <Button + size="sm" + variant="outline" + loading={isDeleting} + disabled={isDeleting} + Icon={TrashIcon} + onClick={async () => { + if (isDeleting) return; + setIsDeleting(true); const yes = confirm( "Are you sure you want to delete this rule?", ); if (yes) { try { const result = await deleteRuleAction(emailAccountId, { id: rule.id!, }); if (result?.serverError) { toastError({ description: result.serverError, }); } else { toastSuccess({ description: "The rule has been deleted.", }); router.push( prefixPath(emailAccountId, "/automation?tab=rules"), ); + // If embedded in a dialog with a cancel/close control, consider closing: + // onCancel?.(); } } catch { toastError({ description: "Failed to delete rule." }); + } finally { + setIsDeleting(false); } } }} > Delete </Button> )}Add state near the other local UI state:
const [isDeleting, setIsDeleting] = useState(false);Note: Because Button disables when loading, this also prevents accidental double clicks.
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (3)
apps/web/app/(app)/[emailAccountId]/assistant/RuleDialog.tsx(2 hunks)apps/web/app/(app)/[emailAccountId]/assistant/RuleForm.tsx(4 hunks)apps/web/app/(app)/[emailAccountId]/assistant/group/LearnedPatterns.tsx(1 hunks)
✅ Files skipped from review due to trivial changes (1)
- apps/web/app/(app)/[emailAccountId]/assistant/group/LearnedPatterns.tsx
🧰 Additional context used
📓 Path-based instructions (14)
apps/web/**/*.{ts,tsx}
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
apps/web/**/*.{ts,tsx}: Use TypeScript with strict null checks
Path aliases: Use@/for imports from project root
Use proper error handling with try/catch blocks
Format code with Prettier
Leverage TypeScript inference for better DX
Files:
apps/web/app/(app)/[emailAccountId]/assistant/RuleDialog.tsxapps/web/app/(app)/[emailAccountId]/assistant/RuleForm.tsx
apps/web/app/**
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
NextJS app router structure with (app) directory
Files:
apps/web/app/(app)/[emailAccountId]/assistant/RuleDialog.tsxapps/web/app/(app)/[emailAccountId]/assistant/RuleForm.tsx
apps/web/**/*.tsx
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
apps/web/**/*.tsx: Follow tailwindcss patterns with prettier-plugin-tailwindcss
Prefer functional components with hooks
Use shadcn/ui components when available
Ensure responsive design with mobile-first approach
Follow consistent naming conventions (PascalCase for components)
Use LoadingContent component for async data
Useresult?.serverErrorwithtoastErrorandtoastSuccess
UseLoadingContentcomponent to handle loading and error states consistently
Passloading,error, and children props toLoadingContent
Files:
apps/web/app/(app)/[emailAccountId]/assistant/RuleDialog.tsxapps/web/app/(app)/[emailAccountId]/assistant/RuleForm.tsx
!{.cursor/rules/*.mdc}
📄 CodeRabbit inference engine (.cursor/rules/cursor-rules.mdc)
Never place rule files in the project root, in subdirectories outside .cursor/rules, or in any other location
Files:
apps/web/app/(app)/[emailAccountId]/assistant/RuleDialog.tsxapps/web/app/(app)/[emailAccountId]/assistant/RuleForm.tsx
**/*.tsx
📄 CodeRabbit inference engine (.cursor/rules/form-handling.mdc)
**/*.tsx: Use React Hook Form with Zod for validation
Validate form inputs before submission
Show validation errors inline next to form fields
Files:
apps/web/app/(app)/[emailAccountId]/assistant/RuleDialog.tsxapps/web/app/(app)/[emailAccountId]/assistant/RuleForm.tsx
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/logging.mdc)
**/*.{ts,tsx}: UsecreateScopedLoggerfor logging in backend TypeScript files
Typically add the logger initialization at the top of the file when usingcreateScopedLogger
Only use.with()on a logger instance within a specific function, not for a global loggerImport Prisma in the project using
import prisma from "@/utils/prisma";
**/*.{ts,tsx}: Don't use TypeScript enums.
Don't use TypeScript const enum.
Don't use the TypeScript directive @ts-ignore.
Don't use primitive type aliases or misleading types.
Don't use empty type parameters in type aliases and interfaces.
Don't use any or unknown as type constraints.
Don't use implicit any type on variable declarations.
Don't let variables evolve into any type through reassignments.
Don't use non-null assertions with the ! postfix operator.
Don't misuse the non-null assertion operator (!) in TypeScript files.
Don't use user-defined types.
Use as const instead of literal types and type annotations.
Use export type for types.
Use import type for types.
Don't declare empty interfaces.
Don't merge interfaces and classes unsafely.
Don't use overload signatures that aren't next to each other.
Use the namespace keyword instead of the module keyword to declare TypeScript namespaces.
Don't use TypeScript namespaces.
Don't export imported variables.
Don't add type annotations to variables, parameters, and class properties that are initialized with literal expressions.
Don't use parameter properties in class constructors.
Use either T[] or Array consistently.
Initialize each enum member value explicitly.
Make sure all enum members are literal values.
Files:
apps/web/app/(app)/[emailAccountId]/assistant/RuleDialog.tsxapps/web/app/(app)/[emailAccountId]/assistant/RuleForm.tsx
apps/web/app/(app)/*/**
📄 CodeRabbit inference engine (.cursor/rules/page-structure.mdc)
Components for the page are either put in page.tsx, or in the apps/web/app/(app)/PAGE_NAME folder
Files:
apps/web/app/(app)/[emailAccountId]/assistant/RuleDialog.tsxapps/web/app/(app)/[emailAccountId]/assistant/RuleForm.tsx
apps/web/app/(app)/*/**/*.tsx
📄 CodeRabbit inference engine (.cursor/rules/page-structure.mdc)
If you need to use onClick in a component, that component is a client component and file must start with 'use client'
Files:
apps/web/app/(app)/[emailAccountId]/assistant/RuleDialog.tsxapps/web/app/(app)/[emailAccountId]/assistant/RuleForm.tsx
apps/web/app/(app)/*/**/**/*.tsx
📄 CodeRabbit inference engine (.cursor/rules/page-structure.mdc)
If we're in a deeply nested component we will use swr to fetch via API
Files:
apps/web/app/(app)/[emailAccountId]/assistant/RuleDialog.tsxapps/web/app/(app)/[emailAccountId]/assistant/RuleForm.tsx
apps/web/app/**/*.tsx
📄 CodeRabbit inference engine (.cursor/rules/project-structure.mdc)
Components with
onClickmust be client components withuse clientdirective
Files:
apps/web/app/(app)/[emailAccountId]/assistant/RuleDialog.tsxapps/web/app/(app)/[emailAccountId]/assistant/RuleForm.tsx
**/*.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)
**/*.{js,jsx,ts,tsx}: Don't useelements in Next.js projects.
Don't use elements in Next.js projects.
Don't use namespace imports.
Don't access namespace imports dynamically.
Don't use global eval().
Don't use console.
Don't use debugger.
Don't use var.
Don't use with statements in non-strict contexts.
Don't use the arguments object.
Don't use consecutive spaces in regular expression literals.
Don't use the comma operator.
Don't use unnecessary boolean casts.
Don't use unnecessary callbacks with flatMap.
Use for...of statements instead of Array.forEach.
Don't create classes that only have static members (like a static namespace).
Don't use this and super in static contexts.
Don't use unnecessary catch clauses.
Don't use unnecessary constructors.
Don't use unnecessary continue statements.
Don't export empty modules that don't change anything.
Don't use unnecessary escape sequences in regular expression literals.
Don't use unnecessary labels.
Don't use unnecessary nested block statements.
Don't rename imports, exports, and destructured assignments to the same name.
Don't use unnecessary string or template literal concatenation.
Don't use String.raw in template literals when there are no escape sequences.
Don't use useless case statements in switch statements.
Don't use ternary operators when simpler alternatives exist.
Don't use useless this aliasing.
Don't initialize variables to undefined.
Don't use the void operators (they're not familiar).
Use arrow functions instead of function expressions.
Use Date.now() to get milliseconds since the Unix Epoch.
Use .flatMap() instead of map().flat() when possible.
Use literal property access instead of computed property access.
Don't use parseInt() or Number.parseInt() when binary, octal, or hexadecimal literals work.
Use concise optional chaining instead of chained logical expressions.
Use regular expression literals instead of the RegExp constructor when possible.
Don't use number literal object member names th...
Files:
apps/web/app/(app)/[emailAccountId]/assistant/RuleDialog.tsxapps/web/app/(app)/[emailAccountId]/assistant/RuleForm.tsx
!pages/_document.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)
!pages/_document.{js,jsx,ts,tsx}: Don't import next/document outside of pages/_document.jsx in Next.js projects.
Don't import next/document outside of pages/_document.jsx in Next.js projects.
Files:
apps/web/app/(app)/[emailAccountId]/assistant/RuleDialog.tsxapps/web/app/(app)/[emailAccountId]/assistant/RuleForm.tsx
**/*.{jsx,tsx}
📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)
**/*.{jsx,tsx}: Don't destructure props inside JSX components in Solid projects.
Don't use both children and dangerouslySetInnerHTML props on the same element.
Don't use Array index in keys.
Don't assign to React component props.
Don't define React components inside other components.
Don't use event handlers on non-interactive elements.
Don't assign JSX properties multiple times.
Don't add extra closing tags for components without children.
Use <>...</> instead of ....
Don't insert comments as text nodes.
Don't use the return value of React.render.
Make sure all dependencies are correctly specified in React hooks.
Make sure all React hooks are called from the top level of component functions.
Don't use unnecessary fragments.
Don't pass children as props.
Use semantic elements instead of role attributes in JSX.
Files:
apps/web/app/(app)/[emailAccountId]/assistant/RuleDialog.tsxapps/web/app/(app)/[emailAccountId]/assistant/RuleForm.tsx
**/*.{html,jsx,tsx}
📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)
**/*.{html,jsx,tsx}: Don't use or elements.
Don't use accessKey attribute on any HTML element.
Don't set aria-hidden="true" on focusable elements.
Don't add ARIA roles, states, and properties to elements that don't support them.
Only use the scope prop on elements.
Don't assign non-interactive ARIA roles to interactive HTML elements.
Make sure label elements have text content and are associated with an input.
Don't assign interactive ARIA roles to non-interactive HTML elements.
Don't assign tabIndex to non-interactive HTML elements.
Don't use positive integers for tabIndex property.
Don't include "image", "picture", or "photo" in img alt prop.
Don't use explicit role property that's the same as the implicit/default role.
Make static elements with click handlers use a valid role attribute.
Always include a title element for SVG elements.
Give all elements requiring alt text meaningful information for screen readers.
Make sure anchors have content that's accessible to screen readers.
Assign tabIndex to non-interactive HTML elements with aria-activedescendant.
Include all required ARIA attributes for elements with ARIA roles.
Make sure ARIA properties are valid for the element's supported roles.
Always include a type attribute for button elements.
Make elements with interactive roles and handlers focusable.
Give heading elements content that's accessible to screen readers (not hidden with aria-hidden).
Always include a lang attribute on the html element.
Always include a title attribute for iframe elements.
Accompany onClick with at least one of: onKeyUp, onKeyDown, or onKeyPress.
Accompany onMouseOver/onMouseOut with onFocus/onBlur.
Include caption tracks for audio and video elements.
Make sure all anchors are valid and navigable.
Ensure all ARIA properties (aria-*) are valid.
Use valid, non-abstract ARIA roles for elements with ARIA roles.
Use valid ARIA state and property values.
Use valid values for the autocomplete attribute on input eleme...Files:
apps/web/app/(app)/[emailAccountId]/assistant/RuleDialog.tsxapps/web/app/(app)/[emailAccountId]/assistant/RuleForm.tsx🧠 Learnings (2)
📚 Learning: 2025-07-18T15:04:30.467Z
Learnt from: CR PR: elie222/inbox-zero#0 File: apps/web/CLAUDE.md:0-0 Timestamp: 2025-07-18T15:04:30.467Z Learning: Applies to apps/web/**/*.tsx : Use LoadingContent component for async dataApplied to files:
apps/web/app/(app)/[emailAccountId]/assistant/RuleDialog.tsx📚 Learning: 2025-07-19T17:50:22.078Z
Learnt from: CR PR: elie222/inbox-zero#0 File: .cursor/rules/ui-components.mdc:0-0 Timestamp: 2025-07-19T17:50:22.078Z Learning: Applies to components/**/*.tsx : Use the `LoadingContent` component to handle loading statesApplied to files:
apps/web/app/(app)/[emailAccountId]/assistant/RuleDialog.tsx🧬 Code graph analysis (1)
apps/web/app/(app)/[emailAccountId]/assistant/RuleForm.tsx (4)
apps/web/utils/actions/rule.validation.ts (1)
CreateRuleBody(158-158)apps/web/components/TooltipExplanation.tsx (1)
TooltipExplanation(33-60)apps/web/app/(app)/[emailAccountId]/assistant/group/LearnedPatterns.tsx (1)
LearnedPatternsDialog(20-89)apps/web/components/Button.tsx (1)
Button(60-87)⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Secret Detection
🔇 Additional comments (10)
apps/web/app/(app)/[emailAccountId]/assistant/RuleDialog.tsx (4)
56-57: Wire up cancel-to-close (edit mode) — looks goodPassing onCancel={onClose} ensures the form’s Cancel button closes the dialog while editing. Matches the new RuleForm API.
74-75: Wire up cancel-to-close (create mode) — looks goodSame as above, for the create path. Good consistency across both branches.
31-31: No unnecessary fetch—no changes neededThe
useRulehook internally passesnulltouseSWRwhenever itsruleIdparameter is falsy (including an empty string), which disables any request. CallinguseRule(ruleId || "")therefore correctly skips fetching in “create” mode. No further guard orenabledflag is required here.
39-39: Fix Dialog onOpenChange signature and behaviorIt appears that the
Dialogcomponent’sonOpenChangeprop is typed as(open: boolean) => void. Passing youronClosehandler (a() => void) directly can lead to an incorrect type and will fire on both open and close transitions. To ensure it only closes when the dialog is dismissed, wrap the call and check the boolean:- <Dialog open={isOpen} onOpenChange={onClose}> + <Dialog + open={isOpen} + onOpenChange={(open) => { + if (!open) onClose(); + }} + >Please verify that in your
@radix-ui/react-dialogtypings,onOpenChangeis indeed declared as(open: boolean) => voidbefore merging.apps/web/app/(app)/[emailAccountId]/assistant/RuleForm.tsx (6)
24-25: Icon import for delete action — OKTrashIcon import added and used in the Delete button; keeps iconography consistent with the rest of the UI.
121-135: New onCancel prop for RuleForm — API shape looks goodOptional onCancel?: () => void is clean and only renders the Cancel control when provided. This keeps RuleForm reusable in and out of dialogs.
882-900: Settings > Automate toggle — UX and state wiring look good
- Uses Toggle with labelRight and TooltipExplanation.
- Writes back via setValue without over-validating.
No issues spotted.
902-914: Settings > Apply to threads — good integrationThe runOnThreads toggle plus ThreadsExplanation provides clear behavior. State is correctly synced with setValue.
962-968: Cancel button — correct conditional render and default typeRenders only when onCancel exists and, via the Button component, defaults to type="button" so it won’t submit the form.
970-977: Submit buttons now rely on loading instead of disabled — goodButton already disables when loading is true. This improves UX consistency and reduces boilerplate.
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
apps/web/app/(app)/[emailAccountId]/assistant/RuleForm.tsx (1)
921-951: Prevent accidental form submissions; separate delete loading state
- The Delete and Cancel buttons do not specify
type. Inside a<form>, a button withouttypedefaults to"submit", which risks accidental submissions.- The Delete button ties its
loadingtoisSubmitting(form submit), not the delete action. Use a dedicatedisDeletingstate so the loading spinner reflects delete progress.Apply these diffs:
- <Button - size="sm" - variant="outline" - loading={isSubmitting} - Icon={TrashIcon} - onClick={async () => { + <Button + size="sm" + variant="outline" + type="button" + loading={isDeleting} + Icon={TrashIcon} + onClick={async () => { + if (isDeleting) return; const yes = confirm( "Are you sure you want to delete this rule?", ); if (yes) { - try { + try { + setIsDeleting(true); const result = await deleteRuleAction(emailAccountId, { id: rule.id!, }); if (result?.serverError) { toastError({ description: result.serverError, }); } else { toastSuccess({ description: "The rule has been deleted.", }); router.push( prefixPath(emailAccountId, "/automation?tab=rules"), ); } - } catch { + } catch { toastError({ description: "Failed to delete rule." }); + } finally { + setIsDeleting(false); } } }} > Delete </Button>- <Button variant="outline" onClick={onCancel}> + <Button variant="outline" type="button" onClick={onCancel}> Cancel </Button>Add the missing local state (outside the selected range; place near other local
useStatedeclarations inRuleForm):const [isDeleting, setIsDeleting] = useState(false);Also applies to: 958-963
♻️ Duplicate comments (1)
apps/web/app/(app)/[emailAccountId]/assistant/RuleForm.tsx (1)
127-135: Type mismatch: rule.groupId is used but not declared in RuleForm props
rule.groupIdis referenced when renderingLearnedPatternsDialog(Line 914) but theruleprop type isCreateRuleBody & { id?: string }. Under strict typing this will error and can break compilation.Apply this minimal typing fix:
-}: { - rule: CreateRuleBody & { id?: string }; +}: { + rule: CreateRuleBody & { id?: string; groupId?: string | null };Optionally, introduce a named alias for clarity:
type RuleFormRule = CreateRuleBody & { id?: string; groupId?: string | null }; // ... // in the props: rule: RuleFormRule;Also applies to: 910-917
🧹 Nitpick comments (1)
apps/web/app/(app)/[emailAccountId]/assistant/RuleForm.tsx (1)
1242-1264: Preserve user-entered label when toggling “AI generated”.Current logic resets the label value to an empty string on each toggle, which discards user input. Preserve the existing value and only flip the
aiflag.Apply this diff:
- <Toggle + <Toggle name={`actions.${index}.${field.name}.ai`} labelRight="AI generated" enabled={isAiGenerated || false} - onChange={(enabled: boolean) => { - setValue( - `actions.${index}.${field.name}`, - enabled - ? { value: "", ai: true } - : { value: "", ai: false }, - ); - }} + onChange={(enabled: boolean) => { + const current = + watch(`actions.${index}.${field.name}.value`) || ""; + setValue(`actions.${index}.${field.name}`, { + value: current, + ai: enabled, + }); + }} />
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (1)
apps/web/app/(app)/[emailAccountId]/assistant/RuleForm.tsx(10 hunks)
🧰 Additional context used
📓 Path-based instructions (14)
apps/web/**/*.{ts,tsx}
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
apps/web/**/*.{ts,tsx}: Use TypeScript with strict null checks
Path aliases: Use@/for imports from project root
Use proper error handling with try/catch blocks
Format code with Prettier
Leverage TypeScript inference for better DX
Files:
apps/web/app/(app)/[emailAccountId]/assistant/RuleForm.tsx
apps/web/app/**
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
NextJS app router structure with (app) directory
Files:
apps/web/app/(app)/[emailAccountId]/assistant/RuleForm.tsx
apps/web/**/*.tsx
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
apps/web/**/*.tsx: Follow tailwindcss patterns with prettier-plugin-tailwindcss
Prefer functional components with hooks
Use shadcn/ui components when available
Ensure responsive design with mobile-first approach
Follow consistent naming conventions (PascalCase for components)
Use LoadingContent component for async data
Useresult?.serverErrorwithtoastErrorandtoastSuccess
UseLoadingContentcomponent to handle loading and error states consistently
Passloading,error, and children props toLoadingContent
Files:
apps/web/app/(app)/[emailAccountId]/assistant/RuleForm.tsx
!{.cursor/rules/*.mdc}
📄 CodeRabbit inference engine (.cursor/rules/cursor-rules.mdc)
Never place rule files in the project root, in subdirectories outside .cursor/rules, or in any other location
Files:
apps/web/app/(app)/[emailAccountId]/assistant/RuleForm.tsx
**/*.tsx
📄 CodeRabbit inference engine (.cursor/rules/form-handling.mdc)
**/*.tsx: Use React Hook Form with Zod for validation
Validate form inputs before submission
Show validation errors inline next to form fields
Files:
apps/web/app/(app)/[emailAccountId]/assistant/RuleForm.tsx
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/logging.mdc)
**/*.{ts,tsx}: UsecreateScopedLoggerfor logging in backend TypeScript files
Typically add the logger initialization at the top of the file when usingcreateScopedLogger
Only use.with()on a logger instance within a specific function, not for a global loggerImport Prisma in the project using
import prisma from "@/utils/prisma";
**/*.{ts,tsx}: Don't use TypeScript enums.
Don't use TypeScript const enum.
Don't use the TypeScript directive @ts-ignore.
Don't use primitive type aliases or misleading types.
Don't use empty type parameters in type aliases and interfaces.
Don't use any or unknown as type constraints.
Don't use implicit any type on variable declarations.
Don't let variables evolve into any type through reassignments.
Don't use non-null assertions with the ! postfix operator.
Don't misuse the non-null assertion operator (!) in TypeScript files.
Don't use user-defined types.
Use as const instead of literal types and type annotations.
Use export type for types.
Use import type for types.
Don't declare empty interfaces.
Don't merge interfaces and classes unsafely.
Don't use overload signatures that aren't next to each other.
Use the namespace keyword instead of the module keyword to declare TypeScript namespaces.
Don't use TypeScript namespaces.
Don't export imported variables.
Don't add type annotations to variables, parameters, and class properties that are initialized with literal expressions.
Don't use parameter properties in class constructors.
Use either T[] or Array consistently.
Initialize each enum member value explicitly.
Make sure all enum members are literal values.
Files:
apps/web/app/(app)/[emailAccountId]/assistant/RuleForm.tsx
apps/web/app/(app)/*/**
📄 CodeRabbit inference engine (.cursor/rules/page-structure.mdc)
Components for the page are either put in page.tsx, or in the apps/web/app/(app)/PAGE_NAME folder
Files:
apps/web/app/(app)/[emailAccountId]/assistant/RuleForm.tsx
apps/web/app/(app)/*/**/*.tsx
📄 CodeRabbit inference engine (.cursor/rules/page-structure.mdc)
If you need to use onClick in a component, that component is a client component and file must start with 'use client'
Files:
apps/web/app/(app)/[emailAccountId]/assistant/RuleForm.tsx
apps/web/app/(app)/*/**/**/*.tsx
📄 CodeRabbit inference engine (.cursor/rules/page-structure.mdc)
If we're in a deeply nested component we will use swr to fetch via API
Files:
apps/web/app/(app)/[emailAccountId]/assistant/RuleForm.tsx
apps/web/app/**/*.tsx
📄 CodeRabbit inference engine (.cursor/rules/project-structure.mdc)
Components with
onClickmust be client components withuse clientdirective
Files:
apps/web/app/(app)/[emailAccountId]/assistant/RuleForm.tsx
**/*.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)
**/*.{js,jsx,ts,tsx}: Don't useelements in Next.js projects.
Don't use elements in Next.js projects.
Don't use namespace imports.
Don't access namespace imports dynamically.
Don't use global eval().
Don't use console.
Don't use debugger.
Don't use var.
Don't use with statements in non-strict contexts.
Don't use the arguments object.
Don't use consecutive spaces in regular expression literals.
Don't use the comma operator.
Don't use unnecessary boolean casts.
Don't use unnecessary callbacks with flatMap.
Use for...of statements instead of Array.forEach.
Don't create classes that only have static members (like a static namespace).
Don't use this and super in static contexts.
Don't use unnecessary catch clauses.
Don't use unnecessary constructors.
Don't use unnecessary continue statements.
Don't export empty modules that don't change anything.
Don't use unnecessary escape sequences in regular expression literals.
Don't use unnecessary labels.
Don't use unnecessary nested block statements.
Don't rename imports, exports, and destructured assignments to the same name.
Don't use unnecessary string or template literal concatenation.
Don't use String.raw in template literals when there are no escape sequences.
Don't use useless case statements in switch statements.
Don't use ternary operators when simpler alternatives exist.
Don't use useless this aliasing.
Don't initialize variables to undefined.
Don't use the void operators (they're not familiar).
Use arrow functions instead of function expressions.
Use Date.now() to get milliseconds since the Unix Epoch.
Use .flatMap() instead of map().flat() when possible.
Use literal property access instead of computed property access.
Don't use parseInt() or Number.parseInt() when binary, octal, or hexadecimal literals work.
Use concise optional chaining instead of chained logical expressions.
Use regular expression literals instead of the RegExp constructor when possible.
Don't use number literal object member names th...
Files:
apps/web/app/(app)/[emailAccountId]/assistant/RuleForm.tsx
!pages/_document.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)
!pages/_document.{js,jsx,ts,tsx}: Don't import next/document outside of pages/_document.jsx in Next.js projects.
Don't import next/document outside of pages/_document.jsx in Next.js projects.
Files:
apps/web/app/(app)/[emailAccountId]/assistant/RuleForm.tsx
**/*.{jsx,tsx}
📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)
**/*.{jsx,tsx}: Don't destructure props inside JSX components in Solid projects.
Don't use both children and dangerouslySetInnerHTML props on the same element.
Don't use Array index in keys.
Don't assign to React component props.
Don't define React components inside other components.
Don't use event handlers on non-interactive elements.
Don't assign JSX properties multiple times.
Don't add extra closing tags for components without children.
Use <>...</> instead of ....
Don't insert comments as text nodes.
Don't use the return value of React.render.
Make sure all dependencies are correctly specified in React hooks.
Make sure all React hooks are called from the top level of component functions.
Don't use unnecessary fragments.
Don't pass children as props.
Use semantic elements instead of role attributes in JSX.
Files:
apps/web/app/(app)/[emailAccountId]/assistant/RuleForm.tsx
**/*.{html,jsx,tsx}
📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)
**/*.{html,jsx,tsx}: Don't use or elements.
Don't use accessKey attribute on any HTML element.
Don't set aria-hidden="true" on focusable elements.
Don't add ARIA roles, states, and properties to elements that don't support them.
Only use the scope prop on elements.
Don't assign non-interactive ARIA roles to interactive HTML elements.
Make sure label elements have text content and are associated with an input.
Don't assign interactive ARIA roles to non-interactive HTML elements.
Don't assign tabIndex to non-interactive HTML elements.
Don't use positive integers for tabIndex property.
Don't include "image", "picture", or "photo" in img alt prop.
Don't use explicit role property that's the same as the implicit/default role.
Make static elements with click handlers use a valid role attribute.
Always include a title element for SVG elements.
Give all elements requiring alt text meaningful information for screen readers.
Make sure anchors have content that's accessible to screen readers.
Assign tabIndex to non-interactive HTML elements with aria-activedescendant.
Include all required ARIA attributes for elements with ARIA roles.
Make sure ARIA properties are valid for the element's supported roles.
Always include a type attribute for button elements.
Make elements with interactive roles and handlers focusable.
Give heading elements content that's accessible to screen readers (not hidden with aria-hidden).
Always include a lang attribute on the html element.
Always include a title attribute for iframe elements.
Accompany onClick with at least one of: onKeyUp, onKeyDown, or onKeyPress.
Accompany onMouseOver/onMouseOut with onFocus/onBlur.
Include caption tracks for audio and video elements.
Make sure all anchors are valid and navigable.
Ensure all ARIA properties (aria-*) are valid.
Use valid, non-abstract ARIA roles for elements with ARIA roles.
Use valid ARIA state and property values.
Use valid values for the autocomplete attribute on input eleme...Files:
apps/web/app/(app)/[emailAccountId]/assistant/RuleForm.tsx🧬 Code graph analysis (1)
apps/web/app/(app)/[emailAccountId]/assistant/RuleForm.tsx (4)
apps/web/utils/actions/rule.validation.ts (1)
CreateRuleBody(158-158)apps/web/components/TooltipExplanation.tsx (1)
TooltipExplanation(33-60)apps/web/app/(app)/[emailAccountId]/assistant/group/LearnedPatterns.tsx (1)
LearnedPatternsDialog(20-89)apps/web/components/Button.tsx (1)
Button(60-87)🔇 Additional comments (5)
apps/web/app/(app)/[emailAccountId]/assistant/RuleForm.tsx (5)
24-25: Icon import looks correct and consistent.
TrashIconfrom lucide-react is used in both the Delete button and the overlay remove buttons. All good.
492-497: Overlay remove control for conditions: UX improvement; confirm CardBasic forwards className.Nice addition moving remove into an overlay; cleaner and consistent. Please confirm
CardBasicforwardsclassNameto the container sorelativepositioning is honored; otherwise the overlay button will misplace.
877-918: Settings section reflow is a net win for clarity.Grouping automation toggles and learned patterns under “Settings” improves discoverability. The tooltips are tight and informative. No issues.
1085-1086: Action overlay remove control: consistent with ConditionsGood consistency with the Conditions overlay. The absolute-positioned control works as expected when
CardBasicisrelative.
1450-1451: ThreadsExplanation tooltip side switchSwitching to
side="right"aligns better with the nearby controls. Looks good.
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
apps/web/app/(app)/[emailAccountId]/assistant/ProcessingPromptFileDialog.tsx (1)
1-1: Add "use client" directive — server/client boundary bugThis component uses state/effects and onClick handlers, so it must be a client component per our guidelines for apps/web/app/**/*.tsx. Without "use client", this will error at runtime/build.
Apply at the top of the file:
+ "use client"; import { useCallback, useEffect, useState } from "react";apps/web/app/(app)/[emailAccountId]/assistant/RulesPrompt.tsx (1)
200-207: Fix dialog result wiring to avoid premature “success” UIYou’re always passing
result={[]}with a TODO. This triggers the success branch in ProcessingPromptFileDialog (empty arrays are truthy). Passnulluntil you have real created-rule items, or remove this dialog until the new CreatedRulesModal flow is fully integrated.- <ProcessingPromptFileDialog - open={isDialogOpen} - result={[]} // TODO: if we revert back to this component we need to fix this + <ProcessingPromptFileDialog + open={isDialogOpen} + result={null} onOpenChange={setIsDialogOpen} setViewedProcessingPromptFileDialog={ setViewedProcessingPromptFileDialog } />
♻️ Duplicate comments (1)
apps/web/app/(app)/[emailAccountId]/assistant/CreatedRulesModal.tsx (1)
76-83: Make clickable Card keyboard-activatable (Enter/Space)Role and tabIndex are present, but there’s no keyboard handler. Add onKeyDown to meet accessibility guidelines. This mirrors a prior comment on earlier commits.
<Card key={rule.id} role="button" tabIndex={0} className="p-4 hover:shadow-md transition-shadow cursor-pointer" - onClick={() => ruleDialog.open({ ruleId: rule.id })} + onClick={() => ruleDialog.open({ ruleId: rule.id })} + onKeyDown={(e) => { + if (e.key === "Enter" || e.key === " ") { + e.preventDefault(); + ruleDialog.open({ ruleId: rule.id }); + } + }} >
🧹 Nitpick comments (5)
apps/web/app/(app)/[emailAccountId]/assistant/ProcessingPromptFileDialog.tsx (2)
135-141: Make the large illustration responsiveThe image width/height jumped to 800x600 while the Dialog is max-w-xl (~576px). Add responsive sizing to prevent overflow and improve layout on small screens.
- <Image + <Image src="/images/assistant/rules.png" alt="Analyzing prompt file" - width={800} - height={600} - className="rounded-lg shadow" + width={800} + height={600} + sizes="(max-width: 640px) 100vw, 576px" + className="rounded-lg shadow w-full h-auto" />
216-274: Remove large block of commented-out codeThe legacy FinalStepReady block is fully commented. Delete or move to a gist to reduce noise. If you plan to restore it, add a TODO with an issue link.
-// function FinalStepReady({ -// back, -// next, -// result, -// }: StepProps & { -// result: ResultProps; -// }) { -// const { emailAccountId } = useAccount(); -// function getDescription() { -// let message = ""; -// ... -// } -// return ( ... ); -// }apps/web/app/(app)/[emailAccountId]/assistant/RulesPrompt.tsx (2)
119-124: Remove unused “result” state (counts) to reduce confusionThis state is set but never used after the UI shift to CreatedRulesContent. Drop it and the setter call in the toast success block.
- const [result, setResult] = useState<{ - createdRules: number; - editedRules: number; - removedRules: number; - }>(); ... - const { - createdRules = 0, - editedRules = 0, - removedRules = 0, - } = result?.data || {}; - setResult({ createdRules, editedRules, removedRules }); + const { + createdRules = 0, + editedRules = 0, + removedRules = 0, + } = result?.data || {};Also applies to: 171-172
163-187: Toast handling is acceptable; consider standardizing on toastError/toastSuccessYou’re using toast.promise here and toastError elsewhere. For consistency with our guidelines, consider using toastError for failure (success toast optional) when
result?.serverErroris present.If you want, I can provide a small helper to normalize API-action toast patterns.
apps/web/app/(app)/[emailAccountId]/assistant/CreatedRulesModal.tsx (1)
66-71: Handle zero-rule case more gracefullyIf rules is an empty array, the description renders “0 rules have been created…”, which reads odd. Provide a clearer zero case.
- <DialogDescription> - {rules.length === 1 - ? "Your rule has been created. You can now test it or view the details below." - : `${rules.length} rules have been created. You can now test them or view the details below.`} - </DialogDescription> + <DialogDescription> + {rules.length === 0 + ? "No new rules were created." + : rules.length === 1 + ? "Your rule has been created. You can now test it or view the details below." + : `${rules.length} rules have been created. You can now test them or view the details below.`} + </DialogDescription>
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (4)
apps/web/app/(app)/[emailAccountId]/assistant/CreatedRulesModal.tsx(1 hunks)apps/web/app/(app)/[emailAccountId]/assistant/ProcessingPromptFileDialog.tsx(7 hunks)apps/web/app/(app)/[emailAccountId]/assistant/RulesPrompt.tsx(7 hunks)apps/web/app/(app)/[emailAccountId]/assistant/RulesPromptNew.tsx(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- apps/web/app/(app)/[emailAccountId]/assistant/RulesPromptNew.tsx
🧰 Additional context used
📓 Path-based instructions (14)
apps/web/**/*.{ts,tsx}
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
apps/web/**/*.{ts,tsx}: Use TypeScript with strict null checks
Path aliases: Use@/for imports from project root
Use proper error handling with try/catch blocks
Format code with Prettier
Leverage TypeScript inference for better DX
Files:
apps/web/app/(app)/[emailAccountId]/assistant/CreatedRulesModal.tsxapps/web/app/(app)/[emailAccountId]/assistant/RulesPrompt.tsxapps/web/app/(app)/[emailAccountId]/assistant/ProcessingPromptFileDialog.tsx
apps/web/app/**
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
NextJS app router structure with (app) directory
Files:
apps/web/app/(app)/[emailAccountId]/assistant/CreatedRulesModal.tsxapps/web/app/(app)/[emailAccountId]/assistant/RulesPrompt.tsxapps/web/app/(app)/[emailAccountId]/assistant/ProcessingPromptFileDialog.tsx
apps/web/**/*.tsx
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
apps/web/**/*.tsx: Follow tailwindcss patterns with prettier-plugin-tailwindcss
Prefer functional components with hooks
Use shadcn/ui components when available
Ensure responsive design with mobile-first approach
Follow consistent naming conventions (PascalCase for components)
Use LoadingContent component for async data
Useresult?.serverErrorwithtoastErrorandtoastSuccess
UseLoadingContentcomponent to handle loading and error states consistently
Passloading,error, and children props toLoadingContent
Files:
apps/web/app/(app)/[emailAccountId]/assistant/CreatedRulesModal.tsxapps/web/app/(app)/[emailAccountId]/assistant/RulesPrompt.tsxapps/web/app/(app)/[emailAccountId]/assistant/ProcessingPromptFileDialog.tsx
!{.cursor/rules/*.mdc}
📄 CodeRabbit inference engine (.cursor/rules/cursor-rules.mdc)
Never place rule files in the project root, in subdirectories outside .cursor/rules, or in any other location
Files:
apps/web/app/(app)/[emailAccountId]/assistant/CreatedRulesModal.tsxapps/web/app/(app)/[emailAccountId]/assistant/RulesPrompt.tsxapps/web/app/(app)/[emailAccountId]/assistant/ProcessingPromptFileDialog.tsx
**/*.tsx
📄 CodeRabbit inference engine (.cursor/rules/form-handling.mdc)
**/*.tsx: Use React Hook Form with Zod for validation
Validate form inputs before submission
Show validation errors inline next to form fields
Files:
apps/web/app/(app)/[emailAccountId]/assistant/CreatedRulesModal.tsxapps/web/app/(app)/[emailAccountId]/assistant/RulesPrompt.tsxapps/web/app/(app)/[emailAccountId]/assistant/ProcessingPromptFileDialog.tsx
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/logging.mdc)
**/*.{ts,tsx}: UsecreateScopedLoggerfor logging in backend TypeScript files
Typically add the logger initialization at the top of the file when usingcreateScopedLogger
Only use.with()on a logger instance within a specific function, not for a global loggerImport Prisma in the project using
import prisma from "@/utils/prisma";
**/*.{ts,tsx}: Don't use TypeScript enums.
Don't use TypeScript const enum.
Don't use the TypeScript directive @ts-ignore.
Don't use primitive type aliases or misleading types.
Don't use empty type parameters in type aliases and interfaces.
Don't use any or unknown as type constraints.
Don't use implicit any type on variable declarations.
Don't let variables evolve into any type through reassignments.
Don't use non-null assertions with the ! postfix operator.
Don't misuse the non-null assertion operator (!) in TypeScript files.
Don't use user-defined types.
Use as const instead of literal types and type annotations.
Use export type for types.
Use import type for types.
Don't declare empty interfaces.
Don't merge interfaces and classes unsafely.
Don't use overload signatures that aren't next to each other.
Use the namespace keyword instead of the module keyword to declare TypeScript namespaces.
Don't use TypeScript namespaces.
Don't export imported variables.
Don't add type annotations to variables, parameters, and class properties that are initialized with literal expressions.
Don't use parameter properties in class constructors.
Use either T[] or Array consistently.
Initialize each enum member value explicitly.
Make sure all enum members are literal values.
Files:
apps/web/app/(app)/[emailAccountId]/assistant/CreatedRulesModal.tsxapps/web/app/(app)/[emailAccountId]/assistant/RulesPrompt.tsxapps/web/app/(app)/[emailAccountId]/assistant/ProcessingPromptFileDialog.tsx
apps/web/app/(app)/*/**
📄 CodeRabbit inference engine (.cursor/rules/page-structure.mdc)
Components for the page are either put in page.tsx, or in the apps/web/app/(app)/PAGE_NAME folder
Files:
apps/web/app/(app)/[emailAccountId]/assistant/CreatedRulesModal.tsxapps/web/app/(app)/[emailAccountId]/assistant/RulesPrompt.tsxapps/web/app/(app)/[emailAccountId]/assistant/ProcessingPromptFileDialog.tsx
apps/web/app/(app)/*/**/*.tsx
📄 CodeRabbit inference engine (.cursor/rules/page-structure.mdc)
If you need to use onClick in a component, that component is a client component and file must start with 'use client'
Files:
apps/web/app/(app)/[emailAccountId]/assistant/CreatedRulesModal.tsxapps/web/app/(app)/[emailAccountId]/assistant/RulesPrompt.tsxapps/web/app/(app)/[emailAccountId]/assistant/ProcessingPromptFileDialog.tsx
apps/web/app/(app)/*/**/**/*.tsx
📄 CodeRabbit inference engine (.cursor/rules/page-structure.mdc)
If we're in a deeply nested component we will use swr to fetch via API
Files:
apps/web/app/(app)/[emailAccountId]/assistant/CreatedRulesModal.tsxapps/web/app/(app)/[emailAccountId]/assistant/RulesPrompt.tsxapps/web/app/(app)/[emailAccountId]/assistant/ProcessingPromptFileDialog.tsx
apps/web/app/**/*.tsx
📄 CodeRabbit inference engine (.cursor/rules/project-structure.mdc)
Components with
onClickmust be client components withuse clientdirective
Files:
apps/web/app/(app)/[emailAccountId]/assistant/CreatedRulesModal.tsxapps/web/app/(app)/[emailAccountId]/assistant/RulesPrompt.tsxapps/web/app/(app)/[emailAccountId]/assistant/ProcessingPromptFileDialog.tsx
**/*.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)
**/*.{js,jsx,ts,tsx}: Don't useelements in Next.js projects.
Don't use elements in Next.js projects.
Don't use namespace imports.
Don't access namespace imports dynamically.
Don't use global eval().
Don't use console.
Don't use debugger.
Don't use var.
Don't use with statements in non-strict contexts.
Don't use the arguments object.
Don't use consecutive spaces in regular expression literals.
Don't use the comma operator.
Don't use unnecessary boolean casts.
Don't use unnecessary callbacks with flatMap.
Use for...of statements instead of Array.forEach.
Don't create classes that only have static members (like a static namespace).
Don't use this and super in static contexts.
Don't use unnecessary catch clauses.
Don't use unnecessary constructors.
Don't use unnecessary continue statements.
Don't export empty modules that don't change anything.
Don't use unnecessary escape sequences in regular expression literals.
Don't use unnecessary labels.
Don't use unnecessary nested block statements.
Don't rename imports, exports, and destructured assignments to the same name.
Don't use unnecessary string or template literal concatenation.
Don't use String.raw in template literals when there are no escape sequences.
Don't use useless case statements in switch statements.
Don't use ternary operators when simpler alternatives exist.
Don't use useless this aliasing.
Don't initialize variables to undefined.
Don't use the void operators (they're not familiar).
Use arrow functions instead of function expressions.
Use Date.now() to get milliseconds since the Unix Epoch.
Use .flatMap() instead of map().flat() when possible.
Use literal property access instead of computed property access.
Don't use parseInt() or Number.parseInt() when binary, octal, or hexadecimal literals work.
Use concise optional chaining instead of chained logical expressions.
Use regular expression literals instead of the RegExp constructor when possible.
Don't use number literal object member names th...
Files:
apps/web/app/(app)/[emailAccountId]/assistant/CreatedRulesModal.tsxapps/web/app/(app)/[emailAccountId]/assistant/RulesPrompt.tsxapps/web/app/(app)/[emailAccountId]/assistant/ProcessingPromptFileDialog.tsx
!pages/_document.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)
!pages/_document.{js,jsx,ts,tsx}: Don't import next/document outside of pages/_document.jsx in Next.js projects.
Don't import next/document outside of pages/_document.jsx in Next.js projects.
Files:
apps/web/app/(app)/[emailAccountId]/assistant/CreatedRulesModal.tsxapps/web/app/(app)/[emailAccountId]/assistant/RulesPrompt.tsxapps/web/app/(app)/[emailAccountId]/assistant/ProcessingPromptFileDialog.tsx
**/*.{jsx,tsx}
📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)
**/*.{jsx,tsx}: Don't destructure props inside JSX components in Solid projects.
Don't use both children and dangerouslySetInnerHTML props on the same element.
Don't use Array index in keys.
Don't assign to React component props.
Don't define React components inside other components.
Don't use event handlers on non-interactive elements.
Don't assign JSX properties multiple times.
Don't add extra closing tags for components without children.
Use <>...</> instead of ....
Don't insert comments as text nodes.
Don't use the return value of React.render.
Make sure all dependencies are correctly specified in React hooks.
Make sure all React hooks are called from the top level of component functions.
Don't use unnecessary fragments.
Don't pass children as props.
Use semantic elements instead of role attributes in JSX.
Files:
apps/web/app/(app)/[emailAccountId]/assistant/CreatedRulesModal.tsxapps/web/app/(app)/[emailAccountId]/assistant/RulesPrompt.tsxapps/web/app/(app)/[emailAccountId]/assistant/ProcessingPromptFileDialog.tsx
**/*.{html,jsx,tsx}
📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)
**/*.{html,jsx,tsx}: Don't use or elements.
Don't use accessKey attribute on any HTML element.
Don't set aria-hidden="true" on focusable elements.
Don't add ARIA roles, states, and properties to elements that don't support them.
Only use the scope prop on elements.
Don't assign non-interactive ARIA roles to interactive HTML elements.
Make sure label elements have text content and are associated with an input.
Don't assign interactive ARIA roles to non-interactive HTML elements.
Don't assign tabIndex to non-interactive HTML elements.
Don't use positive integers for tabIndex property.
Don't include "image", "picture", or "photo" in img alt prop.
Don't use explicit role property that's the same as the implicit/default role.
Make static elements with click handlers use a valid role attribute.
Always include a title element for SVG elements.
Give all elements requiring alt text meaningful information for screen readers.
Make sure anchors have content that's accessible to screen readers.
Assign tabIndex to non-interactive HTML elements with aria-activedescendant.
Include all required ARIA attributes for elements with ARIA roles.
Make sure ARIA properties are valid for the element's supported roles.
Always include a type attribute for button elements.
Make elements with interactive roles and handlers focusable.
Give heading elements content that's accessible to screen readers (not hidden with aria-hidden).
Always include a lang attribute on the html element.
Always include a title attribute for iframe elements.
Accompany onClick with at least one of: onKeyUp, onKeyDown, or onKeyPress.
Accompany onMouseOver/onMouseOut with onFocus/onBlur.
Include caption tracks for audio and video elements.
Make sure all anchors are valid and navigable.
Ensure all ARIA properties (aria-*) are valid.
Use valid, non-abstract ARIA roles for elements with ARIA roles.
Use valid ARIA state and property values.
Use valid values for the autocomplete attribute on input eleme...Files:
apps/web/app/(app)/[emailAccountId]/assistant/CreatedRulesModal.tsxapps/web/app/(app)/[emailAccountId]/assistant/RulesPrompt.tsxapps/web/app/(app)/[emailAccountId]/assistant/ProcessingPromptFileDialog.tsx🧠 Learnings (9)
📚 Learning: 2025-07-20T09:03:06.318Z
Learnt from: CR PR: elie222/inbox-zero#0 File: .cursor/rules/ultracite.mdc:0-0 Timestamp: 2025-07-20T09:03:06.318Z Learning: Applies to **/*.{html,jsx,tsx} : Make static elements with click handlers use a valid role attribute.Applied to files:
apps/web/app/(app)/[emailAccountId]/assistant/CreatedRulesModal.tsx📚 Learning: 2025-07-20T09:03:06.318Z
Learnt from: CR PR: elie222/inbox-zero#0 File: .cursor/rules/ultracite.mdc:0-0 Timestamp: 2025-07-20T09:03:06.318Z Learning: Applies to **/*.{html,jsx,tsx} : Make elements with interactive roles and handlers focusable.Applied to files:
apps/web/app/(app)/[emailAccountId]/assistant/CreatedRulesModal.tsx📚 Learning: 2025-07-20T09:03:06.318Z
Learnt from: CR PR: elie222/inbox-zero#0 File: .cursor/rules/ultracite.mdc:0-0 Timestamp: 2025-07-20T09:03:06.318Z Learning: Applies to **/*.{html,jsx,tsx} : Accompany onClick with at least one of: onKeyUp, onKeyDown, or onKeyPress.Applied to files:
apps/web/app/(app)/[emailAccountId]/assistant/CreatedRulesModal.tsx📚 Learning: 2025-07-20T09:03:06.318Z
Learnt from: CR PR: elie222/inbox-zero#0 File: .cursor/rules/ultracite.mdc:0-0 Timestamp: 2025-07-20T09:03:06.318Z Learning: Applies to **/*.{html,jsx,tsx} : Use valid, non-abstract ARIA roles for elements with ARIA roles.Applied to files:
apps/web/app/(app)/[emailAccountId]/assistant/CreatedRulesModal.tsx📚 Learning: 2025-07-20T09:03:06.318Z
Learnt from: CR PR: elie222/inbox-zero#0 File: .cursor/rules/ultracite.mdc:0-0 Timestamp: 2025-07-20T09:03:06.318Z Learning: Applies to **/*.{html,jsx,tsx} : Don't assign non-interactive ARIA roles to interactive HTML elements.Applied to files:
apps/web/app/(app)/[emailAccountId]/assistant/CreatedRulesModal.tsx📚 Learning: 2025-07-20T09:03:06.318Z
Learnt from: CR PR: elie222/inbox-zero#0 File: .cursor/rules/ultracite.mdc:0-0 Timestamp: 2025-07-20T09:03:06.318Z Learning: Applies to **/*.{jsx,tsx} : Use semantic elements instead of role attributes in JSX.Applied to files:
apps/web/app/(app)/[emailAccountId]/assistant/CreatedRulesModal.tsx📚 Learning: 2025-07-20T09:03:06.318Z
Learnt from: CR PR: elie222/inbox-zero#0 File: .cursor/rules/ultracite.mdc:0-0 Timestamp: 2025-07-20T09:03:06.318Z Learning: Applies to **/*.{html,jsx,tsx} : Don't assign interactive ARIA roles to non-interactive HTML elements.Applied to files:
apps/web/app/(app)/[emailAccountId]/assistant/CreatedRulesModal.tsx📚 Learning: 2025-08-17T16:57:25.834Z
Learnt from: CR PR: elie222/inbox-zero#0 File: .cursor/rules/llm.mdc:0-0 Timestamp: 2025-08-17T16:57:25.834Z Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Format prompts with XML-like tags; remove excessive whitespace; truncate overly long inputs; keep formatting consistentApplied to files:
apps/web/app/(app)/[emailAccountId]/assistant/RulesPrompt.tsx📚 Learning: 2025-07-18T15:04:44.818Z
Learnt from: CR PR: elie222/inbox-zero#0 File: .cursor/rules/data-fetching.mdc:0-0 Timestamp: 2025-07-18T15:04:44.818Z Learning: Applies to {app,components}/**/*.{ts,tsx} : Use `result?.serverError` with `toastError` and `toastSuccess` for error handling; success toast is optionalApplied to files:
apps/web/app/(app)/[emailAccountId]/assistant/RulesPrompt.tsx🧬 Code graph analysis (3)
apps/web/app/(app)/[emailAccountId]/assistant/CreatedRulesModal.tsx (7)
apps/web/utils/rule/types.ts (1)
CreateRuleResult(3-5)apps/web/providers/EmailAccountProvider.tsx (1)
useAccount(72-82)apps/web/hooks/useDialogState.ts (1)
useDialogState(8-32)apps/web/utils/path.ts (1)
prefixPath(1-4)apps/web/utils/condition.ts (1)
conditionsToString(194-227)apps/web/app/(app)/[emailAccountId]/assistant/Rules.tsx (1)
ActionBadges(495-525)apps/web/app/(app)/[emailAccountId]/assistant/RuleDialog.tsx (1)
RuleDialog(23-81)apps/web/app/(app)/[emailAccountId]/assistant/RulesPrompt.tsx (1)
apps/web/app/(app)/[emailAccountId]/assistant/ExamplesList.tsx (1)
Examples(53-53)apps/web/app/(app)/[emailAccountId]/assistant/ProcessingPromptFileDialog.tsx (2)
apps/web/utils/rule/types.ts (1)
CreateRuleResult(3-5)apps/web/app/(app)/[emailAccountId]/assistant/CreatedRulesModal.tsx (1)
CreatedRulesContent(42-121)🔇 Additional comments (8)
apps/web/app/(app)/[emailAccountId]/assistant/ProcessingPromptFileDialog.tsx (4)
35-37: Confirm new prop contract across call sitesProp type is now
result: CreateRuleResult[] | nulland required. Ensure all callers either passnulluntil real data is available or a non-empty array when creation completes. Passing[]causes the false-positive success flow noted above.Would you like me to scan the repo for all ProcessingPromptFileDialog usages and patch them to pass
nulluntil results are fetched?
95-100: Header layout copy changes look goodCentering header and constraining description width improves readability, no functional concerns.
113-116: Intro copy change LGTMShorter “Creating rules...” copy is clearer and avoids implying a fixed duration.
56-56: Dialog width tweak is fine
max-w-xlaligns with the content density here.apps/web/app/(app)/[emailAccountId]/assistant/RulesPrompt.tsx (2)
69-76: Provider plumbing into RulesPromptForm and Examples looks correctPassing
providerdown and letting Examples fetch provider-aware prompts matches the new flow.Also applies to: 95-113, 347-349
314-322: Good switch to toastError for generate failureThis aligns with our “result?.serverError with toastError” guideline for user-visible errors.
apps/web/app/(app)/[emailAccountId]/assistant/CreatedRulesModal.tsx (2)
24-39: Modal wrapper normalizes null to [] — OK as long as zero-case is handledConverting
rulesto an empty array keeps the content simple. With the improved zero messaging, this is fine.
58-112: Overall structure and navigation logic LGTMScoped max sizes, success header, list of rules with actions, and “Test Rules” navigation via prefixPath are coherent and follow our UI patterns.
| {currentStep >= STEPS && | ||
| (result ? ( | ||
| <FinalStepReady | ||
| back={back} | ||
| next={() => onOpenChange(false)} | ||
| result={result} | ||
| /> | ||
| // <FinalStepReady | ||
| // back={back} | ||
| // next={() => onOpenChange(false)} | ||
| // result={result} | ||
| // /> | ||
|
|
||
| <CreatedRulesContent rules={result} onOpenChange={onOpenChange} /> | ||
| ) : ( | ||
| <FinalStepWaiting back={back} /> | ||
| ))} |
There was a problem hiding this comment.
Guard final step on non-empty results to avoid false “success”
The condition treats an empty array as truthy, so you’ll render CreatedRulesContent even when there are 0 rules (e.g., callers passing []). Use a length check.
- {currentStep >= STEPS &&
- (result ? (
+ {currentStep >= STEPS &&
+ (Array.isArray(result) && result.length > 0 ? (
// <FinalStepReady
// back={back}
// next={() => onOpenChange(false)}
// result={result}
// />
-
<CreatedRulesContent rules={result} onOpenChange={onOpenChange} />
) : (
<FinalStepWaiting back={back} />
))}📝 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.
| {currentStep >= STEPS && | |
| (result ? ( | |
| <FinalStepReady | |
| back={back} | |
| next={() => onOpenChange(false)} | |
| result={result} | |
| /> | |
| // <FinalStepReady | |
| // back={back} | |
| // next={() => onOpenChange(false)} | |
| // result={result} | |
| // /> | |
| <CreatedRulesContent rules={result} onOpenChange={onOpenChange} /> | |
| ) : ( | |
| <FinalStepWaiting back={back} /> | |
| ))} | |
| {currentStep >= STEPS && | |
| (Array.isArray(result) && result.length > 0 ? ( | |
| // <FinalStepReady | |
| // back={back} | |
| // next={() => onOpenChange(false)} | |
| // result={result} | |
| // /> | |
| <CreatedRulesContent rules={result} onOpenChange={onOpenChange} /> | |
| ) : ( | |
| <FinalStepWaiting back={back} /> | |
| ))} |
🤖 Prompt for AI Agents
In apps/web/app/(app)/[emailAccountId]/assistant/ProcessingPromptFileDialog.tsx
around lines 62 to 73, the final-step rendering treats an empty array as truthy
and will render CreatedRulesContent for [] — change the guard to explicitly
check for a non-empty result (e.g., result && result.length > 0 or
result?.length > 0) so CreatedRulesContent is only rendered when there is at
least one rule; otherwise render FinalStepWaiting.
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (4)
apps/web/app/(app)/[emailAccountId]/assistant/RulesPromptNew.tsx (4)
96-104: Tighten editor validation: remove unnecessary typeof check and guard whitespace-only input
getMarkdown()already returns a string. Thetypeofcheck is dead code and doesn’t prevent empty/whitespace submissions. Use a trimmed check only.- const onSubmit = useCallback(async () => { - const markdown = editorRef.current?.getMarkdown(); - if (typeof markdown !== "string") return; - if (markdown.trim() === "") { - toastError({ - description: "Please enter a prompt to create rules", - }); - return; - } + const onSubmit = useCallback(async () => { + const markdown = editorRef.current?.getMarkdown() ?? ""; + if (!markdown.trim()) { + toastError({ + description: "Please enter a prompt to create rules", + }); + return; + }
110-123: Always reset submitting state; await mutate for consistencyEnsure
setIsSubmitting(false)runs even ifcreateRulesActionormutatethrows, and awaitmutate()so list refresh completes before success resolves.- toast.promise( - async () => { - const result = await createRulesAction(emailAccountId, { - prompt: markdown, - }).finally(() => { - setIsSubmitting(false); - }); - - if (result?.serverError) throw new Error(result.serverError); - - mutate(); - - return result; - }, + toast.promise( + async () => { + try { + const result = await createRulesAction(emailAccountId, { + prompt: markdown, + }); + if (result?.serverError) throw new Error(result.serverError); + await mutate(); + return result; + } finally { + setIsSubmitting(false); + } + },
170-177: Non-submit buttons inside a form must set type="button"Prevents accidental form submission; also aligns with our accessibility guideline.
<Button + type="button" variant="ghost" size="sm" onClick={() => ruleDialog.open()} Icon={PlusIcon} > @@ - <Button variant="outline" size="sm" onClick={onOpenPersonaDialog}> + <Button type="button" variant="outline" size="sm" onClick={onOpenPersonaDialog}> <UserPenIcon className="mr-2 size-4" /> Choose persona </Button> @@ - <Button + <Button + type="button" variant="outline" size="sm" onClick={() => setIsExamplesOpen((show) => !show)} >Also applies to: 200-203, 205-209
148-156: Auto-open CreatedRulesModal after first-time walkthrough closesAfter the first-time Processing dialog is closed, open the CreatedRulesModal if rules exist. This completes the flow reliably.
useEffect(() => { if (!personaPrompt) return; editorRef.current?.appendText(personaPrompt); }, [personaPrompt]); + // When the processing walkthrough is closed and we have results, + // auto-open the CreatedRulesModal for first-time users. + useEffect(() => { + if ( + !isProcessingDialogOpen && + (createdRules?.length ?? 0) > 0 && + !showCreatedRulesModal + ) { + setShowCreatedRulesModal(true); + } + }, [isProcessingDialogOpen, createdRules, showCreatedRulesModal]);
🧹 Nitpick comments (2)
apps/web/app/(app)/[emailAccountId]/assistant/RulesPromptNew.tsx (2)
76-78: Surface labels loading error in UI via LoadingContentPer guidelines, pass both loading and error to LoadingContent.
- const { userLabels, isLoading: isLoadingLabels } = useLabels(); + const { + userLabels, + isLoading: isLoadingLabels, + error: labelsError, + } = useLabels(); @@ - <LoadingContent - loading={isLoadingLabels} - loadingComponent={<Skeleton className="min-h-[70px] w-full" />} - > + <LoadingContent + loading={isLoadingLabels} + error={labelsError} + loadingComponent={<Skeleton className="min-h-[70px] w-full" />} + >Also applies to: 181-185
60-61: Optional: trigger persona picker after onboarding completesRestores the previous UX where onboarding leads users directly into persona selection. Safe, minimal change.
- <AssistantOnboarding /> + <AssistantOnboarding onComplete={onOpenPersonaDialog} />
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (2)
apps/web/app/(app)/[emailAccountId]/assistant/CreatedRulesModal.tsx(1 hunks)apps/web/app/(app)/[emailAccountId]/assistant/RulesPromptNew.tsx(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- apps/web/app/(app)/[emailAccountId]/assistant/CreatedRulesModal.tsx
🧰 Additional context used
📓 Path-based instructions (14)
apps/web/**/*.{ts,tsx}
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
apps/web/**/*.{ts,tsx}: Use TypeScript with strict null checks
Path aliases: Use@/for imports from project root
Use proper error handling with try/catch blocks
Format code with Prettier
Leverage TypeScript inference for better DX
Files:
apps/web/app/(app)/[emailAccountId]/assistant/RulesPromptNew.tsx
apps/web/app/**
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
NextJS app router structure with (app) directory
Files:
apps/web/app/(app)/[emailAccountId]/assistant/RulesPromptNew.tsx
apps/web/**/*.tsx
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
apps/web/**/*.tsx: Follow tailwindcss patterns with prettier-plugin-tailwindcss
Prefer functional components with hooks
Use shadcn/ui components when available
Ensure responsive design with mobile-first approach
Follow consistent naming conventions (PascalCase for components)
Use LoadingContent component for async data
Useresult?.serverErrorwithtoastErrorandtoastSuccess
UseLoadingContentcomponent to handle loading and error states consistently
Passloading,error, and children props toLoadingContent
Files:
apps/web/app/(app)/[emailAccountId]/assistant/RulesPromptNew.tsx
!{.cursor/rules/*.mdc}
📄 CodeRabbit inference engine (.cursor/rules/cursor-rules.mdc)
Never place rule files in the project root, in subdirectories outside .cursor/rules, or in any other location
Files:
apps/web/app/(app)/[emailAccountId]/assistant/RulesPromptNew.tsx
**/*.tsx
📄 CodeRabbit inference engine (.cursor/rules/form-handling.mdc)
**/*.tsx: Use React Hook Form with Zod for validation
Validate form inputs before submission
Show validation errors inline next to form fields
Files:
apps/web/app/(app)/[emailAccountId]/assistant/RulesPromptNew.tsx
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/logging.mdc)
**/*.{ts,tsx}: UsecreateScopedLoggerfor logging in backend TypeScript files
Typically add the logger initialization at the top of the file when usingcreateScopedLogger
Only use.with()on a logger instance within a specific function, not for a global loggerImport Prisma in the project using
import prisma from "@/utils/prisma";
**/*.{ts,tsx}: Don't use TypeScript enums.
Don't use TypeScript const enum.
Don't use the TypeScript directive @ts-ignore.
Don't use primitive type aliases or misleading types.
Don't use empty type parameters in type aliases and interfaces.
Don't use any or unknown as type constraints.
Don't use implicit any type on variable declarations.
Don't let variables evolve into any type through reassignments.
Don't use non-null assertions with the ! postfix operator.
Don't misuse the non-null assertion operator (!) in TypeScript files.
Don't use user-defined types.
Use as const instead of literal types and type annotations.
Use export type for types.
Use import type for types.
Don't declare empty interfaces.
Don't merge interfaces and classes unsafely.
Don't use overload signatures that aren't next to each other.
Use the namespace keyword instead of the module keyword to declare TypeScript namespaces.
Don't use TypeScript namespaces.
Don't export imported variables.
Don't add type annotations to variables, parameters, and class properties that are initialized with literal expressions.
Don't use parameter properties in class constructors.
Use either T[] or Array consistently.
Initialize each enum member value explicitly.
Make sure all enum members are literal values.
Files:
apps/web/app/(app)/[emailAccountId]/assistant/RulesPromptNew.tsx
apps/web/app/(app)/*/**
📄 CodeRabbit inference engine (.cursor/rules/page-structure.mdc)
Components for the page are either put in page.tsx, or in the apps/web/app/(app)/PAGE_NAME folder
Files:
apps/web/app/(app)/[emailAccountId]/assistant/RulesPromptNew.tsx
apps/web/app/(app)/*/**/*.tsx
📄 CodeRabbit inference engine (.cursor/rules/page-structure.mdc)
If you need to use onClick in a component, that component is a client component and file must start with 'use client'
Files:
apps/web/app/(app)/[emailAccountId]/assistant/RulesPromptNew.tsx
apps/web/app/(app)/*/**/**/*.tsx
📄 CodeRabbit inference engine (.cursor/rules/page-structure.mdc)
If we're in a deeply nested component we will use swr to fetch via API
Files:
apps/web/app/(app)/[emailAccountId]/assistant/RulesPromptNew.tsx
apps/web/app/**/*.tsx
📄 CodeRabbit inference engine (.cursor/rules/project-structure.mdc)
Components with
onClickmust be client components withuse clientdirective
Files:
apps/web/app/(app)/[emailAccountId]/assistant/RulesPromptNew.tsx
**/*.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)
**/*.{js,jsx,ts,tsx}: Don't useelements in Next.js projects.
Don't use elements in Next.js projects.
Don't use namespace imports.
Don't access namespace imports dynamically.
Don't use global eval().
Don't use console.
Don't use debugger.
Don't use var.
Don't use with statements in non-strict contexts.
Don't use the arguments object.
Don't use consecutive spaces in regular expression literals.
Don't use the comma operator.
Don't use unnecessary boolean casts.
Don't use unnecessary callbacks with flatMap.
Use for...of statements instead of Array.forEach.
Don't create classes that only have static members (like a static namespace).
Don't use this and super in static contexts.
Don't use unnecessary catch clauses.
Don't use unnecessary constructors.
Don't use unnecessary continue statements.
Don't export empty modules that don't change anything.
Don't use unnecessary escape sequences in regular expression literals.
Don't use unnecessary labels.
Don't use unnecessary nested block statements.
Don't rename imports, exports, and destructured assignments to the same name.
Don't use unnecessary string or template literal concatenation.
Don't use String.raw in template literals when there are no escape sequences.
Don't use useless case statements in switch statements.
Don't use ternary operators when simpler alternatives exist.
Don't use useless this aliasing.
Don't initialize variables to undefined.
Don't use the void operators (they're not familiar).
Use arrow functions instead of function expressions.
Use Date.now() to get milliseconds since the Unix Epoch.
Use .flatMap() instead of map().flat() when possible.
Use literal property access instead of computed property access.
Don't use parseInt() or Number.parseInt() when binary, octal, or hexadecimal literals work.
Use concise optional chaining instead of chained logical expressions.
Use regular expression literals instead of the RegExp constructor when possible.
Don't use number literal object member names th...
Files:
apps/web/app/(app)/[emailAccountId]/assistant/RulesPromptNew.tsx
!pages/_document.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)
!pages/_document.{js,jsx,ts,tsx}: Don't import next/document outside of pages/_document.jsx in Next.js projects.
Don't import next/document outside of pages/_document.jsx in Next.js projects.
Files:
apps/web/app/(app)/[emailAccountId]/assistant/RulesPromptNew.tsx
**/*.{jsx,tsx}
📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)
**/*.{jsx,tsx}: Don't destructure props inside JSX components in Solid projects.
Don't use both children and dangerouslySetInnerHTML props on the same element.
Don't use Array index in keys.
Don't assign to React component props.
Don't define React components inside other components.
Don't use event handlers on non-interactive elements.
Don't assign JSX properties multiple times.
Don't add extra closing tags for components without children.
Use <>...</> instead of ....
Don't insert comments as text nodes.
Don't use the return value of React.render.
Make sure all dependencies are correctly specified in React hooks.
Make sure all React hooks are called from the top level of component functions.
Don't use unnecessary fragments.
Don't pass children as props.
Use semantic elements instead of role attributes in JSX.
Files:
apps/web/app/(app)/[emailAccountId]/assistant/RulesPromptNew.tsx
**/*.{html,jsx,tsx}
📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)
**/*.{html,jsx,tsx}: Don't use or elements.
Don't use accessKey attribute on any HTML element.
Don't set aria-hidden="true" on focusable elements.
Don't add ARIA roles, states, and properties to elements that don't support them.
Only use the scope prop on elements.
Don't assign non-interactive ARIA roles to interactive HTML elements.
Make sure label elements have text content and are associated with an input.
Don't assign interactive ARIA roles to non-interactive HTML elements.
Don't assign tabIndex to non-interactive HTML elements.
Don't use positive integers for tabIndex property.
Don't include "image", "picture", or "photo" in img alt prop.
Don't use explicit role property that's the same as the implicit/default role.
Make static elements with click handlers use a valid role attribute.
Always include a title element for SVG elements.
Give all elements requiring alt text meaningful information for screen readers.
Make sure anchors have content that's accessible to screen readers.
Assign tabIndex to non-interactive HTML elements with aria-activedescendant.
Include all required ARIA attributes for elements with ARIA roles.
Make sure ARIA properties are valid for the element's supported roles.
Always include a type attribute for button elements.
Make elements with interactive roles and handlers focusable.
Give heading elements content that's accessible to screen readers (not hidden with aria-hidden).
Always include a lang attribute on the html element.
Always include a title attribute for iframe elements.
Accompany onClick with at least one of: onKeyUp, onKeyDown, or onKeyPress.
Accompany onMouseOver/onMouseOut with onFocus/onBlur.
Include caption tracks for audio and video elements.
Make sure all anchors are valid and navigable.
Ensure all ARIA properties (aria-*) are valid.
Use valid, non-abstract ARIA roles for elements with ARIA roles.
Use valid ARIA state and property values.
Use valid values for the autocomplete attribute on input eleme...Files:
apps/web/app/(app)/[emailAccountId]/assistant/RulesPromptNew.tsx🧠 Learnings (4)
📚 Learning: 2025-08-17T16:57:25.834Z
Learnt from: CR PR: elie222/inbox-zero#0 File: .cursor/rules/llm.mdc:0-0 Timestamp: 2025-08-17T16:57:25.834Z Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Keep system and user prompts separate; system defines role/task, user contains data/contextApplied to files:
apps/web/app/(app)/[emailAccountId]/assistant/RulesPromptNew.tsx📚 Learning: 2025-07-18T15:04:57.115Z
Learnt from: CR PR: elie222/inbox-zero#0 File: .cursor/rules/form-handling.mdc:0-0 Timestamp: 2025-07-18T15:04:57.115Z Learning: Applies to **/*.tsx : Validate form inputs before submissionApplied to files:
apps/web/app/(app)/[emailAccountId]/assistant/RulesPromptNew.tsx📚 Learning: 2025-07-18T15:05:16.146Z
Learnt from: CR PR: elie222/inbox-zero#0 File: .cursor/rules/fullstack-workflow.mdc:0-0 Timestamp: 2025-07-18T15:05:16.146Z Learning: Applies to apps/web/components/**/*Form.tsx : Use `result?.serverError` with `toastError` and `toastSuccess` for error and success notifications in form submission handlers.Applied to files:
apps/web/app/(app)/[emailAccountId]/assistant/RulesPromptNew.tsx📚 Learning: 2025-07-20T09:03:06.318Z
Learnt from: CR PR: elie222/inbox-zero#0 File: .cursor/rules/ultracite.mdc:0-0 Timestamp: 2025-07-20T09:03:06.318Z Learning: Applies to **/*.{html,jsx,tsx} : Always include a type attribute for button elements.Applied to files:
apps/web/app/(app)/[emailAccountId]/assistant/RulesPromptNew.tsx🧬 Code graph analysis (1)
apps/web/app/(app)/[emailAccountId]/assistant/RulesPromptNew.tsx (12)
apps/web/app/(app)/[emailAccountId]/assistant/RulesPrompt.tsx (2)
RulesPrompt(39-93)RulesPromptForm(99-367)apps/web/hooks/useModal.tsx (1)
useModal(3-9)apps/web/app/(app)/[emailAccountId]/assistant/examples.ts (1)
getPersonas(85-348)apps/web/app/(app)/[emailAccountId]/assistant/PersonaDialog.tsx (1)
PersonaDialog(7-40)apps/web/hooks/useRules.tsx (1)
useRules(4-8)apps/web/hooks/useLabels.ts (1)
useLabels(62-88)apps/web/hooks/useDialogState.ts (1)
useDialogState(8-32)apps/web/components/editor/SimpleRichTextEditor.tsx (2)
SimpleRichTextEditorRef(22-25)SimpleRichTextEditor(27-153)apps/web/utils/actions/ai-rule.ts (1)
createRulesAction(516-586)apps/web/app/(app)/[emailAccountId]/assistant/RuleDialog.tsx (1)
RuleDialog(23-81)apps/web/app/(app)/[emailAccountId]/assistant/ProcessingPromptFileDialog.tsx (1)
ProcessingPromptFileDialog(27-77)apps/web/app/(app)/[emailAccountId]/assistant/CreatedRulesModal.tsx (1)
CreatedRulesModal(24-40)
Summary by CodeRabbit
New Features
Improvements
Misc