Skip to content

Components

The components exported from autopilot-client/vue are renderless: each one owns the logic (reading from useAutopilotStore and the composables) and exposes its state and actions through a default scoped slot. They render no markup and ship no CSS — you provide the markup, styled however you like.

For a styled, drop-in version of all of these, use @llmor/autopilot-bar; its components are exactly these wrapped with markup + a skin.

ts
import {
  AutopilotOverlay,
  ConversationMessage,
  ConversationMessageAssistant,
  ConfirmationPrompt,
  AskUserPrompt,
} from '@llmor/autopilot-vue';

TIP

These components read from useAutopilotStore. Make sure Pinia is installed and a client has been registered with setAutopilotClient(client) before mounting them.

AutopilotOverlay

The whole chat surface as one renderless component: visibility, messages, connection state, pending prompts, and the message-composer verbs. Build your entire UI from its slot.

Slot propTypeNotes
isOpenbooleanstore.isBarOpen
messagesConversationMessage[]visibleMessages
isStreaming / streamingMessageboolean / ConversationMessage | nullstreaming state
connectionStatus / connectionError / connectionErrorTitleconnection state + friendly heading
isConnected / isConnectingboolean
pendingConfirmations / pendingAskUserarraysfeed into the prompts below
messageText / setMessageText(v)string / (v) => voidinput draft (controlled)
isSending / canSendboolean
sendMessage()() => Promise<boolean>sends the draft
stopStreaming() / resetConversation() / retryConnection()() => Promise<void>
open() / close() / toggle()() => voidvisibility
vue
<AutopilotOverlay v-slot="{ isOpen, messages, messageText, setMessageText, sendMessage }">
  <div v-if="isOpen" class="my-panel">
    <MyMessage v-for="m in messages" :key="m.created_at" :message="m" />
    <form @submit.prevent="sendMessage">
      <input :value="messageText" @input="setMessageText($event.target.value)" />
    </form>
  </div>
</AutopilotOverlay>

ConversationMessage

A single user/system/error message. Props: message: ConversationMessage, isStreaming?: boolean.

Slot props: role, displayRole, content (raw text), html (markdown-rendered), isEmpty, isStreaming.

vue
<ConversationMessage :message="m" v-slot="{ displayRole, html, isEmpty }">
  <div class="bubble">
    <strong>{{ displayRole }}</strong>
    <span v-if="isEmpty">…</span>
    <div v-else v-html="html" />
  </div>
</ConversationMessage>

ConversationMessageAssistant

An assistant message with markdown and function-call disclosure. Props: message, isStreaming?.

Slot props: html, isEmpty, isStreaming, createdAt, functionCalls, isExpanded(id), toggle(id), formatResult(result). Pair with translateFunctionName / getFunctionIcon for friendly labels.

ConfirmationPrompt

Resolves a pending sensitive function confirmation. Prop: pending: PendingConfirmation (from store.pendingConfirmations).

Slot props: pending, functionName, displayName, argumentsJson, allow(), alwaysAllow(), deny().

vue
<ConfirmationPrompt
  v-for="p in store.pendingConfirmations"
  :key="p.id"
  :pending="p"
  v-slot="{ displayName, argumentsJson, allow, deny }"
>
  <div class="confirm">
    <p>Run “{{ displayName }}”?</p>
    <pre>{{ argumentsJson }}</pre>
    <button @click="deny">Deny</button>
    <button @click="allow">Allow</button>
  </div>
</ConfirmationPrompt>

AskUserPrompt

Renders the pending ask-user questions. Prop: prompts: AskUserPrompt[] (from store.pendingAskUser).

Slot props: prompts, active, activeIndex, isLastTab, allAnswered, isSubmitting, the reactive drafts textDraft / boolDraft / radioDraft / checklistDraft (bind with v-model), isAnswered(p), setActiveIndex(i), advance(), send().

vue
<AskUserPrompt :prompts="store.pendingAskUser" v-slot="{ active, textDraft, allAnswered, send }">
  <div>
    <p>{{ active.question }}</p>
    <input v-model="textDraft[active.id]" />
    <button :disabled="!allAnswered" @click="send">Send</button>
  </div>
</AskUserPrompt>

Prefer composables?

Every component above is a thin wrapper over a composable, so you can skip the component and call the logic directly:

  • useMessageComposer() — the input/send/stop/reset/reconnect verbs (behind AutopilotOverlay).
  • useAskUserForm(promptsRef) — tabbed ask-user form state (behind AskUserPrompt).
  • useFunctionCallDetails() — function-call disclosure + result formatting.
  • useConfirmations, useAskUser, useAutopilotContext — the lower-level pieces the store composes.

When to use this vs. enableAutopilotBar

  • Use enableAutopilotBar(client) (bar) when you want a working, styled chat surface and do not need control over markup.
  • Use these renderless components / composables when you want the chat embedded in your own layout and styled with your design system. You provide 100% of the markup; nothing is styled for you.