@bakapiano/ccsm 0.9.0 → 0.10.1

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 (69) hide show
  1. package/CLAUDE.md +222 -195
  2. package/README.md +77 -79
  3. package/lib/cliSessionWatcher.js +249 -0
  4. package/lib/config.js +101 -24
  5. package/lib/folders.js +96 -0
  6. package/lib/localCliSessions.js +177 -0
  7. package/lib/persistedSessions.js +134 -0
  8. package/lib/webTerminal.js +31 -18
  9. package/lib/workspace.js +26 -4
  10. package/package.json +1 -1
  11. package/public/assets/claude-color.svg +1 -0
  12. package/public/assets/codex-color.svg +1 -0
  13. package/public/assets/copilot-color.svg +1 -0
  14. package/public/css/base.css +22 -5
  15. package/public/css/cards.css +37 -3
  16. package/public/css/feedback.css +127 -43
  17. package/public/css/forms.css +97 -25
  18. package/public/css/layout.css +74 -26
  19. package/public/css/modal.css +40 -26
  20. package/public/css/responsive.css +2 -2
  21. package/public/css/sidebar.css +424 -25
  22. package/public/css/terminals.css +138 -0
  23. package/public/css/tokens.css +28 -12
  24. package/public/css/wco.css +38 -39
  25. package/public/css/widgets.css +1177 -6
  26. package/public/index.html +35 -2
  27. package/public/js/api.js +194 -37
  28. package/public/js/components/AdoptModal.js +171 -0
  29. package/public/js/components/App.js +1 -11
  30. package/public/js/components/DirectoryPicker.js +203 -0
  31. package/public/js/components/EntityFormModal.js +105 -0
  32. package/public/js/components/Modal.js +51 -0
  33. package/public/js/components/OfflineBanner.js +29 -23
  34. package/public/js/components/PageTitleBar.js +13 -0
  35. package/public/js/components/Picker.js +179 -0
  36. package/public/js/components/Popover.js +55 -0
  37. package/public/js/components/Sidebar.js +219 -32
  38. package/public/js/components/TerminalView.js +27 -3
  39. package/public/js/components/useDragSort.js +67 -0
  40. package/public/js/dialog.js +10 -2
  41. package/public/js/icons.js +66 -3
  42. package/public/js/main.js +54 -3
  43. package/public/js/pages/AboutPage.js +80 -0
  44. package/public/js/pages/ConfigurePage.js +429 -207
  45. package/public/js/pages/LaunchPage.js +326 -86
  46. package/public/js/pages/SessionsPage.js +91 -41
  47. package/public/js/state.js +102 -73
  48. package/public/manifest.webmanifest +2 -2
  49. package/scripts/install.js +7 -2
  50. package/server.js +755 -441
  51. package/lib/favorites.js +0 -51
  52. package/lib/focus.js +0 -369
  53. package/lib/labels.js +0 -29
  54. package/lib/launcher.js +0 -219
  55. package/lib/sessions.js +0 -272
  56. package/lib/snapshot.js +0 -141
  57. package/public/js/actions.js +0 -107
  58. package/public/js/components/Fab.js +0 -11
  59. package/public/js/components/FavoritesTable.js +0 -81
  60. package/public/js/components/Footer.js +0 -12
  61. package/public/js/components/NewSessionModal.js +0 -153
  62. package/public/js/components/PageHead.js +0 -33
  63. package/public/js/components/Pagination.js +0 -27
  64. package/public/js/components/RecentTable.js +0 -68
  65. package/public/js/components/SessionsTable.js +0 -71
  66. package/public/js/components/SnapshotPanel.js +0 -77
  67. package/public/js/components/TitleCell.js +0 -40
  68. package/public/js/components/WorkspacesGrid.js +0 -41
  69. package/public/js/pages/TerminalsPage.js +0 -74
@@ -2,10 +2,15 @@
2
2
 
3
3
  .card {
4
4
  background: var(--bg-elev);
5
- border: 1px solid var(--border);
6
- border-radius: var(--r-md);
5
+ border: 1px solid var(--border-soft);
6
+ border-radius: 6px;
7
7
  overflow: hidden;
8
8
  box-shadow: var(--shadow);
9
+ transition: box-shadow .2s ease, border-color .2s ease, transform .2s ease;
10
+ }
11
+ .card:hover {
12
+ box-shadow: var(--shadow-md);
13
+ border-color: var(--border);
9
14
  }
10
15
 
11
16
  .card-head {
@@ -56,6 +61,7 @@
56
61
  font-weight: 600;
57
62
  letter-spacing: -0.012em;
58
63
  color: var(--ink);
64
+ line-height: 1.2;
59
65
  }
60
66
  .card-title .title-icon {
61
67
  color: var(--ink-mid);
@@ -92,10 +98,11 @@
92
98
  .about-mark { display: inline-flex; }
93
99
  .about-mark svg { width: 48px; height: 48px; }
94
100
  .about-name {
95
- font-size: 20px;
101
+ font-size: 22px;
96
102
  font-weight: 600;
97
103
  letter-spacing: -0.02em;
98
104
  color: var(--ink);
105
+ line-height: 1.1;
99
106
  }
100
107
  .about-version {
101
108
  font-family: var(--mono);
@@ -104,6 +111,33 @@
104
111
  margin-left: 6px;
105
112
  font-weight: 400;
106
113
  }
114
+ .about-version-row {
115
+ display: flex;
116
+ align-items: center;
117
+ justify-content: space-between;
118
+ gap: 16px;
119
+ flex-wrap: wrap;
120
+ }
121
+ .about-version-line {
122
+ font-size: 13px;
123
+ color: var(--ink);
124
+ }
125
+ .about-update-line {
126
+ margin-top: 4px;
127
+ font-size: 12.5px;
128
+ color: var(--ink-mid);
129
+ font-weight: 500;
130
+ }
131
+ .about-version-actions {
132
+ display: inline-flex;
133
+ align-items: center;
134
+ gap: 6px;
135
+ }
136
+ .muted-text {
137
+ font-size: 11.5px;
138
+ color: var(--ink-muted);
139
+ line-height: 1.5;
140
+ }
107
141
  .about-tagline {
108
142
  margin-top: 2px;
109
143
  font-size: 13px;
@@ -111,70 +111,154 @@
111
111
  /* Offline banner · shown when /api/health is unreachable. Sticks to the
112
112
  top of the main column above all cards. Contains a ccsm:// link that
113
113
  asks Windows to spawn the backend. */
114
- .offline-banner {
115
- background: #fff7e8;
116
- border: 1px solid #d4b27a;
117
- border-radius: var(--r-sm);
118
- padding: var(--s-3) var(--s-5);
119
- position: sticky;
120
- top: var(--s-3);
121
- z-index: 50;
122
- box-shadow: 0 3px 10px -8px rgba(26, 24, 21, 0.15);
123
- }
124
- .offline-banner-inner {
114
+ /* Offline overlay — full-screen modal that blocks all interaction
115
+ until the backend comes back. Z-index above dialogs / toasts. */
116
+ .offline-overlay {
117
+ position: fixed;
118
+ inset: 0;
119
+ z-index: 200;
120
+ background: rgba(26, 24, 21, 0.55);
121
+ backdrop-filter: blur(6px);
125
122
  display: flex;
126
123
  align-items: center;
127
- gap: var(--s-3);
124
+ justify-content: center;
125
+ padding: var(--s-6);
126
+ animation: panel-in .2s ease-out;
128
127
  }
129
- .offline-banner-text {
130
- flex: 1;
128
+ .offline-card {
129
+ background: var(--bg-elev);
130
+ border: 1px solid var(--ui-border);
131
+ border-radius: var(--r-lg);
132
+ box-shadow: 0 24px 60px -20px rgba(0,0,0,0.4);
133
+ padding: var(--s-8) var(--s-8) var(--s-6);
134
+ max-width: 480px;
135
+ width: 100%;
136
+ text-align: center;
131
137
  display: flex;
132
138
  flex-direction: column;
133
- gap: 2px;
134
- font-size: 13px;
135
- color: var(--ink);
139
+ align-items: center;
140
+ gap: var(--s-3);
141
+ }
142
+ .offline-brand {
143
+ width: 56px;
144
+ height: 56px;
145
+ display: inline-flex;
146
+ align-items: center;
147
+ justify-content: center;
148
+ }
149
+ .offline-brand svg { width: 100%; height: 100%; }
150
+ .offline-title {
151
+ margin: 0;
152
+ font-size: 22px;
153
+ font-weight: 600;
136
154
  }
137
- .offline-banner-text .muted-text {
138
- font-size: 11.5px;
155
+ .offline-copy {
156
+ margin: 0;
139
157
  color: var(--ink-mid);
158
+ font-size: 13.5px;
159
+ line-height: 1.55;
160
+ max-width: 380px;
140
161
  }
141
- .offline-banner-actions { display: flex; gap: var(--s-2); }
142
- .offline-dot {
143
- width: 10px;
144
- height: 10px;
145
- border-radius: 50%;
146
- background: var(--red);
147
- flex: 0 0 10px;
148
- animation: dirty-pulse 2s ease-in-out infinite;
162
+ .offline-actions {
163
+ margin-top: var(--s-2);
164
+ }
165
+ .offline-actions .action.big {
166
+ padding: 10px 22px;
167
+ font-size: 14px;
168
+ font-weight: 500;
169
+ }
170
+ .offline-fallback {
171
+ margin-top: var(--s-3);
172
+ width: 100%;
173
+ text-align: left;
174
+ font-size: 12px;
175
+ color: var(--ink-mid);
149
176
  }
177
+ .offline-fallback summary {
178
+ cursor: pointer;
179
+ text-align: center;
180
+ color: var(--ink-muted);
181
+ }
182
+ .offline-fallback-body {
183
+ margin-top: var(--s-3);
184
+ padding: var(--s-3);
185
+ background: var(--bg);
186
+ border-radius: var(--r-sm);
187
+ }
188
+ .offline-fallback-body pre {
189
+ margin: var(--s-2) 0;
190
+ padding: 8px 10px;
191
+ background: var(--ink);
192
+ color: var(--bg-elev);
193
+ border-radius: 4px;
194
+ font-family: var(--mono);
195
+ font-size: 12px;
196
+ overflow-x: auto;
197
+ }
198
+ .offline-fallback-body p { margin: 0; }
199
+
200
+ /* Legacy banner styles (unused after v1.0 redesign but kept tiny in
201
+ case external CSS references them). */
202
+ .offline-banner { display: none; }
150
203
 
151
204
  /* Toast bottom-right — auto-hides via JS */
152
205
  .toast {
153
206
  position: fixed;
154
- bottom: var(--s-6);
155
- right: var(--s-6);
207
+ bottom: var(--s-5);
208
+ right: var(--s-5);
156
209
  z-index: 100;
157
- max-width: 420px;
158
- padding: var(--s-3) var(--s-4);
159
- background: var(--bg-elev);
160
- border: 1px solid var(--border-strong);
161
- border-left: 3px solid var(--ink);
162
- border-radius: var(--r-sm);
163
- font-size: 13px;
164
- color: var(--ink);
210
+ max-width: 380px;
211
+ padding: 11px 16px 11px 14px;
212
+ background: var(--ink);
213
+ color: var(--bg);
214
+ border: 0;
215
+ border-radius: 6px;
216
+ font-size: 12.5px;
217
+ font-weight: 400;
165
218
  letter-spacing: -0.005em;
219
+ line-height: 1.45;
220
+ display: flex;
221
+ align-items: center;
222
+ gap: 12px;
166
223
  opacity: 0;
167
- transform: translateY(8px);
168
- transition: opacity .2s ease, transform .2s ease;
224
+ transform: translateY(10px) scale(0.98);
225
+ transform-origin: bottom right;
226
+ transition:
227
+ opacity .22s cubic-bezier(.4, 0, .2, 1),
228
+ transform .28s cubic-bezier(.34, 1.4, .64, 1);
169
229
  pointer-events: none;
170
- box-shadow: var(--shadow-md);
230
+ box-shadow:
231
+ 0 10px 32px -8px rgba(26, 24, 21, 0.30),
232
+ 0 2px 6px rgba(26, 24, 21, 0.12),
233
+ inset 0 0 0 1px rgba(255, 255, 255, 0.06);
234
+ }
235
+ .toast::before {
236
+ content: "OK";
237
+ font-family: var(--mono);
238
+ font-size: 9px;
239
+ letter-spacing: 0.14em;
240
+ font-weight: 500;
241
+ padding: 3px 6px;
242
+ border-radius: 4px;
243
+ background: rgba(255, 255, 255, 0.10);
244
+ color: rgba(255, 255, 255, 0.75);
245
+ flex-shrink: 0;
246
+ line-height: 1;
171
247
  }
172
248
  .toast.show {
173
249
  opacity: 1;
174
- transform: translateY(0);
250
+ transform: translateY(0) scale(1);
251
+ }
252
+ .toast.error::before {
253
+ content: "ERR";
254
+ background: rgba(183, 63, 63, 0.85);
255
+ color: #fff;
256
+ }
257
+ .toast.ok::before {
258
+ content: "OK";
259
+ background: rgba(74, 138, 74, 0.85);
260
+ color: #fff;
175
261
  }
176
- .toast.error { border-left-color: var(--red); }
177
- .toast.ok { border-left-color: var(--green); }
178
262
 
179
263
  /* Sticky "you have unsaved changes" banner above the Configure card */
180
264
  .dirty-banner {
@@ -11,25 +11,35 @@
11
11
  font-size: 13px;
12
12
  font-weight: 500;
13
13
  letter-spacing: -0.005em;
14
- border-radius: var(--r-sm);
14
+ border-radius: 6px;
15
15
  cursor: pointer;
16
- transition: background .12s ease, border-color .12s ease, color .12s ease, box-shadow .12s ease;
16
+ transition:
17
+ background .14s ease,
18
+ border-color .14s ease,
19
+ color .14s ease,
20
+ box-shadow .14s ease,
21
+ transform .14s cubic-bezier(.4, 0, .2, 1);
17
22
  white-space: nowrap;
18
23
  box-shadow: var(--shadow-sm);
19
- /* Buttons always lay out as icon-left + text-right, gap between them,
20
- and the cluster is centered inside the button's box. Anchor links
21
- using .action (e.g. About page) inherit the same layout. */
22
24
  display: inline-flex;
23
25
  align-items: center;
24
26
  justify-content: center;
25
27
  gap: 6px;
26
28
  text-decoration: none;
29
+ position: relative;
27
30
  }
28
31
  .action:hover {
29
32
  background: var(--bg);
30
33
  border-color: var(--ink-faint);
34
+ box-shadow: 0 2px 4px -2px rgba(26, 24, 21, 0.10);
35
+ transform: translateY(-0.5px);
36
+ }
37
+ .action:active { transform: translateY(0.5px); box-shadow: var(--shadow-sm); }
38
+ .action:focus-visible {
39
+ outline: none;
40
+ box-shadow: 0 0 0 3px rgba(26, 24, 21, 0.10);
41
+ border-color: var(--ink);
31
42
  }
32
- .action:active { transform: translateY(0.5px); }
33
43
  .action:disabled { opacity: .5; cursor: not-allowed; pointer-events: none; }
34
44
  .action.primary {
35
45
  background: var(--ink);
@@ -39,6 +49,7 @@
39
49
  .action.primary:hover {
40
50
  background: #000;
41
51
  border-color: #000;
52
+ box-shadow: 0 4px 12px -4px rgba(26, 24, 21, 0.35);
42
53
  }
43
54
  .action.small { font-size: 12px; padding: 4px 10px; }
44
55
  .action.tiny { font-size: 11px; padding: 3px 8px; }
@@ -64,11 +75,11 @@
64
75
  background: var(--bg-elev);
65
76
  border: 1px solid var(--border-strong);
66
77
  color: var(--ink);
67
- padding: 7px 12px;
78
+ padding: 8px 12px;
68
79
  font-family: var(--body);
69
80
  font-size: 13px;
70
- border-radius: var(--r-sm);
71
- transition: border-color .12s ease, box-shadow .12s ease;
81
+ border-radius: 6px;
82
+ transition: border-color .14s ease, box-shadow .14s ease, background .14s ease;
72
83
  width: 100%;
73
84
  max-width: 480px;
74
85
  }
@@ -203,6 +214,7 @@ input[type="checkbox"]:checked::after {
203
214
  font-weight: 500;
204
215
  cursor: pointer;
205
216
  user-select: none;
217
+ appearance: none;
206
218
  transition: all .12s ease;
207
219
  border-radius: 999px;
208
220
  }
@@ -233,7 +245,7 @@ input[type="checkbox"]:checked::after {
233
245
  /* Two-column field grid used inside the Configure card */
234
246
  .config-grid {
235
247
  display: grid;
236
- grid-template-columns: 1fr 1fr;
248
+ grid-template-columns: 1fr;
237
249
  gap: var(--s-5) var(--s-6);
238
250
  align-items: start;
239
251
  }
@@ -281,40 +293,88 @@ input[type="checkbox"]:checked::after {
281
293
  .repos-table tbody td:last-child { padding-right: var(--s-2); }
282
294
  .repos-table tbody td input { font-size: 12px; max-width: none; }
283
295
 
284
- /* Theme accent picker (Configure tab). 8 swatches + native color picker
285
- + hex input + reset. Swatches are filled circles; the active one wears
286
- a subtle ring in --ink so it pops on any accent. */
296
+ /* Theme accent picker (Configure tab). Pill chips with color dot + name;
297
+ active chip fills with its accent color. A "Custom" chip toggles an
298
+ inline color picker + hex input + reset. */
287
299
  .accent-picker {
288
300
  display: flex;
289
301
  flex-direction: column;
290
302
  gap: var(--s-3);
291
303
  }
292
- .accent-swatches {
304
+ .accent-chips {
293
305
  display: flex;
294
306
  flex-wrap: wrap;
295
- gap: var(--s-2);
307
+ gap: 6px;
296
308
  }
297
- .accent-swatch {
309
+ .accent-chip {
298
310
  appearance: none;
299
- width: 24px;
300
- height: 24px;
311
+ display: inline-flex;
312
+ align-items: center;
313
+ gap: 6px;
314
+ padding: 5px 11px 5px 9px;
315
+ border: 1px solid var(--border);
301
316
  border-radius: 999px;
302
- border: 1px solid rgba(0, 0, 0, 0.1);
317
+ background: var(--bg-elev);
318
+ color: var(--ink-mid);
319
+ font-size: 12px;
320
+ font-weight: 500;
321
+ letter-spacing: -0.005em;
322
+ line-height: 1;
303
323
  cursor: pointer;
304
- padding: 0;
305
- transition: transform .12s ease, box-shadow .12s ease;
324
+ transition: background .12s ease, color .12s ease,
325
+ border-color .12s ease, transform .12s ease,
326
+ box-shadow .12s ease;
306
327
  }
307
- .accent-swatch:hover { transform: scale(1.08); }
308
- .accent-swatch.is-active {
309
- box-shadow: 0 0 0 2px var(--bg), 0 0 0 4px var(--ink);
328
+ .accent-chip:hover {
329
+ color: var(--ink);
330
+ border-color: var(--ink-faint);
331
+ transform: translateY(-1px);
310
332
  }
333
+ .accent-chip-dot {
334
+ width: 10px;
335
+ height: 10px;
336
+ border-radius: 999px;
337
+ background: var(--c, var(--ink-faint));
338
+ box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.08);
339
+ flex: none;
340
+ }
341
+ .accent-chip-plus {
342
+ display: inline-flex;
343
+ align-items: center;
344
+ justify-content: center;
345
+ width: 10px;
346
+ height: 10px;
347
+ color: var(--ink-mid);
348
+ font-size: 14px;
349
+ line-height: 1;
350
+ }
351
+ .accent-chip.is-active {
352
+ background: var(--c);
353
+ border-color: var(--c);
354
+ color: #fff;
355
+ box-shadow: 0 1px 0 rgba(0, 0, 0, 0.05);
356
+ }
357
+ .accent-chip.is-active .accent-chip-dot {
358
+ background: rgba(255, 255, 255, 0.85);
359
+ box-shadow: none;
360
+ }
361
+ .accent-chip-custom {
362
+ border-style: dashed;
363
+ }
364
+ .accent-chip-custom.is-open:not(.is-active) {
365
+ border-style: solid;
366
+ color: var(--ink);
367
+ border-color: var(--ink-faint);
368
+ }
369
+
311
370
  .accent-custom {
312
371
  display: flex;
313
372
  align-items: center;
314
373
  gap: var(--s-2);
374
+ padding-top: 2px;
315
375
  }
316
376
  .accent-custom input[type="color"] {
317
- width: 36px;
377
+ width: 32px;
318
378
  height: 28px;
319
379
  padding: 0;
320
380
  border: 1px solid var(--border);
@@ -331,3 +391,15 @@ input[type="checkbox"]:checked::after {
331
391
  border-radius: var(--r-sm);
332
392
  background: var(--bg-elev);
333
393
  }
394
+ .accent-reset {
395
+ appearance: none;
396
+ background: transparent;
397
+ border: 0;
398
+ padding: 4px 6px;
399
+ font-size: 12px;
400
+ color: var(--ink-faint);
401
+ cursor: pointer;
402
+ border-radius: 6px;
403
+ transition: color .12s ease, background .12s ease;
404
+ }
405
+ .accent-reset:hover { color: var(--ink-mid); background: var(--bg); }
@@ -3,7 +3,13 @@
3
3
  .app {
4
4
  display: grid;
5
5
  grid-template-columns: var(--sidebar-w) 1fr;
6
- min-height: 100vh;
6
+ height: 100vh;
7
+ /* Pin to viewport height (not min-height) — child flex columns set
8
+ min-height:0 and rely on a bounded parent height to compute their own
9
+ space. With min-height:100vh, a transient xterm resize that briefly
10
+ reports a too-large cell count makes the WebGL canvas grow, .session-pane
11
+ grows to fit, .main grows, .app grows... cascading into a multi-hundred-
12
+ thousand-pixel tall page. height:100vh caps the cascade. */
7
13
  transition: grid-template-columns .25s cubic-bezier(.4, 0, .2, 1);
8
14
  }
9
15
  .app:has(.sidebar[data-collapsed="true"]) {
@@ -19,8 +25,13 @@ body.is-resizing-sidebar .app {
19
25
  display: flex;
20
26
  flex-direction: column;
21
27
  min-width: 0;
22
- padding: var(--s-8) var(--s-10) var(--s-6);
23
- gap: var(--s-6);
28
+ /* grid items default to min-height:auto which lets internal content
29
+ (eg a misbehaving xterm canvas that briefly reports an absurd height)
30
+ overflow the grid track and balloon the whole page. min-height:0
31
+ restores the expected "fit my grid cell" behaviour. */
32
+ min-height: 0;
33
+ padding: 0 var(--s-4);
34
+ gap: var(--s-2);
24
35
  }
25
36
 
26
37
  .page-head {
@@ -34,6 +45,7 @@ body.is-resizing-sidebar .app {
34
45
  .page-head-inner { min-width: 0; }
35
46
 
36
47
  .page-title {
48
+ font-family: var(--display);
37
49
  font-size: 26px;
38
50
  font-weight: 600;
39
51
  letter-spacing: -0.024em;
@@ -71,42 +83,78 @@ body.is-resizing-sidebar .app {
71
83
  flex: 1;
72
84
  display: flex;
73
85
  flex-direction: column;
86
+ min-height: 0;
74
87
  }
75
88
 
76
89
  .tab-panel {
77
90
  display: none;
78
91
  flex-direction: column;
79
- gap: var(--s-6);
92
+ gap: var(--s-4);
93
+ min-height: 0;
80
94
  }
81
95
  .tab-panel[data-active] {
82
96
  display: flex;
83
- animation: panel-in .35s cubic-bezier(.4, 0, .2, 1);
84
- }
85
- @keyframes panel-in {
86
- from { opacity: 0; transform: translateY(6px); }
87
- to { opacity: 1; transform: translateY(0); }
97
+ flex: 1;
88
98
  }
89
99
 
90
- .footer-status {
91
- margin-top: auto;
92
- padding-top: var(--s-4);
93
- border-top: 1px solid var(--border-soft);
100
+ /* Per-page title strip — height matches the sidebar collapse-toggle
101
+ (28px) so the topmost row of the window reads as one unified band. */
102
+ .page-title-bar {
94
103
  display: flex;
95
- flex-wrap: wrap;
96
- align-items: baseline;
104
+ flex-direction: row-reverse;
105
+ align-items: center;
106
+ justify-content: flex-start;
107
+ gap: var(--s-3);
108
+ height: 40px;
109
+ margin: 0 calc(-1 * var(--s-4)) 0;
110
+ padding: 0 var(--s-5);
111
+ /* Replace the hard inset shadow rule with a soft gradient fade — gives
112
+ the top band a printed-paper masthead feeling rather than a CSS
113
+ divider. */
114
+ background:
115
+ linear-gradient(to bottom, rgba(216, 212, 198, 0.0) 0%, rgba(216, 212, 198, 0.0) calc(100% - 1px), var(--ui-border-soft) 100%);
116
+ color: var(--ink);
117
+ font-size: 13px;
118
+ font-weight: 400;
119
+ line-height: 1.4;
120
+ }
121
+ .page-title-bar-title {
122
+ margin: 0;
123
+ font-size: 13px;
124
+ font-weight: 400;
125
+ display: flex;
126
+ align-items: center;
97
127
  gap: var(--s-2);
98
- font-size: 11px;
99
- color: var(--ink-muted);
128
+ min-width: 0;
129
+ flex: 1;
130
+ overflow: hidden;
131
+ line-height: 1.4;
100
132
  }
101
- .footer-status .fs-key {
102
- text-transform: uppercase;
103
- letter-spacing: 0.08em;
104
- font-size: 10px;
105
- color: var(--ink-faint);
133
+ .page-title-bar-actions {
134
+ display: flex;
135
+ align-items: center;
136
+ gap: var(--s-2);
137
+ flex-shrink: 0;
138
+ }
139
+ .session-title-text {
140
+ font-weight: 500;
141
+ font-size: 13px;
142
+ letter-spacing: -0.005em;
143
+ color: var(--ink);
144
+ flex-shrink: 0;
106
145
  }
107
- .footer-status .fs-val {
146
+ .session-title-meta {
147
+ display: flex;
148
+ align-items: center;
149
+ gap: 6px;
150
+ color: var(--ink-muted);
151
+ font-size: 11.5px;
152
+ overflow: hidden;
153
+ white-space: nowrap;
154
+ }
155
+ .session-title-meta .mono {
108
156
  font-family: var(--mono);
109
- font-size: 11px;
110
- color: var(--ink-mid);
157
+ overflow: hidden;
158
+ text-overflow: ellipsis;
159
+ max-width: 40vw;
111
160
  }
112
- .footer-status .fs-divider { color: var(--ink-faint); margin: 0 var(--s-1); }