Initial Release: OpenQode Public Alpha v1.3
This commit is contained in:
126
bin/ui/components/DiffView.mjs
Normal file
126
bin/ui/components/DiffView.mjs
Normal file
@@ -0,0 +1,126 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { Box, Text, useInput } from 'ink';
|
||||
import * as Diff from 'diff';
|
||||
|
||||
const h = React.createElement;
|
||||
|
||||
const DiffView = ({
|
||||
original = '',
|
||||
modified = '',
|
||||
file = 'unknown.js',
|
||||
onApply,
|
||||
onSkip,
|
||||
width = 80,
|
||||
height = 20
|
||||
}) => {
|
||||
// Generate diff objects
|
||||
// [{ value: 'line', added: boolean, removed: boolean }]
|
||||
const diff = Diff.diffLines(original, modified);
|
||||
|
||||
// Scroll state
|
||||
const [scrollTop, setScrollTop] = useState(0);
|
||||
|
||||
// Calculate total lines for scrolling
|
||||
const totalLines = diff.reduce((acc, part) => acc + part.value.split('\n').length - 1, 0);
|
||||
const visibleLines = height - 4; // Header + Footer space
|
||||
|
||||
useInput((input, key) => {
|
||||
if (key.upArrow) {
|
||||
setScrollTop(prev => Math.max(0, prev - 1));
|
||||
}
|
||||
if (key.downArrow) {
|
||||
setScrollTop(prev => Math.min(totalLines - visibleLines, prev + 1));
|
||||
}
|
||||
if (key.pageUp) {
|
||||
setScrollTop(prev => Math.max(0, prev - visibleLines));
|
||||
}
|
||||
if (key.pageDown) {
|
||||
setScrollTop(prev => Math.min(totalLines - visibleLines, prev + visibleLines));
|
||||
}
|
||||
|
||||
if (input === 'y' || input === 'Y' || key.return) {
|
||||
onApply();
|
||||
}
|
||||
if (input === 'n' || input === 'N' || key.escape) {
|
||||
onSkip();
|
||||
}
|
||||
});
|
||||
|
||||
// Render Logic
|
||||
let currentLine = 0;
|
||||
const renderedLines = [];
|
||||
|
||||
diff.forEach((part) => {
|
||||
const lines = part.value.split('\n');
|
||||
// last element of split is often empty if value ends with newline
|
||||
if (lines[lines.length - 1] === '') lines.pop();
|
||||
|
||||
lines.forEach((line) => {
|
||||
currentLine++;
|
||||
// Check visibility
|
||||
if (currentLine <= scrollTop || currentLine > scrollTop + visibleLines) {
|
||||
return;
|
||||
}
|
||||
|
||||
let color = 'gray'; // Unchanged
|
||||
let prefix = ' ';
|
||||
let bg = undefined;
|
||||
|
||||
if (part.added) {
|
||||
color = 'green';
|
||||
prefix = '+ ';
|
||||
} else if (part.removed) {
|
||||
color = 'red';
|
||||
prefix = '- ';
|
||||
}
|
||||
|
||||
renderedLines.push(
|
||||
h(Box, { key: currentLine, width: '100%' },
|
||||
h(Text, { color: 'gray', dimColor: true }, `${currentLine.toString().padEnd(4)} `),
|
||||
h(Text, { color: color, backgroundColor: bg, wrap: 'truncate-end' }, prefix + line)
|
||||
)
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
return h(Box, {
|
||||
flexDirection: 'column',
|
||||
width: width,
|
||||
height: height,
|
||||
borderStyle: 'double',
|
||||
borderColor: 'yellow'
|
||||
},
|
||||
// Header
|
||||
h(Box, { flexDirection: 'column', paddingX: 1, borderStyle: 'single', borderBottom: true, borderTop: false, borderLeft: false, borderRight: false },
|
||||
h(Text, { bold: true, color: 'yellow' }, `Reviewing: ${file}`),
|
||||
h(Box, { justifyContent: 'space-between' },
|
||||
h(Text, { dimColor: true }, `Lines: ${totalLines} | Changes: ${diff.filter(p => p.added || p.removed).length} blocks`),
|
||||
h(Text, { color: 'blue' }, 'UP/DOWN to scroll')
|
||||
)
|
||||
),
|
||||
|
||||
// Diff Content
|
||||
h(Box, { flexDirection: 'column', flexGrow: 1, paddingX: 1 },
|
||||
renderedLines.length > 0
|
||||
? renderedLines
|
||||
: h(Text, { color: 'gray' }, 'No changes detected (Files are identical)')
|
||||
),
|
||||
|
||||
// Footer Actions
|
||||
h(Box, {
|
||||
borderStyle: 'single',
|
||||
borderTop: true,
|
||||
borderBottom: false,
|
||||
borderLeft: false,
|
||||
borderRight: false,
|
||||
paddingX: 1,
|
||||
justifyContent: 'center',
|
||||
gap: 4
|
||||
},
|
||||
h(Text, { color: 'green', bold: true }, '[Y] Apply Changes'),
|
||||
h(Text, { color: 'red', bold: true }, '[N] Discard/Skip')
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
export default DiffView;
|
||||
Reference in New Issue
Block a user