Files
wehub-resource-sync 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
chore: import upstream snapshot with attribution
2026-07-13 13:40:13 +08:00

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>
);
};