Skip to content

feat(tui): render hardening, visible state, clipboard/export - #60

Merged
jkyberneees merged 3 commits into
mainfrom
feat/ux-polish
Sep 2, 2026
Merged

jkyberneees merged 3 commits into
mainfrom
feat/ux-polish

Conversation

@jkyberneees

Copy link
Copy Markdown
Contributor

Summary

Implements TEMP_BODEK_UX_POLISH_PLAN.md — five adversarial audits (~130 verified findings) fixed in six batches, then re-attacked by three review judges (25 findings, all addressed).

W1 — Hardening (sanitize + width discipline)

  • sanitize() now also strips C1 controls, bidi overrides/isolates, zero-width/soft-hyphen/line-separator classes; tabs stay in stored content (copy fidelity) and expand at render boundaries
  • Wire fields sanitized at ingestion: tool names (live + resumed), model, odek version, approval risk label, skill-suggestion card, submitted prompts, error markers
  • truncate() cuts by display columns (x/ansi, grapheme-safe) — CJK/emoji can no longer overflow ~40 budget sites
  • clampLines() choke point: transcript lines hard-clamped so logical rows == physical rows (mouse/scroll hit-tests stay true)

W2 — Visible state & escape stack

  • P0 fixed: the armed stop-sub-agent gate now renders in the composer footer (was invisible); armed gates render where they capture the keyboard
  • Disarmed printable runes fall through to the composer — the gate eats exactly one key
  • Events-tab selection cursor drawn (was invisible); alt+↑/↓ jumps report their landing (turn 2/7 — alt+y copies this reply)
  • Find: n next / N prev, scrolling stays live while searching; /help truth pass (phantom r removed, ^S/^K/alt+f/^Y documented)
  • Queue deletes are two-step (d arms → y/d confirms) with a visible ! armed marker

W3 — Clipboard & export

  • P0 fixed: clipboard chain = local helper (pbcopy/wl-copy/clip) → OSC 52 fallback (SSH-aware); notes are honest (copied N bytes via <tool>, OSC 52 marked unconfirmed)
  • ^Y/alt+y no longer copy raw ANSI cards
  • New /export [md|json]: current-session transcript to CWD, 0600, never overwrites (O_EXCL + suffix)

W4–W6 — Voice, rhythm, hostile terminals

  • Copy sweep: pluralization (1 matches→plural()), units (bytes), jargon bans (swarm→sub-agents, SA#→#n, ws conns→connections, trust class→always allow), single count surface for approvals, unified header separators, help key column aligned
  • Hostile terminals: status row clamped, viewport floor 1 with a composer degradation step — the View now fits at any height ≥ 8

Verification

  • make fmt ✓ make vet ✓ make lint 0 issues ✓ make test (-race, all packages) ✓
  • New regression suites: w1_hardening_test.go, w2_visible_state_test.go, w3_copy_export_test.go, w6_hostile_test.go
  • README key table + troubleshooting synced

Deliberately deferred (next milestone)

  • O(n²) streaming append (strings.Builder per segment) and resize debounce — performance work, sized separately
  • Footer micro-separator tint drift; answer-card 2-col inset (boxed-surface design choice)

🤖 Generated with odek

- sanitize: C1 controls, bidi overrides, zero-width classes; tabs expand
  to columns; tool/model/version/risk wire fields collapsed at ingestion
- truncate(): display-column cut via x/ansi — CJK/emoji can no longer
  overflow budgets; conversation lines hard-clamped so hit-tests stay true
- visible state: armed gates render at the composer, disarmed runes fall
  through, events tab + copy cursor drawn, find n/N + live scroll,
  queue two-step delete, /help truth pass, jump landing notes
- clipboard: pbcopy/wl-copy/clip exec chain with honest notes, OSC 52
  fallback for SSH; ^Y no longer copies raw ANSI cards
- /export [md|json]: owner-only 0600, never overwrites
- voice sweep: pluralization, units, jargon bans, single count surface
- hostile terminals: status row clamped, viewport floor 1 with composer
  degradation step
- clampLines: per-line truncate, never pad; offsets track the clamped
  text so hit-test indices cannot drift (P1)
- jumpTurn landing notes batch their sweep cmd — they fade instead of
  sticking forever (P1)
- queue: armed row disarms on moves and head drains; armed state gets a
  visible ! marker in mouse mode too (P2)
- relayout ladder skips the composer shrink while an approval panel owns
  the input area (P2)
- tabs survive sanitize for copy fidelity; display paths (truncate,
  clampLines) expand them where cell math happens (P2)
- writeExport stat loop bounded (P2)
- find bar: width budget from live chrome + hard clamp — the bar can
  no longer wrap on narrow terminals; pasted queries sanitized; empty
  query drops the empty quotes
- clampLines: tabs expand before measuring, so a passing line can never
  render past the viewport
- queue: y joins d as the confirm verb (matches every other gate); the
  armed hint explains itself in mouse mode too; transient note dropped
- jump landing note keys off the landed line — it can no longer name a
  turn alt+y won't copy
- clipboard: small payloads write synchronously (no goroutine leak on a
  failed helper start)
- writeExport: O_EXCL create — a race can no longer truncate a file
- statusLine shear marked with an ellipsis
@jkyberneees
jkyberneees merged commit cf497b6 into main Sep 2, 2026
7 checks passed
@jkyberneees
jkyberneees deleted the feat/ux-polish branch September 2, 2026 07:44
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