Download src/agents/modes/interactive/components/diff.ts from SaylorTwift/openclaw: direct link, hf CLI and curl.
- Browser
- Download file 4.94 kB
-
https://huggingface.co/SaylorTwift/openclaw/resolve/main/src/agents/modes/interactive/components/diff.ts
- Command line
-
hf download hf://SaylorTwift/openclaw/src/agents/modes/interactive/components/diff.ts
-
curl -L -o diff.ts https://huggingface.co/SaylorTwift/openclaw/resolve/main/src/agents/modes/interactive/components/diff.ts
4.94 kB
| /** | |
| * Interactive terminal diff renderer. | |
| * | |
| * Produces colored line and intra-line highlights for the Pi TUI review surfaces. | |
| */ | |
| import { diffWords } from "diff"; | |
| import { interactiveAgentTheme as theme } from "../theme/theme.js"; | |
| /** | |
| * Parse diff line to extract prefix, line number, and content. | |
| * Format: "+123 content" or "-123 content" or " 123 content" or " ..." | |
| */ | |
| function parseDiffLine(line: string): { prefix: string; lineNum: string; content: string } | null { | |
| const match = line.match(/^([+-\s])(\s*\d*)\s(.*)$/); | |
| if (!match) { | |
| return null; | |
| } | |
| const [, prefix, lineNum, content] = match; | |
| return prefix !== undefined && lineNum !== undefined && content !== undefined | |
| ? { prefix, lineNum, content } | |
| : null; | |
| } | |
| /** | |
| * Replace tabs with spaces for consistent rendering. | |
| */ | |
| function replaceTabs(text: string): string { | |
| return text.replace(/\t/g, " "); | |
| } | |
| /** | |
| * Compute word-level diff and render with inverse on changed parts. | |
| * Uses diffWords which groups whitespace with adjacent words for cleaner highlighting. | |
| * Strips leading whitespace from inverse to avoid highlighting indentation. | |
| */ | |
| function renderIntraLineDiff( | |
| oldContent: string, | |
| newContent: string, | |
| ): { removedLine: string; addedLine: string } { | |
| let removedLine = ""; | |
| let addedLine = ""; | |
| const seen = { added: false, removed: false }; | |
| for (const part of diffWords(oldContent, newContent)) { | |
| const kind = part.added ? "added" : part.removed ? "removed" : undefined; | |
| let value = part.value; | |
| if (kind) { | |
| const changed = seen[kind] ? value : value.trimStart(); | |
| const leadingWhitespace = value.slice(0, value.length - changed.length); | |
| value = leadingWhitespace + (changed ? theme.inverse(changed) : ""); | |
| seen[kind] = true; | |
| } | |
| if (!part.added) { | |
| removedLine += value; | |
| } | |
| if (!part.removed) { | |
| addedLine += value; | |
| } | |
| } | |
| return { removedLine, addedLine }; | |
| } | |
| /** | |
| * Render a diff string with colored lines and intra-line change highlighting. | |
| * - Context lines: dim/gray | |
| * - Removed lines: red, with inverse on changed tokens | |
| * - Added lines: green, with inverse on changed tokens | |
| */ | |
| export function renderDiff(diffText: string): string { | |
| const lines = diffText.split("\n"); | |
| const result: string[] = []; | |
| let i = 0; | |
| while (i < lines.length) { | |
| const line = lines.at(i); | |
| if (line === undefined) { | |
| break; | |
| } | |
| const parsed = parseDiffLine(line); | |
| if (!parsed) { | |
| result.push(theme.fg("toolDiffContext", line)); | |
| i++; | |
| continue; | |
| } | |
| if (parsed.prefix === "-") { | |
| // Collect consecutive removed lines | |
| const removedLines: { lineNum: string; content: string }[] = []; | |
| while (i < lines.length) { | |
| const currentLine = lines.at(i); | |
| const p = currentLine === undefined ? null : parseDiffLine(currentLine); | |
| if (!p || p.prefix !== "-") { | |
| break; | |
| } | |
| removedLines.push({ lineNum: p.lineNum, content: p.content }); | |
| i++; | |
| } | |
| // Collect consecutive added lines | |
| const addedLines: { lineNum: string; content: string }[] = []; | |
| while (i < lines.length) { | |
| const currentLine = lines.at(i); | |
| const p = currentLine === undefined ? null : parseDiffLine(currentLine); | |
| if (!p || p.prefix !== "+") { | |
| break; | |
| } | |
| addedLines.push({ lineNum: p.lineNum, content: p.content }); | |
| i++; | |
| } | |
| // Only do intra-line diffing when there's exactly one removed and one added line | |
| // (indicating a single line modification). Otherwise, show lines as-is. | |
| if (removedLines.length === 1 && addedLines.length === 1) { | |
| const removed = removedLines[0]; | |
| const added = addedLines[0]; | |
| if (!removed || !added) { | |
| continue; | |
| } | |
| const { removedLine, addedLine } = renderIntraLineDiff( | |
| replaceTabs(removed.content), | |
| replaceTabs(added.content), | |
| ); | |
| result.push(theme.fg("toolDiffRemoved", `-${removed.lineNum} ${removedLine}`)); | |
| result.push(theme.fg("toolDiffAdded", `+${added.lineNum} ${addedLine}`)); | |
| } else { | |
| // Show all removed lines first, then all added lines | |
| for (const removed of removedLines) { | |
| result.push( | |
| theme.fg("toolDiffRemoved", `-${removed.lineNum} ${replaceTabs(removed.content)}`), | |
| ); | |
| } | |
| for (const added of addedLines) { | |
| result.push(theme.fg("toolDiffAdded", `+${added.lineNum} ${replaceTabs(added.content)}`)); | |
| } | |
| } | |
| } else if (parsed.prefix === "+") { | |
| // Standalone added line | |
| result.push(theme.fg("toolDiffAdded", `+${parsed.lineNum} ${replaceTabs(parsed.content)}`)); | |
| i++; | |
| } else { | |
| // Context line | |
| result.push(theme.fg("toolDiffContext", ` ${parsed.lineNum} ${replaceTabs(parsed.content)}`)); | |
| i++; | |
| } | |
| } | |
| return result.join("\n"); | |
| } | |