e30e75b5d4
Code Quality / Oxlint + Oxfmt (push) Waiting to run
Code Quality / Template Sync (push) Waiting to run
Code Quality / Build Changed Packages (push) Waiting to run
Code Quality / Test Changed Packages (push) Waiting to run
Deploy Expo Example / Deploy Production (push) Waiting to run
Deploy Ink Example / Deploy Production (push) Waiting to run
Python Tests / pytest (assistant-stream, 3.10) (push) Waiting to run
Python Tests / pytest (assistant-stream, 3.12) (push) Waiting to run
Python Tests / pytest (assistant-ui-sync-server-api, 3.10) (push) Waiting to run
Python Tests / pytest (assistant-ui-sync-server-api, 3.12) (push) Waiting to run
Deploy Shadcn Registry / Deploy Production (push) Waiting to run
Template Metrics / LOC + Bundle Size (push) Waiting to run
Changesets / Create Version PR (push) Has been cancelled
107 lines
2.9 KiB
TypeScript
107 lines
2.9 KiB
TypeScript
// Mirrors examples/with-react-ink/src/components/thread.tsx; keep in sync.
|
|
import { Box, Text, useStdout } from "ink";
|
|
import {
|
|
ThreadPrimitive,
|
|
ComposerPrimitive,
|
|
MessagePrimitive,
|
|
ErrorPrimitive,
|
|
LoadingPrimitive,
|
|
LiveChecklist,
|
|
} from "@assistant-ui/react-ink";
|
|
import { MarkdownText } from "@assistant-ui/react-ink-markdown";
|
|
|
|
// markdansi defaults width and color from process.stdout, which does not
|
|
// exist in the browser bundle; pass both explicitly so it never reads it.
|
|
// color is forwarded to markdansi but not yet declared on MarkdownTextProps.
|
|
const BrowserMarkdownText = ({ text }: { text: string }) => {
|
|
const { stdout } = useStdout();
|
|
return (
|
|
<MarkdownText
|
|
text={text}
|
|
width={(stdout?.columns ?? 80) - 4}
|
|
hyperlinks={false}
|
|
{...{ color: true }}
|
|
/>
|
|
);
|
|
};
|
|
|
|
const UserMessage = () => (
|
|
<MessagePrimitive.Root>
|
|
<Box marginBottom={1}>
|
|
<Text bold color="green">
|
|
You:{" "}
|
|
</Text>
|
|
<MessagePrimitive.Content
|
|
renderText={({ part }) => <Text wrap="wrap">{part.text}</Text>}
|
|
/>
|
|
</Box>
|
|
</MessagePrimitive.Root>
|
|
);
|
|
|
|
const AssistantMessage = () => (
|
|
<MessagePrimitive.Root>
|
|
<Box flexDirection="column" marginBottom={1}>
|
|
<Text bold color="blue">
|
|
AI:
|
|
</Text>
|
|
<MessagePrimitive.Content
|
|
renderText={({ part }) => <BrowserMarkdownText text={part.text} />}
|
|
renderReasoning={({ part }) => (
|
|
<Text dimColor italic>
|
|
{part.text}
|
|
</Text>
|
|
)}
|
|
/>
|
|
<LiveChecklist title="Plan" showProgress marginTop={1} />
|
|
<ErrorPrimitive.Root>
|
|
<ErrorPrimitive.Message />
|
|
</ErrorPrimitive.Root>
|
|
</Box>
|
|
</MessagePrimitive.Root>
|
|
);
|
|
|
|
const Loading = () => (
|
|
<LoadingPrimitive.Root marginBottom={1}>
|
|
<LoadingPrimitive.Spinner />
|
|
<Text> </Text>
|
|
<LoadingPrimitive.Text>Working</LoadingPrimitive.Text>
|
|
<Text> </Text>
|
|
<LoadingPrimitive.ElapsedTime />
|
|
</LoadingPrimitive.Root>
|
|
);
|
|
|
|
export const Thread = () => {
|
|
return (
|
|
<ThreadPrimitive.Root>
|
|
<ThreadPrimitive.Empty>
|
|
<Box flexDirection="column" marginBottom={1}>
|
|
<Text>
|
|
A real LLM streaming into a real Ink render loop in your browser.
|
|
</Text>
|
|
<Text dimColor>
|
|
{' try: "what is assistant-ui?" or "how do I render markdown?"'}
|
|
</Text>
|
|
</Box>
|
|
</ThreadPrimitive.Empty>
|
|
|
|
<ThreadPrimitive.Messages>
|
|
{({ message }) =>
|
|
message.role === "user" ? <UserMessage /> : <AssistantMessage />
|
|
}
|
|
</ThreadPrimitive.Messages>
|
|
|
|
<Loading />
|
|
|
|
<Box borderStyle="round" borderColor="gray" paddingX={1}>
|
|
<Text color="gray">{"> "}</Text>
|
|
<ComposerPrimitive.Input
|
|
submitOnEnter
|
|
multiLine
|
|
placeholder="Type a message... (Enter to send)"
|
|
autoFocus
|
|
/>
|
|
</Box>
|
|
</ThreadPrimitive.Root>
|
|
);
|
|
};
|