Thư viện phần tử giao diện
Mod vẽ giao diện của nó từ các element: text, box, nút bấm, ô nhập, và một vài element tự định dạng nội dung giúp bạn. Các mẫu trong trang này cho thấy code vẽ một element, và hầu hết đều kèm ảnh chụp kết quả trong một pane terminal, để bạn chọn element theo hình dáng của nó.
Để hiểu cách vẽ hoạt động, hãy bắt đầu với Vẽ giao diện bằng mod. Các prop chính và ứng dụng nào vẽ được từng element nằm ở phần tham chiếu element. Các file khai báo type liệt kê mọi prop.
Chạy thử một mẫu
Phần tiêu đề “Chạy thử một mẫu”Các mẫu trong trang này là đoạn code (snippet), không phải mod hoàn chỉnh. Mỗi mẫu là code cho một element và những gì lồng bên trong nó.
Để xem một mẫu trong terminal của bạn, hãy tạo mod nhỏ theo các bước dưới đây rồi dán mẫu vào. Mod thêm command /gallery để mở một pane và vẽ mẫu vào đó. Pane là một sidebar bên cạnh transcript khi terminal ở chế độ fullscreen đủ rộng, hoặc một vùng có khung phía trên ô prompt trong các trường hợp còn lại.
Bước 1: Tạo mod
Phần tiêu đề “Bước 1: Tạo mod”Tạo thư mục gallery với hai thư mục con .claude-plugin và hooks. Trang Tạo một mod giải thích các file.
Lưu manifest thành gallery/.claude-plugin/plugin.json:
{ "name": "gallery", "version": "0.1.0", "description": "Opens a pane that draws one sample", "author": { "name": "Your Name" }}Khai báo entry point trong gallery/hooks/hooks.json:
{ "modules": ["./register.js"]}Lưu code thành gallery/hooks/register.js. Nó thêm command /gallery để mở một pane, và vẽ chữ Plain text trong pane đó:
// Đứng thay cho callback của bạn trong các mẫu cần callbackconst noop = () => {}// Mẫu Select lưu lựa chọn ở đâylet picked = 'md'
// Mẫu Raster đóng gói các ô bằng hàm nàyconst DEFAULT_COLOR = 0x01000000function 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) // Thay element phía sau return bằng một mẫu return Text({ children: ['Plain text'] }) })}Bước 2: Chạy mod
Phần tiêu đề “Bước 2: Chạy mod”Trong shell, khởi động Claude Code từ thư mục chứa gallery:
claude --plugin-dir ./galleryTại prompt của Claude Code, chạy /gallery. Một pane mở ra với dòng Plain text bên trong.
Bước 3: Thay bằng một mẫu
Phần tiêu đề “Bước 3: Thay bằng một mẫu”Copy một mẫu trong trang này. Trong register.js, dán nó đè lên Text({ children: ['Plain text'] }), sao cho nó đứng sau return, rồi lưu file. Claude Code reload module mỗi khi bạn lưu, nên chạy lại /gallery để xem mẫu mới.
Chọn một element
Phần tiêu đề “Chọn một element”Các mẫu được nhóm theo thứ bạn muốn đưa lên màn hình:
- Hiển thị text:
Text,MarkdownvàLink - Hiển thị code và thay đổi:
Code - Sắp xếp element:
Box - Nhận input:
Button,InputvàSelect - Vẽ hình:
Raster,Svg,ImagevàClient
Hiển thị text
Phần tiêu đề “Hiển thị text”Ba element đưa chữ lên màn hình: Text cho style do bạn tự đặt, Markdown cho nội dung đã được định dạng sẵn, và Link cho một URL.
Text vẽ một chuỗi với các style bạn cho nó. Mẫu này hiển thị mỗi style một dòng:
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 vẽ text màu xám. backgroundColor chỉ tô nền vừa đúng độ rộng của text.
Markdown
Phần tiêu đề “Markdown”Markdown định dạng text giống cách câu trả lời của Claude được định dạng. Truyền nội dung qua text, không qua 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',})
Tiêu đề được vẽ in đậm và không có dấu #. Inline code được vẽ có màu và không có dấu backtick. Trích dẫn được vẽ in nghiêng, có thanh dọc bên trái.
Link vẽ một nhãn, theo sau là URL của nó:
Link({ href: 'https://code.claude.com/docs', label: 'Claude Code docs' })
Terminal vẽ URL dưới dạng text sau nhãn. Click vào có mở được link hay không tùy thuộc vào terminal của user.
Hiển thị code và thay đổi
Phần tiêu đề “Hiển thị code và thay đổi”Code vẽ mã nguồn với màu cú pháp của chính Claude Code, hoặc một diff.
Ghi rõ language, hoặc truyền một path để Claude Code tự suy ra ngôn ngữ. Với startLine, các dòng được đánh số bắt đầu từ số đó:
Code({ language: 'javascript', startLine: 1, source: "const name = 'mods'\nconsole.log('hello ' + name)",})
Màu sắc lấy theo theme của user.
Code dạng diff
Phần tiêu đề “Code dạng diff”Với format: 'diff', source là một hoặc nhiều hunk của unified diff:
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 vẽ số dòng thay cho dòng @@. Khi một dòng bị xóa và một dòng được thêm giống nhau, những từ thay đổi sẽ được tô đậm hơn.
Sắp xếp element
Phần tiêu đề “Sắp xếp element”Box bố trí các thứ bên trong nó thành một hàng hoặc một cột, và có thể vẽ viền. Mẫu này đặt một hàng chữ phía trên một box có viền:
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'"] })], }), ],})
Viền giãn ra hết chiều rộng của pane.
Nhận input
Phần tiêu đề “Nhận input”Button, Input và Select là các control: user di chuyển giữa chúng bằng Tab và dùng control đang có focus. Phần Keyboard focus và hotkey mô tả những phím nào tới được chúng.
Mở pane với focus: true sẽ trao keyboard focus cho pane. Chữ gõ vào chỉ tới được một Input khi nó có focus, nên hãy thêm autoFocus: true cho ô nhập cần nhận chữ ngay khi pane mở.
Button
Phần tiêu đề “Button”Một nút chạy onPress. Mẫu này cho thấy dạng mặc định, một nút plain có hotkey, và một nút mờ:
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 }), ],})
Nút đang có focus được vẽ đảo màu (inverse video). Ở đây user đã nhấn Tab hai lần:
Input
Phần tiêu đề “Input”Input là một ô nhập text một dòng, chạy onSubmit khi user nhấn Enter:
Input({ key: 'title', label: 'Title', placeholder: 'Type a title and press Enter', value: '', submitLabel: 'save', onSubmit: noop,})Khi không có focus, ô nhập hiển thị nhãn và placeholder:
Khi có focus, nhãn chuyển sang in đậm, con trỏ xuất hiện, và submitLabel hiện sau ⏎:
Gõ chữ sẽ thay thế placeholder:
Select
Phần tiêu đề “Select”Select cho user chọn một trong nhiều lựa chọn, và chạy onSelect với value của lựa chọ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 },})Khi đóng, nó hiển thị nhãn và lựa chọn hiện tại:
Khi mở, nó liệt kê các lựa chọn và đánh dấu một lựa chọn:
Sau khi user chọn, danh sách đóng lại:
Vẽ hình
Phần tiêu đề “Vẽ hình”Raster
Phần tiêu đề “Raster”Raster là một lưới các ô ký tự có màu, dùng cho heat map, sparkline hay bàn cờ game. Terminal vẽ được nó. Mẫu này dùng hàm cellsOf trong module khởi đầu, hàm này đóng gói các ô thành chuỗi mà Raster nhận. Phần Vẽ lưới ô màu giải thích chi tiết:
Raster({ key: 'grid', columns: 3, rows: 2, cells: cellsOf([ [['█', 0x2e7d32], ['█', 0xf9a825], ['█', 0xc62828]], [['█', 0x2e7d32], ['█', 0x2e7d32], ['█', 0xf9a825]], ]),})
Raster làm tròn mỗi màu về một bảng màu nhỏ hơn, nên 0x2e7d32 được vẽ thành #337733.
Svg vẽ một tài liệu SVG trong ứng dụng 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>',})Trong terminal, một pane chỉ trả về Svg sẽ mở ra trống trơn. Để vẽ thứ khác ở đó, hãy kiểm tra e.surface và trả về một cây khác.
Image và Client
Phần tiêu đề “Image và Client”Còn hai element nữa không có mẫu ở đây. Image vẽ ảnh PNG hoặc pixel thô trong terminal. Client là một vùng được vẽ bởi một file thứ hai của bạn, dùng cho animation và thao tác con trỏ. Phần tham chiếu element liệt kê props của chúng.
Xem những nơi mod có thể vẽ
Phần tiêu đề “Xem những nơi mod có thể vẽ”Các mẫu trên đều vẽ trong pane. Mod còn có thể vẽ ở những nơi khác, và gọi Claude Code hiển thị thông tin giúp nó:
- Pane và band: Chọn nơi để vẽ
- Các dòng của chính Claude Code, như spinner: Thay đổi những gì Claude Code đã vẽ sẵn
- Toast, status line và dòng log: Hiển thị thông tin mà không bắt đầu turn
- Hộp thoại hỏi đáp: Giữ tool call cho đến khi user quyết định
Đọc tiếp
Phần tiêu đề “Đọc tiếp”- Vẽ giao diện bằng mod: xây dựng pane có tab, từng bước một
- Kiểm thử phần vẽ: bấm các nút của bạn từ test
- Tham chiếu element: các prop chính của từng element và ứng dụng vẽ được nó
Bài tiếp theo: Phản ứng với event - Quan sát, viết lại hoặc trả lời tool call, prompt và turn.