feat: mvp
This commit is contained in:
126
app/app.go
Normal file
126
app/app.go
Normal file
@@ -0,0 +1,126 @@
|
||||
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()
|
||||
}
|
||||
Reference in New Issue
Block a user