import { Link, Outlet, useLocation } from "@tanstack/react-router";
import { useEffect, useRef, useState } from "react";
import { useAtomRefresh, useAtomValue, Result } from "@effect-atom/atom-react";
import { sourcesAtom, toolsAtom } from "@executor/react/api/atoms";
import { useScope } from "@executor/react/api/scope-context";
import { Button } from "@executor/react/components/button";
import { AUTH_PATHS } from "../auth/api";
import { useAuth } from "./auth";
// ── NavItem ──────────────────────────────────────────────────────────────
function NavItem(props: { to: string; label: string; active: boolean; onNavigate?: () => void }) {
return (
{props.label}
);
}
// ── SourceList ───────────────────────────────────────────────────────────
function SourceList(props: { pathname: string; onNavigate?: () => void }) {
const scopeId = useScope();
const sources = useAtomValue(sourcesAtom(scopeId));
return Result.match(sources, {
onInitial: () => (
Loading…
),
onFailure: () => (
No sources yet
),
onSuccess: ({ value }) =>
value.length === 0 ? (
No sources yet
) : (
{value.map((s) => {
const detailPath = `/sources/${s.id}`;
const active =
props.pathname === detailPath ||
props.pathname.startsWith(`${detailPath}/`);
return (
{s.name}
{s.kind}
);
})}
),
});
}
// ── UserFooter ──────────────────────────────────────────────────────────
function UserFooter() {
const auth = useAuth();
if (auth.status !== "authenticated") return null;
const initials = auth.user.name
? auth.user.name.split(" ").map((n) => n[0]).join("").slice(0, 2).toUpperCase()
: auth.user.email[0]!.toUpperCase();
return (
{auth.user.avatarUrl ? (

) : (
{initials}
)}
{auth.user.name ?? auth.user.email}
{auth.team && (
{auth.team.name}
)}
);
}
// ── SidebarContent ───────────────────────────────────────────────────────
function SidebarContent(props: {
pathname: string;
onNavigate?: () => void;
showBrand?: boolean;
}) {
const isHome = props.pathname === "/";
const isSecrets = props.pathname === "/secrets";
return (
<>
{props.showBrand !== false && (
executor
)}
>
);
}
// ── Shell ─────────────────────────────────────────────────────────────────
export function Shell() {
const location = useLocation();
const pathname = location.pathname;
const scopeId = useScope();
const refreshSources = useAtomRefresh(sourcesAtom(scopeId));
const refreshTools = useAtomRefresh(toolsAtom(scopeId));
const lastPathname = useRef(pathname);
const [mobileSidebarOpen, setMobileSidebarOpen] = useState(false);
if (lastPathname.current !== pathname) {
lastPathname.current = pathname;
if (mobileSidebarOpen) setMobileSidebarOpen(false);
}
// Lock scroll when mobile sidebar open
useEffect(() => {
if (!mobileSidebarOpen) return;
const prev = document.body.style.overflow;
document.body.style.overflow = "hidden";
return () => {
document.body.style.overflow = prev;
};
}, [mobileSidebarOpen]);
return (
{/* Desktop sidebar */}
{/* Mobile sidebar overlay */}
{mobileSidebarOpen && (
)}
{/* Main content */}
{/* Mobile top bar */}
setMobileSidebarOpen(true)}
className="size-8 flex items-center justify-center rounded-md border border-border bg-card hover:bg-accent/50"
>
executor
);
}