# 对话消息

## 使用方法

`ChatMessage` 是一条消息的外壳，负责按发送者角色摆好头像、名字、元信息和对齐方向。

`sender` 必填。它决定这条消息靠哪边，也决定头像、名字、元信息三个插槽要不要渲染：`system` 行居中显示，这三样都不出。密度默认跟着外层 `ChatMessageList` 走，单条消息也可以自己指定。

名字和元信息这两个插槽是给「正文不是气泡」的情况用的。正文第一条是 `ChatMessageBubble` 时，把名字放到气泡上，名字才会跟气泡的内边距对齐；元信息放在最后一条气泡上，道理一样。

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

<template>
  <ChatMessageList>
    <ChatMessage sender="user">
      <ChatMessageBubble>
        Can you review the token validation changes?
      </ChatMessageBubble>
    </ChatMessage>
    <ChatMessage sender="assistant">
      <ChatMessageBubble variant="ghost">
        Looks good — ship it.
      </ChatMessageBubble>
    </ChatMessage>
  </ChatMessageList>
</template>
```

## 示例

常见的用法和不同的状态。

### 头像与名字

带头像和发送者名字的消息。正文用气泡时名字写在气泡上；正文是自由内容时名字写在消息外层。

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

const agent = 'Agent'
</script>

<template>
  <ChatMessageList>
    <ChatMessage sender="assistant">
      <template #avatar>
        <Avatar :name="agent" size="md" />
      </template>
      <ChatMessageBubble :name="agent">
        {{ 'I reviewed the pull request. The changes look solid — clean code and good test coverage.' }}
        <template #metadata>
          <ChatMessageMetadata timestamp="10:15 AM" />
        </template>
      </ChatMessageBubble>
    </ChatMessage>
    <ChatMessage sender="user">
      <ChatMessageBubble>
        {{ 'Thanks! Merging it now.' }}
        <template #metadata>
          <ChatMessageMetadata status="read" timestamp="10:16 AM" />
        </template>
      </ChatMessageBubble>
    </ChatMessage>
    <ChatMessage sender="assistant">
      <template #avatar>
        <Avatar :name="agent" size="md" />
      </template>
      <ChatMessageBubble :name="agent">
        {{ 'I can run the deployment pipeline once it lands. Just let me know.' }}
        <template #metadata>
          <ChatMessageMetadata timestamp="10:17 AM" />
        </template>
      </ChatMessageBubble>
    </ChatMessage>
  </ChatMessageList>
</template>
```

### 透明气泡

没有可见气泡边界的透明样式。内边距还留着，所以还能对齐，只是背景透明，适合助手的长回复。

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

<template>
  <ChatMessageList>
    <ChatMessage sender="assistant">
      <ChatMessageBubble variant="ghost">
        {{ 'Here is an analysis of your production metrics from last week. Traffic peaked at 12,400 requests per second on Wednesday, with a p99 latency of 45ms. Error rate stayed below 0.1% across all endpoints.' }}
        <template #metadata>
          <ChatMessageMetadata footer="Claude Opus 4.6" timestamp="9:45 AM" />
        </template>
      </ChatMessageBubble>
    </ChatMessage>
    <ChatMessage sender="user">
      <ChatMessageBubble>
        {{ 'That looks great. Can you compare it to the week before?' }}
      </ChatMessageBubble>
    </ChatMessage>
    <ChatMessage sender="assistant">
      <ChatMessageBubble variant="ghost">
        {{ 'Compared to the previous week, traffic is up 8% and latency improved by 3ms. The deployment on Tuesday seems to have helped.' }}
        <template #metadata>
          <ChatMessageMetadata footer="Claude Opus 4.6" timestamp="9:46 AM" />
        </template>
      </ChatMessageBubble>
    </ChatMessage>
  </ChatMessageList>
</template>
```

### 多条气泡

用 group 属性收圆角，把同一个人的几条气泡连成一段。first、middle、last 依次写，就能看出是一口气说的。

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

const agent = 'Agent'
</script>

<template>
  <ChatMessageList>
    <ChatMessage sender="user">
      <ChatMessageBubble group="first">
        {{ 'I have a couple of questions about the new API.' }}
      </ChatMessageBubble>
      <ChatMessageBubble group="middle">
        {{ 'First, how should we handle pagination?' }}
      </ChatMessageBubble>
      <ChatMessageBubble group="last">
        {{ 'And second, what\'s the rate limit?' }}
        <template #metadata>
          <ChatMessageMetadata status="delivered" timestamp="11:00 AM" />
        </template>
      </ChatMessageBubble>
    </ChatMessage>
    <ChatMessage sender="assistant">
      <template #avatar>
        <Avatar :name="agent" size="md" />
      </template>
      <ChatMessageBubble group="first" :name="agent">
        {{ 'Great questions! For pagination, use cursor-based with a limit parameter. The response includes a nextCursor field.' }}
      </ChatMessageBubble>
      <ChatMessageBubble group="last">
        {{ 'Rate limit is 100 requests per minute per API key. You\'ll get a 429 response with a Retry-After header if you exceed it.' }}
        <template #metadata>
          <ChatMessageMetadata timestamp="11:01 AM" />
        </template>
      </ChatMessageBubble>
    </ChatMessage>
  </ChatMessageList>
</template>
```

## 属性

| 属性 | 类型 | 默认值 | 说明 |
| --- | --- | --- | --- |
| `sender` | `'user' \| 'assistant' \| 'system'` | — | 这条消息是谁发的，必填。决定靠左还是靠右，也决定排版。写 `system` 时整行居中，头像、名字、元信息三个插槽都不显示。 |
| `name` | `string` | — | 正文上方的发送者名字，纯文本。正文第一条不是气泡时用它；如果第一条是气泡，名字写在气泡上，才能跟气泡的内边距对齐。 |
| `density` | `ChatDensity` | 跟列表，再退回 `'balanced'` | 疏密程度：`'compact'`、`'balanced'` 或 `'spacious'`。不写就跟着外层 `ChatMessageList`。 |

## 插槽

- `default`：消息正文，可以放气泡、工具调用、思考过程，或者一段自由内容。自由内容和消息边缘齐平；想让它跟气泡的文字列对齐，就套一个透明气泡。
- `avatar`：消息旁边的头像，一般用 `Avatar`。`sender` 是 `system` 时不显示。
- `name`：替代 `name` 属性。
- `metadata`：正文下方的元信息。最后一条不是气泡时用它；如果最后一条是气泡，改用气泡的 `metadata` 插槽。

## 暴露

- `element`：根元素 `<article>`。
