import AIChat from "@moduletrace/twui/elements/ai/AIChat"; import type { TWUIAIChatSendFn } from "@moduletrace/twui/elements/ai/useAIChat"; import H1 from "@moduletrace/twui/layout/H1"; import P from "@moduletrace/twui/layout/P"; import Stack from "@moduletrace/twui/layout/Stack"; import DemoSection from "../(components)/DemoSection"; const REPLY = `Here's a quick rundown. ## Streaming Replies arrive **chunk by chunk** and render as markdown on every chunk: 1. Lists 2. \`inline code\` 3. Tables | Prop | What it does | | --- | --- | | \`send\` | Returns the reply stream | | \`chat\` | External \`useAIChat()\` state | \`\`\`ts const chat = useAIChat({ send }); chat.sendMessage("Hello"); \`\`\` > Press stop at any time to keep the partial reply.`; /** Fake model: streams a canned reply word by word, honouring `signal` */ const fakeSend: TWUIAIChatSendFn = async function* ({ messages, signal, model, }) { const last = messages.at(-1)?.content || ""; if (/error/i.test(last)) { await new Promise((r) => setTimeout(r, 400)); throw new Error("The model is overloaded. Try again in a moment."); } const reply = model == "demo-short" ? `You said: "${last}". This is the short model.` : REPLY; for (const token of reply.split(/(?<=\s)/)) { if (signal.aborted) return; await new Promise((r) => setTimeout(r, 25)); yield token; } }; export default function AIPage() { return (

AI

Deep-import only: elements/ai/* needs the{" "} react-markdown and remark-gfm{" "} optional peers.

{ await new Promise((r) => setTimeout(r, 300)); const words = messages[0]?.content .split(/\s+/) .slice(0, 4) .join(" "); return `Chat: ${words}`; }, }} models={[ { label: "Demo Long", value: "demo-long" }, { label: "Demo Short", value: "demo-short" }, ]} feedback suggestions={[ "Explain streaming", "Show me a table", { label: "Trigger an error", prompt: "error please" }, ]} footer="Replies are canned for the demo." />
); }