From c933504d9c7074104d74ee1e15fa3b09d1afa84e Mon Sep 17 00:00:00 2001
From: Brendan Allan <14191578+Brendonovich@users.noreply.github.com>
Date: Mon, 11 May 2026 17:21:59 +0800
Subject: [PATCH] fix(ui): better handle patch file support when rendering
patch/edit tools (#26828)
---
packages/ui/src/components/message-part.tsx | 46 +++++++++++++++------
packages/ui/src/components/session-diff.ts | 18 +++++---
2 files changed, 46 insertions(+), 18 deletions(-)
diff --git a/packages/ui/src/components/message-part.tsx b/packages/ui/src/components/message-part.tsx
index 7a7d5b15fa..35598a170c 100644
--- a/packages/ui/src/components/message-part.tsx
+++ b/packages/ui/src/components/message-part.tsx
@@ -264,6 +264,7 @@ function getDirectory(path: string | undefined) {
}
import type { IconProps } from "./icon"
+import { normalize } from "./session-diff"
export type ToolInfo = {
icon: IconProps["name"]
@@ -1878,6 +1879,31 @@ ToolRegistry.register({
const path = createMemo(() => props.metadata?.filediff?.file || props.input.filePath || "")
const filename = () => getFilename(props.input.filePath ?? "")
const pending = () => props.status === "pending" || props.status === "running"
+
+ const fileCompProps = createMemo(() => {
+ try {
+ if (props.metadata?.filediff) {
+ const diff = normalize({
+ ...props.metadata?.filediff,
+ status: "modified",
+ })
+ const fileDiff = diff.fileDiff
+ if (fileDiff) return { fileDiff, hunkSeparators: fileDiff.isPartial ? "simple" : "line-info-basic" }
+ }
+ } catch {}
+
+ return {
+ before: {
+ name: props.metadata?.filediff?.file || props.input.filePath,
+ contents: props.metadata?.filediff?.before || props.input.oldString || "",
+ },
+ after: {
+ name: props.metadata?.filediff?.file || props.input.filePath,
+ contents: props.metadata?.filediff?.after || props.input.newString || "",
+ },
+ }
+ })
+
return (
-
+
@@ -2111,7 +2126,12 @@ ToolRegistry.register({
-
+
diff --git a/packages/ui/src/components/session-diff.ts b/packages/ui/src/components/session-diff.ts
index 60dcffd83d..52ef0a4bbc 100644
--- a/packages/ui/src/components/session-diff.ts
+++ b/packages/ui/src/components/session-diff.ts
@@ -1,4 +1,4 @@
-import { parseDiffFromFile, type FileDiffMetadata } from "@pierre/diffs"
+import { parseDiffFromFile, parsePatchFiles, type FileDiffMetadata } from "@pierre/diffs"
import { formatPatch, parsePatch, structuredPatch } from "diff"
import type { SnapshotFileDiff, VcsFileDiff } from "@opencode-ai/sdk/v2"
@@ -34,6 +34,8 @@ function patch(diff: ReviewDiff) {
const afterLines: Array<{ text: string; newline: boolean }> = []
let previous: "-" | "+" | " " | undefined
+ const patchIsPartial = patch.hunks.every((h) => h.oldStart > 1)
+
for (const hunk of patch.hunks) {
for (const line of hunk.lines) {
if (line.startsWith("\\")) {
@@ -67,9 +69,10 @@ function patch(diff: ReviewDiff) {
before: beforeLines.map((line) => line.text + (line.newline ? "\n" : "")).join(""),
after: afterLines.map((line) => line.text + (line.newline ? "\n" : "")).join(""),
patch: diff.patch,
+ patchIsPartial,
}
} catch {
- return { before: "", after: "", patch: diff.patch }
+ return { before: "", after: "", patch: diff.patch, patchIsPartial: false }
}
}
return {
@@ -86,27 +89,32 @@ function patch(diff: ReviewDiff) {
{ context: Number.MAX_SAFE_INTEGER },
),
),
+ patchIsPartial: false,
}
}
-function file(file: string, patch: string, before: string, after: string) {
+function file(file: string, patch: string, before: string, after: string, partial = false) {
const hit = cache.get(patch)
if (hit) return hit
- const value = parseDiffFromFile({ name: file, contents: before }, { name: file, contents: after })
+ let value: FileDiffMetadata | undefined
+ if (partial) value = parsePatchFiles(patch)[0]?.files[0]
+ if (value === undefined) value = parseDiffFromFile({ name: file, contents: before }, { name: file, contents: after })
+
cache.set(patch, value)
return value
}
export function normalize(diff: ReviewDiff): ViewDiff {
const next = patch(diff)
+ const fileDiff = file(diff.file, next.patch, next.before, next.after, next.patchIsPartial)
return {
file: diff.file,
patch: next.patch,
additions: diff.additions,
deletions: diff.deletions,
status: diff.status,
- fileDiff: file(diff.file, next.patch, next.before, next.after),
+ fileDiff,
}
}