web-remarq 0.4.9 → 0.6.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/README.md +35 -1
- package/dist/core/index.cjs +94 -38
- package/dist/core/index.cjs.map +1 -1
- package/dist/core/index.d.cts +38 -12
- package/dist/core/index.d.ts +38 -12
- package/dist/core/index.js +93 -38
- package/dist/core/index.js.map +1 -1
- package/dist/index.cjs +103 -45
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +27 -1
- package/dist/index.d.ts +27 -1
- package/dist/index.js +102 -45
- package/dist/index.js.map +1 -1
- package/dist/web-remarq.global.global.js +102 -45
- package/dist/web-remarq.global.global.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -44,10 +44,15 @@ WebRemarq.init({ theme: 'light' })
|
|
|
44
44
|
Initialize the tool. Idempotent — safe to call multiple times.
|
|
45
45
|
|
|
46
46
|
```ts
|
|
47
|
+
import { WebRemarq, LocalStorageAdapter } from 'web-remarq'
|
|
48
|
+
|
|
47
49
|
WebRemarq.init({
|
|
48
50
|
theme: 'light', // 'light' | 'dark'
|
|
49
51
|
classFilter: (name) => boolean, // custom class filter for fingerprinting
|
|
50
52
|
dataAttribute: 'data-annotate', // which data-attr to use as stable anchor
|
|
53
|
+
position: 'bottom-right', // toolbar anchor
|
|
54
|
+
shortcuts: true, // enable keyboard shortcuts (default true)
|
|
55
|
+
storage: new LocalStorageAdapter() // pluggable storage backend (default)
|
|
51
56
|
})
|
|
52
57
|
```
|
|
53
58
|
|
|
@@ -90,6 +95,29 @@ Get all annotations, or filter by route.
|
|
|
90
95
|
|
|
91
96
|
Remove all annotations.
|
|
92
97
|
|
|
98
|
+
## Storage
|
|
99
|
+
|
|
100
|
+
Annotations persist via a pluggable `StorageAdapter` interface. Default = `LocalStorageAdapter` (localStorage, key `remarq:annotations`, automatic in-memory fallback on quota errors).
|
|
101
|
+
|
|
102
|
+
### Custom adapters
|
|
103
|
+
|
|
104
|
+
```ts
|
|
105
|
+
import { WebRemarq, type StorageAdapter, type Annotation, type AnnotationStore } from 'web-remarq'
|
|
106
|
+
|
|
107
|
+
const myAdapter: StorageAdapter = {
|
|
108
|
+
async load(): Promise<AnnotationStore | null> { /* ... */ },
|
|
109
|
+
async save(annotation: Annotation): Promise<void> { /* upsert by id */ },
|
|
110
|
+
async remove(id: string): Promise<void> { /* ... */ },
|
|
111
|
+
async clear(): Promise<void> { /* ... */ },
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
WebRemarq.init({ storage: myAdapter })
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
The interface is async by design — supports remote backends (Supabase, REST, IndexedDB) without changing the public WebRemarq API. The domain layer (`AnnotationStorage`) keeps an in-memory cache, so synchronous getters like `WebRemarq.getAnnotations()` stay sync.
|
|
118
|
+
|
|
119
|
+
`@web-remarq/cloud` (planned) will ship a Supabase-backed `StorageAdapter` implementation for team collaboration.
|
|
120
|
+
|
|
93
121
|
## Agent Export Format
|
|
94
122
|
|
|
95
123
|
The `export('agent')` format is optimized for AI coding agents:
|
|
@@ -134,7 +162,13 @@ Source detection uses a 3-level fallback:
|
|
|
134
162
|
For programmatic access without UI:
|
|
135
163
|
|
|
136
164
|
```ts
|
|
137
|
-
import {
|
|
165
|
+
import {
|
|
166
|
+
createFingerprint,
|
|
167
|
+
matchElement,
|
|
168
|
+
AnnotationStorage,
|
|
169
|
+
LocalStorageAdapter,
|
|
170
|
+
type StorageAdapter,
|
|
171
|
+
} from 'web-remarq/core'
|
|
138
172
|
```
|
|
139
173
|
|
|
140
174
|
## How It Works
|
package/dist/core/index.cjs
CHANGED
|
@@ -50,6 +50,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
50
50
|
var core_exports = {};
|
|
51
51
|
__export(core_exports, {
|
|
52
52
|
AnnotationStorage: () => AnnotationStorage,
|
|
53
|
+
LocalStorageAdapter: () => LocalStorageAdapter,
|
|
53
54
|
createFingerprint: () => createFingerprint,
|
|
54
55
|
detectRemarqPlugin: () => detectRemarqPlugin,
|
|
55
56
|
detectSource: () => detectSource,
|
|
@@ -379,79 +380,133 @@ function toBucket(width) {
|
|
|
379
380
|
}
|
|
380
381
|
|
|
381
382
|
// src/core/storage.ts
|
|
382
|
-
var STORAGE_KEY = "remarq:annotations";
|
|
383
383
|
var AnnotationStorage = class {
|
|
384
|
-
constructor() {
|
|
385
|
-
this.
|
|
386
|
-
this.
|
|
387
|
-
this.
|
|
388
|
-
|
|
384
|
+
constructor(adapter) {
|
|
385
|
+
this.adapter = adapter;
|
|
386
|
+
this.cache = [];
|
|
387
|
+
this.ready = this.init();
|
|
388
|
+
}
|
|
389
|
+
get isMemoryOnly() {
|
|
390
|
+
var _a;
|
|
391
|
+
return (_a = this.adapter.isMemoryOnly) != null ? _a : false;
|
|
389
392
|
}
|
|
390
393
|
getAll() {
|
|
391
|
-
return [...this.
|
|
394
|
+
return [...this.cache];
|
|
392
395
|
}
|
|
393
396
|
getByRoute(route) {
|
|
394
|
-
return this.
|
|
397
|
+
return this.cache.filter((a) => a.route === route);
|
|
395
398
|
}
|
|
396
|
-
add(annotation) {
|
|
397
|
-
this.
|
|
398
|
-
this.save();
|
|
399
|
+
async add(annotation) {
|
|
400
|
+
this.cache.push(annotation);
|
|
401
|
+
await this.adapter.save(annotation);
|
|
399
402
|
}
|
|
400
|
-
remove(id) {
|
|
401
|
-
this.
|
|
402
|
-
this.
|
|
403
|
+
async remove(id) {
|
|
404
|
+
this.cache = this.cache.filter((a) => a.id !== id);
|
|
405
|
+
await this.adapter.remove(id);
|
|
403
406
|
}
|
|
404
|
-
update(id, changes) {
|
|
405
|
-
const idx = this.
|
|
406
|
-
if (idx
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
407
|
+
async update(id, changes) {
|
|
408
|
+
const idx = this.cache.findIndex((a) => a.id === id);
|
|
409
|
+
if (idx === -1) return;
|
|
410
|
+
const updated = __spreadValues(__spreadValues({}, this.cache[idx]), changes);
|
|
411
|
+
this.cache[idx] = updated;
|
|
412
|
+
await this.adapter.save(updated);
|
|
410
413
|
}
|
|
411
|
-
clearAll() {
|
|
412
|
-
this.
|
|
413
|
-
this.
|
|
414
|
+
async clearAll() {
|
|
415
|
+
this.cache = [];
|
|
416
|
+
await this.adapter.clear();
|
|
414
417
|
}
|
|
415
418
|
exportJSON() {
|
|
416
419
|
return {
|
|
417
420
|
version: 1,
|
|
418
|
-
annotations: [...this.
|
|
421
|
+
annotations: [...this.cache]
|
|
419
422
|
};
|
|
420
423
|
}
|
|
421
|
-
importJSON(data) {
|
|
422
|
-
this.
|
|
424
|
+
async importJSON(data) {
|
|
425
|
+
this.cache = [...data.annotations];
|
|
423
426
|
this.migrateViewportBuckets();
|
|
424
|
-
this.
|
|
427
|
+
await this.adapter.clear();
|
|
428
|
+
for (const ann of this.cache) {
|
|
429
|
+
await this.adapter.save(ann);
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
async init() {
|
|
433
|
+
const data = await this.adapter.load();
|
|
434
|
+
if (data) {
|
|
435
|
+
this.cache = data.annotations;
|
|
436
|
+
this.migrateViewportBuckets();
|
|
437
|
+
}
|
|
425
438
|
}
|
|
426
439
|
migrateViewportBuckets() {
|
|
427
|
-
for (const ann of this.
|
|
440
|
+
for (const ann of this.cache) {
|
|
428
441
|
if (ann.viewportBucket == null && ann.viewport) {
|
|
429
442
|
const width = parseInt(ann.viewport.split("x")[0], 10);
|
|
430
443
|
ann.viewportBucket = toBucket(width);
|
|
431
444
|
}
|
|
432
445
|
}
|
|
433
446
|
}
|
|
434
|
-
|
|
447
|
+
};
|
|
448
|
+
|
|
449
|
+
// src/core/local-storage-adapter.ts
|
|
450
|
+
var STORAGE_KEY = "remarq:annotations";
|
|
451
|
+
var LocalStorageAdapter = class {
|
|
452
|
+
constructor() {
|
|
453
|
+
this.isMemoryOnly = false;
|
|
454
|
+
this.extraFields = {};
|
|
455
|
+
this.memoryStore = null;
|
|
456
|
+
}
|
|
457
|
+
async load() {
|
|
458
|
+
if (this.isMemoryOnly) return this.memoryStore;
|
|
435
459
|
try {
|
|
436
460
|
const raw = localStorage.getItem(STORAGE_KEY);
|
|
437
|
-
if (raw)
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
461
|
+
if (!raw) return null;
|
|
462
|
+
const parsed = JSON.parse(raw);
|
|
463
|
+
const _a = parsed, { version, annotations } = _a, rest = __objRest(_a, ["version", "annotations"]);
|
|
464
|
+
this.extraFields = rest;
|
|
465
|
+
const store = {
|
|
466
|
+
version: 1,
|
|
467
|
+
annotations: Array.isArray(annotations) ? annotations : []
|
|
468
|
+
};
|
|
469
|
+
this.memoryStore = store;
|
|
470
|
+
return store;
|
|
444
471
|
} catch (e) {
|
|
445
472
|
this.isMemoryOnly = true;
|
|
473
|
+
return this.memoryStore;
|
|
446
474
|
}
|
|
447
475
|
}
|
|
448
|
-
save() {
|
|
476
|
+
async save(annotation) {
|
|
477
|
+
const store = await this.ensureStore();
|
|
478
|
+
const idx = store.annotations.findIndex((a) => a.id === annotation.id);
|
|
479
|
+
if (idx === -1) {
|
|
480
|
+
store.annotations.push(annotation);
|
|
481
|
+
} else {
|
|
482
|
+
store.annotations[idx] = annotation;
|
|
483
|
+
}
|
|
484
|
+
this.persist(store);
|
|
485
|
+
}
|
|
486
|
+
async remove(id) {
|
|
487
|
+
const store = await this.ensureStore();
|
|
488
|
+
store.annotations = store.annotations.filter((a) => a.id !== id);
|
|
489
|
+
this.persist(store);
|
|
490
|
+
}
|
|
491
|
+
async clear() {
|
|
492
|
+
const store = await this.ensureStore();
|
|
493
|
+
store.annotations = [];
|
|
494
|
+
this.persist(store);
|
|
495
|
+
}
|
|
496
|
+
async ensureStore() {
|
|
497
|
+
if (this.memoryStore) return this.memoryStore;
|
|
498
|
+
const loaded = await this.load();
|
|
499
|
+
if (loaded) return loaded;
|
|
500
|
+
this.memoryStore = { version: 1, annotations: [] };
|
|
501
|
+
return this.memoryStore;
|
|
502
|
+
}
|
|
503
|
+
persist(store) {
|
|
449
504
|
if (this.isMemoryOnly) return;
|
|
450
505
|
try {
|
|
451
506
|
const data = __spreadProps(__spreadValues({
|
|
452
507
|
version: 1
|
|
453
508
|
}, this.extraFields), {
|
|
454
|
-
annotations:
|
|
509
|
+
annotations: store.annotations
|
|
455
510
|
});
|
|
456
511
|
localStorage.setItem(STORAGE_KEY, JSON.stringify(data));
|
|
457
512
|
} catch (e) {
|
|
@@ -544,6 +599,7 @@ function generateAgentExport(annotations, viewportBucket) {
|
|
|
544
599
|
// Annotate the CommonJS export names for ESM import in node:
|
|
545
600
|
0 && (module.exports = {
|
|
546
601
|
AnnotationStorage,
|
|
602
|
+
LocalStorageAdapter,
|
|
547
603
|
createFingerprint,
|
|
548
604
|
detectRemarqPlugin,
|
|
549
605
|
detectSource,
|
package/dist/core/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/core/index.ts","../../src/core/hash-detect.ts","../../src/core/source-detect.ts","../../src/core/fingerprint.ts","../../src/core/matcher.ts","../../src/core/viewport.ts","../../src/core/storage.ts","../../src/core/agent-export.ts"],"sourcesContent":["export type { Annotation, AnnotationStore, ElementFingerprint, CSSModuleClass, SourceDetectionResult, SearchConfidence, GrepQuery, AgentSearchHints, AgentAnnotationSource, AgentAnnotation, AgentExport, ToolbarPosition } from './types'\nexport { createFingerprint } from './fingerprint'\nexport { matchElement } from './matcher'\nexport { AnnotationStorage } from './storage'\nexport { detectRemarqPlugin, detectSource } from './source-detect'\nexport { generateAgentExport } from './agent-export'\n","import type { CSSModuleClass } from './types'\n\nconst CSS_MODULES_RE = /^(.+)__([a-zA-Z0-9]{3,})$/\nconst CSS_MODULES_3SEG_RE = /^([^_]+(?:[_-][^_]+)*)__([a-zA-Z][a-zA-Z0-9]*)__([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\nexport function decomposeCSSModules(classes: string[]): CSSModuleClass[] {\n const result: CSSModuleClass[] = []\n for (const cls of classes) {\n const match = cls.match(CSS_MODULES_3SEG_RE)\n if (match) {\n result.push({\n raw: cls,\n moduleHint: match[1],\n localName: match[2],\n })\n }\n }\n return result\n}\n","import type { SourceDetectionResult } from './types'\n\n/**\n * Level 1: Read data-remarq-source/data-remarq-component attrs\n * injected by @web-remarq/babel-plugin or @web-remarq/unplugin.\n */\nexport function detectRemarqPlugin(el: HTMLElement): SourceDetectionResult {\n const source = el.getAttribute('data-remarq-source')\n if (!source) return { source: null, component: null }\n return {\n source,\n component: el.getAttribute('data-remarq-component'),\n }\n}\n\n/**\n * Level 2a: Read data-source or data-locator attrs\n * from locator.js or similar external tools.\n */\nexport function detectExternalSource(el: HTMLElement): SourceDetectionResult {\n const source = el.dataset.source ?? el.getAttribute('data-locator')\n if (!source) return { source: null, component: null }\n return { source, component: null }\n}\n\n/**\n * Level 2b: Read React fiber _debugSource (dev mode only).\n * Unstable/best-effort — React internals are not public API.\n * Walks up the fiber tree because _debugSource lives on component fibers,\n * not on host (DOM element) fibers.\n */\nexport function detectReactFiber(el: HTMLElement): SourceDetectionResult {\n const key = Object.keys(el).find(k => k.startsWith('__reactFiber$'))\n if (!key) return { source: null, component: null }\n\n let current = (el as unknown as Record<string, unknown>)[key] as Record<string, unknown> | null\n // Walk up fiber tree to find nearest fiber with _debugSource (max 15 levels)\n let depth = 0\n while (current && depth < 15) {\n const debugSource = current._debugSource as { fileName?: string; lineNumber?: number; columnNumber?: number } | undefined\n if (debugSource?.fileName) {\n const source = `${debugSource.fileName}:${debugSource.lineNumber ?? 0}:${debugSource.columnNumber ?? 0}`\n\n // Try to get component name from fiber.type\n const fiberType = current.type as { displayName?: string; name?: string } | string | undefined\n const component = typeof fiberType === 'object' && fiberType\n ? (fiberType.displayName ?? fiberType.name ?? null)\n : null\n\n return { source, component }\n }\n current = current.return as Record<string, unknown> | null\n depth++\n }\n\n return { source: null, component: null }\n}\n\n/**\n * Runs Level 2 detectors in order. Returns first non-null result.\n */\nexport function detectSource(el: HTMLElement): SourceDetectionResult {\n const external = detectExternalSource(el)\n if (external.source) return external\n\n const fiber = detectReactFiber(el)\n if (fiber.source) return fiber\n\n return { source: null, component: null }\n}\n","import type { ElementFingerprint, WebRemarqOptions } from './types'\nimport { filterClasses, isHashedClass, decomposeCSSModules } from './hash-detect'\nimport { detectRemarqPlugin, detectSource } from './source-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 rawClasses: Array.from(el.classList),\n cssModules: decomposeCSSModules(Array.from(el.classList)),\n ...resolveSourceFields(el),\n }\n}\n\nfunction resolveSourceFields(el: HTMLElement): {\n sourceLocation: string | null\n componentName: string | null\n detectedSource: string | null\n detectedComponent: string | null\n} {\n const plugin = detectRemarqPlugin(el)\n if (plugin.source) {\n return {\n sourceLocation: plugin.source,\n componentName: plugin.component,\n detectedSource: null,\n detectedComponent: null,\n }\n }\n\n const detected = detectSource(el)\n return {\n sourceLocation: null,\n componentName: null,\n detectedSource: detected.source,\n detectedComponent: detected.component,\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 // Use only direct text nodes, not nested children's text\n let text = ''\n for (const node of Array.from(el.childNodes)) {\n if (node.nodeType === Node.TEXT_NODE) {\n text += node.textContent ?? ''\n }\n }\n text = text.trim()\n\n // If no direct text, try first meaningful child's text (for wrappers like <span><b>Text</b></span>)\n if (!text && el.children.length <= 3) {\n text = el.textContent?.trim() ?? ''\n }\n\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 let segment = current.tagName.toLowerCase()\n const stable = filterClasses(Array.from(current.classList))\n if (stable.length > 0) {\n segment += '.' + stable.slice(0, 2).join('.')\n }\n parts.unshift(segment)\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'\nimport { filterClasses } from './hash-detect'\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 (+30 scaled)\n if (fp.stableClasses.length > 0) {\n const elClasses = filterClasses(Array.from(el.classList))\n const jaccard = jaccardSimilarity(fp.stableClasses, elClasses)\n score += jaccard * 30\n }\n\n // domPath match (+20 scaled)\n if (fp.domPath) {\n const elPath = buildDomPath(el)\n const pathSim = levenshteinSimilarity(fp.domPath, elPath)\n score += pathSim * 20\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 let segment = current.tagName.toLowerCase()\n const stable = filterClasses(Array.from(current.classList))\n if (stable.length > 0) {\n segment += '.' + stable.slice(0, 2).join('.')\n }\n parts.unshift(segment)\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","type BucketChangeCallback = () => void\n\nlet currentBucket: number = 0\nlet onBucketChange: BucketChangeCallback | null = null\nlet resizeHandler: (() => void) | null = null\n\nexport function toBucket(width: number): number {\n return Math.floor(width / 100) * 100\n}\n\nfunction debounce(fn: () => void, ms: number): () => void {\n let timer: ReturnType<typeof setTimeout>\n return () => {\n clearTimeout(timer)\n timer = setTimeout(fn, ms)\n }\n}\n\nexport function initViewportListener(callback: BucketChangeCallback): void {\n currentBucket = toBucket(window.innerWidth)\n onBucketChange = callback\n\n resizeHandler = debounce(() => {\n const newBucket = toBucket(window.innerWidth)\n if (newBucket !== currentBucket) {\n currentBucket = newBucket\n onBucketChange?.()\n }\n }, 300)\n\n window.addEventListener('resize', resizeHandler)\n}\n\nexport function destroyViewportListener(): void {\n if (resizeHandler) {\n window.removeEventListener('resize', resizeHandler)\n resizeHandler = null\n }\n onBucketChange = null\n}\n","import type { Annotation, AnnotationStore } from './types'\nimport { toBucket } from './viewport'\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.migrateViewportBuckets()\n this.save()\n }\n\n private migrateViewportBuckets(): void {\n for (const ann of this.annotations) {\n if (ann.viewportBucket == null && ann.viewport) {\n const width = parseInt(ann.viewport.split('x')[0], 10)\n ann.viewportBucket = toBucket(width)\n }\n }\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 this.migrateViewportBuckets()\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","import type {\n Annotation,\n AgentExport,\n AgentAnnotation,\n AgentAnnotationSource,\n AgentSearchHints,\n GrepQuery,\n ElementFingerprint,\n} from './types'\n\nfunction parseSourceLocation(raw: string): { file: string; line: number; column: number } | null {\n const parts = raw.split(':')\n if (parts.length < 2) return null\n // file path may contain \":\" on Windows (C:\\...), so rejoin all but last 2\n const column = parseInt(parts.pop()!, 10)\n const line = parseInt(parts.pop()!, 10)\n const file = parts.join(':')\n if (!file || isNaN(line)) return null\n return { file, line, column: isNaN(column) ? 0 : column }\n}\n\nfunction resolveSource(fp: ElementFingerprint): AgentAnnotationSource | null {\n // Level 1: plugin source\n if (fp.sourceLocation) {\n const parsed = parseSourceLocation(fp.sourceLocation)\n if (parsed) return { ...parsed, component: fp.componentName ?? null }\n }\n\n // Level 2: detected source\n if (fp.detectedSource) {\n const parsed = parseSourceLocation(fp.detectedSource)\n if (parsed) return { ...parsed, component: fp.detectedComponent ?? null }\n }\n\n return null\n}\n\nconst TEMPLATE_GLOB = '*.{tsx,jsx,vue,svelte,html}'\nconst CSS_MODULE_GLOB = '*.module.{css,scss,less}'\nconst COMPONENT_GLOB = '*.{tsx,jsx,vue,ts,js}'\n\nfunction buildSearchHints(fp: ElementFingerprint): AgentSearchHints {\n const grepQueries: GrepQuery[] = []\n\n // High confidence — unique selectors\n if (fp.dataAnnotate) {\n grepQueries.push({ query: `data-annotate=\"${fp.dataAnnotate}\"`, glob: TEMPLATE_GLOB, confidence: 'high' })\n }\n if (fp.dataTestId) {\n grepQueries.push({ query: `data-testid=\"${fp.dataTestId}\"`, glob: TEMPLATE_GLOB, confidence: 'high' })\n }\n if (fp.id) {\n grepQueries.push({ query: `id=\"${fp.id}\"`, glob: TEMPLATE_GLOB, confidence: 'high' })\n }\n if (fp.ariaLabel) {\n grepQueries.push({ query: `aria-label=\"${fp.ariaLabel}\"`, glob: TEMPLATE_GLOB, confidence: 'high' })\n }\n\n // Medium confidence — text, CSS modules, role\n if (fp.textContent) {\n grepQueries.push({ query: `\"${fp.textContent}\"`, glob: TEMPLATE_GLOB, confidence: 'medium' })\n }\n if (fp.role) {\n grepQueries.push({ query: `role=\"${fp.role}\"`, glob: TEMPLATE_GLOB, confidence: 'medium' })\n }\n if (fp.cssModules?.length) {\n for (const mod of fp.cssModules) {\n grepQueries.push({ query: `.${mod.localName}`, glob: CSS_MODULE_GLOB, confidence: 'medium' })\n grepQueries.push({ query: `styles.${mod.localName}`, glob: COMPONENT_GLOB, confidence: 'medium' })\n }\n }\n\n // Low confidence — classes, domPath\n if (fp.stableClasses.length) {\n for (const cls of fp.stableClasses.slice(0, 3)) {\n grepQueries.push({ query: `\"${cls}\"`, glob: TEMPLATE_GLOB, confidence: 'low' })\n }\n }\n\n return {\n grepQueries,\n domContext: fp.domPath,\n tagName: fp.tagName,\n classes: fp.rawClasses ?? fp.stableClasses,\n }\n}\n\nexport function generateAgentExport(annotations: Annotation[], viewportBucket: number): AgentExport {\n const agentAnnotations: AgentAnnotation[] = annotations.map(ann => ({\n id: ann.id,\n route: ann.route,\n comment: ann.comment,\n status: ann.status,\n timestamp: ann.timestamp,\n source: resolveSource(ann.fingerprint),\n searchHints: buildSearchHints(ann.fingerprint),\n }))\n\n return {\n version: 1,\n format: 'agent',\n viewportBucket,\n annotations: agentAnnotations,\n }\n}\n"],"mappings":"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","names":["parent","buildDomPath"]}
|
|
1
|
+
{"version":3,"sources":["../../src/core/index.ts","../../src/core/hash-detect.ts","../../src/core/source-detect.ts","../../src/core/fingerprint.ts","../../src/core/matcher.ts","../../src/core/viewport.ts","../../src/core/storage.ts","../../src/core/local-storage-adapter.ts","../../src/core/agent-export.ts"],"sourcesContent":["export type {\n Annotation,\n AnnotationStore,\n ElementFingerprint,\n CSSModuleClass,\n SourceDetectionResult,\n SearchConfidence,\n GrepQuery,\n AgentSearchHints,\n AgentAnnotationSource,\n AgentAnnotation,\n AgentExport,\n ToolbarPosition,\n StorageAdapter,\n StorageChangeEvent,\n} from './types'\nexport { createFingerprint } from './fingerprint'\nexport { matchElement } from './matcher'\nexport { AnnotationStorage } from './storage'\nexport { LocalStorageAdapter } from './local-storage-adapter'\nexport { detectRemarqPlugin, detectSource } from './source-detect'\nexport { generateAgentExport } from './agent-export'\n","import type { CSSModuleClass } from './types'\n\nconst CSS_MODULES_RE = /^(.+)__([a-zA-Z0-9]{3,})$/\nconst CSS_MODULES_3SEG_RE = /^([^_]+(?:[_-][^_]+)*)__([a-zA-Z][a-zA-Z0-9]*)__([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\nexport function decomposeCSSModules(classes: string[]): CSSModuleClass[] {\n const result: CSSModuleClass[] = []\n for (const cls of classes) {\n const match = cls.match(CSS_MODULES_3SEG_RE)\n if (match) {\n result.push({\n raw: cls,\n moduleHint: match[1],\n localName: match[2],\n })\n }\n }\n return result\n}\n","import type { SourceDetectionResult } from './types'\n\n/**\n * Level 1: Read data-remarq-source/data-remarq-component attrs\n * injected by @web-remarq/babel-plugin or @web-remarq/unplugin.\n */\nexport function detectRemarqPlugin(el: HTMLElement): SourceDetectionResult {\n const source = el.getAttribute('data-remarq-source')\n if (!source) return { source: null, component: null }\n return {\n source,\n component: el.getAttribute('data-remarq-component'),\n }\n}\n\n/**\n * Level 2a: Read data-source or data-locator attrs\n * from locator.js or similar external tools.\n */\nexport function detectExternalSource(el: HTMLElement): SourceDetectionResult {\n const source = el.dataset.source ?? el.getAttribute('data-locator')\n if (!source) return { source: null, component: null }\n return { source, component: null }\n}\n\n/**\n * Level 2b: Read React fiber _debugSource (dev mode only).\n * Unstable/best-effort — React internals are not public API.\n * Walks up the fiber tree because _debugSource lives on component fibers,\n * not on host (DOM element) fibers.\n */\nexport function detectReactFiber(el: HTMLElement): SourceDetectionResult {\n const key = Object.keys(el).find(k => k.startsWith('__reactFiber$'))\n if (!key) return { source: null, component: null }\n\n let current = (el as unknown as Record<string, unknown>)[key] as Record<string, unknown> | null\n // Walk up fiber tree to find nearest fiber with _debugSource (max 15 levels)\n let depth = 0\n while (current && depth < 15) {\n const debugSource = current._debugSource as { fileName?: string; lineNumber?: number; columnNumber?: number } | undefined\n if (debugSource?.fileName) {\n const source = `${debugSource.fileName}:${debugSource.lineNumber ?? 0}:${debugSource.columnNumber ?? 0}`\n\n // Try to get component name from fiber.type\n const fiberType = current.type as { displayName?: string; name?: string } | string | undefined\n const component = typeof fiberType === 'object' && fiberType\n ? (fiberType.displayName ?? fiberType.name ?? null)\n : null\n\n return { source, component }\n }\n current = current.return as Record<string, unknown> | null\n depth++\n }\n\n return { source: null, component: null }\n}\n\n/**\n * Runs Level 2 detectors in order. Returns first non-null result.\n */\nexport function detectSource(el: HTMLElement): SourceDetectionResult {\n const external = detectExternalSource(el)\n if (external.source) return external\n\n const fiber = detectReactFiber(el)\n if (fiber.source) return fiber\n\n return { source: null, component: null }\n}\n","import type { ElementFingerprint, WebRemarqOptions } from './types'\nimport { filterClasses, isHashedClass, decomposeCSSModules } from './hash-detect'\nimport { detectRemarqPlugin, detectSource } from './source-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 rawClasses: Array.from(el.classList),\n cssModules: decomposeCSSModules(Array.from(el.classList)),\n ...resolveSourceFields(el),\n }\n}\n\nfunction resolveSourceFields(el: HTMLElement): {\n sourceLocation: string | null\n componentName: string | null\n detectedSource: string | null\n detectedComponent: string | null\n} {\n const plugin = detectRemarqPlugin(el)\n if (plugin.source) {\n return {\n sourceLocation: plugin.source,\n componentName: plugin.component,\n detectedSource: null,\n detectedComponent: null,\n }\n }\n\n const detected = detectSource(el)\n return {\n sourceLocation: null,\n componentName: null,\n detectedSource: detected.source,\n detectedComponent: detected.component,\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 // Use only direct text nodes, not nested children's text\n let text = ''\n for (const node of Array.from(el.childNodes)) {\n if (node.nodeType === Node.TEXT_NODE) {\n text += node.textContent ?? ''\n }\n }\n text = text.trim()\n\n // If no direct text, try first meaningful child's text (for wrappers like <span><b>Text</b></span>)\n if (!text && el.children.length <= 3) {\n text = el.textContent?.trim() ?? ''\n }\n\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 let segment = current.tagName.toLowerCase()\n const stable = filterClasses(Array.from(current.classList))\n if (stable.length > 0) {\n segment += '.' + stable.slice(0, 2).join('.')\n }\n parts.unshift(segment)\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'\nimport { filterClasses } from './hash-detect'\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 (+30 scaled)\n if (fp.stableClasses.length > 0) {\n const elClasses = filterClasses(Array.from(el.classList))\n const jaccard = jaccardSimilarity(fp.stableClasses, elClasses)\n score += jaccard * 30\n }\n\n // domPath match (+20 scaled)\n if (fp.domPath) {\n const elPath = buildDomPath(el)\n const pathSim = levenshteinSimilarity(fp.domPath, elPath)\n score += pathSim * 20\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 let segment = current.tagName.toLowerCase()\n const stable = filterClasses(Array.from(current.classList))\n if (stable.length > 0) {\n segment += '.' + stable.slice(0, 2).join('.')\n }\n parts.unshift(segment)\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","type BucketChangeCallback = () => void\n\nlet currentBucket: number = 0\nlet onBucketChange: BucketChangeCallback | null = null\nlet resizeHandler: (() => void) | null = null\n\nexport function toBucket(width: number): number {\n return Math.floor(width / 100) * 100\n}\n\nfunction debounce(fn: () => void, ms: number): () => void {\n let timer: ReturnType<typeof setTimeout>\n return () => {\n clearTimeout(timer)\n timer = setTimeout(fn, ms)\n }\n}\n\nexport function initViewportListener(callback: BucketChangeCallback): void {\n currentBucket = toBucket(window.innerWidth)\n onBucketChange = callback\n\n resizeHandler = debounce(() => {\n const newBucket = toBucket(window.innerWidth)\n if (newBucket !== currentBucket) {\n currentBucket = newBucket\n onBucketChange?.()\n }\n }, 300)\n\n window.addEventListener('resize', resizeHandler)\n}\n\nexport function destroyViewportListener(): void {\n if (resizeHandler) {\n window.removeEventListener('resize', resizeHandler)\n resizeHandler = null\n }\n onBucketChange = null\n}\n","import type { Annotation, AnnotationStore, StorageAdapter } from './types'\nimport { toBucket } from './viewport'\n\nexport class AnnotationStorage {\n private cache: Annotation[] = []\n readonly ready: Promise<void>\n\n constructor(private adapter: StorageAdapter) {\n this.ready = this.init()\n }\n\n get isMemoryOnly(): boolean {\n return this.adapter.isMemoryOnly ?? false\n }\n\n getAll(): Annotation[] {\n return [...this.cache]\n }\n\n getByRoute(route: string): Annotation[] {\n return this.cache.filter((a) => a.route === route)\n }\n\n async add(annotation: Annotation): Promise<void> {\n this.cache.push(annotation)\n await this.adapter.save(annotation)\n }\n\n async remove(id: string): Promise<void> {\n this.cache = this.cache.filter((a) => a.id !== id)\n await this.adapter.remove(id)\n }\n\n async update(id: string, changes: Partial<Annotation>): Promise<void> {\n const idx = this.cache.findIndex((a) => a.id === id)\n if (idx === -1) return\n const updated = { ...this.cache[idx], ...changes }\n this.cache[idx] = updated\n await this.adapter.save(updated)\n }\n\n async clearAll(): Promise<void> {\n this.cache = []\n await this.adapter.clear()\n }\n\n exportJSON(): AnnotationStore {\n return {\n version: 1,\n annotations: [...this.cache],\n }\n }\n\n async importJSON(data: AnnotationStore): Promise<void> {\n this.cache = [...data.annotations]\n this.migrateViewportBuckets()\n await this.adapter.clear()\n for (const ann of this.cache) {\n await this.adapter.save(ann)\n }\n }\n\n private async init(): Promise<void> {\n const data = await this.adapter.load()\n if (data) {\n this.cache = data.annotations\n this.migrateViewportBuckets()\n }\n }\n\n private migrateViewportBuckets(): void {\n for (const ann of this.cache) {\n if (ann.viewportBucket == null && ann.viewport) {\n const width = parseInt(ann.viewport.split('x')[0], 10)\n ann.viewportBucket = toBucket(width)\n }\n }\n }\n}\n","import type { Annotation, AnnotationStore, StorageAdapter } from './types';\n\nconst STORAGE_KEY = 'remarq:annotations';\n\nexport class LocalStorageAdapter implements StorageAdapter {\n isMemoryOnly = false;\n private extraFields: Record<string, unknown> = {};\n private memoryStore: AnnotationStore | null = null;\n\n async load(): Promise<AnnotationStore | null> {\n if (this.isMemoryOnly) return this.memoryStore;\n try {\n const raw = localStorage.getItem(STORAGE_KEY);\n if (!raw) return null;\n const parsed = JSON.parse(raw);\n const { version, annotations, ...rest } = parsed;\n this.extraFields = rest;\n const store: AnnotationStore = {\n version: 1,\n annotations: Array.isArray(annotations) ? annotations : [],\n };\n this.memoryStore = store;\n return store;\n } catch {\n this.isMemoryOnly = true;\n return this.memoryStore;\n }\n }\n\n async save(annotation: Annotation): Promise<void> {\n const store = await this.ensureStore();\n const idx = store.annotations.findIndex((a) => a.id === annotation.id);\n if (idx === -1) {\n store.annotations.push(annotation);\n } else {\n store.annotations[idx] = annotation;\n }\n this.persist(store);\n }\n\n async remove(id: string): Promise<void> {\n const store = await this.ensureStore();\n store.annotations = store.annotations.filter((a) => a.id !== id);\n this.persist(store);\n }\n\n async clear(): Promise<void> {\n const store = await this.ensureStore();\n store.annotations = [];\n this.persist(store);\n }\n\n private async ensureStore(): Promise<AnnotationStore> {\n if (this.memoryStore) return this.memoryStore;\n const loaded = await this.load();\n if (loaded) return loaded;\n this.memoryStore = { version: 1, annotations: [] };\n return this.memoryStore;\n }\n\n private persist(store: AnnotationStore): void {\n if (this.isMemoryOnly) return;\n try {\n const data = {\n version: 1,\n ...this.extraFields,\n annotations: store.annotations,\n };\n localStorage.setItem(STORAGE_KEY, JSON.stringify(data));\n } catch {\n this.isMemoryOnly = true;\n }\n }\n}\n","import type {\n Annotation,\n AgentExport,\n AgentAnnotation,\n AgentAnnotationSource,\n AgentSearchHints,\n GrepQuery,\n ElementFingerprint,\n} from './types'\n\nfunction parseSourceLocation(raw: string): { file: string; line: number; column: number } | null {\n const parts = raw.split(':')\n if (parts.length < 2) return null\n // file path may contain \":\" on Windows (C:\\...), so rejoin all but last 2\n const column = parseInt(parts.pop()!, 10)\n const line = parseInt(parts.pop()!, 10)\n const file = parts.join(':')\n if (!file || isNaN(line)) return null\n return { file, line, column: isNaN(column) ? 0 : column }\n}\n\nfunction resolveSource(fp: ElementFingerprint): AgentAnnotationSource | null {\n // Level 1: plugin source\n if (fp.sourceLocation) {\n const parsed = parseSourceLocation(fp.sourceLocation)\n if (parsed) return { ...parsed, component: fp.componentName ?? null }\n }\n\n // Level 2: detected source\n if (fp.detectedSource) {\n const parsed = parseSourceLocation(fp.detectedSource)\n if (parsed) return { ...parsed, component: fp.detectedComponent ?? null }\n }\n\n return null\n}\n\nconst TEMPLATE_GLOB = '*.{tsx,jsx,vue,svelte,html}'\nconst CSS_MODULE_GLOB = '*.module.{css,scss,less}'\nconst COMPONENT_GLOB = '*.{tsx,jsx,vue,ts,js}'\n\nfunction buildSearchHints(fp: ElementFingerprint): AgentSearchHints {\n const grepQueries: GrepQuery[] = []\n\n // High confidence — unique selectors\n if (fp.dataAnnotate) {\n grepQueries.push({ query: `data-annotate=\"${fp.dataAnnotate}\"`, glob: TEMPLATE_GLOB, confidence: 'high' })\n }\n if (fp.dataTestId) {\n grepQueries.push({ query: `data-testid=\"${fp.dataTestId}\"`, glob: TEMPLATE_GLOB, confidence: 'high' })\n }\n if (fp.id) {\n grepQueries.push({ query: `id=\"${fp.id}\"`, glob: TEMPLATE_GLOB, confidence: 'high' })\n }\n if (fp.ariaLabel) {\n grepQueries.push({ query: `aria-label=\"${fp.ariaLabel}\"`, glob: TEMPLATE_GLOB, confidence: 'high' })\n }\n\n // Medium confidence — text, CSS modules, role\n if (fp.textContent) {\n grepQueries.push({ query: `\"${fp.textContent}\"`, glob: TEMPLATE_GLOB, confidence: 'medium' })\n }\n if (fp.role) {\n grepQueries.push({ query: `role=\"${fp.role}\"`, glob: TEMPLATE_GLOB, confidence: 'medium' })\n }\n if (fp.cssModules?.length) {\n for (const mod of fp.cssModules) {\n grepQueries.push({ query: `.${mod.localName}`, glob: CSS_MODULE_GLOB, confidence: 'medium' })\n grepQueries.push({ query: `styles.${mod.localName}`, glob: COMPONENT_GLOB, confidence: 'medium' })\n }\n }\n\n // Low confidence — classes, domPath\n if (fp.stableClasses.length) {\n for (const cls of fp.stableClasses.slice(0, 3)) {\n grepQueries.push({ query: `\"${cls}\"`, glob: TEMPLATE_GLOB, confidence: 'low' })\n }\n }\n\n return {\n grepQueries,\n domContext: fp.domPath,\n tagName: fp.tagName,\n classes: fp.rawClasses ?? fp.stableClasses,\n }\n}\n\nexport function generateAgentExport(annotations: Annotation[], viewportBucket: number): AgentExport {\n const agentAnnotations: AgentAnnotation[] = annotations.map(ann => ({\n id: ann.id,\n route: ann.route,\n comment: ann.comment,\n status: ann.status,\n timestamp: ann.timestamp,\n source: resolveSource(ann.fingerprint),\n searchHints: buildSearchHints(ann.fingerprint),\n }))\n\n return {\n version: 1,\n format: 'agent',\n viewportBucket,\n annotations: agentAnnotations,\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACEA,IAAM,iBAAiB;AACvB,IAAM,sBAAsB;AAC5B,IAAM,uBAAuB;AAC7B,IAAM,aAAa;AACnB,IAAM,eAAe;AAEd,SAAS,cAAc,WAA4B;AACxD,MAAI,qBAAqB,KAAK,SAAS,EAAG,QAAO;AACjD,MAAI,WAAW,KAAK,SAAS,EAAG,QAAO;AACvC,MAAI,eAAe,KAAK,SAAS,EAAG,QAAO;AAC3C,MAAI,aAAa,KAAK,SAAS,EAAG,QAAO;AACzC,SAAO;AACT;AAEO,SAAS,UAAU,WAA2B;AACnD,QAAM,QAAQ,UAAU,MAAM,cAAc;AAC5C,MAAI,OAAO;AACT,UAAM,SAAS,UAAU,MAAM,GAAG,UAAU,YAAY,IAAI,CAAC;AAC7D,WAAO;AAAA,EACT;AACA,SAAO;AACT;AAEO,SAAS,cACd,SACA,aACU;AACV,QAAM,SAAmB,CAAC;AAE1B,aAAW,OAAO,SAAS;AACzB,QAAI,qBAAqB,KAAK,GAAG,EAAG;AACpC,QAAI,WAAW,KAAK,GAAG,EAAG;AAC1B,QAAI,aAAa,KAAK,GAAG,EAAG;AAE5B,QAAI,SAAS,UAAU,GAAG;AAE1B,QAAI,eAAe,CAAC,YAAY,MAAM,EAAG;AAEzC,WAAO,KAAK,MAAM;AAAA,EACpB;AAEA,SAAO;AACT;AAEO,SAAS,oBAAoB,SAAqC;AACvE,QAAM,SAA2B,CAAC;AAClC,aAAW,OAAO,SAAS;AACzB,UAAM,QAAQ,IAAI,MAAM,mBAAmB;AAC3C,QAAI,OAAO;AACT,aAAO,KAAK;AAAA,QACV,KAAK;AAAA,QACL,YAAY,MAAM,CAAC;AAAA,QACnB,WAAW,MAAM,CAAC;AAAA,MACpB,CAAC;AAAA,IACH;AAAA,EACF;AACA,SAAO;AACT;;;ACrDO,SAAS,mBAAmB,IAAwC;AACzE,QAAM,SAAS,GAAG,aAAa,oBAAoB;AACnD,MAAI,CAAC,OAAQ,QAAO,EAAE,QAAQ,MAAM,WAAW,KAAK;AACpD,SAAO;AAAA,IACL;AAAA,IACA,WAAW,GAAG,aAAa,uBAAuB;AAAA,EACpD;AACF;AAMO,SAAS,qBAAqB,IAAwC;AAnB7E;AAoBE,QAAM,UAAS,QAAG,QAAQ,WAAX,YAAqB,GAAG,aAAa,cAAc;AAClE,MAAI,CAAC,OAAQ,QAAO,EAAE,QAAQ,MAAM,WAAW,KAAK;AACpD,SAAO,EAAE,QAAQ,WAAW,KAAK;AACnC;AAQO,SAAS,iBAAiB,IAAwC;AA/BzE;AAgCE,QAAM,MAAM,OAAO,KAAK,EAAE,EAAE,KAAK,OAAK,EAAE,WAAW,eAAe,CAAC;AACnE,MAAI,CAAC,IAAK,QAAO,EAAE,QAAQ,MAAM,WAAW,KAAK;AAEjD,MAAI,UAAW,GAA0C,GAAG;AAE5D,MAAI,QAAQ;AACZ,SAAO,WAAW,QAAQ,IAAI;AAC5B,UAAM,cAAc,QAAQ;AAC5B,QAAI,2CAAa,UAAU;AACzB,YAAM,SAAS,GAAG,YAAY,QAAQ,KAAI,iBAAY,eAAZ,YAA0B,CAAC,KAAI,iBAAY,iBAAZ,YAA4B,CAAC;AAGtG,YAAM,YAAY,QAAQ;AAC1B,YAAM,YAAY,OAAO,cAAc,YAAY,aAC9C,qBAAU,gBAAV,YAAyB,UAAU,SAAnC,YAA2C,OAC5C;AAEJ,aAAO,EAAE,QAAQ,UAAU;AAAA,IAC7B;AACA,cAAU,QAAQ;AAClB;AAAA,EACF;AAEA,SAAO,EAAE,QAAQ,MAAM,WAAW,KAAK;AACzC;AAKO,SAAS,aAAa,IAAwC;AACnE,QAAM,WAAW,qBAAqB,EAAE;AACxC,MAAI,SAAS,OAAQ,QAAO;AAE5B,QAAM,QAAQ,iBAAiB,EAAE;AACjC,MAAI,MAAM,OAAQ,QAAO;AAEzB,SAAO,EAAE,QAAQ,MAAM,WAAW,KAAK;AACzC;;;ACjEA,IAAM,kBAAkB;AAEjB,SAAS,kBACd,IACA,SACoB;AATtB;AAUE,QAAM,YAAW,wCAAS,kBAAT,YAA0B;AAE3C,SAAO;AAAA,IACL,eAAc,QAAG,aAAa,QAAQ,MAAxB,YAA6B;AAAA,IAC3C,aAAY,oBAAG,aAAa,aAAa,MAA7B,YACP,GAAG,aAAa,WAAW,MADpB,YAEP,GAAG,aAAa,SAAS,MAFlB,YAGP;AAAA,IACL,IAAI,YAAY,EAAE;AAAA,IAClB,SAAS,GAAG,QAAQ,YAAY;AAAA,IAChC,aAAa,eAAe,EAAE;AAAA,IAC9B,OAAM,QAAG,aAAa,MAAM,MAAtB,YAA2B;AAAA,IACjC,YAAW,QAAG,aAAa,YAAY,MAA5B,YAAiC;AAAA,IAC5C,eAAe;AAAA,MACb,MAAM,KAAK,GAAG,SAAS;AAAA,MACvB,mCAAS;AAAA,IACX;AAAA,IACA,SAAS,aAAa,EAAE;AAAA,IACxB,cAAc,gBAAgB,EAAE;AAAA,IAChC,cAAc,iBAAiB,IAAI,QAAQ;AAAA,IAC3C,YAAY,MAAM,KAAK,GAAG,SAAS;AAAA,IACnC,YAAY,oBAAoB,MAAM,KAAK,GAAG,SAAS,CAAC;AAAA,KACrD,oBAAoB,EAAE;AAE7B;AAEA,SAAS,oBAAoB,IAK3B;AACA,QAAM,SAAS,mBAAmB,EAAE;AACpC,MAAI,OAAO,QAAQ;AACjB,WAAO;AAAA,MACL,gBAAgB,OAAO;AAAA,MACvB,eAAe,OAAO;AAAA,MACtB,gBAAgB;AAAA,MAChB,mBAAmB;AAAA,IACrB;AAAA,EACF;AAEA,QAAM,WAAW,aAAa,EAAE;AAChC,SAAO;AAAA,IACL,gBAAgB;AAAA,IAChB,eAAe;AAAA,IACf,gBAAgB,SAAS;AAAA,IACzB,mBAAmB,SAAS;AAAA,EAC9B;AACF;AAEA,SAAS,YAAY,IAAgC;AACnD,QAAM,KAAK,GAAG;AACd,MAAI,CAAC,GAAI,QAAO;AAChB,MAAI,cAAc,EAAE,EAAG,QAAO;AAC9B,SAAO;AACT;AAEA,SAAS,eAAe,IAAgC;AApExD;AAsEE,MAAI,OAAO;AACX,aAAW,QAAQ,MAAM,KAAK,GAAG,UAAU,GAAG;AAC5C,QAAI,KAAK,aAAa,KAAK,WAAW;AACpC,eAAQ,UAAK,gBAAL,YAAoB;AAAA,IAC9B;AAAA,EACF;AACA,SAAO,KAAK,KAAK;AAGjB,MAAI,CAAC,QAAQ,GAAG,SAAS,UAAU,GAAG;AACpC,YAAO,cAAG,gBAAH,mBAAgB,WAAhB,YAA0B;AAAA,EACnC;AAEA,MAAI,CAAC,KAAM,QAAO;AAClB,SAAO,KAAK,SAAS,kBAAkB,KAAK,MAAM,GAAG,eAAe,IAAI;AAC1E;AAEA,SAAS,aAAa,IAAyB;AAC7C,QAAM,QAAkB,CAAC;AACzB,MAAI,UAA8B;AAElC,SAAO,WAAW,YAAY,SAAS,QAAQ,MAAM,SAAS,GAAG;AAC/D,QAAI,UAAU,QAAQ,QAAQ,YAAY;AAC1C,UAAM,SAAS,cAAc,MAAM,KAAK,QAAQ,SAAS,CAAC;AAC1D,QAAI,OAAO,SAAS,GAAG;AACrB,iBAAW,MAAM,OAAO,MAAM,GAAG,CAAC,EAAE,KAAK,GAAG;AAAA,IAC9C;AACA,UAAM,QAAQ,OAAO;AACrB,cAAU,QAAQ;AAAA,EACpB;AAEA,SAAO,MAAM,KAAK,KAAK;AACzB;AAEA,SAAS,gBAAgB,IAAyB;AAChD,QAAM,SAAS,GAAG;AAClB,MAAI,CAAC,OAAQ,QAAO;AACpB,QAAM,WAAW,MAAM,KAAK,OAAO,QAAQ;AAC3C,SAAO,SAAS,QAAQ,EAAE;AAC5B;AAEA,SAAS,iBAAiB,IAAiB,UAAiC;AAC1E,MAAI,UAAU,GAAG;AACjB,SAAO,WAAW,YAAY,SAAS,MAAM;AAC3C,UAAM,QAAQ,QAAQ,aAAa,QAAQ;AAC3C,QAAI,MAAO,QAAO;AAClB,cAAU,QAAQ;AAAA,EACpB;AACA,SAAO;AACT;;;ACpHA,IAAM,kBAAkB;AAEjB,SAAS,sBAAsB,GAAW,GAAmB;AAClE,MAAI,MAAM,EAAG,QAAO;AACpB,MAAI,CAAC,EAAE,UAAU,CAAC,EAAE,OAAQ,QAAO;AAEnC,QAAM,SAAqB,CAAC;AAC5B,WAAS,IAAI,GAAG,KAAK,EAAE,QAAQ,KAAK;AAClC,WAAO,CAAC,IAAI,CAAC,CAAC;AAAA,EAChB;AACA,WAAS,IAAI,GAAG,KAAK,EAAE,QAAQ,KAAK;AAClC,WAAO,CAAC,EAAE,CAAC,IAAI;AAAA,EACjB;AAEA,WAAS,IAAI,GAAG,KAAK,EAAE,QAAQ,KAAK;AAClC,aAAS,IAAI,GAAG,KAAK,EAAE,QAAQ,KAAK;AAClC,YAAM,OAAO,EAAE,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,IAAI,IAAI;AACzC,aAAO,CAAC,EAAE,CAAC,IAAI,KAAK;AAAA,QAClB,OAAO,IAAI,CAAC,EAAE,CAAC,IAAI;AAAA,QACnB,OAAO,CAAC,EAAE,IAAI,CAAC,IAAI;AAAA,QACnB,OAAO,IAAI,CAAC,EAAE,IAAI,CAAC,IAAI;AAAA,MACzB;AAAA,IACF;AAAA,EACF;AAEA,QAAM,WAAW,OAAO,EAAE,MAAM,EAAE,EAAE,MAAM;AAC1C,SAAO,IAAI,WAAW,KAAK,IAAI,EAAE,QAAQ,EAAE,MAAM;AACnD;AAEA,SAAS,eAAe,GAAkB,GAA0B;AAClE,MAAI,CAAC,KAAK,CAAC,EAAG,QAAO;AACrB,QAAM,KAAK,EAAE,KAAK,EAAE,YAAY;AAChC,QAAM,KAAK,EAAE,KAAK,EAAE,YAAY;AAChC,MAAI,OAAO,GAAI,QAAO;AACtB,MAAI,GAAG,SAAS,EAAE,KAAK,GAAG,SAAS,EAAE,EAAG,QAAO;AAC/C,SAAO,sBAAsB,IAAI,EAAE;AACrC;AAEA,SAAS,kBAAkB,GAAa,GAAqB;AAC3D,MAAI,CAAC,EAAE,UAAU,CAAC,EAAE,OAAQ,QAAO;AACnC,QAAM,OAAO,IAAI,IAAI,CAAC;AACtB,QAAM,OAAO,IAAI,IAAI,CAAC;AACtB,MAAI,eAAe;AACnB,aAAW,QAAQ,MAAM;AACvB,QAAI,KAAK,IAAI,IAAI,EAAG;AAAA,EACtB;AACA,QAAM,SAAQ,oBAAI,IAAI,CAAC,GAAG,GAAG,GAAG,CAAC,CAAC,GAAE;AACpC,SAAO,UAAU,IAAI,IAAI,eAAe;AAC1C;AAEA,SAAS,eAAe,IAAiB,IAAwB,UAA0B;AArD3F;AAsDE,MAAI,QAAQ;AAGZ,QAAM,aAAa,GAAG,aAAa,QAAQ;AAC3C,MAAI,GAAG,gBAAgB,eAAe,GAAG,cAAc;AACrD,aAAS;AAAA,EACX;AAGA,QAAM,UAAS,cAAG,gBAAH,mBAAgB,OAAO,MAAM,GAAG,QAAhC,YAAuC;AACtD,QAAM,UAAU,eAAe,GAAG,aAAa,MAAM;AACrD,MAAI,UAAU,KAAK;AACjB,aAAS,UAAU;AAAA,EACrB;AAGA,MAAI,GAAG,QAAQ,GAAG,aAAa,MAAM,MAAM,GAAG,QAC5C,GAAG,aAAa,GAAG,aAAa,YAAY,MAAM,GAAG,WAAW;AAChE,aAAS;AAAA,EACX;AAGA,MAAI,GAAG,cAAc;AACnB,QAAIA,UAAS,GAAG;AAChB,WAAOA,WAAUA,YAAW,SAAS,MAAM;AACzC,UAAIA,QAAO,aAAa,QAAQ,MAAM,GAAG,cAAc;AACrD,iBAAS;AACT;AAAA,MACF;AACA,MAAAA,UAASA,QAAO;AAAA,IAClB;AAAA,EACF;AAGA,MAAI,GAAG,cAAc,SAAS,GAAG;AAC/B,UAAM,YAAY,cAAc,MAAM,KAAK,GAAG,SAAS,CAAC;AACxD,UAAM,UAAU,kBAAkB,GAAG,eAAe,SAAS;AAC7D,aAAS,UAAU;AAAA,EACrB;AAGA,MAAI,GAAG,SAAS;AACd,UAAM,SAASC,cAAa,EAAE;AAC9B,UAAM,UAAU,sBAAsB,GAAG,SAAS,MAAM;AACxD,aAAS,UAAU;AAAA,EACrB;AAGA,QAAM,SAAS,GAAG;AAClB,MAAI,QAAQ;AACV,UAAM,MAAM,MAAM,KAAK,OAAO,QAAQ,EAAE,QAAQ,EAAE;AAClD,QAAI,QAAQ,GAAG,cAAc;AAC3B,eAAS;AAAA,IACX;AAAA,EACF;AAEA,SAAO;AACT;AAEA,SAASA,cAAa,IAAyB;AAC7C,QAAM,QAAkB,CAAC;AACzB,MAAI,UAA8B;AAClC,SAAO,WAAW,YAAY,SAAS,QAAQ,MAAM,SAAS,GAAG;AAC/D,QAAI,UAAU,QAAQ,QAAQ,YAAY;AAC1C,UAAM,SAAS,cAAc,MAAM,KAAK,QAAQ,SAAS,CAAC;AAC1D,QAAI,OAAO,SAAS,GAAG;AACrB,iBAAW,MAAM,OAAO,MAAM,GAAG,CAAC,EAAE,KAAK,GAAG;AAAA,IAC9C;AACA,UAAM,QAAQ,OAAO;AACrB,cAAU,QAAQ;AAAA,EACpB;AACA,SAAO,MAAM,KAAK,KAAK;AACzB;AAEO,SAAS,aACd,IACA,SACoB;AAnItB;AAoIE,QAAM,YAAW,wCAAS,kBAAT,YAA0B;AAG3C,MAAI,GAAG,cAAc;AACnB,UAAM,KAAK,SAAS,cAA2B,IAAI,QAAQ,KAAK,GAAG,YAAY,IAAI;AACnF,QAAI,GAAI,QAAO;AAAA,EACjB;AAGA,MAAI,GAAG,YAAY;AACjB,UAAM,KAAK,SAAS;AAAA,MAClB,iBAAiB,GAAG,UAAU,mBAAmB,GAAG,UAAU,iBAAiB,GAAG,UAAU;AAAA,IAC9F;AACA,QAAI,GAAI,QAAO;AAAA,EACjB;AAGA,MAAI,GAAG,IAAI;AACT,UAAM,KAAK,SAAS,eAAe,GAAG,EAAE;AACxC,QAAI,GAAI,QAAO;AAAA,EACjB;AAGA,QAAM,aAAa,SAAS,iBAA8B,GAAG,OAAO;AACpE,MAAI,SAA6B;AACjC,MAAI,YAAY;AAEhB,aAAW,aAAa,YAAY;AAClC,UAAM,QAAQ,eAAe,WAAW,IAAI,QAAQ;AACpD,QAAI,QAAQ,WAAW;AACrB,kBAAY;AACZ,eAAS;AAAA,IACX;AAAA,EACF;AAEA,SAAO,aAAa,kBAAkB,SAAS;AACjD;;;AClKO,SAAS,SAAS,OAAuB;AAC9C,SAAO,KAAK,MAAM,QAAQ,GAAG,IAAI;AACnC;;;ACLO,IAAM,oBAAN,MAAwB;AAAA,EAI7B,YAAoB,SAAyB;AAAzB;AAHpB,SAAQ,QAAsB,CAAC;AAI7B,SAAK,QAAQ,KAAK,KAAK;AAAA,EACzB;AAAA,EAEA,IAAI,eAAwB;AAX9B;AAYI,YAAO,UAAK,QAAQ,iBAAb,YAA6B;AAAA,EACtC;AAAA,EAEA,SAAuB;AACrB,WAAO,CAAC,GAAG,KAAK,KAAK;AAAA,EACvB;AAAA,EAEA,WAAW,OAA6B;AACtC,WAAO,KAAK,MAAM,OAAO,CAAC,MAAM,EAAE,UAAU,KAAK;AAAA,EACnD;AAAA,EAEA,MAAM,IAAI,YAAuC;AAC/C,SAAK,MAAM,KAAK,UAAU;AAC1B,UAAM,KAAK,QAAQ,KAAK,UAAU;AAAA,EACpC;AAAA,EAEA,MAAM,OAAO,IAA2B;AACtC,SAAK,QAAQ,KAAK,MAAM,OAAO,CAAC,MAAM,EAAE,OAAO,EAAE;AACjD,UAAM,KAAK,QAAQ,OAAO,EAAE;AAAA,EAC9B;AAAA,EAEA,MAAM,OAAO,IAAY,SAA6C;AACpE,UAAM,MAAM,KAAK,MAAM,UAAU,CAAC,MAAM,EAAE,OAAO,EAAE;AACnD,QAAI,QAAQ,GAAI;AAChB,UAAM,UAAU,kCAAK,KAAK,MAAM,GAAG,IAAM;AACzC,SAAK,MAAM,GAAG,IAAI;AAClB,UAAM,KAAK,QAAQ,KAAK,OAAO;AAAA,EACjC;AAAA,EAEA,MAAM,WAA0B;AAC9B,SAAK,QAAQ,CAAC;AACd,UAAM,KAAK,QAAQ,MAAM;AAAA,EAC3B;AAAA,EAEA,aAA8B;AAC5B,WAAO;AAAA,MACL,SAAS;AAAA,MACT,aAAa,CAAC,GAAG,KAAK,KAAK;AAAA,IAC7B;AAAA,EACF;AAAA,EAEA,MAAM,WAAW,MAAsC;AACrD,SAAK,QAAQ,CAAC,GAAG,KAAK,WAAW;AACjC,SAAK,uBAAuB;AAC5B,UAAM,KAAK,QAAQ,MAAM;AACzB,eAAW,OAAO,KAAK,OAAO;AAC5B,YAAM,KAAK,QAAQ,KAAK,GAAG;AAAA,IAC7B;AAAA,EACF;AAAA,EAEA,MAAc,OAAsB;AAClC,UAAM,OAAO,MAAM,KAAK,QAAQ,KAAK;AACrC,QAAI,MAAM;AACR,WAAK,QAAQ,KAAK;AAClB,WAAK,uBAAuB;AAAA,IAC9B;AAAA,EACF;AAAA,EAEQ,yBAA+B;AACrC,eAAW,OAAO,KAAK,OAAO;AAC5B,UAAI,IAAI,kBAAkB,QAAQ,IAAI,UAAU;AAC9C,cAAM,QAAQ,SAAS,IAAI,SAAS,MAAM,GAAG,EAAE,CAAC,GAAG,EAAE;AACrD,YAAI,iBAAiB,SAAS,KAAK;AAAA,MACrC;AAAA,IACF;AAAA,EACF;AACF;;;AC5EA,IAAM,cAAc;AAEb,IAAM,sBAAN,MAAoD;AAAA,EAApD;AACL,wBAAe;AACf,SAAQ,cAAuC,CAAC;AAChD,SAAQ,cAAsC;AAAA;AAAA,EAE9C,MAAM,OAAwC;AAC5C,QAAI,KAAK,aAAc,QAAO,KAAK;AACnC,QAAI;AACF,YAAM,MAAM,aAAa,QAAQ,WAAW;AAC5C,UAAI,CAAC,IAAK,QAAO;AACjB,YAAM,SAAS,KAAK,MAAM,GAAG;AAC7B,YAA0C,aAAlC,WAAS,YAfvB,IAegD,IAAT,iBAAS,IAAT,CAAzB,WAAS;AACjB,WAAK,cAAc;AACnB,YAAM,QAAyB;AAAA,QAC7B,SAAS;AAAA,QACT,aAAa,MAAM,QAAQ,WAAW,IAAI,cAAc,CAAC;AAAA,MAC3D;AACA,WAAK,cAAc;AACnB,aAAO;AAAA,IACT,SAAQ;AACN,WAAK,eAAe;AACpB,aAAO,KAAK;AAAA,IACd;AAAA,EACF;AAAA,EAEA,MAAM,KAAK,YAAuC;AAChD,UAAM,QAAQ,MAAM,KAAK,YAAY;AACrC,UAAM,MAAM,MAAM,YAAY,UAAU,CAAC,MAAM,EAAE,OAAO,WAAW,EAAE;AACrE,QAAI,QAAQ,IAAI;AACd,YAAM,YAAY,KAAK,UAAU;AAAA,IACnC,OAAO;AACL,YAAM,YAAY,GAAG,IAAI;AAAA,IAC3B;AACA,SAAK,QAAQ,KAAK;AAAA,EACpB;AAAA,EAEA,MAAM,OAAO,IAA2B;AACtC,UAAM,QAAQ,MAAM,KAAK,YAAY;AACrC,UAAM,cAAc,MAAM,YAAY,OAAO,CAAC,MAAM,EAAE,OAAO,EAAE;AAC/D,SAAK,QAAQ,KAAK;AAAA,EACpB;AAAA,EAEA,MAAM,QAAuB;AAC3B,UAAM,QAAQ,MAAM,KAAK,YAAY;AACrC,UAAM,cAAc,CAAC;AACrB,SAAK,QAAQ,KAAK;AAAA,EACpB;AAAA,EAEA,MAAc,cAAwC;AACpD,QAAI,KAAK,YAAa,QAAO,KAAK;AAClC,UAAM,SAAS,MAAM,KAAK,KAAK;AAC/B,QAAI,OAAQ,QAAO;AACnB,SAAK,cAAc,EAAE,SAAS,GAAG,aAAa,CAAC,EAAE;AACjD,WAAO,KAAK;AAAA,EACd;AAAA,EAEQ,QAAQ,OAA8B;AAC5C,QAAI,KAAK,aAAc;AACvB,QAAI;AACF,YAAM,OAAO;AAAA,QACX,SAAS;AAAA,SACN,KAAK,cAFG;AAAA,QAGX,aAAa,MAAM;AAAA,MACrB;AACA,mBAAa,QAAQ,aAAa,KAAK,UAAU,IAAI,CAAC;AAAA,IACxD,SAAQ;AACN,WAAK,eAAe;AAAA,IACtB;AAAA,EACF;AACF;;;AC/DA,SAAS,oBAAoB,KAAoE;AAC/F,QAAM,QAAQ,IAAI,MAAM,GAAG;AAC3B,MAAI,MAAM,SAAS,EAAG,QAAO;AAE7B,QAAM,SAAS,SAAS,MAAM,IAAI,GAAI,EAAE;AACxC,QAAM,OAAO,SAAS,MAAM,IAAI,GAAI,EAAE;AACtC,QAAM,OAAO,MAAM,KAAK,GAAG;AAC3B,MAAI,CAAC,QAAQ,MAAM,IAAI,EAAG,QAAO;AACjC,SAAO,EAAE,MAAM,MAAM,QAAQ,MAAM,MAAM,IAAI,IAAI,OAAO;AAC1D;AAEA,SAAS,cAAc,IAAsD;AArB7E;AAuBE,MAAI,GAAG,gBAAgB;AACrB,UAAM,SAAS,oBAAoB,GAAG,cAAc;AACpD,QAAI,OAAQ,QAAO,iCAAK,SAAL,EAAa,YAAW,QAAG,kBAAH,YAAoB,KAAK;AAAA,EACtE;AAGA,MAAI,GAAG,gBAAgB;AACrB,UAAM,SAAS,oBAAoB,GAAG,cAAc;AACpD,QAAI,OAAQ,QAAO,iCAAK,SAAL,EAAa,YAAW,QAAG,sBAAH,YAAwB,KAAK;AAAA,EAC1E;AAEA,SAAO;AACT;AAEA,IAAM,gBAAgB;AACtB,IAAM,kBAAkB;AACxB,IAAM,iBAAiB;AAEvB,SAAS,iBAAiB,IAA0C;AAzCpE;AA0CE,QAAM,cAA2B,CAAC;AAGlC,MAAI,GAAG,cAAc;AACnB,gBAAY,KAAK,EAAE,OAAO,kBAAkB,GAAG,YAAY,KAAK,MAAM,eAAe,YAAY,OAAO,CAAC;AAAA,EAC3G;AACA,MAAI,GAAG,YAAY;AACjB,gBAAY,KAAK,EAAE,OAAO,gBAAgB,GAAG,UAAU,KAAK,MAAM,eAAe,YAAY,OAAO,CAAC;AAAA,EACvG;AACA,MAAI,GAAG,IAAI;AACT,gBAAY,KAAK,EAAE,OAAO,OAAO,GAAG,EAAE,KAAK,MAAM,eAAe,YAAY,OAAO,CAAC;AAAA,EACtF;AACA,MAAI,GAAG,WAAW;AAChB,gBAAY,KAAK,EAAE,OAAO,eAAe,GAAG,SAAS,KAAK,MAAM,eAAe,YAAY,OAAO,CAAC;AAAA,EACrG;AAGA,MAAI,GAAG,aAAa;AAClB,gBAAY,KAAK,EAAE,OAAO,IAAI,GAAG,WAAW,KAAK,MAAM,eAAe,YAAY,SAAS,CAAC;AAAA,EAC9F;AACA,MAAI,GAAG,MAAM;AACX,gBAAY,KAAK,EAAE,OAAO,SAAS,GAAG,IAAI,KAAK,MAAM,eAAe,YAAY,SAAS,CAAC;AAAA,EAC5F;AACA,OAAI,QAAG,eAAH,mBAAe,QAAQ;AACzB,eAAW,OAAO,GAAG,YAAY;AAC/B,kBAAY,KAAK,EAAE,OAAO,IAAI,IAAI,SAAS,IAAI,MAAM,iBAAiB,YAAY,SAAS,CAAC;AAC5F,kBAAY,KAAK,EAAE,OAAO,UAAU,IAAI,SAAS,IAAI,MAAM,gBAAgB,YAAY,SAAS,CAAC;AAAA,IACnG;AAAA,EACF;AAGA,MAAI,GAAG,cAAc,QAAQ;AAC3B,eAAW,OAAO,GAAG,cAAc,MAAM,GAAG,CAAC,GAAG;AAC9C,kBAAY,KAAK,EAAE,OAAO,IAAI,GAAG,KAAK,MAAM,eAAe,YAAY,MAAM,CAAC;AAAA,IAChF;AAAA,EACF;AAEA,SAAO;AAAA,IACL;AAAA,IACA,YAAY,GAAG;AAAA,IACf,SAAS,GAAG;AAAA,IACZ,UAAS,QAAG,eAAH,YAAiB,GAAG;AAAA,EAC/B;AACF;AAEO,SAAS,oBAAoB,aAA2B,gBAAqC;AAClG,QAAM,mBAAsC,YAAY,IAAI,UAAQ;AAAA,IAClE,IAAI,IAAI;AAAA,IACR,OAAO,IAAI;AAAA,IACX,SAAS,IAAI;AAAA,IACb,QAAQ,IAAI;AAAA,IACZ,WAAW,IAAI;AAAA,IACf,QAAQ,cAAc,IAAI,WAAW;AAAA,IACrC,aAAa,iBAAiB,IAAI,WAAW;AAAA,EAC/C,EAAE;AAEF,SAAO;AAAA,IACL,SAAS;AAAA,IACT,QAAQ;AAAA,IACR;AAAA,IACA,aAAa;AAAA,EACf;AACF;","names":["parent","buildDomPath"]}
|
package/dist/core/index.d.cts
CHANGED
|
@@ -47,6 +47,7 @@ interface WebRemarqOptions {
|
|
|
47
47
|
dataAttribute?: string;
|
|
48
48
|
position?: ToolbarPosition;
|
|
49
49
|
shortcuts?: boolean;
|
|
50
|
+
storage?: StorageAdapter;
|
|
50
51
|
}
|
|
51
52
|
type SearchConfidence = 'high' | 'medium' | 'low';
|
|
52
53
|
interface GrepQuery {
|
|
@@ -81,27 +82,52 @@ interface AgentExport {
|
|
|
81
82
|
viewportBucket: number;
|
|
82
83
|
annotations: AgentAnnotation[];
|
|
83
84
|
}
|
|
85
|
+
interface StorageChangeEvent {
|
|
86
|
+
type: 'add' | 'update' | 'remove' | 'clear';
|
|
87
|
+
annotation?: Annotation;
|
|
88
|
+
id?: string;
|
|
89
|
+
}
|
|
90
|
+
interface StorageAdapter {
|
|
91
|
+
load(): Promise<AnnotationStore | null>;
|
|
92
|
+
save(annotation: Annotation): Promise<void>;
|
|
93
|
+
remove(id: string): Promise<void>;
|
|
94
|
+
clear(): Promise<void>;
|
|
95
|
+
subscribe?(callback: (event: StorageChangeEvent) => void): () => void;
|
|
96
|
+
readonly isMemoryOnly?: boolean;
|
|
97
|
+
}
|
|
84
98
|
|
|
85
99
|
declare function createFingerprint(el: HTMLElement, options?: Pick<WebRemarqOptions, 'classFilter' | 'dataAttribute'>): ElementFingerprint;
|
|
86
100
|
|
|
87
101
|
declare function matchElement(fp: ElementFingerprint, options?: Pick<WebRemarqOptions, 'dataAttribute'>): HTMLElement | null;
|
|
88
102
|
|
|
89
103
|
declare class AnnotationStorage {
|
|
90
|
-
private
|
|
91
|
-
private
|
|
92
|
-
|
|
93
|
-
constructor();
|
|
104
|
+
private adapter;
|
|
105
|
+
private cache;
|
|
106
|
+
readonly ready: Promise<void>;
|
|
107
|
+
constructor(adapter: StorageAdapter);
|
|
108
|
+
get isMemoryOnly(): boolean;
|
|
94
109
|
getAll(): Annotation[];
|
|
95
110
|
getByRoute(route: string): Annotation[];
|
|
96
|
-
add(annotation: Annotation): void
|
|
97
|
-
remove(id: string): void
|
|
98
|
-
update(id: string, changes: Partial<Annotation>): void
|
|
99
|
-
clearAll(): void
|
|
111
|
+
add(annotation: Annotation): Promise<void>;
|
|
112
|
+
remove(id: string): Promise<void>;
|
|
113
|
+
update(id: string, changes: Partial<Annotation>): Promise<void>;
|
|
114
|
+
clearAll(): Promise<void>;
|
|
100
115
|
exportJSON(): AnnotationStore;
|
|
101
|
-
importJSON(data: AnnotationStore): void
|
|
116
|
+
importJSON(data: AnnotationStore): Promise<void>;
|
|
117
|
+
private init;
|
|
102
118
|
private migrateViewportBuckets;
|
|
103
|
-
|
|
104
|
-
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
declare class LocalStorageAdapter implements StorageAdapter {
|
|
122
|
+
isMemoryOnly: boolean;
|
|
123
|
+
private extraFields;
|
|
124
|
+
private memoryStore;
|
|
125
|
+
load(): Promise<AnnotationStore | null>;
|
|
126
|
+
save(annotation: Annotation): Promise<void>;
|
|
127
|
+
remove(id: string): Promise<void>;
|
|
128
|
+
clear(): Promise<void>;
|
|
129
|
+
private ensureStore;
|
|
130
|
+
private persist;
|
|
105
131
|
}
|
|
106
132
|
|
|
107
133
|
/**
|
|
@@ -116,4 +142,4 @@ declare function detectSource(el: HTMLElement): SourceDetectionResult;
|
|
|
116
142
|
|
|
117
143
|
declare function generateAgentExport(annotations: Annotation[], viewportBucket: number): AgentExport;
|
|
118
144
|
|
|
119
|
-
export { type AgentAnnotation, type AgentAnnotationSource, type AgentExport, type AgentSearchHints, type Annotation, AnnotationStorage, type AnnotationStore, type CSSModuleClass, type ElementFingerprint, type GrepQuery, type SearchConfidence, type SourceDetectionResult, type ToolbarPosition, createFingerprint, detectRemarqPlugin, detectSource, generateAgentExport, matchElement };
|
|
145
|
+
export { type AgentAnnotation, type AgentAnnotationSource, type AgentExport, type AgentSearchHints, type Annotation, AnnotationStorage, type AnnotationStore, type CSSModuleClass, type ElementFingerprint, type GrepQuery, LocalStorageAdapter, type SearchConfidence, type SourceDetectionResult, type StorageAdapter, type StorageChangeEvent, type ToolbarPosition, createFingerprint, detectRemarqPlugin, detectSource, generateAgentExport, matchElement };
|
package/dist/core/index.d.ts
CHANGED
|
@@ -47,6 +47,7 @@ interface WebRemarqOptions {
|
|
|
47
47
|
dataAttribute?: string;
|
|
48
48
|
position?: ToolbarPosition;
|
|
49
49
|
shortcuts?: boolean;
|
|
50
|
+
storage?: StorageAdapter;
|
|
50
51
|
}
|
|
51
52
|
type SearchConfidence = 'high' | 'medium' | 'low';
|
|
52
53
|
interface GrepQuery {
|
|
@@ -81,27 +82,52 @@ interface AgentExport {
|
|
|
81
82
|
viewportBucket: number;
|
|
82
83
|
annotations: AgentAnnotation[];
|
|
83
84
|
}
|
|
85
|
+
interface StorageChangeEvent {
|
|
86
|
+
type: 'add' | 'update' | 'remove' | 'clear';
|
|
87
|
+
annotation?: Annotation;
|
|
88
|
+
id?: string;
|
|
89
|
+
}
|
|
90
|
+
interface StorageAdapter {
|
|
91
|
+
load(): Promise<AnnotationStore | null>;
|
|
92
|
+
save(annotation: Annotation): Promise<void>;
|
|
93
|
+
remove(id: string): Promise<void>;
|
|
94
|
+
clear(): Promise<void>;
|
|
95
|
+
subscribe?(callback: (event: StorageChangeEvent) => void): () => void;
|
|
96
|
+
readonly isMemoryOnly?: boolean;
|
|
97
|
+
}
|
|
84
98
|
|
|
85
99
|
declare function createFingerprint(el: HTMLElement, options?: Pick<WebRemarqOptions, 'classFilter' | 'dataAttribute'>): ElementFingerprint;
|
|
86
100
|
|
|
87
101
|
declare function matchElement(fp: ElementFingerprint, options?: Pick<WebRemarqOptions, 'dataAttribute'>): HTMLElement | null;
|
|
88
102
|
|
|
89
103
|
declare class AnnotationStorage {
|
|
90
|
-
private
|
|
91
|
-
private
|
|
92
|
-
|
|
93
|
-
constructor();
|
|
104
|
+
private adapter;
|
|
105
|
+
private cache;
|
|
106
|
+
readonly ready: Promise<void>;
|
|
107
|
+
constructor(adapter: StorageAdapter);
|
|
108
|
+
get isMemoryOnly(): boolean;
|
|
94
109
|
getAll(): Annotation[];
|
|
95
110
|
getByRoute(route: string): Annotation[];
|
|
96
|
-
add(annotation: Annotation): void
|
|
97
|
-
remove(id: string): void
|
|
98
|
-
update(id: string, changes: Partial<Annotation>): void
|
|
99
|
-
clearAll(): void
|
|
111
|
+
add(annotation: Annotation): Promise<void>;
|
|
112
|
+
remove(id: string): Promise<void>;
|
|
113
|
+
update(id: string, changes: Partial<Annotation>): Promise<void>;
|
|
114
|
+
clearAll(): Promise<void>;
|
|
100
115
|
exportJSON(): AnnotationStore;
|
|
101
|
-
importJSON(data: AnnotationStore): void
|
|
116
|
+
importJSON(data: AnnotationStore): Promise<void>;
|
|
117
|
+
private init;
|
|
102
118
|
private migrateViewportBuckets;
|
|
103
|
-
|
|
104
|
-
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
declare class LocalStorageAdapter implements StorageAdapter {
|
|
122
|
+
isMemoryOnly: boolean;
|
|
123
|
+
private extraFields;
|
|
124
|
+
private memoryStore;
|
|
125
|
+
load(): Promise<AnnotationStore | null>;
|
|
126
|
+
save(annotation: Annotation): Promise<void>;
|
|
127
|
+
remove(id: string): Promise<void>;
|
|
128
|
+
clear(): Promise<void>;
|
|
129
|
+
private ensureStore;
|
|
130
|
+
private persist;
|
|
105
131
|
}
|
|
106
132
|
|
|
107
133
|
/**
|
|
@@ -116,4 +142,4 @@ declare function detectSource(el: HTMLElement): SourceDetectionResult;
|
|
|
116
142
|
|
|
117
143
|
declare function generateAgentExport(annotations: Annotation[], viewportBucket: number): AgentExport;
|
|
118
144
|
|
|
119
|
-
export { type AgentAnnotation, type AgentAnnotationSource, type AgentExport, type AgentSearchHints, type Annotation, AnnotationStorage, type AnnotationStore, type CSSModuleClass, type ElementFingerprint, type GrepQuery, type SearchConfidence, type SourceDetectionResult, type ToolbarPosition, createFingerprint, detectRemarqPlugin, detectSource, generateAgentExport, matchElement };
|
|
145
|
+
export { type AgentAnnotation, type AgentAnnotationSource, type AgentExport, type AgentSearchHints, type Annotation, AnnotationStorage, type AnnotationStore, type CSSModuleClass, type ElementFingerprint, type GrepQuery, LocalStorageAdapter, type SearchConfidence, type SourceDetectionResult, type StorageAdapter, type StorageChangeEvent, type ToolbarPosition, createFingerprint, detectRemarqPlugin, detectSource, generateAgentExport, matchElement };
|