@ingcreators/annot-product-docs-astro 0.2.2 → 0.3.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.
@@ -0,0 +1,202 @@
1
+ ---
2
+ // `<AnnotEditorIframeModal>` — Phase 5e of
3
+ // `docs/plans/living-spec-authoring-roadmap.md`. The inline-
4
+ // mode counterpart to `<AnnotEditButton>`'s default newTab
5
+ // behaviour. Mount once per page (typically in the docs site's
6
+ // layout) and any `<AnnotEditButton mode="inline">` on the
7
+ // same page opens the modal instead of a new tab.
8
+ //
9
+ // Lazy-loaded: the modal HTML + script only ship if this
10
+ // component is mounted. Pages that only use `mode="newTab"`
11
+ // (the default per OQ-09) never download the modal code.
12
+ // `<AnnotEditButton mode="inline">` on a page without
13
+ // `<AnnotEditorIframeModal />` gracefully degrades to newTab
14
+ // + a one-time `console.warn`.
15
+ //
16
+ // MDX usage in a layout:
17
+ //
18
+ // ---
19
+ // import { AnnotEditorIframeModal } from "@ingcreators/annot-product-docs-astro/components/AnnotEditorIframeModal.astro";
20
+ // ---
21
+ // <slot />
22
+ // <AnnotEditorIframeModal />
23
+ //
24
+ // Then any `<AnnotEditButton mode="inline" ... />` in MDX
25
+ // opens the modal on click. The modal listens for
26
+ // `EditCommitted` / `EditAbandoned` / `ResizeNeeded` via the
27
+ // 5c postMessage dispatcher and dismisses itself accordingly.
28
+
29
+ interface Props {
30
+ /** Modal aria-label. Overridable for localisation. */
31
+ ariaLabel?: string;
32
+ }
33
+
34
+ const { ariaLabel = "Annot editor" } = Astro.props;
35
+ ---
36
+
37
+ <dialog
38
+ class="annot-editor-iframe-modal"
39
+ data-annot-edit-modal-root
40
+ aria-label={ariaLabel}
41
+ >
42
+ <iframe
43
+ class="annot-editor-iframe-modal-frame"
44
+ title={ariaLabel}
45
+ allow="clipboard-read; clipboard-write"
46
+ referrerpolicy="origin"
47
+ ></iframe>
48
+ </dialog>
49
+
50
+ <script>
51
+ import {
52
+ createEmbedHostMessenger,
53
+ type EmbedEvent,
54
+ type EmbedMessenger,
55
+ } from "@ingcreators/annot-embed-protocol";
56
+
57
+ interface OpenModalOptions {
58
+ readonly cloudUrl: string;
59
+ readonly editorUrl: string;
60
+ }
61
+
62
+ const MAX_HEIGHT_FRACTION = 0.95;
63
+ const DEFAULT_FRAME_HEIGHT_PX = 720;
64
+
65
+ function findModal(): HTMLDialogElement | null {
66
+ return document.querySelector<HTMLDialogElement>(
67
+ "dialog[data-annot-edit-modal-root]",
68
+ );
69
+ }
70
+
71
+ function findFrame(modal: HTMLDialogElement): HTMLIFrameElement | null {
72
+ return modal.querySelector<HTMLIFrameElement>(
73
+ "iframe.annot-editor-iframe-modal-frame",
74
+ );
75
+ }
76
+
77
+ function clampHeight(requested: number): number {
78
+ if (!Number.isFinite(requested) || requested <= 0) {
79
+ return DEFAULT_FRAME_HEIGHT_PX;
80
+ }
81
+ const max = window.innerHeight * MAX_HEIGHT_FRACTION;
82
+ return Math.min(requested, max);
83
+ }
84
+
85
+ function openModal(options: OpenModalOptions): void {
86
+ const modal = findModal();
87
+ if (!modal) {
88
+ // biome-ignore lint/suspicious/noConsole: lazy-mount contract violation surfaces here.
89
+ console.warn(
90
+ "[<AnnotEditorIframeModal>] dialog element not found — was the component mounted on this page?",
91
+ );
92
+ return;
93
+ }
94
+ const frame = findFrame(modal);
95
+ if (!frame) return;
96
+
97
+ frame.style.height = `${DEFAULT_FRAME_HEIGHT_PX}px`;
98
+ frame.src = options.editorUrl;
99
+
100
+ let messenger: EmbedMessenger | null = null;
101
+
102
+ const teardown = (): void => {
103
+ if (messenger) {
104
+ messenger.cleanup();
105
+ messenger = null;
106
+ }
107
+ frame.removeAttribute("src");
108
+ };
109
+
110
+ const onEvent = (event: EmbedEvent): void => {
111
+ if (event.type === "EditCommitted") {
112
+ // Forward to the docs-site's `<AnnotEditCompleteListener>` so the
113
+ // post-edit toast surfaces consistently across newTab + inline
114
+ // modes (Phase 5g).
115
+ document.dispatchEvent(
116
+ new CustomEvent("annot:editor-iframe-committed", {
117
+ detail: {
118
+ editId: event.editId,
119
+ commitSha: event.commitSha,
120
+ branch: event.branch,
121
+ prUrl: event.prUrl,
122
+ },
123
+ }),
124
+ );
125
+ modal.close();
126
+ return;
127
+ }
128
+ if (event.type === "EditAbandoned") {
129
+ modal.close();
130
+ return;
131
+ }
132
+ if (event.type === "ResizeNeeded") {
133
+ frame.style.height = `${clampHeight(event.height)}px`;
134
+ }
135
+ };
136
+
137
+ modal.addEventListener(
138
+ "close",
139
+ () => {
140
+ teardown();
141
+ },
142
+ { once: true },
143
+ );
144
+
145
+ // Set up the host messenger after the iframe loads — its
146
+ // `contentWindow` is null until then. Once loaded, attach
147
+ // the messenger; subsequent postMessages from the cloud
148
+ // editor route through it.
149
+ frame.addEventListener(
150
+ "load",
151
+ () => {
152
+ try {
153
+ messenger = createEmbedHostMessenger({
154
+ frame,
155
+ expectedOrigin: options.cloudUrl,
156
+ onEvent,
157
+ });
158
+ } catch (error) {
159
+ // biome-ignore lint/suspicious/noConsole: surface failed host-messenger setup.
160
+ console.error("[<AnnotEditorIframeModal>] failed to attach host messenger:", error);
161
+ modal.close();
162
+ }
163
+ },
164
+ { once: true },
165
+ );
166
+
167
+ modal.showModal();
168
+ }
169
+
170
+ // Expose the open function for `<AnnotEditButton>`'s inline-
171
+ // mode click handler to call. Keep the surface narrow — just
172
+ // `open` — so future expansion (close-from-outside,
173
+ // post-event listener API) is additive.
174
+ type ModalApi = {
175
+ open: (options: OpenModalOptions) => void;
176
+ };
177
+ (window as unknown as { __annotEditorIframeModal?: ModalApi }).__annotEditorIframeModal =
178
+ { open: openModal };
179
+ </script>
180
+
181
+ <style>
182
+ .annot-editor-iframe-modal {
183
+ width: min(95vw, 1280px);
184
+ max-width: 95vw;
185
+ max-height: 95vh;
186
+ padding: 0;
187
+ border: 0;
188
+ border-radius: 0.6em;
189
+ background: var(--annot-editor-modal-bg, #ffffff);
190
+ box-shadow: 0 0.5em 2em rgba(0, 0, 0, 0.4);
191
+ overflow: hidden;
192
+ }
193
+ .annot-editor-iframe-modal::backdrop {
194
+ background: rgba(0, 0, 0, 0.5);
195
+ }
196
+ .annot-editor-iframe-modal-frame {
197
+ display: block;
198
+ width: 100%;
199
+ min-height: 360px;
200
+ border: 0;
201
+ }
202
+ </style>
@@ -1,27 +1,47 @@
1
1
  ---
2
2
  // `<Screen>` — annotated screenshot block.
3
3
  //
4
- // MDX usage:
4
+ // MDX usage (legacy, inline `<Overlay>` callouts):
5
5
  // <Screen id="login" src="./shots/login.png">
6
6
  // <Overlay match={...} number={1}>...</Overlay>
7
7
  // </Screen>
8
8
  //
9
+ // MDX usage (Phase 2b, yaml-driven callouts):
10
+ // <Screen id="login" src="./shots/login.png"
11
+ // annotations="./annotations/login.yaml">
12
+ // <AnnotCallout for="o1">...</AnnotCallout>
13
+ // </Screen>
14
+ //
9
15
  // The annotated PNG is produced at build time by the Image
10
16
  // 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.
17
+ // `../render.ts`). When `annotations` is set, the service reads
18
+ // the yaml + composes badges from `overlays[]`; otherwise it
19
+ // resolves the inline `<Overlay match>` children. At render time
20
+ // we just emit an `<img>` that points at the cache hash; the
21
+ // Astro pipeline resolves the final URL.
14
22
 
15
23
  interface Props {
16
24
  id: string;
17
25
  src: string;
18
26
  alt?: string;
27
+ /**
28
+ * Phase 2b of `docs/plans/living-spec-authoring-roadmap.md`.
29
+ * Path (relative to the MDX file) to an `.annotations.yaml`
30
+ * file describing the screen's overlays. When set, the Image
31
+ * Service prefers yaml-driven badge composition over the
32
+ * legacy inline `<Overlay>` children.
33
+ */
34
+ annotations?: string;
19
35
  }
20
- const { id, src, alt } = Astro.props;
36
+ const { id, src, alt, annotations } = Astro.props;
21
37
  ---
22
- <figure class="annot-screen" data-screen-id={id}>
38
+ <figure
39
+ class="annot-screen"
40
+ data-screen-id={id}
41
+ data-annotations-path={annotations}
42
+ >
23
43
  <img src={src} alt={alt ?? `Screen ${id}`} loading="lazy" />
24
- <div class="annot-screen-overlays" data-screen-overlays={id}>
44
+ <ol class="annot-screen-overlays" data-screen-overlays={id}>
25
45
  <slot />
26
- </div>
46
+ </ol>
27
47
  </figure>
@@ -0,0 +1,12 @@
1
+ import { EmbedMode } from '@ingcreators/annot-embed-protocol';
2
+ export interface ResolvedEditorConfig {
3
+ readonly embedMode: EmbedMode;
4
+ readonly cloudUrl: string;
5
+ }
6
+ /**
7
+ * Phase 5 OSS-side built-in defaults. Mirror the per-component
8
+ * defaults declared inside `<AnnotEditButton>` (`mode="newTab"`,
9
+ * `cloudUrl="https://annot.work"`) so consumers without the
10
+ * integration installed still see consistent behaviour.
11
+ */
12
+ export declare const ANNOT_EDITOR_CONFIG: ResolvedEditorConfig;
package/dist/index.d.ts CHANGED
@@ -1,7 +1,9 @@
1
1
  export type { CacheKeyInput, FileCache } from './cache.js';
2
2
  export { cacheKey, createFileCache, createMemoryCache, RENDER_PIPELINE_VERSION, } from './cache.js';
3
3
  export type { GraphDirection, GraphEdge, Match, OverlayIntent, ScreenListEntry, TransitionEntry, } from './components/types.js';
4
- export type { ProductDocsIntegrationOptions } from './integration.js';
5
- export { productDocsIntegration } from './integration.js';
4
+ export type { ResolvedEditorConfig } from './editor-config-virtual.js';
5
+ export { ANNOT_EDITOR_CONFIG } from './editor-config-virtual.js';
6
+ export type { ProductDocsEditorOptions, ProductDocsIntegrationOptions, } from './integration.js';
7
+ export { editorConfigVirtualPlugin, productDocsIntegration, resolveEditorConfig, } from './integration.js';
6
8
  export type { RenderAnnotatedScreenOptions, RenderResult, } from './render.js';
7
- export { parseSnapshotBoxes, renderAnnotatedScreen, resolveMdxAnnotations, svgFromBboxAnnotations, } from './render.js';
9
+ export { renderAnnotatedScreen, resolveMdxAnnotations, svgFromBboxAnnotations } from './render.js';
package/dist/index.js CHANGED
@@ -1,18 +1,179 @@
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";
2
- //#region src/integration.ts
3
- var c = "@ingcreators/annot-product-docs-astro";
4
- function l(e = {}) {
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 { burnRedactions as c, createAnnotator as l } from "@ingcreators/annot-annotator";
5
+ import { readElementTreePng as u } from "@ingcreators/annot-core";
6
+ import { buildBadgeAnnotations as d, buildBadgeAnnotationsFromYaml as f, buildRasterRedactRegionsFromYaml as p, buildShapeAnnotationsFromYaml as m, elementTreeToBoxedEntries as h, emptyAnnotationsSvg as g, parseAnnotationsYaml as _, parseMdxFile as v, parseSnapshotBoxes as y, resolveMdxAnnotations as b, svgFromBboxAnnotations as x, svgFromBboxAnnotations as S, warnLegacyOverlay as C } from "@ingcreators/annot-product-docs";
7
+ //#region src/cache.ts
8
+ var w = 2;
9
+ function T(t) {
10
+ let n = e("sha256");
11
+ return n.update("v2\n"), n.update(t.mdxSource), n.update("\0"), n.update(t.screenId), t.editable && n.update("\0editable"), t.annotationsYamlSource !== void 0 && (n.update("\0annotationsYaml\0"), n.update(t.annotationsYamlSource)), n.digest("hex");
12
+ }
13
+ function E(e) {
14
+ return {
15
+ dir: e,
16
+ async get(t) {
17
+ try {
18
+ let r = await n(o(e, `${t}.png`));
19
+ return new Uint8Array(r.buffer, r.byteOffset, r.byteLength);
20
+ } catch (e) {
21
+ if (e.code === "ENOENT") return null;
22
+ throw e;
23
+ }
24
+ },
25
+ async set(n, a) {
26
+ let s = o(e, `${n}.png`);
27
+ await t(i(s), { recursive: !0 }), await r(s, a);
28
+ }
29
+ };
30
+ }
31
+ function D() {
32
+ let e = /* @__PURE__ */ new Map();
33
+ return {
34
+ dir: ":memory:",
35
+ async get(t) {
36
+ return e.get(t) ?? null;
37
+ },
38
+ async set(t, n) {
39
+ e.set(t, n);
40
+ }
41
+ };
42
+ }
43
+ //#endregion
44
+ //#region src/editor-config-virtual.ts
45
+ var O = {
46
+ embedMode: "newTab",
47
+ cloudUrl: "https://annot.work"
48
+ }, k = "@ingcreators/annot-product-docs-astro", A = {
49
+ embedMode: "newTab",
50
+ cloudUrl: "https://annot.work"
51
+ }, j = "virtual:annot-docs/editor-config", M = `\0${j}`, N = "editor-config-virtual";
52
+ function P(e) {
53
+ return {
54
+ name: "annot-docs:editor-config-virtual",
55
+ enforce: "pre",
56
+ resolveId(e) {
57
+ return e === j || e.endsWith(`${N}.ts`) || e.endsWith(`${N}.js`) ? M : null;
58
+ },
59
+ load(t) {
60
+ return t === M ? `export const ANNOT_EDITOR_CONFIG = ${JSON.stringify(e)};\n` : null;
61
+ }
62
+ };
63
+ }
64
+ function F(e) {
65
+ return {
66
+ embedMode: e?.embedMode ?? A.embedMode,
67
+ cloudUrl: e?.cloudUrl ?? A.cloudUrl
68
+ };
69
+ }
70
+ function I(e = {}) {
5
71
  let t = {
6
72
  contentDir: e.contentDir ?? "docs",
7
73
  configPath: e.configPath ?? "annot-docs.config.ts",
74
+ editor: e.editor,
8
75
  verbose: e.verbose ?? !1
9
- };
76
+ }, n = F(t.editor);
10
77
  return {
11
- name: c,
12
- hooks: { "astro:config:setup": ({ logger: e }) => {
13
- t.verbose && e.info(`${c} installed — contentDir=${t.contentDir} configPath=${t.configPath}`);
78
+ name: k,
79
+ hooks: { "astro:config:setup": ({ logger: e, updateConfig: r }) => {
80
+ t.verbose && e.info(`${k} installed — contentDir=${t.contentDir} configPath=${t.configPath} editor.embedMode=${n.embedMode} editor.cloudUrl=${n.cloudUrl}`), r({ vite: { plugins: [P(n)] } });
14
81
  } }
15
82
  };
16
83
  }
17
84
  //#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 };
85
+ //#region src/render.ts
86
+ async function L(e) {
87
+ let t = e.cwd ?? process.cwd(), n = a(e.mdxPath) ? e.mdxPath : s(t, e.mdxPath), r = await v(n);
88
+ if (!r) throw Error(`renderAnnotatedScreen: ${e.mdxPath} has no \`annot:\` frontmatter — cannot render.`);
89
+ let o = r.screens.find((t) => t.id === e.screenId);
90
+ if (!o) throw Error(`renderAnnotatedScreen: ${e.mdxPath} has no <Screen id="${e.screenId}"> block.`);
91
+ let u = R(e.editable), _ = i(n), b = await B(o.annotations, _), x = T({
92
+ mdxSource: r.source,
93
+ screenId: e.screenId,
94
+ editable: u !== null,
95
+ annotationsYamlSource: b?.source
96
+ });
97
+ if (e.cache) {
98
+ let t = await e.cache.get(x);
99
+ if (t) return {
100
+ bytes: t,
101
+ fromCache: !0,
102
+ hadBoundingBoxes: !0
103
+ };
104
+ }
105
+ let w = e.basePngBytes ?? await V(r, o.src, _), E = H(w), D, O = z(w);
106
+ D = O ? h(O) : y(r.commentBlocks.snapshot ?? "");
107
+ let k = b && b.file.annotations ? p(b.file.annotations, D) : [], A = k.length > 0 ? await c(w, k) : w;
108
+ !b && o.overlays.length > 0 && C({
109
+ mdxPath: e.mdxPath,
110
+ screenId: e.screenId,
111
+ overlayCount: o.overlays.length
112
+ });
113
+ let j = b ? f(b.file.overlays, D, E) : d(o.overlays, D, E), M = [...b && b.file.annotations ? m(b.file.annotations, D, E) : [], ...j], N, P;
114
+ if (M.length === 0) if (P = k.length > 0, u !== null) {
115
+ let e = `data:image/png;base64,${Buffer.from(A).toString("base64")}`;
116
+ N = l({ loadSystemFonts: !0 }).toEditablePng({
117
+ originalDataUrl: e,
118
+ annotationsSvg: g(),
119
+ width: E.width,
120
+ height: E.height,
121
+ tags: u.tags
122
+ });
123
+ } else N = A;
124
+ else {
125
+ let e = `data:image/png;base64,${Buffer.from(A).toString("base64")}`, t = S(M), n = l({ loadSystemFonts: !0 }), r = {
126
+ originalDataUrl: e,
127
+ annotationsSvg: t,
128
+ width: E.width,
129
+ height: E.height
130
+ };
131
+ N = u === null ? n.toPng(r) : n.toEditablePng({
132
+ ...r,
133
+ tags: u.tags
134
+ }), P = !0;
135
+ }
136
+ return e.cache && await e.cache.set(x, N), {
137
+ bytes: N,
138
+ fromCache: !1,
139
+ hadBoundingBoxes: P
140
+ };
141
+ }
142
+ function R(e) {
143
+ return e ? e === !0 ? {} : e : null;
144
+ }
145
+ function z(e) {
146
+ try {
147
+ return u(e);
148
+ } catch {
149
+ return null;
150
+ }
151
+ }
152
+ async function B(e, t) {
153
+ if (!e) return null;
154
+ let r = a(e) ? e : s(t, e), i;
155
+ try {
156
+ i = await n(r, "utf8");
157
+ } catch (t) {
158
+ throw Error(`renderAnnotatedScreen: <Screen annotations="${e}"> — failed to read ${r}: ${t.message}`);
159
+ }
160
+ return {
161
+ file: _(i),
162
+ source: i
163
+ };
164
+ }
165
+ async function V(e, t, r) {
166
+ if (!t) throw Error("renderAnnotatedScreen: <Screen src=...> is required — cannot render without a base image.");
167
+ let i = await n(a(t) ? t : s(r, t));
168
+ return new Uint8Array(i.buffer, i.byteOffset, i.byteLength);
169
+ }
170
+ function H(e) {
171
+ if (e.length < 24) throw Error("renderAnnotatedScreen: base PNG too short to contain IHDR.");
172
+ let t = new DataView(e.buffer, e.byteOffset, e.byteLength);
173
+ return {
174
+ width: t.getUint32(16, !1),
175
+ height: t.getUint32(20, !1)
176
+ };
177
+ }
178
+ //#endregion
179
+ export { O as ANNOT_EDITOR_CONFIG, w as RENDER_PIPELINE_VERSION, T as cacheKey, E as createFileCache, D as createMemoryCache, P as editorConfigVirtualPlugin, I as productDocsIntegration, L as renderAnnotatedScreen, F as resolveEditorConfig, b as resolveMdxAnnotations, x as svgFromBboxAnnotations };
@@ -1,4 +1,27 @@
1
+ import { EmbedMode } from '@ingcreators/annot-embed-protocol';
1
2
  import { AstroIntegration } from 'astro';
3
+ import { ResolvedEditorConfig } from './editor-config-virtual.js';
4
+ export interface EditorConfigVitePlugin {
5
+ readonly name: string;
6
+ readonly enforce?: "pre" | "post";
7
+ readonly resolveId: (id: string) => string | null;
8
+ readonly load: (id: string) => string | null;
9
+ }
10
+ export interface ProductDocsEditorOptions {
11
+ /**
12
+ * Default embed mode for every `<AnnotEditButton>` in the
13
+ * site. Per-call `mode` prop wins when set. Defaults to
14
+ * `"newTab"`.
15
+ */
16
+ embedMode?: EmbedMode;
17
+ /**
18
+ * Cloud editor origin override for on-prem deployments
19
+ * (e.g. `"https://annot.internal.example.com"`). Per-call
20
+ * `cloudUrl` prop wins when set. Defaults to
21
+ * `"https://annot.work"`.
22
+ */
23
+ cloudUrl?: string;
24
+ }
2
25
  export interface ProductDocsIntegrationOptions {
3
26
  /**
4
27
  * Directory (relative to the Astro project root) that contains
@@ -13,6 +36,11 @@ export interface ProductDocsIntegrationOptions {
13
36
  * `meta` defaults + per-book template lookups.
14
37
  */
15
38
  configPath?: string;
39
+ /**
40
+ * Project-wide `<AnnotEditButton>` defaults. Phase 5f of
41
+ * `docs/plans/living-spec-authoring-roadmap.md`.
42
+ */
43
+ editor?: ProductDocsEditorOptions;
16
44
  /**
17
45
  * If `true`, the integration logs a debug line on every
18
46
  * config:setup. Useful for verifying the integration is
@@ -20,6 +48,28 @@ export interface ProductDocsIntegrationOptions {
20
48
  */
21
49
  verbose?: boolean;
22
50
  }
51
+ /**
52
+ * Vite plugin that:
53
+ *
54
+ * 1. Generates a virtual module (`virtual:annot-docs/editor-config`)
55
+ * exporting the resolved editor config.
56
+ *
57
+ * 2. Aliases every import of the package's
58
+ * `editor-config-virtual.ts` to the virtual module so
59
+ * `<AnnotEditButton>` picks up the project-wide defaults.
60
+ *
61
+ * Exported here for vitest. Consumers don't need to import it
62
+ * directly — `productDocsIntegration` wires it up automatically.
63
+ */
64
+ export declare function editorConfigVirtualPlugin(resolved: ResolvedEditorConfig): EditorConfigVitePlugin;
65
+ /**
66
+ * Merge the per-call editor options into the built-in defaults.
67
+ * Future revision (Phase 5f follow-up if needed) also reads
68
+ * `annot-docs.config.ts`'s `editor` config + merges in the
69
+ * middle of this precedence ladder; today the integration
70
+ * option is the only override layer.
71
+ */
72
+ export declare function resolveEditorConfig(editor: ProductDocsEditorOptions | undefined): ResolvedEditorConfig;
23
73
  /**
24
74
  * Astro integration factory. Drop into `astro.config.mjs`:
25
75
  *
@@ -28,19 +78,19 @@ export interface ProductDocsIntegrationOptions {
28
78
  * import { productDocsIntegration } from "@ingcreators/annot-product-docs-astro";
29
79
  *
30
80
  * export default defineConfig({
31
- * integrations: [productDocsIntegration()],
81
+ * integrations: [
82
+ * productDocsIntegration({
83
+ * editor: {
84
+ * embedMode: "newTab",
85
+ * cloudUrl: "https://annot.internal.example.com",
86
+ * },
87
+ * }),
88
+ * ],
32
89
  * });
33
90
  * ```
34
- *
35
- * Phase 2 PR 1 ships the scaffold — the hooks are no-ops aside
36
- * from optional verbose logging. PR 2 adds the Image Service;
37
- * PR 3 wires up the seven docs components.
38
91
  */
39
92
  export declare function productDocsIntegration(options?: ProductDocsIntegrationOptions): AstroIntegration;
40
93
  /**
41
94
  * Default-export shape some Astro integration authors prefer.
42
- * Both `import productDocsIntegration from ...` (default) and
43
- * `import { productDocsIntegration }` (named) resolve to the
44
- * same factory; consumers can pick whichever feels natural.
45
95
  */
46
96
  export default productDocsIntegration;
@@ -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';