Implementacion de paleta de comandos con Claude Code
Aprende sobre la implementacion de una paleta de comandos usando Claude Code. Incluye consejos practicos y ejemplos de codigo.
Mejorando drasticamente la experiencia de usuario con una paleta de comandos
La paleta de comandos, como las que se ven en VSCode y Slack, es un elemento de UI para usuarios avanzados que permite operar rapidamente solo con el teclado. Con Claude Code, puedes implementar eficientemente una paleta de comandos con busqueda, filtrado y navegacion por teclado.
Componente basico de paleta de comandos
> Implementa una paleta de comandos que se abra con Cmd+K (Mac) / Ctrl+K (Windows).
> Con soporte para busqueda, operacion por teclado y visualizacion por categorias.
// components/CommandPalette.tsx
import { useState, useEffect, useRef, useCallback } from 'react';
interface Command {
id: string;
label: string;
category: string;
icon?: string;
shortcut?: string;
action: () => void;
}
interface CommandPaletteProps {
commands: Command[];
}
export function CommandPalette({ commands }: CommandPaletteProps) {
const [isOpen, setIsOpen] = useState(false);
const [query, setQuery] = useState('');
const [selectedIndex, setSelectedIndex] = useState(0);
const inputRef = useRef<HTMLInputElement>(null);
const filtered = commands.filter(cmd =>
cmd.label.toLowerCase().includes(query.toLowerCase()) ||
cmd.category.toLowerCase().includes(query.toLowerCase())
);
// Atajo global
useEffect(() => {
const handleKeyDown = (e: KeyboardEvent) => {
if ((e.metaKey || e.ctrlKey) && e.key === 'k') {
e.preventDefault();
setIsOpen(prev => !prev);
}
if (e.key === 'Escape') setIsOpen(false);
};
window.addEventListener('keydown', handleKeyDown);
return () => window.removeEventListener('keydown', handleKeyDown);
}, []);
// Control de foco
useEffect(() => {
if (isOpen) {
inputRef.current?.focus();
setQuery('');
setSelectedIndex(0);
}
}, [isOpen]);
const handleKeyNav = useCallback((e: React.KeyboardEvent) => {
switch (e.key) {
case 'ArrowDown':
e.preventDefault();
setSelectedIndex(i => Math.min(i + 1, filtered.length - 1));
break;
case 'ArrowUp':
e.preventDefault();
setSelectedIndex(i => Math.max(i - 1, 0));
break;
case 'Enter':
e.preventDefault();
if (filtered[selectedIndex]) {
filtered[selectedIndex].action();
setIsOpen(false);
}
break;
}
}, [filtered, selectedIndex]);
if (!isOpen) return null;
return (
<div className="fixed inset-0 z-50 flex items-start justify-center pt-[20vh]">
<div className="fixed inset-0 bg-black/50" onClick={() => setIsOpen(false)} />
<div className="relative w-full max-w-lg rounded-xl bg-white shadow-2xl dark:bg-gray-800">
<input
ref={inputRef}
type="text"
value={query}
onChange={e => { setQuery(e.target.value); setSelectedIndex(0); }}
onKeyDown={handleKeyNav}
placeholder="Buscar comandos..."
className="w-full border-b px-4 py-3 text-lg outline-none dark:bg-gray-800"
/>
<ul className="max-h-80 overflow-y-auto p-2" role="listbox">
{filtered.map((cmd, index) => (
<li
key={cmd.id}
role="option"
aria-selected={index === selectedIndex}
onClick={() => { cmd.action(); setIsOpen(false); }}
className={`flex cursor-pointer items-center justify-between rounded-lg px-3 py-2
${index === selectedIndex ? 'bg-blue-50 dark:bg-blue-900/30' : 'hover:bg-gray-50'}`}
>
<div className="flex items-center gap-3">
{cmd.icon && <span className="text-lg">{cmd.icon}</span>}
<div>
<div className="font-medium">{cmd.label}</div>
<div className="text-xs text-gray-500">{cmd.category}</div>
</div>
</div>
{cmd.shortcut && (
<kbd className="rounded bg-gray-100 px-2 py-1 text-xs dark:bg-gray-700">
{cmd.shortcut}
</kbd>
)}
</li>
))}
{filtered.length === 0 && (
<li className="px-3 py-8 text-center text-gray-500">
No se encontraron comandos coincidentes
</li>
)}
</ul>
</div>
</div>
);
}
Definicion y registro de comandos
// hooks/useCommands.ts
import { useRouter } from 'next/navigation';
export function useCommands(): Command[] {
const router = useRouter();
return [
{
id: 'home', label: 'Ir al inicio', category: 'Navegacion',
icon: '🏠', shortcut: 'G H',
action: () => router.push('/'),
},
{
id: 'blog', label: 'Lista del blog', category: 'Navegacion',
icon: '📝', shortcut: 'G B',
action: () => router.push('/blog'),
},
{
id: 'theme', label: 'Cambiar tema', category: 'Configuracion',
icon: '🌙', shortcut: 'T T',
action: () => document.documentElement.classList.toggle('dark'),
},
{
id: 'search', label: 'Buscar articulos', category: 'Busqueda',
icon: '🔍', shortcut: '/',
action: () => router.push('/search'),
},
];
}
Soporte de accesibilidad
// Aplicacion de WAI-ARIA
<div
role="dialog"
aria-modal="true"
aria-label="Paleta de comandos"
>
<input
role="combobox"
aria-expanded="true"
aria-controls="command-list"
aria-activedescendant={`command-${filtered[selectedIndex]?.id}`}
/>
<ul id="command-list" role="listbox">
{/* ... */}
</ul>
</div>
Resumen
La paleta de comandos, combinada con los atajos de teclado, mejora significativamente la productividad de los usuarios avanzados. Con Claude Code, puedes implementar en poco tiempo una paleta de comandos completa con busqueda, filtrado y navegacion por teclado. No olvides considerar tambien la accesibilidad para crear una UI accesible para todos los usuarios. Como referencia de implementacion, considera tambien la biblioteca cmdk.
Related Posts
Cómo potenciar tus proyectos personales con Claude Code [Con ejemplos]
Aprende a acelerar drásticamente tus proyectos personales de desarrollo usando Claude Code. Incluye ejemplos reales y un flujo de trabajo práctico desde la idea hasta el despliegue.
Cómo automatizar la refactorización con Claude Code
Aprende a automatizar eficientemente la refactorización de código usando Claude Code. Incluye prompts prácticos y patrones concretos de refactorización para proyectos reales.
Guia completa de configuracion CORS con Claude Code
Aprende sobre la configuracion completa de CORS usando Claude Code. Incluye consejos practicos y ejemplos de codigo.