@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
@@ -3,7 +3,7 @@ type ReviewItemScope = 'mobile' | 'tablet' | 'desktop' | 'wide' | 'dom';
3
3
  type ReviewWorkflowStatus = 'todo' | 'doing' | 'review' | 'hold' | 'done';
4
4
  type ReviewItemStatus = 'open' | 'resolved' | ReviewWorkflowStatus;
5
5
  type ReviewMode = 'idle' | 'note' | 'element' | 'area';
6
- type ReviewSource = 'local' | 'df-sheet' | 'supabase' | (string & {});
6
+ type ReviewSource = 'local' | 'supabase' | (string & {});
7
7
  type ReviewSubmitStatus = 'idle' | 'submitting' | 'submitted' | 'failed';
8
8
  type ReviewViewportScope = Exclude<ReviewItemScope, 'dom'>;
9
9
  type DomAnchorStrategy = 'configured-attribute' | 'id' | 'class' | 'dom-path';
@@ -62,6 +62,7 @@ interface ReviewItem {
62
62
  kind: ReviewItemKind;
63
63
  title?: string;
64
64
  comment: string;
65
+ createdBy?: string;
65
66
  status: ReviewItemStatus;
66
67
  viewport: ViewportSize;
67
68
  devicePixelRatio?: number;
@@ -98,17 +99,6 @@ interface WebReviewKitAdapter {
98
99
  interface LocalAdapterOptions {
99
100
  storageKey?: string;
100
101
  }
101
- interface DfSheetAdapterOptions {
102
- baseUrl?: string;
103
- projectId: string;
104
- pageId: string;
105
- reviewProjectId?: string;
106
- reviewPathPrefix?: string;
107
- source?: string;
108
- issueType?: string;
109
- priority?: string;
110
- token?: string;
111
- }
112
102
  interface SupabaseReviewClient {
113
103
  from(table: string): any;
114
104
  rpc?: (fn: string, args?: Record<string, unknown>) => any;
@@ -132,11 +122,12 @@ interface NumberedReviewItem {
132
122
  item: ReviewItem;
133
123
  scope: ReviewItemScope;
134
124
  label: string;
135
- number: number;
125
+ number?: number;
136
126
  displayLabel: string;
137
127
  }
138
128
  interface WebReviewKitOptions {
139
129
  projectId: string;
130
+ userId?: string;
140
131
  adapter?: WebReviewKitAdapter;
141
132
  target?: WebReviewKitTarget | (() => WebReviewKitTarget | undefined);
142
133
  viewports?: {
@@ -150,6 +141,7 @@ interface WebReviewKitOptions {
150
141
  attribute?: string;
151
142
  };
152
143
  onRestoreItem?: (item: ReviewItem) => void | Promise<void>;
144
+ onCreateItem?: (item: ReviewItem) => void | Promise<void>;
153
145
  onItemsChange?: (items: ReviewItem[]) => void;
154
146
  onModeChange?: (mode: ReviewMode) => void;
155
147
  ui?: {
@@ -180,4 +172,4 @@ interface WebReviewKitTarget {
180
172
  getOverlayRect?: () => Pick<DOMRect, 'left' | 'top' | 'width' | 'height'>;
181
173
  }
182
174
 
183
- export type { DfSheetAdapterOptions as D, LocalAdapterOptions as L, NumberedReviewItem as N, ReviewWorkflowStatus as R, SupabaseReviewAdapterOptions as S, ViewportSize as V, WebReviewKitAdapter as W, ReviewItemStatus as a, WebReviewKitOptions as b, WebReviewKitController as c, ReviewViewportPreset as d, ReviewItem as e, ReviewItemScope as f, DomAnchor as g, DomAnchorCandidate as h, DomAnchorStrategy as i, DomSourceHint as j, RelativeSelection as k, ReviewItemKind as l, ReviewItemQuery as m, ReviewMarker as n, ReviewMode as o, ReviewPoint as p, ReviewRulerConfig as q, ReviewSelection as r, ReviewSource as s, ReviewSubmitStatus as t, ReviewViewportScope as u, SupabaseReviewClient as v, WebReviewKitTarget as w };
175
+ export type { DomAnchor as D, LocalAdapterOptions as L, NumberedReviewItem as N, ReviewWorkflowStatus as R, SupabaseReviewAdapterOptions as S, ViewportSize as V, WebReviewKitAdapter as W, ReviewItemStatus as a, WebReviewKitOptions as b, WebReviewKitController as c, ReviewViewportPreset as d, ReviewItem as e, ReviewItemScope as f, DomAnchorCandidate as g, DomAnchorStrategy as h, DomSourceHint as i, RelativeSelection as j, ReviewItemKind as k, ReviewItemQuery as l, ReviewMarker as m, ReviewMode as n, ReviewPoint as o, ReviewRulerConfig as p, ReviewSelection as q, ReviewSource as r, ReviewSubmitStatus as s, ReviewViewportScope as t, SupabaseReviewClient as u, WebReviewKitTarget as v };
@@ -3,7 +3,7 @@ type ReviewItemScope = 'mobile' | 'tablet' | 'desktop' | 'wide' | 'dom';
3
3
  type ReviewWorkflowStatus = 'todo' | 'doing' | 'review' | 'hold' | 'done';
4
4
  type ReviewItemStatus = 'open' | 'resolved' | ReviewWorkflowStatus;
5
5
  type ReviewMode = 'idle' | 'note' | 'element' | 'area';
6
- type ReviewSource = 'local' | 'df-sheet' | 'supabase' | (string & {});
6
+ type ReviewSource = 'local' | 'supabase' | (string & {});
7
7
  type ReviewSubmitStatus = 'idle' | 'submitting' | 'submitted' | 'failed';
8
8
  type ReviewViewportScope = Exclude<ReviewItemScope, 'dom'>;
9
9
  type DomAnchorStrategy = 'configured-attribute' | 'id' | 'class' | 'dom-path';
@@ -62,6 +62,7 @@ interface ReviewItem {
62
62
  kind: ReviewItemKind;
63
63
  title?: string;
64
64
  comment: string;
65
+ createdBy?: string;
65
66
  status: ReviewItemStatus;
66
67
  viewport: ViewportSize;
67
68
  devicePixelRatio?: number;
@@ -98,17 +99,6 @@ interface WebReviewKitAdapter {
98
99
  interface LocalAdapterOptions {
99
100
  storageKey?: string;
100
101
  }
101
- interface DfSheetAdapterOptions {
102
- baseUrl?: string;
103
- projectId: string;
104
- pageId: string;
105
- reviewProjectId?: string;
106
- reviewPathPrefix?: string;
107
- source?: string;
108
- issueType?: string;
109
- priority?: string;
110
- token?: string;
111
- }
112
102
  interface SupabaseReviewClient {
113
103
  from(table: string): any;
114
104
  rpc?: (fn: string, args?: Record<string, unknown>) => any;
@@ -132,11 +122,12 @@ interface NumberedReviewItem {
132
122
  item: ReviewItem;
133
123
  scope: ReviewItemScope;
134
124
  label: string;
135
- number: number;
125
+ number?: number;
136
126
  displayLabel: string;
137
127
  }
138
128
  interface WebReviewKitOptions {
139
129
  projectId: string;
130
+ userId?: string;
140
131
  adapter?: WebReviewKitAdapter;
141
132
  target?: WebReviewKitTarget | (() => WebReviewKitTarget | undefined);
142
133
  viewports?: {
@@ -150,6 +141,7 @@ interface WebReviewKitOptions {
150
141
  attribute?: string;
151
142
  };
152
143
  onRestoreItem?: (item: ReviewItem) => void | Promise<void>;
144
+ onCreateItem?: (item: ReviewItem) => void | Promise<void>;
153
145
  onItemsChange?: (items: ReviewItem[]) => void;
154
146
  onModeChange?: (mode: ReviewMode) => void;
155
147
  ui?: {
@@ -180,4 +172,4 @@ interface WebReviewKitTarget {
180
172
  getOverlayRect?: () => Pick<DOMRect, 'left' | 'top' | 'width' | 'height'>;
181
173
  }
182
174
 
183
- export type { DfSheetAdapterOptions as D, LocalAdapterOptions as L, NumberedReviewItem as N, ReviewWorkflowStatus as R, SupabaseReviewAdapterOptions as S, ViewportSize as V, WebReviewKitAdapter as W, ReviewItemStatus as a, WebReviewKitOptions as b, WebReviewKitController as c, ReviewViewportPreset as d, ReviewItem as e, ReviewItemScope as f, DomAnchor as g, DomAnchorCandidate as h, DomAnchorStrategy as i, DomSourceHint as j, RelativeSelection as k, ReviewItemKind as l, ReviewItemQuery as m, ReviewMarker as n, ReviewMode as o, ReviewPoint as p, ReviewRulerConfig as q, ReviewSelection as r, ReviewSource as s, ReviewSubmitStatus as t, ReviewViewportScope as u, SupabaseReviewClient as v, WebReviewKitTarget as w };
175
+ export type { DomAnchor as D, LocalAdapterOptions as L, NumberedReviewItem as N, ReviewWorkflowStatus as R, SupabaseReviewAdapterOptions as S, ViewportSize as V, WebReviewKitAdapter as W, ReviewItemStatus as a, WebReviewKitOptions as b, WebReviewKitController as c, ReviewViewportPreset as d, ReviewItem as e, ReviewItemScope as f, DomAnchorCandidate as g, DomAnchorStrategy as h, DomSourceHint as i, RelativeSelection as j, ReviewItemKind as k, ReviewItemQuery as l, ReviewMarker as m, ReviewMode as n, ReviewPoint as o, ReviewRulerConfig as p, ReviewSelection as q, ReviewSource as r, ReviewSubmitStatus as s, ReviewViewportScope as t, SupabaseReviewClient as u, WebReviewKitTarget as v };
package/docs/README.md CHANGED
@@ -1,37 +1,28 @@
1
1
  # df-web-review-kit docs
2
2
 
3
- 현재 문서의 읽는 순서:
3
+ Public docs are intentionally small. Keep implementation history, handoff notes, and internal operator decisions out of this package documentation.
4
4
 
5
- 1. [Concept](concept.md)
6
- 2. [Installation](installation.md)
7
- 3. [Supabase setup](supabase.md)
8
- 4. [Supabase review item SQL](supabase-review-items.md)
9
- 5. [Supabase presence](supabase-presence.md)
10
- 6. [Adapter handoff](adapter-handoff.md)
11
- 7. [df-sheet next](df-sheet-next.md)
12
- 8. [Review feedback 2026-06-20](review-feedback-2026-06-20.md)
13
- 9. [Stabilize UI work guide](stabilize-ui-work-guide.md)
14
- 10. [Smoke baseline 2026-06-20](smoke-baseline-2026-06-20.md)
15
- 11. [Package split checkpoint](package-split-checkpoint.md)
5
+ ## Read This Order
16
6
 
17
- ## 문서 역할
7
+ 1. [Installation](installation.md)
8
+ 2. [Custom adapter sample](adaptor.sample.ts)
9
+ 3. [DB setup](db-setup.md)
10
+ 4. [Architecture and runtime logic](architecture.md)
11
+ 5. [Figma overlay](figma-overlay.md)
12
+ 6. [Grid overlay](grid-overlay.md)
18
13
 
19
- - `concept.md`: 왜 이 package가 있고 어떤 문제를 해결하는지.
20
- - `installation.md`: host project에 설치하고 `/review` route에 붙이는 방법.
21
- - `supabase.md`: Supabase를 remote QA 저장소와 presence backend로 연결하는 방법.
22
- - `supabase-review-items.md`: 실제 table/RPC/RLS SQL.
23
- - `supabase-presence.md`: Supabase Realtime Presence adapter 구조.
24
- - `adapter-handoff.md`: package repo로 옮길 때 필요한 adapter contract 정리.
25
- - `df-sheet-next.md`: df-sheet를 source of record로 쓸 때 필요한 제품/API 방향.
26
- - `review-feedback-2026-06-20.md`: 빵빵/팡팡/오빵 package 리뷰 메모와 우선순위.
27
- - `stabilize-ui-work-guide.md`: anchor 안정화, UI token화, package split 전 작업 순서.
28
- - `smoke-baseline-2026-06-20.md`: 761 현재 기능 smoke 기준선 결과.
29
- - `package-split-checkpoint.md`: 765 package export/file/peer dependency와 host 소비 경계.
30
- - `initial-plan.md`: 초기 계획 기록. 현재 기준 문서가 아니다.
14
+ ## Document Roles
31
15
 
32
- ## 현재 기준
16
+ - `installation.md`: install the npm package, create the `/review` route, wire adapters, and run checks.
17
+ - `adaptor.sample.ts`: copyable starting point for host-owned remote adapters.
18
+ - `db-setup.md`: optional Supabase review item table/RPC/RLS/presence setup.
19
+ - `architecture.md`: core/runtime, React shell, coordinate, anchor, and feature boundary notes.
20
+ - `figma-overlay.md`: host requirements for the Figma overlay toggle.
21
+ - `grid-overlay.md`: host requirements for the grid/helper overlay toggle.
33
22
 
34
- - `local`: 개인 draft 저장소.
35
- - `supabase`: 팀 공유 remote 저장소.
36
- - `df-sheet`: 나중에 issue workflow로 연결할 수 있는 remote destination.
37
- - local 번호는 개인 draft용이고, remote 번호는 remote source가 새로 발급하는 canonical 번호다.
23
+ ## Boundary
24
+
25
+ - `local` is the default draft storage.
26
+ - `supabase` is an optional adapter sample for users who configure their own backend.
27
+ - `presence` is temporary session state, not QA item persistence.
28
+ - `kuku` and operator keys belong to OpenClaw or a backend/admin service, not this public package.
@@ -0,0 +1,182 @@
1
+ import {
2
+ REVIEW_WORKFLOW_STATUS_OPTIONS,
3
+ type ReviewItem,
4
+ type ReviewItemQuery,
5
+ type ReviewItemStatus,
6
+ type ReviewSource,
7
+ type WebReviewKitAdapter,
8
+ } from '@designfever/web-review-kit';
9
+ import type {
10
+ ReviewShellAdapter,
11
+ } from '@designfever/web-review-kit/react-shell';
12
+
13
+ type RemoteReviewAdapterOptions = {
14
+ baseUrl: string;
15
+ projectId: string;
16
+ source?: ReviewSource;
17
+ token?: string;
18
+ };
19
+
20
+ type RemoteReviewItemResponse = ReviewItem | { item: ReviewItem };
21
+
22
+ /*
23
+ * WebReviewKitAdapter is the core storage contract.
24
+ *
25
+ * ReviewItem is the full QA payload. Persist it as structured JSON so marker,
26
+ * anchor, selection, viewport, scroll, status, and external issue fields survive.
27
+ *
28
+ * ReviewItemQuery is used by the shell for current-page lists and sitemap counts.
29
+ * A remote backend should support at least projectId, routeKey, status, source,
30
+ * and pageId when the host project needs page-level grouping.
31
+ *
32
+ * Keep private tokens, admin keys, numbering, and permission checks in your
33
+ * backend. Browser code should only call browser-safe endpoints.
34
+ */
35
+ export function createRemoteReviewAdapter(
36
+ options: RemoteReviewAdapterOptions
37
+ ): WebReviewKitAdapter {
38
+ const source = options.source ?? 'remote';
39
+
40
+ return {
41
+ async get(id) {
42
+ return readReviewItem(
43
+ await requestJson<RemoteReviewItemResponse>(
44
+ `/review-items/${encodeURIComponent(id)}`,
45
+ options
46
+ )
47
+ );
48
+ },
49
+
50
+ async list(query) {
51
+ const rows = await requestJson<RemoteReviewItemResponse[]>(
52
+ `/review-items?${createListParams(query, source).toString()}`,
53
+ options
54
+ );
55
+
56
+ return rows.map(readReviewItem);
57
+ },
58
+
59
+ async create(item) {
60
+ return readReviewItem(
61
+ await requestJson<RemoteReviewItemResponse>('/review-items', options, {
62
+ method: 'POST',
63
+ body: JSON.stringify({
64
+ project_id: options.projectId,
65
+ source,
66
+ item,
67
+ }),
68
+ })
69
+ );
70
+ },
71
+
72
+ async update(id, patch) {
73
+ return readReviewItem(
74
+ await requestJson<RemoteReviewItemResponse>(
75
+ `/review-items/${encodeURIComponent(id)}`,
76
+ options,
77
+ {
78
+ method: 'PATCH',
79
+ body: JSON.stringify({ patch }),
80
+ }
81
+ )
82
+ );
83
+ },
84
+
85
+ async remove(id) {
86
+ await requestJson<void>(
87
+ `/review-items/${encodeURIComponent(id)}`,
88
+ options,
89
+ { method: 'DELETE' }
90
+ );
91
+ },
92
+ };
93
+ }
94
+
95
+ /*
96
+ * ReviewShellAdapter is the React shell wiring.
97
+ *
98
+ * label becomes the URL source, for example /review?source=remote.
99
+ * create controls whether the shell can write to this adapter.
100
+ * canWrite can be true or limited to ['dom', 'note', 'area'].
101
+ * updateStatus drives the status buttons in the QA panel.
102
+ * remove enables delete actions for this source.
103
+ */
104
+ export function createRemoteReviewShellAdapter(
105
+ options: RemoteReviewAdapterOptions
106
+ ): ReviewShellAdapter {
107
+ const adapter = createRemoteReviewAdapter(options);
108
+
109
+ return {
110
+ label: options.source ?? 'remote',
111
+ get: (id) => adapter.get(id),
112
+ list: (query) => adapter.list(query),
113
+ create: (item) => adapter.create(item),
114
+ canWrite: true,
115
+ statusOptions: REVIEW_WORKFLOW_STATUS_OPTIONS,
116
+ updateStatus: ({ id, status }) =>
117
+ adapter.update(id, { status: normalizeRemoteStatus(status) }),
118
+ remove: (id) => adapter.remove(id),
119
+ };
120
+ }
121
+
122
+ function appendParam(
123
+ params: URLSearchParams,
124
+ key: string,
125
+ value: string | undefined
126
+ ) {
127
+ if (value) params.set(key, value);
128
+ }
129
+
130
+ function createListParams(query: ReviewItemQuery, source: ReviewSource) {
131
+ const params = new URLSearchParams();
132
+ params.set('project_id', query.projectId);
133
+ params.set('source', query.source ?? source);
134
+ appendParam(params, 'page_id', query.pageId);
135
+ appendParam(params, 'route_key', query.routeKey ?? query.normalizedPath);
136
+ appendParam(params, 'status', query.status);
137
+ return params;
138
+ }
139
+
140
+ async function requestJson<T>(
141
+ path: string,
142
+ options: RemoteReviewAdapterOptions,
143
+ init: RequestInit = {}
144
+ ) {
145
+ const url = new URL(path, ensureTrailingSlash(options.baseUrl));
146
+ const headers = new Headers(init.headers);
147
+
148
+ headers.set('Accept', 'application/json');
149
+ if (init.body && !headers.has('Content-Type')) {
150
+ headers.set('Content-Type', 'application/json');
151
+ }
152
+ if (options.token) {
153
+ headers.set('Authorization', `Bearer ${options.token}`);
154
+ }
155
+
156
+ const response = await fetch(url, {
157
+ ...init,
158
+ headers,
159
+ credentials: 'include',
160
+ });
161
+
162
+ if (!response.ok) {
163
+ throw new Error(`remote review adapter request failed: ${response.status}`);
164
+ }
165
+ if (response.status === 204) return undefined as T;
166
+
167
+ return response.json() as Promise<T>;
168
+ }
169
+
170
+ function readReviewItem(response: RemoteReviewItemResponse) {
171
+ return 'item' in response ? response.item : response;
172
+ }
173
+
174
+ function ensureTrailingSlash(value: string) {
175
+ return value.endsWith('/') ? value : `${value}/`;
176
+ }
177
+
178
+ function normalizeRemoteStatus(status: ReviewItemStatus) {
179
+ if (status === 'open') return 'todo';
180
+ if (status === 'resolved') return 'done';
181
+ return status;
182
+ }
@@ -0,0 +1,125 @@
1
+ # Architecture and Runtime Logic
2
+
3
+ `df-web-review-kit` has two main runtime surfaces:
4
+
5
+ - `core`: a vanilla DOM runtime that can mount review overlays on a same-origin target page.
6
+ - `react-shell`: a React review app that hosts pages in an iframe and controls the core runtime.
7
+
8
+ This split keeps the target-page overlay independent from React while allowing the review shell to provide richer workflow UI.
9
+
10
+ ## High-Level Flow
11
+
12
+ ```txt
13
+ React shell
14
+ -> renders topbar, QA panel, iframe, ruler, settings
15
+ -> creates core runtime with createWebReviewKit()
16
+ -> passes iframe target geometry through getReviewKitTarget()
17
+
18
+ Core runtime
19
+ -> mounts a shadow DOM overlay
20
+ -> handles note/area/DOM selection
21
+ -> creates markers and highlights over the target viewport
22
+ -> persists ReviewItem records through the configured adapter
23
+ ```
24
+
25
+ In the React shell, core is created with:
26
+
27
+ ```ts
28
+ createWebReviewKit({
29
+ target: () => getReviewKitTarget({ frameScrollRef, iframeRef }),
30
+ ui: {
31
+ panel: false,
32
+ },
33
+ });
34
+ ```
35
+
36
+ `ui.panel: false` means the React shell owns the side panel and toolbar. Core still owns target overlays such as note pins, area selection boxes, DOM hover outlines, saved item markers, and highlights.
37
+
38
+ ## Core Modules
39
+
40
+ - `web.review.kit.app.ts`: controller lifecycle, state transitions, adapter calls, item creation, restore flow.
41
+ - `web.review.kit.view.ts`: vanilla DOM renderer for core overlay UI.
42
+ - `dom.anchor.ts`: selector candidate generation, anchor rebinding, text fingerprint matching.
43
+ - `geometry.ts`: target-space and host-space coordinate conversion.
44
+ - `review/item.ts`: marker, selection, highlight, and fallback resolution.
45
+ - `review/scope.ts`: viewport scope grouping and numbering.
46
+ - `review/format.ts`: compact item/draft metadata labels.
47
+ - `scroll.ts`: scroll restore helpers.
48
+ - `location.ts`: public URL and route key helpers.
49
+
50
+ ## Coordinate Spaces
51
+
52
+ Core uses two coordinate spaces:
53
+
54
+ - **Target space**: coordinates relative to the iframe or target page viewport.
55
+ - **Host space**: coordinates relative to the review shell page that contains the iframe.
56
+
57
+ Persisted review data uses target-space viewport values plus optional anchor-relative values. Rendering converts target-space markers and selections into host-space overlay positions.
58
+
59
+ ## Anchor and Restore Logic
60
+
61
+ When a user creates a DOM or area item, core tries to capture:
62
+
63
+ - an explicit configured anchor such as `data-qa-id`
64
+ - a meaningful `id`
65
+ - a meaningful class
66
+ - a DOM path fallback
67
+ - a short text fingerprint
68
+
69
+ Restore prefers anchor-relative coordinates because absolute viewport coordinates drift after layout changes. If an anchor cannot be resolved, core falls back to the original viewport coordinate adjusted by saved scroll position.
70
+
71
+ ## Adapter Boundary
72
+
73
+ Core never owns persistence storage directly. It only calls the configured `WebReviewKitAdapter`:
74
+
75
+ - `list`
76
+ - `get`
77
+ - `create`
78
+ - `update`
79
+ - `remove`
80
+
81
+ The default local adapter is for draft/local review work. Supabase is optional host wiring, not a required backend.
82
+
83
+ ## React Shell Boundary
84
+
85
+ `react-shell` owns reviewer workflow UI:
86
+
87
+ - iframe target routing
88
+ - QA list and item actions
89
+ - viewport presets
90
+ - sitemap modal
91
+ - settings modal
92
+ - ruler UI
93
+ - presence UI
94
+ - host overlay toggles such as grid and Figma
95
+
96
+ React shell should call the core controller instead of duplicating target overlay logic.
97
+
98
+ ## Figma Overlay Direction
99
+
100
+ Figma overlay work should stay outside core unless it needs target runtime primitives.
101
+
102
+ Preferred direction:
103
+
104
+ ```txt
105
+ src/react-shell/figma/
106
+ controls.tsx
107
+ sync.ts
108
+ overlay.state.ts
109
+
110
+ src/figma/
111
+ matching.ts
112
+ types.ts
113
+ ```
114
+
115
+ Shared coordinate math can reuse `core/geometry.ts` or move to a future shared module if both core and Figma need it heavily.
116
+
117
+ Avoid turning `core` into a feature bucket. Core should stay focused on target review runtime behavior.
118
+
119
+ ## Extension Rules
120
+
121
+ - Put target overlay primitives in `core`.
122
+ - Put reviewer workflow UI in `react-shell`.
123
+ - Put feature-specific integration logic, such as Figma matching, in its own module.
124
+ - Keep adapter contracts storage-agnostic.
125
+ - Prefer anchor-relative data for anything that must survive layout changes.