返回 CodeWhale
styles.css
根目录 / crates / tui / src / runtime_web / styles.css
1 :root {
2 color-scheme: dark;
3 --ink-0: #03070d;
4 --ink-1: #08111c;
5 --ink-2: #0e1729;
6 --ink-3: #182742;
7 --line: rgba(106, 174, 242, 0.18);
8 --line-strong: rgba(106, 174, 242, 0.32);
9 --text: #f6f2e8;
10 --text-soft: #b6c0d4;
11 --text-dim: #93a0b8;
12 --action: #6aaef2;
13 --action-strong: #4f8fe8;
14 --action-wash: rgba(106, 174, 242, 0.11);
15 --human: #f6c453;
16 --human-wash: rgba(246, 196, 83, 0.11);
17 --live: #4fd1c5;
18 --live-wash: rgba(79, 209, 197, 0.12);
19 --warning: #ff7a59;
20 --danger: #ff86b2;
21 --ok: #9bd66f;
22 --rail: 248px;
23 font-family: Inter, ui-sans-serif, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
24 font-synthesis: none;
25 text-rendering: optimizeLegibility;
26 }
27
28 * {
29 box-sizing: border-box;
30 }
31
32 html,
33 body {
34 width: 100%;
35 min-width: 280px;
36 height: 100%;
37 margin: 0;
38 overflow: hidden;
39 background: var(--ink-0);
40 color: var(--text);
41 }
42
43 button,
44 input,
45 textarea {
46 color: inherit;
47 font: inherit;
48 }
49
50 button {
51 cursor: pointer;
52 }
53
54 button:disabled,
55 textarea:disabled {
56 cursor: not-allowed;
57 opacity: 0.45;
58 }
59
60 button:focus-visible,
61 input:focus-visible,
62 textarea:focus-visible,
63 summary:focus-visible {
64 outline: 2px solid var(--action);
65 outline-offset: 2px;
66 }
67
68 .shell {
69 display: grid;
70 grid-template-columns: var(--rail) minmax(0, 1fr);
71 width: 100%;
72 height: 100%;
73 background:
74 radial-gradient(circle at 73% -20%, rgba(49, 95, 216, 0.14), transparent 42%),
75 var(--ink-0);
76 }
77
78 .rail {
79 position: relative;
80 z-index: 20;
81 display: grid;
82 grid-template-rows: auto auto auto minmax(0, 1fr) auto;
83 gap: 14px;
84 min-width: 0;
85 min-height: 0;
86 padding: 18px 14px 12px;
87 border-right: 1px solid var(--line);
88 background: rgba(8, 17, 28, 0.96);
89 }
90
91 .rail-brand,
92 .session-heading,
93 .header-actions,
94 .composer-bar,
95 .composer-actions,
96 .rail-footer,
97 .fact-chip,
98 .thread-title-row,
99 .attention-actions,
100 .dialog-actions {
101 display: flex;
102 align-items: center;
103 }
104
105 .rail-brand {
106 min-width: 0;
107 gap: 10px;
108 }
109
110 .brand-mark {
111 display: grid;
112 flex: 0 0 34px;
113 width: 34px;
114 height: 34px;
115 place-items: center;
116 border: 1px solid rgba(246, 196, 83, 0.42);
117 border-radius: 10px;
118 background: var(--human-wash);
119 color: var(--human);
120 font-size: 11px;
121 font-weight: 800;
122 letter-spacing: 0.05em;
123 }
124
125 .eyebrow {
126 margin: 0;
127 color: var(--text-dim);
128 font-size: 10px;
129 font-weight: 700;
130 letter-spacing: 0.11em;
131 line-height: 1.3;
132 text-transform: uppercase;
133 }
134
135 .brand-name {
136 margin: 2px 0 0;
137 font-size: 15px;
138 font-weight: 680;
139 letter-spacing: -0.01em;
140 }
141
142 .primary-button,
143 .quiet-button,
144 .send-button,
145 .icon-button {
146 min-height: 34px;
147 border: 1px solid var(--line);
148 border-radius: 9px;
149 background: var(--ink-2);
150 color: var(--text-soft);
151 font-size: 12px;
152 font-weight: 650;
153 }
154
155 .primary-button,
156 .send-button {
157 border-color: rgba(106, 174, 242, 0.46);
158 background: var(--action);
159 color: var(--ink-0);
160 }
161
162 .primary-button:hover,
163 .send-button:hover {
164 background: var(--action-strong);
165 }
166
167 .quiet-button {
168 padding: 0 11px;
169 }
170
171 .quiet-button:hover,
172 .icon-button:hover {
173 border-color: var(--line-strong);
174 background: var(--ink-3);
175 color: var(--text);
176 }
177
178 .quiet-button.danger:hover {
179 border-color: rgba(255, 134, 178, 0.42);
180 color: var(--danger);
181 }
182
183 .icon-button {
184 display: inline-grid;
185 width: 34px;
186 min-width: 34px;
187 padding: 0;
188 place-items: center;
189 }
190
191 .new-thread {
192 display: flex;
193 width: 100%;
194 align-items: center;
195 justify-content: center;
196 gap: 7px;
197 }
198
199 .search-field {
200 display: flex;
201 min-width: 0;
202 height: 36px;
203 align-items: center;
204 gap: 8px;
205 padding: 0 10px;
206 border: 1px solid var(--line);
207 border-radius: 9px;
208 background: rgba(3, 7, 13, 0.55);
209 color: var(--text-dim);
210 }
211
212 .search-field:focus-within {
213 border-color: rgba(106, 174, 242, 0.46);
214 }
215
216 .search-field input {
217 width: 100%;
218 min-width: 0;
219 border: 0;
220 outline: 0;
221 background: transparent;
222 color: var(--text);
223 font-size: 12px;
224 }
225
226 .search-field input::placeholder,
227 textarea::placeholder {
228 color: var(--text-dim);
229 }
230
231 .thread-list {
232 min-height: 0;
233 margin: 0 -4px;
234 overflow-x: hidden;
235 overflow-y: auto;
236 overscroll-behavior: contain;
237 scrollbar-color: var(--ink-3) transparent;
238 }
239
240 .thread-row {
241 display: block;
242 width: 100%;
243 margin: 2px 0;
244 padding: 10px;
245 overflow: hidden;
246 border: 1px solid transparent;
247 border-radius: 9px;
248 background: transparent;
249 color: var(--text-soft);
250 text-align: left;
251 }
252
253 .thread-row:hover {
254 background: rgba(24, 39, 66, 0.62);
255 color: var(--text);
256 }
257
258 .thread-row[aria-current="true"] {
259 border-color: rgba(106, 174, 242, 0.32);
260 background: var(--action-wash);
261 color: var(--text);
262 }
263
264 .thread-title-row {
265 min-width: 0;
266 justify-content: space-between;
267 gap: 8px;
268 }
269
270 .thread-title {
271 overflow: hidden;
272 font-size: 12px;
273 font-weight: 650;
274 text-overflow: ellipsis;
275 white-space: nowrap;
276 }
277
278 .thread-preview {
279 display: -webkit-box;
280 margin-top: 5px;
281 overflow: hidden;
282 color: var(--text-dim);
283 font-size: 10px;
284 line-height: 1.45;
285 -webkit-box-orient: vertical;
286 -webkit-line-clamp: 2;
287 }
288
289 .thread-meta {
290 margin-top: 7px;
291 overflow: hidden;
292 color: var(--text-dim);
293 font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
294 font-size: 9px;
295 text-overflow: ellipsis;
296 white-space: nowrap;
297 }
298
299 .status-pip {
300 flex: 0 0 6px;
301 width: 6px;
302 height: 6px;
303 border-radius: 50%;
304 background: var(--text-dim);
305 }
306
307 .status-pip.running {
308 background: var(--live);
309 box-shadow: 0 0 0 3px var(--live-wash);
310 }
311
312 .status-pip.failed {
313 background: var(--danger);
314 }
315
316 /* Read-only peek for a saved session. Visually distinct from the live
317 transcript so a recording is never mistaken for a running thread. */
318 .session-peek {
319 min-height: 0;
320 overflow-y: auto;
321 padding: 14px 16px;
322 border-bottom: 1px solid var(--line);
323 }
324
325 .session-peek[hidden] {
326 display: none;
327 }
328
329 .peek-entry {
330 display: flex;
331 flex-wrap: wrap;
332 gap: 6px;
333 align-items: baseline;
334 padding: 6px 0;
335 border-top: 1px solid var(--line);
336 }
337
338 .peek-kind {
339 min-width: 72px;
340 color: var(--text-dim);
341 font-size: 10px;
342 text-transform: uppercase;
343 letter-spacing: 0.06em;
344 }
345
346 .peek-text {
347 flex: 1 1 240px;
348 min-width: 0;
349 margin: 0;
350 overflow-wrap: anywhere;
351 }
352
353 .peek-flag,
354 .peek-omitted {
355 color: var(--text-dim);
356 font-size: 10px;
357 }
358
359 /* Narrow viewports: the peek must not force horizontal scrolling, and the
360 kind label stacks above its text rather than squeezing it. */
361 @media (max-width: 480px) {
362 .session-peek {
363 padding: 10px 12px;
364 max-height: 45vh;
365 }
366
367 .peek-entry {
368 flex-direction: column;
369 gap: 2px;
370 }
371
372 .peek-text {
373 flex-basis: auto;
374 }
375
376 .saved-sessions {
377 max-height: 30%;
378 }
379 }
380
381 /* Saved sessions sit under the live thread list and are capped so a large
382 session store cannot push the connection footer off-screen. */
383 .saved-sessions {
384 min-height: 0;
385 max-height: 34%;
386 display: flex;
387 flex-direction: column;
388 padding-top: 9px;
389 border-top: 1px solid var(--line);
390 }
391
392 .saved-sessions[hidden] {
393 display: none;
394 }
395
396 .rail-section-title {
397 margin: 0 0 6px;
398 color: var(--text-dim);
399 font-size: 10px;
400 font-weight: 600;
401 letter-spacing: 0.08em;
402 text-transform: uppercase;
403 }
404
405 .rail-footer {
406 min-width: 0;
407 gap: 8px;
408 padding: 9px 4px 0;
409 border-top: 1px solid var(--line);
410 color: var(--text-dim);
411 font-size: 10px;
412 }
413
414 .connection-dot {
415 flex: 0 0 7px;
416 width: 7px;
417 height: 7px;
418 border-radius: 50%;
419 background: var(--action);
420 }
421
422 .connection-dot.ready {
423 background: var(--ok);
424 }
425
426 .connection-dot.error {
427 background: var(--danger);
428 }
429
430 .session {
431 display: grid;
432 grid-template-areas:
433 "header"
434 "status"
435 "transcript"
436 "attention"
437 "composer";
438 grid-template-rows: auto auto minmax(0, 1fr) auto auto;
439 min-width: 0;
440 min-height: 0;
441 }
442
443 .session-header {
444 display: grid;
445 grid-area: header;
446 grid-template-columns: minmax(0, 1fr) auto;
447 gap: 12px 20px;
448 padding: 15px 24px 13px;
449 border-bottom: 1px solid var(--line);
450 background: rgba(8, 17, 28, 0.72);
451 backdrop-filter: blur(14px);
452 }
453
454 .session-heading {
455 min-width: 0;
456 gap: 10px;
457 }
458
459 .title-stack {
460 min-width: 0;
461 }
462
463 .session-header h1 {
464 margin: 3px 0 0;
465 overflow: hidden;
466 font-size: 16px;
467 font-weight: 680;
468 letter-spacing: -0.015em;
469 line-height: 1.25;
470 text-overflow: ellipsis;
471 white-space: nowrap;
472 }
473
474 .header-actions {
475 align-self: center;
476 gap: 7px;
477 }
478
479 .session-facts {
480 display: flex;
481 grid-column: 1 / -1;
482 min-width: 0;
483 gap: 6px;
484 overflow-x: auto;
485 scrollbar-width: none;
486 }
487
488 .session-facts::-webkit-scrollbar {
489 display: none;
490 }
491
492 .fact-chip {
493 flex: 0 0 auto;
494 min-height: 23px;
495 gap: 5px;
496 padding: 0 8px;
497 border: 1px solid var(--line);
498 border-radius: 999px;
499 color: var(--text-dim);
500 font-size: 9px;
501 white-space: nowrap;
502 }
503
504 .fact-chip strong {
505 color: var(--text-soft);
506 font-weight: 650;
507 }
508
509 .rail-open,
510 .rail-close,
511 .rail-scrim {
512 display: none;
513 }
514
515 .status-banner {
516 grid-area: status;
517 margin: 12px auto 0;
518 width: min(760px, calc(100% - 32px));
519 padding: 9px 12px;
520 border: 1px solid rgba(255, 122, 89, 0.34);
521 border-radius: 9px;
522 background: rgba(48, 25, 21, 0.48);
523 color: var(--warning);
524 font-size: 11px;
525 line-height: 1.45;
526 }
527
528 .transcript {
529 grid-area: transcript;
530 min-height: 0;
531 padding: 30px max(16px, calc((100% - 760px) / 2)) 48px;
532 overflow-x: hidden;
533 overflow-y: auto;
534 overscroll-behavior: contain;
535 scroll-behavior: smooth;
536 scrollbar-color: var(--ink-3) transparent;
537 }
538
539 .empty-state {
540 display: grid;
541 min-height: 100%;
542 place-content: center;
543 justify-items: center;
544 padding: 32px;
545 color: var(--text-dim);
546 text-align: center;
547 }
548
549 .empty-state h2 {
550 margin: 14px 0 7px;
551 color: var(--text);
552 font-size: 19px;
553 letter-spacing: -0.02em;
554 }
555
556 .empty-state p {
557 max-width: 430px;
558 margin: 0;
559 font-size: 12px;
560 line-height: 1.6;
561 }
562
563 .empty-orbit {
564 color: var(--human);
565 font-size: 34px;
566 }
567
568 .message,
569 .receipt,
570 .reasoning {
571 width: 100%;
572 margin: 0 0 14px;
573 }
574
575 .message-label,
576 .receipt-label {
577 margin-bottom: 6px;
578 color: var(--text-dim);
579 font-size: 9px;
580 font-weight: 700;
581 letter-spacing: 0.08em;
582 text-transform: uppercase;
583 }
584
585 .message-body {
586 max-width: 100%;
587 padding: 13px 15px;
588 border: 1px solid var(--line);
589 border-radius: 12px;
590 background: rgba(14, 23, 41, 0.74);
591 color: var(--text);
592 font-size: 13px;
593 line-height: 1.65;
594 overflow-wrap: anywhere;
595 white-space: pre-wrap;
596 }
597
598 .message.user .message-body {
599 margin-left: clamp(0px, 8vw, 72px);
600 border-color: rgba(246, 196, 83, 0.22);
601 background: rgba(246, 196, 83, 0.07);
602 }
603
604 .message.in-progress .message-body::after {
605 content: "";
606 display: inline-block;
607 width: 5px;
608 height: 12px;
609 margin-left: 4px;
610 background: var(--live);
611 vertical-align: -1px;
612 animation: cursor-pulse 1s steps(2, end) infinite;
613 }
614
615 @keyframes cursor-pulse {
616 50% { opacity: 0; }
617 }
618
619 .receipt {
620 padding: 10px 12px;
621 border: 1px solid var(--line);
622 border-radius: 10px;
623 background: rgba(8, 17, 28, 0.54);
624 }
625
626 .receipt.failed {
627 border-color: rgba(255, 134, 178, 0.28);
628 }
629
630 .receipt-summary {
631 color: var(--text-soft);
632 font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
633 font-size: 10px;
634 line-height: 1.5;
635 overflow-wrap: anywhere;
636 }
637
638 .receipt details,
639 .reasoning details {
640 margin-top: 7px;
641 }
642
643 .receipt summary,
644 .reasoning summary {
645 color: var(--text-dim);
646 cursor: pointer;
647 font-size: 10px;
648 }
649
650 .receipt pre,
651 .reasoning pre {
652 max-height: 280px;
653 margin: 8px 0 0;
654 padding: 10px;
655 overflow: auto;
656 border-radius: 7px;
657 background: var(--ink-0);
658 color: var(--text-soft);
659 font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
660 font-size: 10px;
661 line-height: 1.55;
662 white-space: pre-wrap;
663 }
664
665 .reasoning {
666 padding-left: 12px;
667 border-left: 1px solid var(--line);
668 }
669
670 .attention {
671 grid-area: attention;
672 width: min(760px, calc(100% - 32px));
673 max-height: 42vh;
674 margin: 0 auto 10px;
675 overflow-y: auto;
676 }
677
678 .attention-card {
679 margin-top: 8px;
680 padding: 13px;
681 border: 1px solid rgba(246, 196, 83, 0.34);
682 border-radius: 11px;
683 background: rgba(42, 34, 24, 0.56);
684 }
685
686 .attention-card h2 {
687 margin: 4px 0 5px;
688 font-size: 13px;
689 }
690
691 .attention-card p {
692 margin: 0;
693 color: var(--text-soft);
694 font-size: 11px;
695 line-height: 1.55;
696 }
697
698 .attention-actions {
699 flex-wrap: wrap;
700 gap: 8px;
701 margin-top: 12px;
702 }
703
704 .remember-field,
705 .answer-option {
706 display: flex;
707 align-items: flex-start;
708 gap: 8px;
709 color: var(--text-soft);
710 font-size: 10px;
711 }
712
713 .question-fieldset {
714 margin: 12px 0 0;
715 padding: 10px;
716 border: 1px solid var(--line);
717 border-radius: 9px;
718 }
719
720 .question-fieldset legend {
721 padding: 0 5px;
722 color: var(--text);
723 font-size: 11px;
724 font-weight: 650;
725 }
726
727 .answer-option {
728 margin: 7px 0;
729 }
730
731 .answer-option small {
732 display: block;
733 margin-top: 2px;
734 color: var(--text-dim);
735 line-height: 1.4;
736 }
737
738 .other-answer,
739 .rename-dialog input {
740 width: 100%;
741 min-height: 36px;
742 margin-top: 7px;
743 padding: 0 10px;
744 border: 1px solid var(--line);
745 border-radius: 8px;
746 outline: 0;
747 background: var(--ink-0);
748 }
749
750 .composer-wrap {
751 grid-area: composer;
752 padding: 0 max(16px, calc((100% - 760px) / 2)) 18px;
753 background: linear-gradient(transparent, var(--ink-0) 24%);
754 }
755
756 .composer {
757 overflow: hidden;
758 border: 1px solid var(--line-strong);
759 border-radius: 13px;
760 background: rgba(14, 23, 41, 0.96);
761 box-shadow: 0 18px 60px rgba(0, 0, 0, 0.3);
762 }
763
764 .composer:focus-within {
765 border-color: rgba(106, 174, 242, 0.46);
766 }
767
768 .composer textarea {
769 display: block;
770 width: 100%;
771 min-height: 54px;
772 max-height: 180px;
773 padding: 13px 14px 6px;
774 resize: none;
775 overflow-y: auto;
776 border: 0;
777 outline: 0;
778 background: transparent;
779 color: var(--text);
780 font-size: 13px;
781 line-height: 1.5;
782 }
783
784 .composer-bar {
785 justify-content: space-between;
786 gap: 12px;
787 padding: 6px 7px 7px 13px;
788 }
789
790 .composer-hint {
791 color: var(--text-dim);
792 font-size: 9px;
793 }
794
795 .composer-actions {
796 gap: 7px;
797 }
798
799 .send-button {
800 min-width: 68px;
801 padding: 0 13px;
802 }
803
804 .rename-dialog {
805 width: min(420px, calc(100% - 28px));
806 padding: 0;
807 border: 1px solid var(--line-strong);
808 border-radius: 14px;
809 background: var(--ink-2);
810 color: var(--text);
811 box-shadow: 0 24px 80px rgba(0, 0, 0, 0.55);
812 }
813
814 .rename-dialog::backdrop {
815 background: rgba(3, 7, 13, 0.74);
816 }
817
818 .rename-dialog form {
819 padding: 20px;
820 }
821
822 .rename-dialog h2 {
823 margin: 5px 0 15px;
824 font-size: 17px;
825 }
826
827 .dialog-actions {
828 justify-content: flex-end;
829 gap: 8px;
830 margin-top: 16px;
831 }
832
833 .dialog-actions .primary-button {
834 padding: 0 13px;
835 }
836
837 .sr-only {
838 position: absolute;
839 width: 1px;
840 height: 1px;
841 padding: 0;
842 overflow: hidden;
843 clip: rect(0, 0, 0, 0);
844 white-space: nowrap;
845 border: 0;
846 }
847
848 [hidden] {
849 display: none !important;
850 }
851
852 noscript {
853 position: fixed;
854 inset: 0;
855 z-index: 100;
856 display: grid;
857 place-items: center;
858 padding: 24px;
859 background: var(--ink-0);
860 color: var(--text);
861 }
862
863 @media (max-width: 800px) {
864 .shell {
865 grid-template-columns: minmax(0, 1fr);
866 }
867
868 .rail {
869 position: fixed;
870 inset: 0 auto 0 0;
871 width: min(86vw, 320px);
872 transform: translateX(-102%);
873 box-shadow: 24px 0 70px rgba(0, 0, 0, 0.45);
874 transition: transform 160ms ease-out;
875 }
876
877 .rail-open,
878 .rail-close {
879 display: inline-grid;
880 }
881
882 .rail-close {
883 margin-left: auto;
884 }
885
886 .rail-scrim {
887 position: fixed;
888 inset: 0;
889 z-index: 15;
890 display: block;
891 visibility: hidden;
892 width: 100%;
893 height: 100%;
894 padding: 0;
895 border: 0;
896 opacity: 0;
897 background: rgba(3, 7, 13, 0.64);
898 transition: opacity 160ms ease-out, visibility 160ms step-end;
899 }
900
901 .rail-visible .rail {
902 transform: translateX(0);
903 }
904
905 .rail-visible .rail-scrim {
906 visibility: visible;
907 opacity: 1;
908 transition: opacity 160ms ease-out;
909 }
910
911 .session-header {
912 padding: 12px 14px 10px;
913 }
914
915 .session-header h1 {
916 font-size: 14px;
917 }
918
919 .header-actions {
920 gap: 4px;
921 }
922
923 .header-actions .quiet-button {
924 padding: 0 8px;
925 }
926
927 .transcript {
928 padding-top: 20px;
929 padding-bottom: 34px;
930 }
931
932 .composer-wrap {
933 padding-bottom: max(10px, env(safe-area-inset-bottom));
934 }
935
936 .message.user .message-body {
937 margin-left: 20px;
938 }
939 }
940
941 @media (max-width: 430px) {
942 .session-header {
943 grid-template-columns: minmax(0, 1fr);
944 gap: 8px;
945 }
946
947 .header-actions {
948 position: absolute;
949 top: 10px;
950 right: 10px;
951 }
952
953 .header-actions .quiet-button {
954 width: 34px;
955 padding: 0;
956 overflow: hidden;
957 color: transparent;
958 font-size: 0;
959 }
960
961 #rename-thread::after {
962 content: "✎";
963 color: var(--text-soft);
964 font-size: 13px;
965 }
966
967 #archive-thread::after {
968 content: "⌫";
969 color: var(--danger);
970 font-size: 13px;
971 }
972
973 .session-heading {
974 padding-right: 78px;
975 }
976
977 .session-facts {
978 grid-column: 1;
979 }
980
981 .composer-hint {
982 display: none;
983 }
984
985 .composer-bar {
986 justify-content: flex-end;
987 }
988 }
989
990 @media (prefers-reduced-motion: reduce) {
991 *,
992 *::before,
993 *::after {
994 scroll-behavior: auto !important;
995 transition-duration: 0.01ms !important;
996 animation-duration: 0.01ms !important;
997 animation-iteration-count: 1 !important;
998 }
999 }
1000
1000 lines CSS