capsule-ai-cli
Version:
The AI Model Orchestrator - Intelligent multi-model workflows with device-locked licensing
141 lines • 6.51 kB
JavaScript
import React, { useMemo } from 'react';
import { Box, Text, useStdout } from 'ink';
import SelectInput from 'ink-select-input';
import chalk from 'chalk';
import { diffLines as diffLinesLib } from 'diff';
export const EditConfirmation = ({ filePath, oldContent, newContent, onConfirm, onCancel }) => {
const { stdout } = useStdout();
const boxWidth = useMemo(() => {
const terminalWidth = stdout.columns || 80;
return Math.min(terminalWidth - 6, 100);
}, []);
const changeSummary = useMemo(() => {
const diff = diffLinesLib(oldContent, newContent);
let added = 0;
let removed = 0;
diff.forEach((part) => {
const lines = part.value.split('\n');
if (lines.length > 0 && lines[lines.length - 1] === '') {
lines.pop();
}
if (part.added)
added += lines.length;
else if (part.removed)
removed += lines.length;
});
return { added, removed };
}, [oldContent, newContent]);
const diffLineElements = useMemo(() => {
const diff = diffLinesLib(oldContent, newContent);
let oldLineNumber = 1;
let newLineNumber = 1;
const elements = [];
const contextLines = 3;
diff.forEach((part, index) => {
const lines = part.value.split('\n');
if (lines.length > 0 && lines[lines.length - 1] === '') {
lines.pop();
}
if (part.added || part.removed) {
lines.forEach((line, lineIndex) => {
if (part.added) {
const lineNum = newLineNumber.toString().padStart(3);
elements.push(React.createElement(Box, { key: `${index}-${lineIndex}-added`, width: "100%" },
React.createElement(Text, null,
chalk.gray(lineNum),
" ",
chalk.green('+'),
" ",
line)));
newLineNumber++;
}
else {
const lineNum = oldLineNumber.toString().padStart(3);
elements.push(React.createElement(Box, { key: `${index}-${lineIndex}-removed`, width: "100%" },
React.createElement(Text, null,
chalk.gray(lineNum),
" ",
chalk.red('-'),
" ",
line)));
oldLineNumber++;
}
});
}
else {
const isNearChange = (lineIdx) => {
const prevPart = index > 0 ? diff[index - 1] : null;
const nextPart = index < diff.length - 1 ? diff[index + 1] : null;
if (prevPart && (prevPart.added || prevPart.removed) && lineIdx < contextLines) {
return true;
}
if (nextPart && (nextPart.added || nextPart.removed) && lineIdx >= lines.length - contextLines) {
return true;
}
return false;
};
let hiddenShown = false;
lines.forEach((line, lineIndex) => {
const lineNum = newLineNumber.toString().padStart(3);
if (isNearChange(lineIndex)) {
elements.push(React.createElement(Box, { key: `${index}-${lineIndex}-context`, width: "100%" },
React.createElement(Text, null,
chalk.gray(lineNum),
" ",
line)));
hiddenShown = false;
}
else if (!hiddenShown && lineIndex > contextLines && lineIndex < lines.length - contextLines) {
elements.push(React.createElement(Box, { key: `${index}-${lineIndex}-hidden`, width: "100%" },
React.createElement(Text, null,
chalk.gray('...'),
" ",
chalk.dim(`// ${lines.length - 2 * contextLines} lines hidden`))));
hiddenShown = true;
}
oldLineNumber++;
newLineNumber++;
});
}
});
return elements;
}, [oldContent, newContent, boxWidth]);
const items = [
{ label: chalk.green('✓ Apply this edit'), value: 'apply' },
{ label: chalk.red('✗ Cancel this edit'), value: 'cancel' }
];
const handleSelect = (item) => {
switch (item.value) {
case 'apply':
onConfirm();
break;
case 'cancel':
onCancel();
break;
}
};
const displayPath = useMemo(() => {
if (filePath.length > boxWidth - 8) {
return '...' + filePath.substring(filePath.length - (boxWidth - 11));
}
return filePath;
}, [filePath, boxWidth]);
const maxHeight = useMemo(() => {
const terminalHeight = stdout.rows || 24;
return Math.max(10, Math.floor(terminalHeight * 0.6));
}, [stdout.rows]);
return (React.createElement(Box, { flexDirection: "column", borderStyle: "single", borderColor: "gray", paddingX: 1, width: boxWidth },
React.createElement(Box, { marginBottom: 1, flexDirection: "column" },
React.createElement(Text, null,
chalk.gray('└'),
" Updated ",
chalk.bold(displayPath),
" with ",
chalk.green(`${changeSummary.added} additions`),
" and ",
chalk.red(`${changeSummary.removed} removals`))),
React.createElement(Box, { flexDirection: "column", marginBottom: 1, height: maxHeight - 6, overflow: "hidden" },
React.createElement(Box, { flexDirection: "column", height: maxHeight - 8 }, diffLineElements.slice(0, Math.min(diffLineElements.length, maxHeight - 10)))),
React.createElement(SelectInput, { items: items, onSelect: handleSelect })));
};
//# sourceMappingURL=EditConfirmation.js.map