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() }