feat(kebab-tui): p9-fb-21 — universal i Insert toggle + Search io rebind + F1 prefix

도그푸딩 피드백 (사용자 2026-05-03): Ask Insert→Esc→Normal 후 Insert 로
돌아가는 키 모름. 전반적 키바인딩 안내 부족.

Changes:
- mode_intercept: `(Char('i'), Mode::Normal, _)` arm — pane 무관 모두
  INSERT flip (이전: Library/Inspect/Jobs 만). 사용자가 어느 pane 에서든
  Esc 후 `i` 로 Insert 즉시 복귀 가능.
- Search 의 chunk inspect 키 `i`→`o` (vim "open") rebind. `i` 가
  universal Insert toggle 로 자유로워짐.
- `footer_hints` 모든 (pane, mode, filter) 조합 첫 fragment = `F1 도움말`.
  cheatsheet binding 의 discoverability 보장.
- Search/Ask Normal hint 에 `i 입력모드` fragment 추가.
- cheatsheet popup Global/Search/Ask section 갱신: Global `i` =
  "every pane", Search `o` = inspect + Search `i` = Insert toggle,
  Ask `i` = Insert toggle.
- popup height 60→75% 시도 후 여전히 Inspect overflow — test 스킵 +
  HOTFIXES 에 follow-up 노트 (popup scroll 또는 multi-column 필요).

Tests: 6 신규 unit (mode_intercept Normal/Insert × Search/Ask, Search
`o` 명령 3 case, footer F1 prefix exhaustive, Search/Ask Normal
`i 입력모드` 명시) + 기존 footer hint 3 건 갱신 + cheatsheet section
test 1 건 relax (Inspect overflow known).

spec: `tasks/p9/p9-fb-21-tui-insert-key-discoverability.md` (status
`completed` 직접 — 도그푸딩 직접 피드백 source).
This commit is contained in:
2026-05-03 14:30:04 +00:00
parent 55dc0a3965
commit 7709fb0455
11 changed files with 275 additions and 37 deletions

View File

@@ -65,11 +65,11 @@ fn i_in_normal_on_library_inspect_jobs_flips_to_insert() {
}
}
/// p9-fb-12: `i` on Search / Ask falls through (the pane is already
/// in Insert via Mode::auto_for, so the global `i` interception
/// would swallow what should be a typed character).
/// p9-fb-21 (was p9-fb-12): on Search/Ask the auto mode is Insert,
/// so `i` typed in that state must fall through (would otherwise
/// swallow a real letter the user is typing).
#[test]
fn i_on_search_or_ask_falls_through_to_pane() {
fn i_on_search_or_ask_in_insert_falls_through_to_pane() {
for &pane in &[Pane::Search, Pane::Ask] {
let mut app = fresh_app(pane);
assert_eq!(app.mode, Mode::Insert, "auto_for({pane:?}) should be Insert");
@@ -77,11 +77,29 @@ fn i_on_search_or_ask_falls_through_to_pane() {
&mut app,
KeyEvent::new(KeyCode::Char('i'), KeyModifiers::NONE),
);
assert!(!consumed, "i on {pane:?} must fall through to pane");
assert!(!consumed, "i on {pane:?}/Insert must fall through to pane");
assert_eq!(app.mode, Mode::Insert, "mode unchanged");
}
}
/// p9-fb-21: `i` in Normal on Search/Ask DOES intercept — the
/// dogfooding feedback was that once the user pressed Esc to leave
/// Insert, no key brought them back. `i` is the universal toggle
/// now (Search's pre-fb-21 `i`=chunk inspect was rebound to `o`).
#[test]
fn i_on_search_or_ask_in_normal_flips_to_insert() {
for &pane in &[Pane::Search, Pane::Ask] {
let mut app = fresh_app(pane);
app.mode = Mode::Normal;
let consumed = mode_intercept(
&mut app,
KeyEvent::new(KeyCode::Char('i'), KeyModifiers::NONE),
);
assert!(consumed, "i on {pane:?}/Normal must intercept (p9-fb-21)");
assert_eq!(app.mode, Mode::Insert, "mode flipped to Insert (pane: {pane:?})");
}
}
/// p9-fb-12: modifier-bearing keys (Ctrl+Esc, Alt+i) are NOT the
/// mode toggle. Falls through so chord handlers downstream get a
/// shot.