Ink Web

Chat

A chat panel component with message list, streaming text, tool call display, and text input. Uses ACP-aligned types for interoperability with agent protocols.

Terminal

Installation

npx shadcn@latest add https://raw.githubusercontent.com/cjroth/ink-web/main/packages/ink-ui/registry/chat.json
npx shadcn@latest add https://raw.githubusercontent.com/cjroth/ink-web/main/packages/ink-ui/registry/chat.json
pnpm dlx shadcn@latest add https://raw.githubusercontent.com/cjroth/ink-web/main/packages/ink-ui/registry/chat.json
bunx shadcn@latest add https://raw.githubusercontent.com/cjroth/ink-web/main/packages/ink-ui/registry/chat.json

Usage

app/page.tsx
"use client";

import dynamic from "next/dynamic";

const Terminal = dynamic(() => import("./terminal"), {
  ssr: false,
});

export default function Home() {
  return <Terminal />;
}
app/terminal.tsx
"use client";

import { useState } from "react";
import { Box } from "ink";
import { InkXterm } from "ink-web";
import { ChatPanel } from '@/components/ui/chat'
import type { ChatMessage } from '@/components/ui/chat'
import "ink-web/css";
import "@xterm/xterm/css/xterm.css";

export default function Terminal() {
  const [messages, setMessages] = useState<ChatMessage[]>([]);

  const handleSend = (text: string) => {
    setMessages((prev) => [
      ...prev,
      { id: String(Date.now()), role: 'user', content: text },
    ]);
    // Connect to your AI backend here
  };

  return (
    <InkXterm focus>
      <Box flexDirection="column">
        <ChatPanel
          messages={messages}
          onSendMessage={handleSend}
        />
      </Box>
    </InkXterm>
  )
}

ChatPanel Props

PropTypeDefaultDescription
messagesChatMessage[]-Array of chat messages to display
streamingTextstring""Text currently being streamed from assistant
isLoadingbooleanfalseShow loading indicator
activeToolCallsToolCallInfo[][]Active tool calls to display
onSendMessage(text: string) => void-Called when user submits a message
onCancel() => void-Called when user cancels
placeholderstring"Type a message..."Input placeholder text
promptCharstring"> "Prompt character before input
promptColorstring"green"Color of the prompt
userColorstring"green"Color of user message prefix
assistantColorstring"blue"Color of assistant message prefix
loadingTextstring"Thinking..."Text shown while loading

Types

ChatMessage

interface ChatMessage {
  id: string
  role: 'user' | 'assistant'
  content: string
  toolCalls?: ToolCallInfo[]
}

ToolCallInfo

Uses ACP-aligned status states:

interface ToolCallInfo {
  id: string
  title: string
  status: 'pending' | 'in_progress' | 'completed' | 'failed'
  result?: string
}

Examples

Basic Chat

<ChatPanel
  messages={messages}
  onSendMessage={(text) => console.log(text)}
/>

With Streaming

<ChatPanel
  messages={messages}
  streamingText="The assistant is typing..."
  onSendMessage={handleSend}
/>

With Tool Calls

const toolCalls: ToolCallInfo[] = [
  { id: '1', title: 'get_portfolio', status: 'completed' },
  { id: '2', title: 'calculate_trades', status: 'in_progress' },
];

<ChatPanel
  messages={messages}
  activeToolCalls={toolCalls}
  onSendMessage={handleSend}
/>

Inside a Modal

import { ChatPanel } from '@/components/ui/chat'
import { Modal } from '@/components/ui/modal'

<Modal title="AI Chat" onClose={() => setOpen(false)}>
  <ChatPanel
    messages={messages}
    streamingText={streaming}
    isLoading={loading}
    activeToolCalls={toolCalls}
    onSendMessage={handleSend}
  />
</Modal>

Custom Colors

<ChatPanel
  messages={messages}
  onSendMessage={handleSend}
  userColor="cyan"
  assistantColor="magenta"
  promptColor="yellow"
  promptChar="$ "
/>

On this page