116 lines
3.6 KiB
Markdown
116 lines
3.6 KiB
Markdown
# Page Clipping + Highlight Implementation Plan
|
|
|
|
## Goal
|
|
- 사용자가 특정 페이지에서 선택한 텍스트를 클리핑(저장)한다.
|
|
- 저장된 클립은 원문 위치에 하이라이트로 다시 표시된다.
|
|
- 팝업에서 클립 목록을 보고 해당 위치로 다시 이동할 수 있다.
|
|
|
|
## Scope (v1)
|
|
1. 지원 대상
|
|
- 텍스트 기반 웹페이지 (`contenteditable` 제외, 일반 DOM 문서 우선)
|
|
- 탭 단위 클립 저장/조회
|
|
|
|
2. 포함 기능
|
|
- 선택 텍스트 캡처
|
|
- 하이라이트 주입/복원
|
|
- 클립 목록 조회/삭제
|
|
- 클릭 시 원문 위치로 스크롤
|
|
|
|
3. 제외 기능(후속)
|
|
- PDF/캔버스/이미지 OCR 하이라이트
|
|
- 협업 공유/서버 동기화
|
|
- 다중 색상 태깅, 폴더 분류
|
|
|
|
## Data Model (Draft)
|
|
```ts
|
|
type ClipItem = {
|
|
id: string
|
|
tabId?: number
|
|
pageUrl: string
|
|
pageTitle: string
|
|
quote: string
|
|
createdAt: string
|
|
color: 'yellow'
|
|
anchor: {
|
|
textStart: string
|
|
textEnd: string
|
|
exact: string
|
|
prefix?: string
|
|
suffix?: string
|
|
xpathStart?: string
|
|
xpathEnd?: string
|
|
startOffset?: number
|
|
endOffset?: number
|
|
}
|
|
}
|
|
```
|
|
|
|
## Architecture
|
|
1. Content Script
|
|
- 사용자 선택(`window.getSelection`)에서 `Range` 추출
|
|
- `anchor` 생성(텍스트 인용 + DOM 포지션)
|
|
- background에 `clip:create` 메시지 전송
|
|
- `clip:apply` 이벤트 수신 시 하이라이트 렌더링
|
|
|
|
2. Background (Service Worker)
|
|
- `clip:create`, `clip:list`, `clip:delete`, `clip:reveal` 메시지 처리
|
|
- `storage.local` 기반 영속화
|
|
- 탭 활성화/페이지 완료 시 `clip:sync` 트리거
|
|
|
|
3. Popup/History UI
|
|
- 현재 탭 URL 기준 클립 목록 요청
|
|
- 항목별 `위치로 이동`, `삭제` 버튼
|
|
- 상태 메시지(저장 성공/실패) 표시
|
|
|
|
## Step-by-Step
|
|
1. Step 1: Selection Capture + Overlay
|
|
- `src/lib/clipAnchor.ts` 생성
|
|
- `Range -> anchor` 변환 유틸 구현
|
|
- `src/content/index.ts`에 단축키/우클릭 기반 캡처 진입점 추가
|
|
- `span[data-gomdown-clip]` 하이라이트 렌더링/해제 로직 구현
|
|
|
|
2. Step 2: Store + Message Channel
|
|
- `src/lib/clipStore.ts` 생성 (`list/upsert/delete/byUrl`)
|
|
- background message router에 `clip:*` 타입 추가
|
|
- dedupe(`pageUrl + exact + createdAt window`) 정책 추가
|
|
|
|
3. Step 3: Popup UI
|
|
- `src/popup/main.tsx`에 "Clips" 섹션 추가
|
|
- 현재 탭 URL 클립 조회 + 목록 렌더링
|
|
- `Reveal/Delete` 액션과 오류 상태 처리
|
|
|
|
4. Step 4: Re-anchoring
|
|
- 페이지 로드 시 `clip:list` 후 순차 복원
|
|
- 우선순위:
|
|
- `exact + prefix/suffix` 텍스트 매칭
|
|
- 실패 시 `xpath + offset` 복구
|
|
- 둘 다 실패 시 `broken anchor`로 표시
|
|
|
|
5. Step 5: Export/Import + QA
|
|
- JSON export/import 메시지 추가
|
|
- 샘플 페이지(뉴스, 블로그, SPA) 수동 테스트
|
|
- 회귀 체크리스트 문서화
|
|
|
|
## QA Checklist
|
|
- 같은 페이지 새로고침 후 하이라이트가 유지된다.
|
|
- SPA 라우팅(YouTube/블로그) 후에도 복원 시도가 동작한다.
|
|
- 원문 DOM이 일부 바뀐 경우, 텍스트 매칭 fallback이 동작한다.
|
|
- 클립 삭제 시 화면/저장소에서 모두 제거된다.
|
|
- 확장 비활성화 상태에서 캡처가 차단된다.
|
|
|
|
## Risk & Mitigation
|
|
1. DOM 변형으로 앵커 붕괴
|
|
- 텍스트 인용 앵커 + XPath 이중 저장
|
|
|
|
2. 성능 저하(클립 다수)
|
|
- URL 단위 lazy apply, viewport 근처 우선 렌더
|
|
|
|
3. 사이트 충돌(CSS/스크립트)
|
|
- 고유 data-attribute와 최소 침습 스타일 사용
|
|
|
|
## Definition of Done
|
|
- 사용자는 텍스트 선택 후 1회 액션으로 클립 저장 가능
|
|
- 같은 URL 재방문 시 하이라이트 자동 복원
|
|
- 팝업에서 클립 조회/이동/삭제 가능
|
|
- 크롬 기준 수동 시나리오 10개 중 9개 이상 성공
|