# Chat Message Bubble

## Usage

Styled content container for the chat "bubble." Reads sender from parent ChatMessage context to auto-style the background. Use filled for standard messages and ghost when content needs alignment without a visible boundary. Supports name/metadata slots aligned with bubble padding, and multi-bubble grouping via the group prop for consecutive messages from the same sender.

The sender is not a prop: the bubble reads it from the surrounding `ChatMessage`, so it colors and aligns itself. Outside a `ChatMessage` it falls back to the assistant side. A bubble whose only child is a code block, or an element with class `astryx-chat-card`, drops its own padding and background so the card lines up with the bubble edges.

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

<template>
  <ChatMessage sender="user">
    <ChatMessageBubble>
      Can you review the token validation changes?
    </ChatMessageBubble>
  </ChatMessage>
</template>
```

## Examples

Common configurations, variations, and states.

### Custom Content

Custom in-message content aligned to the bubble text column. An artifact card is wrapped in a ghost bubble with width="100%", so its left edge matches the bubble text and it spans the full message column instead of the default bubble width cap; the timestamp rides the bubble metadata slot.

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

<template>
  <ChatMessageList>
    <ChatMessage sender="user">
      <ChatMessageBubble>
        {{ 'Can you pull up the Q3 report?' }}
      </ChatMessageBubble>
    </ChatMessage>
    <ChatMessage sender="assistant">
      <ChatMessageBubble variant="ghost" width="100%" name="Navi">
        {{ 'Sure, here is the artifact from last quarter.' }}
        <div class="chat-preview__card">
          <p class="chat-preview__card-title">
            {{ 'Q3 Performance Report' }}
          </p>
          <p class="chat-preview__card-meta">
            {{ '12 pages · updated 2 days ago' }}
          </p>
        </div>
        <template #metadata>
          <ChatMessageMetadata footer="Claude Opus 4.6" timestamp="9:45 AM" />
        </template>
      </ChatMessageBubble>
    </ChatMessage>
  </ChatMessageList>
</template>
```

### Density

Compact, balanced, and spacious density modes side by side. Density controls bubble padding, corner radius, and spacing between grouped bubbles.

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

const densities: { id: ChatDensity, label: string }[] = [
  { id: 'compact', label: 'Compact' },
  { id: 'balanced', label: 'Balanced' },
  { id: 'spacious', label: 'Spacious' },
]
</script>

<template>
  <div class="chat-preview-densities">
    <template v-for="(density, index) in densities" :key="density.id">
      <Divider v-if="index > 0" />
      <div>
        <p class="chat-preview__caption">
          {{ density.label }}
        </p>
        <ChatMessageList :density="density.id">
          <ChatMessage sender="assistant">
            <ChatMessageBubble>
              {{ 'The build completed in 4.2 seconds.' }}
            </ChatMessageBubble>
          </ChatMessage>
          <ChatMessage sender="user">
            <ChatMessageBubble>
              {{ 'Ship it to staging.' }}
            </ChatMessageBubble>
          </ChatMessage>
        </ChatMessageList>
      </div>
    </template>
  </div>
</template>
```

### Grouping

Multi-bubble messages using first, middle, and last group positions. Grouped bubbles tighten corner radius on the sender side for a continuous visual flow.

```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 group="first" :name="agent">
        {{ 'I reviewed the three files you shared.' }}
      </ChatMessageBubble>
      <ChatMessageBubble group="middle">
        {{ 'The data model looks solid, but the API handler has a race condition on concurrent writes.' }}
      </ChatMessageBubble>
      <ChatMessageBubble group="last">
        {{ 'I can draft a fix if you want.' }}
        <template #metadata>
          <ChatMessageMetadata timestamp="10:45 AM" />
        </template>
      </ChatMessageBubble>
    </ChatMessage>
    <ChatMessage sender="user">
      <ChatMessageBubble group="first">
        {{ 'Yes please!' }}
      </ChatMessageBubble>
      <ChatMessageBubble group="last">
        {{ 'Also add a test for the concurrent case.' }}
        <template #metadata>
          <ChatMessageMetadata status="delivered" timestamp="10:46 AM" />
        </template>
      </ChatMessageBubble>
    </ChatMessage>
  </ChatMessageList>
</template>
```

### Metadata

Bubbles with name and metadata slots aligned to bubble padding. Put name on the first bubble and metadata on the last bubble in a message.

```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">
        {{ 'Your deployment finished successfully. All 14 checks passed.' }}
        <template #metadata>
          <ChatMessageMetadata footer="Claude Opus 4.6" timestamp="9:15 AM" />
        </template>
      </ChatMessageBubble>
    </ChatMessage>
    <ChatMessage sender="user">
      <ChatMessageBubble>
        {{ 'Great, can you send me the production URL?' }}
        <template #metadata>
          <ChatMessageMetadata status="read" timestamp="9:16 AM" />
        </template>
      </ChatMessageBubble>
    </ChatMessage>
  </ChatMessageList>
</template>
```

### Variants

Filled and ghost bubble variants for both user and assistant senders. Use filled for standard messages and ghost when content needs alignment without a visual boundary.

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

<template>
  <ChatMessageList>
    <p class="chat-preview__caption">
      {{ 'Filled — sender-colored background (default)' }}
    </p>
    <ChatMessage sender="user">
      <ChatMessageBubble>
        {{ 'Can you summarize the latest deployment logs?' }}
      </ChatMessageBubble>
    </ChatMessage>
    <p class="chat-preview__caption">
      {{ 'Ghost — transparent background, keeps alignment padding' }}
    </p>
    <ChatMessage sender="assistant">
      <ChatMessageBubble variant="ghost">
        {{ 'The last deploy completed at 2:41 PM with zero errors across all three regions.' }}
      </ChatMessageBubble>
    </ChatMessage>
  </ChatMessageList>
</template>
```

## Props

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `variant` | `'filled' \| 'ghost'` | `'filled'` | `'filled'` paints the sender-colored background. `'ghost'` keeps the padding and drops the background, so mixed content still lines up. A bubble whose only child is a code block, or an element with class `astryx-chat-card`, drops the padding and background so the card lines up with the bubble. |
| `name` | `string` | — | Sender name rendered above the bubble, aligned to the bubble inline padding. Use it on the first bubble of a message; when the first content is raw, use `ChatMessage`'s `name` instead. |
| `group` | `'first' \| 'middle' \| 'last'` | — | Position within a multi-bubble group. Tightens the corner radius on the sender side. Leave it unset for a standalone bubble. |
| `width` | `string` | — | Overrides the default cap of `max(80%, 280px)`. Pass `100%` when in-bubble content should span the message column. Combine it with `variant="ghost"` for artifact cards and attachments. |

## Slots

- `default`: bubble body. Plain text, markdown, or any element. The bubble only supplies the surface.
- `name`: replaces the `name` prop.
- `metadata`: rendered below the bubble, aligned to the bubble padding. Use it on the last bubble of a message; when the last content is raw, use `ChatMessage`'s `metadata` slot instead.

## Exposed

- `element`: the bubble surface element.
