feat(web): show fast mode as a bolt instead of a "Normal" label#4488
Conversation
The traits trigger spelled out the fast-mode state as text, so the near-universal off case burned horizontal space on the word "Normal". Drop fastMode from the trigger's text label entirely and render a lightning bolt only when it's on, matching how Codex presents it. The label-building logic moves into an exported pure function so it can be tested without mounting the menu. When fast mode is the only trait, fall back to the old Fast/Normal text so the trigger doesn't render as a bare bolt or bare chevron. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
Important Review skippedAuto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit d505694. Configure here.
ApprovabilityVerdict: Needs human review An unresolved review comment identifies a logic bug in the fallback condition where descriptors with empty labels incorrectly trigger the sole-trait fallback, suppressing the bolt icon. This substantive issue warrants human review. You can customize Macroscope's approvability policy. Learn more. |
Keying the Fast/Normal fallback off an empty label list also caught the case where non-fastMode descriptors resolved to no label — a select with neither a currentValue nor an isDefault option. A model without fast mode at all would then print a bogus "Normal" where it used to stay blank. Track whether a fastMode descriptor was actually present and require it for the fallback. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
| if (labels.length === 0 && hasFastMode) { | ||
| return { label: input.fastModeEnabled ? "Fast" : "Normal", showFastModeIcon: false }; | ||
| } |
There was a problem hiding this comment.
🟡 Medium chat/TraitsPicker.tsx:416
When a model has fastMode plus another trait (e.g. a select descriptor) whose current label resolves to empty, buildTraitsTriggerDisplay incorrectly returns "Fast"/"Normal" and suppresses the bolt icon. The fallback labels.length === 0 && hasFastMode only checks that no label was produced — it does not confirm fast mode is the only descriptor. Any non-fast descriptor whose label is empty (a valid state covered by the new tests) triggers the sole-trait fallback, dropping the real trait's display and hiding the bolt. Consider checking the total descriptor count or the presence of non-fast descriptors instead of relying on labels.length.
| if (labels.length === 0 && hasFastMode) { | |
| return { label: input.fastModeEnabled ? "Fast" : "Normal", showFastModeIcon: false }; | |
| } | |
| // Only fall back to text when fast mode is genuinely the sole trait. | |
| const nonFastModeDescriptors = input.descriptors.filter( | |
| (descriptor) => !(descriptor.id === "fastMode" && descriptor.type === "boolean"), | |
| ); | |
| if (nonFastModeDescriptors.length === 0 && hasFastMode) { | |
| return { label: input.fastModeEnabled ? "Fast" : "Normal", showFastModeIcon: false }; | |
| } |
🚀 Reply "fix it for me" or copy this AI Prompt for your agent:
In file @apps/web/src/components/chat/TraitsPicker.tsx around lines 416-418:
When a model has `fastMode` plus another trait (e.g. a select descriptor) whose current label resolves to empty, `buildTraitsTriggerDisplay` incorrectly returns `"Fast"`/`"Normal"` and suppresses the bolt icon. The fallback `labels.length === 0 && hasFastMode` only checks that no label was produced — it does not confirm fast mode is the only descriptor. Any non-fast descriptor whose label is empty (a valid state covered by the new tests) triggers the sole-trait fallback, dropping the real trait's display and hiding the bolt. Consider checking the total descriptor count or the presence of non-fast descriptors instead of relying on `labels.length`.
There was a problem hiding this comment.
I checked this one against the actual behavior and I'm going to decline the suggestion — it makes the case strictly worse.
The scenario is real: fastMode + a select whose label resolves to empty. But compare what the two versions render:
| current | suggested | |
|---|---|---|
| fast on | Fast |
`` (empty, bolt only) |
| fast off | Normal |
`` (empty, bare chevron) |
Since the other descriptor produced no label, there is nothing else to display. Gating on nonFastModeDescriptors.length rather than labels.length doesn't recover the missing trait — it just skips the fallback and emits an empty label. That's the degenerate trigger the fallback exists to prevent: with fast off you get a bare chevron with no text at all.
dropping the real trait's display and hiding the bolt
There is no real trait display to drop in this branch — labels is empty precisely because that descriptor resolved to nothing. Fast/Normal is a lossless description of the only state that actually resolved, and the bolt is redundant next to the word "Fast", which is why it's suppressed.
Verified both paths directly rather than reasoning from the diff: current returns {label: "Fast", showFastModeIcon: false}; the suggested condition returns {label: "", showFastModeIcon: true}.
Worth noting this is a doubly-degenerate state — a provider would have to ship a select with neither a currentValue nor an isDefault option. No provider in the repo does. Both versions are edge-case handling for something unreachable today; I'd rather the unreachable branch degrade to readable text than to an empty button.
## What's Changed * feat(web): show fast mode as a bolt instead of a "Normal" label by @t3dotgg in pingdotgg/t3code#4488 **Full Changelog**: pingdotgg/t3code@v0.0.29-nightly.20260725.898...v0.0.29-nightly.20260725.899 Upstream release: https://github.com/pingdotgg/t3code/releases/tag/v0.0.29-nightly.20260725.899

The composer's traits trigger spelled out the fast-mode state as text, so the near-universal off case burned horizontal space on the word "Normal":
High · Normal · 1M.Now it works the way Codex does it — a lightning bolt when fast mode is on, and nothing at all when it's off.
High · Normal · 1MHigh · 1MHigh · 1MChanges
fastModeno longer contributes to the trigger's text label.ZapIconrenders at the start of the trigger, with ansr-only"Fast mode on" — the icon is now the only signal, so screen readers need the text.buildTraitsTriggerDisplayso it's testable without mounting the menu.Edge case
For models where fast mode is the only trait, dropping the text would leave a bare bolt or bare chevron with nothing to read. That case falls back to the old
Fast/Normaltext and skips the icon. Every model currently in the providers pairs fast mode with at least an effort select, so in practice you always get the icon path — the fallback just keeps the component from degrading if a provider ever exposes fast mode alone.Mobile is untouched:
providerOptionsConfigurationLabelalready drops boolean traits when they're off, so it never showed "Normal".Testing
pnpm --filter @t3tools/web typecheck— cleanpnpm -w run lint— clean, no new warnings on this fileTraitsPicker.test.tsand existingcomposerProviderState.test.tsxNot visually confirmed in a running browser — if the gap next to the label looks off, it's a one-line tweak to the
gap-1.5on the Codex-style span.🤖 Generated with Claude Code
Note
Low Risk
Composer UI and display logic only; behavior is covered by new unit tests with no auth or data changes.
Overview
The composer traits trigger no longer shows Fast/Normal in the joined label when fast mode sits alongside other traits. Off is silent; on adds a
ZapIconbolt (plussr-only“Fast mode on”) ahead of labels likeHigh · 1M.Label logic moves into exported
buildTraitsTriggerDisplay, with unit tests covering off/on, other booleans, ultrathink, fast-mode-only fallback text, and empty labels without bogus Normal. Codex-style triggers use slightly tighter gap and a truncating label span.Reviewed by Cursor Bugbot for commit 364966c. Bugbot is set up for automated code reviews on this repo. Configure here.
Note
Show a bolt icon instead of 'Fast'/'Normal' text for fast mode in the chat traits picker
buildTraitsTriggerDisplayin TraitsPicker.tsx, a pure function that builds the trigger label and ashowFastModeIconflag from provider option descriptors.ZapIcon(bolt) with an sr-only 'Fast mode on' label instead of the word 'Normal' or 'Fast'.ultrathinkPromptControlledmatches the primary select.📊 Macroscope summarized 364966c. 1 file reviewed, 0 issues evaluated, 0 issues filtered, 0 comments posted
🗂️ Filtered Issues
No issues evaluated.