# Chat Workspace

> Messenger-style workspace for agents and people: ChatWorkspace (list | thread | details), ChatList, SearchHits, ThinkingLine, FeedbackRating.

Component · AI · Pro kit · [Live page](https://ui.twentyfirst.media/pro/components/ChatWorkspace/)

Messenger-style workspace for agents and people: `ChatWorkspace` (list | thread | details), `ChatList`, `SearchHits`, `ThinkingLine`, `FeedbackRating`.

ChatWorkspace: `list`, `thread`, `details` (third column, hidden on narrow screens).
ChatList: `groups` ({title, mark (tint), items: {id, name, sub, time, tint, avatar, status, unread, pinned}}), `selected`, `onSelect`, `onSearch`, `filters`, `searchPlaceholder`. Order: pinned first, then one group per project, then people.
SearchHits: `items` ({id, chat, time, who, text}), `query` (marked), `onOpen`.
ThinkingLine: `text` ("Reading 3 mails…"). FeedbackRating: `value` / `onRate`, `onSubmit(value, comment)`, `label`, `placeholder`, `thanks`, `askOnUp`.

## Example

```tsx
import * as React from "react";
import {
  ChatList,
  ChatMessage,
  ChatWorkspace,
  FeedbackRating,
  SearchHits,
  ThinkingLine,
} from "@twentyfirstmedia/pro";
import type { ChatListProps } from "@twentyfirstmedia/pro";

const groups: ChatListProps["groups"] = [
  {
    title: "Pinned",
    items: [
      {
        id: "mb",
        name: "Main Brain",
        sub: "Morning briefing is ready",
        time: "08:00",
        tint: "lilac",
        status: "ok",
        pinned: true,
      },
    ],
  },
  {
    title: "Northwind",
    mark: "sky",
    items: [
      {
        id: "atlas",
        name: "Atlas",
        sub: "Waiting for your approval",
        time: "09:41",
        tint: "sky",
        status: "signal",
        unread: 1,
      },
      {
        id: "ads",
        name: "Ads",
        sub: "Budget at 82%",
        time: "09:12",
        tint: "mint",
        status: "warn",
      },
    ],
  },
  {
    title: "People",
    items: [
      {
        id: "lukas",
        name: "Lukas Weber",
        sub: "Sounds good",
        time: "Mon",
        avatar: true,
      },
    ],
  },
];
function App() {
  const s = React.useState("atlas");
  return (
    <div
      style={{
        height: 580,
      }}
    >
      <ChatWorkspace
        className="demo-fill"
        list={<ChatList groups={groups} selected={s[0]} onSelect={s[1]} />}
        thread={
          <div
            className="tf-col"
            style={{
              gap: 20,
            }}
          >
            <ChatMessage role="user" text="Draft a reply to Northwind" />
            <ChatMessage
              role="assistant"
              name="Atlas"
              tint="sky"
              time="09:41"
              text="Drafted. It quotes **40 seats at €24** prorated to May 1."
            />
            <ThinkingLine text="Reading 3 messages…" />
            <FeedbackRating label="Was this useful?" />
          </div>
        }
        details={
          <SearchHits
            query="seats"
            items={[
              {
                id: 1,
                chat: "Atlas",
                time: "09:41",
                who: "Atlas",
                text: "It quotes 40 seats at €24 prorated to May 1.",
              },
              {
                id: 2,
                chat: "Main Brain",
                time: "Mon",
                text: "Northwind asked about seats twice this quarter.",
              },
            ]}
          />
        }
      />
    </div>
  );
}
export default App;
```

## Props

### ChatList

| Prop | Type | Notes |
| --- | --- | --- |
| `groups` | `{ title: string; mark?: Tint; items: ChatListItem[] }[]` | Required |
| `selected?` | `string` |  |
| `onSelect?` | `(id: string) => void` |  |
| `onSearch?` | `(q: string) => void` |  |
| `filters?` | `ReactNode` |  |
| `searchPlaceholder?` | `string` |  |
| `className?` | `string` | Extra class on the root element. |
| `style?` | `CSSProperties` | Inline style on the root element. |

### ChatMessage

| Prop | Type | Notes |
| --- | --- | --- |
| `role` | `"user" \| "assistant" \| "system"` | Required |
| `name?` | `string` |  |
| `time?` | `string` |  |
| `model?` | `string` |  |
| `text?` | `string` |  |
| `streaming?` | `boolean` |  |
| `tint?` | `Tint` |  |
| `sources?` | `Source[]` |  |
| `attachments?` | `{ name: string; size?: string; kind?: string }[]` |  |
| `usage?` | `string` |  |
| `onRetry?` | `() => void` |  |
| `onFeedback?` | `(v: "up" \| "down") => void` |  |
| `actions?` | `boolean` |  |
| `before?` | `ReactNode` |  |
| `after?` | `ReactNode` |  |
| `children?` | `ReactNode` |  |
| `feedback?` | `"up" \| "down" \| null` |  |
| `className?` | `string` | Extra class on the root element. |
| `style?` | `CSSProperties` | Inline style on the root element. |

### ChatWorkspace

| Prop | Type | Notes |
| --- | --- | --- |
| `list` | `ReactNode` | Required |
| `thread` | `ReactNode` | Required |
| `details?` | `ReactNode` |  |
| `className?` | `string` |  |
| `style?` | `CSSProperties` | Inline style on the root element. |

### FeedbackRating

| Prop | Type | Notes |
| --- | --- | --- |
| `value?` | `"up" \| "down" \| null` |  |
| `onRate?` | `(v: "up" \| "down") => void` |  |
| `onSubmit?` | `(v: "up" \| "down", comment?: string) => void` |  |
| `label?` | `string` |  |
| `placeholder?` | `string` |  |
| `thanks?` | `string` |  |
| `askOnUp?` | `boolean` |  |
| `className?` | `string` | Extra class on the root element. |
| `style?` | `CSSProperties` | Inline style on the root element. |

### SearchHits

| Prop | Type | Notes |
| --- | --- | --- |
| `items` | `{ id: string \| number; chat: string; time?: string; who?: string; text: string }[]` | Required |
| `query?` | `string` |  |
| `onOpen?` | `(item: NonNullable<SearchHitsProps["items"]>[number]) => void` |  |
| `emptyText?` | `string` |  |
| `className?` | `string` | Extra class on the root element. |
| `style?` | `CSSProperties` | Inline style on the root element. |

### ThinkingLine

| Prop | Type | Notes |
| --- | --- | --- |
| `text?` | `string` |  |
| `className?` | `string` | Extra class on the root element. |
| `style?` | `CSSProperties` | Inline style on the root element. |
