diff --git a/packages/kilo-vscode/tests/unit/markdown-annotation-layer.test.ts b/packages/kilo-vscode/tests/unit/markdown-annotation-layer.test.ts index 82865d18c43..5939986abf9 100644 --- a/packages/kilo-vscode/tests/unit/markdown-annotation-layer.test.ts +++ b/packages/kilo-vscode/tests/unit/markdown-annotation-layer.test.ts @@ -1,15 +1,20 @@ import { describe, expect, it } from "bun:test" -import { readFileSync } from "node:fs" -import { join } from "node:path" +import { isAnnotationMutation } from "../../webview-ui/agent-manager/MarkdownAnnotationLayer" -const file = join(import.meta.dir, "../../webview-ui/agent-manager/MarkdownAnnotationLayer.tsx") -const src = readFileSync(file, "utf8") +function mutation(target: Node): Pick { + return { target } +} -describe("MarkdownAnnotationLayer", () => { - it("ignores inline annotation DOM mutations while watching rendered Markdown", () => { - expect(src).toMatch(/function isAnnotationMutation[\s\S]*target\.closest\(selector\)/) - expect(src).toMatch( - /new MutationObserver\(\(mutations\) => \{[\s\S]*mutations\.every\(isAnnotationMutation\)[\s\S]*schedule\(\)/, - ) +describe("isAnnotationMutation", () => { + it("matches nested annotation DOM mutations", () => { + const target = { closest: () => ({}) } as unknown as Node + expect(isAnnotationMutation(mutation(target))).toBe(true) + }) + + it("keeps external Markdown DOM mutations observable", () => { + const markdown = { closest: () => null } as unknown as Node + const plain = {} as Node + expect(isAnnotationMutation(mutation(markdown))).toBe(false) + expect(isAnnotationMutation(mutation(plain))).toBe(false) }) }) diff --git a/packages/kilo-vscode/webview-ui/agent-manager/MarkdownAnnotationLayer.tsx b/packages/kilo-vscode/webview-ui/agent-manager/MarkdownAnnotationLayer.tsx index 707b3b37fbb..2ed9ce2fb83 100644 --- a/packages/kilo-vscode/webview-ui/agent-manager/MarkdownAnnotationLayer.tsx +++ b/packages/kilo-vscode/webview-ui/agent-manager/MarkdownAnnotationLayer.tsx @@ -90,9 +90,11 @@ function matches(annotation: DiffLineAnnotation, anchor: Anchor, const selector = ".am-markdown-inline-annotations, .am-markdown-list-annotation, .am-markdown-table-annotation" -function isAnnotationMutation(mutation: MutationRecord): boolean { +// Keep host insertion observable, only nested annotation UI updates are safe to ignore. +export function isAnnotationMutation(mutation: Pick): boolean { const target = mutation.target - if (!(target instanceof Element)) return false + if (!("closest" in target)) return false + if (typeof target.closest !== "function") return false return target.closest(selector) !== null } @@ -213,7 +215,7 @@ export const MarkdownAnnotationLayer: Component = if (!root) return observer?.disconnect() observer = new MutationObserver((mutations) => { - if (mutations.length > 0 && mutations.every(isAnnotationMutation)) return + if (mutations.every(isAnnotationMutation)) return schedule() }) observer.observe(root, { childList: true, subtree: true })