web-remarq 0.2.5 → 0.3.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/dist/index.d.cts CHANGED
@@ -17,6 +17,10 @@ interface ElementFingerprint {
17
17
  parentAnchor: string | null;
18
18
  rawClasses?: string[];
19
19
  cssModules?: CSSModuleClass[];
20
+ sourceLocation: string | null;
21
+ componentName: string | null;
22
+ detectedSource: string | null;
23
+ detectedComponent: string | null;
20
24
  }
21
25
  interface Annotation {
22
26
  id: string;
@@ -43,16 +47,49 @@ interface ImportResult {
43
47
  otherBreakpoint: number;
44
48
  detached: number;
45
49
  }
50
+ type SearchConfidence = 'high' | 'medium' | 'low';
51
+ interface GrepQuery {
52
+ query: string;
53
+ glob: string;
54
+ confidence: SearchConfidence;
55
+ }
56
+ interface AgentSearchHints {
57
+ grepQueries: GrepQuery[];
58
+ domContext: string;
59
+ tagName: string;
60
+ classes: string[];
61
+ }
62
+ interface AgentAnnotationSource {
63
+ file: string;
64
+ line: number;
65
+ column: number;
66
+ component: string | null;
67
+ }
68
+ interface AgentAnnotation {
69
+ id: string;
70
+ route: string;
71
+ comment: string;
72
+ status: 'pending' | 'resolved';
73
+ timestamp: number;
74
+ source: AgentAnnotationSource | null;
75
+ searchHints: AgentSearchHints;
76
+ }
77
+ interface AgentExport {
78
+ version: 1;
79
+ format: 'agent';
80
+ viewportBucket: number;
81
+ annotations: AgentAnnotation[];
82
+ }
46
83
 
47
84
  declare const WebRemarq: {
48
85
  init(opts?: WebRemarqOptions): void;
49
86
  destroy(): void;
50
87
  setTheme(theme: "light" | "dark"): void;
51
- export(format: "md" | "json"): void;
52
- copy(): void;
88
+ export(format: "md" | "json" | "agent"): void;
89
+ copy(format?: "md" | "agent"): void;
53
90
  import(file: File): Promise<ImportResult>;
54
91
  getAnnotations(route?: string): Annotation[];
55
92
  clearAll(): void;
56
93
  };
57
94
 
58
- export { type Annotation, type AnnotationStore, type CSSModuleClass, type ElementFingerprint, type ImportResult, WebRemarq, type WebRemarqOptions };
95
+ export { type AgentAnnotation, type AgentExport, type Annotation, type AnnotationStore, type CSSModuleClass, type ElementFingerprint, type ImportResult, WebRemarq, type WebRemarqOptions };
package/dist/index.d.ts CHANGED
@@ -17,6 +17,10 @@ interface ElementFingerprint {
17
17
  parentAnchor: string | null;
18
18
  rawClasses?: string[];
19
19
  cssModules?: CSSModuleClass[];
20
+ sourceLocation: string | null;
21
+ componentName: string | null;
22
+ detectedSource: string | null;
23
+ detectedComponent: string | null;
20
24
  }
21
25
  interface Annotation {
22
26
  id: string;
@@ -43,16 +47,49 @@ interface ImportResult {
43
47
  otherBreakpoint: number;
44
48
  detached: number;
45
49
  }
50
+ type SearchConfidence = 'high' | 'medium' | 'low';
51
+ interface GrepQuery {
52
+ query: string;
53
+ glob: string;
54
+ confidence: SearchConfidence;
55
+ }
56
+ interface AgentSearchHints {
57
+ grepQueries: GrepQuery[];
58
+ domContext: string;
59
+ tagName: string;
60
+ classes: string[];
61
+ }
62
+ interface AgentAnnotationSource {
63
+ file: string;
64
+ line: number;
65
+ column: number;
66
+ component: string | null;
67
+ }
68
+ interface AgentAnnotation {
69
+ id: string;
70
+ route: string;
71
+ comment: string;
72
+ status: 'pending' | 'resolved';
73
+ timestamp: number;
74
+ source: AgentAnnotationSource | null;
75
+ searchHints: AgentSearchHints;
76
+ }
77
+ interface AgentExport {
78
+ version: 1;
79
+ format: 'agent';
80
+ viewportBucket: number;
81
+ annotations: AgentAnnotation[];
82
+ }
46
83
 
47
84
  declare const WebRemarq: {
48
85
  init(opts?: WebRemarqOptions): void;
49
86
  destroy(): void;
50
87
  setTheme(theme: "light" | "dark"): void;
51
- export(format: "md" | "json"): void;
52
- copy(): void;
88
+ export(format: "md" | "json" | "agent"): void;
89
+ copy(format?: "md" | "agent"): void;
53
90
  import(file: File): Promise<ImportResult>;
54
91
  getAnnotations(route?: string): Annotation[];
55
92
  clearAll(): void;
56
93
  };
57
94
 
58
- export { type Annotation, type AnnotationStore, type CSSModuleClass, type ElementFingerprint, type ImportResult, WebRemarq, type WebRemarqOptions };
95
+ export { type AgentAnnotation, type AgentExport, type Annotation, type AnnotationStore, type CSSModuleClass, type ElementFingerprint, type ImportResult, WebRemarq, type WebRemarqOptions };
package/dist/index.js CHANGED
@@ -194,12 +194,54 @@ function decomposeCSSModules(classes) {
194
194
  return result;
195
195
  }
196
196
 
197
+ // src/core/source-detect.ts
198
+ function detectRemarqPlugin(el) {
199
+ const source = el.getAttribute("data-remarq-source");
200
+ if (!source) return { source: null, component: null };
201
+ return {
202
+ source,
203
+ component: el.getAttribute("data-remarq-component")
204
+ };
205
+ }
206
+ function detectExternalSource(el) {
207
+ var _a;
208
+ const source = (_a = el.dataset.source) != null ? _a : el.getAttribute("data-locator");
209
+ if (!source) return { source: null, component: null };
210
+ return { source, component: null };
211
+ }
212
+ function detectReactFiber(el) {
213
+ var _a, _b, _c, _d;
214
+ const key = Object.keys(el).find((k) => k.startsWith("__reactFiber$"));
215
+ if (!key) return { source: null, component: null };
216
+ let current = el[key];
217
+ let depth = 0;
218
+ while (current && depth < 15) {
219
+ const debugSource = current._debugSource;
220
+ if (debugSource == null ? void 0 : debugSource.fileName) {
221
+ const source = `${debugSource.fileName}:${(_a = debugSource.lineNumber) != null ? _a : 0}:${(_b = debugSource.columnNumber) != null ? _b : 0}`;
222
+ const fiberType = current.type;
223
+ const component = typeof fiberType === "object" && fiberType ? (_d = (_c = fiberType.displayName) != null ? _c : fiberType.name) != null ? _d : null : null;
224
+ return { source, component };
225
+ }
226
+ current = current.return;
227
+ depth++;
228
+ }
229
+ return { source: null, component: null };
230
+ }
231
+ function detectSource(el) {
232
+ const external = detectExternalSource(el);
233
+ if (external.source) return external;
234
+ const fiber = detectReactFiber(el);
235
+ if (fiber.source) return fiber;
236
+ return { source: null, component: null };
237
+ }
238
+
197
239
  // src/core/fingerprint.ts
198
240
  var TEXT_MAX_LENGTH = 50;
199
241
  function createFingerprint(el, options2) {
200
242
  var _a, _b, _c, _d, _e, _f, _g;
201
243
  const dataAttr = (_a = options2 == null ? void 0 : options2.dataAttribute) != null ? _a : "data-annotate";
202
- return {
244
+ return __spreadValues({
203
245
  dataAnnotate: (_b = el.getAttribute(dataAttr)) != null ? _b : null,
204
246
  dataTestId: (_e = (_d = (_c = el.getAttribute("data-testid")) != null ? _c : el.getAttribute("data-test")) != null ? _d : el.getAttribute("data-cy")) != null ? _e : null,
205
247
  id: getStableId(el),
@@ -216,6 +258,24 @@ function createFingerprint(el, options2) {
216
258
  parentAnchor: findParentAnchor(el, dataAttr),
217
259
  rawClasses: Array.from(el.classList),
218
260
  cssModules: decomposeCSSModules(Array.from(el.classList))
261
+ }, resolveSourceFields(el));
262
+ }
263
+ function resolveSourceFields(el) {
264
+ const plugin = detectRemarqPlugin(el);
265
+ if (plugin.source) {
266
+ return {
267
+ sourceLocation: plugin.source,
268
+ componentName: plugin.component,
269
+ detectedSource: null,
270
+ detectedComponent: null
271
+ };
272
+ }
273
+ const detected = detectSource(el);
274
+ return {
275
+ sourceLocation: null,
276
+ componentName: null,
277
+ detectedSource: detected.source,
278
+ detectedComponent: detected.component
219
279
  };
220
280
  }
221
281
  function getStableId(el) {
@@ -401,6 +461,88 @@ function matchElement(fp, options2) {
401
461
  return bestScore >= MATCH_THRESHOLD ? bestEl : null;
402
462
  }
403
463
 
464
+ // src/core/agent-export.ts
465
+ function parseSourceLocation(raw) {
466
+ const parts = raw.split(":");
467
+ if (parts.length < 2) return null;
468
+ const column = parseInt(parts.pop(), 10);
469
+ const line = parseInt(parts.pop(), 10);
470
+ const file = parts.join(":");
471
+ if (!file || isNaN(line)) return null;
472
+ return { file, line, column: isNaN(column) ? 0 : column };
473
+ }
474
+ function resolveSource(fp) {
475
+ var _a, _b;
476
+ if (fp.sourceLocation) {
477
+ const parsed = parseSourceLocation(fp.sourceLocation);
478
+ if (parsed) return __spreadProps(__spreadValues({}, parsed), { component: (_a = fp.componentName) != null ? _a : null });
479
+ }
480
+ if (fp.detectedSource) {
481
+ const parsed = parseSourceLocation(fp.detectedSource);
482
+ if (parsed) return __spreadProps(__spreadValues({}, parsed), { component: (_b = fp.detectedComponent) != null ? _b : null });
483
+ }
484
+ return null;
485
+ }
486
+ var TEMPLATE_GLOB = "*.{tsx,jsx,vue,svelte,html}";
487
+ var CSS_MODULE_GLOB = "*.module.{css,scss,less}";
488
+ var COMPONENT_GLOB = "*.{tsx,jsx,vue,ts,js}";
489
+ function buildSearchHints(fp) {
490
+ var _a, _b;
491
+ const grepQueries = [];
492
+ if (fp.dataAnnotate) {
493
+ grepQueries.push({ query: `data-annotate="${fp.dataAnnotate}"`, glob: TEMPLATE_GLOB, confidence: "high" });
494
+ }
495
+ if (fp.dataTestId) {
496
+ grepQueries.push({ query: `data-testid="${fp.dataTestId}"`, glob: TEMPLATE_GLOB, confidence: "high" });
497
+ }
498
+ if (fp.id) {
499
+ grepQueries.push({ query: `id="${fp.id}"`, glob: TEMPLATE_GLOB, confidence: "high" });
500
+ }
501
+ if (fp.ariaLabel) {
502
+ grepQueries.push({ query: `aria-label="${fp.ariaLabel}"`, glob: TEMPLATE_GLOB, confidence: "high" });
503
+ }
504
+ if (fp.textContent) {
505
+ grepQueries.push({ query: `"${fp.textContent}"`, glob: TEMPLATE_GLOB, confidence: "medium" });
506
+ }
507
+ if (fp.role) {
508
+ grepQueries.push({ query: `role="${fp.role}"`, glob: TEMPLATE_GLOB, confidence: "medium" });
509
+ }
510
+ if ((_a = fp.cssModules) == null ? void 0 : _a.length) {
511
+ for (const mod of fp.cssModules) {
512
+ grepQueries.push({ query: `.${mod.localName}`, glob: CSS_MODULE_GLOB, confidence: "medium" });
513
+ grepQueries.push({ query: `styles.${mod.localName}`, glob: COMPONENT_GLOB, confidence: "medium" });
514
+ }
515
+ }
516
+ if (fp.stableClasses.length) {
517
+ for (const cls of fp.stableClasses.slice(0, 3)) {
518
+ grepQueries.push({ query: `"${cls}"`, glob: TEMPLATE_GLOB, confidence: "low" });
519
+ }
520
+ }
521
+ return {
522
+ grepQueries,
523
+ domContext: fp.domPath,
524
+ tagName: fp.tagName,
525
+ classes: (_b = fp.rawClasses) != null ? _b : fp.stableClasses
526
+ };
527
+ }
528
+ function generateAgentExport(annotations, viewportBucket) {
529
+ const agentAnnotations = annotations.map((ann) => ({
530
+ id: ann.id,
531
+ route: ann.route,
532
+ comment: ann.comment,
533
+ status: ann.status,
534
+ timestamp: ann.timestamp,
535
+ source: resolveSource(ann.fingerprint),
536
+ searchHints: buildSearchHints(ann.fingerprint)
537
+ }));
538
+ return {
539
+ version: 1,
540
+ format: "agent",
541
+ viewportBucket,
542
+ annotations: agentAnnotations
543
+ };
544
+ }
545
+
404
546
  // src/ui/styles.ts
405
547
  var STYLES_ID = "data-remarq-styles";
406
548
  var CSS = `
@@ -1955,6 +2097,22 @@ function copyToClipboard() {
1955
2097
  console.warn("[web-remarq] Clipboard write failed");
1956
2098
  });
1957
2099
  }
2100
+ function exportAgent() {
2101
+ const anns = storage.getAll();
2102
+ if (!anns.length) return;
2103
+ const data = generateAgentExport(anns, toBucket(window.innerWidth));
2104
+ const json = JSON.stringify(data, null, 2);
2105
+ downloadFile(json, `remarq-agent-${Date.now()}.json`, "application/json");
2106
+ }
2107
+ function copyAgentToClipboard() {
2108
+ const anns = storage.getAll();
2109
+ if (!anns.length) return;
2110
+ const data = generateAgentExport(anns, toBucket(window.innerWidth));
2111
+ const json = JSON.stringify(data, null, 2);
2112
+ navigator.clipboard.writeText(json).catch(() => {
2113
+ console.warn("[web-remarq] Clipboard write failed");
2114
+ });
2115
+ }
1958
2116
  function setupMutationObserver() {
1959
2117
  mutationObserver = new MutationObserver((mutations) => {
1960
2118
  for (const m of mutations) {
@@ -2060,10 +2218,12 @@ var WebRemarq = {
2060
2218
  },
2061
2219
  export(format) {
2062
2220
  if (format === "md") exportMarkdown();
2063
- else exportJSON();
2221
+ else if (format === "json") exportJSON();
2222
+ else exportAgent();
2064
2223
  },
2065
- copy() {
2066
- copyToClipboard();
2224
+ copy(format) {
2225
+ if (format === "agent") copyAgentToClipboard();
2226
+ else copyToClipboard();
2067
2227
  },
2068
2228
  async import(file) {
2069
2229
  const text = await file.text();