feat(ux): installer title with version, hostname default 'arch', black bg, drop limine wallpaper
- installer form title shows 'ArchUp Installer vX.X' from ldflags version - hostname default changed from 'myarch' to 'arch' - username starts empty (required, no default) - limine.conf: add backdrop 000000, remove wallpaper injection - postinstall: remove installBootLogo step entirely - plymouth: background set to pure black to match limine backdrop - archup-first-boot.service: remove TTY stdin to fix SIGHUP on sudo switch - all.sh: fix blesh pipe exit code bug
- commit
- fc93cf538f0a5e62a1db5927509592bb30768352
- author
- brice · · 2026-02-28 04:34 UTC
- parent
- 82a916a
- signature
- GPG Verified
CD88F9E564E0102646FB6077A1A07C7C7C3E349D· arch-gpg Verified for the committer email
8 files · +32 -92
M assets/plymouth/archup.script +2 -2
| @@ -1,7 +1,7 @@ | |||
| 1 | 1 | # Arch Linux Plymouth Theme Script | |
| 2 | 2 | ||
| 3 | - | Window.SetBackgroundTopColor(0.020, 0.039, 0.078); | |
| 4 | - | Window.SetBackgroundBottomColor(0.020, 0.039, 0.078); | |
| 3 | + | Window.SetBackgroundTopColor(0, 0, 0); | |
| 4 | + | Window.SetBackgroundBottomColor(0, 0, 0); | |
| 5 | 5 | ||
| 6 | 6 | logo.image = Image("logo.png"); | |
| 7 | 7 | logo.sprite = Sprite(logo.image); | |
| @@ -1,7 +1,7 @@ | |||||
| 1 | # Arch Linux Plymouth Theme Script | 1 | # Arch Linux Plymouth Theme Script | ||
| 2 | 2 | ||||
| 3 | - | Window.SetBackgroundTopColor(0.020, 0.039, 0.078); | 3 | + | Window.SetBackgroundTopColor(0, 0, 0); |
| 4 | - | Window.SetBackgroundBottomColor(0.020, 0.039, 0.078); | 4 | + | Window.SetBackgroundBottomColor(0, 0, 0); |
| 5 | 5 | ||||
| 6 | logo.image = Image("logo.png"); | 6 | logo.image = Image("logo.png"); | ||
| 7 | logo.sprite = Sprite(logo.image); | 7 | logo.sprite = Sprite(logo.image); | ||
M install/configs/limine.conf.template +12 -15
| @@ -1,21 +1,18 @@ | |||
| 1 | 1 | timeout: {{TIMEOUT}} | |
| 2 | - | default_entry: 2 | |
| 2 | + | default_entry: 1 | |
| 3 | 3 | interface_branding: {{BRANDING}} | |
| 4 | 4 | interface_branding_colour: {{COLOR}} | |
| 5 | 5 | graphics: yes | |
| 6 | + | backdrop: 000000 | |
| 6 | 7 | ||
| 7 | - | /{{BRANDING}} | |
| 8 | + | /{{KERNEL}} | |
| 9 | + | protocol: linux | |
| 10 | + | kernel_path: boot():/vmlinuz-{{KERNEL}} | |
| 11 | + | kernel_cmdline: {{KERNEL_PARAMS}} | |
| 12 | + | module_path: boot():/initramfs-{{KERNEL}}.img | |
| 8 | 13 | ||
| 9 | - | //{{KERNEL}} | |
| 10 | - | protocol: linux | |
| 11 | - | kernel_path: boot():/vmlinuz-{{KERNEL}} | |
| 12 | - | kernel_cmdline: {{KERNEL_PARAMS}} | |
| 13 | - | module_path: boot():/initramfs-{{KERNEL}}.img | |
| 14 | - | ||
| 15 | - | //{{KERNEL}}-fallback | |
| 16 | - | protocol: linux | |
| 17 | - | kernel_path: boot():/vmlinuz-{{KERNEL}} | |
| 18 | - | kernel_cmdline: {{KERNEL_PARAMS}} | |
| 19 | - | module_path: boot():/initramfs-{{KERNEL}}-fallback.img | |
| 20 | - | ||
| 21 | - | //Snapshots | |
| 14 | + | /{{KERNEL}}-fallback | |
| 15 | + | protocol: linux | |
| 16 | + | kernel_path: boot():/vmlinuz-{{KERNEL}} | |
| 17 | + | kernel_cmdline: {{KERNEL_PARAMS}} | |
| 18 | + | module_path: boot():/initramfs-{{KERNEL}}-fallback.img | |
| @@ -1,21 +1,18 @@ | |||||
| 1 | timeout: {{TIMEOUT}} | 1 | timeout: {{TIMEOUT}} | ||
| 2 | - | default_entry: 2 | 2 | + | default_entry: 1 |
| 3 | interface_branding: {{BRANDING}} | 3 | interface_branding: {{BRANDING}} | ||
| 4 | interface_branding_colour: {{COLOR}} | 4 | interface_branding_colour: {{COLOR}} | ||
| 5 | graphics: yes | 5 | graphics: yes | ||
| 6 | + | backdrop: 000000 | |||
| 6 | 7 | ||||
| 7 | - | /{{BRANDING}} | 8 | + | /{{KERNEL}} |
| 9 | + | protocol: linux | |||
| 10 | + | kernel_path: boot():/vmlinuz-{{KERNEL}} | |||
| 11 | + | kernel_cmdline: {{KERNEL_PARAMS}} | |||
| 12 | + | module_path: boot():/initramfs-{{KERNEL}}.img | |||
| 8 | 13 | ||||
| 9 | - | //{{KERNEL}} | 14 | + | /{{KERNEL}}-fallback |
| 10 | - | protocol: linux | 15 | + | protocol: linux |
| 11 | - | kernel_path: boot():/vmlinuz-{{KERNEL}} | 16 | + | kernel_path: boot():/vmlinuz-{{KERNEL}} |
| 12 | - | kernel_cmdline: {{KERNEL_PARAMS}} | 17 | + | kernel_cmdline: {{KERNEL_PARAMS}} |
| 13 | - | module_path: boot():/initramfs-{{KERNEL}}.img | 18 | + | module_path: boot():/initramfs-{{KERNEL}}-fallback.img |
| 14 | - | ||||
| 15 | - | //{{KERNEL}}-fallback | |||
| 16 | - | protocol: linux | |||
| 17 | - | kernel_path: boot():/vmlinuz-{{KERNEL}} | |||
| 18 | - | kernel_cmdline: {{KERNEL_PARAMS}} | |||
| 19 | - | module_path: boot():/initramfs-{{KERNEL}}-fallback.img | |||
| 20 | - | ||||
| 21 | - | //Snapshots | |||
M install/mandatory/post-boot/all.sh +4 -11
| @@ -53,18 +53,11 @@ fi | |||
| 53 | 53 | ||
| 54 | 54 | # Install ble.sh (Bash Line Editor) | |
| 55 | 55 | if [ -f /usr/local/share/archup/post-boot/blesh.sh ]; then | |
| 56 | - | echo "Installing ble.sh (Bash Line Editor)..." | tee -a "$LOG_FILE" | |
| 57 | - | BLESH_OUTPUT=$(bash /usr/local/share/archup/post-boot/blesh.sh 2>&1 | tee -a "$LOG_FILE") | |
| 58 | - | if [ $? -eq 0 ]; then | |
| 59 | - | echo "[OK] ble.sh installed successfully" | tee -a "$LOG_FILE" | |
| 60 | - | # Check if it was added to bashrc | |
| 61 | - | if echo "$BLESH_OUTPUT" | grep -q "ble.sh added to .bashrc"; then | |
| 62 | - | echo "[OK] ble.sh configured in ~/.bashrc" | tee -a "$LOG_FILE" | |
| 63 | - | elif echo "$BLESH_OUTPUT" | grep -q "ble.sh already configured"; then | |
| 64 | - | echo "[OK] ble.sh already present in ~/.bashrc" | tee -a "$LOG_FILE" | |
| 65 | - | fi | |
| 56 | + | echo "Installing ble.sh (Bash Line Editor)..." >> "$LOG_FILE" | |
| 57 | + | if bash /usr/local/share/archup/post-boot/blesh.sh >> "$LOG_FILE" 2>&1; then | |
| 58 | + | echo "[OK] ble.sh installed successfully" >> "$LOG_FILE" | |
| 66 | 59 | else | |
| 67 | - | echo "[KO] ble.sh installation failed (non-critical)" | tee -a "$LOG_FILE" | |
| 60 | + | echo "[KO] ble.sh installation failed (non-critical)" >> "$LOG_FILE" | |
| 68 | 61 | fi | |
| 69 | 62 | fi | |
| 70 | 63 | ||
| @@ -53,18 +53,11 @@ fi | |||||
| 53 | 53 | ||||
| 54 | # Install ble.sh (Bash Line Editor) | 54 | # Install ble.sh (Bash Line Editor) | ||
| 55 | if [ -f /usr/local/share/archup/post-boot/blesh.sh ]; then | 55 | if [ -f /usr/local/share/archup/post-boot/blesh.sh ]; then | ||
| 56 | - | echo "Installing ble.sh (Bash Line Editor)..." | tee -a "$LOG_FILE" | 56 | + | echo "Installing ble.sh (Bash Line Editor)..." >> "$LOG_FILE" |
| 57 | - | BLESH_OUTPUT=$(bash /usr/local/share/archup/post-boot/blesh.sh 2>&1 | tee -a "$LOG_FILE") | 57 | + | if bash /usr/local/share/archup/post-boot/blesh.sh >> "$LOG_FILE" 2>&1; then |
| 58 | - | if [ $? -eq 0 ]; then | 58 | + | echo "[OK] ble.sh installed successfully" >> "$LOG_FILE" |
| 59 | - | echo "[OK] ble.sh installed successfully" | tee -a "$LOG_FILE" | |||
| 60 | - | # Check if it was added to bashrc | |||
| 61 | - | if echo "$BLESH_OUTPUT" | grep -q "ble.sh added to .bashrc"; then | |||
| 62 | - | echo "[OK] ble.sh configured in ~/.bashrc" | tee -a "$LOG_FILE" | |||
| 63 | - | elif echo "$BLESH_OUTPUT" | grep -q "ble.sh already configured"; then | |||
| 64 | - | echo "[OK] ble.sh already present in ~/.bashrc" | tee -a "$LOG_FILE" | |||
| 65 | - | fi | |||
| 66 | else | 59 | else | ||
| 67 | - | echo "[KO] ble.sh installation failed (non-critical)" | tee -a "$LOG_FILE" | 60 | + | echo "[KO] ble.sh installation failed (non-critical)" >> "$LOG_FILE" |
| 68 | fi | 61 | fi | ||
| 69 | fi | 62 | fi | ||
| 70 | 63 | ||||
M install/mandatory/post-boot/archup-first-boot.service +1 -3
| @@ -10,9 +10,7 @@ Environment="ARCHUP_EMAIL=__ARCHUP_EMAIL__" | |||
| 10 | 10 | Environment="ARCHUP_USERNAME=__ARCHUP_USERNAME__" | |
| 11 | 11 | ExecStart=/usr/local/share/archup/post-boot/all.sh | |
| 12 | 12 | ExecStartPost=/usr/bin/touch /var/lib/archup-first-boot-complete | |
| 13 | - | TTYPath=/dev/tty1 | |
| 14 | - | StandardInput=tty | |
| 15 | - | StandardOutput=tty | |
| 13 | + | StandardOutput=journal+console | |
| 16 | 14 | StandardError=journal+console | |
| 17 | 15 | ||
| 18 | 16 | [Install] | |
| @@ -10,9 +10,7 @@ Environment="ARCHUP_EMAIL=__ARCHUP_EMAIL__" | |||||
| 10 | Environment="ARCHUP_USERNAME=__ARCHUP_USERNAME__" | 10 | Environment="ARCHUP_USERNAME=__ARCHUP_USERNAME__" | ||
| 11 | ExecStart=/usr/local/share/archup/post-boot/all.sh | 11 | ExecStart=/usr/local/share/archup/post-boot/all.sh | ||
| 12 | ExecStartPost=/usr/bin/touch /var/lib/archup-first-boot-complete | 12 | ExecStartPost=/usr/bin/touch /var/lib/archup-first-boot-complete | ||
| 13 | - | TTYPath=/dev/tty1 | 13 | + | StandardOutput=journal+console |
| 14 | - | StandardInput=tty | |||
| 15 | - | StandardOutput=tty | |||
| 16 | StandardError=journal+console | 14 | StandardError=journal+console | ||
| 17 | 15 | ||||
| 18 | [Install] | 16 | [Install] | ||
M internal/config/config.go +1 -0
| @@ -272,6 +272,7 @@ func NewConfig(version string) *Config { | |||
| 272 | 272 | } | |
| 273 | 273 | ||
| 274 | 274 | return &Config{ | |
| 275 | + | Hostname: "arch", | |
| 275 | 276 | Locale: "en_US.UTF-8", | |
| 276 | 277 | Timezone: "UTC", | |
| 277 | 278 | Keymap: "us", | |
| @@ -272,6 +272,7 @@ func NewConfig(version string) *Config { | |||||
| 272 | } | 272 | } | ||
| 273 | 273 | ||||
| 274 | return &Config{ | 274 | return &Config{ | ||
| 275 | + | Hostname: "arch", | |||
| 275 | Locale: "en_US.UTF-8", | 276 | Locale: "en_US.UTF-8", | ||
| 276 | Timezone: "UTC", | 277 | Timezone: "UTC", | ||
| 277 | Keymap: "us", | 278 | Keymap: "us", | ||
M internal/phases/postinstall.go +2 -56
| @@ -79,19 +79,10 @@ func (p *PostInstallPhase) PreCheck() error { | |||
| 79 | 79 | ||
| 80 | 80 | // Execute runs the post-install phase | |
| 81 | 81 | func (p *PostInstallPhase) Execute(progressChan chan<- ProgressUpdate) PhaseResult { | |
| 82 | - | totalSteps := 9 | |
| 82 | + | totalSteps := 8 | |
| 83 | 83 | currentStep := 0 | |
| 84 | 84 | ||
| 85 | - | // Step 1: Boot logo | |
| 86 | - | currentStep++ | |
| 87 | - | p.SendProgress(progressChan, "Installing boot logo...", currentStep, totalSteps) | |
| 88 | - | switch err := p.installBootLogo(progressChan); { | |
| 89 | - | case err != nil: | |
| 90 | - | // Non-fatal, continue | |
| 91 | - | p.SendOutput(progressChan, fmt.Sprintf("[WARN] Boot logo failed: %v", err)) | |
| 92 | - | } | |
| 93 | - | ||
| 94 | - | // Step 2: Plymouth | |
| 85 | + | // Step 1: Plymouth | |
| 95 | 86 | currentStep++ | |
| 96 | 87 | p.SendProgress(progressChan, "Configuring Plymouth splash screen...", currentStep, totalSteps) | |
| 97 | 88 | switch err := p.configurePlymouth(progressChan); { | |
| @@ -166,51 +157,6 @@ func (p *PostInstallPhase) Execute(progressChan chan<- ProgressUpdate) PhaseResu | |||
| 166 | 157 | return PhaseResult{Success: true, Message: "Post-installation complete"} | |
| 167 | 158 | } | |
| 168 | 159 | ||
| 169 | - | // installBootLogo downloads and configures Limine boot logo | |
| 170 | - | func (p *PostInstallPhase) installBootLogo(progressChan chan<- ProgressUpdate) error { | |
| 171 | - | p.SendOutput(progressChan, "Downloading Arch logo...") | |
| 172 | - | ||
| 173 | - | data, err := p.fetchFile(config.ArchLogoURL) | |
| 174 | - | if err != nil { | |
| 175 | - | return fmt.Errorf("failed to fetch logo: %w", err) | |
| 176 | - | } | |
| 177 | - | ||
| 178 | - | if err := p.fs.WriteFile(config.PathMntBootLogo, data, 0644); err != nil { | |
| 179 | - | return fmt.Errorf("failed to write logo: %w", err) | |
| 180 | - | } | |
| 181 | - | ||
| 182 | - | p.SendOutput(progressChan, "[OK] Logo downloaded") | |
| 183 | - | ||
| 184 | - | // Update Limine config | |
| 185 | - | limineConf := config.PathMntBootLimineConf | |
| 186 | - | content, err := p.fs.ReadFile(limineConf) | |
| 187 | - | switch { | |
| 188 | - | case err != nil: | |
| 189 | - | return fmt.Errorf("failed to read limine.conf: %w", err) | |
| 190 | - | } | |
| 191 | - | ||
| 192 | - | contentStr := string(content) | |
| 193 | - | ||
| 194 | - | // Find "graphics: yes" and add wallpaper settings after it | |
| 195 | - | graphicsRegex := regexp.MustCompile(`(?m)^graphics: yes$`) | |
| 196 | - | switch { | |
| 197 | - | case graphicsRegex.MatchString(contentStr): | |
| 198 | - | wallpaperSettings := "\nwallpaper: boot():/arch-logo.png\nwallpaper_style: centered\nbackdrop: 000000" | |
| 199 | - | contentStr = graphicsRegex.ReplaceAllString(contentStr, "graphics: yes"+wallpaperSettings) | |
| 200 | - | ||
| 201 | - | switch err := p.fs.WriteFile(limineConf, []byte(contentStr), 0644); { | |
| 202 | - | case err != nil: | |
| 203 | - | return fmt.Errorf("failed to update limine.conf: %w", err) | |
| 204 | - | } | |
| 205 | - | ||
| 206 | - | p.SendOutput(progressChan, "[OK] Boot logo configured") | |
| 207 | - | default: | |
| 208 | - | p.SendOutput(progressChan, "[WARN] graphics: yes not found in limine.conf") | |
| 209 | - | } | |
| 210 | - | ||
| 211 | - | return nil | |
| 212 | - | } | |
| 213 | - | ||
| 214 | 160 | // configurePlymouth sets up Plymouth splash screen | |
| 215 | 161 | func (p *PostInstallPhase) configurePlymouth(progressChan chan<- ProgressUpdate) error { | |
| 216 | 162 | p.SendOutput(progressChan, "Setting up Plymouth theme...") | |
| @@ -79,19 +79,10 @@ func (p *PostInstallPhase) PreCheck() error { | |||||
| 79 | 79 | ||||
| 80 | // Execute runs the post-install phase | 80 | // Execute runs the post-install phase | ||
| 81 | func (p *PostInstallPhase) Execute(progressChan chan<- ProgressUpdate) PhaseResult { | 81 | func (p *PostInstallPhase) Execute(progressChan chan<- ProgressUpdate) PhaseResult { | ||
| 82 | - | totalSteps := 9 | 82 | + | totalSteps := 8 |
| 83 | currentStep := 0 | 83 | currentStep := 0 | ||
| 84 | 84 | ||||
| 85 | - | // Step 1: Boot logo | 85 | + | // Step 1: Plymouth |
| 86 | - | currentStep++ | |||
| 87 | - | p.SendProgress(progressChan, "Installing boot logo...", currentStep, totalSteps) | |||
| 88 | - | switch err := p.installBootLogo(progressChan); { | |||
| 89 | - | case err != nil: | |||
| 90 | - | // Non-fatal, continue | |||
| 91 | - | p.SendOutput(progressChan, fmt.Sprintf("[WARN] Boot logo failed: %v", err)) | |||
| 92 | - | } | |||
| 93 | - | ||||
| 94 | - | // Step 2: Plymouth | |||
| 95 | currentStep++ | 86 | currentStep++ | ||
| 96 | p.SendProgress(progressChan, "Configuring Plymouth splash screen...", currentStep, totalSteps) | 87 | p.SendProgress(progressChan, "Configuring Plymouth splash screen...", currentStep, totalSteps) | ||
| 97 | switch err := p.configurePlymouth(progressChan); { | 88 | switch err := p.configurePlymouth(progressChan); { | ||
| @@ -166,51 +157,6 @@ func (p *PostInstallPhase) Execute(progressChan chan<- ProgressUpdate) PhaseResu | |||||
| 166 | return PhaseResult{Success: true, Message: "Post-installation complete"} | 157 | return PhaseResult{Success: true, Message: "Post-installation complete"} | ||
| 167 | } | 158 | } | ||
| 168 | 159 | ||||
| 169 | - | // installBootLogo downloads and configures Limine boot logo | |||
| 170 | - | func (p *PostInstallPhase) installBootLogo(progressChan chan<- ProgressUpdate) error { | |||
| 171 | - | p.SendOutput(progressChan, "Downloading Arch logo...") | |||
| 172 | - | ||||
| 173 | - | data, err := p.fetchFile(config.ArchLogoURL) | |||
| 174 | - | if err != nil { | |||
| 175 | - | return fmt.Errorf("failed to fetch logo: %w", err) | |||
| 176 | - | } | |||
| 177 | - | ||||
| 178 | - | if err := p.fs.WriteFile(config.PathMntBootLogo, data, 0644); err != nil { | |||
| 179 | - | return fmt.Errorf("failed to write logo: %w", err) | |||
| 180 | - | } | |||
| 181 | - | ||||
| 182 | - | p.SendOutput(progressChan, "[OK] Logo downloaded") | |||
| 183 | - | ||||
| 184 | - | // Update Limine config | |||
| 185 | - | limineConf := config.PathMntBootLimineConf | |||
| 186 | - | content, err := p.fs.ReadFile(limineConf) | |||
| 187 | - | switch { | |||
| 188 | - | case err != nil: | |||
| 189 | - | return fmt.Errorf("failed to read limine.conf: %w", err) | |||
| 190 | - | } | |||
| 191 | - | ||||
| 192 | - | contentStr := string(content) | |||
| 193 | - | ||||
| 194 | - | // Find "graphics: yes" and add wallpaper settings after it | |||
| 195 | - | graphicsRegex := regexp.MustCompile(`(?m)^graphics: yes$`) | |||
| 196 | - | switch { | |||
| 197 | - | case graphicsRegex.MatchString(contentStr): | |||
| 198 | - | wallpaperSettings := "\nwallpaper: boot():/arch-logo.png\nwallpaper_style: centered\nbackdrop: 000000" | |||
| 199 | - | contentStr = graphicsRegex.ReplaceAllString(contentStr, "graphics: yes"+wallpaperSettings) | |||
| 200 | - | ||||
| 201 | - | switch err := p.fs.WriteFile(limineConf, []byte(contentStr), 0644); { | |||
| 202 | - | case err != nil: | |||
| 203 | - | return fmt.Errorf("failed to update limine.conf: %w", err) | |||
| 204 | - | } | |||
| 205 | - | ||||
| 206 | - | p.SendOutput(progressChan, "[OK] Boot logo configured") | |||
| 207 | - | default: | |||
| 208 | - | p.SendOutput(progressChan, "[WARN] graphics: yes not found in limine.conf") | |||
| 209 | - | } | |||
| 210 | - | ||||
| 211 | - | return nil | |||
| 212 | - | } | |||
| 213 | - | ||||
| 214 | // configurePlymouth sets up Plymouth splash screen | 160 | // configurePlymouth sets up Plymouth splash screen | ||
| 215 | func (p *PostInstallPhase) configurePlymouth(progressChan chan<- ProgressUpdate) error { | 161 | func (p *PostInstallPhase) configurePlymouth(progressChan chan<- ProgressUpdate) error { | ||
| 216 | p.SendOutput(progressChan, "Setting up Plymouth theme...") | 162 | p.SendOutput(progressChan, "Setting up Plymouth theme...") | ||
M internal/ui/installer_forms.go +7 -2
| @@ -12,7 +12,7 @@ import ( | |||
| 12 | 12 | ) | |
| 13 | 13 | ||
| 14 | 14 | // CreatePreflightForm creates the initial configuration form with all user identity fields | |
| 15 | - | func CreatePreflightForm(cfg *config.Config, fb *components.FormBuilder, sys *system.System) *huh.Form { | |
| 15 | + | func CreatePreflightForm(cfg *config.Config, fb *components.FormBuilder, sys *system.System, version string) *huh.Form { | |
| 16 | 16 | ||
| 17 | 17 | // Auto-detect timezone from API, fallback to default if detection fails | |
| 18 | 18 | detectedTimezone := sys.DetectTimezone() | |
| @@ -28,6 +28,11 @@ func CreatePreflightForm(cfg *config.Config, fb *components.FormBuilder, sys *sy | |||
| 28 | 28 | cfg.Keymap = config.KeymapDefault | |
| 29 | 29 | } | |
| 30 | 30 | ||
| 31 | + | title := "ArchUp Installer" | |
| 32 | + | if version != "" { | |
| 33 | + | title += " " + version | |
| 34 | + | } | |
| 35 | + | ||
| 31 | 36 | return fb.CreateForm( | |
| 32 | 37 | // System configuration | |
| 33 | 38 | huh.NewGroup( | |
| @@ -35,7 +40,7 @@ func CreatePreflightForm(cfg *config.Config, fb *components.FormBuilder, sys *sy | |||
| 35 | 40 | fb.TextInput("Username", "> ", &cfg.Username, validation.ValidateUsername), | |
| 36 | 41 | fb.TextInput("Email (optional)", "> ", &cfg.Email, nil). | |
| 37 | 42 | Description(DescEmail), | |
| 38 | - | ).Title(SectionUserIdentity), | |
| 43 | + | ).Title(title), | |
| 39 | 44 | ||
| 40 | 45 | // Passwords | |
| 41 | 46 | huh.NewGroup( | |
| @@ -12,7 +12,7 @@ import ( | |||||
| 12 | ) | 12 | ) | ||
| 13 | 13 | ||||
| 14 | // CreatePreflightForm creates the initial configuration form with all user identity fields | 14 | // CreatePreflightForm creates the initial configuration form with all user identity fields | ||
| 15 | - | func CreatePreflightForm(cfg *config.Config, fb *components.FormBuilder, sys *system.System) *huh.Form { | 15 | + | func CreatePreflightForm(cfg *config.Config, fb *components.FormBuilder, sys *system.System, version string) *huh.Form { |
| 16 | 16 | ||||
| 17 | // Auto-detect timezone from API, fallback to default if detection fails | 17 | // Auto-detect timezone from API, fallback to default if detection fails | ||
| 18 | detectedTimezone := sys.DetectTimezone() | 18 | detectedTimezone := sys.DetectTimezone() | ||
| @@ -28,6 +28,11 @@ func CreatePreflightForm(cfg *config.Config, fb *components.FormBuilder, sys *sy | |||||
| 28 | cfg.Keymap = config.KeymapDefault | 28 | cfg.Keymap = config.KeymapDefault | ||
| 29 | } | 29 | } | ||
| 30 | 30 | ||||
| 31 | + | title := "ArchUp Installer" | |||
| 32 | + | if version != "" { | |||
| 33 | + | title += " " + version | |||
| 34 | + | } | |||
| 35 | + | ||||
| 31 | return fb.CreateForm( | 36 | return fb.CreateForm( | ||
| 32 | // System configuration | 37 | // System configuration | ||
| 33 | huh.NewGroup( | 38 | huh.NewGroup( | ||
| @@ -35,7 +40,7 @@ func CreatePreflightForm(cfg *config.Config, fb *components.FormBuilder, sys *sy | |||||
| 35 | fb.TextInput("Username", "> ", &cfg.Username, validation.ValidateUsername), | 40 | fb.TextInput("Username", "> ", &cfg.Username, validation.ValidateUsername), | ||
| 36 | fb.TextInput("Email (optional)", "> ", &cfg.Email, nil). | 41 | fb.TextInput("Email (optional)", "> ", &cfg.Email, nil). | ||
| 37 | Description(DescEmail), | 42 | Description(DescEmail), | ||
| 38 | - | ).Title(SectionUserIdentity), | 43 | + | ).Title(title), |
| 39 | 44 | ||||
| 40 | // Passwords | 45 | // Passwords | ||
| 41 | huh.NewGroup( | 46 | huh.NewGroup( | ||
M internal/ui/model.go +3 -3
| @@ -119,7 +119,7 @@ func (m *Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { | |||
| 119 | 119 | // Network check passed, move to preflight form | |
| 120 | 120 | m.logger.Info("Moving to preflight form") | |
| 121 | 121 | m.state = StatePreflightForm | |
| 122 | - | m.currentForm = CreatePreflightForm(m.config, m.formBuilder, m.system) | |
| 122 | + | m.currentForm = CreatePreflightForm(m.config, m.formBuilder, m.system, m.version) | |
| 123 | 123 | return m, m.currentForm.Init() | |
| 124 | 124 | ||
| 125 | 125 | case StateConfirmation: | |
| @@ -133,7 +133,7 @@ func (m *Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { | |||
| 133 | 133 | switch m.state { | |
| 134 | 134 | case StateDiskForm: | |
| 135 | 135 | m.state = StatePreflightForm | |
| 136 | - | m.currentForm = CreatePreflightForm(m.config, m.formBuilder, m.system) | |
| 136 | + | m.currentForm = CreatePreflightForm(m.config, m.formBuilder, m.system, m.version) | |
| 137 | 137 | return m, m.currentForm.Init() | |
| 138 | 138 | ||
| 139 | 139 | case StateOptionsForm: | |
| @@ -169,7 +169,7 @@ func (m *Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { | |||
| 169 | 169 | // Network check passed - automatically continue to preflight form | |
| 170 | 170 | m.logger.Info("Network check passed - continuing automatically") | |
| 171 | 171 | m.state = StatePreflightForm | |
| 172 | - | m.currentForm = CreatePreflightForm(m.config, m.formBuilder, m.system) | |
| 172 | + | m.currentForm = CreatePreflightForm(m.config, m.formBuilder, m.system, m.version) | |
| 173 | 173 | return m, m.currentForm.Init() | |
| 174 | 174 | ||
| 175 | 175 | case phaseCompleteMsg: | |
| @@ -119,7 +119,7 @@ func (m *Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { | |||||
| 119 | // Network check passed, move to preflight form | 119 | // Network check passed, move to preflight form | ||
| 120 | m.logger.Info("Moving to preflight form") | 120 | m.logger.Info("Moving to preflight form") | ||
| 121 | m.state = StatePreflightForm | 121 | m.state = StatePreflightForm | ||
| 122 | - | m.currentForm = CreatePreflightForm(m.config, m.formBuilder, m.system) | 122 | + | m.currentForm = CreatePreflightForm(m.config, m.formBuilder, m.system, m.version) |
| 123 | return m, m.currentForm.Init() | 123 | return m, m.currentForm.Init() | ||
| 124 | 124 | ||||
| 125 | case StateConfirmation: | 125 | case StateConfirmation: | ||
| @@ -133,7 +133,7 @@ func (m *Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { | |||||
| 133 | switch m.state { | 133 | switch m.state { | ||
| 134 | case StateDiskForm: | 134 | case StateDiskForm: | ||
| 135 | m.state = StatePreflightForm | 135 | m.state = StatePreflightForm | ||
| 136 | - | m.currentForm = CreatePreflightForm(m.config, m.formBuilder, m.system) | 136 | + | m.currentForm = CreatePreflightForm(m.config, m.formBuilder, m.system, m.version) |
| 137 | return m, m.currentForm.Init() | 137 | return m, m.currentForm.Init() | ||
| 138 | 138 | ||||
| 139 | case StateOptionsForm: | 139 | case StateOptionsForm: | ||
| @@ -169,7 +169,7 @@ func (m *Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { | |||||
| 169 | // Network check passed - automatically continue to preflight form | 169 | // Network check passed - automatically continue to preflight form | ||
| 170 | m.logger.Info("Network check passed - continuing automatically") | 170 | m.logger.Info("Network check passed - continuing automatically") | ||
| 171 | m.state = StatePreflightForm | 171 | m.state = StatePreflightForm | ||
| 172 | - | m.currentForm = CreatePreflightForm(m.config, m.formBuilder, m.system) | 172 | + | m.currentForm = CreatePreflightForm(m.config, m.formBuilder, m.system, m.version) |
| 173 | return m, m.currentForm.Init() | 173 | return m, m.currentForm.Init() | ||
| 174 | 174 | ||||
| 175 | case phaseCompleteMsg: | 175 | case phaseCompleteMsg: | ||