Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/todo-card-render.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"kilo-code": patch
---

Speed up rendering of to-do list tool cards in the chat.
54 changes: 51 additions & 3 deletions packages/kilo-ui/src/components/message-part.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {
createEffect,
createMemo,
createSignal,
createUniqueId,
For,
Match,
onCleanup,
Expand Down Expand Up @@ -46,7 +47,6 @@ import { Card } from "./card"
import { Collapsible } from "./collapsible"
import { FileIcon } from "./file-icon"
import { Icon } from "./icon"
import { Checkbox } from "./checkbox"
import { DiffChanges } from "./diff-changes"
import { Markdown } from "./markdown"
import { ImagePreview } from "./image-preview"
Expand Down Expand Up @@ -3296,6 +3296,54 @@ ToolRegistry.register({
},
})

function TodoCheckbox(props: { checked: boolean; children: JSX.Element }) {
const id = createUniqueId()
const state = () => (props.checked ? "" : undefined)
return (
<div role="group" data-component="checkbox" data-readonly="" data-checked={state()}>
<input
type="checkbox"
id={`${id}-input`}
data-slot="checkbox-checkbox-input"
data-readonly=""
data-checked={state()}
Comment thread
WebReflection marked this conversation as resolved.
checked={props.checked}
readOnly
aria-readonly="true"
aria-labelledby={`${id}-label`}
onChange={(event) => {
event.currentTarget.checked = props.checked
}}
/>
<div data-slot="checkbox-checkbox-control" data-readonly="" data-checked={state()}>
<Show when={props.checked}>
<div data-slot="checkbox-checkbox-indicator" data-readonly="" data-checked="">
<svg viewBox="0 0 12 12" fill="none" width="10" height="10" xmlns="http://www.w3.org/2000/svg">
<path
d="M3 7.17905L5.02703 8.85135L9 3.5"
stroke="currentColor"
stroke-width="1.5"
stroke-linecap="square"
/>
</svg>
</div>
</Show>
</div>
<div data-slot="checkbox-checkbox-content">
<label
id={`${id}-label`}
for={`${id}-input`}
data-slot="checkbox-checkbox-label"
data-readonly=""
data-checked={state()}
Comment thread
WebReflection marked this conversation as resolved.
>
{props.children}
</label>
</div>
</div>
)
}

ToolRegistry.register({
name: "todowrite",
render(props) {
Expand Down Expand Up @@ -3341,15 +3389,15 @@ ToolRegistry.register({
</Show>
<For each={shown()}>
{(todo: TodoItem) => (
<Checkbox readOnly checked={todo.status === "completed"}>
<TodoCheckbox checked={todo.status === "completed"}>
<span
data-slot="message-part-todo-content"
data-completed={todo.status === "completed" ? "completed" : undefined}
data-changed={todo.changed ? "changed" : undefined}
>
{todo.content}
</span>
</Checkbox>
</TodoCheckbox>
)}
</For>
<Show when={view()?.mode === "compact" && (view()?.hiddenAfter ?? 0) > 0}>
Expand Down
23 changes: 23 additions & 0 deletions packages/kilo-vscode/tests/accessibility.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -455,4 +455,27 @@ test.describe("webview accessibility ratchet", () => {
await expect(page.getByRole("dialog")).toHaveAccessibleName(/.+/)
}
})

test("To-do cards render read-only checkboxes with labeled states", async ({ page }) => {
await open(page, "composite-webview--todo-write-completed")

const card = page.locator('[data-component="todos"]')
const done = card.getByRole("checkbox", { name: "Create a haiku about Jan" })
const active = card.getByRole("checkbox", { name: "Create a poem about Henk" })

await expect(done).toBeChecked()
await expect(active).not.toBeChecked()
await expect(done).toHaveAttribute("aria-readonly", "true")
await expect(active).toHaveAttribute("aria-readonly", "true")
await expect(card.locator('[data-component="checkbox"][data-readonly]')).toHaveCount(2)
await expect(card.locator('[data-slot="checkbox-checkbox-indicator"]')).toHaveCount(1)
await expect(card.locator('[data-slot="message-part-todo-content"][data-completed="completed"]')).toHaveText(
"Create a haiku about Jan",
)

await active.focus()
await page.keyboard.press("Space")
await expect(active).not.toBeChecked()
await expect(done).toBeChecked()
})
})
Loading