Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 14 additions & 9 deletions apps/web/src/components/chat/ChatComposer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ import { Tooltip, TooltipPopup, TooltipTrigger } from "../ui/tooltip";
import { toastManager } from "../ui/toast";
import {
BotIcon,
CheckIcon,
CircleAlertIcon,
ListTodoIcon,
PencilRulerIcon,
Expand Down Expand Up @@ -271,16 +272,20 @@ const ComposerFooterModeControls = memo(function ComposerFooterModeControls(prop
{runtimeModeOptions.map((mode) => {
const option = runtimeModeConfig[mode];
const OptionIcon = option.icon;
const isSelected = props.runtimeMode === mode;
return (
<SelectItem key={mode} value={mode} className="min-w-64 py-2">
<div className="grid min-w-0 gap-0.5">
<span className="inline-flex items-center gap-1.5 font-medium text-foreground">
<OptionIcon className="size-3.5 shrink-0 text-muted-foreground" />
{option.label}
</span>
<span className="text-muted-foreground text-xs leading-4">
{option.description}
</span>
<SelectItem key={mode} value={mode} hideIndicator className="min-w-64 py-2">
<div className="flex min-w-0 items-center gap-3">
<div className="grid min-w-0 flex-1 gap-0.5">
<span className="inline-flex items-center gap-1.5 font-medium text-foreground">
<OptionIcon className="size-3.5 shrink-0 text-muted-foreground" />
{option.label}
</span>
<span className="text-muted-foreground text-xs leading-4">
{option.description}
</span>
</div>
<CheckIcon className={cn("size-4", isSelected ? "opacity-100" : "opacity-0")} />

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Muted runtime mode checkmark

Low Severity

The new right-side CheckIcon sits inside SelectItem’s ItemText, which forces SVGs without a text-* class to text-muted-foreground. The old left indicator sat outside ItemText and kept normal foreground contrast, so the selected-state check now matches decorative icons instead of a selection affordance.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 4bbdcf7. Configure here.

</div>
</SelectItem>
);
Expand Down
83 changes: 55 additions & 28 deletions apps/web/src/components/chat/TraitsPicker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import {
} from "@t3tools/shared/model";
import { memo, useCallback, useState } from "react";
import type { VariantProps } from "class-variance-authority";
import { ChevronDownIcon } from "lucide-react";
import { CheckIcon, ChevronDownIcon } from "lucide-react";
import { Button, buttonVariants } from "../ui/button";
import {
Menu,
Expand Down Expand Up @@ -298,25 +298,39 @@ export const TraitsMenuContent = memo(function TraitsMenuContentImpl({
option.
</div>
) : null}
<MenuRadioGroup
value={
{(() => {
const selectedValue =
ultrathinkPromptControlled && descriptor.id === primarySelectDescriptor?.id
? "ultrathink"
: (getDescriptorStringValue(descriptor) ?? "")
}
onValueChange={(value) => handleSelectChange(descriptor, value)}
>
{descriptor.options.map((option) => (
<MenuRadioItem
key={option.id}
value={option.id}
disabled={ultrathinkInBodyText && descriptor.id === primarySelectDescriptor?.id}
: (getDescriptorStringValue(descriptor) ?? "");
return (
<MenuRadioGroup
value={selectedValue}
onValueChange={(value) => handleSelectChange(descriptor, value)}
>
{option.label}
{option.isDefault ? " (default)" : ""}
</MenuRadioItem>
))}
</MenuRadioGroup>
{descriptor.options.map((option) => (
<MenuRadioItem
key={option.id}
value={option.id}
hideIndicator
disabled={
ultrathinkInBodyText && descriptor.id === primarySelectDescriptor?.id
}
>
<span className="flex w-full min-w-0 items-center justify-between gap-3">
<span className="min-w-0 truncate">
{option.label}
{option.isDefault ? " (default)" : ""}
</span>
{option.id === selectedValue ? (
<CheckIcon className="size-3.5 shrink-0" />
) : null}
</span>
</MenuRadioItem>
))}
</MenuRadioGroup>
);
})()}
</MenuGroup>
</div>
))}
Expand All @@ -327,17 +341,30 @@ export const TraitsMenuContent = memo(function TraitsMenuContentImpl({
<div className="px-2 py-1.5 font-medium text-muted-foreground text-xs">
{descriptor.label}
</div>
<MenuRadioGroup
value={descriptor.currentValue === true ? "on" : "off"}
onValueChange={(value) => {
updateDescriptors(
replaceDescriptorCurrentValue(descriptors, descriptor.id, value === "on"),
);
}}
>
<MenuRadioItem value="on">On</MenuRadioItem>
<MenuRadioItem value="off">Off</MenuRadioItem>
</MenuRadioGroup>
{(() => {
const selectedValue = descriptor.currentValue === true ? "on" : "off";
return (
<MenuRadioGroup
value={selectedValue}
onValueChange={(value) => {
updateDescriptors(
replaceDescriptorCurrentValue(descriptors, descriptor.id, value === "on"),
);
}}
>
{(["on", "off"] as const).map((value) => (
<MenuRadioItem key={value} value={value} hideIndicator>
<span className="flex w-full min-w-0 items-center justify-between gap-3">
<span>{value === "on" ? "On" : "Off"}</span>
{value === selectedValue ? (
<CheckIcon className="size-3.5 shrink-0" />
) : null}
</span>
</MenuRadioItem>
))}
</MenuRadioGroup>
);
})()}
</MenuGroup>
</div>
))}
Expand Down
46 changes: 28 additions & 18 deletions apps/web/src/components/ui/menu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -147,32 +147,42 @@ function MenuRadioGroup(props: MenuPrimitive.RadioGroup.Props) {
return <MenuPrimitive.RadioGroup data-slot="menu-radio-group" {...props} />;
}

function MenuRadioItem({ className, children, ...props }: MenuPrimitive.RadioItem.Props) {
function MenuRadioItem({
className,
children,
hideIndicator = false,
...props
}: MenuPrimitive.RadioItem.Props & {
hideIndicator?: boolean;
}) {
return (
<MenuPrimitive.RadioItem
className={cn(
"grid min-h-8 in-data-[side=none]:min-w-[calc(var(--anchor-width)+1.25rem)] cursor-default grid-cols-[1rem_1fr] items-center gap-2 rounded-sm py-1 ps-2 pe-4 text-base text-foreground outline-none data-disabled:pointer-events-none data-highlighted:bg-accent data-highlighted:text-accent-foreground data-disabled:opacity-64 sm:min-h-7 sm:text-sm [&_svg:not([class*='size-'])]:size-4.5 sm:[&_svg:not([class*='size-'])]:size-4 [&_svg]:pointer-events-none [&_svg]:shrink-0",
"grid min-h-8 in-data-[side=none]:min-w-[calc(var(--anchor-width)+1.25rem)] cursor-default items-center gap-2 rounded-sm py-1 text-base text-foreground outline-none data-disabled:pointer-events-none data-highlighted:bg-accent data-highlighted:text-accent-foreground data-disabled:opacity-64 sm:min-h-7 sm:text-sm [&_svg:not([class*='size-'])]:size-4.5 sm:[&_svg:not([class*='size-'])]:size-4 [&_svg]:pointer-events-none [&_svg]:shrink-0",
hideIndicator ? "grid-cols-[1fr] ps-3 pe-3" : "grid-cols-[1rem_1fr] ps-2 pe-4",
className,
)}
data-slot="menu-radio-item"
{...props}
>
<MenuPrimitive.RadioItemIndicator className="col-start-1">
<svg
fill="none"
height="24"
stroke="currentColor"
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="2"
viewBox="0 0 24 24"
width="24"
xmlns="http://www.w3.org/2000/svg"
>
<path d="M5.252 12.7 10.2 18.63 18.748 5.37" />
</svg>
</MenuPrimitive.RadioItemIndicator>
<span className="col-start-2">{children}</span>
{hideIndicator ? null : (
<MenuPrimitive.RadioItemIndicator className="col-start-1">
<svg
fill="none"
height="24"
stroke="currentColor"
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="2"
viewBox="0 0 24 24"
width="24"
xmlns="http://www.w3.org/2000/svg"
>
<path d="M5.252 12.7 10.2 18.63 18.748 5.37" />
</svg>
</MenuPrimitive.RadioItemIndicator>
)}
<span className={hideIndicator ? "col-start-1" : "col-start-2"}>{children}</span>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Label truncation may not apply

Low Severity

MenuRadioItem wraps children in a grid cell without min-w-0, unlike SelectItem. The new traits rows use truncate with a right-side check inside that cell, so long option labels can expand or overflow instead of ellipsizing when the menu width is constrained.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 4bbdcf7. Configure here.

</MenuPrimitive.RadioItem>
);
}
Expand Down
Loading