import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import {
BrowserRouter,
Navigate,
NavLink,
Route,
Routes,
useParams,
useSearchParams,
} from "react-router";
import type { ServerStatus } from "../server";
import { AttentionBell } from "./attention-bell";
import { Screen, Scroller, Seg } from "./frame";
import { useLoaded } from "./lib/api";
import { cn } from "./lib/cn";
import { LOG_KEYS, Log, type LogFilter } from "./screens/log";
import { ProjectDetail } from "./screens/project-detail";
import {
isStatusFilter,
Projects,
STATUS_FILTERS,
type StatusFilter,
} from "./screens/projects";
import { Settings } from "./screens/settings";
import { isLayout, LAYOUTS, type Layout, Today } from "./screens/today";
const LAYOUT_KEY = "pal.control-room.layout";
/**
* A blueprint's registration marks are drawn 6px outside its border, so a scroll
* box whose children are blueprints would count them as overflow. The negative
* margin borrows that room back from the page gutter, leaving content aligned.
*/
const MARK_ROOM = "-m-2 p-2";
function rememberedLayout(): Layout {
try {
const stored = localStorage.getItem(LAYOUT_KEY);
return isLayout(stored) ? stored : "matrix";
} catch {
return "matrix";
}
}
function remember(layout: Layout): void {
try {
localStorage.setItem(LAYOUT_KEY, layout);
} catch {}
}
function TodayRoute() {
const [params, setParams] = useSearchParams();
const asked = params.get("layout");
const layout = isLayout(asked) ? asked : rememberedLayout();
return (
{
remember(next);
setParams({ layout: next });
}}
/>
);
}
function ProjectsRoute() {
const [params, setParams] = useSearchParams();
const asked = params.get("status");
const filter: StatusFilter = isStatusFilter(asked) ? asked : "ranked";
return (
setParams({ status: next })}
/>
}
>
);
}
function ProjectDetailRoute() {
const { slug } = useParams();
return (
);
}
function LogRoute() {
const [params, setParams] = useSearchParams();
const filter = Object.fromEntries(
LOG_KEYS.map((key) => [key, params.get(key) ?? ""])
) as LogFilter;
const update = (next: Partial) => {
const merged = { ...filter, ...next };
setParams(Object.fromEntries(Object.entries(merged).filter(([, v]) => v)));
};
return (
every tool call this install recorded
}
>
);
}
function SettingsRoute() {
return (
);
}
const TABS = [
{ to: "/", label: "Today", end: true },
{ to: "/projects", label: "Projects", end: false },
{ to: "/log", label: "Action log", end: false },
{ to: "/settings", label: "Settings", end: false },
] as const;
function Header({ status }: { status: ServerStatus | null }) {
return (
PAL
control
{status?.machine ?? "…"}
· {status ? `${status.ledgerFiles} ledger file(s)` : "…"} · port{" "}
{status?.port ?? "…"}
);
}
function App() {
const status = useLoaded("/api/status");
return (
} />
} />
} />
} />
} />
} />
);
}
const root = document.getElementById("root");
if (root)
createRoot(root).render(
);