# `PhiaUi.Components.ChatMessage`
[🔗](https://github.com/charlenopires/PhiaUI/blob/v0.1.17/lib/phia_ui/components/display/chat_message.ex#L1)

Chat interface components for PhiaUI.

Provides a complete AI/human chat UI: a scrollable message log, individual
message rows aligned by role, styled content bubbles with optional avatars
and feedback buttons, clickable suggestion chips, and a compose form.

CSS-only layout — no JS hook required. Fully compatible with LiveView
streams for real-time message delivery, including streaming AI token output.

## When to use

- AI assistant chat (GPT-style, Anthropic Claude, etc.)
- Customer support live chat embedded in an app
- Team messaging feed within a product

## Anatomy

| Component           | Element | Purpose                                        |
|---------------------|---------|------------------------------------------------|
| `chat_container/1`  | `div`   | Scrollable log (`role="log"`, live region)     |
| `chat_message/1`    | `div`   | A message row, aligned by `:role`              |
| `chat_bubble/1`     | `div`   | Styled content balloon with optional avatar    |
| `chat_suggestions/1`| `div`   | Row of suggestion chips below an AI message    |
| `chat_input/1`      | `form`  | Bottom compose form with `phx-submit`          |

## Role alignment

| Role        | Alignment | Background            |
|-------------|-----------|------------------------|
| `user`      | Right      | `bg-primary`          |
| `assistant` | Left       | `bg-muted`            |
| `system`    | Center     | transparent, italic   |

## Minimal chat example

    <.chat_container id="ai-chat" class="h-96 p-4">
      <.chat_message role="assistant">
        <.chat_bubble role="assistant" timestamp="2:30 PM">
          Welcome! How can I help you today?
        </.chat_bubble>
        <.chat_suggestions
          suggestions={["Show me an example", "What can you do?"]}
          on_select="select_suggestion"
        />
      </.chat_message>

      <.chat_message role="user">
        <.chat_bubble role="user" timestamp="2:31 PM">
          Show me an example
        </.chat_bubble>
      </.chat_message>
    </.chat_container>

    <.chat_input id="chat-compose" on_submit="send_message" placeholder="Ask anything..." />

## Full AI assistant with LiveView streams

Using streams means only new messages are DOM-patched; the full message list
is never re-rendered, which is critical for long conversations.

    # LiveView mount/3 — initialise the stream
    def mount(_params, _session, socket) do
      {:ok, stream(socket, :messages, initial_messages())}
    end

    # Push a new AI message (e.g. from a Task or GenServer)
    def handle_info({:ai_message, msg}, socket) do
      {:noreply, stream_insert(socket, :messages, msg)}
    end

    # Template
    <.chat_container id="ai-chat" class="flex-1 overflow-y-auto p-4">
      <.chat_message
        :for={{dom_id, msg} <- @streams.messages}
        id={dom_id}
        role={msg.role}
      >
        <.chat_bubble
          role={msg.role}
          timestamp={Calendar.strftime(msg.inserted_at, "%I:%M %p")}
          on_feedback={if msg.role == "assistant", do: "rate_message"}
          message_id={msg.id}
        >
          <:avatar :if={msg.role == "assistant"}>
            <.avatar size="sm">
              <.avatar_fallback name="AI" />
            </.avatar>
          </:avatar>
          {msg.content}
        </.chat_bubble>
      </.chat_message>
    </.chat_container>

    <.chat_input
      id="chat-compose"
      on_submit="send_message"
      placeholder="Type a message..."
      max_chars={2000}
    />

## Streaming AI token output

For token-by-token streaming responses, `stream_insert/4` with `at: -1`
updates the last message in-place via LiveView's morphdom patching:

    def handle_info({:token, token}, socket) do
      # Append the token to the last AI message in your stream
      {:noreply, stream_insert(socket, :messages, updated_message, at: -1)}
    end

# `chat_bubble`

Renders the styled chat content balloon.

- `role="user"` — `bg-primary text-primary-foreground` (right-aligned)
- `role="assistant"` — `bg-muted text-foreground` (left-aligned)
- `role="system"` — transparent, italic, centered small text

Supply an `:avatar` slot to show a user or AI avatar beside the bubble.
Set `on_feedback` + `message_id` to enable thumbs up/down rating buttons
for assistant responses (common in AI assistant UIs for RLHF collection).

## Example — assistant bubble with avatar and feedback

    <.chat_bubble
      role="assistant"
      timestamp="2:34 PM"
      on_feedback="rate_message"
      message_id={msg.id}
    >
      <:avatar>
        <.avatar size="sm"><.avatar_fallback name="AI" /></.avatar>
      </:avatar>
      The capital of France is Paris.
    </.chat_bubble>

## Attributes

* `role` (`:string`) (required) - Bubble role — controls background colour and text colour. Must be one of `"user"`, `"assistant"`, or `"system"`.
* `timestamp` (`:string`) - Optional time label rendered beneath the bubble, e.g. "2:34 PM". Defaults to `nil`.
* `on_feedback` (`:string`) - `phx-click` event name for thumbs up/down feedback buttons.
  Only rendered for `role="assistant"` bubbles.
  Pair with `:message_id` to identify which message received feedback.

  Defaults to `nil`.
* `message_id` (`:string`) - ID passed as `phx-value-message-id` to feedback buttons.
  The LiveView receives `%{"message_id" => id, "feedback" => "up" | "down"}`.

  Defaults to `nil`.
* `class` (`:string`) - Additional CSS classes for the bubble wrapper. Defaults to `nil`.
* Global attributes are accepted. HTML attributes forwarded to the bubble wrapper div.
## Slots

* `avatar` - Optional `avatar/1` component displayed beside the bubble.
  For `role="assistant"` the avatar appears on the left;
  for `role="user"` it appears on the right (matching message alignment).

* `inner_block` (required) - Bubble text content. May include inline HTML or Markdown-rendered content.
  For streaming AI output, update this via `stream_insert/4`.

# `chat_container`

Renders the chat log scroll container.

Sets `role="log"` and `aria-live="polite"` so screen readers announce new
messages as they arrive via LiveView streams without interrupting the user's
current focus or reading flow.

Apply a fixed height and `overflow-y-auto` via `:class` to make the log
scrollable within a fixed layout region:

    <.chat_container id="chat" class="h-[600px] overflow-y-auto p-4">
      ...
    </.chat_container>

## Attributes

* `id` (`:string`) - DOM id for the container. Recommended when using LiveView streams so the
  engine can locate and patch individual messages efficiently.

  Defaults to `nil`.
* `class` (`:string`) - Additional CSS classes for the scroll container (e.g. `h-96 overflow-y-auto`). Defaults to `nil`.
* Global attributes are accepted. HTML attributes forwarded to the root div.
## Slots

* `inner_block` (required) - `chat_message/1` children.

# `chat_input`

Renders the chat compose form.

The form fires `on_submit` via `phx-submit`. A `max_chars` counter is
displayed when provided. Attachment chips (e.g. file uploads) can be rendered
above the textarea via the `:attachments` slot.

## Example

    <.chat_input
      id="chat-compose"
      on_submit="send_message"
      placeholder="Ask me anything..."
      max_chars={4000}
      phx-change="typing"
    >
      <:attachments>
        <.badge :for={file <- @pending_attachments} variant="outline">
          <.icon name="paperclip" size={:xs} />
          {file.name}
        </.badge>
      </:attachments>
    </.chat_input>

## Attributes

* `id` (`:string`) (required) - DOM id for the form element.
* `on_submit` (`:string`) (required) - `phx-submit` event name. The LiveView receives `%{"message" => text}`.
  After handling, reset the textarea with `Phoenix.LiveView.push_event/3`
  or by clearing the assign that controls the input value.

* `placeholder` (`:string`) - Textarea placeholder text. Defaults to `"Type a message..."`.
* `max_chars` (`:integer`) - Optional character limit displayed as a static counter below the textarea.
  Note: actual enforcement must be done server-side in the LiveView handler.

  Defaults to `nil`.
* `class` (`:string`) - Additional CSS classes for the form wrapper. Defaults to `nil`.
* Global attributes are accepted. HTML attributes forwarded to the form element. Supports all globals plus: `["phx-change"]`.
## Slots

* `attachments` - Optional attachment chips rendered above the textarea.
  Use `badge/1` or custom chips to show files the user has attached.

# `chat_message`

Renders a message row with role-based alignment.

Alignment is determined by `:role`:
- `user` → `items-end` (right side, mirroring human side of a conversation)
- `assistant` → `items-start` (left side, mirroring AI/agent side)
- `system` → `items-center` (centered, for notices like "Session started")

## Attributes

* `role` (`:string`) (required) - Message role — controls horizontal alignment:
  - `user`      → right-aligned (`items-end`)
  - `assistant` → left-aligned (`items-start`)
  - `system`    → centered (`items-center`)
 Must be one of `"user"`, `"assistant"`, or `"system"`.
* `id` (`:string`) - DOM id — required when using LiveView streams (pass `dom_id` from stream tuple). Defaults to `nil`.
* `class` (`:string`) - Additional CSS classes for the message wrapper. Defaults to `nil`.
* Global attributes are accepted. HTML attributes forwarded to the message wrapper div.
## Slots

* `inner_block` (required) - `chat_bubble/1` and/or `chat_suggestions/1` children.

# `chat_suggestions`

Renders a row of clickable conversation suggestion chips.

Each chip fires `on_select` with `phx-value-suggestion` set to the chip text.
Render these below an assistant bubble to help users discover common queries.

## Example

    <.chat_suggestions
      suggestions={["Tell me more", "Show an example", "How does billing work?"]}
      on_select="select_suggestion"
    />

    # LiveView handler
    def handle_event("select_suggestion", %{"suggestion" => text}, socket) do
      # Treat the suggestion as if the user typed and sent it
      {:noreply, send_user_message(socket, text)}
    end

## Attributes

* `suggestions` (`:list`) (required) - List of suggestion strings shown as clickable pill buttons below an
  assistant bubble. Typically 2–4 concise prompts that guide the user.

* `on_select` (`:string`) (required) - `phx-click` event emitted when a suggestion is selected.
  The LiveView receives `%{"suggestion" => text}`.

* `class` (`:string`) - Additional CSS classes for the suggestions row. Defaults to `nil`.
* Global attributes are accepted. HTML attributes forwarded to the wrapper div.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
