/* Command Palette (⌘K) ============================================== */ function CommandPalette({ open, onClose, accountId, platformId, onAction }){ const [q, setQ] = useState(''); const [idx, setIdx] = useState(0); const inputRef = useRef(null); const listRef = useRef(null); /* Build searchable index */ const items = useMemo(() => { const all = []; /* Pages */ all.push({ kind:'page', icon:'columns', label:'Go to Platform Overview', sub:'Page', action:'go-platform', payload:'overview' }); PLATFORMS.filter(p=>p.id!=='overview').forEach(p => { all.push({ kind:'page', icon:'columns', label:`Go to ${p.label}`, sub:'Platform page', action:'go-platform', payload:p.id }); }); /* Quick actions */ all.push({ kind:'action', icon:'sun', label:'Toggle dark mode', sub:'Theme', action:'toggle-dark' }); all.push({ kind:'action', icon:'sparkles', label:'Open budget reallocator', sub:'What-if', action:'open-reallocator' }); all.push({ kind:'action', icon:'zap', label:'Open Automations', sub:'Rules', action:'open-automations' }); all.push({ kind:'action', icon:'columns', label:'New custom column', sub:'Formula builder',action:'open-columns' }); all.push({ kind:'action', icon:'bell', label:'Mark all notifications read', sub:'Notifications', action:'mark-read' }); /* Campaigns + ad sets across all platforms for this account */ const campaigns = getCampaigns(accountId, 'overview'); campaigns.forEach(c => { all.push({ kind:'campaign', icon:'play', label:c.name, sub:`Campaign · ${(PLATFORMS.find(p=>p.id===c.platform)||{}).label} · ${curPrefix()}${c.spend.toLocaleString()}`, platform:c.platform, action:'view-campaign', payload:c }); c.adsets.forEach(a => { all.push({ kind:'adset', icon:'sliders', label:a.name, sub:`Ad Set in ${c.name}`, platform:c.platform, action:'view-adset', payload:{campaign:c, adset:a} }); }); }); return all; }, [accountId]); const filtered = useMemo(() => { if (!q.trim()) return items.slice(0, 14); const ql = q.toLowerCase(); return items .map(it => { const ll = it.label.toLowerCase(); const sl = it.sub.toLowerCase(); let score = 0; if (ll.startsWith(ql)) score += 100; if (ll.includes(ql)) score += 40; if (sl.includes(ql)) score += 12; return { it, score }; }) .filter(x => x.score > 0) .sort((a,b) => b.score - a.score) .slice(0, 40) .map(x => x.it); }, [q, items]); useEffect(() => { setIdx(0); }, [q]); useEffect(() => { if (open) setTimeout(()=>inputRef.current?.focus(), 50); }, [open]); useEffect(()=>{ if (!open) return; const onKey = (e) => { if (e.key === 'ArrowDown') { e.preventDefault(); setIdx(i => Math.min(filtered.length-1, i+1)); } else if (e.key === 'ArrowUp') { e.preventDefault(); setIdx(i => Math.max(0, i-1)); } else if (e.key === 'Home') { e.preventDefault(); setIdx(0); } else if (e.key === 'End') { e.preventDefault(); setIdx(filtered.length-1); } else if (e.key === 'Enter') { e.preventDefault(); const it = filtered[idx]; if (it){ onAction(it); onClose(); } } else if (e.key === 'Escape') { onClose(); } }; window.addEventListener('keydown', onKey); return () => window.removeEventListener('keydown', onKey); }, [open, filtered, idx, onAction, onClose]); /* Scroll selected into view */ useEffect(()=>{ if (!open || !listRef.current) return; const el = listRef.current.querySelector(`[data-i="${idx}"]`); if (el) el.scrollIntoView({ block:'nearest' }); }, [idx, open]); if (!open) return null; /* Group items for visual sectioning while preserving global idx */ const groups = {}; filtered.forEach((it,i)=>{ const k = it.kind; if (!groups[k]) groups[k] = []; groups[k].push({ ...it, _i: i }); }); const groupLabels = { page:'Pages', action:'Quick actions', campaign:'Campaigns', adset:'Ad sets' }; const groupOrder = ['action','page','campaign','adset']; return (
{/* Search bar */}
setQ(e.target.value)} placeholder="Search campaigns, ad sets, or actions…" className="flex-1 bg-transparent text-[14px] focus:outline-none placeholder:text-ink-400"/> ESC
{/* Results */}
{filtered.length === 0 && (
No results for "{q}"
)} {groupOrder.map(k => { const list = groups[k]; if (!list || !list.length) return null; return (
{groupLabels[k]}
{list.map(it => { const isActive = it._i === idx; return ( ); })}
); })}
{/* Footer */}
navigate · select
Search powered by MCP
); } Object.assign(window, { CommandPalette });