From 940acd5eb82f868c9001faf5ebdc69a703884869 Mon Sep 17 00:00:00 2001 From: Xin Date: Tue, 6 Oct 2026 22:34:33 +0100 Subject: [PATCH 1/2] feat(web): dim reviewed files in sidebar and make sidebar resizable --- web/src/components/DiffView.tsx | 47 ++++++++++++++++++-- web/src/components/diff-view/SidebarTree.tsx | 18 ++++++++ 2 files changed, 61 insertions(+), 4 deletions(-) diff --git a/web/src/components/DiffView.tsx b/web/src/components/DiffView.tsx index 8cbbe59..41a083c 100644 --- a/web/src/components/DiffView.tsx +++ b/web/src/components/DiffView.tsx @@ -8,6 +8,7 @@ import { lazy, Suspense, type CSSProperties, + type PointerEvent as ReactPointerEvent, } from "react"; import { useParams, useSearchParams, Link } from "react-router"; import { @@ -92,6 +93,9 @@ import { DEFAULT_CODE_FONT_FAMILY, DEFAULT_UI_FONT_FAMILY, prependFontFamily } f const MobileSidebarDrawer = lazy(() => import("./diff-view/MobileSidebarDrawer")); +const SIDEBAR_WIDTH_KEY = "diffs-sidebar-width"; +const DEFAULT_SIDEBAR_WIDTH = 320; + const codeViewStyle = { flex: 1, overflow: "auto" as const, @@ -305,6 +309,26 @@ export function DiffView({ source = "pr" }: { source?: "pr" | "local" | "branch" const [shortcutsOpen, setShortcutsOpen] = useState(false); const [sidebarOpen, setSidebarOpen] = useState(true); const [mobileSidebarOpen, setMobileSidebarOpen] = useState(false); + const [sidebarWidth, setSidebarWidth] = useState( + () => Number(localStorage.getItem(SIDEBAR_WIDTH_KEY)) || DEFAULT_SIDEBAR_WIDTH, + ); + useEffect(() => { + localStorage.setItem(SIDEBAR_WIDTH_KEY, String(sidebarWidth)); + }, [sidebarWidth]); + const startSidebarResize = useCallback((event: ReactPointerEvent) => { + event.preventDefault(); + const handle = event.currentTarget; + const left = handle.parentElement!.getBoundingClientRect().left; + handle.setPointerCapture(event.pointerId); + const onMove = (e: PointerEvent) => + setSidebarWidth(Math.round(Math.min(Math.max(e.clientX - left, 200), 640))); + const onUp = () => { + handle.removeEventListener("pointermove", onMove); + handle.removeEventListener("pointerup", onUp); + }; + handle.addEventListener("pointermove", onMove); + handle.addEventListener("pointerup", onUp); + }, []); const [submittingPendingComments, setSubmittingPendingComments] = useState(false); const [exporting, setExporting] = useState(false); const [repoContext, setRepoContext] = useState<{ @@ -555,15 +579,18 @@ export function DiffView({ source = "pr" }: { source?: "pr" | "local" | "branch" if (reviewed.key !== reviewedStorageKey) { setReviewed({ key: reviewedStorageKey, map: readReviewedSignatures(reviewedStorageKey) }); } - const hiddenReviewedNames = useMemo(() => { + const reviewedNames = useMemo(() => { const names = new Set(); - if (!hideReviewed) return names; for (const f of files) { const sig = fileSignatures.get(f.name); if (sig != null && reviewed.map.get(f.name) === sig) names.add(f.name); } return names; - }, [hideReviewed, files, fileSignatures, reviewed]); + }, [files, fileSignatures, reviewed]); + const hiddenReviewedNames = useMemo( + () => (hideReviewed ? reviewedNames : new Set()), + [hideReviewed, reviewedNames], + ); const visibleFiles = useMemo( () => hiddenReviewedNames.size === 0 @@ -1403,6 +1430,7 @@ export function DiffView({ source = "pr" }: { source?: "pr" | "local" | "branch" const sidebarTreeProps = { paths: filePaths, files: visibleFiles, + reviewedPaths: reviewedNames, comments: commentThreads, onFileActivate: scrollToFile, onCommentActivate: scrollToThread, @@ -1442,8 +1470,19 @@ export function DiffView({ source = "pr" }: { source?: "pr" | "local" | "branch"
{sidebarOpen && ( -