"use client";
import {
ActionBarPrimitive,
AuiIf,
AttachmentPrimitive,
ComposerPrimitive,
MessagePrimitive,
ThreadPrimitive,
useAuiState,
useMessageTiming,
} from "@assistant-ui/react";
import {
ArrowUpIcon,
CheckIcon,
ChevronDownIcon,
CopyIcon,
Mic,
Moon,
Paperclip,
PencilIcon,
RefreshCwIcon,
Square,
ThumbsDown,
ThumbsUp,
XIcon,
Zap,
} from "lucide-react";
import { useEffect, useState, type FC } from "react";
import { useShallow } from "zustand/shallow";
import { MarkdownText } from "@/components/assistant-ui/markdown-text";
import { GrokIcon } from "@/components/icons/grok";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
export const Grok: FC = () => {
return (
s.thread.isEmpty}>
s.thread.isEmpty === false}>
{() => }
Grok can make mistakes. Verify important information.
);
};
const Composer: FC = () => {
const isEmpty = useAuiState((s) => s.composer.isEmpty);
const isRunning = useAuiState((s) => s.thread.isRunning);
return (
s.composer.attachments.length > 0}>
{() => }
);
};
const GROK_MODELS = [
{
id: "grok-4.1-fast",
name: "Fast",
description: "Default. Quick responses",
Icon: Zap,
},
{
id: "grok-4.1",
name: "Grok 4.1",
description: "Standard reasoning",
Icon: Moon,
},
{
id: "grok-4.1-think",
name: "Think",
description: "Multi-step reasoning",
Icon: Moon,
},
];
const GrokModelPicker: FC = () => {
const [model, setModel] = useState(GROK_MODELS[0]!.id);
const current = GROK_MODELS.find((m) => m.id === model) ?? GROK_MODELS[0]!;
const CurrentIcon = current.Icon;
return (
{current.name}
{GROK_MODELS.map(({ id, name, description, Icon }) => (
setModel(id)}
className="flex items-start gap-3"
>
{id === model ? : }
{name}
{description}
))}
Subscribe to SuperGrok
);
};
const ChatMessage: FC = () => {
return (
s.message.role === "user"}>
{({ part }) => {
if (part.type === "text") return ;
return null;
}}
s.message.role === "assistant"}>
{({ part }) => {
if (part.type === "text") return ;
return null;
}}
);
};
const formatTime = (ms: number | undefined) => {
if (ms === undefined) return null;
if (ms < 1000) return `${Math.round(ms)}ms`;
return `${(ms / 1000).toFixed(1)}s`;
};
const formatMs = (ms: number | undefined) => {
if (ms === undefined) return "\u2014";
if (ms < 1000) return `${Math.round(ms)}ms`;
return `${(ms / 1000).toFixed(2)}s`;
};
const MessageTimingDisplay: FC = () => {
const timing = useMessageTiming();
if (!timing?.totalStreamTime) return null;
const totalTimeText = formatTime(timing.totalStreamTime);
if (!totalTimeText) return null;
return (
{timing.firstTokenTime !== undefined && (
First token
{formatMs(timing.firstTokenTime)}
)}
Total
{formatMs(timing.totalStreamTime)}
{timing.tokensPerSecond !== undefined && (
Speed
{timing.tokensPerSecond.toFixed(1)} tok/s
)}
{timing.totalChunks > 0 && (
Chunks
{timing.totalChunks}
)}
);
};
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 };
}),
);
const [fileSrc, setFileSrc] = useState(undefined);
useEffect(() => {
if (!file) {
setFileSrc(undefined);
return;
}
const objectUrl = URL.createObjectURL(file);
setFileSrc(objectUrl);
return () => URL.revokeObjectURL(objectUrl);
}, [file]);
return fileSrc ?? src;
};
const GrokAttachment: FC = () => {
const src = useAttachmentSrc();
return (
s.attachment.type === "image"}>
{src ? (
) : (
)}
s.attachment.type !== "image"}>
);
};