From 93fc5e0517ec2d33983f7ede8fd726a842832e7d Mon Sep 17 00:00:00 2001 From: rootiest Date: Fri, 12 Jun 2026 19:17:49 -0400 Subject: [PATCH] feat(config-toggle): adaptive width, centering, and resize handling Panel now selects from four width tiers based on $COLUMNS (with a 6-col buffer per side before stepping up): 52-wide (default), 70, 74, or 78. Each tier carries richer category descriptions sized to fit the layout. The box is horizontally centered on every draw via a left-padding prefix. Key reader switches from stty min 1 to min 0 / time 3, giving a 0.3 s poll interval so COLUMNS changes are detected without a keypress. The erase formula is now wrap-aware: prev_max_line_width / COLUMNS gives the wrap factor, ensuring old wider panels are fully cleared after narrowing. Idle timeout ticks that detect no resize are skipped entirely. --- README.md | 2 +- docs/fish-config.md | 18 ++- functions/__config_toggle_draw.fish | 141 ++++++++++++++++-------- functions/__config_toggle_read_key.fish | 7 +- functions/config-toggle.fish | 22 +++- 5 files changed, 134 insertions(+), 56 deletions(-) diff --git a/README.md b/README.md index 3f61851..ee2bf3b 100644 --- a/README.md +++ b/README.md @@ -168,7 +168,7 @@ end Everything opinionated in this config — command shadows, startup side-effects, key and environment overrides, terminal integrations, logging, and the first-run greeting — is active by default but can be switched off. -> **The easy way — `config-toggle`:** Run `config-toggle` for an interactive TUI that flips every setting below on, off, or back to default — per-session or universally — without typing a single variable name. Use the arrow keys (or `h`/`j`/`k`/`l`) to navigate and adjust, `Tab` to switch scope, and `q` to quit. Changes apply instantly. +> **The easy way — `config-toggle`:** Run `config-toggle` for an interactive TUI that flips every setting below on, off, or back to default — per-session or universally — without typing a single variable name. Use the arrow keys (or `h`/`j`/`k`/`l`) to navigate and adjust, `Tab` to switch scope, and `q` to quit. Changes apply instantly. The panel auto-sizes to your terminal width (four tiers from 52- to 78-wide with a 6-column margin), centers itself horizontally, and redraws within ~0.3 s of a resize. If you'd rather set them by hand, each category is controlled by a universal variable. Six category toggles and one master switch are available: diff --git a/docs/fish-config.md b/docs/fish-config.md index 68dec9d..56509fe 100644 --- a/docs/fish-config.md +++ b/docs/fish-config.md @@ -1246,10 +1246,10 @@ Add -i (interactive confirmation) to destructive commands: Synopsis: config-toggle [-h] - Opens an interactive full-screen TUI for toggling the six opinionated - component categories (C1–C6) and the master disable variable without - having to type or remember variable names. Two scope tabs allow - independent per-scope configuration: + Opens an interactive TUI for toggling the six opinionated component + categories (C1–C6) and the master disable variable without having to + type or remember variable names. Two scope tabs allow independent + per-scope configuration: Universal — persists across all sessions (set -U) Session — current shell only (set -g) @@ -1257,6 +1257,16 @@ Add -i (interactive confirmation) to destructive commands: Changes apply immediately on each value keypress. Always available regardless of the __fish_config_opinionated master state. + The panel adapts to the terminal width automatically, selecting from + four layout tiers (with a 6-column buffer on each side before stepping + up to the next tier) and horizontally centering the box. The panel + redraws within ~0.3 s of a terminal resize with no keypress required. + + COLUMNS >= 90 → 78-wide panel (most detail) + COLUMNS >= 86 → 74-wide panel + COLUMNS >= 82 → 70-wide panel + COLUMNS < 82 → 52-wide panel (default) + Navigation: ↑ ↓ / k j Move cursor ← → / h l Set value: OFF ← DEFAULT → ON (clamped) diff --git a/functions/__config_toggle_draw.fish b/functions/__config_toggle_draw.fish index 4dd5fbe..340c36b 100644 --- a/functions/__config_toggle_draw.fish +++ b/functions/__config_toggle_draw.fish @@ -5,10 +5,19 @@ # __config_toggle_draw ... # # DESCRIPTION -# Renders the 14-line config-toggle TUI panel to stdout. Intended to be -# called once on startup, then again after each keypress preceded by -# `printf '\e[14A\e[J'` (cursor-up 14 lines + clear-to-end) to redraw -# in place. Does not move the cursor — caller manages positioning. +# Renders the 14-line config-toggle TUI panel to stdout. Panel width and +# horizontal position are chosen automatically from $COLUMNS each call, +# so a terminal resize takes effect on the next keypress without any +# extra bookkeeping. Four width tiers with a 6-col buffer per side: +# +# COLUMNS ≥ 90 → 78-wide (IW=76, desc=43 chars) +# COLUMNS ≥ 86 → 74-wide (IW=72, desc=39 chars) +# COLUMNS ≥ 82 → 70-wide (IW=68, desc=35 chars) +# COLUMNS < 82 → 52-wide (IW=50, desc=17 chars) ← default +# +# The box is horizontally centered via a left-padding prefix on every +# output line. \e[14A\e[J erases by line count so the horizontal offset +# does not interfere with the redraw loop. # # ARGUMENTS # cur_row 0–6, the currently highlighted row @@ -37,14 +46,60 @@ function __config_toggle_draw set -l c_head (set_color --bold cyan) set -l c_reset (set_color normal) - set -l labels Aliases Auto-exec Overrides Integrations Logging Greeting Master - set -l descs "cmd shadows" startup "keys/env/prompt" "terminal coupling" scrollback fish_greeting "disable all" + set -l labels Aliases Auto-exec Overrides Integrations Logging Greeting Master - # Panel dimensions - set -l IW 50 # inner width (chars between │ │) - set -l HBR (string repeat -n $IW '─') + # ── Width tier: 6-col buffer per side before stepping up ────────────── + # IW = inner width (chars between │ │); desc field = IW - 33. + # All four layouts are exactly 14 lines tall — panel_h in caller stays 14. + set -l iw 50 + set -l descs \ + "cmd shadows" \ + startup \ + "keys/env/prompt" \ + "terminal coupling" \ + scrollback \ + fish_greeting \ + "disable all" - # ── Scope tabs ──────────────────────────────────────── + if test "$COLUMNS" -ge 90 + set iw 76 + set descs \ + "shadows: ls→eza, cat→bat, cd→z, rm→trash" \ + "Fisher bootstrap, themes, py-venv activate" \ + "vi-mode, bang-bang, PAGER, CDPATH, starship" \ + "Kitty/WezTerm tab/split fns, notifications" \ + "scrollback capture & paru/yay AUR wrappers" \ + "fish_greeting & first-run welcome banner" \ + "master off-switch: overrides all categories" + else if test "$COLUMNS" -ge 86 + set iw 72 + set descs \ + "ls→eza, cat→bat, cd→zoxide, rm→trash" \ + "Fisher bootstrap, themes, py-venv auto" \ + "vi-mode, bang-bang, PAGER, starship" \ + "Kitty/WezTerm fns, done notifications" \ + "scrollback capture & paru/yay wrappers" \ + "fish_greeting: first-run welcome banner" \ + "master off-switch for all categories" + else if test "$COLUMNS" -ge 82 + set iw 68 + set descs \ + "ls→eza, cat→bat, cd→z, rm→trash" \ + "Fisher, themes, py-venv activate" \ + "vi-mode, bang-bang, PAGER, starship" \ + "Kitty/WezTerm, done notifications" \ + "scrollback & paru/yay log wrappers" \ + "fish_greeting & first-run banner" \ + "master disable for all categories" + end + + set -l HBR (string repeat -n $iw '─') + + # ── Center padding ──────────────────────────────────────────────────── + # ponytail: floor division — left margin may be 1 col less than right if gap is odd + set -l p (string repeat -n (math --scale=0 "max(0, ($COLUMNS - ($iw + 2)) / 2)") ' ') + + # ── Scope tabs ──────────────────────────────────────────────────────── set -l u_label set -l s_label if test $cur_scope = universal @@ -55,27 +110,26 @@ function __config_toggle_draw set s_label "$c_hi● Session $c_reset" end - # ── Top border ──────────────────────────────────────── - # Visible: ┌(1) ─(1) space(1) "Opinionated Settings"(20) space(1) N×─ ┐(1) = 52 - # N = 52 - 25 = 27 = IW - 23 - printf '┌─%s Opinionated Settings %s┐\n' \ - $c_head $c_reset(string repeat -n (math $IW - 23) '─') + # ── Top border ──────────────────────────────────────────────────────── + # ┌─ Opinionated Settings (iw-23)×─ ┐ total = iw+2 + printf '%s┌─%s Opinionated Settings %s┐\n' \ + $p $c_head $c_reset(string repeat -n (math $iw - 23) '─') - # ── Scope tab line ──────────────────────────────────── - # Visible inner (50): space(1) + u_label(11) + 2spaces + s_label(11) + 10spaces + "Tab to switch "(15) = 50 - printf '│ %s %s%s│\n' \ - $u_label $s_label \ - (string repeat -n 10 ' ')"Tab to switch " + # ── Scope tab line ──────────────────────────────────────────────────── + # Inner: " ●/○ Universal ●/○ Session " (25) + (iw-40)×space + "Tab to switch " (15) = iw + printf '%s│ %s %s%s│\n' \ + $p $u_label $s_label \ + (string repeat -n (math $iw - 40) ' ')"Tab to switch " - # ── Top divider ─────────────────────────────────────── - printf '│%s│\n' $HBR + # ── Top divider ─────────────────────────────────────────────────────── + printf '%s│%s│\n' $p $HBR - # ── Category rows 0–5 ───────────────────────────────── + # ── Category rows 0–5 ───────────────────────────────────────────────── for i in (seq 0 5) - set -l idx (math $i + 1) - set -l var $vars[$idx] - set -l label $labels[$idx] - set -l desc $descs[$idx] + set -l idx (math $i + 1) + set -l var $vars[$idx] + set -l label $labels[$idx] + set -l desc $descs[$idx] set -l val (__config_toggle_get_val $var $cur_scope) @@ -96,17 +150,17 @@ function __config_toggle_draw set curs "$c_sel▶$c_reset " end - # Label padded to 12, desc padded to 17, right margin 3 + # Label padded to 12, desc padded to (iw-33), right margin 3 set -l lpad (string pad -r -w 12 -- $label) - set -l dpad (string pad -r -w 17 -- $desc) + set -l dpad (string pad -r -w (math $iw - 33) -- $desc) - printf '│ %s%s [ %s ] %s │\n' $curs $lpad $badge $dpad + printf '%s│ %s%s [ %s ] %s │\n' $p $curs $lpad $badge $dpad end - # ── Separator before Master ─────────────────────────── - printf '│ %s │\n' (string repeat -n (math $IW - 6) '─') + # ── Separator before Master ─────────────────────────────────────────── + printf '%s│ %s │\n' $p (string repeat -n (math $iw - 6) '─') - # ── Master row (index 6) ────────────────────────────── + # ── Master row (index 6) ────────────────────────────────────────────── set -l val (__config_toggle_get_val $vars[7] $cur_scope) set -l badge switch $val @@ -121,21 +175,20 @@ function __config_toggle_draw if test $cur_row -eq 6 set curs "$c_sel▶$c_reset " end - printf '│ %s%s [ %s ] %s │\n' \ - $curs \ + printf '%s│ %s%s [ %s ] %s │\n' \ + $p $curs \ (string pad -r -w 12 -- Master) \ $badge \ - (string pad -r -w 17 -- "disable all") + (string pad -r -w (math $iw - 33) -- $descs[7]) - # ── Bottom divider ──────────────────────────────────── - printf '│%s│\n' $HBR + # ── Bottom divider ──────────────────────────────────────────────────── + printf '%s│%s│\n' $p $HBR - # ── Keybind hint ────────────────────────────────────── - # Padded to the inner width so the right border stays aligned regardless - # of the hint text. string pad is width-aware (arrows count as 1 column). + # ── Keybind hint ────────────────────────────────────────────────────── + # string pad is width-aware (arrows count as 1 column) set -l hint " ↑↓/kj move ←→/hl set Tab scope q quit" - printf '│%s%s%s│\n' $c_dim (string pad -r -w $IW -- $hint) $c_reset + printf '%s│%s%s%s│\n' $p $c_dim (string pad -r -w $iw -- $hint) $c_reset - # ── Bottom border ───────────────────────────────────── - printf '└%s┘\n' $HBR + # ── Bottom border ───────────────────────────────────────────────────── + printf '%s└%s┘\n' $p $HBR end diff --git a/functions/__config_toggle_read_key.fish b/functions/__config_toggle_read_key.fish index 1929836..4bafc78 100644 --- a/functions/__config_toggle_read_key.fish +++ b/functions/__config_toggle_read_key.fish @@ -42,9 +42,10 @@ function __config_toggle_read_key set -l saved (stty -g /dev/null) or return 1 - # Raw, no-echo. min 1 / time 1: block for the first byte, then wait up to - # 0.1s for the rest of a burst (escape sequence) before returning. - stty raw -echo min 1 time 1 /dev/null + # Raw, no-echo. min 0 / time 3: return after 0.3s even with no bytes (poll + # interval for resize detection), or immediately when any bytes arrive. + # Escape sequences (e.g. arrow keys) arrive fast enough to land in one read. + stty raw -echo min 0 time 3 /dev/null # One read() of up to 3 bytes — covers ESC [ A style sequences. od emits # the bytes as space-separated decimal codes. diff --git a/functions/config-toggle.fish b/functions/config-toggle.fish index a3e5b7f..c5d922c 100644 --- a/functions/config-toggle.fish +++ b/functions/config-toggle.fish @@ -72,7 +72,8 @@ function config-toggle --description 'Interactive TUI for toggling opinionated c set -l cur_row 0 # 0–6 set -l cur_scope universal # or "session" - set -l panel_h 14 # total panel lines + set -l panel_h 14 # total panel lines (all width tiers are 14 lines) + set -l last_cols $COLUMNS # COLUMNS at the time of the last draw # ── Terminal setup ──────────────────────────────────── printf '\e[?25l' # hide cursor @@ -142,13 +143,26 @@ function config-toggle --description 'Interactive TUI for toggling opinionated c break end - # Redraw in place - printf '\e[%dA\e[J' $panel_h + # Skip redraw entirely when the key reader timed out with no resize + if test -z "$key" -a "$COLUMNS" = "$last_cols" + continue + end + + # Wrap-aware erase: a panel drawn on a wider terminal has longer lines + # (due to center-padding) that wrap into extra physical rows when the + # terminal narrows. 78 = widest box (IW=76+2); the formula gives the + # worst-case old line width for any tier drawn at last_cols. + set -l prev_max_lw (math --scale=0 "($last_cols + 78) / 2") + set -l erase_h (math --scale=0 "$panel_h * max(1, ceil($prev_max_lw / $COLUMNS))") + printf '\e[%dA\e[J' $erase_h + set last_cols $COLUMNS __config_toggle_draw $cur_row $cur_scope $vars end # ── Cleanup ─────────────────────────────────────────── trap - INT # remove the signal handler - printf '\e[%dA\e[J' $panel_h # erase the panel + set -l prev_max_lw (math --scale=0 "($last_cols + 78) / 2") + set -l erase_h (math --scale=0 "$panel_h * max(1, ceil($prev_max_lw / $COLUMNS))") + printf '\e[%dA\e[J' $erase_h # erase the panel (wrap-aware) printf '\e[?25h' # restore cursor end