import { type ProjectEntry, type ProviderDriverKind, type ServerProviderSkill, type ServerProviderSlashCommand, } from "@t3tools/contracts"; import { BotIcon } from "lucide-react"; import { memo, useLayoutEffect, useMemo, useRef } from "react"; import { type ComposerSlashCommand, type ComposerTriggerKind } from "../../composer-logic"; import { formatProviderSkillInstallSource } from "~/providerSkillPresentation"; import { cn } from "~/lib/utils"; import { Command, CommandGroup, CommandGroupLabel, CommandItem, CommandList, CommandSeparator, } from "../ui/command"; import { PierreEntryIcon } from "./PierreEntryIcon"; export type ComposerCommandItem = | { id: string; type: "path"; path: string; pathKind: ProjectEntry["kind"]; label: string; description: string; } | { id: string; type: "slash-command"; command: ComposerSlashCommand; label: string; description: string; } | { id: string; type: "provider-slash-command"; provider: ProviderDriverKind; command: ServerProviderSlashCommand; label: string; description: string; } | { id: string; type: "skill"; provider: ProviderDriverKind; skill: ServerProviderSkill; label: string; description: string; }; type ComposerCommandGroup = { id: string; label: string | null; items: ComposerCommandItem[]; }; function SkillGlyph(props: { className?: string }) { return ( ); } function groupCommandItems( items: ComposerCommandItem[], triggerKind: ComposerTriggerKind | null, groupSlashCommandSections: boolean, ): ComposerCommandGroup[] { if (triggerKind === "skill") { return items.length > 0 ? [{ id: "skills", label: "Skills", items }] : []; } if (triggerKind !== "slash-command" || !groupSlashCommandSections) { return [{ id: "default", label: null, items }]; } const builtInItems = items.filter((item) => item.type === "slash-command"); const providerItems = items.filter((item) => item.type === "provider-slash-command"); const groups: ComposerCommandGroup[] = []; if (builtInItems.length > 0) { groups.push({ id: "built-in", label: "Built-in", items: builtInItems }); } if (providerItems.length > 0) { groups.push({ id: "provider", label: "Provider", items: providerItems }); } return groups; } export const ComposerCommandMenu = memo(function ComposerCommandMenu(props: { items: ComposerCommandItem[]; resolvedTheme: "light" | "dark"; isLoading: boolean; triggerKind: ComposerTriggerKind | null; groupSlashCommandSections?: boolean; emptyStateText?: string; activeItemId: string | null; onHighlightedItemChange: (itemId: string | null) => void; onSelect: (item: ComposerCommandItem) => void; }) { const listRef = useRef(null); const groups = useMemo( () => groupCommandItems(props.items, props.triggerKind, props.groupSlashCommandSections ?? true), [props.groupSlashCommandSections, props.items, props.triggerKind], ); useLayoutEffect(() => { if (!props.activeItemId || !listRef.current) return; const el = listRef.current.querySelector( `[data-composer-item-id="${CSS.escape(props.activeItemId)}"]`, ); el?.scrollIntoView({ block: "nearest" }); }, [props.activeItemId]); return ( { props.onHighlightedItemChange( typeof highlightedValue === "string" ? highlightedValue : null, ); }} >
{props.items.length > 0 ? ( {groups.map((group, groupIndex) => (
{groupIndex > 0 ? : null} {group.label ? ( {group.label} ) : null} {group.items.map((item) => ( ))}
))}
) : (
{props.triggerKind === "skill" ? ( Skills

{props.isLoading ? "Searching workspace skills..." : (props.emptyStateText ?? "No skills found. Try / to browse provider commands.")}

) : (

{props.isLoading ? "Searching workspace files..." : (props.emptyStateText ?? (props.triggerKind === "path" ? "No matching files or folders." : "No matching command."))}

)}
)}
); }); const ComposerCommandMenuItem = memo(function ComposerCommandMenuItem(props: { item: ComposerCommandItem; resolvedTheme: "light" | "dark"; isActive: boolean; onHighlight: (itemId: string | null) => void; onSelect: (item: ComposerCommandItem) => void; }) { const skillSourceLabel = props.item.type === "skill" ? formatProviderSkillInstallSource(props.item.skill) : null; return ( { if (!props.isActive) props.onHighlight(props.item.id); }} onMouseDown={(event) => { event.preventDefault(); }} onClick={() => { props.onSelect(props.item); }} > {props.item.type === "path" ? ( ) : null} {props.item.type === "slash-command" ? ( ) : null} {props.item.type === "provider-slash-command" ? ( ) : null} {props.item.type === "skill" ? ( ) : null} {props.item.label} {props.item.description} {skillSourceLabel ? ( {skillSourceLabel} ) : null} ); });