quassel 0.1.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.
package/css/chat.css ADDED
@@ -0,0 +1,927 @@
1
+ /* Chat-Bausteine auf Basis der foundation-Tokens. */
2
+ @import "./foundation/tokens.css";
3
+ /* Die Rückfrage-Karte (QuestionCard). */
4
+ @import "./question.css";
5
+
6
+ /* Eigenes Fundament, damit die Bausteine ohne foundation/base.css eines Hosts tragen. */
7
+ :is(.qsl-chat-panel, .qsl-chat-wrap, .qsl-input-card, .qsl-popover, .qsl-popover-backdrop),
8
+ :is(.qsl-chat-panel, .qsl-chat-wrap, .qsl-input-card, .qsl-popover, .qsl-popover-backdrop) * {
9
+ box-sizing: border-box;
10
+ }
11
+
12
+ /* Waagerechte Rollbalken bleiben sichtbar - sonst ist der Ueberlauf nicht auffindbar. */
13
+ :is(.qsl-md-scroll, .qsl-md pre) {
14
+ scrollbar-width: thin;
15
+ }
16
+
17
+ :is(.qsl-md-scroll, .qsl-md pre)::-webkit-scrollbar {
18
+ display: block;
19
+ height: 8px;
20
+ }
21
+
22
+ :is(.qsl-md-scroll, .qsl-md pre)::-webkit-scrollbar-thumb {
23
+ border-radius: 999px;
24
+ background: var(--qsl-line);
25
+ }
26
+
27
+ .qsl-chat-panel {
28
+ --qsl-thread-pad-bottom: calc(var(--qsl-composer-height, 0px) + 16px);
29
+ --qsl-jump-bottom: calc(var(--qsl-composer-height, 0px) + 12px);
30
+ position: relative;
31
+ display: flex;
32
+ flex-direction: column;
33
+ min-width: 0;
34
+ min-height: 0;
35
+ overflow: hidden;
36
+ }
37
+
38
+ .qsl-chat-panel__composer {
39
+ position: absolute;
40
+ z-index: 5;
41
+ right: 0;
42
+ bottom: 0;
43
+ left: 0;
44
+ }
45
+
46
+ .qsl-chat-wrap {
47
+ container: qsl-chat / inline-size;
48
+ position: relative;
49
+ flex: 1;
50
+ min-height: 0;
51
+ display: flex;
52
+ flex-direction: column;
53
+ }
54
+
55
+ .qsl-chat {
56
+ flex: 1;
57
+ min-height: 0;
58
+ overflow-y: auto;
59
+ font-family: var(--qsl-font);
60
+ font-size: var(--qsl-chat-font-size, 13px);
61
+ color: var(--qsl-ink);
62
+ }
63
+
64
+ .qsl-thread {
65
+ width: 100%;
66
+ max-width: var(--qsl-thread-max-width, none);
67
+ margin: 0 auto;
68
+ padding: 8px var(--qsl-thread-padding-inline, 0px) var(--qsl-thread-pad-bottom, 40px);
69
+ }
70
+
71
+ .qsl-step {
72
+ display: block;
73
+ margin-top: var(--qsl-message-gap, 16px);
74
+ }
75
+
76
+ .qsl-step--dense {
77
+ margin-top: var(--qsl-dense-message-gap, 8px);
78
+ }
79
+
80
+ /* Optionale Zeitspalte: schmal, tabellarische Ziffern, bewusst zurueckhaltend. */
81
+
82
+ .qsl-line {
83
+ display: flex;
84
+ gap: 8px;
85
+ align-items: flex-start;
86
+ margin-top: var(--qsl-message-gap, 16px);
87
+ }
88
+
89
+ /* Der Zeilenabstand haengt an der Zeile statt am ersten Schritt: so fluchtet die Zeit mit dem Text. */
90
+ .qsl-line:has(> .qsl-line-body > .qsl-step--dense:first-child) {
91
+ margin-top: var(--qsl-dense-message-gap, 8px);
92
+ }
93
+
94
+ .qsl-line > .qsl-line-body > .qsl-step:first-child {
95
+ margin-top: 0;
96
+ }
97
+
98
+ .qsl-line > .qsl-time {
99
+ flex-shrink: 0;
100
+ width: 34px;
101
+ padding-top: 2px;
102
+ color: var(--qsl-muted);
103
+ font-size: 10.5px;
104
+ font-variant-numeric: tabular-nums;
105
+ opacity: 0.85;
106
+ }
107
+
108
+ .qsl-line-body {
109
+ flex: 1;
110
+ min-width: 0;
111
+ }
112
+
113
+ .qsl-user {
114
+ display: flex;
115
+ justify-content: flex-end;
116
+ }
117
+
118
+ .qsl-user > div {
119
+ max-width: 86%;
120
+ padding: 8px 12px;
121
+ border-radius: var(--qsl-radius);
122
+ background: var(--qsl-accent);
123
+ color: #ffffff;
124
+ line-height: var(--qsl-chat-line-height, 1.625);
125
+ overflow-wrap: anywhere;
126
+ }
127
+
128
+ /* Farbige Sprechblasen (Mehrparteien): Seite und Farbe kommen aus Message.bubble. */
129
+
130
+ .qsl-bubble {
131
+ display: flex;
132
+ justify-content: flex-start;
133
+ }
134
+
135
+ .qsl-bubble--end {
136
+ justify-content: flex-end;
137
+ }
138
+
139
+ .qsl-bubble > div {
140
+ max-width: 86%;
141
+ padding: 8px 12px;
142
+ border-radius: var(--qsl-radius);
143
+ color: #ffffff;
144
+ line-height: var(--qsl-chat-line-height, 1.625);
145
+ overflow-wrap: anywhere;
146
+ }
147
+
148
+ .qsl-bubble__label {
149
+ display: table;
150
+ margin-bottom: 5px;
151
+ padding: 1px 7px;
152
+ border: 1px solid rgba(255, 255, 255, 0.32);
153
+ border-radius: 999px;
154
+ background: rgba(0, 0, 0, 0.14);
155
+ font-size: 0.72rem;
156
+ font-weight: 600;
157
+ letter-spacing: 0.02em;
158
+ line-height: 1.45;
159
+ }
160
+
161
+ /* Markdown in farbigen Bubbles: weiss auf Farbe, Code und Links bleiben lesbar. */
162
+
163
+ .qsl-user .qsl-md,
164
+ .qsl-bubble > div .qsl-md {
165
+ color: inherit;
166
+ }
167
+
168
+ .qsl-user .qsl-md-scroll,
169
+ .qsl-bubble > div .qsl-md-scroll {
170
+ --qsl-scroll-cover: var(--qsl-accent);
171
+ }
172
+
173
+ .qsl-user .qsl-md a,
174
+ .qsl-bubble > div .qsl-md a {
175
+ color: inherit;
176
+ text-decoration: underline;
177
+ }
178
+
179
+ .qsl-user .qsl-md code,
180
+ .qsl-bubble > div .qsl-md code {
181
+ background: rgba(255, 255, 255, 0.18);
182
+ color: inherit;
183
+ }
184
+
185
+ .qsl-user .qsl-md pre,
186
+ .qsl-bubble > div .qsl-md pre {
187
+ background: rgba(0, 0, 0, 0.28);
188
+ color: inherit;
189
+ }
190
+
191
+ .qsl-user .qsl-md blockquote,
192
+ .qsl-bubble > div .qsl-md blockquote {
193
+ border-left-color: rgba(255, 255, 255, 0.5);
194
+ color: inherit;
195
+ }
196
+
197
+ .qsl-user .qsl-md-bullet > span:first-child,
198
+ .qsl-bubble > div .qsl-md-bullet > span:first-child {
199
+ color: inherit;
200
+ }
201
+
202
+ .qsl-user .qsl-md th,
203
+ .qsl-bubble > div .qsl-md th {
204
+ background: rgba(255, 255, 255, 0.14);
205
+ color: inherit;
206
+ }
207
+
208
+ .qsl-user .qsl-md th,
209
+ .qsl-user .qsl-md td,
210
+ .qsl-bubble > div .qsl-md th,
211
+ .qsl-bubble > div .qsl-md td {
212
+ border-color: rgba(255, 255, 255, 0.35);
213
+ }
214
+
215
+ .qsl-answer {
216
+ max-width: 100%;
217
+ line-height: var(--qsl-chat-line-height, 1.625);
218
+ color: var(--qsl-ink);
219
+ }
220
+
221
+ .qsl-system {
222
+ padding-left: 12px;
223
+ border-left: 2px solid color-mix(in srgb, var(--qsl-danger) 50%, transparent);
224
+ font-size: 12px;
225
+ color: var(--qsl-muted);
226
+ }
227
+
228
+ .qsl-trace {
229
+ display: flex;
230
+ gap: 8px;
231
+ width: 100%;
232
+ padding: 0 0 0 24px;
233
+ border: 0;
234
+ background: none;
235
+ text-align: left;
236
+ font-family: inherit;
237
+ font-size: 11px;
238
+ line-height: 1.375;
239
+ color: var(--qsl-muted);
240
+ }
241
+
242
+ button.qsl-trace {
243
+ padding: 4px 8px 4px 24px;
244
+ border-radius: 6px;
245
+ cursor: pointer;
246
+ transition: background-color .12s ease, color .12s ease;
247
+ }
248
+
249
+ button.qsl-trace:hover,
250
+ button.qsl-trace:focus-visible {
251
+ background: var(--qsl-surface);
252
+ color: var(--qsl-ink);
253
+ outline: none;
254
+ }
255
+
256
+ .qsl-trace__icon {
257
+ flex: 0 0 auto;
258
+ margin-top: 2px;
259
+ opacity: .6;
260
+ }
261
+
262
+ .qsl-trace__icon--error {
263
+ color: var(--qsl-danger);
264
+ opacity: 1;
265
+ }
266
+
267
+ .qsl-trace__body {
268
+ min-width: 0;
269
+ flex: 1;
270
+ }
271
+
272
+ .qsl-trace__line {
273
+ min-width: 0;
274
+ font-family: var(--qsl-mono);
275
+ overflow-wrap: anywhere;
276
+ }
277
+
278
+ .qsl-trace--compact .qsl-trace__line {
279
+ overflow: hidden;
280
+ text-overflow: ellipsis;
281
+ white-space: nowrap;
282
+ }
283
+
284
+ .qsl-trace--thinking .qsl-trace__line {
285
+ font-family: inherit;
286
+ font-style: italic;
287
+ white-space: pre-wrap;
288
+ }
289
+
290
+ .qsl-trace--compact.qsl-trace--thinking .qsl-trace__line {
291
+ white-space: nowrap;
292
+ }
293
+
294
+ .qsl-trace__running {
295
+ margin-left: 6px;
296
+ opacity: .6;
297
+ }
298
+
299
+ /* --- Schritte gruppiert: Kopfzeile klappt die einzeiligen Schritte auf und zu --- */
300
+
301
+ button.qsl-stepgroup__toggle {
302
+ align-items: center;
303
+ padding-left: 4px;
304
+ }
305
+
306
+ .qsl-stepgroup__chevron {
307
+ flex: 0 0 auto;
308
+ opacity: .6;
309
+ transition: transform .12s ease;
310
+ }
311
+
312
+ .qsl-stepgroup__toggle[aria-expanded="true"] > .qsl-stepgroup__chevron {
313
+ transform: rotate(90deg);
314
+ }
315
+
316
+ .qsl-stepgroup__collapse > .qsl-stepgroup__chevron {
317
+ transform: rotate(-90deg);
318
+ }
319
+
320
+ .qsl-stepgroup__lines {
321
+ display: flex;
322
+ flex-direction: column;
323
+ gap: 2px;
324
+ margin-top: 2px;
325
+ }
326
+
327
+ /* --- Schritte als Chips: nebeneinander, Umbruch bei Platzmangel --- */
328
+
329
+ .qsl-steprow {
330
+ display: flex;
331
+ flex-wrap: wrap;
332
+ align-items: center;
333
+ gap: 4px 6px;
334
+ padding-left: 24px;
335
+ }
336
+
337
+ .qsl-chip {
338
+ display: inline-flex;
339
+ align-items: center;
340
+ gap: 5px;
341
+ max-width: 220px;
342
+ padding: 2px 9px;
343
+ border: 1px solid var(--qsl-line-soft);
344
+ border-radius: 999px;
345
+ background: none;
346
+ color: var(--qsl-muted);
347
+ font-family: var(--qsl-mono);
348
+ font-size: 10.5px;
349
+ line-height: 1.6;
350
+ cursor: pointer;
351
+ transition: background-color .12s ease, color .12s ease, border-color .12s ease;
352
+ }
353
+
354
+ span.qsl-chip--still,
355
+ span.qsl-chip--still:hover {
356
+ cursor: default;
357
+ background: none;
358
+ border-color: var(--qsl-line-soft);
359
+ color: var(--qsl-muted);
360
+ }
361
+
362
+ .qsl-chip:hover,
363
+ .qsl-chip:focus-visible {
364
+ background: var(--qsl-surface);
365
+ border-color: var(--qsl-line);
366
+ color: var(--qsl-ink);
367
+ outline: none;
368
+ }
369
+
370
+ .qsl-chip--icon {
371
+ padding: 4px;
372
+ }
373
+
374
+ .qsl-chip__label {
375
+ min-width: 0;
376
+ overflow: hidden;
377
+ text-overflow: ellipsis;
378
+ white-space: nowrap;
379
+ }
380
+
381
+ /* Fixed slot for the state mark, so a state change never resizes the chip. */
382
+ .qsl-chip__state {
383
+ flex: none;
384
+ display: inline-flex;
385
+ align-items: center;
386
+ justify-content: center;
387
+ width: 10px;
388
+ color: var(--qsl-success);
389
+ }
390
+
391
+ .qsl-chip__icon--error {
392
+ color: var(--qsl-danger);
393
+ }
394
+
395
+ /* The four step states; doubled class beats the border of the non-clickable chip. */
396
+
397
+ .qsl-chip.qsl-chip--running,
398
+ .qsl-chip.qsl-chip--thinking {
399
+ border-color: color-mix(in srgb, var(--qsl-accent) 40%, var(--qsl-line-soft));
400
+ }
401
+
402
+ .qsl-chip.qsl-chip--error {
403
+ border-color: color-mix(in srgb, var(--qsl-danger) 45%, var(--qsl-line-soft));
404
+ }
405
+
406
+ .qsl-chip--done {
407
+ opacity: .85;
408
+ }
409
+
410
+ .qsl-chip--done:hover,
411
+ .qsl-chip--done:focus-visible {
412
+ opacity: 1;
413
+ }
414
+
415
+ .qsl-icon-button--active {
416
+ color: var(--qsl-ink);
417
+ background: var(--qsl-surface);
418
+ }
419
+
420
+ .qsl-details {
421
+ margin-top: 2px;
422
+ padding-left: 8px;
423
+ border-left: 2px solid var(--qsl-line-soft);
424
+ }
425
+
426
+ .qsl-details pre {
427
+ margin: 0 0 2px;
428
+ max-height: 208px;
429
+ overflow: auto;
430
+ font-family: var(--qsl-mono);
431
+ font-size: 10.5px;
432
+ line-height: 1.375;
433
+ white-space: pre-wrap;
434
+ word-break: break-all;
435
+ opacity: .8;
436
+ }
437
+
438
+ .qsl-details pre.qsl-details__pre--error {
439
+ color: var(--qsl-danger);
440
+ opacity: 1;
441
+ }
442
+
443
+ .qsl-busy {
444
+ margin-top: var(--qsl-message-gap, 16px);
445
+ font-size: 11px;
446
+ color: var(--qsl-muted);
447
+ animation: qsl-pulse 1.9s cubic-bezier(0.2, 0.6, 0.35, 1) infinite;
448
+ }
449
+
450
+ /* Der Default-Working-Indikator: kleine Text-Szenen auf fester Buehne. */
451
+ .qsl-working {
452
+ margin-top: var(--qsl-message-gap, 16px);
453
+ display: flex;
454
+ align-items: center;
455
+ gap: 10px;
456
+ font-size: var(--qsl-chat-font-size, 13px);
457
+ color: var(--qsl-ink);
458
+ }
459
+
460
+ .qsl-working::before {
461
+ content: ">";
462
+ color: var(--qsl-muted);
463
+ opacity: 0.7;
464
+ }
465
+
466
+ .qsl-working__stage {
467
+ display: inline-block;
468
+ width: 15rem;
469
+ flex-shrink: 0;
470
+ font-family: ui-monospace, "SF Mono", monospace;
471
+ font-variant-ligatures: none;
472
+ }
473
+
474
+ /* Die Bewegung steckt im Text selbst - jede Szene zeichnet ihre Zeile neu. */
475
+ .qsl-working__character {
476
+ white-space: pre;
477
+ letter-spacing: 0.02em;
478
+ }
479
+
480
+ /* Eine Zelle je Figur: Emoji laufen breiter als Satzzeichen, ohne festes Raster
481
+ wuerde eine Figur bei jedem Bildwechsel vor- und zurueckspringen. */
482
+ .qsl-working__cell {
483
+ display: inline-block;
484
+ width: 1.35em;
485
+ text-align: center;
486
+ }
487
+
488
+ @media (prefers-reduced-motion: reduce) {
489
+ .qsl-working__character {
490
+ opacity: 0.6;
491
+ }
492
+ }
493
+
494
+ .qsl-jump {
495
+ pointer-events: none;
496
+ position: absolute;
497
+ right: 0;
498
+ bottom: var(--qsl-jump-bottom, 12px);
499
+ left: 0;
500
+ z-index: 10;
501
+ display: flex;
502
+ justify-content: center;
503
+ }
504
+
505
+ .qsl-jump button {
506
+ pointer-events: auto;
507
+ display: grid;
508
+ place-items: center;
509
+ width: 36px;
510
+ height: 36px;
511
+ border: 1px solid var(--qsl-line);
512
+ border-radius: 50%;
513
+ background: var(--qsl-content);
514
+ color: var(--qsl-muted);
515
+ cursor: pointer;
516
+ box-shadow: var(--qsl-shadow-bar);
517
+ transition: color .12s ease;
518
+ }
519
+
520
+ .qsl-jump button:hover {
521
+ color: var(--qsl-ink);
522
+ }
523
+
524
+ /* --- Popover fuer Denk- und Werkzeug-Details --- */
525
+
526
+ .qsl-popover-backdrop {
527
+ position: fixed;
528
+ inset: 0;
529
+ z-index: 30;
530
+ }
531
+
532
+ .qsl-popover {
533
+ position: fixed;
534
+ z-index: 40;
535
+ display: flex;
536
+ flex-direction: column;
537
+ max-height: min(70vh, 620px);
538
+ width: min(680px, calc(100vw - 32px));
539
+ overflow: hidden;
540
+ border: 1px solid var(--qsl-line);
541
+ border-radius: var(--qsl-radius-large);
542
+ background: var(--qsl-content);
543
+ box-shadow: var(--qsl-shadow-pop);
544
+ font-family: var(--qsl-font);
545
+ font-size: var(--qsl-chat-font-size, 13px);
546
+ color: var(--qsl-ink);
547
+ }
548
+
549
+ .qsl-popover__head {
550
+ display: flex;
551
+ align-items: center;
552
+ gap: 8px;
553
+ flex-shrink: 0;
554
+ padding: 10px 16px;
555
+ border-bottom: 1px solid var(--qsl-line-soft);
556
+ }
557
+
558
+ .qsl-popover__icon {
559
+ color: var(--qsl-muted);
560
+ }
561
+
562
+ .qsl-popover__icon--error {
563
+ color: var(--qsl-danger);
564
+ }
565
+
566
+ .qsl-popover__title {
567
+ min-width: 0;
568
+ flex: 1;
569
+ overflow: hidden;
570
+ text-overflow: ellipsis;
571
+ white-space: nowrap;
572
+ font-weight: 500;
573
+ }
574
+
575
+ .qsl-popover__close {
576
+ display: grid;
577
+ place-items: center;
578
+ width: 28px;
579
+ height: 28px;
580
+ flex-shrink: 0;
581
+ border: 0;
582
+ border-radius: var(--qsl-radius);
583
+ background: none;
584
+ color: var(--qsl-muted);
585
+ cursor: pointer;
586
+ transition: background-color .12s ease, color .12s ease;
587
+ }
588
+
589
+ .qsl-popover__close:hover {
590
+ background: var(--qsl-surface);
591
+ color: var(--qsl-ink);
592
+ }
593
+
594
+ .qsl-popover__body {
595
+ min-height: 0;
596
+ overflow: auto;
597
+ padding: 16px;
598
+ }
599
+
600
+ .qsl-popover__thinking {
601
+ white-space: pre-wrap;
602
+ font-size: 12px;
603
+ line-height: var(--qsl-chat-line-height, 1.625);
604
+ }
605
+
606
+ .qsl-popover__sections {
607
+ display: flex;
608
+ flex-direction: column;
609
+ gap: 16px;
610
+ }
611
+
612
+ .qsl-popover__toolname {
613
+ font-family: var(--qsl-mono);
614
+ font-size: 12px;
615
+ }
616
+
617
+ .qsl-popover__sections h3 {
618
+ margin: 0 0 6px;
619
+ font-size: 11px;
620
+ font-weight: 600;
621
+ text-transform: uppercase;
622
+ letter-spacing: .04em;
623
+ color: var(--qsl-muted);
624
+ }
625
+
626
+ .qsl-popover__pre {
627
+ margin: 0;
628
+ padding: 12px;
629
+ border-radius: var(--qsl-radius);
630
+ background: var(--qsl-surface);
631
+ font-family: var(--qsl-mono);
632
+ font-size: 11px;
633
+ line-height: var(--qsl-chat-line-height, 1.625);
634
+ white-space: pre-wrap;
635
+ word-break: break-all;
636
+ }
637
+
638
+ .qsl-popover__pre--error {
639
+ color: var(--qsl-danger);
640
+ }
641
+
642
+ .qsl-popover__running {
643
+ font-size: 12px;
644
+ color: var(--qsl-muted);
645
+ }
646
+
647
+ /* --- Eingabe-Karte: Textarea oben, Toolbar unter der Haarlinie --- */
648
+
649
+ .qsl-input-card {
650
+ display: flex;
651
+ flex-direction: column;
652
+ flex-shrink: 0;
653
+ border: 1px solid var(--qsl-line);
654
+ border-radius: var(--qsl-input-card-radius, var(--qsl-radius-large));
655
+ background: var(--qsl-content);
656
+ overflow: hidden;
657
+ font-family: var(--qsl-font);
658
+ transition: border-color .12s ease;
659
+ }
660
+
661
+ .qsl-input-card:focus-within {
662
+ border-color: var(--qsl-accent);
663
+ }
664
+
665
+ .qsl-input-card textarea {
666
+ resize: none;
667
+ border: none;
668
+ background: transparent;
669
+ padding: 12px 12px 8px;
670
+ color: var(--qsl-ink);
671
+ font: inherit;
672
+ font-size: var(--qsl-chat-font-size, 13px);
673
+ line-height: var(--qsl-chat-line-height, 1.5);
674
+ }
675
+
676
+ .qsl-input-card textarea:focus {
677
+ outline: none;
678
+ }
679
+
680
+ .qsl-input-card textarea::placeholder {
681
+ color: var(--qsl-muted);
682
+ }
683
+
684
+ /* Eine Zeile, die nie umbricht: links waechst, rechts schrumpft, Senden bleibt immer da. */
685
+ .qsl-input-card__toolbar {
686
+ display: flex;
687
+ flex-wrap: nowrap;
688
+ align-items: center;
689
+ gap: 8px;
690
+ padding: 6px 8px;
691
+ border-top: 1px solid var(--qsl-line-soft);
692
+ }
693
+
694
+ .qsl-input-card__left {
695
+ flex: 1 1 auto;
696
+ }
697
+
698
+ .qsl-input-card__right {
699
+ flex: 0 1 auto;
700
+ }
701
+
702
+ .qsl-input-card__left,
703
+ .qsl-input-card__right {
704
+ display: flex;
705
+ align-items: center;
706
+ gap: 8px;
707
+ min-width: 0;
708
+ }
709
+
710
+ .qsl-input-card--compact .qsl-collapsible {
711
+ display: none;
712
+ }
713
+
714
+ .qsl-icon-button {
715
+ display: inline-flex;
716
+ align-items: center;
717
+ justify-content: center;
718
+ gap: 5px;
719
+ height: 28px;
720
+ min-width: 28px;
721
+ padding: 0 5px;
722
+ flex-shrink: 0;
723
+ border: none;
724
+ border-radius: var(--qsl-radius);
725
+ background: transparent;
726
+ color: var(--qsl-muted);
727
+ font: inherit;
728
+ font-size: 11px;
729
+ cursor: pointer;
730
+ transition: background-color .12s ease, color .12s ease;
731
+ }
732
+
733
+ .qsl-icon-button:hover:not(:disabled) {
734
+ color: var(--qsl-ink);
735
+ background: var(--qsl-surface);
736
+ }
737
+
738
+ .qsl-icon-button:disabled {
739
+ opacity: 0.4;
740
+ cursor: default;
741
+ }
742
+
743
+ .qsl-send {
744
+ display: inline-flex;
745
+ align-items: center;
746
+ justify-content: center;
747
+ width: 28px;
748
+ height: 28px;
749
+ flex-shrink: 0;
750
+ border: none;
751
+ border-radius: 999px;
752
+ background: var(--qsl-accent);
753
+ color: #ffffff;
754
+ cursor: pointer;
755
+ transition: opacity .12s ease;
756
+ }
757
+
758
+ .qsl-send:disabled {
759
+ opacity: 0.35;
760
+ cursor: default;
761
+ }
762
+
763
+ .qsl-send--stop {
764
+ background: var(--qsl-surface);
765
+ color: var(--qsl-ink);
766
+ }
767
+
768
+ /* --- Markdown --- */
769
+
770
+ .qsl-md > *:first-child {
771
+ margin-top: 0;
772
+ }
773
+
774
+ /* h1/h2 mitnehmen, sonst erbt der Chat die Ueberschriften der Gastseite. */
775
+ .qsl-md h1,
776
+ .qsl-md h2,
777
+ .qsl-md h3 {
778
+ margin: 12px 0 4px;
779
+ font-family: inherit;
780
+ font-size: 15px;
781
+ font-weight: 500;
782
+ }
783
+
784
+ .qsl-md h1 {
785
+ font-size: 16px;
786
+ font-weight: 600;
787
+ }
788
+
789
+ .qsl-md h4 {
790
+ margin: 8px 0 4px;
791
+ font-size: 14px;
792
+ font-weight: 500;
793
+ }
794
+
795
+ .qsl-md p {
796
+ margin: 0;
797
+ }
798
+
799
+ .qsl-md-gap {
800
+ height: 8px;
801
+ }
802
+
803
+ .qsl-md-bullet {
804
+ display: flex;
805
+ gap: 8px;
806
+ }
807
+
808
+ .qsl-md-bullet > span:first-child {
809
+ color: var(--qsl-muted);
810
+ font-variant-numeric: tabular-nums;
811
+ }
812
+
813
+ .qsl-md-bullet > span:last-child {
814
+ min-width: 0;
815
+ }
816
+
817
+ .qsl-md blockquote {
818
+ margin: 4px 0;
819
+ padding-left: 12px;
820
+ border-left: 2px solid var(--qsl-line);
821
+ color: var(--qsl-muted);
822
+ }
823
+
824
+ .qsl-md a {
825
+ color: var(--qsl-accent);
826
+ text-underline-offset: 2px;
827
+ }
828
+
829
+ .qsl-md strong {
830
+ font-weight: 500;
831
+ }
832
+
833
+ .qsl-md code {
834
+ padding: 2px 4px;
835
+ border-radius: var(--qsl-radius-small);
836
+ background: var(--qsl-surface);
837
+ font-family: var(--qsl-mono);
838
+ font-size: 12px;
839
+ overflow-wrap: anywhere;
840
+ }
841
+
842
+ .qsl-md pre {
843
+ margin: 8px 0;
844
+ padding: 8px 12px;
845
+ border-radius: var(--qsl-radius);
846
+ background: var(--qsl-surface);
847
+ font-family: var(--qsl-mono);
848
+ font-size: 12px;
849
+ line-height: var(--qsl-chat-line-height, 1.625);
850
+ overflow-x: auto;
851
+ }
852
+
853
+ .qsl-md pre code {
854
+ padding: 0;
855
+ background: none;
856
+ }
857
+
858
+ .qsl-md-scroll {
859
+ margin: 8px 0;
860
+ overflow-x: auto;
861
+ background:
862
+ linear-gradient(to right, var(--qsl-scroll-cover, var(--qsl-content)) 50%, transparent) left / 20px 100% no-repeat local,
863
+ linear-gradient(to left, var(--qsl-scroll-cover, var(--qsl-content)) 50%, transparent) right / 20px 100% no-repeat local,
864
+ linear-gradient(to right, rgba(0, 0, 0, 0.28), transparent) left / 14px 100% no-repeat scroll,
865
+ linear-gradient(to left, rgba(0, 0, 0, 0.28), transparent) right / 14px 100% no-repeat scroll;
866
+ }
867
+
868
+ /* Ohne max-content schrumpft die Tabelle auf den Container und stapelt Buchstaben. */
869
+ .qsl-md table {
870
+ width: max-content;
871
+ min-width: 100%;
872
+ border-collapse: collapse;
873
+ font-size: 12px;
874
+ }
875
+
876
+ .qsl-md th,
877
+ .qsl-md td {
878
+ max-width: 44ch;
879
+ border: 1px solid var(--qsl-line);
880
+ padding: 4px 8px;
881
+ text-align: left;
882
+ vertical-align: top;
883
+ }
884
+
885
+ .qsl-md th {
886
+ background: var(--qsl-surface);
887
+ font-weight: 500;
888
+ }
889
+
890
+ @container qsl-chat (max-width: 620px) {
891
+ .qsl-user > div,
892
+ .qsl-bubble > div,
893
+ .qsl-answer {
894
+ max-width: 100%;
895
+ }
896
+ }
897
+
898
+ .qsl-message-actions {
899
+ display: flex;
900
+ flex-wrap: wrap;
901
+ align-items: center;
902
+ gap: 4px;
903
+ margin-top: 4px;
904
+ }
905
+
906
+ .qsl-action-error {
907
+ color: var(--qsl-danger);
908
+ font-size: 12px;
909
+ overflow-wrap: anywhere;
910
+ }
911
+
912
+ .qsl-day-separator {
913
+ margin-top: var(--qsl-message-gap, 16px);
914
+ padding: 8px 0;
915
+ border-bottom: 1px solid var(--qsl-line-soft);
916
+ color: var(--qsl-muted);
917
+ font-size: 12px;
918
+ text-align: center;
919
+ }
920
+
921
+ .qsl-code-block {
922
+ margin: 8px 0;
923
+ }
924
+
925
+ .qsl-code-block > pre {
926
+ margin-top: 4px;
927
+ }