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

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

# Angular

Angular needs **no wrappers** — its property binding (`[prop]="value"`) assigns the DOM **property**
directly, so arrays and objects pass through unstringified. Register the elements once, allow the
custom tags with `CUSTOM_ELEMENTS_SCHEMA`, and handle events with `(event)="handler($event)"`.

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**
`bootstrapApplication(...)`. Then add `CUSTOM_ELEMENTS_SCHEMA` to each standalone component that
uses `<kc-*>` tags:

```ts
// main.ts — import BEFORE bootstrapApplication so elements are defined first
import '@kitn.ai/chat/elements';
import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app/app.component';
import { appConfig } from './app/app.config';

bootstrapApplication(AppComponent, appConfig);
```

```ts
// app.component.ts
import { Component, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';

@Component({
  selector: 'app-root',
  standalone: true,
  templateUrl: './app.component.html',
  schemas: [CUSTOM_ELEMENTS_SCHEMA], // required: allows <kc-*> tags in the template
})
export class AppComponent { /* … */ }
```

- **`@kitn.ai/chat/elements`** — the side-effect import that registers every `<kc-*>` element
  globally. Import it once, in `main.ts`.
- **`CUSTOM_ELEMENTS_SCHEMA`** — tells the Angular compiler to accept unknown element names (the
  `<kc-*>` tags). Add it to every standalone component whose template uses them.
- 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**).

## 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 component owns the UI.

```ts
// app.component.ts
import { Component, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';

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

@Component({
  selector: 'app-root',
  standalone: true,
  templateUrl: './app.component.html',
  schemas: [CUSTOM_ELEMENTS_SCHEMA],
})
export class AppComponent {
  messages: Message[] = [
    { id: '1', role: 'assistant', content: 'Hello! How can I help?' },
  ];

  async onSubmit(e: Event) {
    const { value } = (e as CustomEvent<{ value: string }>).detail;
    // Reassign a NEW array so Angular's change detection picks it up.
    const history = [...this.messages, { id: crypto.randomUUID(), role: 'user' as const, content: value }];
    this.messages = history;

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

    let answer = '';
    for await (const token of streamFromYourAPI(history)) {
      answer += token;
      // Reassign a new array — and a new object for the message being streamed.
      this.messages = this.messages.map((m) =>
        m.id === aid ? { ...m, content: answer } : m
      );
    }
  }
}
```

```html
<!-- app.component.html -->
<!-- The elements are display:block and fill their container — lay them out with 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]="messages"
    (kc-submit)="onSubmit($event)"
    style="flex: 1; min-height: 0"
  ></kc-chat>
</div>
```

## Go further — compose the pieces

`<kc-chat>` is one option, not the only one. You can assemble your own layout from individual
elements. Here is a multi-conversation shell — a `<kc-conversations>` sidebar next to the
`<kc-chat>` thread:

```ts
// workspace.component.ts
import { Component, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';

@Component({
  selector: 'app-workspace',
  standalone: true,
  templateUrl: './workspace.component.html',
  schemas: [CUSTOM_ELEMENTS_SCHEMA],
})
export class WorkspaceComponent {
  conversations = myConversations;
  activeId = this.conversations[0]?.id;
  messages = loadMessages(this.activeId);

  onConversationSelect(e: Event) {
    this.activeId = (e as CustomEvent<{ id: string }>).detail.id;
    this.messages = loadMessages(this.activeId);
  }

  onNewChat() {
    startNewConversation();
  }

  onSubmit(e: Event) {
    const { value } = (e as CustomEvent<{ value: string }>).detail;
    sendMessage(value);
  }
}
```

```html
<!-- workspace.component.html -->
<!-- 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]="conversations"
    [activeId]="activeId"
    (kc-conversation-select)="onConversationSelect($event)"
    (kc-new-chat)="onNewChat()"
    style="width: 300px; flex-shrink: 0"
  ></kc-conversations>

  <kc-chat
    [messages]="messages"
    (kc-submit)="onSubmit($event)"
    style="flex: 1; min-width: 0"
  ></kc-chat>
</div>
```

### 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)` (`$event.detail.sizes`)
to persist the layout.

```html
<div style="display: flex; flex-direction: column; height: 100dvh">
  <kc-resizable
    orientation="horizontal"
    (kc-change)="onResize($event)"
    style="flex: 1; min-height: 0"
  >
    <kc-resizable-item size="25%" min="200px">
      <kc-conversations
        [conversations]="conversations"
        [activeId]="activeId"
        (kc-conversation-select)="onConversationSelect($event)"
      ></kc-conversations>
    </kc-resizable-item>
    <kc-resizable-item>
      <kc-chat [messages]="messages" (kc-submit)="onSubmit($event)"></kc-chat>
    </kc-resizable-item>
  </kc-resizable>
</div>
```

```ts
onResize(e: Event) {
  const { sizes } = (e as CustomEvent<{ sizes: string[] }>).detail;
  // persist sizes to localStorage, a service, etc.
  console.log('panel sizes:', sizes);
}
```

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 `[prop]` bindings
and `(event)` listeners.

> **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 Angular (and every other framework).

## Props & events

The rule for all elements: **rich data goes in as properties, interactions come out as events.**
Angular's `[prop]="…"` binding writes to the element's DOM property (not an HTML attribute), so
arrays and objects pass through unstringified — no adapter or wrapper needed.

`(event)="handler($event)"` listens for a DOM CustomEvent. `$event` is the `CustomEvent` itself;
read the payload from `$event.detail`:

| Element | Property binding | Event | `$event.detail` |
|---|---|---|---|
| `kc-chat` | `[messages]="messages"` | `(submit)` | `{ value: string }` |
| `kc-conversations` | `[conversations]="…"` | `(conversationselect)` | `{ id: string }` |
| `kc-conversations` | `[groups]="…"` | `(newchat)` | — |
| `kc-conversations` | — | `(togglesidebar)` | — |
| `kc-resizable` | `orientation="horizontal"` | `(change)` | `{ sizes: string[] }` |
| `kc-resizable-item` | `size="25%"` `min="200px"` `max="…"` | — | — |

> **Scalar attributes** like `orientation="horizontal"` or `theme="dark"` can be plain HTML
> attributes (no brackets). Use `[prop]="…"` only when passing non-string values (arrays, objects,
> booleans) or Angular expressions.

> **`CUSTOM_ELEMENTS_SCHEMA` is required** in every standalone component (or NgModule) whose
> template contains `<kc-*>` tags. Without it, the Angular compiler rejects unknown element names at
> build time.
