

## Overview [#overview]

`Chat.Sidebar` renders a fixed-width panel along the right edge of the viewport. Unlike the floating variant, the sidebar has no animation lifecycle — chrome is always visible, the prompt is always expanded, and there are no expiration timers.

The sidebar provides complete programmatic control over its open/closed state through the `useSidebarLayout()` hook, allowing consumers to open, close, or toggle the sidebar without switching layout variants.

## Usage [#usage]

```tsx
import {
  Chat,
  useChatSelection,
  MessageFeedScrollAnchor,
} from "@tilt-legal/cubitt-components/composites";

function ChatSidebar({ messages, onSend, onModeChange }) {
  const { conversationId, handleNew, handleSelect } = useChatSelection();
  const isStreaming = messages.some((msg) => msg.status === "running");

  return (
    <Chat.Sidebar
      conversationId={conversationId}
      isStreaming={isStreaming}
      onCancelStreaming={() => {
        /* cancel stream */
      }}
      onOpen={() => {
        /* sidebar opened */
      }}
      onNewConversation={handleNew}
      onSend={onSend}
    >
      <Chat.Sidebar.Shell>
        <Chat.Sidebar.Header
          conversations={conversationHistory}
          onConversationSelect={handleSelect}
          onNewConversation={handleNew}
          onModeChange={onModeChange}
        />
        <Chat.Sidebar.Content>
          <Chat.MessageList>
            {messages.map((msg) => (
              <MessageRow key={msg.id} messageId={msg.id} />
            ))}
          </Chat.MessageList>
          <MessageFeedScrollAnchor />
        </Chat.Sidebar.Content>
        <Chat.Sidebar.Footer
          files={{ available: myFiles }}
          instructions={{ available: myInstructions }}
        />
      </Chat.Sidebar.Shell>
    </Chat.Sidebar>
  );
}
```

## Sub-components [#sub-components]

### `Chat.Sidebar.Shell` [#chatsidebarshell]

Full-height container with fixed width and background.

| Prop        | Type        | Description             |
| ----------- | ----------- | ----------------------- |
| `className` | `string`    | Additional CSS classes  |
| `children`  | `ReactNode` | Header, Content, Footer |

### `Chat.Sidebar.Header` [#chatsidebarheader]

Renders conversation history dropdown, a labeled "New chat" button, and view menu for mode switching.

| Prop                   | Type                                         | Description                 |
| ---------------------- | -------------------------------------------- | --------------------------- |
| `conversations`        | `ConversationHistoryItem[]`                  | History dropdown items      |
| `onConversationSelect` | `(id: string) => void`                       | Conversation switch handler |
| `onNewConversation`    | `() => void`                                 | New chat handler            |
| `onModeChange`         | `(mode: "floating" \| "fullscreen") => void` | Mode change handler         |
| `children`             | `ReactNode`                                  | Additional header actions   |

### `Chat.Sidebar.Content` [#chatsidebarcontent]

Always-rendered content area. Wrap your `Chat.MessageList` and `MessageFeedScrollAnchor` here.

| Prop        | Type        | Description            |
| ----------- | ----------- | ---------------------- |
| `className` | `string`    | Additional CSS classes |
| `children`  | `ReactNode` | Message list content   |

### `Chat.Sidebar.Footer` [#chatsidebarfooter]

Always-expanded prompt input.

| Prop           | Type                | Description                       |
| -------------- | ------------------- | --------------------------------- |
| `className`    | `string`            | Additional CSS classes            |
| `placeholder`  | `string`            | Prompt placeholder text           |
| `files`        | `FileConfig`        | File picker configuration         |
| `instructions` | `InstructionConfig` | Instructions picker configuration |
| `children`     | `ReactNode`         | Additional footer content         |

## Props [#props]

### `SidebarChatProps` [#sidebarchatprops]

| Prop                 | Type                                         | Default | Description                                                                                                                                           |
| -------------------- | -------------------------------------------- | ------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- |
| `children`           | `ReactNode`                                  | —       | Shell and sub-components                                                                                                                              |
| `conversationId`     | `string`                                     | —       | Active conversation ID                                                                                                                                |
| `onSend`             | `(payload, ctx?) => void`                    | —       | Message submission handler                                                                                                                            |
| `onNewConversation`  | `() => void`                                 | —       | New conversation handler                                                                                                                              |
| `isStreaming`        | `boolean`                                    | `false` | Whether an agent response is in progress                                                                                                              |
| `onCancelStreaming`  | `() => void`                                 | —       | Cancel streaming handler                                                                                                                              |
| `onOpen`             | `() => void`                                 | —       | Open handler (called when sidebar opens programmatically)                                                                                             |
| `onModeChange`       | `(mode: "floating" \| "fullscreen") => void` | —       | Mode change handler (consumer handles layout transitions via header view menu)                                                                        |
| `onOpenInFullscreen` | `(panelId: string) => void`                  | —       | Handler for "open in fullscreen" from a Sheet panel. Receives the active panel ID so you can navigate to the fullscreen variant with that panel open. |
| `registry`           | `ChunkRegistry`                              | auto    | Custom chunk registry                                                                                                                                 |
| `className`          | `string`                                     | —       | Additional CSS classes on the root                                                                                                                    |

## Layout Hook [#layout-hook]

Access sidebar-specific state from any descendant:

```ts
const { state, actions, meta } = useSidebarLayout();

// state
state.isOpen; // boolean — sidebar is open

// actions
actions.open(); // Open the sidebar (triggers onOpen callback)
actions.close(); // Close the sidebar (triggers onClose callback)
actions.toggle(); // Toggle sidebar open/closed state

// meta
meta.variant; // "sidebar"
```

### Programmatic Control Example [#programmatic-control-example]

```tsx
function SidebarControls() {
  const { state, actions } = useSidebarLayout();

  return (
    <div>
      <p>Sidebar is {state.isOpen ? "open" : "closed"}</p>

      <Button onClick={actions.open}>Open Sidebar</Button>
      <Button onClick={actions.close}>Close Sidebar</Button>
      <Button onClick={actions.toggle}>Toggle Sidebar</Button>
    </div>
  );
}
```

## Differences from Floating [#differences-from-floating]

| Behavior              | Floating                         | Sidebar                                |
| --------------------- | -------------------------------- | -------------------------------------- |
| Header visibility     | Engagement-driven (`showChrome`) | Always visible                         |
| Prompt expansion      | Engagement-driven                | Always expanded                        |
| Collapse animation    | Spring transition                | None                                   |
| Expiration timer      | 2 min inactivity → reset         | None                                   |
| Close button behavior | Collapses to prompt bar          | Closes sidebar (stays in sidebar mode) |
| "New chat" button     | Icon only (+)                    | Labeled button ("New chat")            |
