@ingcreators/annot-core 0.3.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.
@@ -13,6 +13,16 @@ export interface ImageRecord {
13
13
  tags: Record<string, string>;
14
14
  createdAt: string;
15
15
  updatedAt: string;
16
+ /** What created the image: `extension` / `desktop` / `web` /
17
+ * `vscode` / `annotator` / `mcp` / `playwright` / …. Persisted
18
+ * as `annot:producer` in the XMP packet (schema 2.0). Empty /
19
+ * undefined for records that predate 2.0. */
20
+ producer?: string;
21
+ /** `devicePixelRatio` (or display scale factor) at capture time.
22
+ * Persisted as `annot:dpr` (schema 2.0). Maps device-px image
23
+ * dimensions back to CSS px for sources without an ElementTree.
24
+ * 0 / undefined when unknown. */
25
+ dpr?: number;
16
26
  /** Canonical screen-capture tree captured alongside the
17
27
  * screenshot. Source-agnostic: produced by the browser extension's
18
28
  * MAIN-world walker, by Playwright's adapter, or by any future
@@ -46,3 +46,22 @@ export declare function defaultAnnotFilenameStem(date?: Date): string;
46
46
  * and stray callers — and PNG (lossless) is the safer guess.
47
47
  */
48
48
  export declare function defaultAnnotImageFilename(originalDataUrl: string, date?: Date): string;
49
+ /**
50
+ * Normalize a caller-supplied image filename to the `.annot.<ext>`
51
+ * double extension — the single identity rule for Annot-managed
52
+ * files (`docs/plans/metadata-unification.md` Phase 4): a file the
53
+ * gallery manages carries BOTH the `.annot.` infix AND the XMP
54
+ * packet, so every host (including the vscode custom editor, which
55
+ * claims `*.annot.{svg,png,jpeg,jpg}` only) recognizes it.
56
+ *
57
+ * `uploaded.png` → `uploaded.annot.png`; already-normalized names
58
+ * pass through unchanged; the extension is lowercased. A name with
59
+ * no recognizable raster/svg extension gets `.annot.png` appended
60
+ * (production callers always pass one — this is the safe fallback).
61
+ *
62
+ * Persistent stores apply this in `saveImage`; the extension's
63
+ * transient IDB staging deliberately does not (see
64
+ * {@link defaultAnnotImageFilename}'s note — pre-annotation
65
+ * captures aren't annot-native yet).
66
+ */
67
+ export declare function normalizeAnnotImageFilename(filename: string): string;
@@ -1,5 +1,5 @@
1
1
  export { assertNonNull } from './assert.js';
2
2
  export { DEFAULT_FILL_COLOR, DEFAULT_FONT_SIZE, DEFAULT_STROKE_COLOR, DEFAULT_STROKE_WIDTH, JPEG_QUALITY, MOSAIC_BLOCK_SIZE, REDACT_BLUR_RADIUS, REDACT_SOLID_COLOR, } from './constants.js';
3
3
  export { computeDasharray, detectDashKey } from './dash-utils.js';
4
- export { ANNOT_FILENAME_PREFIX, defaultAnnotFilenameStem, defaultAnnotImageFilename, formatLocalTimestamp, } from './filename.js';
4
+ export { ANNOT_FILENAME_PREFIX, defaultAnnotFilenameStem, defaultAnnotImageFilename, formatLocalTimestamp, normalizeAnnotImageFilename, } from './filename.js';
5
5
  export { newIdB58 } from './id.js';
@@ -1,21 +1,6 @@
1
- /**
2
- * Browser-side XMP metadata embedding for re-editable images.
3
- *
4
- * Wraps the Tier-A `xmp-bytes` primitives with Blob input / Blob output
5
- * and adds the JPEG output path, which needs an Image + <canvas> pipeline
6
- * to transcode the rendered PNG to JPEG.
7
- *
8
- * For Node / non-browser callers, import the Tier-A primitives
9
- * directly:
10
- *
11
- * import { createEditablePngBytes, readEditableImage }
12
- * from "@ingcreators/annot-core/xmp-bytes";
13
- *
14
- * Re-exports of Tier-A symbols below keep existing
15
- * `@ingcreators/annot-core/xmp` consumers working unchanged.
16
- */
17
- export { type AnnotMetadata, type CreateEditablePngBytesOptions, createEditablePngBytes, dataUrlToUint8Array, readEditableImage, readEditablePngBytes, WELL_KNOWN_TAG_KEYS, writePngWithTagsOnly, } from './xmp-bytes.js';
18
- export interface EditableImageOptions {
1
+ import { XmpProvenance } from './xmp-bytes.js';
2
+ export { ANNOT_XMP_VERSION, type AnnotMetadata, type BuildXmpOptions, type CreateEditablePngBytesOptions, createEditablePngBytes, dataUrlToUint8Array, readEditableImage, readEditablePngBytes, WELL_KNOWN_TAG_KEYS, writePngWithTagsOnly, type XmpProvenance, } from './xmp-bytes.js';
3
+ export interface EditableImageOptions extends XmpProvenance {
19
4
  /** Rendered image (screenshot + annotations) as Blob */
20
5
  renderedBlob: Blob;
21
6
  /** Original capture image data URL (without annotations) */
@@ -15,7 +15,38 @@
15
15
  * JPEG: XMP in APP1 segment, original image in one or more APP2
16
16
  * segments (each prefixed with `annot:OriginalImage\0`).
17
17
  */
18
- export declare function buildXmp(annotationsSvg: string, width: number, height: number, tags?: Record<string, string>): string;
18
+ /** Schema version written into `<annot:version>`. History lives in
19
+ * `docs/metadata-format.md`. 2.0 added the first-class provenance
20
+ * fields (`sourceUrl` / `createdAt` / `producer` / `dpr`) per
21
+ * `docs/plans/metadata-unification.md`. */
22
+ export declare const ANNOT_XMP_VERSION = "2.0";
23
+ /**
24
+ * Capture provenance persisted in the XMP packet since schema 2.0.
25
+ * All optional — omitted fields emit no element, and readers
26
+ * default them to `""` / `0`. See `docs/metadata-format.md`.
27
+ */
28
+ export interface XmpProvenance {
29
+ /** URL of the page the capture came from. Empty / omitted for
30
+ * non-page sources (desktop screen capture, paste, upload). */
31
+ sourceUrl?: string;
32
+ /** ISO timestamp of the capture / import moment. NOT the file
33
+ * mtime — copies and syncs must not rewrite it. */
34
+ createdAt?: string;
35
+ /** What created the file: `extension` / `desktop` / `web` /
36
+ * `vscode` / `annotator` / `mcp` / `playwright` / … */
37
+ producer?: string;
38
+ /** `window.devicePixelRatio` (or display scale factor) at capture
39
+ * time. Maps device-pixel image dimensions back to CSS px for
40
+ * sources that never produce an ElementTree. */
41
+ dpr?: number;
42
+ }
43
+ export interface BuildXmpOptions extends XmpProvenance {
44
+ annotationsSvg: string;
45
+ width: number;
46
+ height: number;
47
+ tags?: Record<string, string>;
48
+ }
49
+ export declare function buildXmp(opts: BuildXmpOptions): string;
19
50
  export declare function u32be(n: number): Uint8Array;
20
51
  export declare function readU32be(data: Uint8Array, offset: number): number;
21
52
  export declare function concat(...arrays: Uint8Array[]): Uint8Array;
@@ -84,7 +115,7 @@ export declare function writePngWithTagsOnly(pngData: Uint8Array, tags: Record<s
84
115
  * - `commit` — git SHA when applicable.
85
116
  */
86
117
  export declare const WELL_KNOWN_TAG_KEYS: readonly ["source", "screen", "capturedAt", "commit"];
87
- export interface CreateEditablePngBytesOptions {
118
+ export interface CreateEditablePngBytesOptions extends XmpProvenance {
88
119
  /** Rasterised PNG bytes — the visible image, with annotations already
89
120
  * baked into the pixels. */
90
121
  renderedPng: Uint8Array;
@@ -128,6 +159,15 @@ export interface AnnotMetadata {
128
159
  /** Opaque kv tags. Empty object when the XMP carried no `<annot:tags>`
129
160
  * element or the embedded JSON was malformed. */
130
161
  tags: Record<string, string>;
162
+ /** Schema version the packet was written with (`<annot:version>`).
163
+ * Empty string for packets that predate version emission. */
164
+ version: string;
165
+ /** Capture provenance (schema 2.0). Empty string / 0 when the
166
+ * packet doesn't carry the field. */
167
+ sourceUrl: string;
168
+ createdAt: string;
169
+ producer: string;
170
+ dpr: number;
131
171
  }
132
172
  /**
133
173
  * Read XMP metadata from a re-editable PNG.
package/dist/xmp-bytes.js CHANGED
@@ -1,25 +1,31 @@
1
1
  //#region src/xmp/xmp-bytes.ts
2
- var e = "annot", t = "https://ingcreators.com/annot/ns/1.0/", n = new TextEncoder().encode("http://ns.adobe.com/xap/1.0/\0"), r = new TextEncoder().encode("annot:OriginalImage\0"), i = new TextEncoder().encode("XML:com.adobe.xmp");
3
- function a(n, r, i, a) {
4
- let o = a && Object.keys(a).length > 0 ? JSON.stringify(a) : "";
5
- return `<?xpacket begin="" id="W5M0MpCehiHzreSzNTczkc9d"?>
2
+ var e = "annot", t = "https://ingcreators.com/annot/ns/1.0/", n = new TextEncoder().encode("http://ns.adobe.com/xap/1.0/\0"), r = new TextEncoder().encode("annot:OriginalImage\0"), i = new TextEncoder().encode("XML:com.adobe.xmp"), a = "2.0";
3
+ function o(e) {
4
+ return e.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
5
+ }
6
+ function s(e) {
7
+ return e.replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&amp;/g, "&");
8
+ }
9
+ function c(n) {
10
+ let r = [], i = (t, n) => r.push(`\n <${e}:${t}>${n}</${e}:${t}>`), a = n.tags && Object.keys(n.tags).length > 0 ? JSON.stringify(n.tags) : "";
11
+ return a && i("tags", o(a)), n.sourceUrl && i("sourceUrl", o(n.sourceUrl)), n.createdAt && i("createdAt", o(n.createdAt)), n.producer && i("producer", o(n.producer)), n.dpr && n.dpr > 0 && i("dpr", String(n.dpr)), `<?xpacket begin="" id="W5M0MpCehiHzreSzNTczkc9d"?>
6
12
  <x:xmpmeta xmlns:x="adobe:ns:meta/">
7
13
  <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
8
14
  <rdf:Description rdf:about=""
9
15
  xmlns:${e}="${t}">
10
- <${e}:annotations><![CDATA[${n}]]></${e}:annotations>
11
- <${e}:width>${r}</${e}:width>
12
- <${e}:height>${i}</${e}:height>
13
- <${e}:version>1.0</${e}:version>${o ? `\n <${e}:tags>${o}</${e}:tags>` : ""}
16
+ <${e}:annotations><![CDATA[${n.annotationsSvg}]]></${e}:annotations>
17
+ <${e}:width>${n.width}</${e}:width>
18
+ <${e}:height>${n.height}</${e}:height>
19
+ <${e}:version>2.0</${e}:version>${r.join("")}
14
20
  </rdf:Description>
15
21
  </rdf:RDF>
16
22
  </x:xmpmeta>
17
23
  <?xpacket end="w"?>`;
18
24
  }
19
- function o(e) {
25
+ function l(e) {
20
26
  return new Uint8Array([e >> 8 & 255, e & 255]);
21
27
  }
22
- function s(e) {
28
+ function u(e) {
23
29
  return new Uint8Array([
24
30
  e >> 24 & 255,
25
31
  e >> 16 & 255,
@@ -27,58 +33,58 @@ function s(e) {
27
33
  e & 255
28
34
  ]);
29
35
  }
30
- function c(e, t) {
36
+ function d(e, t) {
31
37
  return e[t] << 8 | e[t + 1];
32
38
  }
33
- function l(e, t) {
39
+ function f(e, t) {
34
40
  return (e[t] << 24 | e[t + 1] << 16 | e[t + 2] << 8 | e[t + 3]) >>> 0;
35
41
  }
36
- function u(...e) {
42
+ function p(...e) {
37
43
  let t = 0;
38
44
  for (let n of e) t += n.length;
39
45
  let n = new Uint8Array(t), r = 0;
40
46
  for (let t of e) n.set(t, r), r += t.length;
41
47
  return n;
42
48
  }
43
- function d(e, t, n) {
49
+ function m(e, t, n) {
44
50
  if (t + n.length > e.length) return !1;
45
51
  for (let r = 0; r < n.length; r++) if (e[t + r] !== n[r]) return !1;
46
52
  return !0;
47
53
  }
48
- function f(e) {
54
+ function h(e) {
49
55
  let t = e.split(",")[1] || "", n = atob(t), r = new Uint8Array(n.length);
50
56
  for (let e = 0; e < n.length; e++) r[e] = n.charCodeAt(e);
51
57
  return r;
52
58
  }
53
- function p(e, t) {
59
+ function g(e, t) {
54
60
  let n = t.length + 2;
55
- return u(new Uint8Array([255, e]), o(n), t);
61
+ return p(new Uint8Array([255, e]), l(n), t);
56
62
  }
57
- function m(e) {
63
+ function _(e) {
58
64
  let t = 65533 - r.length - 4, n = Math.ceil(e.length / t), i = [];
59
65
  for (let a = 0; a < n; a++) {
60
- let s = a * t, c = Math.min(s + t, e.length), l = e.slice(s, c), d = u(r, o(a), o(n), l);
61
- i.push(p(226, d));
66
+ let o = a * t, s = Math.min(o + t, e.length), c = e.slice(o, s), u = p(r, l(a), l(n), c);
67
+ i.push(g(226, u));
62
68
  }
63
- return u(...i);
69
+ return p(...i);
64
70
  }
65
- function h(e) {
71
+ function v(e) {
66
72
  let t = [e.slice(0, 2)], i = 2;
67
73
  for (; i + 4 <= e.length && e[i] === 255;) {
68
74
  let a = e[i + 1];
69
- if (a === 217 || a === 218) return t.push(e.slice(i)), u(...t);
70
- let o = c(e, i + 2), s = i + 2 + o;
75
+ if (a === 217 || a === 218) return t.push(e.slice(i)), p(...t);
76
+ let o = d(e, i + 2), s = i + 2 + o;
71
77
  if (s > e.length) break;
72
- let l = a === 225 && d(e, i + 4, n), f = a === 226 && d(e, i + 4, r);
73
- !l && !f && t.push(e.slice(i, s)), i = s;
78
+ let c = a === 225 && m(e, i + 4, n), l = a === 226 && m(e, i + 4, r);
79
+ !c && !l && t.push(e.slice(i, s)), i = s;
74
80
  }
75
- return i < e.length && t.push(e.slice(i)), u(...t);
81
+ return i < e.length && t.push(e.slice(i)), p(...t);
76
82
  }
77
- function g(e, t, r) {
78
- let i = p(225, u(n, t)), a = m(r), o = h(e);
79
- return u(o.slice(0, 2), i, a, o.slice(2));
83
+ function y(e, t, r) {
84
+ let i = g(225, p(n, t)), a = _(r), o = v(e);
85
+ return p(o.slice(0, 2), i, a, o.slice(2));
80
86
  }
81
- var _ = (() => {
87
+ var b = (() => {
82
88
  let e = /* @__PURE__ */ new Uint32Array(256);
83
89
  for (let t = 0; t < 256; t++) {
84
90
  let n = t;
@@ -87,117 +93,123 @@ var _ = (() => {
87
93
  }
88
94
  return e;
89
95
  })();
90
- function v(e) {
96
+ function x(e) {
91
97
  let t = 4294967295;
92
- for (let n = 0; n < e.length; n++) t = _[(t ^ e[n]) & 255] ^ t >>> 8;
98
+ for (let n = 0; n < e.length; n++) t = b[(t ^ e[n]) & 255] ^ t >>> 8;
93
99
  return (t ^ 4294967295) >>> 0;
94
100
  }
95
- function y(e, t) {
96
- let n = u(e, t), r = v(n);
97
- return u(s(t.length), n, s(r));
101
+ function S(e, t) {
102
+ let n = p(e, t), r = x(n);
103
+ return p(u(t.length), n, u(r));
98
104
  }
99
- function b(e) {
100
- let t = u(i, new Uint8Array([
105
+ function C(e) {
106
+ let t = p(i, new Uint8Array([
101
107
  0,
102
108
  0,
103
109
  0,
104
110
  0,
105
111
  0
106
112
  ]), e);
107
- return y(new TextEncoder().encode("iTXt"), t);
113
+ return S(new TextEncoder().encode("iTXt"), t);
108
114
  }
109
- function x(e) {
115
+ function w(e) {
110
116
  let t = [e.slice(0, 8)], n = 8;
111
117
  for (; n + 12 <= e.length;) {
112
- let r = l(e, n), a = e.slice(n + 4, n + 8), o = n + 8, s = o + r + 4;
118
+ let r = f(e, n), a = e.slice(n + 4, n + 8), o = n + 8, s = o + r + 4;
113
119
  if (s > e.length) break;
114
120
  let c = String.fromCharCode(...a);
115
- !(c === "iTXt" && d(e, o, i)) && c !== "svGo" && t.push(e.slice(n, s)), n = s;
121
+ !(c === "iTXt" && m(e, o, i)) && c !== "svGo" && t.push(e.slice(n, s)), n = s;
116
122
  }
117
- return u(...t);
123
+ return p(...t);
118
124
  }
119
- function S(e, t, n) {
120
- let r = b(t), i = y(new TextEncoder().encode("svGo"), n), a = x(e), o = a.length - 12;
121
- return u(a.slice(0, o), r, i, a.slice(o));
125
+ function T(e, t, n) {
126
+ let r = C(t), i = S(new TextEncoder().encode("svGo"), n), a = w(e), o = a.length - 12;
127
+ return p(a.slice(0, o), r, i, a.slice(o));
122
128
  }
123
- function C(n) {
129
+ function E(n) {
124
130
  return `<?xpacket begin="" id="W5M0MpCehiHzreSzNTczkc9d"?>
125
131
  <x:xmpmeta xmlns:x="adobe:ns:meta/">
126
132
  <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
127
133
  <rdf:Description rdf:about=""
128
134
  xmlns:${e}="${t}">
129
- <${e}:tags>${JSON.stringify(n)}</${e}:tags>
135
+ <${e}:tags>${o(JSON.stringify(n))}</${e}:tags>
130
136
  </rdf:Description>
131
137
  </rdf:RDF>
132
138
  </x:xmpmeta>
133
139
  <?xpacket end="w"?>`;
134
140
  }
135
- function w(e, t) {
141
+ function D(e, t) {
136
142
  if (!t || Object.keys(t).length === 0) return e;
137
- let n = b(new TextEncoder().encode(C(t))), r = x(e), i = r.length - 12;
138
- return u(r.slice(0, i), n, r.slice(i));
143
+ let n = C(new TextEncoder().encode(E(t))), r = w(e), i = r.length - 12;
144
+ return p(r.slice(0, i), n, r.slice(i));
139
145
  }
140
- var T = [
146
+ var O = [
141
147
  "source",
142
148
  "screen",
143
149
  "capturedAt",
144
150
  "commit"
145
151
  ];
146
- function E(e) {
147
- let t = a(e.annotationsSvg, e.width, e.height, e.tags), n = new TextEncoder().encode(t), r = typeof e.originalImage == "string" ? f(e.originalImage) : e.originalImage;
148
- return S(e.renderedPng, n, r);
152
+ function k(e) {
153
+ let t = c(e), n = new TextEncoder().encode(t), r = typeof e.originalImage == "string" ? h(e.originalImage) : e.originalImage;
154
+ return T(e.renderedPng, n, r);
149
155
  }
150
- function D(e) {
151
- return A(e) ? M(e) : null;
156
+ function A(e) {
157
+ return N(e) ? F(e) : null;
152
158
  }
153
- function O(e) {
154
- return k(e) ? j(e) : A(e) ? M(e) : null;
159
+ function j(e) {
160
+ return M(e) ? P(e) : N(e) ? F(e) : null;
155
161
  }
156
- function k(e) {
162
+ function M(e) {
157
163
  return e[0] === 255 && e[1] === 216;
158
164
  }
159
- function A(e) {
165
+ function N(e) {
160
166
  return e[0] === 137 && e[1] === 80 && e[2] === 78 && e[3] === 71;
161
167
  }
162
- function j(e) {
163
- let t = F(e), n = I(e);
164
- return t ? N(t, n) : null;
168
+ function P(e) {
169
+ let t = R(e), n = z(e);
170
+ return t ? I(t, n) : null;
165
171
  }
166
- function M(e) {
167
- let t = L(e), n = R(e);
168
- return t ? N(t, n) : null;
172
+ function F(e) {
173
+ let t = B(e), n = V(e);
174
+ return t ? I(t, n) : null;
169
175
  }
170
- function N(e, t) {
171
- let n = P(e, "annotations");
176
+ function I(e, t) {
177
+ let n = L(e, "annotations");
172
178
  if (!n) return null;
173
- let r = n.replace(/^<!\[CDATA\[/, "").replace(/\]\]>$/, ""), i = Number.parseInt(P(e, "width") || "0", 10), a = Number.parseInt(P(e, "height") || "0", 10), o = "";
174
- t && t.length > 0 && (o = `data:${t[0] === 137 && t[1] === 80 ? "image/png" : "image/jpeg"};base64,${z(t)}`);
175
- let s = {}, c = P(e, "tags");
176
- if (c) try {
177
- s = JSON.parse(c);
179
+ let r = n.replace(/^<!\[CDATA\[/, "").replace(/\]\]>$/, ""), i = Number.parseInt(L(e, "width") || "0", 10), a = Number.parseInt(L(e, "height") || "0", 10), o = "";
180
+ t && t.length > 0 && (o = `data:${t[0] === 137 && t[1] === 80 ? "image/png" : "image/jpeg"};base64,${H(t)}`);
181
+ let c = {}, l = L(e, "tags");
182
+ if (l) try {
183
+ c = JSON.parse(s(l));
178
184
  } catch {}
185
+ let u = L(e, "version") || "", d = s(L(e, "sourceUrl") || ""), f = s(L(e, "createdAt") || ""), p = s(L(e, "producer") || ""), m = Number.parseFloat(L(e, "dpr") || "0") || 0;
179
186
  return {
180
187
  originalImageDataUrl: o,
181
188
  annotationsSvg: r,
182
189
  width: i,
183
190
  height: a,
184
- tags: s
191
+ tags: c,
192
+ version: u,
193
+ sourceUrl: d,
194
+ createdAt: f,
195
+ producer: p,
196
+ dpr: m
185
197
  };
186
198
  }
187
- function P(t, n) {
199
+ function L(t, n) {
188
200
  let r = `<${e}:${n}>`, i = `</${e}:${n}>`, a = t.indexOf(r);
189
201
  if (a < 0) return null;
190
202
  let o = t.indexOf(i, a);
191
203
  return o < 0 ? null : t.substring(a + r.length, o);
192
204
  }
193
- function F(e) {
205
+ function R(e) {
194
206
  let t = 2;
195
207
  for (; t + 4 <= e.length && e[t] === 255;) {
196
208
  let r = e[t + 1];
197
209
  if (r === 217 || r === 218) break;
198
- let i = c(e, t + 2), a = t + 2 + i;
210
+ let i = d(e, t + 2), a = t + 2 + i;
199
211
  if (a > e.length) break;
200
- if (r === 225 && d(e, t + 4, n)) {
212
+ if (r === 225 && m(e, t + 4, n)) {
201
213
  let r = t + 4 + n.length;
202
214
  return new TextDecoder().decode(e.slice(r, a));
203
215
  }
@@ -205,17 +217,17 @@ function F(e) {
205
217
  }
206
218
  return null;
207
219
  }
208
- function I(e) {
220
+ function z(e) {
209
221
  let t = r.length, n = [], i = 2;
210
222
  for (; i + 4 <= e.length && e[i] === 255;) {
211
223
  let a = e[i + 1];
212
224
  if (a === 217 || a === 218) break;
213
- let o = c(e, i + 2), s = i + 2 + o;
225
+ let o = d(e, i + 2), s = i + 2 + o;
214
226
  if (s > e.length) break;
215
- if (a === 226 && d(e, i + 4, r)) {
227
+ if (a === 226 && m(e, i + 4, r)) {
216
228
  let r = i + 4 + t;
217
229
  if (r + 4 <= s) {
218
- let t = c(e, r), i = e.slice(r + 4, s);
230
+ let t = d(e, r), i = e.slice(r + 4, s);
219
231
  n.push({
220
232
  seq: t,
221
233
  data: i
@@ -224,14 +236,14 @@ function I(e) {
224
236
  }
225
237
  i = s;
226
238
  }
227
- return n.length === 0 ? null : (n.sort((e, t) => e.seq - t.seq), u(...n.map((e) => e.data)));
239
+ return n.length === 0 ? null : (n.sort((e, t) => e.seq - t.seq), p(...n.map((e) => e.data)));
228
240
  }
229
- function L(e) {
241
+ function B(e) {
230
242
  let t = 8;
231
243
  for (; t + 12 <= e.length;) {
232
- let n = l(e, t), r = String.fromCharCode(e[t + 4], e[t + 5], e[t + 6], e[t + 7]), a = t + 8, o = a + n + 4;
244
+ let n = f(e, t), r = String.fromCharCode(e[t + 4], e[t + 5], e[t + 6], e[t + 7]), a = t + 8, o = a + n + 4;
233
245
  if (o > e.length) break;
234
- if (r === "iTXt" && d(e, a, i)) {
246
+ if (r === "iTXt" && m(e, a, i)) {
235
247
  let t = a + i.length, r = 0, o = t;
236
248
  for (let i = t; i < a + n; i++) if (e[i] === 0 && r++, r >= 4) {
237
249
  o = i + 1;
@@ -243,17 +255,17 @@ function L(e) {
243
255
  }
244
256
  return null;
245
257
  }
246
- function R(e) {
258
+ function V(e) {
247
259
  let t = 8;
248
260
  for (; t + 12 <= e.length;) {
249
- let n = l(e, t), r = String.fromCharCode(e[t + 4], e[t + 5], e[t + 6], e[t + 7]), i = t + 8, a = i + n + 4;
261
+ let n = f(e, t), r = String.fromCharCode(e[t + 4], e[t + 5], e[t + 6], e[t + 7]), i = t + 8, a = i + n + 4;
250
262
  if (a > e.length) break;
251
263
  if (r === "svGo") return e.slice(i, i + n);
252
264
  t = a;
253
265
  }
254
266
  return null;
255
267
  }
256
- function z(e) {
268
+ function H(e) {
257
269
  let t = 32768, n = "";
258
270
  for (let r = 0; r < e.length; r += t) {
259
271
  let i = e.subarray(r, Math.min(r + t, e.length));
@@ -262,4 +274,4 @@ function z(e) {
262
274
  return btoa(n);
263
275
  }
264
276
  //#endregion
265
- export { T as WELL_KNOWN_TAG_KEYS, y as buildPngChunk, a as buildXmp, C as buildXmpTagsOnly, u as concat, v as crc32, E as createEditablePngBytes, f as dataUrlToUint8Array, O as readEditableImage, D as readEditablePngBytes, l as readU32be, d as startsWith, x as stripPngEditableLayer, s as u32be, g as writeJpegWithMetadata, S as writePngWithMetadata, w as writePngWithTagsOnly };
277
+ export { a as ANNOT_XMP_VERSION, O as WELL_KNOWN_TAG_KEYS, S as buildPngChunk, c as buildXmp, E as buildXmpTagsOnly, p as concat, x as crc32, k as createEditablePngBytes, h as dataUrlToUint8Array, j as readEditableImage, A as readEditablePngBytes, f as readU32be, m as startsWith, w as stripPngEditableLayer, u as u32be, y as writeJpegWithMetadata, T as writePngWithMetadata, D as writePngWithTagsOnly };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ingcreators/annot-core",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "description": "Annot SDK — SVG-first screenshot annotation format, storage abstractions, and editor primitives. The foundation shared by the PWA, Chrome extension, Electron desktop, VSCode extension, and the headless annotator.",
5
5
  "license": "Apache-2.0",
6
6
  "author": "ingcreators",
@@ -34,6 +34,10 @@
34
34
  "types": "./dist/xmp/xmp-bytes.d.ts",
35
35
  "default": "./dist/xmp-bytes.js"
36
36
  },
37
+ "./element-tree": {
38
+ "types": "./dist/element-tree/index.d.ts",
39
+ "default": "./dist/element-tree.js"
40
+ },
37
41
  "./styles/*": "./styles/*"
38
42
  },
39
43
  "files": [
@@ -47,14 +51,13 @@
47
51
  },
48
52
  "devDependencies": {
49
53
  "@types/js-yaml": "^4.0.9",
50
- "@types/pako": "^2.0.4",
51
54
  "typescript": "^6.0.3",
52
- "vite": "^8.0.16",
55
+ "vite": "^8.1.3",
53
56
  "vite-plugin-dts": "^5.0.3"
54
57
  },
55
58
  "dependencies": {
56
- "js-yaml": "^4.1.1",
57
- "pako": "^2.1.0"
59
+ "js-yaml": "^4.2.0",
60
+ "pako": "^3.0.1"
58
61
  },
59
62
  "scripts": {
60
63
  "build": "vite build",