@designfever/web-review-kit 0.7.3 → 0.8.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.
@@ -1,5 +1,7 @@
1
1
  import {
2
2
  REVIEW_WORKFLOW_STATUS_OPTIONS,
3
+ type ReviewAttachment,
4
+ type ReviewAttachmentUploadInput,
3
5
  type ReviewItem,
4
6
  type ReviewItemQuery,
5
7
  type ReviewItemStatus,
@@ -17,17 +19,21 @@ type RemoteReviewAdapterOptions = {
17
19
  source?: ReviewSource;
18
20
  token?: string;
19
21
  fields?: ReviewShellAdapter['fields'];
22
+ attachmentUploadPath?: string;
20
23
  assigneeTitle?: string;
21
24
  assigneeOptions?: ReviewShellAssigneeOption[];
22
25
  };
23
26
 
24
27
  type RemoteReviewItemResponse = ReviewItem | { item: ReviewItem };
28
+ type RemoteReviewAttachmentResponse =
29
+ | ReviewAttachment
30
+ | { attachment: ReviewAttachment };
25
31
 
26
32
  /*
27
33
  * WebReviewKitAdapter is the core storage contract.
28
34
  *
29
35
  * ReviewItem is the full QA payload. Persist it as structured JSON so marker,
30
- * anchor, selection, viewport, scroll, status, and external issue fields survive.
36
+ * anchor, selection, viewport, scroll, status, and external link fields survive.
31
37
  *
32
38
  * ReviewItemQuery is used by the shell for current-page lists and sitemap counts.
33
39
  * A remote backend should support at least projectId, routeKey, status, source,
@@ -101,12 +107,14 @@ export function createRemoteReviewAdapter(
101
107
  *
102
108
  * label becomes the URL source, for example /review?source=remote.
103
109
  * create controls whether the shell can write to this adapter.
104
- * canWrite can be true or limited to ['dom', 'note', 'area'].
110
+ * canWrite can be true or limited to ['dom', 'area'].
105
111
  * update enables title/comment edits in the QA panel.
106
112
  * fields enables optional UI fields such as title. Omit title to keep comment-only UI.
107
113
  * updateStatus drives the status buttons in the QA panel.
108
114
  * assigneeOptions + updateAssignee drive the assignee select next to status.
109
115
  * assigneeTitle customizes the empty option/field label. Defaults to "Assignee".
116
+ * uploadAttachment lets paste/capture flows upload File/Blob data before the
117
+ * returned metadata is stored on ReviewItem.attachments.
110
118
  * remove enables delete actions for this source.
111
119
  */
112
120
  export function createRemoteReviewShellAdapter(
@@ -129,10 +137,44 @@ export function createRemoteReviewShellAdapter(
129
137
  assigneeOptions: options.assigneeOptions ?? [],
130
138
  updateAssignee: ({ id, assigneeId, assigneeName }) =>
131
139
  adapter.update(id, { assigneeId, assigneeName }),
140
+ uploadAttachment: options.attachmentUploadPath
141
+ ? (input) => uploadReviewAttachment(input, options)
142
+ : undefined,
132
143
  remove: (id) => adapter.remove(id),
133
144
  };
134
145
  }
135
146
 
147
+ async function uploadReviewAttachment(
148
+ input: ReviewAttachmentUploadInput,
149
+ options: RemoteReviewAdapterOptions
150
+ ) {
151
+ if (!options.attachmentUploadPath) {
152
+ throw new Error('remote review attachment upload path is not configured');
153
+ }
154
+
155
+ const name =
156
+ input.name || (input.file instanceof File ? input.file.name : 'attachment');
157
+ const mime = input.mime || input.file.type;
158
+ const form = new FormData();
159
+ form.set('file', input.file, name);
160
+ form.set('name', name);
161
+ if (mime) form.set('mime', mime);
162
+ if (input.kind) form.set('kind', input.kind);
163
+ if (input.item) form.set('item_id', input.item.id);
164
+ if (input.metadata) form.set('metadata', JSON.stringify(input.metadata));
165
+
166
+ return readReviewAttachment(
167
+ await requestJson<RemoteReviewAttachmentResponse>(
168
+ options.attachmentUploadPath,
169
+ options,
170
+ {
171
+ method: 'POST',
172
+ body: form,
173
+ }
174
+ )
175
+ );
176
+ }
177
+
136
178
  function appendParam(
137
179
  params: URLSearchParams,
138
180
  key: string,
@@ -160,7 +202,11 @@ async function requestJson<T>(
160
202
  const headers = new Headers(init.headers);
161
203
 
162
204
  headers.set('Accept', 'application/json');
163
- if (init.body && !headers.has('Content-Type')) {
205
+ if (
206
+ init.body &&
207
+ !(init.body instanceof FormData) &&
208
+ !headers.has('Content-Type')
209
+ ) {
164
210
  headers.set('Content-Type', 'application/json');
165
211
  }
166
212
  if (options.token) {
@@ -185,6 +231,10 @@ function readReviewItem(response: RemoteReviewItemResponse) {
185
231
  return 'item' in response ? response.item : response;
186
232
  }
187
233
 
234
+ function readReviewAttachment(response: RemoteReviewAttachmentResponse) {
235
+ return 'attachment' in response ? response.attachment : response;
236
+ }
237
+
188
238
  function ensureTrailingSlash(value: string) {
189
239
  return value.endsWith('/') ? value : `${value}/`;
190
240
  }
@@ -17,7 +17,7 @@ React shell
17
17
 
18
18
  Core runtime
19
19
  -> mounts a shadow DOM overlay
20
- -> handles note/area/DOM selection
20
+ -> handles area/DOM selection
21
21
  -> creates markers and highlights over the target viewport
22
22
  -> persists ReviewItem records through the configured adapter
23
23
  ```
@@ -33,14 +33,14 @@ createWebReviewKit({
33
33
  });
34
34
  ```
35
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.
36
+ `ui.panel: false` means the React shell owns the side panel and toolbar. Core still owns target overlays such as area selection boxes, DOM hover outlines, saved item markers, and highlights.
37
37
 
38
38
  When the React shell provides a composer host, core docks DOM/area draft composer UI into the QA panel instead of rendering it as a floating target overlay. Core still owns draft creation, anchor capture, geometry, and adapter submission; React shell only provides the stable panel host.
39
39
 
40
40
  ## Core Modules
41
41
 
42
42
  - `web.review.kit.app.ts`: controller lifecycle, state transitions, adapter calls, item creation, restore flow.
43
- - `web.review.kit.view.ts`: vanilla DOM renderer for core overlay UI.
43
+ - `web.review.kit.view.ts`: thin render orchestrator. Decides which overlay layers to render and docks the draft composer into the shell panel. All DOM building lives in `view/`.
44
44
  - `draft.metrics.ts`: pure geometry for draft adjustment (nudge/scale) previews, kept out of the renderer.
45
45
  - `dom.anchor.ts`: selector candidate generation, anchor rebinding, text fingerprint matching.
46
46
  - `geometry.ts`: target-space and host-space coordinate conversion.
@@ -50,6 +50,21 @@ When the React shell provides a composer host, core docks DOM/area draft compose
50
50
  - `scroll.ts`: scroll restore helpers.
51
51
  - `location.ts`: public URL and route key helpers.
52
52
 
53
+ ### Core View Modules (`src/core/view/`)
54
+
55
+ The overlay renderer is split by role. Modules never reach into the app directly; they receive `WebReviewKitViewConfig` (options + state getter + actions) or the narrower `DraftLayerContext` defined in `view/types.ts`.
56
+
57
+ - `dom.draft.ts`: DOM draft layer — pin, highlight, composer popover, adjustment (nudge/scale) controls, pin/composer drag.
58
+ - `area.draft.ts`: area draft form, on-page selection overlay, and floating/docked composer popover.
59
+ - `selection.layers.ts`: element-pick hover layer and area drag-select layer.
60
+ - `markers.ts`: stored item marker/highlight layer.
61
+ - `panel.ts`: built-in side panel for standalone core usage (header, toolbar, item list). Disabled under the React shell.
62
+ - `form.widgets.ts`: shared draft form widgets (title input, assignee select, save/cancel actions, drag handle).
63
+ - `draft.capture.ts`: viewport capture button and capture payload builders.
64
+ - `composer.position.ts`: composer sizing/clamping and drag wiring (pure placement math).
65
+ - `draft.text.ts`: metric/adjustment display formatting and the saved-comment adjustment suffix.
66
+ - `icons.ts`: stateless SVG icon and spinner builders.
67
+
53
68
  ## Coordinate Spaces
54
69
 
55
70
  Core uses two coordinate spaces:
@@ -103,6 +118,105 @@ The default local adapter is for draft/local review work. Supabase is optional h
103
118
 
104
119
  React shell should call the core controller instead of duplicating target overlay logic.
105
120
 
121
+ ### React Shell Runtime Map
122
+
123
+ `review/shell.tsx` is the React shell entrypoint. It creates instance-scoped
124
+ config, refs, and zustand store objects, then delegates runtime wiring and layout
125
+ assembly to smaller modules.
126
+
127
+ ```mermaid
128
+ flowchart TD
129
+ Shell["review/shell.tsx"]
130
+ Config["store/shell.config.ts"]
131
+ Store["store/create.review.shell.store.ts"]
132
+ Refs["store/shell.refs.ts"]
133
+ Runtime["hooks/use.review.shell.runtime.ts"]
134
+ Providers["review/shell.providers.tsx"]
135
+ FrameContainer["review/shell.frame.container.tsx"]
136
+ Frame["review/shell.frame.tsx"]
137
+ Containers["feature *.container.tsx"]
138
+ Views["presentational UI components"]
139
+
140
+ Shell --> Config
141
+ Shell --> Store
142
+ Shell --> Refs
143
+ Shell --> Runtime
144
+ Runtime --> Providers
145
+ Providers --> FrameContainer
146
+ FrameContainer --> Frame
147
+ Frame --> Containers
148
+ Containers --> Views
149
+ ```
150
+
151
+ The store is created once per mounted `ReviewShell` instance. There is no
152
+ module-level shell store, so a host app using zustand does not share this state.
153
+
154
+ ### State Ownership
155
+
156
+ | Layer | Files | Owns |
157
+ |---|---|---|
158
+ | Shell config | `store/shell.config.ts` | Normalized props and mostly static shell options |
159
+ | Shell refs | `store/shell.refs.ts` | iframe, frame scroll, controller, and one-shot pending refs |
160
+ | Zustand store | `store/*.slice.ts` | Target, QA, side panel, and local UI state |
161
+ | Runtime hooks | `hooks/use.review.shell.*`, feature hooks | Effects, adapter refresh, core controller wiring, and action composition |
162
+ | Runtime contexts | `review/shell.providers.tsx`, `*.context.tsx` | Non-store controllers consumed by feature containers |
163
+ | Containers | `*.container.tsx` | Read store/context and bind actions to views |
164
+ | Presentational views | non-container `.tsx` components | Render UI from explicit props |
165
+
166
+ ### Container Pattern
167
+
168
+ Feature UI should not receive long prop chains from `ReviewShell`. Containers
169
+ read the closest state source directly, then pass only render-ready props to
170
+ presentational components.
171
+
172
+ ```mermaid
173
+ flowchart LR
174
+ Store["zustand store"]
175
+ Config["config/ref contexts"]
176
+ RuntimeContext["runtime contexts"]
177
+ Container["feature container"]
178
+ View["presentational view"]
179
+
180
+ Store --> Container
181
+ Config --> Container
182
+ RuntimeContext --> Container
183
+ Container --> View
184
+ ```
185
+
186
+ When adding shell behavior, prefer this flow:
187
+
188
+ 1. Put persistent UI state in the matching zustand slice.
189
+ 2. Put instance config in `shell.config.ts`.
190
+ 3. Put DOM/controller refs in `shell.refs.ts`.
191
+ 4. Put side effects and adapter/core orchestration in a focused hook.
192
+ 5. Expose cross-feature commands through `ReviewShellActions` only when multiple
193
+ containers need the same command.
194
+
195
+ ### Shell Hook Decomposition
196
+
197
+ `hooks/use.review.shell.runtime.ts` is the runtime assembler. It composes smaller
198
+ hooks and returns provider values; it should not become a render component.
199
+
200
+ - `hooks/use.review.shell.state.ts`: bridge from zustand/config/refs into runtime hook inputs.
201
+ - `hooks/use.review.shell.data.ts`: item list, filters, counts, target URL, and derived view data.
202
+ - `hooks/use.review.shell.refresh.ts`: adapter refresh for current route and sitemap counts.
203
+ - `hooks/use.review.shell.effects.ts`: shell-level effects such as pending restore, frame recentering, and Figma pointer lock.
204
+ - `hooks/use.review.shell.runtime.actions.ts`: local command builders for transient UI, mode, panels, and iframe load.
205
+ - `hooks/use.review.shell.actions.value.ts`: final `ReviewShellActions` context value.
206
+ - `hooks/use.review.controller.ts`: core runtime wiring (init/reload/restore/mode).
207
+ - `hooks/use.review.item.actions.ts`: QA item mutations and prompt/link copy actions.
208
+ - `hooks/use.review.side.panel.ts`: side panel selection, availability fallback, and browser-local persistence.
209
+ - `hooks/use.review.target.navigation.ts`: address parsing, page/source switching, selected-item clearing, and shell URL sync.
210
+ - `hooks/use.review.source.inspector.ts`: source inspector state and target-iframe shortcut binding.
211
+ - `hooks/use.review.section.outline.ts`: Source Tree outline extraction, filter/collapse state, refresh scheduling, and entry actions.
212
+ - `hooks/use.review.command.key.ts`: hide-all-overlays-while-command-held tracking across host and iframe.
213
+
214
+ ### Comment Policy
215
+
216
+ Prefer comments only at module boundaries or non-obvious runtime contracts. Avoid
217
+ file-wide restatements of import names, prop-by-prop explanations, or comments
218
+ that duplicate what a function name already says.
219
+
106
220
  ## Figma Overlay Direction
107
221
 
108
222
  Figma overlay work should stay outside core unless it needs target runtime primitives.
@@ -23,6 +23,42 @@ the same-origin image store request as a fallback.
23
23
 
24
24
  Treat this as a review/debug convenience. Do not expose `VITE_FIGMA_TOKEN`.
25
25
 
26
+ ### Security note: browser-stored token
27
+
28
+ The Settings token lives in plain `localStorage`. A Figma personal access token
29
+ grants read access to every file the account can see, so any XSS on the host
30
+ origin can read this key and exfiltrate it. Reduce the blast radius:
31
+
32
+ - Prefer the server-side `FIGMA_TOKEN` env path. Only fall back to the Settings
33
+ token when a dev/reviewer genuinely cannot set server env.
34
+ - When you must use the Settings token, use a short-lived or narrowly scoped
35
+ token and clear it from Settings when you are done.
36
+ - Never commit or share the token, and never expose it as `VITE_FIGMA_TOKEN`
37
+ (that ships it to every browser bundle).
38
+
39
+ The token is only sent to the same-origin image store endpoint via the
40
+ `X-Figma-Token` header; it is not sent to `api.figma.com` from the browser.
41
+
42
+ ## Dev Server Image Store Hardening
43
+
44
+ The `reviewFigmaImageStore()` Vite plugin runs only under `vite dev` (`apply:
45
+ 'serve'`); it is not part of `vite build` output, so production/Vercel deploys
46
+ are unaffected. Because the dev endpoint is unauthenticated, its middleware
47
+ applies minimal request checks:
48
+
49
+ - **Cross-origin block**: browser requests whose `Origin` does not match the
50
+ request `Host` (and is not loopback) are rejected with `403`. Requests without
51
+ an `Origin` header (curl, scripts) and same-origin requests — including LAN IP
52
+ access for mobile testing — are allowed.
53
+ - **JSON content type**: request bodies must be `application/json`. This blocks
54
+ `text/plain` "simple" cross-site POSTs that would otherwise skip a CORS
55
+ preflight. Rejected with `415`.
56
+ - **Body size limit**: JSON bodies are capped (default 25 MB, configurable via
57
+ the `maxRequestBytes` plugin option). Oversized bodies are rejected with `413`.
58
+
59
+ These guards protect the developer's local `FIGMA_TOKEN` and `.df-review`
60
+ working files from CSRF-style requests made by other pages open in the browser.
61
+
26
62
  ## Availability
27
63
 
28
64
  The shell currently enables the Figma overlay on viewport presets whose `kind` is:
@@ -46,6 +82,18 @@ Expected behavior:
46
82
 
47
83
  The review shell uses those selectors only to detect whether the overlay is active.
48
84
 
85
+ When the package-managed Figma image store is configured, the shell renders image
86
+ overlays into the iframe instead. Those overlays use:
87
+
88
+ - `#df-review-figma-image-target-root`
89
+ - `.df-review-figma-image-target-overlay`
90
+
91
+ The image overlay hit layer is interactive only when the image can be dragged.
92
+ During element review mode or `Option` source selection, the shell temporarily
93
+ locks both host helper layers and package image overlays with
94
+ `pointer-events: none` so target selection is not blocked. Releasing `Option`
95
+ restores the image overlay hit area.
96
+
49
97
  ## Troubleshooting
50
98
 
51
99
  If the button does nothing:
@@ -54,6 +102,9 @@ If the button does nothing:
54
102
  - Confirm the host helper listens for `KeyF`.
55
103
  - Confirm the helper renders `.helper-figma-root` or `.helper-figma-loading-backdrop`.
56
104
  - Confirm the current viewport preset is `mobile` or `wide`.
105
+ - If `Option` source selection cannot pick an element under a Figma image
106
+ overlay, confirm the overlay root uses the package selectors above or the host
107
+ helper selectors listed in Host Requirements.
57
108
 
58
109
  If the overlay needs a private shared Figma integration, move that work to a
59
110
  backend/admin service and expose only browser-safe state to the host page.
@@ -21,11 +21,16 @@ Expected behavior:
21
21
  - `.helper.onShow`
22
22
 
23
23
  The review shell uses those signals to keep the toolbar button state in sync.
24
+ It also injects a small stacking-order safeguard so recognized grid/helper
25
+ layers render above host Figma helpers and package-managed Figma image overlays.
24
26
 
25
27
  ## Notes
26
28
 
27
29
  - The package does not prescribe the grid design.
28
30
  - The grid overlay is not persisted as QA data.
31
+ - Recognized grid roots are `.helper.onShow`, `body.is-help .helper`,
32
+ `#df-review-grid-overlay`, `.df-review-grid-overlay`, and
33
+ `[data-df-review-grid-overlay]`.
29
34
  - Use project-specific CSS/helper code in the host page when the grid differs by brand or design system.
30
35
 
31
36
  ## Troubleshooting
@@ -8,7 +8,7 @@ For the QA adapter and Figma image store split, see [Adapter boundaries](adapter
8
8
  ## Package Install
9
9
 
10
10
  ```bash
11
- pnpm add @designfever/web-review-kit react react-dom
11
+ pnpm add @designfever/web-review-kit react react-dom zustand
12
12
  ```
13
13
 
14
14
  Supabase is optional. Install it only in host projects that use the Supabase adapter.
@@ -17,7 +17,7 @@ Supabase is optional. Install it only in host projects that use the Supabase ada
17
17
  pnpm add @supabase/supabase-js
18
18
  ```
19
19
 
20
- ## Environment
20
+ ## Environment Reference
21
21
 
22
22
  Copy the repository [.env.sample](../.env.sample) into the host project as `.env.local`.
23
23
 
@@ -236,9 +236,9 @@ export default defineConfig({
236
236
  });
237
237
  ```
238
238
 
239
- Captured DOM nodes will include `data-wrk-source-file`, `data-wrk-source-line`, and `data-wrk-source-column`. Data locator also injects `__wrkDataFile` and `__wrkDataLine` props into page data section objects so the shell can expose matching `data-wrk-data-*` hints when host components forward those props to section wrappers.
239
+ Captured DOM nodes will include `data-wrk-source-file`, `data-wrk-source-line`, and `data-wrk-source-column`. When TypeScript is available in the host toolchain, the source locator parses TSX/JSX and also adds `data-wrk-source-component` to intrinsic JSX nodes. For function component render paths, it records the parent JSX call site as `data-wrk-source-parent-*` hints so Source Tree can open the file that used the component. Data locator injects `__wrkDataFile` and `__wrkDataLine` props into page data section objects so the shell can expose matching `data-wrk-data-*` hints when host components forward those props to section wrappers.
240
240
 
241
- In the review shell, hold `Option` over the target iframe to show source candidates from the DOM ancestry. Click the target to pin the candidate list, then choose which file to open. The side rail Source Tree panel lists section/source/data candidates and can scroll to a section or open its source/data file. It can also show live box metrics, text/font metadata, media URLs, and class tags for each node. If the file path is absolute, it opens directly. If the plugin stores relative paths, pass `sourceRoot` when mounting the shell.
241
+ In the review shell, hold `Option` over the target iframe to show source candidates from the DOM ancestry. Click the target to pin the candidate list, then choose which file to open. The side rail Source Tree panel lists section/source/data candidates and can scroll to a section or open its source/data file. When parent usage hints exist, each row shows `used in` with the parent component and call-site file/position, plus an action to open that usage source. It can also show live box metrics, text/font metadata, media URLs, and class tags for each node. If the file path is absolute, it opens directly. If the plugin stores relative paths, pass `sourceRoot` when mounting the shell.
242
242
 
243
243
  Source opening reads these optional host env values from `.env.local`:
244
244
 
@@ -268,11 +268,11 @@ mountReviewShell({
268
268
  });
269
269
  ```
270
270
 
271
- Set `sourceInspector.enabled` to `false` when source code opening should be unavailable. Use `sourceInspector.ignore` to hide infrastructure files from source candidates and the Source Tree. Use `sourceInspector.maxDepth` to cap Source Tree traversal depth. Set `sourceInspector.hoverOutline` to `false` to disable iframe target outlines while hovering Source Tree items. Set `sourceInspector.includePlacer` to `true` when primitive Placer nodes should appear in Source Tree. The `data-font` overlay still belongs to the target project markup and is not required for source opening.
271
+ Set `sourceInspector.enabled` to `false` when source code opening should be unavailable. Use `sourceInspector.ignore` to hide infrastructure files from source candidates and the Source Tree. Use `sourceInspector.maxDepth` to cap Source Tree traversal depth. Set `sourceInspector.hoverOutline` to `false` to disable iframe target outlines while hovering Source Tree items. Set `sourceInspector.includePlacer` to `true` when primitive Placer nodes should appear in Source Tree or source candidate lists. The `data-font` overlay still belongs to the target project markup and is not required for source opening.
272
272
 
273
273
  Use this only in dev/review builds. Source paths are written into the browser DOM and can be persisted with QA items.
274
274
 
275
- Source Tree filter/options, side panel state, and QA status filter are browser-local UI preferences. They are stored in localStorage and are not sent through the adapter.
275
+ Source Tree filter/options, side panel state, QA status filter, and tooltip visibility are browser-local UI preferences. They are stored in localStorage and are not sent through the adapter.
276
276
 
277
277
  In React shell mode, DOM/area draft composers dock into the QA panel when the shell provides a composer host. This keeps mobile Safari keyboard/viewport resize from moving a floating composer layer over the target frame.
278
278
 
@@ -287,6 +287,20 @@ The sample explains the main interfaces:
287
287
  - `WebReviewKitAdapter`: core CRUD contract.
288
288
  - `ReviewShellAdapter`: React shell wiring for source labels, write modes, status updates, and delete actions.
289
289
 
290
+ ### External Links
291
+
292
+ `ReviewItem.externalLinks` can carry multiple external issue/sheet links. Each link should include `label` and `url`, with optional `title` and `icon`.
293
+
294
+ If `externalLinks` is omitted, the shell still falls back to `externalIssueUrl` and renders it as a single `Remote` action. Use `externalLinks` when a host needs separate sheet, issue, preview, or admin links for the same QA item.
295
+
296
+ ### Attachments and Capture
297
+
298
+ `ReviewItem.attachments` stores uploaded QA attachment metadata. Use `ReviewShellAdapter.uploadAttachment` to upload pasted files or iframe captures before adding the returned `ReviewAttachment` to an item.
299
+
300
+ The upload method receives a browser `File | Blob` plus optional `kind`, `name`, `mime`, `item`, and `metadata`, then returns `url`, `name`, `mime`, `size`, and optional storage metadata. If `uploadAttachment` is omitted and a draft contains attachments, submit fails with adapter feedback instead of silently dropping files.
301
+
302
+ Iframe captures are emitted as WebP when the DOM renderer succeeds and fall back to SVG only when raster capture is unavailable. Capture is based on same-origin iframe DOM access; it works on `localhost` and same-origin review hosts and is not HTTPS-only.
303
+
290
304
  Private keys, admin credentials, canonical numbering, and permission checks should stay in your backend, not in browser code.
291
305
 
292
306
  ## Environment
@@ -338,7 +352,7 @@ Open `http://127.0.0.1:5177/review/`.
338
352
 
339
353
  Fixture pages:
340
354
 
341
- - `/`: note, area, and DOM marker creation
355
+ - `/`: area and DOM marker creation
342
356
  - `/components/`: controls and panel spacing
343
357
  - `/long-form/`: scroll and anchor restore
344
358
 
@@ -348,6 +362,7 @@ Package repo:
348
362
 
349
363
  ```bash
350
364
  pnpm typecheck
365
+ pnpm test
351
366
  pnpm build
352
367
  pnpm typecheck:dev
353
368
  pnpm build:dev
@@ -363,6 +378,6 @@ pnpm build
363
378
  Manual smoke:
364
379
 
365
380
  1. Open `/review`.
366
- 2. Create local note, DOM marker, and area marker.
381
+ 2. Create local DOM marker and area marker.
367
382
  3. If Supabase is enabled, submit a local item to remote.
368
383
  4. Confirm local draft removal, remote list display, status update, delete, and deep-link restore.
@@ -0,0 +1,175 @@
1
+ # 릴리즈 노트: 0.8.0
2
+
3
+ 0.7.3 이후 QA 작성, source inspection, iframe capture, dev review fixture를 함께 보강한 minor release.
4
+
5
+ 비교 기준: `0.7.3`
6
+ 검토 기준: `main` release candidate
7
+
8
+ ## 주요 변경
9
+
10
+ - QA item에 여러 외부 링크를 표시할 수 있는 `externalLinks` 필드를 추가했다.
11
+ - QA DOM/area composer에서 paste attachment와 iframe viewport capture를 같은 attachment queue로 다룬다.
12
+ - `ReviewShellAdapter.uploadAttachment`와 `ReviewItem.attachments` contract를 추가했다.
13
+ - point-only Note QA를 제거하고 `ReviewItem.kind`를 `dom | area`로 정리했다.
14
+ - Vitest adapter contract suite를 추가해 local/Supabase adapter의 CRUD, 필터, legacy row 처리를 검증한다.
15
+ - iframe viewport capture는 WebP를 우선 생성하고, raster capture가 실패하면 SVG attachment로 fallback한다.
16
+ - source locator가 TSX/JSX AST를 읽어 `data-wrk-source-component`를 주입한다.
17
+ - Source Tree가 function component의 parent JSX call site를 `used in`으로 표시하고 바로 열 수 있다.
18
+ - Option source inspector의 후보 매칭을 DOM path, component stack, QA target, text, 반복 index 기준으로 개선했다.
19
+ - Source Tree가 component hierarchy를 더 잘 보여주도록 root 수집과 기본 표시 depth를 보정했다.
20
+ - dev review fixture를 page/section/card/component 단위로 나눠 Source Tree와 후보 매칭을 검증하기 쉽게 만들었다.
21
+ - Option source selection 중 Figma image overlay hit layer가 target hit-test를 가로채지 않도록 pointer lock을 분리했다.
22
+ - icon-only control에 hover/focus tooltip을 추가하고 Settings에서 표시 여부를 끌 수 있게 했다.
23
+ - keyboard shortcut matching이 한글 입력 상태의 물리 키를 함께 인식한다.
24
+ - DF logo help modal에 shortcut list를 추가하고 prompt/help modal shell을 공통 component로 정리했다.
25
+
26
+ ## 변경
27
+
28
+ ### QA External Links
29
+
30
+ `ReviewItem.externalLinks`를 추가해 한 QA item에서 여러 remote issue, sheet, admin URL을 표시할 수 있게 했다.
31
+
32
+ - 각 link는 `label`, `url`, optional `title`, optional `icon`을 가진다.
33
+ - QA card는 `externalLinks`를 우선 렌더링한다.
34
+ - 기존 `externalIssueUrl`만 있는 item은 `Remote` link로 fallback 렌더링한다.
35
+ - remote submit 후 local draft의 외부 링크 필드는 중복되지 않도록 정리한다.
36
+
37
+ ### Attachments and Capture
38
+
39
+ QA draft에 attachment queue를 추가했다.
40
+
41
+ - image paste는 draft attachment로 들어가고, 제출 전에 adapter `uploadAttachment`로 업로드된다.
42
+ - DOM/area draft는 attachment만 있어도 제출할 수 있다.
43
+ - `ReviewAttachment`는 `url`, `name`, `mime`, `size`, optional `kind`, `metadata`를 저장한다.
44
+ - `ReviewAttachmentUploadInput`은 browser `File | Blob`과 draft item context를 함께 넘긴다.
45
+ - iframe capture는 현재 viewport, marker, selection을 합성한 attachment를 만든다.
46
+ - capture metadata에는 route, viewport, scroll, marker, selection, timestamp, renderer 정보가 들어간다.
47
+ - capture는 same-origin iframe DOM 접근이 가능한 환경에서 동작한다. HTTPS 전용 기능은 아니다.
48
+
49
+ ### Adapter Contract and Note Removal
50
+
51
+ QA item kind를 DOM/area 중심으로 정리했다.
52
+
53
+ - `ReviewItemKind`는 `dom | area`만 허용한다.
54
+ - `ReviewMode`는 `idle | element | area`만 남긴다.
55
+ - point-only Note QA toolbar, shortcut, draft layer, popover style을 제거했다.
56
+ - 기존 DOM QA는 `kind: 'dom'`으로 저장된다.
57
+ - local/Supabase adapter는 legacy `note` row를 QA list에 노출하지 않는다.
58
+ - legacy `capture` row는 local adapter에서 `area`로 정규화한다.
59
+
60
+ Adapter 회귀를 줄이기 위해 Vitest를 추가했다.
61
+
62
+ - `pnpm test`는 `vitest run`을 실행한다.
63
+ - `pnpm test:watch`는 watch 모드로 Vitest를 실행한다.
64
+ - `src/adapters/adapter.contract.test.ts`에서 local adapter와 Supabase adapter mock을 같은 contract로 검증한다.
65
+ - contract는 `create/list/get/update/remove`, route/status filter, attachments, externalLinks, assignee/status 보존, legacy `note` 필터링을 확인한다.
66
+
67
+ ### Capture Format
68
+
69
+ capture output은 용량을 줄이기 위해 WebP를 우선 사용한다.
70
+
71
+ - `html2canvas`로 iframe DOM을 rasterize한 뒤 `image/webp`로 인코딩한다.
72
+ - 실패하면 기존 SVG clone을 canvas에 그려 WebP로 다시 시도한다.
73
+ - 두 raster path가 모두 실패할 때만 `image/svg+xml` attachment로 저장한다.
74
+ - WebP metadata에는 `captureRenderer`와 `captureScale`이 기록된다.
75
+
76
+ ### Source Locator and Source Tree
77
+
78
+ source opening의 실제 후보 품질을 높였다.
79
+
80
+ - Vite `reviewSourceLocator()`가 TypeScript가 있는 host에서 TSX/JSX를 파싱한다.
81
+ - intrinsic JSX node에 component 이름을 `data-wrk-source-component`로 주입한다.
82
+ - function component render path에는 parent JSX call site를 `data-wrk-source-parent-*` hint로 전달한다.
83
+ - Source Tree는 nested source roots를 flat root로 중복 수집하지 않고 parent/child hierarchy로 보여준다.
84
+ - Source Tree row는 parent usage가 있으면 `used in` meta와 usage source open action을 보여준다.
85
+ - 기본 표시 depth를 늘려 dev fixture처럼 page/section/card로 나뉜 구조가 바로 보인다.
86
+ - `includePlacer: false`일 때 Placer 계열 primitive는 Source Tree와 candidate list에서 숨긴다.
87
+
88
+ ### Source Candidate Matching
89
+
90
+ Option source inspector 후보 정렬을 보강했다.
91
+
92
+ - 후보 dedupe 기준을 file-only에서 source identity 기준으로 바꿨다.
93
+ - target element, QA data attribute, component stack, text match, section hint, line/column을 함께 점수화한다.
94
+ - 같은 component가 반복되는 카드/리스트는 `#1/3`, `#2/3`처럼 반복 index를 표시한다.
95
+ - candidate popover에는 `23:9 · component · target · qa · text · #1/3`처럼 선택 이유를 노출한다.
96
+ - file별 후보는 최대 3개, 전체 후보는 최대 8개까지 보여준다.
97
+
98
+ ### Figma Overlay Interaction
99
+
100
+ Option source selection과 Figma overlay의 hit-test 충돌을 막았다.
101
+
102
+ - 기존 element review mode의 Figma pointer lock은 유지한다.
103
+ - Option source selection용 pointer lock을 별도 style id로 분리했다.
104
+ - lock 대상에 host helper selector와 package Figma image overlay selector를 함께 포함했다.
105
+ - Option 키를 누른 동안 overlay image가 source target hit-test를 가로채지 않고, 키를 떼면 기존 overlay 조작이 복구된다.
106
+
107
+ ### Icon Tooltips
108
+
109
+ 아이콘만 있는 주요 control에 커스텀 tooltip을 추가했다.
110
+
111
+ - topbar action, overlay toggle, side rail, QA card action, Source Tree action, Figma image action에 기능명을 표시한다.
112
+ - tooltip은 `hover`와 `focus-visible`에서 표시되어 마우스와 키보드 탐색 모두에서 확인할 수 있다.
113
+ - Settings의 Tooltips 옵션으로 custom tooltip과 해당 control의 native `title` tooltip을 함께 끌 수 있다.
114
+ - tooltip 표시 여부는 `df-review-tooltips-enabled` localStorage key에 browser-local UI preference로 저장된다.
115
+
116
+ ### Shortcuts and Help
117
+
118
+ 단축키 접근성을 정리했다.
119
+
120
+ - `G/F/R/E/A` shell shortcut은 한글 입력 상태에서도 `ㅎ/ㄹ/ㄱ/ㄷ/ㅁ`로 동작한다.
121
+ - `Shift+Q`와 Figma dev overlay `Shift+F`도 같은 hotkey matcher를 사용한다.
122
+ - side rail 순서를 Figma Images, QA, Component List로 정리하고 `Shift+1/2/3` panel shortcut을 추가했다.
123
+ - DF logo로 여는 help modal에 shortcut list를 추가했다.
124
+ - 기존 설명글은 제거하고 header에 package version만 표시한다.
125
+ - help/prompt modal은 max-height를 제한하고 header 아래 body만 내부 스크롤되게 정리했다.
126
+ - help/prompt 계열 modal은 공통 `ReviewModal` component가 wrapper를 담당하고 contents는 children으로 받는다.
127
+
128
+ ### Dev Review Fixture
129
+
130
+ `pnpm dev:review` fixture를 release 검증용으로 더 현실적으로 나눴다.
131
+
132
+ - page routing, nav, home, components, long-form fixture를 분리했다.
133
+ - home fixture는 hero/action list/smoke grid/card 계층을 가진다.
134
+ - components fixture는 controls, metrics panel, state preview grid로 나뉜다.
135
+ - long-form fixture는 scroll, anchor restore, Source Tree depth 확인에 사용한다.
136
+
137
+ ## Host 영향
138
+
139
+ - attachment paste/capture를 저장하려면 host `ReviewShellAdapter`에 `uploadAttachment`를 구현해야 한다.
140
+ - `uploadAttachment`가 없는 adapter에서 attachment가 포함된 draft를 제출하면 저장하지 않고 error feedback을 표시한다.
141
+ - `note` kind를 저장하거나 반환하던 host adapter는 `dom` 또는 `area`로 매핑해야 한다.
142
+ - capture는 browser package dependency로 `html2canvas`를 사용한다.
143
+ - source locator component/parent usage hint는 TypeScript가 host dependency로 있을 때만 동작한다. 없으면 기존 file/line/column hint만 사용한다.
144
+ - source hints는 DOM에 source path와 component 이름을 쓰므로 dev/review build에서만 켜야 한다.
145
+ - 기존 `externalIssueUrl`만 쓰는 host는 계속 동작한다. 여러 링크가 필요할 때만 `externalLinks`를 추가하면 된다.
146
+ - Figma image overlay를 쓰는 host는 Option source selection 중 overlay drag가 잠깐 비활성화된다.
147
+
148
+ ## 문서
149
+
150
+ - README 최신 release note 링크를 0.8.0으로 갱신했다.
151
+ - docs index에 0.8.0 release note와 0.7.3 patch note를 정리했다.
152
+ - installation guide에 external links, attachment upload, WebP capture contract를 정리했다.
153
+ - adapter boundary 문서에 QA attachment, item kind, contract test, Figma image store 책임 분리를 반영했다.
154
+ - Vitest adapter contract 문서를 추가했다.
155
+ - Figma overlay 문서에 package image overlay selector와 source selection pointer lock behavior를 추가했다.
156
+
157
+ ## 검증
158
+
159
+ 아래 명령을 확인했다.
160
+
161
+ - `pnpm typecheck`
162
+ - `pnpm test`
163
+ - `pnpm typecheck:dev`
164
+ - `pnpm build`
165
+ - `pnpm build:dev`
166
+
167
+ 수동 확인:
168
+
169
+ - iframe fake Figma image overlay에서 Option down/up hit-test가 overlay -> target -> overlay 순서로 바뀌는지 확인했다.
170
+ - source candidate 반복 카드가 `#1/3`, `#2/3`, `#3/3`으로 구분되는지 확인했다.
171
+ - Source Tree가 `/`, `/components/`, `/long-form/` fixture에서 depth 있는 hierarchy와 `used in` parent usage를 보여주는지 확인했다.
172
+ - viewport capture가 WebP attachment와 renderer metadata를 생성하는지 확인했다.
173
+ - icon-only control hover 시 tooltip이 표시되고 Settings에서 끄면 사라지는지 확인했다.
174
+ - 한글 키 synthetic event로 shell shortcut이 처리되는지 확인했다.
175
+ - DF logo help modal에서 shortcut list 13개와 modal layout을 확인했다.