// Live API call log. Every fetch the app makes shows up here in real time. // Replaces the old guided-tour overlay. import { useEffect, useRef, useState } from "react"; import { motion, AnimatePresence } from "framer-motion"; import { useApp } from "../state/store"; import { Close, Refresh, Layers, Pulse } from "./icons"; const METHOD_COLOR: Record = { GET: "var(--bp-muted)", POST: "var(--bp-info)", PUT: "var(--bp-amber)", PATCH: "var(--bp-amber)", DELETE: "var(--bp-err)", }; function statusClass(s: number): string { if (s === 0) return "err"; if (s >= 500) return "err"; if (s >= 400) return "warn"; if (s >= 200) return "ok"; return "info"; } export default function Console() { const open = useApp((s) => s.consoleOpen); const setOpen = useApp((s) => s.setConsoleOpen); const log = useApp((s) => s.apiLog); const clear = useApp((s) => s.clearApiLog); const [expanded, setExpanded] = useState(null); const [filter, setFilter] = useState<"all" | "writes" | "errors">("all"); const tailRef = useRef(null); useEffect(() => { if (!open) return; tailRef.current?.scrollTo({ top: tailRef.current.scrollHeight, behavior: "smooth" }); }, [log, open]); const filtered = log.filter((c) => { if (filter === "writes") return c.method !== "GET"; if (filter === "errors") return c.status === 0 || c.status >= 400; return true; }); return ( {open && (
Live console {log.length}
{filtered.length === 0 && (
No API calls yet. Flip to LIVE mode or perform an action.
)} {filtered.map((c) => { const isExpanded = expanded === c.id; const shortPath = c.path.replace(/^https?:\/\/[^/]+/, ""); return (
{isExpanded && (
{c.reqBody !== undefined && ( <>
request
{JSON.stringify(c.reqBody, null, 2)}
)} {c.error && ( <>
network error
{c.error}
)} {c.resBody !== undefined && ( <>
response
{typeof c.resBody === "string" ? c.resBody : JSON.stringify(c.resBody, null, 2)}
)}
)}
); })}
)}
); }