openvoiceui 1.0.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.
Files changed (185) hide show
  1. package/.env.example +104 -0
  2. package/Dockerfile +30 -0
  3. package/LICENSE +21 -0
  4. package/README.md +638 -0
  5. package/SETUP.md +360 -0
  6. package/app.py +232 -0
  7. package/auto-approve-devices.js +111 -0
  8. package/cli/index.js +372 -0
  9. package/config/__init__.py +4 -0
  10. package/config/default.yaml +43 -0
  11. package/config/flags.yaml +67 -0
  12. package/config/loader.py +203 -0
  13. package/config/providers.yaml +71 -0
  14. package/config/speech_normalization.yaml +182 -0
  15. package/config/theme.json +4 -0
  16. package/data/greetings.json +25 -0
  17. package/default-pages/ai-image-creator.html +915 -0
  18. package/default-pages/bulk-image-uploader.html +492 -0
  19. package/default-pages/desktop.html +2865 -0
  20. package/default-pages/file-explorer.html +854 -0
  21. package/default-pages/interactive-map.html +655 -0
  22. package/default-pages/style-guide.html +1005 -0
  23. package/default-pages/website-setup.html +1623 -0
  24. package/deploy/openclaw/Dockerfile +46 -0
  25. package/deploy/openvoiceui.service +30 -0
  26. package/deploy/setup-nginx.sh +50 -0
  27. package/deploy/setup-sudo.sh +306 -0
  28. package/deploy/skill-runner/Dockerfile +19 -0
  29. package/deploy/skill-runner/requirements.txt +14 -0
  30. package/deploy/skill-runner/server.py +269 -0
  31. package/deploy/supertonic/Dockerfile +22 -0
  32. package/deploy/supertonic/server.py +79 -0
  33. package/docker-compose.pinokio.yml +11 -0
  34. package/docker-compose.yml +59 -0
  35. package/greetings.json +25 -0
  36. package/index.html +65 -0
  37. package/inject-device-identity.js +142 -0
  38. package/package.json +82 -0
  39. package/profiles/default.json +114 -0
  40. package/profiles/manager.py +354 -0
  41. package/profiles/schema.json +337 -0
  42. package/prompts/voice-system-prompt.md +149 -0
  43. package/providers/__init__.py +39 -0
  44. package/providers/base.py +63 -0
  45. package/providers/llm/__init__.py +12 -0
  46. package/providers/llm/base.py +71 -0
  47. package/providers/llm/clawdbot_provider.py +112 -0
  48. package/providers/llm/zai_provider.py +115 -0
  49. package/providers/registry.py +320 -0
  50. package/providers/stt/__init__.py +12 -0
  51. package/providers/stt/base.py +58 -0
  52. package/providers/stt/webspeech_provider.py +49 -0
  53. package/providers/stt/whisper_provider.py +100 -0
  54. package/providers/tts/__init__.py +20 -0
  55. package/providers/tts/base.py +91 -0
  56. package/providers/tts/groq_provider.py +74 -0
  57. package/providers/tts/supertonic_provider.py +72 -0
  58. package/requirements.txt +38 -0
  59. package/routes/__init__.py +10 -0
  60. package/routes/admin.py +515 -0
  61. package/routes/canvas.py +1315 -0
  62. package/routes/chat.py +51 -0
  63. package/routes/conversation.py +2158 -0
  64. package/routes/elevenlabs_hybrid.py +306 -0
  65. package/routes/greetings.py +98 -0
  66. package/routes/icons.py +279 -0
  67. package/routes/image_gen.py +364 -0
  68. package/routes/instructions.py +190 -0
  69. package/routes/music.py +838 -0
  70. package/routes/onboarding.py +43 -0
  71. package/routes/pi.py +62 -0
  72. package/routes/profiles.py +215 -0
  73. package/routes/report_issue.py +68 -0
  74. package/routes/static_files.py +533 -0
  75. package/routes/suno.py +664 -0
  76. package/routes/theme.py +81 -0
  77. package/routes/transcripts.py +199 -0
  78. package/routes/vision.py +348 -0
  79. package/routes/workspace.py +288 -0
  80. package/server.py +1510 -0
  81. package/services/__init__.py +1 -0
  82. package/services/auth.py +143 -0
  83. package/services/canvas_versioning.py +239 -0
  84. package/services/db_pool.py +107 -0
  85. package/services/gateway.py +16 -0
  86. package/services/gateway_manager.py +333 -0
  87. package/services/gateways/__init__.py +12 -0
  88. package/services/gateways/base.py +110 -0
  89. package/services/gateways/compat.py +264 -0
  90. package/services/gateways/openclaw.py +1134 -0
  91. package/services/health.py +100 -0
  92. package/services/memory_client.py +455 -0
  93. package/services/paths.py +26 -0
  94. package/services/speech_normalizer.py +285 -0
  95. package/services/tts.py +270 -0
  96. package/setup-config.js +262 -0
  97. package/sounds/air_horn.mp3 +0 -0
  98. package/sounds/bruh.mp3 +0 -0
  99. package/sounds/crowd_cheer.mp3 +0 -0
  100. package/sounds/gunshot.mp3 +0 -0
  101. package/sounds/impact.mp3 +0 -0
  102. package/sounds/lets_go.mp3 +0 -0
  103. package/sounds/record_stop.mp3 +0 -0
  104. package/sounds/rewind.mp3 +0 -0
  105. package/sounds/sad_trombone.mp3 +0 -0
  106. package/sounds/scratch_long.mp3 +0 -0
  107. package/sounds/yeah.mp3 +0 -0
  108. package/src/adapters/ClawdBotAdapter.js +264 -0
  109. package/src/adapters/_template.js +133 -0
  110. package/src/adapters/elevenlabs-classic.js +841 -0
  111. package/src/adapters/elevenlabs-hybrid.js +812 -0
  112. package/src/adapters/hume-evi.js +676 -0
  113. package/src/admin.html +1339 -0
  114. package/src/app.js +8802 -0
  115. package/src/core/Config.js +173 -0
  116. package/src/core/EmotionEngine.js +307 -0
  117. package/src/core/EventBridge.js +180 -0
  118. package/src/core/EventBus.js +117 -0
  119. package/src/core/VoiceSession.js +607 -0
  120. package/src/face/BaseFace.js +259 -0
  121. package/src/face/EyeFace.js +208 -0
  122. package/src/face/HaloSmokeFace.js +509 -0
  123. package/src/face/manifest.json +27 -0
  124. package/src/face/previews/eyes.svg +16 -0
  125. package/src/face/previews/orb.svg +29 -0
  126. package/src/features/MusicPlayer.js +620 -0
  127. package/src/features/Soundboard.js +128 -0
  128. package/src/providers/DeepgramSTT.js +472 -0
  129. package/src/providers/DeepgramStreamingSTT.js +766 -0
  130. package/src/providers/GroqSTT.js +559 -0
  131. package/src/providers/TTSPlayer.js +323 -0
  132. package/src/providers/WebSpeechSTT.js +479 -0
  133. package/src/providers/tts/BaseTTSProvider.js +81 -0
  134. package/src/providers/tts/HumeProvider.js +77 -0
  135. package/src/providers/tts/SupertonicProvider.js +174 -0
  136. package/src/providers/tts/index.js +140 -0
  137. package/src/shell/adapter-registry.js +154 -0
  138. package/src/shell/caller-bridge.js +35 -0
  139. package/src/shell/camera-bridge.js +28 -0
  140. package/src/shell/canvas-bridge.js +32 -0
  141. package/src/shell/commercial-bridge.js +44 -0
  142. package/src/shell/face-bridge.js +44 -0
  143. package/src/shell/music-bridge.js +60 -0
  144. package/src/shell/orchestrator.js +233 -0
  145. package/src/shell/profile-discovery.js +303 -0
  146. package/src/shell/sounds-bridge.js +28 -0
  147. package/src/shell/transcript-bridge.js +61 -0
  148. package/src/shell/waveform-bridge.js +33 -0
  149. package/src/styles/base.css +2862 -0
  150. package/src/styles/face.css +417 -0
  151. package/src/styles/pi-overrides.css +89 -0
  152. package/src/styles/theme-dark.css +67 -0
  153. package/src/test-tts.html +175 -0
  154. package/src/ui/AppShell.js +544 -0
  155. package/src/ui/ProfileSwitcher.js +228 -0
  156. package/src/ui/SessionControl.js +240 -0
  157. package/src/ui/face/FacePicker.js +195 -0
  158. package/src/ui/face/FaceRenderer.js +309 -0
  159. package/src/ui/settings/PlaylistEditor.js +366 -0
  160. package/src/ui/settings/SettingsPanel.css +684 -0
  161. package/src/ui/settings/SettingsPanel.js +419 -0
  162. package/src/ui/settings/TTSVoicePreview.js +210 -0
  163. package/src/ui/themes/ThemeManager.js +213 -0
  164. package/src/ui/visualizers/BaseVisualizer.js +29 -0
  165. package/src/ui/visualizers/PartyFXVisualizer.css +291 -0
  166. package/src/ui/visualizers/PartyFXVisualizer.js +637 -0
  167. package/static/emulators/jsdos/js-dos.css +1 -0
  168. package/static/emulators/jsdos/js-dos.js +22 -0
  169. package/static/favicon.svg +55 -0
  170. package/static/icons/apple-touch-icon.png +0 -0
  171. package/static/icons/favicon-32.png +0 -0
  172. package/static/icons/icon-192.png +0 -0
  173. package/static/icons/icon-512.png +0 -0
  174. package/static/install.html +449 -0
  175. package/static/manifest.json +26 -0
  176. package/static/sw.js +21 -0
  177. package/tts_providers/__init__.py +136 -0
  178. package/tts_providers/base_provider.py +319 -0
  179. package/tts_providers/groq_provider.py +155 -0
  180. package/tts_providers/hume_provider.py +226 -0
  181. package/tts_providers/providers_config.json +119 -0
  182. package/tts_providers/qwen3_provider.py +371 -0
  183. package/tts_providers/resemble_provider.py +315 -0
  184. package/tts_providers/supertonic_provider.py +557 -0
  185. package/tts_providers/supertonic_tts.py +399 -0
@@ -0,0 +1,417 @@
1
+ /* =============================================================
2
+ Face System Styles
3
+ Eye faces, moods, waveform mouth, visualizer bars
4
+ ============================================================= */
5
+
6
+ /* Main face area */
7
+ .face-container {
8
+ flex: 1;
9
+ display: flex;
10
+ flex-direction: column;
11
+ justify-content: center;
12
+ align-items: center;
13
+ padding: 20px;
14
+ position: relative;
15
+ z-index: 10;
16
+ pointer-events: none;
17
+ }
18
+
19
+ /* Hide face from DOM hit-testing when canvas is active (iOS Safari fix) */
20
+ body.canvas-active .face-container {
21
+ visibility: hidden;
22
+ }
23
+
24
+ /* Keep edge controls visible above canvas overlay */
25
+ body.canvas-active .controls-left,
26
+ body.canvas-active .controls-right {
27
+ z-index: 200;
28
+ }
29
+
30
+ /* Face Box - Black rounded container with glow */
31
+ .face-box {
32
+ position: relative;
33
+ width: min(380px, 80vw);
34
+ height: min(380px, 80vw);
35
+ background: var(--dark-bg);
36
+ border-radius: 25px;
37
+ display: flex;
38
+ flex-direction: column;
39
+ justify-content: flex-start;
40
+ align-items: center;
41
+ padding-top: min(70px, 14.7vw);
42
+ padding-bottom: min(40px, 8.4vw);
43
+ margin: 0;
44
+ box-shadow:
45
+ 0 0 30px rgba(0, 136, 255, 0.3),
46
+ 0 0 60px rgba(0, 136, 255, 0.1),
47
+ inset 0 0 30px rgba(0, 136, 255, 0.05);
48
+ border: 2px solid rgba(0, 136, 255, 0.3);
49
+ z-index: 1;
50
+ --shake-amount: 5px;
51
+ pointer-events: none;
52
+ }
53
+
54
+ /* Halo Smoke Orb — hide square, only circle canvas shows */
55
+ .face-box.halo-smoke-mode {
56
+ background: transparent;
57
+ border-color: transparent;
58
+ box-shadow: none;
59
+ }
60
+ .face-box.halo-smoke-mode .mouth-container {
61
+ display: none;
62
+ }
63
+
64
+ /* Hide square-frame equalizer bars when halo orb is active —
65
+ the box outline clashes with the circular halo face. */
66
+ .face-box.halo-smoke-mode .visualizer-container,
67
+ .face-box.halo-smoke-mode .side-visualizer {
68
+ display: none !important;
69
+ }
70
+
71
+ /* Eyes container */
72
+ .eyes-container {
73
+ display: flex;
74
+ justify-content: center;
75
+ align-items: center;
76
+ gap: min(30px, 6vw);
77
+ margin-bottom: min(40px, 8.4vw);
78
+ position: relative;
79
+ }
80
+
81
+ .eye {
82
+ position: relative;
83
+ width: min(100px, 22vw);
84
+ height: min(115px, 25vw);
85
+ }
86
+
87
+ .eye-white {
88
+ position: absolute;
89
+ width: 100%;
90
+ height: 100%;
91
+ background: radial-gradient(ellipse at center, #ffffff 0%, #e8e8e8 70%, #cccccc 100%);
92
+ border-radius: 50% 50% 45% 45%;
93
+ box-shadow: 0 0 30px rgba(0, 136, 255, 0.3), inset 0 -10px 30px rgba(0, 0, 0, 0.1);
94
+ overflow: hidden;
95
+ }
96
+
97
+ .pupil-container {
98
+ position: absolute;
99
+ width: 100%;
100
+ height: 100%;
101
+ display: flex;
102
+ justify-content: center;
103
+ align-items: center;
104
+ transition: transform 0.1s ease-out;
105
+ }
106
+
107
+ .pupil {
108
+ width: min(40px, 10vw);
109
+ height: min(40px, 10vw);
110
+ background: radial-gradient(circle at 30% 30%, #333 0%, #000 70%);
111
+ border-radius: 50%;
112
+ position: relative;
113
+ box-shadow: 0 0 10px rgba(0, 0, 0, 0.5);
114
+ }
115
+
116
+ .pupil::before {
117
+ content: '';
118
+ position: absolute;
119
+ width: 15px;
120
+ height: 15px;
121
+ background: rgba(255, 255, 255, 0.8);
122
+ border-radius: 50%;
123
+ top: 8px;
124
+ left: 8px;
125
+ }
126
+
127
+ .pupil::after {
128
+ content: '';
129
+ position: absolute;
130
+ width: 8px;
131
+ height: 8px;
132
+ background: rgba(255, 255, 255, 0.4);
133
+ border-radius: 50%;
134
+ top: 20px;
135
+ right: 12px;
136
+ }
137
+
138
+ /* Eyelids */
139
+ .eyelid-top {
140
+ position: absolute;
141
+ width: 130%; height: 75%;
142
+ background: var(--dark-bg);
143
+ top: -60%; left: -15%;
144
+ border-radius: 0 0 50% 50%;
145
+ transition: transform 0.15s ease-in-out;
146
+ z-index: 10;
147
+ }
148
+
149
+ .eyelid-bottom {
150
+ position: absolute;
151
+ width: 130%; height: 40%;
152
+ background: var(--dark-bg);
153
+ bottom: -35%; left: -15%;
154
+ border-radius: 50% 50% 0 0;
155
+ transition: transform 0.15s ease-in-out;
156
+ z-index: 10;
157
+ }
158
+
159
+ .eye-cap-top {
160
+ position: absolute;
161
+ width: 120%; height: 27px;
162
+ background: var(--dark-bg);
163
+ top: -15px; left: -10%;
164
+ z-index: 11;
165
+ }
166
+
167
+ /* Blink animation */
168
+ .eye.blinking .eyelid-top { transform: translateY(90%); }
169
+ .eye.blinking .eyelid-bottom { transform: translateY(-30%); }
170
+
171
+ /* Mood: Angry */
172
+ .eye.angry .eyelid-top { transform: translateY(40%) rotate(var(--angry-rotate, 0deg)); }
173
+ .left-eye.angry .eyelid-top { --angry-rotate: 15deg; transform-origin: right center; }
174
+ .right-eye.angry .eyelid-top { --angry-rotate: -15deg; transform-origin: left center; }
175
+
176
+ /* Mood: Sad */
177
+ .eye.sad .eyelid-top { transform: translateY(30%) rotate(var(--sad-rotate, 0deg)); }
178
+ .left-eye.sad .eyelid-top { --sad-rotate: -10deg; transform-origin: left center; }
179
+ .right-eye.sad .eyelid-top { --sad-rotate: 10deg; transform-origin: right center; }
180
+
181
+ /* Mood: Happy */
182
+ .eye.happy .eyelid-bottom { transform: translateY(-60%); }
183
+
184
+ /* Mood: Thinking */
185
+ .eye.thinking .pupil-container { transform: translate(15px, -15px) !important; }
186
+ .left-eye.thinking .eyelid-top { transform: translateY(25%); }
187
+
188
+ /* Thought Bubbles */
189
+ .thought-bubbles {
190
+ position: absolute;
191
+ top: -60px;
192
+ right: -40px;
193
+ opacity: 0;
194
+ visibility: hidden;
195
+ transition: opacity 0.3s ease, visibility 0.3s ease;
196
+ z-index: 2;
197
+ pointer-events: none;
198
+ }
199
+ .thought-bubbles.active {
200
+ opacity: 1;
201
+ visibility: visible;
202
+ }
203
+ .thought-bubble {
204
+ position: absolute;
205
+ background: radial-gradient(circle at 30% 30%, rgba(255, 255, 255, 0.9) 0%, rgba(200, 220, 255, 0.7) 100%);
206
+ border-radius: 50%;
207
+ box-shadow: 0 0 10px rgba(0, 170, 255, 0.4), 0 0 20px rgba(0, 170, 255, 0.2);
208
+ animation: thought-float 2s ease-in-out infinite;
209
+ }
210
+ .thought-bubble.tb-1 {
211
+ width: 8px;
212
+ height: 8px;
213
+ right: 0;
214
+ bottom: 0;
215
+ animation-delay: 0s;
216
+ }
217
+ .thought-bubble.tb-2 {
218
+ width: 14px;
219
+ height: 14px;
220
+ right: -18px;
221
+ bottom: 16px;
222
+ animation-delay: 0.3s;
223
+ }
224
+ .thought-bubble.tb-3 {
225
+ width: 22px;
226
+ height: 22px;
227
+ right: -42px;
228
+ bottom: 38px;
229
+ animation-delay: 0.6s;
230
+ }
231
+ .thought-bubble.tb-3::after {
232
+ content: '';
233
+ position: absolute;
234
+ width: 6px;
235
+ height: 6px;
236
+ background: rgba(0, 136, 255, 0.5);
237
+ border-radius: 50%;
238
+ top: 50%;
239
+ left: 50%;
240
+ transform: translate(-50%, -50%);
241
+ animation: thought-pulse 1.5s ease-in-out infinite;
242
+ }
243
+ @keyframes thought-float {
244
+ 0%, 100% {
245
+ transform: translateY(0) scale(1);
246
+ }
247
+ 50% {
248
+ transform: translateY(-6px) scale(1.1);
249
+ }
250
+ }
251
+ @keyframes thought-pulse {
252
+ 0%, 100% {
253
+ opacity: 0.3;
254
+ transform: translate(-50%, -50%) scale(0.8);
255
+ }
256
+ 50% {
257
+ opacity: 0.8;
258
+ transform: translate(-50%, -50%) scale(1.2);
259
+ }
260
+ }
261
+
262
+ /* Mood: Surprised */
263
+ .eye.surprised .eye-white { transform: scale(1.1); }
264
+ .eye.surprised .pupil { transform: scale(0.8); }
265
+
266
+ /* Mood: Listening */
267
+ .eye.listening .pupil { animation: listening-pulse 1.5s ease-in-out infinite; }
268
+ @keyframes listening-pulse {
269
+ 0%, 100% { transform: scale(1); }
270
+ 50% { transform: scale(1.1); }
271
+ }
272
+
273
+ /* Waveform mouth */
274
+ .mouth-container {
275
+ width: min(200px, 42vw);
276
+ height: min(80px, 16.8vw);
277
+ position: relative;
278
+ z-index: 10;
279
+ }
280
+
281
+ #waveform-canvas {
282
+ width: 100%;
283
+ height: 100%;
284
+ filter: drop-shadow(0 0 10px var(--cyan));
285
+ }
286
+
287
+ /* Status indicator (now integrated into settings tab) */
288
+ .status-indicator {
289
+ display: none;
290
+ }
291
+
292
+ .status-dot {
293
+ width: 8px; height: 8px;
294
+ border-radius: 50%;
295
+ background: var(--red);
296
+ animation: pulse 2s infinite;
297
+ flex-shrink: 0;
298
+ }
299
+
300
+ .status-dot.connected { background: var(--green); }
301
+ .status-dot.connecting { background: var(--yellow); }
302
+ .status-dot.speaking { background: var(--cyan); animation: none; }
303
+ .status-dot.listening { background: var(--green); animation: pulse 1s infinite; }
304
+
305
+ @keyframes pulse {
306
+ 0%, 100% { opacity: 1; }
307
+ 50% { opacity: 0.5; }
308
+ }
309
+
310
+ /* ===== VISUALIZER BARS - Equalizer bars around face box ===== */
311
+
312
+ /* Top/Bottom visualizers - attached to face box */
313
+ .visualizer-container {
314
+ position: absolute;
315
+ left: 10px;
316
+ right: 10px;
317
+ height: 60px;
318
+ display: flex;
319
+ justify-content: space-between;
320
+ align-items: flex-end;
321
+ gap: 3px;
322
+ pointer-events: none;
323
+ z-index: -1;
324
+ opacity: 0;
325
+ transition: opacity 0.3s ease;
326
+ }
327
+ .visualizer-container.active { opacity: 1; }
328
+ .visualizer-container.top {
329
+ top: -8px;
330
+ transform: translateY(-100%);
331
+ align-items: flex-end;
332
+ }
333
+ .visualizer-container.bottom {
334
+ bottom: -8px;
335
+ transform: translateY(100%);
336
+ align-items: flex-start;
337
+ }
338
+
339
+ .visualizer-bar {
340
+ flex: 1;
341
+ max-width: 8px;
342
+ border-radius: 3px;
343
+ transition: height 0.05s ease;
344
+ box-shadow: 0 0 8px var(--cyan);
345
+ }
346
+ .visualizer-container.top .visualizer-bar {
347
+ background: linear-gradient(to top,
348
+ var(--cyan) 0%,
349
+ var(--blue) 40%,
350
+ #aa00ff 70%,
351
+ var(--red) 100%);
352
+ background-size: 100% 55px;
353
+ background-position: bottom;
354
+ background-repeat: no-repeat;
355
+ }
356
+ .visualizer-container.bottom .visualizer-bar {
357
+ background: linear-gradient(to bottom,
358
+ var(--cyan) 0%,
359
+ var(--blue) 40%,
360
+ #aa00ff 70%,
361
+ var(--red) 100%);
362
+ background-size: 100% 55px;
363
+ background-position: top;
364
+ background-repeat: no-repeat;
365
+ }
366
+
367
+ /* Side visualizers - attached to face box, full height */
368
+ .side-visualizer {
369
+ position: absolute;
370
+ top: 10px;
371
+ bottom: 10px;
372
+ display: flex;
373
+ flex-direction: column;
374
+ justify-content: space-between;
375
+ z-index: -1;
376
+ opacity: 0;
377
+ transition: opacity 0.3s ease;
378
+ pointer-events: none;
379
+ }
380
+ .side-visualizer.active { opacity: 1; }
381
+ .side-visualizer.left { right: 100%; margin-right: 8px; align-items: flex-end; }
382
+ .side-visualizer.right { left: 100%; margin-left: 8px; align-items: flex-start; }
383
+
384
+ .side-bar {
385
+ width: 80px;
386
+ height: 6px;
387
+ border-radius: 3px;
388
+ box-shadow: 0 0 8px var(--cyan);
389
+ transition: width 0.05s ease;
390
+ }
391
+ .side-visualizer.left .side-bar {
392
+ background: linear-gradient(to left,
393
+ var(--cyan) 0%,
394
+ var(--blue) 40%,
395
+ #aa00ff 70%,
396
+ var(--red) 100%);
397
+ background-size: 85px 100%;
398
+ background-position: right;
399
+ background-repeat: no-repeat;
400
+ }
401
+ .side-visualizer.right .side-bar {
402
+ background: linear-gradient(to right,
403
+ var(--cyan) 0%,
404
+ var(--blue) 40%,
405
+ #aa00ff 70%,
406
+ var(--red) 100%);
407
+ background-size: 85px 100%;
408
+ background-position: left;
409
+ background-repeat: no-repeat;
410
+ }
411
+
412
+ @media (max-width: 500px) {
413
+ .eye-cap-top {
414
+ top: -13px;
415
+ }
416
+ }
417
+
@@ -0,0 +1,89 @@
1
+ /*
2
+ * pi-overrides.css — Raspberry Pi performance optimizations
3
+ *
4
+ * Loaded only from index-pi.html, after base.css and face.css.
5
+ * Uses !important throughout to override base styles without touching them.
6
+ *
7
+ * Removes (expensive on ARM without GPU compositing):
8
+ * - backdrop-filter: blur() on all 8 elements — software compositing on
9
+ * ARM causes significant CPU overhead whenever any panel is open
10
+ * - thought-float / thought-pulse decorative animations — constant repaints
11
+ * - party effects system — heavy canvas + DOM particle rendering
12
+ *
13
+ * Preserves (functional):
14
+ * - listening-pulse (pupil animation — shows active listening state)
15
+ * - pulse (status dot animation)
16
+ * - All eyelid CSS transitions (blink, mood expressions)
17
+ * - All transform-based eye mood states (no animation property used)
18
+ */
19
+
20
+
21
+ /* ── 1. Remove backdrop-filter: blur() on all 8 elements ── */
22
+ /*
23
+ * Panels already have near-opaque backgrounds (rgba alpha 0.95–0.97),
24
+ * so removing the blur is visually imperceptible on Pi displays.
25
+ */
26
+
27
+ .edge-tab,
28
+ .ac-tab,
29
+ .face-panel,
30
+ .mode-picker,
31
+ .a2a-panel,
32
+ .listen-panel,
33
+ #action-console,
34
+ #transcript-panel {
35
+ backdrop-filter: none !important;
36
+ -webkit-backdrop-filter: none !important;
37
+ }
38
+
39
+
40
+ /* ── 2. Disable decorative thought-bubble animations ── */
41
+ /*
42
+ * These animate continuously whenever the agent is "thinking".
43
+ * Thought bubbles are purely decorative — removing animation
44
+ * keeps them visible but static.
45
+ */
46
+
47
+ .thought-bubble {
48
+ animation: none !important;
49
+ }
50
+
51
+ .thought-bubble.tb-3::after {
52
+ animation: none !important;
53
+ }
54
+
55
+
56
+ /* ── 3. Hide party effects system ── */
57
+ /*
58
+ * JS still initializes the party effects module, but with display:none
59
+ * the browser skips all compositing, canvas drawing, and DOM particle work.
60
+ * Voice, face, and waveform are completely unaffected.
61
+ */
62
+
63
+ .party-effects-container,
64
+ #party-particle-container,
65
+ #disco-container {
66
+ display: none !important;
67
+ }
68
+
69
+
70
+ /* ── 4. Emoji font fallback for Pi Chromium ── */
71
+ /*
72
+ * Pi Chromium doesn't always pick up the system emoji font automatically.
73
+ * Adding the emoji font family explicitly forces correct glyph rendering.
74
+ * Without this, emoji characters render as blank squares (tofu).
75
+ *
76
+ * 'emoji' is a CSS4 generic font family that maps to the OS emoji font.
77
+ * Noto Color Emoji is the standard Pi OS emoji font (apt: fonts-noto-color-emoji).
78
+ */
79
+
80
+ .edge-tab span,
81
+ .edge-tab,
82
+ button span,
83
+ #canvas-menu-button,
84
+ .mode-opt-icon,
85
+ .console-button span,
86
+ .transcript-button span {
87
+ font-family: 'Noto Color Emoji', 'Apple Color Emoji', 'Segoe UI Emoji',
88
+ 'Segoe UI Symbol', emoji, sans-serif !important;
89
+ }
@@ -0,0 +1,67 @@
1
+ /* =============================================================
2
+ Theme: Dark (default)
3
+ CSS custom properties and global reset for the dark theme
4
+ ============================================================= */
5
+
6
+ * { margin: 0; padding: 0; box-sizing: border-box; }
7
+
8
+ :root {
9
+ --blue: #0088ff;
10
+ --blue-dim: #0055aa;
11
+ --blue-bright: #00aaff;
12
+ --cyan: #00ffff;
13
+ --green: #00ff66;
14
+ --yellow: #ffdd00;
15
+ --orange: #ff6600;
16
+ --red: #ff2244;
17
+ --purple: #aa00ff;
18
+ --dark-bg: #050508;
19
+ --panel-bg: #0a0a12;
20
+ }
21
+
22
+ /* Global scrollbar theme — dark charcoal/blue matching app palette */
23
+ * {
24
+ scrollbar-width: thin;
25
+ scrollbar-color: #1a2a3a #0a0a12;
26
+ }
27
+ ::-webkit-scrollbar {
28
+ width: 6px;
29
+ height: 6px;
30
+ }
31
+ ::-webkit-scrollbar-track {
32
+ background: #0a0a12;
33
+ }
34
+ ::-webkit-scrollbar-thumb {
35
+ background: #1a2a3a;
36
+ border-radius: 3px;
37
+ }
38
+ ::-webkit-scrollbar-thumb:hover {
39
+ background: #254060;
40
+ }
41
+ ::-webkit-scrollbar-corner {
42
+ background: #0a0a12;
43
+ }
44
+
45
+ body {
46
+ background: var(--dark-bg);
47
+ font-family: 'Courier New', monospace;
48
+ color: var(--blue);
49
+ height: 100vh;
50
+ overflow: hidden;
51
+ display: flex;
52
+ flex-direction: column;
53
+ }
54
+
55
+ /* Animated background grid */
56
+ body::before {
57
+ content: '';
58
+ position: fixed;
59
+ top: 0; left: 0;
60
+ width: 100%; height: 100%;
61
+ background-image:
62
+ linear-gradient(rgba(0,136,255,0.03) 1px, transparent 1px),
63
+ linear-gradient(90deg, rgba(0,136,255,0.03) 1px, transparent 1px);
64
+ background-size: 50px 50px;
65
+ pointer-events: none;
66
+ z-index: -1;
67
+ }