feat(ui): add Git Changes tab
Adds repo-wide git changes view with refresh controls and keeps right drawer shortcuts fixed while tabs scroll.
This commit is contained in:
@@ -86,10 +86,12 @@ export const instanceMessages = {
|
||||
"instanceShell.empty.description": "Select a session to view messages",
|
||||
|
||||
"instanceShell.rightPanel.title": "Status Panel",
|
||||
"instanceShell.rightPanel.tabs.changes": "Changes",
|
||||
"instanceShell.rightPanel.tabs.changes": "Session Changes",
|
||||
"instanceShell.rightPanel.tabs.gitChanges": "Git Changes",
|
||||
"instanceShell.rightPanel.tabs.files": "Files",
|
||||
"instanceShell.rightPanel.tabs.status": "Status",
|
||||
"instanceShell.rightPanel.tabs.ariaLabel": "Right panel tabs",
|
||||
"instanceShell.rightPanel.actions.refresh": "Refresh",
|
||||
"instanceShell.rightPanel.sections.sessionChanges": "Session Changes",
|
||||
"instanceShell.rightPanel.sections.plan": "Plan",
|
||||
"instanceShell.rightPanel.sections.backgroundProcesses": "Background Shells",
|
||||
|
||||
86
packages/ui/src/lib/unified-diff-reverse.ts
Normal file
86
packages/ui/src/lib/unified-diff-reverse.ts
Normal file
@@ -0,0 +1,86 @@
|
||||
import { applyPatch, parsePatch } from "diff"
|
||||
|
||||
type ParsedPatchIndex = ReturnType<typeof parsePatch>[number]
|
||||
type ParsedHunk = ParsedPatchIndex["hunks"][number]
|
||||
|
||||
type SdkPatch = {
|
||||
oldFileName: string
|
||||
newFileName: string
|
||||
oldHeader?: string
|
||||
newHeader?: string
|
||||
hunks: Array<{
|
||||
oldStart: number
|
||||
oldLines: number
|
||||
newStart: number
|
||||
newLines: number
|
||||
lines: Array<string>
|
||||
}>
|
||||
index?: string
|
||||
}
|
||||
|
||||
function invertPatchLine(line: string): string {
|
||||
if (!line) return line
|
||||
const op = line[0]
|
||||
if (op === "+") return `-${line.slice(1)}`
|
||||
if (op === "-") return `+${line.slice(1)}`
|
||||
return line
|
||||
}
|
||||
|
||||
function reverseParsedHunk(hunk: ParsedHunk): ParsedHunk {
|
||||
return {
|
||||
oldStart: hunk.newStart,
|
||||
oldLines: hunk.newLines,
|
||||
newStart: hunk.oldStart,
|
||||
newLines: hunk.oldLines,
|
||||
lines: hunk.lines.map(invertPatchLine),
|
||||
linedelimiters: Array.isArray((hunk as any).linedelimiters) ? (hunk as any).linedelimiters : [],
|
||||
} as ParsedHunk
|
||||
}
|
||||
|
||||
function reverseParsedIndex(index: ParsedPatchIndex): ParsedPatchIndex {
|
||||
const hunks = Array.isArray(index.hunks) ? index.hunks : []
|
||||
return {
|
||||
...index,
|
||||
oldFileName: (index as any).newFileName,
|
||||
newFileName: (index as any).oldFileName,
|
||||
oldHeader: (index as any).newHeader,
|
||||
newHeader: (index as any).oldHeader,
|
||||
hunks: hunks.map(reverseParsedHunk),
|
||||
} as ParsedPatchIndex
|
||||
}
|
||||
|
||||
export function buildUnifiedDiffFromSdkPatch(patch: SdkPatch): string {
|
||||
const oldName = patch.oldFileName || "a/file"
|
||||
const newName = patch.newFileName || "b/file"
|
||||
const oldHeader = patch.oldHeader ? `\t${patch.oldHeader}` : ""
|
||||
const newHeader = patch.newHeader ? `\t${patch.newHeader}` : ""
|
||||
|
||||
const lines: string[] = []
|
||||
if (patch.index) {
|
||||
// jsdiff can parse arbitrary metadata lines before file headers.
|
||||
lines.push(`Index: ${patch.index}`)
|
||||
}
|
||||
lines.push(`--- ${oldName}${oldHeader}`)
|
||||
lines.push(`+++ ${newName}${newHeader}`)
|
||||
for (const hunk of patch.hunks || []) {
|
||||
const oldRange = hunk.oldLines === 1 ? `${hunk.oldStart}` : `${hunk.oldStart},${hunk.oldLines}`
|
||||
const newRange = hunk.newLines === 1 ? `${hunk.newStart}` : `${hunk.newStart},${hunk.newLines}`
|
||||
lines.push(`@@ -${oldRange} +${newRange} @@`)
|
||||
for (const line of hunk.lines || []) {
|
||||
lines.push(line)
|
||||
}
|
||||
}
|
||||
return `${lines.join("\n")}\n`
|
||||
}
|
||||
|
||||
export function tryReverseApplyUnifiedDiff(afterText: string, diffText: string): string | null {
|
||||
const normalized = (diffText ?? "").trim()
|
||||
if (!normalized) return null
|
||||
|
||||
const parsed = parsePatch(diffText)
|
||||
if (!Array.isArray(parsed) || parsed.length === 0) return null
|
||||
|
||||
const reversed = reverseParsedIndex(parsed[0])
|
||||
const result = applyPatch(afterText ?? "", reversed)
|
||||
return typeof result === "string" ? result : null
|
||||
}
|
||||
Reference in New Issue
Block a user