import { Meta } from '@storybook/addon-docs/blocks';

<Meta title="Docs/Frameworks/Vue" />

# Vue

Use the `kc-*` custom elements directly in Vue templates. Pass arrays and objects via the **`.prop`
modifier** so Vue sets them as live DOM properties (not stringified attributes). Events use the
standard `@event` syntax.

There are **two ways to build with the kit**, and you can mix them:

1. **`<kc-chat>`** — the batteries-included shell: a whole chat experience in one tag. Fastest start.
2. **Compose the individual elements** (`<kc-conversations>`, `<kc-markdown>`, `<kc-artifact>`, …)
   into your own layout when you want full control.

Both are shown below.

## Install & setup

```bash
npm i @kitn.ai/chat
```

Register the custom elements **once**, as a static side-effect import in `main.ts` **before**
`createApp().mount()`. Vue stamps the tags at mount time — if the elements aren't registered yet,
array/object props set by Vue are clobbered when the elements upgrade later and the UI renders blank.

```ts
// src/main.ts
import '@kitn.ai/chat/elements'; // MUST come before createApp
import { createApp } from 'vue';
import App from './App.vue';

createApp(App).mount('#app');
```

Tell Vue's template compiler that `kc-*` tags are native custom elements, not Vue components —
this prevents "Unknown custom element" warnings and ensures `.prop` bindings work correctly:

```ts
// vite.config.ts
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';

export default defineConfig({
  plugins: [
    vue({
      template: {
        compilerOptions: {
          isCustomElement: (tag) => tag.startsWith('kc-'),
        },
      },
    }),
  ],
});
```

No CSS to import: each element is styled inside its own Shadow DOM. Only pull in
`@kitn.ai/chat/theme.css` if you want to override design tokens (see **Theming**).

### TypeScript / Volar augmentation

Add a reference to the kit's type declarations once so Vue's template compiler (and Volar) knows
the element's attributes and properties:

```ts
// env.d.ts (or vite-env.d.ts)
/// <reference types="@kitn.ai/chat/elements" />
```

## Quick start — the all-in-one shell

`<kc-chat>` is **transport-agnostic**: give it a `messages` array, handle the `submit` event, and
stream your model's reply back into state. You own the request; the element owns the UI.

```html
<script setup lang="ts">
import '@kitn.ai/chat/elements';
import { ref } from 'vue';

type Message = { id: string; role: 'user' | 'assistant'; content: string };

const messages = ref<Message[]>([
  { id: '1', role: 'assistant', content: 'Hello! How can I help?' },
]);

const handleSubmit = async (e: CustomEvent<{ value: string }>) => {
  const history = [...messages.value, { id: crypto.randomUUID(), role: 'user' as const, content: e.detail.value }];
  messages.value = history;

  const aid = crypto.randomUUID();
  messages.value = [...history, { id: aid, role: 'assistant', content: '' }];

  let answer = '';
  for await (const token of streamFromYourAPI(history)) {
    answer += token;
    messages.value = messages.value.map((m) => (m.id === aid ? { ...m, content: answer } : m));
  }
};
</script>

<template>
  <!-- The elements fill their container — use flex and let the element grow with
       flex: 1 rather than hard-coding a height. -->
  <div style="display: flex; flex-direction: column; height: 100dvh;">
    <kc-chat
      :messages.prop="messages"
      :suggestions.prop="['Summarize the chat', 'Start fresh']"
      style="flex: 1; min-height: 0;"
      @kc-submit="handleSubmit"
    />
  </div>
</template>
```

## Go further — compose the pieces

`<kc-chat>` is one option, not the only one. Every element can be composed in your own layout.
Here's a multi-conversation shell — a `<kc-conversations>` sidebar next to the `<kc-chat>` thread:

```html
<script setup lang="ts">
import '@kitn.ai/chat/elements';
import { ref } from 'vue';

const conversations = ref([
  {
    id: 'c1',
    title: 'First chat',
    scope: { type: 'document' },
    messageCount: 3,
    lastMessageAt: '2026-06-01T12:00:00Z',
    updatedAt: '2026-06-01T12:00:00Z',
  },
]);
const activeId = ref('c1');
const messages = ref([{ id: '1', role: 'assistant', content: 'Hi!' }]);

const onSelect = (e: CustomEvent<{ id: string }>) => {
  activeId.value = e.detail.id;
  // load messages for the selected conversation
};

const onSubmit = (e: CustomEvent<{ value: string }>) => {
  // send message and stream reply
};
</script>

<template>
  <!-- Lay panels out with flex: the sidebar is fixed-width, the thread takes the rest
       with flex: 1. The elements fill whatever box you give them. -->
  <div style="display: flex; height: 100dvh;">
    <kc-conversations
      :conversations.prop="conversations"
      :active-id="activeId"
      style="width: 300px; flex-shrink: 0;"
      @kc-conversation-select="onSelect"
      @kc-new-chat="startNewConversation"
    />
    <kc-chat
      :messages.prop="messages"
      style="flex: 1; min-width: 0;"
      @kc-submit="onSubmit"
    />
  </div>
</template>
```

### Make the panels resizable

Want a draggable divider between the sidebar and the thread? Wrap the panels in `<kc-resizable>`
with one `<kc-resizable-item>` each — the handles are inserted for you (up to 3 panels). Each item
takes a `size` (px or `%`) plus optional `min`/`max`; listen for `@change` (`detail.sizes`) to
persist the layout.

```html
<template>
  <div style="display: flex; flex-direction: column; height: 100dvh;">
    <kc-resizable orientation="horizontal" style="flex: 1; min-height: 0;">
      <kc-resizable-item size="25%" min="200px">
        <kc-conversations
          :conversations.prop="conversations"
          :active-id="activeId"
          @kc-conversation-select="onSelect"
        />
      </kc-resizable-item>
      <kc-resizable-item>
        <kc-chat :messages.prop="messages" @kc-submit="onSubmit" />
      </kc-resizable-item>
    </kc-resizable>
  </div>
</template>
```

You can also drop **standalone display elements** anywhere in your own UI — `<kc-markdown>`,
`<kc-code-block>`, `<kc-artifact>`, `<kc-reasoning>`, `<kc-tool>` — to render rich AI content
without adopting the whole chat. Each fills its container and is controlled via props and events.

> **See it all assembled:** **[Examples → Full Chat App](?path=/story/examples-full-chat-app--default)**
> wires a sidebar, threaded markdown, reasoning, a tool call, a model switcher, a context meter, and
> a rich prompt input into one screen — a working reference to crib from.

> **Find every element:** browse the **Components** section in the sidebar. Each element's **API**
> tab lists its props, events, and copy-paste usage for Vue (and every other framework).

## Props & events

The rule for all elements: **rich data (arrays, objects) goes in as DOM properties; interactions
come out as CustomEvents.**

In Vue templates, use the **`.prop` modifier** to bind rich data as DOM properties rather than
stringified HTML attributes. Scalar values (strings, booleans, numbers) can go through normal
attribute bindings. Events use `@event` and carry their payload in `event.detail`.

| Binding type | Vue syntax | When to use |
|---|---|---|
| Rich data (array/object) | `:messages.prop="messages"` | Always for arrays and objects |
| Scalar attribute | `:active-id="activeId"` or `active-id="c1"` | Strings, booleans, numbers |
| Event | `@kc-conversation-select="onSelect"` | All CustomEvents |

Common events and their `detail` payloads:

| Element | DOM event | `detail` |
|---|---|---|
| `kc-chat` | `submit` | `{ value: string }` |
| `kc-conversations` | `conversationselect` | `{ id: string }` |
| `kc-conversations` | `newchat` | — |
| `kc-conversations` | `togglesidebar` | — |
| `kc-resizable` | `change` | `{ sizes: string[] }` |

Example event handler pattern:

```ts
const onSelect = (e: CustomEvent<{ id: string }>) => {
  activeId.value = e.detail.id;
};
```
