@ingcreators/annot-product-docs-astro 0.2.1 → 0.3.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,154 @@
1
1
  # @ingcreators/annot-product-docs-astro
2
2
 
3
+ ## 0.3.0
4
+
5
+ ### Minor Changes
6
+
7
+ - f5dc7cb: **`@ingcreators/annot-product-docs-astro/playwright` becomes a
8
+ deprecated re-export** — Phase 4 of
9
+ `docs/plans/playwright-screenshot-fixture-relayer.md`. The fixture
10
+ that originally lived here moved to `@ingcreators/annot-playwright`
11
+ (generic patch) + `@ingcreators/annot-product-docs` (MDX resolver)
12
+ in Phases 1–3 of the plan. Phase 4 deletes the duplicate code
13
+ from this package and converts the `/playwright` subpath into a
14
+ shim that re-exports the canonical surface so existing callers
15
+ keep compiling.
16
+
17
+ ```ts
18
+ // Was:
19
+ import { test } from "@ingcreators/annot-product-docs-astro/playwright";
20
+
21
+ // Now (recommended):
22
+ import { test } from "@ingcreators/annot-product-docs"; // with MDX
23
+ // or
24
+ import { test } from "@ingcreators/annot-playwright"; // without MDX
25
+ ```
26
+
27
+ The deprecated subpath emits a one-time
28
+ `process.emitWarning("DeprecationWarning", …)` at import time so
29
+ the migration prompt shows up in CI logs. **Reference equality
30
+ preserved** — `test`, `expect`, `patchScreenshot`,
31
+ `rebaseAnnotations`, `describeAnnotation` are reference-equal to
32
+ their canonical homes; a new
33
+ `packages/product-docs-astro/src/playwright/index.test.ts`
34
+ asserts this.
35
+
36
+ **Removal target**: `@ingcreators/annot-product-docs-astro@0.5.0`,
37
+ matching the OQ-2 decision (b) in the parent plan — visible
38
+ deprecation, known sunset.
39
+
40
+ ## render.ts switches to the canonical helpers
41
+
42
+ `renderAnnotatedScreen()` previously carried its own copies of
43
+ `resolveMdxAnnotations` / `parseSnapshotBoxes` /
44
+ `buildBadgeAnnotations` / `svgFromBadges` / `svgFromBboxAnnotations`
45
+ / `emptyAnnotationsSvg`. Phase 2 of the plan moved the canonical
46
+ home into `@ingcreators/annot-product-docs`; this PR deletes the
47
+ duplicates from `product-docs-astro/render.ts` and consumes the
48
+ ones in product-docs going forward.
49
+
50
+ `resolveMdxAnnotations` + `svgFromBboxAnnotations` are re-exported
51
+ from `render.ts` for one deprecation cycle so existing callers
52
+ that imported them from `@ingcreators/annot-product-docs-astro`
53
+ keep compiling. `parseSnapshotBoxes` is dropped from the public
54
+ surface — new code should import it from
55
+ `@ingcreators/annot-product-docs` directly.
56
+
57
+ ## peerDependencies cleanup
58
+
59
+ `@playwright/test` is removed from `peerDependencies` (and from
60
+ `peerDependenciesMeta`). The package no longer has a Playwright
61
+ relationship to advertise — the `/playwright` subpath is purely
62
+ a re-export shim, and its types flow through the
63
+ `@ingcreators/annot-product-docs` workspace dep transitively.
64
+ This matches the OQ-3 decision (b) in the parent plan.
65
+
66
+ ## Verified
67
+ - `pnpm -r typecheck` — 20 packages, all pass.
68
+ - `pnpm test` — 252 files, 3641 tests, 0 failures. New
69
+ `playwright/index.test.ts` (5 reference-equality assertions)
70
+ passes; the deprecation `process.emitWarning` fires on
71
+ import (visible in vitest output) but does not break anything.
72
+ - `pnpm lint` — exit 0; 29 pre-existing warnings unchanged.
73
+ - `pnpm --filter @ingcreators/annot-product-docs-astro build` —
74
+ emits `dist/index.js` (3.96 kB / 1.71 kB gzip) +
75
+ `dist/playwright/index.js` (0.85 kB / 0.39 kB gzip) — the
76
+ shrunken subpath bundle reflects the re-export-only shape.
77
+
78
+ ### Patch Changes
79
+
80
+ - 85d40e6: **Docs + CLAUDE.md + plan archive** — Phase 5 (final) of
81
+ `docs/plans/_done/playwright-screenshot-fixture-relayer.md`.
82
+ Refreshes the doc surfaces, the operational CLAUDE.md notes,
83
+ and archives the relayer plan into `_done/`.
84
+
85
+ ## Doc surface updates
86
+ - `packages/docs-site/src/content/docs/product-docs/playwright-fixture.mdx`
87
+ — recommended import paths updated; new "Choosing your import"
88
+ section covers the `@ingcreators/annot-product-docs` (MDX)
89
+ vs. `@ingcreators/annot-playwright` (no MDX) split; companion
90
+ helpers section now imports from the canonical homes; codegen
91
+ workflow example swapped to `@ingcreators/annot-product-docs`.
92
+ - `packages/docs-site/src/content/docs/api/create-annotator.mdx`
93
+ — "From a Playwright test" example imports from the canonical
94
+ home; mentions the no-MDX alternative.
95
+ - `packages/playwright/README.md` — adds the
96
+ `page.screenshot({ annot: { … } })` (recommended) section
97
+ above the existing `annotator.annotateScreenshot(...)` flow;
98
+ documents the `annotSourceResolvers` extension hook + the
99
+ coordinate-rebase helpers.
100
+ - `packages/product-docs/README.md` — adds a `page.screenshot({
101
+ annot })` Playwright fixture section explaining the
102
+ productDocs.sync + MDX-resolver bundle.
103
+ - `packages/product-docs-astro/README.md` — replaces the
104
+ Playwright fixture section with a Migration note pointing at
105
+ the canonical homes; documents the `0.5.0` removal target.
106
+
107
+ ## CLAUDE.md monorepo layout
108
+ - `playwright/` entry now describes the canonical
109
+ `page.screenshot({ annot })` patch + `annotSourceResolvers`
110
+ extension hook. Version bumped to 0.4.0.
111
+ - `product-docs/` entry mentions the `productDocs` fixture
112
+ rename + the MDX-aware resolver registration via the hook
113
+ registry. Version bumped to 0.3.0.
114
+ - `product-docs-astro/` entry calls out the deprecated
115
+ `/playwright` re-export shim + 0.5.0 removal target.
116
+ `@playwright/test` no longer a peer dep. Version bumped to
117
+ 0.3.0.
118
+
119
+ ## Plan archive
120
+ - `docs/plans/playwright-screenshot-fixture-relayer.md` →
121
+ `docs/plans/_done/playwright-screenshot-fixture-relayer.md`.
122
+ Status header switched to `Done` with the four landing PRs
123
+ enumerated. Internal `./_done/...` link paths updated to
124
+ `./...` (the plan is itself inside `_done/` now).
125
+ - `docs/plans/README.md` — removed the active entry, added a
126
+ "Recently landed plans" row pointing at the archived plan
127
+ with a multi-phase summary covering all four landing PRs.
128
+ - `docs/plans/living-spec-authoring-roadmap.md` — three link
129
+ references updated to point at the archived path; the "How
130
+ this relates" line switched from "Already Draft" to "Landed
131
+ 2026-05-22 (PRs #962 / #963 / #964 / #966)".
132
+
133
+ No source code changes; verified via `pnpm -r typecheck`,
134
+ `pnpm test`, `pnpm lint` regardless to confirm the doc/comment
135
+ edits parse cleanly.
136
+
137
+ - Updated dependencies [f979374]
138
+ - Updated dependencies [5778902]
139
+ - Updated dependencies [96e7625]
140
+ - Updated dependencies [85d40e6]
141
+ - @ingcreators/annot-playwright@0.4.0
142
+ - @ingcreators/annot-product-docs@0.3.0
143
+
144
+ ## 0.2.2
145
+
146
+ ### Patch Changes
147
+
148
+ - Updated dependencies [f485646]
149
+ - @ingcreators/annot-core@0.2.1
150
+ - @ingcreators/annot-annotator@0.5.0
151
+
3
152
  ## 0.2.1
4
153
 
5
154
  ### Patch Changes
package/README.md CHANGED
@@ -89,41 +89,30 @@ When the stored snapshot lacks `[box=x,y,w,h]` markers (no
89
89
  Playwright tour has run yet), the function returns the base
90
90
  PNG verbatim with `hadBoundingBoxes: false`.
91
91
 
92
- ## Playwright fixture (`/playwright` subpath)
92
+ ## Migration — Playwright fixture moved
93
93
 
94
- For Playwright specs producing screenshots of a documented
95
- screen, the `./playwright` subpath ships an extended `test`
96
- fixture that intercepts `page.screenshot()` and
97
- `locator.screenshot()`. Calls carrying a compositional
98
- `annot: { mdx | overlays | tags | editable }` option get a
99
- one-line capture pipeline that replaces the previous
100
- `page.screenshot` + `screen.capture` + `renderAnnotatedScreen`
101
- + `writeFile` quartet:
94
+ > The Playwright fixture that previously lived at
95
+ > `@ingcreators/annot-product-docs-astro/playwright` moved out
96
+ > of this package as part of
97
+ > [`docs/plans/_done/playwright-screenshot-fixture-relayer.md`](https://github.com/ingcreators/annot/blob/main/docs/plans/_done/playwright-screenshot-fixture-relayer.md)
98
+ > (Phases 1–4). Code authoring a Playwright spec should pick
99
+ > one of the two canonical homes:
102
100
 
103
- ```ts
104
- import { test } from "@ingcreators/annot-product-docs-astro/playwright";
105
-
106
- test("app overview", async ({ page }) => {
107
- await page.goto("https://annot.work/app/");
108
- await page.screenshot({
109
- path: "public/app/shots/app-overview.png",
110
- annot: {
111
- mdx: { id: "app-overview", path: "src/content/docs/app/index.mdx" },
112
- tags: { source: "docs-tour", capturedAt: new Date().toISOString() },
113
- },
114
- });
115
- });
116
- ```
117
-
118
- Calls without `annot` (or with `annot: true` / `{}`) fall
119
- through to vanilla Playwright byte-for-byte — codegen-emitted
120
- calls keep working unedited. See the docs at
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
-
125
- `@playwright/test` is an **optional peer** — Astro-only users
126
- don't need to install it.
101
+ | Use case | Import |
102
+ |---|---|
103
+ | Living-product-docs tour (MDX-linked `annot.mdx`) | `import { test } from "@ingcreators/annot-product-docs";` |
104
+ | VRT specs / marketing screenshots / AI agent flows (no MDX) | `import { test } from "@ingcreators/annot-playwright";` |
105
+
106
+ The `@ingcreators/annot-product-docs-astro/playwright` subpath
107
+ remains as a deprecated re-export so existing callers still
108
+ compile; it emits a `DeprecationWarning` at import time and
109
+ will be removed in `@ingcreators/annot-product-docs-astro@0.5.0`.
110
+ `@playwright/test` is no longer a peer dep of this package —
111
+ the canonical homes carry the relationship now.
112
+
113
+ `renderAnnotatedScreen` (the Astro Image Service) lives on as
114
+ the build-time renderer for the `<Screen>` block; this section
115
+ isn't going anywhere.
127
116
 
128
117
  ## Tier
129
118
 
package/dist/index.d.ts CHANGED
@@ -4,4 +4,4 @@ export type { GraphDirection, GraphEdge, Match, OverlayIntent, ScreenListEntry,
4
4
  export type { ProductDocsIntegrationOptions } from './integration.js';
5
5
  export { productDocsIntegration } from './integration.js';
6
6
  export type { RenderAnnotatedScreenOptions, RenderResult, } from './render.js';
7
- export { parseSnapshotBoxes, renderAnnotatedScreen, resolveMdxAnnotations, svgFromBboxAnnotations, } from './render.js';
7
+ export { renderAnnotatedScreen, resolveMdxAnnotations, svgFromBboxAnnotations } from './render.js';
package/dist/index.js CHANGED
@@ -1,18 +1,124 @@
1
- import { a as e, c as t, i as n, n as r, o as i, r as a, s as o, t as s } from "./render-BSQOeqPB.js";
1
+ import { createHash as e } from "node:crypto";
2
+ import { mkdir as t, readFile as n, writeFile as r } from "node:fs/promises";
3
+ import { dirname as i, isAbsolute as a, join as o, resolve as s } from "node:path";
4
+ import { createAnnotator as c } from "@ingcreators/annot-annotator";
5
+ import { buildBadgeAnnotations as l, emptyAnnotationsSvg as u, parseMdxFile as d, parseSnapshotBoxes as f, resolveMdxAnnotations as p, svgFromBadges as m, svgFromBboxAnnotations as h } from "@ingcreators/annot-product-docs";
6
+ //#region src/cache.ts
7
+ var g = 1;
8
+ function _(t) {
9
+ let n = e("sha256");
10
+ return n.update("v1\n"), n.update(t.mdxSource), n.update("\0"), n.update(t.screenId), t.editable && n.update("\0editable"), n.digest("hex");
11
+ }
12
+ function v(e) {
13
+ return {
14
+ dir: e,
15
+ async get(t) {
16
+ try {
17
+ let r = await n(o(e, `${t}.png`));
18
+ return new Uint8Array(r.buffer, r.byteOffset, r.byteLength);
19
+ } catch (e) {
20
+ if (e.code === "ENOENT") return null;
21
+ throw e;
22
+ }
23
+ },
24
+ async set(n, a) {
25
+ let s = o(e, `${n}.png`);
26
+ await t(i(s), { recursive: !0 }), await r(s, a);
27
+ }
28
+ };
29
+ }
30
+ function y() {
31
+ let e = /* @__PURE__ */ new Map();
32
+ return {
33
+ dir: ":memory:",
34
+ async get(t) {
35
+ return e.get(t) ?? null;
36
+ },
37
+ async set(t, n) {
38
+ e.set(t, n);
39
+ }
40
+ };
41
+ }
42
+ //#endregion
2
43
  //#region src/integration.ts
3
- var c = "@ingcreators/annot-product-docs-astro";
4
- function l(e = {}) {
44
+ var b = "@ingcreators/annot-product-docs-astro";
45
+ function x(e = {}) {
5
46
  let t = {
6
47
  contentDir: e.contentDir ?? "docs",
7
48
  configPath: e.configPath ?? "annot-docs.config.ts",
8
49
  verbose: e.verbose ?? !1
9
50
  };
10
51
  return {
11
- name: c,
52
+ name: b,
12
53
  hooks: { "astro:config:setup": ({ logger: e }) => {
13
- t.verbose && e.info(`${c} installed — contentDir=${t.contentDir} configPath=${t.configPath}`);
54
+ t.verbose && e.info(`${b} installed — contentDir=${t.contentDir} configPath=${t.configPath}`);
14
55
  } }
15
56
  };
16
57
  }
17
58
  //#endregion
18
- export { e as RENDER_PIPELINE_VERSION, i as cacheKey, o as createFileCache, t as createMemoryCache, s as parseSnapshotBoxes, l as productDocsIntegration, r as renderAnnotatedScreen, a as resolveMdxAnnotations, n as svgFromBboxAnnotations };
59
+ //#region src/render.ts
60
+ async function S(e) {
61
+ let t = e.cwd ?? process.cwd(), n = a(e.mdxPath) ? e.mdxPath : s(t, e.mdxPath), r = await d(n);
62
+ if (!r) throw Error(`renderAnnotatedScreen: ${e.mdxPath} has no \`annot:\` frontmatter — cannot render.`);
63
+ let o = r.screens.find((t) => t.id === e.screenId);
64
+ if (!o) throw Error(`renderAnnotatedScreen: ${e.mdxPath} has no <Screen id="${e.screenId}"> block.`);
65
+ let p = C(e.editable), h = _({
66
+ mdxSource: r.source,
67
+ screenId: e.screenId,
68
+ editable: p !== null
69
+ });
70
+ if (e.cache) {
71
+ let t = await e.cache.get(h);
72
+ if (t) return {
73
+ bytes: t,
74
+ fromCache: !0,
75
+ hadBoundingBoxes: !0
76
+ };
77
+ }
78
+ let g = e.basePngBytes ?? await w(r, o.src, i(n)), v = T(g), y = f(r.commentBlocks.snapshot ?? ""), b = l(o.overlays, y, v), x, S;
79
+ if (b.length === 0) if (S = !1, p !== null) {
80
+ let e = `data:image/png;base64,${Buffer.from(g).toString("base64")}`;
81
+ x = c({ loadSystemFonts: !0 }).toEditablePng({
82
+ originalDataUrl: e,
83
+ annotationsSvg: u(),
84
+ width: v.width,
85
+ height: v.height,
86
+ tags: p.tags
87
+ });
88
+ } else x = g;
89
+ else {
90
+ let e = `data:image/png;base64,${Buffer.from(g).toString("base64")}`, t = m(b), n = c({ loadSystemFonts: !0 }), r = {
91
+ originalDataUrl: e,
92
+ annotationsSvg: t,
93
+ width: v.width,
94
+ height: v.height
95
+ };
96
+ x = p === null ? n.toPng(r) : n.toEditablePng({
97
+ ...r,
98
+ tags: p.tags
99
+ }), S = !0;
100
+ }
101
+ return e.cache && await e.cache.set(h, x), {
102
+ bytes: x,
103
+ fromCache: !1,
104
+ hadBoundingBoxes: S
105
+ };
106
+ }
107
+ function C(e) {
108
+ return e ? e === !0 ? {} : e : null;
109
+ }
110
+ async function w(e, t, r) {
111
+ if (!t) throw Error("renderAnnotatedScreen: <Screen src=...> is required — cannot render without a base image.");
112
+ let i = await n(a(t) ? t : s(r, t));
113
+ return new Uint8Array(i.buffer, i.byteOffset, i.byteLength);
114
+ }
115
+ function T(e) {
116
+ if (e.length < 24) throw Error("renderAnnotatedScreen: base PNG too short to contain IHDR.");
117
+ let t = new DataView(e.buffer, e.byteOffset, e.byteLength);
118
+ return {
119
+ width: t.getUint32(16, !1),
120
+ height: t.getUint32(20, !1)
121
+ };
122
+ }
123
+ //#endregion
124
+ export { g as RENDER_PIPELINE_VERSION, _ as cacheKey, v as createFileCache, y as createMemoryCache, x as productDocsIntegration, S as renderAnnotatedScreen, p as resolveMdxAnnotations, h as svgFromBboxAnnotations };
@@ -1,5 +1,36 @@
1
+ /**
2
+ * @deprecated Import `AnnotScreenshotOptions` from
3
+ * `@ingcreators/annot-playwright` — that package owns the
4
+ * canonical interface; `@ingcreators/annot-product-docs` augments
5
+ * it with `mdx`. Re-exporting here is back-compat only and will
6
+ * be removed in `@ingcreators/annot-product-docs-astro@0.5.0`.
7
+ */
8
+ /**
9
+ * @deprecated Import the rebase helpers (`Clip`, `RebaseResult`,
10
+ * `describeAnnotation`, `rebaseAnnotations`) from
11
+ * `@ingcreators/annot-playwright`. Re-exporting here is
12
+ * back-compat only and will be removed in
13
+ * `@ingcreators/annot-product-docs-astro@0.5.0`.
14
+ */
15
+ export type { AnnotScreenshotOptions, Clip, RebaseResult } from '@ingcreators/annot-playwright';
16
+ /**
17
+ * @deprecated Import `patchScreenshot` from
18
+ * `@ingcreators/annot-playwright`. Re-exporting here is
19
+ * back-compat only and will be removed in
20
+ * `@ingcreators/annot-product-docs-astro@0.5.0`.
21
+ */
22
+ export { describeAnnotation, patchScreenshot, rebaseAnnotations, } from '@ingcreators/annot-playwright';
23
+ /**
24
+ * @deprecated Import `test` from `@ingcreators/annot-product-docs`
25
+ * (with MDX support) or `@ingcreators/annot-playwright` (without).
26
+ * Re-exporting here is back-compat only and will be removed in
27
+ * `@ingcreators/annot-product-docs-astro@0.5.0`.
28
+ */
29
+ export { test } from '@ingcreators/annot-product-docs';
30
+ /**
31
+ * @deprecated Import `expect` from `@ingcreators/annot-playwright`
32
+ * or `@playwright/test` directly. Re-exporting here is back-compat
33
+ * only and will be removed in
34
+ * `@ingcreators/annot-product-docs-astro@0.5.0`.
35
+ */
1
36
  export { expect } from '@playwright/test';
2
- export type { AnnotScreenshotOptions } from './fixture.js';
3
- export { patchScreenshot, test } from './fixture.js';
4
- export type { Clip, RebaseResult } from './rebase.js';
5
- export { describeAnnotation, rebaseAnnotations } from './rebase.js';
@@ -1,225 +1,7 @@
1
- import { i as e, r as t } from "../render-BSQOeqPB.js";
2
- import { writeFile as n } from "node:fs/promises";
3
- import { createAnnotator as r } from "@ingcreators/annot-annotator";
4
- import { captureScreen as i, test as a } from "@ingcreators/annot-product-docs";
5
- import { expect as o } from "@playwright/test";
6
- import { writePngWithTagsOnly as s } from "@ingcreators/annot-core/xmp-bytes";
7
- //#region src/playwright/rebase.ts
8
- function c(e, t) {
9
- let n = [], r = [];
10
- for (let i of e) {
11
- let e = l(i, t);
12
- e ? n.push(e) : r.push(i);
13
- }
14
- return {
15
- kept: n,
16
- dropped: r
17
- };
18
- }
19
- function l(e, t) {
20
- switch (e.type) {
21
- case "rect": return u(e, t);
22
- case "numberedBadge": return d(e, t);
23
- case "callout": return f(e, t);
24
- case "circle": return p(e, t);
25
- case "arrow": return m(e, t);
26
- case "text": return h(e, t);
27
- case "raw": return g(e);
28
- }
29
- }
30
- function u(e, t) {
31
- return y(e.bbox, t) ? {
32
- ...e,
33
- bbox: _(e.bbox, t)
34
- } : null;
35
- }
36
- function d(e, t) {
37
- return y(e.bbox, t) ? {
38
- ...e,
39
- bbox: _(e.bbox, t),
40
- imageWidth: t.width,
41
- imageHeight: t.height
42
- } : null;
43
- }
44
- function f(e, t) {
45
- return !y(e.targetBbox, t) || !b(e.at, t) ? null : {
46
- ...e,
47
- at: v(e.at, t),
48
- targetBbox: _(e.targetBbox, t)
49
- };
50
- }
51
- function p(e, t) {
52
- return y({
53
- x: e.center.x - e.radius,
54
- y: e.center.y - e.radius,
55
- width: e.radius * 2,
56
- height: e.radius * 2
57
- }, t) ? {
58
- ...e,
59
- center: v(e.center, t)
60
- } : null;
61
- }
62
- function m(e, t) {
63
- return !b(e.from, t) || !b(e.to, t) ? null : {
64
- ...e,
65
- from: v(e.from, t),
66
- to: v(e.to, t)
67
- };
68
- }
69
- function h(e, t) {
70
- return b(e.at, t) ? {
71
- ...e,
72
- at: v(e.at, t)
73
- } : null;
74
- }
75
- function g(e) {
76
- return e;
77
- }
78
- function _(e, t) {
79
- return {
80
- x: e.x - t.x,
81
- y: e.y - t.y,
82
- width: e.width,
83
- height: e.height
84
- };
85
- }
86
- function v(e, t) {
87
- return {
88
- x: e.x - t.x,
89
- y: e.y - t.y
90
- };
91
- }
92
- function y(e, t) {
93
- return e.x >= t.x && e.y >= t.y && e.x + e.width <= t.x + t.width && e.y + e.height <= t.y + t.height;
94
- }
95
- function b(e, t) {
96
- return e.x >= t.x && e.y >= t.y && e.x <= t.x + t.width && e.y <= t.y + t.height;
97
- }
98
- function x(e) {
99
- switch (e.type) {
100
- case "rect":
101
- case "numberedBadge": return `${e.type}@(${e.bbox.x},${e.bbox.y},${e.bbox.width},${e.bbox.height})`;
102
- case "callout": return `callout@(${e.targetBbox.x},${e.targetBbox.y},${e.targetBbox.width},${e.targetBbox.height})`;
103
- case "circle": return `circle@(${e.center.x},${e.center.y},r=${e.radius})`;
104
- case "arrow": return `arrow@(${e.from.x},${e.from.y})→(${e.to.x},${e.to.y})`;
105
- case "text": return `text@(${e.at.x},${e.at.y})`;
106
- case "raw": return "raw[<svg fragment>]";
107
- }
108
- }
1
+ import { test as e } from "@ingcreators/annot-product-docs";
2
+ import { describeAnnotation as t, patchScreenshot as n, rebaseAnnotations as r } from "@ingcreators/annot-playwright";
3
+ import { expect as i } from "@playwright/test";
4
+ //#region src/playwright/index.ts
5
+ typeof process < "u" && typeof process.emitWarning == "function" && !globalThis.__annotProductDocsAstroPlaywrightWarned && (globalThis.__annotProductDocsAstroPlaywrightWarned = !0, process.emitWarning("@ingcreators/annot-product-docs-astro/playwright is deprecated. Import from @ingcreators/annot-product-docs (with MDX support) or @ingcreators/annot-playwright (without). This subpath will be removed in @ingcreators/annot-product-docs-astro@0.5.0.", "DeprecationWarning"));
109
6
  //#endregion
110
- //#region src/playwright/fixture.ts
111
- var S = Symbol.for("@ingcreators/annot:screenshot-patched");
112
- function C(e) {
113
- let t = e;
114
- if (t[S]) return;
115
- let n = e.screenshot;
116
- e.screenshot = async function(e = {}) {
117
- let t = e?.annot;
118
- return w(t) ? D.call(this, n, e) : n.call(this, e);
119
- }, t[S] = !0;
120
- }
121
- function w(e) {
122
- return !e || e === !0 ? !1 : !!(e.mdx || e.overlays && e.overlays.length > 0 || e.tags);
123
- }
124
- function T(e) {
125
- return typeof e.boundingBox == "function";
126
- }
127
- function E(e) {
128
- return T(e) ? e.page() : e;
129
- }
130
- async function D(e, t) {
131
- let { annot: r, path: a, ...o } = t, s = o, c = r.editable ?? !0;
132
- r.mdx && await i(E(this), {
133
- id: r.mdx.id,
134
- mdxPath: r.mdx.path
135
- });
136
- let l = await O(this, t.clip), u = await A({
137
- rawBytes: k(await e.call(this, {
138
- ...s,
139
- path: void 0
140
- })),
141
- annot: r,
142
- editable: c,
143
- clip: l
144
- });
145
- return a && await n(a, u), Buffer.from(u);
146
- }
147
- async function O(e, t) {
148
- if (T(e)) {
149
- let t = await e.boundingBox();
150
- if (!t) throw Error("locator.screenshot({ annot }): locator has no bounding box (probably not visible). Re-test with a stable selector / waitFor().");
151
- return t;
152
- }
153
- return t ?? null;
154
- }
155
- function k(e) {
156
- return e instanceof Uint8Array && !(e instanceof Buffer) ? e : new Uint8Array(e.buffer, e.byteOffset, e.byteLength);
157
- }
158
- async function A(n) {
159
- let { rawBytes: i, annot: a, editable: o, clip: l } = n, u = N(i), d = [];
160
- if (a.mdx) {
161
- let e = l ? {
162
- width: l.x + l.width,
163
- height: l.y + l.height
164
- } : u, n = await t({
165
- mdxPath: a.mdx.path,
166
- screenId: a.mdx.id,
167
- dims: e
168
- });
169
- d.push(...n);
170
- }
171
- a.overlays && d.push(...a.overlays);
172
- let f;
173
- if (l) {
174
- let { kept: e, dropped: t } = c(d, l);
175
- f = e, t.length > 0 && j(t);
176
- } else f = d;
177
- let p = !!(a.mdx || a.overlays);
178
- if (p && !o) {
179
- let t = e(f), n = `data:image/png;base64,${Buffer.from(i).toString("base64")}`, o = r({ loadSystemFonts: !0 }).toPng({
180
- originalDataUrl: n,
181
- annotationsSvg: t,
182
- width: u.width,
183
- height: u.height
184
- });
185
- return a.tags ? s(o, a.tags) : o;
186
- }
187
- if (p) {
188
- let t = e(f), n = `data:image/png;base64,${Buffer.from(i).toString("base64")}`;
189
- return r({ loadSystemFonts: !0 }).toEditablePng({
190
- originalDataUrl: n,
191
- annotationsSvg: t,
192
- width: u.width,
193
- height: u.height,
194
- tags: a.tags
195
- });
196
- }
197
- return a.tags ? s(i, a.tags) : i;
198
- }
199
- function j(e) {
200
- let t = e.map(x).join(", "), n = `annot fixture: dropped ${e.length} overlay(s) outside the screenshot clip — ${t}`;
201
- console.warn(n), M(n, e);
202
- }
203
- function M(e, t) {
204
- try {
205
- let n = a.info?.();
206
- if (!n?.annotations) return;
207
- n.annotations.push({
208
- type: "warning",
209
- description: `${e} (${t.length} dropped)`
210
- });
211
- } catch {}
212
- }
213
- function N(e) {
214
- if (e.length < 24) throw Error("Playwright annot fixture: raw screenshot too short to contain PNG IHDR.");
215
- let t = new DataView(e.buffer, e.byteOffset, e.byteLength);
216
- return {
217
- width: t.getUint32(16, !1),
218
- height: t.getUint32(20, !1)
219
- };
220
- }
221
- var P = a.extend({ page: async ({ page: e }, t) => {
222
- C(Object.getPrototypeOf(e)), C(Object.getPrototypeOf(e.locator("html"))), await t(e);
223
- } });
224
- //#endregion
225
- export { x as describeAnnotation, o as expect, C as patchScreenshot, c as rebaseAnnotations, P as test };
7
+ export { t as describeAnnotation, i as expect, n as patchScreenshot, r as rebaseAnnotations, e as test };
package/dist/render.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { BboxNumberedBadgeAnnotation } from '@ingcreators/annot-annotator';
2
1
  import { FileCache } from './cache.js';
2
+ export { resolveMdxAnnotations, svgFromBboxAnnotations } from '@ingcreators/annot-product-docs';
3
3
  export interface RenderAnnotatedScreenOptions {
4
4
  /** Path to the `.mdx` file (absolute or relative to cwd). */
5
5
  mdxPath: string;
@@ -71,59 +71,3 @@ export interface RenderResult {
71
71
  * and return the result.
72
72
  */
73
73
  export declare function renderAnnotatedScreen(options: RenderAnnotatedScreenOptions): Promise<RenderResult>;
74
- interface BoxedEntry {
75
- role: string;
76
- name: string;
77
- ref: string;
78
- box: {
79
- x: number;
80
- y: number;
81
- width: number;
82
- height: number;
83
- };
84
- }
85
- /**
86
- * Parse aria-snapshot YAML for entries that carry both `[ref=…]`
87
- * and `[box=x,y,w,h]` markers. `box` is the Playwright addition
88
- * available when `ariaSnapshot({ boxes: true })` was passed.
89
- *
90
- * Exposed for unit testing. Returns an empty array if no entries
91
- * have boxes — the caller falls back to a non-annotated render.
92
- */
93
- export declare function parseSnapshotBoxes(yaml: string): BoxedEntry[];
94
- /**
95
- * Resolve the `<Overlay>` blocks inside `screenId` against the MDX
96
- * file's stored `annot:snapshot` block and return a typed annotation
97
- * array ready to feed into `createAnnotator().toEditablePng()`.
98
- *
99
- * Exposed so the Playwright fixture can compose MDX-derived overlays
100
- * with caller-supplied inline overlays before rasterisation. Throws
101
- * the same diagnostics `renderAnnotatedScreen` would (no annot
102
- * frontmatter / no matching `<Screen id>`).
103
- *
104
- * Returns an empty array when no `<Overlay>` matches a bbox marker
105
- * — e.g. the snapshot was never captured, or `<Overlay match>`
106
- * targets an element that's no longer in the page.
107
- */
108
- export declare function resolveMdxAnnotations(opts: {
109
- mdxPath: string;
110
- screenId: string;
111
- dims: {
112
- width: number;
113
- height: number;
114
- };
115
- cwd?: string;
116
- }): Promise<BboxNumberedBadgeAnnotation[]>;
117
- /**
118
- * Wrap a generic `BboxAnnotation[]` (the union DSL accepted by
119
- * `@ingcreators/annot-annotator`) into a single-root `<svg>` ready
120
- * for the annotator's `annotationsSvg` input. Mirrors `svgFromBadges`
121
- * but stays type-permissive — the Playwright fixture's inline overlays
122
- * can be any `BboxAnnotation` shape, not just numbered badges.
123
- *
124
- * Returns the empty wrapper `<svg/>` when `annotations` is empty —
125
- * lets the editor open the file with no annotations layer rather
126
- * than throwing.
127
- */
128
- export declare function svgFromBboxAnnotations(annotations: import('@ingcreators/annot-annotator').BboxAnnotation[]): string;
129
- export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ingcreators/annot-product-docs-astro",
3
- "version": "0.2.1",
3
+ "version": "0.3.0",
4
4
  "description": "Astro integration for `@ingcreators/annot-product-docs`. Wires the MDX components (`<Screen>` / `<Overlay>` / `<Transition>` / `<HistoryEntry>` / `<ScreenList>` / `<TransitionGraph>`) into an Astro site and supplies the Image Service that renders annotated PNGs from `<Screen>` blocks at build time. Phase 2 of docs/plans/living-product-docs.md.",
5
5
  "license": "Apache-2.0",
6
6
  "author": "ingcreators",
@@ -61,19 +61,14 @@
61
61
  "vite-plugin-dts": "^5.0.1"
62
62
  },
63
63
  "dependencies": {
64
- "@ingcreators/annot-core": "0.2.0",
65
64
  "@ingcreators/annot-annotator": "0.5.0",
66
- "@ingcreators/annot-product-docs": "0.2.0"
65
+ "@ingcreators/annot-core": "0.2.1",
66
+ "@ingcreators/annot-product-docs": "0.3.0",
67
+ "@ingcreators/annot-playwright": "0.4.0"
67
68
  },
68
69
  "peerDependencies": {
69
- "@playwright/test": "^1.42.0",
70
70
  "astro": "^5.0.0 || ^6.0.0"
71
71
  },
72
- "peerDependenciesMeta": {
73
- "@playwright/test": {
74
- "optional": true
75
- }
76
- },
77
72
  "scripts": {
78
73
  "build": "vite build",
79
74
  "typecheck": "tsc --noEmit"
@@ -1,72 +0,0 @@
1
- import { BboxAnnotation } from '@ingcreators/annot-annotator';
2
- /**
3
- * Compositional options for `page.screenshot({ annot })` /
4
- * `locator.screenshot({ annot })`. Each field is an independent
5
- * contribution to the embedded XMP record:
6
- *
7
- * - `mdx` — refresh + resolve MDX `<Overlay>` annotations
8
- * - `overlays`— inline `BboxAnnotation[]` (the DSL accepted by
9
- * `@ingcreators/annot-annotator`)
10
- * - `tags` — provenance metadata written verbatim into the XMP
11
- * - `editable`— bake-vs-preserve toggle (default `true`)
12
- *
13
- * `annot: true` / `annot: {}` is treated as a no-op shorthand — the
14
- * fixture detects no contribution and the screenshot falls through
15
- * to vanilla Playwright behaviour.
16
- */
17
- export interface AnnotScreenshotOptions {
18
- /** Refresh the MDX `annot:snapshot` block and resolve the
19
- * `<Screen id>`'s overlays. The MDX file is rewritten in-place
20
- * with the current page's aria-snapshot before overlays resolve.
21
- */
22
- mdx?: {
23
- id: string;
24
- path: string;
25
- };
26
- /** Caller-supplied annotations — merged with MDX-derived ones if
27
- * `mdx` is also set. Same DSL `@ingcreators/annot-annotator`
28
- * accepts. */
29
- overlays?: BboxAnnotation[];
30
- /** Provenance metadata written verbatim into the PNG's XMP. The
31
- * fixture adds no defaults; callers who want `WELL_KNOWN_TAG_KEYS`
32
- * (`source` / `screen` / `capturedAt` / `commit`) write them. */
33
- tags?: Record<string, string>;
34
- /** When `true` (default): annotations stored as SVG in XMP +
35
- * original capture embedded → re-editable in Annot Cloud. When
36
- * `false`: annotations baked into the visible pixels, no XMP
37
- * layer, no embedded original — flat PNG, no round-trip. */
38
- editable?: boolean;
39
- }
40
- declare module "@playwright/test" {
41
- interface PageScreenshotOptions {
42
- annot?: AnnotScreenshotOptions;
43
- }
44
- interface LocatorScreenshotOptions {
45
- annot?: AnnotScreenshotOptions;
46
- }
47
- }
48
- /**
49
- * Idempotent prototype patch — wrap `screenshot` to intercept the
50
- * `annot` opt while falling through to the original method when
51
- * absent / empty. Exported for unit tests; production usage flows
52
- * through the fixture `extend({ page })` body which calls this once
53
- * per worker on the first page (and once for the locator prototype).
54
- */
55
- export declare function patchScreenshot(proto: {
56
- screenshot: (opts?: unknown) => Promise<Buffer>;
57
- }): void;
58
- /**
59
- * `test = base.extend({ page })` — drop-in for `@playwright/test`'s
60
- * `test` plus a one-time patch of `Page.prototype.screenshot` AND
61
- * `Locator.prototype.screenshot` (per worker process) so calls
62
- * carrying `annot: { ... }` run the annot pipeline above.
63
- *
64
- * The base `test` is `@ingcreators/annot-product-docs`'s test which
65
- * already extends `@ingcreators/annot-playwright` — callers get
66
- * `page` + `annotator` + `screen` plus the annot-aware screenshot.
67
- */
68
- export declare const test: import('@playwright/test').TestType<import('@playwright/test').PlaywrightTestArgs & import('@playwright/test').PlaywrightTestOptions & {
69
- annotator: import('@ingcreators/annot-playwright').PlaywrightAnnotator;
70
- } & {
71
- screen: import('@ingcreators/annot-product-docs').Screen;
72
- }, import('@playwright/test').PlaywrightWorkerArgs & import('@playwright/test').PlaywrightWorkerOptions>;
@@ -1,36 +0,0 @@
1
- import { BboxAnnotation } from '@ingcreators/annot-annotator';
2
- export interface Clip {
3
- x: number;
4
- y: number;
5
- width: number;
6
- height: number;
7
- }
8
- export interface RebaseResult {
9
- /** Annotations whose coordinates were rebased into clip-space. */
10
- kept: BboxAnnotation[];
11
- /** Annotations that fell outside `clip` and were dropped. The
12
- * caller surfaces these as `RenderResult.droppedOverlays` /
13
- * `test.info().annotations` warnings. */
14
- dropped: BboxAnnotation[];
15
- }
16
- /**
17
- * Rebase + filter a `BboxAnnotation[]` against a clip rectangle.
18
- *
19
- * - `kept` = annotations whose visible coords were translated by
20
- * `(-clip.x, -clip.y)`. The new coords are in the clipped image's
21
- * coordinate space (0,0 at the top-left of the screenshot).
22
- * - `dropped` = annotations whose bbox / endpoints fell outside the
23
- * clip. Returned verbatim (un-rebased) so the caller can log
24
- * which originals were skipped.
25
- *
26
- * `numberedBadge`'s `imageWidth` / `imageHeight` are also rebased
27
- * to match the clip dimensions, so `placement: "auto"` picks the
28
- * corner against the cropped image edge rather than the page edge.
29
- */
30
- export declare function rebaseAnnotations(annotations: BboxAnnotation[], clip: Clip): RebaseResult;
31
- /**
32
- * Format a short identifier for an annotation so dropped diagnostics
33
- * stay readable. We don't have a stable id field on the DSL types —
34
- * use `type` + first bbox coords as a heuristic.
35
- */
36
- export declare function describeAnnotation(ann: BboxAnnotation): string;
@@ -1,164 +0,0 @@
1
- import { createHash as e } from "node:crypto";
2
- import { mkdir as t, readFile as n, writeFile as r } from "node:fs/promises";
3
- import { dirname as i, isAbsolute as a, join as o, resolve as s } from "node:path";
4
- import { bboxAnnotationsToSvg as c, createAnnotator as l } from "@ingcreators/annot-annotator";
5
- import { parseMdxFile as u } from "@ingcreators/annot-product-docs";
6
- //#region src/cache.ts
7
- var d = 1;
8
- function f(t) {
9
- let n = e("sha256");
10
- return n.update("v1\n"), n.update(t.mdxSource), n.update("\0"), n.update(t.screenId), t.editable && n.update("\0editable"), n.digest("hex");
11
- }
12
- function p(e) {
13
- return {
14
- dir: e,
15
- async get(t) {
16
- try {
17
- let r = await n(o(e, `${t}.png`));
18
- return new Uint8Array(r.buffer, r.byteOffset, r.byteLength);
19
- } catch (e) {
20
- if (e.code === "ENOENT") return null;
21
- throw e;
22
- }
23
- },
24
- async set(n, a) {
25
- let s = o(e, `${n}.png`);
26
- await t(i(s), { recursive: !0 }), await r(s, a);
27
- }
28
- };
29
- }
30
- function m() {
31
- let e = /* @__PURE__ */ new Map();
32
- return {
33
- dir: ":memory:",
34
- async get(t) {
35
- return e.get(t) ?? null;
36
- },
37
- async set(t, n) {
38
- e.set(t, n);
39
- }
40
- };
41
- }
42
- //#endregion
43
- //#region src/render.ts
44
- async function h(e) {
45
- let t = e.cwd ?? process.cwd(), n = a(e.mdxPath) ? e.mdxPath : s(t, e.mdxPath), r = await u(n);
46
- if (!r) throw Error(`renderAnnotatedScreen: ${e.mdxPath} has no \`annot:\` frontmatter — cannot render.`);
47
- let o = r.screens.find((t) => t.id === e.screenId);
48
- if (!o) throw Error(`renderAnnotatedScreen: ${e.mdxPath} has no <Screen id="${e.screenId}"> block.`);
49
- let c = g(e.editable), d = f({
50
- mdxSource: r.source,
51
- screenId: e.screenId,
52
- editable: c !== null
53
- });
54
- if (e.cache) {
55
- let t = await e.cache.get(d);
56
- if (t) return {
57
- bytes: t,
58
- fromCache: !0,
59
- hadBoundingBoxes: !0
60
- };
61
- }
62
- let p = e.basePngBytes ?? await C(r, o.src, i(n)), m = w(p), h = v(r.commentBlocks.snapshot ?? ""), y = b(o.overlays, h, m), S, T;
63
- if (y.length === 0) if (T = !1, c !== null) {
64
- let e = `data:image/png;base64,${Buffer.from(p).toString("base64")}`;
65
- S = l({ loadSystemFonts: !0 }).toEditablePng({
66
- originalDataUrl: e,
67
- annotationsSvg: _(),
68
- width: m.width,
69
- height: m.height,
70
- tags: c.tags
71
- });
72
- } else S = p;
73
- else {
74
- let e = `data:image/png;base64,${Buffer.from(p).toString("base64")}`, t = x(y), n = l({ loadSystemFonts: !0 }), r = {
75
- originalDataUrl: e,
76
- annotationsSvg: t,
77
- width: m.width,
78
- height: m.height
79
- };
80
- S = c === null ? n.toPng(r) : n.toEditablePng({
81
- ...r,
82
- tags: c.tags
83
- }), T = !0;
84
- }
85
- return e.cache && await e.cache.set(d, S), {
86
- bytes: S,
87
- fromCache: !1,
88
- hadBoundingBoxes: T
89
- };
90
- }
91
- function g(e) {
92
- return e ? e === !0 ? {} : e : null;
93
- }
94
- function _() {
95
- return "<svg xmlns=\"http://www.w3.org/2000/svg\"></svg>";
96
- }
97
- function v(e) {
98
- let t = [];
99
- for (let n of e.split(/\r?\n/)) {
100
- if (!n.trim()) continue;
101
- let e = n.match(/^\s*-\s+([a-z]+)(?:\s+"([^"]*?)")?/);
102
- if (!e) continue;
103
- let r = e[1] ?? "", i = e[2] ?? "", a = n.match(/\[ref=(e\d+)\]/)?.[1], o = n.match(/\[box=(-?\d+(?:\.\d+)?),(-?\d+(?:\.\d+)?),(-?\d+(?:\.\d+)?),(-?\d+(?:\.\d+)?)\]/);
104
- !a || !o || t.push({
105
- role: r,
106
- name: i,
107
- ref: a,
108
- box: {
109
- x: Number.parseFloat(o[1]),
110
- y: Number.parseFloat(o[2]),
111
- width: Number.parseFloat(o[3]),
112
- height: Number.parseFloat(o[4])
113
- }
114
- });
115
- }
116
- return t;
117
- }
118
- async function y(e) {
119
- let t = e.cwd ?? process.cwd(), n = await u(a(e.mdxPath) ? e.mdxPath : s(t, e.mdxPath));
120
- if (!n) throw Error(`resolveMdxAnnotations: ${e.mdxPath} has no \`annot:\` frontmatter — cannot resolve.`);
121
- let r = n.screens.find((t) => t.id === e.screenId);
122
- if (!r) throw Error(`resolveMdxAnnotations: ${e.mdxPath} has no <Screen id="${e.screenId}"> block.`);
123
- let i = v(n.commentBlocks.snapshot ?? "");
124
- return b(r.overlays, i, e.dims);
125
- }
126
- function b(e, t, n) {
127
- let r = [], i = 1;
128
- for (let a of e) {
129
- let e = t.find((e) => e.role === a.match.role && e.name === a.match.name);
130
- if (!e) continue;
131
- let o = a.number ?? i++;
132
- r.push({
133
- type: "numberedBadge",
134
- bbox: e.box,
135
- number: o,
136
- placement: "auto",
137
- imageWidth: n.width,
138
- imageHeight: n.height,
139
- intent: a.intent === "action" ? "warning" : a.intent === "required" ? "error" : "info"
140
- });
141
- }
142
- return r;
143
- }
144
- function x(e) {
145
- return `<svg xmlns="http://www.w3.org/2000/svg">${c(e)}</svg>`;
146
- }
147
- function S(e) {
148
- return e.length === 0 ? "<svg xmlns=\"http://www.w3.org/2000/svg\"></svg>" : `<svg xmlns="http://www.w3.org/2000/svg">${c(e)}</svg>`;
149
- }
150
- async function C(e, t, r) {
151
- if (!t) throw Error("renderAnnotatedScreen: <Screen src=...> is required — cannot render without a base image.");
152
- let i = await n(a(t) ? t : s(r, t));
153
- return new Uint8Array(i.buffer, i.byteOffset, i.byteLength);
154
- }
155
- function w(e) {
156
- if (e.length < 24) throw Error("renderAnnotatedScreen: base PNG too short to contain IHDR.");
157
- let t = new DataView(e.buffer, e.byteOffset, e.byteLength);
158
- return {
159
- width: t.getUint32(16, !1),
160
- height: t.getUint32(20, !1)
161
- };
162
- }
163
- //#endregion
164
- export { d as a, m as c, S as i, h as n, f as o, y as r, p as s, v as t };