@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,387 @@
1
+ /* ============================================
2
+ THEME SYSTEM
3
+
4
+ How it works:
5
+ - :root in app.css defines the default theme (Midnight)
6
+ - Each [data-theme] block overrides ONLY the color variables
7
+ - Starfield bg stays black (#000), stars stay white across all dark themes
8
+ - Daylight (light mode) hides starfield entirely
9
+ - JS sets data-theme on <html> and saves to localStorage
10
+ ============================================ */
11
+
12
+ /* ── Midnight (Default) ──
13
+ Values live in :root in app.css. This block is a reference only. */
14
+ :root[data-theme="midnight"] {
15
+ /* All defaults — no overrides needed */
16
+ }
17
+
18
+ /* ── Daylight (Light Mode) ──
19
+ Warm light theme — soft white, washed red accent */
20
+ :root[data-theme="daylight"] {
21
+ --bg: #f8f5f1;
22
+ --bg-secondary: rgba(252, 249, 244, 0.95);
23
+ --bg-input: rgba(0, 0, 0, 0.03);
24
+ --accent: #c84b4b;
25
+ --accent-hover: #a93d3d;
26
+ --accent-secondary: #e06060;
27
+ --text: #1a1a1a;
28
+ --text-muted: #737373;
29
+ --text-dim: #a3a3a3;
30
+ --text-inverse: #f8f5f1;
31
+ --border: rgba(0, 0, 0, 0.08);
32
+
33
+ --accent-05: rgba(200, 75, 75, 0.05);
34
+ --accent-10: rgba(200, 75, 75, 0.1);
35
+ --accent-15: rgba(200, 75, 75, 0.15);
36
+ --accent-20: rgba(200, 75, 75, 0.2);
37
+ --accent-30: rgba(200, 75, 75, 0.3);
38
+ --accent-40: rgba(200, 75, 75, 0.4);
39
+ --accent-50: rgba(200, 75, 75, 0.5);
40
+
41
+ --error-10: rgba(220, 38, 38, 0.08);
42
+ --error-20: rgba(220, 38, 38, 0.12);
43
+ --error-30: rgba(220, 38, 38, 0.2);
44
+ --success-20: rgba(22, 163, 74, 0.1);
45
+
46
+ --user-bubble: rgba(200, 75, 75, 0.33);
47
+ --user-accent: rgba(200, 75, 75, 0.38);
48
+ --user-accent-hover: rgba(200, 75, 75, 0.25);
49
+ --assistant-bubble: rgba(0, 0, 0, 0.11);
50
+
51
+ --success: #16a34a;
52
+ --warning: #d97706;
53
+ --info: #2563eb;
54
+ --recording: #dc2626;
55
+ --error: #dc2626;
56
+ --neutral: #737373;
57
+
58
+ --star-color: #fff;
59
+
60
+ --syntax-keyword: #7c3aed;
61
+ --syntax-string: #16a34a;
62
+ --syntax-comment: #a3a3a3;
63
+ --syntax-number: #c84b4b;
64
+
65
+ --black-20: rgba(0, 0, 0, 0.06);
66
+ --black-40: rgba(0, 0, 0, 0.1);
67
+ }
68
+
69
+ /* ── Ember ──
70
+ Deep fire theme — dark navy base, crimson surfaces, amber/yellow accents
71
+ Palette: #03071e #370617 #6a040f #9d0208 #d00000 #dc2f02 #e85d04 #f48c06 #faa307 #ffba08 */
72
+ :root[data-theme="ember"] {
73
+ --bg: #03071e;
74
+ --bg-secondary: rgba(55, 6, 23, 0.95);
75
+ --bg-input: rgba(106, 4, 15, 0.3);
76
+ --accent: #f48c06;
77
+ --accent-hover: #faa307;
78
+ --accent-secondary: #ffba08;
79
+ --text: #fce8d0;
80
+ --text-muted: #c8906a;
81
+ --text-dim: #8a5a3a;
82
+ --text-inverse: #03071e;
83
+ --border: rgba(244, 140, 6, 0.15);
84
+
85
+ --accent-05: rgba(244, 140, 6, 0.05);
86
+ --accent-10: rgba(244, 140, 6, 0.1);
87
+ --accent-15: rgba(244, 140, 6, 0.15);
88
+ --accent-20: rgba(244, 140, 6, 0.2);
89
+ --accent-30: rgba(244, 140, 6, 0.3);
90
+ --accent-40: rgba(244, 140, 6, 0.4);
91
+ --accent-50: rgba(244, 140, 6, 0.5);
92
+
93
+ --error-10: rgba(208, 0, 0, 0.15);
94
+ --error-20: rgba(208, 0, 0, 0.25);
95
+ --error-30: rgba(208, 0, 0, 0.35);
96
+ --success-20: rgba(74, 222, 128, 0.2);
97
+
98
+ --user-bubble: rgba(157, 2, 8, 0.3);
99
+ --user-accent: #9d0208;
100
+ --user-accent-hover: #6a040f;
101
+ --assistant-bubble: rgba(46, 33, 33, 0.98);
102
+
103
+ --success: #4ade80;
104
+ --warning: #ffba08;
105
+ --info: #60a5fa;
106
+ --recording: #d00000;
107
+ --error: #d00000;
108
+ --neutral: #8a5a3a;
109
+
110
+ --star-color: #f48c06;
111
+
112
+ --syntax-keyword: #faa307;
113
+ --syntax-string: #ffba08;
114
+ --syntax-comment: #6a040f;
115
+ --syntax-number: #e85d04;
116
+ }
117
+
118
+ /* ── Forest ──
119
+ Deep earthy dark theme — dark woodland greens, muted sage accents
120
+ Palette: #dad7cd #a3b18a #588157 #3a5a40 #344e41 */
121
+ :root[data-theme="forest"] {
122
+ --bg: #131813;
123
+ --bg-secondary: rgba(34, 44, 34, 0.95);
124
+ --bg-input: rgba(52, 78, 65, 0.25);
125
+ --accent: #a3b18a;
126
+ --accent-hover: #dad7cd;
127
+ --accent-secondary: #588157;
128
+ --text: #dad7cd;
129
+ --text-muted: #8a9a7a;
130
+ --text-dim: #5a6e4a;
131
+ --text-inverse: #1a1f1a;
132
+ --border: rgba(163, 177, 138, 0.15);
133
+
134
+ --accent-05: rgba(163, 177, 138, 0.05);
135
+ --accent-10: rgba(163, 177, 138, 0.1);
136
+ --accent-15: rgba(163, 177, 138, 0.15);
137
+ --accent-20: rgba(163, 177, 138, 0.2);
138
+ --accent-30: rgba(163, 177, 138, 0.3);
139
+ --accent-40: rgba(163, 177, 138, 0.4);
140
+ --accent-50: rgba(163, 177, 138, 0.5);
141
+
142
+ --error-10: rgba(239, 68, 68, 0.1);
143
+ --error-20: rgba(239, 68, 68, 0.2);
144
+ --error-30: rgba(239, 68, 68, 0.3);
145
+ --success-20: rgba(88, 129, 87, 0.25);
146
+
147
+ --user-bubble: rgba(58, 90, 64, 0.3);
148
+ --user-accent: #3a5a40;
149
+ --user-accent-hover: #344e41;
150
+ --assistant-bubble: rgba(46, 43, 35, 0.63);
151
+
152
+ --success: #a3b18a;
153
+ --warning: #d4a746;
154
+ --info: #60a5fa;
155
+ --recording: #ff3366;
156
+ --error: #ef4444;
157
+ --neutral: #6b7280;
158
+
159
+ --star-color: #a3b18a;
160
+
161
+ --syntax-keyword: #dad7cd;
162
+ --syntax-string: #a3b18a;
163
+ --syntax-comment: #3a5a40;
164
+ --syntax-number: #588157;
165
+ }
166
+
167
+ /* ── Phantom ──
168
+ Monochrome grayscale — no color accent */
169
+ :root[data-theme="phantom"] {
170
+ --bg: #000;
171
+ --bg-secondary: rgba(20, 20, 20, 0.95);
172
+ --bg-input: rgba(30, 30, 30, 0.8);
173
+ --accent: #a0a0a0;
174
+ --accent-hover: #c0c0c0;
175
+ --accent-secondary: #808080;
176
+ --text: #e8e8e8;
177
+ --text-muted: #909090;
178
+ --text-dim: #707070;
179
+ --text-inverse: #0a0a0a;
180
+ --border: var(--accent-15);
181
+
182
+ --accent-05: rgba(160, 160, 160, 0.05);
183
+ --accent-10: rgba(160, 160, 160, 0.1);
184
+ --accent-15: rgba(160, 160, 160, 0.15);
185
+ --accent-20: rgba(160, 160, 160, 0.2);
186
+ --accent-30: rgba(160, 160, 160, 0.3);
187
+ --accent-40: rgba(160, 160, 160, 0.4);
188
+ --accent-50: rgba(160, 160, 160, 0.5);
189
+
190
+ --error-10: rgba(239, 68, 68, 0.1);
191
+ --error-20: rgba(239, 68, 68, 0.2);
192
+ --error-30: rgba(239, 68, 68, 0.3);
193
+ --success-20: rgba(74, 222, 128, 0.2);
194
+
195
+ --user-bubble: rgba(60, 60, 60, 0.4);
196
+ --user-accent: #404040;
197
+ --user-accent-hover: #333333;
198
+ --assistant-bubble: #161f25;
199
+
200
+ --success: #4ade80;
201
+ --warning: #fbbf24;
202
+ --info: #60a5fa;
203
+ --recording: #ff3366;
204
+ --error: #ef4444;
205
+ --neutral: #6b7280;
206
+
207
+ --star-color: #fff;
208
+
209
+ --syntax-keyword: #b0b0b0;
210
+ --syntax-string: #909090;
211
+ --syntax-comment: #505050;
212
+ --syntax-number: #c0c0c0;
213
+ }
214
+
215
+ /* ============================================
216
+ DAYLIGHT (LIGHT MODE) OVERRIDES
217
+ These need component-level selectors since
218
+ light mode changes more than just colors.
219
+ ============================================ */
220
+
221
+ /* Hide starfield in light themes */
222
+ :root[data-theme="daylight"] #stars,
223
+ :root[data-theme="forest"] #stars {
224
+ display: none;
225
+ }
226
+
227
+ /* Light mode needs opaque .app background */
228
+ :root[data-theme="daylight"] .app {
229
+ background: var(--bg);
230
+ }
231
+
232
+ /* Message bubbles need subtle borders in light mode */
233
+ :root[data-theme="daylight"] .assistant-message .message-content {
234
+ border: 1px solid var(--border);
235
+ }
236
+
237
+ :root[data-theme="daylight"] .user-message .message-content {
238
+ border: 1px solid rgba(200, 75, 75, 0.1);
239
+ }
240
+
241
+ /* Override the hardcoded purple border on user bubbles */
242
+ :root[data-theme="daylight"] .message.user {
243
+ border-color: rgba(200, 75, 75, 0.12);
244
+ }
245
+
246
+ /* Panels need subtle shadow instead of glow */
247
+ :root[data-theme="daylight"] .settings-panel,
248
+ :root[data-theme="daylight"] .panel,
249
+ :root[data-theme="daylight"] .dev-panel {
250
+ box-shadow: 0 4px 24px rgba(0, 0, 0, 0.1);
251
+ border-color: rgba(0, 0, 0, 0.1);
252
+ }
253
+
254
+ /* Input fields need visible borders */
255
+ :root[data-theme="daylight"] .setting-input,
256
+ :root[data-theme="daylight"] .setting-select,
257
+ :root[data-theme="daylight"] #messageInput {
258
+ border-color: rgba(0, 0, 0, 0.1);
259
+ }
260
+
261
+ /* Header — barely-there warm tint */
262
+ :root[data-theme="daylight"] .main-header {
263
+ background: rgba(0, 0, 0, 0.02);
264
+ border-bottom: 1px solid var(--border);
265
+ }
266
+
267
+ /* Code blocks — subtle warm gray */
268
+ :root[data-theme="daylight"] pre,
269
+ :root[data-theme="daylight"] code {
270
+ background: rgba(0, 0, 0, 0.035);
271
+ }
272
+
273
+ :root[data-theme="daylight"] pre {
274
+ border: 1px solid rgba(0, 0, 0, 0.06);
275
+ }
276
+
277
+ /* ============================================
278
+ EMBER OVERRIDES — Rising ember sparks
279
+ ============================================ */
280
+ :root[data-theme="ember"] .star {
281
+ animation: ember-rise var(--duration) linear infinite !important;
282
+ animation-duration: calc(var(--duration) * 4) !important;
283
+ }
284
+
285
+ @keyframes ember-rise {
286
+ 0% {
287
+ opacity: 0;
288
+ transform: translateY(0) translateX(0) scale(0.8);
289
+ }
290
+ 5% {
291
+ opacity: var(--opacity);
292
+ transform: translateY(-10px) translateX(2px) scale(1);
293
+ }
294
+ 25% {
295
+ opacity: var(--opacity);
296
+ transform: translateY(-50px) translateX(8px) scale(0.9);
297
+ }
298
+ 50% {
299
+ opacity: calc(var(--opacity) * 0.7);
300
+ transform: translateY(-100px) translateX(-3px) scale(0.7);
301
+ }
302
+ 75% {
303
+ opacity: calc(var(--opacity) * 0.3);
304
+ transform: translateY(-150px) translateX(10px) scale(0.4);
305
+ }
306
+ 95% {
307
+ opacity: 0;
308
+ transform: translateY(-180px) translateX(5px) scale(0.2);
309
+ }
310
+ 100% {
311
+ opacity: 0;
312
+ transform: translateY(0) translateX(0) scale(0.8);
313
+ }
314
+ }
315
+
316
+ /* ============================================
317
+ EMBER (DARK MODE) OVERRIDES
318
+ Ember uses a non-black bg, so starfield stars
319
+ need to be amber-tinted (handled via --star-color)
320
+ ============================================ */
321
+
322
+ /* ============================================
323
+ THEME PICKER — Visual theme selector
324
+ Replaces the plain <select> dropdown
325
+ ============================================ */
326
+
327
+ .theme-picker {
328
+ display: flex;
329
+ flex-wrap: wrap;
330
+ gap: 6px;
331
+ }
332
+
333
+ .theme-picker-item {
334
+ display: flex;
335
+ align-items: center;
336
+ gap: 7px;
337
+ padding: 5px 10px 5px 6px;
338
+ border-radius: 8px;
339
+ border: 1.5px solid transparent;
340
+ background: rgba(255, 255, 255, 0.04);
341
+ cursor: pointer;
342
+ transition: background 0.15s, border-color 0.15s, transform 0.1s;
343
+ user-select: none;
344
+ }
345
+
346
+ .theme-picker-item:hover {
347
+ background: rgba(255, 255, 255, 0.08);
348
+ }
349
+
350
+ .theme-picker-item.active {
351
+ border-color: var(--accent);
352
+ background: var(--accent-10);
353
+ }
354
+
355
+ .theme-picker-item[data-premium-locked="true"] {
356
+ opacity: 0.45;
357
+ cursor: default;
358
+ }
359
+
360
+ .theme-picker-item[data-premium-locked="true"]:hover {
361
+ background: rgba(255, 255, 255, 0.04);
362
+ }
363
+
364
+ .theme-picker-swatch {
365
+ width: 18px;
366
+ height: 18px;
367
+ border-radius: 5px;
368
+ flex-shrink: 0;
369
+ border: 1px solid rgba(255, 255, 255, 0.1);
370
+ }
371
+
372
+ .theme-picker-name {
373
+ font-size: 0.72rem;
374
+ font-weight: 500;
375
+ color: var(--text-muted);
376
+ white-space: nowrap;
377
+ }
378
+
379
+ .theme-picker-item.active .theme-picker-name {
380
+ color: var(--text);
381
+ font-weight: 600;
382
+ }
383
+
384
+ .theme-picker-lock {
385
+ font-size: 0.6rem;
386
+ opacity: 0.6;
387
+ }
@@ -0,0 +1,54 @@
1
+ /* ============================================
2
+ TIMESTAMPS STYLES
3
+ Inline with bottom of bubble, outside
4
+ ============================================ */
5
+
6
+ /* Message needs relative positioning for timestamp */
7
+ .message.show-timestamp {
8
+ position: relative;
9
+ }
10
+
11
+ /* Timestamp element — hidden by default */
12
+ .message-timestamp {
13
+ display: none;
14
+ font-size: 0.6rem;
15
+ font-family: 'Space Mono', monospace;
16
+ color: var(--text-muted);
17
+ opacity: 0.6;
18
+ user-select: none;
19
+ line-height: 1;
20
+ white-space: nowrap;
21
+ position: absolute;
22
+ bottom: 4px;
23
+ pointer-events: none;
24
+ }
25
+
26
+ /* Show when enabled */
27
+ .message.show-timestamp .message-timestamp {
28
+ display: block;
29
+ }
30
+
31
+ /* Slightly brighter on hover */
32
+ .message:hover .message-timestamp {
33
+ opacity: 0.85;
34
+ }
35
+
36
+ /* User messages: timestamp to the left of the bubble */
37
+ .message.user .message-timestamp {
38
+ right: calc(100% + 8px);
39
+ }
40
+
41
+ /* Assistant messages: timestamp to the right of the bubble */
42
+ .message.assistant .message-timestamp {
43
+ left: calc(100% + 8px);
44
+ }
45
+
46
+ /* Mobile: tighter spacing */
47
+ @media (max-width: 480px) {
48
+ .message.user .message-timestamp {
49
+ right: calc(100% + 4px);
50
+ }
51
+ .message.assistant .message-timestamp {
52
+ left: calc(100% + 4px);
53
+ }
54
+ }
@@ -0,0 +1,78 @@
1
+ /* ============================================
2
+ CSS CUSTOM PROPERTIES (DESIGN TOKENS)
3
+ Consolidated design system variables
4
+ ============================================ */
5
+
6
+ :root {
7
+ /* ==========================================
8
+ Z-INDEX SCALE
9
+ Use these instead of hardcoded z-index values
10
+ ==========================================
11
+
12
+ Layer hierarchy (from bottom to top):
13
+ - base (1): Default positioned elements
14
+ - dropdown (100): Dropdowns, autocompletes, tooltips in context
15
+ - sticky (200): Sticky headers, fixed input areas
16
+ - modal (500): Panels, side drawers, activity panels
17
+ - overlay (1000): Full-screen overlays, modals, dialogs
18
+ - toast (9000): Notifications, toasts, alerts
19
+ - tooltip (10000): Tooltips that must appear above everything
20
+ ========================================== */
21
+ --z-base: 1;
22
+ --z-dropdown: 100;
23
+ --z-sticky: 200;
24
+ --z-modal: 500;
25
+ --z-overlay: 1000;
26
+ --z-toast: 9000;
27
+ --z-tooltip: 10000;
28
+
29
+ /* ==========================================
30
+ RESPONSIVE BREAKPOINTS
31
+ Use with @media (max-width: var(--breakpoint-*))
32
+ Note: CSS custom properties don't work in media queries,
33
+ so use these as reference values:
34
+
35
+ --breakpoint-sm: 480px (Small phones, below this is "mobile")
36
+ --breakpoint-md: 768px (Tablets, below this is "small screens")
37
+ --breakpoint-lg: 1024px (Desktop, below this is "tablet")
38
+ --breakpoint-xl: 1440px (Wide desktop)
39
+ --breakpoint-2xl: 1920px (Ultra-wide)
40
+ ========================================== */
41
+
42
+ /* ==========================================
43
+ SPACING SCALE (4px base)
44
+ Consolidated from app.css
45
+ ========================================== */
46
+ --space-1: 4px;
47
+ --space-2: 8px;
48
+ --space-3: 12px;
49
+ --space-4: 16px;
50
+ --space-5: 20px;
51
+ --space-6: 24px;
52
+ --space-8: 32px;
53
+ --space-10: 40px;
54
+ --space-12: 48px;
55
+
56
+ /* ==========================================
57
+ TYPOGRAPHY SCALE
58
+ Consolidated from app.css
59
+ ========================================== */
60
+ --text-xs: 0.75rem; /* 12px */
61
+ --text-sm: 0.875rem; /* 14px */
62
+ --text-base: 1rem; /* 16px */
63
+ --text-lg: 1.125rem; /* 18px */
64
+ --text-xl: 1.25rem; /* 20px */
65
+ --text-2xl: 1.5rem; /* 24px */
66
+ --text-3xl: 2rem; /* 32px */
67
+
68
+ /* ==========================================
69
+ BORDER RADIUS SCALE
70
+ Use these instead of hardcoded border-radius
71
+ ========================================== */
72
+ --radius-sm: 4px;
73
+ --radius-md: 8px;
74
+ --radius-lg: 12px;
75
+ --radius-xl: 16px;
76
+ --radius-2xl: 24px;
77
+ --radius-full: 9999px;
78
+ }