"use client"; import { useEffect, useMemo, useRef, useState } from "react"; import { useRouter } from "next/navigation"; import { useSearch, type SearchResult } from "@lynkd/messaging-inbox-sdk"; import { useUi } from "@/lib/ui-state"; import { Search, Hash, User, MessageSquare, FileText, LifeBuoy, CornerDownLeft, Inbox } from "lucide-react"; import clsx from "clsx"; const icons: Record = { channel: Hash, person: User, message: MessageSquare, file: FileText, ticket: LifeBuoy, }; type Item = { key: string; icon: typeof Hash; title: string; subtitle?: string; run: () => void }; export function CommandPalette() { const { paletteOpen, setPaletteOpen, openProfile, openThread } = useUi(); const { query, setQuery, results, loading } = useSearch(); const router = useRouter(); const [active, setActive] = useState(0); const listRef = useRef(null); const go = (r: SearchResult) => { setPaletteOpen(false); if (r.type === "person") openProfile(r.id); else if (r.type === "ticket") router.push(`/support/${r.id}`); else if (r.type === "message" && r.channelId) { // a reply lives inside a thread (not the main list) → open the thread; // otherwise deep-link so the channel view scrolls to + flashes it if (r.parentId) { router.push(`/c/${r.channelId}`); openThread(r.channelId, r.parentId); } else router.push(`/c/${r.channelId}?msg=${r.id}`); } else if (r.channelId) router.push(`/c/${r.channelId}`); }; const items: Item[] = useMemo(() => { if (!query) { return [ { key: "q1", icon: Hash, title: "Jump to #general", run: () => { setPaletteOpen(false); router.push("/c/c_general"); } }, { key: "q2", icon: Inbox, title: "Open Inbox", run: () => { setPaletteOpen(false); router.push("/inbox"); } }, { key: "q3", icon: LifeBuoy, title: "Support Center", run: () => { setPaletteOpen(false); router.push("/support"); } }, ]; } if (loading) return []; return results.map((r) => ({ key: `${r.type}-${r.id}`, icon: icons[r.type], title: r.title, subtitle: r.subtitle, run: () => go(r) })); // eslint-disable-next-line react-hooks/exhaustive-deps }, [query, loading, results]); useEffect(() => { setActive(0); }, [query, results]); if (!paletteOpen) return null; const onKey = (e: React.KeyboardEvent) => { if (e.key === "ArrowDown") { e.preventDefault(); setActive((a) => Math.min(a + 1, items.length - 1)); } else if (e.key === "ArrowUp") { e.preventDefault(); setActive((a) => Math.max(a - 1, 0)); } else if (e.key === "Enter") { e.preventDefault(); items[active]?.run(); } }; return (
setPaletteOpen(false)}>
e.stopPropagation()}>
setQuery(e.target.value)} onKeyDown={onKey} placeholder="Search messages, channels, people, tickets…" className="w-full bg-transparent py-4 text-[15px] outline-none placeholder:text-dim" /> esc
{!query &&
Quick actions
} {query && loading &&
Searching…
} {query && !loading && items.length === 0 &&
No results for “{query}”
} {items.map((it, i) => ( ))}
); }