Skip to content

Chat parts ​

The smaller pieces the thread and the composer are made of, exported for a header, a card or a list of chats of your own. Each reads the chat from the scope it is rendered in.

tsx
import type { ReactNode } from 'react';
import { NOW, PICTURE, chatInfo, chatItems } from '../shared/agents-data.ts';
import { AgentDemo } from '../shared/agents.tsx';
import { DEMO_CHAT } from '../shared/agents-host.ts';
import { ChatActivity, StatusIcon } from '@adecore/agents-react/chat/ui/ChatActivity';
import { AccountPill } from '@adecore/agents-react/chat/ui/AccountPill';
import { ChatReferenceChip } from '@adecore/agents-react/chat/ui/ChatReferenceChip';
import { ImageThumb } from '@adecore/agents-react/chat/ui/ImageView';
import { UploadThumb } from '@adecore/agents-react/chat/ui/UploadThumb';
import { WelcomeGreeting } from '@adecore/agents-react/chat/ui/Welcome';
import type { StatusWord } from '@adecore/agents-react/agents/status-look';

const WORDS: StatusWord[] = ['running', 'paused', 'done', 'failed', 'cancelled'];

/* The demo chat with a helper agent behind it and a test watcher running in the background, under a second account. */
const busyChat = () => ({
    [DEMO_CHAT]: {
        info: chatInfo(DEMO_CHAT, {
            account: 'claude-work',
            background: [{ id: 'shell-1', kind: 'shell' as const, description: 'Watch the tests', command: 'bun test --watch', startedAt: NOW - 120_000 }]
        }),
        items: chatItems()
    }
});

function Row({ label, children }: { label: string; children: ReactNode }) {
    return (
        <div className="flex items-center gap-4">
            <span className="w-36 shrink-0 text-xs text-text-muted">{label}</span>
            <div className="flex min-w-0 flex-wrap items-center gap-2">{children}</div>
        </div>
    );
}

export default function ChatPartsDemo() {
    return (
        <AgentDemo chats={busyChat}>
            <div className="flex w-full flex-col gap-4">
                <WelcomeGreeting chatId={DEMO_CHAT} />
                <Row label="ChatActivity">
                    <ChatActivity chatId={DEMO_CHAT} />
                </Row>
                <Row label="StatusIcon">
                    {WORDS.map((word) => (
                        <span key={word} className="flex items-center gap-1 text-xs">
                            <StatusIcon word={word} /> {word}
                        </span>
                    ))}
                </Row>
                <Row label="AccountPill">
                    <AccountPill chatId={DEMO_CHAT} />
                </Row>
                <Row label="ChatReferenceChip">
                    <ChatReferenceChip chatId="chat-release" />
                    <ChatReferenceChip chatId="chat-gone" onRemove={() => undefined} />
                </Row>
                <Row label="UploadThumb, ImageThumb">
                    <UploadThumb upload={PICTURE} />
                    <ImageThumb
                        source={{ url: `data:${PICTURE.mime};base64,${PICTURE.data}`, failure: null }}
                        alt={PICTURE.name}
                        className="size-16 object-cover"
                    />
                    <ImageThumb source={{ url: null, failure: 'The host keeps no file under this id.' }} alt="Missing" />
                </Row>
            </div>
        </AgentDemo>
    );
}
ComponentImport
ChatActivity({ chatId })chat/ui/ChatActivityThe line over the composer: the chat's subagents and its background commands, each with a flyout. Nothing while there are none.
StatusIcon({ word })chat/ui/ChatActivityThe mark of a StatusWord: running, paused, done, failed or cancelled.
AccountPill({ chatId })chat/ui/AccountPillThe account a chat runs under, only while its CLI has a choice of accounts.
ChatReferenceChip({ chatId, onRemove? })chat/ui/ChatReferenceChipAnother chat a message points at, under the title useReferableChats gives it now.
UploadThumb({ upload })chat/ui/UploadThumbAn image the composer holds, drawn from a thumbnail made in the page.
ImageThumb({ source, alt, className? })chat/ui/ImageViewAn image that opens large in a lightbox. source is a ResourceUrl; without a URL it shows its failure.
WelcomeGreeting({ chatId, now? })chat/ui/WelcomeThe greeting over an empty chat, by the part of the day.

statusLookOf(word) answers the icon and tone of a StatusWord, and taskStatusWord(task) the word of a task, paused for an open task waiting out a limit. backgroundCounts(tasks) counts the shells and monitors of info.background. useUploadThumbnail(upload) makes the thumbnail UploadThumb draws, at most THUMBNAIL_PX (256) pixels.

Licenses and third-party notices are listed in each package.