authorgravatar for 67492436+naufalw@users.noreply.github.comNaufal Putra <67492436+naufalw@users.noreply.github.com> 2026-03-30 12:33:47+11:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2026-03-30 01:33:47+00:00
log4a4795e5f0f0083e16de64b86b499d8cca718b52
treed45bc13130a726d26d9c6c01ea1a16ee1a299313
parent5b58f74e9ac12387abcbb6a0f727c0571670358e
signature Signed by PGP key B5690EEEBB952194

Scroll active item into view in composer menu (#1557)

Co-authored-by: Julius Marminge <julius0216@outlook.com>

1 files changed, 17 insertions(+), 3 deletions(-)

apps/web/src/components/chat/ComposerCommandMenu.tsx+17-3
...@@ -1,5 +1,5 @@...@@ -1,5 +1,5 @@
1import { type ProjectEntry, type ProviderKind } from "@t3tools/contracts";1import { type ProjectEntry, type ProviderKind } from "@t3tools/contracts";
2import { memo } from "react";2import { memo, useLayoutEffect, useRef } from "react";
3import { type ComposerSlashCommand, type ComposerTriggerKind } from "../../composer-logic";3import { type ComposerSlashCommand, type ComposerTriggerKind } from "../../composer-logic";
4import { BotIcon } from "lucide-react";4import { BotIcon } from "lucide-react";
5import { cn } from "~/lib/utils";5import { cn } from "~/lib/utils";
...@@ -41,6 +41,16 @@ export const ComposerCommandMenu = memo(function ComposerCommandMenu(props: {...@@ -41,6 +41,16 @@ export const ComposerCommandMenu = memo(function ComposerCommandMenu(props: {
41 onHighlightedItemChange: (itemId: string | null) => void;41 onHighlightedItemChange: (itemId: string | null) => void;
42 onSelect: (item: ComposerCommandItem) => void;42 onSelect: (item: ComposerCommandItem) => void;
43}) {43}) {
44 const listRef = useRef<HTMLDivElement>(null);
45
46 useLayoutEffect(() => {
47 if (!props.activeItemId || !listRef.current) return;
48 const el = listRef.current.querySelector<HTMLElement>(
49 `[data-composer-item-id="${CSS.escape(props.activeItemId)}"]`,
50 );
51 el?.scrollIntoView({ block: "nearest" });
52 }, [props.activeItemId]);
53
44 return (54 return (
45 <Command55 <Command
46 autoHighlight={false}56 autoHighlight={false}
...@@ -51,7 +61,10 @@ export const ComposerCommandMenu = memo(function ComposerCommandMenu(props: {...@@ -51,7 +61,10 @@ export const ComposerCommandMenu = memo(function ComposerCommandMenu(props: {
51 );61 );
52 }}62 }}
53 >63 >
54 <div className="relative overflow-hidden rounded-xl border border-border/80 bg-popover/96 shadow-lg/8 backdrop-blur-xs">64 <div
65 ref={listRef}
66 className="relative overflow-hidden rounded-xl border border-border/80 bg-popover/96 shadow-lg/8 backdrop-blur-xs"
67 >
55 <CommandList className="max-h-64">68 <CommandList className="max-h-64">
56 {props.items.map((item) => (69 {props.items.map((item) => (
57 <ComposerCommandMenuItem70 <ComposerCommandMenuItem
...@@ -88,8 +101,9 @@ const ComposerCommandMenuItem = memo(function ComposerCommandMenuItem(props: {...@@ -88,8 +101,9 @@ const ComposerCommandMenuItem = memo(function ComposerCommandMenuItem(props: {
88 return (101 return (
89 <CommandItem102 <CommandItem
90 value={props.item.id}103 value={props.item.id}
104 data-composer-item-id={props.item.id}
91 className={cn(105 className={cn(
92 "cursor-pointer select-none gap-2 data-highlighted:bg-transparent data-highlighted:text-inherit",106 "cursor-pointer select-none gap-2 hover:bg-transparent hover:text-inherit data-highlighted:bg-transparent data-highlighted:text-inherit",
93 props.isActive && "bg-accent! text-accent-foreground!",107 props.isActive && "bg-accent! text-accent-foreground!",
94 )}108 )}
95 onMouseMove={() => {109 onMouseMove={() => {