refactor(web): add pathless _issues layout route

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

introduce /$repo/_issues as a pathless layout route that preloads
validLabels and allIdentities only for issue-related pages, instead
of loading them for every /$repo child (including code browser and
commit pages that don't need them)

move issue routes under _issues/:
  $repo/_issues/issues/     → issue list
  $repo/_issues/issues/new  → new issue
  $repo/_issues/issues/$id  → issue detail
  $repo/_issues/user/$id    → user profile

$repo.tsx now only provides the normalized ref — labels/identities
preloading moved to _issues.tsx beforeLoad

URL paths are unchanged (the _issues prefix is stripped)

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

Change summary

webui2/src/routeTree.gen.ts                      | 160 ++++++++++-------
webui2/src/routes/$repo.tsx                      |  21 --
webui2/src/routes/$repo/_issues.tsx              |  23 ++
webui2/src/routes/$repo/_issues/issues/$id.tsx   |   2 
webui2/src/routes/$repo/_issues/issues/index.tsx |   2 
webui2/src/routes/$repo/_issues/issues/new.tsx   |   2 
webui2/src/routes/$repo/_issues/user/$id.tsx     |   2 
7 files changed, 122 insertions(+), 90 deletions(-)

Detailed changes

webui2/src/routeTree.gen.ts 🔗

@@ -13,11 +13,12 @@ import { Route as RepoRouteImport } from './routes/$repo'
 import { Route as IndexRouteImport } from './routes/index'
 import { Route as RepoIndexRouteImport } from './routes/$repo/index'
 import { Route as AuthSelectIdentityRouteImport } from './routes/auth/select-identity'
-import { Route as RepoIssuesIndexRouteImport } from './routes/$repo/issues/index'
-import { Route as RepoUserIdRouteImport } from './routes/$repo/user/$id'
-import { Route as RepoIssuesNewRouteImport } from './routes/$repo/issues/new'
-import { Route as RepoIssuesIdRouteImport } from './routes/$repo/issues/$id'
+import { Route as RepoIssuesRouteImport } from './routes/$repo/_issues'
 import { Route as RepoCommitHashRouteImport } from './routes/$repo/commit/$hash'
+import { Route as RepoIssuesIssuesIndexRouteImport } from './routes/$repo/_issues/issues/index'
+import { Route as RepoIssuesUserIdRouteImport } from './routes/$repo/_issues/user/$id'
+import { Route as RepoIssuesIssuesNewRouteImport } from './routes/$repo/_issues/issues/new'
+import { Route as RepoIssuesIssuesIdRouteImport } from './routes/$repo/_issues/issues/$id'
 
 const RepoRoute = RepoRouteImport.update({
   id: '/$repo',
@@ -39,64 +40,69 @@ const AuthSelectIdentityRoute = AuthSelectIdentityRouteImport.update({
   path: '/auth/select-identity',
   getParentRoute: () => rootRouteImport,
 } as any)
-const RepoIssuesIndexRoute = RepoIssuesIndexRouteImport.update({
+const RepoIssuesRoute = RepoIssuesRouteImport.update({
+  id: '/_issues',
+  getParentRoute: () => RepoRoute,
+} as any)
+const RepoCommitHashRoute = RepoCommitHashRouteImport.update({
+  id: '/commit/$hash',
+  path: '/commit/$hash',
+  getParentRoute: () => RepoRoute,
+} as any)
+const RepoIssuesIssuesIndexRoute = RepoIssuesIssuesIndexRouteImport.update({
   id: '/issues/',
   path: '/issues/',
-  getParentRoute: () => RepoRoute,
+  getParentRoute: () => RepoIssuesRoute,
 } as any)
-const RepoUserIdRoute = RepoUserIdRouteImport.update({
+const RepoIssuesUserIdRoute = RepoIssuesUserIdRouteImport.update({
   id: '/user/$id',
   path: '/user/$id',
-  getParentRoute: () => RepoRoute,
+  getParentRoute: () => RepoIssuesRoute,
 } as any)
-const RepoIssuesNewRoute = RepoIssuesNewRouteImport.update({
+const RepoIssuesIssuesNewRoute = RepoIssuesIssuesNewRouteImport.update({
   id: '/issues/new',
   path: '/issues/new',
-  getParentRoute: () => RepoRoute,
+  getParentRoute: () => RepoIssuesRoute,
 } as any)
-const RepoIssuesIdRoute = RepoIssuesIdRouteImport.update({
+const RepoIssuesIssuesIdRoute = RepoIssuesIssuesIdRouteImport.update({
   id: '/issues/$id',
   path: '/issues/$id',
-  getParentRoute: () => RepoRoute,
-} as any)
-const RepoCommitHashRoute = RepoCommitHashRouteImport.update({
-  id: '/commit/$hash',
-  path: '/commit/$hash',
-  getParentRoute: () => RepoRoute,
+  getParentRoute: () => RepoIssuesRoute,
 } as any)
 
 export interface FileRoutesByFullPath {
   '/': typeof IndexRoute
-  '/$repo': typeof RepoRouteWithChildren
+  '/$repo': typeof RepoIssuesRouteWithChildren
   '/auth/select-identity': typeof AuthSelectIdentityRoute
   '/$repo/': typeof RepoIndexRoute
   '/$repo/commit/$hash': typeof RepoCommitHashRoute
-  '/$repo/issues/$id': typeof RepoIssuesIdRoute
-  '/$repo/issues/new': typeof RepoIssuesNewRoute
-  '/$repo/user/$id': typeof RepoUserIdRoute
-  '/$repo/issues/': typeof RepoIssuesIndexRoute
+  '/$repo/issues/$id': typeof RepoIssuesIssuesIdRoute
+  '/$repo/issues/new': typeof RepoIssuesIssuesNewRoute
+  '/$repo/user/$id': typeof RepoIssuesUserIdRoute
+  '/$repo/issues/': typeof RepoIssuesIssuesIndexRoute
 }
 export interface FileRoutesByTo {
   '/': typeof IndexRoute
-  '/auth/select-identity': typeof AuthSelectIdentityRoute
   '/$repo': typeof RepoIndexRoute
+  '/auth/select-identity': typeof AuthSelectIdentityRoute
   '/$repo/commit/$hash': typeof RepoCommitHashRoute
-  '/$repo/issues/$id': typeof RepoIssuesIdRoute
-  '/$repo/issues/new': typeof RepoIssuesNewRoute
-  '/$repo/user/$id': typeof RepoUserIdRoute
-  '/$repo/issues': typeof RepoIssuesIndexRoute
+  '/$repo/issues/$id': typeof RepoIssuesIssuesIdRoute
+  '/$repo/issues/new': typeof RepoIssuesIssuesNewRoute
+  '/$repo/user/$id': typeof RepoIssuesUserIdRoute
+  '/$repo/issues': typeof RepoIssuesIssuesIndexRoute
 }
 export interface FileRoutesById {
   __root__: typeof rootRouteImport
   '/': typeof IndexRoute
   '/$repo': typeof RepoRouteWithChildren
+  '/$repo/_issues': typeof RepoIssuesRouteWithChildren
   '/auth/select-identity': typeof AuthSelectIdentityRoute
   '/$repo/': typeof RepoIndexRoute
   '/$repo/commit/$hash': typeof RepoCommitHashRoute
-  '/$repo/issues/$id': typeof RepoIssuesIdRoute
-  '/$repo/issues/new': typeof RepoIssuesNewRoute
-  '/$repo/user/$id': typeof RepoUserIdRoute
-  '/$repo/issues/': typeof RepoIssuesIndexRoute
+  '/$repo/_issues/issues/$id': typeof RepoIssuesIssuesIdRoute
+  '/$repo/_issues/issues/new': typeof RepoIssuesIssuesNewRoute
+  '/$repo/_issues/user/$id': typeof RepoIssuesUserIdRoute
+  '/$repo/_issues/issues/': typeof RepoIssuesIssuesIndexRoute
 }
 export interface FileRouteTypes {
   fileRoutesByFullPath: FileRoutesByFullPath
@@ -113,8 +119,8 @@ export interface FileRouteTypes {
   fileRoutesByTo: FileRoutesByTo
   to:
     | '/'
-    | '/auth/select-identity'
     | '/$repo'
+    | '/auth/select-identity'
     | '/$repo/commit/$hash'
     | '/$repo/issues/$id'
     | '/$repo/issues/new'
@@ -124,13 +130,14 @@ export interface FileRouteTypes {
     | '__root__'
     | '/'
     | '/$repo'
+    | '/$repo/_issues'
     | '/auth/select-identity'
     | '/$repo/'
     | '/$repo/commit/$hash'
-    | '/$repo/issues/$id'
-    | '/$repo/issues/new'
-    | '/$repo/user/$id'
-    | '/$repo/issues/'
+    | '/$repo/_issues/issues/$id'
+    | '/$repo/_issues/issues/new'
+    | '/$repo/_issues/user/$id'
+    | '/$repo/_issues/issues/'
   fileRoutesById: FileRoutesById
 }
 export interface RootRouteChildren {
@@ -169,60 +176,79 @@ declare module '@tanstack/react-router' {
       preLoaderRoute: typeof AuthSelectIdentityRouteImport
       parentRoute: typeof rootRouteImport
     }
-    '/$repo/issues/': {
-      id: '/$repo/issues/'
+    '/$repo/_issues': {
+      id: '/$repo/_issues'
+      path: ''
+      fullPath: '/$repo'
+      preLoaderRoute: typeof RepoIssuesRouteImport
+      parentRoute: typeof RepoRoute
+    }
+    '/$repo/commit/$hash': {
+      id: '/$repo/commit/$hash'
+      path: '/commit/$hash'
+      fullPath: '/$repo/commit/$hash'
+      preLoaderRoute: typeof RepoCommitHashRouteImport
+      parentRoute: typeof RepoRoute
+    }
+    '/$repo/_issues/issues/': {
+      id: '/$repo/_issues/issues/'
       path: '/issues'
       fullPath: '/$repo/issues/'
-      preLoaderRoute: typeof RepoIssuesIndexRouteImport
-      parentRoute: typeof RepoRoute
+      preLoaderRoute: typeof RepoIssuesIssuesIndexRouteImport
+      parentRoute: typeof RepoIssuesRoute
     }
-    '/$repo/user/$id': {
-      id: '/$repo/user/$id'
+    '/$repo/_issues/user/$id': {
+      id: '/$repo/_issues/user/$id'
       path: '/user/$id'
       fullPath: '/$repo/user/$id'
-      preLoaderRoute: typeof RepoUserIdRouteImport
-      parentRoute: typeof RepoRoute
+      preLoaderRoute: typeof RepoIssuesUserIdRouteImport
+      parentRoute: typeof RepoIssuesRoute
     }
-    '/$repo/issues/new': {
-      id: '/$repo/issues/new'
+    '/$repo/_issues/issues/new': {
+      id: '/$repo/_issues/issues/new'
       path: '/issues/new'
       fullPath: '/$repo/issues/new'
-      preLoaderRoute: typeof RepoIssuesNewRouteImport
-      parentRoute: typeof RepoRoute
+      preLoaderRoute: typeof RepoIssuesIssuesNewRouteImport
+      parentRoute: typeof RepoIssuesRoute
     }
-    '/$repo/issues/$id': {
-      id: '/$repo/issues/$id'
+    '/$repo/_issues/issues/$id': {
+      id: '/$repo/_issues/issues/$id'
       path: '/issues/$id'
       fullPath: '/$repo/issues/$id'
-      preLoaderRoute: typeof RepoIssuesIdRouteImport
-      parentRoute: typeof RepoRoute
-    }
-    '/$repo/commit/$hash': {
-      id: '/$repo/commit/$hash'
-      path: '/commit/$hash'
-      fullPath: '/$repo/commit/$hash'
-      preLoaderRoute: typeof RepoCommitHashRouteImport
-      parentRoute: typeof RepoRoute
+      preLoaderRoute: typeof RepoIssuesIssuesIdRouteImport
+      parentRoute: typeof RepoIssuesRoute
     }
   }
 }
 
+interface RepoIssuesRouteChildren {
+  RepoIssuesIssuesIdRoute: typeof RepoIssuesIssuesIdRoute
+  RepoIssuesIssuesNewRoute: typeof RepoIssuesIssuesNewRoute
+  RepoIssuesUserIdRoute: typeof RepoIssuesUserIdRoute
+  RepoIssuesIssuesIndexRoute: typeof RepoIssuesIssuesIndexRoute
+}
+
+const RepoIssuesRouteChildren: RepoIssuesRouteChildren = {
+  RepoIssuesIssuesIdRoute: RepoIssuesIssuesIdRoute,
+  RepoIssuesIssuesNewRoute: RepoIssuesIssuesNewRoute,
+  RepoIssuesUserIdRoute: RepoIssuesUserIdRoute,
+  RepoIssuesIssuesIndexRoute: RepoIssuesIssuesIndexRoute,
+}
+
+const RepoIssuesRouteWithChildren = RepoIssuesRoute._addFileChildren(
+  RepoIssuesRouteChildren,
+)
+
 interface RepoRouteChildren {
+  RepoIssuesRoute: typeof RepoIssuesRouteWithChildren
   RepoIndexRoute: typeof RepoIndexRoute
   RepoCommitHashRoute: typeof RepoCommitHashRoute
-  RepoIssuesIdRoute: typeof RepoIssuesIdRoute
-  RepoIssuesNewRoute: typeof RepoIssuesNewRoute
-  RepoUserIdRoute: typeof RepoUserIdRoute
-  RepoIssuesIndexRoute: typeof RepoIssuesIndexRoute
 }
 
 const RepoRouteChildren: RepoRouteChildren = {
+  RepoIssuesRoute: RepoIssuesRouteWithChildren,
   RepoIndexRoute: RepoIndexRoute,
   RepoCommitHashRoute: RepoCommitHashRoute,
-  RepoIssuesIdRoute: RepoIssuesIdRoute,
-  RepoIssuesNewRoute: RepoIssuesNewRoute,
-  RepoUserIdRoute: RepoUserIdRoute,
-  RepoIssuesIndexRoute: RepoIssuesIndexRoute,
 }
 
 const RepoRouteWithChildren = RepoRoute._addFileChildren(RepoRouteChildren)

webui2/src/routes/$repo.tsx 🔗

@@ -1,26 +1,9 @@
 import { createFileRoute } from "@tanstack/react-router";
 
-import {
-  type ValidLabelsQuery,
-  ValidLabelsDocument,
-  type AllIdentitiesQuery,
-  AllIdentitiesDocument,
-} from "@/__generated__/graphql";
-
 export const Route = createFileRoute("/$repo")({
-  beforeLoad: ({ params: { repo }, context: { preloadQuery } }) => {
+  beforeLoad: ({ params: { repo } }) => {
     // Normalize the repo slug: "_" means the default (null) repo
     const ref = repo === "_" ? null : repo;
-
-    // Preload labels and identities shared by all child routes (issue list,
-    // bug detail, etc.). These are stable across filter/page changes.
-    const labelsRef = preloadQuery<ValidLabelsQuery>(ValidLabelsDocument, {
-      variables: { ref },
-    });
-    const identitiesRef = preloadQuery<AllIdentitiesQuery>(AllIdentitiesDocument, {
-      variables: { ref },
-    });
-
-    return { ref, labelsRef, identitiesRef };
+    return { ref };
   },
 });

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

@@ -0,0 +1,23 @@
+import { createFileRoute } from "@tanstack/react-router";
+
+import {
+  type ValidLabelsQuery,
+  ValidLabelsDocument,
+  type AllIdentitiesQuery,
+  AllIdentitiesDocument,
+} from "@/__generated__/graphql";
+
+// Pathless layout route for all issue-related pages under /$repo.
+// Preloads labels and identities shared by the issue list, detail,
+// new issue form, and user profile pages.
+export const Route = createFileRoute("/$repo/_issues")({
+  beforeLoad: ({ context: { preloadQuery, ref } }) => {
+    const labelsRef = preloadQuery<ValidLabelsQuery>(ValidLabelsDocument, {
+      variables: { ref },
+    });
+    const identitiesRef = preloadQuery<AllIdentitiesQuery>(AllIdentitiesDocument, {
+      variables: { ref },
+    });
+    return { labelsRef, identitiesRef };
+  },
+});

webui2/src/routes/$repo/issues/$id.tsx → webui2/src/routes/$repo/_issues/issues/$id.tsx 🔗

@@ -13,7 +13,7 @@ import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
 import { Separator } from "@/components/ui/separator";
 import { Skeleton } from "@/components/ui/skeleton";
 
-export const Route = createFileRoute("/$repo/issues/$id")({
+export const Route = createFileRoute("/$repo/_issues/issues/$id")({
   component: RouteComponent,
   pendingComponent: BugDetailSkeleton,
   loader: async ({ context: { preloadQuery, ref }, params: { id } }) => {

webui2/src/routes/$repo/issues/index.tsx → webui2/src/routes/$repo/_issues/issues/index.tsx 🔗

@@ -19,7 +19,7 @@ const issuesSearchSchema = v.object({
   after: v.fallback(v.string(), ""),
 });
 
-export const Route = createFileRoute("/$repo/issues/")({
+export const Route = createFileRoute("/$repo/_issues/issues/")({
   component: RouteComponent,
   pendingComponent: BugListSkeleton,
   validateSearch: (search) => v.parse(issuesSearchSchema, search),

webui2/src/routes/$repo/issues/new.tsx → webui2/src/routes/$repo/_issues/issues/new.tsx 🔗

@@ -9,7 +9,7 @@ import { ButtonLink } from "@/components/ui/button-link";
 import { Input } from "@/components/ui/input";
 import { Textarea } from "@/components/ui/textarea";
 
-export const Route = createFileRoute("/$repo/issues/new")({
+export const Route = createFileRoute("/$repo/_issues/issues/new")({
   component: RouteComponent,
 });
 

webui2/src/routes/$repo/user/$id.tsx → webui2/src/routes/$repo/_issues/user/$id.tsx 🔗

@@ -26,7 +26,7 @@ import { Button } from "@/components/ui/button";
 import { Skeleton } from "@/components/ui/skeleton";
 import { cn } from "@/lib/utils";
 
-export const Route = createFileRoute("/$repo/user/$id")({
+export const Route = createFileRoute("/$repo/_issues/user/$id")({
   component: RouteComponent,
 });