From 0837ee263983254040865de83b56cfd28aff285f Mon Sep 17 00:00:00 2001 From: oppiliappan Date: Wed, 9 Sep 2026 18:33:50 +0100 Subject: [PATCH] web/repo: give the diff file tree the whole card on mobile Signed-off-by: oppiliappan --- .../lib/components/repo/DiffCodeView.svelte | 3 + web/src/lib/components/repo/DiffFiles.svelte | 1 + web/src/lib/components/repo/DiffView.svelte | 59 ++++++++++++++----- 3 files changed, 49 insertions(+), 14 deletions(-) diff --git a/web/src/lib/components/repo/DiffCodeView.svelte b/web/src/lib/components/repo/DiffCodeView.svelte index 91007c6d4..328f5b310 100644 --- a/web/src/lib/components/repo/DiffCodeView.svelte +++ b/web/src/lib/components/repo/DiffCodeView.svelte @@ -15,6 +15,7 @@ export interface CodeViewApi { scrollToFile: (key: string) => number | undefined; + resync: () => void; } export interface CodeViewFile { @@ -342,6 +343,8 @@ window.scrollTo({ top: dockTop() + top, behavior: "instant" }); return top; }; + + export const resync = () => syncScroll(); {#if initError} diff --git a/web/src/lib/components/repo/DiffFiles.svelte b/web/src/lib/components/repo/DiffFiles.svelte index b7939d023..2f8f15f97 100644 --- a/web/src/lib/components/repo/DiffFiles.svelte +++ b/web/src/lib/components/repo/DiffFiles.svelte @@ -145,6 +145,7 @@ const file = diff.files.find((candidate) => candidate.name === path); return file ? view?.scrollToFile(file.key) : undefined; }; + export const resync = () => view?.resync(); jumpToAnchoredFile(jumpToFile); diff --git a/web/src/lib/components/repo/DiffView.svelte b/web/src/lib/components/repo/DiffView.svelte index 95cc8258f..c8d13f6c0 100644 --- a/web/src/lib/components/repo/DiffView.svelte +++ b/web/src/lib/components/repo/DiffView.svelte @@ -23,6 +23,8 @@ import repoDiffviewSkeletonFixture from "$lib/bones/repo-diffview--skeleton-fixture.bones.json"; import Bones from "$lib/components/ui/Bones.svelte"; import Separator from "$lib/components/ui/Separator.svelte"; + import { browser } from "$app/environment"; + import { tick } from "svelte"; interface Props { deps: RepoDiffDeps; @@ -56,7 +58,8 @@ stickyOffset = 0 }: Props = $props(); - let treeOpen = $state(true); + const SM = 640; + let treeToggled = $state(null); const TREE_WIDTH = 250; const TREE_MIN_WIDTH = 200; const TREE_MAX_WIDTH = 560; @@ -91,12 +94,14 @@ const stickyTop = $derived(stickyOffset ? stickyOffset + GAP : 0); const maxHeight = $derived(stickyTop ? `calc(100svh - ${stickyTop + GAP}px)` : "100svh"); const dockOffset = $derived(stickyTop || GAP); + const measureDock = () => { + if (!shell) return; + dock = window.scrollY + shell.getBoundingClientRect().top - dockOffset; + }; $effect(() => { const element = shell; if (!element) return; - const measure = () => { - dock = window.scrollY + element.getBoundingClientRect().top - dockOffset; - }; + const measure = measureDock; measure(); const observer = new ResizeObserver(measure); observer.observe(element); @@ -116,9 +121,25 @@ // the scroller lives two levels down; the page's conflict list and this // component's file tree both reach it through here - let files = $state<{ jumpToFile: (path: string) => number | undefined }>(); + let files = $state<{ + jumpToFile: (path: string) => number | undefined; + resync: () => void; + }>(); export const jumpToFile = (path: string): number | undefined => files?.jumpToFile(path); + const selectFromTree = (path: string): number | undefined => { + if (!browser || window.innerWidth >= SM) return jumpToFile(path); + treeToggled = false; + void (async () => { + await tick(); + files?.resync(); + await tick(); + measureDock(); + jumpToFile(path); + })(); + return undefined; + }; + let openStates = $state>({}); const isOpen = (key: string) => openStates[key] ?? true; const allOpen = $derived((diff?.files ?? []).every((file) => isOpen(file.key))); @@ -137,13 +158,18 @@ style="top: {stickyTop}px; max-height: {maxHeight}" > - {#if treeOpen} -