Files
CODE_ASSISTANT/specs/001-snippet-palette/contracts/bridge-messages.md
T
2026-09-16 17:22:14 +09:00

86 lines
5.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Contract: 스니펫 브릿지 메시지
기준 앱은 Tauri이고 환경별 전달은 `specs/004-tauri-shell/contracts/transport-mapping.md`를 따름. 아래 C# 요청·응답 설명은 참고용 legacy 채널 계약임.
## Tauri 단계별 창 크기 (2026-09-11)
`{type:"window.snippetLayout", stage:"search"|"results"|"preview"|"editor"}``invoke("window_snippet_layout", {stage})`.
- 검색 `640×84`, 결과 `640×440`, 프리뷰 `840×520`, 편집 `960×600` 논리 픽셀. 실제 크기와 DPI·작업 영역 보정은 Rust 소유.
- 호스트가 Tauri일 때만 전송. legacy·브라우저는 화면 단계만 바뀌고 실제 창 크기는 제어하지 않음.
- 다른 route 보고 시 원래 배치를 복원. 종료 직전에 복원한 크기를 저장해 축소 상태가 일반 창 크기를 덮어쓰지 않음.
- 허용되지 않은 stage와 크기 조작 실패는 reject. 기존 transport 오류 토스트로 표시.
## Tauri 앱에 붙여넣기 (2026-09-11)
`{type:"paste.code", text}``invoke("paste_code", {text})`. 스니펫 `Ctrl+Enter`와 기존 코드뷰어 붙여넣기 버튼이 같은 경로를 사용함.
- `Enter`는 기존 원문 복사 후 창 숨김, 복사 버튼은 복사만 하고 창 유지. `Ctrl+Enter`는 선택한 코드에만 적용하고 데스크톱·미편집·비조합 상태에서 한 번만 실행.
- 호스트는 키 해제를 기다린 뒤 직전 앱으로 돌아가고, 실제 입력 직전에 대상·포커스·키 상태를 다시 확인. 대기 중 UI는 멈추지 않고 중복 붙여넣기는 실행하지 않음.
- 성공한 입력 전송 뒤에만 창을 숨김. 실패하면 창을 유지하거나 다시 표시하고 기존 bridge 오류 토스트로 안내.
- 클립보드 기록 뒤에 실패하면 원문은 그대로 남김. 클립보드 자체 기록 실패에는 복사됐다고 안내하지 않음.
- 다른 앱의 편집기가 실제로 내용을 받아들였는지까지 성공 응답이 보장하지는 않음. 별도 입력창에서 end-to-end로 검사하고, 관리자 권한 앱 등 Windows 입력 제한은 오류로 구분.
## 기존 (변경 없음)
| 방향 | 메시지 | 처리 |
|---|---|---|
| JS→C# | `{type:"window.hide"}` | 런처 창 숨김 |
| JS→C# | `{type:"paste.code", text}` | 직전 창에 `text` 붙여넣기 + 창 숨김 |
현재 Enter는 선택한 스니펫 원문을 클립보드에 복사한 뒤 창을 숨김. 기존 `paste.code`는 코드뷰어의 붙여넣기 버튼과 스니펫 Ctrl+Enter에서 사용함. Tauri의 오류·창 표시 동작은 위 절을 따름.
## 신규 — C#→JS 푸시(내비게이션)
| 방향 | 메시지 | 처리 |
|---|---|---|
| C#→JS | `{type:"navigate", path}` | 프론트가 react-router로 `path`로 이동 |
- 스니펫 핫키(Ctrl+1) → C# `ShowPalette()``SendToWeb({type:"navigate", path:"/snippet"})`.
- 프론트 `bridgeNavigate.ts`가 수신 → 등록된 navigate 콜백 호출.
## 신규 — 스니펫 데이터(요청 → 응답, reqId 상관)
각 요청은 프론트가 부여한 `reqId`(문자열, 단조 증가) 포함. C#는 같은 `reqId`로 결과를 돌려준다.
### 요청 (JS→C#)
| 메시지 | 의미 |
|---|---|
| `{type:"snippets.list", reqId}` | 전체 스니펫(usage 병합) 반환 |
| `{type:"snippets.create", reqId, snippet:{name,desc,body,category}}` | 생성(중복 name 거부) |
| `{type:"snippets.update", reqId, snippet:{name,desc,body,category}}` | 수정(name=키, 나머지 갱신) |
| `{type:"snippets.delete", reqId, name}` | 삭제 |
| `{type:"snippets.recordUse", reqId, name}` | usage count+1, last_used=now |
### 응답 (C#→JS)
```jsonc
// 성공
{ "type":"snippets.result", "reqId":"<echo>", "ok":true, "data": <op별 페이로드> }
// 실패
{ "type":"snippets.result", "reqId":"<echo>", "ok":false, "error":"<사람이 읽을 메시지>" }
```
| 요청 | 성공 `data` |
|---|---|
| `snippets.list` | `Snippet[]` (usageCount·lastUsed 병합) |
| `snippets.create` / `update` | 저장된 `Snippet` (또는 갱신 후 전체 목록 — 구현 단순한 쪽) |
| `snippets.delete` | `{name}` |
| `snippets.recordUse` | `{name, usageCount, lastUsed}` |
**에러 계약**: 중복 name 생성/빈 필드 → `ok:false` + 한글 메시지. 프론트는 `ok:false``Error`로 reject → react-query `onError` → sonner 토스트(memos 패턴).
## C# 라우팅
`OnWebMessageReceived``type`으로 분기:
- `window.hide` / `paste.code` → 기존.
- `snippets.*``SnippetBridge`(신규)에 위임: JSON에서 `reqId`·`snippet`·`name` 파싱 → `SnippetRepository` 호출 → `SendToWeb``snippets.result` 응답.
- 파싱은 기존 `type`+`text`만 읽던 걸 확장(요청별 필요한 필드 추출).
## 프론트 리스너
- `snippetBridge.ts`: `Map<reqId,{resolve,reject}>`, `chrome.webview.addEventListener("message")`에서 `snippets.result`만 필터해 매칭 reqId resolve/reject. 각 op은 `postMessage`+promise.
- `bridgeNavigate.ts`: 같은 message 이벤트에서 `navigate`만 필터. (리스너 여러 개 가능 — 서로 독립.)
- 순수 브라우저(`isWebView()===false`): 요청은 즉시 빈 결과/비활성(데스크톱 전용, research R7).