/ app / src / components / CommandPalette.tsx
CommandPalette.tsx
  1  import React, { useState, useEffect, KeyboardEvent } from 'react';
  2  import { useApp } from '../contexts/AppContext';
  3  import { mockCommands } from '../data/mockData';
  4  import type { Command } from '../types';
  5  import './CommandPalette.css';
  6  
  7  export const CommandPalette: React.FC = () => {
  8    const { commandPaletteVisible, hideCommandPalette, executeCommand } = useApp();
  9    const [query, setQuery] = useState('');
 10    const [filteredCommands, setFilteredCommands] = useState<Command[]>(mockCommands);
 11    const [selectedIndex, setSelectedIndex] = useState(0);
 12  
 13    useEffect(() => {
 14      if (!commandPaletteVisible) {
 15        setQuery('');
 16        setSelectedIndex(0);
 17      }
 18    }, [commandPaletteVisible]);
 19  
 20    useEffect(() => {
 21      const filtered = mockCommands.filter(cmd =>
 22        query === '' ||
 23        cmd.name.toLowerCase().includes(query.toLowerCase()) ||
 24        cmd.description.toLowerCase().includes(query.toLowerCase()) ||
 25        cmd.id.toLowerCase().includes(query.toLowerCase())
 26      );
 27      setFilteredCommands(filtered);
 28      setSelectedIndex(0);
 29    }, [query]);
 30  
 31    const handleKeyDown = (e: KeyboardEvent) => {
 32      switch (e.key) {
 33        case 'Escape':
 34          hideCommandPalette();
 35          break;
 36        case 'ArrowUp':
 37          e.preventDefault();
 38          setSelectedIndex(prev => Math.max(0, prev - 1));
 39          break;
 40        case 'ArrowDown':
 41          e.preventDefault();
 42          setSelectedIndex(prev => Math.min(filteredCommands.length - 1, prev + 1));
 43          break;
 44        case 'Enter':
 45          e.preventDefault();
 46          if (filteredCommands[selectedIndex]) {
 47            executeCommand(filteredCommands[selectedIndex]);
 48            hideCommandPalette();
 49          }
 50          break;
 51      }
 52    };
 53  
 54    const handleCommandClick = (cmd: Command) => {
 55      executeCommand(cmd);
 56      hideCommandPalette();
 57    };
 58  
 59    if (!commandPaletteVisible) return null;
 60  
 61    const visibleCommands = filteredCommands.slice(0, 12);
 62  
 63    return (
 64      <div className="command-palette-overlay" onClick={hideCommandPalette}>
 65        <div className="command-palette" onClick={(e) => e.stopPropagation()}>
 66          <div className="palette-header">
 67            🔥 KAMAJI'S FURNACE COMMANDS 🔥
 68          </div>
 69  
 70          <div className="palette-search">
 71            <span className="search-icon">âš™</span>
 72            <input
 73              type="text"
 74              value={query}
 75              onChange={(e) => setQuery(e.target.value)}
 76              onKeyDown={handleKeyDown}
 77              placeholder="Type to search..."
 78              className="search-input"
 79              autoFocus
 80            />
 81          </div>
 82  
 83          <div className="palette-commands">
 84            {visibleCommands.map((cmd, idx) => (
 85              <div
 86                key={cmd.id}
 87                className={`command-item ${idx === selectedIndex ? 'selected' : ''}`}
 88                onClick={() => handleCommandClick(cmd)}
 89              >
 90                <span className="command-prefix">
 91                  {idx === selectedIndex ? 'â–¶' : ' '}
 92                </span>
 93                <span className="command-name">{cmd.name}</span>
 94                <span className="command-description">{cmd.description}</span>
 95              </div>
 96            ))}
 97  
 98            {filteredCommands.length === 0 && (
 99              <div className="no-results">No commands found</div>
100            )}
101          </div>
102  
103          <div className="palette-footer">
104            ↑↓ Navigate • ENTER Execute • ESC Exit
105          </div>
106        </div>
107      </div>
108    );
109  };