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
8c227abee4
commit
169212050d
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_SELECTED_FOREGROUND="$VEILOR_FG"
|
||||
export GUM_CHOOSE_SELECTED_BACKGROUND="$VEILOR_DIM"
|
||||
export GUM_CHOOSE_CURSOR="› "
|
||||
export GUM_CHOOSE_CURSOR="❯ "
|
||||
|
||||
# ── gum input ──────────────────────────────────────────
|
||||
# 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_PLACEHOLDER_FOREGROUND="$VEILOR_MUTE"
|
||||
export GUM_INPUT_HEADER_FOREGROUND="$VEILOR_FG"
|
||||
export GUM_INPUT_PROMPT="› "
|
||||
export GUM_INPUT_PROMPT="❯ "
|
||||
|
||||
# ── gum write (multi-line) ─────────────────────────────
|
||||
# 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"
|
||||
ID=veilor
|
||||
ID_LIKE=fedora
|
||||
VERSION="0.1"
|
||||
VERSION_ID="0.1"
|
||||
VERSION="0.5.8"
|
||||
VERSION_ID="0.5.8"
|
||||
HOME_URL="https://github.com/veilor-org/veilor-os"
|
||||
DOCUMENTATION_URL="https://github.com/veilor-org/veilor-os/tree/main/docs"
|
||||
BUG_REPORT_URL="https://github.com/veilor-org/veilor-os/issues"
|
||||
|
|
|
|||
|
|
@ -54,23 +54,34 @@ fi
|
|||
|
||||
banner() {
|
||||
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 [[ $TUI == gum ]]; then
|
||||
# gum style draws a rounded border + padding around the banner.
|
||||
gum style --border rounded --margin "1" --padding "1 2" \
|
||||
# gum style: rounded border, banner + blank line + version line.
|
||||
gum style --border rounded --margin "0 2" --padding "1 3" \
|
||||
--border-foreground "${VEILOR_DIM:-240}" \
|
||||
--foreground "${VEILOR_FG:-15}" \
|
||||
"$(cat "$BANNER_FILE")"
|
||||
"$(cat "$BANNER_FILE")" \
|
||||
"" \
|
||||
"$vline"
|
||||
else
|
||||
cat "$BANNER_FILE"
|
||||
echo
|
||||
echo " $vline"
|
||||
echo
|
||||
fi
|
||||
else
|
||||
# Fallback ASCII if banner.txt missing (older overlay).
|
||||
cat << 'EOF'
|
||||
cat << EOF
|
||||
|
||||
veilor-os installer
|
||||
hardened. branded. yours.
|
||||
$vline
|
||||
|
||||
EOF
|
||||
fi
|
||||
|
|
@ -180,10 +191,14 @@ prompt_error() {
|
|||
}
|
||||
|
||||
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" \
|
||||
"live - (KDE)" \
|
||||
"live - shell" \
|
||||
"live · KDE" \
|
||||
"live · shell" \
|
||||
"──────" \
|
||||
"Reboot" \
|
||||
"Power off"
|
||||
}
|
||||
|
|
@ -202,7 +217,7 @@ collect_answers() {
|
|||
prompt_error "No installable disks found."
|
||||
return 1
|
||||
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 ──
|
||||
# Hardcoded for branded consistency. Post-install: `hostnamectl set-hostname`.
|
||||
|
|
@ -231,27 +246,48 @@ collect_answers() {
|
|||
}
|
||||
|
||||
# ── 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
|
||||
|
||||
# ── 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
|
||||
|
||||
# ── Locale ──
|
||||
locale=$(prompt_choose "Choose locale" \
|
||||
locale=$(prompt_choose "[4/4] Locale" \
|
||||
"en_GB.UTF-8" \
|
||||
"en_US.UTF-8") || return 1
|
||||
|
||||
# ── 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
|
||||
LUKS: set
|
||||
Admin pw: set
|
||||
|
||||
Proceed?" || return 1
|
||||
This action is irreversible.
|
||||
|
||||
Proceed?" 16 60 || return 1
|
||||
fi
|
||||
|
||||
# Export to caller via globals
|
||||
SEL_DISK=$disk
|
||||
|
|
@ -524,36 +560,59 @@ KSEOF
|
|||
}
|
||||
|
||||
run_install() {
|
||||
prompt_message "Installing veilor-os to $SEL_DISK ...
|
||||
This will take 10-30 minutes.
|
||||
Logs: /var/log/veilor-installer.log + /tmp/anaconda.log"
|
||||
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).
|
||||
# Anaconda env setup (see comments below).
|
||||
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.
|
||||
# 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"
|
||||
# --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
|
||||
prompt_message "Install complete. System will reboot.
|
||||
Remove the install media after shutdown."
|
||||
sleep 3
|
||||
systemctl reboot
|
||||
else
|
||||
prompt_error "Anaconda exited non-zero.
|
||||
|
||||
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
|
||||
return 1
|
||||
fi
|
||||
else
|
||||
prompt_message "Installing veilor-os to $SEL_DISK ...
|
||||
This will take 10-30 minutes.
|
||||
Logs: /var/log/veilor-installer.log + /tmp/anaconda.log"
|
||||
sleep 1
|
||||
if anaconda --cmdline --kickstart=/run/install/veilor-generated.ks; then
|
||||
prompt_message "Install complete. System will reboot.
|
||||
Remove the install media after shutdown."
|
||||
sleep 3
|
||||
systemctl reboot
|
||||
else
|
||||
prompt_error "Anaconda exited non-zero.
|
||||
Logs at /tmp/anaconda.log + /var/log/veilor-installer.log.
|
||||
Press OK to drop to shell."
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
|
|
@ -595,10 +654,12 @@ while true; do
|
|||
run_install || continue
|
||||
fi
|
||||
;;
|
||||
"live - (KDE)") launch_desktop ;;
|
||||
"live - shell") drop_to_shell ;;
|
||||
"live · KDE") launch_desktop ;;
|
||||
"live · shell") drop_to_shell ;;
|
||||
"──────") banner ;; # separator clicked: redraw, no-op
|
||||
"Reboot") systemctl reboot ;;
|
||||
"Power off") systemctl poweroff ;;
|
||||
*) drop_to_shell ;;
|
||||
"") banner ;; # ESC/cancel from menu: redraw
|
||||
*) banner ;;
|
||||
esac
|
||||
done
|
||||
|
|
|
|||
Loading…
Reference in a new issue