@ingcreators/annot-product-docs-astro 0.1.0 → 0.2.1
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 +303 -0
- package/README.md +37 -1
- package/dist/cache.d.ts +46 -0
- package/dist/components/HistoryEntry.astro +34 -0
- package/dist/components/Overlay.astro +50 -0
- package/dist/components/Screen.astro +27 -0
- package/dist/components/ScreenList.astro +58 -0
- package/dist/components/Transition.astro +48 -0
- package/dist/components/TransitionGraph.astro +46 -0
- package/dist/components/TransitionTable.astro +55 -0
- package/dist/components/types.d.ts +21 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +18 -0
- package/dist/integration.d.ts +46 -0
- package/dist/playwright/fixture.d.ts +72 -0
- package/dist/playwright/index.d.ts +5 -0
- package/dist/playwright/index.js +225 -0
- package/dist/playwright/rebase.d.ts +36 -0
- package/dist/render-BSQOeqPB.js +164 -0
- package/dist/render.d.ts +129 -0
- package/package.json +20 -8
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
# @ingcreators/annot-product-docs-astro
|
|
2
|
+
|
|
3
|
+
## 0.2.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 87a8bad: **Fix `./playwright` subpath build.** The published `0.1.0`
|
|
8
|
+
and `0.2.0` tarballs shipped `dist/playwright/*.d.ts` (type
|
|
9
|
+
declarations) but NOT the runtime `dist/playwright/index.js`,
|
|
10
|
+
because `vite.config.ts`'s `lib.entry` was single-entry —
|
|
11
|
+
only the top-level `src/index.ts` got built.
|
|
12
|
+
|
|
13
|
+
Multi-entry library mode now emits both bundles:
|
|
14
|
+
|
|
15
|
+
```
|
|
16
|
+
dist/
|
|
17
|
+
index.js # main entry (re-exports integration + components + render)
|
|
18
|
+
playwright/index.js # `test`, `expect`, `patchScreenshot`, `rebaseAnnotations`
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Any consumer doing `import { test } from
|
|
22
|
+
"@ingcreators/annot-product-docs-astro/playwright"`
|
|
23
|
+
previously got `Cannot find module ...dist/playwright/index.js`
|
|
24
|
+
at runtime; the `0.2.1` republish makes the subpath actually
|
|
25
|
+
loadable.
|
|
26
|
+
|
|
27
|
+
Also marks `@playwright/test` as external in the Rollup
|
|
28
|
+
config (matches the package.json `peerDependencies` shape;
|
|
29
|
+
prevents accidentally bundling Playwright into the playwright
|
|
30
|
+
adapter).
|
|
31
|
+
|
|
32
|
+
No public-API change — same exports, same call shapes,
|
|
33
|
+
same TypeScript types. Pure packaging fix.
|
|
34
|
+
|
|
35
|
+
## 0.2.0
|
|
36
|
+
|
|
37
|
+
### Minor Changes
|
|
38
|
+
|
|
39
|
+
- 2e92c97: First publish of the living-product-docs package family. Phases
|
|
40
|
+
1-5 + 7 of `docs/plans/living-product-docs.md` landed across
|
|
41
|
+
PRs 876-899; this entry flips the three packages from
|
|
42
|
+
`private: true` to publishable and stamps `0.1.0`.
|
|
43
|
+
|
|
44
|
+
### `@ingcreators/annot-product-docs`
|
|
45
|
+
- MDX parser (`parseMdx` / `parseMdxFile`) — Remark / unified
|
|
46
|
+
pipeline that walks `.mdx` files with `annot:` frontmatter
|
|
47
|
+
and extracts `<Screen>` / `<Overlay>` / `<Transition>` /
|
|
48
|
+
`<HistoryEntry>` / `<ScreenList>` JSX components.
|
|
49
|
+
- Match resolver (`parseSnapshot` / `resolveMatch` /
|
|
50
|
+
`resolveOverlays`) for the Playwright `aria-snapshot`
|
|
51
|
+
YAML, honouring `match.under` disambiguation and emitting
|
|
52
|
+
`not-found` / `ambiguous` / `renamed` / `role-changed` /
|
|
53
|
+
`live-mismatch` diagnostics.
|
|
54
|
+
- `screen` fixture extending `@ingcreators/annot-playwright`
|
|
55
|
+
with `screen.capture({ id, mdxPath })` that re-syncs
|
|
56
|
+
`annot:snapshot` + `annot:attributes` MDX comment blocks
|
|
57
|
+
in place.
|
|
58
|
+
- Drift detector (`detectDrift` / `detectDriftFromYaml`) — six
|
|
59
|
+
finding kinds (added / removed / renamed / role-changed /
|
|
60
|
+
duplicated / attribute-drift) with severity buckets.
|
|
61
|
+
- `annot-docs` CLI (`init` / `sync` / `lint`) with `--json`
|
|
62
|
+
/ `--ci` / `--fix` flags + a sample GitHub Actions workflow
|
|
63
|
+
emitting GitHub annotations on PR diff views.
|
|
64
|
+
|
|
65
|
+
### `@ingcreators/annot-product-docs-astro`
|
|
66
|
+
- `productDocsIntegration()` Astro 5.x integration factory.
|
|
67
|
+
- 7 docs components: `<Screen>`, `<Overlay>`, `<Transition>`,
|
|
68
|
+
`<TransitionTable>`, `<HistoryEntry>`, `<ScreenList>`,
|
|
69
|
+
`<TransitionGraph>`. Shipped as `.astro` source under
|
|
70
|
+
`./components/*.astro` exports.
|
|
71
|
+
- Image Service (`renderAnnotatedScreen` + SHA-keyed
|
|
72
|
+
`createFileCache` / `createMemoryCache`) that composes the
|
|
73
|
+
base screenshot with overlay callouts at build time.
|
|
74
|
+
|
|
75
|
+
### `@ingcreators/annot-product-docs-xlsx`
|
|
76
|
+
- MDX → normalised bundle extractor; per-role default layout
|
|
77
|
+
(cover / history / list / screen / reference); customer-
|
|
78
|
+
template support with `{var}` placeholder substitution
|
|
79
|
+
(including `{annot:date}` special vars + `{name:format}`
|
|
80
|
+
date formatting); Excel Named Range writers
|
|
81
|
+
(`annotImage` / `annotItemTable` / `annotHistory` /
|
|
82
|
+
`annotList` / `annotSnapshot` / `annotAttributes`).
|
|
83
|
+
- `annot-docs-xlsx render` CLI with multi-book emit + per-book
|
|
84
|
+
template config.
|
|
85
|
+
|
|
86
|
+
- 4768855: **`@ingcreators/annot-product-docs-astro` — new `/playwright`
|
|
87
|
+
subpath** that re-exports an extended Playwright `test` fixture
|
|
88
|
+
whose `page.screenshot()` accepts a compositional `annot: { … }`
|
|
89
|
+
option:
|
|
90
|
+
|
|
91
|
+
```ts
|
|
92
|
+
import { test } from "@ingcreators/annot-product-docs-astro/playwright";
|
|
93
|
+
|
|
94
|
+
test("login flow", async ({ page }) => {
|
|
95
|
+
await page.goto("/login");
|
|
96
|
+
await page.screenshot({
|
|
97
|
+
path: "public/login.png",
|
|
98
|
+
annot: {
|
|
99
|
+
mdx: { id: "login", path: "src/content/docs/login.mdx" },
|
|
100
|
+
tags: { source: "docs-tour", capturedAt: new Date().toISOString() },
|
|
101
|
+
},
|
|
102
|
+
});
|
|
103
|
+
});
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
The `annot` option is compositional — each field is an
|
|
107
|
+
independent contribution to the embedded XMP record:
|
|
108
|
+
- `mdx: { id, path }` — refresh the MDX's `annot:snapshot` block
|
|
109
|
+
against the current page, then resolve `<Overlay match>` blocks
|
|
110
|
+
for the named `<Screen>`.
|
|
111
|
+
- `overlays: BboxAnnotation[]` — caller-supplied annotations
|
|
112
|
+
(same DSL `@ingcreators/annot-annotator` accepts). Merged with
|
|
113
|
+
MDX-derived overlays when both are present.
|
|
114
|
+
- `tags: Record<string, string>` — provenance metadata written
|
|
115
|
+
verbatim into the XMP. No auto-fill — callers write the
|
|
116
|
+
`WELL_KNOWN_TAG_KEYS` they want.
|
|
117
|
+
- `editable: boolean` (default `true`) — toggle between
|
|
118
|
+
"annotations preserved as SVG layer + embedded original"
|
|
119
|
+
(re-editable in Annot Cloud) and "annotations baked into
|
|
120
|
+
visible pixels" (flat PNG, no XMP layer).
|
|
121
|
+
|
|
122
|
+
`page.screenshot()` calls WITHOUT `annot` fall through to
|
|
123
|
+
vanilla Playwright byte-for-byte — codegen / DevTools Recorder
|
|
124
|
+
output keeps working unedited.
|
|
125
|
+
|
|
126
|
+
Phase 1 of
|
|
127
|
+
`docs/plans/playwright-screenshot-annot-fixture.md`. Phase 2 will
|
|
128
|
+
add the same interception on `locator.screenshot()` with
|
|
129
|
+
coordinate rebasing for sub-region overlays.
|
|
130
|
+
|
|
131
|
+
Two helpers also exported from the main `@ingcreators/annot-product-docs-astro`
|
|
132
|
+
entry for callers who want to compose annotations themselves:
|
|
133
|
+
- `resolveMdxAnnotations({ mdxPath, screenId, dims })` — extract
|
|
134
|
+
the MDX's `<Overlay>` blocks into a typed `BboxNumberedBadgeAnnotation[]`
|
|
135
|
+
(the underlying step the fixture uses internally).
|
|
136
|
+
- `svgFromBboxAnnotations(annotations)` — wrap a
|
|
137
|
+
`BboxAnnotation[]` into a single-root `<svg>` ready for
|
|
138
|
+
`Annotator.toEditablePng()` / `toPng()`.
|
|
139
|
+
|
|
140
|
+
**`@ingcreators/annot-core` — new `writePngWithTagsOnly`
|
|
141
|
+
helper** exported from `/xmp-bytes` (and re-exported from
|
|
142
|
+
`/xmp`). Writes `tags` into a PNG's XMP iTXt chunk without
|
|
143
|
+
embedding an original capture or annotations layer — for the
|
|
144
|
+
"PNG with provenance metadata sidecar" path (CI failure
|
|
145
|
+
screenshots, VRT references, etc.). The resulting bytes are
|
|
146
|
+
still a valid PNG and the Annot editor treats them as a normal
|
|
147
|
+
PNG (no `<annot:annotations>` element → not editable round-trip;
|
|
148
|
+
opens as fresh canvas).
|
|
149
|
+
|
|
150
|
+
- 49b5585: **`locator.screenshot({ annot: { … } })` support** — Phase 2 of
|
|
151
|
+
`docs/plans/playwright-screenshot-annot-fixture.md`. The fixture
|
|
152
|
+
now patches `Locator.prototype.screenshot` alongside
|
|
153
|
+
`Page.prototype.screenshot`, and overlay coordinates are
|
|
154
|
+
automatically rebased into the cropped image's coordinate space:
|
|
155
|
+
|
|
156
|
+
```ts
|
|
157
|
+
await page.locator("header").screenshot({
|
|
158
|
+
path: "header.png",
|
|
159
|
+
annot: {
|
|
160
|
+
overlays: [
|
|
161
|
+
// page-space bbox — rebased onto the header-locator's clip
|
|
162
|
+
{ type: "rect", bbox: { x: 120, y: 60, width: 50, height: 30 } },
|
|
163
|
+
],
|
|
164
|
+
},
|
|
165
|
+
});
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
Overlays whose page-space bbox falls outside the locator's
|
|
169
|
+
bounding box are dropped (warning + skip per Open Question 4)
|
|
170
|
+
and surfaced via:
|
|
171
|
+
- `console.warn(...)` to stderr — always
|
|
172
|
+
- `test.info().annotations` as a `warning` entry when running
|
|
173
|
+
under Playwright — best-effort, guarded so vitest unit tests
|
|
174
|
+
don't crash
|
|
175
|
+
|
|
176
|
+
`page.screenshot({ clip, annot })` honours `clip` the same way —
|
|
177
|
+
explicit clip + auto rebase. Mirrors vanilla
|
|
178
|
+
`page.screenshot({ clip })` semantics; only the overlay-rebase
|
|
179
|
+
behaviour is annot-specific.
|
|
180
|
+
|
|
181
|
+
When the locator has no bounding box (off-screen / hidden), the
|
|
182
|
+
fixture throws a friendly diagnostic asking the caller to use
|
|
183
|
+
a stable selector / `waitFor()`.
|
|
184
|
+
|
|
185
|
+
**Coordinate-rebase API** — exported alongside the fixture for
|
|
186
|
+
callers who want to compose annotations themselves:
|
|
187
|
+
|
|
188
|
+
```ts
|
|
189
|
+
import { rebaseAnnotations } from "@ingcreators/annot-product-docs-astro/playwright";
|
|
190
|
+
// Or for direct algorithmic use without the fixture:
|
|
191
|
+
// import { rebaseAnnotations } from "@ingcreators/annot-product-docs-astro/playwright/rebase";
|
|
192
|
+
|
|
193
|
+
const { kept, dropped } = rebaseAnnotations(annotations, clip);
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
Returns `{ kept, dropped }` for each shape in the
|
|
197
|
+
`BboxAnnotation` union (rect / numberedBadge / circle / arrow /
|
|
198
|
+
text / callout). Raw SVG fragments (`type: "raw"`) pass through
|
|
199
|
+
unchanged — the caller is responsible for emitting clip-space
|
|
200
|
+
coords inside arbitrary SVG.
|
|
201
|
+
|
|
202
|
+
`numberedBadge`'s `imageWidth` / `imageHeight` are also rebased
|
|
203
|
+
to the clip dimensions so `placement: "auto"` picks the corner
|
|
204
|
+
against the cropped image edge rather than the page edge.
|
|
205
|
+
|
|
206
|
+
- 730fab7: `renderAnnotatedScreen()` gains an optional `editable?: boolean |
|
|
207
|
+
{ tags?: Record<string, string> }` field. Pass `true` (or an
|
|
208
|
+
object) and the function routes through the new
|
|
209
|
+
`Annotator.toEditablePng()` path: the returned PNG carries the
|
|
210
|
+
same visible callouts plus the original capture + the
|
|
211
|
+
annotations SVG embedded in XMP / custom `svGo` chunk, so
|
|
212
|
+
re-opening the file in the Annot editor / Cloud restores the
|
|
213
|
+
overlays as selectable / movable / restylable objects rather
|
|
214
|
+
than a flat bitmap.
|
|
215
|
+
|
|
216
|
+
```ts
|
|
217
|
+
const result = await renderAnnotatedScreen({
|
|
218
|
+
mdxPath: "docs/app/index.mdx",
|
|
219
|
+
screenId: "app-overview",
|
|
220
|
+
basePngBytes,
|
|
221
|
+
editable: {
|
|
222
|
+
tags: {
|
|
223
|
+
source: "docs-tour",
|
|
224
|
+
capturedAt: new Date().toISOString(),
|
|
225
|
+
},
|
|
226
|
+
},
|
|
227
|
+
});
|
|
228
|
+
await writeFile("public/app/shots/app-overview.png", result.bytes);
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
The cache key folds in the `editable` flag, so flat and editable
|
|
232
|
+
variants of the same screen don't collide. Existing flat-raster
|
|
233
|
+
callers are byte-for-byte unaffected — the option defaults to
|
|
234
|
+
`undefined` (flat).
|
|
235
|
+
|
|
236
|
+
The `CacheKeyInput` type gains a parallel `editable?: boolean`
|
|
237
|
+
field; pure helpers that compute the cache key directly should
|
|
238
|
+
forward the bit when threading the flag through.
|
|
239
|
+
|
|
240
|
+
Internal note: this PR also adds `@ingcreators/annot-core` as a
|
|
241
|
+
devDependency so tests can import `readEditablePngBytes` from
|
|
242
|
+
`/xmp-bytes` for round-trip verification. Runtime dependencies
|
|
243
|
+
are unchanged.
|
|
244
|
+
|
|
245
|
+
### Patch Changes
|
|
246
|
+
|
|
247
|
+
- 657a685: **Republish with `dist/` included.** The `0.1.0` tarballs of all
|
|
248
|
+
three packages shipped to npm without their `dist/` directory —
|
|
249
|
+
the `publish.yml` workflow's pre-pack `pnpm build` step had only
|
|
250
|
+
filtered four other packages, so `pnpm pack` packed the three
|
|
251
|
+
`product-docs*` packages against empty `dist/`s. The
|
|
252
|
+
`publishConfig.main` (`./dist/index.js`) consequently pointed at
|
|
253
|
+
a missing file, breaking `npm install` for every consumer.
|
|
254
|
+
|
|
255
|
+
The source fix landed in
|
|
256
|
+
[#947](https://github.com/ingcreators/annot/pull/947) with two
|
|
257
|
+
defences:
|
|
258
|
+
1. Three new `--filter` lines in the workflow's build step so
|
|
259
|
+
all seven publishable packages get built before pack.
|
|
260
|
+
2. A per-package `prepack` script (`pnpm run build`) so even a
|
|
261
|
+
misconfigured workflow (or a manual `pnpm pack` / `pnpm
|
|
262
|
+
publish`) refreshes `dist/` before packing.
|
|
263
|
+
|
|
264
|
+
No source-code changes in any of the three packages — only the
|
|
265
|
+
packaging is fixed. This patch publish exists solely to deliver
|
|
266
|
+
working tarballs to the registry; the public API surface is
|
|
267
|
+
byte-identical to `0.1.0`.
|
|
268
|
+
|
|
269
|
+
Verified locally:
|
|
270
|
+
|
|
271
|
+
```
|
|
272
|
+
$ pnpm --filter @ingcreators/annot-product-docs pack --dry-run
|
|
273
|
+
Tarball Contents
|
|
274
|
+
bin/annot-docs.mjs
|
|
275
|
+
dist/cli.d.ts
|
|
276
|
+
dist/config.d.ts
|
|
277
|
+
dist/drift.d.ts
|
|
278
|
+
dist/fixture.d.ts
|
|
279
|
+
dist/index.d.ts
|
|
280
|
+
dist/index.js
|
|
281
|
+
dist/mdx.d.ts
|
|
282
|
+
dist/resolver.d.ts
|
|
283
|
+
dist/types-config.d.ts
|
|
284
|
+
dist/types.d.ts
|
|
285
|
+
LICENSE
|
|
286
|
+
package.json
|
|
287
|
+
README.md
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
Before the fix the same command produced 4 files (LICENSE +
|
|
291
|
+
README + package.json + bin/annot-docs.mjs), no compiled code.
|
|
292
|
+
|
|
293
|
+
- Updated dependencies [806badc]
|
|
294
|
+
- Updated dependencies [2e8d397]
|
|
295
|
+
- Updated dependencies [780985d]
|
|
296
|
+
- Updated dependencies [df1a429]
|
|
297
|
+
- Updated dependencies [2e92c97]
|
|
298
|
+
- Updated dependencies [5e74421]
|
|
299
|
+
- Updated dependencies [4768855]
|
|
300
|
+
- Updated dependencies [657a685]
|
|
301
|
+
- @ingcreators/annot-annotator@0.5.0
|
|
302
|
+
- @ingcreators/annot-core@0.2.0
|
|
303
|
+
- @ingcreators/annot-product-docs@0.2.0
|
package/README.md
CHANGED
|
@@ -9,7 +9,7 @@ docs-MDX components into an Astro site and ships an Image Service
|
|
|
9
9
|
that renders annotated PNGs from `<Screen>` blocks at build time.
|
|
10
10
|
|
|
11
11
|
Phase 2 of
|
|
12
|
-
[`docs/plans/living-product-docs.md`](https://github.com/ingcreators/annot/blob/main/docs/plans/living-product-docs.md).
|
|
12
|
+
[`docs/plans/_done/living-product-docs.md`](https://github.com/ingcreators/annot/blob/main/docs/plans/_done/living-product-docs.md).
|
|
13
13
|
|
|
14
14
|
## Install
|
|
15
15
|
|
|
@@ -89,6 +89,42 @@ 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)
|
|
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:
|
|
102
|
+
|
|
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.
|
|
127
|
+
|
|
92
128
|
## Tier
|
|
93
129
|
|
|
94
130
|
Tier B-render — Astro build-time, no live editor.
|
package/dist/cache.d.ts
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bumped any time the rendering pipeline changes the bytes it
|
|
3
|
+
* produces for the same input. Forces a cold cache on every
|
|
4
|
+
* consumer the next time they build. Keep this small (a single
|
|
5
|
+
* digit per breaking change) — large jumps don't help and just
|
|
6
|
+
* obscure the upgrade history.
|
|
7
|
+
*/
|
|
8
|
+
export declare const RENDER_PIPELINE_VERSION = 1;
|
|
9
|
+
export interface CacheKeyInput {
|
|
10
|
+
mdxSource: string;
|
|
11
|
+
screenId: string;
|
|
12
|
+
/**
|
|
13
|
+
* Whether the render targets a re-editable PNG (vs the default flat
|
|
14
|
+
* raster). Folded into the cache key so flat / editable variants of
|
|
15
|
+
* the same screen don't collide. Defaults to `false` to preserve the
|
|
16
|
+
* historical key shape for flat callers.
|
|
17
|
+
*/
|
|
18
|
+
editable?: boolean;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Compute the cache key for a `<Screen>` block. Stable across
|
|
22
|
+
* runs as long as the MDX source bytes and the screen id are
|
|
23
|
+
* unchanged.
|
|
24
|
+
*/
|
|
25
|
+
export declare function cacheKey(input: CacheKeyInput): string;
|
|
26
|
+
export interface FileCache {
|
|
27
|
+
/** Returns the cached bytes for `key`, or `null` on a miss. */
|
|
28
|
+
get(key: string): Promise<Uint8Array | null>;
|
|
29
|
+
/** Writes `bytes` under `key`. Overwrites silently. */
|
|
30
|
+
set(key: string, bytes: Uint8Array): Promise<void>;
|
|
31
|
+
/** Filesystem path the cache writes into. */
|
|
32
|
+
readonly dir: string;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Disk-backed cache rooted at `dir`. The directory is created on
|
|
36
|
+
* first `set`; reads of nonexistent paths return `null`. Files
|
|
37
|
+
* are flat (no subdirs), one per key — SHA-256 collisions are
|
|
38
|
+
* a non-concern given the keyspace.
|
|
39
|
+
*/
|
|
40
|
+
export declare function createFileCache(dir: string): FileCache;
|
|
41
|
+
/**
|
|
42
|
+
* In-memory cache — useful in tests and as a fallback when the
|
|
43
|
+
* caller doesn't want disk persistence. Same shape as the disk
|
|
44
|
+
* cache for swap-in convenience.
|
|
45
|
+
*/
|
|
46
|
+
export declare function createMemoryCache(): FileCache;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
---
|
|
2
|
+
// `<HistoryEntry>` — single revision-history row.
|
|
3
|
+
//
|
|
4
|
+
// MDX usage (inside a `role: history` doc):
|
|
5
|
+
// <HistoryEntry version="1.0" date="2026-03-15" author="Alice">
|
|
6
|
+
// Initial draft, requirements v2.0.
|
|
7
|
+
// </HistoryEntry>
|
|
8
|
+
//
|
|
9
|
+
// Multiple entries stack vertically; the Excel adapter renders
|
|
10
|
+
// them into the workbook's history sheet via the `annotHistory`
|
|
11
|
+
// named range.
|
|
12
|
+
|
|
13
|
+
interface Props {
|
|
14
|
+
version: string;
|
|
15
|
+
date: string;
|
|
16
|
+
author?: string;
|
|
17
|
+
}
|
|
18
|
+
const { version, date, author } = Astro.props;
|
|
19
|
+
---
|
|
20
|
+
<div
|
|
21
|
+
class="annot-history-entry"
|
|
22
|
+
data-version={version}
|
|
23
|
+
data-date={date}
|
|
24
|
+
data-author={author}
|
|
25
|
+
>
|
|
26
|
+
<div class="annot-history-meta">
|
|
27
|
+
<span class="annot-history-version">v{version}</span>
|
|
28
|
+
<time class="annot-history-date" datetime={date}>{date}</time>
|
|
29
|
+
{author && <span class="annot-history-author">{author}</span>}
|
|
30
|
+
</div>
|
|
31
|
+
<div class="annot-history-body">
|
|
32
|
+
<slot />
|
|
33
|
+
</div>
|
|
34
|
+
</div>
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
---
|
|
2
|
+
// `<Overlay>` — caption for one numbered callout on a screen.
|
|
3
|
+
//
|
|
4
|
+
// MDX usage:
|
|
5
|
+
// <Overlay match={{ role: "textbox", name: "Email" }}
|
|
6
|
+
// intent="required" number={1}>
|
|
7
|
+
// **Email** — enter your registered email.
|
|
8
|
+
// </Overlay>
|
|
9
|
+
//
|
|
10
|
+
// Build-time positioning of the visual callout on the
|
|
11
|
+
// screenshot is handled by the Image Service. This component
|
|
12
|
+
// emits the prose body as a `<li>` in a description list
|
|
13
|
+
// alongside the screen — readers see the screenshot + a
|
|
14
|
+
// numbered legend in parallel.
|
|
15
|
+
|
|
16
|
+
interface Match {
|
|
17
|
+
role: string;
|
|
18
|
+
name: string;
|
|
19
|
+
under?: Match;
|
|
20
|
+
}
|
|
21
|
+
type Intent =
|
|
22
|
+
| "info"
|
|
23
|
+
| "warning"
|
|
24
|
+
| "error"
|
|
25
|
+
| "success"
|
|
26
|
+
| "neutral"
|
|
27
|
+
| "required"
|
|
28
|
+
| "action";
|
|
29
|
+
|
|
30
|
+
interface Props {
|
|
31
|
+
match: Match;
|
|
32
|
+
intent?: Intent;
|
|
33
|
+
number?: number;
|
|
34
|
+
}
|
|
35
|
+
const { match, intent, number } = Astro.props;
|
|
36
|
+
---
|
|
37
|
+
<li
|
|
38
|
+
class="annot-overlay"
|
|
39
|
+
data-intent={intent}
|
|
40
|
+
data-overlay-number={number}
|
|
41
|
+
data-match-role={match.role}
|
|
42
|
+
data-match-name={match.name}
|
|
43
|
+
>
|
|
44
|
+
{number !== undefined && (
|
|
45
|
+
<span class="annot-overlay-number" aria-hidden="true">{number}</span>
|
|
46
|
+
)}
|
|
47
|
+
<div class="annot-overlay-body">
|
|
48
|
+
<slot />
|
|
49
|
+
</div>
|
|
50
|
+
</li>
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
---
|
|
2
|
+
// `<Screen>` — annotated screenshot block.
|
|
3
|
+
//
|
|
4
|
+
// MDX usage:
|
|
5
|
+
// <Screen id="login" src="./shots/login.png">
|
|
6
|
+
// <Overlay match={...} number={1}>...</Overlay>
|
|
7
|
+
// </Screen>
|
|
8
|
+
//
|
|
9
|
+
// The annotated PNG is produced at build time by the Image
|
|
10
|
+
// Service in `@ingcreators/annot-product-docs-astro` (see
|
|
11
|
+
// `../render.ts`). At render time we just emit an `<img>` that
|
|
12
|
+
// points at the cache hash; the Astro pipeline resolves the
|
|
13
|
+
// final URL.
|
|
14
|
+
|
|
15
|
+
interface Props {
|
|
16
|
+
id: string;
|
|
17
|
+
src: string;
|
|
18
|
+
alt?: string;
|
|
19
|
+
}
|
|
20
|
+
const { id, src, alt } = Astro.props;
|
|
21
|
+
---
|
|
22
|
+
<figure class="annot-screen" data-screen-id={id}>
|
|
23
|
+
<img src={src} alt={alt ?? `Screen ${id}`} loading="lazy" />
|
|
24
|
+
<div class="annot-screen-overlays" data-screen-overlays={id}>
|
|
25
|
+
<slot />
|
|
26
|
+
</div>
|
|
27
|
+
</figure>
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
---
|
|
2
|
+
// `<ScreenList>` — auto-enumerated list of every `role: screen`
|
|
3
|
+
// MDX in the same `book`.
|
|
4
|
+
//
|
|
5
|
+
// MDX usage (inside a `role: list` doc):
|
|
6
|
+
// <ScreenList book="Screen spec" sort="byId" entries={...} />
|
|
7
|
+
//
|
|
8
|
+
// The `entries` prop is normally populated by the Astro
|
|
9
|
+
// integration's `astro:config:setup` walker (Phase 4 polish).
|
|
10
|
+
// Until that's wired up, callers pass `entries` explicitly so
|
|
11
|
+
// the component renders deterministically in tests and in
|
|
12
|
+
// dev-mode-without-build.
|
|
13
|
+
|
|
14
|
+
export interface ScreenListEntry {
|
|
15
|
+
id: string;
|
|
16
|
+
title?: string;
|
|
17
|
+
href?: string;
|
|
18
|
+
order?: number;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
interface Props {
|
|
22
|
+
book?: string;
|
|
23
|
+
sort?: "byId" | "byOrder" | "byFilePath";
|
|
24
|
+
entries: ScreenListEntry[];
|
|
25
|
+
}
|
|
26
|
+
const { book, sort = "byId", entries } = Astro.props;
|
|
27
|
+
|
|
28
|
+
const sortedEntries = [...entries].sort((a, b) => {
|
|
29
|
+
if (sort === "byOrder") {
|
|
30
|
+
return (a.order ?? 100) - (b.order ?? 100);
|
|
31
|
+
}
|
|
32
|
+
// byId / byFilePath both fall back to alphabetical for
|
|
33
|
+
// determinism.
|
|
34
|
+
return a.id.localeCompare(b.id);
|
|
35
|
+
});
|
|
36
|
+
---
|
|
37
|
+
<table class="annot-screen-list" data-book={book} data-sort={sort}>
|
|
38
|
+
<thead>
|
|
39
|
+
<tr>
|
|
40
|
+
<th>ID</th>
|
|
41
|
+
<th>Screen</th>
|
|
42
|
+
</tr>
|
|
43
|
+
</thead>
|
|
44
|
+
<tbody>
|
|
45
|
+
{sortedEntries.map((entry) => (
|
|
46
|
+
<tr data-screen-id={entry.id}>
|
|
47
|
+
<td>{entry.id}</td>
|
|
48
|
+
<td>
|
|
49
|
+
{entry.href ? (
|
|
50
|
+
<a href={entry.href}>{entry.title ?? entry.id}</a>
|
|
51
|
+
) : (
|
|
52
|
+
<span>{entry.title ?? entry.id}</span>
|
|
53
|
+
)}
|
|
54
|
+
</td>
|
|
55
|
+
</tr>
|
|
56
|
+
))}
|
|
57
|
+
</tbody>
|
|
58
|
+
</table>
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
---
|
|
2
|
+
// `<Transition>` — describes one screen-to-screen transition.
|
|
3
|
+
//
|
|
4
|
+
// MDX usage:
|
|
5
|
+
// <Transition
|
|
6
|
+
// trigger={{ role: "button", name: "Sign in" }}
|
|
7
|
+
// on="click"
|
|
8
|
+
// to="dashboard"
|
|
9
|
+
// >
|
|
10
|
+
// Successful login redirects to the dashboard.
|
|
11
|
+
// </Transition>
|
|
12
|
+
//
|
|
13
|
+
// Renders inline as part of the screen's prose; for a tabular
|
|
14
|
+
// list of every transition on the screen, wrap in
|
|
15
|
+
// `<TransitionTable>` instead.
|
|
16
|
+
|
|
17
|
+
interface Match {
|
|
18
|
+
role: string;
|
|
19
|
+
name: string;
|
|
20
|
+
under?: Match;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
interface Props {
|
|
24
|
+
trigger: Match;
|
|
25
|
+
on?: string;
|
|
26
|
+
to?: string;
|
|
27
|
+
}
|
|
28
|
+
const { trigger, on, to } = Astro.props;
|
|
29
|
+
---
|
|
30
|
+
<div
|
|
31
|
+
class="annot-transition"
|
|
32
|
+
data-trigger-role={trigger.role}
|
|
33
|
+
data-trigger-name={trigger.name}
|
|
34
|
+
data-on={on}
|
|
35
|
+
data-to={to}
|
|
36
|
+
>
|
|
37
|
+
<div class="annot-transition-summary">
|
|
38
|
+
<code class="annot-transition-trigger">{trigger.name}</code>
|
|
39
|
+
{on && <span class="annot-transition-event">{on}</span>}
|
|
40
|
+
{to && (
|
|
41
|
+
<span class="annot-transition-arrow" aria-hidden="true">→</span>
|
|
42
|
+
)}
|
|
43
|
+
{to && <code class="annot-transition-target">{to}</code>}
|
|
44
|
+
</div>
|
|
45
|
+
<div class="annot-transition-body">
|
|
46
|
+
<slot />
|
|
47
|
+
</div>
|
|
48
|
+
</div>
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
---
|
|
2
|
+
// `<TransitionGraph>` — Mermaid-rendered cross-screen graph.
|
|
3
|
+
//
|
|
4
|
+
// MDX usage (typically in a `role: list` doc):
|
|
5
|
+
// <TransitionGraph entries={...} title="Auth flow" />
|
|
6
|
+
//
|
|
7
|
+
// Emits a `<pre class="mermaid">` block containing a Mermaid
|
|
8
|
+
// flowchart definition. Rendering happens client-side via the
|
|
9
|
+
// Mermaid runtime the consumer adds to their Astro site
|
|
10
|
+
// (`<script type="module" src="https://cdn.jsdelivr.net/npm/mermaid/dist/mermaid.esm.min.mjs">`),
|
|
11
|
+
// or build-time via Mermaid CLI / `@napi-rs/canvas` if the
|
|
12
|
+
// consumer opts in.
|
|
13
|
+
|
|
14
|
+
export interface GraphEdge {
|
|
15
|
+
from: string;
|
|
16
|
+
to: string;
|
|
17
|
+
label?: string;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
interface Props {
|
|
21
|
+
entries: GraphEdge[];
|
|
22
|
+
title?: string;
|
|
23
|
+
direction?: "TB" | "LR" | "BT" | "RL";
|
|
24
|
+
}
|
|
25
|
+
const { entries, title, direction = "TB" } = Astro.props;
|
|
26
|
+
|
|
27
|
+
const escape = (id: string): string => id.replace(/[^A-Za-z0-9_]/g, "_");
|
|
28
|
+
|
|
29
|
+
const mermaid = ["flowchart " + direction];
|
|
30
|
+
if (title) {
|
|
31
|
+
mermaid.push(`%% ${title.replace(/\r?\n/g, " ")}`);
|
|
32
|
+
}
|
|
33
|
+
for (const edge of entries) {
|
|
34
|
+
const from = escape(edge.from);
|
|
35
|
+
const to = escape(edge.to);
|
|
36
|
+
const label = edge.label
|
|
37
|
+
? `|${edge.label.replace(/\|/g, "|")}|`
|
|
38
|
+
: "";
|
|
39
|
+
mermaid.push(` ${from}[${edge.from}] -->${label} ${to}[${edge.to}]`);
|
|
40
|
+
}
|
|
41
|
+
const definition = mermaid.join("\n");
|
|
42
|
+
---
|
|
43
|
+
<figure class="annot-transition-graph" data-direction={direction}>
|
|
44
|
+
{title && <figcaption>{title}</figcaption>}
|
|
45
|
+
<pre class="mermaid">{definition}</pre>
|
|
46
|
+
</figure>
|