# Resizable split layout

A two-pane layout where a conversations list and a chat pane sit side-by-side with a draggable divider — the list can go on either side.

Put a sidebar conversation list next to a chat pane and let the user resize both. Drag the divider to redistribute space, or flip the sidebar to the opposite side with the toggle above the demo.

## How it works

`<kai-resizable>` lays out its `<kai-resizable-item>` children along a horizontal (or vertical) axis and inserts a draggable divider between each pair. Set `size`, `min`, and `max` on each item to constrain the sidebar; the main pane flexes to fill the rest.

```html
<kai-resizable orientation="horizontal" style="display:block;height:100%">
  <!-- sidebar -->
  <kai-resizable-item size="25%" min="180px" max="340px">
    <kai-conversations id="conv"></kai-conversations>
  </kai-resizable-item>

  <!-- main chat -->
  <kai-resizable-item>
    <kai-chat id="chat"></kai-chat>
  </kai-resizable-item>
</kai-resizable>

<script type="module">

  await customElements.whenDefined('kai-conversations');

  const conv = document.getElementById('conv');
  const chat = document.getElementById('chat');

  // Assign arrays and objects in JavaScript — they can't be HTML attributes.
  conv.conversations = [
    {
      id: 'c1', title: 'Web component architecture',
      scope: { type: 'document' }, messageCount: 12,
      lastMessageAt: new Date().toISOString(),
      updatedAt: new Date().toISOString(),
    },
  ];
  conv.activeId = 'c1';

  chat.messages = [];

  conv.addEventListener('kai-conversation-select', (e) => {
    // load the selected conversation's messages into chat
    console.log('selected', e.detail.id);
  });

  chat.addEventListener('kai-submit', async (e) => {
    const prompt = e.detail.value;
    const aId = crypto.randomUUID();
    chat.messages = [
      ...chat.messages,
      { id: crypto.randomUUID(), role: 'user', parts: [{ type: 'text', text: prompt }] },
      { id: aId, role: 'assistant', parts: [] },
    ];
    chat.loading = true;
    // stream tokens into the assistant message's parts
    let answer = '';
    for await (const token of streamFromYourModel(prompt)) {
      answer += token;
      chat.messages = chat.messages.map((m) =>
        m.id === aId ? { ...m, parts: [{ type: 'text', text: answer }] } : m,
      );
    }
    chat.loading = false;
  });
</script>
```

To put the sidebar on the right, reverse the child order — put `<kai-resizable-item>` (chat) first, then `<kai-resizable-item>` (conversations).

`<kai-resizable>` fires `kai-change` with `{ sizes: number[] }` (percent) on each drag-end so you can persist or react to the panel sizes.

## Next steps

- **[`kai-conversations` reference](/components/conversations/)** — `groups`, `conversations`, `activeId`, and the `kai-conversation-select` event.
- **[`kai-chat` reference](/components/chat/)** — the full thread + input element, including models, context meter, and slash commands.
- **[Drop-in chat](/examples/drop-in-chat/)** — the streaming loop that drives the chat pane.
- **[Workspace app](/examples/workspace/)** — the same sidebar + thread composed into a complete app.
