AI Elements

Chat

The thread. Message Scroller keeps your place while the reply streams.

New Thread
Ask the agent to change your code.
What should we build?
The agent can read your code, run commands and make changes. Press send to start.

Chat is the thread of a conversation with the agent. Each turn is a Message inside Message Scroller. The person's message is the scroll anchor, so a new question settles near the top and the reply streams in under it. The field is a Prompt Input.

Installation

Chat is built with the Message Scroller, Message, Bubble, and Input Group components.

Usage

<template>
  <UiMessageScrollerProvider auto-scroll>
    <UiMessageScroller>
      <UiMessageScrollerViewport>
        <UiMessageScrollerContent>
          <UiMessageScrollerItem :message-id="id" scroll-anchor>
            <UiMessage align="end">
              <UiMessageContent>
                <UiBubble align="end" variant="muted">
                  <UiBubbleContent>{{ text }}</UiBubbleContent>
                </UiBubble>
              </UiMessageContent>
            </UiMessage>
          </UiMessageScrollerItem>
        </UiMessageScrollerContent>
      </UiMessageScrollerViewport>
      <UiMessageScrollerButton />
    </UiMessageScroller>
  </UiMessageScrollerProvider>
</template>

scroll-anchor goes on the person's turn. auto-scroll follows the reply while the person stays at the live edge.

See Message Scroller.

AI SDK

Render chat.messages from the AI SDK Chat class. Each message is a UiMessageScrollerItem keyed by its id, and the person's messages are the scroll anchors.

<template>
  <UiMessageScrollerProvider auto-scroll>
    <UiMessageScroller>
      <UiMessageScrollerViewport>
        <UiMessageScrollerContent :aria-busy="chat.status === 'streaming'">
          <UiMessageScrollerItem
            v-for="message in chat.messages"
            :key="message.id"
            :message-id="message.id"
            :scroll-anchor="message.role === 'user'"
          >
            <!-- Render the message -->
          </UiMessageScrollerItem>
        </UiMessageScrollerContent>
      </UiMessageScrollerViewport>
      <UiMessageScrollerButton />
    </UiMessageScroller>
  </UiMessageScrollerProvider>
</template>

<script lang="ts" setup>
  import { Chat } from "@ai-sdk/vue";

  const chat = new Chat({});
</script>

Markdown

The agent's reply streams as markdown. Render it with Comark (@comark/vue) and route fenced code to a Code Block with a custom pre component, the same way as in Message. Pass streaming only to the reply that is still streaming.

<template>
  <UiMessage>
    <UiMessageContent>
      <Markdown
        class="typeset"
        :value="getText(message)"
        :components="{ pre: MarkdownCodeBlock }"
        :streaming="chat.status === 'streaming' && message === chat.messages.at(-1)"
      />
    </UiMessageContent>
  </UiMessage>
</template>

<script lang="ts" setup>
  import { Markdown } from "@comark/vue";

  import MarkdownCodeBlock from "./MarkdownCodeBlock.vue";
</script>