Skip to content

fix: keep voice names fully readable in the Generate voice selector - #522

Open
gianpaj wants to merge 1 commit into
mainfrom
claude/issue-521-20260814-2127
Open

fix: keep voice names fully readable in the Generate voice selector#522
gianpaj wants to merge 1 commit into
mainfrom
claude/issue-521-20260814-2127

Conversation

@gianpaj

@gianpaj gianpaj commented Aug 15, 2026

Copy link
Copy Markdown
Owner

What changed

The voice name in the Generate voice selector was ellipsed on mobile (e.g. Zag...) because the name carried truncate and was the flex item that shrank when a long description shared the row.

  • List rows: the name/description row now wraps (flex flex-wrap items-baseline) and the name uses break-words instead of truncate, so a long description moves to its own line and the name is always fully readable.
  • Trigger button: h-12 + whitespace-nowrap + truncate replaced with h-auto min-h-12 py-2 whitespace-normal text-left, so the selected voice name wraps and grows the button instead of being clipped.

Desktop

min-h-12 preserves the current trigger height and rows only wrap when content does not fit, so desktop rendering is unchanged.

Tests

CSS-class only change (no logic, no text-content change). pnpm fixall, pnpm type-check and pnpm test could not be run in the agent environment (no pnpm/node_modules) - relying on CI.

Fixes #521

Generated with Claude Code

Long descriptions squeezed the truncated voice name in the dropdown list
(e.g. "Zag..."). Let the name/description row wrap so the name always
renders in full, and allow the trigger button to grow instead of
clipping the selected voice name.

Refs #521

Co-authored-by: Gianfranco P <899175+gianpaj@users.noreply.github.com>
@vercel

vercel Bot commented Aug 15, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
sexyvoice Ready Ready Preview Aug 15, 2026 1:43am
sexyvoice-docs Ready Ready Preview Aug 15, 2026 1:43am

Request Review

@claude

claude Bot commented Aug 15, 2026

Copy link
Copy Markdown
Contributor

Claude finished @gianpaj's task in 4m 6s —— View job


Code review: PR #522

  • Gather context (diff + voice-select.tsx, ui/button.tsx, call sites, tests)
  • Analyze the change
  • Post review

Verdict: the fix targets the right root cause and looks correct. A few polish/edge-case notes below, nothing blocking.


What's right

  • Correct diagnosis. In both places the name was the shrinking flex item next to a long sibling. In the trigger, <span className="flex min-w-0 flex-col"> (voice-select.tsx:258) is squeezed by the meta line's max-content width, so truncate on the name fired even though the name is short. flex-wrap + break-words fixes that properly rather than papering over it.
  • cn/twMerge overrides check out. buttonVariants puts className last (components/ui/button.tsx:80), so h-auto beats the size variant's h-10, whitespace-normal beats the base whitespace-nowrap, and px-3 py-2 beats px-4 py-2 (components/ui/button.tsx:10,39). No dead classes.
  • Class ordering matches repo convention (whitespace-* before padding, cf. clone-sample-card.tsx:127), and noDuplicateClasses has nothing to flag.
  • Blast radius is small. VoiceSelect has exactly one call site (voice-selector.tsx:104) and it passes no className, so no consumer can reintroduce a fixed h-*. It sits in a vertical space-y-6 stack, so a taller trigger can't break the surrounding layout.
  • No logic or text-content change, so tests/components/voice-selector.test.tsx stays valid — no test update needed.

Notes

1. The trigger can get noticeably taller on mobile (voice-select.tsx:262-267)

Removing whitespace-nowrap also un-clamps the meta line, not just the name. With a real description like Versatile voice suitable for various contexts, Gemini 2.5 · Versatile voice… is ~58 chars at text-xs in ~250px of usable width on a 360px screen → 2 lines, so the trigger goes ~48px → ~62px and changes height as the user switches voices.

Issue #521 is about the name, so you can keep the fix and keep the height stable by clamping only the meta line:

-<span className="flex flex-wrap items-center gap-1.5 text-muted-foreground text-xs">
+<span className="flex w-full min-w-0 items-center gap-1.5 text-muted-foreground text-xs">
   {selectedModel && <ModelDot model={selectedModel} />}
-  <span className="break-words">
+  <span className="truncate">
     {selectedModel} &middot; {selected.description ?? ''}
   </span>
 </span>

The name still wraps in full; the secondary metadata is the thing that ellipses. Your call — deliberately letting it wrap is also defensible, it just isn't described in the PR body ("desktop rendering is unchanged" is true; mobile trigger height does change). Fix this →

2. min-w-0 is on the wrong sibling in the list row (voice-select.tsx:445-453)

min-w-0 was added to the description but not the name. Inside a flex-wrap container this is backwards on both counts:

  • On the description it's close to a no-op — line breaking uses the hypothetical (max-content) size, so a long description already moves to its own line and then wraps normally.
  • On the name it's the one that would actually matter: break-words (overflow-wrap: break-word) doesn't shrink a flex item's automatic minimum size, which stays at min-content. So a single unbreakable long token can push the row wider than the popover instead of breaking.

Only curated publicVoices reach this component today (voice-selector.tsx:107), so names are short and this is theoretical — but if user-cloned voices are ever passed in, an over-long name overflows horizontally. min-w-0 on the name span (or break-all) makes break-words actually able to break.

3. No row gap when the row wraps (voice-select.tsx:445)

gap-x-2 sets no row-gap, so once the description drops to its own line the two lines sit flush. gap-x-2 gap-y-0.5 reads better and matches the mt-0.5 you already use on the line below (voice-select.tsx:455).

4. Icon alignment on the wrapped trigger (voice-select.tsx:254-257)

items-center now vertically centers the 28px icon square against a 2–3 line text block instead of the first line. items-start on that row (or self-start on the icon) keeps it aligned to the name.

5. Pre-existing, on a line you touched (voice-select.tsx:265)

{selectedModel} &middot; {selected.description ?? ''} renders a dangling Gemini 2.5 · when description is null — which is the common case for several voices (the test fixtures use description: null). Cheap to fold in while you're here.

Verification

I could not run pnpm fixall / pnpm type-check / pnpm testpnpm and node_modules are not available in this environment, matching what the PR body says. Since this is a class-string-only change with no logic or rendered-text change, CI should be sufficient; the twMerge and class-ordering concerns I could check statically are clean.
• branch claude/issue-521-20260814-2127

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 856257ba3c

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

to their own line and the voice name stays readable
on narrow screens. */}
<span className="flex flex-wrap items-baseline gap-x-2">
<span className="break-words font-medium text-sm">

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Let unbroken voice names shrink within list rows

When a public voice has a single unbroken name wider than the available mobile row, this span remains a flex item with the default automatic minimum width. break-words does not reduce that min-content width, so the item cannot shrink and wrap; it instead overflows the popover and can run beneath the selection icon. The database stores names as unconstrained text, so add a shrinking constraint such as min-w-0 max-w-full or use an overflow-wrap mode that affects intrinsic sizing.

Useful? React with 👍 / 👎.

@argos-ci

argos-ci Bot commented Aug 15, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Argos notifications ↗︎

Build Status Details Updated (UTC)
default (Inspect) ⚠️ Changes detected (Review) 2 changed Aug 15, 2026, 1:47 AM

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ No new issues found.

Reviewed changes — a CSS-class-only fix to apps/web/components/voice-select.tsx that stops the voice name from being ellipsed in the Generate voice picker on narrow screens (issue #521). No logic, text content, or i18n keys change.

  • List rows stop truncating — the name/description row becomes flex flex-wrap items-baseline gap-x-2 and the name swaps truncate for break-words, so a long description moves to its own line instead of squeezing the name. This is the change that actually fixes the screenshot in the issue.
  • Trigger button grows instead of clippingh-12 … whitespace-nowrap becomes h-auto min-h-12 … whitespace-normal py-2 text-left, so the selected voice name and description wrap. Worth noting the meta line previously overflowed the button's rounded border rather than being clipped, since the cva base whitespace-nowrap applied and nothing hid the overflow — this quietly fixes that too.
  • Class resolution verifiedButton is a cva set whose base carries whitespace-nowrap items-center justify-center and default size h-10 px-4 py-2. Because cva appends className last and cn() is tailwind-merge, every override (h-10h-auto, px-4px-3, justify-centerjustify-between, whitespace-nowrapwhitespace-normal) resolves the intended way. VoiceSelect's only call site (components/voice-selector.tsx:104) passes no className, so there is no override to conflict with.
  • break-words placement — load-bearing on the trigger name, which sits in a column-direction flex container under items-start where its width is clamped to the available space.

ℹ️ The trigger is a few pixels taller than before, so Argos will flag a visual diff

The PR description says desktop rendering is unchanged, which is very nearly true but not exactly. min-h-12 is a floor, not a fixed height: the trigger's content is text-sm leading-tight (17.5px) stacked on text-xs (16px) = 33.5px, which is taller than the size-7 icon, and adding py-2 plus the 1px border pushes the total just past the old fixed 48px. Cosmetically negligible, but apps/web/e2e/generate-dashboard.spec.ts takes an argosScreenshot right after asserting the voice selector is visible, so the collapsed trigger is in the Argos baseline for every Playwright project and the check will report a diff that needs manual approval. Nothing to change — just don't read that red check as a regression.

Technical details
# Argos baseline shift on the Generate dashboard

## Affected sites
- `apps/web/e2e/generate-dashboard.spec.ts:57``argosScreenshot(page, \`generate-dashboard-${testInfo.project.name}\`)` runs after `expect(generatePage.voiceSelector).toBeVisible()`, so the collapsed `VoiceSelect` trigger is inside the snapshot.
- `apps/web/e2e/pages/generate.page.ts:37``voiceSelector = page.getByRole('combobox').first()` resolves to the changed `Button`.
- `apps/web/components/voice-select.tsx:247``h-auto min-h-12 … py-2 text-left` replaces the fixed `h-12`.

## Required outcome
- The Argos diff on `generate-dashboard-*` is reviewed and accepted as the intended baseline rather than investigated as a regression. No code change is required.

## Open questions for the human
- Is the mobile Playwright project's screenshot the evidence you wanted for "still readable on desktop too" from issue #521? If so, the accepted Argos baseline doubles as the verification the issue asked for.

Pullfrog  | ⚠️ this action is pinned to a commit SHA, which freezes the cleanup step — switch to @v0 or keep the SHA fresh with Dependabot | View workflow run | Using Claude Opus𝕏

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Fix UI in Generate voice selector

1 participant