Fetch-Pup / debug-panel.jsx
ElisaTrippetti's picture
Update debug-panel.jsx
cb1c42d verified
// debug-panel.jsx — floating developer panel shared across all variants.
// Surfaces: recent exchanges, fact-match status, backend, latency, and the
// full keyword-lookup table from the Python reference.
function DebugPanel({ debug, onClose, palette, font }) {
const [tab, setTab] = React.useState("turns");
return (
<div style={{
position: "absolute", right: 16, bottom: 16, top: 16,
width: 380, maxWidth: "44vw",
background: palette.bg, color: palette.fg,
border: `1px solid ${palette.muted}`,
borderRadius: 10,
fontFamily: font, fontSize: 12,
display: "flex", flexDirection: "column",
boxShadow: "0 10px 30px rgba(0,0,0,.35)",
zIndex: 50,
overflow: "hidden",
}}>
<div style={{
padding: "8px 12px",
borderBottom: `1px solid ${palette.muted}`,
display: "flex", alignItems: "center", justifyContent: "space-between",
}}>
<div style={{ display: "flex", gap: 10, alignItems: "center" }}>
<span style={{ color: palette.accent }}></span>
<span style={{ letterSpacing: 0.8, textTransform: "uppercase", fontSize: 11 }}>
fetch pup / debug
</span>
</div>
<button onClick={onClose} aria-label="Close debug panel" style={{
background: "transparent", border: "none", color: palette.dim,
cursor: "pointer", fontSize: 14, padding: "0 4px",
}}>×</button>
</div>
<div style={{
display: "flex", gap: 2,
padding: "6px 10px 0",
borderBottom: `1px solid ${palette.muted}`,
}}>
{[
{ id: "turns", label: "turns" },
{ id: "intents", label: "intents" },
{ id: "keywords", label: "keywords" },
{ id: "about", label: "about" },
].map(t => (
<button key={t.id} onClick={() => setTab(t.id)} style={{
background: "transparent",
color: tab === t.id ? palette.fg : palette.dim,
borderBottom: tab === t.id ? `2px solid ${palette.accent}` : "2px solid transparent",
border: "none", borderRadius: 0,
padding: "6px 8px",
fontFamily: "inherit", fontSize: 12, cursor: "pointer",
}}>{t.label}</button>
))}
</div>
<div style={{ flex: 1, overflowY: "auto", padding: 12 }}>
{tab === "turns" && <DebugLog debug={debug} palette={palette} />}
{tab === "intents" && <DebugIntents palette={palette} />}
{tab === "keywords" && <DebugKB palette={palette} />}
{tab === "about" && <DebugAbout palette={palette} />}
</div>
</div>
);
}
function DebugLog({ debug, palette }) {
if (debug.length === 0) {
return (
<div style={{ color: palette.dim, fontStyle: "italic", padding: "8px 4px" }}>
No turns yet. Ask Fetch something.
</div>
);
}
return (
<div style={{ display: "flex", flexDirection: "column", gap: 10 }}>
{debug.map((d, i) => (
<div key={i} style={{
border: `1px solid ${palette.muted}`,
borderRadius: 6,
padding: 8,
}}>
<div style={{ color: palette.dim, fontSize: 11, display: "flex", justifyContent: "space-between", marginBottom: 4 }}>
<span>{new Date(d.t).toLocaleTimeString()}</span>
<span>{d.backend} · {d.ms}ms</span>
</div>
<div style={{ marginBottom: 4 }}>
<span style={{ color: palette.dim }}>user&gt; </span>
{d.user}
</div>
<div style={{ marginBottom: 4 }}>
<span style={{ color: palette.accent }}>fetch&gt; </span>
{d.reply}
</div>
<div style={{ color: palette.dim, fontSize: 11 }}>
{"source: "}
<span style={{
color: d.source === "error" ? "#ff6b6b"
: d.source === "fallback" ? palette.dim
: palette.accent,
}}>
{d.source === "fact" ? `fact · ${d.fact?.keyword}`
: d.source === "intent" ? `intent · ${d.label}`
: d.source || "unknown"}
</span>
</div>
</div>
))}
</div>
);
}
function DebugKB({ palette }) {
return (
<div style={{ display: "flex", flexDirection: "column", gap: 8 }}>
<div style={{ color: palette.dim, marginBottom: 4 }}>
{FETCH_KNOWLEDGE_BASE.length} keywords · first match wins
</div>
{FETCH_KNOWLEDGE_BASE.map(([k, v]) => (
<div key={k} style={{
border: `1px solid ${palette.muted}`,
borderRadius: 6,
padding: 8,
}}>
<div style={{ color: palette.accent, marginBottom: 3 }}>{k}</div>
<div style={{ color: palette.fg, lineHeight: 1.4 }}>{v}</div>
</div>
))}
</div>
);
}
function DebugIntents({ palette }) {
return (
<div style={{ display: "flex", flexDirection: "column", gap: 8 }}>
<div style={{ color: palette.dim, marginBottom: 4 }}>
{FETCH_INTENTS.length} intents · first match wins
</div>
{FETCH_INTENTS.map(intent => (
<div key={intent.label} style={{
border: `1px solid ${palette.muted}`,
borderRadius: 6,
padding: 8,
}}>
<div style={{ color: palette.accent, marginBottom: 3 }}>{intent.label}</div>
<div style={{ color: palette.fg, lineHeight: 1.4 }}>{intent.response}</div>
</div>
))}
</div>
);
}
function DebugAbout({ palette }) {
return (
<div style={{ lineHeight: 1.6, color: palette.fg, display: "flex", flexDirection: "column", gap: 10 }}>
<div>Fetch Pup is a static browser demo — there is no model running.</div>
<div>Responses come from two layers: an intent matcher that handles greetings and small talk, and a keyword lookup table that returns dog facts verbatim. If neither matches, you get one of three fallback replies.</div>
<div>The generative version, powered by Meta's BlenderBot-400M-distill, runs separately as a <a href="https://huggingface.co/spaces/ElisaTrippetti/Fetch-Good-Boi" target="_blank" rel="noopener noreferrer" style={{ color: palette.accent, textDecoration: "underline" }}>Docker Space</a>.</div>
<div style={{ color: palette.dim }}>Designed with Claude Design · Built with Claude Code</div>
</div>
);
}
Object.assign(window, { DebugPanel });