Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
7f4b76c
fix(terminal): converge panes on the final resize
whackur Aug 28, 2026
61bd352
refactor(comments): drop code-restating comments and trim verbose rat…
whackur Aug 28, 2026
2ef10a9
fix(terminal): reject stale taken resizes
whackur Aug 28, 2026
ee98d07
test(terminal): cover resize disconnect race
whackur Aug 28, 2026
0b3eaab
refactor(comments): keep only rationale comments in app session IO
whackur Aug 28, 2026
7306c90
refactor(comments): keep only rationale comments in app
whackur Aug 28, 2026
903db4e
refactor(comments): keep only rationale comments in ui
whackur Aug 28, 2026
456bef1
refactor(comments): keep only rationale comments in runtime
whackur Aug 28, 2026
328790e
refactor(comments): keep only rationale comments in session
whackur Aug 28, 2026
dbb9d23
refactor(comments): keep only rationale comments in web
whackur Aug 28, 2026
efba7b5
refactor(comments): keep only rationale comments in daemon and backend
whackur Aug 28, 2026
feb0b5f
refactor(comments): keep only rationale comments in git and workspace
whackur Aug 28, 2026
a9a0035
refactor(comments): trim plugin recovery and viewer UI comments to ra…
whackur Aug 28, 2026
5b69453
refactor(comments): keep only rationale comments in application, plug…
whackur Aug 28, 2026
50e3f3c
refactor(session): separate catalog membership and runtime
whackur Aug 28, 2026
466af1e
docs(session): align catalog concurrency contract
whackur Aug 28, 2026
4eb69c5
refactor(log): separate commit log controller state
whackur Aug 28, 2026
5d9dce0
perf(tui): skip idle redraws
whackur Aug 28, 2026
41904fb
fix(attach): bound terminal inbox processing
whackur Aug 28, 2026
6b8a2ce
fix(attach): bound control-only inbox traffic
whackur Aug 28, 2026
fa1bb80
test(attach): cover inbox fairness budgets
whackur Aug 28, 2026
3432459
test(attach): measure terminal inbox burst
whackur Aug 28, 2026
ad10866
refactor(viewer): split repository workspace boundaries
whackur Aug 28, 2026
df807ad
perf(viewer): retain unchanged repository snapshots
whackur Aug 28, 2026
97b8112
perf(viewer): virtualize large diff and file views
whackur Aug 28, 2026
e6de6ad
perf(viewer): stabilize workspace contracts
whackur Aug 28, 2026
6f7fc88
chore(viewer): rebuild embedded assets
whackur Aug 28, 2026
b2f6c4e
test(tui): add release redraw benchmark
whackur Aug 28, 2026
43c5bfb
perf(diff): cache render indexes
whackur Aug 28, 2026
1a5dcff
feat(git): load selected views off the UI thread
whackur Aug 28, 2026
bd63239
fix(git): reject stale view load replies
whackur Aug 28, 2026
b7fe706
fix(tui): redraw applied git worker replies
whackur Aug 28, 2026
a465679
test(diff): use cached hunk mutation API
whackur Aug 28, 2026
24ee352
chore: ignore checkout-local agent files
whackur Aug 28, 2026
6980232
refactor(app): extract repository view manager
whackur Aug 28, 2026
8d16f96
fix(tui): redraw hot-file fade deadlines
whackur Aug 28, 2026
b05519d
fix(git): harden async load lifecycle
whackur Aug 28, 2026
0bd3612
fix(tui): preserve redraw and restore intent across clock races
whackur Aug 28, 2026
3ce4642
fix(git): bound and fairly admit load workers
whackur Aug 28, 2026
07cb976
fix(tui): preserve restored scroll across snapshot refresh
whackur Aug 28, 2026
b1cdfc3
fix(git): recover load workers after startup failures
whackur Aug 28, 2026
c58d2d4
fix(git): back off load worker spawn retries
whackur Aug 28, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,11 @@
/viewer-ui/tsconfig.tsbuildinfo
# Agent tool scratch state
/.atl/
/.worktress/

# Checkout-local agent instructions
/AGENTS.local.md
/CLAUDE.local.md

# Internal-only notes: not part of the published documentation.
/docs/internal/
Expand Down
26 changes: 14 additions & 12 deletions docs/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,10 +99,11 @@ src/
│ # logging.rs (file logger, rotation + retention), paths.rs
│ # (tilde expansion), signals.rs (SIGINT/SIGTERM shutdown),
│ # threading.rs (try_timed_join)
├── app.rs, app/ # App aggregate + InteractionState; per-feature impls: auto_follow,
│ # commit-log fetch/pagination/apply, diff & file-view loaders, focus,
│ # navigation, log_nav, scroll, session_io, snapshot_io,
│ # terminal_ctrl, tree, tree_nav
├── app.rs, app/ # App coordinates terminal/focus/fullscreen/notice/interaction with
│ # GitViewManager; RepositoryView owns status/log/tree/diff,
│ # auto-follow, tree watch state, and pending selection;
│ # GitViewManager owns repo identity/cache, snapshot/load workers,
│ # commit-log controller, tracking, branch, and ref decorations
├── config.rs, config/ # config.toml root + layout/theme/input, log, panels,
│ # plugin ([[plugin]]), web (WebViewerConfig, password bootstrap)
├── workspace/
Expand Down Expand Up @@ -153,7 +154,8 @@ src/
│ └── registry.rs, registry/ # ~/.nightcrow/plugins: config snippets, executable
│ # resolution, atomic install/list/remove storage
├── git/
│ ├── diff.rs, diff/ # types, snapshot loader, diff/commit loaders, commit_log, refs
│ ├── diff.rs, diff/ # types, snapshot loader, diff/commit loaders, conflated load worker,
│ │ # commit_log, refs
│ ├── clone.rs, clone/ # delegate `git clone` to the binary; URL scheme whitelist
│ ├── path/ # repo-relative path validation before any filesystem read
│ └── tree/ # lazy read-only directory listing (gitignore filter, symlink guard)
Expand All @@ -162,7 +164,8 @@ src/
│ # encode_wheel/button/arrow, CSI/SS3 helpers)
├── session/ # daemon-owned, transport-neutral shared session core
│ ├── state.rs, operations.rs, reload.rs # ownership, mutations, live config reload
│ ├── catalog/ # opaque repo ids, atomic swap, ordering, config tables
│ ├── catalog/ # pure membership + live runtime reconciliation,
│ │ # opaque repo ids, ordering, config tables
│ ├── runtime/ # SnapshotChannel drain + conflated status fan-out
│ ├── terminal/ # TerminalHub, PtyBackend ownership, shared terminal frames
│ ├── size_owner.rs # which client screen the session PTYs are fitted to
Expand Down Expand Up @@ -238,12 +241,11 @@ PTY 관리는 portable-pty 기반 `PtyBackend` 단일 구현으로 정리됐다.

## Future Refactor Notes

- `App`은 도메인별 sub-struct(`StatusView`, `LogView`, `DiffPane`, `TerminalState`,
`InteractionState`, `RepoInput`)와
`app/` 서브모듈로 impl 책임이 나뉘어 있지만, 여전히 한 구조체가 모든 sub-state를 들고 있다. 추가
분리가 필요해지면 sub-struct별 명시적 manager로 승격하는 게 다음 단계다.
- 대형 diff에서 j/k 빠른 탐색 시 동기 diff 로드가 여전히 ms 단위 블로킹을 만들 수 있다. Repository
캐싱으로 `discover` 비용은 제거됐으나, 추가 향상이 필요하면 채널 기반 비동기 로드 + debouncing.
- 저장소별 상태는 `GitViewManager`와 그 안의 `RepositoryView`로 분리됐다. `App`은 terminal/focus/
fullscreen/notice/interaction을 소유하고 명시적 façade로 UI·입력 계층에 저장소 상태를 제공한다.
이후 분리는 manager 내부 동작이 독립 수명이나 동시성 경계를 실제로 얻을 때만 진행한다.
- diff/file/commit/ref 로드는 lane별 conflation과 generation guard를 갖춘 `GitLoadWorker`로 비동기화돼
있다. 추가 최적화는 측정 결과가 필요할 때 watcher event debouncing이나 lane별 비용을 대상으로 한다.

## Detailed design

Expand Down
23 changes: 19 additions & 4 deletions docs/architecture/git-views.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,30 @@ tree(read-only 파일 트리) — 를 떠받치는 데이터 파이프라인과
같은 `git2::Repository` 캐시와 같은 경로 검증기를 지나며, 우측 pane(diff/file view)은 세 뷰가
공유한다.

`GitViewManager`가 저장소 경로·opaque id, repository cache, snapshot/load workers, commit-log
controller, branch/tracking/ref decoration을 한 수명으로 묶는다. 그 안의 `RepositoryView`는
status/log/tree/diff pane, auto-follow, tree watcher dirty set, snapshot 기반 pending selection을
소유한다. `App`은 terminal·focus·fullscreen·notice·interaction을 소유한 채 이 manager의 명시적
façade만 UI와 입력 계층에 제공한다. 따라서 프로젝트 close는 manager를 drop해 worker를 함께
정리하고, daemon set adopt는 같은 manager에 opaque id만 붙여 선택·watcher·cache를 보존한다.

## Git Diff Pipeline

- **백그라운드 worker 스레드**: `SnapshotChannel`이 `load_snapshot`을 호출해 변경 파일 +
tracking status를 `mpsc` 채널로 푸시한다(읽는 시점 규칙은
[session.md](session.md#상태는-시간이-아니라-변화에-따라-읽는다-runtimesnapshot_watchrs) 참고).
- **UI 스레드 동기 로드**: 파일/커밋 선택이 바뀌면 `load_*_with_repo`를 직접 호출한다. App은
`git2::Repository`를 lazy-cache하므로 매 호출마다 `Repository::discover`를 다시 실행하지 않는다.
cache는 프로젝트와 수명을 같이 하므로 무효화 시점이 따로 없다 — 저장소가 바뀌는 유일한 방법이
탭을 닫고 새로 여는 것이기 때문.
- **선택 로드 worker**: 파일/커밋 선택, file view, commit drill-down, ref decoration은
`GitLoadWorker`가 읽고 UI tick은 결과만 적용한다. `git2::Repository`는 `!Send`이므로 worker가
`Repository::discover`와 cache를 모두 소유한다. 요청은 `(repo, oid/path, generation)`으로 식별하고
diff/file/commit-files/decorations lane마다 아직 시작하지 않은 요청을 하나로 합친다. 실행 중인 이전
요청은 취소할 수 없지만 generation이나 repo가 현재 intent와 다르면 결과를 버리므로 연속 선택,
HEAD 변경, 탭 전환이 과거 내용을 되돌리지 않는다. lane 선택은 round-robin이라 diff 요청이 계속
들어와도 file/commit-files/decorations가 굶지 않는다. 프로세스 전체 git I/O와 동일 저장소 I/O에는
각각 hard bound가 있고, 종료 제한 안에 끝나지 않은 worker handle도 중앙 registry가 bounded하게
추적·회수한다.
- **snapshot reload gate**: 선택 파일의 path·status columns·mtime이 전부 이전 snapshot과 같으면
다른 파일이 바뀌었더라도 선택 diff를 다시 읽지 않는다. 선택 파일 자체가 바뀐 in-place refresh만
기존 scroll을 유지해 요청하고, 새 선택은 scroll/search cursor를 새 대상에 맞춰 reset한다.
- **경로 검증**: 워크트리 안의 파일·디렉토리를 여는 경로는 전부 `git::path::resolve_in_workdir`를
거친다(파일 미리보기와 트리 리스팅 양쪽). plain relative 컴포넌트만 허용하고
`..`·절대경로·NUL·`.git`(대소문자 무시)을 거부하며, 워크디렉토리부터 한 컴포넌트씩 내려가
Expand Down
Loading