feat(web): enhanced file panel — side-by-side diff, hide whitespace, inline review
Adds DiffSplitView component for side-by-side diff mode, whitespace-only change filtering, inline review comments with thread/gutter cell UI, diff preferences persistence, and write-file API support for in-browser editing. Backend: hideWhitespace param on git diff endpoint, write_file route.
This commit is contained in:
@@ -271,7 +271,9 @@ function buildNumstatMap(
|
||||
async function getUncommittedDiff(
|
||||
gitRoot: string,
|
||||
inProgress: string | null,
|
||||
ignoreWhitespace = false,
|
||||
): Promise<GitDiffResult> {
|
||||
const ws = ignoreWhitespace ? ['-w'] : [];
|
||||
const hasCommits = (await runGit(['rev-parse', '--verify', 'HEAD'], gitRoot)) !== null;
|
||||
|
||||
const [nameStatusOut, cachedNameStatusOut, untrackedOut, numstatOut, diffOut, cachedDiffOut] =
|
||||
@@ -284,10 +286,10 @@ async function getUncommittedDiff(
|
||||
: runGit(['diff', '--cached', '--name-status'], gitRoot),
|
||||
runGit(['ls-files', '--others', '--exclude-standard'], gitRoot),
|
||||
hasCommits ? runGit(['diff', '--numstat', 'HEAD'], gitRoot) : Promise.resolve(''),
|
||||
hasCommits ? runGit(['diff', 'HEAD'], gitRoot) : Promise.resolve(''),
|
||||
hasCommits ? runGit(['diff', ...ws, 'HEAD'], gitRoot) : Promise.resolve(''),
|
||||
hasCommits
|
||||
? runGit(['diff', '--cached', 'HEAD'], gitRoot)
|
||||
: runGit(['diff', '--cached'], gitRoot),
|
||||
? runGit(['diff', ...ws, '--cached', 'HEAD'], gitRoot)
|
||||
: runGit(['diff', ...ws, '--cached'], gitRoot),
|
||||
]);
|
||||
|
||||
const allChanged = parseNameStatus(nameStatusOut ?? '');
|
||||
@@ -347,11 +349,13 @@ async function getCommittedDiff(
|
||||
base: string,
|
||||
label: string,
|
||||
inProgress: string | null,
|
||||
ignoreWhitespace = false,
|
||||
): Promise<GitDiffResult> {
|
||||
const ws = ignoreWhitespace ? ['-w'] : [];
|
||||
const [nameStatusOut, numstatOut, diffOut] = await Promise.all([
|
||||
runGit(['diff', '--name-status', base, 'HEAD'], gitRoot),
|
||||
runGit(['diff', '--numstat', base, 'HEAD'], gitRoot),
|
||||
runGit(['diff', base, 'HEAD'], gitRoot),
|
||||
runGit(['diff', ...ws, base, 'HEAD'], gitRoot),
|
||||
]);
|
||||
|
||||
const allChanged = parseNameStatus(nameStatusOut ?? '');
|
||||
@@ -383,23 +387,23 @@ async function getCommittedDiff(
|
||||
* the directory is not a git repository. On a null committed-mode base, falls
|
||||
* back to uncommitted and labels the result accordingly.
|
||||
*/
|
||||
export async function getGitDiff(cwd: string, mode: GitDiffMode): Promise<GitDiffResult | null> {
|
||||
export async function getGitDiff(cwd: string, mode: GitDiffMode, ignoreWhitespace?: boolean): Promise<GitDiffResult | null> {
|
||||
const gitRoot = await resolveGitRoot(cwd);
|
||||
if (!gitRoot) return null;
|
||||
|
||||
const inProgress = await detectInProgress(gitRoot);
|
||||
|
||||
if (mode === 'uncommitted') {
|
||||
return getUncommittedDiff(gitRoot, inProgress);
|
||||
return getUncommittedDiff(gitRoot, inProgress, ignoreWhitespace ?? false);
|
||||
}
|
||||
|
||||
const { base, label } = await resolveCommittedBase(gitRoot);
|
||||
if (!base) {
|
||||
// Fall back to uncommitted with a descriptive label
|
||||
const result = await getUncommittedDiff(gitRoot, inProgress);
|
||||
const result = await getUncommittedDiff(gitRoot, inProgress, ignoreWhitespace ?? false);
|
||||
return { ...result, base_label: label };
|
||||
}
|
||||
return getCommittedDiff(gitRoot, base, label, inProgress);
|
||||
return getCommittedDiff(gitRoot, base, label, inProgress, ignoreWhitespace ?? false);
|
||||
}
|
||||
|
||||
// ── Phase 2: Write helpers ─────────────────────────────────────────────────
|
||||
|
||||
Reference in New Issue
Block a user