Conversation
Agent Composer
Prompt input with Ask / Plan / Act modes, a context tray with token counts and messages that queue while the agent works.
2 variantsMIT
Preview
Can edit files and run commands
- src/i18n/locales/en.json6.2k
- + 6.2k tokens
files · / commands
Installation
Copies the component into components/ui and installs its dependencies and theme tokens.
pnpm dlx shadcn@latest add https://tuffui.com/r/agent-composer.jsonUsage
Pass your own data and wire the callbacks to your agent runtime.
import { useEffect, useState } from "react";
import {
AgentComposer,
type ComposerFile,
type ComposerQueued,
} from "@/components/ui/agent-composer";
export function Composer({ files }: { files: ComposerFile[] }) {
const { status, send, stop } = useAgentRun(); // your runtime
const [queue, setQueue] = useState<ComposerQueued[]>([]);
// When a run ends, send the next queued follow-up.
useEffect(() => {
if (status !== "idle" || !queue.length) return;
send({ text: queue[0].text });
setQueue(queue.slice(1));
}, [status, queue]);
return (
<AgentComposer
status={status}
files={files} // @ mention candidates: { id, path, tokens }
queue={queue}
onQueueChange={setQueue}
onSubmit={({ text, mode, context, model }) => send({ text, mode, context, model })}
onStop={stop}
models={[
{ id: "opus-5.5", name: "Claude Opus 5.5" },
{ id: "sonnet-5.5", name: "Claude Sonnet 5.5" },
]}
commands={[{ name: "review", description: "Review the current diff" }]}
/>
);
}Props
| Prop | Type | Default | Description |
|---|---|---|---|
| onSubmit | (message: ComposerMessage) => void | — | Called on Enter or Send when the agent is idle, with the text, mode, context items and model. The context tray clears afterwards. |
| status | "idle" | "streaming" | "idle" | While streaming, Enter queues the message instead of sending, and a Stop button appears. |
| queue / onQueueChange | ComposerQueued[] | — | Messages written while the agent works, shown above the input with remove buttons. Send the next one when a run ends. |
| onStop | () => void | — | Called by the Stop button. |
| mode / onModeChange | "ask" | "plan" | "act" | "act" | The Ask / Plan / Act switch in the header, with a hint for the active mode. Act sends with the brand color. |
| showModes | boolean | true | Hide the mode switch; messages are then sent as act. |
| context / onContextChange | ComposerContextItem[] | — | Files and attachments in the context tray, each with a token count and a running total. |
| files | ComposerFile[] | — | Candidates for the @ mention menu. Picking one adds it to the tray. |
| commands | ComposerCommand[] | — | Slash commands. Typing / at the start opens a filterable menu. |
| models / model / onModelChange | ComposerModel[] · string | — | Shown as the model id in the header; clicking switches to the next model. |
| value / onValueChange | string | — | Control the text. Uncontrolled when omitted, like mode, context and queue. |
| allowAttachments | boolean | true | Shows Add context, which opens a file picker (tokens estimated at 4 bytes each). |
| placeholder | string | — | Textarea placeholder. |
| className | string | — | Extra classes for the root element. |
type ComposerMode = "ask" | "plan" | "act";
type ComposerContextItem = { id: string; name: string; tokens: number };
type ComposerFile = { id: string; path: string; tokens: number };
type ComposerQueued = { id: string; text: string };
type ComposerModel = { id: string; name: string; description?: string };
type ComposerCommand = { name: string; description: string };
type ComposerMessage = {
text: string;
mode: ComposerMode;
context: ComposerContextItem[];
model?: string;
};