Galería de interfaz para mods
Consulta los elementos de interfaz que puede dibujar un mod de Claude Code, como texto, botones, campos, Markdown, código y diffs, con código de ejemplo y capturas de pantalla de la terminal.
Un mod dibuja su interfaz a partir de elementos: texto, cajas, botones, campos y algunos que dan formato al contenido por ti. Los ejemplos de aquí muestran el código que dibuja un elemento, y la mayoría incluye una captura de pantalla del resultado en un panel de la terminal, para que puedas elegir un elemento según su aspecto.
Para aprender cómo funciona el dibujo, comienza con Dibujar en la interfaz. Para las props principales y qué aplicaciones dibujan cada elemento, consulta la referencia de elementos. Las declaraciones de tipos enumeran todas las props.
Prueba un ejemplo
Los ejemplos de esta página son fragmentos, no mods completos. Cada uno es el código de un elemento y de todo lo que está anidado dentro de él.
Para ver un ejemplo en tu propia terminal, crea el pequeño mod de estos pasos y pega el ejemplo en él. El mod agrega un comando /gallery que abre un panel y dibuja el ejemplo allí. Un panel es una barra lateral junto a la transcripción en una terminal ancha de pantalla completa, o una región enmarcada encima del prompt en caso contrario.
Crea el mod
Crea un directorio llamado gallery con los directorios .claude-plugin y hooks dentro. Crear un mod explica los archivos.
Guarda el manifiesto como gallery/.claude-plugin/plugin.json:
{
"name": "gallery",
"version": "0.1.0",
"description": "Opens a pane that draws one sample",
"author": { "name": "Your Name" }
}
Indica tu punto de entrada en gallery/hooks/hooks.json:
{
"modules": ["./register.js"]
}
Guarda el código como gallery/hooks/register.js. Agrega un comando /gallery que abre un panel y dibuja Plain text en ese panel:
// 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'] })
})
}
Ejecuta el mod
En tu shell, inicia Claude Code desde el directorio que contiene gallery:
claude --plugin-dir ./gallery
En el prompt de Claude Code, ejecuta /gallery. Se abre un panel con Plain text.
Sustituye por un ejemplo
Copia un ejemplo de esta página. En register.js, pégalo sobre Text({ children: ['Plain text'] }), de modo que quede después de return, y guarda el archivo. Claude Code recarga el módulo cada vez que guardas, así que ejecuta /gallery de nuevo para ver el nuevo ejemplo.
Elige un elemento
Los ejemplos están agrupados según lo que quieras mostrar en pantalla:
- Mostrar texto:
Text,MarkdownyLink - Mostrar código y cambios:
Code - Organizar elementos:
Box - Recibir entrada:
Button,InputySelect - Dibujar imágenes:
Raster,Svg,ImageyClient
Mostrar texto
Tres elementos ponen palabras en la pantalla: Text para tu propio estilo, Markdown para contenido que ya tiene formato y Link para una URL.
`Text`
Text dibuja una cadena con los estilos que le indiques. Este ejemplo muestra una línea por cada estilo:
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 dibuja el texto en gris. backgroundColor rellena solo el ancho del texto.
`Markdown`
Markdown da formato al texto de la misma manera en que se formatean las respuestas de Claude. Pasa el contenido en text, no en 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 encabezado se dibuja en negrita sin sus marcas #. El código en línea se dibuja en color sin sus comillas invertidas. Una cita se dibuja en cursiva con una barra a su izquierda.
`Link`
Link dibuja una etiqueta seguida de su URL:
Link({ href: 'https://code.claude.com/docs', label: 'Claude Code docs' })
La terminal dibuja la URL como texto después de la etiqueta. Que un clic la abra o no depende de la terminal del usuario.
Mostrar código y cambios
Code dibuja texto fuente con los propios colores de sintaxis de Claude Code, o un diff.
`Code`
Indica el language, o pasa un path para que Claude Code lo infiera a partir de él. Con startLine, las líneas se numeran a partir de ese número:
Code({
language: 'javascript',
startLine: 1,
source: "const name = 'mods'\nconsole.log('hello ' + name)",
})
Los colores provienen del tema del usuario.
`Code` como diff
Con format: 'diff', source es uno o más fragmentos (hunks) de diff unificado:
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 dibuja números de línea en lugar de la línea @@. Cuando una línea eliminada y una línea añadida se parecen, las palabras que cambiaron reciben un sombreado más intenso.
Organizar elementos
`Box`
Box distribuye lo que contiene en una fila o una columna, y puede dibujar un borde. Este ejemplo coloca una fila de palabras sobre un cuadro con borde:
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'"] })],
}),
],
})
El borde se extiende hasta el ancho del panel.
Recibir entrada
Button, Input y Select son controles: el usuario se mueve entre ellos con Tab y usa el que tiene el foco. Foco del teclado y teclas de acceso rápido explica qué teclas llegan a ellos.
Abrir un panel con focus: true le da el foco del teclado al panel. Las letras que escribes llegan a un Input una vez que tiene el foco, así que agrega autoFocus: true a un campo que deba recibir texto en cuanto se abra el panel.
`Button`
Un botón ejecuta onPress. Este ejemplo muestra la forma predeterminada, un botón plain con una tecla de acceso rápido y uno atenuado:
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 botón que tiene el foco se dibuja en video inverso. Aquí el usuario presionó Tab dos veces:
`Input`
Un Input es un campo de texto de una línea que ejecuta onSubmit cuando el usuario presiona Enter:
Input({
key: 'title',
label: 'Title',
placeholder: 'Type a title and press Enter',
value: '',
submitLabel: 'save',
onSubmit: noop,
})
Sin el foco, el campo muestra su etiqueta y su texto de marcador de posición:
Con el foco, la etiqueta se pone en negrita, aparece un cursor y el submitLabel se muestra después de ⏎:
Al escribir, se reemplaza el marcador de posición:
`Select`
Un Select permite al usuario elegir una de varias opciones y ejecuta onSelect con el value de la opción:
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
},
})
Cerrado, muestra su etiqueta y la opción actual:
Abierto, enumera sus opciones y marca una:
Después de que el usuario elige una opción, la lista se cierra:
Dibujar imágenes
`Raster`
Un Raster es una cuadrícula de celdas de caracteres de colores, para un mapa de calor, un sparkline o un tablero de juego. La terminal lo dibuja. Este ejemplo usa la función cellsOf del módulo inicial, que empaqueta las celdas en la cadena que recibe un Raster. Dibujar una cuadrícula de celdas de colores lo explica:
Raster({
key: 'grid',
columns: 3,
rows: 2,
cells: cellsOf([
[['█', 0x2e7d32], ['█', 0xf9a825], ['█', 0xc62828]],
[['█', 0x2e7d32], ['█', 0x2e7d32], ['█', 0xf9a825]],
]),
})
Un Raster redondea cada color a una paleta más pequeña, por lo que 0x2e7d32 se dibuja como #337733.
`Svg`
Un Svg dibuja un documento SVG en la aplicación 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>',
})
En la terminal, un panel que devuelve solo un Svg se abre vacío. Para dibujar otra cosa allí, comprueba e.surface y devuelve un árbol diferente.
`Image` y `Client`
Hay dos elementos más que no tienen ejemplo aquí. Image dibuja un PNG o píxeles sin procesar en la terminal. Client es una región que dibuja un segundo archivo tuyo, para animación y entrada de puntero. La referencia de elementos enumera sus props.
A menos que Claude Code detecte que la terminal dibuja imágenes del protocolo de gráficos de kitty con marcadores de posición Unicode, el usuario ve el texto alt de un Image, atenuado, en lugar de la imagen. Escribe un texto alt que se entienda por sí solo. La detección se ejecuta al inicio: tiene éxito en kitty 0.28 o posterior y en Ghostty, una vez que la terminal responde a la consulta de gráficos de Claude Code, y falla en estos casos:
- Otras terminales: cualquier terminal que no sea una de esas dos, o que no responda a la consulta.
- tmux y screen: una sesión que se ejecuta dentro de tmux o screen, en cualquier terminal, incluidas kitty y Ghostty.
- Sesiones en segundo plano: toda sesión en segundo plano, sea cual sea la terminal desde la que se conecte.
Si los usuarios de tu mod ven el texto atenuado en una terminal que sí dibuja esas imágenes de marcador de posición, pueden establecer CLAUDE_CODE_FORCE_TERMINAL_IMAGES en 1, lo que omite la detección. Dentro de tmux o screen eso no ayuda: el texto alt desaparece y Claude Code envía la imagen sin envolverla para el paso directo (passthrough) de tmux o screen.
Ve dónde puede dibujar un mod
Todos los ejemplos dibujan en un panel. Un mod también puede dibujar en otros lugares y llamar a Claude Code para que muestre algo por él:
- Panel y banda: Elige dónde dibujar
- Las propias filas de Claude Code, como el spinner: Cambia lo que Claude Code ya dibuja
- Toast, línea de estado y línea de registro: Muestra algo sin iniciar un turno
- Diálogo de pregunta: Retén una llamada a herramienta hasta que el usuario decida
Próximos pasos
- Dibuja en la interfaz: crea un panel con pestañas, paso a paso
- Prueba un dibujo: presiona tus botones desde una prueba
- Referencia de elementos: las props principales de cada elemento y las apps que lo dibujan