@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.
@@ -0,0 +1,291 @@
1
+ import { MatchKey, OverlayIntent } from './types.js';
2
+ /**
3
+ * Current schema version. Per OQ-01 of the roadmap, future v2 / v3
4
+ * bumps go through strict version dispatch (the parser refuses
5
+ * unknown majors); within v1, additive optional fields are allowed
6
+ * without a version bump.
7
+ */
8
+ export declare const ANNOTATIONS_YAML_VERSION: 1;
9
+ /**
10
+ * One numbered-badge overlay entry. Phase 2 supports `numberedBadge`
11
+ * only; Phase 3 of the roadmap extends `OverlayEntry` (and adds a
12
+ * sibling `AnnotationSpec`) for the full annotation palette
13
+ * (`rect` / `arrow` / `text` / `freehand` / `redact` / etc.).
14
+ *
15
+ * The shape mirrors the legacy {@link OverlaySpec} from `./types.ts`
16
+ * minus the inline `body` field — descriptions live in MDX
17
+ * `<AnnotCallout for="id">body</AnnotCallout>` blocks now, not in
18
+ * the yaml.
19
+ */
20
+ export interface OverlayEntry {
21
+ /**
22
+ * Stable identifier targeted by `<AnnotCallout for="id">` in MDX.
23
+ * Author-supplied; the migration CLI (Phase 2d) defaults to
24
+ * `o1` / `o2` / … in `overlays[]` index order.
25
+ */
26
+ id: string;
27
+ /**
28
+ * Discriminator — Phase 2 ships `numberedBadge` only. Adding a
29
+ * new kind here means extending the renderer in `AnnotFigure.astro`
30
+ * AND the drift validator in `./drift.ts`.
31
+ */
32
+ kind: "numberedBadge";
33
+ /** Match key used to locate the element in the page's ElementTree. */
34
+ match: MatchKey;
35
+ /** Optional intent flavour for the rendered callout / border. */
36
+ intent?: OverlayIntent;
37
+ /**
38
+ * Numeric badge label rendered on the image. Author-supplied so
39
+ * the same yaml renders consistent numbering across re-captures
40
+ * even if `overlays[]` order changes. Required by the Astro
41
+ * Image Service today; future kinds may make it optional.
42
+ */
43
+ number: number;
44
+ }
45
+ /**
46
+ * Top-level wire shape for an `.annotations.yaml` file.
47
+ *
48
+ * `overlays[]` carries the numbered-badge entries the docs flow
49
+ * pairs with `<AnnotCallout for="id">` description blocks
50
+ * (Phase 2). `annotations[]` carries the wider visual palette
51
+ * (Phase 3 of the roadmap) — entries are pure visual marking
52
+ * with no MDX description slot; their `id` is self-contained
53
+ * and is NEVER targeted by `<AnnotCallout for>`.
54
+ *
55
+ * Both arrays default to empty when absent. Within v1 the
56
+ * `annotations[]` extension is additive: pre-Phase-3 files
57
+ * (no `annotations` key) keep parsing unchanged.
58
+ *
59
+ * `meta` is an open string map reserved for downstream tooling
60
+ * (book grouping, generator IDs, etc.) — readers must ignore
61
+ * unknown keys.
62
+ */
63
+ export interface AnnotationsFile {
64
+ version: typeof ANNOTATIONS_YAML_VERSION;
65
+ overlays: OverlayEntry[];
66
+ /**
67
+ * Phase 3 of `docs/plans/living-spec-authoring-roadmap.md`.
68
+ * Optional sibling of `overlays[]` carrying the full visual
69
+ * palette (arrows / rects / text labels / callouts / freehand
70
+ * strokes / redact rects / focus masks). Each entry composes
71
+ * onto the annotated PNG but has no MDX `<AnnotCallout>`
72
+ * counterpart.
73
+ */
74
+ annotations?: AnnotationSpec[];
75
+ /** Optional free-form metadata. Readers ignore unknown keys. */
76
+ meta?: Record<string, string>;
77
+ }
78
+ /**
79
+ * Common style fields any annotation kind may carry. Mirrors the
80
+ * `AnnotationStyle` shape in `@ingcreators/annot-annotator`'s DSL
81
+ * so the yaml → `BboxAnnotation` mapper (Phase 3c) flows straight
82
+ * through. Inlined here rather than imported so this Tier A module
83
+ * stays free of the annotator dependency at parse time.
84
+ */
85
+ export interface AnnotationStyleFields {
86
+ /** Semantic colour shorthand. Renderer maps to design-system tokens. */
87
+ intent?: OverlayIntent;
88
+ /** CSS stroke colour override (wins over `intent`-derived default). */
89
+ stroke?: string;
90
+ /** Stroke width in image pixels. */
91
+ strokeWidth?: number;
92
+ /** CSS fill colour override (wins over `intent`-derived default). */
93
+ fill?: string;
94
+ /** CSS text-fill colour override. */
95
+ color?: string;
96
+ }
97
+ /** Axis-aligned bbox in page (image) pixels. */
98
+ export interface AnnotationBBox {
99
+ x: number;
100
+ y: number;
101
+ width: number;
102
+ height: number;
103
+ }
104
+ /** Point in page (image) pixels. */
105
+ export interface AnnotationPoint {
106
+ x: number;
107
+ y: number;
108
+ }
109
+ /**
110
+ * Where a text label sits relative to its anchor element. The
111
+ * renderer picks a sensible offset (image-pixel padding) per
112
+ * position. Default `above`.
113
+ */
114
+ export type TextAnchorPosition = "above" | "below" | "left" | "right" | "center";
115
+ /**
116
+ * Outline (or filled) rect that highlights one element, a group of
117
+ * elements (bbox = union), or a free-coord region.
118
+ *
119
+ * Exactly one of `match` / `coversElements` / `bbox` is required;
120
+ * the parser rejects entries that supply zero or multiple.
121
+ */
122
+ export interface RectAnnotation extends AnnotationStyleFields {
123
+ id: string;
124
+ kind: "rect";
125
+ match?: MatchKey;
126
+ coversElements?: MatchKey[];
127
+ bbox?: AnnotationBBox;
128
+ }
129
+ /**
130
+ * Circle centred on an element (radius derived from the element's
131
+ * bbox half-width) or on a free-coord point.
132
+ */
133
+ export interface CircleAnnotation extends AnnotationStyleFields {
134
+ id: string;
135
+ kind: "circle";
136
+ /** Element to centre the circle on. Mutually exclusive with `center`. */
137
+ match?: MatchKey;
138
+ /** Free-coord centre. Mutually exclusive with `match`. */
139
+ center?: AnnotationPoint;
140
+ /**
141
+ * Radius in image pixels. Required for the `center` form;
142
+ * optional override for the `match` form (defaults to half the
143
+ * element's bbox width).
144
+ */
145
+ radius?: number;
146
+ }
147
+ /** Arrow endpoint — either match-anchored or free-coord. */
148
+ export type ArrowEndpoint = {
149
+ match: MatchKey;
150
+ } | {
151
+ point: AnnotationPoint;
152
+ };
153
+ /** Arrow between two endpoints. */
154
+ export interface ArrowAnnotation extends AnnotationStyleFields {
155
+ id: string;
156
+ kind: "arrow";
157
+ from: ArrowEndpoint;
158
+ to: ArrowEndpoint;
159
+ }
160
+ /**
161
+ * Text label anchored to an element or placed at a free-coord
162
+ * point. Exactly one of `anchor` / `at` is required.
163
+ */
164
+ export interface TextAnnotation extends AnnotationStyleFields {
165
+ id: string;
166
+ kind: "text";
167
+ text: string;
168
+ /** Match-anchored. The renderer offsets per `position`. */
169
+ anchor?: {
170
+ match: MatchKey;
171
+ position?: TextAnchorPosition;
172
+ };
173
+ /** Free-coord placement. */
174
+ at?: AnnotationPoint;
175
+ /** Font size in image pixels. Default `14`. */
176
+ fontSize?: number;
177
+ }
178
+ /** Callout target — either match-anchored or free-coord. */
179
+ export type CalloutTarget = {
180
+ match: MatchKey;
181
+ } | {
182
+ bbox: AnnotationBBox;
183
+ };
184
+ /**
185
+ * Callout = target outline + caption text + arrow from caption to
186
+ * target. The caption sits at `at`; the target highlights at
187
+ * `target` (resolved to a bbox via match or supplied directly).
188
+ */
189
+ export interface CalloutAnnotation extends AnnotationStyleFields {
190
+ id: string;
191
+ kind: "callout";
192
+ text: string;
193
+ target: CalloutTarget;
194
+ /** Caption text position. Required for now (auto-placement is a future enhancement). */
195
+ at: AnnotationPoint;
196
+ }
197
+ /**
198
+ * Free-form path. Always free-coord — UI changes may misalign
199
+ * it; the editor's drift detector intentionally skips it.
200
+ */
201
+ export interface FreehandAnnotation extends AnnotationStyleFields {
202
+ id: string;
203
+ kind: "freehand";
204
+ /** SVG path data — `M` / `L` / `C` / `Q` / `Z` commands. */
205
+ path: string;
206
+ }
207
+ /**
208
+ * Style of a redact rect. `solid` paints an opaque rect over the
209
+ * region; `mosaic` and `blur` are raster transforms applied to
210
+ * the underlying screenshot bitmap before SVG-fragment
211
+ * composition (Phase 3 follow-up — see Astro Image Service for
212
+ * the rendering path).
213
+ */
214
+ export type RedactAnnotationStyle = "solid" | "mosaic" | "blur";
215
+ /**
216
+ * Opaque or scrambled rect obscuring content. `solid` renders as
217
+ * a filled rect via the SVG-fragment path; `mosaic` and `blur`
218
+ * trigger a raster pre-processing pass in the Astro Image Service
219
+ * (Phase 3 follow-up — Phase 3a–3d shipped `solid` only).
220
+ */
221
+ export interface RedactAnnotation extends AnnotationStyleFields {
222
+ id: string;
223
+ kind: "redact";
224
+ match?: MatchKey;
225
+ bbox?: AnnotationBBox;
226
+ /**
227
+ * Defaults to `solid` at render time when omitted. Phase 3 follow-up
228
+ * (3f) widened this from the original `"solid"`-only to also
229
+ * accept `"mosaic"` and `"blur"` — both routed through
230
+ * `burnRedactions` from `@ingcreators/annot-annotator`.
231
+ */
232
+ style?: RedactAnnotationStyle;
233
+ }
234
+ /** Focus-mask cutout — element-anchored (with optional padding) or free-coord bbox. */
235
+ export type FocusMaskCutout = {
236
+ match: MatchKey;
237
+ padding?: number;
238
+ } | {
239
+ bbox: AnnotationBBox;
240
+ };
241
+ /**
242
+ * Darken everything EXCEPT the cutout region. The cutout draws
243
+ * crisp; everything outside is dimmed by `dimColor`
244
+ * (default `rgba(0,0,0,0.5)`).
245
+ */
246
+ export interface FocusMaskAnnotation extends AnnotationStyleFields {
247
+ id: string;
248
+ kind: "focusMask";
249
+ cutout: FocusMaskCutout;
250
+ /** CSS colour for the dim layer. Default `rgba(0,0,0,0.5)`. */
251
+ dimColor?: string;
252
+ }
253
+ /**
254
+ * Discriminated union across every annotation kind Phase 3
255
+ * supports. Parser dispatches on `kind`; serializer round-trips
256
+ * to YAML and back to a byte-equivalent object.
257
+ */
258
+ export type AnnotationSpec = RectAnnotation | CircleAnnotation | ArrowAnnotation | TextAnnotation | CalloutAnnotation | FreehandAnnotation | RedactAnnotation | FocusMaskAnnotation;
259
+ /** The `kind` literal type. */
260
+ export type AnnotationKind = AnnotationSpec["kind"];
261
+ /** Set of every supported `kind`. Reused by the parser + drift detector. */
262
+ export declare const ANNOTATION_KINDS: readonly AnnotationKind[];
263
+ /**
264
+ * Error thrown when a YAML payload cannot be parsed into the
265
+ * documented schema. Carries the offending source so callers can
266
+ * surface line / column context (the CLI prefixes path + heading).
267
+ */
268
+ export declare class AnnotationsYamlError extends Error {
269
+ readonly source?: string | undefined;
270
+ constructor(message: string, source?: string | undefined);
271
+ }
272
+ /**
273
+ * Parse a YAML string into an `AnnotationsFile`. Throws
274
+ * {@link AnnotationsYamlError} on shape violations: missing
275
+ * `version`, unsupported major, missing required `id` / `kind` /
276
+ * `match` / `number` on an overlay entry.
277
+ *
278
+ * The parser is permissive about optional fields: unknown keys at
279
+ * any level are dropped silently (forward compat within a major).
280
+ */
281
+ export declare function parseAnnotationsYaml(source: string): AnnotationsFile;
282
+ /**
283
+ * Serialize an `AnnotationsFile` back to YAML. Output is
284
+ * deterministic (`version` → `overlays` → `meta`), suitable for
285
+ * round-trip + git-diff use.
286
+ *
287
+ * The serializer rejects entries that wouldn't round-trip through
288
+ * `parseAnnotationsYaml` — kind is checked at the type level, but
289
+ * missing `id` / `match` / `number` at runtime throws.
290
+ */
291
+ export declare function serializeAnnotationsYaml(file: AnnotationsFile): string;
package/dist/config.d.ts CHANGED
@@ -5,62 +5,20 @@ export declare const annotFrontmatterSchema: z.ZodObject<{
5
5
  title: z.ZodOptional<z.ZodString>;
6
6
  purpose: z.ZodOptional<z.ZodString>;
7
7
  meta: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
8
- xlsx: z.ZodOptional<z.ZodEffects<z.ZodObject<{
8
+ xlsx: z.ZodOptional<z.ZodObject<{
9
9
  book: z.ZodOptional<z.ZodString>;
10
10
  sheet: z.ZodOptional<z.ZodString>;
11
11
  sheets: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
12
- role: z.ZodOptional<z.ZodEnum<["cover", "history", "list", "screen", "reference"]>>;
12
+ role: z.ZodOptional<z.ZodEnum<{
13
+ screen: "screen";
14
+ cover: "cover";
15
+ history: "history";
16
+ list: "list";
17
+ reference: "reference";
18
+ }>>;
13
19
  order: z.ZodOptional<z.ZodNumber>;
14
- }, "strict", z.ZodTypeAny, {
15
- role?: "cover" | "history" | "list" | "screen" | "reference" | undefined;
16
- book?: string | undefined;
17
- sheet?: string | undefined;
18
- sheets?: Record<string, string> | undefined;
19
- order?: number | undefined;
20
- }, {
21
- role?: "cover" | "history" | "list" | "screen" | "reference" | undefined;
22
- book?: string | undefined;
23
- sheet?: string | undefined;
24
- sheets?: Record<string, string> | undefined;
25
- order?: number | undefined;
26
- }>, {
27
- role?: "cover" | "history" | "list" | "screen" | "reference" | undefined;
28
- book?: string | undefined;
29
- sheet?: string | undefined;
30
- sheets?: Record<string, string> | undefined;
31
- order?: number | undefined;
32
- }, {
33
- role?: "cover" | "history" | "list" | "screen" | "reference" | undefined;
34
- book?: string | undefined;
35
- sheet?: string | undefined;
36
- sheets?: Record<string, string> | undefined;
37
- order?: number | undefined;
38
- }>>;
39
- }, "strict", z.ZodTypeAny, {
40
- id: string;
41
- title?: string | undefined;
42
- purpose?: string | undefined;
43
- meta?: Record<string, unknown> | undefined;
44
- xlsx?: {
45
- role?: "cover" | "history" | "list" | "screen" | "reference" | undefined;
46
- book?: string | undefined;
47
- sheet?: string | undefined;
48
- sheets?: Record<string, string> | undefined;
49
- order?: number | undefined;
50
- } | undefined;
51
- }, {
52
- id: string;
53
- title?: string | undefined;
54
- purpose?: string | undefined;
55
- meta?: Record<string, unknown> | undefined;
56
- xlsx?: {
57
- role?: "cover" | "history" | "list" | "screen" | "reference" | undefined;
58
- book?: string | undefined;
59
- sheet?: string | undefined;
60
- sheets?: Record<string, string> | undefined;
61
- order?: number | undefined;
62
- } | undefined;
63
- }>;
20
+ }, z.core.$strict>>;
21
+ }, z.core.$strict>;
64
22
  export declare const annotDocsConfigSchema: z.ZodObject<{
65
23
  meta: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
66
24
  xlsx: z.ZodOptional<z.ZodObject<{
@@ -73,94 +31,18 @@ export declare const annotDocsConfigSchema: z.ZodObject<{
73
31
  list: z.ZodOptional<z.ZodString>;
74
32
  screen: z.ZodOptional<z.ZodString>;
75
33
  reference: z.ZodOptional<z.ZodString>;
76
- }, "strict", z.ZodTypeAny, {
77
- cover?: string | undefined;
78
- history?: string | undefined;
79
- list?: string | undefined;
80
- screen?: string | undefined;
81
- reference?: string | undefined;
82
- }, {
83
- cover?: string | undefined;
84
- history?: string | undefined;
85
- list?: string | undefined;
86
- screen?: string | undefined;
87
- reference?: string | undefined;
88
- }>>;
89
- }, "strict", z.ZodTypeAny, {
90
- template?: string | undefined;
91
- templateSheets?: {
92
- cover?: string | undefined;
93
- history?: string | undefined;
94
- list?: string | undefined;
95
- screen?: string | undefined;
96
- reference?: string | undefined;
97
- } | undefined;
98
- }, {
99
- template?: string | undefined;
100
- templateSheets?: {
101
- cover?: string | undefined;
102
- history?: string | undefined;
103
- list?: string | undefined;
104
- screen?: string | undefined;
105
- reference?: string | undefined;
106
- } | undefined;
107
- }>>>;
108
- }, "strict", z.ZodTypeAny, {
109
- defaultBook?: string | undefined;
110
- books?: Record<string, {
111
- template?: string | undefined;
112
- templateSheets?: {
113
- cover?: string | undefined;
114
- history?: string | undefined;
115
- list?: string | undefined;
116
- screen?: string | undefined;
117
- reference?: string | undefined;
118
- } | undefined;
119
- }> | undefined;
120
- }, {
121
- defaultBook?: string | undefined;
122
- books?: Record<string, {
123
- template?: string | undefined;
124
- templateSheets?: {
125
- cover?: string | undefined;
126
- history?: string | undefined;
127
- list?: string | undefined;
128
- screen?: string | undefined;
129
- reference?: string | undefined;
130
- } | undefined;
131
- }> | undefined;
132
- }>>;
133
- }, "strict", z.ZodTypeAny, {
134
- meta?: Record<string, unknown> | undefined;
135
- xlsx?: {
136
- defaultBook?: string | undefined;
137
- books?: Record<string, {
138
- template?: string | undefined;
139
- templateSheets?: {
140
- cover?: string | undefined;
141
- history?: string | undefined;
142
- list?: string | undefined;
143
- screen?: string | undefined;
144
- reference?: string | undefined;
145
- } | undefined;
146
- }> | undefined;
147
- } | undefined;
148
- }, {
149
- meta?: Record<string, unknown> | undefined;
150
- xlsx?: {
151
- defaultBook?: string | undefined;
152
- books?: Record<string, {
153
- template?: string | undefined;
154
- templateSheets?: {
155
- cover?: string | undefined;
156
- history?: string | undefined;
157
- list?: string | undefined;
158
- screen?: string | undefined;
159
- reference?: string | undefined;
160
- } | undefined;
161
- }> | undefined;
162
- } | undefined;
163
- }>;
34
+ }, z.core.$strict>>;
35
+ }, z.core.$strict>>>;
36
+ }, z.core.$strict>>;
37
+ editor: z.ZodOptional<z.ZodObject<{
38
+ embedMode: z.ZodOptional<z.ZodEnum<{
39
+ newTab: "newTab";
40
+ inline: "inline";
41
+ disabled: "disabled";
42
+ }>>;
43
+ cloudUrl: z.ZodOptional<z.ZodString>;
44
+ }, z.core.$strict>>;
45
+ }, z.core.$strict>;
164
46
  /**
165
47
  * Identity-with-validation helper.
166
48
  *
@@ -171,7 +53,7 @@ export declare const annotDocsConfigSchema: z.ZodObject<{
171
53
  * dropped by `tsc`'s structural matching.
172
54
  */
173
55
  export declare function defineConfig(config: AnnotDocsConfig): AnnotDocsConfig;
174
- export type { AnnotDocsConfig, AnnotFrontmatter, BookConfig, } from './types-config.js';
56
+ export type { AnnotDocsConfig, AnnotEditorConfig, AnnotFrontmatter, BookConfig, } from './types-config.js';
175
57
  /**
176
58
  * Convenience guard: does this AnnotFrontmatter declare a
177
59
  * `screen` role? Useful for filtering MDXs that have `<Screen>`
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Soft-deprecation shim for the legacy inline `<Overlay>` JSX
3
+ * form. Phase 2e of `docs/plans/living-spec-authoring-roadmap.md`.
4
+ *
5
+ * Policy (per OQ-08 of the roadmap):
6
+ *
7
+ * - Phase 2e (this module) — Build / lint paths emit a deduped
8
+ * warning when they encounter a `<Screen>` block whose overlays
9
+ * live inline as `<Overlay>` children rather than yaml + AnnotCallout.
10
+ * The warning points at `annot docs migrate-overlays-to-annotations`
11
+ * so authors can fix it with one command.
12
+ *
13
+ * - Removal target: ~3 months / 2-3 release cycles after Phase 2e
14
+ * lands, tracked in [the roadmap](../../../docs/plans/living-spec-authoring-roadmap.md)
15
+ * timeline. Removal is its own PR that deletes:
16
+ * - `OverlaySpec` from `./types.ts`
17
+ * - `Overlay.astro` from `@ingcreators/annot-product-docs-astro`
18
+ * - The inline-`<Overlay>` walker branch in `./mdx.ts`
19
+ * - The legacy path in `renderAnnotatedScreen` (yaml becomes
20
+ * the only renderer input)
21
+ *
22
+ * Until then, both forms work. The warning is `console.warn`-only
23
+ * — it doesn't fail the build by default (CI gating happens at
24
+ * `annot docs lint --ci`, not at Astro build time).
25
+ */
26
+ export interface LegacyOverlayUsage {
27
+ /** Absolute MDX path (or any stable identifier the caller has). */
28
+ mdxPath: string;
29
+ /** `<Screen id>` of the block using the legacy form. */
30
+ screenId: string;
31
+ /** Number of `<Overlay>` children seen on the screen. */
32
+ overlayCount: number;
33
+ }
34
+ /**
35
+ * Emit the deprecation warning. No-op when the same
36
+ * `(mdxPath, screenId)` pair has already warned in this process.
37
+ *
38
+ * The caller (Astro Image Service, lint CLI) decides WHEN to
39
+ * call this — typically when it sees `screen.overlays.length > 0
40
+ * && screen.annotations === undefined`.
41
+ *
42
+ * @param emit Optional warning emitter. Defaults to `console.warn`
43
+ * so the message lands on whichever transport the
44
+ * host already wires up (Astro logs / CLI stderr / etc.).
45
+ * Tests inject a spy.
46
+ */
47
+ export declare function warnLegacyOverlay(usage: LegacyOverlayUsage, emit?: (line: string) => void): void;
48
+ /**
49
+ * Render the warning message. Exported so tests + bespoke
50
+ * formatters (CI / GitHub Annotations / etc.) can drive it
51
+ * without going through `console.warn`.
52
+ */
53
+ export declare function formatLegacyOverlayWarning(usage: LegacyOverlayUsage): string;
54
+ /**
55
+ * Test-only: reset the per-process dedup cache. Lets tests
56
+ * exercise the dedup path with a clean slate.
57
+ */
58
+ export declare function _resetLegacyOverlayDedupForTests(): void;
package/dist/drift.d.ts CHANGED
@@ -1,7 +1,9 @@
1
+ import { ElementTree } from '@ingcreators/annot-core';
2
+ import { AnnotationSpec, OverlayEntry } from './annotations-yaml.js';
1
3
  import { SnapshotEntry } from './resolver.js';
2
4
  import { MatchKey, ScreenSpec } from './types.js';
3
5
  export type DriftSeverity = "error" | "warning" | "info";
4
- export type DriftKind = "added" | "removed" | "renamed" | "role-changed" | "duplicated" | "attribute-drift";
6
+ export type DriftKind = "added" | "removed" | "renamed" | "role-changed" | "duplicated" | "attribute-drift" | "description-missing" | "description-orphan";
5
7
  export interface DriftFinding {
6
8
  severity: DriftSeverity;
7
9
  kind: DriftKind;
@@ -25,6 +27,43 @@ export interface DetectDriftOptions {
25
27
  storedAttributesYaml?: string;
26
28
  /** Optional verbatim YAML of the freshly-captured attributes. */
27
29
  freshAttributesYaml?: string;
30
+ /**
31
+ * Phase 2c of `docs/plans/living-spec-authoring-roadmap.md`.
32
+ * When the screen carries `annotations="…"`, callers parse the
33
+ * yaml and pass its overlays here. The detector then:
34
+ * - Pulls match keys from this list instead of
35
+ * `screen.overlays` for the match-cycle (removed / renamed /
36
+ * role-changed / duplicated / added).
37
+ * - Emits new cross-ref findings: `description-missing` (yaml
38
+ * id has no `<AnnotCallout for>`) and `description-orphan`
39
+ * (`<AnnotCallout for>` references a yaml id that doesn't
40
+ * exist).
41
+ * Independent of `screen.overlays`: the legacy inline path stays
42
+ * inert when this is set.
43
+ */
44
+ yamlOverlays?: readonly OverlayEntry[];
45
+ /**
46
+ * Phase 3d of `docs/plans/living-spec-authoring-roadmap.md`.
47
+ * Optional sibling to {@link yamlOverlays} for the Phase 3
48
+ * `annotations[]` palette (rect / circle / arrow / text /
49
+ * callout / freehand / redact / focusMask). The detector walks
50
+ * the match keys reachable from each variant via
51
+ * {@link collectMatchKeysFromAnnotation} and feeds them through
52
+ * the same match-cycle as the overlays (`removed` / `renamed` /
53
+ * `role-changed` / `duplicated`). Free-coord entries
54
+ * (`bbox` / `point` / `at` / `path` / `center`) contribute zero
55
+ * match keys and are silently skipped.
56
+ *
57
+ * `annotations[]` IDs are NEVER referenced from `<AnnotCallout for>`
58
+ * — they're self-contained visual marking — so no
59
+ * `description-missing` / `description-orphan` findings fire for
60
+ * this source.
61
+ *
62
+ * Findings from this source attach a `match` derived from the
63
+ * annotation entry so authors can trace which annotation's match
64
+ * key triggered the finding.
65
+ */
66
+ yamlAnnotations?: readonly AnnotationSpec[];
28
67
  }
29
68
  /**
30
69
  * Compute the drift findings for one `<Screen>` block.
@@ -53,6 +92,45 @@ export declare function detectDriftFromYaml(args: {
53
92
  liveSnapshotYaml: string;
54
93
  storedAttributesYaml?: string;
55
94
  freshAttributesYaml?: string;
95
+ /** Phase 2c. See {@link DetectDriftOptions.yamlOverlays}. */
96
+ yamlOverlays?: readonly OverlayEntry[];
97
+ /** Phase 3d. See {@link DetectDriftOptions.yamlAnnotations}. */
98
+ yamlAnnotations?: readonly AnnotationSpec[];
99
+ }): DriftFinding[];
100
+ /**
101
+ * Convert an `ElementTree` (Phase 1a of
102
+ * `docs/plans/living-spec-authoring-roadmap.md`) into the
103
+ * `SnapshotEntry[]` shape the drift detector consumes. Pure data
104
+ * transform — walks the tree depth-first, emits one entry per node
105
+ * that has both `role` and `name` (decorative containers and
106
+ * synthetic roots are skipped, matching the legacy parser's filter).
107
+ *
108
+ * Phase 1i. Exposed for tests + callers that want to construct
109
+ * adapters in front of the existing `detectDrift` API without
110
+ * paying the YAML detour.
111
+ */
112
+ export declare function elementTreeToSnapshotEntries(tree: ElementTree): SnapshotEntry[];
113
+ /**
114
+ * Drift detection driven by an `ElementTree` (the canonical
115
+ * screen-capture model from Phase 1a) instead of the legacy
116
+ * YAML-parsed `SnapshotEntry[]`. Internally converts the tree
117
+ * via `elementTreeToSnapshotEntries` and dispatches to
118
+ * `detectDrift` — both paths produce identical findings for
119
+ * equivalent inputs.
120
+ *
121
+ * Phase 1i. Lets the CLI's `lint` flow consume the new PNG XMP
122
+ * `annot:elementTree` chunk directly (via `readElementTreePng`)
123
+ * without round-tripping through Playwright YAML.
124
+ */
125
+ export declare function detectDriftFromElementTree(args: {
126
+ screen: ScreenSpec;
127
+ liveElementTree: ElementTree;
128
+ storedAttributesYaml?: string;
129
+ freshAttributesYaml?: string;
130
+ /** Phase 2c. See {@link DetectDriftOptions.yamlOverlays}. */
131
+ yamlOverlays?: readonly OverlayEntry[];
132
+ /** Phase 3d. See {@link DetectDriftOptions.yamlAnnotations}. */
133
+ yamlAnnotations?: readonly AnnotationSpec[];
56
134
  }): DriftFinding[];
57
135
  /**
58
136
  * Summarise findings by severity. Used by the CLI to decide an
@@ -65,8 +143,10 @@ export declare function summariseDrift(findings: DriftFinding[]): {
65
143
  };
66
144
  /**
67
145
  * A `<Screen>` is drift-checkable when it has at least one
68
- * `<Overlay>`. Files without `<Screen>` blocks (cover / history /
69
- * list / reference MDXs) are skipped by the lint walker.
146
+ * `<Overlay>` (legacy form), an `annotations` ref (yaml form), or
147
+ * an `<AnnotCallout>` (yaml form, in case the author wrote callouts
148
+ * before the yaml). Files without `<Screen>` blocks (cover / history
149
+ * / list / reference MDXs) are skipped by the lint walker.
70
150
  */
71
151
  export declare function isLintableScreen(screen: ScreenSpec): boolean;
72
152
  /**
@@ -75,3 +155,18 @@ export declare function isLintableScreen(screen: ScreenSpec): boolean;
75
155
  * "skipped cover.mdx" path cheap.
76
156
  */
77
157
  export declare function lintableScreens(screens: ScreenSpec[]): ScreenSpec[];
158
+ /**
159
+ * Walk an `AnnotationSpec` and emit every `MatchKey` reachable
160
+ * from its match-anchored fields. Free-coord variants
161
+ * (bbox-only rect / center-only circle / point-only arrow
162
+ * endpoint / `at`-only text / bbox-only callout target / freehand /
163
+ * bbox-only redact / bbox-only focusMask cutout) contribute zero
164
+ * keys.
165
+ *
166
+ * Phase 3d of `docs/plans/living-spec-authoring-roadmap.md`.
167
+ * Exposed publicly so callers (the CLI, alternative drift
168
+ * detectors, future editor surfaces) can resolve the match keys
169
+ * a Phase 3a `AnnotationSpec[]` exposes without duplicating the
170
+ * per-variant traversal.
171
+ */
172
+ export declare function collectMatchKeysFromAnnotation(spec: AnnotationSpec): MatchKey[];