Merge pull request #9331 from Kilo-Org/docs/generated-screenshot-refs

Reference generated screenshots in docs
This commit is contained in:
Mark IJbema
2026-04-22 09:57:57 +02:00
committed by GitHub
11 changed files with 274 additions and 16 deletions
+6
View File
@@ -30,6 +30,12 @@ Use the Markdoc image tag format:
Note that this site is served under kilo.ai/docs so the `/docs` prefix **must** be present in every image path.
#### Generated screenshots
When updating screenshots for active docs pages, prefer generated screenshot-test assets from `packages/kilo-docs/public/img/screenshot-tests/` and reference them as `/docs/img/screenshot-tests/...`. Only replace a hand-captured image when the generated screenshot matches the docs content closely. Do not replace screenshots in VSCode Legacy docs tabs or sections.
If a docs page references a generated VS Code visual-regression screenshot, record that usage in `packages/kilo-vscode/tests/visual-regression.spec.ts` by adding the story ID to the `DOCS` map. Keep `packages/kilo-vscode/tests/visual-regression.spec.mts` in sync while that file exists. If no matching generated screenshot exists, add or update a Storybook story in `packages/kilo-vscode/webview-ui/src/stories/` and let visual-regression CI generate the baseline.
Image attributes:
| Attribute | Type | Required | Description |
@@ -9,7 +9,7 @@ description: "Track and manage tasks with AI-generated todo lists"
**Why it matters**: Complex workflows have lots of moving parts. Without structure, it's easy to miss steps, duplicate work, or forget what comes next.
{% image src="/docs/img/task-todo-list/task-todo-list-1.png" alt="Task Todo List overview showing interactive checklist in Kilo Code" width="500" /%}
{% image src="/docs/img/screenshot-tests/kilo-vscode/visual-regression/composite-webview/todo-write-docs-overview-chromium-linux.png" alt="Task Todo List overview showing interactive checklist in Kilo Code" width="420" /%}
## How to trigger todo lists
@@ -53,7 +53,7 @@ Kilo updates the list when:
**1. Task Header Summary**
Quick progress overview with your next important item
{% image src="/docs/img/task-todo-list/task-header.png" alt="Task header summary showing todo list progress" width="500" /%}
{% image src="/docs/img/screenshot-tests/kilo-vscode/visual-regression/chat/task-header-with-todos-chromium-linux.png" alt="Task header summary showing todo list progress" width="420" /%}
Click the task header summary to expand the full list inline and jump to the current item.
@@ -8,7 +8,7 @@ platform: new
Workflows (also called **slash commands** in the new extension) automate repetitive tasks by defining step-by-step instructions for Kilo Code to execute.
{% image src="/docs/img/slash-commands/workflows.png" alt="Workflows tab in Kilo Code" width="600" caption="Workflows tab in Kilo Code" /%}
{% image src="/docs/img/screenshot-tests/kilo-vscode/visual-regression/settings/agent-behaviour-workflows-chromium-linux.png" alt="Workflows tab in Kilo Code" width="420" caption="Workflows tab in Kilo Code" /%}
## Creating Workflows
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:5ab73f8c0585e9f889c51073428aa0e7082d7aecdc3d5b0c459720c1b860c436
size 4890
oid sha256:d34153690f8aa4ef7e06e12385a184c151843f3ae75a9955619a6d7e88dc8c8b
size 7981
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:d4d7ac9d6322d2cf673a6353cb07cf9979ad55351769ba9d90eed386170f6c42
size 40854
+6
View File
@@ -186,6 +186,12 @@ New webview features must use **`@kilocode/kilo-ui`** components instead of raw
- **Prefer kilo-ui styles**: Always reuse existing kilo-ui CSS variables, tokens, and component styles instead of writing custom CSS. If a style doesn't exist in kilo-ui yet, add it there and reuse it rather than inlining or duplicating styles in the webview.
- **Icons**: kilo-ui has 75+ custom SVG icons in [`packages/ui/src/components/icon.tsx`](../../packages/ui/src/components/icon.tsx). To list all available icon names: `node -e "const c=require('fs').readFileSync('../../packages/ui/src/components/icon.tsx','utf8');[...c.matchAll(/^\\s{2}[\"']?([\\w-]+)[\"']?:\\s*\x60/gm)].map(m=>m[1]).sort().forEach(n=>console.log(n))"`. Icon names use both hyphenated (`arrow-left`) and bare-word (`brain`, `console`, `providers`) keys.
## Docs Screenshot Stories
When adding or updating Storybook stories for screenshots used by docs, make the story content match the docs page closely before replacing the docs image. Do not replace screenshots from VSCode Legacy docs tabs or sections.
Generated screenshot baselines live under `packages/kilo-docs/public/img/screenshot-tests/` and are referenced from docs as `/docs/img/screenshot-tests/...`. If a generated VS Code visual-regression screenshot is used in docs, add the docs usage to the `DOCS` map in `tests/visual-regression.spec.ts` and keep `tests/visual-regression.spec.mts` in sync while that file exists.
## Debugging
- Extension logs: "Extension Host" output channel (not Debug Console)
@@ -53,6 +53,27 @@ const SKIP = new Set<string>([
"composite-webview--permission-dock-config-preloaded",
])
const DOCS = new Map<string, string[]>([
[
"chat--task-header-with-todos",
[
"packages/kilo-docs/pages/code-with-ai/features/task-todo-list.md:/docs/img/screenshot-tests/kilo-vscode/visual-regression/chat/task-header-with-todos-chromium-linux.png",
],
],
[
"composite-webview--todo-write-docs-overview",
[
"packages/kilo-docs/pages/code-with-ai/features/task-todo-list.md:/docs/img/screenshot-tests/kilo-vscode/visual-regression/composite-webview/todo-write-docs-overview-chromium-linux.png",
],
],
[
"settings--agent-behaviour-workflows",
[
"packages/kilo-docs/pages/customize/workflows.md:/docs/img/screenshot-tests/kilo-vscode/visual-regression/settings/agent-behaviour-workflows-chromium-linux.png",
],
],
])
// Generate one test() per story so Playwright's scheduler can distribute
// them freely across workers — no manual sharding needed.
// Skip fetching stories on macOS since test.skip() above already marks the file skipped.
@@ -60,6 +81,10 @@ const stories = IS_DARWIN ? [] : (await fetchStories()).filter((s) => !SKIP.has(
for (const story of stories) {
test(`${story.title} / ${story.name}`, async ({ page }) => {
for (const ref of DOCS.get(story.id) ?? []) {
test.info().annotations.push({ type: "docs", description: ref })
}
// Narrow stories (IDs ending in "-200") use a 200px viewport
// The "-200" suffix comes from the export name convention (e.g. Default200, WithThinking200)
const narrow = story.id.endsWith("-200")
@@ -74,6 +99,6 @@ for (const story of stories) {
const [component, variant] = story.id.split("--")
const root = page.locator("#storybook-root")
await expect(root).toHaveScreenshot(["visual-regression", component!, `${variant}.png`])
await expect(root).toHaveScreenshot(["visual-regression", component!, `${variant}-chromium-linux.png`])
})
}
@@ -54,6 +54,27 @@ const SKIP = new Set<string>([
"composite-webview--permission-dock-config-preloaded",
])
const DOCS = new Map<string, string[]>([
[
"chat--task-header-with-todos",
[
"packages/kilo-docs/pages/code-with-ai/features/task-todo-list.md:/docs/img/screenshot-tests/kilo-vscode/visual-regression/chat/task-header-with-todos-chromium-linux.png",
],
],
[
"composite-webview--todo-write-docs-overview",
[
"packages/kilo-docs/pages/code-with-ai/features/task-todo-list.md:/docs/img/screenshot-tests/kilo-vscode/visual-regression/composite-webview/todo-write-docs-overview-chromium-linux.png",
],
],
[
"settings--agent-behaviour-workflows",
[
"packages/kilo-docs/pages/customize/workflows.md:/docs/img/screenshot-tests/kilo-vscode/visual-regression/settings/agent-behaviour-workflows-chromium-linux.png",
],
],
])
// Generate one test() per story so Playwright's scheduler can distribute
// them freely across workers — no manual sharding needed.
// Skip fetching stories on macOS since test.skip() above already marks the file skipped.
@@ -61,6 +82,10 @@ const stories = IS_DARWIN ? [] : (await fetchStories()).filter((s) => !SKIP.has(
for (const story of stories) {
test(`${story.title} / ${story.name}`, async ({ page }) => {
for (const ref of DOCS.get(story.id) ?? []) {
test.info().annotations.push({ type: "docs", description: ref })
}
// Narrow stories (IDs ending in "-200") use a 200px viewport
const narrow = story.id.endsWith("-200")
await page.setViewportSize({ width: narrow ? 200 : 420, height: 720 })
@@ -74,6 +99,6 @@ for (const story of stories) {
const [component, variant] = story.id.split("--")
const root = page.locator("#storybook-root")
await expect(root).toHaveScreenshot(["visual-regression", component!, `${variant!}.png`])
await expect(root).toHaveScreenshot(["visual-regression", component!, `${variant!}-chromium-linux.png`])
})
}
@@ -67,6 +67,7 @@ const MOCK_PROVIDERS = {
name: "Anthropic: Claude Sonnet 4.6",
inputPrice: 0.003,
outputPrice: 0.015,
limit: { context: 200000, output: 8192 },
},
},
},
@@ -16,7 +16,7 @@ import { SuggestBar } from "../components/chat/SuggestBar"
import { MessageList } from "../components/chat/MessageList"
import { SessionContext } from "../context/session"
import { ServerContext } from "../context/server"
import type { QuestionRequest, SuggestionRequest, TodoItem } from "../types/messages"
import type { Message, Part, QuestionRequest, SuggestionRequest, TodoItem } from "../types/messages"
const SESSION_ID = "story-session-chat-001"
@@ -449,10 +449,141 @@ export const MessageListSubagentToQueuedUserSpacing: Story = {
// TaskHeader with todos
// ---------------------------------------------------------------------------
const headerNow = 1_700_000_000_000
const headerUserID = "user-task-header-001"
const headerAssistantID = "asst-task-header-001"
const headerMessages: Message[] = [
{
id: headerUserID,
sessionID: SESSION_ID,
role: "user",
content: "Can you use the update_todo_list tool to create a CLI interface implementation plan?",
createdAt: new Date(headerNow - 12000).toISOString(),
time: { created: headerNow - 12000 },
},
{
id: headerAssistantID,
sessionID: SESSION_ID,
role: "assistant",
parentID: headerUserID,
content: "I'll track the CLI interface implementation with a todo list.",
createdAt: new Date(headerNow - 10000).toISOString(),
time: { created: headerNow - 10000 },
modelID: "anthropic/claude-sonnet-4-6",
providerID: "kilo",
mode: "default",
agent: "code",
path: { cwd: "/project", root: "/project" },
},
]
const headerParts: Record<string, Part[]> = {
[headerAssistantID]: [
{
id: "part-header-read-001",
sessionID: SESSION_ID,
messageID: headerAssistantID,
type: "tool",
tool: "read",
state: {
status: "completed",
input: { filePath: "packages/opencode/src/cli/index.ts" },
output: "export async function main() { /* existing CLI bootstrap */ }",
title: "Read CLI entrypoint",
},
},
{
id: "part-header-text-001",
sessionID: SESSION_ID,
messageID: headerAssistantID,
type: "text",
text: "I found the existing command registration and argument parsing flow.",
},
{
id: "part-header-glob-001",
sessionID: SESSION_ID,
messageID: headerAssistantID,
type: "tool",
tool: "glob",
state: {
status: "completed",
input: { pattern: "packages/opencode/src/**/*.ts" },
output:
"packages/opencode/src/cli/index.ts\npackages/opencode/src/command/run.ts\npackages/opencode/src/config/config.ts",
title: "Find CLI files",
},
},
{
id: "part-header-edit-001",
sessionID: SESSION_ID,
messageID: headerAssistantID,
type: "tool",
tool: "edit",
state: {
status: "completed",
input: { filePath: "packages/opencode/src/cli/index.ts" },
output: "Updated the command registry to expose the new interface hook.",
title: "Update CLI registry",
},
},
{
id: "part-header-bash-001",
sessionID: SESSION_ID,
messageID: headerAssistantID,
type: "tool",
tool: "bash",
state: {
status: "completed",
input: { command: "bun run check-types:webview", description: "Typecheck webview" },
output: "Checked 1 project. No type errors found.",
title: "Run typecheck",
},
},
{
id: "part-header-write-001",
sessionID: SESSION_ID,
messageID: headerAssistantID,
type: "tool",
tool: "write",
state: {
status: "completed",
input: { filePath: "packages/opencode/src/cli/interface.ts" },
output: "Created the CLI interface implementation scaffold.",
title: "Create interface scaffold",
},
},
{
id: "part-header-text-002",
sessionID: SESSION_ID,
messageID: headerAssistantID,
type: "text",
text: "Next I am wiring the implementation into the existing command path.",
},
{
id: "part-header-bash-002",
sessionID: SESSION_ID,
messageID: headerAssistantID,
type: "tool",
tool: "bash",
state: {
status: "running",
input: { command: "bun test packages/opencode/test/cli.test.ts", description: "Run CLI tests" },
title: "Run CLI tests",
},
},
],
}
const mockTodosInProgress: TodoItem[] = [
{ id: "1", content: "Create a haiku about Jan", status: "completed" },
{ id: "2", content: "Create a poem about Henk", status: "in_progress" },
{ id: "3", content: "Write a limerick about the team", status: "pending" },
{ id: "1", content: "Project setup and architecture backlog", status: "completed" },
{ id: "2", content: "Configuration schema for target jobs", status: "completed" },
{ id: "3", content: "Core scanning logic", status: "completed" },
{ id: "4", content: "Build invocation and error handling", status: "completed" },
{ id: "5", content: "CLI interface implementation", status: "in_progress" },
{ id: "6", content: "Storage layer implementation", status: "pending" },
{ id: "7", content: "Character profiles and prompt types", status: "pending" },
{ id: "8", content: "Local tests and integration tests", status: "pending" },
{ id: "9", content: "Migration guide", status: "pending" },
{ id: "10", content: "Release validation", status: "pending" },
]
const mockTodosAllDone: TodoItem[] = [
@@ -465,19 +596,22 @@ export const TaskHeaderWithTodos: Story = {
render: () => {
const session = {
...mockSessionValue({ id: SESSION_ID, status: "busy" }),
messages: () => [{ id: "msg-001" }] as any[],
messages: () => headerMessages,
currentSession: () => ({
id: SESSION_ID,
title: "Writing poems about the team",
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
title: "Task: Can you use the update_todo_list tool to create a CLI interface implementation?",
createdAt: new Date(headerNow - 12000).toISOString(),
updatedAt: new Date(headerNow).toISOString(),
}),
todos: () => mockTodosInProgress,
getParts: (id: string) => headerParts[id] ?? [],
contextUsage: () => ({ tokens: 34300, percentage: 17 }),
costBreakdown: () => [{ label: "Session", cost: 0.64 }],
}
return (
<StoryProviders sessionID={SESSION_ID} status="busy" noPadding>
<SessionContext.Provider value={session as any}>
<div style={{ width: "380px" }}>
<div style={{ width: "100%" }}>
<TaskHeader />
</div>
</SessionContext.Provider>
@@ -292,6 +292,52 @@ const todoWriteCompleted: ToolPart = {
},
}
const docsTodos = [
{
id: "1",
content: "Project setup and core architecture (package.json, tsconfig, documentation, type definitions)",
status: "completed",
},
{
id: "2",
content: "Configuration system (scraper config, targets.json, validation utilities)",
status: "completed",
},
{
id: "3",
content: "Core scraping engine (browser manager, orchestrator, selector engine, content extractor)",
status: "completed",
},
{
id: "4",
content: "Utility modules (DOM utils, retry logic, URL handling, validation)",
status: "completed",
},
{ id: "5", content: "CLI interface implementation", status: "in_progress" },
{ id: "6", content: "Storage layer implementation (database and file storage)", status: "pending" },
{ id: "7", content: "Chart extractors for specific chart types", status: "pending" },
{ id: "8", content: "Logging and error handling systems", status: "pending" },
{ id: "9", content: "Test suites (unit and integration tests)", status: "pending" },
{ id: "10", content: "Main entry point and final integration", status: "pending" },
]
const todoWriteDocsOverview: ToolPart = {
id: "part-todo-docs-001",
sessionID: SESSION_ID,
messageID: ASST_MSG_ID,
type: "tool",
callID: "call-todo-docs-001",
tool: "todowrite",
state: {
status: "completed",
input: { todos: docsTodos },
output: "Updated 10 todos",
title: "Todo List Updated",
metadata: { todos: docsTodos },
time: { start: now - 3000, end: now - 2800 },
},
}
const todoWritePermission: PermissionRequest = {
id: "perm-todo-001",
sessionID: SESSION_ID,
@@ -609,6 +655,18 @@ export const TodoWriteCompleted: Story = {
},
}
export const TodoWriteDocsOverview: Story = {
name: "TodoWrite — docs overview",
render: () => {
const data = dataWith([todoWriteDocsOverview])
return (
<StoryProviders data={data} sessionID={SESSION_ID}>
<AssistantMessage message={baseAssistantMessage} />
</StoryProviders>
)
},
}
// ---------------------------------------------------------------------------
// 12. Permission dock — edit tool with file patterns
// ---------------------------------------------------------------------------