main 6af0a61

installation.go RawHistoryFolder

1package models
2
3import (
4	"fmt"
5	"strings"
6	"time"
7
8	"github.com/bnema/archup/internal/application/dto"
9	tea "github.com/charmbracelet/bubbletea"
10	"github.com/charmbracelet/lipgloss"
11)
12
13// InstallationModelImpl implements InstallationModel interface
14type InstallationModelImpl struct {
15	BaseModel
16
17	status   *dto.InstallationStatus
18	err      string
19	notice   string
20	complete bool
21}
22
23// NewInstallationModel creates a new installation model
24func NewInstallationModel() *InstallationModelImpl {
25	return &InstallationModelImpl{
26		status:   &dto.InstallationStatus{},
27		err:      "",
28		notice:   "",
29		complete: false,
30	}
31}
32
33// Init initializes the model
34func (im *InstallationModelImpl) Init() tea.Cmd {
35	return nil
36}
37
38// Update handles messages
39func (im *InstallationModelImpl) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
40	switch msg := msg.(type) {
41	case tea.KeyMsg:
42		if msg.String() == "q" || msg.String() == "ctrl+c" {
43			return im, tea.Quit
44		}
45	}
46	return im, nil
47}
48
49// View renders the installation status or summary
50// Deprecated: Use views.RenderSummary instead
51func (im *InstallationModelImpl) View() string {
52	if im.err != "" {
53		return im.ViewError()
54	}
55
56	if im.complete {
57		return im.renderSummary()
58	}
59
60	return im.renderStatus()
61}
62
63// ViewError renders the error screen
64// Deprecated: Use views.RenderError instead
65func (im *InstallationModelImpl) ViewError() string {
66	var b strings.Builder
67
68	b.WriteString("\n")
69	b.WriteString(lipgloss.NewStyle().
70		Bold(true).
71		Foreground(lipgloss.Color("1")).
72		Render("Installation Failed"))
73	b.WriteString("\n\n")
74
75	b.WriteString(lipgloss.NewStyle().
76		Foreground(lipgloss.Color("1")).
77		Render(im.err))
78	b.WriteString("\n\n")
79
80	b.WriteString(lipgloss.NewStyle().
81		Faint(true).
82		Render("Press 'q' or Ctrl+C to exit"))
83
84	return b.String()
85}
86
87// SetError sets an error message
88func (im *InstallationModelImpl) SetError(message string) {
89	im.err = message
90}
91
92func (im *InstallationModelImpl) SetNotice(message string) {
93	im.notice = message
94}
95
96// SetComplete marks installation as complete
97func (im *InstallationModelImpl) SetComplete() {
98	im.complete = true
99}
100
101// SetStatus sets the installation status
102func (im *InstallationModelImpl) SetStatus(status *dto.InstallationStatus) {
103	im.status = status
104}
105
106// GetError returns the error message if any
107func (im *InstallationModelImpl) GetError() string {
108	return im.err
109}
110
111func (im *InstallationModelImpl) GetNotice() string {
112	return im.notice
113}
114
115// IsComplete returns whether the installation is complete
116func (im *InstallationModelImpl) IsComplete() bool {
117	return im.complete
118}
119
120// GetStatus returns the installation status
121func (im *InstallationModelImpl) GetStatus() *dto.InstallationStatus {
122	return im.status
123}
124
125// Helper methods
126
127func (im *InstallationModelImpl) renderStatus() string {
128	var b strings.Builder
129
130	b.WriteString("\n")
131	b.WriteString(lipgloss.NewStyle().
132		Bold(true).
133		Foreground(lipgloss.Color("12")).
134		Render("Installation Status"))
135	b.WriteString("\n\n")
136
137	// Current phase
138	if im.status.CurrentPhase != "" {
139		b.WriteString("Phase: ")
140		b.WriteString(lipgloss.NewStyle().
141			Foreground(lipgloss.Color("11")).
142			Render(im.status.CurrentPhase))
143		b.WriteString("\n")
144	}
145
146	// Progress
147	if im.status.Progress > 0 {
148		b.WriteString(fmt.Sprintf("Progress: %d%%\n", im.status.Progress))
149	}
150
151	// Status
152	if im.status.State != "" {
153		b.WriteString(fmt.Sprintf("State: %s\n", im.status.State))
154	}
155
156	b.WriteString("\n")
157	b.WriteString(lipgloss.NewStyle().
158		Faint(true).
159		Render("Installation in progress... Press Ctrl+C to cancel"))
160
161	return b.String()
162}
163
164func (im *InstallationModelImpl) renderSummary() string {
165	var b strings.Builder
166
167	b.WriteString("\n")
168	b.WriteString(lipgloss.NewStyle().
169		Bold(true).
170		Foreground(lipgloss.Color("10")).
171		Render("Installation Complete!"))
172	b.WriteString("\n\n")
173
174	// System information
175	if im.status.Hostname != "" {
176		b.WriteString("Hostname: ")
177		b.WriteString(lipgloss.NewStyle().
178			Foreground(lipgloss.Color("11")).
179			Render(im.status.Hostname))
180		b.WriteString("\n")
181	}
182
183	if im.status.Username != "" {
184		b.WriteString("User: ")
185		b.WriteString(lipgloss.NewStyle().
186			Foreground(lipgloss.Color("11")).
187			Render(im.status.Username))
188		b.WriteString("\n")
189	}
190
191	if im.status.TargetDisk != "" {
192		b.WriteString("Target Disk: ")
193		b.WriteString(lipgloss.NewStyle().
194			Foreground(lipgloss.Color("11")).
195			Render(im.status.TargetDisk))
196		b.WriteString("\n")
197	}
198
199	// Duration
200	if im.status.StartedAt != nil && im.status.CompletedAt != nil {
201		duration := im.status.CompletedAt.Sub(*im.status.StartedAt)
202		b.WriteString(fmt.Sprintf("Duration: %s\n", formatDuration(duration)))
203	}
204
205	b.WriteString("\n")
206	b.WriteString(lipgloss.NewStyle().
207		Faint(true).
208		Render("Press 'r' to unmount and reboot, or 'q' to exit"))
209
210	return b.String()
211}
212
213func formatDuration(d time.Duration) string {
214	hours := int(d.Hours())
215	minutes := int(d.Minutes()) % 60
216	seconds := int(d.Seconds()) % 60
217
218	if hours > 0 {
219		return fmt.Sprintf("%dh %dm %ds", hours, minutes, seconds)
220	}
221	if minutes > 0 {
222		return fmt.Sprintf("%dm %ds", minutes, seconds)
223	}
224	return fmt.Sprintf("%ds", seconds)
225}

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 1

Tags 0

    ↑↓ moveenter openesc close