@jaychang1989/dsh-webchat 0.3.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 (45) hide show
  1. package/LICENSE +201 -0
  2. package/NOTICE +18 -0
  3. package/README.md +78 -0
  4. package/README.zh.md +83 -0
  5. package/cordis.patch.yml +22 -0
  6. package/lib/client.js +1725 -0
  7. package/lib/client.js.map +1 -0
  8. package/lib/index.js +3887 -0
  9. package/lib/types/client/api.d.ts +74 -0
  10. package/lib/types/client/controller.d.ts +19 -0
  11. package/lib/types/client/index.d.ts +30 -0
  12. package/lib/types/client/locales.d.ts +169 -0
  13. package/lib/types/client/mount.d.ts +23 -0
  14. package/lib/types/client/panel/Markdown.d.ts +15 -0
  15. package/lib/types/client/panel/WebChatPanel.d.ts +28 -0
  16. package/lib/types/client/sidebar-entry-core.d.ts +60 -0
  17. package/lib/types/client/sidebar-entry.d.ts +22 -0
  18. package/lib/types/engine/engine.d.ts +247 -0
  19. package/lib/types/engine/html-md.d.ts +23 -0
  20. package/lib/types/index.d.ts +63 -0
  21. package/lib/types/protocol.d.ts +107 -0
  22. package/lib/types/routes.d.ts +22 -0
  23. package/lib/types/store.d.ts +69 -0
  24. package/lib/types/tools.d.ts +27 -0
  25. package/lib/types/transfer.d.ts +138 -0
  26. package/package.json +96 -0
  27. package/src/client/api.ts +90 -0
  28. package/src/client/controller.ts +43 -0
  29. package/src/client/css-modules.d.ts +5 -0
  30. package/src/client/index.ts +75 -0
  31. package/src/client/locales.ts +172 -0
  32. package/src/client/mount.tsx +125 -0
  33. package/src/client/panel/Markdown.tsx +251 -0
  34. package/src/client/panel/WebChatPanel.tsx +760 -0
  35. package/src/client/panel/panel.module.css +936 -0
  36. package/src/client/sidebar-entry-core.ts +207 -0
  37. package/src/client/sidebar-entry.ts +49 -0
  38. package/src/engine/engine.ts +1385 -0
  39. package/src/engine/html-md.ts +126 -0
  40. package/src/index.ts +212 -0
  41. package/src/protocol.ts +116 -0
  42. package/src/routes.ts +307 -0
  43. package/src/store.ts +217 -0
  44. package/src/tools.ts +257 -0
  45. package/src/transfer.ts +602 -0
@@ -0,0 +1,936 @@
1
+ /**
2
+ * dsh-webchat panel styles. Scoped by the plugin's own data attributes so
3
+ * nothing leaks into the rest of the GUI; colors ride the dsh --dsw-* tokens
4
+ * so the panel follows the active theme (light/dark and skins). The
5
+ * center-column hide rules are attribute-scoped and must stay in this
6
+ * stylesheet (it is imported by mount.tsx, so the styles load with the
7
+ * plugin).
8
+ *
9
+ * Design language: the panel is a bridge between two modes of working — a
10
+ * human conversation (proportional type, bubbles, calm) and a handoff into
11
+ * executable harness context (monospace, hairlines, a labeled dock). The
12
+ * monospace "utility" register is reserved for machine things (timestamps,
13
+ * model names, counts, status, and the handoff eyebrow); chat copy stays in
14
+ * the shell's proportional family. The handoff dock is the signature.
15
+ */
16
+
17
+ /* --- center-column takeover (global rules, attribute-scoped) ----------------- */
18
+
19
+ [data-pane='conversation'],
20
+ [class*='centerCol'] {
21
+ position: relative;
22
+ }
23
+
24
+ [data-dsh-webchat-view] {
25
+ position: absolute;
26
+ inset: 0;
27
+ display: none;
28
+ z-index: 60;
29
+ background: var(--dsw-alias-bg-base);
30
+ }
31
+
32
+ html[data-dsh-webchat-active]:not([data-dsh-taskboard-active]):not([data-dsh-ssh-active]) [data-dsh-webchat-view] {
33
+ display: block;
34
+ }
35
+
36
+ html[data-dsh-webchat-active]:not([data-dsh-taskboard-active]):not([data-dsh-ssh-active]) [data-pane='conversation'] > :not([data-dsh-webchat-view]),
37
+ html[data-dsh-webchat-active]:not([data-dsh-taskboard-active]):not([data-dsh-ssh-active]) [class*='centerCol'] > :not([data-dsh-webchat-view]) {
38
+ display: none !important;
39
+ }
40
+
41
+ /* --- sidebar entry row ------------------------------------------------------- */
42
+
43
+ .entry {
44
+ display: flex;
45
+ align-items: center;
46
+ gap: 8px;
47
+ width: 100%;
48
+ height: 32px;
49
+ padding: 0 12px;
50
+ background: transparent;
51
+ border: none;
52
+ border-radius: 8px;
53
+ color: var(--dsw-alias-label-secondary);
54
+ cursor: pointer;
55
+ font-size: 13px;
56
+ white-space: nowrap;
57
+ }
58
+
59
+ .entry:hover {
60
+ background: var(--dsw-specific-sidebar-nav-item-hover);
61
+ color: var(--dsw-alias-label-primary);
62
+ }
63
+
64
+ .entry[data-active] {
65
+ background: var(--dsw-specific-sidebar-nav-item-active);
66
+ color: var(--dsw-alias-label-primary);
67
+ font-weight: 600;
68
+ }
69
+
70
+ .entryIcon {
71
+ display: inline-flex;
72
+ align-items: center;
73
+ justify-content: center;
74
+ flex: none;
75
+ }
76
+
77
+ .entryLabel {
78
+ overflow: hidden;
79
+ text-overflow: ellipsis;
80
+ }
81
+
82
+ [data-dsh-frame][data-sidebar-collapsed] .entry {
83
+ justify-content: center;
84
+ padding: 0;
85
+ width: 100%;
86
+ }
87
+
88
+ [data-dsh-frame][data-sidebar-collapsed] .entryLabel {
89
+ display: none;
90
+ }
91
+
92
+ /* --- panel frame -------------------------------------------------------------- */
93
+
94
+ .view {
95
+ overflow: hidden;
96
+ box-sizing: border-box;
97
+ }
98
+
99
+ /* The shell does not apply a global border-box reset to divs, so the panel
100
+ and every element inside it set it explicitly — otherwise height:100% plus
101
+ padding overflows the center column and the composer is clipped. */
102
+ .panel,
103
+ .panel * {
104
+ box-sizing: border-box;
105
+ }
106
+
107
+ .panel {
108
+ display: flex;
109
+ flex-direction: column;
110
+ height: 100%;
111
+ min-width: 0;
112
+ min-height: 0;
113
+ padding: 12px 14px 14px;
114
+ gap: 8px;
115
+ background: var(--dsw-alias-bg-base);
116
+ color: var(--dsw-alias-label-primary);
117
+ font-family: var(--dsw-font-family);
118
+ }
119
+
120
+ /* --- header (identity + live status) ----------------------------------------- */
121
+
122
+ .panelHeader {
123
+ display: flex;
124
+ align-items: center;
125
+ gap: 10px;
126
+ flex: none;
127
+ flex-wrap: wrap;
128
+ }
129
+
130
+ .panelTitle {
131
+ margin: 0;
132
+ font-size: 15px;
133
+ font-weight: 700;
134
+ letter-spacing: -0.01em;
135
+ color: var(--dsw-alias-label-primary);
136
+ white-space: nowrap;
137
+ }
138
+
139
+ .headerSpacer {
140
+ flex: 1;
141
+ min-width: 8px;
142
+ }
143
+
144
+ .statusChip {
145
+ display: inline-flex;
146
+ align-items: center;
147
+ gap: 7px;
148
+ flex: none;
149
+ padding: 4px 10px;
150
+ border: 1px solid var(--dsw-alias-border-l1);
151
+ border-radius: 999px;
152
+ background: var(--dsw-alias-bg-layer1, var(--dsw-alias-bg-elevated));
153
+ font-size: 11.5px;
154
+ color: var(--dsw-alias-label-secondary);
155
+ }
156
+
157
+ .statusText {
158
+ min-width: 0;
159
+ overflow: hidden;
160
+ text-overflow: ellipsis;
161
+ white-space: nowrap;
162
+ font-family: var(--dsw-font-mono, ui-monospace, SFMono-Regular, Menlo, Consolas, monospace);
163
+ letter-spacing: 0.01em;
164
+ }
165
+
166
+ .statusDot {
167
+ width: 7px;
168
+ height: 7px;
169
+ border-radius: 50%;
170
+ flex: none;
171
+ }
172
+
173
+ .statusDot[data-state='ok'] {
174
+ background: var(--dsw-alias-state-success, #22c55e);
175
+ }
176
+
177
+ .statusDot[data-state='warn'] {
178
+ background: var(--dsw-alias-state-warning, #f59e0b);
179
+ }
180
+
181
+ .statusDot[data-state='bad'] {
182
+ background: var(--dsw-alias-state-danger, #ef4444);
183
+ }
184
+
185
+ .statusDot[data-state='busy'] {
186
+ background: var(--dsw-alias-state-info, #3b82f6);
187
+ animation: dshWcPulse 1.2s ease-in-out infinite;
188
+ }
189
+
190
+ @keyframes dshWcPulse {
191
+ 0%, 100% { opacity: 1; }
192
+ 50% { opacity: 0.35; }
193
+ }
194
+
195
+ /* --- panel body (sidebar + main) ---------------------------------------------- */
196
+
197
+ .panelBody {
198
+ flex: 1;
199
+ min-height: 0;
200
+ display: flex;
201
+ gap: 10px;
202
+ }
203
+
204
+ .sidebar {
205
+ flex: 0 0 172px;
206
+ min-width: 0;
207
+ display: flex;
208
+ flex-direction: column;
209
+ gap: 6px;
210
+ padding: 8px;
211
+ border: 1px solid var(--dsw-alias-border-l1);
212
+ border-radius: 10px;
213
+ background: var(--dsw-alias-bg-layer1, var(--dsw-alias-bg-elevated));
214
+ overflow: hidden;
215
+ }
216
+
217
+ .sidebarNew {
218
+ width: 100%;
219
+ justify-content: center;
220
+ }
221
+
222
+ .recoverList {
223
+ max-height: 160px;
224
+ overflow-y: auto;
225
+ display: flex;
226
+ flex-direction: column;
227
+ gap: 2px;
228
+ padding: 4px;
229
+ border: 1px solid var(--dsw-alias-border-l2);
230
+ border-radius: 8px;
231
+ background: var(--dsw-alias-bg-layer1, var(--dsw-alias-bg-elevated));
232
+ }
233
+
234
+ .recoverItem {
235
+ display: block;
236
+ width: 100%;
237
+ padding: 6px 8px;
238
+ border: none;
239
+ border-radius: 6px;
240
+ background: transparent;
241
+ color: var(--dsw-alias-label-primary);
242
+ font: inherit;
243
+ font-size: 12.5px;
244
+ text-align: left;
245
+ cursor: pointer;
246
+ }
247
+
248
+ .recoverItem:hover:not(:disabled) {
249
+ background: var(--dsw-alias-interactive-bg-hover, rgba(128, 128, 128, 0.12));
250
+ }
251
+
252
+ .recoverItem:disabled {
253
+ opacity: 0.5;
254
+ cursor: default;
255
+ }
256
+
257
+ .recoverTitle {
258
+ display: inline-block;
259
+ max-width: 100%;
260
+ overflow: hidden;
261
+ text-overflow: ellipsis;
262
+ white-space: nowrap;
263
+ }
264
+
265
+ .recoverEmpty {
266
+ padding: 6px 8px;
267
+ font-size: 12px;
268
+ color: var(--dsw-alias-label-tertiary);
269
+ }
270
+
271
+ .sidebarList {
272
+ flex: 1;
273
+ min-height: 0;
274
+ overflow-y: auto;
275
+ display: flex;
276
+ flex-direction: column;
277
+ gap: 2px;
278
+ }
279
+
280
+ .sidebarItem {
281
+ display: flex;
282
+ align-items: center;
283
+ gap: 2px;
284
+ border-radius: 7px;
285
+ }
286
+
287
+ .sidebarItem:hover {
288
+ background: var(--dsw-alias-interactive-bg-hover, rgba(128, 128, 128, 0.12));
289
+ }
290
+
291
+ .sidebarItem[data-active] {
292
+ background: var(--dsw-specific-sidebar-nav-item-active, var(--dsw-alias-interactive-bg-hover));
293
+ }
294
+
295
+ .sidebarRow {
296
+ flex: 1;
297
+ min-width: 0;
298
+ display: flex;
299
+ flex-direction: column;
300
+ align-items: flex-start;
301
+ gap: 1px;
302
+ padding: 6px 8px;
303
+ background: transparent;
304
+ border: none;
305
+ border-radius: 7px;
306
+ color: var(--dsw-alias-label-primary);
307
+ cursor: pointer;
308
+ text-align: left;
309
+ }
310
+
311
+ .sidebarTitle {
312
+ display: block;
313
+ width: 100%;
314
+ overflow: hidden;
315
+ text-overflow: ellipsis;
316
+ white-space: nowrap;
317
+ font-size: 12.5px;
318
+ line-height: 1.3;
319
+ }
320
+
321
+ .sidebarMeta {
322
+ font-family: var(--dsw-font-mono, ui-monospace, SFMono-Regular, Menlo, Consolas, monospace);
323
+ font-size: 10px;
324
+ color: var(--dsw-alias-label-tertiary);
325
+ }
326
+
327
+ .sidebarActions {
328
+ flex: none;
329
+ display: flex;
330
+ gap: 2px;
331
+ padding-right: 4px;
332
+ opacity: 0;
333
+ transition: opacity 120ms ease;
334
+ }
335
+
336
+ .sidebarItem:hover .sidebarActions,
337
+ .sidebarItem[data-active] .sidebarActions {
338
+ opacity: 1;
339
+ }
340
+
341
+ .sidebarAction {
342
+ display: inline-flex;
343
+ align-items: center;
344
+ justify-content: center;
345
+ width: 22px;
346
+ height: 22px;
347
+ padding: 0;
348
+ border: none;
349
+ border-radius: 6px;
350
+ background: transparent;
351
+ color: var(--dsw-alias-label-secondary);
352
+ cursor: pointer;
353
+ }
354
+
355
+ .sidebarAction:hover {
356
+ background: var(--dsw-alias-interactive-bg-hover);
357
+ color: var(--dsw-alias-label-primary);
358
+ }
359
+
360
+ .sidebarActionDanger {
361
+ display: inline-flex;
362
+ align-items: center;
363
+ justify-content: center;
364
+ height: 22px;
365
+ padding: 0 6px;
366
+ border: none;
367
+ border-radius: 6px;
368
+ background: var(--dsw-alias-state-danger, #ef4444);
369
+ color: #fff;
370
+ font-size: 11px;
371
+ cursor: pointer;
372
+ white-space: nowrap;
373
+ }
374
+
375
+ .sidebarRename {
376
+ flex: 1;
377
+ min-width: 0;
378
+ display: flex;
379
+ align-items: center;
380
+ gap: 2px;
381
+ padding: 2px;
382
+ }
383
+
384
+ .sidebarRenameInput {
385
+ flex: 1;
386
+ min-width: 0;
387
+ padding: 4px 6px;
388
+ font-size: 12px;
389
+ color: var(--dsw-alias-label-primary);
390
+ background: var(--dsw-specific-input-major, var(--dsw-alias-bg-layer1));
391
+ border: 1px solid var(--dsw-alias-state-business-primary, #4f6bfa);
392
+ border-radius: 6px;
393
+ outline: none;
394
+ }
395
+
396
+ .sidebarEmpty {
397
+ margin: auto;
398
+ padding: 12px 8px;
399
+ text-align: center;
400
+ font-size: 12px;
401
+ color: var(--dsw-alias-label-tertiary);
402
+ }
403
+
404
+ .sidebarClear {
405
+ width: 100%;
406
+ justify-content: center;
407
+ }
408
+
409
+ /* --- main chat column ----------------------------------------------------------- */
410
+
411
+ .main {
412
+ flex: 1;
413
+ min-width: 0;
414
+ min-height: 0;
415
+ display: flex;
416
+ flex-direction: column;
417
+ overflow: hidden;
418
+ gap: 8px;
419
+ }
420
+
421
+ /* --- composer toggles (深度思考 / 智能搜索) -------------------------------------- */
422
+
423
+ .composerToolbar {
424
+ display: flex;
425
+ align-items: center;
426
+ gap: 6px;
427
+ flex: none;
428
+ padding: 2px 12px 2px;
429
+ }
430
+
431
+ .toggleButton {
432
+ appearance: none;
433
+ display: inline-flex;
434
+ align-items: center;
435
+ gap: 5px;
436
+ padding: 4px 11px;
437
+ border: 1px solid var(--dsw-alias-border-l2);
438
+ border-radius: 999px;
439
+ background: transparent;
440
+ color: var(--dsw-alias-label-secondary);
441
+ font-family: var(--dsw-font-mono, ui-monospace, SFMono-Regular, Menlo, Consolas, monospace);
442
+ font-size: 11.5px;
443
+ cursor: pointer;
444
+ white-space: nowrap;
445
+ transition: color 120ms ease, border-color 120ms ease, background 120ms ease;
446
+ }
447
+
448
+ .toggleButton:hover:not(:disabled) {
449
+ color: var(--dsw-alias-label-primary);
450
+ border-color: var(--dsw-alias-border-l1);
451
+ }
452
+
453
+ .toggleButton:disabled {
454
+ opacity: 0.45;
455
+ cursor: not-allowed;
456
+ }
457
+
458
+ .toggleButton[data-active] {
459
+ color: var(--dsw-alias-state-business-primary, #4f6bfa);
460
+ border-color: var(--dsw-alias-state-business-primary, #4f6bfa);
461
+ background: var(--dsw-alias-interactive-bg-hover, rgba(128, 128, 128, 0.1));
462
+ font-weight: 600;
463
+ }
464
+
465
+ /* --- segmented control (model + transfer mode) -------------------------------- */
466
+
467
+ .segmented {
468
+ display: inline-flex;
469
+ align-items: stretch;
470
+ flex: none;
471
+ gap: 2px;
472
+ padding: 2px;
473
+ border: 1px solid var(--dsw-alias-border-l2);
474
+ border-radius: 8px;
475
+ background: var(--dsw-alias-bg-base);
476
+ }
477
+
478
+ .segmentedButton {
479
+ appearance: none;
480
+ border: none;
481
+ background: transparent;
482
+ padding: 4px 10px;
483
+ border-radius: 6px;
484
+ font-family: var(--dsw-font-mono, ui-monospace, SFMono-Regular, Menlo, Consolas, monospace);
485
+ font-size: 11.5px;
486
+ color: var(--dsw-alias-label-secondary);
487
+ cursor: pointer;
488
+ white-space: nowrap;
489
+ }
490
+
491
+ .segmentedButton:hover:not([data-active]):not(:disabled) {
492
+ color: var(--dsw-alias-label-primary);
493
+ background: var(--dsw-alias-interactive-bg-hover);
494
+ }
495
+
496
+ .segmentedButton[data-active] {
497
+ background: var(--dsw-alias-bg-elevated, var(--dsw-alias-bg-layer1));
498
+ color: var(--dsw-alias-state-business-primary, #4f6bfa);
499
+ font-weight: 600;
500
+ box-shadow: 0 1px 2px rgba(0, 0, 0, 0.12);
501
+ }
502
+
503
+ /* --- buttons ------------------------------------------------------------------ */
504
+
505
+ .button {
506
+ display: inline-flex;
507
+ align-items: center;
508
+ gap: 6px;
509
+ padding: 6px 12px;
510
+ font-size: 12.5px;
511
+ border: 1px solid var(--dsw-alias-border-l2);
512
+ border-radius: 8px;
513
+ background: var(--dsw-specific-input-major, var(--dsw-alias-bg-layer1));
514
+ color: var(--dsw-alias-label-primary);
515
+ cursor: pointer;
516
+ white-space: nowrap;
517
+ transition: background 120ms ease, filter 120ms ease;
518
+ }
519
+
520
+ .button:hover:not(:disabled) {
521
+ background: var(--dsw-alias-interactive-bg-hover);
522
+ }
523
+
524
+ .button:disabled {
525
+ opacity: 0.45;
526
+ cursor: not-allowed;
527
+ }
528
+
529
+ .buttonPrimary {
530
+ border-color: transparent;
531
+ background: var(--dsw-alias-state-business-primary, #4f6bfa);
532
+ color: #fff;
533
+ }
534
+
535
+ .buttonPrimary:hover:not(:disabled) {
536
+ filter: brightness(1.08);
537
+ }
538
+
539
+ .buttonDanger {
540
+ border-color: transparent;
541
+ background: var(--dsw-alias-state-danger, #ef4444);
542
+ color: #fff;
543
+ }
544
+
545
+ .buttonGhost {
546
+ border-color: transparent;
547
+ background: transparent;
548
+ color: var(--dsw-alias-label-secondary);
549
+ font-size: 12px;
550
+ padding: 5px 8px;
551
+ }
552
+
553
+ .buttonGhost:hover:not(:disabled) {
554
+ color: var(--dsw-alias-label-primary);
555
+ background: var(--dsw-alias-interactive-bg-hover);
556
+ }
557
+
558
+ /* --- chat area (message list) --------------------------------------------------- */
559
+
560
+ .messageList {
561
+ flex: 1;
562
+ min-height: 0;
563
+ overflow-y: auto;
564
+ display: flex;
565
+ flex-direction: column;
566
+ gap: 14px;
567
+ padding: 4px 2px;
568
+ }
569
+
570
+ .messageRow {
571
+ display: flex;
572
+ flex-direction: column;
573
+ gap: 5px;
574
+ max-width: 92%;
575
+ }
576
+
577
+ .messageRow[data-role='user'] {
578
+ align-self: flex-end;
579
+ align-items: flex-end;
580
+ }
581
+
582
+ .messageRow[data-role='assistant'] {
583
+ align-self: flex-start;
584
+ align-items: flex-start;
585
+ }
586
+
587
+ .messageMeta {
588
+ display: flex;
589
+ align-items: baseline;
590
+ gap: 8px;
591
+ padding: 0 4px;
592
+ font-family: var(--dsw-font-mono, ui-monospace, SFMono-Regular, Menlo, Consolas, monospace);
593
+ font-size: 10.5px;
594
+ line-height: 1;
595
+ }
596
+
597
+ .messageSpeaker {
598
+ color: var(--dsw-alias-label-secondary);
599
+ font-weight: 600;
600
+ }
601
+
602
+ .messageTime {
603
+ color: var(--dsw-alias-label-tertiary);
604
+ font-variant-numeric: tabular-nums;
605
+ }
606
+
607
+ .streamBadge {
608
+ color: var(--dsw-alias-state-info, #3b82f6);
609
+ }
610
+
611
+ .messageBubble {
612
+ padding: 10px 14px;
613
+ border-radius: 12px;
614
+ font-size: 13.5px;
615
+ line-height: 1.6;
616
+ overflow-wrap: anywhere;
617
+ max-width: 100%;
618
+ min-width: 0;
619
+ }
620
+
621
+ .messageRow[data-role='user'] .messageBubble {
622
+ background: var(--dsw-alias-state-business-primary, #4f6bfa);
623
+ color: #fff;
624
+ border-bottom-right-radius: 4px;
625
+ }
626
+
627
+ .messageRow[data-role='assistant'] .messageBubble {
628
+ background: var(--dsw-alias-bg-layer1, var(--dsw-alias-bg-elevated));
629
+ border: 1px solid var(--dsw-alias-border-l1);
630
+ border-bottom-left-radius: 4px;
631
+ }
632
+
633
+ .messageError {
634
+ font-size: 11px;
635
+ color: var(--dsw-alias-state-danger, #ef4444);
636
+ }
637
+
638
+ /* --- empty state (first-run hero) --------------------------------------------- */
639
+
640
+ .emptyState {
641
+ margin: auto;
642
+ display: flex;
643
+ flex-direction: column;
644
+ align-items: center;
645
+ gap: 10px;
646
+ text-align: center;
647
+ max-width: 420px;
648
+ padding: 24px 20px;
649
+ }
650
+
651
+ .emptyEyebrow {
652
+ font-family: var(--dsw-font-mono, ui-monospace, SFMono-Regular, Menlo, Consolas, monospace);
653
+ font-size: 10.5px;
654
+ letter-spacing: 0.12em;
655
+ text-transform: uppercase;
656
+ color: var(--dsw-alias-state-business-primary, #4f6bfa);
657
+ }
658
+
659
+ .emptyTitle {
660
+ margin: 0;
661
+ font-size: 17px;
662
+ font-weight: 700;
663
+ letter-spacing: -0.01em;
664
+ color: var(--dsw-alias-label-primary);
665
+ }
666
+
667
+ .emptyBody {
668
+ margin: 0;
669
+ font-size: 13px;
670
+ line-height: 1.6;
671
+ color: var(--dsw-alias-label-secondary);
672
+ }
673
+
674
+ /* --- composer ------------------------------------------------------------------ */
675
+
676
+ .composer {
677
+ display: flex;
678
+ flex-direction: column;
679
+ flex: none;
680
+ border: 1px solid var(--dsw-alias-border-l2);
681
+ border-radius: 12px;
682
+ background: var(--dsw-specific-input-major, var(--dsw-alias-bg-layer1));
683
+ transition: border-color 120ms ease;
684
+ }
685
+
686
+ .composer:focus-within {
687
+ border-color: var(--dsw-alias-state-business-primary, #4f6bfa);
688
+ }
689
+
690
+ .composerInput {
691
+ flex: 1;
692
+ min-height: 44px;
693
+ max-height: 180px;
694
+ resize: none;
695
+ border: none;
696
+ outline: none;
697
+ background: transparent;
698
+ color: var(--dsw-alias-label-primary);
699
+ font: inherit;
700
+ font-size: 13.5px;
701
+ line-height: 1.5;
702
+ padding: 10px 12px 6px;
703
+ }
704
+
705
+ .composerInput::placeholder {
706
+ color: var(--dsw-alias-label-tertiary);
707
+ }
708
+
709
+ .attachInput {
710
+ margin: 0 12px 4px;
711
+ padding: 7px 10px;
712
+ border: 1px solid var(--dsw-alias-border-l2);
713
+ border-radius: 8px;
714
+ background: var(--dsw-alias-bg-layer1, var(--dsw-alias-bg-elevated));
715
+ color: var(--dsw-alias-label-primary);
716
+ font: inherit;
717
+ font-size: 12.5px;
718
+ }
719
+
720
+ .attachInput::placeholder {
721
+ color: var(--dsw-alias-label-tertiary);
722
+ }
723
+
724
+ .composerFooter {
725
+ display: flex;
726
+ align-items: center;
727
+ gap: 8px;
728
+ padding: 0 8px 8px 12px;
729
+ }
730
+
731
+ .composerHint {
732
+ font-family: var(--dsw-font-mono, ui-monospace, SFMono-Regular, Menlo, Consolas, monospace);
733
+ font-size: 10.5px;
734
+ color: var(--dsw-alias-label-tertiary);
735
+ letter-spacing: 0.02em;
736
+ }
737
+
738
+ .composerSpacer {
739
+ flex: 1;
740
+ }
741
+
742
+ /* --- handoff dock (signature) -------------------------------------------------- */
743
+
744
+ .handoffDock {
745
+ display: flex;
746
+ align-items: center;
747
+ gap: 10px;
748
+ flex: none;
749
+ flex-wrap: wrap;
750
+ padding: 9px 12px;
751
+ border: 1px solid var(--dsw-alias-border-l2);
752
+ border-left: 2px solid var(--dsw-alias-state-business-primary, #4f6bfa);
753
+ border-radius: 10px;
754
+ background: var(--dsw-alias-bg-layer1, var(--dsw-alias-bg-elevated));
755
+ }
756
+
757
+ .handoffEyebrow {
758
+ flex: none;
759
+ font-family: var(--dsw-font-mono, ui-monospace, SFMono-Regular, Menlo, Consolas, monospace);
760
+ font-size: 10.5px;
761
+ letter-spacing: 0.1em;
762
+ text-transform: uppercase;
763
+ color: var(--dsw-alias-label-tertiary);
764
+ white-space: nowrap;
765
+ }
766
+
767
+ .handoffSpacer {
768
+ flex: 1;
769
+ min-width: 8px;
770
+ }
771
+
772
+ .workspaceSelect {
773
+ flex: none;
774
+ max-width: 280px;
775
+ appearance: none;
776
+ padding: 6px 10px;
777
+ font-size: 12px;
778
+ font-family: var(--dsw-font-mono, ui-monospace, SFMono-Regular, Menlo, Consolas, monospace);
779
+ color: var(--dsw-alias-label-primary);
780
+ background: var(--dsw-specific-input-major, var(--dsw-alias-bg-layer1));
781
+ border: 1px solid var(--dsw-alias-border-l2);
782
+ border-radius: 8px;
783
+ cursor: pointer;
784
+ }
785
+
786
+ .workspaceSelect:focus-visible {
787
+ outline: 2px solid var(--dsw-alias-state-business-primary, #4f6bfa);
788
+ outline-offset: 1px;
789
+ }
790
+
791
+ /* --- markdown ------------------------------------------------------------------- */
792
+
793
+ .markdown {
794
+ font-size: 13.5px;
795
+ line-height: 1.65;
796
+ }
797
+
798
+ .markdown p {
799
+ margin: 0.4em 0;
800
+ }
801
+
802
+ .markdown pre {
803
+ position: relative;
804
+ margin: 0.6em 0;
805
+ padding: 10px 12px;
806
+ border-radius: 8px;
807
+ background: var(--dsw-alias-bg-deep, rgba(0, 0, 0, 0.45));
808
+ overflow-x: auto;
809
+ font-size: 12.5px;
810
+ line-height: 1.5;
811
+ }
812
+
813
+ .markdown code {
814
+ font-family: var(--dsw-font-mono, ui-monospace, SFMono-Regular, Menlo, monospace);
815
+ font-size: 0.92em;
816
+ }
817
+
818
+ .markdown :not(pre) > code {
819
+ padding: 1px 5px;
820
+ border-radius: 5px;
821
+ background: var(--dsw-alias-interactive-bg-hover, rgba(128, 128, 128, 0.18));
822
+ }
823
+
824
+ .markdown h1, .markdown h2, .markdown h3, .markdown h4 {
825
+ margin: 0.8em 0 0.4em;
826
+ line-height: 1.3;
827
+ }
828
+
829
+ .markdown ul, .markdown ol {
830
+ margin: 0.4em 0;
831
+ padding-left: 1.4em;
832
+ }
833
+
834
+ .markdown blockquote {
835
+ margin: 0.5em 0;
836
+ padding: 2px 12px;
837
+ border-left: 3px solid var(--dsw-alias-border-l2);
838
+ color: var(--dsw-alias-label-secondary);
839
+ }
840
+
841
+ .tableWrap {
842
+ overflow-x: auto;
843
+ }
844
+
845
+ .markdown table {
846
+ border-collapse: collapse;
847
+ margin: 0.6em 0;
848
+ font-size: 12.5px;
849
+ }
850
+
851
+ .markdown th, .markdown td {
852
+ border: 1px solid var(--dsw-alias-border-l2);
853
+ padding: 4px 10px;
854
+ }
855
+
856
+ .markdown sup.citation {
857
+ font-family: var(--dsw-font-mono, ui-monospace, SFMono-Regular, Menlo, Consolas, monospace);
858
+ font-size: 9.5px;
859
+ font-weight: 600;
860
+ line-height: 1.4;
861
+ color: var(--dsw-alias-state-business-primary, #4f6bfa);
862
+ background: var(--dsw-alias-interactive-bg-hover, rgba(128, 128, 128, 0.16));
863
+ border-radius: 999px;
864
+ padding: 0 4px;
865
+ margin: 0 2px;
866
+ vertical-align: super;
867
+ white-space: nowrap;
868
+ }
869
+
870
+ .markdown a {
871
+ color: var(--dsw-alias-state-business-primary, #4f6bfa);
872
+ }
873
+
874
+ .markdown details {
875
+ margin: 0.5em 0;
876
+ padding: 6px 10px;
877
+ border-radius: 8px;
878
+ background: var(--dsw-alias-interactive-bg-hover, rgba(128, 128, 128, 0.12));
879
+ font-size: 12.5px;
880
+ }
881
+
882
+ .markdown summary {
883
+ cursor: pointer;
884
+ font-weight: 600;
885
+ color: var(--dsw-alias-label-secondary);
886
+ }
887
+
888
+ /* --- toast ----------------------------------------------------------------------- */
889
+
890
+ .toast {
891
+ position: fixed;
892
+ right: 20px;
893
+ bottom: 20px;
894
+ z-index: 200;
895
+ max-width: 420px;
896
+ padding: 10px 14px;
897
+ border-radius: 10px;
898
+ background: var(--dsw-alias-bg-elevated);
899
+ border: 1px solid var(--dsw-alias-border-l2);
900
+ box-shadow: 0 8px 24px rgba(0, 0, 0, 0.18);
901
+ font-size: 13px;
902
+ color: var(--dsw-alias-label-primary);
903
+ animation: dshWcToast 180ms ease-out;
904
+ }
905
+
906
+ @keyframes dshWcToast {
907
+ from { transform: translateY(8px); opacity: 0; }
908
+ to { transform: translateY(0); opacity: 1; }
909
+ }
910
+
911
+ /* --- accessibility --------------------------------------------------------------- */
912
+
913
+ .button:focus-visible,
914
+ .segmentedButton:focus-visible,
915
+ .toggleButton:focus-visible,
916
+ .sidebarRow:focus-visible,
917
+ .sidebarAction:focus-visible,
918
+ .sidebarActionDanger:focus-visible,
919
+ .sidebarRenameInput:focus-visible,
920
+ .composerInput:focus-visible {
921
+ outline: 2px solid var(--dsw-alias-state-business-primary, #4f6bfa);
922
+ outline-offset: 1px;
923
+ }
924
+
925
+ @media (prefers-reduced-motion: reduce) {
926
+ .statusDot[data-state='busy'] {
927
+ animation: none;
928
+ }
929
+ .toast {
930
+ animation: none;
931
+ }
932
+ .button,
933
+ .composer {
934
+ transition: none;
935
+ }
936
+ }