Initial Commit
This commit is contained in:
@@ -0,0 +1,193 @@
|
||||
---
|
||||
description: "Task list for 스니펫 팔레트 (Snippet Palette)"
|
||||
---
|
||||
|
||||
# Tasks: 스니펫 팔레트 (Snippet Palette)
|
||||
|
||||
**Input**: `specs/001-snippet-palette/` (plan.md, spec.md, research.md, data-model.md, contracts/, quickstart.md)
|
||||
|
||||
**Tests**: 포함(이 프로젝트는 superpowers TDD RED→GREEN + `npm run test`/`dotnet test` 통과가 완료 기준 — CLAUDE.md §4).
|
||||
|
||||
**Organization**: user story별 phase. US1(P1)만 해도 MVP(스니펫 있으면 검색→붙여넣기 됨).
|
||||
|
||||
## Format: `[ID] [P?] [Story] Description (파일경로)`
|
||||
|
||||
- **[P]**: 다른 파일·의존 없음 → 병렬 가능
|
||||
- **[US#]**: 해당 user story
|
||||
- 핸드오프: 여기서부터 superpowers 뒷단(`executing-plans`/`subagent-driven-development` + `test-driven-development`). 단계(phase) 하나 끝날 때마다 `specs/001-snippet-palette/stage-N.md` 기록.
|
||||
|
||||
---
|
||||
|
||||
## Phase 1: Setup (공용 기반)
|
||||
|
||||
- [ ] T001 `Microsoft.Data.Sqlite` 패키지 추가 (`3_windowsApp/CodeAssist.Shell/CodeAssist.Shell.csproj`)
|
||||
- [ ] T002 [P] `PATHS.SNIPPET = "/snippet"` 추가 (`2_frontend/src/config/routes.ts`)
|
||||
- [ ] T003 [P] feature 폴더 뼈대 + 타입 생성: `Snippet`/`SnippetInput` (`2_frontend/src/features/snippets/types.ts`, data-model.md 준용)
|
||||
|
||||
---
|
||||
|
||||
## Phase 2: Foundational (모든 story 선행 — 반드시 먼저)
|
||||
|
||||
**목표 체크포인트**: `Ctrl+1` → 팔레트가 `/snippet`(빈 화면)로 뜨고, `snippets.api.list()`가 시드된 데이터를 왕복으로 돌려준다.
|
||||
|
||||
- [ ] T004 `HotKeyService` 멀티 핫키로 확장 — `Register(int id, mods, vk)`·`HashSet<int>` 추적·`event Action<int> HotKeyPressed`·`ProcessMessage(int msg, IntPtr wParam)` (`3_windowsApp/CodeAssist.Shell/Platform/HotKeyService.cs`) + `App.WndProc`가 wParam 전달하도록 수정 (`3_windowsApp/CodeAssist.App/App.xaml.cs`)
|
||||
- [ ] T005 `Ctrl+1`(id `0x1001`, `MOD_CONTROL|VK_1`) 등록 + 눌리면 `ShowPalette()` 후 navigate 신호 송신. 핫키 조합 상수는 한 곳에 모음 (`3_windowsApp/CodeAssist.App/App.xaml.cs`) — depends T004
|
||||
- [ ] T006 `WebHostView`에 `SendToWeb(object)`(=`PostWebMessageAsJson`) 추가 + `OnWebMessageReceived`가 `snippets.*`를 `SnippetBridge`로 위임 + `navigate` 푸시 경로 (`3_windowsApp/CodeAssist.App/Views/WebHostView.xaml.cs`) — depends T004/T005 (navigate 대상)
|
||||
- [ ] T007 [P] `SnippetRepository` 뼈대 + 스키마 초기화(`CREATE TABLE snippets/usage`) + DB 경로 `%LOCALAPPDATA%\CodeAssist\snippets.db` (`3_windowsApp/CodeAssist.Shell/Storage/SnippetRepository.cs`, data-model.md 스키마)
|
||||
- [ ] T008 [P] `SnippetSeed` — 첫 실행 시 대상 DB 없고 003 `snippets.db` 있으면 1회 복사, 없으면 빈 DB. App 시작 시 repo 사용 전 호출 (`3_windowsApp/CodeAssist.Shell/Storage/SnippetSeed.cs`) — 003 경로는 구현 시 확인(research R5)
|
||||
- [ ] T009 [P] 프론트 브릿지 왕복 인프라 — `reqId` Map + `chrome.webview` 메시지 리스너(`snippets.result` 매칭 resolve/reject), 순수 브라우저면 비활성 (`2_frontend/src/lib/bridge/snippetBridge.ts`, contracts/bridge-messages.md)
|
||||
- [ ] T010 [P] `bridgeNavigate.ts` — `navigate` 메시지 수신 → 등록된 react-router navigate 콜백 호출. 앱 최상위에서 mount 시 콜백 등록 (`2_frontend/src/lib/bridge/bridgeNavigate.ts` + 등록 지점)
|
||||
- [ ] T011 `/snippet` 라우트 + 임시 placeholder 페이지를 `SnapLayout` 그룹에 추가 (`2_frontend/src/routes.tsx`) — depends T002
|
||||
|
||||
**Checkpoint**: Ctrl+1 → /snippet 빈 화면 뜨고 list 왕복 확인.
|
||||
|
||||
---
|
||||
|
||||
## Phase 3: User Story 1 - 핫키로 검색→앞 창 붙여넣기 (P1) 🎯 MVP
|
||||
|
||||
**Goal**: 스니펫 검색·프리뷰·usage랭킹 + Enter/Alt로 앞 창에 붙여넣기.
|
||||
|
||||
**Independent Test**: 시드된 스니펫 상태에서 에디터 포커스 → Ctrl+1 → 검색 → Enter → 에디터에 body 원문 삽입 + usage 상승.
|
||||
|
||||
### Tests (RED 먼저)
|
||||
|
||||
- [ ] T012 [P] [US1] `core/search` 단위테스트 — 키워드 AND, name+desc 대문자, body 제외, 빈쿼리 (`2_frontend/src/features/snippets/core/search.test.ts`)
|
||||
- [ ] T013 [P] [US1] `core/ranking` 단위테스트 — `(-count,-lastUsed,name)` 정렬 (`2_frontend/src/features/snippets/core/ranking.test.ts`)
|
||||
- [ ] T014 [P] [US1] `SnippetRepository.List/RecordUse` xUnit (temp sqlite, usage 조인·upsert) (`3_windowsApp/CodeAssist.Tests/SnippetRepositoryTests.cs`)
|
||||
- [ ] T015 [P] [US1] `snippets.api` list/recordUse — 브릿지 mock 테스트 (`2_frontend/src/features/snippets/api/snippets.api.test.ts`)
|
||||
|
||||
### Impl
|
||||
|
||||
- [ ] T016 [P] [US1] `core/search.ts` (검색 순수함수, category 파라미터 포함) (`2_frontend/src/features/snippets/core/search.ts`)
|
||||
- [ ] T017 [P] [US1] `core/ranking.ts` (usage 정렬) (`2_frontend/src/features/snippets/core/ranking.ts`)
|
||||
- [ ] T018 [US1] `SnippetRepository.List`(snippets⨝usage) + `RecordUse`(count+1,last_used=now) (`3_windowsApp/CodeAssist.Shell/Storage/SnippetRepository.cs`)
|
||||
- [ ] T019 [US1] `SnippetBridge` 핸들러 `snippets.list`·`snippets.recordUse` → repo 호출 후 `snippets.result` 응답 (`3_windowsApp/CodeAssist.App/Views/WebHostView.xaml.cs` 또는 신규 SnippetBridge.cs)
|
||||
- [ ] T020 [US1] `snippets.api.ts`(list, recordUse) + `useSnippets`/`useRecordUse` (SNIPPETS_KEY·invalidate, memos 패턴) (`2_frontend/src/features/snippets/api/snippets.api.ts`, `hooks/useSnippets.ts`)
|
||||
- [ ] T021 [US1] `SnippetPalettePage` — snap `SessionListPage` 복제 → 검색입력 + 결과리스트 + 우측 프리뷰, Raycast 룩 리스킨 (`2_frontend/src/features/snippets/pages/SnippetPalettePage.tsx`)
|
||||
- [ ] T022 [US1] 키 조작 — ↑↓ nav(프리뷰 갱신), Enter→`pasteToApp(body)`+`recordUse`+`hideWindow`, Alt+1~9 즉시 붙여넣기, Esc 단계(비움→숨김), IME 안전 (`SnippetPalettePage.tsx`)
|
||||
- [ ] T023 [P] [US1] `SnippetRow`(제목/부제/우측 번호 액세서리) + `PreviewPane`(`<pre>` 고정폭·원문보존, react-markdown 안 씀) Raycast 결 (`2_frontend/src/features/snippets/components/`)
|
||||
- [ ] T024 [US1] 빈 상태 안내 + 상위 9개 `1~9` 번호 표시 (`SnippetPalettePage.tsx`)
|
||||
|
||||
**Checkpoint**: US1 단독으로 완전 동작(MVP). **STOP & VALIDATE** — quickstart 1~6,10,11.
|
||||
|
||||
---
|
||||
|
||||
## Phase 4: User Story 2 - 생성/편집/삭제 (P2)
|
||||
|
||||
**Goal**: 팔레트에서 스니펫 CRUD. 생성 시 body는 클립보드로 프리필.
|
||||
|
||||
**Independent Test**: 생성→검색에 뜸 / 빈 이름·내용 거부 / 중복 이름 거부 / F2 편집(이름 읽기전용) / 편집창 삭제.
|
||||
|
||||
### Tests (RED 먼저)
|
||||
|
||||
- [ ] T025 [P] [US2] `SnippetRepository.Create/Update/Delete` xUnit — 중복 거부·정규화·검증 (`3_windowsApp/CodeAssist.Tests/SnippetRepositoryTests.cs`)
|
||||
- [ ] T026 [P] [US2] `snippets.api` create/update/remove — 브릿지 mock(성공/`ok:false` reject) (`2_frontend/src/features/snippets/api/snippets.api.test.ts`)
|
||||
|
||||
### Impl
|
||||
|
||||
- [ ] T027 [US2] `SnippetRepository.Create/Update/Delete` — name 정규화·유일성·body 비어있지않음(위반 시 에러) (`3_windowsApp/CodeAssist.Shell/Storage/SnippetRepository.cs`)
|
||||
- [ ] T028 [US2] `SnippetBridge` 핸들러 create/update/delete (`ok:false`+한글 에러 계약) (`WebHostView.xaml.cs`/SnippetBridge.cs)
|
||||
- [ ] T029 [US2] `snippets.api` create/update/remove + `useCreateSnippet`/`useUpdateSnippet`/`useDeleteSnippet`(invalidate + sonner 한글 토스트, ApiError 형태) (`api/snippets.api.ts`, `hooks/useSnippets.ts`)
|
||||
- [ ] T030 [US2] `EditDialog` — 생성/편집 공용, 편집 시 name 읽기전용, 생성 시 body=클립보드(`navigator.clipboard.readText()`, 실패 시 빈값), 검증 (`2_frontend/src/features/snippets/components/EditDialog.tsx`)
|
||||
- [ ] T031 [US2] 팔레트 배선 — 생성 액션(Ctrl+N/버튼), F2·더블클릭 편집, 다이얼로그 삭제+확인, 완료 후 목록 새로고침 (`SnippetPalettePage.tsx`)
|
||||
|
||||
**Checkpoint**: US1+US2 동작. quickstart 7~8.
|
||||
|
||||
---
|
||||
|
||||
## Phase 5: User Story 3 - category 칩 필터 (P3)
|
||||
|
||||
**Goal**: category 칩으로 분류별 필터.
|
||||
|
||||
**Independent Test**: 다른 category 스니펫 있을 때 칩 선택 → 그 분류만.
|
||||
|
||||
### Tests (RED 먼저)
|
||||
|
||||
- [x] T032 [P] [US3] `core/search` category 필터 케이스 추가 (`core/search.test.ts`)
|
||||
|
||||
### Impl
|
||||
|
||||
- [x] T033 [US3] category 필터 확정(`core/search.ts` 파라미터 활용) + 목록에서 존재하는 category 도출 (`core/search.ts`, `SnippetPalettePage.tsx`)
|
||||
- [x] T034 [US3] `CategoryChips` — "전체" + categories, `Ctrl+←/→` 칩 이동, 페이지 배선 (`2_frontend/src/features/snippets/components/CategoryChips.tsx`, `SnippetPalettePage.tsx`)
|
||||
|
||||
**Checkpoint**: 전 story 동작. quickstart 9.
|
||||
|
||||
---
|
||||
|
||||
## Phase 6: Polish & 검증
|
||||
|
||||
- [ ] T035 `npm run format` → `npm run lint` + `npm run test` + `npm run build` 통과 + `dotnet test 3_windowsApp/CodeAssist.Tests` 통과
|
||||
- [ ] T036 quickstart.md 수동 시나리오 1~11 전체 확인(붙여넣기 대상 정확성·영속·시드)
|
||||
- [ ] T037 [P] `specs/001-snippet-palette/report.md`(계획 vs 결과) 작성 → 최종 승인용
|
||||
|
||||
---
|
||||
|
||||
## Dependencies & Execution Order
|
||||
|
||||
- **Phase 1 Setup** → **Phase 2 Foundational**(전 story 차단) → **US1 → US2 → US3** → **Polish**.
|
||||
- Foundational 안: T004→T005→T006 순(같은 C# App/WebHostView 계열). T007/T008/T009/T010은 서로 [P]. T011은 T002 이후.
|
||||
- US 간: US1은 read/paste, US2는 write(같은 repo/api/page 파일 확장이라 US1 뒤가 안전 — 병렬 시 파일 충돌 주의), US3은 category(대부분 독립).
|
||||
- Story 내: 테스트(RED) → 순수함수/모델 → repo/bridge → api/hooks → 페이지/컴포넌트.
|
||||
|
||||
## Parallel Opportunities
|
||||
|
||||
- Setup: T002·T003 [P].
|
||||
- Foundational: T007·T008·T009·T010 [P](C# 저장소 vs 프론트 브릿지 서로 다른 파일).
|
||||
- US1 테스트: T012·T013·T014·T015 [P]. US1 순수함수: T016·T017 [P]. 컴포넌트 T023 [P].
|
||||
- **주의**: `SnippetRepository.cs`·`snippets.api.ts`·`SnippetPalettePage.tsx`는 여러 태스크가 손대므로 같은 story 안에선 [P] 안 붙임(파일 충돌).
|
||||
|
||||
## Implementation Strategy
|
||||
|
||||
1. Setup + Foundational → "Ctrl+1로 /snippet 뜨고 데이터 왕복" 체크포인트.
|
||||
2. **US1(MVP)** → 독립 검증(붙여넣기 되면 도구로서 완성) → 데모.
|
||||
3. US2(CRUD) → US3(category) 순 증분.
|
||||
4. 각 단계 끝 `stage-N.md`, 완료 전 `superpowers:verification-before-completion` + `/code-review`, 최종 `report.md` → 승인 → merge(`/cmp`).
|
||||
|
||||
## Notes
|
||||
|
||||
- [P] = 다른 파일·무의존. [US#] = 추적용.
|
||||
- 정당 편차(plan §Constitution): 데이터는 `@/lib/api/client` 아닌 브릿지, `Snippet` 타입은 feature-local.
|
||||
- 붙여넣기·핫키·앞창캡처·팔레트 nav는 **기존 재사용** — 새로 만들지 말 것.
|
||||
|
||||
## 2026-09-11: 단계별 런처와 실제 창 크기
|
||||
|
||||
`spec.md`의 2026-09-11 사용자 승인 범위만 완료함. 위 초기 .NET 이식·CRUD·사용기록의 미완료 항목을 완료로 바꾸지 않음.
|
||||
|
||||
- [x] 검색창만 시작하고 검색 후 결과를 표시. 미선택 Enter는 아무것도 복사하지 않음.
|
||||
- [x] 화살표·클릭으로 직접 선택할 때만 프리뷰를 열고 검색·분류 변경 시 선택 해제.
|
||||
- [x] 결과 행을 간결하게 바꾸고 하단 키 안내 추가. 기존 코드뷰어·분할 크기 조절 유지.
|
||||
- [x] Tauri `window_snippet_layout`으로 검색 640×84 / 결과 640×440 / 프리뷰·편집 960×600 적용. DPI·작업 영역·창 테두리 보정.
|
||||
- [x] 챗봇 복귀·최대화 복원·종료 저장에서 원래 배치 보존. CloseRequested 캐시 순서 문제를 실제로 재현하고 명시적 저장으로 수정.
|
||||
- [x] Esc 포커스 복원과 재소환 시 삭제 확인창 닫힘을 실패하는 회귀 검사로 재현 후 수정.
|
||||
- [x] `npm run format`, lint(오류 0·기존 경고 9), 프론트 46파일·259검사, build 통과. Rust fmt·6검사 통과·로컬 DB 전용 1검사 기본 제외, clippy `-D warnings` 통과.
|
||||
- [x] 실제 저장된 스니펫 SELECT 검색 7개, 키보드 선택·코드 프리뷰·빈 결과·편집·재소환을 Tauri에서 확인. 데이터 저장·삭제는 실행하지 않음.
|
||||
- [x] 실제 Windows 크기 전환·화면 우하단 확장·최대화 전 일반 배치 복원·축소 종료 후 960×680 재시작 확인.
|
||||
|
||||
## 2026-09-11: 코드 프리뷰 군더더기 제거
|
||||
|
||||
- [x] 프리뷰 이름·설명·분류, 장식 점과 코드 헤더, 박스 테두리·그림자를 제거. 문법 강조와 줄 번호 유지.
|
||||
- [x] `CodeBlock`의 plain 표시를 재사용해 프리뷰 안쪽 스크롤을 하나로 합침. 챗봇 카드의 기본 모양은 유지.
|
||||
- [x] 기존 복사·붙여넣기 버튼을 `CodeActions`로 공유하고 스니펫 하단에 배치. 버튼 Enter를 전역 즉시 복사가 가로채지 않도록 수정.
|
||||
- [x] 실제 Tauri 프리뷰의 헤더·버튼 0개, 세로 스크롤 1개, 하단 액션 2개와 문법 강조를 확인. 클릭·키보드 복사 원문과 창 유지 회귀 검사 통과.
|
||||
- [x] format·lint(오류 0, 기존 경고 9)·46파일 260검사·build 통과.
|
||||
|
||||
### 별도 관찰
|
||||
|
||||
- [ ] 최소화된 앱을 검사 도중 종료한 뒤 `/snap`이 640×84로 시작하는 현상을 관찰함. 최소화 종료 경로의 배치 저장은 별도로 재현·확인할 것. 이번 프리뷰 UI 변경에는 Rust 수정을 포함하지 않음.
|
||||
|
||||
## 2026-09-11: 프리뷰 전체 창 축소
|
||||
|
||||
- [x] 사용자 추가 요청에 따라 코드 프리뷰만 960×600에서 840×520으로 축소. 검색 640×84·결과 640×440·편집 960×600은 유지.
|
||||
- [x] 실제 Tauri에서 840×520 화면, 하단 액션 표시, 세로 스크롤 1개를 확인. F2 편집 960×600 → 닫기 후 프리뷰 840×520 복귀 확인.
|
||||
- [x] Rust fmt 검사와 6개 검사 통과. 프론트는 직전 UI 변경 검증 46파일·260개 검사 및 build 통과 상태를 유지함.
|
||||
|
||||
## 2026-09-11: Ctrl+Enter 앱에 붙여넣기
|
||||
|
||||
- [x] 기존 `pasteToApp`을 Ctrl+Enter에서도 사용. 일반 Enter·복사 버튼 동작은 유지하고, 미선택·IME·편집·브라우저·반복키·추가 modifier는 차단.
|
||||
- [x] 네이티브 붙여넣기를 비동기 작업으로 실행. 키 해제는 최대 2초 대기하고, 중복 요청·대상 HWND/PID/thread 변경·포커스 이탈·재입력은 취소.
|
||||
- [x] `SetForegroundWindow` 직후 정상 활성화를 실패로 오판하는 경합을 실제로 재현. 80ms 구간에서 첫 활성화 전 팔레트·NULL만 기다리고, 대상 활성화 이후 이탈은 취소하도록 수정.
|
||||
- [x] `SendInput` 전송 개수를 확인하고 성공 때만 창 숨김. 실패하면 창과 오류 안내 유지, 이미 복사된 원문은 클립보드에 보존.
|
||||
- [x] 저장하지 않는 별도 Windows 입력창에서 실제 Ctrl+Enter 누름·반복·해제 → 1,420자 원문 1회 입력·창 숨김 확인. 기존 붙여넣기 버튼도 같은 원문 입력 확인.
|
||||
- [x] 실제 네이티브 입력으로 한글·CRLF·탭·앞뒤 공백 보존 확인. 대상 없음·닫힌 대상·중복 요청·2초 키 대기 초과·대기 중 포커스 이탈에서 입력 취소·오류·클립보드 보존 확인.
|
||||
- [x] 프론트 format·lint(오류 0, 기존 경고 9)·46파일 264검사·build 통과. Rust fmt·6검사(로컬 DB 검사 1개 기본 제외)·clippy `-D warnings` 통과.
|
||||
- 검증 범위: 별도 WinForms 입력창을 사용했고 사용자 문서·원본 스니펫 DB를 바꾸지 않음. VS Code·SAP·관리자 권한 앱 각각의 실제 수신 검사는 별도이며, 키 전송 성공이 모든 앱의 붙여넣기 수신을 보장하지는 않음.
|
||||
Reference in New Issue
Block a user