Galeria de interface para mods
Veja os elementos de interface que um mod do Claude Code pode desenhar, como texto, botões, campos, Markdown, código e diffs, com código de exemplo e capturas de tela do terminal.
Um mod desenha sua interface a partir de elementos: texto, caixas, botões, campos e alguns que formatam o conteúdo para você. Os exemplos aqui mostram o código que desenha um elemento, e a maioria vem com uma captura de tela do resultado em um painel do terminal, para que você possa escolher um elemento pela aparência.
Para aprender como o desenho funciona, comece com Desenhar na interface. Para as principais props e quais aplicativos desenham cada elemento, consulte a referência de elementos. As declarações de tipo listam todas as props.
Experimentar um exemplo
Os exemplos nesta página são trechos, não mods inteiros. Cada um é o código de um elemento e de tudo o que está aninhado dentro dele.
Para ver um exemplo no seu próprio terminal, crie o pequeno mod destas etapas e cole o exemplo nele. O mod adiciona um comando /gallery que abre um painel e desenha o exemplo ali. Um painel é uma barra lateral ao lado da transcrição em um terminal largo em tela cheia, ou, caso contrário, uma região emoldurada acima do prompt.
Criar o mod
Crie um diretório chamado gallery com os diretórios .claude-plugin e hooks dentro dele. Criar um mod explica os arquivos.
Salve o manifesto como gallery/.claude-plugin/plugin.json:
{
"name": "gallery",
"version": "0.1.0",
"description": "Opens a pane that draws one sample",
"author": { "name": "Your Name" }
}
Indique seu ponto de entrada em gallery/hooks/hooks.json:
{
"modules": ["./register.js"]
}
Salve o código como gallery/hooks/register.js. Ele adiciona um comando /gallery que abre um painel e desenha Plain text nesse painel:
// 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'] })
})
}
Executar o mod
No seu shell, inicie o Claude Code a partir do diretório que contém gallery:
claude --plugin-dir ./gallery
No prompt do Claude Code, execute /gallery. Um painel é aberto com Plain text nele.
Trocar por um exemplo
Copie um exemplo desta página. Em register.js, cole-o sobre Text({ children: ['Plain text'] }), de modo que ele venha depois de return, e salve o arquivo. O Claude Code recarrega o módulo cada vez que você salva, então execute /gallery novamente para ver o novo exemplo.
Escolher um elemento
Os exemplos estão agrupados pelo que você quer colocar na tela:
- Mostrar texto:
Text,MarkdowneLink - Mostrar código e alterações:
Code - Organizar elementos:
Box - Receber entrada:
Button,InputeSelect - Desenhar imagens:
Raster,Svg,ImageeClient
Mostrar texto
Três elementos colocam palavras na tela: Text para o seu próprio estilo, Markdown para conteúdo já formatado e Link para uma URL.
`Text`
Text desenha uma string com os estilos que você fornece. Este exemplo mostra uma linha para 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 desenha o texto em cinza. backgroundColor preenche apenas a largura do texto.
`Markdown`
Markdown formata o texto da mesma forma que as respostas do Claude são formatadas. Passe o conteúdo em text, não em 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',
})
Um título é desenhado em negrito sem suas marcas #. O código inline é desenhado em cor sem seus acentos graves. Uma citação é desenhada em itálico com uma barra à esquerda.
`Link`
Link desenha um rótulo seguido de sua URL:
Link({ href: 'https://code.claude.com/docs', label: 'Claude Code docs' })
O terminal desenha a URL como texto após o rótulo. Se um clique a abre ou não depende do terminal do usuário.
Mostrar código e alterações
Code desenha texto-fonte com as próprias cores de sintaxe do Claude Code, ou um diff.
`Code`
Indique a language, ou passe um path para que o Claude Code a deduza. Com startLine, as linhas são numeradas a partir desse número:
Code({
language: 'javascript',
startLine: 1,
source: "const name = 'mods'\nconsole.log('hello ' + name)",
})
As cores vêm do tema do usuário.
`Code` como diff
Com format: 'diff', source é um ou mais 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.',
})
O Claude Code desenha números de linha no lugar da linha @@. Onde uma linha removida e uma linha adicionada são parecidas, as palavras que mudaram recebem uma sombra mais forte.
Organizar elementos
`Box`
Box dispõe o que está dentro dele em uma linha ou em uma coluna, e pode desenhar uma borda. Este exemplo coloca uma linha de palavras acima de uma caixa com borda:
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'"] })],
}),
],
})
A borda se estende até a largura do painel.
Receber entrada
Button, Input e Select são controles: o usuário se move entre eles com Tab e usa aquele que tem o foco. Foco do teclado e teclas de atalho explica quais teclas chegam até eles.
Abrir um painel com focus: true dá ao painel o foco do teclado. As letras digitadas chegam a um Input assim que ele tem o foco, então adicione autoFocus: true a um campo que deve receber a digitação assim que o painel abrir.
`Button`
Um botão executa onPress. Este exemplo mostra a forma padrão, um botão plain com uma tecla de atalho e um esmaecido:
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 }),
],
})
Um botão que tem o foco é desenhado em vídeo inverso. Aqui o usuário pressionou Tab duas vezes:
`Input`
Um Input é um campo de texto de uma linha que executa onSubmit quando o usuário pressiona Enter:
Input({
key: 'title',
label: 'Title',
placeholder: 'Type a title and press Enter',
value: '',
submitLabel: 'save',
onSubmit: noop,
})
Sem o foco, o campo mostra seu rótulo e seu placeholder:
Com o foco, o rótulo fica em negrito, um cursor aparece e o submitLabel é exibido após ⏎:
Digitar substitui o placeholder:
`Select`
Um Select permite que o usuário escolha uma entre várias opções e executa onSelect com o value da opção:
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
},
})
Fechado, ele mostra seu rótulo e a opção atual:
Aberto, ele lista suas opções e marca uma:
Depois que o usuário escolhe uma opção, a lista se fecha:
Desenhar imagens
`Raster`
Um Raster é uma grade de células de caracteres coloridas, para um mapa de calor, um sparkline ou um tabuleiro de jogo. O terminal o desenha. Este exemplo usa a função cellsOf do módulo inicial, que empacota as células na string que um Raster recebe. Desenhar uma grade de células coloridas explica isso:
Raster({
key: 'grid',
columns: 3,
rows: 2,
cells: cellsOf([
[['█', 0x2e7d32], ['█', 0xf9a825], ['█', 0xc62828]],
[['█', 0x2e7d32], ['█', 0x2e7d32], ['█', 0xf9a825]],
]),
})
Um Raster arredonda cada cor para uma paleta menor, então 0x2e7d32 é desenhado como #337733.
`Svg`
Um Svg desenha um documento SVG no aplicativo 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>',
})
No terminal, um painel que retorna apenas um Svg abre vazio. Para desenhar outra coisa ali, verifique e.surface e retorne uma árvore diferente.
`Image` e `Client`
Mais dois elementos não têm exemplo aqui. Image desenha um PNG ou pixels brutos no terminal. Client é uma região que um segundo arquivo seu desenha, para animação e entrada de ponteiro. A referência de elementos lista suas props.
A menos que o Claude Code detecte que o terminal desenha imagens do protocolo gráfico do kitty com placeholders Unicode, o usuário vê o texto alt de uma Image, esmaecido, no lugar da imagem. Escreva um texto alt que faça sentido por si só. A detecção é executada na inicialização: ela tem sucesso no kitty 0.28 ou posterior e no Ghostty, desde que o terminal responda à consulta gráfica do Claude Code, e falha nestes casos:
- Outros terminais: qualquer terminal que não seja um desses dois, ou que não responda à consulta.
- tmux e screen: uma sessão executada dentro do tmux ou do screen, em qualquer terminal, incluindo kitty e Ghostty.
- Sessões em segundo plano: toda sessão em segundo plano, independentemente do terminal a partir do qual ela esteja conectada.
Se os usuários do seu mod virem o texto esmaecido em um terminal que de fato desenha essas imagens com placeholders, eles podem definir CLAUDE_CODE_FORCE_TERMINAL_IMAGES como 1, o que ignora a detecção. Dentro do tmux ou do screen isso não ajuda: o texto alt desaparece, e o Claude Code envia a imagem sem encapsulá-la para o passthrough do tmux ou do screen.
Ver onde um mod pode desenhar
Todos os exemplos desenham em um painel. Um mod também pode desenhar em outros lugares e chamar o Claude Code para mostrar algo por ele:
- Painel e faixa: Escolher onde desenhar
- As próprias linhas do Claude Code, como o spinner: Alterar o que o Claude Code já desenha
- Toast, linha de status e linha de log: Mostrar algo sem iniciar um turno
- Diálogo de pergunta: Reter uma chamada de ferramenta até que o usuário decida
Próximos passos
- Desenhar na interface: construa um painel com abas, passo a passo
- Testar um desenho: pressione seus botões a partir de um teste
- Referência de elementos: as principais props de cada elemento e os aplicativos que o desenham