web-remarq 0.1.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,321 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/core/index.ts
21
+ var core_exports = {};
22
+ __export(core_exports, {
23
+ AnnotationStorage: () => AnnotationStorage,
24
+ createFingerprint: () => createFingerprint,
25
+ matchElement: () => matchElement
26
+ });
27
+ module.exports = __toCommonJS(core_exports);
28
+
29
+ // src/core/hash-detect.ts
30
+ var CSS_MODULES_RE = /^(.+)__([a-zA-Z0-9]{3,})$/;
31
+ var STYLED_COMPONENTS_RE = /^sc-/;
32
+ var EMOTION_RE = /^css-[a-zA-Z0-9]+$/;
33
+ var PURE_HASH_RE = /^(?=.*[a-zA-Z])(?=.*\d)[a-zA-Z0-9]{8,}$/;
34
+ function isHashedClass(className) {
35
+ if (STYLED_COMPONENTS_RE.test(className)) return true;
36
+ if (EMOTION_RE.test(className)) return true;
37
+ if (CSS_MODULES_RE.test(className)) return true;
38
+ if (PURE_HASH_RE.test(className)) return true;
39
+ return false;
40
+ }
41
+ function stripHash(className) {
42
+ const match = className.match(CSS_MODULES_RE);
43
+ if (match) {
44
+ const prefix = className.slice(0, className.lastIndexOf("__"));
45
+ return prefix;
46
+ }
47
+ return className;
48
+ }
49
+ function filterClasses(classes, classFilter) {
50
+ const result = [];
51
+ for (const cls of classes) {
52
+ if (STYLED_COMPONENTS_RE.test(cls)) continue;
53
+ if (EMOTION_RE.test(cls)) continue;
54
+ if (PURE_HASH_RE.test(cls)) continue;
55
+ let stable = stripHash(cls);
56
+ if (classFilter && !classFilter(stable)) continue;
57
+ result.push(stable);
58
+ }
59
+ return result;
60
+ }
61
+
62
+ // src/core/fingerprint.ts
63
+ var TEXT_MAX_LENGTH = 50;
64
+ function createFingerprint(el, options) {
65
+ const dataAttr = options?.dataAttribute ?? "data-annotate";
66
+ return {
67
+ dataAnnotate: el.getAttribute(dataAttr) ?? null,
68
+ dataTestId: el.getAttribute("data-testid") ?? el.getAttribute("data-test") ?? el.getAttribute("data-cy") ?? null,
69
+ id: getStableId(el),
70
+ tagName: el.tagName.toLowerCase(),
71
+ textContent: getTextContent(el),
72
+ role: el.getAttribute("role") ?? null,
73
+ ariaLabel: el.getAttribute("aria-label") ?? null,
74
+ stableClasses: filterClasses(
75
+ Array.from(el.classList),
76
+ options?.classFilter
77
+ ),
78
+ domPath: buildDomPath(el),
79
+ siblingIndex: getSiblingIndex(el),
80
+ parentAnchor: findParentAnchor(el, dataAttr)
81
+ };
82
+ }
83
+ function getStableId(el) {
84
+ const id = el.id;
85
+ if (!id) return null;
86
+ if (isHashedClass(id)) return null;
87
+ return id;
88
+ }
89
+ function getTextContent(el) {
90
+ const text = el.textContent?.trim() ?? null;
91
+ if (!text) return null;
92
+ return text.length > TEXT_MAX_LENGTH ? text.slice(0, TEXT_MAX_LENGTH) : text;
93
+ }
94
+ function buildDomPath(el) {
95
+ const parts = [];
96
+ let current = el;
97
+ while (current && current !== document.body && parts.length < 5) {
98
+ parts.unshift(current.tagName.toLowerCase());
99
+ current = current.parentElement;
100
+ }
101
+ return parts.join(" > ");
102
+ }
103
+ function getSiblingIndex(el) {
104
+ const parent = el.parentElement;
105
+ if (!parent) return 0;
106
+ const children = Array.from(parent.children);
107
+ return children.indexOf(el);
108
+ }
109
+ function findParentAnchor(el, dataAttr) {
110
+ let current = el.parentElement;
111
+ while (current && current !== document.body) {
112
+ const value = current.getAttribute(dataAttr);
113
+ if (value) return value;
114
+ current = current.parentElement;
115
+ }
116
+ return null;
117
+ }
118
+
119
+ // src/core/matcher.ts
120
+ var MATCH_THRESHOLD = 50;
121
+ function levenshteinSimilarity(a, b) {
122
+ if (a === b) return 1;
123
+ if (!a.length || !b.length) return 0;
124
+ const matrix = [];
125
+ for (let i = 0; i <= a.length; i++) {
126
+ matrix[i] = [i];
127
+ }
128
+ for (let j = 0; j <= b.length; j++) {
129
+ matrix[0][j] = j;
130
+ }
131
+ for (let i = 1; i <= a.length; i++) {
132
+ for (let j = 1; j <= b.length; j++) {
133
+ const cost = a[i - 1] === b[j - 1] ? 0 : 1;
134
+ matrix[i][j] = Math.min(
135
+ matrix[i - 1][j] + 1,
136
+ matrix[i][j - 1] + 1,
137
+ matrix[i - 1][j - 1] + cost
138
+ );
139
+ }
140
+ }
141
+ const distance = matrix[a.length][b.length];
142
+ return 1 - distance / Math.max(a.length, b.length);
143
+ }
144
+ function textSimilarity(a, b) {
145
+ if (!a || !b) return 0;
146
+ const na = a.trim().toLowerCase();
147
+ const nb = b.trim().toLowerCase();
148
+ if (na === nb) return 1;
149
+ if (na.includes(nb) || nb.includes(na)) return 1;
150
+ return levenshteinSimilarity(na, nb);
151
+ }
152
+ function jaccardSimilarity(a, b) {
153
+ if (!a.length && !b.length) return 0;
154
+ const setA = new Set(a);
155
+ const setB = new Set(b);
156
+ let intersection = 0;
157
+ for (const item of setA) {
158
+ if (setB.has(item)) intersection++;
159
+ }
160
+ const union = (/* @__PURE__ */ new Set([...a, ...b])).size;
161
+ return union === 0 ? 0 : intersection / union;
162
+ }
163
+ function scoreCandidate(el, fp, dataAttr) {
164
+ let score = 0;
165
+ const elAnnotate = el.getAttribute(dataAttr);
166
+ if (fp.dataAnnotate && elAnnotate === fp.dataAnnotate) {
167
+ score += 100;
168
+ }
169
+ const elText = el.textContent?.trim().slice(0, 50) ?? null;
170
+ const textSim = textSimilarity(fp.textContent, elText);
171
+ if (textSim > 0.7) {
172
+ score += textSim * 35;
173
+ }
174
+ if (fp.role && el.getAttribute("role") === fp.role && fp.ariaLabel && el.getAttribute("aria-label") === fp.ariaLabel) {
175
+ score += 30;
176
+ }
177
+ if (fp.parentAnchor) {
178
+ let parent2 = el.parentElement;
179
+ while (parent2 && parent2 !== document.body) {
180
+ if (parent2.getAttribute(dataAttr) === fp.parentAnchor) {
181
+ score += 15;
182
+ break;
183
+ }
184
+ parent2 = parent2.parentElement;
185
+ }
186
+ }
187
+ if (fp.stableClasses.length > 0) {
188
+ const elClasses = Array.from(el.classList);
189
+ const jaccard = jaccardSimilarity(fp.stableClasses, elClasses);
190
+ score += jaccard * 15;
191
+ }
192
+ if (fp.domPath) {
193
+ const elPath = buildDomPath2(el);
194
+ const pathSim = levenshteinSimilarity(fp.domPath, elPath);
195
+ score += pathSim * 15;
196
+ }
197
+ const parent = el.parentElement;
198
+ if (parent) {
199
+ const idx = Array.from(parent.children).indexOf(el);
200
+ if (idx === fp.siblingIndex) {
201
+ score += 5;
202
+ }
203
+ }
204
+ return score;
205
+ }
206
+ function buildDomPath2(el) {
207
+ const parts = [];
208
+ let current = el;
209
+ while (current && current !== document.body && parts.length < 5) {
210
+ parts.unshift(current.tagName.toLowerCase());
211
+ current = current.parentElement;
212
+ }
213
+ return parts.join(" > ");
214
+ }
215
+ function matchElement(fp, options) {
216
+ const dataAttr = options?.dataAttribute ?? "data-annotate";
217
+ if (fp.dataAnnotate) {
218
+ const el = document.querySelector(`[${dataAttr}="${fp.dataAnnotate}"]`);
219
+ if (el) return el;
220
+ }
221
+ if (fp.dataTestId) {
222
+ const el = document.querySelector(
223
+ `[data-testid="${fp.dataTestId}"], [data-test="${fp.dataTestId}"], [data-cy="${fp.dataTestId}"]`
224
+ );
225
+ if (el) return el;
226
+ }
227
+ if (fp.id) {
228
+ const el = document.getElementById(fp.id);
229
+ if (el) return el;
230
+ }
231
+ const candidates = document.querySelectorAll(fp.tagName);
232
+ let bestEl = null;
233
+ let bestScore = 0;
234
+ for (const candidate of candidates) {
235
+ const score = scoreCandidate(candidate, fp, dataAttr);
236
+ if (score > bestScore) {
237
+ bestScore = score;
238
+ bestEl = candidate;
239
+ }
240
+ }
241
+ return bestScore >= MATCH_THRESHOLD ? bestEl : null;
242
+ }
243
+
244
+ // src/core/storage.ts
245
+ var STORAGE_KEY = "remarq:annotations";
246
+ var AnnotationStorage = class {
247
+ constructor() {
248
+ this.annotations = [];
249
+ this.extraFields = {};
250
+ this.isMemoryOnly = false;
251
+ this.load();
252
+ }
253
+ getAll() {
254
+ return [...this.annotations];
255
+ }
256
+ getByRoute(route) {
257
+ return this.annotations.filter((a) => a.route === route);
258
+ }
259
+ add(annotation) {
260
+ this.annotations.push(annotation);
261
+ this.save();
262
+ }
263
+ remove(id) {
264
+ this.annotations = this.annotations.filter((a) => a.id !== id);
265
+ this.save();
266
+ }
267
+ update(id, changes) {
268
+ const idx = this.annotations.findIndex((a) => a.id === id);
269
+ if (idx !== -1) {
270
+ this.annotations[idx] = { ...this.annotations[idx], ...changes };
271
+ this.save();
272
+ }
273
+ }
274
+ clearAll() {
275
+ this.annotations = [];
276
+ this.save();
277
+ }
278
+ exportJSON() {
279
+ return {
280
+ version: 1,
281
+ annotations: [...this.annotations]
282
+ };
283
+ }
284
+ importJSON(data) {
285
+ this.annotations = [...data.annotations];
286
+ this.save();
287
+ }
288
+ load() {
289
+ try {
290
+ const raw = localStorage.getItem(STORAGE_KEY);
291
+ if (raw) {
292
+ const parsed = JSON.parse(raw);
293
+ const { version, annotations, ...rest } = parsed;
294
+ this.annotations = annotations ?? [];
295
+ this.extraFields = rest;
296
+ }
297
+ } catch {
298
+ this.isMemoryOnly = true;
299
+ }
300
+ }
301
+ save() {
302
+ if (this.isMemoryOnly) return;
303
+ try {
304
+ const data = {
305
+ version: 1,
306
+ ...this.extraFields,
307
+ annotations: this.annotations
308
+ };
309
+ localStorage.setItem(STORAGE_KEY, JSON.stringify(data));
310
+ } catch {
311
+ this.isMemoryOnly = true;
312
+ }
313
+ }
314
+ };
315
+ // Annotate the CommonJS export names for ESM import in node:
316
+ 0 && (module.exports = {
317
+ AnnotationStorage,
318
+ createFingerprint,
319
+ matchElement
320
+ });
321
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/core/index.ts","../../src/core/hash-detect.ts","../../src/core/fingerprint.ts","../../src/core/matcher.ts","../../src/core/storage.ts"],"sourcesContent":["export type { Annotation, AnnotationStore, ElementFingerprint } from './types'\nexport { createFingerprint } from './fingerprint'\nexport { matchElement } from './matcher'\nexport { AnnotationStorage } from './storage'\n","const CSS_MODULES_RE = /^(.+)__([a-zA-Z0-9]{3,})$/\nconst STYLED_COMPONENTS_RE = /^sc-/\nconst EMOTION_RE = /^css-[a-zA-Z0-9]+$/\nconst PURE_HASH_RE = /^(?=.*[a-zA-Z])(?=.*\\d)[a-zA-Z0-9]{8,}$/\n\nexport function isHashedClass(className: string): boolean {\n if (STYLED_COMPONENTS_RE.test(className)) return true\n if (EMOTION_RE.test(className)) return true\n if (CSS_MODULES_RE.test(className)) return true\n if (PURE_HASH_RE.test(className)) return true\n return false\n}\n\nexport function stripHash(className: string): string {\n const match = className.match(CSS_MODULES_RE)\n if (match) {\n const prefix = className.slice(0, className.lastIndexOf('__'))\n return prefix\n }\n return className\n}\n\nexport function filterClasses(\n classes: string[],\n classFilter?: (className: string) => boolean,\n): string[] {\n const result: string[] = []\n\n for (const cls of classes) {\n if (STYLED_COMPONENTS_RE.test(cls)) continue\n if (EMOTION_RE.test(cls)) continue\n if (PURE_HASH_RE.test(cls)) continue\n\n let stable = stripHash(cls)\n\n if (classFilter && !classFilter(stable)) continue\n\n result.push(stable)\n }\n\n return result\n}\n","import type { ElementFingerprint, WebRemarqOptions } from './types'\nimport { filterClasses, isHashedClass } from './hash-detect'\n\nconst TEXT_MAX_LENGTH = 50\n\nexport function createFingerprint(\n el: HTMLElement,\n options?: Pick<WebRemarqOptions, 'classFilter' | 'dataAttribute'>,\n): ElementFingerprint {\n const dataAttr = options?.dataAttribute ?? 'data-annotate'\n\n return {\n dataAnnotate: el.getAttribute(dataAttr) ?? null,\n dataTestId: el.getAttribute('data-testid')\n ?? el.getAttribute('data-test')\n ?? el.getAttribute('data-cy')\n ?? null,\n id: getStableId(el),\n tagName: el.tagName.toLowerCase(),\n textContent: getTextContent(el),\n role: el.getAttribute('role') ?? null,\n ariaLabel: el.getAttribute('aria-label') ?? null,\n stableClasses: filterClasses(\n Array.from(el.classList),\n options?.classFilter,\n ),\n domPath: buildDomPath(el),\n siblingIndex: getSiblingIndex(el),\n parentAnchor: findParentAnchor(el, dataAttr),\n }\n}\n\nfunction getStableId(el: HTMLElement): string | null {\n const id = el.id\n if (!id) return null\n if (isHashedClass(id)) return null\n return id\n}\n\nfunction getTextContent(el: HTMLElement): string | null {\n const text = el.textContent?.trim() ?? null\n if (!text) return null\n return text.length > TEXT_MAX_LENGTH ? text.slice(0, TEXT_MAX_LENGTH) : text\n}\n\nfunction buildDomPath(el: HTMLElement): string {\n const parts: string[] = []\n let current: HTMLElement | null = el\n\n while (current && current !== document.body && parts.length < 5) {\n parts.unshift(current.tagName.toLowerCase())\n current = current.parentElement\n }\n\n return parts.join(' > ')\n}\n\nfunction getSiblingIndex(el: HTMLElement): number {\n const parent = el.parentElement\n if (!parent) return 0\n const children = Array.from(parent.children)\n return children.indexOf(el)\n}\n\nfunction findParentAnchor(el: HTMLElement, dataAttr: string): string | null {\n let current = el.parentElement\n while (current && current !== document.body) {\n const value = current.getAttribute(dataAttr)\n if (value) return value\n current = current.parentElement\n }\n return null\n}\n","import type { ElementFingerprint, WebRemarqOptions } from './types'\n\nconst MATCH_THRESHOLD = 50\n\nexport function levenshteinSimilarity(a: string, b: string): number {\n if (a === b) return 1\n if (!a.length || !b.length) return 0\n\n const matrix: number[][] = []\n for (let i = 0; i <= a.length; i++) {\n matrix[i] = [i]\n }\n for (let j = 0; j <= b.length; j++) {\n matrix[0][j] = j\n }\n\n for (let i = 1; i <= a.length; i++) {\n for (let j = 1; j <= b.length; j++) {\n const cost = a[i - 1] === b[j - 1] ? 0 : 1\n matrix[i][j] = Math.min(\n matrix[i - 1][j] + 1,\n matrix[i][j - 1] + 1,\n matrix[i - 1][j - 1] + cost,\n )\n }\n }\n\n const distance = matrix[a.length][b.length]\n return 1 - distance / Math.max(a.length, b.length)\n}\n\nfunction textSimilarity(a: string | null, b: string | null): number {\n if (!a || !b) return 0\n const na = a.trim().toLowerCase()\n const nb = b.trim().toLowerCase()\n if (na === nb) return 1\n if (na.includes(nb) || nb.includes(na)) return 1\n return levenshteinSimilarity(na, nb)\n}\n\nfunction jaccardSimilarity(a: string[], b: string[]): number {\n if (!a.length && !b.length) return 0\n const setA = new Set(a)\n const setB = new Set(b)\n let intersection = 0\n for (const item of setA) {\n if (setB.has(item)) intersection++\n }\n const union = new Set([...a, ...b]).size\n return union === 0 ? 0 : intersection / union\n}\n\nfunction scoreCandidate(el: HTMLElement, fp: ElementFingerprint, dataAttr: string): number {\n let score = 0\n\n // dataAnnotate match (+100)\n const elAnnotate = el.getAttribute(dataAttr)\n if (fp.dataAnnotate && elAnnotate === fp.dataAnnotate) {\n score += 100\n }\n\n // textContent match (+35 scaled)\n const elText = el.textContent?.trim().slice(0, 50) ?? null\n const textSim = textSimilarity(fp.textContent, elText)\n if (textSim > 0.7) {\n score += textSim * 35\n }\n\n // role + ariaLabel match (+30)\n if (fp.role && el.getAttribute('role') === fp.role &&\n fp.ariaLabel && el.getAttribute('aria-label') === fp.ariaLabel) {\n score += 30\n }\n\n // parentAnchor match (+15)\n if (fp.parentAnchor) {\n let parent = el.parentElement\n while (parent && parent !== document.body) {\n if (parent.getAttribute(dataAttr) === fp.parentAnchor) {\n score += 15\n break\n }\n parent = parent.parentElement\n }\n }\n\n // stableClasses overlap (+15 scaled)\n if (fp.stableClasses.length > 0) {\n const elClasses = Array.from(el.classList)\n const jaccard = jaccardSimilarity(fp.stableClasses, elClasses)\n score += jaccard * 15\n }\n\n // domPath match (+15 scaled)\n if (fp.domPath) {\n const elPath = buildDomPath(el)\n const pathSim = levenshteinSimilarity(fp.domPath, elPath)\n score += pathSim * 15\n }\n\n // siblingIndex match (+5)\n const parent = el.parentElement\n if (parent) {\n const idx = Array.from(parent.children).indexOf(el)\n if (idx === fp.siblingIndex) {\n score += 5\n }\n }\n\n return score\n}\n\nfunction buildDomPath(el: HTMLElement): string {\n const parts: string[] = []\n let current: HTMLElement | null = el\n while (current && current !== document.body && parts.length < 5) {\n parts.unshift(current.tagName.toLowerCase())\n current = current.parentElement\n }\n return parts.join(' > ')\n}\n\nexport function matchElement(\n fp: ElementFingerprint,\n options?: Pick<WebRemarqOptions, 'dataAttribute'>,\n): HTMLElement | null {\n const dataAttr = options?.dataAttribute ?? 'data-annotate'\n\n // 1. Exact match by data-annotate\n if (fp.dataAnnotate) {\n const el = document.querySelector<HTMLElement>(`[${dataAttr}=\"${fp.dataAnnotate}\"]`)\n if (el) return el\n }\n\n // 2. Exact match by data-testid\n if (fp.dataTestId) {\n const el = document.querySelector<HTMLElement>(\n `[data-testid=\"${fp.dataTestId}\"], [data-test=\"${fp.dataTestId}\"], [data-cy=\"${fp.dataTestId}\"]`,\n )\n if (el) return el\n }\n\n // 3. Exact match by id\n if (fp.id) {\n const el = document.getElementById(fp.id) as HTMLElement | null\n if (el) return el\n }\n\n // 4. Fuzzy match by tagName + weighted scoring\n const candidates = document.querySelectorAll<HTMLElement>(fp.tagName)\n let bestEl: HTMLElement | null = null\n let bestScore = 0\n\n for (const candidate of candidates) {\n const score = scoreCandidate(candidate, fp, dataAttr)\n if (score > bestScore) {\n bestScore = score\n bestEl = candidate\n }\n }\n\n return bestScore >= MATCH_THRESHOLD ? bestEl : null\n}\n","import type { Annotation, AnnotationStore } from './types'\n\nconst STORAGE_KEY = 'remarq:annotations'\n\nexport class AnnotationStorage {\n private annotations: Annotation[] = []\n private extraFields: Record<string, unknown> = {}\n isMemoryOnly = false\n\n constructor() {\n this.load()\n }\n\n getAll(): Annotation[] {\n return [...this.annotations]\n }\n\n getByRoute(route: string): Annotation[] {\n return this.annotations.filter((a) => a.route === route)\n }\n\n add(annotation: Annotation): void {\n this.annotations.push(annotation)\n this.save()\n }\n\n remove(id: string): void {\n this.annotations = this.annotations.filter((a) => a.id !== id)\n this.save()\n }\n\n update(id: string, changes: Partial<Annotation>): void {\n const idx = this.annotations.findIndex((a) => a.id === id)\n if (idx !== -1) {\n this.annotations[idx] = { ...this.annotations[idx], ...changes }\n this.save()\n }\n }\n\n clearAll(): void {\n this.annotations = []\n this.save()\n }\n\n exportJSON(): AnnotationStore {\n return {\n version: 1,\n annotations: [...this.annotations],\n }\n }\n\n importJSON(data: AnnotationStore): void {\n this.annotations = [...data.annotations]\n this.save()\n }\n\n private load(): void {\n try {\n const raw = localStorage.getItem(STORAGE_KEY)\n if (raw) {\n const parsed = JSON.parse(raw)\n const { version, annotations, ...rest } = parsed\n this.annotations = annotations ?? []\n this.extraFields = rest\n }\n } catch {\n this.isMemoryOnly = true\n }\n }\n\n private save(): void {\n if (this.isMemoryOnly) return\n try {\n const data = {\n version: 1,\n ...this.extraFields,\n annotations: this.annotations,\n }\n localStorage.setItem(STORAGE_KEY, JSON.stringify(data))\n } catch {\n this.isMemoryOnly = true\n }\n }\n}\n"],"mappings":"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","names":["parent","buildDomPath"]}
@@ -0,0 +1,53 @@
1
+ interface ElementFingerprint {
2
+ dataAnnotate: string | null;
3
+ dataTestId: string | null;
4
+ id: string | null;
5
+ tagName: string;
6
+ textContent: string | null;
7
+ role: string | null;
8
+ ariaLabel: string | null;
9
+ stableClasses: string[];
10
+ domPath: string;
11
+ siblingIndex: number;
12
+ parentAnchor: string | null;
13
+ }
14
+ interface Annotation {
15
+ id: string;
16
+ comment: string;
17
+ fingerprint: ElementFingerprint;
18
+ route: string;
19
+ timestamp: number;
20
+ status: 'pending' | 'resolved';
21
+ }
22
+ interface AnnotationStore {
23
+ version: 1;
24
+ annotations: Annotation[];
25
+ }
26
+ interface WebRemarqOptions {
27
+ theme?: 'light' | 'dark';
28
+ classFilter?: (className: string) => boolean;
29
+ dataAttribute?: string;
30
+ }
31
+
32
+ declare function createFingerprint(el: HTMLElement, options?: Pick<WebRemarqOptions, 'classFilter' | 'dataAttribute'>): ElementFingerprint;
33
+
34
+ declare function matchElement(fp: ElementFingerprint, options?: Pick<WebRemarqOptions, 'dataAttribute'>): HTMLElement | null;
35
+
36
+ declare class AnnotationStorage {
37
+ private annotations;
38
+ private extraFields;
39
+ isMemoryOnly: boolean;
40
+ constructor();
41
+ getAll(): Annotation[];
42
+ getByRoute(route: string): Annotation[];
43
+ add(annotation: Annotation): void;
44
+ remove(id: string): void;
45
+ update(id: string, changes: Partial<Annotation>): void;
46
+ clearAll(): void;
47
+ exportJSON(): AnnotationStore;
48
+ importJSON(data: AnnotationStore): void;
49
+ private load;
50
+ private save;
51
+ }
52
+
53
+ export { type Annotation, AnnotationStorage, type AnnotationStore, type ElementFingerprint, createFingerprint, matchElement };
@@ -0,0 +1,53 @@
1
+ interface ElementFingerprint {
2
+ dataAnnotate: string | null;
3
+ dataTestId: string | null;
4
+ id: string | null;
5
+ tagName: string;
6
+ textContent: string | null;
7
+ role: string | null;
8
+ ariaLabel: string | null;
9
+ stableClasses: string[];
10
+ domPath: string;
11
+ siblingIndex: number;
12
+ parentAnchor: string | null;
13
+ }
14
+ interface Annotation {
15
+ id: string;
16
+ comment: string;
17
+ fingerprint: ElementFingerprint;
18
+ route: string;
19
+ timestamp: number;
20
+ status: 'pending' | 'resolved';
21
+ }
22
+ interface AnnotationStore {
23
+ version: 1;
24
+ annotations: Annotation[];
25
+ }
26
+ interface WebRemarqOptions {
27
+ theme?: 'light' | 'dark';
28
+ classFilter?: (className: string) => boolean;
29
+ dataAttribute?: string;
30
+ }
31
+
32
+ declare function createFingerprint(el: HTMLElement, options?: Pick<WebRemarqOptions, 'classFilter' | 'dataAttribute'>): ElementFingerprint;
33
+
34
+ declare function matchElement(fp: ElementFingerprint, options?: Pick<WebRemarqOptions, 'dataAttribute'>): HTMLElement | null;
35
+
36
+ declare class AnnotationStorage {
37
+ private annotations;
38
+ private extraFields;
39
+ isMemoryOnly: boolean;
40
+ constructor();
41
+ getAll(): Annotation[];
42
+ getByRoute(route: string): Annotation[];
43
+ add(annotation: Annotation): void;
44
+ remove(id: string): void;
45
+ update(id: string, changes: Partial<Annotation>): void;
46
+ clearAll(): void;
47
+ exportJSON(): AnnotationStore;
48
+ importJSON(data: AnnotationStore): void;
49
+ private load;
50
+ private save;
51
+ }
52
+
53
+ export { type Annotation, AnnotationStorage, type AnnotationStore, type ElementFingerprint, createFingerprint, matchElement };