gentleman-bubbletea について
gentleman-bubbletea は、Bubble Tea と Lipgloss を使った Go 製 TUI インストーラーを拡張するスキルです。画面状態、キー入力、描画、テストの規約を説明します。
主な機能
- 01model.go で画面と状態を定義
- 02Update() を通じた入力処理
- 03画面ごとのキー処理関数
- 04画面描画とタイトルの追加
- 05インストーラーのビルドとテスト
活用シーン
- 01インストーラー画面の追加
- 02キーボード操作への対応
- 03Lipgloss UI コンポーネントの作成
- 04画面遷移と状態の管理
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ gentleman-programming/ engram --skill gentleman-bubbleteaインストール後、Claude Code で/gentleman-bubbleteaを入力して読み込まれたか確認します。
詳細
使用する場面
次の場合にこのスキルを使用します。
- TUI インストーラーに新しい画面を追加する場合
- キーボード入力やナビゲーションを処理する場合
- Lipgloss を使って新しい UI コンポーネントを作成する場合
- 画面遷移や状態管理に取り組む場合
重要なパターン
パターン 1:model.go の画面定数
すべての画面は、model.go で Screen 定数として定義する必要があります。
type Screen int
const (
ScreenWelcome Screen = iota
ScreenMainMenu
ScreenOSSelect
// ... new screens go here
ScreenNewFeature // Add new screen
ScreenNewFeatureCat // Add category screen if needed
)
パターン 2:Model 構造体ですべての状態を保持
model.go の Model 構造体は、アプリケーションの状態をすべて保持します。
type Model struct {
Screen Screen
PrevScreen Screen // For back navigation
Width int
Height int
Cursor int
// Add new state here
NewFeatureData []SomeType
NewFeatureScroll int
}
パターン 3:型 switch を使った Update パターン
すべての入力処理は、型 switch を使って Update() を通じて行います。
func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
switch msg := msg.(type) {
case tea.KeyMsg:
return m.handleKeyPress(msg)
case tea.WindowSizeMsg:
m.Width = msg.Width
m.Height = msg.Height
return m, nil
case customMsg:
// Handle custom messages
return m, nil
}
return m, nil
}
パターン 4:キー入力ハンドラーは (Model, Cmd) を返す
画面ごとにハンドラーを分け、常に (tea.Model, tea.Cmd) を返します。
func (m Model) handleNewFeatureKeys(key string) (tea.Model, tea.Cmd) {
options := m.GetCurrentOptions()
switch key {
case "up", "k":
if m.Cursor > 0 {
m.Cursor--
// Skip separator
if strings.HasPrefix(options[m.Cursor], "───") && m.Cursor > 0 {
m.Cursor--
}
}
case "down", "j":
if m.Cursor < len(options)-1 {
m.Cursor++
if strings.HasPrefix(options[m.Cursor], "───") && m.Cursor < len(options)-1 {
m.Cursor++
}
}
case "enter", " ":
// Handle selection
return m.handleNewFeatureSelection()
case "esc":
m.Screen = m.PrevScreen
m.Cursor = 0
}
return m, nil
}
判断ツリー
Adding a new screen?
├── Define Screen constant in model.go
├── Add state fields to Model struct
├── Add handler in handleKeyPress switch
├── Create handle{Screen}Keys function in update.go
├── Add view case in view.go
└── Add title in GetScreenTitle()
Adding navigation to existing screen?
├── Use m.PrevScreen for back navigation
├── Reset m.Cursor = 0 on screen change
└── Save scroll position if scrollable
Adding scrollable content?
├── Add {Screen}Scroll int to Model
├── Calculate visibleItems from m.Height
├── Handle up/down for scroll position
└── Reset scroll on screen exit
コード例
例 1:handleKeyPress への画面の追加
// In handleKeyPress switch statement:
case ScreenNewFeature:
return m.handleNewFeatureKeys(key)
case ScreenNewFeatureCat:
return m.handleNewFeatureCatKeys(key)
例 2:画面オプションのパターン
func (m Model) GetCurrentOptions() []string {
switch m.Screen {
case ScreenNewFeature:
categories := make([]string, len(m.NewFeatureData)+2)
for i, item := range m.NewFeatureData {
categories[i] = item.Name
}
categories[len(m.NewFeatureData)] = "─────────────"
categories[len(m.NewFeatureData)+1] = "← Back"
return categories
// ...
}
}
例 3:スクロール可能なビューのパターン
func (m Model) handleNewFeatureCatKeys(key string) (tea.Model, tea.Cmd) {
data := m.NewFeatureData[m.SelectedNewFeature]
visibleItems := m.Height - 9
if visibleItems < 5 {
visibleItems = 5
}
maxScroll := len(data.Items) - visibleItems
if maxScroll < 0 {
maxScroll = 0
}
switch key {
case "up", "k":
if m.NewFeatureScroll > 0 {
m.NewFeatureScroll--
}
case "down", "j":
if m.NewFeatureScroll < maxScroll {
m.NewFeatureScroll++
}
case "esc", "q", "enter", " ":
m.Screen = ScreenNewFeature
m.NewFeatureScroll = 0
}
return m, nil
}
例 4:カスタムメッセージのパターン
// Define message type
type newFeatureLoadedMsg struct {
data []SomeType
err error
}
// Send message from command
func loadNewFeatureCmd() tea.Cmd {
return func() tea.Msg {
data, err := loadData()
return newFeatureLoadedMsg{data: data, err: err}
}
}
// Handle in Update
case newFeatureLoadedMsg:
if msg.err != nil {
m.ErrorMsg = msg.err.Error()
return m, nil
}
m.NewFeatureData = msg.data
return m, nil
コマンド
cd installer && go build ./cmd/gentleman-installer # Build installer
cd installer && go test ./internal/tui/... # Run TUI tests
cd installer && go test -run TestNewFeature # Run specific test
リソース
- Model:状態管理については
installer/internal/tui/model.goを参照してください - Update:入力処理については
installer/internal/tui/update.goを参照してください - View:描画については
installer/internal/tui/view.goを参照してください - Styles:Lipgloss のスタイルについては
installer/internal/tui/styles.goを参照してください
コメント
Markdown 対応