@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.
- package/README.md +59 -184
- package/dist/{chunk-U5K2YGGL.js → chunk-EJDROXJM.js} +2583 -3085
- package/dist/chunk-EJDROXJM.js.map +1 -0
- package/dist/index.cjs +2615 -2900
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +10 -6
- package/dist/index.d.ts +10 -6
- package/dist/index.js +220 -7
- package/dist/index.js.map +1 -1
- package/dist/react-shell.cjs +8364 -6651
- package/dist/react-shell.cjs.map +1 -1
- package/dist/react-shell.d.cts +7 -3
- package/dist/react-shell.d.ts +7 -3
- package/dist/react-shell.js +5163 -3425
- package/dist/react-shell.js.map +1 -1
- package/dist/{types-D_mNjOHx.d.cts → types-NiCp9JJQ.d.cts} +6 -14
- package/dist/{types-D_mNjOHx.d.ts → types-NiCp9JJQ.d.ts} +6 -14
- package/docs/README.md +21 -30
- package/docs/adaptor.sample.ts +182 -0
- package/docs/architecture.md +125 -0
- package/docs/db-setup.md +253 -0
- package/docs/figma-overlay.md +52 -0
- package/docs/grid-overlay.md +38 -0
- package/docs/installation.md +75 -40
- package/package.json +8 -3
- package/dist/chunk-U5K2YGGL.js.map +0 -1
- package/docs/adapter-handoff.md +0 -146
- package/docs/concept.md +0 -102
- package/docs/df-sheet-adapter.md +0 -336
- package/docs/df-sheet-next.md +0 -222
- package/docs/initial-plan.md +0 -226
- package/docs/package-split-checkpoint.md +0 -79
- package/docs/presence-handoff.md +0 -138
- package/docs/review-feedback-2026-06-20.md +0 -267
- package/docs/smoke-baseline-2026-06-20.md +0 -41
- package/docs/stabilize-ui-work-guide.md +0 -243
- package/docs/supabase-presence.md +0 -198
- package/docs/supabase-review-items.md +0 -365
- 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' | '
|
|
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
|
|
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 {
|
|
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' | '
|
|
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
|
|
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 {
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
35
|
-
|
|
36
|
-
- `
|
|
37
|
-
-
|
|
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.
|