diff --git a/frontend/app/components/CommandPalette.tsx b/frontend/app/components/CommandPalette.tsx index 5ca28d3..ffa253f 100644 --- a/frontend/app/components/CommandPalette.tsx +++ b/frontend/app/components/CommandPalette.tsx @@ -11,20 +11,34 @@ const commands = [ { id: 5, label: "Insights Feed", href: "/insights/feed" }, ]; +function buildSearchableCommands( + commandList: typeof commands +) { + return commandList.map((command) => ({ + ...command, + searchLabel: + command.label.toLowerCase(), + })); +} + function filterCommands( - commandList: typeof commands, + commandList: ReturnType< + typeof buildSearchableCommands + >, query: string ) { - const normalizedQuery = query.trim().toLowerCase(); + const normalizedQuery = + query.trim().toLowerCase(); if (!normalizedQuery) { return commandList; } - return commandList.filter((command) => - command.label - .toLowerCase() - .includes(normalizedQuery) + return commandList.filter( + (command) => + command.searchLabel.includes( + normalizedQuery + ) ); } @@ -46,21 +60,39 @@ export default function CommandPalette() { const [open, setOpen] = useState(false); const [query, setQuery] = useState(""); const [selected, setSelected] = useState(0); - const [recentCommands, setRecentCommands] = useState([]); - - + const [recentCommands, setRecentCommands] = + useState([]); + + const searchableCommands = + useMemo( + () => + buildSearchableCommands( + commands + ), + [] + ); + useEffect(() => { - const stored = localStorage.getItem("flowforge-recent-commands"); + const stored = localStorage.getItem( + "flowforge-recent-commands" + ); if (stored) { - setRecentCommands(JSON.parse(stored)); + setRecentCommands( + JSON.parse(stored) + ); } }, []); + useEffect(() => { - function handleKeyDown(event: KeyboardEvent) { + function handleKeyDown( + event: KeyboardEvent + ) { const isShortcut = - (event.ctrlKey || event.metaKey) && - event.key.toLowerCase() === "k"; + (event.ctrlKey || + event.metaKey) && + event.key.toLowerCase() === + "k"; if (isShortcut) { event.preventDefault(); @@ -73,31 +105,47 @@ export default function CommandPalette() { } } - window.addEventListener("keydown", handleKeyDown); + window.addEventListener( + "keydown", + handleKeyDown + ); return () => { - window.removeEventListener("keydown", handleKeyDown); + window.removeEventListener( + "keydown", + handleKeyDown + ); }; }, []); - const filteredCommands = useMemo( - () => filterCommands(commands, query), - [query] - ); - + const filteredCommands = + useMemo( + () => + filterCommands( + searchableCommands, + query + ), + [searchableCommands, query] + ); + useEffect(() => { setSelected(0); }, [query]); useEffect(() => { - function handleNavigation(event: KeyboardEvent) { + function handleNavigation( + event: KeyboardEvent + ) { if (!open) return; if (event.key === "ArrowDown") { event.preventDefault(); setSelected((prev) => - prev === filteredCommands.length - 1 ? 0 : prev + 1 + prev === + filteredCommands.length - 1 + ? 0 + : prev + 1 ); } @@ -105,24 +153,32 @@ export default function CommandPalette() { event.preventDefault(); setSelected((prev) => - prev === 0 ? filteredCommands.length - 1 : prev - 1 + prev === 0 + ? filteredCommands.length - 1 + : prev - 1 ); } if (event.key === "Enter") { - const command = filteredCommands[selected]; + const command = + filteredCommands[selected]; if (command) { - const updatedRecent = buildRecentCommands( - command, - recentCommands + const updatedRecent = + buildRecentCommands( + command, + recentCommands + ); + + setRecentCommands( + updatedRecent ); - setRecentCommands(updatedRecent); - localStorage.setItem( "flowforge-recent-commands", - JSON.stringify(updatedRecent) + JSON.stringify( + updatedRecent + ) ); router.push(command.href); @@ -133,19 +189,30 @@ export default function CommandPalette() { } } - window.addEventListener("keydown", handleNavigation); + window.addEventListener( + "keydown", + handleNavigation + ); return () => { - window.removeEventListener("keydown", handleNavigation); + window.removeEventListener( + "keydown", + handleNavigation + ); }; - }, [open, filteredCommands, selected, router]); + }, [ + open, + filteredCommands, + selected, + router, + recentCommands, + ]); if (!open) return null; return (
-
search @@ -154,7 +221,11 @@ export default function CommandPalette() { setQuery(event.target.value)} + onChange={(event) => + setQuery( + event.target.value + ) + } placeholder="Search pages and actions..." className="flex-1 bg-transparent outline-none text-on-surface placeholder:text-outline" /> @@ -164,46 +235,50 @@ export default function CommandPalette() {
- {!query && recentCommands.length > 0 && ( - <> -
- Recent -
- - {recentCommands.map((command) => ( - - ))} - -
- -)} + {!query && + recentCommands.length > 0 && ( + <> +
+ Recent +
+ + {recentCommands.map( + (command) => ( + + ) + )} + +
+ + )}
-
Navigation
- {filteredCommands.length === 0 ? ( + {filteredCommands.length === + 0 ? (
- search_off @@ -213,47 +288,56 @@ export default function CommandPalette() {

- Try searching for pages or actions. + Try searching for pages + or actions.

) : ( - filteredCommands.map((command, index) => ( - - )) + filteredCommands.map( + (command, index) => ( + + ) + ) )}