v0.5.8: installer UX polish — pro design
User-locked design changes for serious/pro feel:
Banner:
- Full VEILOR OS wordmark (figlet ANSI Regular block)
- Version + date + live indicator: "veilor-os 0.5.8 · 2026-05-03 · live"
- No tagline, no credit
- Rounded gum border, dim grey accent
Menu:
- Drop "Welcome" header (banner = welcome enough)
- Reorder + simplify:
Install
live · KDE
live · shell
──────
Reboot
Power off
- Cursor: ❯ (sharp angle, matches box-drawing weight)
- Middle-dot · separators (cleaner than en-dash)
- Visual separator line between primary/session actions
Install flow:
- Step indicators on each prompt: [1/4] [2/4] [3/4] [4/4]
- Disk: "[1/4] Select install disk · WILL BE ERASED"
- LUKS: "[2/4] Encryption · LUKS2 passphrase (min 8)"
- Admin: "[3/4] Admin user · password for 'admin'"
- Locale: "[4/4] Locale"
Confirm screen:
- Boxed (gum style --border rounded)
- "WILL BE ERASED" colored red (FG=1)
- "This action is irreversible" colored amber (FG=3)
- gum confirm with --affirmative "Yes, install" / --negative "Cancel"
Install progress:
- gum spin with --show-output during anaconda run
- Title: "Installing veilor-os to /dev/X · 10-30min · logs on tty2"
- Success: green-bordered "✓ Install complete" box, 5s reboot countdown
os-release: VERSION_ID 0.1 → 0.5.8
This commit is contained in:
parent
53949b0899
commit
f8fc89e399
3 changed files with 106 additions and 45 deletions
|
|
@ -28,7 +28,7 @@ export GUM_CHOOSE_HEADER_FOREGROUND="$VEILOR_FG"
|
||||||
export GUM_CHOOSE_ITEM_FOREGROUND="$VEILOR_FG"
|
export GUM_CHOOSE_ITEM_FOREGROUND="$VEILOR_FG"
|
||||||
export GUM_CHOOSE_SELECTED_FOREGROUND="$VEILOR_FG"
|
export GUM_CHOOSE_SELECTED_FOREGROUND="$VEILOR_FG"
|
||||||
export GUM_CHOOSE_SELECTED_BACKGROUND="$VEILOR_DIM"
|
export GUM_CHOOSE_SELECTED_BACKGROUND="$VEILOR_DIM"
|
||||||
export GUM_CHOOSE_CURSOR="› "
|
export GUM_CHOOSE_CURSOR="❯ "
|
||||||
|
|
||||||
# ── gum input ──────────────────────────────────────────
|
# ── gum input ──────────────────────────────────────────
|
||||||
# Single-line text entry (hostname).
|
# Single-line text entry (hostname).
|
||||||
|
|
@ -36,7 +36,7 @@ export GUM_INPUT_PROMPT_FOREGROUND="$VEILOR_DIM"
|
||||||
export GUM_INPUT_CURSOR_FOREGROUND="$VEILOR_FG"
|
export GUM_INPUT_CURSOR_FOREGROUND="$VEILOR_FG"
|
||||||
export GUM_INPUT_PLACEHOLDER_FOREGROUND="$VEILOR_MUTE"
|
export GUM_INPUT_PLACEHOLDER_FOREGROUND="$VEILOR_MUTE"
|
||||||
export GUM_INPUT_HEADER_FOREGROUND="$VEILOR_FG"
|
export GUM_INPUT_HEADER_FOREGROUND="$VEILOR_FG"
|
||||||
export GUM_INPUT_PROMPT="› "
|
export GUM_INPUT_PROMPT="❯ "
|
||||||
|
|
||||||
# ── gum write (multi-line) ─────────────────────────────
|
# ── gum write (multi-line) ─────────────────────────────
|
||||||
# Reserved for any longer-form prompts; not used in v0.5.1 yet.
|
# Reserved for any longer-form prompts; not used in v0.5.1 yet.
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,8 @@ NAME="veilor-os"
|
||||||
PRETTY_NAME="veilor-os 0.1"
|
PRETTY_NAME="veilor-os 0.1"
|
||||||
ID=veilor
|
ID=veilor
|
||||||
ID_LIKE=fedora
|
ID_LIKE=fedora
|
||||||
VERSION="0.1"
|
VERSION="0.5.8"
|
||||||
VERSION_ID="0.1"
|
VERSION_ID="0.5.8"
|
||||||
HOME_URL="https://github.com/veilor-org/veilor-os"
|
HOME_URL="https://github.com/veilor-org/veilor-os"
|
||||||
DOCUMENTATION_URL="https://github.com/veilor-org/veilor-os/tree/main/docs"
|
DOCUMENTATION_URL="https://github.com/veilor-org/veilor-os/tree/main/docs"
|
||||||
BUG_REPORT_URL="https://github.com/veilor-org/veilor-os/issues"
|
BUG_REPORT_URL="https://github.com/veilor-org/veilor-os/issues"
|
||||||
|
|
|
||||||
|
|
@ -54,23 +54,34 @@ fi
|
||||||
|
|
||||||
banner() {
|
banner() {
|
||||||
clear
|
clear
|
||||||
|
# Read version + date for version line. /etc/os-release has VERSION_ID.
|
||||||
|
local ver=""
|
||||||
|
[[ -r /etc/os-release ]] && ver=$(. /etc/os-release; echo "${VERSION_ID:-0.0}")
|
||||||
|
local d
|
||||||
|
d=$(date +%Y-%m-%d)
|
||||||
|
local vline="veilor-os ${ver} · ${d} · live"
|
||||||
|
|
||||||
if [[ -r $BANNER_FILE ]]; then
|
if [[ -r $BANNER_FILE ]]; then
|
||||||
if [[ $TUI == gum ]]; then
|
if [[ $TUI == gum ]]; then
|
||||||
# gum style draws a rounded border + padding around the banner.
|
# gum style: rounded border, banner + blank line + version line.
|
||||||
gum style --border rounded --margin "1" --padding "1 2" \
|
gum style --border rounded --margin "0 2" --padding "1 3" \
|
||||||
--border-foreground "${VEILOR_DIM:-240}" \
|
--border-foreground "${VEILOR_DIM:-240}" \
|
||||||
--foreground "${VEILOR_FG:-15}" \
|
--foreground "${VEILOR_FG:-15}" \
|
||||||
"$(cat "$BANNER_FILE")"
|
"$(cat "$BANNER_FILE")" \
|
||||||
|
"" \
|
||||||
|
"$vline"
|
||||||
else
|
else
|
||||||
cat "$BANNER_FILE"
|
cat "$BANNER_FILE"
|
||||||
echo
|
echo
|
||||||
|
echo " $vline"
|
||||||
|
echo
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
# Fallback ASCII if banner.txt missing (older overlay).
|
# Fallback ASCII if banner.txt missing (older overlay).
|
||||||
cat << 'EOF'
|
cat << EOF
|
||||||
|
|
||||||
veilor-os installer
|
veilor-os installer
|
||||||
hardened. branded. yours.
|
$vline
|
||||||
|
|
||||||
EOF
|
EOF
|
||||||
fi
|
fi
|
||||||
|
|
@ -180,10 +191,14 @@ prompt_error() {
|
||||||
}
|
}
|
||||||
|
|
||||||
main_menu() {
|
main_menu() {
|
||||||
prompt_choose "Welcome" \
|
# Empty header — banner already provides context. `──────` line splits
|
||||||
|
# primary actions (top) from session controls (bottom). Cursor `❯` set
|
||||||
|
# via GUM_CHOOSE_CURSOR in colors.gum.
|
||||||
|
prompt_choose "" \
|
||||||
"Install" \
|
"Install" \
|
||||||
"live - (KDE)" \
|
"live · KDE" \
|
||||||
"live - shell" \
|
"live · shell" \
|
||||||
|
"──────" \
|
||||||
"Reboot" \
|
"Reboot" \
|
||||||
"Power off"
|
"Power off"
|
||||||
}
|
}
|
||||||
|
|
@ -202,7 +217,7 @@ collect_answers() {
|
||||||
prompt_error "No installable disks found."
|
prompt_error "No installable disks found."
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
disk=$(prompt_choose_pairs "Select install disk (will be ERASED)" "${disks_pairs[@]}") || return 1
|
disk=$(prompt_choose_pairs "[1/4] Select install disk · WILL BE ERASED" "${disks_pairs[@]}") || return 1
|
||||||
|
|
||||||
# ── Hostname ──
|
# ── Hostname ──
|
||||||
# Hardcoded for branded consistency. Post-install: `hostnamectl set-hostname`.
|
# Hardcoded for branded consistency. Post-install: `hostnamectl set-hostname`.
|
||||||
|
|
@ -231,27 +246,48 @@ collect_answers() {
|
||||||
}
|
}
|
||||||
|
|
||||||
# ── LUKS passphrase ──
|
# ── LUKS passphrase ──
|
||||||
luks_pw=$(prompt_password "LUKS passphrase (full-disk encryption)") || return 1
|
luks_pw=$(prompt_password "[2/4] Encryption · LUKS2 passphrase (min 8)") || return 1
|
||||||
validate_pw "$luks_pw" "passphrase" || return 1
|
validate_pw "$luks_pw" "passphrase" || return 1
|
||||||
|
|
||||||
# ── Admin password ──
|
# ── Admin password ──
|
||||||
admin_pw=$(prompt_password "Admin user password (login after install)") || return 1
|
admin_pw=$(prompt_password "[3/4] Admin user · password for 'admin'") || return 1
|
||||||
validate_pw "$admin_pw" "password" || return 1
|
validate_pw "$admin_pw" "password" || return 1
|
||||||
|
|
||||||
# ── Locale ──
|
# ── Locale ──
|
||||||
locale=$(prompt_choose "Choose locale" \
|
locale=$(prompt_choose "[4/4] Locale" \
|
||||||
"en_GB.UTF-8" \
|
"en_GB.UTF-8" \
|
||||||
"en_US.UTF-8") || return 1
|
"en_US.UTF-8") || return 1
|
||||||
|
|
||||||
# ── Confirmation ──
|
# ── Confirmation ──
|
||||||
prompt_confirm "About to install veilor-os:
|
# Render summary box + danger lines via gum style, then gum confirm.
|
||||||
|
# Whiptail fallback: simple yesno.
|
||||||
|
if [[ $TUI == gum ]]; then
|
||||||
|
clear
|
||||||
|
gum style --border rounded --margin "1 2" --padding "1 3" \
|
||||||
|
--border-foreground "${VEILOR_DIM:-240}" \
|
||||||
|
--foreground "${VEILOR_FG:-15}" \
|
||||||
|
"Confirm install" \
|
||||||
|
"" \
|
||||||
|
" Disk $disk $(gum style --foreground 1 'WILL BE ERASED')" \
|
||||||
|
" Locale $locale" \
|
||||||
|
" LUKS ✓ set" \
|
||||||
|
" Admin ✓ set" \
|
||||||
|
"" \
|
||||||
|
"$(gum style --foreground 3 'This action is irreversible.')"
|
||||||
|
gum confirm --affirmative "Yes, install" --negative "Cancel" "Proceed?" || return 1
|
||||||
|
else
|
||||||
|
whiptail --title "Confirm install" --yesno \
|
||||||
|
"About to install veilor-os:
|
||||||
|
|
||||||
Disk: $disk (will be ERASED)
|
Disk: $disk (WILL BE ERASED)
|
||||||
Locale: $locale
|
Locale: $locale
|
||||||
LUKS: set
|
LUKS: set
|
||||||
Admin pw: set
|
Admin pw: set
|
||||||
|
|
||||||
Proceed?" || return 1
|
This action is irreversible.
|
||||||
|
|
||||||
|
Proceed?" 16 60 || return 1
|
||||||
|
fi
|
||||||
|
|
||||||
# Export to caller via globals
|
# Export to caller via globals
|
||||||
SEL_DISK=$disk
|
SEL_DISK=$disk
|
||||||
|
|
@ -524,26 +560,48 @@ KSEOF
|
||||||
}
|
}
|
||||||
|
|
||||||
run_install() {
|
run_install() {
|
||||||
|
# Anaconda env setup (see comments below).
|
||||||
|
export LANG="${SEL_LOCALE:-en_GB.UTF-8}"
|
||||||
|
export LC_ALL="$LANG"
|
||||||
|
# LANG / LC_ALL: anaconda's keyboard.activate_keyboard() raises
|
||||||
|
# AnacondaError: 'LANG' if missing. tty1 inherits no locale by default.
|
||||||
|
# XDG_RUNTIME_DIR: anaconda's display.setup_display() probes
|
||||||
|
# os.getenv("XDG_RUNTIME_DIR") + Wayland socket and crashes with
|
||||||
|
# TypeError on None. We're running unattended so no display needed,
|
||||||
|
# but env var still has to exist.
|
||||||
|
export XDG_RUNTIME_DIR="/run/user/$(id -u)"
|
||||||
|
mkdir -p "$XDG_RUNTIME_DIR"
|
||||||
|
chmod 0700 "$XDG_RUNTIME_DIR"
|
||||||
|
|
||||||
|
if [[ $TUI == gum ]]; then
|
||||||
|
clear
|
||||||
|
# gum spin runs anaconda as subprocess. --show-output forwards
|
||||||
|
# stdout/stderr to /tmp/anaconda-cmdline.log so we can debug.
|
||||||
|
# --title shows live-updating header. --spinner dot is minimal.
|
||||||
|
local rc=0
|
||||||
|
gum spin --spinner dot \
|
||||||
|
--title "Installing veilor-os to $SEL_DISK · this takes 10-30 min · logs on tty2" \
|
||||||
|
--show-output \
|
||||||
|
-- bash -c 'anaconda --cmdline --kickstart=/run/install/veilor-generated.ks 2>&1 | tee /tmp/anaconda-cmdline.log' || rc=$?
|
||||||
|
if [[ $rc -eq 0 ]]; then
|
||||||
|
gum style --foreground 2 --border rounded --margin "1 2" --padding "1 3" \
|
||||||
|
"✓ Install complete" \
|
||||||
|
"" \
|
||||||
|
"System will reboot in 5 seconds." \
|
||||||
|
"Remove the install media."
|
||||||
|
sleep 5
|
||||||
|
systemctl reboot
|
||||||
|
else
|
||||||
|
prompt_error "Anaconda exited non-zero (status $rc).
|
||||||
|
Logs at /tmp/anaconda.log + /var/log/veilor-installer.log.
|
||||||
|
Press OK to drop to shell."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
else
|
||||||
prompt_message "Installing veilor-os to $SEL_DISK ...
|
prompt_message "Installing veilor-os to $SEL_DISK ...
|
||||||
This will take 10-30 minutes.
|
This will take 10-30 minutes.
|
||||||
Logs: /var/log/veilor-installer.log + /tmp/anaconda.log"
|
Logs: /var/log/veilor-installer.log + /tmp/anaconda.log"
|
||||||
sleep 1
|
sleep 1
|
||||||
# Hand off to anaconda. --kickstart runs unattended.
|
|
||||||
# LANG / LC_ALL must be set — anaconda's keyboard.activate_keyboard()
|
|
||||||
# raises AnacondaError: 'LANG' if missing. tty1 inherits no locale by
|
|
||||||
# default. Use the locale the user picked (or fall back to en_GB).
|
|
||||||
export LANG="${SEL_LOCALE:-en_GB.UTF-8}"
|
|
||||||
export LC_ALL="$LANG"
|
|
||||||
# XDG_RUNTIME_DIR must exist — anaconda's display.setup_display()
|
|
||||||
# tries os.getenv("XDG_RUNTIME_DIR") + constants.WAYLAND_SOCKET_NAME
|
|
||||||
# and crashes with TypeError on None. We're running unattended so
|
|
||||||
# no graphical display is needed, but the env var still has to exist.
|
|
||||||
export XDG_RUNTIME_DIR="/run/user/$(id -u)"
|
|
||||||
mkdir -p "$XDG_RUNTIME_DIR"
|
|
||||||
chmod 0700 "$XDG_RUNTIME_DIR"
|
|
||||||
# --cmdline: fully unattended text-only mode. No Wayland setup, no
|
|
||||||
# graphical/text TUI. Reads kickstart, executes, reboots. Right mode
|
|
||||||
# for our gum-driven flow where ks is fully self-contained.
|
|
||||||
if anaconda --cmdline --kickstart=/run/install/veilor-generated.ks; then
|
if anaconda --cmdline --kickstart=/run/install/veilor-generated.ks; then
|
||||||
prompt_message "Install complete. System will reboot.
|
prompt_message "Install complete. System will reboot.
|
||||||
Remove the install media after shutdown."
|
Remove the install media after shutdown."
|
||||||
|
|
@ -555,6 +613,7 @@ Logs at /tmp/anaconda.log + /var/log/veilor-installer.log.
|
||||||
Press OK to drop to shell."
|
Press OK to drop to shell."
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
drop_to_shell() {
|
drop_to_shell() {
|
||||||
|
|
@ -595,10 +654,12 @@ while true; do
|
||||||
run_install || continue
|
run_install || continue
|
||||||
fi
|
fi
|
||||||
;;
|
;;
|
||||||
"live - (KDE)") launch_desktop ;;
|
"live · KDE") launch_desktop ;;
|
||||||
"live - shell") drop_to_shell ;;
|
"live · shell") drop_to_shell ;;
|
||||||
|
"──────") banner ;; # separator clicked: redraw, no-op
|
||||||
"Reboot") systemctl reboot ;;
|
"Reboot") systemctl reboot ;;
|
||||||
"Power off") systemctl poweroff ;;
|
"Power off") systemctl poweroff ;;
|
||||||
*) drop_to_shell ;;
|
"") banner ;; # ESC/cancel from menu: redraw
|
||||||
|
*) banner ;;
|
||||||
esac
|
esac
|
||||||
done
|
done
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue