Add config-toggle — a full ANSI TUI (pure fish, no external deps beyond stty/dd/od) for toggling all 7 opinionated component variables (the six C1–C6 categories plus the master __fish_config_opinionated) without typing variable names
Two scope tabs (Universal / Session) switchable with Tab; changes apply immediately via set -U / set -g — no confirm step. The two scopes are independent (e.g. Integrations off universally, on for this session)
Three states per variable — ON, OFF, DEFAULT — set directionally along an OFF ← DEFAULT → ON scale (clamped at the ends). DEFAULT erases the variable so the master switch / built-in default takes over
fish's read builtin invokes its interactive line editor on a TTY, which prints a read> prompt, intercepts Tab and arrow keys for its own editing (so they never reach the handler), and shifts the cursor — breaking in-place redraws. __config_toggle_read_key instead puts the terminal in raw mode (stty raw -echo min 1 time 1), reads one keypress from /dev/tty, and decodes the raw bytes into normalized tokens (up/down/left/right/tab/escape/quit/literal char). This makes arrow keys and Tab work, removes the stray prompt, keeps the redraw aligned, and lets bare Esc exit cleanly (Ctrl-C in raw mode arrives as byte 3 → handled as quit).
Manual Verification
Run config-toggle — panel renders once with a clean border, 7 rows (Aliases → Master), Universal tab active, no read> line, no flicker/stacking
Press ↓/↑ and j/k — cursor moves through rows, clamped at the first and last row
Press →/l on a row — value steps OFF → DEFAULT → ON and stops at ON (no wrap)
Press ←/h on a row — value steps ON → DEFAULT → OFF and stops at OFF (no wrap)
After changing a value, run set --show <var> in another shell — the universal variable reflects the change immediately
Press Tab — scope switches between Universal and Session; the highlighted tab updates
Set a category ON in Universal and OFF in Session (Tab between scopes) — both held independently; confirm with set --show __fish_config_op_aliases
Press q, Esc, and Ctrl-C (separately) — each exits cleanly, cursor restored, prompt on a fresh line
Run config-toggle -h — prints the keymap/help to stdout without entering the TUI
Run help config toggle — jumps to the config-toggle section in the offline docs
## Summary
- Add `config-toggle` — a full ANSI TUI (pure fish, no external deps beyond `stty`/`dd`/`od`) for toggling all 7 opinionated component variables (the six C1–C6 categories plus the master `__fish_config_opinionated`) without typing variable names
- Two scope tabs (**Universal** / **Session**) switchable with `Tab`; changes apply immediately via `set -U` / `set -g` — no confirm step. The two scopes are independent (e.g. Integrations off universally, on for this session)
- Three states per variable — `ON`, `OFF`, `DEFAULT` — set **directionally** along an `OFF ← DEFAULT → ON` scale (clamped at the ends). `DEFAULT` erases the variable so the master switch / built-in default takes over
- Fully `hjkl`-consistent keymap (see below)
### Keymap
| Key | Action |
|---|---|
| `↑` `↓` or `k` `j` | Move cursor |
| `←` `→` or `h` `l` | Set value: `OFF ← DEFAULT → ON` (clamped) |
| `Tab` | Switch scope (Universal ↔ Session) |
| `q` / `Esc` | Exit |
### Files
- `functions/config-toggle.fish` — entry point: arg/help parsing, terminal setup, event loop
- `functions/__config_toggle_read_key.fish` — raw-tty single-keypress reader (see note below)
- `functions/__config_toggle_draw.fish` — bordered panel renderer
- `functions/__config_toggle_get_val.fish` — scope-aware value reader (parses `set --show`)
- `functions/__config_toggle_apply.fish` — shared scope-aware `set -U`/`-g`/`-Ue`/`-eg` apply logic
- `docs/fish-config.md` + `docs/fish-config.index` — offline docs entry; `README.md` — Minimal Mode tip
### Implementation note: why not fish `read`?
fish's `read` builtin invokes its interactive line editor on a TTY, which prints a `read> ` prompt, **intercepts Tab and arrow keys** for its own editing (so they never reach the handler), and shifts the cursor — breaking in-place redraws. `__config_toggle_read_key` instead puts the terminal in raw mode (`stty raw -echo min 1 time 1`), reads one keypress from `/dev/tty`, and decodes the raw bytes into normalized tokens (`up`/`down`/`left`/`right`/`tab`/`escape`/`quit`/literal char). This makes arrow keys and Tab work, removes the stray prompt, keeps the redraw aligned, and lets bare `Esc` exit cleanly (Ctrl-C in raw mode arrives as byte 3 → handled as quit).
## Manual Verification
- [x] Run `config-toggle` — panel renders once with a clean border, 7 rows (Aliases → Master), Universal tab active, no `read>` line, no flicker/stacking
- [x] Press `↓`/`↑` and `j`/`k` — cursor moves through rows, clamped at the first and last row
- [x] Press `→`/`l` on a row — value steps `OFF → DEFAULT → ON` and stops at `ON` (no wrap)
- [x] Press `←`/`h` on a row — value steps `ON → DEFAULT → OFF` and stops at `OFF` (no wrap)
- [x] After changing a value, run `set --show <var>` in another shell — the universal variable reflects the change immediately
- [x] Press `Tab` — scope switches between Universal and Session; the highlighted tab updates
- [x] Set a category `ON` in Universal and `OFF` in Session (Tab between scopes) — both held independently; confirm with `set --show __fish_config_op_aliases`
- [x] Press `q`, `Esc`, and `Ctrl-C` (separately) — each exits cleanly, cursor restored, prompt on a fresh line
- [x] Run `config-toggle -h` — prints the keymap/help to stdout without entering the TUI
- [x] Run `help config toggle` — jumps to the config-toggle section in the offline docs
Interactive TUI for toggling opinionated component categories and master
variable. Tabbed Universal/Session scope, arrow-key navigation, Space to
cycle ON/OFF/DEFAULT with immediate apply.
Replace two-read-k-1 ESC handling with a single read -k 3 so that a bare
ESC returns immediately instead of blocking the shell indefinitely waiting
for a second byte. Add trap - INT in the cleanup block so the signal
handler does not leak into the global session after config-toggle exits.
fish's read -k N waits for exactly N bytes; single-char keys ('q', 'j',
Space, Tab) were blocking indefinitely waiting for 2 more bytes, causing
the input loop to appear unresponsive. Random 3-key bundles were then
processed as unrecognised input, triggering infinite redraws.
Switch to read -k 1 (one raw byte per call). Arrow keys still work: the
terminal sends ESC+[+A/B as a burst, so after reading ESC the two
continuation bytes are already in the TTY buffer and the follow-up reads
return immediately. Bare ESC now passes the next keypress through as the
effective key rather than exiting (q/Q remain the exit keys).
The event loop used `read -k 1`, but `-k` is not a valid fish read option
(it errors with "unknown option", status 2). read therefore returned
instantly every iteration without consuming a keypress, so the loop
redrew the panel as fast as the terminal could render and ignored all
input. The nchars flag is `-n`, not `-k`.
Switch to `read -s -n 1` (one char, silent so keystrokes don't garble the
panel) and add `or break` so a read failure (EOF / non-tty stdin) exits
cleanly instead of spinning. Applies to the primary read and both escape
sequence continuation reads.
fish's `read` invokes its interactive line editor on a TTY, which (1)
prints a `read> ` prompt below the panel, (2) intercepts Tab and arrow
keys for its own line editing so they never reach the switch, and (3)
shifts the cursor down a line, throwing off the `\e[14A` redraw so top
borders stacked on every keypress. Plain keys (j/k/q/space) passed
through, masking the problem.
Add __config_toggle_read_key: puts the terminal in raw, no-echo mode
(`stty raw -echo min 1 time 1`), reads one keypress from /dev/tty, and
returns a normalized token (up/down/left/right/tab/space/escape/quit or
the literal char) by decoding the bytes via od. Arrow keys and Tab now
work, there is no stray prompt, and the redraw stays aligned. Ctrl-C in
raw mode arrives as byte 3 and maps to quit; bare Esc exits after the
0.1s inter-byte timer.
Rewrite the event loop to consume these tokens and restore Esc to the
help text.
Left/Right now adjust the highlighted value one step along the
OFF ← DEFAULT → ON scale, clamped at the ends (no wrap), complementing
Space which cycles through all states and wraps. Right steps toward ON,
Left toward OFF.
Extract the set/erase side-effect into __config_toggle_apply so Space,
Left, and Right share one implementation of the scope-aware
set -U/-g/-Ue/-eg logic. Update the in-panel keybind hint (now
width-padded via string pad so the border stays aligned) and the help
text and offline docs to cover the new keys.
Directional arrows fully cover value changes, so the Space cycle is
removed. Add h/l as vim-style aliases for ←/→ (matching the existing
j/k for up/down), keeping the whole keymap hjkl-consistent. Update the
in-panel hint, help text, docstrings, and offline docs accordingly.
The badge now positions each state by where it sits on the scale: OFF
left-aligned, DEFAULT centered, ON right-aligned. This makes the
OFF ← DEFAULT → ON ordering visible at a glance and reinforces the
direction the ←→/h l keys move.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Summary
config-toggle— a full ANSI TUI (pure fish, no external deps beyondstty/dd/od) for toggling all 7 opinionated component variables (the six C1–C6 categories plus the master__fish_config_opinionated) without typing variable namesTab; changes apply immediately viaset -U/set -g— no confirm step. The two scopes are independent (e.g. Integrations off universally, on for this session)ON,OFF,DEFAULT— set directionally along anOFF ← DEFAULT → ONscale (clamped at the ends).DEFAULTerases the variable so the master switch / built-in default takes overhjkl-consistent keymap (see below)Keymap
↑↓orkj←→orhlOFF ← DEFAULT → ON(clamped)Tabq/EscFiles
functions/config-toggle.fish— entry point: arg/help parsing, terminal setup, event loopfunctions/__config_toggle_read_key.fish— raw-tty single-keypress reader (see note below)functions/__config_toggle_draw.fish— bordered panel rendererfunctions/__config_toggle_get_val.fish— scope-aware value reader (parsesset --show)functions/__config_toggle_apply.fish— shared scope-awareset -U/-g/-Ue/-egapply logicdocs/fish-config.md+docs/fish-config.index— offline docs entry;README.md— Minimal Mode tipImplementation note: why not fish
read?fish's
readbuiltin invokes its interactive line editor on a TTY, which prints aread>prompt, intercepts Tab and arrow keys for its own editing (so they never reach the handler), and shifts the cursor — breaking in-place redraws.__config_toggle_read_keyinstead puts the terminal in raw mode (stty raw -echo min 1 time 1), reads one keypress from/dev/tty, and decodes the raw bytes into normalized tokens (up/down/left/right/tab/escape/quit/literal char). This makes arrow keys and Tab work, removes the stray prompt, keeps the redraw aligned, and lets bareEscexit cleanly (Ctrl-C in raw mode arrives as byte 3 → handled as quit).Manual Verification
config-toggle— panel renders once with a clean border, 7 rows (Aliases → Master), Universal tab active, noread>line, no flicker/stacking↓/↑andj/k— cursor moves through rows, clamped at the first and last row→/lon a row — value stepsOFF → DEFAULT → ONand stops atON(no wrap)←/hon a row — value stepsON → DEFAULT → OFFand stops atOFF(no wrap)set --show <var>in another shell — the universal variable reflects the change immediatelyTab— scope switches between Universal and Session; the highlighted tab updatesONin Universal andOFFin Session (Tab between scopes) — both held independently; confirm withset --show __fish_config_op_aliasesq,Esc, andCtrl-C(separately) — each exits cleanly, cursor restored, prompt on a fresh lineconfig-toggle -h— prints the keymap/help to stdout without entering the TUIhelp config toggle— jumps to the config-toggle section in the offline docsfish's read -k N waits for exactly N bytes; single-char keys ('q', 'j', Space, Tab) were blocking indefinitely waiting for 2 more bytes, causing the input loop to appear unresponsive. Random 3-key bundles were then processed as unrecognised input, triggering infinite redraws. Switch to read -k 1 (one raw byte per call). Arrow keys still work: the terminal sends ESC+[+A/B as a burst, so after reading ESC the two continuation bytes are already in the TTY buffer and the follow-up reads return immediately. Bare ESC now passes the next keypress through as the effective key rather than exiting (q/Q remain the exit keys).