"use client"; import { cn } from "@/lib/utils"; import { MarkdownText } from "@/components/assistant-ui/markdown-text"; import { ActionBarPrimitive, AuiIf, AttachmentPrimitive, BranchPickerPrimitive, ComposerPrimitive, MessagePrimitive, ThreadPrimitive, useAuiState, } from "@assistant-ui/react"; import { ArrowRight, AudioLines, CheckIcon, ChevronDownIcon, ChevronLeftIcon, ChevronRightIcon, CopyIcon, FileIcon, PencilIcon, Plus, RefreshCwIcon, Search, Sparkles, Square, Telescope, XIcon, } from "lucide-react"; import { type FC, useEffect, useState } from "react"; import { useShallow } from "zustand/shallow"; import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, } from "@/components/ui/dropdown-menu"; const composerPrimaryActionClassName = "absolute inset-0 flex items-center justify-center rounded-full transition-all duration-200 ease-out"; const composerPrimaryActionColorsClassName = "bg-[#25211c] text-[#f8f5f0] hover:bg-[#171411] dark:bg-[#f5f2ed] dark:text-[#1b1713] dark:hover:bg-white"; const messageActionClassName = "flex size-8 items-center justify-center rounded-full text-[#7a7268] transition-colors hover:bg-[#f1ece5] hover:text-[#1f1b17] dark:text-[#9d968d] dark:hover:bg-[#2a2724] dark:hover:text-[#f5f2ed]"; export const Perplexity: FC = () => { return ( s.thread.isEmpty}> !s.thread.isEmpty}> {() => } ); }; const EmptyState: FC = () => { return (

perplexity

); }; const Composer: FC<{ placeholder: string }> = ({ placeholder }) => { return ( s.composer.attachments.length > 0}>
{() => }
); }; const ComposerPrimaryAction: FC = () => { return (
s.thread.isRunning}> !s.thread.isRunning && s.composer.dictation != null} > !s.thread.isRunning && s.composer.dictation == null && !s.composer.isEmpty } > !s.thread.isRunning && s.composer.dictation == null && s.composer.isEmpty } >
); }; const SEARCH_MODES = [ { id: "search", name: "Search", description: "Fast answers to everyday questions", Icon: Search, }, { id: "research", name: "Research", description: "In-depth reports on complex topics", Icon: Telescope, }, { id: "labs", name: "Labs", description: "Apps, slides, and dashboards", Icon: Sparkles, }, ]; const SearchModePicker: FC = () => { const [mode, setMode] = useState(SEARCH_MODES[0]!.id); const current = SEARCH_MODES.find((m) => m.id === mode) ?? SEARCH_MODES[0]!; const CurrentIcon = current.Icon; return ( {current.name} {SEARCH_MODES.map(({ id, name, description, Icon }) => ( setMode(id)} className="flex items-start gap-3" > {id === mode ? : } {name} {description} ))} ); }; const PERPLEXITY_MODELS = [ { id: "best", name: "Best", description: "Auto-pick the best model" }, { id: "sonar", name: "Sonar", description: "Perplexity's fast model" }, { id: "claude", name: "Claude 4.5 Sonnet", description: "Anthropic" }, { id: "gpt-5", name: "GPT-5", description: "OpenAI" }, { id: "gemini", name: "Gemini 3 Pro", description: "Google" }, ]; const ModelPicker: FC = () => { const [model, setModel] = useState(PERPLEXITY_MODELS[0]!.id); const current = PERPLEXITY_MODELS.find((m) => m.id === model) ?? PERPLEXITY_MODELS[0]!; return ( {current.name} {PERPLEXITY_MODELS.map((m) => ( setModel(m.id)} className="flex items-start gap-3" > {m.id === model ? : null} {m.name} {m.description} ))} ); }; const ChatMessage: FC = () => { return ( s.message.role === "user"}>
{() => }
s.message.role === "assistant"}>
s.message.isCopied}> !s.message.isCopied}>
); }; const BranchPicker: FC = ({ className, ...props }) => { return ( / ); }; const useFileSrc = (file: File | undefined) => { const [src, setSrc] = useState(undefined); useEffect(() => { if (!file) { setSrc(undefined); return; } const objectUrl = URL.createObjectURL(file); setSrc(objectUrl); return () => { URL.revokeObjectURL(objectUrl); }; }, [file]); return src; }; const useAttachmentSrc = () => { const { file, src } = useAuiState( useShallow((s): { file?: File; src?: string } => { if (s.attachment.type !== "image") return {}; if (s.attachment.file) return { file: s.attachment.file }; const src = s.attachment.content?.filter((c) => c.type === "image")[0] ?.image; if (!src) return {}; return { src }; }), ); return useFileSrc(file) ?? src; }; const AttachmentTypeLabel: FC = () => { const typeLabel = useAuiState((s) => { switch (s.attachment.type) { case "image": return "Image"; case "document": return "Document"; case "file": return "File"; default: return s.attachment.type; } }); return {typeLabel}; }; const AttachmentPreview: FC<{ removable: boolean }> = ({ removable }) => { const src = useAttachmentSrc(); return (
{src ? ( Attachment ) : ( )}

{removable ? ( ) : null}
); };