Files
dirmd/internal/tui/model.go
T
2026-08-27 15:49:18 +01:00

173 lines
3.4 KiB
Go

package tui
import (
"fmt"
"strings"
"charm.land/bubbles/v2/textinput"
tea "charm.land/bubbletea/v2"
)
type State int
const (
StateForm State = iota
StateGenerating
StateDone
StateError
)
type genResultMsg struct {
result GenerateResult
}
type spinnerState struct {
frame int
}
func (s *spinnerState) View() string {
frames := []string{"⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"}
return frames[s.frame%len(frames)]
}
type Model struct {
state State
form FormModel
result GenerateResult
spinner spinnerState
isDark bool
width int
height int
}
func New() Model {
return Model{
state: StateForm,
form: NewForm(),
isDark: true,
}
}
func (m Model) Init() tea.Cmd {
return textinput.Blink
}
func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
switch msg := msg.(type) {
case tea.BackgroundColorMsg:
m.isDark = msg.IsDark()
m.form.SetDark(m.isDark)
return m, nil
case tea.WindowSizeMsg:
m.width = msg.Width
m.height = msg.Height
m.form.SetSize(msg.Width, msg.Height)
return m, nil
case tea.KeyPressMsg:
if isQuit(msg) && m.state != StateGenerating {
return m, tea.Quit
}
switch m.state {
case StateForm:
if isSubmit(msg) && m.form.focusIndex == fieldGenerateBtn {
cfg, err := m.form.ToConfig()
if err != nil {
m.form.SetError(err.Error())
return m, nil
}
m.state = StateGenerating
return m, func() tea.Msg {
return genResultMsg{result: Generate(cfg)}
}
}
if isSubmit(msg) && m.form.focusIndex == fieldCancelBtn {
return m, tea.Quit
}
var cmd tea.Cmd
m.form, cmd = m.form.Update(msg)
return m, cmd
case StateDone, StateError:
return m, tea.Quit
}
case genResultMsg:
m.result = msg.result
if m.result.Error != nil {
m.state = StateError
} else {
m.state = StateDone
}
return m, nil
}
if m.state == StateForm {
var cmd tea.Cmd
m.form, cmd = m.form.Update(msg)
return m, cmd
}
return m, nil
}
func (m Model) View() tea.View {
var content string
switch m.state {
case StateForm:
content = m.form.View()
case StateGenerating:
content = m.renderGenerating()
case StateDone:
content = m.renderDone()
case StateError:
content = m.renderError()
}
v := tea.NewView(content)
v.AltScreen = true
return v
}
func (m Model) renderGenerating() string {
var b strings.Builder
b.WriteString(titleStyle.Render("dirmd"))
b.WriteString("\n\n")
b.WriteString(fmt.Sprintf(" %s Generating...\n", m.spinner.View()))
b.WriteString("\n")
b.WriteString(footerStyle.Render("Please wait."))
return appBorder.Render(b.String())
}
func (m Model) renderDone() string {
var b strings.Builder
b.WriteString(titleStyle.Render("dirmd"))
b.WriteString("\n\n")
b.WriteString(successStyle.Render("✓ Done"))
b.WriteString("\n\n")
for _, f := range m.result.Files {
b.WriteString(fmt.Sprintf(" %s\n", f))
}
if m.result.Uploaded {
b.WriteString(successStyle.Render("\n Uploaded to Proton Drive"))
}
b.WriteString("\n\n")
b.WriteString(footerStyle.Render("Press any key to exit."))
return appBorder.Render(b.String())
}
func (m Model) renderError() string {
var b strings.Builder
b.WriteString(titleStyle.Render("dirmd"))
b.WriteString("\n\n")
b.WriteString(errorStyle.Render("✗ Error"))
b.WriteString("\n\n")
b.WriteString(fmt.Sprintf(" %s\n", m.result.Error.Error()))
b.WriteString("\n")
b.WriteString(footerStyle.Render("Press any key to exit."))
return appBorder.Render(b.String())
}