// CommandPalette — ⌘K overlay. Glass surface, blue focus ring.
const CommandPalette = ({ open, onClose, onRun }) => {
const [q, setQ] = React.useState('');
const [sel, setSel] = React.useState(0);
const inputRef = React.useRef(null);
const items = React.useMemo(() => [
{ id: 'build', label: 'Build project', hint: '⌘B', kind: 'Action' },
{ id: 'deploy', label: 'Deploy to prod', hint: '⌘D', kind: 'Action' },
{ id: 'theme', label: 'Theme: Australis Dark', hint: '⌘T', kind: 'Setting' },
{ id: 'tokens', label: 'Open tokens.css', hint: 'go', kind: 'File' },
{ id: 'aurora', label: 'Open aurora.ts', hint: 'go', kind: 'File' },
{ id: 'sign', label: 'Sign out', hint: '', kind: 'Action', danger: true },
], []);
const filtered = items.filter(i => i.label.toLowerCase().includes(q.toLowerCase()));
React.useEffect(() => {
if (open && inputRef.current) inputRef.current.focus();
setQ(''); setSel(0);
}, [open]);
React.useEffect(() => {
if (!open) return;
const onKey = (e) => {
if (e.key === 'Escape') onClose();
if (e.key === 'ArrowDown') { e.preventDefault(); setSel(s => Math.min(s + 1, filtered.length - 1)); }
if (e.key === 'ArrowUp') { e.preventDefault(); setSel(s => Math.max(s - 1, 0)); }
if (e.key === 'Enter' && filtered[sel]) { onRun(filtered[sel]); onClose(); }
};
window.addEventListener('keydown', onKey);
return () => window.removeEventListener('keydown', onKey);
}, [open, sel, filtered, onClose, onRun]);
if (!open) return null;
return (
e.stopPropagation()}>
{ setQ(e.target.value); setSel(0); }}
placeholder="Type a command…"
style={cpStyles.input}
/>
esc
{filtered.length === 0 && (
No matches.
)}
{filtered.map((it, i) => (
setSel(i)}
onClick={() => { onRun(it); onClose(); }}
style={{
...cpStyles.item,
background: i === sel ? 'rgba(99,136,216,0.18)' : 'transparent',
color: i === sel ? '#a4c4ff' : (it.danger ? '#ff854f' : 'var(--fg-1)'),
}}>
{it.label}
{it.kind}
{it.hint && {it.hint}}
))}
);
};
const cpStyles = {
scrim: {
position: 'absolute', inset: 0,
background: 'rgba(34,37,49,0.72)',
backdropFilter: 'blur(4px)',
display: 'flex', alignItems: 'flex-start', justifyContent: 'center',
paddingTop: '15vh',
zIndex: 100,
},
panel: {
width: 540,
background: 'rgba(55,59,70,0.85)',
backdropFilter: 'blur(16px)',
border: '1px solid #565f69',
borderRadius: 10,
boxShadow: '0 24px 64px rgba(10,12,18,0.6), 0 8px 16px rgba(10,12,18,0.45)',
overflow: 'hidden',
},
head: {
display: 'flex', alignItems: 'center', gap: 10,
padding: '12px 14px',
borderBottom: '1px solid #414751',
},
input: {
flex: 1, background: 'transparent', border: 0, outline: 'none',
color: 'var(--fg-0)',
fontFamily: 'var(--font-sans)', fontSize: 14,
},
esc: {
fontFamily: 'var(--font-mono)', fontSize: 10,
color: 'var(--fg-3)', background: '#1b1e28',
padding: '2px 6px', borderRadius: 4, border: '1px solid #414751',
},
list: { padding: 6, maxHeight: 360, overflowY: 'auto' },
item: {
display: 'flex', alignItems: 'center', gap: 10,
padding: '8px 10px',
borderRadius: 5,
fontSize: 13,
cursor: 'pointer',
},
kind: {
fontFamily: 'var(--font-mono)', fontSize: 10,
color: 'var(--fg-3)',
textTransform: 'uppercase', letterSpacing: '0.06em',
},
hint: {
fontFamily: 'var(--font-mono)', fontSize: 10,
color: 'var(--fg-3)', background: '#1b1e28',
padding: '2px 6px', borderRadius: 4, border: '1px solid #414751',
},
empty: { padding: 16, fontSize: 13, color: 'var(--fg-3)' },
};
window.CommandPalette = CommandPalette;