// Sticky top navigation bar. Adapts based on whether we're on the repo picker
// page (root) or inside a specific repo:
// - Root: shows logo only, no Code/Issues links
// - Repo: shows Code + Issues nav links scoped to the current repo slug
//
// In external mode, shows a "Sign in" button when logged out and a sign-out
// action when logged in.
import { Link, useParams, useRouterState } from "@tanstack/react-router";
import { Bug, Plus, Sun, Moon, LogIn, LogOut } from "lucide-react";
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
import { Button } from "@/components/ui/button";
import { ButtonLink } from "@/components/ui/button-link";
import { useAuth } from "@/lib/auth";
import { useTheme } from "@/lib/theme";
import { cn } from "@/lib/utils";
// SignOutButton sends a POST to /auth/logout and reloads the page.
// A full reload is the simplest way to reset all Apollo cache + React state.
function handleSignOut() {
void fetch("/auth/logout", { method: "POST", credentials: "include" }).finally(() =>
window.location.assign("/"),
);
}
function SignOutButton() {
return (
);
}
export function Header() {
const { user, mode, loginProviders } = useAuth();
const { theme, toggle } = useTheme();
// Detect if we're inside a /$repo route and grab the slug.
const params = useParams({ strict: false });
const repo = params.repo ?? null;
// Don't show repo nav on the /auth/* pages.
const effectiveRepo = repo === "auth" ? null : repo;
return (
{/* Logo always goes to the repo picker root */}
git-bug
{/* Repo-scoped nav links — only shown when inside a repo */}
{effectiveRepo && }
{mode === "readonly" && Read only}
{/* External mode: show sign-in buttons when logged out */}
{mode === "external" &&
!user &&
loginProviders.map((p) => (
))}
{user && effectiveRepo && (
<>
New issue
{user.displayName.slice(0, 2).toUpperCase()}
>
)}
{/* Sign out only shown in external mode when logged in */}
{mode === "external" && user && }
);
}
const navLinkBase = "rounded-md px-3 py-1.5 text-sm font-medium transition-colors";
const navLinkActive = "bg-accent text-accent-foreground";
const navLinkInactive = "text-muted-foreground hover:bg-accent hover:text-accent-foreground";
function RepoNav({ repo }: { repo: string }) {
// Determine which section is active from the matched route IDs.
// The _code layout match means we're in the code browser; _issues means issues.
const matchedIds = useRouterState({
select: (s) => s.matches.map((m) => m.routeId),
});
const isCodeActive = matchedIds.some((id) => id.includes("/_code"));
const isIssuesActive = matchedIds.some((id) => id.includes("/_issues"));
return (
);
}
function providerLabel(name: string): string {
const labels: Record = { github: "GitHub", gitlab: "GitLab", gitea: "Gitea" };
return labels[name] ?? name;
}