spectater 0.0.0-stage → 0.2.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/web/style.css ADDED
@@ -0,0 +1,647 @@
1
+ /* Clean tokens adapted from nexu-io/open-design (Apache-2.0).
2
+ * Reader layout and dark palette added; license: docs/licenses/open-design.txt.
3
+ */
4
+ :root {
5
+ color-scheme: light;
6
+ --bg: #ffffff;
7
+ --surface: #f7f7f7;
8
+ --surface-warm: #eeeeee;
9
+ --fg: #111111;
10
+ --fg-2: #3a3a3a;
11
+ --muted: #707070;
12
+ --border: #d9d9d9;
13
+ --border-soft: #eeeeee;
14
+ --accent: #111111;
15
+ --accent-on: #ffffff;
16
+ --accent-hover: color-mix(in oklab, var(--accent), var(--bg) 15%);
17
+ --danger: #c53030;
18
+ --font-display: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
19
+ --font-body: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
20
+ --font-mono: "SF Mono", ui-monospace, Menlo, monospace;
21
+ --text-xs: 12px;
22
+ --text-sm: 14px;
23
+ --text-base: 16px;
24
+ --text-lg: 18px;
25
+ --text-xl: 24px;
26
+ --leading-body: 1.52;
27
+ --tracking-display: -0.025em;
28
+ --space-1: 4px;
29
+ --space-2: 8px;
30
+ --space-3: 12px;
31
+ --space-4: 16px;
32
+ --space-6: 24px;
33
+ --space-8: 32px;
34
+ --space-12: 48px;
35
+ --radius-sm: 4px;
36
+ --radius-md: 8px;
37
+ font: var(--text-sm) / var(--leading-body) var(--font-body);
38
+ color: var(--fg);
39
+ background: var(--bg);
40
+ }
41
+ :root[data-theme="dark"] {
42
+ color-scheme: dark;
43
+ --bg: #111111;
44
+ --surface: #191919;
45
+ --surface-warm: #242424;
46
+ --fg: #ededed;
47
+ --fg-2: #c4c4c4;
48
+ --muted: #a1a1a1;
49
+ --border: #383838;
50
+ --border-soft: #292929;
51
+ --accent: #f5f5f5;
52
+ --accent-on: #111111;
53
+ --danger: #ff938a;
54
+ }
55
+ * {
56
+ box-sizing: border-box;
57
+ }
58
+ body {
59
+ margin: 0;
60
+ }
61
+ button,
62
+ textarea {
63
+ font: inherit;
64
+ }
65
+ button {
66
+ color: var(--fg);
67
+ background: var(--bg);
68
+ border: 1px solid var(--border);
69
+ border-radius: var(--radius-md);
70
+ min-height: 44px;
71
+ padding: var(--space-2) var(--space-3);
72
+ }
73
+ button,
74
+ select,
75
+ summary {
76
+ cursor: pointer;
77
+ }
78
+ button:hover,
79
+ select:hover {
80
+ background: var(--surface);
81
+ }
82
+ button:active {
83
+ background: var(--surface-warm);
84
+ }
85
+ button:disabled {
86
+ cursor: default;
87
+ opacity: 0.4;
88
+ }
89
+ #save,
90
+ #submit {
91
+ background: var(--accent);
92
+ border-color: var(--accent);
93
+ color: var(--accent-on);
94
+ }
95
+ #save:hover,
96
+ #submit:hover {
97
+ background: var(--accent-hover);
98
+ }
99
+ :focus-visible {
100
+ outline: 2px solid var(--accent);
101
+ outline-offset: 3px;
102
+ }
103
+ ::selection {
104
+ color: var(--accent-on);
105
+ background: var(--accent);
106
+ }
107
+ textarea {
108
+ caret-color: var(--fg);
109
+ }
110
+ * {
111
+ scrollbar-color: var(--border) var(--surface);
112
+ }
113
+ header {
114
+ display: flex;
115
+ align-items: center;
116
+ justify-content: space-between;
117
+ gap: var(--space-4);
118
+ padding: var(--space-3) var(--space-6);
119
+ border-bottom: 1px solid var(--border-soft);
120
+ }
121
+ .header-actions {
122
+ display: flex;
123
+ gap: var(--space-2);
124
+ }
125
+ .wordmark {
126
+ font-size: var(--text-lg);
127
+ letter-spacing: var(--tracking-display);
128
+ font-weight: 650;
129
+ color: var(--fg);
130
+ text-decoration: none;
131
+ }
132
+ #message {
133
+ margin: 0;
134
+ padding: var(--space-3) var(--space-6);
135
+ color: var(--fg-2);
136
+ background: var(--surface);
137
+ border-bottom: 1px solid var(--border-soft);
138
+ }
139
+ #message.error {
140
+ color: var(--danger);
141
+ }
142
+ .sr-only,
143
+ #message.quiet {
144
+ position: absolute;
145
+ width: 1px;
146
+ height: 1px;
147
+ padding: 0;
148
+ margin: -1px;
149
+ overflow: hidden;
150
+ clip-path: inset(50%);
151
+ white-space: nowrap;
152
+ border: 0;
153
+ }
154
+ .workspace {
155
+ display: grid;
156
+ grid-template-columns: 200px minmax(0, 1fr) 300px;
157
+ min-height: calc(100vh - 69px);
158
+ }
159
+ nav {
160
+ padding: var(--space-8) var(--space-4);
161
+ border-right: 1px solid var(--border-soft);
162
+ min-width: 0;
163
+ }
164
+ h1,
165
+ h2,
166
+ h3 {
167
+ line-height: 1.3;
168
+ text-wrap: balance;
169
+ }
170
+ h2 {
171
+ font-size: var(--text-base);
172
+ margin: 0 0 var(--space-4);
173
+ font-weight: 600;
174
+ }
175
+
176
+ nav > h2 {
177
+ font-size: var(--text-sm);
178
+ overflow-wrap: anywhere;
179
+ margin-bottom: var(--space-6);
180
+ }
181
+ h3 {
182
+ font-size: var(--text-sm);
183
+ margin: 0 0 var(--space-2);
184
+ font-weight: 600;
185
+ }
186
+ p {
187
+ margin: 0 0 var(--space-4);
188
+ }
189
+
190
+ :where(.prose li) p {
191
+ margin-bottom: var(--space-2);
192
+ }
193
+ .note,
194
+ .artifact-status {
195
+ color: var(--muted);
196
+ font-size: var(--text-xs);
197
+ overflow-wrap: anywhere;
198
+ }
199
+ .artifact-status {
200
+ margin: var(--space-4) var(--space-2) var(--space-1);
201
+ }
202
+ .artifact {
203
+ border: 0;
204
+ background: transparent;
205
+ display: block;
206
+ width: 100%;
207
+ text-align: left;
208
+ overflow-wrap: anywhere;
209
+ margin: var(--space-1) 0;
210
+ }
211
+ .artifact[aria-current] {
212
+ background: var(--surface);
213
+ font-weight: 600;
214
+ }
215
+ .review-details {
216
+ margin-top: var(--space-8);
217
+ color: var(--muted);
218
+ font-size: var(--text-xs);
219
+ overflow-wrap: anywhere;
220
+ }
221
+ .review-details p {
222
+ margin: var(--space-3) 0;
223
+ }
224
+ #revision {
225
+ font-family: var(--font-mono);
226
+ }
227
+ summary {
228
+ min-height: 44px;
229
+ display: list-item;
230
+ align-content: center;
231
+ }
232
+ main {
233
+ min-width: 0;
234
+ padding: var(--space-8) var(--space-8) var(--space-12);
235
+ }
236
+ .document-toolbar {
237
+ display: flex;
238
+ flex-wrap: wrap;
239
+ gap: var(--space-4);
240
+ justify-content: space-between;
241
+ align-items: center;
242
+ margin-bottom: var(--space-8);
243
+ }
244
+ .document-toolbar h1 {
245
+ font-size: var(--text-sm);
246
+ font-weight: 500;
247
+ color: var(--muted);
248
+ overflow-wrap: anywhere;
249
+ margin: 0;
250
+ }
251
+ .document-toolbar fieldset {
252
+ border: 0;
253
+ margin: 0;
254
+ min-inline-size: 0;
255
+ display: flex;
256
+ gap: var(--space-1);
257
+ background: var(--surface);
258
+ padding: var(--space-1);
259
+ border-radius: var(--radius-md);
260
+ }
261
+ .document-toolbar button {
262
+ border: 0;
263
+ background: transparent;
264
+ }
265
+ .document-toolbar button[aria-pressed="true"] {
266
+ background: var(--bg);
267
+ color: var(--fg);
268
+ box-shadow: 0 1px 3px #00000012;
269
+ }
270
+ .prose {
271
+ max-width: 72ch;
272
+ margin: auto;
273
+ font: var(--text-base) / 1.7 var(--font-body);
274
+ color: var(--fg-2);
275
+ overflow-wrap: anywhere;
276
+ }
277
+ .prose h1,
278
+ .prose h2,
279
+ .prose h3 {
280
+ color: var(--fg);
281
+ font-family: var(--font-display);
282
+ font-weight: 600;
283
+ }
284
+ .prose h1 {
285
+ font-size: 28px;
286
+ letter-spacing: var(--tracking-display);
287
+ margin: 0 0 var(--space-8);
288
+ }
289
+ .prose h2 {
290
+ font-size: var(--text-xl);
291
+ margin: var(--space-8) 0 var(--space-3);
292
+ }
293
+ .prose h3 {
294
+ font-size: var(--text-lg);
295
+ margin: var(--space-6) 0 var(--space-2);
296
+ }
297
+ .prose p,
298
+ .prose ul,
299
+ .prose ol {
300
+ margin: 0 0 var(--space-4);
301
+ }
302
+ .prose li + li {
303
+ margin-top: var(--space-2);
304
+ }
305
+ code,
306
+ pre {
307
+ font-family: var(--font-mono);
308
+ font-size: 0.85em;
309
+ }
310
+ pre {
311
+ white-space: pre-wrap;
312
+ overflow-wrap: anywhere;
313
+ background: var(--surface);
314
+ padding: var(--space-4);
315
+ border-radius: var(--radius-sm);
316
+ }
317
+ #source-view:not([hidden]) {
318
+ display: flex;
319
+ overflow: hidden;
320
+ background: var(--surface);
321
+ border-radius: var(--radius-md);
322
+ }
323
+ #source,
324
+ #source-lines {
325
+ font-size: 13px;
326
+ line-height: 1.7;
327
+ tab-size: 4;
328
+ white-space: pre;
329
+ }
330
+ #source {
331
+ min-width: 0;
332
+ flex: 1;
333
+ overflow-x: auto;
334
+ padding-left: var(--space-3);
335
+ }
336
+ #source-lines {
337
+ flex: 0 0 auto;
338
+ padding-right: 0;
339
+ text-align: right;
340
+ color: var(--muted);
341
+ user-select: none;
342
+ }
343
+ blockquote {
344
+ margin: var(--space-3) 0;
345
+ padding: 0 var(--space-4);
346
+ border-left: 1px solid var(--border);
347
+ color: var(--muted);
348
+ }
349
+ a {
350
+ color: var(--fg);
351
+ text-underline-offset: 3px;
352
+ }
353
+ .table-scroll {
354
+ overflow-x: auto;
355
+ }
356
+ table {
357
+ border-collapse: collapse;
358
+ font-size: var(--text-sm);
359
+ }
360
+ td,
361
+ th {
362
+ text-align: left;
363
+ border-bottom: 1px solid var(--border);
364
+ padding: var(--space-2) var(--space-3);
365
+ min-width: 100px;
366
+ }
367
+ aside {
368
+ border-left: 1px solid var(--border-soft);
369
+ padding: var(--space-8) var(--space-6);
370
+ min-width: 0;
371
+ }
372
+ textarea {
373
+ width: 100%;
374
+ resize: vertical;
375
+ color: var(--fg);
376
+ background: var(--bg);
377
+ border: 1px solid var(--border);
378
+ border-radius: var(--radius-md);
379
+ padding: var(--space-3);
380
+ margin-bottom: var(--space-2);
381
+ }
382
+ textarea::placeholder {
383
+ color: var(--muted);
384
+ }
385
+ .selection {
386
+ max-height: 120px;
387
+ overflow: auto;
388
+ font-size: var(--text-xs);
389
+ color: var(--muted);
390
+ white-space: pre-wrap;
391
+ margin-bottom: var(--space-3);
392
+ }
393
+ .comment {
394
+ border-top: 1px solid var(--border-soft);
395
+ padding-top: var(--space-4);
396
+ margin-top: var(--space-6);
397
+ overflow-wrap: anywhere;
398
+ }
399
+ .comment > p,
400
+ .comment blockquote {
401
+ white-space: pre-wrap;
402
+ }
403
+ .comment details {
404
+ color: var(--muted);
405
+ font-size: var(--text-xs);
406
+ }
407
+ .comment button {
408
+ margin: var(--space-1) var(--space-1) 0 0;
409
+ font-size: var(--text-xs);
410
+ }
411
+ .submission {
412
+ margin-top: var(--space-6);
413
+ }
414
+ .submission label {
415
+ display: block;
416
+ font-size: var(--text-xs);
417
+ color: var(--muted);
418
+ margin-bottom: var(--space-1);
419
+ }
420
+ .submit-actions {
421
+ display: flex;
422
+ flex-wrap: wrap;
423
+ gap: var(--space-2);
424
+ margin: var(--space-2) 0;
425
+ }
426
+ .receipt {
427
+ margin: var(--space-3) 0;
428
+ }
429
+ .receipt button {
430
+ width: 100%;
431
+ text-align: left;
432
+ }
433
+ #retrieval-command {
434
+ display: block;
435
+ overflow-wrap: anywhere;
436
+ font-size: 11px;
437
+ padding: var(--space-3) 0;
438
+ user-select: all;
439
+ }
440
+ #export {
441
+ font: 11px / 1.5 var(--font-mono);
442
+ }
443
+ .export-actions {
444
+ display: flex;
445
+ flex-wrap: wrap;
446
+ gap: var(--space-2);
447
+ }
448
+ @media (max-width: 1100px) {
449
+ .workspace {
450
+ grid-template-columns: 180px minmax(0, 1fr);
451
+ }
452
+ aside {
453
+ grid-column: 2;
454
+ border-left: 0;
455
+ border-top: 1px solid var(--border-soft);
456
+ padding: var(--space-6) var(--space-8);
457
+ }
458
+ nav {
459
+ grid-row: span 2;
460
+ }
461
+ }
462
+ @media (max-width: 700px) {
463
+ header {
464
+ padding: var(--space-3) var(--space-4);
465
+ gap: var(--space-2);
466
+ }
467
+ .header-actions {
468
+ gap: var(--space-1);
469
+ }
470
+ header button {
471
+ padding: var(--space-2);
472
+ }
473
+ #message {
474
+ padding: var(--space-3) var(--space-4);
475
+ }
476
+ .workspace {
477
+ display: block;
478
+ }
479
+ nav {
480
+ border-right: 0;
481
+ border-bottom: 1px solid var(--border-soft);
482
+ padding: var(--space-4);
483
+ }
484
+ nav > h2 {
485
+ margin-bottom: var(--space-2);
486
+ }
487
+ #artifacts {
488
+ display: flex;
489
+ flex-wrap: wrap;
490
+ gap: var(--space-1);
491
+ }
492
+ .artifact-group {
493
+ max-width: 100%;
494
+ }
495
+ .artifact {
496
+ width: auto;
497
+ }
498
+ .review-details {
499
+ margin-top: var(--space-2);
500
+ }
501
+ main,
502
+ aside {
503
+ padding: var(--space-6) var(--space-4);
504
+ }
505
+ .document-toolbar {
506
+ margin-bottom: var(--space-6);
507
+ }
508
+ }
509
+
510
+ .wordmark {
511
+ display: inline-flex;
512
+ align-items: center;
513
+ gap: var(--space-2);
514
+ }
515
+ .logo {
516
+ width: 36px;
517
+ height: 36px;
518
+ flex: 0 0 auto;
519
+ }
520
+ .spud {
521
+ fill: #e3be7a;
522
+ stroke: #493a24;
523
+ stroke-width: 1.5;
524
+ }
525
+ .logo-ink {
526
+ fill: none;
527
+ stroke: #30291f;
528
+ stroke-width: 1.8;
529
+ stroke-linecap: round;
530
+ stroke-linejoin: round;
531
+ }
532
+ .icon-button {
533
+ display: inline-grid;
534
+ place-items: center;
535
+ width: 44px;
536
+ height: 44px;
537
+ padding: 10px;
538
+ }
539
+ .icon-button svg {
540
+ width: 20px;
541
+ height: 20px;
542
+ fill: none;
543
+ stroke: currentColor;
544
+ stroke-width: 1.7;
545
+ stroke-linecap: round;
546
+ stroke-linejoin: round;
547
+ }
548
+ .sun-icon,
549
+ :root[data-theme="dark"] .moon-icon {
550
+ display: none;
551
+ }
552
+ :root[data-theme="dark"] .sun-icon {
553
+ display: block;
554
+ }
555
+ #comment-popover {
556
+ position: fixed;
557
+ inset: auto;
558
+ margin: 0;
559
+ width: min(360px, calc(100vw - 32px));
560
+ max-height: calc(100dvh - 32px);
561
+ overflow: auto;
562
+ padding: var(--space-4);
563
+ color: var(--fg);
564
+ background: var(--bg);
565
+ border: 1px solid var(--border);
566
+ border-radius: var(--radius-md);
567
+ box-shadow: 0 12px 40px rgb(0 0 0 / 0.18);
568
+ }
569
+ .popover-header {
570
+ display: flex;
571
+ align-items: center;
572
+ justify-content: space-between;
573
+ gap: var(--space-3);
574
+ margin-bottom: var(--space-2);
575
+ }
576
+ .popover-header h2 {
577
+ margin: 0;
578
+ }
579
+ #comment-popover .selection {
580
+ max-height: 72px;
581
+ }
582
+ #composer-error {
583
+ margin: var(--space-3) 0 0;
584
+ color: var(--danger);
585
+ }
586
+ #resume-draft {
587
+ margin-bottom: var(--space-4);
588
+ }
589
+ #undo-delete {
590
+ font-size: var(--text-xs);
591
+ margin-bottom: var(--space-3);
592
+ }
593
+ #undo {
594
+ margin-left: var(--space-2);
595
+ }
596
+ #deleted-comments {
597
+ margin-top: var(--space-4);
598
+ font-size: var(--text-xs);
599
+ color: var(--muted);
600
+ }
601
+ .deleted-comment {
602
+ padding: var(--space-3) 0;
603
+ border-top: 1px solid var(--border-soft);
604
+ overflow-wrap: anywhere;
605
+ }
606
+
607
+ .agent-reply {
608
+ border-left: 2px solid var(--border);
609
+ padding-left: 12px;
610
+ }
611
+ .agent-reply p {
612
+ white-space: pre-wrap;
613
+ overflow-wrap: anywhere;
614
+ }
615
+
616
+ .queue-actions {
617
+ display: flex;
618
+ flex-wrap: wrap;
619
+ gap: var(--space-2);
620
+ margin-top: var(--space-2);
621
+ }
622
+ .receipt .queue-actions button {
623
+ width: auto;
624
+ flex: 1 1 auto;
625
+ padding-inline: var(--space-2);
626
+ text-align: center;
627
+ }
628
+ .batch-history {
629
+ margin-top: var(--space-4);
630
+ }
631
+
632
+ #changes {
633
+ margin-bottom: var(--space-6);
634
+ }
635
+ #changed-files {
636
+ display: flex;
637
+ flex-wrap: wrap;
638
+ gap: var(--space-2);
639
+ }
640
+ #changed-files button {
641
+ overflow-wrap: anywhere;
642
+ text-align: left;
643
+ max-width: 100%;
644
+ }
645
+ #review-lifecycle {
646
+ margin-top: var(--space-4);
647
+ }
package/web/theme.js ADDED
@@ -0,0 +1,45 @@
1
+ (() => {
2
+ const root = document.documentElement;
3
+ const media = matchMedia('(prefers-color-scheme: dark)');
4
+ let preference = 'system';
5
+ try {
6
+ preference = localStorage.getItem('spectater-theme') || 'system';
7
+ } catch {
8
+ /* Theme controls remain available when storage is denied. */
9
+ }
10
+ if (!['system', 'light', 'dark'].includes(preference)) preference = 'system';
11
+ const apply = () => {
12
+ root.dataset.themePreference = preference;
13
+ root.dataset.theme = preference === 'system' ? (media.matches ? 'dark' : 'light') : preference;
14
+ const control = document.getElementById('theme');
15
+ if (control) {
16
+ control.setAttribute('aria-pressed', String(root.dataset.theme === 'dark'));
17
+ control.title = `Switch to ${root.dataset.theme === 'dark' ? 'light' : 'dark'} mode${preference === 'system' ? ' (currently follows system)' : ''}`;
18
+ document.getElementById('theme-system').hidden = preference === 'system';
19
+ }
20
+ };
21
+ apply();
22
+ media.addEventListener('change', apply);
23
+ document.addEventListener('DOMContentLoaded', () => {
24
+ const control = document.getElementById('theme');
25
+ apply();
26
+ control.addEventListener('click', () => {
27
+ preference = root.dataset.theme === 'dark' ? 'light' : 'dark';
28
+ apply();
29
+ try {
30
+ localStorage.setItem('spectater-theme', preference);
31
+ } catch {
32
+ /* The selection still lasts for this page. */
33
+ }
34
+ });
35
+ document.getElementById('theme-system').addEventListener('click', () => {
36
+ preference = 'system';
37
+ apply();
38
+ try {
39
+ localStorage.removeItem('spectater-theme');
40
+ } catch {
41
+ /* System mode still works for this page. */
42
+ }
43
+ });
44
+ });
45
+ })();