Skip to content
kitn AI/UI

Resizable split layout

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.

<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.

<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">
import '@kitn.ai/ui/elements';
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', content: prompt },
{ id: aId, role: 'assistant', content: '' },
];
chat.loading = true;
// stream tokens into the assistant message
for await (const token of streamFromYourModel(prompt)) {
chat.messages = chat.messages.map((m) =>
m.id === aId ? { ...m, content: m.content + token } : 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.

  • kai-conversations referencegroups, conversations, activeId, and the kai-conversation-select event.
  • kai-chat reference — the full thread + input element, including models, context meter, and slash commands.
  • Drop-in chat — the streaming loop that drives the chat pane.
  • Workspace app — the same sidebar + thread composed into a complete app.