Spaces:
Sleeping
Sleeping
| // debug-panel.jsx — floating developer panel shared across all variants. | |
| // Surfaces: recent turns, source tracking, backend, latency, and the | |
| // full keyword-lookup table. | |
| 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 good boi / 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: "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 === "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: 12, 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> </span> | |
| {d.user} | |
| </div> | |
| <div style={{ marginBottom: 4 }}> | |
| <span style={{ color: palette.accent }}>fetch> </span> | |
| {d.reply} | |
| </div> | |
| <div style={{ color: palette.dim, fontSize: 12 }}> | |
| {"source: "} | |
| <span style={{ | |
| color: d.source === "error" ? "#ff6b6b" | |
| : d.source === "fallback" ? palette.dim | |
| : palette.accent, | |
| }}> | |
| {d.source === "fact" ? `fact · ${d.fact?.keyword}` : 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 DebugAbout({ palette }) { | |
| return ( | |
| <div style={{ lineHeight: 1.6, color: palette.fg, display: "flex", flexDirection: "column", gap: 10 }}> | |
| <div>Fetch Good Boi is a dog-loving chatbot backed by BlenderBot-400M-distill running on CPU.</div> | |
| <div>Responses use a keyword lookup layer — if a fact matches the query, it's prepended to the prompt. BlenderBot then generates a reply within a four-turn context window. Fallbacks cover anything the model returns too short or empty. If the backend fails, the browser falls back to keyword lookup automatically.</div> | |
| <div>The demo version runs separately as a <a href="https://huggingface.co/spaces/ElisaTrippetti/Fetch-Pup" target="_blank" rel="noopener noreferrer" style={{ color: palette.accent, textDecoration: "underline" }}>static Space</a>.</div> | |
| <div>Fetch Good Boi runs on CPU, so responses may take a few seconds.</div> | |
| <div style={{ color: palette.dim }}>Designed with Claude Design · Built with Claude Code</div> | |
| </div> | |
| ); | |
| } | |
| Object.assign(window, { DebugPanel }); | |