@bivy/bivy 0.17.0 → 0.17.1-staging.10

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.
@@ -169,6 +169,8 @@ input, textarea, select { font-family: inherit; font-size: 16px; }
169
169
  .badge[data-variant="soft"][data-tone="ok"] { background: color-mix(in srgb, var(--ok) 16%, var(--surface)); color: var(--ok); }
170
170
  .badge[data-variant="soft"][data-tone="warn"] { background: var(--warn-soft); color: var(--warn); }
171
171
  .badge[data-variant="soft"][data-tone="danger"] { background: var(--danger-soft); color: var(--danger); }
172
+ .badge[data-variant="soft"][data-tone="merged"] { background: color-mix(in srgb, var(--merged) 16%, var(--surface)); color: var(--merged); }
173
+ .badge[data-variant="soft"]:not([data-tone]) { background: var(--surface-3); color: var(--muted); }
172
174
  .badge[data-variant="solid"] { border-color: transparent; color: var(--accent-contrast); }
173
175
  .badge[data-variant="solid"][data-tone="accent"] { background: var(--accent); }
174
176
  .badge[data-variant="solid"][data-tone="ok"] { background: var(--ok); }
@@ -211,29 +213,39 @@ input, textarea, select { font-family: inherit; font-size: 16px; }
211
213
  }
212
214
  .sidebar-head {
213
215
  display: flex; align-items: center; justify-content: space-between;
214
- padding: 14px 16px; border-bottom: 1px solid var(--line);
216
+ min-height: 56px; padding: var(--space-2) var(--space-2) var(--space-1) var(--space-4);
215
217
  /* As a fixed drawer on mobile the sidebar starts at the very top of the
216
218
  screen, so its header must clear the status bar / notch the same way the
217
- main .topbar does — otherwise "Bivy" and "+ New" sit under the clock. */
218
- padding-top: max(14px, env(safe-area-inset-top));
219
+ main .topbar does — otherwise the wordmark sits under the clock. */
220
+ padding-top: max(var(--space-2), env(safe-area-inset-top));
219
221
  }
220
- .sidebar-head-actions { display: flex; align-items: center; gap: 8px; }
221
- .brand { font-family: var(--font-serif); font-weight: 500; font-size: 17px; letter-spacing: 0; }
222
+ .sidebar-head-actions { display: flex; align-items: center; gap: var(--space-2); }
223
+ .brand { font-family: var(--font-serif); font-weight: 500; font-size: var(--text-xl); letter-spacing: 0; }
222
224
  /* Primary destinations sit above the session stream. Keeping Automations here
223
225
  makes it discoverable without competing with the quieter Settings footer. */
224
- .sidebar-nav { padding: var(--space-2) 0 var(--space-3); }
226
+ .sidebar-nav { display: flex; flex-direction: column; gap: 2px; padding: 0 0 var(--space-3); }
225
227
  .sidebar-nav-item {
226
- display: flex; align-items: center; gap: 10px; width: 100%; min-height: 40px;
227
- padding: 9px 10px; border: 0; border-radius: var(--radius-sm);
228
- background: transparent; color: var(--ink); font: inherit; font-size: var(--text-sm); font-weight: var(--weight-medium);
228
+ display: flex; align-items: center; gap: var(--space-3); width: 100%; min-height: 40px;
229
+ padding: var(--space-2); border: 0; border-radius: var(--radius-md);
230
+ background: transparent; color: var(--ink); font: inherit; font-size: var(--text-md); font-weight: var(--weight-normal);
229
231
  text-align: left; cursor: pointer; transition: color var(--dur) var(--ease-standard), background var(--dur) var(--ease-standard);
230
232
  }
231
- .sidebar-nav-item svg { color: var(--accent); flex: none; }
232
- .sidebar-nav-item:hover { background: var(--surface-2); }
233
- .sidebar-nav-item.active { background: transparent; color: var(--accent); }
234
- .sidebar-nav-item.active:hover { background: var(--surface-2); }
235
- .sidebar-nav-chevron { margin-left: auto; color: var(--muted); font-size: var(--text-xl); font-weight: var(--weight-normal); line-height: 1; }
236
- .sidebar-foot { margin-top: auto; padding: var(--space-2); border-top: 1px solid var(--line); display: flex; flex-direction: column; gap: var(--space-1); }
233
+ .sidebar-nav-item svg { color: var(--ink); flex: none; }
234
+ .sidebar-nav-item:hover:not(:disabled) { background: var(--surface-2); }
235
+ .sidebar-nav-item:disabled { opacity: 0.45; cursor: default; }
236
+ .sidebar-nav-item.active { background: var(--surface-2); }
237
+ /* Foot: the single primary action (New session) + the Settings gear. No rule
238
+ above it — the list simply scrolls underneath. */
239
+ .sidebar-foot {
240
+ margin-top: auto; display: flex; align-items: center; justify-content: space-between; gap: var(--space-2);
241
+ padding: var(--space-3) var(--space-3) max(var(--space-3), env(safe-area-inset-bottom));
242
+ background: var(--surface);
243
+ }
244
+ .btn.new-session-btn {
245
+ display: inline-flex; align-items: center; gap: var(--space-2); min-height: 44px;
246
+ padding: 0 var(--space-5) 0 var(--space-4); border-radius: var(--radius-full);
247
+ font-size: var(--text-md); font-weight: var(--weight-semibold);
248
+ }
237
249
  /* Red dot on the mobile ☰ burger when something needs the user that they
238
250
  haven't seen (an agent blocked on approval/question, an unseen finished run,
239
251
  a provider needing auth). Replaces the old separate inbox surface — the
@@ -281,15 +293,18 @@ input, textarea, select { font-family: inherit; font-size: 16px; }
281
293
  .session-list { display: flex; flex-direction: column; min-height: 0; flex: 1; overflow-y: auto; overflow-x: hidden; padding: 8px; }
282
294
  .session-list-heading { display: flex; align-items: baseline; justify-content: space-between; padding: var(--space-3) var(--space-2) var(--space-2); color: var(--ink); font-size: var(--text-md); font-weight: var(--weight-semibold); }
283
295
  .session-list-count { color: var(--muted); font-size: var(--text-xs); font-weight: var(--weight-normal); font-variant-numeric: tabular-nums; }
284
- .session-list-tools { display: flex; align-items: center; gap: var(--space-2); margin-bottom: var(--space-2); }
285
- .session-search { flex: 1; min-width: 0; }
296
+ /* Search (the canonical .field, with a leading glyph) + a quiet filter icon. */
297
+ .session-list-tools { display: flex; align-items: center; gap: var(--space-1); margin-bottom: var(--space-2); }
298
+ .session-search-wrap { position: relative; flex: 1; min-width: 0; display: flex; color: var(--muted); }
299
+ .session-search-wrap > svg { position: absolute; left: var(--space-3); top: 50%; transform: translateY(-50%); pointer-events: none; }
300
+ .session-search { flex: 1; min-width: 0; padding-left: calc(var(--space-3) + 17px + var(--space-2)); }
286
301
  .session-filter { position: relative; flex: none; }
287
302
  .session-filter-btn {
288
303
  position: relative; display: inline-flex; align-items: center; justify-content: center;
289
- width: 38px; height: 38px; border: 1px solid var(--line); border-radius: var(--radius-md);
290
- background: var(--surface-2); color: var(--muted); font-size: 20px; line-height: 1;
304
+ width: 32px; height: 32px; border: 0; border-radius: var(--radius-full);
305
+ background: transparent; color: var(--muted); font-size: 20px; line-height: 1;
291
306
  }
292
- .session-filter-btn:hover, .session-filter-btn.active, .session-filter-btn[aria-expanded="true"] { color: var(--ink); border-color: var(--line-strong); background: var(--surface-3); }
307
+ .session-filter-btn:hover, .session-filter-btn.active, .session-filter-btn[aria-expanded="true"] { color: var(--ink); background: var(--surface-3); }
293
308
  .session-filter-count { position: absolute; right: -5px; top: -5px; min-width: 17px; height: 17px; padding: 0 var(--space-1); }
294
309
  .session-filter-menu {
295
310
  position: absolute; right: 0; top: calc(100% + 6px); width: min(280px, calc(100vw - 24px)); z-index: var(--z-dropdown);
@@ -305,36 +320,30 @@ input, textarea, select { font-family: inherit; font-size: 16px; }
305
320
  .session-filter-clear { flex: none; border: 0; background: transparent; color: var(--accent); padding: 2px 0; font-size: 12px; }
306
321
  .session-list ul { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 2px; }
307
322
  .session-group-label {
308
- padding: 12px 8px 4px; color: var(--muted); font-size: 11px; line-height: 1.2;
309
- font-weight: 650; letter-spacing: 0.02em;
323
+ padding: var(--space-4) var(--space-2) var(--space-1); color: var(--muted);
324
+ font-size: var(--text-xs); line-height: var(--lh-xs); font-weight: var(--weight-semibold);
310
325
  }
311
- .session-group-label:first-child { padding-top: 6px; }
326
+ .session-group-label:first-child { padding-top: var(--space-2); }
312
327
  .session-item {
313
- position: relative; display: flex; align-items: center; gap: 8px; width: 100%; text-align: left;
314
- padding: 9px 8px; border: 0; border-radius: var(--radius-sm); background: transparent; color: var(--ink);
328
+ position: relative; display: flex; align-items: center; gap: var(--space-2); width: 100%; text-align: left;
329
+ padding: var(--space-2); border: 0; border-radius: var(--radius-md); background: transparent; color: var(--ink);
315
330
  }
316
331
  .session-item:hover { background: var(--surface-2); }
317
- .session-item.active, .session-item[aria-current="page"] {
318
- background: var(--surface-2);
319
- }
320
- .session-item.active::before, .session-item[aria-current="page"]::before {
321
- content: ""; position: absolute; inset: 0 auto 0 0; width: 2px;
322
- background: var(--accent);
323
- }
324
- /* Column wrapper for the two-line row (title row + meta row) so the dot can
325
- stay a single flex sibling, vertically centered across both lines. */
332
+ .session-item.active, .session-item[aria-current="page"] { background: var(--surface-2); }
333
+ /* Two single-line rows: title (+ status dot, PR mark, age) over a quiet meta
334
+ line. A dot appears only for states that want attention (see RowMark). */
326
335
  .session-body { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
327
- .session-title-row { display: flex; align-items: flex-start; gap: 6px; min-width: 0; }
336
+ .session-title-row { display: flex; align-items: center; gap: var(--space-2); min-width: 0; }
337
+ .row-status { flex: none; display: inline-flex; }
328
338
  .session-name {
329
- flex: 1; min-width: 0; overflow: hidden; font-size: 13px; font-weight: 600; line-height: 1.25;
330
- display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
339
+ flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
340
+ font-size: var(--text-base); line-height: var(--lh-base); font-weight: var(--weight-medium);
331
341
  }
332
342
  /* Relative age, pinned to the row's trailing edge; flex:none so it never
333
343
  shrinks or forces the row to scroll sideways. */
334
- .session-age { flex: none; font-size: 12px; color: var(--muted); font-variant-numeric: tabular-nums; line-height: 1.25; }
335
- /* Agent / node / branch descriptor line, below the title. Slightly larger than
336
- before: 11px muted text was the main source of an illegible sidebar. */
337
- .session-meta { font-size: 12px; line-height: 1.25; color: var(--muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
344
+ .session-age { flex: none; font-size: var(--text-xs); color: var(--muted); font-variant-numeric: tabular-nums; line-height: var(--lh-xs); }
345
+ /* Agent / repo / branch descriptor line, below the title. */
346
+ .session-meta { font-size: var(--text-xs); line-height: var(--lh-xs); color: var(--muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
338
347
  @keyframes dot-pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.6; } }
339
348
  .session-row { position: relative; display: flex; align-items: center; }
340
349
  .session-row .session-item { flex: 1; min-width: 0; }
@@ -396,6 +405,12 @@ input, textarea, select { font-family: inherit; font-size: 16px; }
396
405
  the composer no longer moves it. margin-bottom stays auto so a tall pane keeps
397
406
  it in the upper-middle rather than pinned hard to the top. */
398
407
  .chat-empty { margin: calc(var(--app-h, 100dvh) * 0.22) auto auto; color: var(--muted); max-width: 320px; padding: 0 20px; text-align: center; }
408
+ .chat-greeting { margin: auto; padding: var(--space-6) var(--space-4); text-align: center; }
409
+ .chat-greeting h2 {
410
+ margin: 0; font-family: var(--font-serif); font-weight: var(--weight-normal);
411
+ font-size: var(--text-3xl); line-height: var(--lh-3xl); color: var(--ink); text-wrap: balance;
412
+ }
413
+ @media (max-width: 720px) { .chat-greeting h2 { font-size: var(--text-2xl); line-height: var(--lh-2xl); } }
399
414
  .chat-empty-title { margin: 0 0 6px; font-size: 16px; font-weight: 600; color: var(--ink); }
400
415
  .chat-empty-sub { margin: 0; font-size: 13px; line-height: 1.5; }
401
416
  .chat-empty-sub b { color: var(--ink); font-weight: 600; }
@@ -571,19 +586,27 @@ input, textarea, select { font-family: inherit; font-size: 16px; }
571
586
  discoverability pattern as .row-menu-btn — and a touch-friendly baseline
572
587
  opacity on devices with no hover. */
573
588
  .assistant-row { align-self: stretch; width: 100%; max-width: 680px; }
574
- .msg-actions { display: flex; align-items: center; gap: 2px; margin-top: 4px; }
589
+ .msg-actions { display: flex; align-items: center; gap: 2px; margin-top: var(--space-1); margin-left: -6px; }
575
590
  .msg-copy-btn, .msg-speak-btn {
576
591
  border: 0; background: transparent; color: var(--muted);
577
592
  display: inline-flex; align-items: center; justify-content: center;
578
- width: 26px; height: 26px; padding: 0; border-radius: var(--radius-sm); opacity: 0.35;
579
- cursor: pointer; transition: opacity 0.12s ease, color 0.12s ease;
593
+ width: 32px; height: 32px; padding: 0; border-radius: var(--radius-sm);
594
+ cursor: pointer; transition: opacity var(--dur-fast) ease, color var(--dur-fast) ease;
580
595
  }
581
- .assistant-row:hover .msg-copy-btn, .assistant-row:hover .msg-speak-btn,
582
- .msg-copy-btn:focus-visible, .msg-speak-btn:focus-visible { opacity: 1; }
583
596
  .msg-copy-btn:hover, .msg-speak-btn:hover { background: var(--surface-2); color: var(--ink); }
584
- .msg-copy-btn.copied { color: var(--ok); opacity: 1; }
585
- .msg-speak-btn.speaking { color: var(--accent); opacity: 1; }
586
- @media (hover: none) { .msg-copy-btn, .msg-speak-btn { opacity: 0.6; } }
597
+ .msg-copy-btn.copied { color: var(--ok); }
598
+ .msg-speak-btn.speaking { color: var(--accent); }
599
+ /* Actions belong to the answer: only a turn's final reply carries them. An
600
+ interim reply ("Let me look at…") drops them on touch, and on a wide pointer
601
+ screen keeps them one hover (or Tab) away in the column's right gutter. */
602
+ .turn-response-body > .assistant-row:has(~ .assistant-row) > .msg-actions { display: none; }
603
+ @media (hover: hover) and (min-width: 1100px) {
604
+ .turn-response-body > .assistant-row:has(~ .assistant-row) { position: relative; }
605
+ .turn-response-body > .assistant-row:has(~ .assistant-row) > .msg-actions {
606
+ display: flex; position: absolute; top: 0; left: 100%; margin: 0 0 0 var(--space-2); opacity: 0;
607
+ }
608
+ .turn-response-body > .assistant-row:has(~ .assistant-row):is(:hover, :focus-within) > .msg-actions { opacity: 1; }
609
+ }
587
610
 
588
611
  /* ---- Tool activity (grouped summary line -> bottom sheet detail + diff) ---- */
589
612
  .tool-group { align-self: stretch; width: 100%; max-width: 680px; }
@@ -711,14 +734,13 @@ input, textarea, select { font-family: inherit; font-size: 16px; }
711
734
  .github-pill .gh-icon { width: 14px; height: 14px; }
712
735
  .github-pill .gh-text { max-width: 220px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
713
736
 
714
- /* Compact PR badge on a sidebar row (open/merged/closed), colour-matched to the
715
- header pill. Non-interactive — it sits inside the row's <button>. */
716
- .session-pr { flex: none; gap: 3px; padding: 0 6px; height: 16px; font-size: 10px; line-height: 1; letter-spacing: 0.02em; }
717
- .session-pr .gh-icon { width: 10px; height: 10px; }
718
- /* The label is all-caps with no descenders, so within the line box its ink
719
- rides above the optical centre and reads high in the fixed-height pill. Nudge
720
- it down a touch to sit on the pill's true centre next to the glyph. */
721
- .session-pr-text { white-space: nowrap; position: relative; top: 0.5px; }
737
+ /* Compact PR mark (open/merged/closed): a soft tinted chip whose glyph shape
738
+ names the state, plus a count when there's more than one. Non-interactive —
739
+ it sits inside a row/pill <button>. */
740
+ .session-pr { flex: none; gap: 3px; padding: 0 var(--space-1); height: 18px; font-size: 11px; line-height: 1; font-variant-numeric: tabular-nums; }
741
+ .session-pr:not(:has(.session-pr-text)) { width: 18px; padding: 0; justify-content: center; }
742
+ .session-pr .pr-glyph { width: 12px; height: 12px; flex: none; }
743
+ .session-pr-text { white-space: nowrap; font-weight: var(--weight-semibold); }
722
744
 
723
745
  /* ---- Source marks & run pill (see sessionSource.ts / SourceMark.tsx) ----
724
746
  Source identity is carried by the glyph alone: every mark renders a quiet
@@ -746,6 +768,9 @@ input, textarea, select { font-family: inherit; font-size: 16px; }
746
768
  .source-mark svg { width: 17px; height: 17px; display: block; }
747
769
  .source-mark.sm { width: 22px; height: 22px; border-radius: var(--radius-sm); }
748
770
  .source-mark.sm svg { width: 13px; height: 13px; }
771
+ /* Inline in a row's meta line, sized to the text. */
772
+ .source-mark.xs { width: 16px; height: 16px; border-radius: calc(var(--radius-sm) / 2); margin-right: var(--space-1); vertical-align: -3px; }
773
+ .source-mark.xs svg { width: 10px; height: 10px; }
749
774
  .source-mark.has-status { position: relative; }
750
775
  .source-mark.has-status .status-dot {
751
776
  position: absolute; right: -3px; bottom: -3px;
@@ -757,26 +782,20 @@ input, textarea, select { font-family: inherit; font-size: 16px; }
757
782
  /* In-session run pill (App.tsx composer band): supersedes the GitHub pill for a
758
783
  triggered session. Source glyph + short label + a status segment. */
759
784
  .run-pill {
760
- display: inline-flex; align-items: center; gap: 7px; flex: 0 1 auto; min-width: 0;
761
- border: 1px solid var(--line); background: var(--surface-2); color: var(--ink);
762
- border-radius: var(--radius-full); padding: 4px 11px 4px 5px; font-size: 12px; font-weight: 600;
785
+ display: inline-flex; align-items: center; gap: var(--space-2); flex: 0 1 auto; min-width: 0; min-height: 32px;
786
+ border: 0; background: var(--surface-2); color: var(--ink);
787
+ border-radius: var(--radius-full); padding: var(--space-1) var(--space-3);
788
+ font-size: var(--text-sm); font-weight: var(--weight-medium);
763
789
  }
764
- .run-pill:hover { border-color: var(--line-strong); background: var(--surface-3); }
790
+ .run-pill:has(> .source-mark) { padding-left: var(--space-1); }
791
+ .run-pill:hover { background: var(--surface-3); }
765
792
  .run-pill-label { flex: none; }
766
793
  .run-pill-stat {
767
- display: inline-flex; align-items: center; gap: 5px; min-width: 0;
768
- padding-left: 8px; border-left: 1px solid var(--line); color: var(--muted);
794
+ display: inline-flex; align-items: center; gap: 6px; min-width: 0; color: var(--muted);
769
795
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
770
796
  }
771
797
  /* Session-level file edit count — replaces the bulky ChangesCard above the composer. */
772
- .run-pill-files {
773
- flex: none;
774
- padding-left: 8px;
775
- border-left: 1px solid var(--line);
776
- color: var(--muted);
777
- font-weight: 600;
778
- white-space: nowrap;
779
- }
798
+ .run-pill-files { flex: none; color: var(--muted); white-space: nowrap; }
780
799
  .run-pill.working .run-pill-stat { color: var(--accent); }
781
800
  .run-pill.needs-action .run-pill-stat, .run-pill.failed .run-pill-stat { color: var(--danger); }
782
801
 
@@ -800,9 +819,10 @@ input, textarea, select { font-family: inherit; font-size: 16px; }
800
819
  /* Run-pill sheet outcome: the run evidence (checks, attempts, duration,
801
820
  branch/commit, agent) joined from the account queue — see runEvidence.ts. */
802
821
  .run-sheet-failure { padding: 11px 16px; border-bottom: 1px solid var(--line); color: var(--danger); font-size: 13px; line-height: 1.4; }
803
- .run-sheet-rows { display: flex; flex-direction: column; }
804
- .run-sheet-row { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; padding: 9px 16px; border-bottom: 1px solid var(--line); font-size: 13px; }
805
- .run-sheet-row:last-child { border-bottom: 0; }
822
+ /* Key/value facts read as one quiet group: no rule between rows, one rule
823
+ between groups (and before the action rows that follow). */
824
+ .run-sheet-rows { display: flex; flex-direction: column; padding: var(--space-2) 0; border-bottom: 1px solid var(--line); }
825
+ .run-sheet-row { display: flex; align-items: flex-start; justify-content: space-between; gap: var(--space-3); padding: 6px var(--space-4); font-size: var(--text-sm); }
806
826
  .run-sheet-row .k { flex: none; color: var(--muted); }
807
827
  .run-sheet-row .v { min-width: 0; display: flex; flex-wrap: wrap; gap: 4px 8px; justify-content: flex-end; align-items: center; color: var(--ink); font-weight: 600; text-align: right; }
808
828
  .run-sheet-row code { font-family: ui-monospace, "SF Mono", SFMono-Regular, Menlo, Consolas, monospace; font-size: 12px; padding: 1px 5px; border-radius: var(--radius-sm); border: 1px solid var(--line); background: var(--surface-2); }
@@ -836,7 +856,10 @@ button.sheet-action:last-child { border-bottom: 0; }
836
856
  /* GitHub pills parked in a band just above the composer: the repo picker on a
837
857
  draft (.composer-lead) and the issue/PR context pill on an active session
838
858
  (.composer-gh). .composer-gh collapses out of flow when there's no context. */
839
- .composer-gh, .composer-lead { display: flex; padding: 8px 10px 0; }
859
+ /* The band and the composer share the transcript's centred column, so on a wide
860
+ screen the input sits under the conversation rather than spanning the pane. */
861
+ .composer-gh, .composer-lead, .composer { align-self: center; width: min(100%, 800px); box-sizing: border-box; }
862
+ .composer-gh, .composer-lead { display: flex; padding: var(--space-2) var(--space-3) 0; }
840
863
  .composer-gh { align-items: center; gap: 6px; }
841
864
  /* The draft lead row carries more than one pick (repo + sandbox), so space them
842
865
  and let them wrap on a narrow phone instead of squashing. */
@@ -862,14 +885,14 @@ button.sheet-action:last-child { border-bottom: 0; }
862
885
 
863
886
  /* ---- Composer ---- */
864
887
  .composer {
865
- display: flex; padding: 8px 10px;
866
- padding-bottom: max(8px, env(safe-area-inset-bottom));
888
+ display: flex; padding: var(--space-2) var(--space-3);
889
+ padding-bottom: max(var(--space-2), env(safe-area-inset-bottom));
867
890
  }
868
891
  .composer-card {
869
892
  position: relative;
870
- flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 6px;
871
- border: 1px solid var(--line-strong); border-radius: var(--radius-md); background: var(--surface-2);
872
- padding: 8px 8px 8px 12px;
893
+ flex: 1; min-width: 0; display: flex; flex-direction: column; gap: var(--space-1);
894
+ border: 1px solid var(--line); border-radius: var(--radius-xl); background: var(--surface-2);
895
+ padding: var(--space-2) var(--space-2) var(--space-2) var(--space-4);
873
896
  }
874
897
  /* Slash-command autocomplete: a popover floating just above the composer. */
875
898
  .slash-menu {
@@ -880,7 +903,7 @@ button.sheet-action:last-child { border-bottom: 0; }
880
903
  .slash-empty { padding: 10px 12px; color: var(--muted); font-size: 13px; }
881
904
  .slash-name { font-weight: 600; font-size: 14px; flex: none; min-width: 74px; }
882
905
  .slash-desc { color: var(--muted); font-size: 13px; }
883
- .composer-card:focus-within { border-color: var(--accent); box-shadow: 0 0 0 1px var(--accent); }
906
+ .composer-card:focus-within { border-color: var(--accent); }
884
907
  /* Keep two readable lines even when autosize runs inside the hidden onboarding shell. */
885
908
  .composer-input {
886
909
  min-height: calc(2 * var(--lh-lg) + 2 * var(--space-1)); flex-shrink: 0;
@@ -896,11 +919,12 @@ button.sheet-action:last-child { border-bottom: 0; }
896
919
  flex: 1; min-width: 0; display: flex; align-items: center; gap: 6px; overflow: hidden;
897
920
  }
898
921
  .composer-meta > .btn {
899
- gap: var(--space-1); min-width: 0; flex: 0 1 auto; color: var(--ink);
922
+ gap: 6px; min-width: 0; flex: 0 1 auto; color: var(--muted); font-weight: var(--weight-medium);
900
923
  }
924
+ .composer-meta > .btn:hover { color: var(--ink); }
901
925
  /* Keep the startup action readable beside a long agent name on phones. */
902
926
  .composer-meta:has([data-pending-model]) { flex-wrap: wrap; }
903
- .pill-glyph { color: var(--accent); flex: none; display: inline-flex; align-items: center; }
927
+ .pill-glyph { color: inherit; flex: none; display: inline-flex; align-items: center; }
904
928
  .pill-label { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
905
929
  /* Glyph-only / short controls keep their natural width; only the long
906
930
  agent + model labels give way when space is tight. */
@@ -914,7 +938,9 @@ button.sheet-action:last-child { border-bottom: 0; }
914
938
  .composer-btn.send:not(:disabled):hover { background: var(--accent-hover); }
915
939
  .composer-btn.send:not(:disabled):active { transform: scale(0.92); }
916
940
  .composer-btn.send:disabled { opacity: 0.35; cursor: default; }
917
- .composer-btn.stop { background: var(--danger); color: var(--danger-contrast); }
941
+ /* Stop is a neutral, high-contrast square — the most important control while a
942
+ turn runs, without borrowing the danger colour for an ordinary action. */
943
+ .composer-btn.stop { background: var(--ink); color: var(--bg); }
918
944
  .composer-btn.stop.is-stopping { width: auto; border-radius: var(--radius-full); padding-inline: var(--space-3); display: inline-flex; gap: var(--space-2); }
919
945
  .composer-btn.stop:active { transform: scale(0.92); }
920
946
  /* "Steer current turn": an explicit interrupt, distinct from the default Send
@@ -927,7 +953,7 @@ button.sheet-action:last-child { border-bottom: 0; }
927
953
  .attach-pill { padding: 4px 8px; }
928
954
 
929
955
  /* Voice-input trigger: a circular icon button seated just left of Send. */
930
- .composer-btn.mic { background: var(--surface-3); color: var(--muted); }
956
+ .composer-btn.mic { background: transparent; color: var(--muted); }
931
957
  .composer-btn.mic:not(:disabled):hover { color: var(--accent); background: color-mix(in srgb, var(--accent) 14%, var(--surface-3)); }
932
958
  .composer-btn.mic:not(:disabled):active { transform: scale(0.92); }
933
959
  .composer-btn.mic:disabled { opacity: 0.4; cursor: default; }
@@ -2193,6 +2219,13 @@ a.queue-item:hover { border-color: var(--line-strong); background: var(--surface
2193
2219
  /* Keep both selected values visible. Long agent/model names share the
2194
2220
  available row and use the composer's standard ellipsis behavior. */
2195
2221
  .composer-meta { gap: 2px; }
2222
+ /* Phone width can't hold both full names beside two 44px buttons: the
2223
+ (already compacted) model keeps its label, the agent name gives way first,
2224
+ and the agent's decorative glyph goes so its name keeps more letters. */
2225
+ .composer-meta > .btn { padding-inline: 6px; }
2226
+ .composer-meta > .model-pill { flex-shrink: 0; max-width: 50%; }
2227
+ .agent-pill .pill-glyph { display: none; }
2228
+ .composer-actions { gap: var(--space-1); }
2196
2229
  .automations-view-head { align-items: center; padding-bottom: 8px; }
2197
2230
  .automations-view-head-text { min-width: 0; }
2198
2231
  .automations-view-sub { display: none; }
@@ -2629,15 +2662,13 @@ textarea.picker-search { resize: vertical; font-family: ui-monospace, monospace;
2629
2662
  .btn.lg { padding: 11px 18px; font-size: var(--text-md); }
2630
2663
  .btn.icon { padding: var(--space-1); min-width: var(--space-7); min-height: var(--space-7); font-size: var(--text-xl); line-height: 1; }
2631
2664
  .btn[aria-busy="true"] { cursor: wait; }
2632
- /* ---- Sidebar settings gear (single entry point, ChatGPT-style) ---- */
2633
- .settings-gear {
2634
- display: inline-flex; align-items: center; justify-content: flex-start; gap: 10px; position: relative;
2635
- width: 100%; border: 0; background: transparent; color: var(--ink);
2636
- border-radius: var(--radius-sm); padding: 9px 10px; min-height: 40px; font-size: 13px; font-weight: 550;
2637
- }
2638
- .settings-gear:hover { background: var(--surface-2); }
2639
- .settings-gear svg { flex: none; color: var(--muted); }
2640
- .settings-gear:hover svg { color: var(--ink); }
2665
+ /* ---- Sidebar settings gear (round companion to New session, ChatGPT-style) ---- */
2666
+ .btn.icon.settings-gear {
2667
+ display: inline-flex; align-items: center; justify-content: center; flex: none;
2668
+ width: 44px; height: 44px; padding: 0; border: 0; border-radius: var(--radius-full);
2669
+ background: var(--surface-2); color: var(--ink);
2670
+ }
2671
+ .btn.icon.settings-gear:hover { background: var(--surface-3); }
2641
2672
 
2642
2673
  /* ---- Usage bar ---- */
2643
2674
 
@@ -12,8 +12,9 @@
12
12
  * hex or px. `pnpm run check:design` enforces that no other stylesheet redeclares
13
13
  * a palette token. See packages/web/CLAUDE.md for the design rules agents follow.
14
14
  *
15
- * Palette: high-contrast neutral light + dark themes, with color reserved for
16
- * actions and status. Toggle dark with <html data-theme="dark">; with no attribute it
15
+ * Palette: high-contrast light + dark themes whose neutrals carry a faint cool
16
+ * tint (the accent's hue) so surfaces and the accent read as one family; dark
17
+ * sits on true black. Color is reserved for actions and status. Toggle dark with <html data-theme="dark">; with no attribute it
17
18
  * follows the OS. prefers-reduced-motion collapses the motion tokens.
18
19
  *
19
20
  * Naming: the canonical vocabulary is --bg / --surface* / --ink / --muted /
@@ -29,12 +30,12 @@
29
30
  /* ---- Core surfaces & text (neutral) ---- */
30
31
  --bg: #ffffff;
31
32
  --surface: #ffffff;
32
- --surface-2: #f5f5f5;
33
- --surface-3: #ededed;
34
- --ink: #111111;
35
- --muted: #6b6b6b;
36
- --line: #e5e5e5;
37
- --line-strong: #d4d4d4;
33
+ --surface-2: #f3f5f7;
34
+ --surface-3: #e8ecef;
35
+ --ink: #0f1215;
36
+ --muted: #5f6a73;
37
+ --line: #e3e7ea;
38
+ --line-strong: #cdd4d9;
38
39
  --overlay: rgba(0, 0, 0, 0.46);
39
40
  --overlay-strong: rgba(0, 0, 0, 0.8);
40
41
 
@@ -156,8 +157,8 @@
156
157
 
157
158
  :root[data-theme="dark"] {
158
159
  color-scheme: dark;
159
- --bg: #0a0a0a; --surface: #0a0a0a; --surface-2: #171717; --surface-3: #262626;
160
- --ink: #fafafa; --muted: #a3a3a3; --line: #262626; --line-strong: #404040;
160
+ --bg: #000000; --surface: #000000; --surface-2: #15181b; --surface-3: #22272b;
161
+ --ink: #f3f5f7; --muted: #99a2aa; --line: #1f2327; --line-strong: #353b41;
161
162
  --overlay: rgba(0, 0, 0, 0.7); --overlay-strong: rgba(0, 0, 0, 0.8);
162
163
  --accent: #8fb6cd; --accent-contrast: #0a0a0a; --accent-hover: #a7cadd;
163
164
  --danger: #c08a83; --danger-soft: #3a2420; --danger-contrast: #0a0a0a; --ok: #86a99f; --merged: #a99bc4;
@@ -178,8 +179,8 @@
178
179
  @media (prefers-color-scheme: dark) {
179
180
  :root:not([data-theme]) {
180
181
  color-scheme: dark;
181
- --bg: #0a0a0a; --surface: #0a0a0a; --surface-2: #171717; --surface-3: #262626;
182
- --ink: #fafafa; --muted: #a3a3a3; --line: #262626; --line-strong: #404040;
182
+ --bg: #000000; --surface: #000000; --surface-2: #15181b; --surface-3: #22272b;
183
+ --ink: #f3f5f7; --muted: #99a2aa; --line: #1f2327; --line-strong: #353b41;
183
184
  --overlay: rgba(0, 0, 0, 0.7); --overlay-strong: rgba(0, 0, 0, 0.8);
184
185
  --accent: #8fb6cd; --accent-contrast: #0a0a0a; --accent-hover: #a7cadd;
185
186
  --danger: #c08a83; --danger-soft: #3a2420; --danger-contrast: #0a0a0a; --ok: #86a99f; --merged: #a99bc4;
@@ -71,6 +71,14 @@ export function messageContentAtoms(message) {
71
71
  }
72
72
  else if (type === "text")
73
73
  pushText(b.text);
74
+ // Reasoning is an atom too: Claude streams each thinking block as its own
75
+ // assistant message, and without this a reloaded thinking-only message had
76
+ // no atoms, read as "opaque", and was re-appended after the final answer.
77
+ else if (type === "thinking" || type === "reasoning") {
78
+ const text = thinkingTextFromContent([b]).trim();
79
+ if (text)
80
+ texts.push(`thinking#${text}`);
81
+ }
74
82
  }
75
83
  }
76
84
  return { toolIds, texts };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bivy/bivy",
3
- "version": "0.17.0",
3
+ "version": "0.17.1-staging.10",
4
4
  "type": "module",
5
5
  "license": "AGPL-3.0-only",
6
6
  "description": "Run coding agents on machines you own. Open-source, self-hostable agent workspace.",