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:
68
apps/web/src/hooks/useDiffPreferences.ts
Normal file
68
apps/web/src/hooks/useDiffPreferences.ts
Normal file
@@ -0,0 +1,68 @@
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
|
||||
export interface DiffPreferences {
|
||||
layout: 'unified' | 'split';
|
||||
wrapLines: boolean;
|
||||
hideWhitespace: boolean;
|
||||
}
|
||||
|
||||
const DEFAULT_PREFERENCES: DiffPreferences = {
|
||||
layout: 'unified',
|
||||
wrapLines: false,
|
||||
hideWhitespace: false,
|
||||
};
|
||||
|
||||
const STORAGE_KEY = 'boocode.diff.preferences';
|
||||
|
||||
function loadPreferences(): DiffPreferences {
|
||||
try {
|
||||
const stored = localStorage.getItem(STORAGE_KEY);
|
||||
if (stored) {
|
||||
const parsed = JSON.parse(stored) as Partial<DiffPreferences>;
|
||||
return {
|
||||
layout: parsed.layout ?? DEFAULT_PREFERENCES.layout,
|
||||
wrapLines: parsed.wrapLines ?? DEFAULT_PREFERENCES.wrapLines,
|
||||
hideWhitespace: parsed.hideWhitespace ?? DEFAULT_PREFERENCES.hideWhitespace,
|
||||
};
|
||||
}
|
||||
} catch {
|
||||
// ignore parse errors
|
||||
}
|
||||
return DEFAULT_PREFERENCES;
|
||||
}
|
||||
|
||||
function savePreferences(prefs: DiffPreferences): void {
|
||||
try {
|
||||
localStorage.setItem(STORAGE_KEY, JSON.stringify(prefs));
|
||||
} catch {
|
||||
// ignore storage errors
|
||||
}
|
||||
}
|
||||
|
||||
export function useDiffPreferences(): {
|
||||
preferences: DiffPreferences;
|
||||
updatePreferences: (updates: Partial<DiffPreferences>) => void;
|
||||
resetPreferences: () => void;
|
||||
} {
|
||||
const [preferences, setPreferences] = useState<DiffPreferences>(loadPreferences);
|
||||
|
||||
// Sync from localStorage on mount (handles multi-tab changes if we add a storage listener later)
|
||||
useEffect(() => {
|
||||
setPreferences(loadPreferences());
|
||||
}, []);
|
||||
|
||||
const updatePreferences = useCallback((updates: Partial<DiffPreferences>) => {
|
||||
setPreferences((prev) => {
|
||||
const next = { ...prev, ...updates };
|
||||
savePreferences(next);
|
||||
return next;
|
||||
});
|
||||
}, []);
|
||||
|
||||
const resetPreferences = useCallback(() => {
|
||||
setPreferences(DEFAULT_PREFERENCES);
|
||||
savePreferences(DEFAULT_PREFERENCES);
|
||||
}, []);
|
||||
|
||||
return { preferences, updatePreferences, resetPreferences };
|
||||
}
|
||||
Reference in New Issue
Block a user