feat(kebab-tui): P9-4 Inspect pane #46
Reference in New Issue
Block a user
Delete Branch "feat/p9-4-tui-inspect"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
요약
Library Enter / Search
i가 활성화. Inspect 패널이 Doc 또는 Chunk 단일 view 로 metadata / provenance / blocks (doc) 또는 spans / text / embeddings (chunk) 6 section 을 collapsible 로 표시. Esc/q 로 originating pane (Library 또는 Search) 으로 복귀.핵심 결정
InspectTargetenum:Doc(DocumentId) | Chunk(ChunkId). Library Enter → Doc, Searchi→ Chunk.enter_inspect(state, target, return_to)helper: Library / Search 공통 entry point.return_to: Pane가 Esc 시 원래 pane 으로 돌려보냄.needs_fetchflag → run-loop idle tickrefresh_inspect→inspect_doc_with_config/inspect_chunk_with_config.c가 모든 section 일괄 toggle (v1, spec § "focus 기반 selective collapse 는 P+").metadata.user는serde_json::to_string_pretty로 indented 표시.Spec deviation (HOTFIXES
2026-05-02 P9-4)render_inspect<B: Backend>generic 제거 (P9-1/2/3 와 동일).i키 추가 — spec 명시했지만 P9-2 머지 시점에 빠져 있던 entry. P9-4 가 retroactive 추가.c일괄 collapse — spec 의 "focus 기반 selective" 는 P+ enhancement.테스트 (12개,
tests/inspect.rs)Docs (sync rule)
completed.검증
cargo test -p kebab-tui51 passed (10 library + 15 search + 14 ask + 12 inspect)cargo clippy --workspace --all-targets -- -D warningscleancargo build --release -p kebab-clicleanTest plan
-D warningskebab tui→ Library Enter → Inspect doc → c collapse → Esc → Search → 결과 →i→ Inspect chunk → Esc다음
P9 phase 4/5 완료. P9-5 (desktop tauri) 만 남음.
Library Enter / Search 'i' 가 Inspect 진입. Doc 또는 Chunk 단일 view 로 metadata / provenance / blocks (doc) 또는 spans / text / embeddings (chunk) 6 section 을 collapsible 로 표시. Esc/q 로 originating pane 으로 복귀. 핵심: - InspectTarget enum (`Doc(DocumentId) | Chunk(ChunkId)`). - InspectState 본체 (`app.rs`) — target / doc / chunk / collapsed HashSet / scroll / return_to / needs_fetch / loading. - `src/inspect.rs`: - `render_inspect` — target 종류별 render_doc / render_chunk 분기, section header 가 collapse marker (▾/▸) 표시. metadata.user JSON pretty-printed. - `handle_key_inspect`: j/k / Down/Up scroll. PageDown/PageUp 10 row. c = toggle all sections (v1 simplification). Esc/q = SwitchPane(return_to). - `enter_inspect(state, target, return_to)` helper — Library 와 Search 공통 entry point. - run-loop hook `refresh_inspect` — needs_fetch 면 lazy inspect_doc_with_config / inspect_chunk_with_config. - run.rs: Pane::Inspect arm 이 handle_key_inspect + render_inspect. Idle tick 마다 refresh_inspect. SwitchPane(Inspect) lazy init. - Library: Enter 가 enter_inspect(Doc(selected)) 호출 후 SwitchPane. - Search: 'i' (plain modifier) 가 enter_inspect(Chunk(selected_hit)) 호출 후 SwitchPane. typing 'i' (\"instance\") 와 충돌 가드. 테스트 12개 (`tests/inspect.rs`, TestBackend) — Esc 가 return_to 사용 / q 도 동작 / j/k scroll bounds / PgUp PgDn ±10 / c 일괄 toggle / no target hint / loading / doc view header+metadata+provenance+blocks / collapse hides body / chunk view text+block_ids / no slot → SwitchPane(Library) / enter_inspect helper sets fields. Spec deviation (HOTFIXES `2026-05-02 P9-4`): - `render_inspect<B: Backend>` generic 제거 (P9-1/2/3 와 동일). - Search `i` 키 추가 (P9-2 spec 에 없었음, P9-4 retroactive 추가). - `c` 일괄 collapse — spec 의 \"focus 기반 selective collapse\" 는 P+. Docs (sync rule): - README: TUI 행 \"4 패널\" + Quick start 코멘트. - HANDOFF: 한 줄 요약 + Phase status (P9 3/5 → 4/5) + deviation 한 줄. - HOTFIXES: P9-4 entry. - tasks/p9/p9-4 status: completed. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>회차 1 — collapse 일관성 issue 2건. blocks/embeddings 의 count 라인이 collapse 검사 밖에서 push 되어 collapsed 상태에서 부분만 사라짐. fix: section header 에 inline count (예: ) 후 body 만 collapse 검사 안. 그 외 suggestion 1 (scroll bound — follow-up scope) + 칭찬 (enter_inspect helper, NLL 코멘트, Search i 컨벤션 일관, HOTFIXES retroactive 명시).
@@ -0,0 +323,4 @@};format!("Paragraph: {trimmed}")}Block::Quote(q) => format!("Quote: {} chars", q.text.len()),(issue / collapse 작동 안 함)
blocks섹션에서count = N표시가 collapse 검사 전 에 무조건 push 됨 — collapsed 상태에서 count 가 그대로 보임. 다른 섹션 (metadata / provenance) 은 collapse 검사 안에 모든 line 이 들어가 있어 일관성 깨짐.Why: 사용자 입장에서
c누르면 6 섹션 일괄 collapse 되는데 blocks 의 count 라인만 사라지지 않음. snapshot 테스트가 collapse 후 "section header still visible" 만 검증하고 "count line hidden" 은 검증 안 해서 issue 가 회귀 grid 에 안 잡힘.How to apply:
count = N라인을 collapse 검사 안으로 옮기거나, count 를 section header 옆에 붙임 (예:▾ blocks (N)). 후자가 정보 dense 하면서 collapse 의도 그대로.회귀 테스트도 "collapsed 상태에서 block describe 가 사라짐" 검증 추가.
@@ -0,0 +407,4 @@// simplify by toggling all sections").toggle_all_sections(s);KeyOutcome::Continue}(issue / collapse 작동 안 함) 같은 패턴 —
block_ids = N라인이 collapse 검사 안 에 있지만(embedding records not loaded — out of v1 scope)라인 자체가 항상 collapse 안에서만 보이고 헤더는 별개. 그런데 위 issue 와 일관성 위해 한번에 정리: SECTION_EMBEDDINGS 도 같은 패턴 (header 에 카운트 inline) 이면 모든 섹션이 동일 모양.How to apply:
▾ embeddings (block_ids=2)같은 inline count. body 는 "(records not loaded)" + each block_id list 만.(suggestion / scroll 한계)
scroll = scroll.saturating_add(1)가 위로는 무한 스크롤 가능 — 빈 영역이 본문 위로 끝없이 올라감. 사용자가 j 만 누르고 있으면 scroll 값이 큰 정수로 이르고 다시 위로 스크롤 못 함 (k 가 1씩 빼므로 시간 걸림). 이 패널은 텍스트 길이가 가변이라 max scroll 계산이 까다롭지만, 적어도 build_doc_lines / build_chunk_lines 의 line 수를 미리 캐시 + max bound 검사 하면 UX 안정.Why: scroll 이
u16::MAX까지 갔다가 사용자가 "왜 위��� 안 가지" 의문 발생 → 무한 PgUp 으로 escape 해야 함.How to apply (선택): InspectState 에
total_lines: u16캐시 → render 직전 build 에서 갱신 →jarm 이if scroll < total_lines.saturating_sub(viewport_h) { scroll += 1 }. 본 PR 에서는 scope 외 — follow-up 가능.(칭찬)
enter_inspecthelper 가 Library / Search 두 호출자에서 같은 진입 path 를 쓰도록 만든 게 좋습니다.return_to: Pane가 Esc 시 정확히 원래 pane 으로 돌려보내고,needs_fetch = true가 run-loop idle tick 에 위임 — 호출자가 fetch 를 직접 트리거 안 함. 미래 P9-5 desktop 가 동일 helper 재사용 가능.@@ -282,0 +287,4 @@// we can re-borrow `state` mutably for the inspect-side// mutation below.let target = crate::app::InspectTarget::Doc(doc_id);crate::inspect::enter_inspect(state, target, Pane::Library);(칭찬) Library Enter 에서
enter_inspect호출 + SwitchPane(Inspect). NLL 코멘트 가 "왜 inner 별도 drop 없이 state 재차용 가능한지" 명시 — 미래 reader 가 borrow checker 마술이라고 오해할 위험 차단. 한 줄짜리 코멘트 가 큰 가치.@@ -184,0 +193,4 @@if s.hits.is_empty() {None} else {Some(s.hits[s.selected_hit].chunk_id.clone())(칭찬)
i키 가g키 와 같은 "plain modifier only" 컨벤션 적용. 사용자가 "instance" / "interface" 같은 검색어 입력 시 첫 'i' 가 input 으로 가게 — P9-2 의 SHIFT/none 분리 패턴 일관 유지.@@ -17,0 +19,4 @@**Discovered**: P9-4 implementation start.**Symptom 1 (cosmetic)**: Same shape as P9-1/2/3 — `tasks/p9/p9-4-tui-inspect.md` § Public surface declares `render_inspect<B: ratatui::backend::Backend>(...)`. ratatui 0.28's `Frame` is backend-agnostic; the generic is unused.(칭찬) HOTFIXES entry 가 spec 의 3 deviation 을 분리 명시 — generic / Search
i누락 / collapse simplification. 특히 Symptom 2 (Search 가 P9-2 머지 시점에i미포함) 는 이전 task 의 spec 누락이 retroactively 잡힌 케이스 — "누가 빠뜨렸는지" 가 아니라 "누가 채워 넣었는지" 로 표현 정직.회차 2 — 회차 1 지적 1건 (collapse 일관성) 반영. blocks/embeddings 의 count 가 section header inline 으로 이동, body 만 collapse 검사. 회귀 테스트 보강. 51 tests pass + clippy clean. 머지 가능. P9 phase 4/5 완성, P9-5 desktop 만 남음.
@@ -0,0 +329,4 @@}}fn describe_block(b: &Block) -> String {(칭찬) blocks 섹션 의 count 가 section header 에 inline. 사용자가 collapsed 상태에서 "이 doc 가 몇 개 block 이 있나" 답을 헤더에서 즉시 얻음 — collapse 의 의도 (스캔 없이 overview) 정확히 충족.
@@ -0,0 +477,4 @@InspectTarget::Doc(doc_id) => {let result = kebab_app::inspect_doc_with_config(cfg, &doc_id);let s = state.inspect.as_mut().unwrap();s.loading = false;(칭찬)
push_section_header_with_counthelper 가 두 use case (count 있음 / 없음) 를 한 함수로 통합. metadata / provenance / spans / text 는 count 없이 (None), blocks / embeddings 는 count inline. 호출 site 모두 readable + 일관.@@ -0,0 +242,4 @@assert!(rendered.contains("Test Doc"), "title rendered");assert!(rendered.contains("notes/test.md"), "doc_path rendered");assert!(rendered.contains("test-parser"), "parser_version rendered");assert!(rendered.contains("metadata"), "metadata section visible");(칭찬) collapse 회귀 테스트가 (1) "blocks (2)" inline count 표시 (2) Heading L1 body 숨김 (3) provenance kb-source-fs 숨김 세 invariant 모두 검증. 미래에 누군가 count 라인을 다시 collapse 검사 밖으로 옮기면 즉시 빨개짐.