Skip to content

feat: accessibility base — mouse wheel and 16-color downconvert (#74) - #147

Merged
marcoarnulfo merged 8 commits into
mainfrom
feat/accessibility-base
Aug 1, 2026
Merged

marcoarnulfo merged 8 commits into
mainfrom
feat/accessibility-base

Conversation

@marcoarnulfo

Copy link
Copy Markdown
Owner

Closes #74 — the last two open boxes of "Accessibility base": mouse support and the truecolor→256→16 downconvert. (NO_COLOR and SetColorProfile were already closed in v1.9 tranche A.)

Design: docs/superpowers/specs/2026-08-01-accessibility-base-design.md · Plan: docs/superpowers/plans/2026-08-01-accessibility-base.md

The downconvert already existed

Measured before designing anything: termenv converts the palette down on its own, and correctly. #FF5FAF → 38;2;255;95;175 at truecolor → 38;5;205 at 256 → 95 at 16. Five of the six palette tokens land where they should.

The sixth does not. The conversion picks the nearest color, and at 16 colors there is no mid-grey, so the zebra stripe's 236 collapses to \x1b[40m (black) and 254 to \x1b[107m (bright white) — on a normal terminal, exactly the background color. The stripe disappears.

The fix gives that one token an explicit value per profile, with the 256-color index kept in the TrueColor slot so the change is surgical: truecolor, 256-color and Ascii stay byte-identical, and only the 16-color profile moves, from invisible to \x1b[100m / \x1b[47m. The two palette goldens render at truecolor and did not move — that is the guard, not a coincidence.

The wheel

A wheel notch becomes a synthetic up/down tea.KeyMsg replayed through the routing that already exists, which is the same trick the command palette uses for its own actions. Every screen that handles up/down inherits wheel scrolling without being modified.

Two contexts are deliberately gated off, both found by measurement: on the error screen and on the delete-confirmation prompt any key is an answer, so a stray notch would have dismissed an error before it was read, or cancelled a confirmation. A wheel notch is not an answer. The predicate is named anyKeyIsAnAnswer so the list has an admission rule rather than being two special cases.

The report screen does not scroll — reportModel has no cursor and no scroll offset. An earlier draft of the spec claimed it did; the whole-branch review disproved it by sweeping a wheel event through 30 screen/mode states, and the spec is corrected. Giving the report a viewport is a feature, not this issue.

The cost, and the way out

Mouse reporting takes the terminal's native text selection away — on an app people copy amounts out of, that is not a detail. Two exits: Shift while dragging in most terminals for the momentary case, and mouse: false in the config for good. Both documented in both READMEs, and the wheel now appears in the TUI-commands section too, since it is not a key.Binding and ? help structurally cannot mention it.

Verification

Every escape sequence in a comment was measured by rendering, never worked out by hand. Every test was watched failing against the specific defect it claims to catch — nine mutation proofs across the branch, each reproduced independently by a reviewer rather than taken from a report. The whole-branch review found six gaps, including two tests that passed for the wrong reason: one would have stayed green with tea.WithAltScreen() deleted outright, the other never pinned wheelKey's deliberate decision to ignore msg.Action. Both are fixed and both fixes carry their own mutation proof.

Gate clean at HEAD: gofmt -l . · go vet ./... · staticcheck ./... · go build ./... · go test ./... -race. No golden regenerated.

🤖 Generated with Claude Code

Subtle downconverts through termenv's nearest-color search like the
other five palette tokens, but at 16 colors there is no mid-grey: 236
lands on plain black and 254 on bright white, exactly the terminal
background, so the zebra row vanishes. Give Subtle an explicit
CompleteAdaptiveColor with the 256-color index kept in the TrueColor
slot, so only the ANSI (16-color) profile moves and TrueColor/ANSI256
stay byte-identical to today.
…iew (#74)

The final review of feat/accessibility-base found six issues, each verified
by running something:

- cli_test.go: TestProgramOptionsAddsOneWhenMouseIsEnabled only checked the
  difference in option count, so deleting tea.WithAltScreen() from
  programOptions still passed (0 vs 1 is still a difference of one). Pin the
  mouse-off case to exactly one option first.
- mouse_test.go: TestWheelKey never pinned wheelKey's "ignore msg.Action"
  decision — its two negative rows used non-wheel buttons, not a non-press
  wheel action. Add a wheel-down-while-moving row so reintroducing an Action
  check fails the suite instead of passing unnoticed.
- mouse.go: the doc comment cited mouse.go:249-257 as evidence the X10 parser
  never applies the motion bit to a wheel event, but line 257 is only the
  comment; the condition is at 258. Widen the range to 249-258.
- README.md / README.it.md: restore the "in most terminals" / "in quasi
  tutti i terminali" hedge on the Shift-drag text-selection workaround,
  matching config.go's own hedge (Terminal.app uses Fn, not Shift).
- docs/superpowers/specs/2026-08-01-accessibility-base-design.md: §3.1 wrongly
  listed "report" among the screens that scroll with the wheel on day one;
  reportModel has no cursor or scroll offset and updateReport has no up/down
  case. Drop it from the list and note why.
- README.md / README.it.md: the wheel and its text-selection cost were only
  documented in Configuration; add a line in the TUI commands section (the
  wheel isn't a key.Binding, so `?` help can't mention it either).
@marcoarnulfo
marcoarnulfo merged commit 0760cb4 into main Aug 1, 2026
4 checks passed
@marcoarnulfo
marcoarnulfo deleted the feat/accessibility-base branch August 1, 2026 00:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Accessibility base

1 participant