@designfever/web-review-kit 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/README.md +59 -184
  2. package/dist/{chunk-U5K2YGGL.js → chunk-EJDROXJM.js} +2583 -3085
  3. package/dist/chunk-EJDROXJM.js.map +1 -0
  4. package/dist/index.cjs +2615 -2900
  5. package/dist/index.cjs.map +1 -1
  6. package/dist/index.d.cts +10 -6
  7. package/dist/index.d.ts +10 -6
  8. package/dist/index.js +220 -7
  9. package/dist/index.js.map +1 -1
  10. package/dist/react-shell.cjs +8364 -6651
  11. package/dist/react-shell.cjs.map +1 -1
  12. package/dist/react-shell.d.cts +7 -3
  13. package/dist/react-shell.d.ts +7 -3
  14. package/dist/react-shell.js +5163 -3425
  15. package/dist/react-shell.js.map +1 -1
  16. package/dist/{types-D_mNjOHx.d.cts → types-NiCp9JJQ.d.cts} +6 -14
  17. package/dist/{types-D_mNjOHx.d.ts → types-NiCp9JJQ.d.ts} +6 -14
  18. package/docs/README.md +21 -30
  19. package/docs/adaptor.sample.ts +182 -0
  20. package/docs/architecture.md +125 -0
  21. package/docs/db-setup.md +253 -0
  22. package/docs/figma-overlay.md +52 -0
  23. package/docs/grid-overlay.md +38 -0
  24. package/docs/installation.md +75 -40
  25. package/package.json +8 -3
  26. package/dist/chunk-U5K2YGGL.js.map +0 -1
  27. package/docs/adapter-handoff.md +0 -146
  28. package/docs/concept.md +0 -102
  29. package/docs/df-sheet-adapter.md +0 -336
  30. package/docs/df-sheet-next.md +0 -222
  31. package/docs/initial-plan.md +0 -226
  32. package/docs/package-split-checkpoint.md +0 -79
  33. package/docs/presence-handoff.md +0 -138
  34. package/docs/review-feedback-2026-06-20.md +0 -267
  35. package/docs/smoke-baseline-2026-06-20.md +0 -41
  36. package/docs/stabilize-ui-work-guide.md +0 -243
  37. package/docs/supabase-presence.md +0 -198
  38. package/docs/supabase-review-items.md +0 -365
  39. package/docs/supabase.md +0 -205
@@ -1,226 +0,0 @@
1
- # df-web-review-kit 초기 정리
2
-
3
- ## 결정된 방향
4
-
5
- - Repository: `Designfever/df-web-review-kit`
6
- - NPM package: `@designfever/web-review-kit`
7
- - 목적: QA 전용 도구가 아니라 웹 페이지 위에 얹는 검수용 toolkit.
8
- - 1차 대상: Lexus, KIA, Toyota 같은 웹 프로젝트.
9
- - 설치 방식: 각 프로젝트에 npm package로 설치하고, 프로젝트별 설정만 넣는다.
10
- - 단축키: 기본은 `Shift + Q`로 QA mode 진입.
11
- - Chrome extension은 1차 대상이 아니다. 소스 접근 없는 사이트까지 다뤄야 할 때 adapter/wrapper로 나중에 검토한다.
12
-
13
- ## 큰 구조
14
-
15
- 패키지는 core와 module, adapter로 나눈다.
16
-
17
- - Core
18
- - overlay root 생성
19
- - keyboard shortcut 관리
20
- - panel/modal/layer 공통 UI
21
- - 현재 URL/viewport/scroll 상태 수집
22
- - module lifecycle 관리
23
- - QA module
24
- - 현재 페이지 기준 QA list 표시
25
- - text-only QA 등록
26
- - 화면 영역 드래그 캡처 QA 등록
27
- - DOM anchor 계산
28
- - Grid module
29
- - 기존 grid helper를 나중에 module로 이관
30
- - 1차 MVP에는 인터페이스만 열어둔다
31
- - Figma module
32
- - Figma overlay helper를 나중에 module로 이관
33
- - 1차 MVP에는 인터페이스만 열어둔다
34
- - Adapter
35
- - 저장소/외부 서비스 연동만 담당
36
- - core와 QA module은 df-sheet, GitHub, localStorage 같은 저장소를 직접 알지 않는다
37
-
38
- ## Adapter로 뺀다는 뜻
39
-
40
- Core는 "무엇을 수집하고 어떤 UI를 보여줄지"만 담당한다.
41
- Adapter는 "수집한 QA item을 어디에 저장하고 어떻게 읽어올지"만 담당한다.
42
-
43
- 예시:
44
-
45
- - Lexus: `dfSheetAdapter`
46
- - 초기 개발/검증: `localAdapter`
47
- - 미래 확장: `githubIssueAdapter`, `jsonFileAdapter` 등
48
-
49
- 이렇게 분리하면 df-sheet 연동 방식이 바뀌어도 overlay core를 건드리지 않아도 된다.
50
-
51
- ## 1차 MVP
52
-
53
- 우선 df-sheet 연동 없이 local adapter로 기능을 검증한다.
54
-
55
- 1. npm package scaffold
56
- 2. target project에서 초기화 가능한 API
57
- 3. `Shift + Q`로 QA mode toggle
58
- 4. 현재 URL 기준 QA list 표시
59
- 5. text-only QA 생성
60
- 6. drag capture QA 생성
61
- 7. DOM anchor + relative coordinate 저장
62
- 8. local adapter 저장/조회/삭제
63
-
64
- 1차에서 Figma overlay와 grid helper를 완성하려고 하지 않는다.
65
- 다만 나중에 붙일 수 있게 module 구조는 처음부터 열어둔다.
66
-
67
- ## Local adapter 저장 방식
68
-
69
- 이름은 local adapter지만 실제 저장은 둘로 나눈다.
70
-
71
- - Metadata: `localStorage`
72
- - Screenshot/blob: `IndexedDB`
73
-
74
- 이유:
75
-
76
- - `localStorage`에 screenshot data URL을 계속 넣으면 용량이 빨리 찬다.
77
- - IndexedDB는 이미지 blob 저장에 더 적합하다.
78
-
79
- ## QA item 기본 데이터
80
-
81
- ```ts
82
- type ReviewItem = {
83
- id: string;
84
- projectId: string;
85
- pageUrl: string;
86
- normalizedPath: string;
87
- kind: "text" | "capture";
88
- title?: string;
89
- comment: string;
90
- status: "open" | "resolved";
91
- viewport: {
92
- width: number;
93
- height: number;
94
- };
95
- scroll?: {
96
- x: number;
97
- y: number;
98
- };
99
- anchor?: DomAnchor;
100
- selection?: RelativeSelection;
101
- screenshotAssetId?: string;
102
- externalIssueId?: string;
103
- createdAt: string;
104
- updatedAt: string;
105
- };
106
- ```
107
-
108
- ## DOM anchor 전략
109
-
110
- 단순 viewport 좌표만 저장하면 반응형 화면에서 위치가 틀어진다.
111
- 따라서 선택 영역과 가장 가까운 DOM element를 anchor로 잡고, 그 element 기준 상대 좌표를 저장한다.
112
-
113
- Anchor 우선순위:
114
-
115
- 1. `data-qa-id` 또는 package 설정으로 지정한 attribute
116
- 2. `id`가 안정적인 element
117
- 3. 의미 있는 class/name/role 기반 selector
118
- 4. 텍스트 fingerprint
119
- 5. DOM path fallback
120
-
121
- Relative coordinate 예시:
122
-
123
- ```ts
124
- type RelativeSelection = {
125
- x: number; // (selection.left - anchor.left) / anchor.width
126
- y: number; // (selection.top - anchor.top) / anchor.height
127
- width: number; // selection.width / anchor.width
128
- height: number; // selection.height / anchor.height
129
- };
130
- ```
131
-
132
- 나중에 다시 표시할 때는 현재 anchor rect를 찾고 위 비율로 위치를 복원한다.
133
-
134
- ## QA 생성 흐름
135
-
136
- Text-only:
137
-
138
- 1. 사용자가 QA mode에서 text 등록 선택
139
- 2. 현재 URL, viewport width/height 저장
140
- 3. 사용자가 comment 입력
141
- 4. adapter에 저장
142
-
143
- Capture:
144
-
145
- 1. 사용자가 capture 선택
146
- 2. 깨진 영역을 drag로 선택
147
- 3. 선택 영역 screenshot 생성
148
- 4. 선택 영역과 가장 가까운 DOM anchor 계산
149
- 5. anchor 기준 relative coordinate 저장
150
- 6. comment 입력
151
- 7. adapter에 저장
152
-
153
- Screenshot 구현은 처음에는 `html2canvas` 계열로 시작할 수 있다.
154
- 단, cross-origin image/video/canvas는 깨질 수 있으니 정확도가 중요해지면 browser extension 또는 native capture 방식도 검토한다.
155
-
156
- ## df-sheet 연동 방향
157
-
158
- df-sheet adapter는 QA item을 df-sheet issue로 매핑한다.
159
-
160
- - 생성: QA item 생성 시 df-sheet issue 생성
161
- - 조회: 현재 URL에 해당하는 unresolved issue만 QA list에 표시
162
- - 해결: df-sheet issue status가 `done`이면 QA list에서 제외
163
- - 첨부: capture screenshot은 issue attachment로 등록
164
- - Metadata: page URL, viewport, anchor, selection 정보를 issue에 저장
165
-
166
- df-sheet에 dedicated metadata field가 없으면 1차는 description이나 hidden JSON block으로 저장할 수 있다.
167
- 다만 장기적으로는 issue metadata field를 추가하는 편이 낫다.
168
-
169
- ## 예상 초기화 API
170
-
171
- ```ts
172
- import { createWebReviewKit, localAdapter } from "@designfever/web-review-kit";
173
-
174
- createWebReviewKit({
175
- projectId: "lexus-renewal",
176
- adapter: localAdapter({
177
- storageKey: "lexus-review-items",
178
- }),
179
- hotkeys: {
180
- qa: "Shift+Q",
181
- },
182
- anchors: {
183
- attribute: "data-qa-id",
184
- },
185
- modules: {
186
- qa: true,
187
- grid: false,
188
- figma: false,
189
- },
190
- });
191
- ```
192
-
193
- ## 나중에 붙일 모듈
194
-
195
- Grid helper:
196
-
197
- - breakpoint/grid overlay
198
- - column/gutter 표시
199
- - 프로젝트별 grid preset
200
-
201
- Figma helper:
202
-
203
- - Figma screenshot 또는 frame overlay
204
- - opacity/blend mode 조절
205
- - viewport별 기준 이미지 전환
206
-
207
- 둘 다 core overlay와 단축키/패널 시스템은 공유하되, QA 저장소와는 강하게 묶지 않는다.
208
-
209
- ## 열어둔 질문
210
-
211
- - package 배포를 public npm으로 할지 private npm으로 할지
212
- - target project가 Next/Vite/vanilla를 모두 포함하는지
213
- - React dependency를 peer로 둘지, framework-agnostic DOM package로 갈지
214
- - df-sheet 인증은 cookie 기반인지 token 기반인지
215
- - df-sheet에 QA metadata 전용 field를 추가할지
216
- - target site에 `data-qa-id`를 어느 정도 심을지
217
- - screenshot 정확도를 어디까지 요구할지
218
-
219
- ## 다음 세션에서 할 일
220
-
221
- 1. package scaffold 선택: Vite library, tsup, or rollup
222
- 2. core API 타입 먼저 작성
223
- 3. local adapter 인터페이스 작성
224
- 4. QA overlay 최소 UI 구현
225
- 5. Lexus/KIA/Toyota 중 하나에 playground로 붙여 검증
226
-
@@ -1,79 +0,0 @@
1
- # Package split checkpoint
2
-
3
- Date: 2026-06-20
4
- Branch: `uforgot/feat/review-kit-stabilize-ui`
5
-
6
- ## Goal
7
-
8
- Prepare `packages/df-web-review-kit` to behave like an independent package before moving Figma overlay or editing proposal work into it.
9
-
10
- This checkpoint is not the actual repo split and does not publish the package.
11
-
12
- ## Public entrypoints
13
-
14
- Only these imports are public:
15
-
16
- ```ts
17
- import { createWebReviewKit, localAdapter } from '@designfever/web-review-kit';
18
- import { mountReviewShell } from '@designfever/web-review-kit/react-shell';
19
- ```
20
-
21
- The package export map exposes:
22
-
23
- - `.` → core API, adapters, shared types.
24
- - `./react-shell` → review shell UI, presence adapters, page glob helper.
25
- - `./package.json` → package metadata for tooling.
26
-
27
- `src/*` paths are not public API.
28
-
29
- ## Publish/file policy
30
-
31
- `package.json#files` intentionally includes only:
32
-
33
- - `dist`
34
- - `docs`
35
- - `README.md`
36
-
37
- `src` is kept in the repo for development but excluded from the package file list so consumers rely on the exported API and generated declarations.
38
-
39
- ## Dependency policy
40
-
41
- - `react` and `react-dom` stay peer dependencies.
42
- - `lucide-react` stays bundled into the built `react-shell` output for now. It is a dev/build dependency, not a host peer dependency.
43
- - Figma token/API/fetch logic must not enter package core in this branch.
44
-
45
- ## Host consumption policy
46
-
47
- The Lexus host imports the package through the same public package entrypoints it would use after a repo split:
48
-
49
- - `@designfever/web-review-kit`
50
- - `@designfever/web-review-kit/react-shell`
51
-
52
- Vite no longer aliases those package imports to `packages/df-web-review-kit/src` in dev. The host resolves the installed file dependency under `node_modules`, so `pnpm review-kit:build` must be run after package source changes to refresh `dist` and sync the installed package.
53
-
54
- ## Review/test page policy
55
-
56
- The Lexus `/review` page remains the integration smoke page. It is a consumer of the package, not part of the package surface.
57
-
58
- If this package is moved to a separate repo later, keep a small playground/example app outside the package publish files so review-shell behavior can still be manually verified.
59
-
60
- ## Verification gate
61
-
62
- Before moving this item to review, run:
63
-
64
- ```bash
65
- pnpm review-kit:typecheck
66
- pnpm review-kit:build
67
- pnpm exec tsc --noEmit
68
- pnpm exec vite build --mode seo
69
- pnpm --dir packages/df-web-review-kit pack --pack-destination /tmp/df-web-review-kit-pack
70
- ```
71
-
72
- Manual smoke:
73
-
74
- - Open `/review`.
75
- - Confirm empty QA state.
76
- - Inject or create a local QA item.
77
- - Confirm card and prompt modal still render.
78
- - Confirm settings and sitemap modals still open.
79
- - Confirm browser console has no errors/warnings.
@@ -1,138 +0,0 @@
1
- # review-kit presence handoff
2
-
3
- ## 목적
4
-
5
- QA 작업 중 "누가 어떤 review page를 보고 있는지"를 보여주기 위한 presence 기능 handoff 문서다.
6
-
7
- Presence는 저장소가 아니다. `local`, `df-sheet`, `supabase` 같은 review item adapter는 영속 데이터 CRUD를 맡고, presence adapter는 현재 접속 세션 상태만 공유한다.
8
-
9
- ## 현재 구현
10
-
11
- 추가된 파일:
12
-
13
- - `src/react-shell/presence.ts`
14
- - `src/react-shell/types.ts`
15
- - `src/react-shell.tsx`
16
- - Lexus mount: `page/review/index.tsx`
17
-
18
- 현재 `page/review/index.tsx`는 local 개발용 presence adapter를 붙인다.
19
-
20
- ```ts
21
- import { createLocalPresenceAdapter } from '@designfever/web-review-kit/react-shell';
22
-
23
- const REVIEW_PRESENCE_ADAPTER = createLocalPresenceAdapter({
24
- channelName: `${REVIEW_PROJECT_ID}:review-presence`,
25
- });
26
-
27
- mountReviewShell({
28
- projectId: REVIEW_PROJECT_ID,
29
- pages,
30
- adapters: REVIEW_ADAPTERS,
31
- presence: REVIEW_PRESENCE_ADAPTER,
32
- });
33
- ```
34
-
35
- 현재 Lexus pilot은 `VITE_REVIEW_SUPABASE_ANON_KEY`가 있으면 Supabase Presence를 쓰고, 없으면 `createLocalPresenceAdapter()`로 fallback한다.
36
-
37
- `createLocalPresenceAdapter()`는 `BroadcastChannel` 기반이다. 같은 origin의 여러 review 탭에서만 동작한다. 실제 팀 공유용 구현은 Supabase adapter를 사용한다.
38
-
39
- ```env
40
- VITE_REVIEW_SUPABASE_URL=https://vhqnvfkamnpgyqclohso.supabase.co
41
- VITE_REVIEW_SUPABASE_ANON_KEY=
42
- VITE_REVIEW_SUPABASE_PRESENCE_PRIVATE=false
43
- ```
44
-
45
- ## Public contract
46
-
47
- ```ts
48
- type ReviewPresenceAdapter = {
49
- label: string;
50
- connect: (
51
- context: ReviewPresenceContext
52
- ) => Promise<ReviewPresenceSession> | ReviewPresenceSession;
53
- };
54
-
55
- type ReviewPresenceSession = {
56
- update: (state: Partial<ReviewPresenceState>) => void | Promise<void>;
57
- subscribe: (
58
- callback: (users: ReviewPresenceUser[]) => void
59
- ) => () => void;
60
- disconnect: () => void | Promise<void>;
61
- };
62
- ```
63
-
64
- `ReviewPresenceState`는 현재 이 shape을 사용한다.
65
-
66
- ```ts
67
- type ReviewPresenceState = {
68
- projectId: string;
69
- sessionId: string;
70
- userId: string;
71
- displayName: string;
72
- color: string;
73
- routeKey: string;
74
- target: string;
75
- source: 'local' | 'df-sheet';
76
- viewport: {
77
- label: string;
78
- width: number;
79
- height: number;
80
- kind: 'mobile' | 'tablet' | 'desktop' | 'wide';
81
- };
82
- mode: 'idle' | 'note' | 'element' | 'area';
83
- selectedItemId?: string | null;
84
- selectedReviewNumber?: number | null;
85
- status: 'idle' | 'reviewing' | 'editing';
86
- updatedAt: string;
87
- };
88
- ```
89
-
90
- ## Shell behavior
91
-
92
- - Settings의 `User ID`가 비어 있으면 presence는 연결하지 않는다.
93
- - `User ID`가 있으면 우측 QA list header 아래에 `online N`과 user chip을 보여준다.
94
- - user chip에는 `displayName`, `target`, `viewport.label`을 표시한다.
95
- - 현재 탭은 `is-self` class로 표시된다.
96
- - 업데이트는 느린 상태만 보낸다:
97
- - route/target
98
- - source
99
- - viewport
100
- - review mode
101
- - selected item/review number
102
- - status
103
- - scroll, mousemove, cursor 위치는 보내지 않는다.
104
-
105
- ## 왜 storage adapter와 분리했나
106
-
107
- storage adapter:
108
-
109
- - `get`
110
- - `list`
111
- - `create`
112
- - `updateStatus`
113
- - `remove`
114
- - remote promote/move
115
-
116
- presence adapter:
117
-
118
- - `connect`
119
- - `update`
120
- - `subscribe`
121
- - `disconnect`
122
-
123
- 수명주기가 다르다. storage는 item의 영속 상태이고, presence는 websocket session 상태다. 같은 adapter array에 넣으면 API가 어색해지고, remote item source를 바꾸는 일과 협업 session을 바꾸는 일이 섞인다.
124
-
125
- ## 다음 작업
126
-
127
- 1. `supabasePresenceAdapter` 추가
128
- 2. `@supabase/supabase-js`를 peer/dev dependency로 둘지 별도 package adapter로 분리할지 결정
129
- 3. Supabase project/env 연결
130
- 4. private channel + Realtime Authorization 적용
131
- 5. 두 브라우저 또는 두 기기에서 같은 project presence 확인
132
-
133
- ## 주의점
134
-
135
- - Presence payload는 작게 유지한다.
136
- - high-frequency state는 Presence에 넣지 않는다.
137
- - 현재 `ReviewSource`가 `'local' | 'df-sheet'`로 고정되어 있어서 source generalization 작업과 Supabase storage adapter 작업이 만나면 타입을 먼저 풀어야 한다.
138
- - `User ID`는 현재 localStorage 기반이다. 실제 remote presence에서는 auth user id나 project member id로 바꾸는 편이 맞다.
@@ -1,267 +0,0 @@
1
- # Review feedback 2026-06-20
2
-
3
- `packages/df-web-review-kit`를 독립 package로 분리하기 전에 확인할 리뷰 메모다.
4
-
5
- 검토자:
6
-
7
- - 빵빵: package source와 host wiring typecheck 확인
8
- - 팡팡: 기능 버그와 adapter edge case 리뷰
9
- - 오빵: package 경계, 구조, concept 방향 리뷰
10
-
11
- ## 결론
12
-
13
- 컨셉은 유지할 만하다. 차별점은 browser extension이나 외부 SaaS가 아니라 host project 안에 `/review` shell을 심어서 route, viewport, DOM anchor, style context를 그대로 쓰는 점이다.
14
-
15
- 다만 0.1 안정성은 `anchor restore` 신뢰도에 걸려 있다. DOM anchor가 흔들리면 이 package는 좌표 메모장 수준으로 내려간다.
16
-
17
- ## High priority
18
-
19
- ### 1. DOM anchor restore 조건 수정
20
-
21
- 파일:
22
-
23
- ```txt
24
- src/react-shell/anchor-restore.ts
25
- ```
26
-
27
- 현재 문제:
28
-
29
- ```ts
30
- if (!anchor || item.scope !== 'dom') return undefined;
31
- ```
32
-
33
- element mode로 만든 item은 `anchor`와 `selection`을 가지지만, `scope`는 `mobile`, `tablet`, `desktop`, `wide` 같은 viewport preset으로 저장된다. 그래서 restore 시 anchor 기반 scroll 복원이 early return으로 막힐 수 있다.
34
-
35
- 판정 기준은 shell의 `isDomReviewItem`과 맞춰야 한다.
36
-
37
- 권장 방향:
38
-
39
- ```ts
40
- const isAnchorRestorable =
41
- item.scope === 'dom' ||
42
- (item.kind === 'note' && Boolean(item.anchor && item.selection));
43
-
44
- if (!anchor || !isAnchorRestorable) return undefined;
45
- ```
46
-
47
- 검증:
48
-
49
- - element mode로 QA 생성
50
- - scroll 이동 후 item 클릭
51
- - 원래 DOM element 기준으로 scroll restore 되는지 확인
52
- - viewport를 바꿔도 selector candidate가 같은 element를 찾는지 확인
53
-
54
- ### 2. Supabase review number fallback 정책 정리
55
-
56
- 파일:
57
-
58
- ```txt
59
- src/adapters/supabase.ts
60
- ```
61
-
62
- 현재 fallback:
63
-
64
- ```ts
65
- const reviewNumber = await getNextReviewNumber(...);
66
- await fromTable().insert(row).select('*').single();
67
- ```
68
-
69
- `SELECT max(review_number)`와 `INSERT` 사이에 gap이 있어서 동시 생성 시 unique conflict가 날 수 있다. retry는 있지만 fallback 이름이 `unsafeClientReviewNumberFallback`이라 운영 경로가 아니란 뜻은 전달된다.
70
-
71
- 권장 방향:
72
-
73
- - default는 지금처럼 RPC 유지
74
- - client fallback은 문서상 dev/test only로 못 박기
75
- - 이름은 유지해도 되지만, "optimistic retry fallback" 성격을 docs에 명시
76
- - package 외부 공개 시 fallback option을 숨기거나 experimental로 내리기
77
-
78
- 검증:
79
-
80
- - concurrent create 2개 이상이 서로 다른 remote `reviewNumber`를 받는지 확인
81
- - 삭제 후 다음 번호가 재사용되지 않는지 확인
82
-
83
- ### 3. Shell adapter update 분기 단순화
84
-
85
- 파일:
86
-
87
- ```txt
88
- src/react-shell/adapters.ts
89
- ```
90
-
91
- 현재 문제:
92
-
93
- - `patch.status`가 있고 `updateStatus`가 있으면 status 전용 path를 탄다.
94
- - `updateStatus`가 없으면 `syncSubmission`으로 generic patch를 보내는 구조다.
95
- - status patch와 submission patch가 같은 `update` method 안에서 섞여 있어, adapter capability가 늘어날수록 디버깅이 어려워진다.
96
-
97
- 권장 방향:
98
-
99
- - shell UI에서 status 변경은 `activeAdapterEntry.updateStatus`만 직접 호출한다.
100
- - `adapter.update` wrapper는 submission sync 같은 generic patch만 담당한다.
101
- - remote adapter가 generic update를 지원해야 하면 `syncSubmission`보다 `updateItem` 같은 명시적 이름을 검토한다.
102
-
103
- 검증:
104
-
105
- - local status dropdown 변경
106
- - remote status dropdown 변경
107
- - local item remote 등록 실패 후 `submitStatus: failed` 저장
108
-
109
- ### 4. Submit success 후 UI refresh 흐름 확인
110
-
111
- 파일:
112
-
113
- ```txt
114
- src/react-shell.tsx
115
- ```
116
-
117
- 현재 flow:
118
-
119
- ```ts
120
- await remoteAdapterEntry.adapter.create(...);
121
- await localAdapterEntry.adapter.remove(item.id);
122
- await refreshReviewData();
123
- ```
124
-
125
- 성공 후 local draft를 바로 삭제하므로, remote list 반영 타이밍이 늦으면 UI에서 item이 잠깐 사라질 수 있다.
126
-
127
- 권장 방향:
128
-
129
- - `remote.create`의 반환 item을 받아서 remote source로 즉시 이동하거나
130
- - 성공 직후 selected item을 clear하면서 "remote 등록됨" state를 명확히 보여주거나
131
- - refresh 순서를 local remove와 분리해 깜빡임을 줄인다.
132
-
133
- 검증:
134
-
135
- - local item 생성
136
- - remote 등록 클릭
137
- - 등록 직후 list/sitemap count가 어색하게 튀지 않는지 확인
138
- - network delay가 있을 때도 local item이 실패/성공 상태를 분명히 보여주는지 확인
139
-
140
- ## Medium priority
141
-
142
- ### 5. `getItemSelection` normalize 책임 정리
143
-
144
- 파일:
145
-
146
- ```txt
147
- src/core/web-review-kit-app.ts
148
- ```
149
-
150
- 현재는 legacy `RelativeSelection`과 현재 `ReviewSelection`을 core에서 즉석 판정한다. local adapter에도 migration/normalize가 있어 책임이 분산돼 있다.
151
-
152
- 권장 방향:
153
-
154
- - `normalizeReviewSelection(value)` helper를 core 또는 shared module로 분리
155
- - adapter migration과 render/restore path가 같은 helper를 쓰게 정리
156
-
157
- ### 6. Local presence fallback 문서화
158
-
159
- 파일:
160
-
161
- ```txt
162
- src/react-shell/presence.ts
163
- ```
164
-
165
- `BroadcastChannel`이 없으면 local presence는 현재 tab 내부 상태만 가진다. 이건 큰 버그는 아니지만, fallback 동작으로 문서화해야 한다.
166
-
167
- 권장 문구:
168
-
169
- - `BroadcastChannel` available: same-origin tab/window 간 공유
170
- - unavailable: current tab only
171
- - Supabase presence 실패 시 local fallback으로 degrade
172
-
173
- ## Package split
174
-
175
- 현재 package 경계는 나쁘지 않다.
176
-
177
- - host hardcoding은 거의 없다.
178
- - `core/adapters`는 React에 의존하지 않는다.
179
- - Supabase adapter는 `@supabase/supabase-js`를 직접 import하지 않고 client interface를 주입받는다.
180
- - host project wiring은 `/review` route에서만 담당한다.
181
-
182
- 분리 전 체크:
183
-
184
- - `private: true` 제거 여부 결정
185
- - package name/scope 확정
186
- - `files`에 `src`, `dist`, `docs`를 모두 넣을지 결정
187
- - `react`, `react-dom`은 peerDependency 유지
188
- - `lucide-react`는 peer로 둘지 bundle할지 결정
189
- - export map에서 `.`와 `./react-shell` 유지
190
- - root project에서 `pnpm install --frozen-lockfile --ignore-scripts` 후 typecheck 통과 확인
191
-
192
- 확인된 사항:
193
-
194
- ```txt
195
- pnpm --dir packages/df-web-review-kit typecheck
196
- pnpm exec tsc --noEmit
197
- ```
198
-
199
- 둘 다 통과한다. 단, pull 직후 stale `node_modules` 상태에서는 새 export를 못 볼 수 있으므로 install이 필요했다.
200
-
201
- ## Structure debt
202
-
203
- 독립 package로 공개하기 전에 가장 큰 구조 문제는 파일 크기다.
204
-
205
- - `src/react-shell.tsx`: shell UI가 한 파일에 몰려 있다.
206
- - `src/core/web-review-kit-app.ts`: overlay core가 한 파일에 크다.
207
- - `src/react-shell/style.ts`: CSS string이 크다.
208
- - `src/core/overlay-style.ts`: overlay CSS string이 크다.
209
-
210
- 우선 분리 후보:
211
-
212
- - `react-shell/Topbar`
213
- - `react-shell/SitemapModal`
214
- - `react-shell/SettingsModal`
215
- - `react-shell/PromptModal`
216
- - `react-shell/ItemList`
217
- - `react-shell/ViewportStage`
218
- - `react-shell/RulerOverlay`
219
-
220
- 목표는 추상화가 아니라 리뷰 가능한 단위로 쪼개는 것이다.
221
-
222
- ## Product direction
223
-
224
- ### 0.1 focus
225
-
226
- 0.1은 "QA issue capture and restore"에 집중한다.
227
-
228
- 필수:
229
-
230
- - local draft 생성
231
- - remote source 등록
232
- - canonical remote number
233
- - page/viewport/source deep link
234
- - DOM anchor restore
235
- - prompt copy
236
-
237
- 보류 또는 optional:
238
-
239
- - screenshot upload
240
- - full collaboration presence
241
- - source code edit automation
242
-
243
- Presence는 있으면 좋지만, 0.1의 핵심 가치는 아니다. anchor restore 안정화가 우선이다.
244
-
245
- ### Visual editing idea
246
-
247
- 마진, 폰트, spacing 같은 값을 review shell에서 임시 조정하고 저장하는 방향은 가능하다. 다만 이건 두 층으로 나눠야 한다.
248
-
249
- 1층: override and proposal
250
-
251
- - 브라우저에서 element style을 임시 조정한다.
252
- - 저장하면 review item에 before/after value가 구조화 데이터로 붙는다.
253
- - AI 없이도 review-kit scope 안에서 자연스럽다.
254
-
255
- 2층: source patch suggestion
256
-
257
- - 실제 source 수정은 별도 AI/codegen adapter가 맡는다.
258
- - review-kit은 `anchor`, `DOM path`, `source hint`, `before/after`를 넘긴다.
259
- - AI는 "어느 컴포넌트의 어느 token/style을 바꿀지" diff나 PR 제안을 만든다.
260
- - 사람이 확인하고 merge한다.
261
-
262
- 중요한 경계:
263
-
264
- - review-kit core는 "제안 생산"까지 담당한다.
265
- - 실제 source 반영은 package core 밖 adapter 또는 별도 package로 둔다.
266
-
267
- 이 경계를 지키면 review-kit이 issue editor나 visual builder로 비대해지는 걸 막을 수 있다.