Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 | import React, { useCallback, useEffect, useRef } from "react" import { getMatchingSlashCommands, SlashCommand } from "@/utils/slash-commands" interface SlashCommandMenuProps { onSelect: (command: SlashCommand) => void selectedIndex: number setSelectedIndex: (index: number) => void onMouseDown: () => void query: string localWorkflowToggles?: Record<string, boolean> globalWorkflowToggles?: Record<string, boolean> } const SlashCommandMenu: React.FC<SlashCommandMenuProps> = ({ onSelect, selectedIndex, setSelectedIndex, onMouseDown, query, localWorkflowToggles = {}, globalWorkflowToggles = {}, }) => { const menuRef = useRef<HTMLDivElement>(null) const handleClick = useCallback( (command: SlashCommand) => { onSelect(command) }, [onSelect], ) useEffect(() => { if (menuRef.current) { const selectedElement = menuRef.current.querySelector(`#slash-command-menu-item-${selectedIndex}`) as HTMLElement if (selectedElement) { const menuRect = menuRef.current.getBoundingClientRect() const selectedRect = selectedElement.getBoundingClientRect() if (selectedRect.bottom > menuRect.bottom) { menuRef.current.scrollTop += selectedRect.bottom - menuRect.bottom } else if (selectedRect.top < menuRect.top) { menuRef.current.scrollTop -= menuRect.top - selectedRect.top } } } }, [selectedIndex]) // Filter commands based on query const filteredCommands = getMatchingSlashCommands(query, localWorkflowToggles, globalWorkflowToggles) const defaultCommands = filteredCommands.filter((cmd) => cmd.section === "default" || !cmd.section) const workflowCommands = filteredCommands.filter((cmd) => cmd.section === "custom") // Create a reusable function for rendering a command section const renderCommandSection = (commands: SlashCommand[], title: string, indexOffset: number, showDescriptions: boolean) => { if (commands.length === 0) { return null } return ( <> <div className="text-xs text-[var(--vscode-descriptionForeground)] px-3 py-1 font-bold border-b border-[var(--vscode-editorGroup-border)]"> {title} </div> {commands.map((command, index) => { const itemIndex = index + indexOffset return ( <div className={`slash-command-menu-item py-2 px-3 cursor-pointer flex flex-col border-b border-[var(--vscode-editorGroup-border)] ${ itemIndex === selectedIndex ? "bg-[var(--vscode-quickInputList-focusBackground)] text-[var(--vscode-quickInputList-focusForeground)]" : "" } hover:bg-[var(--vscode-list-hoverBackground)]`} id={`slash-command-menu-item-${itemIndex}`} key={command.name} onClick={() => handleClick(command)} onMouseEnter={() => setSelectedIndex(itemIndex)}> <div className="font-bold whitespace-nowrap overflow-hidden text-ellipsis"> <span className="ph-no-capture">/{command.name}</span> </div> {showDescriptions && command.description && ( <div className="text-[0.85em] text-[var(--vscode-descriptionForeground)] whitespace-normal overflow-hidden text-ellipsis"> <span className="ph-no-capture">{command.description}</span> </div> )} </div> ) })} </> ) } return ( <div className="absolute bottom-[calc(100%-10px)] left-[15px] right-[15px] overflow-x-hidden z-[1000]" onMouseDown={onMouseDown}> <div className="bg-[var(--vscode-dropdown-background)] border border-[var(--vscode-editorGroup-border)] rounded-[3px] shadow-[0_4px_10px_rgba(0,0,0,0.25)] flex flex-col overflow-y-auto" ref={menuRef} style={{ maxHeight: "min(200px, calc(50vh))", overscrollBehavior: "contain" }}> {filteredCommands.length > 0 ? ( <> {renderCommandSection(defaultCommands, "Default Commands", 0, true)} {renderCommandSection(workflowCommands, "Workflow Commands", defaultCommands.length, false)} </> ) : ( <div className="py-2 px-3 cursor-default flex flex-col"> <div className="text-[0.85em] text-[var(--vscode-descriptionForeground)]">No matching commands found</div> </div> )} </div> </div> ) } export default SlashCommandMenu |