/** * Activity: one surface over the queries nxdns answered, in two modes. * * History reads the persisted log and Live reads the stream, but they are the * same seven columns over the same filters, and the reader moves between them * without losing the question they were asking. The mode lives in the URL with * the filters, so an investigation is one link — including which half of it the * recipient should be looking at. */ import { Link, useNavigate, useSearch } from "@tanstack/react-router"; import * as stylex from "@stylexjs/stylex"; import { styles as shared } from "@/ui/styles"; import { colors } from "@/ui/tokens.stylex"; import ActivityFilters, { NO_FILTERS, type AppliedFilters } from "./ActivityFilters"; import HistoryActivity from "./HistoryActivity"; import LiveActivity from "./LiveActivity"; import type { ActivityMode } from "./search"; const MODES: ReadonlyArray<{ mode: ActivityMode; label: string }> = [ { mode: "history", label: "History" }, { mode: "live", label: "Live" }, ]; const styles = stylex.create({ header: { display: "flex", flexWrap: "wrap", alignItems: "center", gap: "0.75rem", }, heading: { fontSize: "1.5rem", lineHeight: "2rem", fontWeight: 600, }, switch: { display: "flex", gap: "0.25rem", borderRadius: "0.25rem", borderWidth: 1, borderStyle: "solid", borderColor: colors.border, padding: "0.125rem", }, modeButton: { borderStyle: "none", borderRadius: "0.1875rem", paddingInline: "0.75rem", paddingBlock: "0.25rem", fontSize: "0.875rem", lineHeight: "1.25rem", fontWeight: 500, cursor: "pointer", }, modeIdle: { backgroundColor: { default: "transparent", ":hover": colors.surfaceHover }, color: { default: colors.textSecondary, ":hover": colors.text }, }, /** The selected mode reads as a filled chip, the same weight the nav uses. */ modeSelected: { backgroundColor: colors.primary, color: colors.primaryText, }, /** The one way into the simulation from here, so it cannot sit behind a row. */ simulationLink: { marginInlineStart: "auto", fontSize: "0.875rem", lineHeight: "1.25rem", color: colors.primaryOnSurface, textDecorationLine: "none", }, liveNote: { marginTop: "0.5rem", fontSize: "0.875rem", lineHeight: "1.25rem", color: colors.textMuted, }, }); export default function ActivityPage() { const search = useSearch({ from: "/shell/activity" }); const navigate = useNavigate({ from: "/activity" }); const live = search.mode === "live"; // The functional form, not a replacement object: the filters are retained // across a mode switch on purpose, and spelling out a new search here would // drop every one of them on the way to Live and back. function selectMode(mode: ActivityMode) { if (mode === search.mode) return; void navigate({ search: (prev) => ({ ...prev, mode }) }); } function apply(filters: AppliedFilters) { void navigate({ search: { mode: search.mode, ...filters } }); } return (

Activity

{MODES.map((option) => { const selected = option.mode === search.mode; return ( ); })}
Current policy simulation
{/* * Remounted whenever the applied search changes, which is what makes * the back button work: the draft is derived state, and the browser * moving the URL under it has to move the form with it. */} apply(NO_FILTERS)} /> {live ? ( <>

The stream carries every query the server answers; these filters apply to history only.

) : ( )}
); }