From 9c1aa7374252cac87de4b7054907f546a99e4ce9 Mon Sep 17 00:00:00 2001 From: artheru Date: Wed, 10 Jun 2026 15:08:26 +0800 Subject: [PATCH] home: turn the API peek into a live console (0.98.0) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit the agent-native callout's API peek showed a curl line + the JSON, but felt static. made it feel like a real API client: - after each call it shows the response status and timing (green dot, '200 OK · 47 ms') measured with performance.now() — the visitor's actual request time - a '↻ run' button re-fetches the current endpoint (running… -> new status) - endpoint tabs still switch (stats / roster / activity), all read-only & no-auth - verified live: status '200 OK · 683 ms', run re-fetches (running… -> 200 OK), green dot on success - changelog entry (0.98) added with the bump --- index.html | 2 +- package.json | 2 +- src/Scene2D.jsx | 32 ++++++++++++++++++++++---------- src/styles.css | 11 +++++++++++ 4 files changed, 35 insertions(+), 12 deletions(-) diff --git a/index.html b/index.html index 0f769c9..6153c40 100644 --- a/index.html +++ b/index.html @@ -17,7 +17,7 @@ @media (prefers-reduced-motion: reduce) { .app-loading-gem { animation: none; transform: rotate(45deg); } } - + diff --git a/package.json b/package.json index ccfc2a3..0bd260e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "amerc-site", - "version": "0.97.0-signin", + "version": "0.98.0-apiconsole", "private": true, "type": "module", "scripts": { diff --git a/src/Scene2D.jsx b/src/Scene2D.jsx index aef332e..8c4b881 100644 --- a/src/Scene2D.jsx +++ b/src/Scene2D.jsx @@ -10,7 +10,7 @@ import { api } from './api.js'; const Menu = ({ size = 20 }) => (); const X = ({ size = 20 }) => (); -const RELEASE = '0.97.0-signin'; +const RELEASE = '0.98.0-apiconsole'; const NAV = [ { id: 'home', label: 'Tavern' }, @@ -250,17 +250,20 @@ function jsonHl(obj) { function ApiPeek() { const [ep, setEp] = useState('stats'); const [out, setOut] = useState(null); + const [meta, setMeta] = useState(null); const [loading, setLoading] = useState(true); - useEffect(() => { - let live = true; - setLoading(true); setOut(null); - const e = PEEK.find((x) => x.id === ep); + const run = useCallback((id) => { + let cancelled = false; + setLoading(true); setOut(null); setMeta(null); + const e = PEEK.find((x) => x.id === id); + const t0 = performance.now(); api(e.path) - .then((d) => { if (live) setOut(d); }) - .catch((err) => { if (live) setOut({ error: err.message }); }) - .finally(() => { if (live) setLoading(false); }); - return () => { live = false; }; - }, [ep]); + .then((d) => { if (!cancelled) { setOut(d); setMeta({ ok: true, ms: Math.round(performance.now() - t0) }); } }) + .catch((err) => { if (!cancelled) { setOut({ error: err.message }); setMeta({ ok: false, ms: Math.round(performance.now() - t0) }); } }) + .finally(() => { if (!cancelled) setLoading(false); }); + return () => { cancelled = true; }; + }, []); + useEffect(() => run(ep), [ep, run]); const cur = PEEK.find((x) => x.id === ep); return (
@@ -271,6 +274,14 @@ function ApiPeek() {
curl https://amerc.ai/api{cur.path}
+
+ + {loading + ? running… + : meta && <> + +
{loading ?
fetching live response…
:
}
@@ -465,6 +476,7 @@ function StatusPage() {
 }
 
 const CHANGELOG = [
+  { v: '0.98', date: 'Jun 2026', title: 'Live API console', notes: ['The home-page API peek now shows the response status and timing, with a run button to re-fetch'] },
   { v: '0.97', date: 'Jun 2026', title: 'Clearer sign-in', notes: ['The join form now uses plain labels (Email, Password) and a clear Log in button'] },
   { v: '0.96', date: 'Jun 2026', title: 'Hero fits the fold', notes: ['The tagline, calls-to-action and live stats now sit above the fold on laptop screens'] },
   { v: '0.95', date: 'Jun 2026', title: 'Docs that flow', notes: ['Fixed dead Quickstart links and cross-linked the guides so you can click straight through'] },
diff --git a/src/styles.css b/src/styles.css
index 5c352ed..74a49e7 100644
--- a/src/styles.css
+++ b/src/styles.css
@@ -1554,3 +1554,14 @@ button {
 .ap-keys-curl code { flex: 1; min-width: 0; font-family: ui-monospace, "SF Mono", Menlo, monospace; font-size: 11.5px; color: #cfe2f2; overflow-x: auto; white-space: nowrap; }
 .ap-keys-copy { flex: 0 0 auto; background: #46c8e0; color: #07111a; border: none; border-radius: 6px; padding: 5px 11px; font-size: 11px; font-weight: 600; cursor: pointer; }
 .ap-keys-copy:hover { background: #6ad6ea; }
+
+/* api peek: response status/timing bar + run button (0.98) */
+.hf-peek-bar { display: flex; align-items: center; justify-content: space-between; gap: 10px; margin: 8px 0 9px; font-size: 11px; }
+.hf-peek-status { display: flex; align-items: center; gap: 7px; color: #8fa3b8; min-height: 18px; }
+.hf-peek-muted { color: #5b6b7d; }
+.hf-peek-dot { width: 7px; height: 7px; border-radius: 50%; flex: 0 0 auto; }
+.hf-peek-dot.ok { background: #4ee08a; box-shadow: 0 0 7px #4ee08a; }
+.hf-peek-dot.err { background: #ff6b6b; box-shadow: 0 0 7px #ff6b6b; }
+.hf-peek-run { background: none; border: 1px solid #2a4a5c; color: #7fd9ea; border-radius: 6px; padding: 3px 10px; font-size: 11px; cursor: pointer; font-family: inherit; flex: 0 0 auto; }
+.hf-peek-run:hover:not(:disabled) { background: rgba(39, 216, 255, 0.1); color: #cfeff7; }
+.hf-peek-run:disabled { opacity: 0.5; cursor: default; }