mirror of
https://github.com/Kilo-Org/kilocode.git
synced 2026-08-28 19:11:03 +08:00
feat: Add diff view for edit tool
This commit is contained in:
committed by
Johnny Eric Amancio
parent
83b153d6bb
commit
e3476be3bb
@@ -2022,11 +2022,28 @@ ToolRegistry.register({
|
||||
const filename = () => getFilename(props.input.filePath ?? "")
|
||||
const pending = () => busy(props.status)
|
||||
const reveal = useToolReveal(pending, () => props.reveal !== false)
|
||||
const before = () => props.metadata?.filediff?.before ?? props.input.oldString ?? ""
|
||||
const after = () => props.metadata?.filediff?.after ?? props.input.newString ?? ""
|
||||
const canOpenDiff = () => !!data.openDiff && !!path() && (before() !== "" || after() !== "")
|
||||
const canOpenFile = () => !!data.openFile && !!path()
|
||||
|
||||
const handleFileClick = (e: MouseEvent) => {
|
||||
if (!data.openFile || !props.input.filePath) return
|
||||
e.stopPropagation()
|
||||
data.openFile(props.input.filePath)
|
||||
|
||||
if (canOpenDiff()) {
|
||||
data.openDiff!({
|
||||
file: path(),
|
||||
before: before(),
|
||||
after: after(),
|
||||
additions: props.metadata?.filediff?.additions ?? 0,
|
||||
deletions: props.metadata?.filediff?.deletions ?? 0,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
if (canOpenFile()) {
|
||||
data.openFile!(path())
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
@@ -2049,7 +2066,7 @@ ToolRegistry.register({
|
||||
path={props.input.filePath?.includes("/") ? getDirectory(props.input.filePath!) : undefined}
|
||||
changes={props.metadata.filediff}
|
||||
animate={reveal()}
|
||||
onClick={data.openFile && props.input.filePath ? handleFileClick : undefined}
|
||||
onClick={canOpenDiff() || canOpenFile() ? handleFileClick : undefined}
|
||||
/>
|
||||
)}
|
||||
</Show>
|
||||
@@ -2072,12 +2089,12 @@ ToolRegistry.register({
|
||||
component={fileComponent}
|
||||
mode="diff"
|
||||
before={{
|
||||
name: props.metadata?.filediff?.file || props.input.filePath,
|
||||
contents: props.metadata?.filediff?.before || props.input.oldString,
|
||||
name: path(),
|
||||
contents: before(),
|
||||
}}
|
||||
after={{
|
||||
name: props.metadata?.filediff?.file || props.input.filePath,
|
||||
contents: props.metadata?.filediff?.after || props.input.newString,
|
||||
name: path(),
|
||||
contents: after(),
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -2,6 +2,9 @@ import { describe, it, expect } from "bun:test"
|
||||
import { readFileSync } from "node:fs"
|
||||
import { join } from "node:path"
|
||||
|
||||
const APP_FILE = join(__dirname, "..", "..", "webview-ui", "src", "App.tsx")
|
||||
const src = readFileSync(APP_FILE, "utf8")
|
||||
|
||||
/**
|
||||
* Static guard against the perf regression fixed in this PR.
|
||||
*
|
||||
@@ -28,9 +31,6 @@ import { join } from "node:path"
|
||||
* `tests/webview-reactivity/databridge-reactivity.test.ts`.
|
||||
*/
|
||||
describe("DataBridge shape (perf regression guard)", () => {
|
||||
const path = join(__dirname, "..", "..", "webview-ui", "src", "App.tsx")
|
||||
const src = readFileSync(path, "utf8")
|
||||
|
||||
it("DataBridge exists in App.tsx", () => {
|
||||
expect(src).toMatch(/export const DataBridge/)
|
||||
})
|
||||
@@ -59,3 +59,16 @@ describe("DataBridge shape (perf regression guard)", () => {
|
||||
expect(block).toMatch(/get\s+part\s*\(\s*\)\s*\{/)
|
||||
})
|
||||
})
|
||||
|
||||
describe("DataBridge openDiff wiring (regression guard)", () => {
|
||||
const openDiffBlock = () => {
|
||||
const match = src.match(/const\s+openDiff\s*=\s*\(diff:\s*\{[\s\S]*?\n\s*\}\n\n\s*const\s+openUrl/)
|
||||
expect(match).toBeTruthy()
|
||||
return match![0]
|
||||
}
|
||||
|
||||
it("wires openDiff to the openDiffVirtual webview message", () => {
|
||||
expect(openDiffBlock()).toMatch(/postMessage\(\{\s*type:\s*["']openDiffVirtual["']\s*,\s*diff\s*\}\)/)
|
||||
expect(src).toContain("onOpenDiff={openDiff}")
|
||||
})
|
||||
})
|
||||
|
||||
@@ -21,6 +21,7 @@ const MONOREPO_ROOT = path.resolve(import.meta.dir, "../../../..")
|
||||
const KILO_UI_DIR = path.join(MONOREPO_ROOT, "packages/kilo-ui")
|
||||
const DATA_CONTEXT_FILE = path.join(MONOREPO_ROOT, "packages/ui/src/context/data.tsx")
|
||||
const MESSAGE_PART_FILE = path.join(MONOREPO_ROOT, "packages/ui/src/components/message-part.tsx")
|
||||
const KILO_MESSAGE_PART_FILE = path.join(MONOREPO_ROOT, "packages/kilo-ui/src/components/message-part.tsx")
|
||||
|
||||
function check(code: string): { ok: boolean; output: string } {
|
||||
const result = Bun.spawnSync(["bun", "--conditions=browser", "-e", code], {
|
||||
@@ -118,6 +119,27 @@ describe("DataProvider contract (runtime)", () => {
|
||||
expect(src).toContain("OpenFileFn")
|
||||
expect(src).toMatch(/openFile:\s*props\.onOpenFile/)
|
||||
})
|
||||
|
||||
it("DataProvider accepts onOpenDiff prop and exports OpenDiffFn (source)", () => {
|
||||
// onOpenDiff and OpenDiffFn are `kilocode_change` additions — TypeScript types
|
||||
// erased at runtime, so we verify via source analysis
|
||||
const src = fs.readFileSync(DATA_CONTEXT_FILE, "utf-8")
|
||||
expect(src).toContain("onOpenDiff")
|
||||
expect(src).toContain("OpenDiffFn")
|
||||
expect(src).toMatch(/openDiff:\s*props\.onOpenDiff/)
|
||||
})
|
||||
})
|
||||
|
||||
describe("Edit tool diff-first click contract (source)", () => {
|
||||
const src = fs.readFileSync(KILO_MESSAGE_PART_FILE, "utf-8")
|
||||
|
||||
const editBlockMatch = src.match(/ToolRegistry\.register\(\{\s*name:\s*"edit"[\s\S]*?(?=ToolRegistry\.register\(|$)/)
|
||||
const editBlock = editBlockMatch?.[0] ?? ""
|
||||
|
||||
it("edit tool derives before/after content from filediff or input", () => {
|
||||
expect(editBlock).toMatch(/filediff\?\.before\s*\?\?.*oldString/)
|
||||
expect(editBlock).toMatch(/filediff\?\.after\s*\?\?.*newString/)
|
||||
})
|
||||
})
|
||||
|
||||
describe("BasicTool export contract (runtime)", () => {
|
||||
|
||||
@@ -132,6 +132,10 @@ export const DataBridge: Component<{ children: any }> = (props) => {
|
||||
vscode.postMessage({ type: "openFile", filePath, line, column })
|
||||
}
|
||||
|
||||
const openDiff = (diff: { file: string; before: string; after: string; additions: number; deletions: number }) => {
|
||||
vscode.postMessage({ type: "openDiffVirtual", diff })
|
||||
}
|
||||
|
||||
const openUrl = (url: string) => {
|
||||
vscode.postMessage({ type: "openExternal", url })
|
||||
}
|
||||
@@ -151,6 +155,7 @@ export const DataBridge: Component<{ children: any }> = (props) => {
|
||||
onQuestionReply={reply}
|
||||
onQuestionReject={reject}
|
||||
onOpenFile={open}
|
||||
onOpenDiff={openDiff}
|
||||
onOpenUrl={openUrl}
|
||||
>
|
||||
{props.children}
|
||||
|
||||
@@ -30,9 +30,19 @@ export type NavigateToSessionFn = (sessionID: string) => void
|
||||
|
||||
export type SessionHrefFn = (sessionID: string) => string
|
||||
|
||||
export type OpenFileFn = (filePath: string, line?: number, column?: number) => void // kilocode_change
|
||||
// kilocode_change start
|
||||
export type OpenFileFn = (filePath: string, line?: number, column?: number) => void
|
||||
|
||||
export type OpenUrlFn = (url: string) => void // kilocode_change
|
||||
export type OpenDiffFn = (diff: {
|
||||
file: string
|
||||
before: string
|
||||
after: string
|
||||
additions: number
|
||||
deletions: number
|
||||
}) => void
|
||||
|
||||
export type OpenUrlFn = (url: string) => void
|
||||
// kilocode_change end
|
||||
|
||||
export const { use: useData, provider: DataProvider } = createSimpleContext({
|
||||
name: "Data",
|
||||
@@ -42,6 +52,7 @@ export const { use: useData, provider: DataProvider } = createSimpleContext({
|
||||
onNavigateToSession?: NavigateToSessionFn
|
||||
onSessionHref?: SessionHrefFn
|
||||
onOpenFile?: OpenFileFn // kilocode_change
|
||||
onOpenDiff?: OpenDiffFn // kilocode_change
|
||||
onOpenUrl?: OpenUrlFn // kilocode_change
|
||||
}) => {
|
||||
return {
|
||||
@@ -54,6 +65,7 @@ export const { use: useData, provider: DataProvider } = createSimpleContext({
|
||||
navigateToSession: props.onNavigateToSession,
|
||||
sessionHref: props.onSessionHref,
|
||||
openFile: props.onOpenFile, // kilocode_change
|
||||
openDiff: props.onOpenDiff, // kilocode_change
|
||||
openUrl: props.onOpenUrl, // kilocode_change
|
||||
}
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user