Galerie d'interface pour les mods
Découvrez les éléments d'interface qu'un mod Claude Code peut dessiner, comme du texte, des boutons, des champs, du Markdown, du code et des diffs, avec des exemples de code et des captures d'écran du terminal.
Un mod dessine son interface à partir d'éléments : du texte, des boîtes, des boutons, des champs, et quelques éléments qui mettent en forme le contenu pour vous. Les exemples présentés ici montrent le code qui dessine un élément, et la plupart sont accompagnés d'une capture d'écran du résultat dans un panneau du terminal, afin que vous puissiez choisir un élément selon son apparence.
Pour comprendre le fonctionnement du dessin, commencez par Dessiner dans l'interface. Pour les principales props et les applications qui dessinent chaque élément, consultez la référence des éléments. Les déclarations de types répertorient toutes les props.
Essayer un exemple
Les exemples de cette page sont des extraits, et non des mods complets. Chacun correspond au code d'un élément et de tout ce qui y est imbriqué.
Pour voir un exemple dans votre propre terminal, créez le petit mod décrit dans ces étapes et collez-y l'exemple. Le mod ajoute une commande /gallery qui ouvre un panneau et y dessine l'exemple. Un panneau est une barre latérale à côté de la transcription dans un terminal large en plein écran, ou sinon une zone encadrée au-dessus du prompt.
Créer le mod
Créez un répertoire nommé gallery contenant les répertoires .claude-plugin et hooks. Créer un mod explique les fichiers.
Enregistrez le manifeste sous gallery/.claude-plugin/plugin.json :
{
"name": "gallery",
"version": "0.1.0",
"description": "Opens a pane that draws one sample",
"author": { "name": "Your Name" }
}
Indiquez votre point d'entrée dans gallery/hooks/hooks.json :
{
"modules": ["./register.js"]
}
Enregistrez le code sous gallery/hooks/register.js. Il ajoute une commande /gallery qui ouvre un panneau, et dessine Plain text dans ce panneau :
// Stands in for your own callback in the samples that take one
const noop = () => {}
// The Select sample keeps its choice here
let picked = 'md'
// The Raster sample packs its cells with this function
const DEFAULT_COLOR = 0x01000000
function cellsOf(rows) {
const numbers = rows.flat().flatMap(([char, color]) => [char.codePointAt(0), color, DEFAULT_COLOR])
return new Uint8Array(Uint32Array.from(numbers).buffer).toBase64()
}
export function register(on) {
on('session.start', async ($, e, next) => {
await $.command.register({ name: 'gallery', description: 'Open the sample pane' })
return next(e)
})
on('command.run', { command: 'gallery' }, async ($) => {
await $.ui.open({ id: 'gallery', focus: true, closeOnEscape: true })
return {}
})
on('ui.render', { component: 'Pane' }, async ($, e, next) => {
if (e.requestId !== 'gallery') return next(e)
const { Box, Text, Button, Input, Select, Link, Markdown, Code, Raster, Svg } = $.ui.resolve(e)
// Replace the element after return with a sample
return Text({ children: ['Plain text'] })
})
}
Exécuter le mod
Dans votre shell, démarrez Claude Code depuis le répertoire qui contient gallery :
claude --plugin-dir ./gallery
Dans le prompt de Claude Code, exécutez /gallery. Un panneau s'ouvre et affiche Plain text.
Remplacer par un exemple
Copiez un exemple de cette page. Dans register.js, collez-le à la place de Text({ children: ['Plain text'] }), de sorte qu'il suive return, puis enregistrez le fichier. Claude Code recharge le module à chaque enregistrement : exécutez donc à nouveau /gallery pour voir le nouvel exemple.
Choisir un élément
Les exemples sont regroupés selon ce que vous souhaitez afficher à l'écran :
- Afficher du texte :
Text,MarkdownetLink - Afficher du code et des modifications :
Code - Disposer des éléments :
Box - Recueillir une saisie :
Button,InputetSelect - Dessiner des images :
Raster,Svg,ImageetClient
Afficher du texte
Trois éléments affichent des mots à l'écran : Text pour votre propre mise en forme, Markdown pour un contenu déjà formaté, et Link pour une URL.
`Text`
Text dessine une chaîne avec les styles que vous lui donnez. Cet exemple affiche une ligne pour chaque style :
Box({
flexDirection: 'column',
children: [
Text({ children: ['Plain text'] }),
Text({ bold: true, children: ['bold'] }),
Text({ italic: true, children: ['italic'] }),
Text({ underline: true, children: ['underline'] }),
Text({ strikethrough: true, children: ['strikethrough'] }),
Text({ dimColor: true, children: ['dimColor'] }),
Text({ inverse: true, children: ['inverse'] }),
Text({ color: 'red', children: ["color: 'red'"] }),
Text({ backgroundColor: 'blue', children: ["backgroundColor: 'blue'"] }),
],
})
dimColor dessine le texte en gris. backgroundColor ne remplit que la largeur du texte.
`Markdown`
Markdown met en forme le texte de la même manière que les réponses de Claude. Transmettez le contenu dans text, et non dans children :
Markdown({
text: '## Release notes\n\nThis build has **two** fixes and one `flag`:\n\n- Faster start\n- Fewer prompts\n\n> Quoted text',
})
Un titre s'affiche en gras sans ses marques #. Le code en ligne s'affiche en couleur sans ses backticks. Une citation s'affiche en italique avec une barre à sa gauche.
`Link`
Link dessine un libellé suivi de son URL :
Link({ href: 'https://code.claude.com/docs', label: 'Claude Code docs' })
Le terminal dessine l'URL sous forme de texte après le libellé. La possibilité de l'ouvrir d'un clic dépend du terminal de l'utilisateur.
Afficher du code et des modifications
Code dessine du texte source avec les couleurs syntaxiques propres à Claude Code, ou un diff.
`Code`
Indiquez le language, ou transmettez un path à partir duquel Claude Code le déduira. Avec startLine, les lignes sont numérotées à partir de ce nombre :
Code({
language: 'javascript',
startLine: 1,
source: "const name = 'mods'\nconsole.log('hello ' + name)",
})
Les couleurs proviennent du thème de l'utilisateur.
`Code` sous forme de diff
Avec format: 'diff', source contient un ou plusieurs blocs de diff unifié :
Code({
format: 'diff',
source: '@@ -1,3 +1,3 @@\n # Mods\n-A mod is a plugin.\n+A mod is a plugin that runs code.\n Read on.',
})
Claude Code dessine des numéros de ligne à la place de la ligne @@. Lorsqu'une ligne supprimée et une ligne ajoutée se ressemblent, les mots modifiés reçoivent une teinte plus prononcée.
Disposer des éléments
`Box`
Box dispose son contenu en ligne ou en colonne, et peut dessiner une bordure. Cet exemple place une ligne de mots au-dessus d'une boîte avec bordure :
Box({
flexDirection: 'column',
gap: 1,
children: [
Box({
flexDirection: 'row',
columnGap: 4,
children: [Text({ children: ['a row'] }), Text({ children: ['of three'] }), Text({ children: ['items'] })],
}),
Box({
borderStyle: 'round',
paddingX: 1,
children: [Text({ children: ["borderStyle: 'round'"] })],
}),
],
})
La bordure s'étend sur toute la largeur du panneau.
Recueillir une saisie
Button, Input et Select sont des contrôles : l'utilisateur passe de l'un à l'autre avec Tab et utilise celui qui a le focus. Focus clavier et raccourcis décrit les touches qui leur parviennent.
Ouvrir un panneau avec focus: true donne le focus clavier au panneau. Les lettres saisies parviennent à un Input une fois qu'il a le focus : ajoutez donc autoFocus: true à un champ qui doit recevoir la saisie dès l'ouverture du panneau.
`Button`
Un bouton exécute onPress. Cet exemple montre la forme par défaut, un bouton plain avec un raccourci, et un bouton atténué :
Box({
flexDirection: 'column',
children: [
Button({ key: 'save', label: 'Save', onPress: noop }),
Button({ key: 'next', label: 'Next', hotkey: 'n', plain: true, onPress: noop }),
Button({ key: 'skip', label: 'Skip', dimColor: true, onPress: noop }),
],
})
Un bouton qui a le focus s'affiche en vidéo inverse. Ici, l'utilisateur a appuyé deux fois sur Tab :
`Input`
Un Input est un champ de texte sur une ligne qui exécute onSubmit lorsque l'utilisateur appuie sur Entrée :
Input({
key: 'title',
label: 'Title',
placeholder: 'Type a title and press Enter',
value: '',
submitLabel: 'save',
onSubmit: noop,
})
Sans le focus, le champ affiche son libellé et son texte indicatif :
Avec le focus, le libellé passe en gras, un curseur apparaît et le submitLabel s'affiche après ⏎ :
La saisie remplace le texte indicatif :
`Select`
Un Select permet à l'utilisateur de choisir une option parmi plusieurs, et exécute onSelect avec la value de l'option :
Select({
key: 'format',
label: 'Format',
value: picked,
options: [
{ value: 'md', label: 'Markdown' },
{ value: 'html', label: 'HTML' },
{ value: 'txt', label: 'Plain text' },
],
onSelect: (value) => {
picked = value
},
})
Fermé, il affiche son libellé et l'option actuelle :
Ouvert, il liste ses options et en met une en évidence :
Une fois que l'utilisateur a choisi une option, la liste se ferme :
Dessiner des images
`Raster`
Un Raster est une grille de cellules de caractères colorées, pour une carte de chaleur, une sparkline ou un plateau de jeu. Le terminal la dessine. Cet exemple utilise la fonction cellsOf du module de départ, qui regroupe les cellules dans la chaîne attendue par un Raster. Dessiner une grille de cellules colorées l'explique :
Raster({
key: 'grid',
columns: 3,
rows: 2,
cells: cellsOf([
[['█', 0x2e7d32], ['█', 0xf9a825], ['█', 0xc62828]],
[['█', 0x2e7d32], ['█', 0x2e7d32], ['█', 0xf9a825]],
]),
})
Un Raster arrondit chaque couleur à une palette plus restreinte : 0x2e7d32 s'affiche donc en #337733.
`Svg`
Un Svg dessine un document SVG dans l'application Desktop :
Svg({
alt: 'Three bars of rising height',
width: 120,
height: 60,
source:
'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 120 60"><rect x="10" y="40" width="20" height="20" fill="#2e7d32"/><rect x="50" y="25" width="20" height="35" fill="#f9a825"/><rect x="90" y="5" width="20" height="55" fill="#c62828"/></svg>',
})
Dans le terminal, un panneau qui ne renvoie qu'un Svg s'ouvre vide. Pour y dessiner autre chose, vérifiez e.surface et renvoyez une arborescence différente.
`Image` et `Client`
Deux autres éléments n'ont pas d'exemple ici. Image dessine un PNG ou des pixels bruts dans le terminal. Client est une zone dessinée par un second fichier de votre part, pour l'animation et la saisie au pointeur. La référence des éléments répertorie leurs props.
À moins que Claude Code ne détecte que le terminal affiche les images du protocole graphique kitty avec des espaces réservés Unicode, l'utilisateur voit le texte alt d'une Image, atténué, à la place de l'image. Rédigez un texte alt qui se suffit à lui-même. La détection s'exécute au démarrage : elle réussit dans kitty 0.28 ou ultérieur et dans Ghostty, dès que le terminal répond à la requête graphique de Claude Code, et échoue dans les cas suivants :
- Autres terminaux : tout terminal autre que ces deux-là, ou qui ne répond pas à la requête.
- tmux et screen : une session exécutée dans tmux ou screen, dans n'importe quel terminal, kitty et Ghostty compris.
- Sessions en arrière-plan : toute session en arrière-plan, quel que soit le terminal depuis lequel elle est rattachée.
Si les utilisateurs de votre mod voient le texte atténué dans un terminal qui affiche bien ces images à espaces réservés, ils peuvent définir CLAUDE_CODE_FORCE_TERMINAL_IMAGES sur 1, ce qui ignore la détection. Dans tmux ou screen, cela n'aide pas : le texte alt disparaît, et Claude Code envoie l'image sans l'encapsuler pour le passthrough de tmux ou screen.
Voir où un mod peut dessiner
Tous les exemples dessinent dans un panneau. Un mod peut aussi dessiner à d'autres endroits, et appeler Claude Code pour afficher quelque chose à sa place :
- Panneau et bandeau : Choisir où dessiner
- Les lignes propres à Claude Code, comme le spinner : Modifier ce que Claude Code dessine déjà
- Toast, barre de statut et ligne de log : Afficher quelque chose sans démarrer un tour
- Boîte de dialogue de question : Suspendre un appel d'outil jusqu'à la décision de l'utilisateur
Étapes suivantes
- Dessiner dans l'interface : créer un panneau avec des onglets, étape par étape
- Tester un dessin : appuyer sur vos boutons depuis un test
- Référence des éléments : les principales props de chaque élément et les applications qui le dessinent