@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.

Files changed (158) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +185 -0
  3. package/bin/uplink.js +279 -0
  4. package/middleware/error-handler.js +69 -0
  5. package/package.json +93 -0
  6. package/public/css/agents.36b98c0f.css +1469 -0
  7. package/public/css/agents.css +1469 -0
  8. package/public/css/app.a6a7f8f5.css +2731 -0
  9. package/public/css/app.css +2731 -0
  10. package/public/css/artifacts.css +444 -0
  11. package/public/css/commands.css +55 -0
  12. package/public/css/connection.css +131 -0
  13. package/public/css/dashboard.css +233 -0
  14. package/public/css/developer.css +328 -0
  15. package/public/css/files.css +123 -0
  16. package/public/css/markdown.css +156 -0
  17. package/public/css/message-actions.css +278 -0
  18. package/public/css/mobile.css +614 -0
  19. package/public/css/panels-unified.css +483 -0
  20. package/public/css/premium.css +415 -0
  21. package/public/css/realtime.css +189 -0
  22. package/public/css/satellites.css +401 -0
  23. package/public/css/shortcuts.css +185 -0
  24. package/public/css/split-view.4def0262.css +673 -0
  25. package/public/css/split-view.css +673 -0
  26. package/public/css/theme-generator.css +391 -0
  27. package/public/css/themes.css +387 -0
  28. package/public/css/timestamps.css +54 -0
  29. package/public/css/variables.css +78 -0
  30. package/public/dist/bundle.b55050c4.js +15757 -0
  31. package/public/favicon.svg +24 -0
  32. package/public/img/agents/ada.png +0 -0
  33. package/public/img/agents/clarice.png +0 -0
  34. package/public/img/agents/dennis-nedry.png +0 -0
  35. package/public/img/agents/elliot-alderson.png +0 -0
  36. package/public/img/agents/main.png +0 -0
  37. package/public/img/agents/scotty.png +0 -0
  38. package/public/img/agents/top-flight-security.png +0 -0
  39. package/public/index.html +1083 -0
  40. package/public/js/agents-data.js +234 -0
  41. package/public/js/agents-ui.js +72 -0
  42. package/public/js/agents.js +1525 -0
  43. package/public/js/app.js +79 -0
  44. package/public/js/appearance-settings.js +111 -0
  45. package/public/js/artifacts.js +432 -0
  46. package/public/js/audio-queue.js +168 -0
  47. package/public/js/bootstrap.js +54 -0
  48. package/public/js/chat.js +1211 -0
  49. package/public/js/commands.js +581 -0
  50. package/public/js/connection-api.js +121 -0
  51. package/public/js/connection.js +1231 -0
  52. package/public/js/context-tracker.js +271 -0
  53. package/public/js/core.js +172 -0
  54. package/public/js/dashboard.js +452 -0
  55. package/public/js/developer.js +432 -0
  56. package/public/js/encryption.js +124 -0
  57. package/public/js/errors.js +122 -0
  58. package/public/js/event-bus.js +77 -0
  59. package/public/js/fetch-utils.js +171 -0
  60. package/public/js/file-handler.js +229 -0
  61. package/public/js/files.js +352 -0
  62. package/public/js/gateway-chat.js +538 -0
  63. package/public/js/logger.js +112 -0
  64. package/public/js/markdown.js +190 -0
  65. package/public/js/message-actions.js +431 -0
  66. package/public/js/message-renderer.js +288 -0
  67. package/public/js/missed-messages.js +235 -0
  68. package/public/js/mobile-debug.js +95 -0
  69. package/public/js/notifications.js +367 -0
  70. package/public/js/offline-queue.js +178 -0
  71. package/public/js/onboarding.js +543 -0
  72. package/public/js/panels.js +156 -0
  73. package/public/js/premium.js +412 -0
  74. package/public/js/realtime-voice.js +844 -0
  75. package/public/js/satellite-sync.js +256 -0
  76. package/public/js/satellite-ui.js +175 -0
  77. package/public/js/satellites.js +1516 -0
  78. package/public/js/settings.js +1087 -0
  79. package/public/js/shortcuts.js +381 -0
  80. package/public/js/split-chat.js +1234 -0
  81. package/public/js/split-resize.js +211 -0
  82. package/public/js/splitview.js +340 -0
  83. package/public/js/storage.js +408 -0
  84. package/public/js/streaming-handler.js +324 -0
  85. package/public/js/stt-settings.js +316 -0
  86. package/public/js/theme-generator.js +661 -0
  87. package/public/js/themes.js +164 -0
  88. package/public/js/timestamps.js +198 -0
  89. package/public/js/tts-settings.js +575 -0
  90. package/public/js/ui.js +267 -0
  91. package/public/js/update-notifier.js +143 -0
  92. package/public/js/utils/constants.js +165 -0
  93. package/public/js/utils/sanitize.js +93 -0
  94. package/public/js/utils/sse-parser.js +195 -0
  95. package/public/js/voice.js +883 -0
  96. package/public/manifest.json +58 -0
  97. package/public/moon_texture.jpg +0 -0
  98. package/public/sw.js +221 -0
  99. package/public/three.min.js +6 -0
  100. package/server/channel.js +529 -0
  101. package/server/chat.js +270 -0
  102. package/server/config-store.js +362 -0
  103. package/server/config.js +159 -0
  104. package/server/context.js +131 -0
  105. package/server/gateway-commands.js +211 -0
  106. package/server/gateway-proxy.js +318 -0
  107. package/server/index.js +22 -0
  108. package/server/logger.js +89 -0
  109. package/server/middleware/auth.js +188 -0
  110. package/server/middleware.js +218 -0
  111. package/server/openclaw-discover.js +308 -0
  112. package/server/premium/index.js +156 -0
  113. package/server/premium/license.js +140 -0
  114. package/server/realtime/bridge.js +837 -0
  115. package/server/realtime/index.js +349 -0
  116. package/server/realtime/tts-stream.js +446 -0
  117. package/server/routes/agents.js +564 -0
  118. package/server/routes/artifacts.js +174 -0
  119. package/server/routes/chat.js +311 -0
  120. package/server/routes/config-settings.js +345 -0
  121. package/server/routes/config.js +603 -0
  122. package/server/routes/files.js +307 -0
  123. package/server/routes/index.js +18 -0
  124. package/server/routes/media.js +451 -0
  125. package/server/routes/missed-messages.js +107 -0
  126. package/server/routes/premium.js +75 -0
  127. package/server/routes/push.js +156 -0
  128. package/server/routes/satellite.js +406 -0
  129. package/server/routes/status.js +251 -0
  130. package/server/routes/stt.js +35 -0
  131. package/server/routes/voice.js +260 -0
  132. package/server/routes/webhooks.js +203 -0
  133. package/server/routes.js +206 -0
  134. package/server/runtime-config.js +336 -0
  135. package/server/share.js +305 -0
  136. package/server/stt/faster-whisper.js +72 -0
  137. package/server/stt/groq.js +51 -0
  138. package/server/stt/index.js +196 -0
  139. package/server/stt/openai.js +49 -0
  140. package/server/sync.js +244 -0
  141. package/server/tailscale-https.js +175 -0
  142. package/server/tts.js +646 -0
  143. package/server/update-checker.js +172 -0
  144. package/server/utils/filename.js +129 -0
  145. package/server/utils.js +147 -0
  146. package/server/watchdog.js +318 -0
  147. package/server/websocket/broadcast.js +359 -0
  148. package/server/websocket/connections.js +339 -0
  149. package/server/websocket/index.js +215 -0
  150. package/server/websocket/routing.js +277 -0
  151. package/server/websocket/sync.js +102 -0
  152. package/server.js +404 -0
  153. package/utils/detect-tool-usage.js +93 -0
  154. package/utils/errors.js +158 -0
  155. package/utils/html-escape.js +84 -0
  156. package/utils/id-sanitize.js +94 -0
  157. package/utils/response.js +130 -0
  158. package/utils/with-retry.js +105 -0
@@ -0,0 +1,415 @@
1
+ /* ============================================
2
+ PREMIUM — License gating styles
3
+ ============================================ */
4
+
5
+ /* ── Premium badge in section header ── */
6
+ .premium-badge {
7
+ font-size: 0.65rem;
8
+ font-weight: 600;
9
+ text-transform: uppercase;
10
+ letter-spacing: 0.05em;
11
+ padding: 2px 8px;
12
+ border-radius: 10px;
13
+ background: var(--accent);
14
+ color: var(--bg);
15
+ margin-left: 8px;
16
+ }
17
+
18
+ /* ── Lock icon in section headers ── */
19
+ .premium-lock-icon {
20
+ font-size: 0.75rem;
21
+ margin-left: 6px;
22
+ opacity: 0.6;
23
+ }
24
+
25
+ /* ── Promo card ── */
26
+ .premium-promo {
27
+ text-align: center;
28
+ padding: 20px 16px;
29
+ }
30
+
31
+ .premium-promo-title {
32
+ font-size: 1.1rem;
33
+ font-weight: 700;
34
+ color: var(--text);
35
+ margin-bottom: 8px;
36
+ }
37
+
38
+ .premium-promo-desc {
39
+ font-size: 0.85rem;
40
+ color: var(--text-secondary);
41
+ margin-bottom: 6px;
42
+ line-height: 1.4;
43
+ }
44
+
45
+ .premium-promo-price {
46
+ font-size: 0.9rem;
47
+ font-weight: 600;
48
+ color: var(--accent);
49
+ margin-bottom: 16px;
50
+ }
51
+
52
+ .premium-promo-actions {
53
+ display: flex;
54
+ gap: 8px;
55
+ justify-content: center;
56
+ }
57
+
58
+ .premium-buy-btn {
59
+ text-decoration: none;
60
+ display: inline-flex;
61
+ align-items: center;
62
+ justify-content: center;
63
+ }
64
+
65
+ /* ── Key entry ── */
66
+ .premium-key-entry {
67
+ padding: 12px 16px;
68
+ border-top: 1px solid var(--border);
69
+ }
70
+
71
+ .premium-key-entry .setting-input-mono {
72
+ text-transform: uppercase;
73
+ letter-spacing: 0.1em;
74
+ font-size: 0.85rem;
75
+ }
76
+
77
+ #premiumKeyStatus {
78
+ transition: color 0.2s;
79
+ }
80
+
81
+ #premiumKeyStatus.premium-success {
82
+ color: var(--accent);
83
+ }
84
+
85
+ #premiumKeyStatus.premium-error {
86
+ color: #ef4444;
87
+ }
88
+
89
+ /* ── Active state ── */
90
+ .premium-active-text {
91
+ color: var(--accent) !important;
92
+ }
93
+
94
+ /* ── Locked section overlay ── */
95
+ .premium-locked {
96
+ position: relative;
97
+ pointer-events: none;
98
+ user-select: none;
99
+ }
100
+
101
+ .premium-locked > *:not(.premium-overlay) {
102
+ opacity: 0.3;
103
+ filter: blur(1px);
104
+ }
105
+
106
+ .premium-overlay {
107
+ position: absolute;
108
+ inset: 0;
109
+ display: flex;
110
+ align-items: center;
111
+ justify-content: center;
112
+ pointer-events: auto;
113
+ z-index: 2;
114
+ }
115
+
116
+ .premium-overlay-content {
117
+ display: flex;
118
+ flex-direction: column;
119
+ align-items: center;
120
+ gap: 8px;
121
+ padding: 20px;
122
+ text-align: center;
123
+ }
124
+
125
+ .premium-overlay-icon {
126
+ font-size: 1.5rem;
127
+ }
128
+
129
+ .premium-overlay-text {
130
+ font-size: 0.85rem;
131
+ color: var(--text-secondary);
132
+ font-weight: 500;
133
+ }
134
+
135
+ .premium-overlay-btn {
136
+ margin-top: 4px;
137
+ font-size: 0.8rem;
138
+ padding: 6px 16px;
139
+ }
140
+
141
+ /* ── Toast notification (fallback) ── */
142
+ .premium-toast {
143
+ position: fixed;
144
+ bottom: 80px;
145
+ left: 50%;
146
+ transform: translateX(-50%) translateY(20px);
147
+ background: var(--bg-secondary, #1a1a2e);
148
+ border: 1px solid var(--border);
149
+ border-radius: 12px;
150
+ padding: 12px 16px;
151
+ display: flex;
152
+ align-items: center;
153
+ gap: 12px;
154
+ font-size: 0.85rem;
155
+ color: var(--text);
156
+ box-shadow: 0 8px 24px rgba(0, 0, 0, 0.3);
157
+ opacity: 0;
158
+ transition: opacity 0.3s, transform 0.3s;
159
+ z-index: 10000;
160
+ pointer-events: auto;
161
+ }
162
+
163
+ .premium-toast.visible {
164
+ opacity: 1;
165
+ transform: translateX(-50%) translateY(0);
166
+ }
167
+
168
+ .premium-toast-btn {
169
+ background: var(--accent);
170
+ color: var(--bg);
171
+ border: none;
172
+ border-radius: 6px;
173
+ padding: 4px 12px;
174
+ font-size: 0.8rem;
175
+ font-weight: 600;
176
+ cursor: pointer;
177
+ white-space: nowrap;
178
+ }
179
+
180
+ .premium-toast-btn:hover {
181
+ opacity: 0.9;
182
+ }
183
+
184
+ /* ── Upgrade modal ── */
185
+ .premium-modal {
186
+ position: fixed;
187
+ inset: 0;
188
+ background: rgba(0, 0, 0, 0.75);
189
+ z-index: var(--z-overlay, 1000);
190
+ display: none;
191
+ align-items: center;
192
+ justify-content: center;
193
+ padding: 20px;
194
+ opacity: 0;
195
+ transition: opacity 0.25s ease;
196
+ }
197
+
198
+ .premium-modal.visible {
199
+ display: flex;
200
+ opacity: 1;
201
+ }
202
+
203
+ /* Entry animation */
204
+ .premium-modal.entering {
205
+ display: flex;
206
+ opacity: 0;
207
+ }
208
+
209
+ .premium-modal-card {
210
+ background: linear-gradient(180deg, var(--bg-secondary, #1a1a2e) 0%, var(--bg, #0a0a1a) 100%);
211
+ border: 1px solid var(--border, #2a2a3e);
212
+ border-radius: 16px;
213
+ max-width: 380px;
214
+ width: 100%;
215
+ box-shadow: 0 20px 60px rgba(0, 0, 0, 0.5), 0 0 40px rgba(var(--accent-rgb, 0, 240, 255), 0.08);
216
+ overflow: hidden;
217
+ transform: scale(0.95) translateY(10px);
218
+ transition: transform 0.25s ease;
219
+ }
220
+
221
+ .premium-modal.visible .premium-modal-card {
222
+ transform: scale(1) translateY(0);
223
+ }
224
+
225
+ .premium-modal-close {
226
+ position: absolute;
227
+ top: 8px;
228
+ right: 8px;
229
+ z-index: 10;
230
+ background: rgba(255, 255, 255, 0.06);
231
+ border: none;
232
+ color: var(--text-muted, #999);
233
+ font-size: 1.4rem;
234
+ cursor: pointer;
235
+ width: 32px;
236
+ height: 32px;
237
+ display: flex;
238
+ align-items: center;
239
+ justify-content: center;
240
+ border-radius: 50%;
241
+ line-height: 1;
242
+ transition: color 0.15s, background 0.15s;
243
+ }
244
+
245
+ .premium-modal-close:hover {
246
+ color: var(--text, #eee);
247
+ background: rgba(255, 255, 255, 0.12);
248
+ }
249
+
250
+ .premium-modal-header {
251
+ position: relative;
252
+ text-align: center;
253
+ padding: 28px 24px 16px;
254
+ }
255
+
256
+ .premium-modal-icon {
257
+ font-size: 2rem;
258
+ margin-bottom: 12px;
259
+ display: block;
260
+ }
261
+
262
+ .premium-modal-title {
263
+ font-family: 'Space Mono', monospace;
264
+ font-size: 1.1rem;
265
+ font-weight: 700;
266
+ color: var(--text, #eee);
267
+ margin: 0 0 6px;
268
+ letter-spacing: 0.5px;
269
+ }
270
+
271
+ .premium-modal-desc {
272
+ font-size: 0.85rem;
273
+ color: var(--text-secondary, #999);
274
+ line-height: 1.5;
275
+ margin: 0;
276
+ }
277
+
278
+ .premium-modal-body {
279
+ padding: 0 24px 8px;
280
+ }
281
+
282
+ .premium-modal-features {
283
+ list-style: none;
284
+ margin: 0;
285
+ padding: 0;
286
+ }
287
+
288
+ .premium-modal-features li {
289
+ display: flex;
290
+ align-items: center;
291
+ gap: 10px;
292
+ padding: 8px 0;
293
+ font-size: 0.82rem;
294
+ color: var(--text-secondary, #999);
295
+ border-bottom: 1px solid rgba(255, 255, 255, 0.04);
296
+ }
297
+
298
+ .premium-modal-features li:last-child {
299
+ border-bottom: none;
300
+ }
301
+
302
+ .premium-modal-features li .feature-check {
303
+ color: var(--accent, #00f0ff);
304
+ font-size: 0.9rem;
305
+ flex-shrink: 0;
306
+ width: 18px;
307
+ text-align: center;
308
+ }
309
+
310
+ .premium-modal-features li .feature-highlight {
311
+ color: var(--text, #eee);
312
+ font-weight: 600;
313
+ }
314
+
315
+ .premium-modal-footer {
316
+ padding: 16px 24px 24px;
317
+ display: flex;
318
+ flex-direction: column;
319
+ gap: 10px;
320
+ }
321
+
322
+ .premium-modal-price {
323
+ text-align: center;
324
+ font-size: 0.8rem;
325
+ color: var(--text-muted, #666);
326
+ margin-bottom: 4px;
327
+ }
328
+
329
+ .premium-modal-price strong {
330
+ color: var(--accent, #00f0ff);
331
+ font-size: 1rem;
332
+ }
333
+
334
+ .premium-modal-buy {
335
+ display: flex;
336
+ align-items: center;
337
+ justify-content: center;
338
+ gap: 8px;
339
+ width: 100%;
340
+ padding: 12px 20px;
341
+ background: var(--accent, #00f0ff);
342
+ color: var(--bg, #0a0a1a);
343
+ border: none;
344
+ border-radius: 10px;
345
+ font-size: 0.9rem;
346
+ font-weight: 700;
347
+ cursor: pointer;
348
+ transition: opacity 0.15s, transform 0.1s;
349
+ text-decoration: none;
350
+ }
351
+
352
+ .premium-modal-buy:hover {
353
+ opacity: 0.9;
354
+ }
355
+
356
+ .premium-modal-buy:active {
357
+ transform: scale(0.98);
358
+ }
359
+
360
+ .premium-modal-key {
361
+ display: flex;
362
+ align-items: center;
363
+ justify-content: center;
364
+ width: 100%;
365
+ padding: 10px 20px;
366
+ background: transparent;
367
+ color: var(--text-secondary, #999);
368
+ border: 1px solid var(--border, #2a2a3e);
369
+ border-radius: 10px;
370
+ font-size: 0.82rem;
371
+ font-weight: 500;
372
+ cursor: pointer;
373
+ transition: color 0.15s, border-color 0.15s;
374
+ }
375
+
376
+ .premium-modal-key:hover {
377
+ color: var(--text, #eee);
378
+ border-color: var(--text-muted, #666);
379
+ }
380
+
381
+ /* Mobile adjustments */
382
+ @media (max-width: 480px) {
383
+ .premium-modal {
384
+ padding: 16px;
385
+ align-items: flex-end;
386
+ }
387
+
388
+ .premium-modal-card {
389
+ max-width: 100%;
390
+ border-radius: 16px 16px 0 0;
391
+ transform: translateY(100%);
392
+ }
393
+
394
+ .premium-modal.visible .premium-modal-card {
395
+ transform: translateY(0);
396
+ }
397
+ }
398
+
399
+ /* ── Coming Soon state ── */
400
+ .premium-modal-coming-soon {
401
+ text-align: center;
402
+ font-size: 0.82rem;
403
+ color: var(--text-secondary, #999);
404
+ padding: 8px 0;
405
+ }
406
+
407
+ .premium-overlay-text {
408
+ font-size: 0.85rem;
409
+ }
410
+
411
+ /* ── Theme select locked options ── */
412
+ select.setting-select option:disabled {
413
+ color: var(--text-secondary);
414
+ opacity: 0.5;
415
+ }
@@ -0,0 +1,189 @@
1
+ /* ============================================
2
+ REALTIME VOICE UI
3
+ Styles for active call indicator, timer, and transcript
4
+ ============================================ */
5
+
6
+ /* Active Call Indicator */
7
+ #realtimeIndicator {
8
+ display: none;
9
+ align-items: center;
10
+ justify-content: center;
11
+ position: fixed;
12
+ top: 20px;
13
+ right: 20px;
14
+ padding: 12px 20px;
15
+ background: rgba(0, 0, 0, 0.8);
16
+ backdrop-filter: blur(10px);
17
+ border-radius: 24px;
18
+ box-shadow: 0 4px 16px rgba(0, 0, 0, 0.3);
19
+ z-index: 1000;
20
+ gap: 12px;
21
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
22
+ }
23
+
24
+ #realtimeIndicator.active {
25
+ animation: pulseGlow 2s ease-in-out infinite;
26
+ }
27
+
28
+ @keyframes pulseGlow {
29
+ 0%, 100% {
30
+ box-shadow: 0 4px 16px rgba(0, 0, 0, 0.3),
31
+ 0 0 20px rgba(52, 211, 153, 0.4);
32
+ }
33
+ 50% {
34
+ box-shadow: 0 4px 16px rgba(0, 0, 0, 0.3),
35
+ 0 0 30px rgba(52, 211, 153, 0.7);
36
+ }
37
+ }
38
+
39
+ /* Pulsing dot */
40
+ #realtimeIndicator::before {
41
+ content: '';
42
+ width: 10px;
43
+ height: 10px;
44
+ background: #34D399;
45
+ border-radius: 50%;
46
+ animation: pulse 1.5s ease-in-out infinite;
47
+ }
48
+
49
+ @keyframes pulse {
50
+ 0%, 100% {
51
+ transform: scale(1);
52
+ opacity: 1;
53
+ }
54
+ 50% {
55
+ transform: scale(1.2);
56
+ opacity: 0.7;
57
+ }
58
+ }
59
+
60
+ /* Call Timer */
61
+ #realtimeTimer {
62
+ display: none;
63
+ color: #fff;
64
+ font-size: 14px;
65
+ font-weight: 500;
66
+ letter-spacing: 0.5px;
67
+ margin: 0;
68
+ min-width: 40px;
69
+ text-align: center;
70
+ }
71
+
72
+ /* Transcript Overlay */
73
+ #realtimeTranscript {
74
+ display: none;
75
+ position: fixed;
76
+ bottom: 120px;
77
+ left: 50%;
78
+ transform: translateX(-50%);
79
+ max-width: 600px;
80
+ width: 90%;
81
+ padding: 16px 24px;
82
+ background: rgba(0, 0, 0, 0.85);
83
+ backdrop-filter: blur(12px);
84
+ border-radius: 16px;
85
+ box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4);
86
+ color: #fff;
87
+ font-size: 16px;
88
+ line-height: 1.5;
89
+ text-align: center;
90
+ z-index: 999;
91
+ animation: slideUp 0.3s ease-out;
92
+ min-height: 56px;
93
+ }
94
+
95
+ #realtimeTranscript:empty::before {
96
+ content: 'Listening...';
97
+ color: rgba(255, 255, 255, 0.5);
98
+ font-style: italic;
99
+ }
100
+
101
+ @keyframes slideUp {
102
+ from {
103
+ opacity: 0;
104
+ transform: translateX(-50%) translateY(20px);
105
+ }
106
+ to {
107
+ opacity: 1;
108
+ transform: translateX(-50%) translateY(0);
109
+ }
110
+ }
111
+
112
+ /* Mobile Responsive */
113
+ @media (max-width: 768px) {
114
+ #realtimeIndicator {
115
+ top: 10px;
116
+ right: 10px;
117
+ padding: 8px 16px;
118
+ gap: 8px;
119
+ }
120
+
121
+ #realtimeIndicator::before {
122
+ width: 8px;
123
+ height: 8px;
124
+ }
125
+
126
+ #realtimeTimer {
127
+ font-size: 12px;
128
+ min-width: 35px;
129
+ }
130
+
131
+ #realtimeTranscript {
132
+ bottom: 100px;
133
+ width: 85%;
134
+ padding: 12px 20px;
135
+ font-size: 14px;
136
+ min-height: 48px;
137
+ }
138
+ }
139
+
140
+ /* Dark mode adjustments (if app uses dark mode toggle) */
141
+ @media (prefers-color-scheme: dark) {
142
+ #realtimeIndicator {
143
+ background: rgba(30, 30, 30, 0.9);
144
+ }
145
+
146
+ #realtimeTranscript {
147
+ background: rgba(20, 20, 20, 0.9);
148
+ }
149
+ }
150
+
151
+ /* High contrast mode */
152
+ @media (prefers-contrast: high) {
153
+ #realtimeIndicator {
154
+ border: 2px solid #34D399;
155
+ }
156
+
157
+ #realtimeTranscript {
158
+ border: 2px solid rgba(255, 255, 255, 0.3);
159
+ }
160
+ }
161
+
162
+ /* Reduced motion */
163
+ @media (prefers-reduced-motion: reduce) {
164
+ #realtimeIndicator.active,
165
+ #realtimeIndicator::before {
166
+ animation: none;
167
+ }
168
+
169
+ #realtimeTranscript {
170
+ animation: none;
171
+ }
172
+ }
173
+
174
+ /* Voice button integration - add realtime mode indicator */
175
+ .voice-btn.realtime-active {
176
+ background: linear-gradient(135deg, #34D399 0%, #10B981 100%);
177
+ box-shadow: 0 4px 12px rgba(52, 211, 153, 0.4);
178
+ }
179
+
180
+ .voice-btn.realtime-active:hover {
181
+ background: linear-gradient(135deg, #10B981 0%, #059669 100%);
182
+ box-shadow: 0 6px 16px rgba(52, 211, 153, 0.5);
183
+ }
184
+
185
+ /* Accessibility - ensure focus states are visible */
186
+ #realtimeIndicator:focus-within {
187
+ outline: 2px solid #34D399;
188
+ outline-offset: 2px;
189
+ }