diff --git a/.changeset/rendered-document-inspector.md b/.changeset/rendered-document-inspector.md index a625b494b3..90049a3491 100644 --- a/.changeset/rendered-document-inspector.md +++ b/.changeset/rendered-document-inspector.md @@ -2,4 +2,4 @@ "kilo-code": minor --- -Add an Agent Manager document inspector that previews Markdown and text files, with inline Markdown review comments that can be sent to the agent. +Add an Agent Manager document inspector that previews Markdown files with inline review comments that can be sent to the agent, while source files open in the VS Code editor. diff --git a/packages/kilo-vscode/tests/unit/agent-manager-documents.test.ts b/packages/kilo-vscode/tests/unit/agent-manager-documents.test.ts index 4d73a993e9..aeeaeccc52 100644 --- a/packages/kilo-vscode/tests/unit/agent-manager-documents.test.ts +++ b/packages/kilo-vscode/tests/unit/agent-manager-documents.test.ts @@ -1,6 +1,11 @@ import { describe, expect, it } from "bun:test" import { createRoot, createSignal } from "solid-js" -import { createDocumentComments, createDocuments } from "../../webview-ui/documents/state" +import { + createDocumentComments, + createDocuments, + handleDocumentOpen, + isMarkdownPath, +} from "../../webview-ui/documents/state" import type { AgentManagerDocumentMessage } from "../../webview-ui/src/types/messages" describe("Agent Manager document state", () => { @@ -69,4 +74,35 @@ describe("Agent Manager document state", () => { dispose() }) }) + + it("keeps Markdown in the document inspector and opens source files in VS Code", () => { + expect(isMarkdownPath(".kilo/plans/feature.md")).toBe(true) + expect(isMarkdownPath("docs/architecture.MDX")).toBe(true) + expect(isMarkdownPath("src/index.ts")).toBe(false) + + const opened: unknown[] = [] + const native: unknown[] = [] + const markdown = new CustomEvent("kilo:open-file", { + cancelable: true, + detail: { filePath: ".kilo/plans/feature.md", sessionID: "wt-a", line: 4, column: 2 }, + }) + handleDocumentOpen(markdown, (...args) => { + opened.push(args) + return true + }) + expect(markdown.defaultPrevented).toBe(true) + expect(opened).toEqual([[".kilo/plans/feature.md", "wt-a", 4, 2]]) + + const source = new CustomEvent("kilo:open-file", { + cancelable: true, + detail: { filePath: "src/index.ts", sessionID: "wt-a", line: 8, column: 3 }, + }) + handleDocumentOpen( + source, + () => false, + (...args) => native.push(args), + ) + expect(source.defaultPrevented).toBe(true) + expect(native).toEqual([["src/index.ts", 8, 3, "wt-a"]]) + }) }) diff --git a/packages/kilo-vscode/webview-ui/diff-viewer/MarkdownAnnotationLayer.tsx b/packages/kilo-vscode/webview-ui/diff-viewer/MarkdownAnnotationLayer.tsx index 70324d1ca0..317baff475 100644 --- a/packages/kilo-vscode/webview-ui/diff-viewer/MarkdownAnnotationLayer.tsx +++ b/packages/kilo-vscode/webview-ui/diff-viewer/MarkdownAnnotationLayer.tsx @@ -199,14 +199,14 @@ export const MarkdownAnnotationLayer: Component = }) createEffect(() => { - const root = props.root() - if (!root) return + const pane = props.pane() + if (!pane) return observer?.disconnect() observer = new MutationObserver((mutations) => { if (mutations.every(isAnnotationMutation)) return schedule() }) - observer.observe(root, { childList: true, subtree: true }) + observer.observe(pane, { childList: true, subtree: true }) }) onCleanup(() => { diff --git a/packages/kilo-vscode/webview-ui/documents/state.ts b/packages/kilo-vscode/webview-ui/documents/state.ts index c78307898c..6df50c9020 100644 --- a/packages/kilo-vscode/webview-ui/documents/state.ts +++ b/packages/kilo-vscode/webview-ui/documents/state.ts @@ -175,8 +175,11 @@ export function createDocumentInspector( openPanel() return true } + const openFile = (file: string, line?: number, column?: number, sessionId = context()) => { + if (sessionId) vscode.postMessage({ type: "agentManager.openFile", sessionId, filePath: file, line, column }) + } onMount(() => { - const handler = (event: Event) => handleDocumentOpen(event, open) + const handler = (event: Event) => handleDocumentOpen(event, open, openFile) const message = vscode.onMessage((item) => { if (item.type === "document.result" || item.type === "agentManager.document") documents.onMessage(item) }) @@ -191,10 +194,6 @@ export function createDocumentInspector( // Tabs are keyed per worktree, so this hides itself on a worktree with none, // and stays visible while the panel is open so it can still be toggled shut. const available = () => documents.tabs().length > 0 || isOpen() - const openFile = (file: string, line?: number, column?: number) => { - const sessionId = context() - if (sessionId) vscode.postMessage({ type: "agentManager.openFile", sessionId, filePath: file, line, column }) - } const toggle = () => (isOpen() ? closePanel() : open()) return { documents, comments, open, openFile, toggle, available, isOpen, scope } } @@ -202,6 +201,7 @@ export function createDocumentInspector( export function handleDocumentOpen( event: Event, open: (file: string, sessionId?: string, line?: number, column?: number) => boolean, + openFile?: (file: string, line?: number, column?: number, sessionId?: string) => void, ): void { const detail = (event as CustomEvent<{ filePath?: unknown; sessionID?: unknown; line?: unknown; column?: unknown }>) .detail @@ -210,5 +210,11 @@ export function handleDocumentOpen( const sessionId = typeof detail.sessionID === "string" ? detail.sessionID : undefined const line = typeof detail.line === "number" ? detail.line : undefined const column = typeof detail.column === "number" ? detail.column : undefined + if (!isMarkdownPath(file)) { + if (!openFile) return + openFile(file, line, column, sessionId) + event.preventDefault() + return + } if (open(file, sessionId, line, column)) event.preventDefault() }