This commit is contained in:
jb
2026-03-05 08:46:04 -03:00
commit 315807bbab
10 changed files with 308 additions and 0 deletions
+28
View File
@@ -0,0 +1,28 @@
package tui
import (
"os/exec"
tea "charm.land/bubbletea/v2"
)
// --- Messages ---
type DockerCheckedMsg struct{ Installed bool }
type DockerInstalledMsg struct{ Err error }
// --- Commands ---
func CheckDockerCmd() tea.Cmd {
return func() tea.Msg {
_, err := exec.LookPath("docker")
return DockerCheckedMsg{Installed: err == nil}
}
}
func InstallDockerCmd() tea.Cmd {
return func() tea.Msg {
err := exec.Command("sh", "-c", "curl -fsSL https://get.docker.com | sh").Run()
return DockerInstalledMsg{Err: err}
}
}
+1
View File
@@ -0,0 +1 @@
package tui
+25
View File
@@ -0,0 +1,25 @@
package tui
import (
"charm.land/bubbles/v2/textinput"
tea "charm.land/bubbletea/v2"
)
type Model struct {
currentStep step
dockerInstalled bool
isPublicIP bool
inputs []textinput.Model
cursor int
err error
}
func InitialModel() Model {
return Model{
currentStep: StepCheckDocker,
}
}
func (m Model) Init() tea.Cmd {
return CheckDockerCmd()
}
+12
View File
@@ -0,0 +1,12 @@
package tui
type step int
const (
StepCheckDocker step = iota
StepDockerInstall
StepIPQuestion
StepInstallWireguard
StepTextInputs
StepDone
)
+12
View File
@@ -0,0 +1,12 @@
package tui
import "charm.land/lipgloss/v2"
const padding = 2
var (
TitleStyle = lipgloss.NewStyle().Bold(true).Foreground(lipgloss.Color("#FAFAFA"))
HelpStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("#626262"))
CursorStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("#FF75B7"))
ErrorStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("#FF5555"))
)
+76
View File
@@ -0,0 +1,76 @@
package tui
import tea "charm.land/bubbletea/v2"
func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
// Global keys first — always handled regardless of step
if key, ok := msg.(tea.KeyPressMsg); ok {
switch key.String() {
case "ctrl+c", "q":
return m, tea.Quit
}
}
switch m.currentStep {
case StepCheckDocker:
return m.updateCheckDocker(msg)
case StepDockerInstall:
return m.updateDockerInstall(msg)
case StepIPQuestion:
return m.updateIPQuestion(msg)
case StepDone:
return m, tea.Quit
}
return m, nil
}
func (m Model) updateCheckDocker(msg tea.Msg) (tea.Model, tea.Cmd) {
if msg, ok := msg.(DockerCheckedMsg); ok {
m.dockerInstalled = msg.Installed
if msg.Installed {
m.currentStep = StepIPQuestion
} else {
m.currentStep = StepDockerInstall
m.cursor = 0 // reset cursor on enter
}
}
return m, nil
}
func (m Model) updateDockerInstall(msg tea.Msg) (tea.Model, tea.Cmd) {
const numOptions = 2
switch msg := msg.(type) {
case tea.KeyPressMsg:
switch msg.String() {
case "up", "k":
if m.cursor > 0 {
m.cursor--
}
case "down", "j":
if m.cursor < numOptions-1 {
m.cursor++
}
case "enter":
switch m.cursor {
case 0:
return m, InstallDockerCmd()
case 1:
return m, tea.Quit
}
}
case DockerInstalledMsg:
if msg.Err != nil {
m.err = msg.Err
} else {
m.currentStep = StepIPQuestion
}
}
return m, nil
}
func (m Model) updateIPQuestion(msg tea.Msg) (tea.Model, tea.Cmd) {
return m, nil
}
+65
View File
@@ -0,0 +1,65 @@
package tui
import (
"fmt"
"strings"
tea "charm.land/bubbletea/v2"
)
const header = "App do Dono - Instalador Cliente"
func (m Model) View() tea.View {
pad := strings.Repeat(" ", padding)
var body string
switch m.currentStep {
case StepCheckDocker:
body = m.viewCheckDocker()
case StepDockerInstall:
body = m.viewDockerInstall()
case StepIPQuestion:
body = m.viewIPQuestion()
}
help := HelpStyle.Render("Pressione q ou ctrl+c para sair")
return tea.NewView(fmt.Sprintf("\n%s%s\n\n%s\n\n%s%s\n",
pad, TitleStyle.Render(header),
body,
pad, help,
))
}
func (m Model) viewCheckDocker() string {
pad := strings.Repeat(" ", padding)
return pad + "Avaliando instalação do Docker. Aguarde..."
}
func (m Model) viewDockerInstall() string {
pad := strings.Repeat(" ", padding)
options := []string{"Instalar automaticamente", "Instalar manualmente"}
var sb strings.Builder
sb.WriteString(pad + "Nenhuma versão do Docker encontrada.\n")
sb.WriteString(pad + "Deseja:\n\n")
for i, opt := range options {
cursor := " "
if m.cursor == i {
cursor = CursorStyle.Render("> ")
}
sb.WriteString(pad + cursor + opt + "\n")
}
if m.err != nil {
sb.WriteString("\n" + pad + ErrorStyle.Render("Erro: "+m.err.Error()))
}
return sb.String()
}
func (m Model) viewIPQuestion() string {
pad := strings.Repeat(" ", padding)
return pad + "Existe IP público disponível para a máquina?"
}