Skip to content
kitn AI/UI

Workspace app

One element gives you the full workspace: a resizable, collapsible sidebar listing past conversations grouped by recency, plus a streaming chat thread. Select a conversation, start a new one, or send a message below.

<kai-workspace> wraps <kai-conversations> and <kai-chat> inside a resizable split. You pass a flat conversations array (the component auto-buckets by recency) and the active thread’s messages. Three events drive all the interactivity:

<kai-workspace id="ws"></kai-workspace>
<script type="module">
import '@kitn.ai/ui/elements';
const ws = document.getElementById('ws');
// Seed conversations (assign in JavaScript — arrays can't be attributes).
ws.conversations = [
{
id: 'c1', title: 'Migrating the dashboard to web components',
scope: { type: 'collection' }, messageCount: 4,
lastMessageAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
},
// … more conversations
];
// Seed the active thread.
ws.messages = [
{ id: 'u1', role: 'user', content: 'How do I share state across the panels?' },
{ id: 'a1', role: 'assistant', content: 'Keep one source of truth and reassign it on each change …', actions: ['copy', 'like', 'dislike'] },
];
ws.activeId = 'c1';
// Swap the thread when the user picks a conversation.
ws.addEventListener('kai-conversation-select', (e) => {
ws.activeId = e.detail.id;
ws.messages = threadFor(e.detail.id); // load from your store
});
// Clear the thread and start fresh.
ws.addEventListener('kai-new-chat', () => {
ws.messages = [];
ws.activeId = undefined;
});
// Stream a reply — identical pattern to <kai-chat>.
ws.addEventListener('kai-submit', async (e) => {
const { value } = e.detail;
const aId = crypto.randomUUID();
ws.messages = [
...ws.messages,
{ id: crypto.randomUUID(), role: 'user', content: value },
{ id: aId, role: 'assistant', content: '' },
];
ws.loading = true;
for await (const token of streamFromYourModel(value)) {
ws.messages = ws.messages.map((m) =>
m.id === aId ? { ...m, content: m.content + token } : m,
);
}
ws.loading = false;
});
</script>

Key data shapes:

  • conversations — each entry needs id, title, scope ({ type: 'collection' }), messageCount, lastMessageAt, and updatedAt. The component uses the timestamps to bucket rows into “Today”, “Yesterday”, “Last 7 days”, etc.
  • messages — the same ChatMessage[] shape as <kai-chat>: id, role, content, plus optional actions, reasoning, tools, and attachments.

Sidebar controls:

PropDefaultEffect
sidebarWidth22Initial sidebar width (%)
sidebarMinWidth200Minimum sidebar width (px)
sidebarMaxWidth420Maximum sidebar width (px)
sidebarCollapsedfalseStart with sidebar collapsed

The user can drag the divider to resize or click the sidebar toggle. Listen to kai-sidebar-toggle (detail.collapsed) to persist the state.

  • Drop-in chat — the simpler single-thread starting point and streaming loop.
  • kai-workspace reference — every prop and event, including model switcher, context meter, and slash commands.
  • kai-conversations reference — use this element standalone when you want to build your own layout instead of using the pre-composed workspace.
  • Theming — brand the sidebar (--color-sidebar) and chat thread in one pass.