diff --git a/apps/web/app/(app)/[emailAccountId]/assistant/FixWithChat.tsx b/apps/web/app/(app)/[emailAccountId]/assistant/FixWithChat.tsx index bd946a5f1..6a5b793a7 100644 --- a/apps/web/app/(app)/[emailAccountId]/assistant/FixWithChat.tsx +++ b/apps/web/app/(app)/[emailAccountId]/assistant/FixWithChat.tsx @@ -24,6 +24,7 @@ import { ButtonList } from "@/components/ButtonList"; import type { RulesResponse } from "@/app/api/user/rules/route"; import { ProcessResultDisplay } from "@/app/(app)/[emailAccountId]/assistant/ProcessResultDisplay"; import { NONE_RULE_ID } from "@/app/(app)/[emailAccountId]/assistant/consts"; +import { createEmailDisplayValue } from "@/utils/email-display"; export function FixWithChat({ setInput, @@ -84,6 +85,7 @@ export function FixWithChat({ if (setInput) { // this is only set if we're in the correct context + // The input will automatically show as "@(Subject)" in the chat setInput(input); } else { // redirect to the assistant page diff --git a/apps/web/components/assistant-chat/chat.tsx b/apps/web/components/assistant-chat/chat.tsx index 81f0e35fc..a9d3642aa 100644 --- a/apps/web/components/assistant-chat/chat.tsx +++ b/apps/web/components/assistant-chat/chat.tsx @@ -36,6 +36,7 @@ import { useIsMobile } from "@/hooks/use-mobile"; import { ExamplesDialog } from "@/components/assistant-chat/examples-dialog"; import { Tooltip } from "@/components/Tooltip"; import { toastError } from "@/components/Toast"; +import { createDisplayValueForInput } from "@/utils/email-display"; // 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 @@ -180,6 +181,9 @@ function ChatUI({ chat }: { chat: ReturnType }) { reload, } = chat; + // Create display value for email data + const displayValue = createDisplayValueForInput(input); + return (
@@ -223,6 +227,7 @@ function ChatUI({ chat }: { chat: ReturnType }) { // messages={messages} setMessages={setMessages} // append={append} + displayValue={displayValue} />
diff --git a/apps/web/components/assistant-chat/multimodal-input.tsx b/apps/web/components/assistant-chat/multimodal-input.tsx index 7da355e7f..283a9a91c 100644 --- a/apps/web/components/assistant-chat/multimodal-input.tsx +++ b/apps/web/components/assistant-chat/multimodal-input.tsx @@ -1,7 +1,7 @@ "use client"; import type React from "react"; -import { useRef, useEffect, useCallback, memo } from "react"; +import { useRef, useEffect, useCallback, memo, useState } from "react"; import { toast } from "sonner"; import { useLocalStorage, useWindowSize } from "usehooks-ts"; import type { UseChatHelpers } from "@ai-sdk/react"; @@ -23,6 +23,7 @@ function PureMultimodalInput({ // append, handleSubmit, className, + displayValue, }: { // chatId?: string; input: UseChatHelpers["input"]; @@ -36,9 +37,11 @@ function PureMultimodalInput({ // append: UseChatHelpers["append"]; handleSubmit: UseChatHelpers["handleSubmit"]; className?: string; + displayValue?: string; }) { const textareaRef = useRef(null); const { width } = useWindowSize(); + const [isEditing, setIsEditing] = useState(false); useEffect(() => { if (textareaRef.current) { @@ -92,6 +95,16 @@ function PureMultimodalInput({ // adjustHeight(); // handled in useEffect }; + const handleFocus = () => { + if (displayValue !== undefined && displayValue !== input) { + setIsEditing(true); + } + }; + + const handleBlur = () => { + setIsEditing(false); + }; + // biome-ignore lint/correctness/useExhaustiveDependencies: const submitForm = useCallback(() => { // window.history.replaceState({}, "", `/chat/${chatId}`); @@ -106,6 +119,10 @@ function PureMultimodalInput({ } }, [handleSubmit, setLocalStorageInput, width]); + // Use displayValue if provided and not editing, otherwise use input + const visibleValue = + displayValue !== undefined && !isEditing ? displayValue : input; + return (
{/* {messages.length === 0 && ( @@ -116,8 +133,10 @@ function PureMultimodalInput({ data-testid="multimodal-input" ref={textareaRef} placeholder="Send a message..." - value={input} + value={visibleValue} onChange={handleInput} + onFocus={handleFocus} + onBlur={handleBlur} className={cn( "max-h-[calc(75dvh)] min-h-[24px] resize-none overflow-hidden rounded-2xl bg-muted pb-10 !text-base dark:border-zinc-700", className, @@ -157,6 +176,7 @@ export const MultimodalInput = memo( (prevProps, nextProps) => { if (prevProps.input !== nextProps.input) return false; if (prevProps.status !== nextProps.status) return false; + if (prevProps.displayValue !== nextProps.displayValue) return false; // if (!equal(prevProps.attachments, nextProps.attachments)) return false; return true; diff --git a/apps/web/utils/email-display.ts b/apps/web/utils/email-display.ts new file mode 100644 index 000000000..77c46536b --- /dev/null +++ b/apps/web/utils/email-display.ts @@ -0,0 +1,56 @@ +import type { ParsedMessage } from "@/utils/types"; + +/** + * Creates a simplified display value for email data in chat input + * Shows "@(Subject)" format while keeping full data under the hood + */ +export function createEmailDisplayValue(message: ParsedMessage): string { + const subject = message.headers.subject || "No subject"; + return `@(${subject})`; +} + +/** + * Checks if the input contains email data that should be displayed in simplified format + */ +export function hasEmailData(input: string): boolean { + // Check if input contains email-related patterns + return ( + input.includes("*From*:") || + input.includes("*Subject*:") || + input.includes("*Content*:") || + input.includes("Email details:") || + input.includes("Current rule applied:") + ); +} + +/** + * Extracts the subject from email data input for display purposes + */ +export function extractSubjectFromInput(input: string): string | null { + const subjectMatch = input.match(/\*Subject\*:\s*(.+?)(?:\n|$)/); + return subjectMatch ? subjectMatch[1].trim() : null; +} + +/** + * Creates a display value for email-related input + * Replaces the email details section with a simplified version + */ +export function createDisplayValueForInput(input: string): string | undefined { + if (!hasEmailData(input)) { + return undefined; // Use original input + } + + const subject = extractSubjectFromInput(input); + if (!subject) { + return input; // Keep original if no subject found + } + + // Replace the email details section with simplified version + const emailDetailsPattern = + /Email details:\s*\*From\*:[\s\S]*?\*Content\*:[\s\S]*?(?=\n\n|\nCurrent rule|$)/; + const replacement = `Email details:\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n📧 [${subject}]\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━`; + + const displayValue = input.replace(emailDetailsPattern, replacement); + + return displayValue; +}