Install and status
Install the published chat package with pnpm add @texuli/chat. It is pre-1.0, so a new minor version may break the API. The thread, message list, composer, typing indicator and streaming events are on npm.
Composer autocomplete, stopped replies and ChatAttachment were released in 0.18.0. Message text renders emoji with EmojiText from texuli/emoji, loaded the first time a message holds one. Texuli's own emoji art is still landing, so emoji show as Unicode.
<script lang="ts">
import { type ChatMessage, ChatThread } from '@texuli/chat';
let messages = $state<ChatMessage[]>([]);
</script>
<ChatThread {messages} currentUserId="me" onSend={(body) => console.log(body)} />Streamed replies and Stop
Apply stream events with applyChatStreamEvent(messages, event): start, delta and end (or error). Events are checked, so a malformed event changes nothing. While a reply streams, its partial text is hidden from the live log, and the finished message is announced once.
Pass streaming and onStop to the thread. Stop, or Escape in the composer, calls onStop. Send { type: 'end', id, stopped: true } to keep the partial text and mark the reply as stopped.
messages = applyChatStreamEvent(messages, { type: 'start', id, author: agent, at: Date.now() });
messages = applyChatStreamEvent(messages, { type: 'delta', id, text: 'Here is the **summary**.' });
messages = applyChatStreamEvent(messages, { type: 'end', id, stopped: true });Markdown, mentions and dialect export
Message text is read by the shared CommonMark inline parser and filtered down to the chat vocabulary: strong, emphasis, code, links, mentions (@[Name](mention:id)), dates ([[date:ISO]]) and status chips ([[status:tone|Label]]). Nothing becomes HTML. Links pass the untrusted URL policy, and a refused link stays as the text that was written.
To post a reply in another chat, read it with parseMarkdown from @texuli/core/markdown and write it with writeMarkdownDialect from @texuli/core/markdown-dialects. The writer supports Slack, Discord, WhatsApp, Telegram, Teams, Google Chat, Mattermost and plain text, and reports every construct it had to adapt.
import { parseMarkdown } from '@texuli/core/markdown';
import { writeMarkdownDialect } from '@texuli/core/markdown-dialects';
const { value, diagnostics } = writeMarkdownDialect(parseMarkdown(reply).tree, 'slack');Composer autocomplete
Pass suggestions to the composer or the thread. In development: released in 0.18.0.
mentionSuggestions(people)uses the@trigger and inserts a mention token.commandSuggestions(commands)uses the/trigger at the start of a line.emojiSuggestions(search)uses the:trigger after one character. You pass the search, for examplesearchEmojisfrom@texuli/emojis/search, so the chat package carries no emoji data.- Any other source is
{ trigger, label, search }.
The list follows the APG combobox list-autocomplete pattern: arrows choose, Enter or Tab inserts, and Escape closes.
import { commandSuggestions, mentionSuggestions } from '@texuli/chat';
const suggestions = [
mentionSuggestions([{ id: 'jordan', name: 'Jordan Lee' }]),
commandSuggestions([{ name: 'report', description: 'Build the weekly report' }]),
];Blueprint attachments
ChatAttachment shows a blueprint an agent attached. In development: released in 0.18.0.
The host passes check and preview, because @texuli/chat sits below the catalog and the player:
checkvalidates the artifact, usually withvalidateDraftfrom@texuli/catalog/validateat the untrusted tier.previewpaints the validated value, usually withPlayerfrom@texuli/player.
The card never paints an artifact that was refused. It lists the reasons as text instead.
<ChatAttachment artifact={blueprint} title="Weekly report" {check}>
{#snippet preview(value)}<Player blueprint={value} label="Weekly report" />{/snippet}
</ChatAttachment>
