From 4d984a744840db9c46eec4022e0c2580c819ff91 Mon Sep 17 00:00:00 2001 From: Thomas Ricouard Date: Sun, 18 Jan 2026 12:15:26 +0100 Subject: [PATCH 1/4] Improve git diff panel scrolling --- src/features/git/components/GitDiffPanel.tsx | 3 +- src/features/git/components/GitDiffViewer.tsx | 55 +++++++++++++++++++ src/features/layout/hooks/useLayoutNodes.tsx | 1 + src/styles/diff-viewer.css | 20 +++++-- 4 files changed, 72 insertions(+), 7 deletions(-) diff --git a/src/features/git/components/GitDiffPanel.tsx b/src/features/git/components/GitDiffPanel.tsx index 015b69036..cc55a8348 100644 --- a/src/features/git/components/GitDiffPanel.tsx +++ b/src/features/git/components/GitDiffPanel.tsx @@ -168,6 +168,7 @@ export function GitDiffPanel({ gitRootScanLoading = false, gitRootScanError = null, gitRootScanHasScanned = false, + selectedPath = null, onGitRootScanDepthChange, onScanGitRoots, onSelectGitRoot, @@ -461,7 +462,7 @@ export function GitDiffPanel({ return (
onSelectFile?.(file.path)} diff --git a/src/features/git/components/GitDiffViewer.tsx b/src/features/git/components/GitDiffViewer.tsx index 85a9ed51b..cec4b389a 100644 --- a/src/features/git/components/GitDiffViewer.tsx +++ b/src/features/git/components/GitDiffViewer.tsx @@ -109,9 +109,11 @@ export function GitDiffViewer({ selectedPath, isLoading, error, + onActivePathChange, }: GitDiffViewerProps) { const containerRef = useRef(null); const lastScrolledPathRef = useRef(null); + const activePathRef = useRef(null); const poolOptions = useMemo(() => ({ workerFactory }), []); const highlighterOptions = useMemo( () => ({ theme: { dark: "pierre-dark", light: "pierre-light" } }), @@ -147,6 +149,59 @@ export function GitDiffViewer({ lastScrolledPathRef.current = selectedPath; }, [selectedPath, indexByPath, rowVirtualizer]); + useEffect(() => { + activePathRef.current = selectedPath; + }, [selectedPath]); + + useEffect(() => { + const container = containerRef.current; + if (!container || !onActivePathChange) { + return; + } + let frameId: number | null = null; + + const updateActivePath = () => { + frameId = null; + const items = rowVirtualizer.getVirtualItems(); + if (!items.length) { + return; + } + const scrollTop = container.scrollTop; + const targetOffset = scrollTop + 8; + let activeItem = items[0]; + for (const item of items) { + if (item.start <= targetOffset) { + activeItem = item; + } else { + break; + } + } + const nextPath = diffs[activeItem.index]?.path; + if (!nextPath || nextPath === activePathRef.current) { + return; + } + activePathRef.current = nextPath; + lastScrolledPathRef.current = nextPath; + onActivePathChange(nextPath); + }; + + const handleScroll = () => { + if (frameId !== null) { + return; + } + frameId = requestAnimationFrame(updateActivePath); + }; + + handleScroll(); + container.addEventListener("scroll", handleScroll, { passive: true }); + return () => { + if (frameId !== null) { + cancelAnimationFrame(frameId); + } + container.removeEventListener("scroll", handleScroll); + }; + }, [diffs, onActivePathChange, rowVirtualizer]); + return ( Date: Sun, 18 Jan 2026 12:34:18 +0100 Subject: [PATCH 2/4] Fix sticky diff headers with virtual rows --- src/features/git/components/GitDiffViewer.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/features/git/components/GitDiffViewer.tsx b/src/features/git/components/GitDiffViewer.tsx index cec4b389a..89ced38df 100644 --- a/src/features/git/components/GitDiffViewer.tsx +++ b/src/features/git/components/GitDiffViewer.tsx @@ -233,7 +233,7 @@ export function GitDiffViewer({ data-index={virtualRow.index} ref={rowVirtualizer.measureElement} style={{ - transform: `translateY(${virtualRow.start}px)`, + top: virtualRow.start, }} > Date: Sun, 18 Jan 2026 12:46:51 +0100 Subject: [PATCH 3/4] style(diff): refine git diff viewer layout --- src/features/git/components/GitDiffViewer.tsx | 14 +++-- src/styles/diff-viewer.css | 57 ++++++++++++++++--- 2 files changed, 57 insertions(+), 14 deletions(-) diff --git a/src/features/git/components/GitDiffViewer.tsx b/src/features/git/components/GitDiffViewer.tsx index 89ced38df..2365f40a9 100644 --- a/src/features/git/components/GitDiffViewer.tsx +++ b/src/features/git/components/GitDiffViewer.tsx @@ -80,16 +80,18 @@ const DiffCard = memo(function DiffCard({ }, [entry.diff, entry.path]); return ( -
+
- {entry.status} + + {entry.status} + {entry.path}
{entry.diff.trim().length > 0 && fileDiff ? ( -
+
Date: Sun, 18 Jan 2026 13:25:38 +0100 Subject: [PATCH 4/4] chore(diff): remove unused file diff class --- src/features/git/components/GitDiffViewer.tsx | 28 +++++++++++++++++-- src/styles/diff-viewer.css | 21 ++++++++++---- 2 files changed, 42 insertions(+), 7 deletions(-) diff --git a/src/features/git/components/GitDiffViewer.tsx b/src/features/git/components/GitDiffViewer.tsx index 2365f40a9..c991bd96b 100644 --- a/src/features/git/components/GitDiffViewer.tsx +++ b/src/features/git/components/GitDiffViewer.tsx @@ -95,7 +95,6 @@ const DiffCard = memo(function DiffCard({
@@ -135,6 +134,18 @@ export function GitDiffViewer({ overscan: 6, }); const virtualItems = rowVirtualizer.getVirtualItems(); + const stickyEntry = useMemo(() => { + if (!diffs.length) { + return null; + } + if (selectedPath) { + const index = indexByPath.get(selectedPath); + if (index !== undefined) { + return diffs[index]; + } + } + return diffs[0]; + }, [diffs, selectedPath, indexByPath]); useEffect(() => { if (!selectedPath) { @@ -210,6 +221,19 @@ export function GitDiffViewer({ highlighterOptions={highlighterOptions} >
+ {!error && stickyEntry && ( +
+
+ + {stickyEntry.status} + + {stickyEntry.path} +
+
+ )} {error &&
{error}
} {!error && isLoading && diffs.length > 0 && (
@@ -235,7 +259,7 @@ export function GitDiffViewer({ data-index={virtualRow.index} ref={rowVirtualizer.measureElement} style={{ - top: virtualRow.start, + transform: `translate3d(0, ${virtualRow.start}px, 0)`, }} >