batman
This commit is contained in:
+17
@@ -0,0 +1,17 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
tea "charm.land/bubbletea/v2"
|
||||||
|
"git.davinti.com.br/davinTI/app-dono/tui/tui"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
p := tea.NewProgram(tui.InitialModel())
|
||||||
|
if _, err := p.Run(); err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "error: %v\n", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
module git.davinti.com.br/davinTI/app-dono/tui
|
||||||
|
|
||||||
|
go 1.25.0
|
||||||
|
|
||||||
|
require (
|
||||||
|
charm.land/bubbles/v2 v2.0.0
|
||||||
|
charm.land/bubbletea/v2 v2.0.1
|
||||||
|
)
|
||||||
|
|
||||||
|
require (
|
||||||
|
charm.land/lipgloss/v2 v2.0.0 // indirect
|
||||||
|
github.com/atotto/clipboard v0.1.4 // indirect
|
||||||
|
github.com/charmbracelet/colorprofile v0.4.2 // indirect
|
||||||
|
github.com/charmbracelet/ultraviolet v0.0.0-20260205113103-524a6607adb8 // indirect
|
||||||
|
github.com/charmbracelet/x/ansi v0.11.6 // indirect
|
||||||
|
github.com/charmbracelet/x/term v0.2.2 // indirect
|
||||||
|
github.com/charmbracelet/x/termios v0.1.1 // indirect
|
||||||
|
github.com/charmbracelet/x/windows v0.2.2 // indirect
|
||||||
|
github.com/clipperhouse/displaywidth v0.11.0 // indirect
|
||||||
|
github.com/clipperhouse/uax29/v2 v2.7.0 // indirect
|
||||||
|
github.com/lucasb-eyer/go-colorful v1.3.0 // indirect
|
||||||
|
github.com/mattn/go-runewidth v0.0.20 // indirect
|
||||||
|
github.com/muesli/cancelreader v0.2.2 // indirect
|
||||||
|
github.com/rivo/uniseg v0.4.7 // indirect
|
||||||
|
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
|
||||||
|
golang.org/x/sync v0.19.0 // indirect
|
||||||
|
golang.org/x/sys v0.41.0 // indirect
|
||||||
|
)
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
charm.land/bubbles/v2 v2.0.0 h1:tE3eK/pHjmtrDiRdoC9uGNLgpopOd8fjhEe31B/ai5s=
|
||||||
|
charm.land/bubbles/v2 v2.0.0/go.mod h1:rCHoleP2XhU8um45NTuOWBPNVHxnkXKTiZqcclL/qOI=
|
||||||
|
charm.land/bubbletea/v2 v2.0.1 h1:B8e9zzK7x9JJ+XvHGF4xnYu9Xa0E0y0MyggY6dbaCfQ=
|
||||||
|
charm.land/bubbletea/v2 v2.0.1/go.mod h1:3LRff2U4WIYXy7MTxfbAQ+AdfM3D8Xuvz2wbsOD9OHQ=
|
||||||
|
charm.land/lipgloss/v2 v2.0.0 h1:sd8N/B3x892oiOjFfBQdXBQp3cAkvjGaU5TvVZC3ivo=
|
||||||
|
charm.land/lipgloss/v2 v2.0.0/go.mod h1:w6SnmsBFBmEFBodiEDurGS/sdUY/u1+v72DqUzc6J14=
|
||||||
|
github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4=
|
||||||
|
github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI=
|
||||||
|
github.com/aymanbagabas/go-udiff v0.4.0 h1:TKnLPh7IbnizJIBKFWa9mKayRUBQ9Kh1BPCk6w2PnYM=
|
||||||
|
github.com/aymanbagabas/go-udiff v0.4.0/go.mod h1:0L9PGwj20lrtmEMeyw4WKJ/TMyDtvAoK9bf2u/mNo3w=
|
||||||
|
github.com/charmbracelet/colorprofile v0.4.2 h1:BdSNuMjRbotnxHSfxy+PCSa4xAmz7szw70ktAtWRYrY=
|
||||||
|
github.com/charmbracelet/colorprofile v0.4.2/go.mod h1:0rTi81QpwDElInthtrQ6Ni7cG0sDtwAd4C4le060fT8=
|
||||||
|
github.com/charmbracelet/ultraviolet v0.0.0-20260205113103-524a6607adb8 h1:eyFRbAmexyt43hVfeyBofiGSEmJ7krjLOYt/9CF5NKA=
|
||||||
|
github.com/charmbracelet/ultraviolet v0.0.0-20260205113103-524a6607adb8/go.mod h1:SQpCTRNBtzJkwku5ye4S3HEuthAlGy2n9VXZnWkEW98=
|
||||||
|
github.com/charmbracelet/x/ansi v0.11.6 h1:GhV21SiDz/45W9AnV2R61xZMRri5NlLnl6CVF7ihZW8=
|
||||||
|
github.com/charmbracelet/x/ansi v0.11.6/go.mod h1:2JNYLgQUsyqaiLovhU2Rv/pb8r6ydXKS3NIttu3VGZQ=
|
||||||
|
github.com/charmbracelet/x/exp/golden v0.0.0-20250806222409-83e3a29d542f h1:pk6gmGpCE7F3FcjaOEKYriCvpmIN4+6OS/RD0vm4uIA=
|
||||||
|
github.com/charmbracelet/x/exp/golden v0.0.0-20250806222409-83e3a29d542f/go.mod h1:IfZAMTHB6XkZSeXUqriemErjAWCCzT0LwjKFYCZyw0I=
|
||||||
|
github.com/charmbracelet/x/term v0.2.2 h1:xVRT/S2ZcKdhhOuSP4t5cLi5o+JxklsoEObBSgfgZRk=
|
||||||
|
github.com/charmbracelet/x/term v0.2.2/go.mod h1:kF8CY5RddLWrsgVwpw4kAa6TESp6EB5y3uxGLeCqzAI=
|
||||||
|
github.com/charmbracelet/x/termios v0.1.1 h1:o3Q2bT8eqzGnGPOYheoYS8eEleT5ZVNYNy8JawjaNZY=
|
||||||
|
github.com/charmbracelet/x/termios v0.1.1/go.mod h1:rB7fnv1TgOPOyyKRJ9o+AsTU/vK5WHJ2ivHeut/Pcwo=
|
||||||
|
github.com/charmbracelet/x/windows v0.2.2 h1:IofanmuvaxnKHuV04sC0eBy/smG6kIKrWG2/jYn2GuM=
|
||||||
|
github.com/charmbracelet/x/windows v0.2.2/go.mod h1:/8XtdKZzedat74NQFn0NGlGL4soHB0YQZrETF96h75k=
|
||||||
|
github.com/clipperhouse/displaywidth v0.11.0 h1:lBc6kY44VFw+TDx4I8opi/EtL9m20WSEFgwIwO+UVM8=
|
||||||
|
github.com/clipperhouse/displaywidth v0.11.0/go.mod h1:bkrFNkf81G8HyVqmKGxsPufD3JhNl3dSqnGhOoSD/o0=
|
||||||
|
github.com/clipperhouse/uax29/v2 v2.7.0 h1:+gs4oBZ2gPfVrKPthwbMzWZDaAFPGYK72F0NJv2v7Vk=
|
||||||
|
github.com/clipperhouse/uax29/v2 v2.7.0/go.mod h1:EFJ2TJMRUaplDxHKj1qAEhCtQPW2tJSwu5BF98AuoVM=
|
||||||
|
github.com/lucasb-eyer/go-colorful v1.3.0 h1:2/yBRLdWBZKrf7gB40FoiKfAWYQ0lqNcbuQwVHXptag=
|
||||||
|
github.com/lucasb-eyer/go-colorful v1.3.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
|
||||||
|
github.com/mattn/go-runewidth v0.0.20 h1:WcT52H91ZUAwy8+HUkdM3THM6gXqXuLJi9O3rjcQQaQ=
|
||||||
|
github.com/mattn/go-runewidth v0.0.20/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs=
|
||||||
|
github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA=
|
||||||
|
github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo=
|
||||||
|
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
|
||||||
|
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
||||||
|
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no=
|
||||||
|
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM=
|
||||||
|
golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI=
|
||||||
|
golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo=
|
||||||
|
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
|
||||||
|
golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||||
|
golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k=
|
||||||
|
golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||||
+28
@@ -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}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
package tui
|
||||||
@@ -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()
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
package tui
|
||||||
|
|
||||||
|
type step int
|
||||||
|
|
||||||
|
const (
|
||||||
|
StepCheckDocker step = iota
|
||||||
|
StepDockerInstall
|
||||||
|
StepIPQuestion
|
||||||
|
StepInstallWireguard
|
||||||
|
StepTextInputs
|
||||||
|
StepDone
|
||||||
|
)
|
||||||
@@ -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"))
|
||||||
|
)
|
||||||
@@ -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
@@ -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?"
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user