

## Overview [#overview]

`Chat.Fullscreen` occupies the full available space and adds a **resizable panel system** for side-by-side content (documents, references) and a **collapsible conversation nav** sidebar with built-in toggle controls. Panels also work in floating and sidebar variants via a Sheet overlay — see [`usePanelSlot`](#usepanelslot).

## Usage [#usage]

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

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

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

## Conversation Nav [#conversation-nav]

The nav sidebar has two states controlled by a built-in toggle button:

* **Expanded** — Full sidebar (`w-64`) with toggle button, "Conversations" heading, new chat button, and conversation list.
* **Collapsed** — Narrow icon column with just the toggle and new chat buttons (with tooltips).

The toggle and new chat buttons are always visible regardless of collapsed state.

```tsx
<Chat.Fullscreen.Nav
  conversations={conversationHistory}
  onConversationSelect={handleSelect}
  onNewConversation={handleNew}
/>
```

Place the `Nav` as a direct child of `Chat.Fullscreen`, before the `Shell`.

## Panel System [#panel-system]

The fullscreen variant uses `ResizablePanelGroup` under the hood. Panels are **co-located with their chunk registrations** — every panel is an extended view of a chunk. When a chunk is registered with a `panel` option via `registerChunk`, the panel is automatically available in the fullscreen variant. See the [Chunk Registry](./custom-chunks) for the full registration pattern.

### How It Works [#how-it-works]

1. A chunk is registered with a `panel` option via `registerChunk`
2. The fullscreen variant reads panels from the registry automatically
3. Chunk renderers use `usePanelSlot` to open/close their panel
4. Only one panel can be open at a time — opening a new panel closes the current one

The built-in `artifact` chunk includes a panel that renders documents. When an artifact chunk is expanded, the document panel opens automatically.

### `PanelProps` [#panelprops]

Every panel component receives these props automatically:

| Field      | Type                    | Description                                                                                          |
| ---------- | ----------------------- | ---------------------------------------------------------------------------------------------------- |
| `panelId`  | `string`                | The panel's unique identifier                                                                        |
| `onClose`  | `() => void`            | Closes this panel. Use to render a close button in the panel header.                                 |
| `mode`     | `"inline" \| "overlay"` | `"inline"` in fullscreen (resizable side panel), `"overlay"` in floating/sidebar (Sheet).            |
| `onExpand` | `() => void`            | Navigate to fullscreen view. Only set when `mode` is `"overlay"` and an expand handler is available. |

## Sub-components [#sub-components]

### `Chat.Fullscreen.Nav` [#chatfullscreennav]

Collapsible left sidebar with conversation history and built-in toggle/new chat controls.

| Prop                   | Type                        | Description                                |
| ---------------------- | --------------------------- | ------------------------------------------ |
| `conversations`        | `ConversationHistoryItem[]` | History list items                         |
| `onConversationSelect` | `(id: string) => void`      | Conversation switch handler                |
| `onNewConversation`    | `() => void`                | New chat handler                           |
| `className`            | `string`                    | Additional CSS classes                     |
| `children`             | `ReactNode`                 | Additional content above conversation list |

### `Chat.Fullscreen.Shell` [#chatfullscreenshell]

Container with `ResizablePanelGroup` for the horizontal chat + panels layout. Panels are automatically derived from the chunk registry.

| Prop        | Type        | Description               |
| ----------- | ----------- | ------------------------- |
| `className` | `string`    | Additional CSS classes    |
| `children`  | `ReactNode` | MessageList, Footer, etc. |

### `Chat.Fullscreen.MessageList` [#chatfullscreenmessagelist]

Scroll container with stick-to-bottom behavior. Content is centered with a max-width for readability while the scrollbar stays at the panel edge.

| Prop        | Type        | Description                                    |
| ----------- | ----------- | ---------------------------------------------- |
| `className` | `string`    | Additional CSS classes on the scroll container |
| `children`  | `ReactNode` | Message row components                         |

### `Chat.Fullscreen.Footer` [#chatfullscreenfooter]

Always-expanded prompt input with centered content.

| 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]

### `FullscreenChatProps` [#fullscreenchatprops]

| Prop                         | Type                                 | Default | Description                                                     |
| ---------------------------- | ------------------------------------ | ------- | --------------------------------------------------------------- |
| `children`                   | `ReactNode`                          | —       | Nav, 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                                        |
| `defaultActivePanelIds`      | `string[]`                           | `[]`    | Initially active panel IDs                                      |
| `defaultConversationNavOpen` | `boolean`                            | `true`  | Whether the conversation nav is initially open                  |
| `onPanelChange`              | `(activePanelIds: string[]) => void` | —       | Fires when panels open or close. Use for URL/state persistence. |
| `registry`                   | `ChunkRegistry`                      | auto    | Custom chunk registry                                           |
| `className`                  | `string`                             | —       | Additional CSS classes on the root                              |

## Layout Hook [#layout-hook]

Access fullscreen-specific state from any descendant:

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

// state
state.panels; // Panel descriptors derived from chunk registry
state.activePanelIds; // string[] — currently open panel IDs
state.conversationNavOpen; // boolean — nav sidebar is open

// actions
actions.openPanel(id); // Open a panel (closes any currently open panel)
actions.closePanel(id); // Close a specific panel
actions.togglePanel(id); // Toggle a panel open/closed
actions.toggleConversationNav(); // Toggle the nav sidebar

// meta
meta.variant; // "fullscreen"
```

## `usePanelSlot` [#usepanelslot]

A hook that lets any component (including custom chunk renderers) interact with the panel system without importing variant-specific internals. It works across all three variants:

* **Fullscreen** — panels render as resizable side panels
* **Floating / Sidebar** — panels open in a Sheet overlay

```ts
import { usePanelSlot } from "@tilt-legal/cubitt-components/composites";

const panel = usePanelSlot("my-panel");

panel.isAvailable; // true in all variants (fullscreen, floating, sidebar)
panel.isOpen; // whether the panel is currently active
panel.mode; // "inline" (fullscreen) or "overlay" (floating/sidebar)
panel.open(); // open the panel
panel.close(); // close the panel
panel.toggle(); // toggle the panel
```

When called outside of any chat variant, the hook returns a safe noop object with `isAvailable: false` — no errors, no conditional logic needed.

### `PanelSlot` [#panelslot]

| Field         | Type                    | Description                                                                                                                         |
| ------------- | ----------------------- | ----------------------------------------------------------------------------------------------------------------------------------- |
| `isAvailable` | `boolean`               | `true` when inside any chat variant with a panel provider                                                                           |
| `isOpen`      | `boolean`               | Whether this panel is currently active                                                                                              |
| `open`        | `() => void`            | Open this panel                                                                                                                     |
| `close`       | `() => void`            | Close this panel                                                                                                                    |
| `toggle`      | `() => void`            | Toggle this panel open/closed                                                                                                       |
| `mode`        | `"inline" \| "overlay"` | `"inline"` in fullscreen (side-by-side), `"overlay"` in floating/sidebar (Sheet). Defaults to `"overlay"` when outside any variant. |

## Layout Structure [#layout-structure]

```
Chat.Fullscreen
  └── ChatProvider (owns chunk registry)
      └── FullscreenLayoutProvider (panels derived from registry)
          └── PanelSlotContext + ArtifactProvider
              └── Root (flex)
                  ├── FullscreenNav (collapsible sidebar with toggle)
                  └── FullscreenShell
                      └── ResizablePanelGroup (horizontal)
                          ├── ResizablePanel — conversation (messages + prompt)
                          ├── ResizableHandle
                          └── ResizablePanel — active panel (if any)
```

Only one panel can be active at a time. Panels are derived from chunk registrations — each chunk registered with a `panel` option contributes a panel descriptor. Only the active panel is mounted. The conversation panel content is centered with a max-width for readability.
