216 lines
19 KiB
HTML
216 lines
19 KiB
HTML
<!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 · Your private 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="hope-themes.css">
|
||
<link rel="stylesheet" href="style.css">
|
||
</head>
|
||
<body data-mode="voice">
|
||
<a class="skip-link" href="#chatTimeline">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>
|
||
<aside id="chatSidebar" class="chat-sidebar" aria-label="Conversations"><div class="sidebar-brand"><span class="hope-mark" aria-hidden="true">✳</span><strong>Hope</strong><button id="drawerClose" class="btn btn-ghost" aria-label="Close navigation">×</button></div><button id="newConversation" class="btn new-chat">+ New chat</button><label class="sr-only" for="conversationSearch">Search conversations</label><input id="conversationSearch" type="search" placeholder="Search conversations"><nav aria-label="Chat history"><h2>Pinned</h2><div id="pinnedConversations"></div><div class="nav-heading"><h2>Workspaces</h2><button id="newWorkspace" class="btn btn-ghost" aria-label="Create workspace">+</button></div><div id="workspaceList"></div><h2>Recent</h2><div id="recentConversations"></div><button id="loadMoreConversations" class="btn btn-ghost" hidden>Load more</button></nav><div class="sidebar-footer"><button id="openVoice" class="btn btn-ghost">Voice & account</button><button id="openPreferences" class="btn btn-ghost">Appearance</button></div></aside><button id="drawerBackdrop" class="drawer-backdrop" aria-label="Close navigation" hidden></button>
|
||
<section id="primaryChat" class="primary-chat" aria-labelledby="chatTitle"><header class="chat-topbar"><button id="drawerOpen" class="btn btn-ghost" aria-label="Open navigation" aria-expanded="false" aria-controls="chatSidebar">☰</button><h1 id="chatTitle">New chat</h1><div id="featureActions" class="feature-actions"></div><div class="conversation-actions"><button id="pinConversation" class="btn btn-ghost" disabled>Pin</button><button id="renameConversation" class="btn btn-ghost" disabled>Rename</button><button id="archiveConversation" class="btn btn-ghost" disabled>Archive</button><button id="deleteConversation" class="btn btn-ghost" disabled>Delete</button></div></header><div id="chatNotice" class="chat-notice" role="status" aria-live="polite"></div><div id="chatTimeline" class="chat-timeline" tabindex="-1" aria-label="Messages"><div class="chat-welcome"><span class="hope-mark" aria-hidden="true">✳</span><h2>A little space to think.</h2><p>Ask a question, work through an idea, or start something new.</p></div></div><div id="runProgress" class="run-progress" role="status" aria-live="polite"></div><form id="primaryComposer" class="primary-composer" aria-label="Message Hope"><label class="sr-only" for="primaryInput">Message Hope</label><textarea id="primaryInput" rows="2" placeholder="What's on your mind?" required disabled></textarea><div id="attachmentPreviews"></div><div class="composer-footer"><span>Enter to send · Shift + Enter for a new line</span><button id="cancelRun" class="btn" type="button" hidden>Stop response</button><button id="primarySend" class="btn btn-primary" disabled>Send ↑</button></div></form><p class="chat-footnote">Your chats are private to your account. Hope can make mistakes.</p></section><section id="legacyVoice" class="legacy-voice" hidden aria-label="Voice and account settings"><button id="backToChat" class="btn btn-secondary">← Back to chat</button><p>Live voice controls and account settings.</p> <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…</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="Hope settings" title="Hope 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>
|
||
</section> </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.</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>
|
||
<div class="sheet-content">
|
||
<form id="modelSettingsForm" class="account-form model-settings" aria-labelledby="modelSettingsHeading" aria-describedby="modelSettingsScope">
|
||
<h3 id="modelSettingsHeading">Model and thinking</h3>
|
||
<p id="modelSettingsScope" class="model-settings-note">Saved for your account. Applies to your next request, including in an active conversation, without interrupting it.</p>
|
||
<div>
|
||
<label for="modelSelect">Model</label>
|
||
<select id="modelSelect" name="model" required disabled aria-describedby="modelSettingsStatus"><option value="">Open settings to load models</option></select>
|
||
</div>
|
||
<div>
|
||
<label for="thinkingLevelSelect">Thinking level</label>
|
||
<select id="thinkingLevelSelect" name="thinking_level" required disabled aria-describedby="thinkingLevelNote"><option value="">Open settings to load thinking levels</option></select>
|
||
<p id="thinkingLevelNote" class="model-settings-note">Thinking requests may be limited or ignored by the selected model.</p>
|
||
</div>
|
||
<p id="modelSettingsStatus" class="model-settings-status" role="status" aria-live="polite" aria-atomic="true">Open settings to load your saved preferences.</p>
|
||
<div class="model-settings-actions">
|
||
<button id="saveModelSettings" type="submit" class="btn btn-primary" disabled>Save</button>
|
||
<button id="retryModelSettings" type="button" class="btn btn-secondary" hidden>Retry</button>
|
||
</div>
|
||
</form>
|
||
<h3 class="sheet-section-title">Voice</h3>
|
||
<label for="voiceSearch">Search English voices</label>
|
||
<input id="voiceSearch" type="search" placeholder="Name, locale, gender, or Omni" autocomplete="off" aria-controls="voiceList">
|
||
<p id="voiceCatalogStatus" class="model-settings-note" role="status" aria-live="polite">Ava Dragon HD Omni is the recommended female default.</p>
|
||
<button id="retryVoices" type="button" class="btn btn-secondary" hidden>Retry voice catalog</button>
|
||
<div class="voice-list" id="voiceList" role="radiogroup" aria-label="English voices"></div>
|
||
</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>
|
||
<dialog id="chatActionDialog" aria-labelledby="chatActionTitle"><form id="chatActionForm" class="account-form"><h2 id="chatActionTitle"></h2><p id="chatActionDescription"></p><label id="chatActionLabel" for="chatActionValue">Name</label><textarea id="chatActionValue" rows="3" required></textarea><p id="chatActionError" role="status"></p><div class="dialog-actions"><button id="chatActionCancel" type="button" class="btn">Cancel</button><button id="chatActionSave" class="btn btn-primary">Save</button></div></form></dialog><dialog id="preferencesDialog" aria-labelledby="preferencesTitle"><h2 id="preferencesTitle">Appearance</h2><form id="preferencesForm" class="account-form"><label for="paletteSelect">Palette</label><select id="paletteSelect"><option value="opal">Opal</option><option value="graphite">Graphite</option><option value="ocean">Ocean</option><option value="sage">Sage</option><option value="lavender">Lavender</option></select><label for="appearanceSelect">Appearance</label><select id="appearanceSelect"><option value="system">System</option><option value="light">Light</option><option value="dark">Dark</option></select><p id="preferencesMessage" role="status"></p><div class="dialog-actions"><button id="closePreferences" class="btn" type="button">Close</button><button class="btn btn-primary">Save appearance</button></div></form></dialog><script src="auth.js"></script>
|
||
<script src="app.js"></script>
|
||
<script src="chat.js"></script>
|
||
<script src="features.js"></script>
|
||
</body>
|
||
</html>
|