Implémentation d'une palette de commandes avec Claude Code
Découvrez l'implémentation d'une palette de commandes avec Claude Code. Conseils pratiques et exemples de code inclus.
Améliorer radicalement l’expérience utilisateur avec une palette de commandes
La palette de commandes, que l’on retrouve dans VSCode ou Slack, est un élément d’interface destiné aux utilisateurs avancés permettant des opérations rapides au clavier. Avec Claude Code, vous pouvez implémenter efficacement une palette de commandes dotée de recherche, filtrage et navigation clavier.
Composant de palette de commandes de base
> Implémente une palette de commandes qui s'ouvre avec Cmd+K (Mac) / Ctrl+K (Windows).
> Avec recherche, navigation clavier et affichage par catégorie.
// 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())
);
// Raccourci 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);
}, []);
// Gestion du focus
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="Rechercher une commande..."
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">
Aucune commande correspondante
</li>
)}
</ul>
</div>
</div>
);
}
Définition et enregistrement des commandes
// hooks/useCommands.ts
import { useRouter } from 'next/navigation';
export function useCommands(): Command[] {
const router = useRouter();
return [
{
id: 'home', label: 'Aller à l\'accueil', category: 'Navigation',
icon: '🏠', shortcut: 'G H',
action: () => router.push('/'),
},
{
id: 'blog', label: 'Liste des articles', category: 'Navigation',
icon: '📝', shortcut: 'G B',
action: () => router.push('/blog'),
},
{
id: 'theme', label: 'Changer de thème', category: 'Paramètres',
icon: '🌙', shortcut: 'T T',
action: () => document.documentElement.classList.toggle('dark'),
},
{
id: 'search', label: 'Rechercher un article', category: 'Recherche',
icon: '🔍', shortcut: '/',
action: () => router.push('/search'),
},
];
}
Support de l’accessibilité
// Application WAI-ARIA
<div
role="dialog"
aria-modal="true"
aria-label="Palette de commandes"
>
<input
role="combobox"
aria-expanded="true"
aria-controls="command-list"
aria-activedescendant={`command-${filtered[selectedIndex]?.id}`}
/>
<ul id="command-list" role="listbox">
{/* ... */}
</ul>
</div>
Résumé
La palette de commandes, combinée aux raccourcis clavier, améliore considérablement la productivité des utilisateurs avancés. Avec Claude Code, vous pouvez implémenter en peu de temps une palette de commandes complète avec recherche, filtrage et navigation clavier. Pensez aussi à l’accessibilité pour viser une interface utilisable par tous. Comme référence d’implémentation, envisagez aussi la bibliothèque cmdk.
Related Posts
Comment booster vos projets personnels avec Claude Code [Avec exemples]
Apprenez à accélérer considérablement vos projets de développement personnels avec Claude Code. Inclut des exemples concrets et un workflow pratique de l'idée au déploiement.
Comment automatiser le refactoring avec Claude Code
Apprenez à automatiser efficacement le refactoring de code avec Claude Code. Inclut des prompts pratiques et des patterns de refactoring concrets pour des projets réels.
Guide complet de configuration CORS avec Claude Code
Découvrez le guide complet de configuration CORS avec Claude Code. Conseils pratiques et exemples de code inclus.