@ingcreators/annot-product-docs 0.2.0 → 0.4.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/CHANGELOG.md CHANGED
@@ -1,5 +1,458 @@
1
1
  # @ingcreators/annot-product-docs
2
2
 
3
+ ## 0.4.0
4
+
5
+ ### Minor Changes
6
+
7
+ - b5d52f6: **Annotation palette composes onto the annotated PNG** — Phase 3c
8
+ of `docs/plans/living-spec-authoring-roadmap.md`. The Astro Image
9
+ Service's `renderAnnotatedScreen` now reads the Phase 3a yaml
10
+ `annotations[]` section and bakes the full visual palette
11
+ (rect / circle / arrow / text / callout / freehand / redact /
12
+ focusMask) onto the base PNG, layered underneath the existing
13
+ numbered-badge overlays.
14
+
15
+ ### New public surface — `@ingcreators/annot-product-docs`
16
+
17
+ `buildShapeAnnotationsFromYaml(annotations, boxed, dims) →
18
+ BboxAnnotation[]` maps each Phase 3a `AnnotationSpec` against the
19
+ page's `BoxedEntry[]` (from snapshot YAML or PNG XMP ElementTree)
20
+ and produces `BboxAnnotation` shapes the headless annotator's
21
+ `bboxAnnotationsToSvg` consumes. Per-variant resolution:
22
+ - **`rect`** — `match` / `coversElements[]` / `bbox`. `coversElements`
23
+ unions the per-element bboxes into one.
24
+ - **`circle`** — match-anchored circles centre on the element bbox
25
+ with radius defaulting to half the longer axis; `center` + `radius`
26
+ is the free-coord form.
27
+ - **`arrow`** — endpoints can be `{ match }` (centre-to-centre) or
28
+ `{ point }`.
29
+ - **`text`** — `anchor.position` (above / below / left / right /
30
+ center) offsets a centred / left- / right-anchored label by 8 px
31
+ outside the element bbox.
32
+ - **`callout`** — target = match-resolved or free-coord bbox; `at`
33
+ is the caption position.
34
+ - **`freehand`** — passes through verbatim.
35
+ - **`redact`** — style: `solid` renders as a filled rect (default
36
+ fill `#222222`, no stroke); `fill` / `stroke` overrides honoured.
37
+ - **`focusMask`** — cutout expands by `padding` (match-anchored);
38
+ outer rect collapses to the supplied image dims.
39
+
40
+ Intent mapping mirrors the existing badge path
41
+ (`required → error`, `action → warning`, others pass through).
42
+
43
+ Match resolution failures are silently skipped — the drift
44
+ detector (Phase 3d) surfaces them upstream so the build keeps
45
+ producing a useful PNG even when the snapshot has drifted.
46
+
47
+ ### Behaviour change — `renderAnnotatedScreen`
48
+
49
+ When `<Screen annotations="…">` resolves to a yaml carrying
50
+ `annotations[]`, the renderer composes shapes (underneath) + badges
51
+ (on top) into one SVG fragment via `svgFromBboxAnnotations`. The
52
+ cache key already includes the annotations-yaml source from
53
+ Phase 2b, so edits to the yaml bust the cached PNG without extra
54
+ bookkeeping. Pre-Phase-3 yaml files (no `annotations` key) parse
55
+ - render unchanged.
56
+
57
+ ### Compatibility
58
+
59
+ Additive. Existing callers that only use `overlays[]` see no
60
+ behaviour change. The new `buildShapeAnnotationsFromYaml` export
61
+ is opt-in.
62
+
63
+ - fa712fd: **Annotation palette drift + xlsx coverage (Phase 3d)** — closes
64
+ Phase 3 of `docs/plans/living-spec-authoring-roadmap.md`.
65
+
66
+ ### Drift detector — `yamlAnnotations` opt-in
67
+
68
+ `detectDrift` / `detectDriftFromYaml` / `detectDriftFromElementTree`
69
+ gain an optional `yamlAnnotations: readonly AnnotationSpec[]`
70
+ field. When set, the detector walks the match keys reachable from
71
+ each Phase 3a `AnnotationSpec` (via the new
72
+ `collectMatchKeysFromAnnotation(spec) → MatchKey[]` helper) and
73
+ runs them through the same match-cycle as overlays — emitting
74
+ `removed` / `renamed` / `role-changed` / `duplicated` findings
75
+ with the annotation `id` referenced in the message.
76
+
77
+ Free-coord variants (`bbox`-only rect / `point`-only arrow
78
+ endpoint / `at`-only text / `bbox`-only callout target /
79
+ freehand / `bbox`-only redact / `bbox`-only focusMask cutout)
80
+ contribute zero keys and pass through silently.
81
+
82
+ `annotations[]` IDs are NEVER referenced from `<AnnotCallout for>`
83
+ (overlays[] owns that contract), so no
84
+ `description-missing` / `description-orphan` findings fire for
85
+ this source.
86
+
87
+ ### Excel adapter — yaml-driven rows for migrated screens
88
+
89
+ `@ingcreators/annot-product-docs-xlsx`'s `extractFromParsed`
90
+ gains an optional `annotationsYamlByPath` context map. When a
91
+ `<Screen>` carries `annotations="…"` and the matching yaml is in
92
+ the map, the item-table rows are sourced from the yaml's
93
+ `overlays[]` (each row's body cross-referenced from
94
+ `screen.callouts` by id). `extractMdxFile` loads each
95
+ referenced yaml file from disk automatically — missing files are
96
+ a loud failure on the same "explicit reference, but file gone"
97
+ reasoning the Astro Image Service uses.
98
+
99
+ `annotations[]` entries in the yaml are deliberately NOT
100
+ surfaced as rows. The Astro Image Service composes them onto
101
+ the annotated PNG; the Excel adapter renders the resulting image
102
+ in the spreadsheet's picture column while the items table stays
103
+ scoped to overlays.
104
+
105
+ ### workflow-app dogfood
106
+
107
+ `examples/workflow-app/docs/books/operation-manual/OM-001-login.mdx`
108
+ migrates from inline `<Overlay>` to the
109
+ `<Screen annotations="./OM-001-login.annotations.yaml">` +
110
+ `<AnnotCallout for>` form. The companion yaml ships three
111
+ overlays plus three `annotations[]` entries exercising
112
+ `rect` + `arrow` + `focusMask` — the full Phase 3 palette
113
+ end-to-end through the workflow-app's docs build.
114
+
115
+ ### Compatibility
116
+
117
+ Additive. Existing drift callers see no behaviour change unless
118
+ they opt in to `yamlAnnotations`. Existing xlsx callers see no
119
+ behaviour change unless they migrate a screen to the yaml form;
120
+ inline-`<Overlay>` screens continue to drive rows as before.
121
+
122
+ - f09a6b1: **Annotation yaml `redact.style` accepts `mosaic` / `blur`** —
123
+ Phase 3f of `docs/plans/living-spec-authoring-roadmap.md`
124
+ (Phase 3 follow-up).
125
+
126
+ The Phase 3a parser shipped `redact.style: "solid"` only; mosaic
127
+ / blur were explicitly rejected with a "reserved for follow-up"
128
+ message. Phase 3f widens the enum to all three (`solid` /
129
+ `mosaic` / `blur`) so authoring tools and the Astro Image
130
+ Service can use the values end-to-end.
131
+
132
+ ### Parser behaviour
133
+
134
+ ```yaml
135
+ # Phase 3a: accepted
136
+ annotations:
137
+ - id: redact-1
138
+ kind: redact
139
+ bbox: { x: 0, y: 0, width: 100, height: 30 }
140
+ style: solid
141
+
142
+ # Phase 3f: NEW — both accepted
143
+ annotations:
144
+ - id: redact-2
145
+ kind: redact
146
+ match: { role: textbox, name: Reason }
147
+ style: mosaic
148
+ - id: redact-3
149
+ kind: redact
150
+ bbox: { x: 421, y: 269, width: 438, height: 40 }
151
+ style: blur
152
+ ```
153
+
154
+ Unknown style values still error with an updated message:
155
+ `redact.style must be one of "solid" / "mosaic" / "blur"`.
156
+
157
+ ### Render behaviour (transitional)
158
+
159
+ Between 3f (this PR) and 3g (Astro Image Service raster
160
+ pre-processing), `mosaic` and `blur` redact entries are
161
+ parser-accepted but the Image Service still routes them
162
+ through the SVG-fragment filled-rect path — so they LOOK
163
+ identical to `solid` until 3g lands. 3g wires the raster
164
+ pass that gives `mosaic` / `blur` their distinct visual
165
+ output.
166
+
167
+ ### New public type
168
+
169
+ `RedactAnnotationStyle` (the `"solid" | "mosaic" | "blur"` union)
170
+ is exported from `@ingcreators/annot-product-docs` so callers
171
+ can reference it directly.
172
+
173
+ ### Compatibility
174
+
175
+ Additive within v1. Pre-3f files (no redact entries, or
176
+ `style: solid` only) parse identically. Files authored with
177
+ `style: "mosaic" | "blur"` are rejected by the pre-3f parser
178
+ (loud failure pointing at the unsupported style) — consumers
179
+ on older `@ingcreators/annot-product-docs` upgrade to consume
180
+ the new files.
181
+
182
+ - 0d19345: **Astro Image Service bakes mosaic / blur redacts onto the base
183
+ PNG** — Phase 3g of `docs/plans/living-spec-authoring-roadmap.md`
184
+ (Phase 3 follow-up).
185
+
186
+ When a screen's `annotations[]` yaml carries
187
+ `redact { style: "mosaic" | "blur" }` entries, the renderer now
188
+ calls `burnRedactions` from `@ingcreators/annot-annotator` on
189
+ the base PNG before SVG-fragment composition. `style: solid`
190
+ redacts continue to flow through the existing SVG filled-rect
191
+ path — avoiding an unnecessary PNG round-trip for the common
192
+ solid case.
193
+
194
+ ### New public surface
195
+
196
+ `buildRasterRedactRegionsFromYaml(annotations, boxed) →
197
+ BboxRedactRegion[]` (exported from
198
+ `@ingcreators/annot-product-docs`) walks `annotations[]` for
199
+ raster-style redact entries, resolves each cutout to a bbox,
200
+ and emits regions ready for `burnRedactions`. Match-anchored
201
+ entries whose `match` doesn't resolve are skipped silently —
202
+ the drift detector (Phase 3d) surfaces them upstream so the
203
+ build keeps producing a useful PNG while the snapshot
204
+ catches up.
205
+
206
+ ### `renderAnnotatedScreen` flow
207
+
208
+ ```
209
+ load base PNG bytes
210
+
211
+ read element-tree bboxes
212
+
213
+ walk annotations[] → split into:
214
+ • raster redacts (mosaic / blur) → burnRedactions(base, regions)
215
+ • SVG annotations (rect / circle / arrow / text / callout
216
+ / freehand / solid-redact / focusMask / numberedBadge)
217
+
218
+ compose SVG fragments on top of the (possibly burned) base PNG
219
+
220
+ emit final PNG (flat or editable)
221
+ ```
222
+
223
+ ### `mapRedact` change
224
+
225
+ `mapRedact` in `mdx-annotations.ts` now returns `null` for
226
+ `style: "mosaic" | "blur"` so those entries don't double-bake
227
+ as a filled rect on top of the already-pixelated bitmap. Solid
228
+ redacts continue to produce a `BboxRectAnnotation` for the SVG
229
+ path.
230
+
231
+ ### `hadBoundingBoxes` semantics
232
+
233
+ The flag flips true when raster redacts resolved through bbox
234
+ data, even when no SVG annotations composed on top. This
235
+ matches the flag's intent ("we used the snapshot's bbox data
236
+ to produce a useful render") — a screen with only a mosaic
237
+ redact still benefited from the bbox tour.
238
+
239
+ ### Caching
240
+
241
+ The cache key already includes the annotations-yaml source
242
+ bytes (Phase 2b), so editing a yaml `style: mosaic` → `style:
243
+ blur` value busts the cached PNG without additional
244
+ bookkeeping.
245
+
246
+ ### Compatibility
247
+
248
+ Additive. Existing screens (no `annotations[]`, or
249
+ `annotations[]` with no raster-style redacts) render
250
+ byte-identical. mosaic / blur redacts that were parser-accepted
251
+ in 3f but rendered as solid rects now render as their proper
252
+ raster effect.
253
+
254
+ ### Patch Changes
255
+
256
+ - Updated dependencies [6124d59]
257
+ - Updated dependencies [0c7ac26]
258
+ - Updated dependencies [64dc6e8]
259
+ - Updated dependencies [691bec5]
260
+ - Updated dependencies [9697f27]
261
+ - Updated dependencies [266b05a]
262
+ - @ingcreators/annot-annotator@0.6.0
263
+ - @ingcreators/annot-core@0.3.0
264
+ - @ingcreators/annot-playwright@0.4.1
265
+
266
+ ## 0.3.0
267
+
268
+ ### Minor Changes
269
+
270
+ - 5778902: **MDX resolver moves home + auto-registers into the screenshot
271
+ patch** — Phase 2 of
272
+ `docs/plans/playwright-screenshot-fixture-relayer.md`. The
273
+ MDX-aware annotation pipeline that powers
274
+ `page.screenshot({ annot: { mdx: { id, path } } })` now lives in
275
+ `@ingcreators/annot-product-docs` (the package that already owns
276
+ MDX parsing + the `screen.capture()` fixture). Calls go through
277
+ annot-playwright's generic patch + the new
278
+ `annotSourceResolvers` hook registry, so consumers no longer
279
+ need an Astro peer dep for the dogfood tour pattern:
280
+
281
+ ```ts
282
+ // Was: @ingcreators/annot-product-docs-astro/playwright
283
+ import { test } from "@ingcreators/annot-product-docs";
284
+
285
+ test("docs tour", async ({ page }) => {
286
+ await page.goto(APP_URL);
287
+ await page.screenshot({
288
+ path: "shots/app-overview.png",
289
+ annot: {
290
+ mdx: { id: "app-overview", path: "src/content/docs/app/index.mdx" },
291
+ tags: { source: "docs-tour", screen: "app-overview" },
292
+ },
293
+ });
294
+ });
295
+ ```
296
+
297
+ The `annot: { mdx }` resolver fires in two stages:
298
+ 1. `prepare()` — calls `captureScreen(page, { id, mdxPath })`
299
+ internally, refreshing the MDX's `annot:snapshot` +
300
+ `annot:attributes` blocks against the live page BEFORE the
301
+ raw screenshot is taken.
302
+ 2. `resolveAnnotations(dims)` — reads the freshly-written
303
+ `annot:snapshot` block + `<Overlay match>` entries and
304
+ returns page-space `BboxNumberedBadgeAnnotation[]` which
305
+ annot-playwright merges with any caller-supplied
306
+ `annot.overlays` before rebasing onto the clipped image.
307
+
308
+ **New public surface**
309
+ - `resolveMdxAnnotations({ mdxPath, screenId, dims, cwd? })` —
310
+ pure-data resolver used by both the Playwright hook and the
311
+ Astro Image Service (Phase 4 of the relayer plan switches
312
+ product-docs-astro's `renderAnnotatedScreen` to consume it).
313
+ - `parseSnapshotBoxes(yaml)` / `buildBadgeAnnotations(overlays,
314
+ boxed, dims)` — exposed for callers that want to drive
315
+ annotation production from custom snapshot pipelines.
316
+ - `svgFromBboxAnnotations(annotations)` /
317
+ `svgFromBadges(badges)` / `emptyAnnotationsSvg()` — single-root
318
+ `<svg>` wrappers that the headless annotator's `annotationsSvg`
319
+ input expects.
320
+ - `BoxedEntry` type — parsed aria-snapshot YAML entry with
321
+ `[ref=…]` + `[box=…]` markers.
322
+
323
+ **Compatibility**
324
+ - Module augmentation extends annot-playwright's
325
+ `AnnotScreenshotOptions` with the `mdx?: { id, path }` field
326
+ via `declare module "@ingcreators/annot-playwright"`. Imports
327
+ from `@ingcreators/annot-product-docs/fixture` or the package
328
+ root register the resolver via side-effect.
329
+ - The existing
330
+ `@ingcreators/annot-product-docs-astro/playwright` subpath
331
+ continues to work unchanged (it ships its own duplicate
332
+ augmentation + patch — Phase 4 of the plan converts that subpath
333
+ into a deprecated re-export pointing here).
334
+ - `packages/docs-site/tests/docs/annot-app.spec.ts` — the
335
+ dogfood tour spec swaps `import { test } from
336
+ "@ingcreators/annot-product-docs-astro/playwright"` to
337
+ `"@ingcreators/annot-product-docs"`. PNG output stays
338
+ byte-identical (same patch → same composer → same encoder).
339
+
340
+ ### Patch Changes
341
+
342
+ - 96e7625: **`screen` fixture → `productDocs`, `capture` method → `sync`** —
343
+ Phase 3 of `docs/plans/playwright-screenshot-fixture-relayer.md`.
344
+ The fixture's old name reads like a Playwright built-in and
345
+ collides with `@testing-library/react`'s `screen`; `capture`
346
+ implies a screenshot but the method actually synchronizes MDX
347
+ comment blocks. The rename aligns with the established
348
+ `annotator` convention from `@ingcreators/annot-annotator`.
349
+
350
+ ```ts
351
+ // Was:
352
+ test("login", async ({ page, screen }) => {
353
+ await screen.capture({ id: "login", mdxPath: "..." });
354
+ });
355
+
356
+ // Now:
357
+ test("login", async ({ page, productDocs }) => {
358
+ await productDocs.sync({ id: "login", mdxPath: "..." });
359
+ });
360
+ ```
361
+
362
+ **Back-compat — old names keep working**:
363
+ - Fixture: `test.extend({ screen })` is preserved alongside
364
+ `productDocs`. Both expose the same `.sync()` method (the
365
+ deprecated `.capture()` method on `screen` aliases `.sync()`).
366
+ - Standalone helper: `captureScreen` re-exports the new
367
+ `syncProductDocs` implementation. `captureScreen ===
368
+ syncProductDocs` is true — reference-equal so callers that
369
+ identity-check the function across the rename boundary keep
370
+ working.
371
+ - Types: `Screen` aliases `ProductDocs`, `ScreenCaptureOptions`
372
+ aliases `ProductDocsSyncOptions`. Both flagged
373
+ `@deprecated` in JSDoc; deletion is scheduled for the
374
+ deprecation window noted in
375
+ `living-spec-authoring-roadmap.md` OQ-08.
376
+
377
+ **In-tree consumers migrated**:
378
+ - `packages/product-docs/src/cli.ts` — `annot docs sync` /
379
+ `annot docs lint --fix` call `syncProductDocs` directly. The
380
+ `init` scaffold's sample tour uses `productDocs.sync(...)`.
381
+ - `packages/product-docs-astro/src/playwright/fixture.ts` —
382
+ imports `syncProductDocs` (was `captureScreen`).
383
+ - `packages/docs-site/src/content/docs/product-docs/playwright-tour.mdx`
384
+ - `getting-started/product-docs.mdx` +
385
+ `api/product-docs.mdx` + `concepts.mdx` +
386
+ `recipes/living-product-docs.mdx` — all example snippets
387
+ updated to the new names.
388
+ - `packages/product-docs/README.md` — quickstart updated.
389
+
390
+ **Test of the back-compat surface** — a new
391
+ `fixture.test.ts` case asserts `captureScreen === syncProductDocs`
392
+ so the alias contract is enforced by CI; any future refactor
393
+ that breaks the reference equality fails the build.
394
+
395
+ - 85d40e6: **Docs + CLAUDE.md + plan archive** — Phase 5 (final) of
396
+ `docs/plans/_done/playwright-screenshot-fixture-relayer.md`.
397
+ Refreshes the doc surfaces, the operational CLAUDE.md notes,
398
+ and archives the relayer plan into `_done/`.
399
+
400
+ ## Doc surface updates
401
+ - `packages/docs-site/src/content/docs/product-docs/playwright-fixture.mdx`
402
+ — recommended import paths updated; new "Choosing your import"
403
+ section covers the `@ingcreators/annot-product-docs` (MDX)
404
+ vs. `@ingcreators/annot-playwright` (no MDX) split; companion
405
+ helpers section now imports from the canonical homes; codegen
406
+ workflow example swapped to `@ingcreators/annot-product-docs`.
407
+ - `packages/docs-site/src/content/docs/api/create-annotator.mdx`
408
+ — "From a Playwright test" example imports from the canonical
409
+ home; mentions the no-MDX alternative.
410
+ - `packages/playwright/README.md` — adds the
411
+ `page.screenshot({ annot: { … } })` (recommended) section
412
+ above the existing `annotator.annotateScreenshot(...)` flow;
413
+ documents the `annotSourceResolvers` extension hook + the
414
+ coordinate-rebase helpers.
415
+ - `packages/product-docs/README.md` — adds a `page.screenshot({
416
+ annot })` Playwright fixture section explaining the
417
+ productDocs.sync + MDX-resolver bundle.
418
+ - `packages/product-docs-astro/README.md` — replaces the
419
+ Playwright fixture section with a Migration note pointing at
420
+ the canonical homes; documents the `0.5.0` removal target.
421
+
422
+ ## CLAUDE.md monorepo layout
423
+ - `playwright/` entry now describes the canonical
424
+ `page.screenshot({ annot })` patch + `annotSourceResolvers`
425
+ extension hook. Version bumped to 0.4.0.
426
+ - `product-docs/` entry mentions the `productDocs` fixture
427
+ rename + the MDX-aware resolver registration via the hook
428
+ registry. Version bumped to 0.3.0.
429
+ - `product-docs-astro/` entry calls out the deprecated
430
+ `/playwright` re-export shim + 0.5.0 removal target.
431
+ `@playwright/test` no longer a peer dep. Version bumped to
432
+ 0.3.0.
433
+
434
+ ## Plan archive
435
+ - `docs/plans/playwright-screenshot-fixture-relayer.md` →
436
+ `docs/plans/_done/playwright-screenshot-fixture-relayer.md`.
437
+ Status header switched to `Done` with the four landing PRs
438
+ enumerated. Internal `./_done/...` link paths updated to
439
+ `./...` (the plan is itself inside `_done/` now).
440
+ - `docs/plans/README.md` — removed the active entry, added a
441
+ "Recently landed plans" row pointing at the archived plan
442
+ with a multi-phase summary covering all four landing PRs.
443
+ - `docs/plans/living-spec-authoring-roadmap.md` — three link
444
+ references updated to point at the archived path; the "How
445
+ this relates" line switched from "Already Draft" to "Landed
446
+ 2026-05-22 (PRs #962 / #963 / #964 / #966)".
447
+
448
+ No source code changes; verified via `pnpm -r typecheck`,
449
+ `pnpm test`, `pnpm lint` regardless to confirm the doc/comment
450
+ edits parse cleanly.
451
+
452
+ - Updated dependencies [f979374]
453
+ - Updated dependencies [85d40e6]
454
+ - @ingcreators/annot-playwright@0.4.0
455
+
3
456
  ## 0.2.0
4
457
 
5
458
  ### Minor Changes
package/README.md CHANGED
@@ -78,15 +78,50 @@ don't. The resolver re-finds each element on every run.
78
78
  // tests/docs/auth.spec.ts
79
79
  import { test } from "@ingcreators/annot-product-docs";
80
80
 
81
- test("login flow", async ({ page, screen }) => {
81
+ test("login flow", async ({ page, productDocs }) => {
82
82
  await page.goto("/login");
83
- await screen.capture({
83
+ await productDocs.sync({
84
84
  id: "login",
85
85
  mdxPath: "docs/books/example/SC-001-login.mdx",
86
86
  });
87
87
  });
88
88
  ```
89
89
 
90
+ ## `page.screenshot({ annot })` Playwright fixture
91
+
92
+ The package's `test` extends
93
+ [`@ingcreators/annot-playwright`](../playwright)'s test with the
94
+ `productDocs.sync(...)` fixture above AND registers an MDX-aware
95
+ resolver into the `annotSourceResolvers` registry. Pass
96
+ `annot: { mdx }` on `page.screenshot()` to bundle the
97
+ refresh-snapshot + take-screenshot + bake-overlays + write-PNG
98
+ sequence into one call — the same `<Screen id>` block in the
99
+ target MDX gets re-synced before the screenshot fires, and the
100
+ output PNG is re-editable in Annot Cloud:
101
+
102
+ ```ts
103
+ test("app overview", async ({ page }) => {
104
+ await page.goto("https://annot.work/app/");
105
+ await page.screenshot({
106
+ path: "public/app/shots/app-overview.png",
107
+ annot: {
108
+ mdx: { id: "app-overview", path: "src/content/docs/app/index.mdx" },
109
+ tags: { source: "docs-tour", capturedAt: new Date().toISOString() },
110
+ },
111
+ });
112
+ });
113
+ ```
114
+
115
+ Calls without `annot` (or with `annot: true` / `{}`) fall
116
+ through to vanilla Playwright byte-for-byte — codegen-emitted
117
+ calls keep working unedited. The generic `annot.overlays` /
118
+ `annot.tags` / `annot.editable` fields are handled by
119
+ annot-playwright; this package contributes the MDX-aware
120
+ `annot.mdx` field on top via the hook registry. See
121
+ [`annot.work/docs/product-docs/playwright-fixture/`](https://annot.work/docs/product-docs/playwright-fixture/)
122
+ for the compositional vocabulary, locator screenshot semantics,
123
+ and the codegen→hand-edit workflow.
124
+
90
125
  ## CLI
91
126
 
92
127
  ```sh