feat(kebab-tui): p9-fb-13 follow-up — verb-form hint line redesign
`pub fn footer_hints(focus: Pane, mode: Mode, filter_open: bool) -> &'static str` 신규 (run.rs). 기존 `render_footer` 의 영문 `key=action` 형식이 한국어 동사구로 — `"위로"` / `"아래로"` / `"필터"` / `"타이핑 검색어"` / `"Esc 로 NORMAL 모드"` 등 — 변경되고 (pane, mode, filter_open) 조합에 따라 자동 분기. NORMAL 모드는 navigation verbs, INSERT 모드는 typing + Esc reminder. Library filter overlay 는 overlay-only key 3 개로 override. 8 unit tests pin: 모든 (pane, mode, filter) 조합 non-empty exhaustive + Library Normal/filter, Search Normal/Insert, Ask Normal/Insert, Inspect Normal 별 verb fragment 존재 검증. spec status `in_progress` → `completed` — p9-fb-13 partial 의 deferred verb-form 항목이 닫힘.
This commit is contained in:
@@ -60,6 +60,7 @@ P0~P5 직렬. P6~P9 P5 이후 병렬 가능.
|
||||
- **2026-05-03 P9 도그푸딩 후속 (p9-fb-12 follow-up)** — heuristic 제거 (partial PR 의 deferred 부분 finalize). `search::is_typing_mod` (CTRL/ALT chord filter) 함수 삭제 + `ask::handle_key_ask` 의 input-empty heuristic 삭제. 새 dispatch: `search::handle_key_search` 의 `i` (chunk inspect) / `g` (editor jump) pre-pass 가 `state.mode == Mode::Normal` 일 때만 fire (Insert 에서는 typed char). main match 의 `j`/`k`/Char(c) 가 `state.mode` 로 분기 (Normal → 선택 이동, Insert → input.push). `ask::handle_key_ask` 의 `e`/`j`/`k` 도 동일 패턴 — Normal 에서 toggle/scroll, Insert 에서 input typing. 테스트 fixture (`tests/search.rs::fresh_app`, `tests/ask.rs::fresh_app`) 가 `app.mode = Mode::auto_for(focus)` 로 run-loop 동작 mirror. 기존 nav 테스트 (j_k_move, g_key_enqueues, e_toggles) 는 explicit `app.mode = Mode::Normal` 추가, 신규 4 테스트 (j_in_insert_types / arbitrary_char_in_normal_noop / e_types_in_insert / jk_scroll-in-normal-type-in-insert) 가 mode-authoritative 동작 pin. spec status `in_progress` → `completed`. spec: `tasks/p9/p9-fb-12-tui-mode-machine.md`.
|
||||
- **2026-05-03 P9 도그푸딩 후속 (p9-fb-10 partial)** — TUI CJK rendering helpers. `kebab-tui::input::{display_width, truncate_to_display_width}` 신규 — `unicode-width` 위에서 column-단위 width 계산 (ASCII=1, Hangul/CJK/fullwidth=2, combining=0) + char-boundary 안전 truncate (wide char 를 split 없이 keep-or-omit, ellipsis 1 col). library.rs 의 중복 `truncate_to_display_width` private fn 제거 — 단일 source. 9 unit tests (ASCII / Hangul / Japanese / mixed / truncate fits·overflow·zero-cols·wide-char-boundary / `String::pop` char-aware sanity) + 1 integration render test (Korean + Japanese fixture, TestBackend 80×20, 한글/일본어 글자가 frame 에 살아남음 확인). spec 의 `InputBuffer` struct (cursor 가 column 단위 wide-char width 추적) 도입은 follow-up — Ask/Search/Editor pane 의 String + cursor 일괄 마이그레이션이 회귀 표면이 커서 helper 만 먼저 머지. backspace 는 모든 pane 이 이미 `String::pop()` 사용 (char-aware) → byte-boundary 안전성 helper 없이도 확보. crossterm 0.28 이 native IME composing 미노출 — preedit handling out of scope. spec status `planned` → `in_progress`. spec: `tasks/p9/p9-fb-10-tui-cjk-input.md`.
|
||||
- **2026-05-03 P9 도그푸딩 후속 (p9-fb-10 follow-up)** — InputBuffer struct + 모든 text-input pane 마이그레이션 + cursor column 정렬. `kebab-tui::input::InputBuffer { content, cursor_col }` 신규 — `push_char` / `pop_char` / `clear` / `take` 가 wide-char 단위로 cursor_col 진행 (ASCII=1, Hangul/CJK=2, combining=0). `SearchState.input` / `AskState.input` / `FilterEdit.{tags_buf, lang_buf}` 가 InputBuffer 로 교체. render 단계에서 `f.set_cursor_position(...)` 가 `block.inner(area)` 기반 prompt 폭 + cursor_col 으로 caret 을 정확한 column 에 배치 (right-edge clamp). ratatui 0.28 의 cursor visibility 는 `cursor_position` Some/None 으로 자동 결정 — Search/Ask/Filter 가 `Some` 이라 caret 보임, Library/Inspect 는 `None` 이라 hidden. Korean lexical 검색은 `crates/kebab-app/tests/search_korean.rs` 에서 ingest → search → 결과 한 건 이상 + Korean 파일 stem 매칭 assert 로 회귀 핀. `lexical_query` test helper 가 `crates/kebab-app/tests/common/mod.rs` 로 promotion. spec status `in_progress` → `completed`. spec: `tasks/p9/p9-fb-10-tui-cjk-input.md`.
|
||||
- **2026-05-03 P9 도그푸딩 후속 (p9-fb-13 follow-up)** — verb-form hint line 재구성. `pub fn footer_hints(focus: Pane, mode: Mode, filter_open: bool) -> &'static str` 신규 (run.rs). 한국어 동사구 (`"위로"` / `"아래로"` / `"필터"` / `"타이핑 검색어"` / `"Esc 로 NORMAL 모드"` 등) + mode-aware (NORMAL = navigation verbs, INSERT = typing + Esc reminder) + Library filter overlay 별 분기. 8 unit tests pin 모든 (pane, mode, filter) 조합 — exhaustive non-empty + Library Normal/filter, Search Normal/Insert, Ask Normal/Insert, Inspect Normal 별 verb fragment 존재 검증. spec status `in_progress` → `completed` — p9-fb-13 partial 의 deferred verb-form 항목이 닫힘.
|
||||
- **2026-05-03 P9 도그푸딩 후속 (p9-fb-13)** — TUI cheatsheet popup. `kebab-tui::cheatsheet::render_cheatsheet(f, area, app)` 신규 — 70%/60% centered modal, sections (Global / Library / Search / Ask / Inspect) + global toggle table + 현재 focused pane footer. `App.cheatsheet_visible: bool` 필드 + `pub fn cheatsheet_visible()` getter. run loop `cheatsheet_intercept(app, key)` 가 mode_intercept 보다 먼저 dispatch — `F1` 토글 (open/close), `Esc` 가 visible 일 때 닫기 (mode_intercept 를 우회해서 cheatsheet 닫기 가 mode flip 도 발동시키지 않도록), 그 외 키는 fall-through (popup 열린 채 navigation 가능). modifier-bearing F1 (Ctrl-F1 등) 은 무시. **HOTFIXES 기록**: spec 의 `?` trigger 가 Library 의 quick-Ask binding 과 충돌해서 `F1` 으로 rebind. spec 의 verb-form hint line 재구성은 별 후속 PR (기존 footer 가 동일 역할). spec status `planned` → `in_progress` (verb hint deferral 으로 partial). spec: `tasks/p9/p9-fb-13-tui-cheatsheet.md`.
|
||||
|
||||
## 다음 task 후보
|
||||
|
||||
@@ -76,7 +76,7 @@ kebab doctor
|
||||
| `kebab inspect doc <id>` / `kebab inspect chunk <id>` | raw record 보기 |
|
||||
| `kebab ask "<query>" [--show-citations / --hide-citations] [--session <id>]` | RAG 답변 + 근거 인용. 답변 후 `근거:` block 으로 full path / line range / score 한 줄씩 (default ON — `--hide-citations` 로 끄기, pipe 시 유용). 근거 부족 시 거절. Ollama 필요. `--session <id>` 로 multi-turn — 첫 호출에서 SQLite `chat_sessions` 에 자동 생성, 이후 호출은 prior turns 를 history 로 받아 follow-up. session id 는 사용자 지정 (e.g. `kb-rust-async-2026-05`) — `kebab reset --data-only` 로 모든 session wipe |
|
||||
| `kebab doctor` | 설정/모델/DB 헬스 체크 |
|
||||
| `kebab tui` | Ratatui 셸 (Library + Search + Ask + Inspect 패널, desktop 진행 중). Library 에서 `r` 키로 background ingest 시작 — 화면 하단 status bar 가 진행 표시, 완료/abort 시 final 라인 잠시 유지 후 자동 hide. ingest 진행 중 `Esc` / `Ctrl-C` 가 cancel signal (그 외에는 quit). vim-style mode (header 우측 `-- NORMAL --` / `-- INSERT --`) — Library/Inspect 는 자동 NORMAL, Search/Ask 는 자동 INSERT. `i` 로 Normal→Insert (Library/Inspect 만), `Esc` 로 Insert→Normal 어디서나. mode-authoritative dispatch — Search 의 `j/k/i/g`, Ask 의 `e/j/k` 는 NORMAL 모드에서만 명령으로 동작, INSERT 에서는 입력 문자로 typing. **`F1` 로 cheatsheet popup** (현재 pane 의 키 매핑 + global 토글 표) — `Esc` / `F1` 로 닫기. Search 패널은 200ms debounce 후 background worker 가 검색 — 키 입력으로 UI freeze 안 됨, 사용자가 계속 타이핑하면 stale 결과 자동 폐기 (generation counter). Ask 패널은 multi-turn — 같은 conversation 안에서 Q1/A1, Q2/A2 transcript 누적, 다음 질문이 이전 턴을 history 로 받아 답변. 답변 본문은 markdown 렌더 (bold/italic/inline code/heading/list/code fence/table/blockquote, raw `**bold**` 가 실제 굵게 표시). `Ctrl-L` 로 새 conversation 시작. Search 의 `g` 키가 `$EDITOR` (기본 `vi`) 로 hit 의 citation 위치 열기 — 종료 후 TUI 화면이 자동으로 깨끗이 redraw. CLI `kebab ask` 는 raw markdown 그대로 (terminal 호환성 위해). Library 의 doc-list 가 한글 / 일본어 / 중국어 (CJK) 제목을 wide-char 정확한 column width 로 truncate — 한글 제목이 한 줄을 넘기지 않음 (CJK 1 자 = 2 col). Search/Ask/Filter 입력의 cursor 가 wide char 위에서 column 단위로 정렬 — 한글 입력 시 caret 이 글자 옆에 정확히 놓임. |
|
||||
| `kebab tui` | Ratatui 셸 (Library + Search + Ask + Inspect 패널, desktop 진행 중). Library 에서 `r` 키로 background ingest 시작 — 화면 하단 status bar 가 진행 표시, 완료/abort 시 final 라인 잠시 유지 후 자동 hide. ingest 진행 중 `Esc` / `Ctrl-C` 가 cancel signal (그 외에는 quit). vim-style mode (header 우측 `-- NORMAL --` / `-- INSERT --`) — Library/Inspect 는 자동 NORMAL, Search/Ask 는 자동 INSERT. `i` 로 Normal→Insert (Library/Inspect 만), `Esc` 로 Insert→Normal 어디서나. mode-authoritative dispatch — Search 의 `j/k/i/g`, Ask 의 `e/j/k` 는 NORMAL 모드에서만 명령으로 동작, INSERT 에서는 입력 문자로 typing. **`F1` 로 cheatsheet popup** (현재 pane 의 키 매핑 + global 토글 표) — `Esc` / `F1` 로 닫기. Search 패널은 200ms debounce 후 background worker 가 검색 — 키 입력으로 UI freeze 안 됨, 사용자가 계속 타이핑하면 stale 결과 자동 폐기 (generation counter). Ask 패널은 multi-turn — 같은 conversation 안에서 Q1/A1, Q2/A2 transcript 누적, 다음 질문이 이전 턴을 history 로 받아 답변. 답변 본문은 markdown 렌더 (bold/italic/inline code/heading/list/code fence/table/blockquote, raw `**bold**` 가 실제 굵게 표시). `Ctrl-L` 로 새 conversation 시작. Search 의 `g` 키가 `$EDITOR` (기본 `vi`) 로 hit 의 citation 위치 열기 — 종료 후 TUI 화면이 자동으로 깨끗이 redraw. CLI `kebab ask` 는 raw markdown 그대로 (terminal 호환성 위해). Library 의 doc-list 가 한글 / 일본어 / 중국어 (CJK) 제목을 wide-char 정확한 column width 로 truncate — 한글 제목이 한 줄을 넘기지 않음 (CJK 1 자 = 2 col). Search/Ask/Filter 입력의 cursor 가 wide char 위에서 column 단위로 정렬 — 한글 입력 시 caret 이 글자 옆에 정확히 놓임. 화면 하단 hint line 은 한국어 동사구로 (`"위로"` / `"아래로"` / `"필터"` / `"타이핑 검색어"` / `"Esc 로 NORMAL 모드"` 등) + 현재 (pane, mode) 조합에 맞춰 자동 분기. |
|
||||
| `kebab reset [--all / --data-only / --vector-only / --config-only] [--yes]` | XDG 데이터 wipe. **Irreversible.** TTY 면 confirm prompt, 아니면 `--yes` 필수. `--vector-only` 는 SQLite `embedding_records` 도 함께 truncate (orphan 방지) |
|
||||
| `kebab eval run / compare` | golden query 회귀 측정 |
|
||||
|
||||
|
||||
@@ -61,3 +61,6 @@ pub use run::mode_intercept;
|
||||
// for integration tests + future TUI consumers.
|
||||
pub use cheatsheet::render_cheatsheet;
|
||||
pub use run::cheatsheet_intercept;
|
||||
// p9-fb-13 follow-up: expose footer_hints so integration tests can
|
||||
// pin the verb-form per (pane, mode) without standing up the run loop.
|
||||
pub use run::footer_hints;
|
||||
|
||||
@@ -328,19 +328,7 @@ fn render_header(f: &mut Frame, area: Rect, app: &App) {
|
||||
}
|
||||
|
||||
fn render_footer(f: &mut Frame, area: Rect, app: &App) {
|
||||
let hints = match app.focus {
|
||||
Pane::Library => {
|
||||
if app.library.inner.filter_edit.is_some() {
|
||||
"Tab=field Enter=apply Esc=cancel"
|
||||
} else {
|
||||
"j/k=move gg=top G=bottom f=filter /=search ?=ask Enter=inspect r=ingest q=quit"
|
||||
}
|
||||
}
|
||||
Pane::Search => "type=query Tab=mode Enter=search j/k=move g=open in $EDITOR Esc=back",
|
||||
Pane::Ask => "type=question Enter=submit e=explain (when input empty) j/k=scroll (when input empty) Esc=back",
|
||||
Pane::Inspect => "j/k=scroll PgUp/PgDn=page scroll c=collapse/expand sections Esc/q=back",
|
||||
Pane::Jobs => "Jobs pane not yet implemented — q to return",
|
||||
};
|
||||
let hints = footer_hints(app.focus, app.mode, app.library.inner.filter_edit.is_some());
|
||||
let line = Line::from(Span::styled(
|
||||
hints,
|
||||
app.theme.style(crate::theme::Role::Hint),
|
||||
@@ -351,6 +339,49 @@ fn render_footer(f: &mut Frame, area: Rect, app: &App) {
|
||||
);
|
||||
}
|
||||
|
||||
/// p9-fb-13 follow-up: produce the footer hint text for a given
|
||||
/// `(focus, mode, filter_open)` tuple. Pure function — extracted so
|
||||
/// integration tests can pin the verb-form fragments per pane×mode
|
||||
/// without standing up the full render loop.
|
||||
///
|
||||
/// Style contract:
|
||||
/// - **Verb-form Korean fragments** (e.g. `"위로"` not `"=move"`).
|
||||
/// The original `key=action` form was English-only and read like
|
||||
/// a dev cheat-sheet, not user help.
|
||||
/// - **Mode-aware**: NORMAL shows navigation verbs;
|
||||
/// INSERT shows typing verbs + `Esc 로 NORMAL 모드` reminder.
|
||||
/// - **Filter overlay** overrides Library hints — short list of the
|
||||
/// 3 keys that work inside the overlay.
|
||||
/// - **Order**: most-frequent verb first; last fragment is always
|
||||
/// the way back out (`Esc`/`q`).
|
||||
pub fn footer_hints(focus: Pane, mode: crate::app::Mode, filter_open: bool) -> &'static str {
|
||||
use crate::app::Mode::*;
|
||||
match (focus, mode, filter_open) {
|
||||
// Library filter overlay — same on both modes (overlay
|
||||
// captures every key, mode label irrelevant).
|
||||
(Pane::Library, _, true) => "Tab 필드전환 Enter 적용 Esc 취소",
|
||||
// Library Normal: full navigation surface.
|
||||
(Pane::Library, Normal, false) => "↑/k 위로 ↓/j 아래로 gg 맨위 G 맨아래 f 필터 / 검색 ? 질문 Enter 자세히 r 인덱싱 q 종료",
|
||||
// Library Insert: degenerate — nothing types in Library, so
|
||||
// tell the user how to get back out.
|
||||
(Pane::Library, Insert, false) => "Esc 로 NORMAL 모드",
|
||||
// Search Insert: typing the query is the dominant action.
|
||||
(Pane::Search, Insert, _) => "타이핑 검색어 Tab 모드전환 Enter 검색 Esc 로 NORMAL 모드 (j/k 이동 i 인스펙트 g 에디터)",
|
||||
// Search Normal: navigation + commands.
|
||||
(Pane::Search, Normal, _) => "↑/k 위로 ↓/j 아래로 Tab 모드전환 Enter 검색 i 인스펙트 g 에디터 Esc 종료",
|
||||
// Ask Insert: typing the question.
|
||||
(Pane::Ask, Insert, _) => "타이핑 질문 Enter 전송 Esc 로 NORMAL 모드 (e 상세 j/k 스크롤)",
|
||||
// Ask Normal: scroll + toggle.
|
||||
(Pane::Ask, Normal, _) => "e 상세설명 ↑/k 위로 ↓/j 아래로 Enter 전송 Ctrl-L 새대화 Esc 종료",
|
||||
// Inspect Normal (default): scroll + collapse.
|
||||
(Pane::Inspect, Normal, _) => "↑/k 위로 ↓/j 아래로 PgUp/PgDn 페이지 c 섹션접기 Esc/q 뒤로",
|
||||
// Inspect Insert: degenerate.
|
||||
(Pane::Inspect, Insert, _) => "Esc 로 NORMAL 모드",
|
||||
// Jobs pane: placeholder.
|
||||
(Pane::Jobs, _, _) => "Jobs pane 미구현 — q 로 복귀",
|
||||
}
|
||||
}
|
||||
|
||||
/// p9-fb-12: global mode toggle interception. Returns `true` when
|
||||
/// the key was consumed (caller should `continue` and skip pane
|
||||
/// dispatch); `false` when the key should fall through to the
|
||||
@@ -429,3 +460,98 @@ pub fn cheatsheet_intercept(app: &mut crate::app::App, key: crossterm::event::Ke
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod footer_hints_tests {
|
||||
use super::*;
|
||||
use crate::app::Mode;
|
||||
|
||||
/// p9-fb-13 follow-up: Library Normal hint includes nav verbs in
|
||||
/// Korean and ends with the quit shortcut.
|
||||
#[test]
|
||||
fn library_normal_hint_uses_korean_verb_fragments() {
|
||||
let h = footer_hints(Pane::Library, Mode::Normal, false);
|
||||
assert!(h.contains("위로"), "expected 위로 verb: {h}");
|
||||
assert!(h.contains("아래로"), "expected 아래로 verb: {h}");
|
||||
assert!(h.contains("필터"), "expected 필터 verb: {h}");
|
||||
assert!(h.ends_with("q 종료"), "expected q 종료 last: {h}");
|
||||
}
|
||||
|
||||
/// p9-fb-13 follow-up: Library filter overlay overrides the
|
||||
/// usual hint with the 3 keys that actually work in the overlay.
|
||||
#[test]
|
||||
fn library_filter_overlay_hint_lists_overlay_keys_only() {
|
||||
let h = footer_hints(Pane::Library, Mode::Normal, true);
|
||||
assert_eq!(h, "Tab 필드전환 Enter 적용 Esc 취소");
|
||||
}
|
||||
|
||||
/// p9-fb-13 follow-up: Insert mode reminds user how to leave —
|
||||
/// this is the most common confusion point per the dogfooding
|
||||
/// feedback.
|
||||
#[test]
|
||||
fn insert_mode_hint_mentions_esc_to_normal() {
|
||||
for pane in [Pane::Library, Pane::Search, Pane::Ask, Pane::Inspect] {
|
||||
let h = footer_hints(pane, Mode::Insert, false);
|
||||
assert!(
|
||||
h.contains("Esc") && h.contains("NORMAL"),
|
||||
"{pane:?} insert hint must mention Esc + NORMAL: {h}"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// p9-fb-13 follow-up: Search Insert hint leads with the typing
|
||||
/// verb (the dominant action) and lists the NORMAL-only commands
|
||||
/// in parentheses so the user knows they're gated.
|
||||
#[test]
|
||||
fn search_insert_hint_leads_with_typing_verb() {
|
||||
let h = footer_hints(Pane::Search, Mode::Insert, false);
|
||||
assert!(h.starts_with("타이핑 검색어"), "should lead with 타이핑: {h}");
|
||||
assert!(h.contains("Tab 모드전환"), "expected Tab 모드전환: {h}");
|
||||
assert!(h.contains("Enter 검색"), "expected Enter 검색: {h}");
|
||||
}
|
||||
|
||||
/// p9-fb-13 follow-up: Ask Insert hint leads with typing.
|
||||
#[test]
|
||||
fn ask_insert_hint_leads_with_typing_verb() {
|
||||
let h = footer_hints(Pane::Ask, Mode::Insert, false);
|
||||
assert!(h.starts_with("타이핑 질문"), "should lead with 타이핑: {h}");
|
||||
assert!(h.contains("Enter 전송"), "expected Enter 전송: {h}");
|
||||
}
|
||||
|
||||
/// p9-fb-13 follow-up: Inspect Normal hint covers scroll +
|
||||
/// collapse + back-out.
|
||||
#[test]
|
||||
fn inspect_normal_hint_covers_scroll_collapse_back() {
|
||||
let h = footer_hints(Pane::Inspect, Mode::Normal, false);
|
||||
assert!(h.contains("위로"), "expected 위로 verb: {h}");
|
||||
assert!(h.contains("페이지"), "expected 페이지 verb: {h}");
|
||||
assert!(h.contains("섹션접기"), "expected 섹션접기 verb: {h}");
|
||||
assert!(h.contains("뒤로"), "expected 뒤로 verb: {h}");
|
||||
}
|
||||
|
||||
/// p9-fb-13 follow-up: Search Normal hint enables j/k/i/g as
|
||||
/// commands (no parens — they're first-class in Normal mode).
|
||||
#[test]
|
||||
fn search_normal_hint_lists_commands_directly() {
|
||||
let h = footer_hints(Pane::Search, Mode::Normal, false);
|
||||
assert!(h.contains("위로"), "expected 위로 verb: {h}");
|
||||
assert!(h.contains("Tab 모드전환"), "expected Tab 모드전환: {h}");
|
||||
assert!(h.contains("i 인스펙트"), "expected i 인스펙트: {h}");
|
||||
assert!(h.contains("g 에디터"), "expected g 에디터: {h}");
|
||||
}
|
||||
|
||||
/// p9-fb-13 follow-up: every (pane, mode, filter_open) tuple
|
||||
/// returns a non-empty hint — exhaustive sanity that the match
|
||||
/// covers every arm.
|
||||
#[test]
|
||||
fn every_pane_mode_combo_returns_non_empty_hint() {
|
||||
for pane in [Pane::Library, Pane::Search, Pane::Ask, Pane::Inspect, Pane::Jobs] {
|
||||
for mode in [Mode::Normal, Mode::Insert] {
|
||||
for filter_open in [false, true] {
|
||||
let h = footer_hints(pane, mode, filter_open);
|
||||
assert!(!h.is_empty(), "{pane:?}/{mode:?}/filter={filter_open} empty");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,6 +70,8 @@ bearing 변종 (Ctrl-F1 등) 은 미발동. cheatsheet 가 visible 인 동안
|
||||
힌트 문자열이 동일 역할을 하므로 사용자 경험상 누락 없음. 후속 PR
|
||||
가 mode-aware verb fragments 로 split 가능.
|
||||
|
||||
**Follow-up shipped 2026-05-03 — verb-form hint line redesign.** `pub fn footer_hints(focus: Pane, mode: Mode, filter_open: bool) -> &'static str` 신규 (run.rs). 한국어 동사구 (`"위로"` / `"아래로"` / `"필터"` / `"타이핑 검색어"` / `"Esc 로 NORMAL 모드"`) + mode-aware (NORMAL = navigation, INSERT = typing + Esc reminder) + filter overlay 분기. 8 unit tests pin (Library Normal/Insert/filter, Search Normal/Insert, Ask Normal/Insert, Inspect Normal/Insert + 모든 (pane, mode, filter) 조합 non-empty exhaustive). spec status `in_progress` → `completed`.
|
||||
|
||||
## 2026-05-03 — p9-fb-12 partial: mode machine without dispatch removal
|
||||
|
||||
**Spec amended**: `tasks/p9/p9-fb-12-tui-mode-machine.md` (status stays
|
||||
|
||||
@@ -3,7 +3,7 @@ phase: P9
|
||||
component: kebab-tui + README
|
||||
task_id: p9-fb-13
|
||||
title: "Cheatsheet popup (?) + README keymap table + verb hint line"
|
||||
status: in_progress
|
||||
status: completed
|
||||
depends_on: [p9-fb-12]
|
||||
unblocks: []
|
||||
contract_source: ../../docs/superpowers/specs/2026-04-27-kebab-final-form-design.md
|
||||
@@ -49,11 +49,16 @@ README 갱신:
|
||||
|
||||
## DoD
|
||||
|
||||
- [ ] `cargo test -p kebab-tui` 통과
|
||||
- [ ] README **TUI** 절에 키 매핑 표 + cheatsheet 안내
|
||||
- [ ] 도그푸딩: 첫 사용자가 `?` 만 알면 나머지 발견 가능
|
||||
- [x] `cargo test -p kebab-tui` 통과
|
||||
- [x] README **TUI** 절에 키 매핑 표 + cheatsheet 안내
|
||||
- [x] 도그푸딩: 첫 사용자가 `?` 만 알면 나머지 발견 가능
|
||||
|
||||
## Out of scope
|
||||
|
||||
- 사용자 정의 keymap 파일 (P+)
|
||||
- popup 의 검색 (`/` 로 키 찾기) — 우선 skip
|
||||
|
||||
## Notes
|
||||
|
||||
- 2026-05-03 partial: `?` rebound to `F1` (HOTFIXES — Library `?` 가 quick-Ask binding 과 충돌). cheatsheet popup + 기존 `render_footer` 의 pane-별 hint 시작 (영문 `key=action` 형식).
|
||||
- 2026-05-03 follow-up: verb-form hint line 재구성. `pub fn footer_hints(focus, mode, filter_open) -> &'static str` 신규 — 한국어 동사구 (`"위로"`, `"아래로"`, `"필터"`, `"타이핑 검색어"`, `"Esc 로 NORMAL 모드"`) + mode-aware (NORMAL = navigation, INSERT = typing + Esc reminder) + filter overlay 별 분기. 8 unit tests pin 한다 (Library Normal/Insert/filter, Search Normal/Insert, Ask Normal/Insert, Inspect Normal/Insert + 모든 (pane,mode,filter) 조합 non-empty exhaustive). spec status `in_progress` → `completed`.
|
||||
|
||||
Reference in New Issue
Block a user