Skip to content

feat(tui): UX ergonomics round — clear confirm, OSC 52 copy, transcript find - #39

Merged
jkyberneees merged 5 commits into
mainfrom
feat/ux-ergonomics
Aug 29, 2026
Merged

feat(tui): UX ergonomics round — clear confirm, OSC 52 copy, transcript find#39
jkyberneees merged 5 commits into
mainfrom
feat/ux-ergonomics

Conversation

@jkyberneees

Copy link
Copy Markdown
Contributor

Top-3 UI/UX & ergonomics improvements, selected by code audit and validated by a 3-judge agent panel (majority rules; proposal in UX_PROPOSAL.md, kept out of the tree).

Judge verdicts

Improvement Accuracy Feasibility Quality Result
P1 two-step confirm for ^L//clear approved 3/3
P2 OSC 52 copy-last-reply approved 3/3
P3 transcript find ❌ (scope) approved 2/3 — implemented as the minimal slice

P1 — feat(tui): two-step confirm for ^L and /clear

The whole transcript + all session telemetry was one accidental ^L from gone, while deleting a single session/fact in the panels already required the d→y gate. ^L and /clear now arm confirmClear: y fires, any other key disarms, the composer footer shows the danger hint. /clear also gains the idle-only rule (^L always had it), so a mid-turn wipe can't drop the view out from under a streaming turn.

P2 — feat(tui): copy the last reply via OSC 52 (^Y, /copy)

The #1 post-turn action (pasting the answer elsewhere) had no keyboard path. ^Y//copy copy the latest finalized assistant reply via ansi.SetSystemClipboard. Emission goes through tea.Exectea.Println output is dropped on the alt-screen — so the sequence reaches the terminal verbatim. Payload stays local (consumed by the terminal emulator); >100 KB replies are refused, not truncated; unsupported terminals silently ignore the sequence and the transient note says so. charmbracelet/x/ansi promotes from indirect to direct — no new dependency.

P3 — feat(tui): transcript find bar (alt+f)

Long sessions had no content search — turn jumps/fold navigate by position only. alt+f opens a one-row find strip: live match count across message text, reasoning/reply segments, and tool steps (names/args/results/sub-agent logs); next, N previous, esc closes. The bar captures the keyboard while open; the ^L confirm chord stays live through it. Per the quality judge's scope concern, inline markdown highlighting is deliberately excluded — navigation + count only.

Verification

  • TDD throughout: RED tests first (clear_confirm_test.go, clipboard_test.go, find_test.go), then implementation to green; 4 existing tests deliberately updated to the new confirm contract, never weakened.
  • make fmt vet lint test all green: gofmt clean, vet clean, golangci-lint 0 issues, full race suite green.
  • The working tree was rebuilt into 4 conventional commits and verified byte-for-byte against the pre-split snapshot (tracked files); the one intentional delta (the ^L-through-find routing fix) is covered by TestClearClosesFind.

Notes

  • README key bindings / commands synced in the same change set (docs commit).
  • No odek-side changes; pure front-end per project rules.

The whole transcript — plus every turn stat, tool count, and context
counter — was one accidental ^L away from gone, while deleting a single
session or fact in the panels already required the d→y two-step gate.
Arm the same confirmClear gate instead: y fires the wipe, any other key
disarms, the composer footer shows the danger hint. /clear gains the
same idle-only rule ^L always had, so a mid-turn wipe can no longer
drop the view out from under a streaming turn.
The #1 post-turn action — pasting the answer into a PR, issue, or
editor — had no keyboard path: selecting long markdown inside a
full-screen alt-buffer TUI is clunky, especially without --mouse.
^Y and /copy now copy the latest finalized assistant reply via OSC 52.
The sequence is emitted through tea.Exec so it reaches the terminal
verbatim (tea.Println output is dropped on the alt-screen), the payload
is consumed by the local terminal emulator only, and oversized replies
are refused rather than silently truncated. Terminals without OSC 52
support ignore the sequence; the note says so. Promotes
charmbracelet/x/ansi from indirect to direct — no new dependency.
Long agent sessions had no way to search content: turn jumps and
folding navigate by position only. alt+f opens a one-row find strip
above the composer — typed runes filter matches live across message
text, reasoning/reply segments, and tool steps (names, args, results,
sub-agent logs); enter jumps forward, N back, esc closes. The bar
captures the keyboard while open, and the ^L clear-confirm chord stays
live through it. Matches map to message blocks through a per-message
line index built alongside the existing turn/step indexes, so jumps
land with a line of context above the target.
alt+f find bar, ^Y OSC 52 copy, and the two-step confirm on ^L and
/clear.
Every uncovered block in the UX round's new code now has a test:
find-bar key routing (ctrl+c quit, backspace incl. the empty-query
no-op, busy ctrl+l guard), per-kind matching across reasoning/reply
items and step name/arg/result/logs, stale-match jumps, close
idempotence, find-bar render states (hint, count, truncation), and
the clipboardWrite runner (nil-writer guard, verbatim write, error
propagation).

All functions in find.go and clipboard.go are at 100% statement
coverage; SetStdin/SetStderr stay empty-bodied (zero instrumentable
statements) and are exercised by TestClipboardWriteRunner.
@jkyberneees
jkyberneees merged commit 9480439 into main Aug 29, 2026
5 checks passed
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.

1 participant