authorgravatar for julius0216@outlook.comJulius Marminge <julius0216@outlook.com> 2026-09-22 12:22:11-07:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2026-09-22 12:22:11-07:00
loged856946c63e51861a8153d264856db0dbb677a4
tree1a3d457d4bba41cfc972abeb755f849a42394d65
parentce98d4107758ed7a492f94a93b3685a828caeba1
signature Signed by PGP key B5690EEEBB952194

refactor(web): add an active prop to CommandItem (#13023)

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>

4 files changed, 17 insertions(+), 11 deletions(-)

apps/web/src/components/CommandPaletteResults.tsx+2-5
...@@ -15,7 +15,6 @@ import {...@@ -15,7 +15,6 @@ import {
15 CommandShortcut,15 CommandShortcut,
16} from "./ui/command";16} from "./ui/command";
17import { ThreadSearchMatchExcerpt } from "./ThreadSearchMatch";17import { ThreadSearchMatchExcerpt } from "./ThreadSearchMatch";
18import { cn } from "~/lib/utils";
1918
20interface CommandPaletteResultsProps {19interface CommandPaletteResultsProps {
21 emptyStateMessage?: string;20 emptyStateMessage?: string;
...@@ -109,10 +108,8 @@ function CommandPaletteResultRow(props: {...@@ -109,10 +108,8 @@ function CommandPaletteResultRow(props: {
109 return (108 return (
110 <CommandItem109 <CommandItem
111 value={props.item.value}110 value={props.item.value}
112 className={cn(111 active={props.isActive}
113 "cursor-pointer gap-2 hover:bg-transparent hover:text-inherit data-highlighted:bg-transparent data-highlighted:text-inherit data-selected:bg-transparent data-selected:text-inherit [&[data-highlighted][data-selected]]:bg-transparent [&[data-highlighted][data-selected]]:text-inherit",112 className="gap-2"
114 props.isActive && "bg-accent! text-accent-foreground!",
115 )}
116 onMouseDown={(event) => {113 onMouseDown={(event) => {
117 event.preventDefault();114 event.preventDefault();
118 }}115 }}
apps/web/src/components/chat/ComposerCommandMenu.tsx+2-4
...@@ -161,10 +161,8 @@ const ComposerCommandMenuItem = memo(function ComposerCommandMenuItem(props: {...@@ -161,10 +161,8 @@ const ComposerCommandMenuItem = memo(function ComposerCommandMenuItem(props: {
161 <CommandItem161 <CommandItem
162 value={props.item.id}162 value={props.item.id}
163 data-composer-item-id={props.item.id}163 data-composer-item-id={props.item.id}
164 className={cn(164 active={props.isActive}
165 "cursor-pointer select-none gap-3 rounded-lg px-3 py-2! hover:bg-transparent hover:text-inherit data-highlighted:bg-transparent data-highlighted:text-inherit",165 className="gap-3 rounded-lg px-3 py-2!"
166 props.isActive && "bg-accent! text-accent-foreground!",
167 )}
168 onMouseMove={() => {166 onMouseMove={() => {
169 if (!props.isActive) props.onHighlight(props.item.id);167 if (!props.isActive) props.onHighlight(props.item.id);
170 }}168 }}
apps/web/src/components/ui/command.tsx+12-1
...@@ -160,13 +160,24 @@ function CommandCollection({ ...props }: React.ComponentProps<typeof Autocomplet...@@ -160,13 +160,24 @@ function CommandCollection({ ...props }: React.ComponentProps<typeof Autocomplet
160 return <AutocompleteCollection data-slot="command-collection" {...props} />;160 return <AutocompleteCollection data-slot="command-collection" {...props} />;
161}161}
162162
163function CommandItem({ className, ...props }: React.ComponentProps<typeof AutocompleteItem>) {163// Pass `active` when the palette tracks the highlighted row itself; the
164// primitive's hover and keyboard highlight are then ignored so the two never
165// disagree, and the row shows the active surface instead.
166function CommandItem({
167 className,
168 active,
169 ...props
170}: React.ComponentProps<typeof AutocompleteItem> & { active?: boolean }) {
164 return (171 return (
165 <AutocompleteItem172 <AutocompleteItem
166 className={cn(173 className={cn(
167 "py-1.5 data-selected:bg-foreground/[0.06] data-highlighted:bg-foreground/[0.09] data-highlighted:text-foreground [&[data-highlighted][data-selected]]:bg-foreground/[0.09] [&[data-highlighted][data-selected]]:text-foreground",174 "py-1.5 data-selected:bg-foreground/[0.06] data-highlighted:bg-foreground/[0.09] data-highlighted:text-foreground [&[data-highlighted][data-selected]]:bg-foreground/[0.09] [&[data-highlighted][data-selected]]:text-foreground",
175 active !== undefined &&
176 "cursor-pointer hover:bg-transparent hover:text-inherit data-highlighted:bg-transparent data-highlighted:text-inherit data-selected:bg-transparent data-selected:text-inherit [&[data-highlighted][data-selected]]:bg-transparent [&[data-highlighted][data-selected]]:text-inherit",
177 active && "bg-accent! text-accent-foreground!",
168 className,178 className,
169 )}179 )}
180 data-active={active || undefined}
170 data-slot="command-item"181 data-slot="command-item"
171 {...props}182 {...props}
172 />183 />
scripts/lint-restyle-ceiling.ts+1-1
...@@ -8,7 +8,7 @@ import * as NodeURL from "node:url";...@@ -8,7 +8,7 @@ import * as NodeURL from "node:url";
8// components/ui exports are migrated to variants (see vite.config.ts). This gate keeps the8// components/ui exports are migrated to variants (see vite.config.ts). This gate keeps the
9// count from growing: CI fails when findings exceed the ceiling. Lower the ceiling when you9// count from growing: CI fails when findings exceed the ceiling. Lower the ceiling when you
10// migrate a file, and delete this script when the rule becomes an error.10// migrate a file, and delete this script when the rule becomes an error.
11export const RESTYLE_CEILING = 1159;11export const RESTYLE_CEILING = 1143;
1212
13const RULE = "shadcn(no-restyle)";13const RULE = "shadcn(no-restyle)";
1414