# 对话布局

## 使用方法

`ChatLayout` 是整页对话的外壳。消息按普通文档流从上往下排，输入区用一层毛玻璃压在底部。`density` 控制疏密，默认是 `balanced`。把 `ChatMessageList` 和 `ChatComposer` 套进来，就得到一个带自动滚动和「滚到底」按钮的完整对话界面。

```ts
import { ChatLayout } from '@astryx-vue/core/Chat'
```

### 结构

- **消息区**: 可滚动的消息区域。默认插槽（一般是 ChatMessageList）排成纵向 flex，消息少的时候把内容推到底部。
- **毛玻璃停靠区**: 底部粘住或固定的容器，带一层背景模糊。里面放滚到底按钮和输入区。
- **滚到底按钮**: 用户往上滚、或者有新消息时出现。默认是 ChatLayoutScrollButton；传 null 就隐藏，换成自己的控件也可以。
- **输入区**: 发消息的输入区域，一般是 ChatComposer。停在毛玻璃层里的底部。
- **空状态**: 还没有消息时居中显示的占位。用 EmptyState 可以保持外观一致。

## 建议

| 指引 | 实践 |
| --- | --- |
| 推荐 | 默认插槽放 `ChatMessageList`，`composer` 插槽放 `ChatComposer`，这样就是一套完整的对话界面。 |
| 推荐 | 给 `empty` 插槽一点内容，新对话才不至于是一块白屏。 |
| 推荐 | 对话嵌在由外层负责滚动的页面里时，用 `scrollRef` 指定那个滚动元素。 |
| 避免 | 给布局写死高度。让它用 `flex: 1` 填满外层就好。 |
| 避免 | 在同一个滚动容器里放多个 `ChatLayout`。每个都认为自己独占滚动。 |

## 示例

常见的用法、变化和状态。

### ChatLayout — 面板

窄栏里的侧边对话，容器一窄就自动变成紧凑密度。侧栏、抽屉、嵌在页面里的小对话窗，横向空间紧张时用它。

```vue
<script setup lang="ts">
import { ChatComposer, ChatLayout, ChatMessage, ChatMessageBubble, ChatMessageList, ChatSystemMessage } from '@astryx-vue/core/Chat'
</script>

<template>
  <div class="chat-preview__panel">
    <ChatLayout>
      <ChatMessageList>
        <ChatSystemMessage variant="divider">
          Today
        </ChatSystemMessage>

        <ChatMessage sender="user">
          <ChatMessageBubble>
            Can you review the Button component and fix the focus ring?
          </ChatMessageBubble>
        </ChatMessage>

        <ChatMessage sender="assistant">
          <div class="chat-preview__lines">
            <p>I'll check the Button component now.</p>
            <p>Found the issue — the border radius was hardcoded. Replaced with the theme token.</p>
          </div>
        </ChatMessage>

        <ChatMessage sender="user">
          <ChatMessageBubble>
            Nice, can you also check the Card component?
          </ChatMessageBubble>
        </ChatMessage>

        <ChatMessage sender="assistant">
          <div class="chat-preview__lines">
            <p>Checking the component now.</p>
            <p>Found the issue — the border radius was hardcoded. Replaced with the theme token.</p>
          </div>
        </ChatMessage>
      </ChatMessageList>
      <template #composer>
        <ChatComposer placeholder="Ask something..." />
      </template>
    </ChatLayout>
  </div>
</template>
```

## 属性

| 属性 | 类型 | 默认值 | 说明 |
| --- | --- | --- | --- |
| `density` | `ChatDensity` | `'balanced'` | 疏密、气泡宽度，以及底部那层毛玻璃。`compact`、`balanced`、`spacious`。 |
| `scrollRef` | `HTMLElement \| null` | — | 换一个元素来滚，而不是布局自己滚。不传就自己滚、底部粘住；传了就改成压在目标元素上的固定层。 |
| `showScrollButton` | `boolean` | `true` | 渲染自带的滚到底按钮。想让 `scrollButton` 插槽成为唯一的控件、或者干脆不要按钮，就设为 `false`。 |

## 插槽

| 插槽 | 说明 |
| --- | --- |
| （默认） | 消息记录，一般放一个 `ChatMessageList`。 |
| `composer` | 固定压在底部、毛玻璃之上，一般放一个 `ChatComposer`。 |
| `empty` | 默认插槽为空时显示，居中。放一个 `EmptyState` 外观最统一。 |
| `scrollButton` | 换掉自带的滚到底按钮。插槽参数有 `isVisible`、`label`，以及 `scrollToBottom`——它会清掉新消息提示并滚到底。 |

## 暴露

| 名称 | 类型 | 说明 |
| --- | --- | --- |
| `element` | `HTMLElement \| null` | 布局的最外层。 |
| `scrollToBottom` | `() => void` | 清掉新消息状态并滚到最新一条。 |
