@bakapiano/ccsm 0.5.0 → 0.8.3

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 (70) hide show
  1. package/README.md +172 -38
  2. package/bin/ccsm.js +194 -0
  3. package/lib/config.js +1 -0
  4. package/lib/favorites.js +23 -45
  5. package/lib/focus.js +90 -14
  6. package/lib/jsonStore.js +60 -0
  7. package/lib/labels.js +29 -0
  8. package/lib/webTerminal.js +173 -0
  9. package/lib/workspace.js +8 -4
  10. package/package.json +11 -3
  11. package/public/css/base.css +82 -0
  12. package/public/css/cards.css +149 -0
  13. package/public/css/feedback.css +219 -0
  14. package/public/css/forms.css +282 -0
  15. package/public/css/layout.css +107 -0
  16. package/public/css/modal.css +169 -0
  17. package/public/css/responsive.css +10 -0
  18. package/public/css/sidebar.css +165 -0
  19. package/public/css/tables.css +266 -0
  20. package/public/css/terminals.css +112 -0
  21. package/public/css/tokens.css +63 -0
  22. package/public/css/wco.css +70 -0
  23. package/public/css/widgets.css +204 -0
  24. package/public/favicon.svg +18 -0
  25. package/public/index.html +53 -379
  26. package/public/js/actions.js +87 -0
  27. package/public/js/api.js +103 -0
  28. package/public/js/backend.js +28 -0
  29. package/public/js/components/App.js +45 -0
  30. package/public/js/components/Card.js +24 -0
  31. package/public/js/components/DialogHost.js +45 -0
  32. package/public/js/components/Fab.js +11 -0
  33. package/public/js/components/FavoritesTable.js +81 -0
  34. package/public/js/components/Footer.js +12 -0
  35. package/public/js/components/NewSessionModal.js +142 -0
  36. package/public/js/components/OfflineBanner.js +52 -0
  37. package/public/js/components/PageHead.js +33 -0
  38. package/public/js/components/Pagination.js +27 -0
  39. package/public/js/components/ProgressList.js +32 -0
  40. package/public/js/components/RecentTable.js +68 -0
  41. package/public/js/components/RepoPicker.js +40 -0
  42. package/public/js/components/ReposEditor.js +74 -0
  43. package/public/js/components/ServerStatus.js +18 -0
  44. package/public/js/components/SessionsTable.js +71 -0
  45. package/public/js/components/Sidebar.js +52 -0
  46. package/public/js/components/SnapshotPanel.js +77 -0
  47. package/public/js/components/TerminalView.js +108 -0
  48. package/public/js/components/TitleCell.js +40 -0
  49. package/public/js/components/Toast.js +8 -0
  50. package/public/js/components/WorkspacePicker.js +19 -0
  51. package/public/js/components/WorkspacesGrid.js +41 -0
  52. package/public/js/dialog.js +59 -0
  53. package/public/js/html.js +6 -0
  54. package/public/js/icons.js +114 -0
  55. package/public/js/main.js +81 -0
  56. package/public/js/pages/AboutPage.js +85 -0
  57. package/public/js/pages/ConfigurePage.js +194 -0
  58. package/public/js/pages/LaunchPage.js +117 -0
  59. package/public/js/pages/SessionsPage.js +47 -0
  60. package/public/js/pages/TerminalsPage.js +74 -0
  61. package/public/js/state.js +87 -0
  62. package/public/js/streaming.js +96 -0
  63. package/public/js/toast.js +14 -0
  64. package/public/js/util.js +24 -0
  65. package/public/manifest.webmanifest +14 -0
  66. package/scripts/install.js +111 -0
  67. package/scripts/uninstall.js +56 -0
  68. package/server.js +314 -31
  69. package/public/app.js +0 -894
  70. package/public/styles.css +0 -1204
package/public/styles.css DELETED
@@ -1,1204 +0,0 @@
1
- /* ─────────────────────────────────────────────────────────────
2
- ccsm · light cream theme · sidebar nav · v0.6
3
- ───────────────────────────────────────────────────────────── */
4
-
5
- :root {
6
- /* Surfaces — cream with neutral whites */
7
- --bg: #faf9f5;
8
- --bg-elev: #ffffff;
9
- --sidebar-bg: #f3f0e8;
10
- --sidebar-hover: #ebe7db;
11
- --sidebar-active: #e3ddca;
12
-
13
- /* Borders & rules */
14
- --border: #e8e3d5;
15
- --border-soft: #ece8da;
16
- --border-strong: #d4cdb8;
17
-
18
- /* Ink */
19
- --ink: #1a1815;
20
- --ink-mid: #534e44;
21
- --ink-muted: #8a8475;
22
- --ink-faint: #b5af9d;
23
-
24
- /* Accent — Claude warm orange/copper */
25
- --accent: #c45f3f;
26
- --accent-deep: #a14d33;
27
- --accent-soft: rgba(196, 95, 63, 0.10);
28
- --accent-softer: rgba(196, 95, 63, 0.04);
29
-
30
- /* Status */
31
- --green: #4a8a4a;
32
- --yellow: #c4892b;
33
- --red: #b73f3f;
34
- --blue: #4a73a5;
35
-
36
- /* Type */
37
- --body: "Geist", -apple-system, "Segoe UI", system-ui, sans-serif;
38
- --mono: "JetBrains Mono", "Cascadia Mono", "Consolas", monospace;
39
-
40
- /* Scale */
41
- --s-1: 4px; --s-2: 8px; --s-3: 12px; --s-4: 16px;
42
- --s-5: 20px; --s-6: 24px; --s-8: 32px; --s-10: 40px;
43
- --s-12: 48px; --s-16: 64px;
44
-
45
- /* Radius */
46
- --r-sm: 6px;
47
- --r: 8px;
48
- --r-md: 10px;
49
- --r-lg: 14px;
50
-
51
- /* Shadow — soft, like print on cream */
52
- --shadow-sm: 0 1px 0 rgba(26, 24, 21, 0.04);
53
- --shadow: 0 1px 2px rgba(26, 24, 21, 0.04),
54
- 0 1px 0 rgba(26, 24, 21, 0.03);
55
- --shadow-md: 0 4px 12px -2px rgba(26, 24, 21, 0.08),
56
- 0 1px 0 rgba(26, 24, 21, 0.04);
57
-
58
- /* Sidebar geometry */
59
- --sidebar-w: 232px;
60
- --sidebar-w-collapsed: 60px;
61
- }
62
-
63
- * { box-sizing: border-box; margin: 0; padding: 0; }
64
-
65
- html, body {
66
- background: var(--bg);
67
- color: var(--ink);
68
- font-family: var(--body);
69
- font-size: 14px;
70
- line-height: 1.5;
71
- font-variant-numeric: tabular-nums;
72
- min-height: 100vh;
73
- -webkit-font-smoothing: antialiased;
74
- -moz-osx-font-smoothing: grayscale;
75
- }
76
-
77
- ::selection { background: var(--accent); color: var(--bg-elev); }
78
-
79
- /* ─────────────────────────────────────────────────────────────
80
- App layout
81
- ───────────────────────────────────────────────────────────── */
82
-
83
- .app {
84
- display: grid;
85
- grid-template-columns: var(--sidebar-w) 1fr;
86
- min-height: 100vh;
87
- transition: grid-template-columns .25s cubic-bezier(.4, 0, .2, 1);
88
- }
89
- .app:has(.sidebar[data-collapsed="true"]) {
90
- grid-template-columns: var(--sidebar-w-collapsed) 1fr;
91
- }
92
-
93
- /* ─────────────────────────────────────────────────────────────
94
- Sidebar
95
- ───────────────────────────────────────────────────────────── */
96
-
97
- .sidebar {
98
- position: sticky;
99
- top: 0;
100
- height: 100vh;
101
- background: var(--sidebar-bg);
102
- border-right: 1px solid var(--border);
103
- display: flex;
104
- flex-direction: column;
105
- padding: var(--s-4) var(--s-3);
106
- overflow: hidden;
107
- transition: padding .25s cubic-bezier(.4, 0, .2, 1);
108
- }
109
- .sidebar[data-collapsed="true"] {
110
- padding: var(--s-4) var(--s-2);
111
- }
112
-
113
- .sidebar-brand {
114
- display: flex;
115
- align-items: center;
116
- gap: var(--s-2);
117
- padding: var(--s-2) var(--s-2) var(--s-4);
118
- height: 56px;
119
- }
120
- .brand-mark {
121
- display: inline-flex;
122
- align-items: center;
123
- justify-content: center;
124
- width: 32px;
125
- height: 32px;
126
- flex: 0 0 32px;
127
- background: var(--accent);
128
- color: var(--bg-elev);
129
- border-radius: var(--r-sm);
130
- }
131
- .brand-name {
132
- font-size: 19px;
133
- font-weight: 600;
134
- letter-spacing: -0.02em;
135
- color: var(--ink);
136
- white-space: nowrap;
137
- opacity: 1;
138
- transition: opacity .15s ease;
139
- }
140
- .brand-dot { color: var(--accent); }
141
- .sidebar[data-collapsed="true"] .brand-name { opacity: 0; pointer-events: none; }
142
-
143
- .sidebar-nav {
144
- display: flex;
145
- flex-direction: column;
146
- gap: 2px;
147
- flex: 0 0 auto;
148
- }
149
-
150
- .nav-item, .util-item {
151
- appearance: none;
152
- background: transparent;
153
- border: 0;
154
- display: flex;
155
- align-items: center;
156
- gap: var(--s-3);
157
- width: 100%;
158
- padding: 8px 10px;
159
- border-radius: var(--r-sm);
160
- cursor: pointer;
161
- color: var(--ink-mid);
162
- font-family: var(--body);
163
- font-size: 13.5px;
164
- font-weight: 500;
165
- text-align: left;
166
- transition: background .12s ease, color .12s ease;
167
- position: relative;
168
- }
169
- .nav-item:hover, .util-item:hover {
170
- background: var(--sidebar-hover);
171
- color: var(--ink);
172
- }
173
- .nav-item[aria-selected="true"] {
174
- background: var(--bg-elev);
175
- color: var(--ink);
176
- box-shadow: var(--shadow-sm);
177
- }
178
- .nav-item[aria-selected="true"]::before {
179
- content: "";
180
- position: absolute;
181
- left: -4px;
182
- top: 8px;
183
- bottom: 8px;
184
- width: 3px;
185
- background: var(--accent);
186
- border-radius: 2px;
187
- }
188
- .nav-icon {
189
- display: inline-flex;
190
- width: 18px;
191
- height: 18px;
192
- flex: 0 0 18px;
193
- color: currentColor;
194
- }
195
- .nav-label {
196
- white-space: nowrap;
197
- opacity: 1;
198
- transition: opacity .15s ease;
199
- flex: 1;
200
- }
201
- .sidebar[data-collapsed="true"] .nav-label { opacity: 0; pointer-events: none; }
202
-
203
- .nav-badge {
204
- font-family: var(--mono);
205
- font-size: 10.5px;
206
- background: var(--border-soft);
207
- color: var(--ink-muted);
208
- padding: 1px 6px;
209
- border-radius: 4px;
210
- font-variant-numeric: tabular-nums;
211
- opacity: 1;
212
- transition: opacity .15s ease;
213
- }
214
- .sidebar[data-collapsed="true"] .nav-badge { opacity: 0; }
215
- .nav-item[aria-selected="true"] .nav-badge {
216
- background: var(--accent-soft);
217
- color: var(--accent);
218
- }
219
-
220
- .sidebar-divider {
221
- margin: var(--s-3) var(--s-2);
222
- border-top: 1px solid var(--border);
223
- }
224
-
225
- .sidebar-utility {
226
- display: flex;
227
- flex-direction: column;
228
- gap: 2px;
229
- }
230
- .util-accent {
231
- color: var(--accent);
232
- }
233
- .util-accent:hover {
234
- background: var(--accent-soft);
235
- color: var(--accent);
236
- }
237
-
238
- .sidebar-foot {
239
- margin-top: auto;
240
- padding: var(--s-2);
241
- display: flex;
242
- justify-content: flex-end;
243
- }
244
-
245
- .collapse-toggle {
246
- appearance: none;
247
- background: transparent;
248
- border: 1px solid var(--border);
249
- color: var(--ink-muted);
250
- width: 26px;
251
- height: 26px;
252
- border-radius: var(--r-sm);
253
- display: inline-flex;
254
- align-items: center;
255
- justify-content: center;
256
- cursor: pointer;
257
- transition: background .12s ease, color .12s ease, transform .25s cubic-bezier(.4, 0, .2, 1), border-color .12s;
258
- }
259
- .collapse-toggle:hover {
260
- background: var(--bg-elev);
261
- color: var(--ink);
262
- border-color: var(--border-strong);
263
- }
264
- .sidebar[data-collapsed="true"] .collapse-toggle {
265
- transform: rotate(180deg);
266
- }
267
- .sidebar[data-collapsed="true"] .sidebar-foot {
268
- justify-content: center;
269
- }
270
-
271
- /* ─────────────────────────────────────────────────────────────
272
- Main column
273
- ───────────────────────────────────────────────────────────── */
274
-
275
- .main {
276
- display: flex;
277
- flex-direction: column;
278
- min-width: 0;
279
- padding: var(--s-8) var(--s-10) var(--s-6);
280
- gap: var(--s-6);
281
- }
282
-
283
- .page-head {
284
- display: flex;
285
- align-items: flex-start;
286
- justify-content: space-between;
287
- gap: var(--s-8);
288
- padding-bottom: var(--s-5);
289
- border-bottom: 1px solid var(--border);
290
- }
291
- .page-head-inner { min-width: 0; }
292
-
293
- .page-title {
294
- font-size: 26px;
295
- font-weight: 600;
296
- letter-spacing: -0.024em;
297
- color: var(--ink);
298
- line-height: 1.1;
299
- }
300
- .page-subtitle {
301
- margin-top: 4px;
302
- font-size: 13.5px;
303
- color: var(--ink-mid);
304
- }
305
-
306
- .page-head-meta {
307
- display: flex;
308
- align-items: baseline;
309
- gap: var(--s-3);
310
- flex-shrink: 0;
311
- white-space: nowrap;
312
- font-family: var(--mono);
313
- font-size: 11px;
314
- color: var(--ink-muted);
315
- }
316
- .ph-stat { display: inline-flex; gap: 6px; align-items: baseline; }
317
- .ph-key {
318
- font-family: var(--body);
319
- font-size: 10.5px;
320
- letter-spacing: 0.06em;
321
- text-transform: uppercase;
322
- color: var(--ink-faint);
323
- }
324
- .ph-val { color: var(--ink-mid); }
325
- .ph-divider { color: var(--ink-faint); }
326
-
327
- .content {
328
- flex: 1;
329
- display: flex;
330
- flex-direction: column;
331
- }
332
-
333
- .tab-panel {
334
- display: none;
335
- flex-direction: column;
336
- gap: var(--s-6);
337
- }
338
- .tab-panel[data-active] {
339
- display: flex;
340
- animation: panel-in .35s cubic-bezier(.4, 0, .2, 1);
341
- }
342
- @keyframes panel-in {
343
- from { opacity: 0; transform: translateY(6px); }
344
- to { opacity: 1; transform: translateY(0); }
345
- }
346
-
347
- /* ─────────────────────────────────────────────────────────────
348
- Cards
349
- ───────────────────────────────────────────────────────────── */
350
-
351
- .card {
352
- background: var(--bg-elev);
353
- border: 1px solid var(--border);
354
- border-radius: var(--r-md);
355
- overflow: hidden;
356
- box-shadow: var(--shadow);
357
- }
358
-
359
- .card-head {
360
- padding: var(--s-4) var(--s-6) var(--s-3);
361
- border-bottom: 1px solid var(--border-soft);
362
- display: flex;
363
- justify-content: space-between;
364
- align-items: baseline;
365
- gap: var(--s-4);
366
- }
367
- .card-titles { min-width: 0; }
368
- .card-title {
369
- font-size: 15.5px;
370
- font-weight: 600;
371
- letter-spacing: -0.012em;
372
- color: var(--ink);
373
- }
374
- .card-meta {
375
- margin-top: 2px;
376
- font-size: 12.5px;
377
- color: var(--ink-muted);
378
- font-family: var(--body);
379
- }
380
- .card-meta code {
381
- font-family: var(--mono);
382
- font-size: 11.5px;
383
- color: var(--ink-mid);
384
- background: var(--bg);
385
- padding: 1px 5px;
386
- border-radius: 4px;
387
- border: 1px solid var(--border-soft);
388
- }
389
-
390
- .card-body { padding: var(--s-5) var(--s-6); }
391
- .card-body-flush { padding: 0; }
392
-
393
- /* ─────────────────────────────────────────────────────────────
394
- Page-level inline actions (above the cards on a tab)
395
- ───────────────────────────────────────────────────────────── */
396
-
397
- .page-actions {
398
- display: flex;
399
- align-items: center;
400
- justify-content: space-between;
401
- gap: var(--s-4);
402
- padding: var(--s-3) var(--s-5);
403
- background: var(--bg-elev);
404
- border: 1px solid var(--border);
405
- border-radius: var(--r-md);
406
- box-shadow: var(--shadow-sm);
407
- }
408
- .page-actions-hint {
409
- font-size: 13px;
410
- color: var(--ink-mid);
411
- }
412
- .page-actions .action svg { stroke-width: 2; }
413
-
414
- /* ─────────────────────────────────────────────────────────────
415
- Data table
416
- ───────────────────────────────────────────────────────────── */
417
-
418
- .table-scroll {
419
- overflow-x: auto;
420
- /* fade out the right edge slightly when scrollable, like a hint */
421
- }
422
- .table-scroll .data { min-width: 760px; }
423
- .table-scroll::-webkit-scrollbar { height: 8px; }
424
-
425
- .data {
426
- width: 100%;
427
- border-collapse: collapse;
428
- font-family: var(--body);
429
- font-size: 13.5px;
430
- }
431
-
432
- .data thead th {
433
- padding: 10px var(--s-5) 10px;
434
- text-align: left;
435
- font-family: var(--body);
436
- font-size: 11px;
437
- font-weight: 600;
438
- letter-spacing: 0.04em;
439
- color: var(--ink-muted);
440
- border-bottom: 1px solid var(--border);
441
- background: var(--bg);
442
- vertical-align: bottom;
443
- white-space: nowrap;
444
- }
445
- .data thead th.num { text-align: right; }
446
- .data thead th.col-mark { width: 28px; padding-left: 0; padding-right: 0; }
447
- .data thead th.col-star { width: 28px; padding-left: 0; padding-right: 0; }
448
- .data thead th.col-actions { width: 1px; }
449
- .data thead th:first-child { padding-left: var(--s-6); }
450
- .data thead th:last-child { padding-right: var(--s-6); }
451
-
452
- .data tbody tr {
453
- border-bottom: 1px solid var(--border-soft);
454
- transition: background .12s ease;
455
- }
456
- .data tbody tr:last-child { border-bottom: 0; }
457
- .data tbody tr:hover { background: var(--bg); }
458
-
459
- .data tbody td {
460
- padding: 12px var(--s-5);
461
- vertical-align: middle;
462
- color: var(--ink);
463
- }
464
- .data tbody td:first-child { padding-left: var(--s-6); }
465
- .data tbody td:last-child { padding-right: var(--s-6); }
466
- .data tbody td.num {
467
- text-align: right;
468
- font-family: var(--mono);
469
- font-size: 11.5px;
470
- color: var(--ink-mid);
471
- }
472
-
473
- /* Row entry animation: plays once on first render of each tbody. JS adds
474
- .no-anim after that initial render so subsequent re-renders (auto-refresh
475
- every 5s) don't restage and flicker. */
476
- .data tbody:not(.no-anim) tr {
477
- animation: row-in .3s ease-out backwards;
478
- }
479
- .data tbody:not(.no-anim) tr:nth-child(1) { animation-delay: 0ms; }
480
- .data tbody:not(.no-anim) tr:nth-child(2) { animation-delay: 20ms; }
481
- .data tbody:not(.no-anim) tr:nth-child(3) { animation-delay: 40ms; }
482
- .data tbody:not(.no-anim) tr:nth-child(4) { animation-delay: 60ms; }
483
- .data tbody:not(.no-anim) tr:nth-child(5) { animation-delay: 80ms; }
484
- .data tbody:not(.no-anim) tr:nth-child(6) { animation-delay: 100ms; }
485
- .data tbody:not(.no-anim) tr:nth-child(7) { animation-delay: 120ms; }
486
- .data tbody:not(.no-anim) tr:nth-child(8) { animation-delay: 140ms; }
487
- .data tbody:not(.no-anim) tr:nth-child(n+9) { animation-delay: 160ms; }
488
- @keyframes row-in {
489
- from { opacity: 0; transform: translateY(3px); }
490
- to { opacity: 1; transform: translateY(0); }
491
- }
492
-
493
- /* Status indicator — small dot, claude-like */
494
- .status-mark {
495
- display: inline-block;
496
- width: 8px;
497
- height: 8px;
498
- border-radius: 50%;
499
- background: currentColor;
500
- vertical-align: middle;
501
- position: relative;
502
- }
503
- .status-mark.busy {
504
- color: var(--yellow);
505
- box-shadow: 0 0 0 0 currentColor;
506
- animation: pulse 1.5s ease-in-out infinite;
507
- }
508
- .status-mark.idle { color: var(--green); }
509
- .status-mark.unknown { color: var(--ink-faint); }
510
- @keyframes pulse {
511
- 0% {
512
- box-shadow: 0 0 0 0 rgba(196, 137, 43, 0.4);
513
- }
514
- 70% {
515
- box-shadow: 0 0 0 7px rgba(196, 137, 43, 0);
516
- }
517
- 100% {
518
- box-shadow: 0 0 0 0 rgba(196, 137, 43, 0);
519
- }
520
- }
521
-
522
- /* Composite cells */
523
- .title-cell { max-width: 420px; min-width: 0; }
524
- .title-cell .title-row {
525
- display: flex;
526
- align-items: center;
527
- gap: 6px;
528
- min-width: 0;
529
- }
530
- .title-cell .primary {
531
- color: var(--ink);
532
- font-weight: 500;
533
- white-space: nowrap;
534
- overflow: hidden;
535
- text-overflow: ellipsis;
536
- font-size: 13.5px;
537
- min-width: 0;
538
- flex: 0 1 auto;
539
- }
540
- .title-cell .secondary {
541
- font-family: var(--mono);
542
- font-size: 10.5px;
543
- color: var(--ink-muted);
544
- letter-spacing: 0.02em;
545
- margin-top: 2px;
546
- }
547
-
548
- .path-cell {
549
- font-family: var(--mono);
550
- font-size: 11.5px;
551
- color: var(--ink-mid);
552
- max-width: 380px;
553
- white-space: nowrap;
554
- overflow: hidden;
555
- text-overflow: ellipsis;
556
- }
557
-
558
- .branch-tag {
559
- display: inline-block;
560
- font-family: var(--mono);
561
- font-size: 11px;
562
- color: var(--ink-mid);
563
- background: var(--bg);
564
- padding: 2px 7px;
565
- border-radius: 4px;
566
- border: 1px solid var(--border-soft);
567
- }
568
-
569
- .row-actions {
570
- display: flex;
571
- gap: var(--s-2);
572
- justify-content: flex-end;
573
- }
574
-
575
- /* Star button (favorite toggle) — sits inline next to the title text.
576
- Outline by default (faint), fades in to ink on row hover, fills with
577
- the accent color when starred. */
578
- .star-btn {
579
- appearance: none;
580
- background: transparent;
581
- border: 0;
582
- padding: 2px;
583
- margin: 0;
584
- cursor: pointer;
585
- color: var(--border-strong);
586
- display: inline-flex;
587
- align-items: center;
588
- justify-content: center;
589
- border-radius: 4px;
590
- transition: color .12s ease, background .12s ease, transform .15s ease;
591
- line-height: 0;
592
- flex: 0 0 auto;
593
- opacity: 0.55;
594
- }
595
- .data tbody tr:hover .star-btn {
596
- opacity: 1;
597
- color: var(--ink-faint);
598
- }
599
- .star-btn:hover {
600
- color: var(--accent) !important;
601
- background: var(--accent-softer);
602
- opacity: 1 !important;
603
- }
604
- .star-btn:active { transform: scale(0.88); }
605
- .star-btn.is-fav {
606
- color: var(--accent);
607
- opacity: 1;
608
- }
609
- .star-btn svg { display: block; }
610
-
611
- /* Title with icon glyph */
612
- .card-title .title-icon {
613
- color: var(--accent);
614
- margin-right: 6px;
615
- vertical-align: -2px;
616
- }
617
-
618
- /* Favorites empty state — sits inside the card body (not generic table empty) */
619
- #favoritesEmpty {
620
- padding: var(--s-6) var(--s-6);
621
- text-align: center;
622
- font-size: 12.5px;
623
- color: var(--ink-muted);
624
- }
625
-
626
- .empty {
627
- padding: var(--s-12) var(--s-6);
628
- text-align: center;
629
- font-size: 13px;
630
- color: var(--ink-muted);
631
- }
632
- .empty code {
633
- font-family: var(--mono);
634
- font-size: 12px;
635
- color: var(--ink-mid);
636
- background: var(--bg);
637
- padding: 1px 5px;
638
- border-radius: 4px;
639
- }
640
-
641
- /* ─────────────────────────────────────────────────────────────
642
- Buttons & form
643
- ───────────────────────────────────────────────────────────── */
644
-
645
- .action {
646
- appearance: none;
647
- background: var(--bg-elev);
648
- border: 1px solid var(--border-strong);
649
- color: var(--ink);
650
- padding: 7px 14px;
651
- font-family: var(--body);
652
- font-size: 13px;
653
- font-weight: 500;
654
- letter-spacing: -0.005em;
655
- border-radius: var(--r-sm);
656
- cursor: pointer;
657
- transition: background .12s ease, border-color .12s ease, color .12s ease, box-shadow .12s ease;
658
- white-space: nowrap;
659
- box-shadow: var(--shadow-sm);
660
- }
661
- .action:hover {
662
- background: var(--bg);
663
- border-color: var(--ink-faint);
664
- }
665
- .action:active { transform: translateY(0.5px); }
666
- .action:disabled { opacity: .5; cursor: not-allowed; pointer-events: none; }
667
- .action.primary {
668
- background: var(--accent);
669
- border-color: var(--accent);
670
- color: var(--bg-elev);
671
- }
672
- .action.primary:hover {
673
- background: var(--accent-deep);
674
- border-color: var(--accent-deep);
675
- }
676
- .action.small {
677
- font-size: 12px;
678
- padding: 4px 10px;
679
- }
680
- .action.tiny {
681
- font-size: 11px;
682
- padding: 3px 8px;
683
- }
684
- .action.subtle {
685
- background: transparent;
686
- border-color: var(--border);
687
- box-shadow: none;
688
- color: var(--ink-mid);
689
- }
690
- .action.subtle:hover { background: var(--bg); color: var(--ink); }
691
- .action.danger {
692
- color: var(--red);
693
- border-color: var(--border);
694
- }
695
- .action.danger:hover {
696
- background: rgba(183, 63, 63, 0.06);
697
- border-color: var(--red);
698
- }
699
-
700
- .input, input[type="text"], input[type="number"], select, textarea {
701
- appearance: none;
702
- background: var(--bg-elev);
703
- border: 1px solid var(--border-strong);
704
- color: var(--ink);
705
- padding: 7px 12px;
706
- font-family: var(--body);
707
- font-size: 13px;
708
- border-radius: var(--r-sm);
709
- transition: border-color .12s ease, box-shadow .12s ease;
710
- width: 100%;
711
- max-width: 480px;
712
- }
713
- .input.narrow { min-width: 240px; max-width: 320px; }
714
- .input:focus, input:focus, select:focus, textarea:focus {
715
- outline: none;
716
- border-color: var(--accent);
717
- box-shadow: 0 0 0 3px var(--accent-soft);
718
- }
719
- select {
720
- background-image: url("data:image/svg+xml;utf8,<svg viewBox='0 0 12 8' xmlns='http://www.w3.org/2000/svg' fill='none' stroke='%238a8475' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'><polyline points='1,1 6,7 11,1'/></svg>");
721
- background-repeat: no-repeat;
722
- background-position: right 10px center;
723
- background-size: 10px;
724
- padding-right: 28px;
725
- }
726
- textarea {
727
- font-family: var(--mono);
728
- font-size: 12px;
729
- resize: vertical;
730
- line-height: 1.55;
731
- }
732
- input[type="checkbox"] {
733
- appearance: none;
734
- width: 16px;
735
- height: 16px;
736
- flex: 0 0 16px;
737
- border: 1px solid var(--border-strong);
738
- background: var(--bg-elev);
739
- border-radius: 4px;
740
- cursor: pointer;
741
- position: relative;
742
- transition: background .12s, border-color .12s;
743
- }
744
- input[type="checkbox"]:checked {
745
- background: var(--accent);
746
- border-color: var(--accent);
747
- }
748
- input[type="checkbox"]:checked::after {
749
- content: "";
750
- position: absolute;
751
- left: 4px; top: 1px;
752
- width: 5px; height: 9px;
753
- border: solid var(--bg-elev);
754
- border-width: 0 1.5px 1.5px 0;
755
- transform: rotate(45deg);
756
- }
757
-
758
- /* Form layout */
759
- .form-row {
760
- display: flex;
761
- align-items: center;
762
- gap: var(--s-4);
763
- margin-bottom: var(--s-3);
764
- flex-wrap: wrap;
765
- }
766
- .form-row:last-child { margin-bottom: 0; }
767
- .form-label {
768
- font-family: var(--body);
769
- font-size: 12px;
770
- font-weight: 500;
771
- color: var(--ink-mid);
772
- min-width: 96px;
773
- }
774
- .row { display: flex; align-items: center; }
775
- .gap-row { gap: var(--s-3); flex-wrap: wrap; }
776
- .divider-dot { color: var(--ink-faint); padding: 0 var(--s-1); }
777
-
778
- .post-result {
779
- margin-top: var(--s-3);
780
- font-family: var(--mono);
781
- font-size: 11.5px;
782
- color: var(--ink-muted);
783
- }
784
- .muted-text {
785
- color: var(--ink-muted);
786
- font-size: 12.5px;
787
- }
788
- .muted-text strong { color: var(--ink-mid); font-weight: 600; }
789
-
790
- /* Chips */
791
- .chip-row {
792
- display: flex;
793
- flex-wrap: wrap;
794
- gap: var(--s-2);
795
- flex: 1;
796
- }
797
- .chip {
798
- display: inline-flex;
799
- align-items: center;
800
- gap: 6px;
801
- padding: 5px 12px;
802
- border: 1px solid var(--border-strong);
803
- background: var(--bg-elev);
804
- color: var(--ink-mid);
805
- font-family: var(--body);
806
- font-size: 12.5px;
807
- font-weight: 500;
808
- cursor: pointer;
809
- user-select: none;
810
- transition: all .12s ease;
811
- border-radius: 999px;
812
- }
813
- .chip input { display: none; }
814
- .chip:hover {
815
- color: var(--ink);
816
- border-color: var(--ink-faint);
817
- }
818
- .chip::before {
819
- content: "";
820
- width: 7px;
821
- height: 7px;
822
- border: 1px solid var(--ink-faint);
823
- border-radius: 50%;
824
- background: transparent;
825
- }
826
- .chip.checked {
827
- background: var(--accent-soft);
828
- border-color: var(--accent);
829
- color: var(--accent-deep);
830
- }
831
- .chip.checked::before {
832
- background: var(--accent);
833
- border-color: var(--accent);
834
- box-shadow: inset 0 0 0 2px var(--accent-soft);
835
- }
836
-
837
- /* ─────────────────────────────────────────────────────────────
838
- Workspace cards
839
- ───────────────────────────────────────────────────────────── */
840
-
841
- .workspace-grid {
842
- display: grid;
843
- grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
844
- gap: var(--s-3);
845
- }
846
- .workspace-card {
847
- padding: var(--s-4);
848
- border: 1px solid var(--border);
849
- background: var(--bg-elev);
850
- border-radius: var(--r-md);
851
- transition: border-color .12s, box-shadow .12s;
852
- }
853
- .workspace-card:hover {
854
- border-color: var(--border-strong);
855
- box-shadow: var(--shadow);
856
- }
857
- .workspace-card.in-use {
858
- background: linear-gradient(180deg, rgba(196, 137, 43, 0.05), var(--bg-elev));
859
- border-color: rgba(196, 137, 43, 0.35);
860
- }
861
- .workspace-card .ws-head {
862
- display: flex;
863
- align-items: baseline;
864
- justify-content: space-between;
865
- gap: var(--s-2);
866
- margin-bottom: 4px;
867
- }
868
- .workspace-card .ws-name {
869
- font-size: 14.5px;
870
- font-weight: 600;
871
- letter-spacing: -0.01em;
872
- color: var(--ink);
873
- }
874
- .workspace-card .ws-tag {
875
- font-family: var(--mono);
876
- font-size: 10px;
877
- text-transform: uppercase;
878
- letter-spacing: 0.08em;
879
- padding: 2px 7px;
880
- border-radius: 4px;
881
- background: var(--bg);
882
- color: var(--ink-muted);
883
- border: 1px solid var(--border-soft);
884
- }
885
- .workspace-card.in-use .ws-tag {
886
- background: rgba(196, 137, 43, 0.10);
887
- color: var(--yellow);
888
- border-color: rgba(196, 137, 43, 0.3);
889
- }
890
- .workspace-card .ws-path {
891
- font-family: var(--mono);
892
- font-size: 11px;
893
- color: var(--ink-muted);
894
- word-break: break-all;
895
- margin-bottom: var(--s-3);
896
- }
897
- .workspace-card .ws-repos {
898
- display: flex;
899
- flex-wrap: wrap;
900
- gap: 4px;
901
- }
902
- .workspace-card .ws-repo {
903
- font-family: var(--mono);
904
- font-size: 10.5px;
905
- padding: 2px 7px;
906
- border-radius: 4px;
907
- background: var(--bg);
908
- color: var(--ink-muted);
909
- border: 1px solid var(--border-soft);
910
- }
911
- .workspace-card .ws-repo.cloned {
912
- color: var(--green);
913
- background: rgba(74, 138, 74, 0.06);
914
- border-color: rgba(74, 138, 74, 0.25);
915
- }
916
-
917
- /* ─────────────────────────────────────────────────────────────
918
- Config grid
919
- ───────────────────────────────────────────────────────────── */
920
-
921
- .config-grid {
922
- display: grid;
923
- grid-template-columns: 1fr 1fr;
924
- gap: var(--s-5) var(--s-6);
925
- align-items: start;
926
- }
927
- .config-grid .field { display: flex; flex-direction: column; gap: 6px; }
928
- .config-grid .field.full { grid-column: 1 / -1; }
929
- .config-grid .field .label {
930
- font-size: 12px;
931
- font-weight: 500;
932
- color: var(--ink-mid);
933
- }
934
- .config-grid .field .hint {
935
- font-size: 11.5px;
936
- color: var(--ink-muted);
937
- font-style: italic;
938
- }
939
- .config-grid .field .hint.inline {
940
- display: inline;
941
- margin-left: 4px;
942
- font-style: italic;
943
- }
944
- .config-grid .field.toggle {
945
- flex-direction: row;
946
- align-items: flex-start;
947
- gap: var(--s-3);
948
- padding-top: var(--s-4);
949
- }
950
- .config-grid .field.toggle .toggle-text {
951
- display: flex;
952
- flex-direction: column;
953
- gap: 2px;
954
- }
955
- .config-grid .field.toggle .label { font-weight: 500; }
956
-
957
- .repos-head {
958
- display: flex;
959
- justify-content: space-between;
960
- align-items: center;
961
- margin-bottom: var(--s-2);
962
- }
963
- .repos-table thead th, .repos-table tbody td {
964
- padding: 8px var(--s-2);
965
- }
966
- .repos-table thead th:first-child,
967
- .repos-table tbody td:first-child { padding-left: var(--s-2); }
968
- .repos-table thead th:last-child,
969
- .repos-table tbody td:last-child { padding-right: var(--s-2); }
970
- .repos-table tbody td input { font-size: 12px; max-width: none; }
971
-
972
- .form-actions {
973
- display: flex;
974
- align-items: center;
975
- gap: var(--s-3);
976
- margin-top: var(--s-4);
977
- }
978
-
979
- /* ─────────────────────────────────────────────────────────────
980
- Progress list
981
- ───────────────────────────────────────────────────────────── */
982
-
983
- .progress-list {
984
- display: flex;
985
- flex-direction: column;
986
- gap: var(--s-2);
987
- margin-top: var(--s-3);
988
- }
989
- .progress-list:empty { display: none; }
990
-
991
- .progress-item {
992
- border: 1px solid var(--border);
993
- background: var(--bg);
994
- border-radius: var(--r-sm);
995
- padding: var(--s-3) var(--s-4);
996
- }
997
- .progress-item .head {
998
- display: grid;
999
- grid-template-columns: 1fr auto auto;
1000
- align-items: baseline;
1001
- gap: var(--s-3);
1002
- margin-bottom: var(--s-2);
1003
- }
1004
- .progress-item .name {
1005
- font-family: var(--body);
1006
- font-size: 12.5px;
1007
- font-weight: 600;
1008
- color: var(--ink);
1009
- }
1010
- .progress-item .phase {
1011
- font-family: var(--mono);
1012
- font-size: 10.5px;
1013
- color: var(--ink-muted);
1014
- letter-spacing: 0.04em;
1015
- }
1016
- .progress-item .pct {
1017
- font-family: var(--mono);
1018
- font-size: 11.5px;
1019
- color: var(--accent);
1020
- font-variant-numeric: tabular-nums;
1021
- }
1022
- .progress-bar {
1023
- height: 3px;
1024
- background: var(--border);
1025
- position: relative;
1026
- overflow: hidden;
1027
- border-radius: 2px;
1028
- }
1029
- .progress-bar .fill {
1030
- height: 100%;
1031
- width: 0;
1032
- background: var(--accent);
1033
- transition: width .2s ease;
1034
- border-radius: 2px;
1035
- }
1036
- .progress-bar .fill.indeterminate {
1037
- width: 35% !important;
1038
- animation: indeterm 1.4s ease-in-out infinite;
1039
- }
1040
- @keyframes indeterm {
1041
- from { transform: translateX(-110%); }
1042
- to { transform: translateX(330%); }
1043
- }
1044
- .progress-item.ok .fill { background: var(--green); }
1045
- .progress-item.error .fill { background: var(--red); }
1046
- .progress-item .detail {
1047
- margin-top: 4px;
1048
- font-family: var(--mono);
1049
- font-size: 10.5px;
1050
- color: var(--ink-muted);
1051
- min-height: 12px;
1052
- }
1053
-
1054
- /* ─────────────────────────────────────────────────────────────
1055
- Pagination (recently closed)
1056
- ───────────────────────────────────────────────────────────── */
1057
-
1058
- .pagination {
1059
- display: flex;
1060
- align-items: center;
1061
- gap: var(--s-3);
1062
- padding: var(--s-3) var(--s-6);
1063
- border-top: 1px solid var(--border-soft);
1064
- background: var(--bg);
1065
- font-size: 12.5px;
1066
- color: var(--ink-mid);
1067
- flex-wrap: wrap;
1068
- }
1069
- .pagination-info {
1070
- flex: 1;
1071
- text-align: center;
1072
- font-family: var(--mono);
1073
- font-size: 11.5px;
1074
- color: var(--ink-muted);
1075
- }
1076
- .pagination-info strong {
1077
- color: var(--ink);
1078
- font-weight: 600;
1079
- }
1080
- .pagination select {
1081
- font-size: 11.5px;
1082
- padding: 4px 24px 4px 8px;
1083
- }
1084
-
1085
- /* ─────────────────────────────────────────────────────────────
1086
- Snapshot preview
1087
- ───────────────────────────────────────────────────────────── */
1088
-
1089
- .snapshot-detail {
1090
- margin-top: var(--s-4);
1091
- }
1092
- .snapshot-detail summary {
1093
- cursor: pointer;
1094
- font-size: 12.5px;
1095
- color: var(--ink-mid);
1096
- padding: var(--s-2) 0;
1097
- user-select: none;
1098
- font-weight: 500;
1099
- }
1100
- .snapshot-detail summary::marker { color: var(--accent); }
1101
- .snapshot-detail summary:hover { color: var(--ink); }
1102
- .preview {
1103
- font-family: var(--mono);
1104
- font-size: 11.5px;
1105
- color: var(--ink-mid);
1106
- background: var(--bg);
1107
- border: 1px solid var(--border);
1108
- border-radius: var(--r-sm);
1109
- padding: var(--s-3);
1110
- margin-top: var(--s-2);
1111
- max-height: 280px;
1112
- overflow: auto;
1113
- white-space: pre;
1114
- line-height: 1.55;
1115
- }
1116
-
1117
- /* ─────────────────────────────────────────────────────────────
1118
- Footer status line
1119
- ───────────────────────────────────────────────────────────── */
1120
-
1121
- .footer-status {
1122
- margin-top: auto;
1123
- padding-top: var(--s-4);
1124
- border-top: 1px solid var(--border-soft);
1125
- display: flex;
1126
- flex-wrap: wrap;
1127
- align-items: baseline;
1128
- gap: var(--s-2);
1129
- font-size: 11px;
1130
- color: var(--ink-muted);
1131
- }
1132
- .footer-status .fs-key {
1133
- text-transform: uppercase;
1134
- letter-spacing: 0.08em;
1135
- font-size: 10px;
1136
- color: var(--ink-faint);
1137
- }
1138
- .footer-status .fs-val {
1139
- font-family: var(--mono);
1140
- font-size: 11px;
1141
- color: var(--ink-mid);
1142
- }
1143
- .footer-status .fs-divider { color: var(--ink-faint); margin: 0 var(--s-1); }
1144
-
1145
- /* ─────────────────────────────────────────────────────────────
1146
- Toast
1147
- ───────────────────────────────────────────────────────────── */
1148
-
1149
- .toast {
1150
- position: fixed;
1151
- bottom: var(--s-6);
1152
- right: var(--s-6);
1153
- z-index: 100;
1154
- max-width: 420px;
1155
- padding: var(--s-3) var(--s-4);
1156
- background: var(--bg-elev);
1157
- border: 1px solid var(--border-strong);
1158
- border-left: 3px solid var(--accent);
1159
- border-radius: var(--r-sm);
1160
- font-size: 13px;
1161
- color: var(--ink);
1162
- letter-spacing: -0.005em;
1163
- opacity: 0;
1164
- transform: translateY(8px);
1165
- transition: opacity .2s ease, transform .2s ease;
1166
- pointer-events: none;
1167
- box-shadow: var(--shadow-md);
1168
- }
1169
- .toast.show {
1170
- opacity: 1;
1171
- transform: translateY(0);
1172
- }
1173
- .toast.error { border-left-color: var(--red); }
1174
- .toast.ok { border-left-color: var(--green); }
1175
-
1176
- /* ─────────────────────────────────────────────────────────────
1177
- Small utilities
1178
- ───────────────────────────────────────────────────────────── */
1179
-
1180
- code, .kbd {
1181
- font-family: var(--mono);
1182
- font-size: 11.5px;
1183
- padding: 1px 5px;
1184
- background: var(--bg);
1185
- border: 1px solid var(--border-soft);
1186
- border-radius: 4px;
1187
- color: var(--ink-mid);
1188
- }
1189
-
1190
- /* Scrollbar */
1191
- ::-webkit-scrollbar { width: 10px; height: 10px; }
1192
- ::-webkit-scrollbar-track { background: transparent; }
1193
- ::-webkit-scrollbar-thumb { background: var(--border-strong); border-radius: 8px; border: 2px solid var(--bg); }
1194
- ::-webkit-scrollbar-thumb:hover { background: var(--ink-faint); }
1195
-
1196
- /* Responsive — narrow screens collapse sidebar */
1197
- @media (max-width: 900px) {
1198
- .app { grid-template-columns: var(--sidebar-w-collapsed) 1fr !important; }
1199
- .sidebar { padding: var(--s-4) var(--s-2); }
1200
- .brand-name, .nav-label, .nav-badge { opacity: 0; pointer-events: none; }
1201
- .main { padding: var(--s-6) var(--s-5) var(--s-5); }
1202
- .page-head { flex-direction: column; gap: var(--s-3); }
1203
- .config-grid { grid-template-columns: 1fr; }
1204
- }