web-remarq 0.2.6 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  Visual annotation tool for design review workflows. Framework-agnostic, zero dependencies.
4
4
 
5
- Designer annotates UI elements on staging/dev, exports a report. Developer imports the report and sees markers on the exact elements. Copy annotations as agent-friendly markdown with search hints for AI coding agents.
5
+ Designer annotates UI elements on staging/dev, exports a report. Developer imports the report and sees markers on the exact elements. Export as agent-friendly JSON with source locations and search hints for AI coding agents.
6
6
 
7
7
  ## Install
8
8
 
@@ -10,6 +10,18 @@ Designer annotates UI elements on staging/dev, exports a report. Developer impor
10
10
  npm install web-remarq
11
11
  ```
12
12
 
13
+ ### Build plugins (optional)
14
+
15
+ For precise source location injection (`file:line:col` on every element):
16
+
17
+ ```bash
18
+ # Babel (React, Preact, Solid)
19
+ npm install -D @web-remarq/babel-plugin
20
+
21
+ # Vite / webpack / Rollup / esbuild / Rspack
22
+ npm install -D @web-remarq/unplugin
23
+ ```
24
+
13
25
  ## Quick Start
14
26
 
15
27
  ```ts
@@ -47,14 +59,18 @@ Remove all DOM nodes, event listeners, and observers. Full cleanup.
47
59
 
48
60
  Switch between `'light'` and `'dark'` themes.
49
61
 
50
- ### `WebRemarq.copy()`
62
+ ### `WebRemarq.copy(format?)`
51
63
 
52
- Copy annotations as agent-friendly markdown to clipboard. Includes ranked search hints (CSS selectors, class names, text content, DOM path) so AI coding agents can grep and locate the source code.
64
+ Copy annotations to clipboard.
65
+
66
+ - `'md'` (default) — agent-friendly markdown with search hints
67
+ - `'agent'` — structured JSON with source locations and grep queries
53
68
 
54
69
  ### `WebRemarq.export(format)`
55
70
 
56
- - `'md'` — downloads `.md` file with search hints (same content as `copy()`)
57
- - `'json'` — downloads `.json` file with full annotation data
71
+ - `'md'` — downloads `.md` file with search hints
72
+ - `'json'` — downloads `.json` file with full annotation data (for import)
73
+ - `'agent'` — downloads `.json` with source locations, grep queries, and confidence levels
58
74
 
59
75
  ### `WebRemarq.import(file)`
60
76
 
@@ -70,15 +86,49 @@ const result = await WebRemarq.import(input.files[0])
70
86
 
71
87
  Get all annotations, or filter by route.
72
88
 
73
- ```ts
74
- WebRemarq.getAnnotations() // all
75
- WebRemarq.getAnnotations('/casino') // by route
76
- ```
77
-
78
89
  ### `WebRemarq.clearAll()`
79
90
 
80
91
  Remove all annotations.
81
92
 
93
+ ## Agent Export Format
94
+
95
+ The `export('agent')` format is optimized for AI coding agents:
96
+
97
+ ```jsonc
98
+ {
99
+ "version": 1,
100
+ "format": "agent",
101
+ "viewportBucket": 1200,
102
+ "annotations": [{
103
+ "id": "a1b2c3d4",
104
+ "route": "/dashboard",
105
+ "comment": "Increase button padding",
106
+ "status": "pending",
107
+ "source": {
108
+ "file": "src/components/ActionBar.tsx",
109
+ "line": 24,
110
+ "column": 6,
111
+ "component": "ActionBar"
112
+ },
113
+ "searchHints": {
114
+ "grepQueries": [
115
+ { "query": "data-testid=\"save-btn\"", "glob": "*.{tsx,jsx,vue}", "confidence": "high" },
116
+ { "query": "\"Save changes\"", "glob": "*.{tsx,jsx,vue}", "confidence": "medium" }
117
+ ],
118
+ "domContext": "div.action-bar > button",
119
+ "tagName": "button",
120
+ "classes": ["action-button"]
121
+ }
122
+ }]
123
+ }
124
+ ```
125
+
126
+ Source detection uses a 3-level fallback:
127
+
128
+ 1. **Build plugin** — exact `file:line:col` from [`@web-remarq/babel-plugin`](https://www.npmjs.com/package/@web-remarq/babel-plugin) or [`@web-remarq/unplugin`](https://www.npmjs.com/package/@web-remarq/unplugin)
129
+ 2. **Runtime detection** — `data-source` attrs (locator.js), React fiber `_debugSource` (dev mode)
130
+ 3. **Heuristic** — grep queries ranked by confidence (`high` / `medium` / `low`)
131
+
82
132
  ## Core-only usage
83
133
 
84
134
  For programmatic access without UI:
@@ -95,57 +145,29 @@ When a user clicks an element, a multi-signal fingerprint is captured:
95
145
 
96
146
  - **Stable anchors** — `data-annotate`, `data-testid`, `id`
97
147
  - **Semantics** — tag name, text content, ARIA role/label
98
- - **Structure** — stable CSS classes (hashes stripped), DOM path with parent classes, sibling index
99
- - **Parent context** — nearest ancestor's `data-annotate` value
100
- - **Agent export** — raw classes, CSS Module decomposition (module hint + local class name)
148
+ - **Structure** — stable CSS classes (hashes stripped), DOM path, sibling index
149
+ - **Source location** — from build plugin or runtime detection
101
150
 
102
151
  ### Matching
103
152
 
104
- When loading annotations, elements are found via a fallback chain:
153
+ Elements are found via a fallback chain:
105
154
 
106
155
  1. Exact match by `data-annotate` or `data-testid`
107
156
  2. Exact match by `id`
108
157
  3. Fuzzy match using weighted scoring (text similarity, ARIA, classes, DOM path)
109
- 4. Unmatched annotations sorted into "other viewport" or "detached" panels
158
+ 4. Unmatched annotations go to "other viewport" or "detached" panels
110
159
 
111
160
  ### Viewport Breakpoints
112
161
 
113
- Annotations are tagged with a viewport bucket (width rounded to 100px). When resizing:
114
-
115
- - **Attached** — element found in current viewport
116
- - **Other viewport** — element not found, but annotation belongs to a different breakpoint (not an error)
117
- - **Detached** — element not found even in its native breakpoint (real problem)
118
-
119
- Automatic reconnection when returning to the annotation's native viewport.
120
-
121
- ### Agent-Friendly Copy
122
-
123
- The Copy button produces markdown with ranked search hints:
124
-
125
- ```markdown
126
- ### 1. [pending] "Button too small on mobile"
127
- Element: <button> "Submit"
128
- Viewport: 300px
129
-
130
- Search hints:
131
- - `data-testid="submit-btn"` — in template files
132
- - `"Submit"` — text content in templates
133
- - `.submitButton` — in CSS Module file (likely `form.module.*`)
134
- - DOM: div.form-wrapper > form > button.submit
135
- - Classes: form__submitButton__cEqts flex items-center
136
- ```
137
-
138
- ### Hash Detection
139
-
140
- Automatically strips hashed classes from CSS Modules, styled-components, Emotion, and pure hash patterns.
162
+ Annotations are tagged with a viewport bucket (width rounded to 100px). Automatic reconnection when returning to the annotation's native viewport.
141
163
 
142
164
  ### SPA Support
143
165
 
144
- Listens for `popstate`, `hashchange`, and intercepts `history.pushState`/`replaceState`. Annotations are scoped per route (`pathname + hash`).
166
+ Intercepts `history.pushState`/`replaceState` and listens for `popstate`/`hashchange`. Annotations are scoped per route.
145
167
 
146
168
  ## Stable Selectors
147
169
 
148
- Works without any markup changes, but for guaranteed stable matching add `data-annotate` to key components:
170
+ Works without any markup changes, but for guaranteed stable matching add `data-annotate`:
149
171
 
150
172
  ```html
151
173
  <CasinoTabs data-annotate="casino-tabs" />
@@ -154,21 +176,11 @@ Works without any markup changes, but for guaranteed stable matching add `data-a
154
176
 
155
177
  ## UI Components
156
178
 
157
- - **Toolbar** — fixed bottom-right panel with inspect, copy, export, import, clear, theme, minimize
179
+ - **Toolbar** — fixed bottom-right panel with inspect, spacing, copy, export, import, clear, theme, minimize
158
180
  - **Inspect mode** — hover to highlight, click to annotate
181
+ - **Spacing inspector** — visualizes margin, padding, content, flex gap on hover
159
182
  - **Markers** — numbered circles (orange = pending, green = resolved)
160
- - **Popup** — comment input for new annotations, detail view with Resolve/Delete for existing
161
- - **Other viewport panel** — annotations from different breakpoints, click to see required viewport
162
- - **Detached panel** — annotations whose elements can't be found in their native viewport
163
-
164
- ## Build Outputs
165
-
166
- | Format | File | Use |
167
- |--------|------|-----|
168
- | ESM | `dist/index.js` | Bundlers |
169
- | CJS | `dist/index.cjs` | `require()` |
170
- | IIFE | `dist/web-remarq.global.global.js` | `<script>` tag |
171
- | Types | `dist/index.d.ts` | TypeScript |
183
+ - **Popup** — comment input / detail view with Resolve/Delete
172
184
 
173
185
  ## License
174
186
 
@@ -51,6 +51,9 @@ var core_exports = {};
51
51
  __export(core_exports, {
52
52
  AnnotationStorage: () => AnnotationStorage,
53
53
  createFingerprint: () => createFingerprint,
54
+ detectRemarqPlugin: () => detectRemarqPlugin,
55
+ detectSource: () => detectSource,
56
+ generateAgentExport: () => generateAgentExport,
54
57
  matchElement: () => matchElement
55
58
  });
56
59
  module.exports = __toCommonJS(core_exports);
@@ -103,12 +106,54 @@ function decomposeCSSModules(classes) {
103
106
  return result;
104
107
  }
105
108
 
109
+ // src/core/source-detect.ts
110
+ function detectRemarqPlugin(el) {
111
+ const source = el.getAttribute("data-remarq-source");
112
+ if (!source) return { source: null, component: null };
113
+ return {
114
+ source,
115
+ component: el.getAttribute("data-remarq-component")
116
+ };
117
+ }
118
+ function detectExternalSource(el) {
119
+ var _a;
120
+ const source = (_a = el.dataset.source) != null ? _a : el.getAttribute("data-locator");
121
+ if (!source) return { source: null, component: null };
122
+ return { source, component: null };
123
+ }
124
+ function detectReactFiber(el) {
125
+ var _a, _b, _c, _d;
126
+ const key = Object.keys(el).find((k) => k.startsWith("__reactFiber$"));
127
+ if (!key) return { source: null, component: null };
128
+ let current = el[key];
129
+ let depth = 0;
130
+ while (current && depth < 15) {
131
+ const debugSource = current._debugSource;
132
+ if (debugSource == null ? void 0 : debugSource.fileName) {
133
+ const source = `${debugSource.fileName}:${(_a = debugSource.lineNumber) != null ? _a : 0}:${(_b = debugSource.columnNumber) != null ? _b : 0}`;
134
+ const fiberType = current.type;
135
+ const component = typeof fiberType === "object" && fiberType ? (_d = (_c = fiberType.displayName) != null ? _c : fiberType.name) != null ? _d : null : null;
136
+ return { source, component };
137
+ }
138
+ current = current.return;
139
+ depth++;
140
+ }
141
+ return { source: null, component: null };
142
+ }
143
+ function detectSource(el) {
144
+ const external = detectExternalSource(el);
145
+ if (external.source) return external;
146
+ const fiber = detectReactFiber(el);
147
+ if (fiber.source) return fiber;
148
+ return { source: null, component: null };
149
+ }
150
+
106
151
  // src/core/fingerprint.ts
107
152
  var TEXT_MAX_LENGTH = 50;
108
153
  function createFingerprint(el, options) {
109
154
  var _a, _b, _c, _d, _e, _f, _g;
110
155
  const dataAttr = (_a = options == null ? void 0 : options.dataAttribute) != null ? _a : "data-annotate";
111
- return {
156
+ return __spreadValues({
112
157
  dataAnnotate: (_b = el.getAttribute(dataAttr)) != null ? _b : null,
113
158
  dataTestId: (_e = (_d = (_c = el.getAttribute("data-testid")) != null ? _c : el.getAttribute("data-test")) != null ? _d : el.getAttribute("data-cy")) != null ? _e : null,
114
159
  id: getStableId(el),
@@ -125,6 +170,24 @@ function createFingerprint(el, options) {
125
170
  parentAnchor: findParentAnchor(el, dataAttr),
126
171
  rawClasses: Array.from(el.classList),
127
172
  cssModules: decomposeCSSModules(Array.from(el.classList))
173
+ }, resolveSourceFields(el));
174
+ }
175
+ function resolveSourceFields(el) {
176
+ const plugin = detectRemarqPlugin(el);
177
+ if (plugin.source) {
178
+ return {
179
+ sourceLocation: plugin.source,
180
+ componentName: plugin.component,
181
+ detectedSource: null,
182
+ detectedComponent: null
183
+ };
184
+ }
185
+ const detected = detectSource(el);
186
+ return {
187
+ sourceLocation: null,
188
+ componentName: null,
189
+ detectedSource: detected.source,
190
+ detectedComponent: detected.component
128
191
  };
129
192
  }
130
193
  function getStableId(el) {
@@ -396,10 +459,95 @@ var AnnotationStorage = class {
396
459
  }
397
460
  }
398
461
  };
462
+
463
+ // src/core/agent-export.ts
464
+ function parseSourceLocation(raw) {
465
+ const parts = raw.split(":");
466
+ if (parts.length < 2) return null;
467
+ const column = parseInt(parts.pop(), 10);
468
+ const line = parseInt(parts.pop(), 10);
469
+ const file = parts.join(":");
470
+ if (!file || isNaN(line)) return null;
471
+ return { file, line, column: isNaN(column) ? 0 : column };
472
+ }
473
+ function resolveSource(fp) {
474
+ var _a, _b;
475
+ if (fp.sourceLocation) {
476
+ const parsed = parseSourceLocation(fp.sourceLocation);
477
+ if (parsed) return __spreadProps(__spreadValues({}, parsed), { component: (_a = fp.componentName) != null ? _a : null });
478
+ }
479
+ if (fp.detectedSource) {
480
+ const parsed = parseSourceLocation(fp.detectedSource);
481
+ if (parsed) return __spreadProps(__spreadValues({}, parsed), { component: (_b = fp.detectedComponent) != null ? _b : null });
482
+ }
483
+ return null;
484
+ }
485
+ var TEMPLATE_GLOB = "*.{tsx,jsx,vue,svelte,html}";
486
+ var CSS_MODULE_GLOB = "*.module.{css,scss,less}";
487
+ var COMPONENT_GLOB = "*.{tsx,jsx,vue,ts,js}";
488
+ function buildSearchHints(fp) {
489
+ var _a, _b;
490
+ const grepQueries = [];
491
+ if (fp.dataAnnotate) {
492
+ grepQueries.push({ query: `data-annotate="${fp.dataAnnotate}"`, glob: TEMPLATE_GLOB, confidence: "high" });
493
+ }
494
+ if (fp.dataTestId) {
495
+ grepQueries.push({ query: `data-testid="${fp.dataTestId}"`, glob: TEMPLATE_GLOB, confidence: "high" });
496
+ }
497
+ if (fp.id) {
498
+ grepQueries.push({ query: `id="${fp.id}"`, glob: TEMPLATE_GLOB, confidence: "high" });
499
+ }
500
+ if (fp.ariaLabel) {
501
+ grepQueries.push({ query: `aria-label="${fp.ariaLabel}"`, glob: TEMPLATE_GLOB, confidence: "high" });
502
+ }
503
+ if (fp.textContent) {
504
+ grepQueries.push({ query: `"${fp.textContent}"`, glob: TEMPLATE_GLOB, confidence: "medium" });
505
+ }
506
+ if (fp.role) {
507
+ grepQueries.push({ query: `role="${fp.role}"`, glob: TEMPLATE_GLOB, confidence: "medium" });
508
+ }
509
+ if ((_a = fp.cssModules) == null ? void 0 : _a.length) {
510
+ for (const mod of fp.cssModules) {
511
+ grepQueries.push({ query: `.${mod.localName}`, glob: CSS_MODULE_GLOB, confidence: "medium" });
512
+ grepQueries.push({ query: `styles.${mod.localName}`, glob: COMPONENT_GLOB, confidence: "medium" });
513
+ }
514
+ }
515
+ if (fp.stableClasses.length) {
516
+ for (const cls of fp.stableClasses.slice(0, 3)) {
517
+ grepQueries.push({ query: `"${cls}"`, glob: TEMPLATE_GLOB, confidence: "low" });
518
+ }
519
+ }
520
+ return {
521
+ grepQueries,
522
+ domContext: fp.domPath,
523
+ tagName: fp.tagName,
524
+ classes: (_b = fp.rawClasses) != null ? _b : fp.stableClasses
525
+ };
526
+ }
527
+ function generateAgentExport(annotations, viewportBucket) {
528
+ const agentAnnotations = annotations.map((ann) => ({
529
+ id: ann.id,
530
+ route: ann.route,
531
+ comment: ann.comment,
532
+ status: ann.status,
533
+ timestamp: ann.timestamp,
534
+ source: resolveSource(ann.fingerprint),
535
+ searchHints: buildSearchHints(ann.fingerprint)
536
+ }));
537
+ return {
538
+ version: 1,
539
+ format: "agent",
540
+ viewportBucket,
541
+ annotations: agentAnnotations
542
+ };
543
+ }
399
544
  // Annotate the CommonJS export names for ESM import in node:
400
545
  0 && (module.exports = {
401
546
  AnnotationStorage,
402
547
  createFingerprint,
548
+ detectRemarqPlugin,
549
+ detectSource,
550
+ generateAgentExport,
403
551
  matchElement
404
552
  });
405
553
  //# sourceMappingURL=index.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/core/index.ts","../../src/core/hash-detect.ts","../../src/core/fingerprint.ts","../../src/core/matcher.ts","../../src/core/viewport.ts","../../src/core/storage.ts"],"sourcesContent":["export type { Annotation, AnnotationStore, ElementFingerprint, CSSModuleClass } from './types'\nexport { createFingerprint } from './fingerprint'\nexport { matchElement } from './matcher'\nexport { AnnotationStorage } from './storage'\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 { ElementFingerprint, WebRemarqOptions } from './types'\nimport { filterClasses, isHashedClass, decomposeCSSModules } 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 rawClasses: Array.from(el.classList),\n cssModules: decomposeCSSModules(Array.from(el.classList)),\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"],"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/agent-export.ts"],"sourcesContent":["export type { Annotation, AnnotationStore, ElementFingerprint, CSSModuleClass, SourceDetectionResult, SearchConfidence, GrepQuery, AgentSearchHints, AgentAnnotationSource, AgentAnnotation, AgentExport } 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"]}
@@ -3,6 +3,10 @@ interface CSSModuleClass {
3
3
  moduleHint: string;
4
4
  localName: string;
5
5
  }
6
+ interface SourceDetectionResult {
7
+ source: string | null;
8
+ component: string | null;
9
+ }
6
10
  interface ElementFingerprint {
7
11
  dataAnnotate: string | null;
8
12
  dataTestId: string | null;
@@ -17,6 +21,10 @@ interface ElementFingerprint {
17
21
  parentAnchor: string | null;
18
22
  rawClasses?: string[];
19
23
  cssModules?: CSSModuleClass[];
24
+ sourceLocation: string | null;
25
+ componentName: string | null;
26
+ detectedSource: string | null;
27
+ detectedComponent: string | null;
20
28
  }
21
29
  interface Annotation {
22
30
  id: string;
@@ -37,6 +45,39 @@ interface WebRemarqOptions {
37
45
  classFilter?: (className: string) => boolean;
38
46
  dataAttribute?: string;
39
47
  }
48
+ type SearchConfidence = 'high' | 'medium' | 'low';
49
+ interface GrepQuery {
50
+ query: string;
51
+ glob: string;
52
+ confidence: SearchConfidence;
53
+ }
54
+ interface AgentSearchHints {
55
+ grepQueries: GrepQuery[];
56
+ domContext: string;
57
+ tagName: string;
58
+ classes: string[];
59
+ }
60
+ interface AgentAnnotationSource {
61
+ file: string;
62
+ line: number;
63
+ column: number;
64
+ component: string | null;
65
+ }
66
+ interface AgentAnnotation {
67
+ id: string;
68
+ route: string;
69
+ comment: string;
70
+ status: 'pending' | 'resolved';
71
+ timestamp: number;
72
+ source: AgentAnnotationSource | null;
73
+ searchHints: AgentSearchHints;
74
+ }
75
+ interface AgentExport {
76
+ version: 1;
77
+ format: 'agent';
78
+ viewportBucket: number;
79
+ annotations: AgentAnnotation[];
80
+ }
40
81
 
41
82
  declare function createFingerprint(el: HTMLElement, options?: Pick<WebRemarqOptions, 'classFilter' | 'dataAttribute'>): ElementFingerprint;
42
83
 
@@ -60,4 +101,16 @@ declare class AnnotationStorage {
60
101
  private save;
61
102
  }
62
103
 
63
- export { type Annotation, AnnotationStorage, type AnnotationStore, type CSSModuleClass, type ElementFingerprint, createFingerprint, matchElement };
104
+ /**
105
+ * Level 1: Read data-remarq-source/data-remarq-component attrs
106
+ * injected by @web-remarq/babel-plugin or @web-remarq/unplugin.
107
+ */
108
+ declare function detectRemarqPlugin(el: HTMLElement): SourceDetectionResult;
109
+ /**
110
+ * Runs Level 2 detectors in order. Returns first non-null result.
111
+ */
112
+ declare function detectSource(el: HTMLElement): SourceDetectionResult;
113
+
114
+ declare function generateAgentExport(annotations: Annotation[], viewportBucket: number): AgentExport;
115
+
116
+ export { type AgentAnnotation, type AgentAnnotationSource, type AgentExport, type AgentSearchHints, type Annotation, AnnotationStorage, type AnnotationStore, type CSSModuleClass, type ElementFingerprint, type GrepQuery, type SearchConfidence, type SourceDetectionResult, createFingerprint, detectRemarqPlugin, detectSource, generateAgentExport, matchElement };
@@ -3,6 +3,10 @@ interface CSSModuleClass {
3
3
  moduleHint: string;
4
4
  localName: string;
5
5
  }
6
+ interface SourceDetectionResult {
7
+ source: string | null;
8
+ component: string | null;
9
+ }
6
10
  interface ElementFingerprint {
7
11
  dataAnnotate: string | null;
8
12
  dataTestId: string | null;
@@ -17,6 +21,10 @@ interface ElementFingerprint {
17
21
  parentAnchor: string | null;
18
22
  rawClasses?: string[];
19
23
  cssModules?: CSSModuleClass[];
24
+ sourceLocation: string | null;
25
+ componentName: string | null;
26
+ detectedSource: string | null;
27
+ detectedComponent: string | null;
20
28
  }
21
29
  interface Annotation {
22
30
  id: string;
@@ -37,6 +45,39 @@ interface WebRemarqOptions {
37
45
  classFilter?: (className: string) => boolean;
38
46
  dataAttribute?: string;
39
47
  }
48
+ type SearchConfidence = 'high' | 'medium' | 'low';
49
+ interface GrepQuery {
50
+ query: string;
51
+ glob: string;
52
+ confidence: SearchConfidence;
53
+ }
54
+ interface AgentSearchHints {
55
+ grepQueries: GrepQuery[];
56
+ domContext: string;
57
+ tagName: string;
58
+ classes: string[];
59
+ }
60
+ interface AgentAnnotationSource {
61
+ file: string;
62
+ line: number;
63
+ column: number;
64
+ component: string | null;
65
+ }
66
+ interface AgentAnnotation {
67
+ id: string;
68
+ route: string;
69
+ comment: string;
70
+ status: 'pending' | 'resolved';
71
+ timestamp: number;
72
+ source: AgentAnnotationSource | null;
73
+ searchHints: AgentSearchHints;
74
+ }
75
+ interface AgentExport {
76
+ version: 1;
77
+ format: 'agent';
78
+ viewportBucket: number;
79
+ annotations: AgentAnnotation[];
80
+ }
40
81
 
41
82
  declare function createFingerprint(el: HTMLElement, options?: Pick<WebRemarqOptions, 'classFilter' | 'dataAttribute'>): ElementFingerprint;
42
83
 
@@ -60,4 +101,16 @@ declare class AnnotationStorage {
60
101
  private save;
61
102
  }
62
103
 
63
- export { type Annotation, AnnotationStorage, type AnnotationStore, type CSSModuleClass, type ElementFingerprint, createFingerprint, matchElement };
104
+ /**
105
+ * Level 1: Read data-remarq-source/data-remarq-component attrs
106
+ * injected by @web-remarq/babel-plugin or @web-remarq/unplugin.
107
+ */
108
+ declare function detectRemarqPlugin(el: HTMLElement): SourceDetectionResult;
109
+ /**
110
+ * Runs Level 2 detectors in order. Returns first non-null result.
111
+ */
112
+ declare function detectSource(el: HTMLElement): SourceDetectionResult;
113
+
114
+ declare function generateAgentExport(annotations: Annotation[], viewportBucket: number): AgentExport;
115
+
116
+ export { type AgentAnnotation, type AgentAnnotationSource, type AgentExport, type AgentSearchHints, type Annotation, AnnotationStorage, type AnnotationStore, type CSSModuleClass, type ElementFingerprint, type GrepQuery, type SearchConfidence, type SourceDetectionResult, createFingerprint, detectRemarqPlugin, detectSource, generateAgentExport, matchElement };