Skip to content

feat(tui): keyboard-navigable chapter palette#13

Merged
ivanmaierg merged 7 commits into
mainfrom
feat/palette-chapter-picker
May 13, 2026
Merged

feat(tui): keyboard-navigable chapter palette#13
ivanmaierg merged 7 commits into
mainfrom
feat/palette-chapter-picker

Conversation

@ivanmaierg

Copy link
Copy Markdown
Owner

Summary

  • Add a fully keyboard-navigable chapter picker that opens after selecting a book in the palette.
  • Users can now: pick a book → land in chapter phase → navigate the chapter grid with arrows → Enter to commit chapter and open the verse picker.
  • Direct references (e.g. John 3:16) still parse and load the passage immediately — no UX regression.

Changes

  • src/domain/book-chapters.ts — new module with the 66-entry canonical chapter counts.
  • src/tui/reader/reader-reducer.ts
    • New phases (book / chapter), intents (view / pick-verse), and verse-picker state.
    • Chapter grid actions: ChapterGridMoved {Up,Down,Left,Right} (10-wide stride for Up/Down).
    • QueryTyped decodes trailing digits as chapter highlight (John 10 → highlight 10).
    • SuggestionAccepted (Enter) commits the highlighted chapter into the verse picker; queries containing : still parse as direct refs.
    • fzf-style auto-highlight: top suggestion selected by default so Tab/Enter accept the obvious choice.
    • All key + lookup paths are now case-insensitive.
  • src/tui/tui-driver.tsx — driver gate routes arrow keys to ChapterGridMoved* when phase is chapter, separate from verse-picker keys.
  • src/tui/reader/ — chapter grid + verse-picker overlay views.

Testing

  • 235 reader/domain/TUI tests pass (reader-reducer.test.ts covers all new transitions: digit-suffix highlight, grid arrow nav, Enter-commits-chapter, fzf auto-highlight, case-insensitive lookups).
  • 2 pre-existing failures in src/cli/loading.test.ts — unrelated to this branch (present on main); tracked separately.
  • Manual TUI verification recommended before merge.

User feedback: 'also make it everything not case sensitive'.

Three sites normalized:

1. chaptersForBook(canonical): upper-cases the input before BOOK_CHAPTERS
   lookup. chaptersForBook('gen') and chaptersForBook('GEN') both return 50.
2. bookIdFromCanonical(canonical): upper-cases before delegating to
   makeBookId. bookIdFromCanonical('jhn') returns 'JHN' as BookId.
3. tui-driver useKeyboard handler: normalizes keyEvent.name to lowercase
   once at the top, so all letter keybinds (q, n, p) accept either case
   (Q, N, P with Shift) without explicit pair-checks. Special keys (up,
   down, tab, escape, [, ], /) are already lowercase emitted by OpenTUI.

makeBookId itself stays strict — it's the domain invariant gate; only
canonical uppercase IDs cross it. The two helpers above wrap it with
case-insensitive input handling.

Tests: 208 → 214 (+6 case-insensitivity assertions). tsc clean.
Bug: pressing Tab on a book suggestion showed the chapter grid for
exactly one render, then immediately disappeared back to book mode.

Root cause: OpenTUI's controlled <input> synthesizes an onInput event
whenever its value prop changes — including programmatic changes.
SuggestionAccepted rewrites state.query to '{DisplayName} ' (e.g.
'John ') to autocomplete the book token. That value change fires
onInput with 'John ', which dispatches QueryTyped. QueryTyped's old
handler unconditionally reset phase to 'book', clearing bookChosen
and chapters.

Fix: QueryTyped now preserves phase: 'chapter' when the new query
still has the chosen book's display name as a case-insensitive prefix
(e.g. 'John ', 'John 3', 'JOHN 3:16' all keep chapter mode). Free-
typing past the book name (e.g. backspacing to 'Joh') correctly drops
back to book mode. This is also the foundation for typing chapter
digits to filter the chapter list in a future iteration.

Tests: +2 (chapter-phase preservation, case-insensitive variant);
existing 'resets to book phase' test still passes because its query
'j' doesn't have the book prefix. 214 → 216. tsc clean.
…apter

- Add ChapterGridMoved {Up,Down,Left,Right} actions for 10-wide stride grid nav
- Decode trailing digit suffix in query to highlight chapter (e.g. "John 10")
- Enter in chapter phase commits highlighted chapter into verse picker;
  query containing ':' still routes through parseReference for direct refs
- Auto-highlight top book suggestion (fzf-style) so Tab/Enter accept without arrow-down
- Driver gates arrow keys to grid actions when phase is chapter
@ivanmaierg ivanmaierg merged commit 3d7eccb into main May 13, 2026
1 check passed
@ivanmaierg ivanmaierg deleted the feat/palette-chapter-picker branch May 13, 2026 02:09
ivanmaierg added a commit that referenced this pull request May 13, 2026
Archive two verified-PASS SDD changes and merge the cli-loading-state
delta into openspec/specs/cli/spec.md as the new canonical CLI spec.

- cli-loading-state: PASS, 99/99 tests, withLoading helper shipped
- palette-chapter-picker: PASS WITH WARNINGS (PR #13); warnings carried
  forward in the archive report (dead-code actions, spec wording)
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