@linxin666/dsh-remote-web-ui 0.1.1

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 (118) hide show
  1. package/LICENSE +28 -0
  2. package/README.md +346 -0
  3. package/cordis.patch.yml +10 -0
  4. package/lib/client.js +2817 -0
  5. package/lib/index.js +1455 -0
  6. package/lib/invariant.js +21 -0
  7. package/lib/types/client/PairFailedNotice.d.ts +12 -0
  8. package/lib/types/client/PairFailedNotice.d.ts.map +1 -0
  9. package/lib/types/client/PhoneIcon.d.ts +19 -0
  10. package/lib/types/client/PhoneIcon.d.ts.map +1 -0
  11. package/lib/types/client/PluginSettingsCard.d.ts +78 -0
  12. package/lib/types/client/PluginSettingsCard.d.ts.map +1 -0
  13. package/lib/types/client/RemoteEntry.d.ts +10 -0
  14. package/lib/types/client/RemoteEntry.d.ts.map +1 -0
  15. package/lib/types/client/RemotePanel.d.ts +50 -0
  16. package/lib/types/client/RemotePanel.d.ts.map +1 -0
  17. package/lib/types/client/RemoteSettingsCard.d.ts +75 -0
  18. package/lib/types/client/RemoteSettingsCard.d.ts.map +1 -0
  19. package/lib/types/client/deep-link.d.ts +40 -0
  20. package/lib/types/client/deep-link.d.ts.map +1 -0
  21. package/lib/types/client/index.d.ts +54 -0
  22. package/lib/types/client/index.d.ts.map +1 -0
  23. package/lib/types/client/locales.d.ts +144 -0
  24. package/lib/types/client/locales.d.ts.map +1 -0
  25. package/lib/types/client/pair-api.d.ts +100 -0
  26. package/lib/types/client/pair-api.d.ts.map +1 -0
  27. package/lib/types/client/settings-form.d.ts +119 -0
  28. package/lib/types/client/settings-form.d.ts.map +1 -0
  29. package/lib/types/gate.d.ts +58 -0
  30. package/lib/types/gate.d.ts.map +1 -0
  31. package/lib/types/index.d.ts +78 -0
  32. package/lib/types/index.d.ts.map +1 -0
  33. package/lib/types/invariant.d.ts +16 -0
  34. package/lib/types/invariant.d.ts.map +1 -0
  35. package/lib/types/lan.d.ts +15 -0
  36. package/lib/types/lan.d.ts.map +1 -0
  37. package/lib/types/mobile/api.d.ts +64 -0
  38. package/lib/types/mobile/api.d.ts.map +1 -0
  39. package/lib/types/mobile/index.d.ts +10 -0
  40. package/lib/types/mobile/index.d.ts.map +1 -0
  41. package/lib/types/mobile/messages.d.ts +103 -0
  42. package/lib/types/mobile/messages.d.ts.map +1 -0
  43. package/lib/types/mobile/messages.test.d.ts +2 -0
  44. package/lib/types/mobile/messages.test.d.ts.map +1 -0
  45. package/lib/types/mobile/mobile-styles.d.ts +6 -0
  46. package/lib/types/mobile/mobile-styles.d.ts.map +1 -0
  47. package/lib/types/mobile/mobile-theme.d.ts +22 -0
  48. package/lib/types/mobile/mobile-theme.d.ts.map +1 -0
  49. package/lib/types/mobile/mobile-theme.test.d.ts +2 -0
  50. package/lib/types/mobile/mobile-theme.test.d.ts.map +1 -0
  51. package/lib/types/mobile/mux.d.ts +52 -0
  52. package/lib/types/mobile/mux.d.ts.map +1 -0
  53. package/lib/types/mobile/rpc.d.ts +38 -0
  54. package/lib/types/mobile/rpc.d.ts.map +1 -0
  55. package/lib/types/mobile/theme-toggle.d.ts +12 -0
  56. package/lib/types/mobile/theme-toggle.d.ts.map +1 -0
  57. package/lib/types/mobile/views/App.d.ts +59 -0
  58. package/lib/types/mobile/views/App.d.ts.map +1 -0
  59. package/lib/types/mobile/views/ChatView.d.ts +39 -0
  60. package/lib/types/mobile/views/ChatView.d.ts.map +1 -0
  61. package/lib/types/mobile/views/ChatView.test.d.ts +2 -0
  62. package/lib/types/mobile/views/ChatView.test.d.ts.map +1 -0
  63. package/lib/types/mobile/views/SessionListView.d.ts +27 -0
  64. package/lib/types/mobile/views/SessionListView.d.ts.map +1 -0
  65. package/lib/types/mobile/views/SessionListView.test.d.ts +2 -0
  66. package/lib/types/mobile/views/SessionListView.test.d.ts.map +1 -0
  67. package/lib/types/mobile/views/WorkspaceView.d.ts +19 -0
  68. package/lib/types/mobile/views/WorkspaceView.d.ts.map +1 -0
  69. package/lib/types/mobile-api.d.ts +41 -0
  70. package/lib/types/mobile-api.d.ts.map +1 -0
  71. package/lib/types/mobile-routes.d.ts +16 -0
  72. package/lib/types/mobile-routes.d.ts.map +1 -0
  73. package/lib/types/pairing.d.ts +215 -0
  74. package/lib/types/pairing.d.ts.map +1 -0
  75. package/lib/types/routes.d.ts +58 -0
  76. package/lib/types/routes.d.ts.map +1 -0
  77. package/lib/types/tunnel.d.ts +99 -0
  78. package/lib/types/tunnel.d.ts.map +1 -0
  79. package/package.json +104 -0
  80. package/src/client/PairFailedNotice.tsx +33 -0
  81. package/src/client/PhoneIcon.tsx +31 -0
  82. package/src/client/PluginSettingsCard.tsx +207 -0
  83. package/src/client/RemoteEntry.tsx +202 -0
  84. package/src/client/RemotePanel.tsx +186 -0
  85. package/src/client/RemoteSettingsCard.tsx +220 -0
  86. package/src/client/deep-link.ts +137 -0
  87. package/src/client/index.ts +167 -0
  88. package/src/client/locales.ts +149 -0
  89. package/src/client/pair-api.ts +176 -0
  90. package/src/client/remote.module.css +422 -0
  91. package/src/client/settings-card.module.css +296 -0
  92. package/src/client/settings-form.ts +294 -0
  93. package/src/css-modules.d.ts +6 -0
  94. package/src/gate.ts +121 -0
  95. package/src/index.ts +299 -0
  96. package/src/invariant.ts +32 -0
  97. package/src/lan.ts +21 -0
  98. package/src/mobile/api.ts +106 -0
  99. package/src/mobile/index.tsx +27 -0
  100. package/src/mobile/messages.test.ts +222 -0
  101. package/src/mobile/messages.ts +555 -0
  102. package/src/mobile/mobile-styles.ts +853 -0
  103. package/src/mobile/mobile-theme.test.ts +84 -0
  104. package/src/mobile/mobile-theme.ts +76 -0
  105. package/src/mobile/mux.ts +126 -0
  106. package/src/mobile/rpc.ts +90 -0
  107. package/src/mobile/theme-toggle.tsx +47 -0
  108. package/src/mobile/views/App.tsx +152 -0
  109. package/src/mobile/views/ChatView.test.tsx +243 -0
  110. package/src/mobile/views/ChatView.tsx +650 -0
  111. package/src/mobile/views/SessionListView.test.tsx +139 -0
  112. package/src/mobile/views/SessionListView.tsx +186 -0
  113. package/src/mobile/views/WorkspaceView.tsx +108 -0
  114. package/src/mobile-api.ts +271 -0
  115. package/src/mobile-routes.ts +77 -0
  116. package/src/pairing.ts +390 -0
  117. package/src/routes.ts +346 -0
  118. package/src/tunnel.ts +264 -0
@@ -0,0 +1,853 @@
1
+ /**
2
+ * Mobile surface stylesheet, shipped as a string and injected at boot (the
3
+ * standalone page has no CSS-module pipeline).
4
+ */
5
+ export const mobileCss = `/* Mobile surface chrome: standalone stylesheet (no main-UI design tokens —
6
+ this page boots without the shell). Light-first palette; the dark palette
7
+ applies only when the header toggle sets [data-theme='dark']. */
8
+
9
+ :root {
10
+ --m-bg: #f3f5f9;
11
+ --m-bg-raised: #ffffff;
12
+ --m-bg-input: #ffffff;
13
+ --m-border: #dfe4ec;
14
+ --m-text: #1c2333;
15
+ --m-text-secondary: #4d5768;
16
+ --m-text-tertiary: #8b95a7;
17
+ --m-accent: #2f6fed;
18
+ --m-accent-soft: rgba(47, 111, 237, 0.12);
19
+ --m-danger: #d64545;
20
+ --m-success: #1e9e6a;
21
+ --m-radius: 12px;
22
+ --m-shadow: 0 1px 2px rgba(16, 24, 40, 0.06);
23
+ --m-shadow-raise: 0 6px 20px rgba(16, 24, 40, 0.1);
24
+ --m-backdrop: rgba(15, 23, 42, 0.4);
25
+ }
26
+
27
+ :root[data-theme='dark'] {
28
+ --m-bg: #111418;
29
+ --m-bg-raised: #1a1f26;
30
+ --m-bg-input: #20262e;
31
+ --m-border: #2b333d;
32
+ --m-text: #e8eaed;
33
+ --m-text-secondary: #9aa3ad;
34
+ --m-text-tertiary: #6b747f;
35
+ --m-accent: #4f8cff;
36
+ --m-accent-soft: rgba(79, 140, 255, 0.14);
37
+ --m-danger: #e5534b;
38
+ --m-success: #3fb68b;
39
+ --m-radius: 12px;
40
+ --m-shadow: 0 1px 2px rgba(0, 0, 0, 0.3);
41
+ --m-shadow-raise: 0 6px 20px rgba(0, 0, 0, 0.4);
42
+ --m-backdrop: rgba(0, 0, 0, 0.55);
43
+ }
44
+
45
+ * {
46
+ box-sizing: border-box;
47
+ }
48
+
49
+ html,
50
+ body {
51
+ margin: 0;
52
+ padding: 0;
53
+ height: 100%;
54
+ background: var(--m-bg);
55
+ color: var(--m-text);
56
+ font-family: -apple-system, BlinkMacSystemFont, 'PingFang SC', 'Noto Sans SC', 'Segoe UI', sans-serif;
57
+ font-size: 15px;
58
+ line-height: 1.5;
59
+ -webkit-text-size-adjust: 100%;
60
+ }
61
+
62
+ /* The app fills exactly one viewport: the page itself never scrolls, each
63
+ view owns its scroll region (chat history, session lists) and the chat
64
+ composer stays pinned to the bottom of the screen. */
65
+ #root {
66
+ height: 100dvh;
67
+ display: flex;
68
+ flex-direction: column;
69
+ overflow: hidden;
70
+ }
71
+
72
+ .mobile {
73
+ min-height: 0;
74
+ flex: 1;
75
+ display: flex;
76
+ flex-direction: column;
77
+ overflow: hidden;
78
+ }
79
+
80
+ /* ── header ─────────────────────────────────────────────────────────── */
81
+
82
+ .mobile-header {
83
+ position: sticky;
84
+ top: 0;
85
+ z-index: 10;
86
+ flex-shrink: 0;
87
+ display: flex;
88
+ align-items: center;
89
+ gap: 8px;
90
+ padding: calc(env(safe-area-inset-top, 0px) + 12px) 16px 10px;
91
+ background: color-mix(in srgb, var(--m-bg) 88%, transparent);
92
+ backdrop-filter: blur(10px);
93
+ border-bottom: 1px solid var(--m-border);
94
+ }
95
+
96
+ .mobile-title {
97
+ margin: 0;
98
+ font-size: 17px;
99
+ font-weight: 600;
100
+ }
101
+
102
+ .mobile-titleInline {
103
+ flex: 1;
104
+ overflow: hidden;
105
+ text-overflow: ellipsis;
106
+ white-space: nowrap;
107
+ }
108
+
109
+ .mobile-back {
110
+ flex: none;
111
+ width: 32px;
112
+ height: 32px;
113
+ border: none;
114
+ border-radius: 50%;
115
+ background: transparent;
116
+ color: var(--m-text);
117
+ font-size: 22px;
118
+ line-height: 1;
119
+ cursor: pointer;
120
+ transition: background-color 0.12s ease, box-shadow 0.12s ease;
121
+ }
122
+
123
+ .mobile-back:active {
124
+ background: var(--m-bg-raised);
125
+ }
126
+
127
+ .mobile-back:focus-visible {
128
+ outline: none;
129
+ box-shadow: 0 0 0 2px var(--m-bg), 0 0 0 4px var(--m-accent);
130
+ }
131
+
132
+ .mobile-theme-toggle {
133
+ flex: none;
134
+ margin-left: auto;
135
+ width: 32px;
136
+ height: 32px;
137
+ display: inline-flex;
138
+ align-items: center;
139
+ justify-content: center;
140
+ border: none;
141
+ border-radius: 50%;
142
+ background: transparent;
143
+ color: var(--m-text-secondary);
144
+ cursor: pointer;
145
+ transition: background-color 0.12s ease, color 0.12s ease, box-shadow 0.12s ease;
146
+ }
147
+
148
+ .mobile-theme-toggle:active {
149
+ background: var(--m-bg-raised);
150
+ }
151
+
152
+ .mobile-theme-toggle:focus-visible {
153
+ outline: none;
154
+ box-shadow: 0 0 0 2px var(--m-bg), 0 0 0 4px var(--m-accent);
155
+ }
156
+
157
+ .mobile-theme-toggle svg {
158
+ width: 18px;
159
+ height: 18px;
160
+ }
161
+
162
+ /* ── lists ───────────────────────────────────────────────────────────── */
163
+
164
+ .mobile-list {
165
+ flex: 1;
166
+ min-height: 0;
167
+ overflow-y: auto;
168
+ list-style: none;
169
+ margin: 0;
170
+ padding: 8px 0 calc(env(safe-area-inset-bottom, 0px) + 16px);
171
+ }
172
+
173
+ .mobile-row {
174
+ display: flex;
175
+ align-items: center;
176
+ gap: 10px;
177
+ width: calc(100% - 24px);
178
+ margin: 0 12px 8px;
179
+ padding: 13px 14px;
180
+ border: 1px solid var(--m-border);
181
+ border-radius: 14px;
182
+ background: var(--m-bg-raised);
183
+ box-shadow: var(--m-shadow);
184
+ color: var(--m-text);
185
+ text-align: left;
186
+ cursor: pointer;
187
+ }
188
+
189
+ .mobile-row:active {
190
+ background: color-mix(in srgb, var(--m-text) 5%, var(--m-bg-raised));
191
+ }
192
+
193
+ .mobile-rowMain {
194
+ flex: 1;
195
+ min-width: 0;
196
+ display: flex;
197
+ align-items: baseline;
198
+ gap: 8px;
199
+ }
200
+
201
+ .mobile-rowTitle {
202
+ flex: 1;
203
+ min-width: 0;
204
+ overflow: hidden;
205
+ text-overflow: ellipsis;
206
+ white-space: nowrap;
207
+ font-size: 15px;
208
+ }
209
+
210
+ .mobile-rowMeta {
211
+ flex: none;
212
+ color: var(--m-text-tertiary);
213
+ font-size: 12px;
214
+ }
215
+
216
+ .mobile-chevron {
217
+ flex: none;
218
+ color: var(--m-text-tertiary);
219
+ font-size: 18px;
220
+ }
221
+
222
+ .mobile-live {
223
+ margin-left: 6px;
224
+ color: var(--m-success);
225
+ font-size: 10px;
226
+ vertical-align: middle;
227
+ }
228
+
229
+ /* ── empty / error states ────────────────────────────────────────────── */
230
+
231
+ .mobile-empty {
232
+ flex: 1;
233
+ display: flex;
234
+ flex-direction: column;
235
+ align-items: center;
236
+ justify-content: center;
237
+ gap: 12px;
238
+ padding: 32px;
239
+ }
240
+
241
+ .mobile-muted {
242
+ margin: 0;
243
+ color: var(--m-text-tertiary);
244
+ }
245
+
246
+ .mobile-error {
247
+ margin: 0;
248
+ color: var(--m-danger);
249
+ }
250
+
251
+ .mobile-pad {
252
+ padding: 8px 16px;
253
+ }
254
+
255
+ /* ── buttons ─────────────────────────────────────────────────────────── */
256
+
257
+ .mobile-button {
258
+ height: 38px;
259
+ padding: 0 18px;
260
+ border: 1px solid var(--m-border);
261
+ border-radius: 10px;
262
+ background: var(--m-bg-raised);
263
+ color: var(--m-text);
264
+ font-size: 14px;
265
+ cursor: pointer;
266
+ }
267
+
268
+ .mobile-button:disabled {
269
+ opacity: 0.5;
270
+ }
271
+
272
+ .mobile-block {
273
+ display: block;
274
+ width: calc(100% - 32px);
275
+ margin: 4px 16px;
276
+ }
277
+
278
+ /* Primary action (create session): accent fill, readable on both themes. */
279
+ .mobile-new {
280
+ display: block;
281
+ width: 100%;
282
+ height: 44px;
283
+ border: none;
284
+ border-radius: 12px;
285
+ background: linear-gradient(135deg, var(--m-accent) 0%, #4c8dff 100%);
286
+ color: #fff;
287
+ font-size: 15px;
288
+ font-weight: 600;
289
+ cursor: pointer;
290
+ box-shadow: 0 2px 8px rgba(47, 111, 237, 0.3);
291
+ }
292
+
293
+ .mobile-new:active {
294
+ opacity: 0.88;
295
+ }
296
+
297
+ .mobile-new:disabled {
298
+ opacity: 0.6;
299
+ }
300
+
301
+ .mobile-hint {
302
+ display: block;
303
+ margin-top: 2px;
304
+ color: var(--m-text-tertiary);
305
+ font-size: 12px;
306
+ }
307
+
308
+ /* ── chat ────────────────────────────────────────────────────────────── */
309
+
310
+ .chat {
311
+ flex: 1;
312
+ display: flex;
313
+ flex-direction: column;
314
+ min-height: 0;
315
+ }
316
+
317
+ .chat-scroll {
318
+ flex: 1;
319
+ overflow-y: auto;
320
+ padding: 12px 14px calc(env(safe-area-inset-bottom, 0px) + 12px);
321
+ display: flex;
322
+ flex-direction: column;
323
+ gap: 10px;
324
+ }
325
+
326
+ .chat-msg {
327
+ max-width: 88%;
328
+ padding: 10px 12px;
329
+ border-radius: var(--m-radius);
330
+ font-size: 14.5px;
331
+ line-height: 1.55;
332
+ overflow-wrap: break-word;
333
+ white-space: pre-wrap;
334
+ }
335
+
336
+ .chat-msg-user {
337
+ align-self: flex-end;
338
+ background: linear-gradient(135deg, var(--m-accent) 0%, #4c8dff 100%);
339
+ color: #fff;
340
+ border-bottom-right-radius: 4px;
341
+ box-shadow: 0 1px 3px rgba(47, 111, 237, 0.28);
342
+ }
343
+
344
+ .chat-msg-assistant {
345
+ align-self: flex-start;
346
+ background: var(--m-bg-raised);
347
+ border: 1px solid var(--m-border);
348
+ border-bottom-left-radius: 4px;
349
+ box-shadow: var(--m-shadow);
350
+ }
351
+
352
+ .chat-msg-pending .chat-msg-text::after {
353
+ content: '▍';
354
+ animation: chat-blink 1s steps(2) infinite;
355
+ }
356
+
357
+ @keyframes chat-blink {
358
+ 50% {
359
+ opacity: 0;
360
+ }
361
+ }
362
+
363
+ .chat-msg-failed {
364
+ border-color: var(--m-danger);
365
+ }
366
+
367
+ .chat-msg-failtag {
368
+ display: inline-block;
369
+ margin-top: 6px;
370
+ padding: 1px 8px;
371
+ border: 1px solid var(--m-danger);
372
+ border-radius: 999px;
373
+ color: var(--m-danger);
374
+ font-size: 11px;
375
+ }
376
+
377
+ .chat-msg-time {
378
+ display: block;
379
+ margin-top: 4px;
380
+ color: var(--m-text-tertiary);
381
+ font-size: 10.5px;
382
+ line-height: 1.2;
383
+ }
384
+
385
+ .chat-msg-toggle {
386
+ display: block;
387
+ margin-top: 8px;
388
+ padding: 4px 0;
389
+ border: none;
390
+ background: transparent;
391
+ color: var(--m-accent);
392
+ font-size: 13px;
393
+ cursor: pointer;
394
+ }
395
+
396
+ /* ── message disclosures (reasoning / tools) ─────────────────────────── */
397
+
398
+ .chat-disclosure {
399
+ margin-bottom: 8px;
400
+ border: 1px solid var(--m-border);
401
+ border-radius: 10px;
402
+ background: color-mix(in srgb, var(--m-bg) 55%, transparent);
403
+ overflow: hidden;
404
+ }
405
+
406
+ .chat-disclosure-head {
407
+ display: flex;
408
+ align-items: center;
409
+ gap: 6px;
410
+ width: 100%;
411
+ padding: 7px 10px;
412
+ border: none;
413
+ background: transparent;
414
+ color: var(--m-text);
415
+ font-size: 12.5px;
416
+ text-align: left;
417
+ cursor: pointer;
418
+ white-space: nowrap;
419
+ }
420
+
421
+ .chat-disclosure-caret {
422
+ flex: none;
423
+ color: var(--m-text-tertiary);
424
+ font-size: 14px;
425
+ line-height: 1;
426
+ transition: transform 0.15s ease;
427
+ }
428
+
429
+ .chat-disclosure-open .chat-disclosure-caret {
430
+ transform: rotate(90deg);
431
+ }
432
+
433
+ .chat-disclosure-label {
434
+ flex: none;
435
+ color: var(--m-text-secondary);
436
+ }
437
+
438
+ .chat-reasoning[data-pending] .chat-disclosure-label {
439
+ color: var(--m-accent);
440
+ }
441
+
442
+ .chat-disclosure-summary {
443
+ flex: 1;
444
+ min-width: 0;
445
+ overflow: hidden;
446
+ text-overflow: ellipsis;
447
+ color: var(--m-text-tertiary);
448
+ font-size: 12px;
449
+ }
450
+
451
+ .chat-disclosure-count {
452
+ flex: none;
453
+ color: var(--m-text-tertiary);
454
+ font-size: 11px;
455
+ }
456
+
457
+ .chat-disclosure-body {
458
+ padding: 8px 10px 10px;
459
+ border-top: 1px dashed var(--m-border);
460
+ color: var(--m-text-secondary);
461
+ font-size: 12.5px;
462
+ line-height: 1.6;
463
+ overflow-wrap: break-word;
464
+ white-space: pre-wrap;
465
+ max-height: 40vh;
466
+ overflow-y: auto;
467
+ }
468
+
469
+ .chat-tools-body {
470
+ display: flex;
471
+ flex-direction: column;
472
+ gap: 8px;
473
+ }
474
+
475
+ .chat-tool-item {
476
+ display: flex;
477
+ flex-direction: column;
478
+ gap: 2px;
479
+ }
480
+
481
+ .chat-tool-name {
482
+ color: var(--m-text);
483
+ font-size: 12.5px;
484
+ font-weight: 600;
485
+ }
486
+
487
+ .chat-tool-args {
488
+ margin: 0;
489
+ padding: 6px 8px;
490
+ border-radius: 6px;
491
+ background: var(--m-bg-input);
492
+ color: var(--m-text-secondary);
493
+ font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
494
+ font-size: 11px;
495
+ line-height: 1.5;
496
+ overflow-wrap: break-word;
497
+ white-space: pre-wrap;
498
+ }
499
+
500
+ /* ── composer toolbar (model / permission chips) ─────────────────────── */
501
+
502
+ .chat-tools {
503
+ display: flex;
504
+ gap: 8px;
505
+ padding: 6px 12px 0;
506
+ }
507
+
508
+ .chat-chip {
509
+ flex: 1;
510
+ min-width: 0;
511
+ display: flex;
512
+ align-items: center;
513
+ gap: 6px;
514
+ height: 32px;
515
+ padding: 0 10px;
516
+ border: 1px solid var(--m-border);
517
+ border-radius: 999px;
518
+ background: var(--m-bg-raised);
519
+ box-shadow: var(--m-shadow);
520
+ color: var(--m-text);
521
+ font-size: 12px;
522
+ cursor: pointer;
523
+ }
524
+
525
+ .chat-chip:active {
526
+ border-color: var(--m-accent);
527
+ background: var(--m-accent-soft);
528
+ }
529
+
530
+ .chat-chip-label {
531
+ flex: none;
532
+ color: var(--m-text-tertiary);
533
+ }
534
+
535
+ .chat-chip-value {
536
+ flex: 1;
537
+ min-width: 0;
538
+ overflow: hidden;
539
+ text-overflow: ellipsis;
540
+ white-space: nowrap;
541
+ }
542
+
543
+ .chat-chip-chevron {
544
+ flex: none;
545
+ color: var(--m-text-tertiary);
546
+ font-size: 14px;
547
+ }
548
+
549
+ /* ── bottom sheets ───────────────────────────────────────────────────── */
550
+
551
+ .sheet-backdrop {
552
+ position: fixed;
553
+ inset: 0;
554
+ z-index: 50;
555
+ display: flex;
556
+ flex-direction: column;
557
+ justify-content: flex-end;
558
+ background: var(--m-backdrop);
559
+ animation: sheet-fade 0.18s ease;
560
+ }
561
+
562
+ @keyframes sheet-fade {
563
+ from {
564
+ opacity: 0;
565
+ }
566
+ to {
567
+ opacity: 1;
568
+ }
569
+ }
570
+
571
+ .sheet {
572
+ max-height: 74dvh;
573
+ display: flex;
574
+ flex-direction: column;
575
+ background: var(--m-bg-raised);
576
+ border: 1px solid var(--m-border);
577
+ border-bottom: none;
578
+ border-radius: 16px 16px 0 0;
579
+ box-shadow: var(--m-shadow-raise);
580
+ padding-bottom: calc(env(safe-area-inset-bottom, 0px) + 10px);
581
+ animation: sheet-up 0.22s ease;
582
+ }
583
+
584
+ @keyframes sheet-up {
585
+ from {
586
+ transform: translateY(40px);
587
+ opacity: 0.6;
588
+ }
589
+ to {
590
+ transform: translateY(0);
591
+ opacity: 1;
592
+ }
593
+ }
594
+
595
+ .sheet-handle {
596
+ flex: none;
597
+ align-self: center;
598
+ width: 36px;
599
+ height: 4px;
600
+ margin: 8px 0 4px;
601
+ border-radius: 999px;
602
+ background: var(--m-border);
603
+ }
604
+
605
+ .sheet-title {
606
+ flex: none;
607
+ padding: 6px 16px 10px;
608
+ font-size: 15px;
609
+ font-weight: 600;
610
+ }
611
+
612
+ .sheet-body {
613
+ flex: 1;
614
+ min-height: 0;
615
+ overflow-y: auto;
616
+ padding: 0 12px 8px;
617
+ }
618
+
619
+ .sheet-status {
620
+ padding: 18px 8px;
621
+ color: var(--m-text-tertiary);
622
+ font-size: 13px;
623
+ text-align: center;
624
+ display: flex;
625
+ flex-direction: column;
626
+ align-items: center;
627
+ gap: 8px;
628
+ }
629
+
630
+ .sheet-status-error {
631
+ color: var(--m-danger);
632
+ }
633
+
634
+ .sheet-hint {
635
+ margin: 0;
636
+ color: var(--m-text-secondary);
637
+ font-size: 12px;
638
+ line-height: 1.5;
639
+ max-width: 32em;
640
+ }
641
+
642
+ .sheet-error {
643
+ margin: 0 0 8px;
644
+ color: var(--m-danger);
645
+ font-size: 12.5px;
646
+ }
647
+
648
+ .sheet-section {
649
+ margin-bottom: 14px;
650
+ }
651
+
652
+ .sheet-section-title {
653
+ padding: 4px 8px 6px;
654
+ color: var(--m-text-tertiary);
655
+ font-size: 12px;
656
+ font-weight: 600;
657
+ }
658
+
659
+ .sheet-option {
660
+ display: flex;
661
+ align-items: center;
662
+ gap: 10px;
663
+ width: 100%;
664
+ padding: 10px 12px;
665
+ border: 1px solid transparent;
666
+ border-radius: 10px;
667
+ background: transparent;
668
+ color: var(--m-text);
669
+ text-align: left;
670
+ cursor: pointer;
671
+ }
672
+
673
+ .sheet-option:active {
674
+ background: var(--m-bg-input);
675
+ }
676
+
677
+ .sheet-option-selected {
678
+ border-color: var(--m-accent);
679
+ background: color-mix(in srgb, var(--m-accent) 12%, transparent);
680
+ }
681
+
682
+ .sheet-option:disabled {
683
+ opacity: 0.5;
684
+ }
685
+
686
+ .sheet-option-copy {
687
+ flex: 1;
688
+ min-width: 0;
689
+ display: flex;
690
+ flex-direction: column;
691
+ gap: 2px;
692
+ }
693
+
694
+ .sheet-option-title {
695
+ font-size: 14px;
696
+ }
697
+
698
+ .sheet-option-desc {
699
+ color: var(--m-text-tertiary);
700
+ font-size: 12px;
701
+ line-height: 1.4;
702
+ }
703
+
704
+ .sheet-option-check {
705
+ flex: none;
706
+ color: var(--m-accent);
707
+ font-size: 15px;
708
+ font-weight: 700;
709
+ }
710
+
711
+ .sheet-confirm-desc {
712
+ margin: 0 4px 14px;
713
+ color: var(--m-text-secondary);
714
+ font-size: 13px;
715
+ line-height: 1.6;
716
+ }
717
+
718
+ .sheet-confirm-actions {
719
+ display: flex;
720
+ gap: 10px;
721
+ padding: 4px;
722
+ }
723
+
724
+ .sheet-confirm-actions .mobile-button {
725
+ flex: 1;
726
+ }
727
+
728
+ .sheet-confirm-danger {
729
+ flex: 1;
730
+ height: 38px;
731
+ border: none;
732
+ border-radius: 10px;
733
+ background: var(--m-danger);
734
+ color: #fff;
735
+ font-size: 14px;
736
+ cursor: pointer;
737
+ }
738
+
739
+ .sheet-confirm-danger:disabled {
740
+ opacity: 0.5;
741
+ }
742
+
743
+ .chat-meta {
744
+ margin-top: 4px;
745
+ color: var(--m-text-tertiary);
746
+ font-size: 11px;
747
+ }
748
+
749
+ .chat-typing {
750
+ color: var(--m-text-tertiary);
751
+ font-size: 13px;
752
+ padding: 4px 2px;
753
+ }
754
+
755
+ .chat-inputbar {
756
+ display: flex;
757
+ gap: 8px;
758
+ padding: 10px 12px calc(env(safe-area-inset-bottom, 0px) + 10px);
759
+ border-top: 1px solid var(--m-border);
760
+ background: var(--m-bg);
761
+ }
762
+
763
+ .chat-input {
764
+ flex: 1;
765
+ min-width: 0;
766
+ min-height: 40px;
767
+ max-height: 120px;
768
+ padding: 9px 12px;
769
+ border: 1px solid var(--m-border);
770
+ border-radius: 10px;
771
+ background: var(--m-bg-input);
772
+ color: var(--m-text);
773
+ font: inherit;
774
+ font-size: 14.5px;
775
+ resize: none;
776
+ outline: none;
777
+ }
778
+
779
+ .chat-input:focus {
780
+ border-color: var(--m-accent);
781
+ }
782
+
783
+ .chat-send {
784
+ flex: none;
785
+ align-self: flex-end;
786
+ height: 40px;
787
+ padding: 0 16px;
788
+ border: none;
789
+ border-radius: 10px;
790
+ background: var(--m-accent);
791
+ color: #fff;
792
+ font-size: 14px;
793
+ cursor: pointer;
794
+ }
795
+
796
+ .chat-send:disabled {
797
+ opacity: 0.5;
798
+ }
799
+
800
+ .chat-load-older {
801
+ align-self: center;
802
+ margin: 4px 0;
803
+ border: none;
804
+ background: transparent;
805
+ color: var(--m-text-secondary);
806
+ font-size: 13px;
807
+ cursor: pointer;
808
+ padding: 6px 10px;
809
+ }
810
+
811
+ /* Touch surfaces have no hover, so keyboard focus must be fully visible.
812
+ One shared ring keeps every interactive control consistent and readable
813
+ on both palettes. */
814
+ .mobile-row:focus-visible,
815
+ .mobile-button:focus-visible,
816
+ .mobile-new:focus-visible,
817
+ .chat-send:focus-visible,
818
+ .chat-chip:focus-visible,
819
+ .chat-msg-toggle:focus-visible,
820
+ .chat-load-older:focus-visible,
821
+ .chat-disclosure-head:focus-visible,
822
+ .sheet-option:focus-visible,
823
+ .sheet-confirm-danger:focus-visible,
824
+ .chat-input:focus-visible {
825
+ outline: none;
826
+ box-shadow: 0 0 0 2px var(--m-bg), 0 0 0 4px var(--m-accent);
827
+ }
828
+
829
+ /* Accessibility: collapse every interaction/enter animation for
830
+ reduced-motion users; surface layout itself stays static. */
831
+ @media (prefers-reduced-motion: reduce) {
832
+ .mobile-theme-toggle,
833
+ .mobile-back,
834
+ .mobile-row,
835
+ .mobile-button,
836
+ .mobile-new,
837
+ .chat-send,
838
+ .chat-chip,
839
+ .chat-msg-toggle,
840
+ .chat-load-older,
841
+ .chat-disclosure-head,
842
+ .chat-disclosure-caret,
843
+ .chat-msg-pending .chat-msg-text::after,
844
+ .sheet-backdrop,
845
+ .sheet,
846
+ .sheet-option,
847
+ .sheet-confirm-danger,
848
+ .chat-input {
849
+ animation: none;
850
+ transition: none;
851
+ }
852
+ }
853
+ `