// Command palette overlay
const Palette = ({ onClose }) => {
const { go } = useRouter();
const [q, setQ] = React.useState('');
const [sel, setSel] = React.useState(0);
const inputRef = React.useRef(null);
React.useEffect(() => { inputRef.current && inputRef.current.focus(); }, []);
React.useEffect(() => { setSel(0); }, [q]);
const jumps = [
{ kind: 'jump', label: 'Recent', hint: 'g h', go: { name: 'home' } },
{ kind: 'jump', label: 'Projects', hint: 'g p', go: { name: 'projects' } },
{ kind: 'jump', label: 'Stats', hint: 'g s', go: { name: 'stats' } },
{ kind: 'jump', label: 'Health', hint: 'g i', go: { name: 'health' } },
{ kind: 'jump', label: 'Settings', hint: '', go: { name: 'settings' } },
];
const sessionItems = SESSIONS.map(s => ({
kind: 'session', label: s.q, hint: `${s.tool} · ${s.cwd}`,
go: { name: 'session', id: s.id }
}));
const projectItems = PROJECTS.map(p => ({
kind: 'project', label: p.cwd, hint: `${p.sessions} sessions`,
go: { name: 'project', cwd: p.cwd }
}));
const all = [...jumps, ...projectItems, ...sessionItems];
const filtered = q.trim() === '' ? all : all.filter(x =>
(x.label + ' ' + x.hint).toLowerCase().includes(q.toLowerCase())
);
const showSearch = q.trim() !== '';
const total = filtered.length + (showSearch ? 1 : 0);
const fire = (idx) => {
if (showSearch && idx === 0) { go({ name: 'search', q }); onClose(); return; }
const item = filtered[showSearch ? idx - 1 : idx];
if (item) { go(item.go); onClose(); }
};
const onKey = (e) => {
if (e.key === 'Escape') { e.preventDefault(); onClose(); }
else if (e.key === 'ArrowDown') { e.preventDefault(); setSel(s => Math.min(total - 1, s + 1)); }
else if (e.key === 'ArrowUp') { e.preventDefault(); setSel(s => Math.max(0, s - 1)); }
else if (e.key === 'Enter') { e.preventDefault(); fire(sel); }
};
return (
<div className="scrim" onClick={onClose}>
<div className="palette" onClick={e => e.stopPropagation()}>
<div className="pin">
<span className="mono muted" style={{ fontSize: 11 }}>⌘K</span>
<input ref={inputRef} value={q} onChange={e => setQ(e.target.value)}
onKeyDown={onKey}
placeholder="search turns, jump to a page, pick a session…" />
</div>
<div className="plist">
{showSearch && (
<div className={'pitem' + (sel === 0 ? ' active' : '')}
onClick={() => fire(0)}>
<span className="kind accent-c">search</span>
<span style={{ flex: 1 }}>"{q}"</span>
<span className="mono muted" style={{ fontSize: 10 }}>↵</span>
</div>
)}
{filtered.map((item, i) => {
const idx = showSearch ? i + 1 : i;
const active = idx === sel;
return (
<div key={i} className={'pitem' + (active ? ' active' : '')}
onClick={() => fire(idx)}>
<span className="kind">{item.kind}</span>
<span className="truncate" style={{ flex: 1 }}>{item.label}</span>
<span className="mono muted" style={{ fontSize: 10.5 }}>{item.hint}</span>
</div>
);
})}
{filtered.length === 0 && !showSearch && (
<div className="muted" style={{ padding: 14, fontSize: 11.5 }}>no matches</div>
)}
</div>
<div className="pfoot">
<span>↑↓ move</span><span>↵ open</span><span>esc close</span>
<span style={{ flex: 1 }} />
<span>g h · g p · g s · g i</span>
</div>
</div>
</div>
);
};
Object.assign(window, { Palette });