@ingcreators/annot-annotator 0.1.0 → 0.2.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,21 @@
1
1
  # @ingcreators/annot-annotator
2
2
 
3
+ ## 0.2.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 92378f9: Public DSL surface (since 0.2.0). The annotation DSL that was previously private to `@ingcreators/annot-mcp` now lives on the annotator package so any Annot consumer (test runtimes, AI agents, plugin authors) can use the same vocabulary.
8
+
9
+ New exports:
10
+ - Types: `BBox`, `Point`, `Intent`, `AnnotationStyle`, `BboxAnnotation` (`rect` / `circle` / `arrow` / `text` / `callout` / `raw`), `RawAnnotation`, `BboxRedactRegion`, `RedactStyle`.
11
+ - Converter: `bboxAnnotationsToSvg(annotations)` returns the SVG fragment string `createAnnotator(...).toPng({ annotationsSvg })` accepts.
12
+ - SVG primitives: `rectForBoundingBox`, `arrowBetween`, `textAt`, plus `BoundingBox` / `RectOptions` / `ArrowOptions` / `TextOptions`.
13
+ - JSON Schemas: `SHARED_DEFS`, `BBOX_ANNOTATION_SCHEMA`, `BBOX_REDACT_REGION_SCHEMA` (drop into MCP tool `inputSchema` `$defs` blocks).
14
+
15
+ The `intent` shorthand (`"info"` / `"warning"` / `"error"` / `"success"` / `"neutral"`) resolves to the Annot design system's semantic colour tokens automatically — no more thinking in raw hex values.
16
+
17
+ Marker id prefix in `arrowBetween` changed from `annot-pw-arrow-N` (previous helper in `@ingcreators/annot-playwright`) / `annot-mcp-arrow-N` (previous helper in `@ingcreators/annot-mcp`) to the package-neutral `annot-arrow-N`. Snapshot-on-SVG tests should expect this minor cosmetic delta.
18
+
3
19
  ## 0.1.0
4
20
 
5
21
  ### Minor Changes
package/README.md CHANGED
@@ -1,18 +1,17 @@
1
1
  # `@ingcreators/annot-annotator`
2
2
 
3
+ [![npm](https://img.shields.io/npm/v/@ingcreators/annot-annotator.svg)](https://www.npmjs.com/package/@ingcreators/annot-annotator)
4
+ [![license](https://img.shields.io/npm/l/@ingcreators/annot-annotator.svg)](https://github.com/ingcreators/annot/blob/main/LICENSE)
5
+
3
6
  Headless annotator — produce annotated screenshots from Node without
4
7
  a browser. Reads an `ImageRecord`-shaped input (base image + saved
5
8
  annotations SVG) and emits a PNG or the merged SVG.
6
9
 
7
- > **Status:** Workspace package, not yet published to npm. The public
8
- > API is stable; the package becomes installable in Phase 3 of the
9
- > headless-annotator track (gated on Changesets).
10
- >
11
- > Companion package: `@ingcreators/annot-playwright` (Phase 2 of the
12
- > same track) — a Playwright fixture composing this annotator into
13
- > idiomatic `test.extend({ annotator })` form.
10
+ Companion package: [`@ingcreators/annot-playwright`](https://www.npmjs.com/package/@ingcreators/annot-playwright)
11
+ a Playwright fixture composing this annotator into idiomatic
12
+ `test.extend({ annotator })` form.
14
13
 
15
- ## Install (post-publish)
14
+ ## Install
16
15
 
17
16
  ```sh
18
17
  npm install @ingcreators/annot-annotator
@@ -20,6 +19,10 @@ npm install @ingcreators/annot-annotator
20
19
  pnpm add @ingcreators/annot-annotator
21
20
  ```
22
21
 
22
+ Peer runtime requirements: Node 20+. The package depends on
23
+ `@resvg/resvg-js` (native binding via `@napi-rs`) — npm install
24
+ fetches the platform-matched prebuild automatically.
25
+
23
26
  ## Usage
24
27
 
25
28
  ```ts
@@ -0,0 +1,239 @@
1
+ /** Common subschemas to spread into a tool's `$defs` block. */
2
+ export declare const SHARED_DEFS: {
3
+ BBox: {
4
+ type: string;
5
+ required: string[];
6
+ additionalProperties: boolean;
7
+ properties: {
8
+ x: {
9
+ type: string;
10
+ };
11
+ y: {
12
+ type: string;
13
+ };
14
+ width: {
15
+ type: string;
16
+ minimum: number;
17
+ };
18
+ height: {
19
+ type: string;
20
+ minimum: number;
21
+ };
22
+ };
23
+ };
24
+ Point: {
25
+ type: string;
26
+ required: string[];
27
+ additionalProperties: boolean;
28
+ properties: {
29
+ x: {
30
+ type: string;
31
+ };
32
+ y: {
33
+ type: string;
34
+ };
35
+ };
36
+ };
37
+ Intent: {
38
+ type: string;
39
+ enum: string[];
40
+ };
41
+ AnnotationStyle: {
42
+ type: string;
43
+ additionalProperties: boolean;
44
+ properties: {
45
+ intent: {
46
+ $ref: string;
47
+ };
48
+ stroke: {
49
+ type: string;
50
+ };
51
+ strokeWidth: {
52
+ type: string;
53
+ minimum: number;
54
+ };
55
+ fill: {
56
+ type: string;
57
+ };
58
+ color: {
59
+ type: string;
60
+ };
61
+ };
62
+ };
63
+ };
64
+ export declare const BBOX_ANNOTATION_SCHEMA: {
65
+ oneOf: ({
66
+ type: string;
67
+ required: string[];
68
+ additionalProperties: boolean;
69
+ properties: {
70
+ type: {
71
+ const: string;
72
+ };
73
+ bbox: {
74
+ $ref: string;
75
+ };
76
+ intent: {
77
+ $ref: string;
78
+ };
79
+ stroke: {
80
+ type: string;
81
+ };
82
+ strokeWidth: {
83
+ type: string;
84
+ minimum: number;
85
+ };
86
+ fill: {
87
+ type: string;
88
+ };
89
+ color: {
90
+ type: string;
91
+ };
92
+ };
93
+ } | {
94
+ type: string;
95
+ required: string[];
96
+ additionalProperties: boolean;
97
+ properties: {
98
+ type: {
99
+ const: string;
100
+ };
101
+ center: {
102
+ $ref: string;
103
+ };
104
+ radius: {
105
+ type: string;
106
+ minimum: number;
107
+ };
108
+ intent: {
109
+ $ref: string;
110
+ };
111
+ stroke: {
112
+ type: string;
113
+ };
114
+ strokeWidth: {
115
+ type: string;
116
+ minimum: number;
117
+ };
118
+ fill: {
119
+ type: string;
120
+ };
121
+ color: {
122
+ type: string;
123
+ };
124
+ };
125
+ } | {
126
+ type: string;
127
+ required: string[];
128
+ additionalProperties: boolean;
129
+ properties: {
130
+ type: {
131
+ const: string;
132
+ };
133
+ from: {
134
+ $ref: string;
135
+ };
136
+ to: {
137
+ $ref: string;
138
+ };
139
+ intent: {
140
+ $ref: string;
141
+ };
142
+ stroke: {
143
+ type: string;
144
+ };
145
+ strokeWidth: {
146
+ type: string;
147
+ minimum: number;
148
+ };
149
+ color: {
150
+ type: string;
151
+ };
152
+ };
153
+ } | {
154
+ type: string;
155
+ required: string[];
156
+ additionalProperties: boolean;
157
+ properties: {
158
+ type: {
159
+ const: string;
160
+ };
161
+ at: {
162
+ $ref: string;
163
+ };
164
+ content: {
165
+ type: string;
166
+ };
167
+ fontSize: {
168
+ type: string;
169
+ minimum: number;
170
+ };
171
+ anchor: {
172
+ type: string;
173
+ enum: string[];
174
+ };
175
+ intent: {
176
+ $ref: string;
177
+ };
178
+ color: {
179
+ type: string;
180
+ };
181
+ };
182
+ } | {
183
+ type: string;
184
+ required: string[];
185
+ additionalProperties: boolean;
186
+ properties: {
187
+ type: {
188
+ const: string;
189
+ };
190
+ at: {
191
+ $ref: string;
192
+ };
193
+ targetBbox: {
194
+ $ref: string;
195
+ };
196
+ content: {
197
+ type: string;
198
+ };
199
+ intent: {
200
+ $ref: string;
201
+ };
202
+ stroke: {
203
+ type: string;
204
+ };
205
+ color: {
206
+ type: string;
207
+ };
208
+ };
209
+ } | {
210
+ type: string;
211
+ required: string[];
212
+ additionalProperties: boolean;
213
+ properties: {
214
+ type: {
215
+ const: string;
216
+ };
217
+ svgFragment: {
218
+ type: string;
219
+ };
220
+ };
221
+ })[];
222
+ };
223
+ export declare const BBOX_REDACT_REGION_SCHEMA: {
224
+ type: string;
225
+ required: string[];
226
+ additionalProperties: boolean;
227
+ properties: {
228
+ bbox: {
229
+ $ref: string;
230
+ };
231
+ style: {
232
+ type: string;
233
+ enum: string[];
234
+ };
235
+ color: {
236
+ type: string;
237
+ };
238
+ };
239
+ };
@@ -0,0 +1,32 @@
1
+ import { BBox, Point } from './types.js';
2
+ /**
3
+ * Back-compat alias retained because the original
4
+ * `@ingcreators/annot-playwright` helpers exposed this name.
5
+ * Prefer importing `BBox` from `@ingcreators/annot-annotator`
6
+ * directly.
7
+ */
8
+ export type BoundingBox = BBox;
9
+ export interface RectOptions {
10
+ stroke?: string;
11
+ strokeWidth?: number;
12
+ fill?: string;
13
+ }
14
+ export interface ArrowOptions {
15
+ color?: string;
16
+ strokeWidth?: number;
17
+ }
18
+ export interface TextOptions {
19
+ color?: string;
20
+ fontSize?: number;
21
+ anchor?: "start" | "middle" | "end";
22
+ }
23
+ /** Outline a bounding box with an SVG `<rect>`. */
24
+ export declare function rectForBoundingBox(bbox: BoundingBox, opts?: RectOptions): string;
25
+ /**
26
+ * Draw an arrow from `from` to `to`. Inlines a unique-id `<marker>`
27
+ * definition so the helper is self-contained — call it twice and
28
+ * each call produces a distinct arrowhead marker.
29
+ */
30
+ export declare function arrowBetween(from: Point, to: Point, opts?: ArrowOptions): string;
31
+ /** Drop a text label at a position. */
32
+ export declare function textAt(at: Point, content: string, opts?: TextOptions): string;
@@ -0,0 +1,11 @@
1
+ import { BboxAnnotation } from './types.js';
2
+ /**
3
+ * Convert a list of bbox-flavour annotations into a single SVG
4
+ * fragment string. The fragment is suitable as the
5
+ * `annotationsSvg` payload for `createAnnotator(...).toPng()` /
6
+ * `.toSvg()`.
7
+ *
8
+ * Emits bare fragments (no outer `<svg>` wrapper); the annotator's
9
+ * sanitiser wraps them at rasterise time.
10
+ */
11
+ export declare function bboxAnnotationsToSvg(annotations: readonly BboxAnnotation[]): string;
@@ -0,0 +1,66 @@
1
+ /** Axis-aligned bounding box. Coordinates are page pixels. */
2
+ export interface BBox {
3
+ x: number;
4
+ y: number;
5
+ width: number;
6
+ height: number;
7
+ }
8
+ /** Point in page pixels. */
9
+ export interface Point {
10
+ x: number;
11
+ y: number;
12
+ }
13
+ /**
14
+ * Semantic colour shorthand. Resolves to the Annot design system's
15
+ * matching token (e.g. `intent: "error"` → `--annot-color-error`).
16
+ * Explicit `stroke` / `color` / `fill` on an annotation override
17
+ * the intent-derived defaults.
18
+ */
19
+ export type Intent = "info" | "warning" | "error" | "success" | "neutral";
20
+ /** Style overrides shared across most annotation shapes. */
21
+ export interface AnnotationStyle {
22
+ intent?: Intent;
23
+ stroke?: string;
24
+ strokeWidth?: number;
25
+ fill?: string;
26
+ color?: string;
27
+ }
28
+ export type BboxRectAnnotation = AnnotationStyle & {
29
+ type: "rect";
30
+ bbox: BBox;
31
+ };
32
+ export type BboxCircleAnnotation = AnnotationStyle & {
33
+ type: "circle";
34
+ center: Point;
35
+ radius: number;
36
+ };
37
+ export type BboxArrowAnnotation = AnnotationStyle & {
38
+ type: "arrow";
39
+ from: Point;
40
+ to: Point;
41
+ };
42
+ export type BboxTextAnnotation = AnnotationStyle & {
43
+ type: "text";
44
+ at: Point;
45
+ content: string;
46
+ fontSize?: number;
47
+ anchor?: "start" | "middle" | "end";
48
+ };
49
+ export type BboxCalloutAnnotation = AnnotationStyle & {
50
+ type: "callout";
51
+ at: Point;
52
+ targetBbox: BBox;
53
+ content: string;
54
+ };
55
+ export interface RawAnnotation {
56
+ type: "raw";
57
+ svgFragment: string;
58
+ }
59
+ export type BboxAnnotation = BboxRectAnnotation | BboxCircleAnnotation | BboxArrowAnnotation | BboxTextAnnotation | BboxCalloutAnnotation | RawAnnotation;
60
+ export type RedactStyle = "solid" | "mosaic" | "blur";
61
+ export interface BboxRedactRegion {
62
+ bbox: BBox;
63
+ style?: RedactStyle;
64
+ /** CSS colour, applied only when `style` is `"solid"`. */
65
+ color?: string;
66
+ }
package/dist/index.d.ts CHANGED
@@ -1 +1,5 @@
1
1
  export { type Annotator, type AnnotatorInput, type AnnotatorOptions, createAnnotator, } from './annotator.js';
2
+ export { BBOX_ANNOTATION_SCHEMA, BBOX_REDACT_REGION_SCHEMA, SHARED_DEFS, } from './dsl/schema.js';
3
+ export { type ArrowOptions, arrowBetween, type BoundingBox, type RectOptions, rectForBoundingBox, type TextOptions, textAt, } from './dsl/svg-primitives.js';
4
+ export { bboxAnnotationsToSvg } from './dsl/to-svg.js';
5
+ export type { AnnotationStyle, BBox, BboxAnnotation, BboxArrowAnnotation, BboxCalloutAnnotation, BboxCircleAnnotation, BboxRectAnnotation, BboxRedactRegion, BboxTextAnnotation, Intent, Point, RawAnnotation, RedactStyle, } from './dsl/types.js';
package/dist/index.js CHANGED
@@ -1,80 +1,317 @@
1
- import { Resvg as e } from "@resvg/resvg-js";
2
- import { DOMParser as t, XMLSerializer as n } from "@xmldom/xmldom";
3
- //#region ../core/src/editor/svg-format.ts
4
- var r = "data-annot-version";
5
- //#endregion
6
- //#region src/sanitise-svg.ts
7
- function i(e) {
8
- if (!e || e.trim().length === 0) return "";
9
- let r = new t(), i;
10
- try {
11
- i = r.parseFromString(e, "image/svg+xml");
12
- } catch {
13
- return "";
14
- }
15
- let o = i.documentElement;
16
- if (!o) return "";
17
- let s = new n(), c = "";
18
- for (let e = 0; e < o.childNodes.length; e++) {
19
- let t = o.childNodes.item(e);
20
- if (!t || t.nodeType !== 1) continue;
21
- let n = t, r = n.localName ?? n.tagName ?? "";
22
- if (r === "defs") {
23
- let e = a(n);
24
- e !== null && (c += s.serializeToString(e));
25
- continue;
26
- }
27
- if (!(r === "image" && !n.getAttribute("data-redact-style")) && n.getAttribute("id") !== "ui-overlay") {
28
- if (n.getAttribute("id") === "annotations") {
29
- for (let e = 0; e < n.childNodes.length; e++) {
30
- let t = n.childNodes.item(e);
31
- t && t.nodeType === 1 && (c += s.serializeToString(t));
32
- }
33
- continue;
34
- }
35
- c += s.serializeToString(n);
36
- }
37
- }
38
- return c;
1
+ import { Resvg as b } from "@resvg/resvg-js";
2
+ import { DOMParser as w, XMLSerializer as B } from "@xmldom/xmldom";
3
+ const S = "1", N = "data-annot-version";
4
+ function T(e) {
5
+ if (!e || e.trim().length === 0)
6
+ return "";
7
+ const t = new w();
8
+ let r;
9
+ try {
10
+ r = t.parseFromString(e, "image/svg+xml");
11
+ } catch {
12
+ return "";
13
+ }
14
+ const n = r.documentElement;
15
+ if (!n) return "";
16
+ const o = new B();
17
+ let i = "";
18
+ for (let d = 0; d < n.childNodes.length; d++) {
19
+ const f = n.childNodes.item(d);
20
+ if (!f || f.nodeType !== 1) continue;
21
+ const s = f, p = s.localName ?? s.tagName ?? "";
22
+ if (p === "defs") {
23
+ const c = A(s);
24
+ c !== null && (i += o.serializeToString(c));
25
+ continue;
26
+ }
27
+ if (!(p === "image" && !s.getAttribute("data-redact-style")) && s.getAttribute("id") !== "ui-overlay") {
28
+ if (s.getAttribute("id") === "annotations") {
29
+ for (let c = 0; c < s.childNodes.length; c++) {
30
+ const u = s.childNodes.item(c);
31
+ u && u.nodeType === 1 && (i += o.serializeToString(u));
32
+ }
33
+ continue;
34
+ }
35
+ i += o.serializeToString(s);
36
+ }
37
+ }
38
+ return i;
39
39
  }
40
+ function A(e) {
41
+ const t = e.cloneNode(!0);
42
+ for (let r = t.childNodes.length - 1; r >= 0; r--) {
43
+ const n = t.childNodes.item(r);
44
+ if (!n || n.nodeType !== 1) continue;
45
+ const o = n;
46
+ (o.localName ?? o.tagName ?? "") === "style" && o.hasAttribute("data-annot-fonts") && t.removeChild(n);
47
+ }
48
+ return t.childNodes.length === 0 ? null : t;
49
+ }
50
+ function H(e = {}) {
51
+ const t = {
52
+ loadSystemFonts: e.loadSystemFonts ?? !1,
53
+ ...e.fontFiles ? { fontFiles: e.fontFiles } : {},
54
+ ...e.fontDirs ? { fontDirs: e.fontDirs } : {},
55
+ ...e.defaultFontFamily ? { defaultFontFamily: e.defaultFontFamily } : {}
56
+ };
57
+ return {
58
+ toSvg(r) {
59
+ return m(r);
60
+ },
61
+ toPng(r) {
62
+ const n = m(r);
63
+ return new b(n, {
64
+ fitTo: { mode: "width", value: r.width },
65
+ background: "rgba(0, 0, 0, 0)",
66
+ font: t
67
+ }).render().asPng();
68
+ }
69
+ };
70
+ }
71
+ function m(e) {
72
+ const t = "http://www.w3.org/2000/svg", r = "http://www.w3.org/1999/xlink", n = T(e.annotationsSvg);
73
+ return `<svg xmlns="${t}" xmlns:xlink="${r}" ${N}="${S}" width="${e.width}" height="${e.height}" viewBox="0 0 ${e.width} ${e.height}"><image href="${e.originalDataUrl}" width="${e.width}" height="${e.height}"/>` + n + "</svg>";
74
+ }
75
+ const K = {
76
+ BBox: {
77
+ type: "object",
78
+ required: ["x", "y", "width", "height"],
79
+ additionalProperties: !1,
80
+ properties: {
81
+ x: { type: "number" },
82
+ y: { type: "number" },
83
+ width: { type: "number", minimum: 0 },
84
+ height: { type: "number", minimum: 0 }
85
+ }
86
+ },
87
+ Point: {
88
+ type: "object",
89
+ required: ["x", "y"],
90
+ additionalProperties: !1,
91
+ properties: {
92
+ x: { type: "number" },
93
+ y: { type: "number" }
94
+ }
95
+ },
96
+ Intent: {
97
+ type: "string",
98
+ enum: ["info", "warning", "error", "success", "neutral"]
99
+ },
100
+ AnnotationStyle: {
101
+ type: "object",
102
+ additionalProperties: !1,
103
+ properties: {
104
+ intent: { $ref: "#/$defs/Intent" },
105
+ stroke: { type: "string" },
106
+ strokeWidth: { type: "number", minimum: 0 },
107
+ fill: { type: "string" },
108
+ color: { type: "string" }
109
+ }
110
+ }
111
+ }, F = {
112
+ type: "object",
113
+ required: ["type", "bbox"],
114
+ additionalProperties: !1,
115
+ properties: {
116
+ type: { const: "rect" },
117
+ bbox: { $ref: "#/$defs/BBox" },
118
+ intent: { $ref: "#/$defs/Intent" },
119
+ stroke: { type: "string" },
120
+ strokeWidth: { type: "number", minimum: 0 },
121
+ fill: { type: "string" },
122
+ color: { type: "string" }
123
+ }
124
+ }, v = {
125
+ type: "object",
126
+ required: ["type", "center", "radius"],
127
+ additionalProperties: !1,
128
+ properties: {
129
+ type: { const: "circle" },
130
+ center: { $ref: "#/$defs/Point" },
131
+ radius: { type: "number", minimum: 0 },
132
+ intent: { $ref: "#/$defs/Intent" },
133
+ stroke: { type: "string" },
134
+ strokeWidth: { type: "number", minimum: 0 },
135
+ fill: { type: "string" },
136
+ color: { type: "string" }
137
+ }
138
+ }, O = {
139
+ type: "object",
140
+ required: ["type", "from", "to"],
141
+ additionalProperties: !1,
142
+ properties: {
143
+ type: { const: "arrow" },
144
+ from: { $ref: "#/$defs/Point" },
145
+ to: { $ref: "#/$defs/Point" },
146
+ intent: { $ref: "#/$defs/Intent" },
147
+ stroke: { type: "string" },
148
+ strokeWidth: { type: "number", minimum: 0 },
149
+ color: { type: "string" }
150
+ }
151
+ }, W = {
152
+ type: "object",
153
+ required: ["type", "at", "content"],
154
+ additionalProperties: !1,
155
+ properties: {
156
+ type: { const: "text" },
157
+ at: { $ref: "#/$defs/Point" },
158
+ content: { type: "string" },
159
+ fontSize: { type: "number", minimum: 0 },
160
+ anchor: { type: "string", enum: ["start", "middle", "end"] },
161
+ intent: { $ref: "#/$defs/Intent" },
162
+ color: { type: "string" }
163
+ }
164
+ }, P = {
165
+ type: "object",
166
+ required: ["type", "at", "targetBbox", "content"],
167
+ additionalProperties: !1,
168
+ properties: {
169
+ type: { const: "callout" },
170
+ at: { $ref: "#/$defs/Point" },
171
+ targetBbox: { $ref: "#/$defs/BBox" },
172
+ content: { type: "string" },
173
+ intent: { $ref: "#/$defs/Intent" },
174
+ stroke: { type: "string" },
175
+ color: { type: "string" }
176
+ }
177
+ }, I = {
178
+ type: "object",
179
+ required: ["type", "svgFragment"],
180
+ additionalProperties: !1,
181
+ properties: {
182
+ type: { const: "raw" },
183
+ svgFragment: { type: "string" }
184
+ }
185
+ }, Y = {
186
+ oneOf: [F, v, O, W, P, I]
187
+ }, J = {
188
+ type: "object",
189
+ required: ["bbox"],
190
+ additionalProperties: !1,
191
+ properties: {
192
+ bbox: { $ref: "#/$defs/BBox" },
193
+ style: { type: "string", enum: ["solid", "mosaic", "blur"] },
194
+ color: { type: "string" }
195
+ }
196
+ };
197
+ function $(e, t = {}) {
198
+ const r = t.stroke ?? "red", n = t.strokeWidth ?? 2, o = t.fill ?? "none";
199
+ return `<rect x="${e.x}" y="${e.y}" width="${e.width}" height="${e.height}" fill="${l(o)}" stroke="${l(r)}" stroke-width="${n}"/>`;
200
+ }
201
+ function k(e, t, r = {}) {
202
+ const n = r.color ?? "red", o = r.strokeWidth ?? 2, i = `annot-arrow-${_()}`;
203
+ return `<defs><marker id="${i}" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="6" markerHeight="6" orient="auto" markerUnits="strokeWidth"><path d="M 0 0 L 10 5 L 0 10 z" fill="${l(n)}"/></marker></defs><line x1="${e.x}" y1="${e.y}" x2="${t.x}" y2="${t.y}" stroke="${l(n)}" stroke-width="${o}" marker-end="url(#${i})"/>`;
204
+ }
205
+ function x(e, t, r = {}) {
206
+ const n = r.color ?? "red", o = r.fontSize ?? 14, i = r.anchor ?? "start";
207
+ return `<text x="${e.x}" y="${e.y}" fill="${l(n)}" font-size="${o}" text-anchor="${i}">` + E(t) + "</text>";
208
+ }
209
+ let g = 0;
210
+ function _() {
211
+ return g = g + 1 | 0, g;
212
+ }
213
+ function l(e) {
214
+ return e.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&apos;");
215
+ }
216
+ function E(e) {
217
+ return e.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
218
+ }
219
+ const R = {
220
+ info: { stroke: "#3b82f6", fill: "rgba(59, 130, 246, 0.12)", text: "#1e40af" },
221
+ warning: { stroke: "#f59e0b", fill: "rgba(245, 158, 11, 0.12)", text: "#92400e" },
222
+ error: { stroke: "#ef4444", fill: "rgba(239, 68, 68, 0.12)", text: "#991b1b" },
223
+ success: { stroke: "#10b981", fill: "rgba(16, 185, 129, 0.12)", text: "#065f46" },
224
+ neutral: { stroke: "#6b7280", fill: "rgba(107, 114, 128, 0.12)", text: "#374151" }
225
+ }, z = "error";
40
226
  function a(e) {
41
- let t = e.cloneNode(!0);
42
- for (let e = t.childNodes.length - 1; e >= 0; e--) {
43
- let n = t.childNodes.item(e);
44
- if (!n || n.nodeType !== 1) continue;
45
- let r = n;
46
- (r.localName ?? r.tagName ?? "") === "style" && r.hasAttribute("data-annot-fonts") && t.removeChild(n);
47
- }
48
- return t.childNodes.length === 0 ? null : t;
49
- }
50
- //#endregion
51
- //#region src/annotator.ts
52
- function o(t = {}) {
53
- let n = {
54
- loadSystemFonts: t.loadSystemFonts ?? !1,
55
- ...t.fontFiles ? { fontFiles: t.fontFiles } : {},
56
- ...t.fontDirs ? { fontDirs: t.fontDirs } : {},
57
- ...t.defaultFontFamily ? { defaultFontFamily: t.defaultFontFamily } : {}
58
- };
59
- return {
60
- toSvg(e) {
61
- return s(e);
62
- },
63
- toPng(t) {
64
- return new e(s(t), {
65
- fitTo: {
66
- mode: "width",
67
- value: t.width
68
- },
69
- background: "rgba(0, 0, 0, 0)",
70
- font: n
71
- }).render().asPng();
72
- }
73
- };
74
- }
75
- function s(e) {
76
- let t = i(e.annotationsSvg);
77
- return `<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" ${r}="1" width="${e.width}" height="${e.height}" viewBox="0 0 ${e.width} ${e.height}"><image href="${e.originalDataUrl}" width="${e.width}" height="${e.height}"/>` + t + "</svg>";
78
- }
79
- //#endregion
80
- export { o as createAnnotator };
227
+ const t = e.intent ?? z, r = R[t];
228
+ return {
229
+ stroke: e.stroke ?? r.stroke,
230
+ fill: e.fill ?? "none",
231
+ text: e.color ?? r.text
232
+ };
233
+ }
234
+ function Q(e) {
235
+ return e.map(j).join("");
236
+ }
237
+ function j(e) {
238
+ switch (e.type) {
239
+ case "rect":
240
+ return q(e);
241
+ case "circle":
242
+ return C(e);
243
+ case "arrow":
244
+ return X(e);
245
+ case "text":
246
+ return D(e);
247
+ case "callout":
248
+ return L(e);
249
+ case "raw":
250
+ return M(e);
251
+ }
252
+ }
253
+ function q(e) {
254
+ const t = a(e);
255
+ return $(e.bbox, {
256
+ stroke: t.stroke,
257
+ strokeWidth: e.strokeWidth ?? 2,
258
+ fill: t.fill
259
+ });
260
+ }
261
+ function C(e) {
262
+ const t = a(e), r = e.strokeWidth ?? 2;
263
+ return `<circle cx="${e.center.x}" cy="${e.center.y}" r="${e.radius}" fill="${y(t.fill)}" stroke="${y(t.stroke)}" stroke-width="${r}"/>`;
264
+ }
265
+ function X(e) {
266
+ const t = a(e);
267
+ return k(e.from, e.to, {
268
+ color: t.stroke,
269
+ strokeWidth: e.strokeWidth ?? 2
270
+ });
271
+ }
272
+ function D(e) {
273
+ const t = a(e);
274
+ return x(e.at, e.content, {
275
+ color: t.text,
276
+ fontSize: e.fontSize ?? 14,
277
+ anchor: e.anchor ?? "start"
278
+ });
279
+ }
280
+ function L(e) {
281
+ const t = a(e), r = e.targetBbox, n = U(r, e.at);
282
+ return $(r, {
283
+ stroke: t.stroke,
284
+ strokeWidth: e.strokeWidth ?? 2,
285
+ fill: t.fill
286
+ }) + k(e.at, n, {
287
+ color: t.stroke,
288
+ strokeWidth: 2
289
+ }) + x(e.at, e.content, {
290
+ color: t.text,
291
+ fontSize: 14,
292
+ anchor: "start"
293
+ });
294
+ }
295
+ function M(e) {
296
+ return e.svgFragment;
297
+ }
298
+ function U(e, t) {
299
+ const r = h(t.x, e.x, e.x + e.width), n = h(t.y, e.y, e.y + e.height);
300
+ return { x: r, y: n };
301
+ }
302
+ function h(e, t, r) {
303
+ return Math.min(Math.max(e, t), r);
304
+ }
305
+ function y(e) {
306
+ return e.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&apos;");
307
+ }
308
+ export {
309
+ Y as BBOX_ANNOTATION_SCHEMA,
310
+ J as BBOX_REDACT_REGION_SCHEMA,
311
+ K as SHARED_DEFS,
312
+ k as arrowBetween,
313
+ Q as bboxAnnotationsToSvg,
314
+ H as createAnnotator,
315
+ $ as rectForBoundingBox,
316
+ x as textAt
317
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ingcreators/annot-annotator",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Headless annotator — render annotated screenshots from Node without a browser. Built on @resvg/resvg-js. Pairs with @ingcreators/annot-playwright for fail-on-test annotated reports.",
5
5
  "license": "Apache-2.0",
6
6
  "author": "ingcreators",
@@ -44,7 +44,7 @@
44
44
  },
45
45
  "devDependencies": {
46
46
  "typescript": "^6.0.3",
47
- "vite": "^8.0.13",
47
+ "vite": "^6.4.2",
48
48
  "vite-plugin-dts": "^5.0.0",
49
49
  "@ingcreators/annot-core": "0.1.0"
50
50
  },