@mooncompany/uplink-chat 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of @mooncompany/uplink-chat might be problematic. Click here for more details.
- package/LICENSE +21 -0
- package/README.md +185 -0
- package/bin/uplink.js +279 -0
- package/middleware/error-handler.js +69 -0
- package/package.json +93 -0
- package/public/css/agents.36b98c0f.css +1469 -0
- package/public/css/agents.css +1469 -0
- package/public/css/app.a6a7f8f5.css +2731 -0
- package/public/css/app.css +2731 -0
- package/public/css/artifacts.css +444 -0
- package/public/css/commands.css +55 -0
- package/public/css/connection.css +131 -0
- package/public/css/dashboard.css +233 -0
- package/public/css/developer.css +328 -0
- package/public/css/files.css +123 -0
- package/public/css/markdown.css +156 -0
- package/public/css/message-actions.css +278 -0
- package/public/css/mobile.css +614 -0
- package/public/css/panels-unified.css +483 -0
- package/public/css/premium.css +415 -0
- package/public/css/realtime.css +189 -0
- package/public/css/satellites.css +401 -0
- package/public/css/shortcuts.css +185 -0
- package/public/css/split-view.4def0262.css +673 -0
- package/public/css/split-view.css +673 -0
- package/public/css/theme-generator.css +391 -0
- package/public/css/themes.css +387 -0
- package/public/css/timestamps.css +54 -0
- package/public/css/variables.css +78 -0
- package/public/dist/bundle.b55050c4.js +15757 -0
- package/public/favicon.svg +24 -0
- package/public/img/agents/ada.png +0 -0
- package/public/img/agents/clarice.png +0 -0
- package/public/img/agents/dennis-nedry.png +0 -0
- package/public/img/agents/elliot-alderson.png +0 -0
- package/public/img/agents/main.png +0 -0
- package/public/img/agents/scotty.png +0 -0
- package/public/img/agents/top-flight-security.png +0 -0
- package/public/index.html +1083 -0
- package/public/js/agents-data.js +234 -0
- package/public/js/agents-ui.js +72 -0
- package/public/js/agents.js +1525 -0
- package/public/js/app.js +79 -0
- package/public/js/appearance-settings.js +111 -0
- package/public/js/artifacts.js +432 -0
- package/public/js/audio-queue.js +168 -0
- package/public/js/bootstrap.js +54 -0
- package/public/js/chat.js +1211 -0
- package/public/js/commands.js +581 -0
- package/public/js/connection-api.js +121 -0
- package/public/js/connection.js +1231 -0
- package/public/js/context-tracker.js +271 -0
- package/public/js/core.js +172 -0
- package/public/js/dashboard.js +452 -0
- package/public/js/developer.js +432 -0
- package/public/js/encryption.js +124 -0
- package/public/js/errors.js +122 -0
- package/public/js/event-bus.js +77 -0
- package/public/js/fetch-utils.js +171 -0
- package/public/js/file-handler.js +229 -0
- package/public/js/files.js +352 -0
- package/public/js/gateway-chat.js +538 -0
- package/public/js/logger.js +112 -0
- package/public/js/markdown.js +190 -0
- package/public/js/message-actions.js +431 -0
- package/public/js/message-renderer.js +288 -0
- package/public/js/missed-messages.js +235 -0
- package/public/js/mobile-debug.js +95 -0
- package/public/js/notifications.js +367 -0
- package/public/js/offline-queue.js +178 -0
- package/public/js/onboarding.js +543 -0
- package/public/js/panels.js +156 -0
- package/public/js/premium.js +412 -0
- package/public/js/realtime-voice.js +844 -0
- package/public/js/satellite-sync.js +256 -0
- package/public/js/satellite-ui.js +175 -0
- package/public/js/satellites.js +1516 -0
- package/public/js/settings.js +1087 -0
- package/public/js/shortcuts.js +381 -0
- package/public/js/split-chat.js +1234 -0
- package/public/js/split-resize.js +211 -0
- package/public/js/splitview.js +340 -0
- package/public/js/storage.js +408 -0
- package/public/js/streaming-handler.js +324 -0
- package/public/js/stt-settings.js +316 -0
- package/public/js/theme-generator.js +661 -0
- package/public/js/themes.js +164 -0
- package/public/js/timestamps.js +198 -0
- package/public/js/tts-settings.js +575 -0
- package/public/js/ui.js +267 -0
- package/public/js/update-notifier.js +143 -0
- package/public/js/utils/constants.js +165 -0
- package/public/js/utils/sanitize.js +93 -0
- package/public/js/utils/sse-parser.js +195 -0
- package/public/js/voice.js +883 -0
- package/public/manifest.json +58 -0
- package/public/moon_texture.jpg +0 -0
- package/public/sw.js +221 -0
- package/public/three.min.js +6 -0
- package/server/channel.js +529 -0
- package/server/chat.js +270 -0
- package/server/config-store.js +362 -0
- package/server/config.js +159 -0
- package/server/context.js +131 -0
- package/server/gateway-commands.js +211 -0
- package/server/gateway-proxy.js +318 -0
- package/server/index.js +22 -0
- package/server/logger.js +89 -0
- package/server/middleware/auth.js +188 -0
- package/server/middleware.js +218 -0
- package/server/openclaw-discover.js +308 -0
- package/server/premium/index.js +156 -0
- package/server/premium/license.js +140 -0
- package/server/realtime/bridge.js +837 -0
- package/server/realtime/index.js +349 -0
- package/server/realtime/tts-stream.js +446 -0
- package/server/routes/agents.js +564 -0
- package/server/routes/artifacts.js +174 -0
- package/server/routes/chat.js +311 -0
- package/server/routes/config-settings.js +345 -0
- package/server/routes/config.js +603 -0
- package/server/routes/files.js +307 -0
- package/server/routes/index.js +18 -0
- package/server/routes/media.js +451 -0
- package/server/routes/missed-messages.js +107 -0
- package/server/routes/premium.js +75 -0
- package/server/routes/push.js +156 -0
- package/server/routes/satellite.js +406 -0
- package/server/routes/status.js +251 -0
- package/server/routes/stt.js +35 -0
- package/server/routes/voice.js +260 -0
- package/server/routes/webhooks.js +203 -0
- package/server/routes.js +206 -0
- package/server/runtime-config.js +336 -0
- package/server/share.js +305 -0
- package/server/stt/faster-whisper.js +72 -0
- package/server/stt/groq.js +51 -0
- package/server/stt/index.js +196 -0
- package/server/stt/openai.js +49 -0
- package/server/sync.js +244 -0
- package/server/tailscale-https.js +175 -0
- package/server/tts.js +646 -0
- package/server/update-checker.js +172 -0
- package/server/utils/filename.js +129 -0
- package/server/utils.js +147 -0
- package/server/watchdog.js +318 -0
- package/server/websocket/broadcast.js +359 -0
- package/server/websocket/connections.js +339 -0
- package/server/websocket/index.js +215 -0
- package/server/websocket/routing.js +277 -0
- package/server/websocket/sync.js +102 -0
- package/server.js +404 -0
- package/utils/detect-tool-usage.js +93 -0
- package/utils/errors.js +158 -0
- package/utils/html-escape.js +84 -0
- package/utils/id-sanitize.js +94 -0
- package/utils/response.js +130 -0
- package/utils/with-retry.js +105 -0
|
@@ -0,0 +1,1083 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">
|
|
6
|
+
<meta name="mobile-web-app-capable" content="yes">
|
|
7
|
+
<meta name="apple-mobile-web-app-capable" content="yes">
|
|
8
|
+
<meta name="theme-color" content="#000000">
|
|
9
|
+
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
|
|
10
|
+
<link rel="icon" type="image/svg+xml" href="/favicon.svg">
|
|
11
|
+
<link rel="apple-touch-icon" href="/icon-192.png">
|
|
12
|
+
<link rel="manifest" href="/manifest.json">
|
|
13
|
+
<title>Uplink</title>
|
|
14
|
+
<link href="https://fonts.googleapis.com/css2?family=Nunito+Sans:wght@400;500;600;700&family=Space+Mono:wght@400;700&display=swap" rel="stylesheet">
|
|
15
|
+
<script src="/three.min.js"></script>
|
|
16
|
+
<link rel="stylesheet" href="/css/variables.css">
|
|
17
|
+
<link rel="stylesheet" href="/css/app.a6a7f8f5.css">
|
|
18
|
+
<link rel="stylesheet" href="/css/commands.css">
|
|
19
|
+
<link rel="stylesheet" href="/css/developer.css">
|
|
20
|
+
<link rel="stylesheet" href="/css/agents.36b98c0f.css">
|
|
21
|
+
<!-- waveform.css removed: replaced by audio-player-bar -->
|
|
22
|
+
<link rel="stylesheet" href="/css/dashboard.css">
|
|
23
|
+
<link rel="stylesheet" href="/css/files.css">
|
|
24
|
+
<link rel="stylesheet" href="/css/themes.css">
|
|
25
|
+
<link rel="stylesheet" href="/css/premium.css">
|
|
26
|
+
<link rel="stylesheet" href="/css/theme-generator.css">
|
|
27
|
+
<link rel="stylesheet" href="/css/shortcuts.css">
|
|
28
|
+
<link rel="stylesheet" href="/css/timestamps.css">
|
|
29
|
+
<link rel="stylesheet" href="/css/markdown.css">
|
|
30
|
+
<link rel="stylesheet" href="/css/connection.css">
|
|
31
|
+
<link rel="stylesheet" href="/css/satellites.css">
|
|
32
|
+
<link rel="stylesheet" href="/css/panels-unified.css">
|
|
33
|
+
<link rel="stylesheet" href="/css/message-actions.css">
|
|
34
|
+
<link rel="stylesheet" href="/css/split-view.4def0262.css">
|
|
35
|
+
<link rel="stylesheet" href="/css/mobile.css">
|
|
36
|
+
<link rel="stylesheet" href="/css/artifacts.css">
|
|
37
|
+
<link rel="stylesheet" href="/css/realtime.css">
|
|
38
|
+
|
|
39
|
+
<!-- Mobile debug panel - uncomment to enable
|
|
40
|
+
<script src="/js/mobile-debug.js"></script>
|
|
41
|
+
-->
|
|
42
|
+
<!-- cache-bust: v20260205075805 -->
|
|
43
|
+
<!-- Apply theme before render to prevent flash -->
|
|
44
|
+
<script>
|
|
45
|
+
(function() {
|
|
46
|
+
try {
|
|
47
|
+
var s = JSON.parse(localStorage.getItem('uplink-settings') || '{}');
|
|
48
|
+
if (s.theme) document.documentElement.setAttribute('data-theme', s.theme);
|
|
49
|
+
} catch(e) {}
|
|
50
|
+
})();
|
|
51
|
+
</script>
|
|
52
|
+
</head>
|
|
53
|
+
<body>
|
|
54
|
+
<!-- Banner killer: removes reconnection banner + paddingTop from any cached JS version -->
|
|
55
|
+
<script>
|
|
56
|
+
(function(){
|
|
57
|
+
var k=function(){
|
|
58
|
+
var b=document.getElementById('reconnection-banner');
|
|
59
|
+
if(b){b.remove();document.body.style.paddingTop='';}
|
|
60
|
+
};
|
|
61
|
+
k();
|
|
62
|
+
var o=new MutationObserver(k);
|
|
63
|
+
o.observe(document.body||document.documentElement,{childList:true,subtree:true});
|
|
64
|
+
setTimeout(function(){o.disconnect();},30000);
|
|
65
|
+
})();
|
|
66
|
+
</script>
|
|
67
|
+
<!-- Skip link for screen readers -->
|
|
68
|
+
<a href="#messages" class="skip-link">Skip to main content</a>
|
|
69
|
+
|
|
70
|
+
<!-- Onboarding Screen -->
|
|
71
|
+
<div class="overlay-screen" id="onboardingScreen">
|
|
72
|
+
<div class="overlay-card">
|
|
73
|
+
<div class="overlay-header">
|
|
74
|
+
<div class="overlay-logo">🛰️</div>
|
|
75
|
+
<h1 class="overlay-title">Uplink</h1>
|
|
76
|
+
<p class="overlay-subtitle">Connect to your AI assistant</p>
|
|
77
|
+
</div>
|
|
78
|
+
|
|
79
|
+
<div class="form-group">
|
|
80
|
+
<label class="form-label">Your Name</label>
|
|
81
|
+
<input type="text" class="form-input" id="onboardUserName" placeholder="Your name" autocomplete="off">
|
|
82
|
+
<p class="form-hint">How should your assistant address you?</p>
|
|
83
|
+
</div>
|
|
84
|
+
|
|
85
|
+
<div class="form-group">
|
|
86
|
+
<label class="form-label">Assistant Name</label>
|
|
87
|
+
<input type="text" class="form-input" id="onboardBotName" placeholder="Assistant" autocomplete="off">
|
|
88
|
+
<p class="form-hint">What should we call your assistant?</p>
|
|
89
|
+
</div>
|
|
90
|
+
|
|
91
|
+
<div class="form-group">
|
|
92
|
+
<label class="form-label">Gateway URL</label>
|
|
93
|
+
<div class="url-input-group">
|
|
94
|
+
<input type="url" class="form-input" id="onboardGatewayUrl" placeholder="http://127.0.0.1:23248" autocomplete="off">
|
|
95
|
+
<span class="url-status" id="urlStatus"></span>
|
|
96
|
+
</div>
|
|
97
|
+
<p class="form-hint">Your OpenClaw gateway address</p>
|
|
98
|
+
<div class="setting-desc" style="font-size: 0.8rem; margin-top: 0.25rem; opacity: 0.7;">OpenClaw Gateway handles AI model routing. <a href="https://docs.openclaw.ai" target="_blank" rel="noopener" style="color: var(--accent-color, #60a5fa);">Learn more</a></div>
|
|
99
|
+
<p class="form-error" id="urlError"></p>
|
|
100
|
+
</div>
|
|
101
|
+
|
|
102
|
+
<div class="form-group">
|
|
103
|
+
<label class="form-label">Gateway Token</label>
|
|
104
|
+
<input type="password" class="form-input" id="onboardGatewayToken" placeholder="Your gateway authentication token" autocomplete="off">
|
|
105
|
+
<p class="form-hint">Found in your OpenClaw config.yaml</p>
|
|
106
|
+
</div>
|
|
107
|
+
|
|
108
|
+
<div class="encrypt-toggle-row">
|
|
109
|
+
<div>
|
|
110
|
+
<div class="encrypt-label">Voice responses</div>
|
|
111
|
+
<div class="encrypt-desc">Play audio when assistant replies</div>
|
|
112
|
+
</div>
|
|
113
|
+
<div class="toggle on" id="onboardVoiceToggle" tabindex="0" role="switch" aria-checked="true" aria-label="Toggle voice responses"></div>
|
|
114
|
+
</div>
|
|
115
|
+
|
|
116
|
+
<div class="encrypt-toggle-row">
|
|
117
|
+
<div>
|
|
118
|
+
<div class="encrypt-label">Encrypt chat history</div>
|
|
119
|
+
<div class="encrypt-desc">Protect local messages with a password</div>
|
|
120
|
+
</div>
|
|
121
|
+
<div class="toggle" id="onboardEncryptToggle" tabindex="0" role="switch" aria-checked="false" aria-label="Toggle chat encryption"></div>
|
|
122
|
+
</div>
|
|
123
|
+
|
|
124
|
+
<div class="password-fields" id="onboardPasswordFields">
|
|
125
|
+
<div class="form-group">
|
|
126
|
+
<label class="form-label">Password</label>
|
|
127
|
+
<input type="password" class="form-input" id="onboardPassword" placeholder="Enter password" autocomplete="new-password">
|
|
128
|
+
</div>
|
|
129
|
+
<div class="form-group">
|
|
130
|
+
<label class="form-label">Confirm Password</label>
|
|
131
|
+
<input type="password" class="form-input" id="onboardPasswordConfirm" placeholder="Confirm password" autocomplete="new-password">
|
|
132
|
+
<p class="form-error" id="passwordError"></p>
|
|
133
|
+
</div>
|
|
134
|
+
</div>
|
|
135
|
+
|
|
136
|
+
<button class="btn-primary" id="onboardSubmit">Get Started</button>
|
|
137
|
+
</div>
|
|
138
|
+
</div>
|
|
139
|
+
|
|
140
|
+
<!-- Unlock Screen -->
|
|
141
|
+
<div class="overlay-screen" id="unlockScreen">
|
|
142
|
+
<div class="overlay-card">
|
|
143
|
+
<div class="overlay-header">
|
|
144
|
+
<div class="overlay-logo">🔐</div>
|
|
145
|
+
<h1 class="overlay-title" id="unlockBotName">Assistant</h1>
|
|
146
|
+
<p class="overlay-subtitle">Enter your encryption password to access your chat history</p>
|
|
147
|
+
</div>
|
|
148
|
+
|
|
149
|
+
<div class="form-group">
|
|
150
|
+
<input type="password" class="form-input" id="unlockPassword" placeholder="Password" autocomplete="current-password" aria-label="Password to unlock chat">
|
|
151
|
+
<p class="form-error" id="unlockError"></p>
|
|
152
|
+
</div>
|
|
153
|
+
|
|
154
|
+
<button class="btn-primary" id="unlockSubmit">Unlock</button>
|
|
155
|
+
|
|
156
|
+
<span class="forgot-link" id="forgotLink">Forgot password?</span>
|
|
157
|
+
<p class="setting-desc" style="color: var(--error);">⚠️ This will permanently delete all encrypted chat history.</p>
|
|
158
|
+
|
|
159
|
+
<div class="forgot-confirm" id="forgotConfirm">
|
|
160
|
+
<p>This will clear your local chat history.</p>
|
|
161
|
+
<p class="reassurance">✓ Your assistant still remembers everything — only the browser history is lost.</p>
|
|
162
|
+
<div class="forgot-buttons">
|
|
163
|
+
<button class="btn-cancel" id="forgotCancel">Cancel</button>
|
|
164
|
+
<button class="btn-danger" id="forgotReset">Clear & Reset</button>
|
|
165
|
+
</div>
|
|
166
|
+
</div>
|
|
167
|
+
</div>
|
|
168
|
+
</div>
|
|
169
|
+
|
|
170
|
+
<div id="stars"></div>
|
|
171
|
+
|
|
172
|
+
<!-- Layout wrapper for split view -->
|
|
173
|
+
<div class="layout-wrapper">
|
|
174
|
+
<div class="app">
|
|
175
|
+
<!-- Header -->
|
|
176
|
+
<header class="header">
|
|
177
|
+
<div class="header-left">
|
|
178
|
+
<span class="logo-icon">🛰️</span>
|
|
179
|
+
<span class="logo" id="logoRefresh" title="Click to refresh chat" role="button" tabindex="0" aria-label="Refresh chat" style="cursor: pointer;">Uplink</span>
|
|
180
|
+
<div class="status-badge" id="connectionBadge" title="Connection status">
|
|
181
|
+
<span class="status-dot"></span>
|
|
182
|
+
<span id="status" class="sr-only">Offline</span>
|
|
183
|
+
</div>
|
|
184
|
+
<div class="status-badge encryption-badge" id="encryptionBadge" style="display: none;">
|
|
185
|
+
<span>🔒</span>
|
|
186
|
+
<span>Encrypted</span>
|
|
187
|
+
</div>
|
|
188
|
+
<div class="status-badge connection-mode-badge" id="connectionModeBadge" style="display: none;">
|
|
189
|
+
<span class="mode-dot"></span>
|
|
190
|
+
<span class="mode-text">Direct</span>
|
|
191
|
+
</div>
|
|
192
|
+
<div class="context-badge" id="contextBadge" role="status" aria-label="Context window usage">
|
|
193
|
+
<div class="context-bar" id="contextBar">
|
|
194
|
+
<div class="context-bar-fill" id="contextBarFill"></div>
|
|
195
|
+
</div>
|
|
196
|
+
<span class="context-text" id="contextText"></span>
|
|
197
|
+
</div>
|
|
198
|
+
</div>
|
|
199
|
+
<div class="header-center" id="headerModeToggle">
|
|
200
|
+
<button class="header-mode-btn active" id="headerTextMode" title="Text mode" aria-label="Switch to text mode">
|
|
201
|
+
<svg width="18" height="18" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24">
|
|
202
|
+
<path d="M21 15a2 2 0 01-2 2H7l-4 4V5a2 2 0 012-2h14a2 2 0 012 2z"/>
|
|
203
|
+
</svg>
|
|
204
|
+
</button>
|
|
205
|
+
<button class="header-mode-btn" id="splitViewBtn" title="Split View" aria-label="Split View">
|
|
206
|
+
<svg width="18" height="18" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24">
|
|
207
|
+
<rect x="3" y="3" width="18" height="18" rx="2"/>
|
|
208
|
+
<line x1="12" y1="3" x2="12" y2="21"/>
|
|
209
|
+
</svg>
|
|
210
|
+
</button>
|
|
211
|
+
<button class="header-mode-btn" id="headerVoiceMode" title="Voice mode" aria-label="Switch to voice mode">
|
|
212
|
+
<svg width="18" height="18" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24">
|
|
213
|
+
<path d="M12 1a3 3 0 00-3 3v8a3 3 0 006 0V4a3 3 0 00-3-3z"/><path d="M19 10v2a7 7 0 01-14 0v-2"/><line x1="12" y1="19" x2="12" y2="23"/><line x1="8" y1="23" x2="16" y2="23"/>
|
|
214
|
+
</svg>
|
|
215
|
+
</button>
|
|
216
|
+
</div>
|
|
217
|
+
<div class="header-right">
|
|
218
|
+
<button class="header-btn" id="satellitesBtn" title="Satellites" aria-label="Satellites">
|
|
219
|
+
<svg width="18" height="18" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24">
|
|
220
|
+
<circle cx="12" cy="12" r="3"/><circle cx="12" cy="12" r="8" stroke-dasharray="2 4"/><path d="M12 2v2m0 16v2M2 12h2m16 0h2"/>
|
|
221
|
+
</svg>
|
|
222
|
+
</button>
|
|
223
|
+
<button class="header-btn" id="artifactsBtn" title="Artifacts" aria-label="Artifacts">
|
|
224
|
+
<svg width="18" height="18" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24">
|
|
225
|
+
<path d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"/>
|
|
226
|
+
</svg>
|
|
227
|
+
</button>
|
|
228
|
+
<button class="header-btn" id="activityBtn" title="Activity" aria-label="Activity">
|
|
229
|
+
<svg width="18" height="18" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24">
|
|
230
|
+
<path d="M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.77-3.77a6 6 0 0 1-7.94 7.94l-6.91 6.91a2.12 2.12 0 0 1-3-3l6.91-6.91a6 6 0 0 1 7.94-7.94l-3.76 3.76z"/>
|
|
231
|
+
</svg>
|
|
232
|
+
</button>
|
|
233
|
+
<button class="header-btn" id="settingsBtn" title="Settings" aria-label="Settings">
|
|
234
|
+
<svg width="18" height="18" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24">
|
|
235
|
+
<circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 00.33 1.82l.06.06a2 2 0 010 2.83 2 2 0 01-2.83 0l-.06-.06a1.65 1.65 0 00-1.82-.33 1.65 1.65 0 00-1 1.51V21a2 2 0 01-2 2 2 2 0 01-2-2v-.09A1.65 1.65 0 009 19.4a1.65 1.65 0 00-1.82.33l-.06.06a2 2 0 01-2.83 0 2 2 0 010-2.83l.06-.06a1.65 1.65 0 00.33-1.82 1.65 1.65 0 00-1.51-1H3a2 2 0 01-2-2 2 2 0 012-2h.09A1.65 1.65 0 004.6 9a1.65 1.65 0 00-.33-1.82l-.06-.06a2 2 0 010-2.83 2 2 0 012.83 0l.06.06a1.65 1.65 0 001.82.33H9a1.65 1.65 0 001-1.51V3a2 2 0 012-2 2 2 0 012 2v.09a1.65 1.65 0 001 1.51 1.65 1.65 0 001.82-.33l.06-.06a2 2 0 012.83 0 2 2 0 010 2.83l-.06.06a1.65 1.65 0 00-.33 1.82V9a1.65 1.65 0 001.51 1H21a2 2 0 012 2 2 2 0 01-2 2h-.09a1.65 1.65 0 00-1.51 1z"/>
|
|
236
|
+
</svg>
|
|
237
|
+
</button>
|
|
238
|
+
<button class="header-btn" id="refreshBtn" title="Hard refresh" aria-label="Hard refresh">
|
|
239
|
+
<svg width="18" height="18" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24">
|
|
240
|
+
<polyline points="23 4 23 10 17 10"/><path d="M20.49 15a9 9 0 11-2.12-9.36L23 10"/>
|
|
241
|
+
</svg>
|
|
242
|
+
</button>
|
|
243
|
+
</div>
|
|
244
|
+
</header>
|
|
245
|
+
|
|
246
|
+
<!-- Settings panel -->
|
|
247
|
+
<div class="settings-panel panel" id="settingsPanel">
|
|
248
|
+
<div class="settings-panel-header">
|
|
249
|
+
<span class="settings-panel-title">SETTINGS</span>
|
|
250
|
+
<button class="settings-panel-close" id="settingsCloseBtn" title="Close" aria-label="Close settings">×</button>
|
|
251
|
+
</div>
|
|
252
|
+
<div class="settings-panel-content">
|
|
253
|
+
|
|
254
|
+
<!-- ═══ General ═══ -->
|
|
255
|
+
<div class="settings-section" data-section="general">
|
|
256
|
+
<button class="settings-section-header" aria-expanded="true" aria-controls="section-general">
|
|
257
|
+
<span class="settings-section-icon"><svg width="18" height="18" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 00.33 1.82l.06.06a2 2 0 010 2.83 2 2 0 01-2.83 0l-.06-.06a1.65 1.65 0 00-1.82-.33 1.65 1.65 0 00-1 1.51V21a2 2 0 01-2 2 2 2 0 01-2-2v-.09A1.65 1.65 0 009 19.4a1.65 1.65 0 00-1.82.33l-.06.06a2 2 0 01-2.83 0 2 2 0 010-2.83l.06-.06a1.65 1.65 0 00.33-1.82 1.65 1.65 0 00-1.51-1H3a2 2 0 01-2-2 2 2 0 012-2h.09A1.65 1.65 0 004.6 9a1.65 1.65 0 00-.33-1.82l-.06-.06a2 2 0 010-2.83 2 2 0 012.83 0l.06.06a1.65 1.65 0 001.82.33H9a1.65 1.65 0 001-1.51V3a2 2 0 012-2 2 2 0 012 2v.09a1.65 1.65 0 001 1.51 1.65 1.65 0 001.82-.33l.06-.06a2 2 0 012.83 0 2 2 0 010 2.83l-.06.06a1.65 1.65 0 00-.33 1.82V9a1.65 1.65 0 001.51 1H21a2 2 0 012 2 2 2 0 01-2 2h-.09a1.65 1.65 0 00-1.51 1z"/></svg></span>
|
|
258
|
+
<span class="settings-section-title">General</span>
|
|
259
|
+
<span class="settings-section-chevron"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="9 18 15 12 9 6"/></svg></span>
|
|
260
|
+
</button>
|
|
261
|
+
<div class="settings-section-body" id="section-general">
|
|
262
|
+
<div class="panel-row setting-row">
|
|
263
|
+
<div>
|
|
264
|
+
<div class="setting-label">Assistant name</div>
|
|
265
|
+
<div class="setting-desc">Name of your assistant</div>
|
|
266
|
+
</div>
|
|
267
|
+
<input type="text" class="setting-input" id="agentNameInput" value="Assistant" aria-label="Assistant name">
|
|
268
|
+
</div>
|
|
269
|
+
</div>
|
|
270
|
+
</div>
|
|
271
|
+
|
|
272
|
+
<!-- ═══ Appearance ═══ -->
|
|
273
|
+
<div class="settings-section" data-section="appearance">
|
|
274
|
+
<button class="settings-section-header" aria-expanded="false" aria-controls="section-appearance">
|
|
275
|
+
<span class="settings-section-icon"><svg width="18" height="18" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><circle cx="13.5" cy="6.5" r="2.5"/><path d="M19.5 12c0 5.25-3.36 9.5-7.5 9.5S4.5 17.25 4.5 12 7.86 2.5 12 2.5"/><path d="M12 2.5c1.5 2 2.5 4.5 2.5 7"/></svg></span>
|
|
276
|
+
<span class="settings-section-title">Appearance</span>
|
|
277
|
+
<span class="settings-section-chevron"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="9 18 15 12 9 6"/></svg></span>
|
|
278
|
+
</button>
|
|
279
|
+
<div class="settings-section-body collapsed" id="section-appearance">
|
|
280
|
+
<div class="panel-row setting-row">
|
|
281
|
+
<div>
|
|
282
|
+
<div class="setting-label">Theme</div>
|
|
283
|
+
<div class="setting-desc">Color scheme</div>
|
|
284
|
+
</div>
|
|
285
|
+
<!-- Hidden select kept for compatibility with existing JS -->
|
|
286
|
+
<select class="setting-select" id="themeSelect" aria-label="Theme" style="display:none">
|
|
287
|
+
<option value="midnight">Midnight</option>
|
|
288
|
+
<option value="daylight">Daylight</option>
|
|
289
|
+
<option value="ember">Ember</option>
|
|
290
|
+
<option value="forest">Forest</option>
|
|
291
|
+
<option value="phantom">Phantom</option>
|
|
292
|
+
</select>
|
|
293
|
+
<div class="theme-picker" id="themePicker" role="listbox" aria-label="Theme"></div>
|
|
294
|
+
</div>
|
|
295
|
+
<!-- Custom Theme Generator (Premium) -->
|
|
296
|
+
<div class="panel-row" id="themeGeneratorContainer"></div>
|
|
297
|
+
<div class="panel-row setting-row">
|
|
298
|
+
<div>
|
|
299
|
+
<div class="setting-label">Text size</div>
|
|
300
|
+
<div class="setting-desc">Adjust message font size</div>
|
|
301
|
+
</div>
|
|
302
|
+
<select class="setting-select" id="textSizeSelect" aria-label="Text size">
|
|
303
|
+
<option value="small">Small</option>
|
|
304
|
+
<option value="medium" selected>Medium</option>
|
|
305
|
+
<option value="large">Large</option>
|
|
306
|
+
</select>
|
|
307
|
+
</div>
|
|
308
|
+
</div>
|
|
309
|
+
</div>
|
|
310
|
+
|
|
311
|
+
<!-- ═══ Connection ═══ -->
|
|
312
|
+
<div class="settings-section" data-section="connection">
|
|
313
|
+
<button class="settings-section-header" aria-expanded="false" aria-controls="section-connection">
|
|
314
|
+
<span class="settings-section-icon"><svg width="18" height="18" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><path d="M10 13a5 5 0 007.54.54l3-3a5 5 0 00-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 00-7.54-.54l-3 3a5 5 0 007.07 7.07l1.71-1.71"/></svg></span>
|
|
315
|
+
<span class="settings-section-title">Connection</span>
|
|
316
|
+
<span class="settings-section-chevron"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="9 18 15 12 9 6"/></svg></span>
|
|
317
|
+
</button>
|
|
318
|
+
<div class="settings-section-body collapsed" id="section-connection">
|
|
319
|
+
<div class="panel-row setting-row">
|
|
320
|
+
<div>
|
|
321
|
+
<div class="setting-label">Gateway URL</div>
|
|
322
|
+
<div class="setting-desc">Your bot's gateway address</div>
|
|
323
|
+
</div>
|
|
324
|
+
<input type="url" class="setting-input setting-input-wide" id="gatewayUrlInput" placeholder="http://127.0.0.1:23248" aria-label="Gateway URL">
|
|
325
|
+
</div>
|
|
326
|
+
</div>
|
|
327
|
+
</div>
|
|
328
|
+
|
|
329
|
+
<!-- ═══ Server ═══ -->
|
|
330
|
+
<div class="settings-section" data-section="server">
|
|
331
|
+
<button class="settings-section-header" aria-expanded="false" aria-controls="section-server">
|
|
332
|
+
<span class="settings-section-icon"><svg width="18" height="18" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><rect x="2" y="2" width="20" height="8" rx="2" ry="2"/><rect x="2" y="14" width="20" height="8" rx="2" ry="2"/><line x1="6" y1="6" x2="6.01" y2="6"/><line x1="6" y1="18" x2="6.01" y2="18"/></svg></span>
|
|
333
|
+
<span class="settings-section-title">Server</span>
|
|
334
|
+
<span class="settings-section-chevron"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="9 18 15 12 9 6"/></svg></span>
|
|
335
|
+
</button>
|
|
336
|
+
<div class="settings-section-body collapsed" id="section-server">
|
|
337
|
+
<div class="panel-row setting-row">
|
|
338
|
+
<div>
|
|
339
|
+
<div class="setting-label">Auto-Restart (Watchdog)</div>
|
|
340
|
+
<div class="setting-desc">Automatically restart the server if it crashes</div>
|
|
341
|
+
</div>
|
|
342
|
+
<label class="toggle-switch" aria-label="Toggle watchdog">
|
|
343
|
+
<input type="checkbox" id="watchdogToggle" checked>
|
|
344
|
+
<span class="toggle-slider"></span>
|
|
345
|
+
</label>
|
|
346
|
+
</div>
|
|
347
|
+
<div class="panel-row setting-row">
|
|
348
|
+
<div>
|
|
349
|
+
<div class="setting-label">Network Access</div>
|
|
350
|
+
<div class="setting-desc">Allow connections from other devices (LAN/Tailscale)</div>
|
|
351
|
+
</div>
|
|
352
|
+
<label class="toggle-switch" aria-label="Toggle network access">
|
|
353
|
+
<input type="checkbox" id="networkAccessToggle">
|
|
354
|
+
<span class="toggle-slider"></span>
|
|
355
|
+
</label>
|
|
356
|
+
</div>
|
|
357
|
+
<div class="panel-row setting-row setting-hidden" id="serverRestartRow">
|
|
358
|
+
<div>
|
|
359
|
+
<div class="setting-label setting-label-accent">Restart required</div>
|
|
360
|
+
<div class="setting-desc">Changes take effect after restart</div>
|
|
361
|
+
</div>
|
|
362
|
+
<button class="setting-btn setting-btn-primary" id="serverRestartBtn">Restart Server</button>
|
|
363
|
+
</div>
|
|
364
|
+
</div>
|
|
365
|
+
</div>
|
|
366
|
+
|
|
367
|
+
<!-- ═══ Voice & TTS ═══ -->
|
|
368
|
+
<div class="settings-section" data-section="voice">
|
|
369
|
+
<button class="settings-section-header" aria-expanded="false" aria-controls="section-voice">
|
|
370
|
+
<span class="settings-section-icon"><svg width="18" height="18" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><polygon points="11 5 6 9 2 9 2 15 6 15 11 19 11 5"/><path d="M19.07 4.93a10 10 0 010 14.14M15.54 8.46a5 5 0 010 7.07"/></svg></span>
|
|
371
|
+
<span class="settings-section-title">Voice & TTS</span>
|
|
372
|
+
<span class="premium-lock-icon setting-hidden" id="voicePremiumLock" title="Requires Premium">🔒</span>
|
|
373
|
+
<span class="settings-section-chevron"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="9 18 15 12 9 6"/></svg></span>
|
|
374
|
+
</button>
|
|
375
|
+
<div class="settings-section-body collapsed" id="section-voice">
|
|
376
|
+
<div class="panel-row setting-row">
|
|
377
|
+
<div>
|
|
378
|
+
<div class="setting-label">Voice responses</div>
|
|
379
|
+
<div class="setting-desc">Play audio when assistant replies</div>
|
|
380
|
+
</div>
|
|
381
|
+
<div class="toggle on" id="audioToggle" tabindex="0" role="switch" aria-checked="true" aria-label="Toggle voice responses"></div>
|
|
382
|
+
</div>
|
|
383
|
+
|
|
384
|
+
<!-- Voice Mode Selection (Radio Cards) -->
|
|
385
|
+
<div class="panel-row setting-row">
|
|
386
|
+
<div class="setting-full-width">
|
|
387
|
+
<div class="setting-label">Voice Mode</div>
|
|
388
|
+
<div class="setting-desc">How to interact with voice</div>
|
|
389
|
+
<div class="voice-mode-cards">
|
|
390
|
+
<div class="voice-mode-card" data-mode="push-to-talk" role="radio" tabindex="0" aria-checked="false">
|
|
391
|
+
<div class="voice-mode-icon">🎤</div>
|
|
392
|
+
<div class="voice-mode-content">
|
|
393
|
+
<div class="voice-mode-title">Push-to-talk</div>
|
|
394
|
+
<div class="voice-mode-desc">Hold to record, release to send</div>
|
|
395
|
+
</div>
|
|
396
|
+
</div>
|
|
397
|
+
<div class="voice-mode-card" data-mode="live-voice" role="radio" tabindex="0" aria-checked="false">
|
|
398
|
+
<div class="voice-mode-icon">🗣️</div>
|
|
399
|
+
<div class="voice-mode-content">
|
|
400
|
+
<div class="voice-mode-title">Live Voice</div>
|
|
401
|
+
<div class="voice-mode-desc">Open conversation with AI voice model</div>
|
|
402
|
+
</div>
|
|
403
|
+
</div>
|
|
404
|
+
<div class="voice-mode-card" data-mode="agent-voice" role="radio" tabindex="0" aria-checked="false">
|
|
405
|
+
<div class="voice-mode-icon">🧠</div>
|
|
406
|
+
<div class="voice-mode-content">
|
|
407
|
+
<div class="voice-mode-title">Agent Voice</div>
|
|
408
|
+
<div class="voice-mode-desc">Talk to Assistant — full tools & memory</div>
|
|
409
|
+
</div>
|
|
410
|
+
</div>
|
|
411
|
+
</div>
|
|
412
|
+
</div>
|
|
413
|
+
</div>
|
|
414
|
+
|
|
415
|
+
<!-- Conditional Settings: Push-to-talk -->
|
|
416
|
+
<div id="pushToTalkSettings" class="setting-hidden">
|
|
417
|
+
<!-- STT settings are already in the STT section below, no need to duplicate -->
|
|
418
|
+
</div>
|
|
419
|
+
|
|
420
|
+
<!-- Conditional Settings: Live Voice -->
|
|
421
|
+
<div id="liveVoiceSettings" class="setting-hidden">
|
|
422
|
+
<div class="panel-row setting-row">
|
|
423
|
+
<div style="flex:1">
|
|
424
|
+
<div class="setting-label">OpenAI API Key</div>
|
|
425
|
+
<div class="setting-desc" id="liveVoiceKeyStatus">Required for real-time voice</div>
|
|
426
|
+
</div>
|
|
427
|
+
<div style="display:flex;gap:6px;align-items:center">
|
|
428
|
+
<input type="password" class="setting-input setting-input-mono" id="liveVoiceKeyInput" placeholder="sk-..." style="width:180px">
|
|
429
|
+
<button class="setting-btn setting-btn-primary" id="liveVoiceKeySaveBtn">Save</button>
|
|
430
|
+
</div>
|
|
431
|
+
</div>
|
|
432
|
+
<div class="panel-row setting-row">
|
|
433
|
+
<div>
|
|
434
|
+
<div class="setting-label">Voice</div>
|
|
435
|
+
<div class="setting-desc">AI voice for conversation</div>
|
|
436
|
+
</div>
|
|
437
|
+
<select class="setting-select" id="liveVoiceSelect" aria-label="Live Voice">
|
|
438
|
+
<option value="marin">Marin (recommended)</option>
|
|
439
|
+
<option value="cedar">Cedar (recommended)</option>
|
|
440
|
+
<option value="alloy">Alloy</option>
|
|
441
|
+
<option value="ash">Ash</option>
|
|
442
|
+
<option value="ballad">Ballad</option>
|
|
443
|
+
<option value="coral">Coral</option>
|
|
444
|
+
<option value="echo">Echo</option>
|
|
445
|
+
<option value="sage">Sage</option>
|
|
446
|
+
<option value="shimmer">Shimmer</option>
|
|
447
|
+
<option value="verse">Verse</option>
|
|
448
|
+
</select>
|
|
449
|
+
</div>
|
|
450
|
+
</div>
|
|
451
|
+
|
|
452
|
+
<!-- Conditional Settings: Agent Voice -->
|
|
453
|
+
<div id="agentVoiceSettings" class="setting-hidden">
|
|
454
|
+
<div class="panel-row setting-row">
|
|
455
|
+
<div>
|
|
456
|
+
<div class="setting-label">TTS Engine</div>
|
|
457
|
+
<div class="setting-desc">Voice synthesis for agent responses</div>
|
|
458
|
+
</div>
|
|
459
|
+
<select class="setting-select" id="agentVoiceTtsEngineSelect" aria-label="Agent TTS Engine">
|
|
460
|
+
<option value="openai">OpenAI TTS</option>
|
|
461
|
+
<option value="edge">Edge TTS (Free)</option>
|
|
462
|
+
</select>
|
|
463
|
+
</div>
|
|
464
|
+
<div class="panel-row setting-row">
|
|
465
|
+
<div>
|
|
466
|
+
<div class="setting-label">TTS Voice</div>
|
|
467
|
+
<div class="setting-desc">Voice for agent responses</div>
|
|
468
|
+
</div>
|
|
469
|
+
<select class="setting-select" id="agentVoiceTtsVoiceSelect" aria-label="Agent TTS Voice">
|
|
470
|
+
<option value="alloy">Alloy</option>
|
|
471
|
+
<option value="ash">Ash</option>
|
|
472
|
+
<option value="ballad">Ballad</option>
|
|
473
|
+
<option value="coral">Coral</option>
|
|
474
|
+
<option value="echo">Echo</option>
|
|
475
|
+
<option value="fable">Fable</option>
|
|
476
|
+
<option value="nova">Nova</option>
|
|
477
|
+
<option value="onyx">Onyx</option>
|
|
478
|
+
<option value="sage">Sage</option>
|
|
479
|
+
<option value="shimmer">Shimmer</option>
|
|
480
|
+
</select>
|
|
481
|
+
</div>
|
|
482
|
+
<div class="panel-row setting-row" style="flex-direction:column;align-items:stretch;gap:8px">
|
|
483
|
+
<div style="display:flex;justify-content:space-between;align-items:baseline">
|
|
484
|
+
<div>
|
|
485
|
+
<div class="setting-label">Speech Detection</div>
|
|
486
|
+
<div class="setting-desc">How quickly it detects you stopped talking</div>
|
|
487
|
+
</div>
|
|
488
|
+
<span id="vadSensitivityValue" style="font-size:12px;opacity:0.7;font-variant-numeric:tabular-nums">0.4s</span>
|
|
489
|
+
</div>
|
|
490
|
+
<div style="display:flex;align-items:center;gap:8px">
|
|
491
|
+
<span style="font-size:11px;opacity:0.4">Fast</span>
|
|
492
|
+
<input type="range" id="vadSensitivitySlider" min="200" max="1500" step="100" value="400" style="flex:1;height:4px" aria-label="Speech detection speed">
|
|
493
|
+
<span style="font-size:11px;opacity:0.4">Patient</span>
|
|
494
|
+
</div>
|
|
495
|
+
</div>
|
|
496
|
+
</div>
|
|
497
|
+
|
|
498
|
+
<!-- Legacy Real-time Voice Settings (keep for backwards compat, hidden) -->
|
|
499
|
+
<div class="panel-row setting-row setting-hidden" id="realtimeKeyRow">
|
|
500
|
+
<div style="flex:1">
|
|
501
|
+
<div class="setting-label">OpenAI API Key</div>
|
|
502
|
+
<div class="setting-desc" id="realtimeKeyStatus">Required for real-time voice</div>
|
|
503
|
+
</div>
|
|
504
|
+
<div style="display:flex;gap:6px;align-items:center">
|
|
505
|
+
<input type="password" class="setting-input setting-input-mono" id="realtimeKeyInput" placeholder="sk-..." style="width:180px">
|
|
506
|
+
<button class="setting-btn setting-btn-primary" id="realtimeKeySaveBtn">Save</button>
|
|
507
|
+
</div>
|
|
508
|
+
</div>
|
|
509
|
+
<div class="panel-row setting-row setting-hidden" id="realtimeVoiceRow">
|
|
510
|
+
<div>
|
|
511
|
+
<div class="setting-label">Real-time Voice</div>
|
|
512
|
+
<div class="setting-desc">AI voice for real-time conversation</div>
|
|
513
|
+
</div>
|
|
514
|
+
<select class="setting-select" id="realtimeVoiceSelect" aria-label="Real-time Voice">
|
|
515
|
+
<option value="marin">Marin (recommended)</option>
|
|
516
|
+
<option value="cedar">Cedar (recommended)</option>
|
|
517
|
+
<option value="alloy">Alloy</option>
|
|
518
|
+
<option value="ash">Ash</option>
|
|
519
|
+
<option value="ballad">Ballad</option>
|
|
520
|
+
<option value="coral">Coral</option>
|
|
521
|
+
<option value="echo">Echo</option>
|
|
522
|
+
<option value="sage">Sage</option>
|
|
523
|
+
<option value="shimmer">Shimmer</option>
|
|
524
|
+
<option value="verse">Verse</option>
|
|
525
|
+
</select>
|
|
526
|
+
</div>
|
|
527
|
+
|
|
528
|
+
<div class="panel-row setting-row" id="ttsProviderRow">
|
|
529
|
+
<div>
|
|
530
|
+
<div class="setting-label">TTS Provider</div>
|
|
531
|
+
<div class="setting-desc" id="ttsProviderDesc">Voice synthesis service</div>
|
|
532
|
+
</div>
|
|
533
|
+
<select class="setting-select" id="ttsProviderSelect" aria-label="TTS Provider">
|
|
534
|
+
<option value="none">None</option>
|
|
535
|
+
<option value="elevenlabs">ElevenLabs (Cloud)</option>
|
|
536
|
+
<option value="openai">OpenAI TTS (Cloud)</option>
|
|
537
|
+
<option value="edge" id="edgeTtsOption">Edge TTS (Free)</option>
|
|
538
|
+
<option value="piper">Piper (Local)</option>
|
|
539
|
+
<option value="local">XTTS (Local, GPU)</option>
|
|
540
|
+
</select>
|
|
541
|
+
</div>
|
|
542
|
+
|
|
543
|
+
<!-- ── ElevenLabs Config ── -->
|
|
544
|
+
<div class="tts-provider-config setting-hidden" id="ttsConfig-elevenlabs">
|
|
545
|
+
<div class="panel-row setting-row">
|
|
546
|
+
<div class="setting-full-width">
|
|
547
|
+
<div class="setting-label">API Key</div>
|
|
548
|
+
<div class="setting-desc" id="elevenLabsKeyStatus">Enter your API key from elevenlabs.io</div>
|
|
549
|
+
<div class="setting-input-group">
|
|
550
|
+
<input type="password" class="setting-input setting-input-mono" id="elevenLabsKeyInput" placeholder="xi-xxxxxxxxxxxxxxxx">
|
|
551
|
+
<button class="setting-btn setting-btn-primary" id="elevenLabsSaveBtn">Save</button>
|
|
552
|
+
</div>
|
|
553
|
+
</div>
|
|
554
|
+
</div>
|
|
555
|
+
<div class="panel-row setting-row setting-hidden" id="elevenLabsVoiceRow">
|
|
556
|
+
<div>
|
|
557
|
+
<div class="setting-label">Voice</div>
|
|
558
|
+
<div class="setting-desc">Select ElevenLabs voice</div>
|
|
559
|
+
</div>
|
|
560
|
+
<select class="setting-select" id="elevenLabsVoiceSelect" aria-label="ElevenLabs Voice">
|
|
561
|
+
<option value="">Loading voices...</option>
|
|
562
|
+
</select>
|
|
563
|
+
</div>
|
|
564
|
+
</div>
|
|
565
|
+
|
|
566
|
+
<!-- ── OpenAI TTS Config ── -->
|
|
567
|
+
<div class="tts-provider-config setting-hidden" id="ttsConfig-openai">
|
|
568
|
+
<div class="panel-row setting-row">
|
|
569
|
+
<div class="setting-full-width">
|
|
570
|
+
<div class="setting-label">API Key</div>
|
|
571
|
+
<div class="setting-desc" id="openaiKeyStatus">Enter your OpenAI API key</div>
|
|
572
|
+
<div class="setting-input-group">
|
|
573
|
+
<input type="password" class="setting-input setting-input-mono" id="openaiKeyInput" placeholder="sk-xxxxxxxxxxxxxxxx">
|
|
574
|
+
<button class="setting-btn setting-btn-primary" id="openaiKeySaveBtn">Save</button>
|
|
575
|
+
</div>
|
|
576
|
+
</div>
|
|
577
|
+
</div>
|
|
578
|
+
<div class="panel-row setting-row">
|
|
579
|
+
<div>
|
|
580
|
+
<div class="setting-label">Voice</div>
|
|
581
|
+
<div class="setting-desc">OpenAI TTS voice</div>
|
|
582
|
+
</div>
|
|
583
|
+
<select class="setting-select" id="openaiTtsVoiceSelect" aria-label="OpenAI TTS Voice">
|
|
584
|
+
<option value="alloy">Alloy</option>
|
|
585
|
+
<option value="ash">Ash</option>
|
|
586
|
+
<option value="ballad">Ballad</option>
|
|
587
|
+
<option value="coral">Coral</option>
|
|
588
|
+
<option value="echo">Echo</option>
|
|
589
|
+
<option value="fable">Fable</option>
|
|
590
|
+
<option value="nova" selected>Nova</option>
|
|
591
|
+
<option value="onyx">Onyx</option>
|
|
592
|
+
<option value="sage">Sage</option>
|
|
593
|
+
<option value="shimmer">Shimmer</option>
|
|
594
|
+
</select>
|
|
595
|
+
</div>
|
|
596
|
+
<div class="panel-row setting-row">
|
|
597
|
+
<div>
|
|
598
|
+
<div class="setting-label">Model</div>
|
|
599
|
+
<div class="setting-desc">Quality vs speed</div>
|
|
600
|
+
</div>
|
|
601
|
+
<select class="setting-select" id="openaiTtsModelSelect" aria-label="OpenAI TTS Model">
|
|
602
|
+
<option value="tts-1">Standard (tts-1)</option>
|
|
603
|
+
<option value="tts-1-hd">HD (tts-1-hd)</option>
|
|
604
|
+
<option value="gpt-4o-mini-tts">GPT-4o Mini TTS</option>
|
|
605
|
+
</select>
|
|
606
|
+
</div>
|
|
607
|
+
</div>
|
|
608
|
+
|
|
609
|
+
<!-- ── Edge TTS Config ── -->
|
|
610
|
+
<div class="tts-provider-config setting-hidden" id="ttsConfig-edge">
|
|
611
|
+
<div class="panel-row setting-row" id="edgeTtsStatus">
|
|
612
|
+
<div>
|
|
613
|
+
<div class="setting-label">Status</div>
|
|
614
|
+
<div class="setting-desc" id="edgeTtsStatusDesc">Checking...</div>
|
|
615
|
+
</div>
|
|
616
|
+
<span class="status-indicator" id="edgeTtsStatusDot"></span>
|
|
617
|
+
</div>
|
|
618
|
+
<div class="panel-row setting-row" id="edgeTtsVoiceRow">
|
|
619
|
+
<div>
|
|
620
|
+
<div class="setting-label">Voice</div>
|
|
621
|
+
<div class="setting-desc">Microsoft Edge TTS voice</div>
|
|
622
|
+
</div>
|
|
623
|
+
<select class="setting-select" id="edgeTtsVoiceSelect" aria-label="Edge TTS Voice">
|
|
624
|
+
<option value="">Loading voices...</option>
|
|
625
|
+
</select>
|
|
626
|
+
</div>
|
|
627
|
+
</div>
|
|
628
|
+
|
|
629
|
+
<!-- ── Piper Config ── -->
|
|
630
|
+
<div class="tts-provider-config setting-hidden" id="ttsConfig-piper">
|
|
631
|
+
<div class="panel-row setting-row">
|
|
632
|
+
<div>
|
|
633
|
+
<div class="setting-label">Status</div>
|
|
634
|
+
<div class="setting-desc" id="piperStatusDesc">Checking...</div>
|
|
635
|
+
</div>
|
|
636
|
+
<span class="status-indicator" id="piperStatusDot"></span>
|
|
637
|
+
</div>
|
|
638
|
+
</div>
|
|
639
|
+
|
|
640
|
+
<!-- ── XTTS Config ── -->
|
|
641
|
+
<div class="tts-provider-config setting-hidden" id="ttsConfig-local">
|
|
642
|
+
<div class="panel-row setting-row">
|
|
643
|
+
<div class="setting-full-width">
|
|
644
|
+
<div class="setting-label">Server URL</div>
|
|
645
|
+
<div class="setting-desc">XTTS server address (e.g. http://localhost:8020)</div>
|
|
646
|
+
<div class="setting-input-group">
|
|
647
|
+
<input type="url" class="setting-input setting-input-mono" id="localTtsUrlInput" placeholder="http://localhost:8020">
|
|
648
|
+
<button class="setting-btn setting-btn-primary" id="localTtsSaveBtn">Save</button>
|
|
649
|
+
</div>
|
|
650
|
+
</div>
|
|
651
|
+
</div>
|
|
652
|
+
</div>
|
|
653
|
+
|
|
654
|
+
<!-- ── STT (Speech-to-Text) ── -->
|
|
655
|
+
<div class="panel-divider"></div>
|
|
656
|
+
<div class="panel-row setting-row" id="sttProviderRow">
|
|
657
|
+
<div>
|
|
658
|
+
<div class="setting-label">STT Provider</div>
|
|
659
|
+
<div class="setting-desc" id="sttProviderDesc">Speech recognition service</div>
|
|
660
|
+
</div>
|
|
661
|
+
<select class="setting-select" id="sttProviderSelect" aria-label="STT Provider">
|
|
662
|
+
<option value="none">None</option>
|
|
663
|
+
<option value="openai">OpenAI Whisper (Cloud)</option>
|
|
664
|
+
<option value="groq">Groq (Cloud, Free)</option>
|
|
665
|
+
<option value="faster-whisper">Faster-Whisper (Local)</option>
|
|
666
|
+
</select>
|
|
667
|
+
</div>
|
|
668
|
+
|
|
669
|
+
<!-- OpenAI STT Config (reuses OpenAI key from TTS if available) -->
|
|
670
|
+
<div class="stt-provider-config setting-hidden" id="sttConfig-openai">
|
|
671
|
+
<div class="panel-row setting-row">
|
|
672
|
+
<div>
|
|
673
|
+
<div class="setting-label">Model</div>
|
|
674
|
+
<div class="setting-desc">OpenAI Whisper model</div>
|
|
675
|
+
</div>
|
|
676
|
+
<select class="setting-select" id="openaiSttModelSelect" aria-label="OpenAI STT Model">
|
|
677
|
+
<option value="whisper-1" selected>Whisper v1</option>
|
|
678
|
+
</select>
|
|
679
|
+
</div>
|
|
680
|
+
<div class="panel-row setting-row" id="openaiSttKeyStatus">
|
|
681
|
+
<div>
|
|
682
|
+
<div class="setting-label">API Key</div>
|
|
683
|
+
<div class="setting-desc" id="openaiSttKeyDesc">Uses OpenAI key from TTS settings</div>
|
|
684
|
+
</div>
|
|
685
|
+
</div>
|
|
686
|
+
</div>
|
|
687
|
+
|
|
688
|
+
<!-- Groq STT Config -->
|
|
689
|
+
<div class="stt-provider-config setting-hidden" id="sttConfig-groq">
|
|
690
|
+
<div class="panel-row setting-row">
|
|
691
|
+
<div class="setting-full-width">
|
|
692
|
+
<div class="setting-label">API Key</div>
|
|
693
|
+
<div class="setting-desc" id="groqKeyStatus">Enter your API key from console.groq.com</div>
|
|
694
|
+
<div class="setting-input-group">
|
|
695
|
+
<input type="password" class="setting-input setting-input-mono" id="groqKeyInput" placeholder="gsk_xxxxxxxxxxxxxxxx">
|
|
696
|
+
<button class="setting-btn setting-btn-primary" id="groqKeySaveBtn">Save</button>
|
|
697
|
+
</div>
|
|
698
|
+
</div>
|
|
699
|
+
</div>
|
|
700
|
+
<div class="panel-row setting-row">
|
|
701
|
+
<div>
|
|
702
|
+
<div class="setting-label">Model</div>
|
|
703
|
+
<div class="setting-desc">Groq STT model</div>
|
|
704
|
+
</div>
|
|
705
|
+
<select class="setting-select" id="groqSttModelSelect" aria-label="Groq STT Model">
|
|
706
|
+
<option value="whisper-large-v3-turbo" selected>Whisper Large v3 Turbo</option>
|
|
707
|
+
<option value="whisper-large-v3">Whisper Large v3</option>
|
|
708
|
+
</select>
|
|
709
|
+
</div>
|
|
710
|
+
</div>
|
|
711
|
+
|
|
712
|
+
<!-- Faster-Whisper STT Config -->
|
|
713
|
+
<div class="stt-provider-config setting-hidden" id="sttConfig-faster-whisper">
|
|
714
|
+
<div class="panel-row setting-row">
|
|
715
|
+
<div class="setting-full-width">
|
|
716
|
+
<div class="setting-label">Server URL</div>
|
|
717
|
+
<div class="setting-desc" id="fasterWhisperStatus">Local faster-whisper-server address</div>
|
|
718
|
+
<div class="setting-input-group">
|
|
719
|
+
<input type="text" class="setting-input setting-input-mono" id="fasterWhisperUrlInput" placeholder="http://localhost:8000">
|
|
720
|
+
<button class="setting-btn setting-btn-primary" id="fasterWhisperSaveBtn">Save</button>
|
|
721
|
+
</div>
|
|
722
|
+
</div>
|
|
723
|
+
</div>
|
|
724
|
+
</div>
|
|
725
|
+
|
|
726
|
+
<!-- STT Test -->
|
|
727
|
+
<div class="panel-row setting-row setting-hidden" id="sttTestRow">
|
|
728
|
+
<div>
|
|
729
|
+
<div class="setting-label">Test STT</div>
|
|
730
|
+
<div class="setting-desc" id="sttTestStatus">Verify your configuration works</div>
|
|
731
|
+
</div>
|
|
732
|
+
<button class="setting-btn" id="sttTestBtn">Test</button>
|
|
733
|
+
</div>
|
|
734
|
+
</div>
|
|
735
|
+
</div>
|
|
736
|
+
|
|
737
|
+
<!-- ═══ Notifications ═══ -->
|
|
738
|
+
<div class="settings-section" data-section="notifications">
|
|
739
|
+
<button class="settings-section-header" aria-expanded="false" aria-controls="section-notifications">
|
|
740
|
+
<span class="settings-section-icon"><svg width="18" height="18" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><path d="M18 8A6 6 0 006 8c0 7-3 9-3 9h18s-3-2-3-9"/><path d="M13.73 21a2 2 0 01-3.46 0"/></svg></span>
|
|
741
|
+
<span class="settings-section-title">Notifications</span>
|
|
742
|
+
<span class="settings-section-chevron"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="9 18 15 12 9 6"/></svg></span>
|
|
743
|
+
</button>
|
|
744
|
+
<div class="settings-section-body collapsed" id="section-notifications">
|
|
745
|
+
<!-- Push notification toggle injected by notifications.js -->
|
|
746
|
+
<div id="notificationSettingsSlot"></div>
|
|
747
|
+
</div>
|
|
748
|
+
</div>
|
|
749
|
+
|
|
750
|
+
<!-- ═══ Privacy ═══ -->
|
|
751
|
+
<div class="settings-section" data-section="privacy">
|
|
752
|
+
<button class="settings-section-header" aria-expanded="false" aria-controls="section-privacy">
|
|
753
|
+
<span class="settings-section-icon"><svg width="18" height="18" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><rect x="3" y="11" width="18" height="11" rx="2" ry="2"/><path d="M7 11V7a5 5 0 0110 0v4"/></svg></span>
|
|
754
|
+
<span class="settings-section-title">Privacy</span>
|
|
755
|
+
<span class="settings-section-chevron"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="9 18 15 12 9 6"/></svg></span>
|
|
756
|
+
</button>
|
|
757
|
+
<div class="settings-section-body collapsed" id="section-privacy">
|
|
758
|
+
<div class="panel-row setting-row">
|
|
759
|
+
<div>
|
|
760
|
+
<div class="setting-label">Encrypt history</div>
|
|
761
|
+
<div class="setting-desc">Password-protect local chat</div>
|
|
762
|
+
</div>
|
|
763
|
+
<div class="toggle" id="encryptToggle" tabindex="0" role="switch" aria-checked="false" aria-label="Toggle chat encryption"></div>
|
|
764
|
+
</div>
|
|
765
|
+
<div class="panel-row setting-row setting-hidden" id="changePasswordRow">
|
|
766
|
+
<div>
|
|
767
|
+
<div class="setting-label">Change password</div>
|
|
768
|
+
<div class="setting-desc">Update encryption password</div>
|
|
769
|
+
</div>
|
|
770
|
+
<button class="setting-btn setting-btn-secondary" id="changePasswordBtn">Change</button>
|
|
771
|
+
</div>
|
|
772
|
+
<div class="panel-row setting-row setting-hidden" id="syncRow">
|
|
773
|
+
<div>
|
|
774
|
+
<div class="setting-label">☁️ Sync across devices</div>
|
|
775
|
+
<div class="setting-desc" id="syncStatus">Same password syncs to same account</div>
|
|
776
|
+
</div>
|
|
777
|
+
<div class="setting-input-group">
|
|
778
|
+
<button class="setting-btn setting-btn-primary" id="syncPushBtn">Push</button>
|
|
779
|
+
<button class="setting-btn setting-btn-secondary" id="syncPullBtn">Pull</button>
|
|
780
|
+
</div>
|
|
781
|
+
</div>
|
|
782
|
+
<div class="panel-row setting-row">
|
|
783
|
+
<div>
|
|
784
|
+
<div class="setting-label">Clear chat</div>
|
|
785
|
+
<div class="setting-desc">Remove local chat history</div>
|
|
786
|
+
</div>
|
|
787
|
+
<button class="setting-btn setting-btn-secondary" id="settingsClearBtn">Clear</button>
|
|
788
|
+
</div>
|
|
789
|
+
</div>
|
|
790
|
+
</div>
|
|
791
|
+
|
|
792
|
+
<!-- ═══ Premium ═══ -->
|
|
793
|
+
<div class="settings-section" data-section="premium">
|
|
794
|
+
<button class="settings-section-header" aria-expanded="false" aria-controls="section-premium">
|
|
795
|
+
<span class="settings-section-icon"><svg width="18" height="18" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"/></svg></span>
|
|
796
|
+
<span class="settings-section-title">Premium</span>
|
|
797
|
+
<span class="premium-badge setting-hidden" id="premiumActiveBadge">Active</span>
|
|
798
|
+
<span class="settings-section-chevron"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="9 18 15 12 9 6"/></svg></span>
|
|
799
|
+
</button>
|
|
800
|
+
<div class="settings-section-body collapsed" id="section-premium">
|
|
801
|
+
<!-- Free state -->
|
|
802
|
+
<div id="premiumFreeState">
|
|
803
|
+
<div class="premium-promo">
|
|
804
|
+
<div class="premium-promo-title">⬡ Uplink Premium</div>
|
|
805
|
+
<div class="premium-promo-desc">Unlock voice chat, all themes, agent management & more.</div>
|
|
806
|
+
<div class="premium-promo-price">$5 — one time, forever yours.</div>
|
|
807
|
+
<div class="premium-promo-actions">
|
|
808
|
+
<a class="setting-btn setting-btn-primary premium-buy-btn" id="premiumBuyBtn" href="https://store.moonco.pro" target="_blank" rel="noopener">Buy License</a>
|
|
809
|
+
<button class="setting-btn setting-btn-secondary" id="premiumEnterKeyBtn">Enter Key</button>
|
|
810
|
+
</div>
|
|
811
|
+
</div>
|
|
812
|
+
<div class="premium-key-entry setting-hidden" id="premiumKeyEntry">
|
|
813
|
+
<div class="setting-full-width">
|
|
814
|
+
<div class="setting-label">License Key</div>
|
|
815
|
+
<div class="setting-desc" id="premiumKeyStatus">Paste your license key below</div>
|
|
816
|
+
<div class="setting-input-group">
|
|
817
|
+
<input type="text" class="setting-input setting-input-mono" id="premiumKeyInput" placeholder="UPL-XXXXX-XXXXX-XXXXX-XXXXX" autocomplete="off" spellcheck="false">
|
|
818
|
+
<button class="setting-btn setting-btn-primary" id="premiumActivateBtn">Activate</button>
|
|
819
|
+
</div>
|
|
820
|
+
</div>
|
|
821
|
+
</div>
|
|
822
|
+
</div>
|
|
823
|
+
<!-- Active state -->
|
|
824
|
+
<div id="premiumActiveState" class="setting-hidden">
|
|
825
|
+
<div class="panel-row setting-row">
|
|
826
|
+
<div>
|
|
827
|
+
<div class="setting-label">Status</div>
|
|
828
|
+
<div class="setting-desc premium-active-text">✓ Uplink Premium is active</div>
|
|
829
|
+
</div>
|
|
830
|
+
</div>
|
|
831
|
+
<div class="panel-row setting-row">
|
|
832
|
+
<div>
|
|
833
|
+
<div class="setting-label setting-label-danger">Deactivate</div>
|
|
834
|
+
<div class="setting-desc">Remove license and revert to free</div>
|
|
835
|
+
</div>
|
|
836
|
+
<button class="setting-btn setting-btn-secondary" id="premiumDeactivateBtn">Deactivate</button>
|
|
837
|
+
</div>
|
|
838
|
+
</div>
|
|
839
|
+
</div>
|
|
840
|
+
</div>
|
|
841
|
+
|
|
842
|
+
<!-- ═══ About ═══ -->
|
|
843
|
+
<div class="settings-section" data-section="about">
|
|
844
|
+
<button class="settings-section-header" aria-expanded="false" aria-controls="section-about">
|
|
845
|
+
<span class="settings-section-icon"><svg width="18" height="18" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><circle cx="12" cy="12" r="10"/><line x1="12" y1="16" x2="12" y2="12"/><line x1="12" y1="8" x2="12.01" y2="8"/></svg></span>
|
|
846
|
+
<span class="settings-section-title">About</span>
|
|
847
|
+
<span class="settings-section-chevron"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="9 18 15 12 9 6"/></svg></span>
|
|
848
|
+
</button>
|
|
849
|
+
<div class="settings-section-body collapsed" id="section-about">
|
|
850
|
+
<div class="panel-row setting-row">
|
|
851
|
+
<div>
|
|
852
|
+
<div class="setting-label">Uplink</div>
|
|
853
|
+
<div class="setting-desc" id="aboutVersion">v1.0.0</div>
|
|
854
|
+
</div>
|
|
855
|
+
</div>
|
|
856
|
+
<div class="panel-row setting-row" id="aboutGatewayRow">
|
|
857
|
+
<div>
|
|
858
|
+
<div class="setting-label">Gateway</div>
|
|
859
|
+
<div class="setting-desc" id="aboutGatewayStatus">Checking...</div>
|
|
860
|
+
</div>
|
|
861
|
+
<span class="status-indicator" id="aboutGatewayDot"></span>
|
|
862
|
+
</div>
|
|
863
|
+
<div class="panel-row setting-row desktop-only">
|
|
864
|
+
<div>
|
|
865
|
+
<div class="setting-label">Keyboard shortcuts</div>
|
|
866
|
+
<div class="setting-desc">View available shortcuts</div>
|
|
867
|
+
</div>
|
|
868
|
+
<button class="setting-btn setting-btn-secondary" id="showShortcutsBtn">View</button>
|
|
869
|
+
</div>
|
|
870
|
+
</div>
|
|
871
|
+
</div>
|
|
872
|
+
|
|
873
|
+
<!-- ═══ Danger Zone ═══ -->
|
|
874
|
+
<div class="settings-section settings-section-danger" data-section="danger">
|
|
875
|
+
<button class="settings-section-header" aria-expanded="false" aria-controls="section-danger">
|
|
876
|
+
<span class="settings-section-icon"><svg width="18" height="18" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><path d="M10.29 3.86L1.82 18a2 2 0 001.71 3h16.94a2 2 0 001.71-3L13.71 3.86a2 2 0 00-3.42 0z"/><line x1="12" y1="9" x2="12" y2="13"/><line x1="12" y1="17" x2="12.01" y2="17"/></svg></span>
|
|
877
|
+
<span class="settings-section-title">Danger Zone</span>
|
|
878
|
+
<span class="settings-section-chevron"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="9 18 15 12 9 6"/></svg></span>
|
|
879
|
+
</button>
|
|
880
|
+
<div class="settings-section-body collapsed" id="section-danger">
|
|
881
|
+
<div class="panel-row setting-row">
|
|
882
|
+
<div>
|
|
883
|
+
<div class="setting-label setting-label-danger">Logout</div>
|
|
884
|
+
<div class="setting-desc">Clear all data and return to setup</div>
|
|
885
|
+
</div>
|
|
886
|
+
<button class="setting-btn setting-btn-danger" id="logoutBtn">Logout</button>
|
|
887
|
+
</div>
|
|
888
|
+
</div>
|
|
889
|
+
</div>
|
|
890
|
+
|
|
891
|
+
</div><!-- end settings-panel-content -->
|
|
892
|
+
</div>
|
|
893
|
+
|
|
894
|
+
<!-- Artifacts panel -->
|
|
895
|
+
<div class="artifacts-panel panel" id="artifactsPanel">
|
|
896
|
+
<div class="artifacts-panel-header">
|
|
897
|
+
<span class="artifacts-panel-title">ARTIFACTS</span>
|
|
898
|
+
<button class="artifacts-panel-refresh" id="artifactsRefreshBtn" title="Refresh" aria-label="Refresh artifacts">
|
|
899
|
+
<svg width="16" height="16" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24">
|
|
900
|
+
<polyline points="23 4 23 10 17 10"/><path d="M20.49 15a9 9 0 11-2.12-9.36L23 10"/>
|
|
901
|
+
</svg>
|
|
902
|
+
</button>
|
|
903
|
+
<button class="artifacts-panel-close" id="artifactsCloseBtn" title="Close" aria-label="Close artifacts">×</button>
|
|
904
|
+
</div>
|
|
905
|
+
<div class="artifacts-panel-content">
|
|
906
|
+
<input type="text" class="artifacts-search" id="artifactsSearch" placeholder="Search artifacts..." aria-label="Search artifacts">
|
|
907
|
+
<div class="artifacts-list" id="artifactsList">
|
|
908
|
+
<!-- List populated by JS -->
|
|
909
|
+
</div>
|
|
910
|
+
<div class="artifacts-reader" id="artifactsReader" style="display: none;">
|
|
911
|
+
<button class="artifacts-reader-back" id="readerBackBtn" aria-label="Back to list">
|
|
912
|
+
<svg width="16" height="16" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24">
|
|
913
|
+
<line x1="19" y1="12" x2="5" y2="12"/><polyline points="12 19 5 12 12 5"/>
|
|
914
|
+
</svg>
|
|
915
|
+
Back
|
|
916
|
+
</button>
|
|
917
|
+
<h3 class="artifacts-reader-title" id="readerTitle"></h3>
|
|
918
|
+
<div class="artifacts-reader-content" id="readerContent"></div>
|
|
919
|
+
</div>
|
|
920
|
+
</div>
|
|
921
|
+
</div>
|
|
922
|
+
|
|
923
|
+
<!-- ARIA live regions for screen reader announcements -->
|
|
924
|
+
<div id="sr-announcer" class="sr-only" aria-live="polite" aria-atomic="true"></div>
|
|
925
|
+
<div id="connection-status-region" class="sr-only" aria-live="polite" aria-atomic="true"></div>
|
|
926
|
+
|
|
927
|
+
<!-- Split chat container: wraps primary + secondary chat areas -->
|
|
928
|
+
<div class="split-chat-container" id="splitChatContainer">
|
|
929
|
+
|
|
930
|
+
<!-- Primary chat pane -->
|
|
931
|
+
<div class="split-pane-primary" id="splitPrimary">
|
|
932
|
+
|
|
933
|
+
<!-- Main content area -->
|
|
934
|
+
<main id="messages" class="messages" aria-label="Chat messages" aria-live="polite" aria-atomic="false">
|
|
935
|
+
<div class="empty-state" id="emptyState">
|
|
936
|
+
<svg fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24">
|
|
937
|
+
<path d="M8.625 12a.375.375 0 11-.75 0 .375.375 0 01.75 0zm0 0H8.25m4.125 0a.375.375 0 11-.75 0 .375.375 0 01.75 0zm0 0H12m4.125 0a.375.375 0 11-.75 0 .375.375 0 01.75 0zm0 0h-.375M21 12c0 4.556-4.03 8.25-9 8.25a9.764 9.764 0 01-2.555-.337A5.972 5.972 0 015.41 20.97a5.969 5.969 0 01-.474-.065 4.48 4.48 0 00.978-2.025c.09-.457-.133-.901-.467-1.226C3.93 16.178 3 14.189 3 12c0-4.556 4.03-8.25 9-8.25s9 3.694 9 8.25z"/>
|
|
938
|
+
</svg>
|
|
939
|
+
<h3>Start a conversation</h3>
|
|
940
|
+
<p>Type a message or switch to voice mode</p>
|
|
941
|
+
</div>
|
|
942
|
+
</main>
|
|
943
|
+
|
|
944
|
+
<!-- Bottom dock: input + mode tabs -->
|
|
945
|
+
<div class="bottom-dock">
|
|
946
|
+
<div class="input-area">
|
|
947
|
+
<!-- Image preview -->
|
|
948
|
+
<div class="image-preview" id="imagePreview">
|
|
949
|
+
<img id="previewImg" alt="Image attachment preview" style="display:none">
|
|
950
|
+
<button class="preview-remove" id="removePreview" title="Remove attachment" aria-label="Remove attachment">✕</button>
|
|
951
|
+
</div>
|
|
952
|
+
|
|
953
|
+
<!-- Text input - no form to avoid autofill triggers -->
|
|
954
|
+
<div class="text-input-row active" id="textInputRow">
|
|
955
|
+
<input type="file" id="fileInput" aria-label="Attach file">
|
|
956
|
+
<div class="input-actions">
|
|
957
|
+
<label for="fileInput" class="input-btn" title="Attach file" aria-label="Attach file">
|
|
958
|
+
<svg fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24" aria-hidden="true">
|
|
959
|
+
<path d="M21.44 11.05l-9.19 9.19a6 6 0 01-8.49-8.49l9.19-9.19a4 4 0 015.66 5.66l-9.2 9.19a2 2 0 01-2.83-2.83l8.49-8.48"/>
|
|
960
|
+
</svg>
|
|
961
|
+
</label>
|
|
962
|
+
</div>
|
|
963
|
+
<textarea class="text-field" id="textInput" placeholder="Message your assistant..." rows="1" autocomplete="off" enterkeyhint="enter" aria-label="Message input"></textarea>
|
|
964
|
+
<button type="button" class="send-btn" id="sendBtn" title="Send message" aria-label="Send message">
|
|
965
|
+
<svg fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24" aria-hidden="true">
|
|
966
|
+
<line x1="22" y1="2" x2="11" y2="13"/><polygon points="22 2 15 22 11 13 2 9 22 2"/>
|
|
967
|
+
</svg>
|
|
968
|
+
</button>
|
|
969
|
+
</div>
|
|
970
|
+
|
|
971
|
+
<!-- Voice input with orbiting rings -->
|
|
972
|
+
<div class="voice-input-row" id="voiceInputRow">
|
|
973
|
+
<div class="voice-orb-container">
|
|
974
|
+
<div class="voice-ring voice-ring-1"></div>
|
|
975
|
+
<div class="voice-ring voice-ring-2"></div>
|
|
976
|
+
<button class="voice-btn" id="voiceBtn" title="Hold to talk. Double-tap for hands-free mode. Spacebar to start/stop recording." aria-label="Voice input. Hold to record, double tap for hands-free mode, or press spacebar when in voice mode">
|
|
977
|
+
<canvas id="moonCanvas"></canvas>
|
|
978
|
+
</button>
|
|
979
|
+
</div>
|
|
980
|
+
<div class="voice-timer" id="voiceTimer">0:00</div>
|
|
981
|
+
<div class="voice-status" id="voiceStatus">Hold to talk</div>
|
|
982
|
+
<!-- Real-time voice UI elements -->
|
|
983
|
+
<div class="realtime-indicator" id="realtimeIndicator" style="display:none">
|
|
984
|
+
<span class="realtime-dot"></span>
|
|
985
|
+
<span class="realtime-timer" id="realtimeTimer">0:00</span>
|
|
986
|
+
</div>
|
|
987
|
+
<div class="realtime-transcript" id="realtimeTranscript" style="display:none"></div>
|
|
988
|
+
</div>
|
|
989
|
+
</div>
|
|
990
|
+
|
|
991
|
+
<!-- Mode tabs - below input -->
|
|
992
|
+
<div class="mode-tabs" role="tablist" aria-label="Input mode selection">
|
|
993
|
+
<button class="mode-tab active" id="textModeTab" role="tab" aria-selected="true" aria-controls="textInputRow" tabindex="0">
|
|
994
|
+
<svg width="16" height="16" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24" aria-hidden="true">
|
|
995
|
+
<path d="M21 15a2 2 0 01-2 2H7l-4 4V5a2 2 0 012-2h14a2 2 0 012 2z"/>
|
|
996
|
+
</svg>
|
|
997
|
+
Text
|
|
998
|
+
</button>
|
|
999
|
+
<button class="mode-tab" id="voiceModeTab" role="tab" aria-selected="false" aria-controls="voiceInputRow" tabindex="-1">
|
|
1000
|
+
<svg width="16" height="16" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24" aria-hidden="true">
|
|
1001
|
+
<path d="M12 1a3 3 0 00-3 3v8a3 3 0 006 0V4a3 3 0 00-3-3z"/><path d="M19 10v2a7 7 0 01-14 0v-2"/><line x1="12" y1="19" x2="12" y2="23"/><line x1="8" y1="23" x2="16" y2="23"/>
|
|
1002
|
+
</svg>
|
|
1003
|
+
Voice
|
|
1004
|
+
</button>
|
|
1005
|
+
</div>
|
|
1006
|
+
</div><!-- end bottom-dock -->
|
|
1007
|
+
|
|
1008
|
+
</div><!-- end split-pane-primary -->
|
|
1009
|
+
|
|
1010
|
+
<!-- Split view: drag handle -->
|
|
1011
|
+
<div class="split-drag-handle" id="splitDragHandle" style="display: none;" aria-label="Resize split view"></div>
|
|
1012
|
+
|
|
1013
|
+
<!-- Split view: secondary chat pane -->
|
|
1014
|
+
<div class="split-pane-secondary" id="splitSecondary" style="display: none;">
|
|
1015
|
+
<div class="split-secondary-header">
|
|
1016
|
+
<div class="split-secondary-info">
|
|
1017
|
+
<img class="split-secondary-icon" id="splitSecondaryIcon" src="/img/agents/default.png" alt="Agent avatar">
|
|
1018
|
+
<span class="split-secondary-name" id="splitSecondaryName">Select Agent</span>
|
|
1019
|
+
<div class="context-badge split-context-badge" id="splitContextBadge" role="status" aria-label="Context window usage" style="display: none;">
|
|
1020
|
+
<div class="context-bar" id="splitContextBar">
|
|
1021
|
+
<div class="context-bar-fill" id="splitContextBarFill"></div>
|
|
1022
|
+
</div>
|
|
1023
|
+
<span class="context-text" id="splitContextText"></span>
|
|
1024
|
+
</div>
|
|
1025
|
+
</div>
|
|
1026
|
+
<div class="split-secondary-actions">
|
|
1027
|
+
<button class="split-secondary-switch" id="splitSecondarySwitch" title="Switch session" aria-label="Switch session">
|
|
1028
|
+
<svg width="16" height="16" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24">
|
|
1029
|
+
<polyline points="16 3 21 3 21 8"/><line x1="4" y1="20" x2="21" y2="3"/><polyline points="21 16 21 21 16 21"/><line x1="15" y1="15" x2="21" y2="21"/>
|
|
1030
|
+
</svg>
|
|
1031
|
+
</button>
|
|
1032
|
+
<button class="split-secondary-close" id="splitSecondaryClose" title="Close split view" aria-label="Close split view">×</button>
|
|
1033
|
+
</div>
|
|
1034
|
+
</div>
|
|
1035
|
+
<div class="split-secondary-messages" id="splitSecondaryMessages">
|
|
1036
|
+
<div class="messages-spacer"></div>
|
|
1037
|
+
</div>
|
|
1038
|
+
<div class="split-secondary-input">
|
|
1039
|
+
<div class="split-input-row">
|
|
1040
|
+
<textarea class="split-text-field" id="splitSecondaryInput" placeholder="Message..." rows="1" aria-label="Message input for secondary chat"></textarea>
|
|
1041
|
+
<button class="split-send-btn" id="splitSecondarySend" aria-label="Send message">
|
|
1042
|
+
<svg width="18" height="18" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24">
|
|
1043
|
+
<line x1="22" y1="2" x2="11" y2="13"/><polygon points="22 2 15 22 11 13 2 9 22 2"/>
|
|
1044
|
+
</svg>
|
|
1045
|
+
</button>
|
|
1046
|
+
</div>
|
|
1047
|
+
</div>
|
|
1048
|
+
</div>
|
|
1049
|
+
|
|
1050
|
+
</div><!-- end split-chat-container -->
|
|
1051
|
+
|
|
1052
|
+
</div><!-- end .app -->
|
|
1053
|
+
|
|
1054
|
+
<!-- Side panel for split view (desktop) -->
|
|
1055
|
+
<div class="side-panel" id="sidePanel">
|
|
1056
|
+
<div class="side-panel-header">
|
|
1057
|
+
<h3 id="sidePanelTitle">Panel</h3>
|
|
1058
|
+
<button class="side-panel-close" id="sidePanelClose" aria-label="Close side panel">×</button>
|
|
1059
|
+
</div>
|
|
1060
|
+
<div class="side-panel-content" id="sidePanelContent"></div>
|
|
1061
|
+
</div>
|
|
1062
|
+
|
|
1063
|
+
</div><!-- end layout-wrapper -->
|
|
1064
|
+
|
|
1065
|
+
<!-- Audio Player Bar -->
|
|
1066
|
+
<div class="audio-player-bar" id="audioPlayerBar" style="display: none;">
|
|
1067
|
+
<button class="audio-player-play" id="audioPlayerPlay" aria-label="Play/Pause">
|
|
1068
|
+
<svg width="18" height="18" viewBox="0 0 24 24" fill="currentColor"><path id="audioPlayerIcon" d="M6 4l15 8-15 8V4z"/></svg>
|
|
1069
|
+
</button>
|
|
1070
|
+
<div class="audio-player-progress" id="audioPlayerProgress">
|
|
1071
|
+
<div class="audio-player-bar-fill" id="audioPlayerFill"></div>
|
|
1072
|
+
</div>
|
|
1073
|
+
<span class="audio-player-time" id="audioPlayerTime">0:00</span>
|
|
1074
|
+
<button class="audio-player-close" id="audioPlayerClose" aria-label="Close audio player">×</button>
|
|
1075
|
+
<audio id="audio" playsinline></audio>
|
|
1076
|
+
</div>
|
|
1077
|
+
|
|
1078
|
+
<!-- Bundled client application (built by esbuild from public/js/app.js) -->
|
|
1079
|
+
<script src="/dist/bundle.b55050c4.js" defer></script>
|
|
1080
|
+
|
|
1081
|
+
</body>
|
|
1082
|
+
</html>
|
|
1083
|
+
|