From e332839aa7d80a599b625f01220a65b60888e969 Mon Sep 17 00:00:00 2001 From: Jesus Armando Anaya Date: Sun, 9 Aug 2026 07:47:03 -0700 Subject: [PATCH 1/2] fix(ui): a select option is two lines when it is an identifier plus its facts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The curated model select put the id, the download size and the hint on one line, inside a trigger measured for one. The text wrapped and read squashed. `SelectItem` takes a `meta`: the children stay the identifier at the label role, `meta` goes underneath at the meta role. Radix renders the selected item's own `ItemText` into the trigger, so the closed control and the open list are the same two lines by construction rather than by a second copy of the layout at the call site. The trigger is `min-h-9` with `py-1` instead of a fixed `h-9`: a one-line option still measures exactly 36px, so every select that shipped before this is unmoved, and a two-line one grows to fit. Nothing truncates and nothing ellipsises — half a model id is not a model id. The styleguide gains the specimen, and its e2e measures what jsdom cannot: the one-line trigger on 36px, the two-line one taller. --- DESIGN.md | 8 +++ frontend/app/e2e/styleguide.spec.ts | 27 ++++++++ frontend/app/src/styleguide/Styleguide.tsx | 38 +++++++++++ frontend/ui-core/src/primitives/Select.tsx | 61 ++++++++++++++++-- .../src/primitives/primitives.test.tsx | 64 +++++++++++++++++++ .../ui-core/src/screens/InferenceScreen.tsx | 22 ++++++- .../ui-core/src/screens/inference.test.tsx | 30 +++++++++ 7 files changed, 241 insertions(+), 9 deletions(-) diff --git a/DESIGN.md b/DESIGN.md index 562fd27e..59513fac 100644 --- a/DESIGN.md +++ b/DESIGN.md @@ -498,6 +498,14 @@ market segment and the domain does not hold one. The single map is quietly out of a list; headings are non-selectable `SelectLabel`s and a category with nothing under it renders none. +**An option that is an identifier plus the facts about it takes two lines** (#472, +2026-08-09) — the identifier at the label role, the facts beneath it at the meta role in +`muted-foreground`. It is `SelectItem`'s `meta` prop, so the closed trigger shows the same +two lines the open list does; the trigger is `min-h-9` rather than `h-9` and grows to fit, +which leaves every one-line select on the contract's 36px. **Nothing truncates**: an +identifier cut off in the middle is not an identifier, so a long one wraps. The specimen is +on the styleguide page. + ### Lists and filtering Any list that can exceed ~20 rows carries a filter input. Filtering is client-side and diff --git a/frontend/app/e2e/styleguide.spec.ts b/frontend/app/e2e/styleguide.spec.ts index eadc51d5..b99dc8a9 100644 --- a/frontend/app/e2e/styleguide.spec.ts +++ b/frontend/app/e2e/styleguide.spec.ts @@ -177,3 +177,30 @@ test("the tab bar sits one rhythm step above its content", async ({ page }) => { expect(panel).not.toBeNull(); expect(panel!.y - (list!.y + list!.height)).toBeCloseTo(12, 0); }); + +/** + * #472's claim, which is a claim about pixels. + * + * `primitives.test.tsx` asserts the *structure* — two elements, the meta in the + * muted role, the id keeping its own line — and jsdom computes no layout, so the + * one thing it cannot see is the thing that was reported: a two-line value inside + * a control measured for one line. A revert to `h-9` leaves every unit test green + * and fails here. + */ +test("a two-line option grows its trigger, and a one-line one stays on the contract's 36px", async ({ + page, +}) => { + const plain = await page.getByLabel("Geometry").boundingBox(); + const stacked = await page.getByLabel("Model").boundingBox(); + expect(plain).not.toBeNull(); + expect(stacked).not.toBeNull(); + + // Unmoved: every select that shipped before the variant is still exactly 36px. + expect(plain!.height).toBeCloseTo(36, 0); + // Grown, not squashed: the second line is inside the box rather than over it. + expect(stacked!.height).toBeGreaterThan(plain!.height); + + // And the identifier is whole — no ellipsis, no clipped end. + const id = page.getByLabel("Model").locator("span", { hasText: "facebook/sam2.1-hiera-base-plus" }).first(); + await expect(id).toHaveCSS("text-overflow", "clip"); +}); diff --git a/frontend/app/src/styleguide/Styleguide.tsx b/frontend/app/src/styleguide/Styleguide.tsx index 9348d140..0e27afd4 100644 --- a/frontend/app/src/styleguide/Styleguide.tsx +++ b/frontend/app/src/styleguide/Styleguide.tsx @@ -235,6 +235,44 @@ export function Styleguide(): JSX.Element { Singular per class — picking a class picks a tool. + {/* + The two-line option (#472). Here because it is a primitive variant + rather than one screen's styling: an option that is an identifier + plus the facts about it stacks them, and the trigger shows the same + two lines the list does because Radix renders the selected item's + own text into it. + */} +
+ + + + The id at the label role, what it costs beneath it. Nothing truncates. + +