Wire `format_doc_header` into `render_doc_list`: render the block
independently, split block_inner into a 1-row header + list via
vertical Layout, and drop the `.block(block)` from the List widget.
Remove `#[allow(dead_code)]` from `format_doc_header` now that it
is consumed. Add `library_renders_column_header_row` integration test.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Refactor the Inspect pane's PageDown/PageUp handlers to consume the
PAGE_STEP constant from pager.rs instead of hard-coding 10. Adds
regression tests to pin the scroll delta (=10), ensuring future
viewport-aware refactors surface here rather than silently in
user-visible behaviour.
Test coverage: added page_down_scrolls_by_ten_in_inspect and
page_up_rewinds_by_ten_saturating_in_inspect (+ existing
page_keys_scroll_by_ten still passes).
Remove #[allow(dead_code)] from pager.rs now that PAGE_STEP is
consumed.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
도그푸딩 피드백 3 건 (Library 컬럼 헤더 부재, PgUp/PgDn 페이지 스크롤,
모든 모드에서 항상 떠 있는 상태바 + 키 안내바 + 버전 정보) 을 단일
spec 으로 묶음.
설계 핵심:
- bottom 영역을 2 row 로 분할: 윗줄 = 상태바 (`kebab v0.1.0 │ pane │
doc_count │ 동적 상태`), 아랫줄 = 기존 footer_hints 그대로 이전.
- ingest progress 의 dedicated row 를 status bar 의 동적 영역으로 흡수
(시각적 source 단일화).
- Library `List` 위에 `format_doc_header` 헤더 row 추가 (TITLE / TAGS
/ UPDATED / CHUNKS, display-width 정렬, Role::Heading).
- Ask + Inspect 양쪽에 PgUp/PgDn (fixed step 10). Ask 는 j/k 와 동일
하게 follow_tail = false 로 freeze.
p9-fb-13 (footer 단행 row) + p9-fb-03 (ingest dedicated row) frozen
spec 들과 layout 충돌. frozen 텍스트는 그대로 두고 본 spec + 머지 후
HOTFIXES `2026-05-04 — p9-fb-24` 항목이 live source of truth.
Spec status `planned`. 다음 단계: writing-plans skill 로 implementation
plan 작성.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
회차 2 review (PR #96 회차 2) 의 2 건 cosmetic nit 모두 수렴.
- `tasks/HOTFIXES.md`, `tasks/p9/p9-fb-22-tui-cursor-and-autoscroll.md`:
\"기존 38 개\" → \"기존 39 개 (input.rs unit 18 + tests/ask.rs 21)\"
로 정확 카운트 + 출처 명시.
- `crates/kebab-tui/src/library.rs`: `active_buf_mut` doc comment 의
\"3-line dispatch\" → \"2-arm dispatch\" (실제 dispatch 가 2 arm 이라
가장 정확한 표현).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
도그푸딩 중 발견된 두 건 (Gitea #94, #95) 동시 수정.
#94 — `InputBuffer` 가 append-only 라 Ask/Search/Filter overlay 에서
타이핑한 텍스트의 중간을 편집할 수 없었음. cursor 모델을 byte-position
기반으로 재구성 (cursor_col 은 prefix slice 의 unicode-width 합으로
derive). 신규 메서드: `move_left / move_right / move_home / move_end /
delete_after`. 기존 `push_char` / `pop_char` 는 cursor 위치에서 동작
(cursor 가 끝일 때 backwards-compatible). Ask / Search / Library filter
overlay 세 곳에 `← / → / Home / End / Delete` key handler 추가. Search 는
cursor 이동만으로는 input_dirty_at 을 reset 하지 않음 (커서 이동 ≠ 쿼리
변경 → debounce 타이머 유지).
#95 — Ask 트랜스크립트의 `Paragraph::scroll((s.scroll, 0))` 가 위에서
부터 카운트라, 새 답변 도착 시 `s.scroll = 0` 으로 리셋하면 viewport 가
위쪽 고정 → 트랜스크립트가 길어지면 새 응답이 시야 밖으로 밀림. `AskState`
에 `follow_tail: bool` (default true) 추가. `render_answer` 가 follow_tail
동안 매 프레임 `Paragraph::line_count(width)` 로 wrapped row 수 계산해
스크롤을 `line_count - inner_height` 에 pin. `j` / `k` 가 follow_tail 끄고
`Shift-G` 가 다시 켬. 새 submission, `Ctrl-L` 도 follow-tail 재활성화.
`kebab-tui` 의 ratatui dep 에 `unstable-rendered-line-info` feature
활성화 — `Paragraph::line_count` 가 ratatui 0.28 에서 unstable. 0.28 에
pin 되어있는 동안 안정. 향후 ratatui bump 시 본 feature 의 stable 여부
재확인 필요.
cheatsheet popup Search/Ask section 에 화살표 + Home/End + Delete row
추가, Ask 에 `Shift-G` row 추가. README + HANDOFF + HOTFIXES + INDEX 동기.
Tests: 12 신규 InputBuffer unit + 6 신규 Ask integration. 기존 699 워크
스페이스 테스트 모두 통과 (cursor 가 끝일 때 backwards-compat).
Spec: `tasks/p9/p9-fb-22-tui-cursor-and-autoscroll.md` (status `completed`).
Live deviation 기록: `tasks/HOTFIXES.md` `2026-05-04 — p9-fb-22`.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
docs/components/<group>/README.md 12 페이지 + 인덱스 작성. 각 그룹
페이지가 구성 crate 표 + 구조 mermaid + data flow mermaid + 주요
type/trait/함수 시그니처 + 외부 의존 + 핵심 결정 (HOTFIXES + spec
의 "왜" 통합) + 관련 spec/HOTFIXES 링크. 인덱스가 그룹 wiring
다이어그램 + 진입 가이드 보유.
ARCHITECTURE.md 의 ASCII crate 의존 그래프를 mermaid flowchart 로
교체 (등가 정보, Gitea/GitHub 자동 렌더). docs/components/ 진입
링크 추가.
이 layer 는 contributor 향 — 사용자 향 grand picture 는 README.md
의 logical-architecture diagram 그대로 유지. 진척도는 HANDOFF.md,
per-task spec 은 tasks/INDEX.md 가 기존대로 source of truth.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Per-component README pages under docs/components/<group>/, grouped by
responsibility (12 groups). Each page carries 구조 + flow mermaid +
key-decision rationale consolidated from HOTFIXES + spec. Index page
hosts group-wiring diagram; ARCHITECTURE crate graph migrates from
ASCII to mermaid.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
도그푸딩 피드백 (사용자 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).
Plan was authored in the worktree branch but never committed there
(generated artifact for the subagent-driven-development flow). Worktree
removed post-merge; preserving the plan in main so future readers can
trace how the 6-task decomposition + two-stage review per task
produced PR #88.
`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 항목이 닫힘.
- Add `place_cursor_x(inner_x, inner_width, prompt_w, cursor_col) -> u16`
to `input.rs`: sums in `usize` (no u16 wrap), clamps to inner right
edge, tries_into with u16::MAX fallback. Two unit tests pin the clamp
and the in-bounds path.
- Re-export from `lib.rs` alongside `InputBuffer`, `display_width`,
`truncate_to_display_width`.
- Replace the open-coded 2-line `raw_x`/`cursor_x` blocks in Search,
Ask, and Library with a single `place_cursor_x` call each —
consistent usize arithmetic across all three panes.
- Add `filter_overlay_render_places_cursor_on_focused_field` integration
test in `tests/library.rs`: opens the filter overlay, renders through
`TestBackend`, asserts `terminal.get_cursor_position().x > 0` (label
offset > 0 proves `set_cursor_position` was called with a meaningful
coordinate, not stuck at origin).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
p9-fb-10: verifies that a Korean (Hangul) token survives the
ingest → FTS5 lexical search round-trip via the kebab-app facade.
NFC normalization is wired upstream in kebab-normalize; this test
only exercises end-to-end correctness — no AVX, no fastembed required.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Migrates FilterEdit.tags_buf and lang_buf from String to InputBuffer
(the same display-width-aware type used by Search/Ask), adds cursor
placement in render_filter_overlay so the caret sits at the end of the
focused field, and adds a Hangul filter test
(filter_overlay_accepts_hangul_tags) that confirms wide chars round-trip
through key events and commit_into unchanged.
Also adds App::library_filter_for_testing accessor mirroring
populate_library_for_testing style.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Migrates SearchState.input from String to InputBuffer so wide-char
(Hangul/CJK) keystrokes advance the terminal cursor by display columns
instead of char count. Adds cursor placement in render_input_bar via
f.set_cursor_position and a Hangul round-trip pin in tests/search.rs.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add `InputBuffer` with `push_char`/`push_str`/`pop_char`/`clear` tracking
cursor position in display columns (CJK = 2, ASCII = 1) plus 6 unit tests
(p9-fb-10 Task 1). Re-export from crate root.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- TAGS_COL_W const 추출 (truncate + pad 동시 사용 — drift 방지).
- format_doc_row 직접 unit test 2개 (Hangul title / Hangul tag) 가
display column 정렬을 정확히 pin. `<title_w$>` 으로 되돌리는
회귀가 unit-level 에서 catch 됨.
- format_doc_row: title/tags padding 을 display_width 기반으로 명시
계산 (std::fmt 의 char-count 기반 `<width$>` 가 wide char 에서 column
drift). truncate 가 보장하는 width 계약 위에 padding 도 같은 단위로
통일.
- input.rs 테스트 코멘트 cleanup (`= wait` 디버깅 잔재 제거).
- HOTFIXES "후속 spec issue" → "후속 PR 체크리스트" 로 owner 명시,
체크박스 5 개로 actionable 화.
`kebab-tui::input::{display_width, truncate_to_display_width}` 신규.
unicode-width 위에서 column-단위 width 계산 (ASCII=1, Hangul/CJK/
fullwidth=2, combining=0) + char-boundary 안전 truncate.
library.rs 의 중복 `truncate_to_display_width` private fn 제거 — 단일
source 로 통일. 9 unit tests + 1 integration render test (Korean +
Japanese fixture, TestBackend 80×20).
spec 의 `InputBuffer` struct 도입은 follow-up — Ask/Search/Editor pane
의 String + cursor 일괄 마이그레이션이 회귀 표면이 커서 helper 만
먼저 머지. backspace 는 모든 pane 이 이미 `String::pop()` 사용 → byte-
boundary 안전성 확보. crossterm 0.28 가 native IME composing 미노출 →
preedit handling out of scope.
spec status `planned` → `in_progress`. HOTFIXES.md 에 InputBuffer
struct deferral 사유 기록.
- `cheatsheet_intercept` doc 에 trade-off 명시: Esc-when-visible 가
cheatsheet 만 닫고 mode flip 안 함 (single-effect-per-keystroke).
Insert 모드 사용자는 두 번째 Esc 로 Normal 전환.
- `cheatsheet.rs` 모듈 doc 에 maintenance 경고 추가: push_section()
이 hard-coded string 이라 binding 변경 시 cheatsheet 동기화 수동.
자동 link 없음 — future PR 가 키 바꾸면 cheatsheet 도 갱신 필수.
118 TUI 테스트 통과 + clippy clean.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
도그푸딩 item 11 — vim 비익숙 사용자도 TUI 조작 가능. F1 으로 cheatsheet
modal popup, 현재 pane 의 키 매핑 + global 토글 (i/Esc/F1) 한 자리.
## 핵심 변경
- **`kebab-tui::cheatsheet::render_cheatsheet(f, area, app)`** 신규 —
70%/60% centered modal. 5 sections (Global / Library / Search / Ask
/ Inspect) 각 pane 의 모든 키 + 동사구 설명. footer 에 현재 focused
pane 명시. theme.style(Role::Heading/CitationMarker/Hint) 으로 색
계층 (header bold, key cyan-marker, body plain, hint dim).
- **`App.cheatsheet_visible: bool`** field + `pub fn cheatsheet_
visible() -> bool` getter (read-only — set/unset 은 F1 intercept
invariant).
- **`cheatsheet_intercept(app, key)`** in run.rs:
- F1 → toggle (open ↔ close), consumed
- Esc 가 visible 일 때 → close, consumed (mode_intercept 가 같은
Esc 를 mode flip 으로 해석하지 않도록 cheatsheet_intercept 가
먼저 dispatch)
- 그 외 키 → fall-through (popup 열린 채 navigation 가능)
- modifier-bearing F1 (Ctrl-F1 등) 무시
- **run loop 통합**: `cheatsheet_intercept` → `mode_intercept` →
pane dispatch 순. render_root 가 error overlay 위에 cheatsheet
overlay (사용자가 error 도중에도 도움말 소환 가능).
## HOTFIXES (`?` → `F1` rebind)
spec 은 `?` 를 trigger 로 명시했지만 Library 가 이미 `Char('?')` 를
quick-Ask binding 으로 사용 중 (handle_key_library line 305). spec 의
`?` 채택 = 기존 binding 깨거나 mode-aware special case 추가. 후자는
mode machine 에 더 많은 분기 추가하므로 회피.
**Live binding**: `F1` (universal help key, no collision).
**Per-pane verb hint line**: spec 의 verb-form hint 재구성도 본 PR
에서 deferral. 기존 `render_footer` 의 pane-별 힌트 문자열이 동일 UX
역할 — 후속 PR 에서 mode-aware verb fragments 로 split 가능.
spec status `planned` → `in_progress` (NOT `completed` — verb hint
deferral 명시).
## 테스트
- 5 신규 integration unit (`tests/cheatsheet.rs`):
- F1 toggles visibility (open ↔ close, consumed 양쪽)
- Esc closes when visible / falls through when hidden
- modifier-bearing F1 (Ctrl-F1, Alt-F1) 무시
- arbitrary keys (j, /, q, Enter) fall through 하면서 popup 열린 채
- render_cheatsheet 가 모든 section header (Global/Library/Search/
Ask/Inspect) + global toggle (F1, Esc) 출력
- 기존 113 TUI 테스트 + 신규 5 = 118 통과
- `cargo test --workspace --no-fail-fast -j 1` exit 0
- `cargo clippy --workspace --all-targets -- -D warnings` clean
## 문서
- README `kebab tui` 행: F1 cheatsheet popup 안내
- HANDOFF: 2026-05-03 entry
- HOTFIXES: ?→F1 rebind rationale + verb hint deferral
- spec status `planned` → `in_progress`
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- search::handle_key_search 의 j/k 두 개 arm (Insert 가드 + Normal
no-guard) 을 single arm + body if-branch 로 flatten. 4 arm → 2 arm,
\"j/k 가 mode 따라 다르다\" 가 한 자리에서 보임. ask.rs 패턴과 정렬.
- `is_typing_mod` 자리에 남아있던 \"removed\" placeholder 코멘트 3 줄
삭제. commit history 와 매치 블록 안 코멘트 가 reference.
113 TUI 테스트 통과 + clippy clean.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>