"use client"; import { useEffect, useRef, useState } from "react"; import { useSdk } from "@lynkd/messaging-inbox-sdk"; import { useUi } from "@/lib/ui-state"; import { Sparkles, X, Copy, Send, RefreshCw, MessageSquareReply, FileText, WandSparkles, Languages } from "lucide-react"; interface Turn { role: "user" | "ai"; text: string; error?: boolean; } const QUICK = [ { label: "Summarize message", icon: FileText, prompt: "Summarize the message above in one or two sentences." }, { label: "What should I reply?", icon: MessageSquareReply, prompt: "Suggest a thoughtful reply to the message above. Write it in first person, ready to send." }, { label: "Improve my writing", icon: WandSparkles, prompt: "Rewrite the message above to be clearer and more professional, keeping the meaning." }, { label: "Explain simply", icon: Languages, prompt: "Explain the message above in simple, plain language." }, ]; export function AiPanel() { const { aiPanel, closeAi, toast } = useUi(); const { client } = useSdk(); const [context, setContext] = useState(""); const [turns, setTurns] = useState([]); const [input, setInput] = useState(""); const [busy, setBusy] = useState(false); const scrollRef = useRef(null); // seed from the message that opened the panel useEffect(() => { if (!aiPanel) return; setContext(aiPanel.context ?? ""); setTurns([]); setInput(""); if (aiPanel.prompt) void ask(aiPanel.prompt, aiPanel.context ?? ""); // eslint-disable-next-line react-hooks/exhaustive-deps }, [aiPanel]); useEffect(() => { scrollRef.current?.scrollTo({ top: scrollRef.current.scrollHeight, behavior: "smooth" }); }, [turns, busy]); if (!aiPanel) return null; async function ask(prompt: string, ctx: string) { const p = prompt.trim(); if (!p || busy) return; setInput(""); setTurns((t) => [...t, { role: "user", text: p }]); setBusy(true); try { const res = await client.ai(p, ctx || undefined); if (res.ok && res.text) { setTurns((t) => [...t, { role: "ai", text: res.text as string }]); } else { setTurns((t) => [...t, { role: "ai", text: res.error || "The AI couldn't respond.", error: true }]); } } catch (e) { setTurns((t) => [...t, { role: "ai", text: `Request failed: ${(e as Error).message}`, error: true }]); } finally { setBusy(false); } } const copy = async (text: string) => { try { await navigator.clipboard?.writeText(text); toast("Copied to clipboard", "success"); } catch { toast("Couldn't access the clipboard", "danger"); } }; return (
e.stopPropagation()}>
AI Assistant
Powered by Gemini
{context && (
Message context
{context}
)} {turns.length === 0 && !busy && (
{context ? "Pick a quick action below, or ask anything about this message." : "Ask me anything — summaries, replies, rewrites, and more."}
)} {turns.map((t, i) => (
{t.text}
{t.role === "ai" && !t.error && ( )}
))} {busy && (
Thinking…
)}
{/* quick actions */}
{QUICK.map((a) => ( ))}