Files
hope-voice-api/web/index.html
T

188 lines
13 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">
<meta name="theme-color" content="#1C191F">
<title>Hope · Voice companion</title>
<link rel="manifest" href="manifest.json">
<link rel="icon" type="image/svg+xml" href="favicon.svg">
<link rel="apple-touch-icon" href="brand/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="192x192" href="brand/hope-icon-192.png">
<link rel="icon" type="image/png" sizes="512x512" href="brand/hope-icon-512.png">
<link rel="stylesheet" href="style.css">
</head>
<body data-mode="voice">
<a class="skip-link" href="#conversation">Skip to conversation</a>
<section id="loginGate" class="login-gate" aria-labelledby="loginHeading">
<h1 id="loginHeading">Welcome to Hope</h1>
<p>Sign in to your private voice and text companion.</p>
<form id="loginForm" class="account-form">
<label for="loginUsername">Username</label><input id="loginUsername" autocomplete="username" required maxlength="64">
<label for="loginPassword">Password</label><input id="loginPassword" type="password" autocomplete="current-password" required>
<button id="loginSubmit" class="btn btn-primary" disabled>Sign in</button>
</form>
<p id="loginMessage" role="status" aria-live="polite">Checking your session…</p>
<button id="retryLogout" class="btn btn-secondary" hidden>Retry sign out</button>
</section>
<main class="app-shell" id="authenticatedApp" hidden>
<header class="topbar">
<div class="brand">
<span class="brand-glyph" aria-hidden="true"><i></i></span>
<div>
<h1>Hope</h1>
<p>Voice and text companion</p>
</div>
</div>
<div class="topbar-actions" id="sessionActions" hidden>
<div class="mode-switch" role="group" aria-label="Conversation mode">
<button id="voiceModeBtn" class="mode-option active" type="button" aria-pressed="true">
<svg viewBox="0 0 24 24" width="15" height="15" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><path d="M12 2a3 3 0 0 0-3 3v7a3 3 0 0 0 6 0V5a3 3 0 0 0-3-3Z"></path><path d="M19 10v2a7 7 0 0 1-14 0v-2"></path></svg>
<span>Voice</span>
</button>
<button id="textModeBtn" class="mode-option" type="button" aria-pressed="false">
<svg viewBox="0 0 24 24" width="15" height="15" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><path d="M21 15a4 4 0 0 1-4 4H8l-5 3V7a4 4 0 0 1 4-4h10a4 4 0 0 1 4 4Z"></path></svg>
<span>Text</span>
</button>
</div>
<div class="identity-chip" id="identityChip" aria-label="Current account">
<span class="presence-dot" aria-hidden="true"></span>
<span>Private to <strong id="identityName"></strong></span>
</div>
<button id="tasksBtn" class="btn btn-secondary tasks-trigger" aria-label="Assistant activity" title="Assistant activity">
<svg viewBox="0 0 24 24" width="17" height="17" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M8 6h11M8 12h11M8 18h7"></path><path d="m3 6 1 1 2-2M3 12l1 1 2-2M3 18l1 1 2-2"></path></svg>
<span>Activity</span>
<span class="tasks-badge" id="tasksBadge" hidden>0</span>
</button>
</div>
</header>
<div class="account-toolbar">
<p id="accountStatus" role="status"></p>
<button id="adminBtn" class="btn btn-secondary" hidden aria-expanded="false" aria-controls="adminSection">Manage accounts</button>
<button id="logoutBtn" class="btn btn-secondary">Sign out</button>
</div>
<section id="adminSection" class="admin-section" hidden aria-labelledby="adminHeading">
<div class="admin-header"><div><h2 id="adminHeading" tabindex="-1">Accounts</h2><p>Create a private assistant for each person.</p></div><button id="refreshUsers" class="btn btn-secondary">Refresh status</button></div>
<p id="adminMessage" role="status">Loading accounts…</p>
<div class="table-scroll"><table><caption class="sr-only">Accounts and private assistant provisioning status</caption><thead><tr><th scope="col">Account</th><th scope="col">Role</th><th scope="col">Status</th><th scope="col">Container</th><th scope="col">Error</th><th scope="col">Actions</th></tr></thead><tbody id="usersBody"></tbody></table></div>
<h3>Create account</h3>
<form id="createUserForm" class="create-user-form account-form">
<div><label for="newUsername">Username</label><input id="newUsername" required autocomplete="off" maxlength="64"></div>
<div><label for="newDisplayName">Display name</label><input id="newDisplayName" required autocomplete="off" maxlength="120"></div>
<div><label for="newPassword">Initial password</label><input id="newPassword" type="password" required minlength="12" autocomplete="new-password"></div>
<div><label for="newRole">Role</label><select id="newRole"><option value="user">User</option><option value="admin">Administrator</option></select></div>
<button id="createUserSubmit" class="btn btn-primary">Create account</button>
</form><p id="createUserMessage" role="status"></p>
</section>
<div class="workspace">
<section class="voice-console" aria-labelledby="voiceHeading">
<div class="console-intro">
<h2 id="voiceHeading">I'm here.</h2>
<p id="modeDescription">Speak naturally. I’ll listen, remember, and answer in real time.</p>
</div>
<div class="orb-stage">
<div class="led-wall" id="ledWall" data-state="idle" aria-hidden="true">
</div>
<div class="orb-labels" aria-live="polite">
<span class="state-label" id="stateLabel">Ready when you are</span>
<span class="thinking-chip" id="thinkingChip" hidden>Thinking&hellip;</span>
<span class="barge-hint" id="bargeHint" hidden>Speak to interrupt</span>
</div>
</div>
<div class="controls" aria-label="Conversation controls">
<button id="startBtn" class="btn btn-primary">
<span class="button-spark" aria-hidden="true"></span>
Start conversation
</button>
<button id="stopBtn" class="btn btn-danger" disabled>End conversation</button>
<button id="muteBtn" class="btn btn-icon" aria-label="Mute microphone" title="Mute microphone" disabled>
<svg viewBox="0 0 24 24" width="19" height="19" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 2a3 3 0 0 0-3 3v7a3 3 0 0 0 6 0V5a3 3 0 0 0-3-3Z"></path><path d="M19 10v2a7 7 0 0 1-14 0v-2"></path><line x1="8" y1="22" x2="16" y2="22"></line></svg>
</button>
<button id="settingsBtn" class="btn btn-icon" aria-label="Voice settings" title="Voice settings">
<svg viewBox="0 0 24 24" width="19" height="19" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 7h10M18 7h2M4 17h2M10 17h10"></path><circle cx="16" cy="7" r="2"></circle><circle cx="8" cy="17" r="2"></circle></svg>
</button>
</div>
<div class="connection-strip">
<div class="status" id="status" role="status">Secure connection ready</div>
<div class="mic-meter" id="micLevel" aria-hidden="true"><span class="mic-meter-fill" id="micMeterFill"></span></div>
</div>
<p class="privacy-note">
<svg viewBox="0 0 24 24" width="15" height="15" fill="none" stroke="currentColor" stroke-width="1.8" aria-hidden="true"><rect x="5" y="10" width="14" height="10" rx="2"></rect><path d="M8 10V7a4 4 0 0 1 8 0v3"></path></svg>
<span id="privacyIdentity"></span>
</p>
</section>
<section class="transcript" id="conversation" aria-labelledby="conversationHeading">
<div class="transcript-header">
<div>
<h2 id="conversationHeading">Conversation</h2>
<p id="transcriptModeLabel">Live voice transcript</p>
</div>
<button id="clearBtn" class="btn btn-ghost" title="Clear conversation">Clear</button>
</div>
<div id="messages" aria-live="polite"></div>
<form class="chat-composer" id="chatComposer" aria-label="Send a text message">
<label class="sr-only" for="chatInput">Message Hope</label>
<textarea id="chatInput" rows="1" maxlength="2000" placeholder="Message Hope…" autocomplete="off"></textarea>
<button id="sendChatBtn" class="send-chat" type="submit" disabled aria-label="Send message">
<svg viewBox="0 0 24 24" width="19" height="19" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m22 2-7 20-4-9-9-4Z"></path><path d="M22 2 11 13"></path></svg>
</button>
<span class="composer-hint">Enter to send · Shift+Enter for a new line</span>
</form>
</section>
</div>
</main>
<div class="sheet-backdrop" id="sheetBackdrop" hidden></div>
<div class="settings-sheet" id="settingsSheet" hidden inert role="dialog" aria-modal="true" aria-label="Hope settings">
<div class="sheet-handle" aria-hidden="true"></div>
<div class="sheet-header">
<div>
<h2>Hope’s settings</h2>
<p>Your voice preference applies to this conversation. Reasoning is managed by your private assistant.</p>
</div>
<button id="closeSheetBtn" class="btn btn-icon" aria-label="Close settings">
<svg viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><path d="M18 6 6 18M6 6l12 12"></path></svg>
</button>
</div>
<h3 class="sheet-section-title">Voice</h3>
<div class="voice-list" id="voiceList"></div>
</div>
<div class="tasks-panel" id="tasksPanel" hidden inert aria-label="Assistant activity">
<div class="tasks-header">
<div>
<h2>Assistant activity</h2>
<p>Progress from your private assistant.</p>
</div>
<button id="closeTasksBtn" class="btn btn-icon" aria-label="Close activity">
<svg viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><path d="M18 6 6 18M6 6l12 12"></path></svg>
</button>
</div>
<div class="tasks-list" id="tasksList">
<div class="tasks-empty">
<strong>No activity yet</strong>
<span>Progress appears here while Hope works. Replies stay in your conversation.</span>
</div>
</div>
</div>
<script src="livekit-client.umd.js"></script>
<dialog id="accountDialog" aria-labelledby="accountDialogTitle" aria-describedby="accountDialogDescription">
<form id="accountActionForm" class="account-form"><h2 id="accountDialogTitle"></h2><p id="accountDialogDescription"></p>
<div id="resetPasswordField"><label for="resetPassword">New password</label><input id="resetPassword" type="password" minlength="12" autocomplete="new-password"></div>
<p id="accountDialogMessage" role="status"></p><div class="dialog-actions"><button id="cancelAccountAction" type="button" class="btn btn-secondary">Cancel</button><button id="confirmAccountAction" class="btn btn-danger">Confirm</button></div>
</form>
</dialog>
<script src="auth.js"></script>
<script src="app.js"></script>
</body>
</html>