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
+91
View File
@@ -0,0 +1,91 @@
# Data Model: 스니펫 팔레트 (Phase 1)
003 스키마를 그대로 따른다. 랭킹·검색은 프론트에서 계산하므로, 저장은 원자적 데이터만 담는다.
## 엔티티
### Snippet
붙여넣을 코드 조각 한 개.
| 필드 | 타입 | 제약 | 설명 |
|---|---|---|---|
| `name` | string | **PK**, 정규화(대문자·공백→`_`), 유일 | 식별 키, 결과 행 제목 |
| `desc` | string | 기본 `""` | 짧은 설명, 결과 부제 · 검색 대상 |
| `body` | string | 비어있지 않음, 원문 보존 | 실제 붙여넣어지는 내용 |
| `category` | string | 기본 `"코드"` | 분류 라벨 |
- 003의 `change_word1/2`**가져오지 않음**(안 쓰는 유물).
- 저장 시 검증: `name` 정규화 후 비어있지 않음 + 유일(중복 거부), `body` 비어있지 않음. (spec FR-016)
- `name`은 편집 불가(키). desc/body/category만 수정. (FR-017)
### Usage
스니펫별 사용 통계. 별도 테이블(003과 동일).
| 필드 | 타입 | 제약 | 설명 |
|---|---|---|---|
| `name` | string | **PK**, Snippet.name 참조 | 어떤 스니펫 |
| `count` | integer | 기본 0 | 붙여넣기 성공 누적 |
| `last_used` | integer | unix 초 | 마지막 붙여넣기 시각 |
- 붙여넣기 성공 시 upsert: `count+1`, `last_used=now`. (FR-011)
- Snippet에 대응 Usage가 없으면 `(0,0)`으로 취급.
## SQLite 스키마 (C# 호스트 소유)
```sql
CREATE TABLE IF NOT EXISTS snippets (
name TEXT PRIMARY KEY,
desc TEXT NOT NULL DEFAULT '',
body TEXT NOT NULL DEFAULT '',
category TEXT NOT NULL DEFAULT '코드'
);
CREATE TABLE IF NOT EXISTS usage (
name TEXT PRIMARY KEY,
count INTEGER NOT NULL DEFAULT 0,
last_used INTEGER NOT NULL DEFAULT 0
);
```
파일: `%LOCALAPPDATA%\CodeAssist\snippets.db`. 첫 실행 시 003의 `snippets.db`가 있으면 1회 복사(시드). (research R4·R5)
## 프론트 타입 (`features/snippets/types.ts`)
로컬 엔티티라 `types/api.ts`(서버 DTO 전용) 아님.
```ts
export interface Snippet {
name: string
desc: string
body: string
category: string
usageCount: number // usage.count (없으면 0)
lastUsed: number // usage.last_used unix초 (없으면 0)
}
export interface SnippetInput { // 생성/수정 입력
name: string
desc: string
body: string
category: string
}
```
- 브릿지 응답에서 snippets ⨝ usage 조인 결과를 `Snippet`(usageCount/lastUsed 병합)로 받는다. C#가 조인해 내려주거나, 두 테이블을 각각 내려 프론트에서 병합 — 구현 시 단순한 쪽(C# 조인 권장).
## 파생 규칙 (프론트 순수함수)
### 검색 (`core/search.ts`) — FR-005/006
- 입력: 스니펫 목록, 쿼리 문자열, (선택)category.
- category가 있으면 그 분류로 먼저 제한. `"전체"`면 전체.
- 쿼리를 공백으로 split → 키워드들. 각 키워드(대문자화)가 **전부(AND)** `(name + " " + desc)`의 대문자화 문자열에 포함될 때만 통과. `body`는 검색 안 함. 퍼지 없음.
- 빈 쿼리 → (category 제한된) 전체.
### 랭킹 (`core/ranking.ts`) — FR-007
- 정렬 키: `count desc, lastUsed desc, name asc`.
- 모든 결과 집합(검색·전체 공통)에 적용. 상위 9개에 `1~9` 번호(Alt+숫자 대응). (FR-008)
## 상태 전이
스니펫: (없음) → 생성 → (편집)* → 삭제.
Usage: 붙여넣기 성공마다 count 증가 · last_used 갱신 → 랭킹 상단으로 부상.