

## Overview [#overview]

`Chat.Floating` renders a floating chat interface fixed to the bottom-center of the viewport. It features an animation-driven lifecycle — the shell starts collapsed as a prompt bar, expands on user engagement, reveals chrome (header, message list) after a delay, and collapses back after inactivity.

## Usage [#usage]

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

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

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

## Lifecycle [#lifecycle]

The floating chat manages four distinct phases:

1. **Collapsed** — Only the prompt bar is visible (224px wide). This is the resting state.
2. **Engaged** — User focuses the prompt input. The shell expands to 445px wide.
3. **Expanded with chrome** — Messages exist and the chrome delay timer (150ms) has elapsed. Header and message list become visible, max height grows to 540px.
4. **Expired** — After 2 minutes of inactivity in collapsed state, `onNewConversation` fires to reset the conversation.

```
User focuses prompt → isPromptEngaged = true → width: 445px
  ↓
User sends message → hasMessages = true → chrome delay (150ms) → showChrome = true
  ↓
User clicks X → collapsed = true → expiration timer starts (2 min)
  ↓
Timer expires → onNewConversation()
```

## Sub-components [#sub-components]

### `Chat.Floating.Shell` [#chatfloatingshell]

The animated container. Handles width/height transitions and backdrop blur.

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

### `Chat.Floating.Header` [#chatfloatingheader]

Renders close (X), conversation history dropdown, new chat (+), and view menu for mode switching. Only visible when `showChrome` is true.

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

### `Chat.Floating.Content` [#chatfloatingcontent]

Conditionally renders children based on `showChrome`. Wrap your `Chat.MessageList` and `MessageFeedScrollAnchor` here.

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

### `Chat.Floating.Footer` [#chatfloatingfooter]

Renders the prompt input with engagement-driven expansion.

| 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 (e.g. `ScrollToBottomButton`) |

## Props [#props]

### `FloatingChatProps` [#floatingchatprops]

| Prop                 | Type                                        | Default | Description                                                                                                                                           |
| -------------------- | ------------------------------------------- | ------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- |
| `children`           | `ReactNode`                                 | —       | Shell and sub-components                                                                                                                              |
| `conversationId`     | `string`                                    | —       | Active conversation ID                                                                                                                                |
| `hasMessages`        | `boolean`                                   | —       | Whether the conversation has messages (drives expansion)                                                                                              |
| `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                                                                                                                              |
| `onModeChange`       | `(mode: "sidebar" \| "fullscreen") => void` | —       | Mode change handler (consumer handles layout transitions)                                                                                             |
| `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 (defaults to built-in renderers)                                                                                                |
| `className`          | `string`                                    | —       | Additional CSS classes on the root                                                                                                                    |

## Layout Hook [#layout-hook]

Access floating-specific state from any descendant:

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

// state
state.isCollapsed; // boolean — shell is in collapsed state
state.isExpanded; // boolean — shell is expanded (has messages or prompt engaged)
state.showChrome; // boolean — header and message list are visible
state.isPromptEngaged; // boolean — prompt input is focused

// actions
actions.collapse(); // Collapse the shell
actions.expandToSidebar(); // Trigger expand-to-sidebar callback
actions.setPromptEngaged(v); // Set prompt engagement state
actions.registerBlurHandler(fn); // Register blur handler for cleanup

// meta
meta.variant; // "floating"
```
