summaryrefslogtreecommitdiffhomepage
path: root/packages/ui/src/components/session-diff.ts
diff options
context:
space:
mode:
authorDax <[email protected]>2026-04-07 19:48:23 -0400
committerGitHub <[email protected]>2026-04-07 19:48:23 -0400
commitb7fab49b64275b83bcec8200d7492fc5d15ffe06 (patch)
treed9dadf65ca69eb4b8fe75654eb15666ee2b23774 /packages/ui/src/components/session-diff.ts
parent463318486f94fa20e8d864d77708a347fa8423e3 (diff)
downloadopencode-b7fab49b64275b83bcec8200d7492fc5d15ffe06.tar.gz
opencode-b7fab49b64275b83bcec8200d7492fc5d15ffe06.zip
refactor(snapshot): store unified patches in file diffs (#21244)
Co-authored-by: Adam <[email protected]>
Diffstat (limited to 'packages/ui/src/components/session-diff.ts')
-rw-r--r--packages/ui/src/components/session-diff.ts83
1 files changed, 83 insertions, 0 deletions
diff --git a/packages/ui/src/components/session-diff.ts b/packages/ui/src/components/session-diff.ts
new file mode 100644
index 000000000..cc2b1ce52
--- /dev/null
+++ b/packages/ui/src/components/session-diff.ts
@@ -0,0 +1,83 @@
+import { parsePatchFiles, type FileDiffMetadata } from "@pierre/diffs"
+import { sampledChecksum } from "@opencode-ai/util/encode"
+import { formatPatch, structuredPatch } from "diff"
+import type { SnapshotFileDiff, VcsFileDiff } from "@opencode-ai/sdk/v2"
+
+type LegacyDiff = {
+ file: string
+ patch?: string
+ before?: string
+ after?: string
+ additions: number
+ deletions: number
+ status?: "added" | "deleted" | "modified"
+}
+
+type ReviewDiff = SnapshotFileDiff | VcsFileDiff | LegacyDiff
+
+export type ViewDiff = {
+ file: string
+ patch: string
+ additions: number
+ deletions: number
+ status?: "added" | "deleted" | "modified"
+ fileDiff: FileDiffMetadata
+}
+
+const cache = new Map<string, FileDiffMetadata>()
+
+function empty(file: string, key: string) {
+ return {
+ name: file,
+ type: "change",
+ hunks: [],
+ splitLineCount: 0,
+ unifiedLineCount: 0,
+ isPartial: true,
+ deletionLines: [],
+ additionLines: [],
+ cacheKey: key,
+ } satisfies FileDiffMetadata
+}
+
+function patch(diff: ReviewDiff) {
+ if (typeof diff.patch === "string") return diff.patch
+ return formatPatch(
+ structuredPatch(
+ diff.file,
+ diff.file,
+ "before" in diff && typeof diff.before === "string" ? diff.before : "",
+ "after" in diff && typeof diff.after === "string" ? diff.after : "",
+ "",
+ "",
+ { context: Number.MAX_SAFE_INTEGER },
+ ),
+ )
+}
+
+function file(file: string, patch: string) {
+ const hit = cache.get(patch)
+ if (hit) return hit
+
+ const key = sampledChecksum(patch) ?? file
+ const value = parsePatchFiles(patch, key).flatMap((item) => item.files)[0] ?? empty(file, key)
+ cache.set(patch, value)
+ return value
+}
+
+export function normalize(diff: ReviewDiff): ViewDiff {
+ const next = patch(diff)
+ return {
+ file: diff.file,
+ patch: next,
+ additions: diff.additions,
+ deletions: diff.deletions,
+ status: diff.status,
+ fileDiff: file(diff.file, next),
+ }
+}
+
+export function text(diff: ViewDiff, side: "deletions" | "additions") {
+ if (side === "deletions") return diff.fileDiff.deletionLines.join("")
+ return diff.fileDiff.additionLines.join("")
+}