1
0

fix(ui): give the rail its own short labels instead of the sheet's sentences

The corner rail rendered `Shortcut.meaning`, which is written for the `?` sheet
where a full sentence is exactly right because somebody is reading a reference.
The rail is a glance over a moving 3D board with room for one line, and it is a
`flex-wrap` container: the sentence wrapped, and the wrapped line drew straight
through the chip beneath it. On the deployed build "Fly to a chapter of the tour,
or to a viewpoint inside a studio" and "Walk into the studio, or step back out to
the city" overlapped and neither could be read, over a bright sky with no
background to hide it. Nothing errored — it just looked broken, on every screen.

Two labels for two jobs. `Shortcut.rail` carries the two-or-three-word form for
the nine ids `railHints` can select; `meaning` keeps the sentence for the sheet.

`.hint` also had no `line-height`, so it inherited the body's and a wrapped chip
had nowhere to go, and no `max-width`, so the rail grew leftward until it met the
mode pill. Chips are now `white-space: nowrap`: wrapping belongs between chips,
which the flex container already does, not inside one.

Tested, because this is a defect a test can hold: every id the rail can return
must have a `rail` label, it must fit on a line, and it must not contain a
clause.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-21 19:56:55 -07:00
parent 75908774f7
commit 655848746d
4 changed files with 101 additions and 2 deletions
+57
View File
@@ -244,3 +244,60 @@ describe("the rail hint", () => {
}
});
});
/**
* The rail is a glance, not a reference.
*
* Every hint the rail can show reused `meaning` — a full sentence written for
* the `?` sheet. In a `flex-wrap` container over a live 3D board that wrapped,
* and the wrapped line drew straight through the chip beneath it: on the
* deployed build "Fly to a chapter of the tour, or to a viewpoint inside a
* studio" and "Walk into the studio, or step back out to the city" overlapped
* and neither could be read. Nothing failed; it just looked broken.
*
* So: every id `railHints` is capable of returning must carry a short `rail`
* label, and it must be short enough to stay on one line.
*/
describe("rail hint labels", () => {
/** Every mode the rail is asked about, from `controlMode.ts`. */
const MODES = [
"overview",
"drive",
"actor",
"aircraft",
"office-overview",
"office-walk",
] as const;
it("gives every rail-selectable shortcut a short label of its own", () => {
for (const mode of MODES) {
for (const entry of railHints(mode, false)) {
assert.ok(
entry.rail !== undefined,
`${mode}: shortcut "${entry.id}" can reach the rail with no rail label, ` +
`so it would fall back to its ${entry.meaning.length}-character sentence`,
);
assert.ok(
entry.rail.length <= 16,
`${mode}: rail label "${entry.rail}" is ${entry.rail.length} characters; ` +
`the rail has room for a glance, not a clause`,
);
assert.ok(
!entry.rail.includes(","),
`${mode}: rail label "${entry.rail}" carries a clause; that belongs in meaning`,
);
}
}
});
it("keeps the long form for the sheet, which is where it reads correctly", () => {
const [chapters] = railHints("overview", false);
assert.ok(chapters !== undefined);
assert.notEqual(chapters.rail, chapters.meaning);
assert.ok(chapters.meaning.length > 20, "the sheet should still get the sentence");
});
it("shows nothing at all on a coarse pointer, which has no keys to press", () => {
for (const mode of MODES) assert.deepEqual(railHints(mode, true), []);
});
});