diff --git a/webui2/src/components/bugs/Timeline.tsx b/webui2/src/components/bugs/Timeline.tsx
index 7a10da2469f98a5e4ba44aaf6dc25d88714faa3b..403f1f0aa978522cc9e0fb1595ea9dcd72e275b4 100644
--- a/webui2/src/components/bugs/Timeline.tsx
+++ b/webui2/src/components/bugs/Timeline.tsx
@@ -14,7 +14,6 @@ import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
import { Button } from "@/components/ui/button";
import { Textarea } from "@/components/ui/textarea";
import { useAuth } from "@/lib/auth";
-import { useRepo } from "@/lib/repo";
import { LabelBadge } from "./LabelBadge";
@@ -23,6 +22,7 @@ type TimelineNode = NonNullable<
>;
interface TimelineProps {
+ repo: string | null;
bugPrefix: string;
items: TimelineNode[];
}
@@ -30,20 +30,20 @@ interface TimelineProps {
// Ordered sequence of events on a bug: comments (create and add-comment) and
// inline events (label changes, status changes, title edits). Comment items
// support inline editing for the logged-in user.
-export function Timeline({ bugPrefix, items }: TimelineProps) {
+export function Timeline({ repo, bugPrefix, items }: TimelineProps) {
return (
{items.map((item) => {
switch (item.__typename) {
case "BugCreateTimelineItem":
case "BugAddCommentTimelineItem":
- return
;
+ return
;
case "BugLabelChangeTimelineItem":
- return
;
+ return
;
case "BugSetStatusTimelineItem":
- return
;
+ return
;
case "BugSetTitleTimelineItem":
- return
;
+ return
;
default:
return null;
}
@@ -59,9 +59,16 @@ type CommentItem = Extract<
{ __typename: "BugCreateTimelineItem" | "BugAddCommentTimelineItem" }
>;
-function CommentItem({ item, bugPrefix }: { item: CommentItem; bugPrefix: string }) {
+function CommentItem({
+ item,
+ bugPrefix,
+ repo,
+}: {
+ item: CommentItem;
+ bugPrefix: string;
+ repo: string | null;
+}) {
const { user } = useAuth();
- const repo = useRepo();
const [editing, setEditing] = useState(false);
const [editValue, setEditValue] = useState(item.message ?? "");
@@ -172,8 +179,7 @@ function EventRow({ icon, children }: { icon: React.ReactNode; children: React.R
);
}
-function LabelChangeItem({ item }: { item: LabelChangeItem }) {
- const repo = useRepo();
+function LabelChangeItem({ item, repo }: { item: LabelChangeItem; repo: string | null }) {
return (
}>
@@ -206,8 +212,7 @@ function LabelChangeItem({ item }: { item: LabelChangeItem }) {
);
}
-function StatusChangeItem({ item }: { item: StatusChangeItem }) {
- const repo = useRepo();
+function StatusChangeItem({ item, repo }: { item: StatusChangeItem; repo: string | null }) {
const isOpen = item.status === Status.Open;
return (
}>
diff --git a/webui2/src/components/code/CommitList.tsx b/webui2/src/components/code/CommitList.tsx
index 9e87fee884b3450d317f8c02456e6cb6da1caff6..3b0bec3fff3b05ca4a17ce9b1135f13b8aefac0b 100644
--- a/webui2/src/components/code/CommitList.tsx
+++ b/webui2/src/components/code/CommitList.tsx
@@ -10,7 +10,6 @@ import { useEffect, useState } from "react";
import { Button } from "@/components/ui/button";
import { Skeleton } from "@/components/ui/skeleton";
-import { useRepo } from "@/lib/repo";
const COMMITS_QUERY = gql`
query CommitList($repo: String, $ref: String!, $path: String, $after: String, $first: Int) {
@@ -44,6 +43,7 @@ interface CommitListQueryData {
}
interface CommitListProps {
+ repo: string | null;
ref_: string;
path?: string;
}
@@ -56,8 +56,7 @@ type CommitNode = {
date: string;
};
-export function CommitList({ ref_, path }: CommitListProps) {
- const repo = useRepo();
+export function CommitList({ repo, ref_, path }: CommitListProps) {
const [cursor, setCursor] = useState(null);
const [allCommits, setAllCommits] = useState([]);
diff --git a/webui2/src/components/code/FileDiffView.tsx b/webui2/src/components/code/FileDiffView.tsx
index b1f88e2f1957af5df85a9721756e45309a532834..086b903fbd60774ba825469482ec50b016a2dd08 100644
--- a/webui2/src/components/code/FileDiffView.tsx
+++ b/webui2/src/components/code/FileDiffView.tsx
@@ -6,7 +6,6 @@ import { useLazyQuery } from "@apollo/client/react";
import { ChevronRight, FilePlus, FileMinus, FileEdit } from "lucide-react";
import { useState } from "react";
-import { useRepo } from "@/lib/repo";
import { cn } from "@/lib/utils";
const DIFF_QUERY = gql`
@@ -53,6 +52,7 @@ interface DiffQueryData {
}
interface FileDiffViewProps {
+ repo: string | null;
hash: string;
path: string;
oldPath?: string;
@@ -72,8 +72,7 @@ const statusBadge: Record = {
RENAMED: "R",
};
-export function FileDiffView({ hash, path, oldPath, status }: FileDiffViewProps) {
- const repo = useRepo();
+export function FileDiffView({ repo, hash, path, oldPath, status }: FileDiffViewProps) {
const [open, setOpen] = useState(false);
const [fetchDiff, { data, loading, error }] = useLazyQuery(DIFF_QUERY);
diff --git a/webui2/src/components/code/FileTree.tsx b/webui2/src/components/code/FileTree.tsx
index fa5daa21c53a538c33f8a3a403080f844d3eafcf..2d28742a8ca0189234e4c94b023f84a010ca71d7 100644
--- a/webui2/src/components/code/FileTree.tsx
+++ b/webui2/src/components/code/FileTree.tsx
@@ -4,7 +4,6 @@ import { Folder, File } from "lucide-react";
import { GitObjectType, type GitTreeEntry } from "@/__generated__/graphql";
import { Skeleton } from "@/components/ui/skeleton";
-import { useRepo } from "@/lib/repo";
export interface TreeEntryWithCommit extends GitTreeEntry {
lastCommit?: {
@@ -16,6 +15,7 @@ export interface TreeEntryWithCommit extends GitTreeEntry {
}
interface FileTreeProps {
+ repo: string | null;
entries: TreeEntryWithCommit[];
path: string;
loading?: boolean;
@@ -25,7 +25,14 @@ interface FileTreeProps {
// Directory listing table for the code browser. Shows each entry's icon,
// name, last-commit message (linked to commit detail), and relative date.
-export function FileTree({ entries, path, loading, onNavigate, onNavigateUp }: FileTreeProps) {
+export function FileTree({
+ repo,
+ entries,
+ path,
+ loading,
+ onNavigate,
+ onNavigateUp,
+}: FileTreeProps) {
// Directories first, then files — each group alphabetical
const sorted = entries.toSorted((a, b) => {
if (a.type !== b.type) return a.type === GitObjectType.Tree ? -1 : 1;
@@ -49,7 +56,7 @@ export function FileTree({ entries, path, loading, onNavigate, onNavigateUp }: F
)}
{sorted.map((entry) => (
-
+
))}
@@ -59,13 +66,14 @@ export function FileTree({ entries, path, loading, onNavigate, onNavigateUp }: F
function FileTreeRow({
entry,
+ repo,
onNavigate,
}: {
entry: TreeEntryWithCommit;
+ repo: string | null;
onNavigate: (entry: TreeEntryWithCommit) => void;
}) {
const isDir = entry.type === GitObjectType.Tree;
- const repo = useRepo();
return (
onNavigate(entry)}>
diff --git a/webui2/src/lib/repo.tsx b/webui2/src/lib/repo.tsx
deleted file mode 100644
index 78c995331ff0f8c685d6b1a48b0febb03b078bff..0000000000000000000000000000000000000000
--- a/webui2/src/lib/repo.tsx
+++ /dev/null
@@ -1,12 +0,0 @@
-// Returns the resolved repo ref from the router context.
-// Returns null when rendered outside of a /$repo route (e.g. the picker page).
-//
-// The $repo route's beforeLoad normalizes the slug ("_" → null) and provides
-// it as context.ref, so callers don't need to handle the "_" case.
-
-import { useRouteContext } from "@tanstack/react-router";
-
-export function useRepo(): string | null {
- const context = useRouteContext({ strict: false });
- return (context as { ref?: string | null }).ref ?? null;
-}
diff --git a/webui2/src/routes/$repo/commit/$hash.tsx b/webui2/src/routes/$repo/commit/$hash.tsx
index 930c26411b760c5048ee147a5ae5209d72adc270..1f76a9732c52d294f4562c921647d628cadca12d 100644
--- a/webui2/src/routes/$repo/commit/$hash.tsx
+++ b/webui2/src/routes/$repo/commit/$hash.tsx
@@ -9,7 +9,6 @@ import { ArrowLeft, GitCommit } from "lucide-react";
import { FileDiffView } from "@/components/code/FileDiffView";
import { Skeleton } from "@/components/ui/skeleton";
-import { useRepo } from "@/lib/repo";
const COMMIT_QUERY = gql`
query CommitPageDetail($repo: String, $hash: String!) {
@@ -65,7 +64,7 @@ export const Route = createFileRoute("/$repo/commit/$hash")({
});
function RouteComponent() {
- const repo = useRepo();
+ const { ref: repo } = Route.useRouteContext();
const { commitRef } = Route.useLoaderData();
const { data } = useReadQuery(commitRef);
@@ -137,6 +136,7 @@ function RouteComponent() {
{files.map((file: { path: string; oldPath?: string | null; status: string }) => (
{viewMode === "commits" ? (
-
+
) : viewMode === "tree" || !blob ? (
<>
{/* Timeline + comment box */}
-
+
diff --git a/webui2/src/routes/$repo/issues/index.tsx b/webui2/src/routes/$repo/issues/index.tsx
index b8a941b2433ecbc95a861b9c3dce4652d30e363c..309c05eed7bb161937120ea8d94fe373f0e70028 100644
--- a/webui2/src/routes/$repo/issues/index.tsx
+++ b/webui2/src/routes/$repo/issues/index.tsx
@@ -12,7 +12,6 @@ import { QueryInput } from "@/components/bugs/QueryInput";
import { Button } from "@/components/ui/button";
import { ButtonLink } from "@/components/ui/button-link";
import { Skeleton } from "@/components/ui/skeleton";
-import { useRepo } from "@/lib/repo";
import { cn } from "@/lib/utils";
const issuesSearchSchema = v.object({
@@ -47,7 +46,7 @@ const PAGE_SIZE = 25;
type StatusFilter = "open" | "closed";
function RouteComponent() {
- const repo = useRepo();
+ const { ref: repo } = Route.useRouteContext();
const navigate = useNavigate({ from: "/$repo/issues/" });
const { q, after } = Route.useSearch();
diff --git a/webui2/src/routes/$repo/issues/new.tsx b/webui2/src/routes/$repo/issues/new.tsx
index d703048fc4b1244e5737ce4d1bffd29d142f1fcf..3bc47e6a4a6027b7b119a9888ea2fea24dbcd844 100644
--- a/webui2/src/routes/$repo/issues/new.tsx
+++ b/webui2/src/routes/$repo/issues/new.tsx
@@ -8,7 +8,6 @@ import { Button } from "@/components/ui/button";
import { ButtonLink } from "@/components/ui/button-link";
import { Input } from "@/components/ui/input";
import { Textarea } from "@/components/ui/textarea";
-import { useRepo } from "@/lib/repo";
export const Route = createFileRoute("/$repo/issues/new")({
component: RouteComponent,
@@ -17,7 +16,7 @@ export const Route = createFileRoute("/$repo/issues/new")({
// New issue form (/:repo/issues/new). Title + body with write/preview tabs.
function RouteComponent() {
const navigate = useNavigate();
- const repo = useRepo();
+ const { ref: repo } = Route.useRouteContext();
const [title, setTitle] = useState("");
const [message, setMessage] = useState("");
const [preview, setPreview] = useState(false);
diff --git a/webui2/src/routes/$repo/user/$id.tsx b/webui2/src/routes/$repo/user/$id.tsx
index d5caba926773d664a84440cc16a274bc36d6841d..3a2c1c55520860229c4ab570d164e6afa38d4277 100644
--- a/webui2/src/routes/$repo/user/$id.tsx
+++ b/webui2/src/routes/$repo/user/$id.tsx
@@ -24,7 +24,6 @@ import { LabelBadge } from "@/components/bugs/LabelBadge";
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
import { Button } from "@/components/ui/button";
import { Skeleton } from "@/components/ui/skeleton";
-import { useRepo } from "@/lib/repo";
import { cn } from "@/lib/utils";
export const Route = createFileRoute("/$repo/user/$id")({
@@ -35,7 +34,7 @@ const PAGE_SIZE = 25;
function RouteComponent() {
const { id } = useParams({ strict: false });
- const repo = useRepo();
+ const { ref: repo } = Route.useRouteContext();
const [statusFilter, setStatusFilter] = useState<"open" | "closed">("open");
// Cursor-stack pagination: cursors[i] is the `after` value to fetch page i.