diff --git a/apps/web/app/(app)/[emailAccountId]/automation/onboarding/CategoriesSetup.tsx b/apps/web/app/(app)/[emailAccountId]/assistant/onboarding/CategoriesSetup.tsx similarity index 97% rename from apps/web/app/(app)/[emailAccountId]/automation/onboarding/CategoriesSetup.tsx rename to apps/web/app/(app)/[emailAccountId]/assistant/onboarding/CategoriesSetup.tsx index fcc4a0cbcd..8fcb11d385 100644 --- a/apps/web/app/(app)/[emailAccountId]/automation/onboarding/CategoriesSetup.tsx +++ b/apps/web/app/(app)/[emailAccountId]/assistant/onboarding/CategoriesSetup.tsx @@ -1,7 +1,6 @@ "use client"; import { useCallback } from "react"; -import Link from "next/link"; import { useRouter } from "next/navigation"; import { useForm } from "react-hook-form"; import { zodResolver } from "@hookform/resolvers/zod"; @@ -38,7 +37,7 @@ import { } from "@/utils/cookies"; import { prefixPath } from "@/utils/path"; -const NEXT_URL = "/automation/onboarding/draft-replies"; +const NEXT_URL = "/assistant/onboarding/draft-replies"; export function CategoriesSetup({ emailAccountId, @@ -148,7 +147,7 @@ export function CategoriesSetup({ variant="outline" onClick={() => { markOnboardingAsCompleted(ASSISTANT_ONBOARDING_COOKIE); - router.push(prefixPath(emailAccountId, "/automation")); + router.push(prefixPath(emailAccountId, "/assistant")); }} > Skip diff --git a/apps/web/app/(app)/[emailAccountId]/automation/onboarding/completed/page.tsx b/apps/web/app/(app)/[emailAccountId]/assistant/onboarding/completed/page.tsx similarity index 95% rename from apps/web/app/(app)/[emailAccountId]/automation/onboarding/completed/page.tsx rename to apps/web/app/(app)/[emailAccountId]/assistant/onboarding/completed/page.tsx index dcdef4785b..2bcce98a22 100644 --- a/apps/web/app/(app)/[emailAccountId]/automation/onboarding/completed/page.tsx +++ b/apps/web/app/(app)/[emailAccountId]/assistant/onboarding/completed/page.tsx @@ -36,7 +36,7 @@ export default function CompletedPage(props: {
diff --git a/apps/web/app/(app)/[emailAccountId]/automation/onboarding/draft-replies/page.tsx b/apps/web/app/(app)/[emailAccountId]/assistant/onboarding/draft-replies/page.tsx similarity index 96% rename from apps/web/app/(app)/[emailAccountId]/automation/onboarding/draft-replies/page.tsx rename to apps/web/app/(app)/[emailAccountId]/assistant/onboarding/draft-replies/page.tsx index 7d87afe528..ed76f3ef64 100644 --- a/apps/web/app/(app)/[emailAccountId]/automation/onboarding/draft-replies/page.tsx +++ b/apps/web/app/(app)/[emailAccountId]/assistant/onboarding/draft-replies/page.tsx @@ -32,7 +32,7 @@ export default function DraftRepliesPage() { markOnboardingAsCompleted(ASSISTANT_ONBOARDING_COOKIE); router.push( - prefixPath(emailAccountId, "/automation/onboarding/completed"), + prefixPath(emailAccountId, "/assistant/onboarding/completed"), ); }, [router, emailAccountId], diff --git a/apps/web/app/(app)/[emailAccountId]/automation/onboarding/page.tsx b/apps/web/app/(app)/[emailAccountId]/assistant/onboarding/page.tsx similarity index 100% rename from apps/web/app/(app)/[emailAccountId]/automation/onboarding/page.tsx rename to apps/web/app/(app)/[emailAccountId]/assistant/onboarding/page.tsx diff --git a/apps/web/app/(app)/[emailAccountId]/assistant/page.tsx b/apps/web/app/(app)/[emailAccountId]/assistant/page.tsx index e639d62e87..c6dead6851 100644 --- a/apps/web/app/(app)/[emailAccountId]/assistant/page.tsx +++ b/apps/web/app/(app)/[emailAccountId]/assistant/page.tsx @@ -29,7 +29,7 @@ export default async function AssistantPage({ }); if (!hasRule) { - redirect(prefixPath(emailAccountId, "/automation/onboarding")); + redirect(prefixPath(emailAccountId, "/assistant?onboarding=true")); } } diff --git a/apps/web/app/(app)/[emailAccountId]/automation/AssistantTabs.tsx b/apps/web/app/(app)/[emailAccountId]/automation/AssistantTabs.tsx index 468e922ecc..439799e6eb 100644 --- a/apps/web/app/(app)/[emailAccountId]/automation/AssistantTabs.tsx +++ b/apps/web/app/(app)/[emailAccountId]/automation/AssistantTabs.tsx @@ -41,7 +41,7 @@ export function AssistantTabs() { Set Up diff --git a/apps/web/app/(app)/[emailAccountId]/automation/ProcessingPromptFileDialog.tsx b/apps/web/app/(app)/[emailAccountId]/automation/ProcessingPromptFileDialog.tsx index f5f5ba6fe8..498f63a835 100644 --- a/apps/web/app/(app)/[emailAccountId]/automation/ProcessingPromptFileDialog.tsx +++ b/apps/web/app/(app)/[emailAccountId]/automation/ProcessingPromptFileDialog.tsx @@ -271,7 +271,7 @@ function FinalStepReady({ Back diff --git a/apps/web/app/(app)/[emailAccountId]/automation/RuleForm.tsx b/apps/web/app/(app)/[emailAccountId]/automation/RuleForm.tsx index a4bc00d4a4..44089e8bd4 100644 --- a/apps/web/app/(app)/[emailAccountId]/automation/RuleForm.tsx +++ b/apps/web/app/(app)/[emailAccountId]/automation/RuleForm.tsx @@ -187,7 +187,7 @@ export function RuleForm({ rule }: { rule: CreateRuleBody & { id?: string } }) { automate: data.automate, runOnThreads: data.runOnThreads, }); - router.push(prefixPath(emailAccountId, "/automation?tab=rules")); + router.push(prefixPath(emailAccountId, "/assistant?tab=rules")); } } else { const res = await createRuleAction(emailAccountId, data); @@ -210,7 +210,7 @@ export function RuleForm({ rule }: { rule: CreateRuleBody & { id?: string } }) { router.replace( prefixPath(emailAccountId, `/automation/rule/${res.data.rule.id}`), ); - router.push(prefixPath(emailAccountId, "/automation?tab=rules")); + router.push(prefixPath(emailAccountId, "/assistant?tab=rules")); } } }, @@ -735,7 +735,7 @@ export function RuleForm({ rule }: { rule: CreateRuleBody & { id?: string } }) { description: "The rule has been deleted.", }); router.push( - prefixPath(emailAccountId, "/automation?tab=rules"), + prefixPath(emailAccountId, "/assistant?tab=rules"), ); } } catch (error) { diff --git a/apps/web/app/(app)/[emailAccountId]/automation/Rules.tsx b/apps/web/app/(app)/[emailAccountId]/automation/Rules.tsx index 09d231f58a..a1867df18d 100644 --- a/apps/web/app/(app)/[emailAccountId]/automation/Rules.tsx +++ b/apps/web/app/(app)/[emailAccountId]/automation/Rules.tsx @@ -290,7 +290,7 @@ export function Rules({ size = "md" }: { size?: "sm" | "md" }) { ) : prefixPath( emailAccountId, - `/automation?tab=history&ruleId=${rule.id}`, + `/assistant?tab=history&ruleId=${rule.id}`, ) } target={ @@ -396,13 +396,13 @@ export function Rules({ size = "md" }: { size?: "sm" | "md" }) { {hasRules && (
{/* */} - - - - - - -
-
- - - -
- Examples - - -
- {examplePrompts.map((example) => ( - - ))} -
-
+ /> + +
+ + + + + + + +
- + ); } + +export function PromptFile() { + const { emailAccountId } = useAccount(); + const { data, isLoading, error, mutate } = useSWR< + RulesPromptResponse, + { error: string } + >("/api/user/rules/prompt"); + + const { isModalOpen, setIsModalOpen } = useModal(); + + const [persona, setPersona] = useState(null); + + const personaPrompt = persona + ? personas[persona as keyof typeof personas]?.prompt + : undefined; + + return ( + + {data && ( + <> + +
+ setIsModalOpen(true)} + mutate={mutate} + /> +
+ + + )} +
+ ); +} diff --git a/apps/web/app/(app)/[emailAccountId]/automation/page.tsx b/apps/web/app/(app)/[emailAccountId]/automation/page.tsx index 754b54e6c7..73cd2fadcb 100644 --- a/apps/web/app/(app)/[emailAccountId]/automation/page.tsx +++ b/apps/web/app/(app)/[emailAccountId]/automation/page.tsx @@ -1,6 +1,8 @@ import { Suspense } from "react"; import { cookies } from "next/headers"; import { redirect } from "next/navigation"; +import Link from "next/link"; +import { SparklesIcon } from "lucide-react"; import prisma from "@/utils/prisma"; import { History } from "@/app/(app)/[emailAccountId]/automation/History"; import { Pending } from "@/app/(app)/[emailAccountId]/automation/Pending"; @@ -15,6 +17,8 @@ import { TabsToolbar } from "@/components/TabsToolbar"; import { GmailProvider } from "@/providers/GmailProvider"; import { ASSISTANT_ONBOARDING_COOKIE } from "@/utils/cookies"; import { prefixPath } from "@/utils/path"; +import { AlertWithButton } from "@/components/Alert"; +import { Button } from "@/components/ui/button"; export const maxDuration = 300; // Applies to the actions @@ -37,7 +41,7 @@ export default async function AutomationPage({ }); if (!hasRule) { - redirect(prefixPath(emailAccountId, "/automation/onboarding")); + redirect(prefixPath(emailAccountId, "/assistant/onboarding")); } } @@ -51,6 +55,22 @@ export default async function AutomationPage({ +
+ } + variant="blue" + button={ + + } + /> +
+
diff --git a/apps/web/app/(app)/[emailAccountId]/bulk-unsubscribe/common.tsx b/apps/web/app/(app)/[emailAccountId]/bulk-unsubscribe/common.tsx index d16ea10942..df19bd81b1 100644 --- a/apps/web/app/(app)/[emailAccountId]/bulk-unsubscribe/common.tsx +++ b/apps/web/app/(app)/[emailAccountId]/bulk-unsubscribe/common.tsx @@ -545,7 +545,7 @@ export function HeaderButton(props: { // {error && Error loading groups} // // -// +// // // New Group // diff --git a/apps/web/app/(app)/[emailAccountId]/setup/page.tsx b/apps/web/app/(app)/[emailAccountId]/setup/page.tsx index 09ef82758b..a376ffbbe6 100644 --- a/apps/web/app/(app)/[emailAccountId]/setup/page.tsx +++ b/apps/web/app/(app)/[emailAccountId]/setup/page.tsx @@ -104,7 +104,7 @@ const features = [ description: "Easily unsubscribe from unwanted newsletters in one click", }, { - href: "/automation", + href: "/assistant", icon: BotIcon, iconBg: "bg-green-100 dark:bg-green-900/50", iconColor: "text-green-600 dark:text-green-400", @@ -287,7 +287,7 @@ function Checklist({
} iconBg="bg-green-100 dark:bg-green-900/50" iconColor="text-green-500 dark:text-green-400" diff --git a/apps/web/app/api/v1/openapi/route.ts b/apps/web/app/api/v1/openapi/route.ts index 935f7ca9d8..bb712d7337 100644 --- a/apps/web/app/api/v1/openapi/route.ts +++ b/apps/web/app/api/v1/openapi/route.ts @@ -35,7 +35,7 @@ registry.registerPath({ groupId: z .string() .describe( - "You can find the group id by going to `https://www.getinboxzero.com/automation?tab=groups`, clicking `Matching Emails`, and then copying the id from the URL.", + "You can find the group id by going to `https://www.getinboxzero.com/assistant?tab=groups`, clicking `Matching Emails`, and then copying the id from the URL.", ), }), query: groupEmailsQuerySchema, diff --git a/apps/web/components/CrispChat.tsx b/apps/web/components/CrispChat.tsx index ad4ab8df81..94d1258137 100644 --- a/apps/web/components/CrispChat.tsx +++ b/apps/web/components/CrispChat.tsx @@ -27,7 +27,11 @@ const CrispChat = ({ email }: { email?: string }) => { useEffect(() => { if (!env.NEXT_PUBLIC_CRISP_WEBSITE_ID || !isConfigured) return; - if (pathname.includes("/automation") || pathname.includes("/reply-zero")) { + if ( + pathname.includes("/assistant") || + pathname.includes("/automation") || + pathname.includes("/reply-zero") + ) { Crisp.chat.hide(); } else { Crisp.chat.show(); diff --git a/apps/web/components/NavBottom.tsx b/apps/web/components/NavBottom.tsx index 29e080a5a1..ba6b4d30f2 100644 --- a/apps/web/components/NavBottom.tsx +++ b/apps/web/components/NavBottom.tsx @@ -49,7 +49,7 @@ function NavBarBottom({ const links = [ { - path: "/automation", + path: "/assistant", label: "Assistant", icon: SparklesIcon, }, diff --git a/apps/web/components/SideNav.tsx b/apps/web/components/SideNav.tsx index 5fa89cb2bd..131d22e140 100644 --- a/apps/web/components/SideNav.tsx +++ b/apps/web/components/SideNav.tsx @@ -73,7 +73,7 @@ export const useNavigation = () => { () => [ { name: "Assistant", - href: prefixPath(emailAccountId, "/automation"), + href: prefixPath(emailAccountId, "/assistant"), icon: SparklesIcon, }, { @@ -248,7 +248,7 @@ export function SideNav({ ...props }: React.ComponentProps) { ? [ { name: "Back", - href: "/automation", + href: "/assistant", icon: ArrowLeftIcon, }, ...bottomLinks.filter((l) => !l.hideInMail), diff --git a/apps/web/components/assistant-chat/chat.tsx b/apps/web/components/assistant-chat/chat.tsx index 5590bd024c..e51554585b 100644 --- a/apps/web/components/assistant-chat/chat.tsx +++ b/apps/web/components/assistant-chat/chat.tsx @@ -6,7 +6,13 @@ import { type ScopedMutator, SWRConfig, useSWRConfig } from "swr"; import type { UIMessage } from "ai"; import { useChat } from "@ai-sdk/react"; import { toast } from "sonner"; -import { HistoryIcon, Loader2, PlusIcon } from "lucide-react"; +import { + FileIcon, + HistoryIcon, + Loader2, + MessageCircleIcon, + PlusIcon, +} from "lucide-react"; import { useQueryState } from "nuqs"; import { MultimodalInput } from "@/components/assistant-chat/multimodal-input"; import { Messages } from "./messages"; @@ -31,6 +37,10 @@ import type { GetChatResponse } from "@/app/api/chats/[chatId]/route"; import { useIsMobile } from "@/hooks/use-mobile"; import { ExamplesDialog } from "@/components/assistant-chat/examples-dialog"; import { Tooltip } from "@/components/Tooltip"; +import { + PromptFile, + RulesPrompt, +} from "@/app/(app)/[emailAccountId]/automation/RulesPrompt"; // Some mega hacky code used here to workaround AI SDK's use of SWR // AI SDK uses SWR too and this messes with the global SWR config @@ -123,7 +133,7 @@ function ChatInner({ className="flex-grow" > - + @@ -137,13 +147,7 @@ function ChatInner({ ); } -function ChatUI({ - chat, - chatId, -}: { - chat: ReturnType; - chatId?: string; -}) { +function ChatUI({ chat }: { chat: ReturnType }) { const { messages, setMessages, @@ -155,64 +159,92 @@ function ChatUI({ reload, } = chat; + const [mode, setMode] = useQueryState("mode"); + + const isDocumentMode = mode === "document"; + return (
- {messages.length > MAX_MESSAGES ? ( -
- The chat is too long. Please start a new conversation. -
- ) : ( -
- )} +
+ {isDocumentMode ? ( + + + + ) : ( + + + + )} + + {!isDocumentMode && messages.length > MAX_MESSAGES && ( +
+ The chat is too long. Please start a new conversation. +
+ )} +
- - - - - + {!isDocumentMode && ( + <> + + + + + + + )}
- -
- - + {isDocumentMode ? ( + +
+ +
+
+ ) : ( + <> + - {/* */} +
+ + + + )}
); } diff --git a/apps/web/components/email-list/EmailList.tsx b/apps/web/components/email-list/EmailList.tsx index 0ebb25c6df..3ca4594035 100644 --- a/apps/web/components/email-list/EmailList.tsx +++ b/apps/web/components/email-list/EmailList.tsx @@ -127,10 +127,10 @@ export function List({ <> Set rules on the{" "} - Automation page + Assistant page {" "} for our AI to handle incoming emails for you. diff --git a/apps/web/next.config.ts b/apps/web/next.config.ts index 6b837e028f..4724b18da8 100644 --- a/apps/web/next.config.ts +++ b/apps/web/next.config.ts @@ -53,7 +53,7 @@ const nextConfig: NextConfig = { return [ { source: "/", - destination: "/automation", + destination: "/assistant", has: [ { type: "cookie", diff --git a/apps/web/utils/actions/rule.ts b/apps/web/utils/actions/rule.ts index bfcdfaf563..b2cf3623c5 100644 --- a/apps/web/utils/actions/rule.ts +++ b/apps/web/utils/actions/rule.ts @@ -245,7 +245,7 @@ export const updateRuleAction = actionClient }); revalidatePath(prefixPath(emailAccountId, `/automation/rule/${id}`)); - revalidatePath(prefixPath(emailAccountId, "/automation")); + revalidatePath(prefixPath(emailAccountId, "/assistant")); return { rule: updatedRule }; } catch (error) { @@ -283,7 +283,7 @@ export const updateRuleInstructionsAction = actionClient }); revalidatePath(prefixPath(emailAccountId, `/automation/rule/${id}`)); - revalidatePath(prefixPath(emailAccountId, "/automation")); + revalidatePath(prefixPath(emailAccountId, "/assistant")); }, ); @@ -303,7 +303,7 @@ export const updateRuleSettingsAction = actionClient }); revalidatePath(prefixPath(emailAccountId, `/automation/rule/${id}`)); - revalidatePath(prefixPath(emailAccountId, "/automation")); + revalidatePath(prefixPath(emailAccountId, "/assistant")); revalidatePath(prefixPath(emailAccountId, "/reply-zero")); }, ); @@ -335,7 +335,7 @@ export const enableDraftRepliesAction = actionClient } revalidatePath(prefixPath(emailAccountId, `/automation/rule/${rule.id}`)); - revalidatePath(prefixPath(emailAccountId, "/automation")); + revalidatePath(prefixPath(emailAccountId, "/assistant")); revalidatePath(prefixPath(emailAccountId, "/reply-zero")); }); diff --git a/apps/web/utils/ai/assistant/process-user-request.ts b/apps/web/utils/ai/assistant/process-user-request.ts index 7e38ff9356..f99e877592 100644 --- a/apps/web/utils/ai/assistant/process-user-request.ts +++ b/apps/web/utils/ai/assistant/process-user-request.ts @@ -105,7 +105,7 @@ Best practices: Always end by using the reply tool to explain what changes were made. Use simple language and avoid jargon in your reply. -When you've made updates, include a link to the rules page at the end of your reply: ${env.NEXT_PUBLIC_BASE_URL}/automation?tab=rules +When you've made updates, include a link to the rules page at the end of your reply: ${env.NEXT_PUBLIC_BASE_URL}/assistant?tab=rules If you are unable to fix the rule, say so.`; const prompt = `${ diff --git a/packages/resend/emails/summary.tsx b/packages/resend/emails/summary.tsx index cce65f9716..88b3fd4865 100644 --- a/packages/resend/emails/summary.tsx +++ b/packages/resend/emails/summary.tsx @@ -320,7 +320,7 @@ function PendingEmails({