A directory chip that stopped eating the row

The project directory showed its whole path, which on anything real filled the
chip's 16rem basis and pushed the Manual/Edit/Auto/Plan select off the end of
the composer. It shows the directory's own name now, with the full path in the
tooltip -- the leading directories are the part nobody reads, since what you
check before sending is that you are in `myproject` rather than `myproject-old`.

The hidden field still submits the whole path. Shortening a label must never
shorten a value, and there is a test on the row rather than on the markup for
exactly that.

Three CSS rules hold the row together, and none of them is visible from the
markup. `.composer__agent` needed `min-width: 0`: a flex item will not shrink
below its content without it, so the group refused to give and the *last* child
was what fell off -- which is why the mode select was the thing being cut rather
than the path that was too long. `.composer__dir` is capped, being the only
child here whose content is unbounded; a connection name and a mode are both
short and known. And the mode select is `flex: none`, because it is read and
changed constantly and should never be the thing that scrolls out of reach.

`baseName` driven under node against ten paths, trailing slashes and `/`
included. The topbar's copy of the same path was already capped and truncating,
so it is left alone.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Jaroslav Beneš
2026-08-04 13:17:56 +02:00
parent 48a66a4037
commit c0d6056ec4
4 changed files with 117 additions and 8 deletions
+25 -5
View File
@@ -911,7 +911,17 @@
scrollbar-width: none;
}
.composer__context::-webkit-scrollbar { display: none; }
.composer__agent { display: flex; align-items: center; gap: var(--sp-2); flex-wrap: nowrap; }
/* `min-width: 0` because a flex item will not shrink below its content without
it, and this one holds a connection name, a path and a mode. Without it the
whole group refused to shrink and the last child -- the mode select -- was
what fell off the end. */
.composer__agent {
display: flex;
align-items: center;
gap: var(--sp-2);
flex-wrap: nowrap;
min-width: 0;
}
/* Floors, not fixed widths: a select narrower than this shows no text at all,
which is worse than the scrolling it was avoiding. */
@@ -922,14 +932,24 @@
bracketing the row. */
.composer__btn { flex: none; border-radius: var(--radius-full); }
/* The directory, on a new chat. Monospace because it is a path, and it grows
to fit rather than being pinned to a width that truncates every real one. */
/* The directory, on a new chat. Monospace because it is a path.
It shows the directory's own name rather than the whole path (see `setDir`),
so it needs a fraction of the room it used to and is capped rather than
allowed to grow: at `0 1 16rem` a real project path filled the basis, and the
mode select next to it was pushed off the end of the row. The cap is on this
one because it is the only child here whose content is unbounded -- a
connection name and a mode are both short and known. */
.composer__dir {
flex: 0 1 16rem;
min-width: 5rem;
flex: 0 1 auto;
min-width: 4rem;
max-width: 11rem;
font-family: var(--font-mono);
font-weight: 400;
}
/* The mode is a control that is read and changed constantly; it never shrinks
and never scrolls out of reach. It is the thing that was being cut. */
.composer__context select[name="agent_mode"] { flex: none; }
.composer__dir-path { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.composer__hint {
+20 -2
View File
@@ -542,6 +542,15 @@ document.addEventListener("lembas:notify", function (event) {
exactly what it means.
*/
(function () {
/* The last segment of a path, with trailing slashes ignored so `/srv/app/`
reads as `app` rather than as nothing. `/` is itself, since it has no name
of its own and "the root" is what somebody means by it. */
function baseName(path) {
var trimmed = String(path || "").replace(/\/+$/, "");
if (!trimmed) return "/";
return trimmed.slice(trimmed.lastIndexOf("/") + 1) || "/";
}
function wire(root) {
var kind = root.querySelector("#chat-kind");
var extra = root.querySelector("[data-agent-extra]");
@@ -551,11 +560,20 @@ document.addEventListener("lembas:notify", function (event) {
if (!kind || !extra) return;
/* The directory is a hidden field plus a button, so the two have to be set
together or the button shows one path and the form submits another. */
together or the button shows one path and the form submits another.
The button shows the directory's own name; the full path goes in the
tooltip and, of course, in the field that is submitted. A real project
path is long enough that showing it whole made the button eat the row and
squeeze the mode select beside it, and the leading directories are the
part nobody is reading -- what you check before sending is that you are
in `myproject` rather than `myproject-old`. */
function setDir(value) {
if (!dir) return;
dir.value = value || "";
if (dirLabel) dirLabel.textContent = value || "the login directory";
if (dirLabel) dirLabel.textContent = value ? baseName(value) : "the login directory";
var button = dirLabel && dirLabel.closest("[data-dir-browse]");
if (button) button.title = value || "The connection's own login directory";
}
function sync() {