feat(config-settings): replace the ANSI renderer with a curses front-end
config-settings is now a launcher for scripts/config-settings-tui.py, drawn with Python's stdlib curses. The seven fish files that hand-rolled the ANSI renderer are gone, along with the golden harness that had to pin their byte-exact output. The TUI is a child process, so it can neither read the session's global variables nor write them. State goes in as a dump from the new __config_settings_state; the edits come back as a fish script that config-settings sources, which is what lets the Session page's `set -g` land in the caller's shell instead of in a child that is about to exit. Every edit is emitted as a call to __config_settings_apply or __config_settings_set_value, so list splitting, the SCROLLBACK_HISTORY_* export mirror and the shadow-warning suppression all stay in the fish layer that already owned them. The consequence, and the one behaviour change: edits are applied in one batch on exit rather than on each keypress. The status bar shows a pending count. New: `/` filters the current page, and on the Universal and Session pages it reaches into every category's sub-categories, listing hits as "Category › Sub" so a sub-category can be toggled without drilling into its parent first. Also a `?` help overlay, mouse selection, and a drill-down page that leads with the category's own toggle. Gone with the renderer: the four width tiers, the wrap-aware erase arithmetic, the stty/dd/od raw key reader, the panel-height bookkeeping and the hand-written redraw differ. curses owns all of it, and the alt screen plus absolute addressing makes the desync class behind608b022,4210f3b,93fc5e0and3c4f720unreachable. The sub-category taxonomy is NOT duplicated in Python: it travels in the state dump, still sourced from __config_settings_subcats. The category, Sponge and Paths row tables move into Python, consolidating the two copies the fish renderers kept. Dependency: python3 with curses. Stdlib on Arch, Fedora and a full Debian/Ubuntu python3; python3-minimal alone lacks _curses. The launcher checks for both and names what is missing. Called out in the README. Verified: 416/416 assertions, plus a live end-to-end in a sandbox HOME confirming the Universal page writes universal scope only (U1/G0) and the Session page global scope only (U0/G1).
This commit is contained in:
@@ -0,0 +1,76 @@
|
||||
# Copyright (C) 2026 Rootiest
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
# DEPENDENCIES
|
||||
# __config_settings_get_val, __config_settings_get_raw,
|
||||
# __config_settings_subcats
|
||||
#
|
||||
# SYNOPSIS
|
||||
# __config_settings_state
|
||||
#
|
||||
# DESCRIPTION
|
||||
# Dumps everything scripts/config-settings-tui.py needs to render the
|
||||
# settings TUI: the current value of every opinionated-component,
|
||||
# sponge, scrollback and user-dots variable, plus the sub-category
|
||||
# taxonomy from __config_settings_subcats.
|
||||
#
|
||||
# Two record types, separated by RS (0x1e), fields separated by US
|
||||
# (0x1f). Both are ASCII control characters, so no value these variables
|
||||
# can hold needs escaping on the way through:
|
||||
#
|
||||
# var<US><scope><US><name><US><value>
|
||||
# sub<US><category_var><US><slug><US><label><US><description>
|
||||
#
|
||||
# Only variables that are actually set are emitted; the TUI renders an
|
||||
# absent variable as DEFAULT. That is what keeps the variable list out of
|
||||
# this function -- names are discovered with `set --names` and filtered by
|
||||
# prefix, so a new sub-category needs no edit here, only in
|
||||
# __config_settings_subcats.
|
||||
#
|
||||
# Toggle variables are dumped once per scope (universal and session), since
|
||||
# the TUI edits those scopes independently. Sponge and Paths variables are
|
||||
# universal-only and dumped as the value the running shell resolves,
|
||||
# space-joined for list variables.
|
||||
#
|
||||
# EXIT STATUS
|
||||
# 0 Always
|
||||
#
|
||||
# RETURNS
|
||||
# The RS/US-separated state dump, printed to stdout
|
||||
#
|
||||
# EXAMPLE
|
||||
# __config_settings_state | string split \x1e
|
||||
function __config_settings_state --description 'Dump config-settings state for the curses TUI'
|
||||
# ── Variable values ───────────────────────────────────────────────────
|
||||
for name in (set --names)
|
||||
switch $name
|
||||
case '__fish_config_op_registry_*'
|
||||
# conf.d data table, not a setting -- shares the op_ prefix.
|
||||
continue
|
||||
case '__fish_config_op_*' __fish_config_opinionated
|
||||
# Toggles: both scopes, independently editable.
|
||||
for scope in universal session
|
||||
set -l val (__config_settings_get_val $name $scope)
|
||||
test "$val" = DEFAULT; and continue
|
||||
printf '%s\x1f%s\x1f%s\x1f%s\x1e' var $scope $name $val
|
||||
end
|
||||
case 'sponge_*' __fish_sponge_extra_sensitive \
|
||||
'__fish_scrollback_history_*' __fish_user_dots_path \
|
||||
__fish_user_dots_symlink
|
||||
# Value rows: universal-only, list variables space-joined.
|
||||
set -l val (__config_settings_get_raw $name)
|
||||
test "$val" = DEFAULT; and continue
|
||||
printf '%s\x1f%s\x1f%s\x1f%s\x1e' var universal $name $val
|
||||
end
|
||||
end
|
||||
|
||||
# ── Sub-category taxonomy ─────────────────────────────────────────────
|
||||
for cvar in __fish_config_op_aliases __fish_config_op_autoexec \
|
||||
__fish_config_op_overrides __fish_config_op_integrations \
|
||||
__fish_config_op_logging __fish_config_op_greeting
|
||||
for row in (__config_settings_subcats $cvar)
|
||||
set -l f (string split -- \t $row)
|
||||
printf '%s\x1f%s\x1f%s\x1f%s\x1f%s\x1e' sub $cvar $f[1] $f[2] $f[3]
|
||||
end
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user