169 lines
7.7 KiB
HTML
169 lines
7.7 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="en" data-loading="true">
|
||
<head>
|
||
<meta charset="utf-8" />
|
||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||
<meta name="color-scheme" content="dark" />
|
||
<title>AGIBOT A3 · Voice Control</title>
|
||
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>🤖</text></svg>" />
|
||
<link rel="stylesheet" href="/styles/main.css" />
|
||
</head>
|
||
<body>
|
||
|
||
<!-- ================= HEADER ================= -->
|
||
<header class="topbar">
|
||
<div class="topbar__brand">
|
||
<span class="brand__mark" aria-hidden="true">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round">
|
||
<rect x="4" y="8" width="16" height="12" rx="3" />
|
||
<path d="M12 4v4" /><circle cx="12" cy="3" r="1.4" />
|
||
<circle cx="9" cy="14" r="1.2" fill="currentColor" stroke="none" />
|
||
<circle cx="15" cy="14" r="1.2" fill="currentColor" stroke="none" />
|
||
<path d="M9.5 17.5h5" />
|
||
</svg>
|
||
</span>
|
||
<div class="brand__text">
|
||
<h1>AGIBOT A3</h1>
|
||
<p>Voice Control</p>
|
||
</div>
|
||
<span class="mode-badge" id="modeBadge" title="Robot mode">--</span>
|
||
</div>
|
||
|
||
<div class="topbar__status">
|
||
<!-- starts amber, not red: before the socket opens we genuinely do not
|
||
know the robot's state, and a red flash on every load reads as a fault -->
|
||
<div class="conn" id="connPill" data-state="connecting" title="Robot connection">
|
||
<span class="conn__dot" aria-hidden="true"></span>
|
||
<span class="conn__label" id="connLabel">Starting…</span>
|
||
</div>
|
||
<div class="chip" id="latencyChip" hidden title="Round-trip time to the robot">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><path d="M13 2 4 14h7l-1 8 9-12h-7l1-8Z"/></svg>
|
||
<span id="latencyValue">–</span>
|
||
</div>
|
||
<button class="icon-btn" id="reconnectBtn" title="Reconnect to robot" aria-label="Reconnect to robot">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
|
||
<path d="M21 12a9 9 0 1 1-2.64-6.36" /><path d="M21 3v6h-6" />
|
||
</svg>
|
||
</button>
|
||
</div>
|
||
</header>
|
||
|
||
<!-- ================= CONFIG BANNER ================= -->
|
||
<div class="banner" id="configBanner" hidden role="alert">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><path d="M12 9v4"/><path d="M12 17h.01"/><path d="M10.3 3.9 1.8 18a2 2 0 0 0 1.7 3h17a2 2 0 0 0 1.7-3L13.7 3.9a2 2 0 0 0-3.4 0Z"/></svg>
|
||
<div class="banner__body"><strong id="bannerTitle">Configuration</strong><span id="bannerText"></span></div>
|
||
</div>
|
||
|
||
<!-- ================= MAIN ================= -->
|
||
<main class="layout">
|
||
|
||
<!-- ---------- speech panel ---------- -->
|
||
<section class="panel panel--speech" aria-label="Speech">
|
||
<div class="panel__head">
|
||
<h2>Say something</h2>
|
||
<span class="panel__hint">The robot speaks through its built-in speaker</span>
|
||
</div>
|
||
|
||
<div class="composer">
|
||
<textarea
|
||
id="textInput"
|
||
class="composer__input"
|
||
placeholder="Type what you want the robot to say…"
|
||
spellcheck="true"
|
||
autocomplete="off"
|
||
rows="7"
|
||
maxlength="1000"></textarea>
|
||
|
||
<div class="composer__meta">
|
||
<span class="kbd-hint"><kbd>Ctrl</kbd><span>+</span><kbd>Enter</kbd> to speak · <kbd>Esc</kbd> to stop</span>
|
||
<span class="counter" id="counter"><b id="counterNow">0</b> / <span id="counterMax">1000</span></span>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="actions">
|
||
<button class="btn btn--speak" id="speakBtn" disabled>
|
||
<span class="btn__icon" aria-hidden="true">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||
<path d="M11 5 6 9H2v6h4l5 4V5Z" /><path d="M15.5 8.5a5 5 0 0 1 0 7" /><path d="M18.5 5.5a9 9 0 0 1 0 13" />
|
||
</svg>
|
||
</span>
|
||
<span class="btn__label" id="speakLabel">Speak</span>
|
||
<span class="btn__spinner" aria-hidden="true"></span>
|
||
</button>
|
||
|
||
<button class="btn btn--ghost" id="stopBtn" disabled>
|
||
<svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><rect x="6" y="6" width="12" height="12" rx="2" /></svg>
|
||
Stop
|
||
</button>
|
||
|
||
<button class="btn btn--ghost" id="clearBtn">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><path d="M3 6h18"/><path d="M8 6V4h8v2"/><path d="m19 6-1 14H6L5 6"/></svg>
|
||
Clear
|
||
</button>
|
||
</div>
|
||
|
||
<!-- pipeline -->
|
||
<div class="pipeline" id="pipeline" data-stage="idle">
|
||
<div class="pipeline__track" aria-hidden="true"><i id="pipelineFill"></i></div>
|
||
<ol class="pipeline__steps">
|
||
<li data-step="sending"><span class="dot"></span>Sending</li>
|
||
<li data-step="processing"><span class="dot"></span>Processing</li>
|
||
<li data-step="speaking"><span class="dot"></span>Playing</li>
|
||
<li data-step="completed"><span class="dot"></span>Completed</li>
|
||
</ol>
|
||
</div>
|
||
|
||
<!-- live status -->
|
||
<div class="live" id="live" data-tone="idle" role="status" aria-live="polite">
|
||
<span class="live__wave" id="liveWave" aria-hidden="true"><i></i><i></i><i></i><i></i><i></i></span>
|
||
<span class="live__text" id="liveText">Ready</span>
|
||
<span class="live__detail" id="liveDetail"></span>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- ---------- sidebar ---------- -->
|
||
<aside class="sidebar">
|
||
|
||
<section class="panel panel--status" aria-label="Robot status">
|
||
<div class="panel__head"><h2>Robot</h2></div>
|
||
<dl class="facts">
|
||
<div class="fact"><dt>State</dt><dd id="factState">–</dd></div>
|
||
<div class="fact"><dt>Mode</dt><dd id="factMode">–</dd></div>
|
||
<div class="fact"><dt>Link</dt><dd id="factTransport">–</dd></div>
|
||
<div class="fact"><dt>Address</dt><dd id="factAddress" class="mono">–</dd></div>
|
||
<div class="fact"><dt>Latency</dt><dd id="factLatency">–</dd></div>
|
||
<div class="fact"><dt>Uptime</dt><dd id="factUptime">–</dd></div>
|
||
</dl>
|
||
<p class="panel__note" id="statusNote" hidden></p>
|
||
</section>
|
||
|
||
<section class="panel panel--audio" aria-label="Saved audio">
|
||
<div class="panel__head">
|
||
<h2>Saved audio <span class="count" id="audioCount">0</span></h2>
|
||
<button class="link-btn" id="clearAudioBtn">Clear</button>
|
||
</div>
|
||
<p class="panel__note" id="audioNote">
|
||
Every line spoken with the neural voice is saved as a .wav file, so it
|
||
replays instantly with no synthesis and no internet.
|
||
</p>
|
||
<ul class="audio-list" id="audioList"></ul>
|
||
</section>
|
||
|
||
<section class="panel panel--history" aria-label="Speech history">
|
||
<div class="panel__head">
|
||
<h2>History <span class="count" id="historyCount">0</span></h2>
|
||
<button class="link-btn" id="clearHistoryBtn">Clear</button>
|
||
</div>
|
||
<ul class="history" id="historyList"></ul>
|
||
<p class="empty" id="historyEmpty">Nothing spoken yet. Your recent utterances appear here — click one to reuse it.</p>
|
||
</section>
|
||
|
||
</aside>
|
||
</main>
|
||
|
||
<div class="toasts" id="toasts" aria-live="assertive"></div>
|
||
|
||
<script type="module" src="/js/app.js"></script>
|
||
</body>
|
||
</html>
|