fix(web): move refs preload to $repo context, fix blinking on ref switch

Quentin Gliech and Claude Opus 4.6 (1M context) created

the refs query only depends on the repo slug, not the child route
(tree/blob/commits/ref). Preloading it in _code.tsx beforeLoad caused
a new QueryRef on every navigation, triggering suspense flashes.

move refs preload to $repo.tsx beforeLoad so it's loaded once per
repo and shared via context. The _code layout and /$repo redirect
both read from context instead of firing their own queries.

also replace window.location.href ref switch with router navigation

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

Change summary

webui2/src/routes/$repo.tsx       | 38 +++++++++++++++++++++-
webui2/src/routes/$repo/_code.tsx | 56 ++++++++++----------------------
webui2/src/routes/$repo/index.tsx | 24 ++-----------
3 files changed, 57 insertions(+), 61 deletions(-)

Detailed changes

webui2/src/routes/$repo.tsx 🔗

@@ -1,9 +1,43 @@
+import { gql } from "@apollo/client";
 import { createFileRoute } from "@tanstack/react-router";
 
+import type { GitRef } from "@/__generated__/graphql";
+
+export const REFS_QUERY = gql`
+  query CodePageRefs($repo: String) {
+    repository(ref: $repo) {
+      name
+      refs {
+        nodes {
+          name
+          shortName
+          type
+          hash
+          isDefault
+        }
+      }
+    }
+  }
+`;
+
+export interface RefsQueryData {
+  repository: {
+    name: string;
+    refs: { nodes: GitRef[] } | null;
+  } | null;
+}
+
 export const Route = createFileRoute("/$repo")({
-  beforeLoad: ({ params: { repo } }) => {
+  beforeLoad: ({ params: { repo }, context: { preloadQuery } }) => {
     // Normalize the repo slug: "_" means the default (null) repo
     const ref = repo === "_" ? null : repo;
-    return { ref };
+
+    // Preload refs once for the entire repo — shared by code browser,
+    // and used for the /$repo → tree redirect.
+    const refsRef = preloadQuery<RefsQueryData>(REFS_QUERY, {
+      variables: { repo: ref },
+    });
+
+    return { ref, refsRef };
   },
 });

webui2/src/routes/$repo/_code.tsx 🔗

@@ -1,10 +1,15 @@
-// Pathless layout for the code browser. Preloads refs (branches/tags)
-// and renders the shared header (breadcrumb + ref selector + history toggle).
-// Child routes (tree, blob, commits) render inside the Outlet.
+// Pathless layout for the code browser. Reads preloaded refs from the
+// $repo context and renders the shared header (breadcrumb + ref selector
+// + history toggle). Child routes (tree, blob, commits) render in Outlet.
 
-import { gql } from "@apollo/client";
 import { useReadQuery } from "@apollo/client/react";
-import { createFileRoute, Outlet, useMatchRoute, useParams } from "@tanstack/react-router";
+import {
+  createFileRoute,
+  Outlet,
+  useMatchRoute,
+  useNavigate,
+  useParams,
+} from "@tanstack/react-router";
 import { GitCommit } from "lucide-react";
 
 import type { GitRef } from "@/__generated__/graphql";
@@ -13,45 +18,14 @@ import { RefSelector } from "@/components/code/RefSelector";
 import { ButtonLink } from "@/components/ui/button-link";
 import { Skeleton } from "@/components/ui/skeleton";
 
-export const REFS_QUERY = gql`
-  query CodePageRefs($repo: String) {
-    repository(ref: $repo) {
-      name
-      refs {
-        nodes {
-          name
-          shortName
-          type
-          hash
-          isDefault
-        }
-      }
-    }
-  }
-`;
-
-export interface RefsQueryData {
-  repository: {
-    name: string;
-    refs: { nodes: GitRef[] } | null;
-  } | null;
-}
-
 export const Route = createFileRoute("/$repo/_code")({
   component: CodeLayout,
   pendingComponent: CodeLayoutSkeleton,
-  beforeLoad: ({ context: { preloadQuery, ref } }) => {
-    const refsRef = preloadQuery<RefsQueryData>(REFS_QUERY, {
-      variables: { repo: ref },
-    });
-    return { refsRef };
-  },
 });
 
 function CodeLayout() {
   const { repo } = Route.useParams();
-  const { ref: repoRef } = Route.useRouteContext();
-  const { refsRef } = Route.useRouteContext();
+  const { ref: repoRef, refsRef } = Route.useRouteContext();
   const { data: refsData } = useReadQuery(refsRef);
   const refs: GitRef[] = refsData?.repository?.refs?.nodes ?? [];
   const repoName = refsData?.repository?.name ?? repoRef ?? "default-repo";
@@ -71,9 +45,13 @@ function CodeLayout() {
     fuzzy: true,
   });
 
+  const navigate = useNavigate();
+
   function handleRefSelect(ref: GitRef) {
-    // When switching refs, always go to tree root
-    window.location.href = `/${repo}/tree/${ref.shortName}`;
+    void navigate({
+      to: "/$repo/tree/$ref/$",
+      params: { repo, ref: ref.shortName, _splat: "" },
+    });
   }
 
   return (

webui2/src/routes/$repo/index.tsx 🔗

@@ -1,31 +1,15 @@
 // /$repo index — redirects to the tree view with the default ref.
+// The refs are already being preloaded by $repo.tsx beforeLoad, so we
+// use client.query() which will hit the Apollo cache.
 
-import { gql } from "@apollo/client";
 import { createFileRoute, redirect } from "@tanstack/react-router";
 
-import type { GitRef } from "@/__generated__/graphql";
 import { client } from "@/lib/apollo";
-
-const REFS_QUERY = gql`
-  query RepoDefaultRef($repo: String) {
-    repository(ref: $repo) {
-      refs {
-        nodes {
-          shortName
-          isDefault
-        }
-      }
-    }
-  }
-`;
-
-interface DefaultRefQueryData {
-  repository: { refs: { nodes: Pick<GitRef, "shortName" | "isDefault">[] } | null } | null;
-}
+import { REFS_QUERY, type RefsQueryData } from "@/routes/$repo";
 
 export const Route = createFileRoute("/$repo/")({
   beforeLoad: async ({ context: { ref }, params: { repo } }) => {
-    const { data } = await client.query<DefaultRefQueryData>({
+    const { data } = await client.query<RefsQueryData>({
       query: REFS_QUERY,
       variables: { repo: ref },
     });