// cmdk.jsx — Cmd+K command palette const { useState: useStateK, useMemo: useMemoK, useEffect: useEffectK, useRef: useRefK } = React; function CommandPalette({ open, onClose, onNavigate }) { const [q, setQ] = useStateK(""); const [cursor, setCursor] = useStateK(0); const inputRef = useRefK(null); useEffectK(() => { if (open) { setQ(""); setCursor(0); setTimeout(() => inputRef.current && inputRef.current.focus(), 10); } }, [open]); // Build items list: pages + accounts + chats + checks shortcuts const items = useMemoK(() => { const list = []; NAV.forEach((g) => g.items.forEach((item) => list.push({ kind: "page", group: g.group, id: item.id, icon: item.icon, title: item.label, subtitle: g.group, hotkey: item.hotkey, run: () => onNavigate(item.id), }) ) ); ACCOUNTS.slice(0, 14).forEach((a) => list.push({ kind: "account", group: "Аккаунты", id: "acc-" + a.id, icon: "users", title: a.name, subtitle: a.username, color: a.color, run: () => onNavigate("accounts"), }) ); CHATS.slice(0, 8).forEach((c) => list.push({ kind: "chat", group: "Переписки", id: "chat-" + c.id, icon: "chat", title: c.name, subtitle: "через " + c.account.name, color: c.account.color, run: () => onNavigate("chats"), }) ); // Actions [ { id: "act-add-account", title: "Добавить аккаунт", icon: "plus", subtitle: "Аккаунты", run: () => onNavigate("accounts") }, { id: "act-add-req", title: "Добавить реквизит", icon: "card", subtitle: "Реквизиты", run: () => onNavigate("requisites") }, { id: "act-upload", title: "Загрузить контент", icon: "upload", subtitle: "Контент", run: () => onNavigate("content") }, { id: "act-pending", title: "Открыть чеки на проверке", icon: "receipt", subtitle: "Чеки", run: () => onNavigate("checks") }, { id: "act-stop", title: "Остановить бота", icon: "square", subtitle: "Бот · сейчас запущен", run: () => onNavigate("settings") }, { id: "act-restart", title: "Перезапустить бота", icon: "refresh", subtitle: "Бот", run: () => onNavigate("settings") }, ].forEach((a) => list.push({ kind: "action", group: "Действия", ...a })); return list; }, []); const filtered = useMemoK(() => { if (!q.trim()) return items; const lo = q.toLowerCase(); return items.filter((i) => i.title.toLowerCase().includes(lo) || (i.subtitle || "").toLowerCase().includes(lo) ); }, [q, items]); // Reset cursor when filter changes useEffectK(() => { setCursor(0); }, [q]); // Group filtered items by group, preserving order const grouped = useMemoK(() => { const map = new Map(); filtered.forEach((it) => { if (!map.has(it.group)) map.set(it.group, []); map.get(it.group).push(it); }); return Array.from(map.entries()); }, [filtered]); // Keyboard useEffectK(() => { if (!open) return; const onKey = (e) => { if (e.key === "Escape") { onClose(); } else if (e.key === "ArrowDown") { e.preventDefault(); setCursor((c) => Math.min(c + 1, filtered.length - 1)); } else if (e.key === "ArrowUp") { e.preventDefault(); setCursor((c) => Math.max(c - 1, 0)); } else if (e.key === "Enter") { e.preventDefault(); const it = filtered[cursor]; if (it) { it.run(); onClose(); } } }; window.addEventListener("keydown", onKey); return () => window.removeEventListener("keydown", onKey); }, [open, filtered, cursor, onClose]); if (!open) return null; let globalIdx = -1; return (