Files
arinera-minecraft-tool/app/app.go
2026-05-27 19:31:47 +08:00

127 lines
2.5 KiB
Go

package app
import (
"github.com/charmbracelet/bubbles/list"
tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/lipgloss"
)
// Module is the interface that all TUI feature modules must implement.
type Module interface {
tea.Model
Title() string
Description() string
}
// Entry represents a registered module with its factory function.
type Entry struct {
Title string
Desc string
Create func() tea.Model
}
// ===== menu item =====
type menuItem struct {
title string
desc string
}
func (mi menuItem) FilterValue() string { return mi.title }
func (mi menuItem) Title() string { return mi.title }
func (mi menuItem) Description() string { return mi.desc }
// ===== styles =====
var menuStyle = lipgloss.NewStyle().
Bold(true).
Foreground(lipgloss.Color("39")).
PaddingLeft(1)
// ===== model =====
type Model struct {
menu list.Model
active tea.Model
entries []Entry
}
func New(entries []Entry) *Model {
items := make([]list.Item, len(entries))
for i, e := range entries {
items[i] = menuItem{title: e.Title, desc: e.Desc}
}
l := list.New(items, list.NewDefaultDelegate(), 0, 0)
l.Title = "ARinera Minecraft 管理工具"
l.SetShowStatusBar(false)
l.SetShowHelp(true)
return &Model{menu: l, entries: entries}
}
func (m *Model) Init() tea.Cmd {
return nil
}
func (m *Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
if key, ok := msg.(tea.KeyMsg); ok {
switch key.String() {
case "ctrl+c":
return m, tea.Quit
case "esc":
if m.active != nil {
m.active = nil
return m, nil
}
return m, tea.Quit
}
}
if ws, ok := msg.(tea.WindowSizeMsg); ok {
if m.active == nil {
m.menu.SetSize(ws.Width, ws.Height-4)
}
}
if m.active != nil {
return m.updateModule(msg)
}
return m.updateMenu(msg)
}
func (m *Model) updateMenu(msg tea.Msg) (tea.Model, tea.Cmd) {
switch msg := msg.(type) {
case tea.KeyMsg:
if msg.String() == "enter" {
sel, ok := m.menu.SelectedItem().(menuItem)
if !ok {
return m, nil
}
for _, e := range m.entries {
if e.Title == sel.title {
m.active = e.Create()
return m, m.active.Init()
}
}
}
}
var cmd tea.Cmd
m.menu, cmd = m.menu.Update(msg)
return m, cmd
}
func (m *Model) updateModule(msg tea.Msg) (tea.Model, tea.Cmd) {
newMod, cmd := m.active.Update(msg)
m.active = newMod
return m, cmd
}
func (m *Model) View() string {
if m.active != nil {
return m.active.View()
}
return menuStyle.Render("ARinera Minecraft 管理工具") + "\n\n" + m.menu.View()
}