| ... | @@ -1,5 +1,5 @@ | ... | @@ -1,5 +1,5 @@ |
| 1 | import { type ProjectEntry, type ProviderKind } from "@t3tools/contracts"; | 1 | import { type ProjectEntry, type ProviderKind } from "@t3tools/contracts"; |
| 2 | import { memo } from "react"; | 2 | import { memo, useLayoutEffect, useRef } from "react"; |
| 3 | import { type ComposerSlashCommand, type ComposerTriggerKind } from "../../composer-logic"; | 3 | import { type ComposerSlashCommand, type ComposerTriggerKind } from "../../composer-logic"; |
| 4 | import { BotIcon } from "lucide-react"; | 4 | import { BotIcon } from "lucide-react"; |
| 5 | import { cn } from "~/lib/utils"; | 5 | import { 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 | <Command | 55 | <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 | <ComposerCommandMenuItem | 70 | <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 | <CommandItem | 102 | <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={() => { |