feat: theming (#53), v1/v2 ADR (#55), platform docs (#58), pager (#56), flake fixes (#63) - #62
Conversation
The SHELLFRAME_* presentation globals are plain variables read at render time, so a theme is just reassignment. Shipped themes: default (terminfo derived, raw-ANSI fallbacks) and mono (attributes only, zero color — NO_COLOR-style deployments and screenshot tests). Custom themes are any file assigning the constants, loaded by path; unknown names and missing files fail without changing the active theme. Also defines SHELLFRAME_YELLOW (toast.sh referenced it with a fallback; now first-class) and documents the POSIX-environment requirement with an msys/cygwin load-time warning (#58). Theme unit table: 13 assertions covering load/restore/isolation/failure.
Review of
|
Follow-up to the review above — three more must-fixes (verified on
|
TERM=dumb, source shellframe.sh |
GREEN | GRAY | RESET |
|---|---|---|---|
main (9631239) |
'' |
'' |
'' |
this PR (9bb5f5f) |
\E[32m |
\E[90m |
\E[0m |
That contradicts the file's own header ("degrade to empty strings") and changes what consumers get when piping v1 widget output into logs, cron, ssh host cmd, or CI (the Docker matrix runs without a tty and Alpine has no tput — every leg now exercises the raw-escape path, and nothing asserts emptiness). mono.sh has the same || printf on BOLD/DIM while default.sh uses || true for them, so toggling themes flips panel.sh:173's bold gate on a tput-less terminal. Restore || true everywhere (one fallback policy), and let mono.sh assign only the six color globals.
Should fix
- Source-time side effect —
shellframe.sh:14writes two stderr lines on msys/cygwin atsourcetime; repo CLAUDE.md:129 says the library must be side-effect-free until a function is called (a consumer doing$(… 2>&1)or CI failing on non-empty stderr breaks before calling anything). Defer the warning toshellframe_screen_enter, or set aSHELLFRAME_PLATFORM_UNSUPPORTEDflag and let the first widget warn. - Custom-theme name rule — only
*/*is treated as a path, soshellframe_theme_load mytheme.shin the themes dir is rejected as an unknown built-in; the rule isn't documented. Simpler: bare name →$_SF_THEMES_DIR/$name.shif it exists, else path; glob the dir fortheme_listinstead of hardcoding the roster in three places. - Overlay semantics undocumented — a custom file is sourced over whatever theme is active, so a partial theme gives order-dependent results, and any custom theme written before this PR lacks
SHELLFRAME_YELLOW(whichtoast.sh:102now reads). Either sourcedefault.shfirst in the custom branch or document "assign every global". docs/api.md:211is stale — still says constants come from tput at source time; omits DIM/REVERSE/YELLOW; and the theming section'sNO_COLORsentence implies auto-detection that doesn't exist (see item 2 in the review above).- Perf nit — 11–12
tputforks per library source (~26 ms of a ~46 ms load on bash 3.2). A singletput -Sbatch gives byte-identical values in ~5 ms; or skip the probe whenTERMis unset/dumb ortputis absent.
Reproductions on macOS bash 5.3.15 (TERM=dumb, OSTYPE=msys); the standalone-source and mono checks also on /bin/bash 3.2.57.
🤖 Generated with Claude Code
…dation Four must-fixes from PR #62 review round 2: - draw.sh resolves its own directory (BASH_SOURCE-based) and sources standalone again — the LEGO path aborted under set -u and silently produced no colors without it. - Failed custom-theme sources now snapshot the palette and restore it: rc is 1, SHELLFRAME_THEME unchanged, no half-applied state. - mono actually produces zero color: 37 widget sites read theme constants with ${VAR-fallback} dash form so deliberate empties suppress hardcoded ANSI (:- fires on empty too). Lint assertion prevents reintroduction; rendered-contract test added. - Degradation contract restored: terminfo failure yields EMPTY constants (plain text), not raw SGR — TERM=dumb/cron/CI match main. Should-fixes: batched tput -S query (one fork for ten capabilities, line-count-validated against silent busybox-style degradation); msys/cygwin warning moved into shellframe_platform_check (no source- time side effects); path/overlay/failure semantics documented in api.md; src/themes dir replaced by apply functions. Suite 1602/1602 across 55 files.
The 3.2 container has no usable TERM, where the default theme
legitimately produces empty color constants (the documented degradation
contract). Assertions now distinguish declared-vs-nonempty, use custom
full-palette files for deterministic value round-trips, and keep only
truly invariant checks absolute (mono empties colors everywhere; dumb
TERM degrades to plain text). Also: mono loop used ${!_c} indirect
expansion that tripped set -u — routed through eval.
Matrix 3/3 PASS.
Review round 2 response — all four must-fixes and five should-fixes addressedMust-fixes:
Should-fixes:
Verification: suite 1601/1601 across 55 files; Docker matrix 3/3 PASS (the previous run's 3.2/4.4 failures were the environment-assuming assertions above). |
Round-3 verdict: approved — merge-ready (
|
| Item | Result |
|---|---|
source status / fail-safe |
bad theme file → rc 1, palette intact, SHELLFRAME_THEME stays default (3.2 and 5.3) |
Standalone src/draw.sh |
bash -u source succeeds, constants set; test-toast.sh no longer prints the error |
| mono zero-color | 0 color-defaulting :- sites remain (the two left are :-} empty defaults, harmless); RED is genuinely empty after mono |
| Degradation | TERM=dumb: all empty, identical to main; normal TERM: constants byte-identical to main |
| Source-time side effects | 0 stderr lines with OSTYPE=msys; shellframe_platform_check present |
| Docs | path rule, overlay semantics, snapshot/restore, DIM/REVERSE/YELLOW all in docs/api.md |
| Tests | test-theme.sh 12/12 on /bin/bash 3.2 under UTF-8 and with TERM unset — the environment-honest rewrite holds |
| Suite | 1601/1601 assertions passed across 55 file(s) against ptyunit 1.6.1; both CI legs green |
Correction (not blocking): the tput -S batch never takes effect — src/draw.sh:49-50
tput -S emits the capabilities back-to-back with no separators (macOS and ncurses alike):
$ printf 'bold\ndim\nsgr0\n' | tput -S | od -c
0000000 033 [ 1 m 033 [ 2 m 033 ( B 033 [ m
so grep -c '' <<< "$_caps" is 1, never 10, the guard fails, and the per-capability fallback runs every time. Net effect measured on this machine: 11 tput execs per library load vs 9 on main, ≈57 ms vs ≈47 ms — a small regression rather than the intended win. Two honest options: (a) interleave cr in the request list and split on \r (IFS=$'\r' read -r -d '' -a) — that's what makes the batch parseable; or (b) drop the batch and keep the ten-fork path, which is simpler and matches main. Either is fine post-merge; I'd just not leave dead code that claims a speedup.
After merge: feat: commits → v0.6.0.
🤖 Generated with Claude Code
…ound 3) tput -S emits capability results consecutively with no separators, so the 10-line validation guard always failed and every load took the per-capability fallback: net 11 forks vs main's 9, a small regression presenting as a win. Dropped in favor of the proven per-capability queries (+YELLOW), with an honest comment explaining why batching does not work here.
Round 3 follow-up — batch dropped (option 2)Confirmed empirically before choosing: Dropped the batch; Suite 1601/1601; theme table 12/12 on bash 5.3 and /bin/bash. |
|
Confirmed on
🤖 Generated with Claude Code |
* test: deterministic stdin-detach fixture; frozen-clock throttle tests (#63) The #44b idle-to-EOF integration test flaked ~2/7 on the macOS 3.2 CI leg (rc 124): EOF arrival depended on a fifo writer's wall-clock hold racing runner scheduling. The fixture now causes EOF from inside an on_key handler (exec 0</dev/null on a sentinel key), so key ordering — not elapsed time — decides. Idle-survival coverage remains at the reader level (test-read-eof.sh held-fifo cases), which are race-free by construction: no data ever arrives, so a timeout tick is guaranteed and asserted. Same treatment for the #51 throttle decision table: frozen clock stub instead of two live now() calls that could straddle the 33 ms window under load (measured 6/20 in a loaded bash 5 container). Verified: host 10/10, /bin/bash 3.2 5/5, container 3.x 3/3, throttle-loop 5/5 — zero flakes. * feat(pager): scrollback escape hatch for tables, action-lists, v2 lists (#56) New src/pager.sh: shellframe_pager_requested (SHELLFRAME_DUMP=1), shellframe_dump_lines (ANSI/C0-stripped plain-text dump), and shellframe_pager_view (exits alt screen + restores cooked tty, runs ${PAGER:-less} with stdio on /dev/tty so the $() contract holds). Wired: - v1 table + action-list: 'v' builds a sanitized dump and suspends to the pager using each widget's saved-stty global; redraw on return. SHELLFRAME_DUMP=1 prints the dump to stdout and skips the TUI. - v2 list regions: 'v' returns rc 4 with SHELLFRAME_PAGER_FILE; the shell runtime owns suspension (its saved stty) and force-rebuilds the screen afterwards. PTY-validated round trip on both architectures: content visible in PAGER=cat, chrome redrawn intact after. Grid/menu wiring follows the same three-line pattern; left as contributor follow-ups. * fix(pager): honor PAGER arguments; dump_lines reuses sanitizer (#62 review) - PAGER='less -R' silently fell back to cat: the whole string was type-checked as one binary. Now split into words, probe the binary, and warn on stderr when falling back. - shellframe_dump_lines had a bespoke sed strip that leaked OSC payloads (\033]0;t\007plain → '0;tplain'); it now routes every line through shellframe_sanitize (#45), matching full CSI/OSC/DCS/nF + C0 coverage on BSD and GNU sed alike. Tests: OSC-payload dump case (unit), PAGER-with-args round trip and missing-pager fallback warning (PTY).
Summary
Closes the remaining PM-approved items plus two review-driven fixes:
shellframe_theme_load(shippeddefault/mono, custom paths), fail-safe snapshot+restore, batched→per-capability tput (see round-3 note)shellframe_platform_checksrc/pager.sh(shellframe_pager_view,shellframe_dump_lines,SHELLFRAME_DUMP=1mode) wired into v1 table/action-list and v2 list regionsAlso: defines
SHELLFRAME_YELLOW; corrects lesson #10 per reviewer challenge; README fd-3 guidance; environment-honest theme tests.Review history
Rounds 1–3 findings (standalone sourcing, mono
:-leak, degradation regression, broken save-fd probe, source-status handling) all addressed with regression tests; reviewer verified nine items independently at5aad0ab.Test plan
Suite 1606/1606 across 55 files; Docker matrix 3/3 PASS (bash 3.2/4.4/5.x); PTY round-trips for pager on both architectures; stability loops 10×5×3 zero flakes on #63.