feat(studio): wrap gutter names instead of truncating them

A truncated name needs a hover to be read at all, and a tooltip is no use
for a name you are scanning a column of. All three gutter labels — the
plain track header, the group header, and the keyframe layer row — now
wrap and drop their `title`.

`break-words` so a long single token breaks rather than forcing the column
wider. Measured at the 232px gutter: a name long enough to wrap takes two
lines (28px) inside the 48px row with room to spare; a third line would
overflow, which is what the row affords either way.

One test located the name by its `title` and now reads the rendered text.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Vance Ingalls
2026-08-20 02:21:19 -07:00
co-authored by Claude Opus 5
parent b6e5afd25d
commit 46ca2f3e56
4 changed files with 20 additions and 14 deletions
@@ -95,9 +95,9 @@ export function LayerDisclosureRow({
<span aria-hidden="true" className="shrink-0 text-[13px] leading-none text-white/40">
</span>
<span className="min-w-0 flex-1 truncate font-medium" title={name}>
{name}
</span>
{/* Wraps rather than truncating: a truncated name needs a hover to be
read, which a scanned column cannot rely on. */}
<span className="min-w-0 flex-1 break-words font-medium leading-tight">{name}</span>
<TrackClipCount clipCount={clipCount} />
{children}
{/* Anchored right, on every header that has one: the lane toggle is the
@@ -65,7 +65,9 @@ function GroupNameButton({
<span aria-hidden="true" className="shrink-0 text-[12px] leading-none text-white/50">
</span>
<span className="min-w-0 truncate font-medium">{label}</span>
{/* Wraps rather than truncating — a name that needs a hover to be read
is no use in a column you scan. */}
<span className="min-w-0 break-words text-left font-medium leading-tight">{label}</span>
<span
className="shrink-0 rounded-full bg-white/10 px-1 text-[9px] leading-[14px] tabular-nums text-white/55"
aria-hidden="true"
@@ -107,7 +109,7 @@ export function TimelineGroupHeader({
}}
>
{/* One line, like a track header's: caret and name, then every control
anchored to the right edge. The name truncates and the controls are
anchored to the right edge. The name wraps and the controls are
`shrink-0`, so they hold the edge and the name gives way — no second
line needed to keep five controls off the label. */}
<button
@@ -747,7 +747,9 @@ describe("TimelineTrackHeader", () => {
it("names the header for the track, not for one of the clips on it", () => {
const view = renderHeader({ ...ROW, keyframeClip: NARRATION_2 });
expect(view.host.textContent).not.toContain("narration-2");
expect(view.host.querySelector('[title="Track 1"]')?.textContent).toBe("Track 1");
// Asserted on the rendered text, not a `title`: the name wraps now rather
// than truncating, so it no longer carries a tooltip to be found by.
expect(view.host.textContent).toContain("Track 1");
// Alone on the track it is still named for itself.
view.rerender({
...ROW,
@@ -755,7 +757,7 @@ describe("TimelineTrackHeader", () => {
trackElements: [NARRATION_2],
clipCount: 1,
});
expect(view.host.querySelector('[title="narration-2"]')?.textContent).toBe("narration-2");
expect(view.host.textContent).toContain("narration-2");
act(() => view.root.unmount());
});
});
@@ -89,14 +89,16 @@ export function PlainTrackHeader({
{isAudioTrack && (
<Music size={12} weight="fill" aria-hidden="true" className="text-white/35" />
)}
{/* No `flex-1`: the name takes only the width it needs (still
truncating at `min-w-0` when the row is narrow) so the clip count
sits against it rather than being pushed out to meet the controls.
The slack goes to the `ml-auto` group below instead. */}
{/* No `flex-1`: the name takes only the width it needs, so the clip
count sits against it rather than being pushed out to meet the
controls. The slack goes to the `ml-auto` group below instead.
Wraps rather than truncating: a truncated name needs a hover to be
read at all, and a tooltip is no use to a name you are scanning a
column of. `break-words` so a long single token breaks instead of
forcing the column wider. */}
{showTrackLabel && (
<span className="min-w-0 truncate text-[11px]" title={trackLabel}>
{trackLabel}
</span>
<span className="min-w-0 break-words text-[11px] leading-tight">{trackLabel}</span>
)}
{showTrackLabel && <TrackClipCount clipCount={clipCount} />}
{/* `ml-auto` is what anchors the group right: it absorbs the slack the