Merge pull request 'feat(installer): v0.6 ergonomics + polish — 5 quick wins' (#3) from feat/ux-installer-v06-polish into main
This commit is contained in:
commit
8b1b49b5fc
1 changed files with 97 additions and 11 deletions
|
|
@ -69,6 +69,21 @@ banner() {
|
||||||
local vline="veilor-os ${ver} · ${d} · live"
|
local vline="veilor-os ${ver} · ${d} · live"
|
||||||
|
|
||||||
if [[ -r $BANNER_FILE ]]; then
|
if [[ -r $BANNER_FILE ]]; then
|
||||||
|
# v0.6: staged line-by-line reveal of the banner before the
|
||||||
|
# gum-style border draws around it. 40ms/line gives a subtle
|
||||||
|
# "typewriter" feel — 5 lines × 40ms = 200ms total, fast enough
|
||||||
|
# not to feel laggy but slow enough to land an aesthetic on the
|
||||||
|
# very first frame the user sees. Once the reveal finishes we
|
||||||
|
# clear and re-draw with the bordered gum-style version so the
|
||||||
|
# operator never sees both stacked on top of each other.
|
||||||
|
local line
|
||||||
|
while IFS= read -r line; do
|
||||||
|
printf ' %s\n' "$line"
|
||||||
|
sleep 0.04
|
||||||
|
done < "$BANNER_FILE"
|
||||||
|
sleep 0.08
|
||||||
|
clear
|
||||||
|
|
||||||
if [[ $TUI == gum ]]; then
|
if [[ $TUI == gum ]]; then
|
||||||
# gum style: rounded border, banner + blank line + version line.
|
# gum style: rounded border, banner + blank line + version line.
|
||||||
gum style --border rounded --margin "0 2" --padding "1 3" \
|
gum style --border rounded --margin "0 2" --padding "1 3" \
|
||||||
|
|
@ -150,10 +165,30 @@ prompt_input() {
|
||||||
}
|
}
|
||||||
|
|
||||||
# prompt_password <header>
|
# prompt_password <header>
|
||||||
|
#
|
||||||
|
# v0.6: gum-path replaced with bash `read -srp` because `gum input
|
||||||
|
# --password` rendered as a duplicate-"Install" + stray-T artefact on
|
||||||
|
# the linux fbcon since v0.5.27 (Agent 7 of the v0.6 polish research
|
||||||
|
# wave traced this to gum's bubbletea screen-restore writing back the
|
||||||
|
# previous menu buffer when the framebuffer terminfo lacked
|
||||||
|
# `civis/cnorm` cursor-hide sequences). bash `read -srp` is a single
|
||||||
|
# write to stdout + termios echo-off — no redraw, no glitch. Header
|
||||||
|
# rendered separately via gum style for visual parity with the rest
|
||||||
|
# of the installer.
|
||||||
prompt_password() {
|
prompt_password() {
|
||||||
local header=$1
|
local header=$1
|
||||||
if [[ $TUI == gum ]]; then
|
if [[ $TUI == gum ]]; then
|
||||||
gum input --password --header "$header"
|
# Render the prompt header as a styled box so it looks at home
|
||||||
|
# next to the other gum prompts, then collect the password via
|
||||||
|
# plain bash read on the next line. `read -s` disables echo,
|
||||||
|
# `read -p` writes the prompt to stderr (so command-substitution
|
||||||
|
# callers still get the password on stdout cleanly).
|
||||||
|
gum style --foreground "${VEILOR_FG:-15}" --border rounded \
|
||||||
|
--border-foreground "${VEILOR_DIM:-240}" --padding "0 2" -- "$header"
|
||||||
|
local pw
|
||||||
|
read -srp " password: " pw
|
||||||
|
echo >&2 # newline after silent read so next prompt isn't on same line
|
||||||
|
printf '%s' "$pw"
|
||||||
else
|
else
|
||||||
whiptail --title "veilor-os" --passwordbox "$header" 10 60 \
|
whiptail --title "veilor-os" --passwordbox "$header" 10 60 \
|
||||||
3>&1 1>&2 2>&3
|
3>&1 1>&2 2>&3
|
||||||
|
|
@ -253,12 +288,36 @@ collect_answers() {
|
||||||
}
|
}
|
||||||
|
|
||||||
# ── LUKS passphrase ──
|
# ── LUKS passphrase ──
|
||||||
|
# v0.6: prompt twice + string-compare. A typo in the LUKS passphrase
|
||||||
|
# is unrecoverable — the disk is unmountable without it and we
|
||||||
|
# don't escrow the key. Re-prompting until the two reads match
|
||||||
|
# catches keyboard-layout surprises (US vs UK quote position is
|
||||||
|
# the most common one) before they brick the install.
|
||||||
|
local luks_pw_confirm
|
||||||
|
while true; do
|
||||||
luks_pw=$(prompt_password "[2/3] Encryption · LUKS2 passphrase (min 8)") || return 1
|
luks_pw=$(prompt_password "[2/3] Encryption · LUKS2 passphrase (min 8)") || return 1
|
||||||
validate_pw "$luks_pw" "passphrase" || return 1
|
validate_pw "$luks_pw" "passphrase" || continue
|
||||||
|
luks_pw_confirm=$(prompt_password "[2/3] Confirm LUKS2 passphrase") || return 1
|
||||||
|
if [[ $luks_pw == "$luks_pw_confirm" ]]; then
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
prompt_error "Passphrases do not match — try again."
|
||||||
|
done
|
||||||
|
|
||||||
# ── Admin password ──
|
# ── Admin password ──
|
||||||
|
# Same confirm-twice pattern. Less catastrophic than LUKS (admin
|
||||||
|
# password can be reset from a recovery shell) but a mismatch here
|
||||||
|
# still locks the user out of their fresh install on first boot.
|
||||||
|
local admin_pw_confirm
|
||||||
|
while true; do
|
||||||
admin_pw=$(prompt_password "[3/3] Admin user · password for 'admin'") || return 1
|
admin_pw=$(prompt_password "[3/3] Admin user · password for 'admin'") || return 1
|
||||||
validate_pw "$admin_pw" "password" || return 1
|
validate_pw "$admin_pw" "password" || continue
|
||||||
|
admin_pw_confirm=$(prompt_password "[3/3] Confirm admin password") || return 1
|
||||||
|
if [[ $admin_pw == "$admin_pw_confirm" ]]; then
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
prompt_error "Passwords do not match — try again."
|
||||||
|
done
|
||||||
|
|
||||||
# ── Locale ──
|
# ── Locale ──
|
||||||
# Hardcoded en_US.UTF-8 for branded consistency. The picker that
|
# Hardcoded en_US.UTF-8 for branded consistency. The picker that
|
||||||
|
|
@ -984,12 +1043,39 @@ run_install() {
|
||||||
--show-output \
|
--show-output \
|
||||||
-- bash -c 'anaconda --cmdline --kickstart=/run/install/veilor-generated.ks 2>&1 | tee /tmp/anaconda-cmdline.log' || rc=$?
|
-- bash -c 'anaconda --cmdline --kickstart=/run/install/veilor-generated.ks 2>&1 | tee /tmp/anaconda-cmdline.log' || rc=$?
|
||||||
if [[ $rc -eq 0 ]]; then
|
if [[ $rc -eq 0 ]]; then
|
||||||
|
# v0.6: split the success screen into THREE stacked boxes.
|
||||||
|
#
|
||||||
|
# 1. Green success box — quiet confirmation.
|
||||||
|
# 2. Yellow eject box — promoted out of the buried
|
||||||
|
# one-liner the v0.5 success box used. Operators on
|
||||||
|
# both onyx and the friend's RTX 4080 rig missed the
|
||||||
|
# reminder and rebooted into the live ISO instead of
|
||||||
|
# the install. Now it's its own loud thick-bordered
|
||||||
|
# box that sits BELOW the success box and is
|
||||||
|
# impossible to miss.
|
||||||
|
# 3. Reboot countdown — embedded inside the green
|
||||||
|
# success box so the operator can see "complete +
|
||||||
|
# Xs to reboot" at a glance.
|
||||||
|
#
|
||||||
|
# Each tick clears + redraws all three, so the eject-media
|
||||||
|
# box stays in front of the operator for the full 10-second
|
||||||
|
# window and isn't scrolled off by a banner refresh.
|
||||||
|
local secs
|
||||||
|
for secs in 10 9 8 7 6 5 4 3 2 1; do
|
||||||
|
clear
|
||||||
gum style --foreground 2 --border rounded --margin "1 2" --padding "1 3" \
|
gum style --foreground 2 --border rounded --margin "1 2" --padding "1 3" \
|
||||||
"✓ Install complete" \
|
"✓ Install complete" \
|
||||||
"" \
|
"" \
|
||||||
"System will reboot in 5 seconds." \
|
"Rebooting in ${secs}s..."
|
||||||
"Remove the install media."
|
gum style --foreground 3 --border thick --margin "0 2" --padding "1 3" \
|
||||||
sleep 5
|
--border-foreground 3 \
|
||||||
|
" Remove the install media NOW " \
|
||||||
|
"" \
|
||||||
|
" Unplug the USB stick / eject the DVD before " \
|
||||||
|
" reboot, otherwise the system will boot back " \
|
||||||
|
" into the live ISO instead of your fresh install. "
|
||||||
|
sleep 1
|
||||||
|
done
|
||||||
systemctl reboot
|
systemctl reboot
|
||||||
else
|
else
|
||||||
prompt_error "Anaconda exited non-zero (status $rc).
|
prompt_error "Anaconda exited non-zero (status $rc).
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue