Skip to content

feat: 대화 내용 전문 검색 - #42

Merged
jeongph merged 15 commits into
mainfrom
feature/chat-content-search
Aug 8, 2026
Merged

feat: 대화 내용 전문 검색#42
jeongph merged 15 commits into
mainfrom
feature/chat-content-search

Conversation

@jeongph

@jeongph jeongph commented Aug 8, 2026

Copy link
Copy Markdown
Member

기억나는 단어 하나로 모든 프로젝트의 대화 본문을 찾고, 고르면 그 메시지로 바로 이동한다.

기존 ⌘F는 세션 제목과 첫 메시지만 훑는다. 정작 찾고 싶은 건 "그때 그 얘기"인데 제목에는 남아 있지 않다.

동작

  • 창 상단 중앙에 검색창이 생겼다. 누르거나 ⌘K(Windows·Linux는 Ctrl+K)로 그 아래 팔레트가 내려온다. 뒤의 대화를 덮지 않아 보던 화면을 유지한 채 검색한다.
  • 세션 최신순으로 매칭 스니펫을 보여주고 각 묶음에 프로젝트 이름을 붙인다. 프로젝트별로 묶지 않은 이유는, 주 사용처가 "어느 프로젝트였는지 기억나지 않는" 상황이라 그룹핑하면 최근 매칭이 오래된 그룹 아래로 밀리기 때문이다.
  • ↑↓ 로 훑고 로 연다. 열면 해당 세션의 그 메시지로 스크롤·강조되고, 사이드바에서도 그 프로젝트(워크트리면 그룹 루트까지)가 펼쳐진다. Esc 로 닫는다.
  • 기존 ⌘F 사이드바 검색은 그대로 두되, 세션 제목·첫 메시지에 더해 폴더(프로젝트·워크트리) 이름도 매칭한다. 폴더 이름이 걸리면 그 안의 세션을 통째로 보여준다.

곁들여 사이드바가 들고 있던 자체 타이틀바(앱 이름·접기 버튼)를 상단 바로 합쳤다. 사이드바를 접었을 때 화면마다 펼치기 버튼을 따로 얹던 처리가 사라지고, 접힘 상태에서도 상단 바 토글 하나로 항상 되돌릴 수 있다.

인덱스

main 프로세스가 userData/search-index.jsonl에 대화 텍스트만 담은 인덱스를 유지한다.

  • 대상은 사용자 발화와 어시스턴트 응답 텍스트뿐이다. 도구 호출·결과는 세션 JSONL의 대부분을 차지하면서 사용자가 기억하는 문장이 아니라, 넣으면 결과를 노이즈로 덮는다. 사고 과정도 같은 이유로 뺀다. 덕분에 인덱스가 원본의 한 자릿수 퍼센트에 머문다.
  • 추출은 기존 parseConversation 출력에서 한다. 엔트리 해석 규칙을 복제하지 않기 위해서이기도 하고, 파서가 연속 assistant 엔트리를 한 아이템으로 병합하기 때문에 원본 엔트리 uuid를 쓰면 화면에 없는 아이템을 가리키게 되기 때문이다.
  • 앱 시작 직후 백그라운드로 한 번 정합하고, 이후에는 (마지막 엔트리 시각, 파일 크기)가 바뀐 세션만 다시 읽는다. 세션 JSONL이 append-only라 성립하는 판정이다.
  • 검색은 정합을 기다리지 않는다. 현재 인덱스로 즉시 답하고, 정합 결과 내용이 바뀌면 revision이 올라 렌더러가 같은 질의를 자동으로 다시 던진다. 검색 응답이 디스크 스캔에 묶이지 않게 하려는 것이다.
  • 인덱싱 중에는 진행률(n/N)을 보여주고, 끝나는 순간 결과가 저절로 채워진다.

런타임 의존성은 추가하지 않았다.

검증

이 저장소에 테스트 러너가 없어 vitest를 devDependency로 들였다. Electron에 의존하지 않는 순수 로직(추출 규칙, 매칭·스니펫 절단, 신선도 판정, 대소문자 접기)에 27개 테스트를 붙였다.

테스트를 들여왔으니 PR에서 실제로 돌도록 ci 워크플로도 함께 추가했다 — 타입 검사·린트·테스트. 검증에는 electron 바이너리가 필요 없어 설치에서 건너뛴다.

실제 앱을 띄워 콜드 스타트(인덱스 없음) → 인덱싱 진행률 표시 → 완료 시 자동 재질의 → ⌘K → 화살표 이동 → 로 메시지 이동·강조 → Esc 닫기와 포커스 복원까지 확인했다.

리뷰에서 잡아 고친 것

구현 후 리뷰에서 나온 것 중 실제로 조용히 틀리던 것들이다.

  • 일시적 오류 하나로 인덱스가 통째로 지워졌다. 프로젝트 루트를 읽지 못한 경우와 프로젝트가 없는 경우를 구분하지 않아, 정리 단계가 "세션이 전부 사라졌다"로 오판하고 빈 인덱스를 디스크에 덮어썼다. 이후 모든 검색이 아무 신호 없이 "결과 없음"이 된다.
  • 검색 가능한 본문이 없는 세션이 매 정합마다 재파싱됐다. 인덱스에서 빼두니 신선도 검사에 영원히 걸리지 않아, 바뀐 게 없는데도 인덱스 전체 재작성과 렌더러 재질의를 5초마다 유발했다.
  • 대소문자 접기가 길이를 바꾸면 강조 위치가 어긋났다. toLowerCase()로 길어지는 코드포인트(U+0130)가 섞이면 이후 인덱스가 밀려 엉뚱한 글자가 하이라이트된다. 길이를 보존하는 접기로 질의와 본문에 같은 규칙을 쓴다.
  • 세션을 빠르게 연달아 고르면 헤더와 본문이 어긋날 수 있었다. 대화 로드가 역순으로 끝나는 경우다. 선택마다 번호를 매겨 마지막 것만 반영한다.
  • 검색 실패가 "결과 없음"과 구분되지 않았다. IPC 질의 실패를 아무도 받지 않아 상태줄이 비거나 이전 질의 결과가 그대로 남았다. 결과를 열 때 세션 조회가 실패하면 클릭이 반응 없이 죽었다.
  • 팔레트에서 Tab을 누르면 포커스가 뒤 화면으로 빠져나갔다. 그 뒤로는 ↑↓· 가 듣지 않는다. Tab을 막아 입력창에 붙잡아 두고, 닫을 때 연 컨트롤로 되돌린다.
  • 모션 감소 설정에서 강조가 아예 보이지 않았다. 강조 테두리를 키프레임 안에만 둬서 전역 animation: none이 이를 지웠다.
  • CapsLock이 켜져 있으면 ⌘F가 먹지 않았다. 이건 기존 코드의 문제인데 같이 고쳤다.

알려진 한계

  • 다중 토큰 AND 검색은 없다. 질의 전체를 하나의 리터럴로 취급한다.
  • 사용자가 !로 직접 실행한 셸 명령과 컨텍스트 요약은 화면에는 보이지만 검색에 잡히지 않는다. "대화 텍스트만" 범위를 따른 결과인데, 넓힐 여지는 있다.
  • 결과는 세션 200개, 세션당 스니펫 5개에서 자르고 잘렸음을 표시한다.
  • CHV_DATA_DIR로 데이터 루트를 바꿔도 인덱스 파일은 하나를 공유해서, 데모 데이터와 실제 데이터를 오가면 매번 전체 재구축한다. 자기 치유되므로 그대로 뒀다.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • 새로운 기능

    • ⌘K(Windows·Linux: Ctrl+K)로 모든 프로젝트의 대화 내용을 검색할 수 있습니다.
    • 검색 결과에서 일치하는 메시지로 이동하고 해당 내용을 강조 표시합니다.
    • 사이드바에서 세션을 제목, 첫 메시지, 프로젝트·워크트리 이름으로 필터링할 수 있습니다.
    • 상단 바에서 사이드바를 접거나 펼칠 수 있습니다.
    • 검색 진행 상태, 부분 결과, 오류 및 빈 결과를 안내합니다.
  • 버그 수정

    • Caps Lock이 활성화된 상태에서도 세션 검색 단축키가 정상 작동합니다.
  • 문서

    • 검색 범위, 단축키, 색인 저장 및 삭제 후 재생성 동작을 문서화했습니다.
  • 품질 개선

    • 자동 타입 검사, 린트, 테스트를 포함한 CI 검증을 추가했습니다.

jeongph added 11 commits August 8, 2026 13:59
프로젝트 루트를 읽지 못했을 때 빈 목록과 구분하지 않아, 일시적 오류 한 번으로
정리 단계가 인덱스를 통째로 지우고 빈 인덱스를 디스크에 덮어썼다. 이후 모든 검색이
아무 신호 없이 '결과 없음'이 됐다. 세션 목록을 못 읽은 프로젝트의 기존 문서도
같은 이유로 사라졌다.

본문이 없는 세션을 인덱스에서 빼던 것도 되돌린다. 빠진 세션은 신선도 검사에 영원히
걸리지 않아 매 정합마다 재파싱되고, 바뀐 게 없는데도 인덱스 전체 재작성과 렌더러
재질의를 5초마다 유발했다.

대소문자 접기가 길이를 바꾸면(U+0130) 매칭 인덱스가 원문과 어긋나 엉뚱한 구간이
강조된다. 길이를 보존하는 foldCase로 질의와 본문에 같은 규칙을 적용한다.
IPC 질의가 실패해도 아무도 받지 않아, 첫 질의면 상태줄이 빈 채로 남고 두 번째
질의면 이전 결과가 새 질의의 결과처럼 남아 있었다. 결과 클릭 시 세션 목록 조회가
실패하면 클릭이 반응 없이 죽었고, 대화 로드 실패는 헤더만 남은 빈 패널이 됐다.

응답의 query를 입력과 대조해 낡은 결과를 그리지 않고, 왕복 중에는 '검색 중'을
보여준다. CapsLock이 켜지면 key가 'F'로 와서 ⌘F가 조용히 먹히지 않던 것도 고친다.
강조 테두리를 키프레임 안에만 두어, prefers-reduced-motion의 전역 animation:none이
이를 통째로 지웠다. 어느 메시지가 매칭됐는지 알 방법이 없어진다. 정적 테두리를
기본값으로 두고 애니메이션은 사라지는 연출만 맡는다.

두 파일에 흩어져 있던 강조 지속 시간은 커스텀 프로퍼티로 묶는다.
이 브랜치가 저장소 최초의 테스트를 들여왔는데 어떤 워크플로도 돌리지 않았다.
electron 바이너리와 husky 훅은 검증에 필요 없어 설치에서 건너뛴다.
@coderabbitai

coderabbitai Bot commented Aug 8, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 05b32d80-4c37-443e-85ac-bf839e233822

📥 Commits

Reviewing files that changed from the base of the PR and between f2e14bd and d8cfcf1.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (3)
  • package.json
  • website/src/content/changelog/en/0.7.0.md
  • website/src/content/changelog/ko/0.7.0.md

📝 Walkthrough

Walkthrough

로컬 JSONL 검색 색인과 IPC API를 추가했습니다. 대화 본문 검색, 검색 결과 팔레트, 세션 이동과 메시지 강조를 구현했습니다. 상단 바와 사이드바 검색 동작을 변경하고 테스트, 문서, CI 검증을 추가했습니다.

Changes

대화 내용 검색

Layer / File(s) Summary
검색 데이터 계약과 매칭
src/shared/types.ts, src/main/lib/searchExtract.ts, src/main/lib/searchMatch.ts, src/main/lib/*.test.ts, package.json, vitest.config.ts
검색 타입과 메시지 추출 규칙을 추가했습니다. 대소문자 무시 매칭, 문서 최신성 검사, 스니펫 생성을 구현했습니다. Vitest 실행과 관련 검증을 구성했습니다.
검색 색인과 IPC 연결
src/main/lib/searchIndex.ts, src/main/index.ts, src/preload/index.ts
버전이 있는 JSONL 색인을 로드·검증·원자적으로 저장합니다. 세션 변경을 정합화하고 검색 결과와 진행 상태를 IPC로 전달합니다.
검색 상태와 결과 선택
src/renderer/src/App.tsx, src/renderer/src/components/Sidebar.tsx, src/renderer/src/components/TopBar.tsx, src/renderer/src/i18n.ts, src/renderer/src/lib/platform.ts
검색 상태, 지연 실행, 오래된 응답 무시, 오류 처리를 추가했습니다. 단축키로 검색을 열고 프로젝트와 세션을 확인한 뒤 대화를 엽니다.
결과 표시와 메시지 강조
src/renderer/src/components/SearchPalette.tsx, src/renderer/src/components/ConversationView.tsx, src/renderer/src/components/MessageItems.tsx
검색 결과를 세션별로 표시합니다. 키보드와 마우스 선택을 지원합니다. 선택한 메시지로 스크롤하고 사용자 메시지 또는 assistant 턴을 강조합니다.
상단 바와 검색 UI 구성
src/renderer/src/styles/app.css, README.md, README.en.md, website/src/content/changelog/*/0.7.0.md, .github/workflows/ci.yml
상단 바와 ⌘K 검색 진입점을 추가했습니다. 세션 필터와 대화 내용 검색을 분리하고 검색 색인의 저장 및 포함 데이터를 문서화했습니다. 릴리스 변경 로그와 CI 검증을 추가했습니다.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant 사용자
  participant SearchPalette
  participant App
  participant Preload
  participant MainIPC
  participant SearchIndex
  participant ConversationView

  사용자->>SearchPalette: 검색어 입력
  SearchPalette->>App: 질의 전달
  App->>Preload: searchSessions(query)
  Preload->>MainIPC: search:query
  MainIPC->>SearchIndex: 검색 실행
  SearchIndex-->>App: SearchResults
  사용자->>SearchPalette: 결과 선택
  SearchPalette->>App: 세션과 메시지 UUID 전달
  App->>ConversationView: highlightRef와 함께 대화 열기
  ConversationView->>ConversationView: 대상 메시지 스크롤 및 강조
Loading

Possibly related PRs

  • followingseas/wake#41: 이 PR의 0.7.0 로케일별 변경 로그 파일과 릴리스 변경 로그 시스템의 연결이 있습니다.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 22.58% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed 제목은 모든 프로젝트의 대화 본문을 검색하는 이번 변경의 핵심 기능을 간결하고 정확하게 설명합니다.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/chat-content-search

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (5)
.github/workflows/ci.yml (2)

15-15: 🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick win

체크아웃에서 자격 증명 유지를 끄세요.

actions/checkout은 기본적으로 GITHUB_TOKEN.git/config에 남깁니다. 이 잡은 타입 검사·린트·테스트만 실행하므로 토큰이 필요 없습니다. persist-credentials: false를 설정해 이후 스텝에서 토큰이 노출될 여지를 없애세요.

🔒️ 제안 수정
-      - uses: actions/checkout@v4
+      - uses: actions/checkout@v4
+        with:
+          persist-credentials: false
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/ci.yml at line 15, Update the actions/checkout@v4 step in
the CI workflow to disable credential persistence by setting persist-credentials
to false, while leaving the existing checkout behavior unchanged.

Source: Linters/SAST tools


3-6: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

동시 실행 제어를 추가하는 것을 고려하세요.

같은 브랜치에 연속으로 푸시하면 이전 실행이 계속 돌아 러너 시간을 씁니다. concurrency 그룹을 두면 오래된 실행을 취소합니다.

♻️ 제안 수정
 permissions:
   contents: read
+
+concurrency:
+  group: ${{ github.workflow }}-${{ github.ref }}
+  cancel-in-progress: true
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/ci.yml around lines 3 - 6, Configure a concurrency group
for the workflow near the existing on triggers, using the workflow or branch
context to group runs and cancel in-progress runs when a newer run starts.
Preserve the current pull_request and main push triggers.
src/main/lib/searchIndex.ts (3)

262-267: 🚀 Performance & Scalability | 🔵 Trivial | 🏗️ Heavy lift

질의마다 코퍼스 전체를 동기로 훑습니다. 큰 인덱스에서 메인 프로세스가 멈춥니다.

searchSessionsawait 없이 모든 문서의 모든 메시지에 대해 indexOf를 돌립니다. 렌더러가 200 ms 디바운스로 질의를 보내므로, 타이핑 중에도 이 루프가 반복 실행됩니다. MAX_HITS는 결과 수만 제한하고 스캔 비용은 줄이지 않습니다. 코퍼스가 수백 MB면 질의 한 번이 메인 프로세스를 수백 ms 동안 붙잡고, 그동안 모든 IPC 응답이 지연됩니다.

완화 방법입니다.

  • 문서 루프 중간에 주기적으로 이벤트 루프에 양보한다.
  • 문서를 updatedAt 내림차순으로 미리 정렬해 두고, MAX_HITS에 도달하면 스캔을 중단한다. 지금은 전체를 스캔한 뒤 정렬하므로 조기 종료가 불가능하다.
  • 인덱싱과 검색을 utilityProcess로 옮긴다.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/lib/searchIndex.ts` around lines 262 - 267, Update searchSessions
around the documents.values() loop to avoid scanning the entire corpus
synchronously: iterate documents pre-sorted by updatedAt descending,
periodically yield to the event loop during matching, and stop once MAX_HITS is
reached. Preserve the existing matchDocument filtering and result ordering while
ensuring indexing/search work is isolated in a utility process if that
architecture is already available.

221-241: 🩺 Stability & Availability | 🔵 Trivial | 💤 Low value

실패가 반복될 때 재시도 간격을 늘리는 것을 고려하세요.

인덱싱이 실패하면 lastReconcileAt이 갱신되므로, 이후 검색마다 5초 간격으로 전체 정합을 다시 시도합니다. 루트 디렉터리 권한 문제처럼 원인이 지속되면 매 5초마다 전체 트리 스캔과 오류 로그가 반복됩니다. failed가 이어질 때 지수 백오프를 적용하면 부하와 로그 소음을 줄일 수 있습니다.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/lib/searchIndex.ts` around lines 221 - 241, Update ensureIndex and
the reconciliation scheduling logic to apply exponential backoff after repeated
indexing failures: increase the retry delay while failed persists, cap it at a
reasonable maximum, and reset it after a successful load/reconcile. Preserve the
existing failed-state signaling and progress emission while preventing full
scans and error logs from recurring every five seconds.

78-113: 🚀 Performance & Scalability | 🔵 Trivial | ⚖️ Poor tradeoff

대용량 인덱스에서는 줄 단위 스트리밍을 고려하세요.

readFile은 인덱스 파일 전체를 한 문자열로 올립니다. 이후 documentslower 사본이 추가로 쌓입니다. 세션이 많은 사용자는 시작 시 메모리 피크가 커집니다. readline 인터페이스로 줄 단위로 읽으면 피크를 낮출 수 있습니다. 현재 규모에서 문제가 없다면 그대로 두어도 됩니다.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/lib/searchIndex.ts` around lines 78 - 113, 대용량 인덱스 로딩 시 전체 파일을 메모리에
읽는 loadFromDisk의 readFile 사용을 줄 단위 스트리밍으로 변경하세요. readline 또는 기존 프로젝트의 동등한 스트리밍
API로 첫 줄의 버전을 검증한 뒤 나머지 줄을 순차 처리하고, 잘못된 JSON·누락 파일·유효하지 않은 문서에 대한 현재 동작은 유지하세요.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@README.en.md`:
- Around line 32-33: Update the shortcut documentation to show platform-specific
modifiers: in README.en.md lines 32-33 and 86-87, replace ⌘F and ⌘⇧F with
⌘/Ctrl+F and ⌘/Ctrl+Shift+F; in README.md lines 32 and 85-86, apply the same
replacements in the feature text and usage table.

In `@src/renderer/src/App.tsx`:
- Around line 161-184: Update the search useEffect to increment
searchRequestRef.current before the early returns, invalidating any in-flight
request when searchOpen closes or searchQuery becomes empty. In those
empty/closed states, reset searchFailed and clear searchResults; preserve the
existing debounced search behavior and stale-response checks for non-empty
queries.
- Around line 247-265: Update the session-selection flow centered on
selectSession and openHit to track a monotonically increasing selection request
ID. Increment it when openHit starts and when the sidebar directly invokes
selectSession, then verify the ID after listSessions and loadConversation
complete before applying setSelected, setConversation, or related UI updates, so
only the latest selection can affect the screen.

In `@src/renderer/src/components/SearchView.tsx`:
- Around line 40-58: Update the status logic after the results.degraded check so
any degraded SearchResults displays a partial or failure status even when
results.hits contains items, while preserving the result list. Use the existing
search.partial translation key if available, or add it if necessary, and keep
the normal search.summary path only for complete results.

---

Nitpick comments:
In @.github/workflows/ci.yml:
- Line 15: Update the actions/checkout@v4 step in the CI workflow to disable
credential persistence by setting persist-credentials to false, while leaving
the existing checkout behavior unchanged.
- Around line 3-6: Configure a concurrency group for the workflow near the
existing on triggers, using the workflow or branch context to group runs and
cancel in-progress runs when a newer run starts. Preserve the current
pull_request and main push triggers.

In `@src/main/lib/searchIndex.ts`:
- Around line 262-267: Update searchSessions around the documents.values() loop
to avoid scanning the entire corpus synchronously: iterate documents pre-sorted
by updatedAt descending, periodically yield to the event loop during matching,
and stop once MAX_HITS is reached. Preserve the existing matchDocument filtering
and result ordering while ensuring indexing/search work is isolated in a utility
process if that architecture is already available.
- Around line 221-241: Update ensureIndex and the reconciliation scheduling
logic to apply exponential backoff after repeated indexing failures: increase
the retry delay while failed persists, cap it at a reasonable maximum, and reset
it after a successful load/reconcile. Preserve the existing failed-state
signaling and progress emission while preventing full scans and error logs from
recurring every five seconds.
- Around line 78-113: 대용량 인덱스 로딩 시 전체 파일을 메모리에 읽는 loadFromDisk의 readFile 사용을 줄
단위 스트리밍으로 변경하세요. readline 또는 기존 프로젝트의 동등한 스트리밍 API로 첫 줄의 버전을 검증한 뒤 나머지 줄을 순차
처리하고, 잘못된 JSON·누락 파일·유효하지 않은 문서에 대한 현재 동작은 유지하세요.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: b48b859d-1ea8-41da-833a-3320f4d89cc7

📥 Commits

Reviewing files that changed from the base of the PR and between 042af54 and b469835.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (20)
  • .github/workflows/ci.yml
  • README.en.md
  • README.md
  • package.json
  • src/main/index.ts
  • src/main/lib/searchExtract.test.ts
  • src/main/lib/searchExtract.ts
  • src/main/lib/searchIndex.ts
  • src/main/lib/searchMatch.test.ts
  • src/main/lib/searchMatch.ts
  • src/preload/index.ts
  • src/renderer/src/App.tsx
  • src/renderer/src/components/ConversationView.tsx
  • src/renderer/src/components/MessageItems.tsx
  • src/renderer/src/components/SearchView.tsx
  • src/renderer/src/components/Sidebar.tsx
  • src/renderer/src/i18n.ts
  • src/renderer/src/styles/app.css
  • src/shared/types.ts
  • vitest.config.ts

Comment thread README.en.md Outdated
Comment thread src/renderer/src/App.tsx
Comment thread src/renderer/src/App.tsx
Comment thread src/renderer/src/components/SearchView.tsx Outdated
전용 화면 대신 창 전체 폭 타이틀바 중앙의 검색창에서 열고, 결과는 그 아래
드롭다운 팔레트로 떨어뜨린다. 뒤 대화를 가리지 않고 ↑↓·↵로 훑을 수 있다.
단축키는 ⌘K.

사이드바가 자기 타이틀바와 접기 버튼을 들고 있던 것도 상단 바로 합친다.
접힌 상태에서 펼치기 버튼을 화면마다 따로 얹던 처리가 사라진다.

사이드바 ⌘F는 폴더(프로젝트·워크트리) 이름도 매칭한다 — 이름이 걸리면 그
안의 세션을 통째로 보여준다.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/renderer/src/App.tsx (1)

198-202: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

사이드바 질의를 우선 적용하세요.

이전 팔레트 질의가 남아 있으면 previous || query가 이전 질의를 유지합니다. 그 상태에서 사이드바에 새 질의를 입력하고 콘텐츠 검색을 열면 새 질의가 전달되지 않습니다.

사이드바 질의가 비어 있지 않으면 이를 우선 사용하세요.

수정 예시
-    setSearchQuery((previous) => previous || query)
+    setSearchQuery((previous) => (query.trim() ? query : previous))
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/renderer/src/App.tsx` around lines 198 - 202, Update openContentSearch so
the current sidebar query takes precedence over the previous search state when
it is non-empty. Replace the previous-first fallback in setSearchQuery while
preserving the existing behavior of retaining the prior query only when the
sidebar query is empty.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/renderer/src/components/SearchPalette.tsx`:
- Around line 97-175: Update the SearchPalette dialog container to declare
aria-modal="true" and trap Tab/Shift+Tab focus within .palette, keeping focus on
the first or last focusable control when cycling. When the palette closes
through onClose, restore focus to the control that opened it, using the existing
inputRef and lifecycle/close handling without changing result navigation.

In `@src/renderer/src/components/TopBar.tsx`:
- Around line 42-56: Update the search shortcut hint in the TopBar search button
to represent both supported platforms, replacing the macOS-only “⌘K” display
with a platform-aware key or a combined “⌘/Ctrl K” label consistent with App.tsx
handling.

---

Outside diff comments:
In `@src/renderer/src/App.tsx`:
- Around line 198-202: Update openContentSearch so the current sidebar query
takes precedence over the previous search state when it is non-empty. Replace
the previous-first fallback in setSearchQuery while preserving the existing
behavior of retaining the prior query only when the sidebar query is empty.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 87689718-16ed-48fc-a876-72cdc25cf190

📥 Commits

Reviewing files that changed from the base of the PR and between b469835 and 304d88c.

📒 Files selected for processing (10)
  • README.en.md
  • README.md
  • src/renderer/src/App.tsx
  • src/renderer/src/components/ConversationView.tsx
  • src/renderer/src/components/SearchPalette.tsx
  • src/renderer/src/components/Sidebar.tsx
  • src/renderer/src/components/SidebarExpand.tsx
  • src/renderer/src/components/TopBar.tsx
  • src/renderer/src/i18n.ts
  • src/renderer/src/styles/app.css
💤 Files with no reviewable changes (1)
  • src/renderer/src/components/SidebarExpand.tsx
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/renderer/src/i18n.ts
  • README.md
  • README.en.md

Comment thread src/renderer/src/components/SearchPalette.tsx
Comment thread src/renderer/src/components/TopBar.tsx Outdated
세션을 빠르게 연달아 고르면 대화 로드가 역순으로 끝나 헤더와 다른 세션의 본문이
그려질 수 있었다. 선택마다 번호를 매겨 마지막 것만 반영한다.

질의를 지워도 이미 떠난 검색 요청이 무효화되지 않아, 그 요청이 실패하면 빈 질의
화면에 '검색 실패'가 떴다. 번호를 이른 시점에 올려 무효화하고, 질의가 비면 실패보다
안내를 먼저 보여준다.

팔레트에서 Tab을 누르면 포커스가 뒤 화면 컨트롤로 빠져나가 ↑↓·↵가 더는 듣지
않았다. Tab을 막아 입력창에 붙잡아 두고, 닫을 때는 팔레트를 연 컨트롤로 되돌린다.
aria-modal도 함께 붙인다.

인덱싱이 실패해 인덱스가 불완전한데도 결과가 있으면 정상 요약을 보여줬다. 이제
불완전함을 알린다.

단축키 표기는 플랫폼을 따른다 — Windows·Linux에서는 Ctrl로 보인다.
@jeongph

jeongph commented Aug 8, 2026

Copy link
Copy Markdown
Member Author

리뷰 6건 전부 코드와 대조해 반영했습니다 (f2e14bd). 두 건은 지적하신 방향과 조금 다르게 갔고, 이유를 아래 적었습니다.

🟠 마지막으로 선택한 세션만 화면에 반영 — 유효한 지적이었습니다. selectSession에 선택 번호를 두고, loadConversation 이후 번호가 바뀌었으면 무시합니다. openHitlistSessions 쪽은 별도 번호를 두지 않았습니다 — 그 경로가 마지막에 selectSession을 호출하므로 화면에 보이는 결과는 이미 보호되고, 중간의 setSessions·setExpanded는 캐시와 펼침 상태라 늦게 반영돼도 해가 없습니다.

🟠 팔레트 내부에 포커스 유지aria-modal="true"를 붙이고 Tab을 막았습니다. 전체 포커스 트랩 대신 Tab 자체를 차단한 이유는, 이 팔레트가 입력창에서만 조작되는 구조라(결과 탐색이 ↑↓·) 순환시킬 대상이 사실상 없기 때문입니다. 결과 행이 수십 개라 Tab으로 훑게 하면 오히려 방해가 됩니다. 닫을 때 포커스 복원도 넣었는데, 첫 렌더 시점에 잡아야 했습니다 — 이펙트 시점에는 입력창의 autoFocus가 이미 포커스를 가져간 뒤였습니다.

🟡 빈 질의에서도 이전 요청 무효화 — 유효했습니다. 요청 번호를 early return 앞으로 옮겼고, 재시도 시작 시점에 실패 표시를 걷습니다. 더해서 상태 문구 순서를 바꿔 질의가 비면 직전 실패보다 안내가 먼저 나오게 했습니다.

🟡 불완전한 색인 결과를 완전한 결과로 표시하지 않기 — 유효했습니다. search.partial 키를 추가해, degraded이면 결과가 있어도 "인덱싱에 실패해 결과가 불완전할 수 있습니다"를 보여줍니다. 목록 자체는 그대로 둡니다.

🟡 플랫폼 공통 단축키 표기 (상단 바 / README) — 유효했습니다. 다만 ⌘/Ctrl K 병기 대신 플랫폼을 감지해 하나만 보여주도록 했습니다(lib/platform.ts). 좁은 칩 안에서 병기는 읽기 어렵고, 사용자에게는 자기 플랫폼 키 하나만 필요합니다. README는 기존 문서 전체가 ⌘, 같은 표기를 쓰고 있어 이번 줄만 바꾸면 오히려 어긋나므로, 사용법 절 앞에 " 는 Windows·Linux에서 Ctrl" 한 줄을 두어 모든 단축키에 한 번에 적용했습니다.

검증: npm test(27개)·npm run typecheck·npm run lint 통과, 실제 앱에서 ⌘K → 검색 → ↑↓ 이동·강조 → Esc 닫기와 포커스 복원, Tab 차단, 질의를 지웠을 때의 안내 문구까지 확인했습니다.

@jeongph

jeongph commented Aug 8, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 8, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@jeongph
jeongph merged commit aeb9e44 into main Aug 8, 2026
5 checks passed
@jeongph
jeongph deleted the feature/chat-content-search branch August 8, 2026 10:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant