Initial Commit

This commit is contained in:
2026-09-16 17:22:14 +09:00
commit 858ee9e9da
335 changed files with 123898 additions and 0 deletions
+82
View File
@@ -0,0 +1,82 @@
# Research: 스니펫 팔레트 (Phase 0)
브레인스토밍·clarify에서 큰 결정은 끝났고(저장=로컬 SQLite, 핫키=Ctrl+1, UI=Raycast, 데이터=003 시드), 여기선 **구현 미해결점**만 결정한다.
---
## R1. React ↔ SQLite 브릿지: 요청→응답 방식
**결정**: 기존 postMessage 채널을 **reqId 상관관계(correlation) 왕복**으로 확장. `AddHostObjectToScript`(COM 호스트 객체) 안 씀.
- JS→C#: `{type:"snippets.list", reqId}` 식으로 요청(각 호출마다 증가하는 reqId).
- C#→JS: `Web.CoreWebView2.PostWebMessageAsJson({type:"snippets.result", reqId, ok, data|error})`로 응답.
- JS: `Map<reqId, {resolve,reject}>` 유지, `chrome.webview.addEventListener("message")`에서 매칭 reqId를 resolve.
**Rationale**:
- 기존 브릿지가 이미 postMessage(단방향)라 **같은 메커니즘 하나로 통일** — 스타일 일관, 학습비용 0.
- COM 마샬링(hostObjects) 회피: 배열/객체 반환 시 JSON 문자열 마샬링이 번거롭고, 기존 코드와 이질적.
- SQLite 호출은 로컬·즉각이라 C#에서 동기로 처리 후 바로 응답 → async 복잡도 불필요(UI 스레드에서 안전, 기존 paste도 UI 스레드).
**Alternatives 기각**:
- `AddHostObjectToScript`: 호출부는 깔끔(`await hostObjects.snippets.list()`)하나 COM 세팅·마샬링·기존 스타일 이탈.
- 별도 로컬 HTTP 서버(C#): 과함(YAGNI). 브릿지로 충분.
## R2. C#→JS 방향(navigate 신호)
**결정**: `CoreWebView2.PostWebMessageAsJson`로 C#→JS 푸시. JS는 `chrome.webview.addEventListener("message", …)`로 수신. 이 채널을 (a) 스니펫 핫키 시 `{type:"navigate", path:"/snippet"}`와 (b) R1의 `snippets.result` 양쪽에 공용.
- `WebHostView``SendToWeb(object msg)` 추가(`PostWebMessageAsJson(JsonSerializer.Serialize(msg))`).
- 프론트 `bridgeNavigate.ts`: navigate 메시지 수신 → 등록된 react-router `navigate` 콜백 호출. React 최상위에서 mount 시 콜백 등록.
**Rationale**: WebView2 기본 제공 기능, 의존성 0. 팔레트는 부팅 시 `/snap`으로 뜨므로(WebHostView `StartPath`), 스니펫은 이 navigate로 진입.
**주의**: 여러 `addEventListener` 등록 가능 → `snippetBridge`(결과용)와 `bridgeNavigate`(내비용)가 각자 리스너를 걸고 `type`으로 필터. 서로 결합 안 함.
## R3. 멀티 핫키(C#)
**결정**: `HotKeyService`를 **id별 다중 등록**으로 확장.
- 현재: 단일 `HotKeyId=0x1000`, 단일 `HotKeyPressed`(Action), `ProcessMessage(int msg)`**wParam(=핫키 id)을 무시**.
- 변경:
- `Register(int id, uint mods, uint vk)` — 등록 id를 인자로, 내부 `HashSet<int>`로 추적, Dispose 시 전부 Unregister.
- `event Action<int>? HotKeyPressed` — 눌린 id 전달.
- `ProcessMessage(int msg, IntPtr wParam)` — wParam.ToInt32()가 핫키 id. `App.WndProc`가 wParam을 넘기도록 수정.
- `App`: id `0x1000`=snap 토글(Ctrl+Alt+Space, 기존), `0x1001`=스니펫(Ctrl+1). 등록 실패해도 죽지 말고 트레이 안내(기존 패턴).
**Ctrl+1 상수**: `MOD_CONTROL(0x2)`, `VK_1(0x31)`. **핫키 조합은 App 상수 한 곳**에 모아 나중에 바꾸기 쉽게(spec Edge: 전역 Ctrl+1 가로채기 감수).
**스니펫 핫키 동작**: 눌리면 `ShowPalette()`(앞창 캡처+표시, 기존) 후 `SendToWeb({type:"navigate", path:"/snippet"})`. 이미 /snippet에서 보이는 상태면 토글로 숨김(선택, 구현 단순하면 적용).
## R4. SQLite 라이브러리·DB 경로
**결정**:
- 라이브러리: **`Microsoft.Data.Sqlite`** (경량·MS 공식) 를 `CodeAssist.Shell.csproj`에 추가. (현재 리포에 SQLite 참조 전무.)
- DB 경로: `%LOCALAPPDATA%\CodeAssist\snippets.db` (쓰기 가능·안정. WebView2 UserDataFolder(temp)와 별개 — temp는 캐시성).
- 스키마: 003 그대로 — `snippets(name PK, desc, body, category)`, `usage(name PK, count, last_used)`. (data-model.md 참조.)
- 저장 방식: 003의 통짜 replace 대신 **행 단위 upsert/delete**로(개별 CRUD API라 그게 자연스러움). 랭킹·검색은 프론트가 하므로 C# repo는 순수 CRUD + usage upsert만.
**Rationale**: MS 공식·의존성 가벼움. `SQLitePCLRaw` 번들 포함이라 네이티브 배포도 무난.
## R5. 첫 실행 시드(003 데이터 1회 복사)
**결정**: `SnippetSeed` — 앱 시작 시 대상 DB(`%LOCALAPPDATA%\CodeAssist\snippets.db`)가 **없을 때만**, 003의 `snippets.db`가 알려진 경로에 있으면 그 파일을 대상 경로로 복사. 있으면(=이미 시드됨) 아무것도 안 함. 003 파일이 없으면 빈 DB로 시작(에러 아님).
- **003 경로 확인은 구현 시점 lookup**: 003(winkit)의 데이터 디렉토리 규칙(`winkit.shell` 데이터 폴더 + `snippets.db`)을 그때 확인해 상수화. 못 찾으면 빈 DB로 폴백(기능 안 막음).
- 파일 복사 한 번이라 "임포트 기능"급 작업 아님(spec FR-024).
## R6. 프리뷰 렌더링
**결정**: 프리뷰·body 표시는 **순수 `<pre>` 고정폭**(공백·줄바꿈 원문 보존). `react-markdown` **안 씀**.
**Rationale**: body는 마크다운이 아니라 **날 코드**다. 마크다운 파싱하면 특수문자(`*`,`#`,`` ` ``)가 깨지고 붙여넣을 원문과 화면이 달라짐. `<pre>`가 요구사항(원문 그대로·고정폭·no-wrap)을 정확히 만족. → docs-lib(react-markdown) 트리거 안 함.
## R7. 순수 브라우저(dev) 폴백
**결정**: 스니펫 기능은 **데스크톱(WebView2) 전용**. 순수 브라우저에서 `isWebView()===false`면 `snippets.api`는 빈 목록/명확한 비활성 상태를 반환(throw 대신 graceful). 붙여넣기·핫키·SQLite 모두 호스트가 필요하므로 순수 브라우저 지원은 비목표.
**Rationale**: DEBUG 개발도 WebView2 안에서 vite를 물어 돌리므로(WebHostView가 localhost:15173을 WebView2로 네비) 브릿지가 살아있음 → 개발 지장 없음. 순수 크롬 탭 지원은 YAGNI.
## 미해결/구현 시점 확인 목록
- 003 `snippets.db` 실제 경로(R5) — winkit 데이터 디렉토리 규칙 확인 후 상수화.
- `Microsoft.Data.Sqlite` 버전 핀 — 구현 시 최신 안정.