Spaces:
Running
Review fixes: stop the title growing over the header's own furniture
Browse filesTwo real defects the reviewer found, both reproduced in a browser before
believing them and after fixing them.
The header is a three-column grid whose centre track is `auto`, and an `auto`
track is sized to its max-content BEFORE the two `1fr` side tracks get
anything. So a wide title does not shrink — it takes its width out of its
neighbours, and the title's box grows over the logo on one side and the close
button on the other. Measured at a 900px window in a 2x2: `[Agent-manager]
claude-code-8` overlapped `.ph-left` by 4px, and a 31-character name overlapped
both sides by 28px and 16px. The long-name half of that is on main already; the
prefix made it reachable at ordinary names. A `min-content` floor on the two
side tracks fixes both: the title is capped at what is actually free and
ellipsises inside its own box.
Second, `flex-shrink` distributes a deficit proportionally, so weighting the
group heavier still took a sliver off the name — and a name that misses its
natural width by 0.03px renders an ellipsis anyway. `claude-code-8` read
`claude-code…` in every tile of a 2x2 at 900px. The name now has shrink 0 and
is bounded by `max-width` instead, which also puts the ellipsis on the name
itself when the name alone is too long for the header.
Also from the review: `align-items: center` rather than a synthesised baseline
on two overflow-clipped boxes, drop the `text-align` that a blockified flex
item ignores, and cite the endpoint the app actually reads (`/api/tree`) rather
than the agent roster.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
- web/src/lib/sessionTitle.ts +5 -3
- web/src/styles.css +23 -11
|
@@ -14,9 +14,11 @@
|
|
| 14 |
/**
|
| 15 |
* The group's display name, or null when the session sits loose in the tree.
|
| 16 |
*
|
| 17 |
-
* Ungrouped is an absence here, not a word:
|
| 18 |
-
*
|
| 19 |
-
*
|
|
|
|
|
|
|
| 20 |
*/
|
| 21 |
export function groupLabel(name?: string | null): string | null {
|
| 22 |
const trimmed = (name ?? '').trim();
|
|
|
|
| 14 |
/**
|
| 15 |
* The group's display name, or null when the session sits loose in the tree.
|
| 16 |
*
|
| 17 |
+
* Ungrouped is an absence here, not a word: `/api/tree` gives the app groups
|
| 18 |
+
* with their `sessionIds`, so a loose session is simply one no group lists and
|
| 19 |
+
* the caller has nothing to pass. Every empty shape collapses to the same bare
|
| 20 |
+
* name — there is no `[None] foo`. (The roster that agents read, `/api/agents`,
|
| 21 |
+
* spells the same absence as `group: null`.)
|
| 22 |
*/
|
| 23 |
export function groupLabel(name?: string | null): string | null {
|
| 24 |
const trimmed = (name ?? '').trim();
|
|
@@ -560,16 +560,28 @@ body {
|
|
| 560 |
.welcome-reopen { font-size: 11px; color: var(--muted); }
|
| 561 |
.welcome-foot .btn-primary { flex: none; padding: 8px 18px; }
|
| 562 |
|
| 563 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 564 |
.pane-head .ph-left { grid-column: 1; justify-self: start; display: inline-flex; align-items: center; gap: 7px; }
|
| 565 |
-
.pane-head .ph-title { grid-column: 2; min-width: 0; max-width: 100%; display: inline-flex; align-items:
|
| 566 |
-
/* `[Group] name` is one title in two parts, and the group is the part that
|
| 567 |
-
gives way:
|
| 568 |
-
|
| 569 |
-
|
| 570 |
-
|
| 571 |
-
.pane-head .ph-group { flex: 0
|
| 572 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 573 |
.pane-head .ph-right { grid-column: 3; min-width: 0; display: inline-flex; align-items: center; justify-self: end; gap: 6px; }
|
| 574 |
.pane-head .ph-path { min-width: 0; max-width: min(24vw, 220px); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--muted); font-family: var(--font-mono); font-size: 10.5px; font-weight: 500; }
|
| 575 |
.pane-head .ph-title-input { width: 100%; text-align: center; font: inherit; font-family: var(--font-mono); font-weight: 600; padding: 1px 6px; border: 1px solid var(--accent); border-radius: var(--r-sm); background: var(--panel-2); color: var(--text); min-width: 0; }
|
|
@@ -577,8 +589,8 @@ body {
|
|
| 577 |
@container (max-width: 520px) {
|
| 578 |
.pane-head .ph-path { display: none; }
|
| 579 |
}
|
| 580 |
-
/* Narrower than this
|
| 581 |
-
|
| 582 |
@container (max-width: 260px) {
|
| 583 |
.pane-head .ph-group { display: none; }
|
| 584 |
}
|
|
|
|
| 560 |
.welcome-reopen { font-size: 11px; color: var(--muted); }
|
| 561 |
.welcome-foot .btn-primary { flex: none; padding: 8px 18px; }
|
| 562 |
|
| 563 |
+
/* The side columns keep a min-content floor. Without one the centre track is
|
| 564 |
+
sized to its max-content BEFORE the two `1fr` tracks get anything, so a wide
|
| 565 |
+
title takes its width out of its neighbours: the logo and the close button
|
| 566 |
+
keep their place in the flow but the title's box grows over them. That was
|
| 567 |
+
already reachable with a long enough agent name; putting the group in front
|
| 568 |
+
of the name made it reachable at ordinary ones. With the floor the title is
|
| 569 |
+
capped at what is actually free and ellipsises instead. */
|
| 570 |
+
.pane-head { container-type: inline-size; display: grid; grid-template-columns: minmax(min-content, 1fr) minmax(0, auto) minmax(min-content, 1fr); align-items: center; gap: 9px; padding: 7px 11px; background: var(--panel); border-bottom: 1px solid var(--border); font-size: 12px; flex: none; }
|
| 571 |
.pane-head .ph-left { grid-column: 1; justify-self: start; display: inline-flex; align-items: center; gap: 7px; }
|
| 572 |
+
.pane-head .ph-title { grid-column: 2; min-width: 0; max-width: 100%; display: inline-flex; align-items: center; justify-content: center; gap: 5px; font-family: var(--font-mono); font-weight: 600; white-space: nowrap; overflow: hidden; cursor: text; }
|
| 573 |
+
/* `[Group] name` is one title in two parts, and the group is the only part that
|
| 574 |
+
gives way: a tile too narrow for both truncates the context, never the thing
|
| 575 |
+
you are looking for. The squeeze passes through a band where the prefix reads
|
| 576 |
+
as a stub (`[Agent-man…`); below a tile width where that is all it could ever
|
| 577 |
+
be, it drops out entirely and the name has the header to itself. */
|
| 578 |
+
.pane-head .ph-group { flex: 0 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--muted); font-weight: 500; }
|
| 579 |
+
/* flex-shrink 0, not a small weight: shrinking is proportional, so ANY shared
|
| 580 |
+
deficit takes a sliver off the name too — and a name that misses by 0.03px
|
| 581 |
+
still renders an ellipsis. So the name never shrinks, and `max-width` is what
|
| 582 |
+
bounds it: it can fill the whole title but no more, which is what puts the
|
| 583 |
+
ellipsis on the name itself when the name alone is too long for the header. */
|
| 584 |
+
.pane-head .ph-name { flex: 0 0 auto; min-width: 0; max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
| 585 |
.pane-head .ph-right { grid-column: 3; min-width: 0; display: inline-flex; align-items: center; justify-self: end; gap: 6px; }
|
| 586 |
.pane-head .ph-path { min-width: 0; max-width: min(24vw, 220px); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--muted); font-family: var(--font-mono); font-size: 10.5px; font-weight: 500; }
|
| 587 |
.pane-head .ph-title-input { width: 100%; text-align: center; font: inherit; font-family: var(--font-mono); font-weight: 600; padding: 1px 6px; border: 1px solid var(--accent); border-radius: var(--r-sm); background: var(--panel-2); color: var(--text); min-width: 0; }
|
|
|
|
| 589 |
@container (max-width: 520px) {
|
| 590 |
.pane-head .ph-path { display: none; }
|
| 591 |
}
|
| 592 |
+
/* Narrower than this the prefix could only ever be a stub, and a header that
|
| 593 |
+
reads "[A… name" is worse than one that just reads "name". */
|
| 594 |
@container (max-width: 260px) {
|
| 595 |
.pane-head .ph-group { display: none; }
|
| 596 |
}
|