mod 인터페이스 갤러리 (Interface gallery for mods)
기준일: 2026-10-23
공식 기준: Interface gallery for mods
개요
mod는 텍스트, 상자, 버튼, 필드, 그리고 내용을 대신 서식화해 주는 몇 가지 요소로 인터페이스를 그립니다. 공식 페이지는 요소를 그리는 코드와 터미널 pane에 그려진 스크린샷을 함께 보여 주어 모양을 보고 요소를 고를 수 있게 합니다. 이 문서에서는 스크린샷 대신 각 요소가 어떻게 보이는지 글로 설명합니다.
그리기 원리는 인터페이스 그리기부터 보세요. 주요 prop과 요소별 지원 앱은 elements reference에, 모든 prop은 빌드용 type declarations에 있습니다.
핵심 개념
| 하고 싶은 것 | 요소 |
|---|---|
| 텍스트 표시 | Text, Markdown, Link |
| 코드와 변경 표시 | Code |
| 요소 배치 | Box |
| 입력 받기 | Button, Input, Select |
| 그림 그리기 | Raster, Svg, Image, Client |
상세
샘플 써 보기
이 페이지의 샘플은 mod 전체가 아니라 요소 하나와 그 안에 중첩된 것의 코드 조각입니다. 직접 보려면 아래 작은 mod를 만들고 샘플을 붙여 넣습니다. 이 mod는 pane을 여는 /gallery 명령을 추가하고 그 pane에 샘플을 그립니다. pane은 넓은 fullscreen 터미널에서는 transcript 옆 사이드바, 그 외에는 프롬프트 위 테두리 영역입니다.
1. mod 만들기
gallery 디렉터리 안에 .claude-plugin과 hooks 디렉터리를 만듭니다(파일 설명은 mod 만들기).
gallery/.claude-plugin/plugin.json:
{
"name": "gallery",
"version": "0.1.0",
"description": "Opens a pane that draws one sample",
"author": { "name": "Your Name" }
}
gallery/hooks/hooks.json:
{
"modules": ["./register.js"]
}
gallery/hooks/register.js는 /gallery 명령을 추가하고, pane을 열어 Plain text를 그립니다.
// 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'] })
})
}
2. mod 실행
gallery가 있는 디렉터리에서 시작합니다.
claude --plugin-dir ./gallery
프롬프트에서 /gallery를 실행하면 Plain text가 든 pane이 열립니다.
3. 샘플 바꿔 넣기
샘플을 복사해 register.js의 Text({ children: ['Plain text'] }) 자리에(return 뒤) 붙여 넣고 저장합니다. 저장할 때마다 Claude Code가 모듈을 다시 로드하므로 /gallery를 다시 실행하면 새 샘플이 보입니다.
텍스트 표시
화면에 글자를 놓는 요소는 세 가지입니다: 스타일을 직접 주는 Text, 이미 서식이 있는 내용용 Markdown, URL용 Link.
Text
주어진 스타일로 문자열을 그립니다. 아래 샘플은 일반 텍스트와 스타일 8가지를 한 줄씩 보여 줍니다.
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'"] }),
],
})
표시 모습: pane에 스타일 이름이 붙은 9줄(plain, bold, italic, underline, strikethrough, 회색 dimColor, inverse, 빨간 color, 파란 backgroundColor)이 나옵니다. dimColor는 회색으로 그리고, backgroundColor는 텍스트 너비만큼만 칠합니다.
Markdown
Claude 응답과 같은 방식으로 텍스트를 서식화합니다. 내용은 children이 아니라 text로 넘깁니다.
Markdown({
text: '## Release notes\n\nThis build has **two** fixes and one `flag`:\n\n- Faster start\n- Fewer prompts\n\n> Quoted text',
})
표시 모습: 굵은 제목 Release notes, 굵은 단어 하나와 색이 입혀진 코드 단어 하나가 든 문장, 항목 두 개짜리 목록, 왼쪽에 막대가 있는 기울임꼴 인용문. 제목은 # 없이 굵게, 인라인 코드는 backtick 없이 색으로, 인용은 왼쪽 막대와 함께 기울임꼴로 그려집니다.
Link
라벨 뒤에 URL을 그립니다.
Link({ href: 'https://code.claude.com/docs', label: 'Claude Code docs' })
표시 모습: 한 줄에 라벨 Claude Code docs, 이어서 회색 URL. 터미널은 URL을 라벨 뒤 텍스트로 그리며, 클릭으로 열리는지는 사용자 터미널에 달려 있습니다.
코드와 변경 표시
Code는 Claude Code 자체 구문 색상으로 소스 텍스트나 diff를 그립니다. VS Code 확장에서는 source를 일반 텍스트로 보여 주며 색·줄 번호·diff 음영이 없습니다.
Code
language를 지정하거나, Claude Code가 추론하도록 path를 넘깁니다. startLine을 주면 그 번호부터 줄 번호를 붙입니다.
Code({
language: 'javascript',
startLine: 1,
source: "const name = 'mods'\nconsole.log('hello ' + name)",
})
표시 모습: 구문 색상이 입혀진 JavaScript 두 줄에 줄 번호. 색은 사용자 테마를 따릅니다.
diff로 쓰는 Code
format: 'diff'이면 source는 하나 이상의 unified diff hunk입니다.
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.',
})
표시 모습: 4줄 diff. 삭제 줄은 빨강, 추가 줄은 초록 음영이고 각각 줄 번호가 있으며, 추가 줄의 "that runs code"는 더 진한 음영입니다. Claude Code는 @@ 줄 대신 줄 번호를 그리고, 비슷한 삭제·추가 줄 쌍에서는 바뀐 단어를 더 진하게 칠합니다.
요소 배치
Box
안의 요소를 행 또는 열로 배치하고 테두리를 그릴 수 있습니다. 아래 샘플은 단어 한 줄 아래에 테두리 상자를 놓습니다.
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'"] })],
}),
],
})
표시 모습: 4열 간격의 단어 세 개, 빈 줄, 텍스트 한 줄을 둘러싼 둥근 테두리. 테두리는 pane 너비만큼 늘어납니다.
입력 받기
Button, Input, Select는 컨트롤입니다. 사용자는 Tab으로 이동하고 포커스가 있는 컨트롤을 사용합니다. 어떤 키가 닿는지는 키보드 포커스와 hotkey를 봅니다.
pane을 focus: true로 열어 키보드 포커스를 요청합니다. Input에 포커스가 가면 입력한 글자가 들어갑니다. pane이 열릴 때 포커스를 받을 필드를 고르는 법은 같은 문서의 "hotkey와 첫 포커스 지정"을 봅니다.
Button
onPress를 실행합니다. 기본형, hotkey가 있는 plain 버튼, 흐린 버튼 샘플:
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 }),
],
})
표시 모습: 한 줄에 하나씩 괄호 속 Save, 괄호 없이 n이 색으로 강조된 n: Next, 회색 괄호 속 Skip. 포커스가 있는 버튼은 inverse video로 그려집니다. 예를 들어 Tab을 두 번 누르면 두 번째 버튼 n: Next가 반전됩니다.
Input
Enter를 누르면 onSubmit을 실행하는 한 줄 텍스트 필드입니다.
Input({
key: 'title',
label: 'Title',
placeholder: 'Type a title and press Enter',
value: '',
submitLabel: 'save',
onSubmit: noop,
})
표시 모습:
- 포커스 없음: 라벨 Title, 이어서 회색 placeholder
Type a title and press Enter - 포커스 있음: 라벨이 굵어지고, placeholder 첫 글자에 블록 커서가 생기며,
⏎뒤에submitLabel인 save가 표시 - 입력 중: placeholder가 입력한 글자(예: Rel)로 바뀌고, 뒤에
⏎ save가 유지
Select
여러 옵션 중 하나를 고르게 하고, 고른 옵션의 value로 onSelect를 실행합니다.
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
},
})
표시 모습:
- 닫힘: 라벨 Format, 현재 옵션 Markdown, 작은 아래 화살표
- 열림: 라벨 아래 세 옵션이 나열되고 하나가 표시됨(예: 두 번째 HTML이 inverse video)
- 선택 후: 목록이 닫히고 현재 옵션으로 HTML 표시
그림 그리기
Raster
heat map, sparkline, 게임 보드용 색 문자 셀 격자이며 터미널이 그립니다. starter 모듈의 cellsOf 함수로 셀을 Raster가 받는 문자열로 압축합니다(설명: 색 셀 격자 그리기).
Raster({
key: 'grid',
columns: 3,
rows: 2,
cells: cellsOf([
[['█', 0x2e7d32], ['█', 0xf9a825], ['█', 0xc62828]],
[['█', 0x2e7d32], ['█', 0x2e7d32], ['█', 0xf9a825]],
]),
})
표시 모습: 2행 3열 색 블록. 위 줄 초록·주황·빨강, 아래 줄 초록·초록·주황. Raster는 각 색을 더 작은 팔레트로 반올림하므로 0x2e7d32는 #337733으로 그려집니다.
Svg
Desktop 앱과 VS Code 확장에서 SVG 문서를 그립니다.
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>',
})
터미널에서 Svg만 반환하는 pane은 비어 있는 채로 열립니다. 터미널에서 다른 것을 그리려면 e.surface를 확인해 다른 tree를 반환합니다.
Image와 Client
샘플이 없는 두 요소입니다. props는 elements reference에 있습니다.
Image: 터미널에서 PNG 또는 raw 픽셀을 그립니다.Client: 애니메이션·포인터 입력을 위해 내 두 번째 파일이 그리는 영역입니다.
Image가 그림으로 그려지는 터미널:
- kitty 0.28 이상
- Ghostty
- iTerm2 3.5 이상. iTerm2에서 그리려면 Claude Code v2.1.305 이상 필요
그 밖의 터미널에서는 그림 대신 alt 텍스트가 흐리게 보입니다. 다음 경우에도 alt가 보이므로 단독으로 의미가 통하게 쓰세요.
- tmux, screen: 어떤 터미널이든 tmux나 screen 안에서 실행하는 세션
- 백그라운드 세션: 어느 터미널에서 attach하든 모든 background session
- fullscreen 렌더링이 아닌 iTerm2: iTerm2에서는 fullscreen 렌더링에서만, 그리고
CLAUDE_CODE_ALT_SCREEN_FULL_REPAINT가1이 아닐 때만 그려집니다. 보려면 프롬프트에서/tui fullscreen을 실행하고 그 변수는 설정하지 않습니다. - iTerm2의 파일 경로 source: iTerm2는
sourceprop에png또는rgba바이트가 든Image는 그리지만,source가file을 가리키면alt를 보여 줍니다. iTerm2 사용자가 있다면source.file경로 대신source.png또는source.rgba에 base64로 넘기세요.
Unicode placeholder 방식의 kitty graphics protocol 이미지를 그리는 터미널인데도 흐린 텍스트가 보인다면, 사용자가 CLAUDE_CODE_FORCE_TERMINAL_IMAGES를 1로 설정할 수 있습니다. tmux나 screen 안에서는 소용없습니다. alt 텍스트는 사라지지만 Claude Code가 tmux/screen passthrough로 감싸지 않고 그림을 보냅니다.
mod가 그릴 수 있는 다른 곳
샘플은 모두 pane에 그리지만, mod는 다른 곳에도 그리고 Claude Code에 무언가를 대신 보여 달라고 요청할 수 있습니다.
| 위치 | 참고 |
|---|---|
| pane, band | 인터페이스 그리기의 그릴 위치 고르기 |
| 스피너 등 Claude Code 자체 행 | 인터페이스 그리기의 Claude Code가 이미 그리는 것 바꾸기 |
| toast, status 줄, log 줄 | mods API의 턴 없이 보여 주기 |
| 질문 대화상자 | 이벤트의 사용자가 결정할 때까지 도구 호출 보류 |
체크리스트
-
/gallerystarter mod를 만들고claude --plugin-dir ./gallery로 샘플을 확인했다. -
Markdown은children이 아니라text로 내용을 넘겼다. - VS Code 확장에서는
Code가 색·줄 번호 없이 일반 텍스트로 보인다는 점을 고려했다. - 터미널에서
Svg만 반환하지 않도록e.surface로 분기했다. -
Image의alt를 단독으로 의미가 통하게 썼고, iTerm2 사용자를 위해source.png/source.rgbabase64를 썼다. - 컨트롤 샘플을 쓸 때 pane을
focus: true로 열었다.
다음 단계
- Draw in the interface with a mod: 탭이 있는 pane을 단계별로 만듭니다.
- Test a mod: 테스트에서 버튼을 누릅니다.
- Mods reference: 요소별 주요 props와 지원 앱을 확인합니다.
기준일
이 문서는 2026-10-23에 공식 Interface gallery for mods 페이지를 기준으로 작성했습니다. 공식 페이지의 터미널 스크린샷은 글로 대체했습니다. iTerm2 Image 요건(Claude Code v2.1.305 이상)과 터미널 버전(kitty 0.28, iTerm2 3.5)은 공식 원문에 명시된 값입니다.