diff --git a/packages/kilo-ui/src/stories/dock-prompt.stories.tsx b/packages/kilo-ui/src/stories/dock-prompt.stories.tsx
new file mode 100644
index 00000000000..df4b32ae6da
--- /dev/null
+++ b/packages/kilo-ui/src/stories/dock-prompt.stories.tsx
@@ -0,0 +1,60 @@
+/** @jsxImportSource solid-js */
+import type { Meta, StoryObj } from "storybook-solidjs-vite"
+import { DockPrompt } from "@opencode-ai/ui/dock-prompt"
+import { Button } from "@opencode-ai/ui/button"
+
+const meta: Meta = {
+ title: "Components/DockPrompt",
+ parameters: { layout: "padded" },
+}
+export default meta
+type Story = StoryObj
+
+export const Question: Story = {
+ name: "Question kind",
+ render: () => (
+ Which testing framework should I use?}
+ footer={
+
+
+
+
+ }
+ >
+
+
Choose one of the following options:
+
+ - Vitest
+ - Jest
+ - Playwright
+ - Bun test
+
+
+
+ ),
+}
+
+export const Permission: Story = {
+ name: "Permission kind",
+ render: () => (
+ Permission required — write}
+ footer={
+
+
+
+
+
+ }
+ >
+
+ src/main.tsx
+
+ src/utils.ts
+
+
+ ),
+}
diff --git a/packages/kilo-ui/src/stories/dock-surface.stories.tsx b/packages/kilo-ui/src/stories/dock-surface.stories.tsx
new file mode 100644
index 00000000000..e602659c5de
--- /dev/null
+++ b/packages/kilo-ui/src/stories/dock-surface.stories.tsx
@@ -0,0 +1,60 @@
+/** @jsxImportSource solid-js */
+import type { Meta, StoryObj } from "storybook-solidjs-vite"
+import { DockShell, DockShellForm, DockTray } from "@opencode-ai/ui/dock-surface"
+
+const meta: Meta = {
+ title: "Components/DockSurface",
+ parameters: { layout: "padded" },
+}
+export default meta
+type Story = StoryObj
+
+export const Shell: Story = {
+ name: "DockShell",
+ render: () => (
+
+ DockShell — main container body for the dock surface.
+
+ ),
+}
+
+export const Tray: Story = {
+ name: "DockTray",
+ render: () => (
+
+ DockTray — footer tray area (attach=none).
+
+ ),
+}
+
+export const TrayAttachTop: Story = {
+ name: "DockTray attach=top",
+ render: () => (
+
+ DockTray — header tray area (attach=top).
+
+ ),
+}
+
+export const ShellWithTray: Story = {
+ name: "DockShell + DockTray",
+ render: () => (
+
+
+ Body content goes here inside DockShell.
+
+
+ Footer actions slot
+
+
+ ),
+}
+
+export const ShellForm: Story = {
+ name: "DockShellForm",
+ render: () => (
+
+ DockShellForm — shell variant using a form element.
+
+ ),
+}
diff --git a/packages/kilo-vscode/webview-ui/src/stories/StoryProviders.tsx b/packages/kilo-vscode/webview-ui/src/stories/StoryProviders.tsx
index dbbe0f64bea..2f2610e1cae 100644
--- a/packages/kilo-vscode/webview-ui/src/stories/StoryProviders.tsx
+++ b/packages/kilo-vscode/webview-ui/src/stories/StoryProviders.tsx
@@ -4,10 +4,16 @@
*
* Instead of instantiating the full VSCodeProvider → ServerProvider → SessionProvider
* chain (which requires a real extension host / SSE connection), we provide mock
- * context values directly.
+ * context values directly. Where a real provider is safe to instantiate without an
+ * extension host (VSCodeProvider, ServerProvider, ProviderProvider), we use the real
+ * thing so components that call useVSCode()/useServer()/useProvider() don't throw.
*/
import { createSignal, type ParentComponent } from "solid-js"
+import { VSCodeProvider } from "../context/vscode"
+import { ServerProvider } from "../context/server"
+import { ProviderProvider } from "../context/provider"
+import { ConfigProvider } from "../context/config"
import { DataProvider } from "@kilocode/kilo-ui/context/data"
import { DiffComponentProvider } from "@kilocode/kilo-ui/context/diff"
import { CodeComponentProvider } from "@kilocode/kilo-ui/context/code"
@@ -144,29 +150,37 @@ export const StoryProviders: ParentComponent = (props) => {
const [locale] = createSignal<"en">("en")
return (
-
- "" as any,
- t,
- }}
- >
- "en", t }}>
-
-
-
-
-
- {props.noPadding ? props.children : {props.children}
}
-
-
-
-
-
-
-
-
+
+
+
+
+
+ "" as any,
+ t,
+ }}
+ >
+ "en", t }}>
+
+
+
+
+
+ {props.noPadding ? props.children : {props.children}
}
+
+
+
+
+
+
+
+
+
+
+
+
)
}
diff --git a/packages/kilo-vscode/webview-ui/src/stories/agent-manager.stories.tsx b/packages/kilo-vscode/webview-ui/src/stories/agent-manager.stories.tsx
new file mode 100644
index 00000000000..0c75731604d
--- /dev/null
+++ b/packages/kilo-vscode/webview-ui/src/stories/agent-manager.stories.tsx
@@ -0,0 +1,188 @@
+/** @jsxImportSource solid-js */
+/**
+ * Stories for Agent Manager components:
+ * AgentManagerApp, FileTree, DiffPanel, FullScreenDiffView
+ *
+ * FileTree, DiffPanel, and FullScreenDiffView accept data-only props and only
+ * require a LanguageContext, so they are storied with StoryProviders.
+ * AgentManagerApp sets up its own full provider chain and is rendered directly.
+ */
+
+import type { Meta, StoryObj } from "storybook-solidjs-vite"
+import { StoryProviders } from "./StoryProviders"
+import { AgentManagerApp } from "../../agent-manager/AgentManagerApp"
+import { FileTree } from "../../agent-manager/FileTree"
+import { DiffPanel } from "../../agent-manager/DiffPanel"
+import { FullScreenDiffView } from "../../agent-manager/FullScreenDiffView"
+import type { WorktreeFileDiff } from "../types/messages"
+
+// ---------------------------------------------------------------------------
+// Shared mock data
+// ---------------------------------------------------------------------------
+
+const mockDiffs: WorktreeFileDiff[] = [
+ {
+ file: "src/components/chat/ChatView.tsx",
+ status: "modified",
+ additions: 12,
+ deletions: 4,
+ before: `import { Component } from "solid-js"\n\nexport const ChatView: Component = () => {\n return \n}\n`,
+ after: `import { Component, createSignal } from "solid-js"\n\nexport const ChatView: Component = () => {\n const [open, setOpen] = createSignal(false)\n return \n}\n`,
+ },
+ {
+ file: "src/components/chat/MessageList.tsx",
+ status: "modified",
+ additions: 3,
+ deletions: 1,
+ before: `export const MessageList = () => \n`,
+ after: `export const MessageList = () => (\n \n)\n`,
+ },
+ {
+ file: "src/stories/chat.stories.tsx",
+ status: "added",
+ additions: 80,
+ deletions: 0,
+ before: "",
+ after: `/** @jsxImportSource solid-js */\nimport type { Meta } from "storybook-solidjs-vite"\nconst meta: Meta = { title: "Chat" }\nexport default meta\n`,
+ },
+]
+
+// ---------------------------------------------------------------------------
+// Meta
+// ---------------------------------------------------------------------------
+
+const meta: Meta = {
+ title: "AgentManager",
+ parameters: { layout: "padded" },
+}
+export default meta
+type Story = StoryObj
+
+// ---------------------------------------------------------------------------
+// FileTree
+// ---------------------------------------------------------------------------
+
+export const FileTreeWithChanges: Story = {
+ name: "FileTree — with modifications and additions",
+ render: () => (
+
+
+ {}}
+ showSummary
+ />
+
+
+ ),
+}
+
+export const FileTreeEmpty: Story = {
+ name: "FileTree — no changes",
+ render: () => (
+
+
+ {}} />
+
+
+ ),
+}
+
+// ---------------------------------------------------------------------------
+// DiffPanel
+// ---------------------------------------------------------------------------
+
+export const DiffPanelLoading: Story = {
+ name: "DiffPanel — loading",
+ render: () => (
+
+
+ {}}
+ onClose={() => {}}
+ />
+
+
+ ),
+}
+
+export const DiffPanelWithDiffs: Story = {
+ name: "DiffPanel — with diffs (unified)",
+ render: () => (
+
+
+ {}}
+ comments={[]}
+ onCommentsChange={() => {}}
+ onClose={() => {}}
+ onExpand={() => {}}
+ />
+
+
+ ),
+}
+
+// ---------------------------------------------------------------------------
+// FullScreenDiffView
+// ---------------------------------------------------------------------------
+
+export const FullScreenDiffLoading: Story = {
+ name: "FullScreenDiffView — loading",
+ render: () => (
+
+
+ {}}
+ comments={[]}
+ onCommentsChange={() => {}}
+ onClose={() => {}}
+ />
+
+
+ ),
+}
+
+export const FullScreenDiffWithChanges: Story = {
+ name: "FullScreenDiffView — with changes",
+ render: () => (
+
+
+ {}}
+ comments={[]}
+ onCommentsChange={() => {}}
+ onClose={() => {}}
+ />
+
+
+ ),
+}
+
+// ---------------------------------------------------------------------------
+// AgentManagerApp
+// ---------------------------------------------------------------------------
+
+export const AgentManagerRoot: Story = {
+ name: "AgentManagerApp — initial connecting state",
+ render: () => (
+ // AgentManagerApp sets up its own VSCodeProvider + ThemeProvider + layout.
+ // In Storybook it renders its empty/connecting state (no extension host).
+
+ ),
+}
diff --git a/packages/kilo-vscode/webview-ui/src/stories/chat.stories.tsx b/packages/kilo-vscode/webview-ui/src/stories/chat.stories.tsx
new file mode 100644
index 00000000000..d34da6baa86
--- /dev/null
+++ b/packages/kilo-vscode/webview-ui/src/stories/chat.stories.tsx
@@ -0,0 +1,162 @@
+/** @jsxImportSource solid-js */
+/**
+ * Stories for high-priority chat components:
+ * ChatView, MessageList, QuestionDock
+ *
+ * These render with mocked session/server/provider contexts — the components
+ * will show their "idle / empty" states since no real extension host is connected.
+ */
+
+import type { Meta, StoryObj } from "storybook-solidjs-vite"
+import { StoryProviders } from "./StoryProviders"
+import { ChatView } from "../components/chat/ChatView"
+import { MessageList } from "../components/chat/MessageList"
+import { QuestionDock } from "../components/chat/QuestionDock"
+import type { QuestionRequest } from "../types/messages"
+
+const SESSION_ID = "story-session-chat-001"
+
+// ---------------------------------------------------------------------------
+// Question fixtures
+// ---------------------------------------------------------------------------
+
+const singleQuestion: QuestionRequest = {
+ id: "q-single-001",
+ sessionID: SESSION_ID,
+ questions: [
+ {
+ question: "Which testing framework should I use for this project?",
+ header: "Choose a framework",
+ options: [
+ { label: "Vitest", description: "Fast, Vite-native unit testing" },
+ { label: "Jest", description: "Widely adopted, rich ecosystem" },
+ { label: "Playwright", description: "End-to-end browser testing" },
+ { label: "Bun test", description: "Built-in, zero config" },
+ ],
+ },
+ ],
+ tool: { messageID: "asst-msg-001", callID: "call-question-001" },
+}
+
+const multiQuestion: QuestionRequest = {
+ id: "q-multi-001",
+ sessionID: SESSION_ID,
+ questions: [
+ {
+ question: "Which testing framework?",
+ options: [{ label: "Vitest" }, { label: "Jest" }, { label: "Bun test" }],
+ },
+ {
+ question: "Should I include coverage reporting?",
+ options: [{ label: "Yes, Istanbul" }, { label: "Yes, V8" }, { label: "No" }],
+ },
+ ],
+ tool: { messageID: "asst-msg-001", callID: "call-question-002" },
+}
+
+// ---------------------------------------------------------------------------
+// Meta
+// ---------------------------------------------------------------------------
+
+const meta: Meta = {
+ title: "Chat",
+ parameters: { layout: "padded" },
+}
+export default meta
+type Story = StoryObj
+
+// ---------------------------------------------------------------------------
+// ChatView stories
+// ---------------------------------------------------------------------------
+
+export const ChatViewIdle: Story = {
+ name: "ChatView — idle (empty)",
+ render: () => (
+
+
+
+
+
+ ),
+}
+
+export const ChatViewReadonly: Story = {
+ name: "ChatView — readonly",
+ render: () => (
+
+
+
+
+
+ ),
+}
+
+export const ChatViewWithQuestion: Story = {
+ name: "ChatView — with QuestionDock",
+ render: () => (
+
+
+
+
+
+ ),
+}
+
+// ---------------------------------------------------------------------------
+// MessageList stories
+// ---------------------------------------------------------------------------
+
+export const MessageListEmpty: Story = {
+ name: "MessageList — empty",
+ render: () => (
+
+
+
+
+
+ ),
+}
+
+export const MessageListLoading: Story = {
+ name: "MessageList — loading",
+ render: () => {
+ const loadingSession = {
+ loading: () => true,
+ }
+ // Render empty state — the real "loading" state requires overriding session.loading()
+ // which we approximate here: the component will show its empty-state logo instead.
+ return (
+
+
+
+
+
+ )
+ },
+}
+
+// ---------------------------------------------------------------------------
+// QuestionDock stories
+// ---------------------------------------------------------------------------
+
+export const QuestionDockSingle: Story = {
+ name: "QuestionDock — single question",
+ render: () => (
+
+
+
+
+
+ ),
+}
+
+export const QuestionDockMulti: Story = {
+ name: "QuestionDock — multi-question wizard",
+ render: () => (
+
+
+
+
+
+ ),
+}
diff --git a/packages/kilo-vscode/webview-ui/src/stories/history.stories.tsx b/packages/kilo-vscode/webview-ui/src/stories/history.stories.tsx
new file mode 100644
index 00000000000..82ea82fa7f6
--- /dev/null
+++ b/packages/kilo-vscode/webview-ui/src/stories/history.stories.tsx
@@ -0,0 +1,152 @@
+/** @jsxImportSource solid-js */
+/**
+ * Stories for the SessionList component (history panel).
+ */
+
+import type { Meta, StoryObj } from "storybook-solidjs-vite"
+import { createSignal, type ParentComponent } from "solid-js"
+import { DialogProvider } from "@kilocode/kilo-ui/context/dialog"
+import { DataProvider } from "@kilocode/kilo-ui/context/data"
+import { DiffComponentProvider } from "@kilocode/kilo-ui/context/diff"
+import { CodeComponentProvider } from "@kilocode/kilo-ui/context/code"
+import { MarkedProvider } from "@kilocode/kilo-ui/context/marked"
+import { I18nProvider } from "@kilocode/kilo-ui/context"
+import { Diff } from "@kilocode/kilo-ui/diff"
+import { Code } from "@kilocode/kilo-ui/code"
+import { VSCodeProvider } from "../context/vscode"
+import { ServerProvider } from "../context/server"
+import { ConfigProvider } from "../context/config"
+import { ProviderProvider } from "../context/provider"
+import { SessionContext } from "../context/session"
+import { LanguageContext } from "../context/language"
+import { dict as uiEn } from "@kilocode/kilo-ui/i18n/en"
+import { dict as appEn } from "../i18n/en"
+import { dict as kiloEn } from "@kilocode/kilo-i18n/en"
+import { SessionList } from "../components/history/SessionList"
+
+const dict: Record = { ...appEn, ...uiEn, ...kiloEn }
+function t(key: string) { return dict[key] ?? key }
+function noop() {}
+
+const now = new Date().toISOString()
+const yesterday = new Date(Date.now() - 86400000).toISOString()
+const weekAgo = new Date(Date.now() - 7 * 86400000).toISOString()
+
+const mockSessions = [
+ { id: "s1", title: "Refactor authentication module", createdAt: now, updatedAt: now },
+ { id: "s2", title: "Add screenshot test coverage", createdAt: yesterday, updatedAt: yesterday },
+ { id: "s3", title: "Fix TypeScript errors in webview", createdAt: weekAgo, updatedAt: weekAgo },
+ { id: "s4", title: undefined, createdAt: weekAgo, updatedAt: weekAgo },
+]
+
+const WithSessions: ParentComponent<{ sessions?: typeof mockSessions }> = (props) => {
+ const [locale] = createSignal<"en">("en")
+ const sessions = props.sessions ?? []
+ const session = {
+ currentSessionID: () => "s1",
+ currentSession: () => sessions[0],
+ setCurrentSessionID: noop,
+ sessions: () => sessions as any,
+ status: () => "idle" as const,
+ statusInfo: () => ({ type: "idle" }),
+ statusText: () => undefined,
+ busySince: () => undefined,
+ loading: () => false,
+ messages: () => [],
+ userMessages: () => [],
+ allMessages: () => ({}),
+ allParts: () => ({}),
+ allStatusMap: () => ({}),
+ getParts: () => [],
+ todos: () => [],
+ permissions: () => [],
+ questions: () => [],
+ questionErrors: () => new Set(),
+ selected: () => ({ providerID: "anthropic", modelID: "claude-sonnet-4-20250514" }),
+ selectModel: noop,
+ totalCost: () => 0,
+ contextUsage: () => undefined,
+ agents: () => [{ name: "code", description: "Code mode", mode: "primary" as const }],
+ selectedAgent: () => "code",
+ selectAgent: noop,
+ getSessionAgent: () => "code",
+ getSessionModel: () => ({ providerID: "anthropic", modelID: "claude-sonnet-4-20250514" }),
+ setSessionModel: noop,
+ setSessionAgent: noop,
+ variantList: () => [],
+ currentVariant: () => undefined,
+ selectVariant: noop,
+ sendMessage: noop,
+ abort: noop,
+ compact: noop,
+ respondToPermission: noop,
+ replyToQuestion: noop,
+ rejectQuestion: noop,
+ createSession: noop,
+ clearCurrentSession: noop,
+ loadSessions: noop,
+ selectSession: noop,
+ deleteSession: noop,
+ renameSession: noop,
+ syncSession: noop,
+ cloudPreviewId: () => null,
+ selectCloudSession: noop,
+ }
+
+ return (
+
+
+
+
+
+ "" as any, t }}>
+ "en", t }}>
+
+
+
+
+
+ {props.children}
+
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
+
+const meta: Meta = {
+ title: "History/SessionList",
+ parameters: { layout: "padded" },
+}
+export default meta
+type Story = StoryObj
+
+export const Empty: Story = {
+ name: "Empty list",
+ render: () => (
+
+
+
+
+
+ ),
+}
+
+export const WithItems: Story = {
+ name: "With sessions",
+ render: () => (
+
+
+
+
+
+ ),
+}
diff --git a/packages/kilo-vscode/webview-ui/src/stories/settings.stories.tsx b/packages/kilo-vscode/webview-ui/src/stories/settings.stories.tsx
new file mode 100644
index 00000000000..7bc720d822c
--- /dev/null
+++ b/packages/kilo-vscode/webview-ui/src/stories/settings.stories.tsx
@@ -0,0 +1,38 @@
+/** @jsxImportSource solid-js */
+/**
+ * Stories for Settings and ProvidersTab components.
+ */
+
+import type { Meta, StoryObj } from "storybook-solidjs-vite"
+import { StoryProviders } from "./StoryProviders"
+import Settings from "../components/settings/Settings"
+import ProvidersTab from "../components/settings/ProvidersTab"
+
+const meta: Meta = {
+ title: "Settings",
+ parameters: { layout: "fullscreen" },
+}
+export default meta
+type Story = StoryObj
+
+export const SettingsPanel: Story = {
+ name: "Settings — full panel",
+ render: () => (
+
+
+ {}} />
+
+
+ ),
+}
+
+export const ProvidersConfigure: Story = {
+ name: "ProvidersTab — no providers configured",
+ render: () => (
+
+
+
+ ),
+}
diff --git a/packages/kilo-vscode/webview-ui/src/stories/shared.stories.tsx b/packages/kilo-vscode/webview-ui/src/stories/shared.stories.tsx
new file mode 100644
index 00000000000..1c17d51ab50
--- /dev/null
+++ b/packages/kilo-vscode/webview-ui/src/stories/shared.stories.tsx
@@ -0,0 +1,103 @@
+/** @jsxImportSource solid-js */
+/**
+ * Stories for shared controls: ModelSelector and ModeSwitcher.
+ */
+
+import type { Meta, StoryObj } from "storybook-solidjs-vite"
+import { StoryProviders } from "./StoryProviders"
+import { ModelSelectorBase } from "../components/shared/ModelSelector"
+import { ModeSwitcherBase, ModeSwitcher } from "../components/shared/ModeSwitcher"
+
+const agents = [
+ { name: "code", description: "Write, edit and review code", mode: "primary" as const },
+ { name: "ask", description: "Answer questions without making changes", mode: "primary" as const },
+ { name: "architect", description: "Plan and design before implementation", mode: "primary" as const },
+ { name: "debug", description: "Diagnose and fix issues", mode: "primary" as const },
+]
+
+const meta: Meta = {
+ title: "Shared",
+ parameters: { layout: "padded" },
+}
+export default meta
+type Story = StoryObj
+
+// ---------------------------------------------------------------------------
+// ModelSelector
+// ---------------------------------------------------------------------------
+
+export const ModelSelectorNoProviders: Story = {
+ name: "ModelSelector — no providers",
+ render: () => (
+
+
+ {}}
+ placement="bottom-start"
+ />
+
+
+ ),
+}
+
+export const ModelSelectorAllowClear: Story = {
+ name: "ModelSelector — allow clear",
+ render: () => (
+
+
+ {}}
+ placement="bottom-start"
+ allowClear
+ clearLabel="Use default model"
+ />
+
+
+ ),
+}
+
+// ---------------------------------------------------------------------------
+// ModeSwitcher
+// ---------------------------------------------------------------------------
+
+export const ModeSwitcherSingle: Story = {
+ name: "ModeSwitcherBase — single agent (hidden)",
+ render: () => (
+
+ {}}
+ />
+
+ ),
+}
+
+export const ModeSwitcherMultiple: Story = {
+ name: "ModeSwitcherBase — multiple agents",
+ render: () => (
+
+ {}} />
+
+ ),
+}
+
+export const ModeSwitcherAskSelected: Story = {
+ name: "ModeSwitcherBase — ask mode selected",
+ render: () => (
+
+ {}} />
+
+ ),
+}
+
+export const ModeSwitcherFromSession: Story = {
+ name: "ModeSwitcher — wired to session context",
+ render: () => (
+
+
+
+ ),
+}