Rhys Sullivanremove v3 workinged7a3488 months agoHistory
161 lines · 6.0 KB · TypeScript
RawDownload
import { Link, Outlet, useMatchRoute } from "@tanstack/react-router";import { useSources, type Source } from "@executor/react";import { cn } from "../lib/utils";import { IconPlus } from "./icons";import { LoadableBlock } from "./loadable";import { SourceFavicon } from "./source-favicon"; // ── Status dot color ───────────────────────────────────────────────────── const statusColor: Record<string, string> = {  connected: "bg-primary",  probing: "bg-amber-400",  draft: "bg-muted-foreground/30",  auth_required: "bg-amber-500",  error: "bg-destructive",}; type AppMetaEnv = {  readonly VITE_APP_VERSION: string;  readonly VITE_GITHUB_URL: string;}; const { VITE_APP_VERSION, VITE_GITHUB_URL } = (import.meta as ImportMeta & {  readonly env: AppMetaEnv;}).env; // ── AppShell ─────────────────────────────────────────────────────────────export function AppShell() {  const sources = useSources();  const matchRoute = useMatchRoute();  const isHome = matchRoute({ to: "/" });  const isSecrets = matchRoute({ to: "/secrets" });  return (    <div className="flex h-screen overflow-hidden">      {/* Sidebar */}      <aside className="flex w-52 shrink-0 flex-col border-r border-sidebar-border bg-sidebar lg:w-56">        {/* Brand */}        <div className="flex h-12 shrink-0 items-center border-b border-sidebar-border px-4">          <Link to="/" className="flex items-center gap-1.5">            <span className="font-display text-base tracking-tight text-foreground">              executor            </span>            <span className="rounded bg-primary/15 px-1.5 py-0.5 text-[10px] font-medium text-primary">              v3            </span>          </Link>        </div>         {/* Main nav */}        <nav className="flex flex-1 flex-col p-2 overflow-y-auto">          <NavItem to="/" label="Dashboard" active={!!isHome} />          <NavItem to="/secrets" label="Secrets" active={!!isSecrets} />           {/* Sources */}          <div className="mt-5 mb-1 px-2.5 text-[10px] font-medium uppercase tracking-widest text-muted-foreground/50">            <div className="flex items-center justify-between gap-2">              <span>Sources</span>              <Link                to="/sources/add"                className="inline-flex items-center gap-1 rounded-md px-1.5 py-1 text-[10px] font-medium normal-case tracking-normal text-primary transition-colors hover:bg-sidebar-active hover:text-foreground"              >                <IconPlus className="size-3" />                Add              </Link>            </div>          </div>          <LoadableBlock loadable={sources} loading="Loading...">            {(items) =>              items.length === 0 ? (                <div className="px-2.5 py-2 text-[11px] leading-relaxed text-muted-foreground/40">                  No sources yet                </div>              ) : (                <div className="flex flex-col gap-px">                  {items.map((source) => (                    <SourceItem key={source.id} source={source} matchRoute={matchRoute} />                  ))}                </div>              )            }          </LoadableBlock>        </nav>         {/* Footer */}        <div className="shrink-0 border-t border-sidebar-border px-4 py-2.5">          <div className="flex flex-col items-start gap-1 text-[10px] leading-none">            <span className="text-muted-foreground/35">v{VITE_APP_VERSION}</span>            <a              href={VITE_GITHUB_URL}              target="_blank"              rel="noreferrer"              className="text-muted-foreground/50 transition-colors hover:text-foreground"            >              Star on GitHub            </a>          </div>        </div>      </aside>       {/* Main content */}      <main className="flex flex-1 flex-col min-w-0 overflow-hidden">        <Outlet />      </main>    </div>  );} // ── SourceItem ─────────────────────────────────────────────────────────── function SourceItem(props: {  source: Source;  matchRoute: ReturnType<typeof useMatchRoute>;}) {  const { source, matchRoute } = props;  const active = matchRoute({    to: "/sources/$sourceId",    params: { sourceId: source.id },    fuzzy: true,  });   return (    <Link      to="/sources/$sourceId"      params={{ sourceId: source.id }}      search={{ tab: "model" }}      className={cn(        "group flex items-center gap-2 rounded-md px-2.5 py-1.5 text-[13px] transition-colors",        active          ? "bg-sidebar-active text-foreground font-medium"          : "text-sidebar-foreground hover:bg-sidebar-active/60 hover:text-foreground",      )}    >      <div className="flex size-3 shrink-0 items-center justify-center text-muted-foreground/50">        <SourceFavicon endpoint={source.endpoint} kind={source.kind} className="size-3" />      </div>      <span className="flex-1 truncate">{source.name}</span>      <span        className={cn("size-1.5 shrink-0 rounded-full", statusColor[source.status] ?? "bg-muted-foreground/30")}        title={source.status}      />    </Link>  );} // ── NavItem ────────────────────────────────────────────────────────────── function NavItem(props: { to: string; label: string; active: boolean }) {  return (    <Link      to={props.to}      className={cn(        "flex items-center gap-2.5 rounded-md px-2.5 py-1.5 text-[13px] transition-colors",        props.active          ? "bg-sidebar-active text-foreground font-medium"          : "text-sidebar-foreground hover:bg-sidebar-active/60 hover:text-foreground",      )}    >      {props.label}    </Link>  );}