@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
@@ -0,0 +1,253 @@
1
+ # DB Setup
2
+
3
+ Supabase is an optional backend adapter. A host project may use it for canonical QA items and Realtime Presence, but the public package does not own the Supabase project or any operator secrets.
4
+
5
+ ## Environment
6
+
7
+ ```env
8
+ VITE_REVIEW_PROJECT_ID=df-web-review-kit
9
+ VITE_REVIEW_SUPABASE_URL=https://your-project.supabase.co
10
+ VITE_REVIEW_SUPABASE_ANON_KEY=
11
+ VITE_REVIEW_SUPABASE_TABLE=review_items
12
+ VITE_REVIEW_SUPABASE_PRESENCE_PRIVATE=false
13
+ ```
14
+
15
+ Use only an `anon` key in browser env. Keep `service_role`, OpenClaw `KUKU_*`, and future admin keys in an operator layer or backend service.
16
+
17
+ ## Review Item Schema
18
+
19
+ Run this in the Supabase SQL editor to create the tables, indexes, RPC, and grants.
20
+
21
+ ```sql
22
+ create table if not exists public.review_items (
23
+ id text primary key,
24
+ project_id text not null,
25
+ route_key text not null,
26
+ source text not null default 'supabase',
27
+ review_number integer,
28
+ status text not null default 'todo',
29
+ item jsonb not null,
30
+ created_at timestamptz not null default now(),
31
+ updated_at timestamptz not null default now()
32
+ );
33
+
34
+ create unique index if not exists review_items_project_review_number_idx
35
+ on public.review_items (project_id, source, review_number)
36
+ where review_number is not null;
37
+
38
+ create index if not exists review_items_project_route_updated_idx
39
+ on public.review_items (project_id, source, route_key, updated_at desc);
40
+
41
+ create index if not exists review_items_project_status_idx
42
+ on public.review_items (project_id, source, status);
43
+
44
+ create table if not exists public.review_project_counters (
45
+ project_id text not null,
46
+ source text not null default 'supabase',
47
+ next_review_number integer not null default 1,
48
+ updated_at timestamptz not null default now(),
49
+ primary key (project_id, source),
50
+ constraint review_project_counters_next_review_number_check
51
+ check (next_review_number > 0)
52
+ );
53
+
54
+ create or replace function public.create_review_item(
55
+ p_id text,
56
+ p_project_id text,
57
+ p_route_key text,
58
+ p_source text,
59
+ p_status text,
60
+ p_item jsonb
61
+ )
62
+ returns public.review_items
63
+ language plpgsql
64
+ security invoker
65
+ set search_path = public
66
+ as $$
67
+ declare
68
+ v_review_number integer;
69
+ v_now timestamptz := now();
70
+ v_row public.review_items;
71
+ begin
72
+ insert into public.review_project_counters (
73
+ project_id,
74
+ source,
75
+ next_review_number,
76
+ updated_at
77
+ )
78
+ select
79
+ p_project_id,
80
+ p_source,
81
+ coalesce(max(review_number), 0) + 2,
82
+ v_now
83
+ from public.review_items
84
+ where project_id = p_project_id
85
+ and source = p_source
86
+ on conflict (project_id, source) do update
87
+ set next_review_number = greatest(
88
+ public.review_project_counters.next_review_number + 1,
89
+ excluded.next_review_number
90
+ ),
91
+ updated_at = excluded.updated_at
92
+ returning next_review_number - 1 into v_review_number;
93
+
94
+ insert into public.review_items (
95
+ id,
96
+ project_id,
97
+ route_key,
98
+ source,
99
+ review_number,
100
+ status,
101
+ item,
102
+ created_at,
103
+ updated_at
104
+ )
105
+ values (
106
+ p_id,
107
+ p_project_id,
108
+ p_route_key,
109
+ p_source,
110
+ v_review_number,
111
+ p_status,
112
+ p_item || jsonb_build_object(
113
+ 'id', p_id,
114
+ 'reviewNumber', v_review_number,
115
+ 'projectId', p_project_id,
116
+ 'routeKey', p_route_key,
117
+ 'normalizedPath', coalesce(nullif(p_item->>'normalizedPath', ''), p_route_key),
118
+ 'status', p_status,
119
+ 'externalIssueId', p_id,
120
+ 'submittedAt', coalesce(p_item->>'submittedAt', v_now::text),
121
+ 'submitStatus', coalesce(p_item->>'submitStatus', 'submitted'),
122
+ 'createdAt', v_now::text,
123
+ 'updatedAt', v_now::text
124
+ ),
125
+ v_now,
126
+ v_now
127
+ )
128
+ returning * into v_row;
129
+
130
+ return v_row;
131
+ end;
132
+ $$;
133
+
134
+ grant execute on function public.create_review_item(
135
+ text,
136
+ text,
137
+ text,
138
+ text,
139
+ text,
140
+ jsonb
141
+ ) to anon;
142
+
143
+ grant select, insert, update, delete on public.review_items to anon;
144
+ grant select, insert, update on public.review_project_counters to anon;
145
+ ```
146
+
147
+ ## RLS Policies
148
+
149
+ Run this after the schema SQL. Replace `df-web-review-kit` if `VITE_REVIEW_PROJECT_ID` uses a different value.
150
+
151
+ ```sql
152
+ alter table public.review_items enable row level security;
153
+ alter table public.review_project_counters enable row level security;
154
+
155
+ drop policy if exists review_items_sample_read on public.review_items;
156
+ create policy review_items_sample_read
157
+ on public.review_items
158
+ for select
159
+ to anon
160
+ using (project_id = 'df-web-review-kit');
161
+
162
+ drop policy if exists review_items_sample_insert on public.review_items;
163
+ create policy review_items_sample_insert
164
+ on public.review_items
165
+ for insert
166
+ to anon
167
+ with check (project_id = 'df-web-review-kit');
168
+
169
+ drop policy if exists review_items_sample_update on public.review_items;
170
+ create policy review_items_sample_update
171
+ on public.review_items
172
+ for update
173
+ to anon
174
+ using (project_id = 'df-web-review-kit')
175
+ with check (project_id = 'df-web-review-kit');
176
+
177
+ drop policy if exists review_items_sample_delete on public.review_items;
178
+ create policy review_items_sample_delete
179
+ on public.review_items
180
+ for delete
181
+ to anon
182
+ using (project_id = 'df-web-review-kit');
183
+
184
+ drop policy if exists review_project_counters_sample_read on public.review_project_counters;
185
+ create policy review_project_counters_sample_read
186
+ on public.review_project_counters
187
+ for select
188
+ to anon
189
+ using (project_id = 'df-web-review-kit');
190
+
191
+ drop policy if exists review_project_counters_sample_insert on public.review_project_counters;
192
+ create policy review_project_counters_sample_insert
193
+ on public.review_project_counters
194
+ for insert
195
+ to anon
196
+ with check (project_id = 'df-web-review-kit');
197
+
198
+ drop policy if exists review_project_counters_sample_update on public.review_project_counters;
199
+ create policy review_project_counters_sample_update
200
+ on public.review_project_counters
201
+ for update
202
+ to anon
203
+ using (project_id = 'df-web-review-kit')
204
+ with check (project_id = 'df-web-review-kit');
205
+ ```
206
+
207
+ ## Adapter Behavior
208
+
209
+ - `list`: reads by `project_id`, `source`, optional `route_key`, optional `status`.
210
+ - `create`: discards local draft number and calls `create_review_item` for a canonical `review_number`.
211
+ - `update`: updates row columns and the nested `item` JSON.
212
+ - `remove`: deletes the row.
213
+
214
+ Remote numbers are canonical. Local draft numbers can overlap between browsers and are not reused as remote numbers.
215
+
216
+ ## Presence
217
+
218
+ Supabase Presence is optional session state. It is not QA item storage.
219
+
220
+ Default topic:
221
+
222
+ ```txt
223
+ review-presence-<projectId>
224
+ ```
225
+
226
+ The adapter normalizes unsafe topic characters. `private=false` is enough for quick dev validation. `private=true` requires an authenticated Supabase session and Realtime authorization policies for `realtime.messages`.
227
+
228
+ Use project-level channels instead of page-level channels so the sitemap can show who is on each page.
229
+
230
+ ## Security
231
+
232
+ The RLS policies above are sample/dev policies. Anyone with the anon key and allowed `project_id` can create, update, and delete QA rows.
233
+
234
+ For production, use one of these instead:
235
+
236
+ - Supabase Auth plus project member table based RLS
237
+ - Supabase Edge Function
238
+ - host project backend proxy
239
+ - private admin service
240
+
241
+ Never put a `service_role` key in browser env.
242
+
243
+ ## Validation
244
+
245
+ 1. Open `/review?source=supabase`.
246
+ 2. Create a local item.
247
+ 3. Submit it to remote.
248
+ 4. Confirm the local draft disappears.
249
+ 5. Confirm remote list/get works.
250
+ 6. Change status.
251
+ 7. Delete the remote item.
252
+ 8. Create another remote item and confirm the number is not reused.
253
+ 9. If presence is enabled, open two browser tabs and confirm each user appears on the active page or sitemap.
@@ -0,0 +1,52 @@
1
+ # Figma Overlay
2
+
3
+ The Figma overlay button in the review shell toggles a host-page helper. The package does not fetch Figma data by itself and does not own a server-side Figma token.
4
+
5
+ ## User Flow
6
+
7
+ - Open `/review`.
8
+ - Click the settings button.
9
+ - Enter a Figma token if the host helper requires one.
10
+ - Click the Figma overlay button or press `f`.
11
+
12
+ The token is stored in browser localStorage with this key:
13
+
14
+ ```txt
15
+ figma-token
16
+ ```
17
+
18
+ Treat this as a dev/debug convenience. Do not use it for private server tokens.
19
+
20
+ ## Availability
21
+
22
+ The shell currently enables the Figma overlay on viewport presets whose `kind` is:
23
+
24
+ - `mobile`
25
+ - `wide`
26
+
27
+ Other viewport kinds show the unavailable message.
28
+
29
+ ## Host Requirements
30
+
31
+ The target page inside the iframe must already support the Figma helper.
32
+
33
+ Expected behavior:
34
+
35
+ - The target page reacts to a `KeyboardEvent` with `code: 'KeyF'`.
36
+ - The target page mounts a visible Figma helper layer.
37
+ - The helper root uses one of these selectors:
38
+ - `.helper-figma-root`
39
+ - `.helper-figma-loading-backdrop`
40
+
41
+ The review shell uses those selectors only to detect whether the overlay is active.
42
+
43
+ ## Troubleshooting
44
+
45
+ If the button does nothing:
46
+
47
+ - Confirm the iframe target page is same-origin and can receive dispatched keyboard events.
48
+ - Confirm the host helper listens for `KeyF`.
49
+ - Confirm the helper renders `.helper-figma-root` or `.helper-figma-loading-backdrop`.
50
+ - Confirm the current viewport preset is `mobile` or `wide`.
51
+
52
+ If the overlay needs a private Figma integration, move that work to a backend/admin service and expose only browser-safe state to the host page.
@@ -0,0 +1,38 @@
1
+ # Grid Overlay
2
+
3
+ The grid overlay button in the review shell toggles a host-page layout/helper overlay. It is for visual alignment while reviewing pages.
4
+
5
+ ## User Flow
6
+
7
+ - Open `/review`.
8
+ - Click the grid button or press `g`.
9
+ - The target page toggles its own grid/helper layer.
10
+
11
+ ## Host Requirements
12
+
13
+ The target page inside the iframe must already support the grid helper.
14
+
15
+ Expected behavior:
16
+
17
+ - The target page reacts to a `KeyboardEvent` with `code: 'KeyG'`.
18
+ - The target page toggles a visible grid/helper layer.
19
+ - The active state is reflected by one of these:
20
+ - `document.body.classList.contains('is-help')`
21
+ - `.helper.onShow`
22
+
23
+ The review shell uses those signals to keep the toolbar button state in sync.
24
+
25
+ ## Notes
26
+
27
+ - The package does not prescribe the grid design.
28
+ - The grid overlay is not persisted as QA data.
29
+ - Use project-specific CSS/helper code in the host page when the grid differs by brand or design system.
30
+
31
+ ## Troubleshooting
32
+
33
+ If the icon state does not change:
34
+
35
+ - Confirm the target page helper is mounted.
36
+ - Confirm it listens for `KeyG`.
37
+ - Confirm it sets `body.is-help` or `.helper.onShow`.
38
+ - Confirm the iframe is same-origin and keyboard events are not blocked.
@@ -1,30 +1,29 @@
1
1
  # Installation
2
2
 
3
- Host project에 `df-web-review-kit`를 설치하고 `/review` route에서 `mountReviewShell()`을 호출한다.
3
+ Install `df-web-review-kit` in a host project and mount the review shell on a `/review` route.
4
4
 
5
- ## Package install
5
+ The default setup is local-only. Remote DB and presence are optional adapters.
6
6
 
7
- NPM package로 사용할 때:
7
+ ## Package Install
8
8
 
9
9
  ```bash
10
10
  pnpm add @designfever/web-review-kit react react-dom
11
11
  ```
12
12
 
13
- Supabase remote/presence를 쓰면 host project에 Supabase client도 설치한다.
13
+ Supabase is optional. Install it only in host projects that use the Supabase adapter.
14
14
 
15
15
  ```bash
16
16
  pnpm add @supabase/supabase-js
17
17
  ```
18
18
 
19
- Lexus repo 안에서 검증할 때는 file dependency를 사용한다.
19
+ ## Vite Route
20
20
 
21
- ```json
22
- "@designfever/web-review-kit": "file:packages/df-web-review-kit"
23
- ```
24
-
25
- ## Vite route
21
+ Create a review entry such as:
26
22
 
27
- Vite project에서는 `page/review/index.html`과 `page/review/index.tsx` 같은 review entry를 만든다.
23
+ ```txt
24
+ page/review/index.html
25
+ page/review/index.tsx
26
+ ```
28
27
 
29
28
  Minimal `index.html`:
30
29
 
@@ -33,7 +32,7 @@ Minimal `index.html`:
33
32
  <script type="module" src="./index.tsx"></script>
34
33
  ```
35
34
 
36
- Minimal `index.tsx`:
35
+ Minimal local-only `index.tsx`:
37
36
 
38
37
  ```tsx
39
38
  import {
@@ -46,6 +45,8 @@ import {
46
45
  } from '@designfever/web-review-kit';
47
46
 
48
47
  const REVIEW_PROJECT_ID = 'my-project';
48
+ const REVIEW_PATH_PREFIX = '/review';
49
+
49
50
  const local = localAdapter({
50
51
  storageKey: `${REVIEW_PROJECT_ID}-review-items`,
51
52
  });
@@ -69,13 +70,13 @@ mountReviewShell({
69
70
  remove: (id) => local.remove(id),
70
71
  },
71
72
  ],
72
- reviewPathPrefix: '/review',
73
+ reviewPathPrefix: REVIEW_PATH_PREFIX,
73
74
  });
74
75
  ```
75
76
 
76
- ## Supabase remote example
77
+ ## Supabase Adapter
77
78
 
78
- Supabase를 붙일 때는 host project에서 client 만들고 package adapter에 주입한다.
79
+ Host projects that choose Supabase create the client themselves and pass it into the package adapter.
79
80
 
80
81
  ```tsx
81
82
  import {
@@ -93,7 +94,7 @@ import {
93
94
  } from '@designfever/web-review-kit';
94
95
  import { createClient } from '@supabase/supabase-js';
95
96
 
96
- const REVIEW_PROJECT_ID = 'lexus-official-v2026';
97
+ const REVIEW_PROJECT_ID = 'my-project';
97
98
  const REVIEW_PATH_PREFIX = '/review';
98
99
 
99
100
  const local = localAdapter({
@@ -157,13 +158,35 @@ const presence = supabaseClient
157
158
  localPresence
158
159
  )
159
160
  : localPresence;
161
+
162
+ mountReviewShell({
163
+ projectId: REVIEW_PROJECT_ID,
164
+ pages,
165
+ adapters,
166
+ presence,
167
+ reviewPathPrefix: REVIEW_PATH_PREFIX,
168
+ });
160
169
  ```
161
170
 
162
- 다음 `mountReviewShell({ adapters, presence, ... })`에 넘긴다.
171
+ See [DB setup](db-setup.md) before enabling Supabase in a shared environment.
172
+
173
+ ## Custom Adapter
174
+
175
+ If a team or host project owns its own QA backend, keep that adapter in the host project or in a separate package. Start from [adaptor.sample.ts](adaptor.sample.ts) and map its `WebReviewKitAdapter` methods to your backend API.
176
+
177
+ The sample explains the main interfaces:
178
+
179
+ - `ReviewItem`: the full QA payload to persist as structured JSON.
180
+ - `ReviewItemQuery`: filters used by page lists and sitemap counts.
181
+ - `WebReviewKitAdapter`: core CRUD contract.
182
+ - `ReviewShellAdapter`: React shell wiring for source labels, write modes, status updates, and delete actions.
183
+
184
+ Private keys, admin credentials, canonical numbering, and permission checks should stay in your backend, not in browser code.
163
185
 
164
186
  ## Environment
165
187
 
166
188
  ```env
189
+ VITE_REVIEW_PROJECT_ID=df-web-review-kit
167
190
  VITE_REVIEW_SUPABASE_URL=https://your-project.supabase.co
168
191
  VITE_REVIEW_SUPABASE_ANON_KEY=
169
192
  VITE_REVIEW_SUPABASE_TABLE=review_items
@@ -172,13 +195,13 @@ VITE_REVIEW_SUPABASE_PRESENCE_PRIVATE=false
172
195
 
173
196
  Rules:
174
197
 
175
- - browser env에는 Supabase `anon` key 넣는다.
176
- - `service_role` key는 절대 browser env에 넣지 않는다.
177
- - package는 Supabase dependency를 직접 만들지 않는다. host project가 `createClient()`를 호출한다.
198
+ - Browser env uses a Supabase `anon` key only.
199
+ - Never expose `service_role` in browser env.
200
+ - OpenClaw/operator secrets stay outside the host browser and outside this package.
178
201
 
179
- ## Viewport preset
202
+ ## Viewport Presets
180
203
 
181
- Project별 design width가 다르면 `presets`를 넘긴다.
204
+ Pass `presets` when a project has custom design widths.
182
205
 
183
206
  ```tsx
184
207
  mountReviewShell({
@@ -194,29 +217,41 @@ mountReviewShell({
194
217
  });
195
218
  ```
196
219
 
197
- ## Development commands
198
-
199
- Lexus repo 기준:
220
+ ## Local Dev Harness
200
221
 
201
222
  ```bash
202
223
  pnpm dev:review
203
- pnpm review-kit:typecheck
204
- pnpm typecheck:review
205
- pnpm review-kit:build
206
- pnpm build:review
207
224
  ```
208
225
 
209
- Package source를 수정하면 `pnpm review-kit:build`로 `dist`를 갱신한다.
226
+ Open `http://127.0.0.1:5177/review/`.
227
+
228
+ Fixture pages:
229
+
230
+ - `/`: note, area, and DOM marker creation
231
+ - `/components/`: controls and panel spacing
232
+ - `/long-form/`: scroll and anchor restore
233
+
234
+ ## Checks
235
+
236
+ Package repo:
237
+
238
+ ```bash
239
+ pnpm typecheck
240
+ pnpm build
241
+ pnpm typecheck:dev
242
+ pnpm build:dev
243
+ ```
244
+
245
+ Host repo:
210
246
 
211
- ## Publish checklist
247
+ ```bash
248
+ pnpm typecheck
249
+ pnpm build
250
+ ```
212
251
 
213
- 0.1 package 배포 전 확인:
252
+ Manual smoke:
214
253
 
215
- - `packages/df-web-review-kit/package.json`의 `files`에 `dist`, `src`, `docs`, `README.md` 포함
216
- - `pnpm review-kit:typecheck`
217
- - `pnpm review-kit:build`
218
- - local source에서 note/dom/area 생성 확인
219
- - local item을 remote로 등록하면 local draft 삭제 확인
220
- - remote source에서 status update/delete 확인
221
- - `/review?source=supabase&target=...&item=...` restore 확인
222
- - Supabase `reviewNumber`가 삭제 후 재사용되지 않는지 확인
254
+ 1. Open `/review`.
255
+ 2. Create local note, DOM marker, and area marker.
256
+ 3. If Supabase is enabled, submit a local item to remote.
257
+ 4. Confirm local draft removal, remote list display, status update, delete, and deep-link restore.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@designfever/web-review-kit",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "type": "module",
5
5
  "description": "Designfever web page review overlay toolkit.",
6
6
  "license": "UNLICENSED",
@@ -41,21 +41,26 @@
41
41
  "scripts": {
42
42
  "build": "tsup src/index.ts src/react-shell.tsx --format esm,cjs --dts --sourcemap --clean --external react --external react-dom --external react/jsx-runtime",
43
43
  "dev": "tsup src/index.ts src/react-shell.tsx --format esm,cjs --dts --sourcemap --watch --external react --external react-dom --external react/jsx-runtime",
44
+ "dev:review": "vite --config dev/vite.config.ts",
45
+ "build:dev": "vite build --config dev/vite.config.ts",
44
46
  "prepare": "pnpm build",
45
- "typecheck": "tsc --noEmit"
47
+ "typecheck": "tsc --noEmit",
48
+ "typecheck:dev": "tsc --noEmit -p tsconfig.dev.json"
46
49
  },
47
50
  "peerDependencies": {
48
51
  "react": ">=18",
49
52
  "react-dom": ">=18"
50
53
  },
51
54
  "devDependencies": {
55
+ "@supabase/supabase-js": "^2.108.2",
52
56
  "@types/react": "^19.2.17",
53
57
  "@types/react-dom": "^19.2.3",
54
58
  "lucide-react": "^1.20.0",
55
59
  "react": "^19.2.7",
56
60
  "react-dom": "^19.2.7",
57
61
  "tsup": "^8.3.6",
58
- "typescript": "^6.0.2"
62
+ "typescript": "^6.0.2",
63
+ "vite": "^8.0.8"
59
64
  },
60
65
  "packageManager": "pnpm@9.0.0"
61
66
  }