-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathui.go
More file actions
123 lines (104 loc) · 2.93 KB
/
Copy pathui.go
File metadata and controls
123 lines (104 loc) · 2.93 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
package matcha
import (
"fmt"
"strings"
"time"
)
// Terminal formatting helpers
func bold(s string) string { return "\033[1m" + s + "\033[0m" }
func dim(s string) string { return "\033[2m" + s + "\033[0m" }
func green(s string) string { return "\033[32m" + s + "\033[0m" }
func cyan(s string) string { return "\033[36m" + s + "\033[0m" }
var spinnerFrames = []string{"⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"}
// Spinner handles animated progress display.
type Spinner struct {
name string
stopCh chan struct{}
doneCh chan struct{}
}
// StartSpinner creates and starts an animated spinner for a step.
func (m *Matcha) StartSpinner(name string) *Spinner {
s := &Spinner{
name: name,
stopCh: make(chan struct{}),
doneCh: make(chan struct{}),
}
// Pad name to 16 chars
paddedName := name
for len(paddedName) < 16 {
paddedName += " "
}
go func() {
ticker := time.NewTicker(80 * time.Millisecond)
defer ticker.Stop()
defer close(s.doneCh)
idx := 0
for {
select {
case <-s.stopCh:
return
case <-ticker.C:
fmt.Printf("\r\033[K %s%s", paddedName, dim(spinnerFrames[idx]))
idx = (idx + 1) % len(spinnerFrames)
}
}
}()
return s
}
// Stop stops the spinner and shows success or failure.
func (s *Spinner) Stop(success bool) {
close(s.stopCh)
<-s.doneCh
paddedName := s.name
for len(paddedName) < 16 {
paddedName += " "
}
fmt.Print("\r\033[K")
if success {
fmt.Printf(" %s%s\n", paddedName, green("✓"))
} else {
fmt.Printf(" %s%s\n", paddedName, "✗")
}
}
// printWelcome prints the installer welcome message.
func (m *Matcha) printWelcome() {
fmt.Println()
// Capitalize first letter for display
title := m.config.Name
if len(title) > 0 {
title = strings.ToUpper(title[:1]) + title[1:]
}
fmt.Println(bold(title + " Installer"))
fmt.Println()
fmt.Println(dim("* Ports 80 and 443 must be available"))
fmt.Println(dim("* DNS pointing to this server recommended for SSL"))
fmt.Println()
}
// printComplete prints the completion message.
func (m *Matcha) printComplete(domain string, dnsWarning bool, serverIP string) {
fmt.Println()
fmt.Println(bold("Done."))
fmt.Println()
if dnsWarning {
fmt.Printf("%s Point %s to %s\n", dim("DNS not configured."), domain, serverIP)
fmt.Println(dim("SSL activates once DNS propagates."))
fmt.Println()
}
fmt.Printf("Visit %s to create your account.\n", cyan("https://"+domain))
fmt.Println()
fmt.Printf("Run %s to check your server security.\n", bold(m.config.Name+" check"))
}
// printHeader prints a bold header.
func printHeader(text string) {
fmt.Println()
fmt.Println(bold(text))
fmt.Println()
}
// printSuccess prints a success message.
func printSuccess(format string, args ...any) {
fmt.Printf("%s %s\n", green("✓"), fmt.Sprintf(format, args...))
}
// printWarn prints a warning message.
func printWarn(format string, args ...any) {
fmt.Printf(" %s %s\n", dim("!"), fmt.Sprintf(format, args...))
}