web-remarq 0.4.1 → 0.4.3

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.
@@ -145,7 +145,7 @@ var WebRemarq = (() => {
145
145
  const raw = localStorage.getItem(STORAGE_KEY);
146
146
  if (raw) {
147
147
  const parsed = JSON.parse(raw);
148
- const _a = parsed, { version, annotations } = _a, rest = __objRest(_a, ["version", "annotations"]);
148
+ const _a3 = parsed, { version, annotations } = _a3, rest = __objRest(_a3, ["version", "annotations"]);
149
149
  this.annotations = annotations != null ? annotations : [];
150
150
  this.extraFields = rest;
151
151
  this.migrateViewportBuckets();
@@ -227,13 +227,13 @@ var WebRemarq = (() => {
227
227
  };
228
228
  }
229
229
  function detectExternalSource(el) {
230
- var _a;
231
- const source = (_a = el.dataset.source) != null ? _a : el.getAttribute("data-locator");
230
+ var _a3;
231
+ const source = (_a3 = el.dataset.source) != null ? _a3 : el.getAttribute("data-locator");
232
232
  if (!source) return { source: null, component: null };
233
233
  return { source, component: null };
234
234
  }
235
235
  function detectReactFiber(el) {
236
- var _a, _b, _c, _d;
236
+ var _a3, _b, _c, _d;
237
237
  const key = Object.keys(el).find((k) => k.startsWith("__reactFiber$"));
238
238
  if (!key) return { source: null, component: null };
239
239
  let current = el[key];
@@ -241,7 +241,7 @@ var WebRemarq = (() => {
241
241
  while (current && depth < 15) {
242
242
  const debugSource = current._debugSource;
243
243
  if (debugSource == null ? void 0 : debugSource.fileName) {
244
- const source = `${debugSource.fileName}:${(_a = debugSource.lineNumber) != null ? _a : 0}:${(_b = debugSource.columnNumber) != null ? _b : 0}`;
244
+ const source = `${debugSource.fileName}:${(_a3 = debugSource.lineNumber) != null ? _a3 : 0}:${(_b = debugSource.columnNumber) != null ? _b : 0}`;
245
245
  const fiberType = current.type;
246
246
  const component = typeof fiberType === "object" && fiberType ? (_d = (_c = fiberType.displayName) != null ? _c : fiberType.name) != null ? _d : null : null;
247
247
  return { source, component };
@@ -262,8 +262,8 @@ var WebRemarq = (() => {
262
262
  // src/core/fingerprint.ts
263
263
  var TEXT_MAX_LENGTH = 50;
264
264
  function createFingerprint(el, options2) {
265
- var _a, _b, _c, _d, _e, _f, _g;
266
- const dataAttr = (_a = options2 == null ? void 0 : options2.dataAttribute) != null ? _a : "data-annotate";
265
+ var _a3, _b, _c, _d, _e, _f, _g;
266
+ const dataAttr = (_a3 = options2 == null ? void 0 : options2.dataAttribute) != null ? _a3 : "data-annotate";
267
267
  return __spreadValues({
268
268
  dataAnnotate: (_b = el.getAttribute(dataAttr)) != null ? _b : null,
269
269
  dataTestId: (_e = (_d = (_c = el.getAttribute("data-testid")) != null ? _c : el.getAttribute("data-test")) != null ? _d : el.getAttribute("data-cy")) != null ? _e : null,
@@ -308,11 +308,11 @@ var WebRemarq = (() => {
308
308
  return id;
309
309
  }
310
310
  function getTextContent(el) {
311
- var _a, _b, _c;
311
+ var _a3, _b, _c;
312
312
  let text = "";
313
313
  for (const node of Array.from(el.childNodes)) {
314
314
  if (node.nodeType === Node.TEXT_NODE) {
315
- text += (_a = node.textContent) != null ? _a : "";
315
+ text += (_a3 = node.textContent) != null ? _a3 : "";
316
316
  }
317
317
  }
318
318
  text = text.trim();
@@ -397,13 +397,13 @@ var WebRemarq = (() => {
397
397
  return union === 0 ? 0 : intersection / union;
398
398
  }
399
399
  function scoreCandidate(el, fp, dataAttr) {
400
- var _a, _b;
400
+ var _a3, _b;
401
401
  let score = 0;
402
402
  const elAnnotate = el.getAttribute(dataAttr);
403
403
  if (fp.dataAnnotate && elAnnotate === fp.dataAnnotate) {
404
404
  score += 100;
405
405
  }
406
- const elText = (_b = (_a = el.textContent) == null ? void 0 : _a.trim().slice(0, 50)) != null ? _b : null;
406
+ const elText = (_b = (_a3 = el.textContent) == null ? void 0 : _a3.trim().slice(0, 50)) != null ? _b : null;
407
407
  const textSim = textSimilarity(fp.textContent, elText);
408
408
  if (textSim > 0.7) {
409
409
  score += textSim * 35;
@@ -455,8 +455,8 @@ var WebRemarq = (() => {
455
455
  return parts.join(" > ");
456
456
  }
457
457
  function matchElement(fp, options2) {
458
- var _a;
459
- const dataAttr = (_a = options2 == null ? void 0 : options2.dataAttribute) != null ? _a : "data-annotate";
458
+ var _a3;
459
+ const dataAttr = (_a3 = options2 == null ? void 0 : options2.dataAttribute) != null ? _a3 : "data-annotate";
460
460
  if (fp.dataAnnotate) {
461
461
  const el = document.querySelector(`[${dataAttr}="${fp.dataAnnotate}"]`);
462
462
  if (el) return el;
@@ -495,10 +495,10 @@ var WebRemarq = (() => {
495
495
  return { file, line, column: isNaN(column) ? 0 : column };
496
496
  }
497
497
  function resolveSource(fp) {
498
- var _a, _b;
498
+ var _a3, _b;
499
499
  if (fp.sourceLocation) {
500
500
  const parsed = parseSourceLocation(fp.sourceLocation);
501
- if (parsed) return __spreadProps(__spreadValues({}, parsed), { component: (_a = fp.componentName) != null ? _a : null });
501
+ if (parsed) return __spreadProps(__spreadValues({}, parsed), { component: (_a3 = fp.componentName) != null ? _a3 : null });
502
502
  }
503
503
  if (fp.detectedSource) {
504
504
  const parsed = parseSourceLocation(fp.detectedSource);
@@ -510,7 +510,7 @@ var WebRemarq = (() => {
510
510
  var CSS_MODULE_GLOB = "*.module.{css,scss,less}";
511
511
  var COMPONENT_GLOB = "*.{tsx,jsx,vue,ts,js}";
512
512
  function buildSearchHints(fp) {
513
- var _a, _b;
513
+ var _a3, _b;
514
514
  const grepQueries = [];
515
515
  if (fp.dataAnnotate) {
516
516
  grepQueries.push({ query: `data-annotate="${fp.dataAnnotate}"`, glob: TEMPLATE_GLOB, confidence: "high" });
@@ -530,7 +530,7 @@ var WebRemarq = (() => {
530
530
  if (fp.role) {
531
531
  grepQueries.push({ query: `role="${fp.role}"`, glob: TEMPLATE_GLOB, confidence: "medium" });
532
532
  }
533
- if ((_a = fp.cssModules) == null ? void 0 : _a.length) {
533
+ if ((_a3 = fp.cssModules) == null ? void 0 : _a3.length) {
534
534
  for (const mod of fp.cssModules) {
535
535
  grepQueries.push({ query: `.${mod.localName}`, glob: CSS_MODULE_GLOB, confidence: "medium" });
536
536
  grepQueries.push({ query: `styles.${mod.localName}`, glob: COMPONENT_GLOB, confidence: "medium" });
@@ -597,8 +597,6 @@ var WebRemarq = (() => {
597
597
 
598
598
  .remarq-toolbar {
599
599
  position: fixed;
600
- bottom: 16px;
601
- right: 16px;
602
600
  z-index: 2147483647;
603
601
  display: flex;
604
602
  gap: 4px;
@@ -612,6 +610,11 @@ var WebRemarq = (() => {
612
610
  color: var(--remarq-text);
613
611
  }
614
612
 
613
+ .remarq-toolbar.remarq-pos-bottom-right { bottom: 16px; right: 16px; }
614
+ .remarq-toolbar.remarq-pos-bottom-left { bottom: 16px; left: 16px; flex-direction: row-reverse; }
615
+ .remarq-toolbar.remarq-pos-top-right { top: 16px; right: 16px; }
616
+ .remarq-toolbar.remarq-pos-top-left { top: 16px; left: 16px; flex-direction: row-reverse; }
617
+
615
618
  .remarq-toolbar.remarq-minimized { padding: 4px; }
616
619
 
617
620
  .remarq-toolbar-btn {
@@ -765,8 +768,6 @@ var WebRemarq = (() => {
765
768
 
766
769
  .remarq-detached-panel {
767
770
  position: fixed;
768
- bottom: 60px;
769
- right: 16px;
770
771
  z-index: 2147483646;
771
772
  width: 280px;
772
773
  max-height: 300px;
@@ -780,6 +781,11 @@ var WebRemarq = (() => {
780
781
  color: var(--remarq-text);
781
782
  }
782
783
 
784
+ .remarq-detached-panel.remarq-pos-bottom-right { bottom: 60px; right: 16px; }
785
+ .remarq-detached-panel.remarq-pos-bottom-left { bottom: 60px; left: 16px; }
786
+ .remarq-detached-panel.remarq-pos-top-right { top: 60px; right: 16px; }
787
+ .remarq-detached-panel.remarq-pos-top-left { top: 60px; left: 16px; }
788
+
783
789
  .remarq-detached-header {
784
790
  padding: 8px 12px;
785
791
  border-bottom: 1px solid var(--remarq-border);
@@ -834,6 +840,20 @@ var WebRemarq = (() => {
834
840
  overflow: hidden;
835
841
  }
836
842
 
843
+ .remarq-pos-top-right .remarq-export-menu,
844
+ .remarq-pos-top-left .remarq-export-menu {
845
+ bottom: auto;
846
+ top: 100%;
847
+ margin-bottom: 0;
848
+ margin-top: 4px;
849
+ }
850
+
851
+ .remarq-pos-bottom-left .remarq-export-menu,
852
+ .remarq-pos-top-left .remarq-export-menu {
853
+ right: auto;
854
+ left: 0;
855
+ }
856
+
837
857
  .remarq-export-menu button {
838
858
  display: block;
839
859
  width: 100%;
@@ -937,6 +957,70 @@ var WebRemarq = (() => {
937
957
  .remarq-toolbar-btn:disabled { opacity: 0.3; cursor: default; }
938
958
  .remarq-toolbar-btn:disabled:hover { background: transparent; }
939
959
 
960
+ .remarq-shortcuts-backdrop {
961
+ position: fixed;
962
+ top: 0;
963
+ left: 0;
964
+ width: 100%;
965
+ height: 100%;
966
+ background: rgba(0, 0, 0, 0.4);
967
+ z-index: 2147483647;
968
+ display: flex;
969
+ align-items: center;
970
+ justify-content: center;
971
+ }
972
+
973
+ .remarq-shortcuts-modal {
974
+ background: var(--remarq-bg);
975
+ border: 1px solid var(--remarq-border);
976
+ border-radius: 12px;
977
+ box-shadow: var(--remarq-shadow);
978
+ padding: 20px 24px;
979
+ min-width: 300px;
980
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
981
+ color: var(--remarq-text);
982
+ }
983
+
984
+ .remarq-shortcuts-title {
985
+ font-size: 15px;
986
+ font-weight: 600;
987
+ margin-bottom: 12px;
988
+ padding-bottom: 8px;
989
+ border-bottom: 1px solid var(--remarq-border);
990
+ }
991
+
992
+ .remarq-shortcuts-row {
993
+ display: flex;
994
+ align-items: center;
995
+ gap: 12px;
996
+ padding: 4px 0;
997
+ font-size: 13px;
998
+ }
999
+
1000
+ .remarq-shortcuts-key {
1001
+ display: inline-flex;
1002
+ align-items: center;
1003
+ justify-content: center;
1004
+ min-width: 28px;
1005
+ padding: 2px 8px;
1006
+ background: var(--remarq-bg-secondary);
1007
+ border: 1px solid var(--remarq-border);
1008
+ border-radius: 4px;
1009
+ font-family: inherit;
1010
+ font-size: 12px;
1011
+ font-weight: 600;
1012
+ white-space: nowrap;
1013
+ }
1014
+
1015
+ .remarq-shortcuts-context {
1016
+ font-size: 10px;
1017
+ color: var(--remarq-text-secondary);
1018
+ background: var(--remarq-bg-secondary);
1019
+ border-radius: 3px;
1020
+ padding: 1px 6px;
1021
+ margin-left: auto;
1022
+ }
1023
+
940
1024
  .remarq-popup-hint {
941
1025
  font-size: 11px;
942
1026
  color: var(--remarq-text-secondary);
@@ -972,9 +1056,9 @@ var WebRemarq = (() => {
972
1056
  var THEME_KEY = "remarq:theme";
973
1057
  var ThemeManager = class {
974
1058
  constructor(parent, initialTheme) {
975
- var _a;
1059
+ var _a3;
976
1060
  const persisted = this.loadTheme();
977
- this.theme = (_a = initialTheme != null ? initialTheme : persisted) != null ? _a : "light";
1061
+ this.theme = (_a3 = initialTheme != null ? initialTheme : persisted) != null ? _a3 : "light";
978
1062
  this.container = document.createElement("div");
979
1063
  this.container.setAttribute("data-remarq-theme", this.theme);
980
1064
  parent.appendChild(this.container);
@@ -1011,14 +1095,18 @@ var WebRemarq = (() => {
1011
1095
  };
1012
1096
 
1013
1097
  // src/ui/toolbar.ts
1098
+ var _a;
1099
+ var isMac = typeof navigator !== "undefined" && /Mac|iPhone|iPad/.test((_a = navigator.platform) != null ? _a : "");
1100
+ var modKey = isMac ? "\u2325" : "Alt";
1014
1101
  var TOOLTIPS = {
1015
- inspect: "Inspect element",
1102
+ inspect: `Inspect element (${modKey}+I)`,
1016
1103
  spacing: "Spacing overlay (S)",
1017
- copy: "Copy as Markdown",
1104
+ copy: "Copy as Markdown (C)",
1018
1105
  export: "Export",
1019
1106
  import: "Import JSON",
1020
1107
  clear: "Clear all",
1021
1108
  theme: "Toggle theme",
1109
+ help: "Keyboard shortcuts (?)",
1022
1110
  minimize: "Minimize"
1023
1111
  };
1024
1112
  var ICONS = {
@@ -1029,17 +1117,19 @@ var WebRemarq = (() => {
1029
1117
  import: '<svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M8 10V2M4 6l4 4 4-4M2 12h12"/></svg>',
1030
1118
  clear: '<svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M3 4h10M6 4V3h4v1M5 4v9h6V4"/></svg>',
1031
1119
  theme: '<svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5"><circle cx="8" cy="8" r="3"/><path d="M8 1v2M8 13v2M1 8h2M13 8h2"/></svg>',
1120
+ help: '<svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5"><circle cx="8" cy="8" r="6"/><path d="M6 6.5a2 2 0 0 1 3.5 1.5c0 1-1.5 1-1.5 2"/><circle cx="8" cy="12" r="0.5" fill="currentColor" stroke="none"/></svg>',
1032
1121
  minimize: '<svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M4 8h8"/></svg>'
1033
1122
  };
1034
1123
  var Toolbar = class {
1035
- constructor(container, callbacks) {
1124
+ constructor(container, callbacks, position = "bottom-right") {
1036
1125
  this.container = container;
1037
1126
  this.callbacks = callbacks;
1127
+ this.position = position;
1038
1128
  this.exportMenu = null;
1039
1129
  this.minimized = false;
1040
1130
  this.buttons = [];
1041
1131
  this.toolbarEl = document.createElement("div");
1042
- this.toolbarEl.className = "remarq-toolbar";
1132
+ this.toolbarEl.className = `remarq-toolbar remarq-pos-${position}`;
1043
1133
  this.inspectBtn = this.createButton("inspect", ICONS.inspect, () => callbacks.onInspect());
1044
1134
  this.badgeEl = document.createElement("span");
1045
1135
  this.badgeEl.className = "remarq-badge";
@@ -1060,8 +1150,9 @@ var WebRemarq = (() => {
1060
1150
  const importBtn = this.createButton("import", ICONS.import, () => this.fileInput.click());
1061
1151
  const clearBtn = this.createButton("clear", ICONS.clear, () => callbacks.onClear());
1062
1152
  const themeBtn = this.createButton("theme", ICONS.theme, () => callbacks.onThemeToggle());
1153
+ const helpBtn = this.createButton("help", ICONS.help, () => callbacks.onHelp());
1063
1154
  const minimizeBtn = this.createButton("minimize", ICONS.minimize, () => this.toggleMinimize());
1064
- this.buttons = [this.inspectBtn, this.spacingBtn, copyBtn, exportBtn, importBtn, clearBtn, themeBtn];
1155
+ this.buttons = [this.inspectBtn, this.spacingBtn, copyBtn, exportBtn, importBtn, clearBtn, themeBtn, helpBtn];
1065
1156
  this.toolbarEl.appendChild(this.inspectBtn);
1066
1157
  this.toolbarEl.appendChild(this.spacingBtn);
1067
1158
  this.toolbarEl.appendChild(copyBtn);
@@ -1069,6 +1160,7 @@ var WebRemarq = (() => {
1069
1160
  this.toolbarEl.appendChild(importBtn);
1070
1161
  this.toolbarEl.appendChild(clearBtn);
1071
1162
  this.toolbarEl.appendChild(themeBtn);
1163
+ this.toolbarEl.appendChild(helpBtn);
1072
1164
  this.toolbarEl.appendChild(minimizeBtn);
1073
1165
  this.toolbarEl.appendChild(this.fileInput);
1074
1166
  container.appendChild(this.toolbarEl);
@@ -1100,11 +1192,11 @@ var WebRemarq = (() => {
1100
1192
  this.toolbarEl.remove();
1101
1193
  }
1102
1194
  createButton(action, icon, handler) {
1103
- var _a;
1195
+ var _a3;
1104
1196
  const btn = document.createElement("button");
1105
1197
  btn.className = "remarq-toolbar-btn";
1106
1198
  btn.setAttribute("data-remarq-action", action);
1107
- btn.title = (_a = TOOLTIPS[action]) != null ? _a : "";
1199
+ btn.title = (_a3 = TOOLTIPS[action]) != null ? _a3 : "";
1108
1200
  btn.innerHTML = icon;
1109
1201
  btn.addEventListener("click", handler);
1110
1202
  return btn;
@@ -1226,11 +1318,11 @@ var WebRemarq = (() => {
1226
1318
  return parts.join(" ");
1227
1319
  }
1228
1320
  function getDirectText(el) {
1229
- var _a, _b, _c;
1321
+ var _a3, _b, _c;
1230
1322
  let text = "";
1231
1323
  for (const node of Array.from(el.childNodes)) {
1232
1324
  if (node.nodeType === Node.TEXT_NODE) {
1233
- text += (_a = node.textContent) != null ? _a : "";
1325
+ text += (_a3 = node.textContent) != null ? _a3 : "";
1234
1326
  }
1235
1327
  }
1236
1328
  text = text.trim();
@@ -1589,6 +1681,12 @@ var WebRemarq = (() => {
1589
1681
  });
1590
1682
  actions.appendChild(resolveBtn);
1591
1683
  }
1684
+ const copyBtn = document.createElement("button");
1685
+ copyBtn.textContent = "Copy";
1686
+ copyBtn.addEventListener("click", () => {
1687
+ callbacks.onCopy();
1688
+ });
1689
+ actions.appendChild(copyBtn);
1592
1690
  const deleteBtn = document.createElement("button");
1593
1691
  deleteBtn.textContent = "Delete";
1594
1692
  deleteBtn.addEventListener("click", () => {
@@ -1742,17 +1840,19 @@ var WebRemarq = (() => {
1742
1840
  markerEl.textContent = String(this.counter);
1743
1841
  markerEl.title = annotation.comment;
1744
1842
  markerEl.addEventListener("click", () => {
1745
- var _a;
1746
- (_a = this.onClick) == null ? void 0 : _a.call(this, annotation.id);
1843
+ var _a3;
1844
+ (_a3 = this.onClick) == null ? void 0 : _a3.call(this, annotation.id);
1747
1845
  });
1748
1846
  this.container.appendChild(markerEl);
1749
1847
  this.markers.set(annotation.id, { annotation, target, markerEl });
1848
+ this.applyOutline(target, annotation.status);
1750
1849
  this.updatePosition(annotation.id);
1751
1850
  }
1752
1851
  removeMarker(id) {
1753
1852
  const entry = this.markers.get(id);
1754
1853
  if (entry) {
1755
1854
  entry.markerEl.remove();
1855
+ this.removeOutline(entry.target);
1756
1856
  this.markers.delete(id);
1757
1857
  }
1758
1858
  }
@@ -1766,6 +1866,7 @@ var WebRemarq = (() => {
1766
1866
  clear() {
1767
1867
  for (const entry of this.markers.values()) {
1768
1868
  entry.markerEl.remove();
1869
+ this.removeOutline(entry.target);
1769
1870
  }
1770
1871
  this.markers.clear();
1771
1872
  this.counter = 0;
@@ -1777,6 +1878,15 @@ var WebRemarq = (() => {
1777
1878
  }
1778
1879
  this.clear();
1779
1880
  }
1881
+ applyOutline(target, status) {
1882
+ const color = status === "pending" ? "#f97316" : "rgba(34, 197, 94, 0.5)";
1883
+ target.style.outline = `2px solid ${color}`;
1884
+ target.style.outlineOffset = "2px";
1885
+ }
1886
+ removeOutline(target) {
1887
+ target.style.outline = "";
1888
+ target.style.outlineOffset = "";
1889
+ }
1780
1890
  updatePosition(id) {
1781
1891
  const entry = this.markers.get(id);
1782
1892
  if (!entry) return;
@@ -1828,16 +1938,17 @@ var WebRemarq = (() => {
1828
1938
 
1829
1939
  // src/ui/detached-panel.ts
1830
1940
  var DetachedPanel = class {
1831
- constructor(container, onDelete) {
1941
+ constructor(container, onDelete, position = "bottom-right") {
1832
1942
  this.container = container;
1833
1943
  this.onDelete = onDelete;
1944
+ this.position = position;
1834
1945
  this.panelEl = null;
1835
1946
  }
1836
1947
  update(otherBreakpoint, detached) {
1837
1948
  this.remove();
1838
1949
  if (otherBreakpoint.length === 0 && detached.length === 0) return;
1839
1950
  const panel = document.createElement("div");
1840
- panel.className = "remarq-detached-panel";
1951
+ panel.className = `remarq-detached-panel remarq-pos-${this.position}`;
1841
1952
  if (otherBreakpoint.length > 0) {
1842
1953
  this.renderSection(panel, `Other viewport (${otherBreakpoint.length})`, otherBreakpoint, "other");
1843
1954
  }
@@ -1885,8 +1996,8 @@ var WebRemarq = (() => {
1885
1996
  deleteBtn.className = "remarq-detached-delete";
1886
1997
  deleteBtn.textContent = "\xD7";
1887
1998
  deleteBtn.addEventListener("click", () => {
1888
- var _a;
1889
- (_a = this.onDelete) == null ? void 0 : _a.call(this, ann.id);
1999
+ var _a3;
2000
+ (_a3 = this.onDelete) == null ? void 0 : _a3.call(this, ann.id);
1890
2001
  });
1891
2002
  item.appendChild(deleteBtn);
1892
2003
  }
@@ -1901,6 +2012,76 @@ var WebRemarq = (() => {
1901
2012
  }
1902
2013
  };
1903
2014
 
2015
+ // src/ui/shortcuts-modal.ts
2016
+ var _a2;
2017
+ var isMac2 = typeof navigator !== "undefined" && /Mac|iPhone|iPad/.test((_a2 = navigator.platform) != null ? _a2 : "");
2018
+ var modKey2 = isMac2 ? "\u2325" : "Alt";
2019
+ var SHORTCUTS = [
2020
+ { key: `${modKey2}+I`, description: "Toggle inspect mode" },
2021
+ { key: "S", description: "Toggle spacing overlay", context: "inspect" },
2022
+ { key: "C", description: "Copy all annotations to clipboard", context: "inspect" },
2023
+ { key: "Esc", description: "Exit inspect mode / close popup" },
2024
+ { key: "?", description: "Show this help" },
2025
+ { key: "Enter", description: "Submit annotation", context: "popup" },
2026
+ { key: "Shift+Enter", description: "New line", context: "popup" }
2027
+ ];
2028
+ var modalEl = null;
2029
+ var keyHandler = null;
2030
+ function showShortcutsModal(container) {
2031
+ if (modalEl) {
2032
+ hideShortcutsModal();
2033
+ return;
2034
+ }
2035
+ const backdrop = document.createElement("div");
2036
+ backdrop.className = "remarq-shortcuts-backdrop";
2037
+ const modal = document.createElement("div");
2038
+ modal.className = "remarq-shortcuts-modal";
2039
+ const title = document.createElement("div");
2040
+ title.className = "remarq-shortcuts-title";
2041
+ title.textContent = "Keyboard Shortcuts";
2042
+ modal.appendChild(title);
2043
+ for (const s of SHORTCUTS) {
2044
+ const row = document.createElement("div");
2045
+ row.className = "remarq-shortcuts-row";
2046
+ const key = document.createElement("kbd");
2047
+ key.className = "remarq-shortcuts-key";
2048
+ key.textContent = s.key;
2049
+ const desc = document.createElement("span");
2050
+ desc.textContent = s.description;
2051
+ row.appendChild(key);
2052
+ row.appendChild(desc);
2053
+ if ("context" in s && s.context) {
2054
+ const badge = document.createElement("span");
2055
+ badge.className = "remarq-shortcuts-context";
2056
+ badge.textContent = s.context;
2057
+ row.appendChild(badge);
2058
+ }
2059
+ modal.appendChild(row);
2060
+ }
2061
+ backdrop.appendChild(modal);
2062
+ container.appendChild(backdrop);
2063
+ modalEl = backdrop;
2064
+ backdrop.addEventListener("click", (e) => {
2065
+ if (e.target === backdrop) hideShortcutsModal();
2066
+ });
2067
+ keyHandler = (e) => {
2068
+ if (e.key === "Escape" || e.key === "?") {
2069
+ hideShortcutsModal();
2070
+ }
2071
+ };
2072
+ document.addEventListener("keydown", keyHandler);
2073
+ }
2074
+ function hideShortcutsModal() {
2075
+ if (modalEl) {
2076
+ modalEl.remove();
2077
+ modalEl = null;
2078
+ }
2079
+ if (keyHandler) {
2080
+ document.removeEventListener("keydown", keyHandler);
2081
+ keyHandler = null;
2082
+ }
2083
+ }
2084
+
1904
2085
  // src/spa.ts
1905
2086
  var RouteObserver = class {
1906
2087
  constructor() {
@@ -1968,10 +2149,10 @@ var WebRemarq = (() => {
1968
2149
  var savedCursor = "";
1969
2150
  var elementCache = /* @__PURE__ */ new Map();
1970
2151
  function describeTarget(el) {
1971
- var _a, _b, _c, _d;
2152
+ var _a3, _b, _c, _d;
1972
2153
  const parts = [];
1973
2154
  if (el.id) parts.push(`#${el.id}`);
1974
- const dataAnnotate = el.getAttribute((_a = options.dataAttribute) != null ? _a : "data-annotate");
2155
+ const dataAnnotate = el.getAttribute((_a3 = options.dataAttribute) != null ? _a3 : "data-annotate");
1975
2156
  const dataTestId = el.getAttribute("data-testid") || el.getAttribute("data-test") || el.getAttribute("data-cy");
1976
2157
  if (dataAnnotate) parts.push(`[${dataAnnotate}]`);
1977
2158
  else if (dataTestId) parts.push(`[${dataTestId}]`);
@@ -2106,8 +2287,9 @@ var WebRemarq = (() => {
2106
2287
  overlay.updateTooltipPosition(e.clientX, e.clientY);
2107
2288
  }
2108
2289
  function handleInspectKeydown(e) {
2109
- var _a, _b;
2110
- const tag = (_a = e.target) == null ? void 0 : _a.tagName;
2290
+ var _a3, _b;
2291
+ if (options.shortcuts === false) return;
2292
+ const tag = (_a3 = e.target) == null ? void 0 : _a3.tagName;
2111
2293
  if (tag === "INPUT" || tag === "TEXTAREA" || ((_b = e.target) == null ? void 0 : _b.isContentEditable)) return;
2112
2294
  if (e.key === "Escape" && inspecting) {
2113
2295
  setInspecting(false);
@@ -2119,13 +2301,20 @@ var WebRemarq = (() => {
2119
2301
  toolbar.setSpacingActive(spacingMode);
2120
2302
  if (!spacingMode) spacingOverlay.hide();
2121
2303
  }
2122
- if (e.key === "i") {
2304
+ if (e.key === "i" && e.altKey) {
2305
+ e.preventDefault();
2123
2306
  setInspecting(!inspecting);
2124
2307
  if (!inspecting) {
2125
2308
  overlay.hide();
2126
2309
  spacingOverlay.hide();
2127
2310
  }
2128
2311
  }
2312
+ if (e.key === "c" && inspecting) {
2313
+ copyToClipboard();
2314
+ }
2315
+ if (e.key === "?") {
2316
+ showShortcutsModal(themeManager.container);
2317
+ }
2129
2318
  }
2130
2319
  function setInspecting(value) {
2131
2320
  if (value && !inspecting) {
@@ -2146,7 +2335,7 @@ var WebRemarq = (() => {
2146
2335
  }
2147
2336
  }
2148
2337
  function handleMarkerClick(annotationId) {
2149
- var _a;
2338
+ var _a3;
2150
2339
  const ann = storage.getAll().find((a) => a.id === annotationId);
2151
2340
  if (!ann) return;
2152
2341
  const el = resolveElement(ann);
@@ -2155,7 +2344,7 @@ var WebRemarq = (() => {
2155
2344
  popup.showDetail(
2156
2345
  {
2157
2346
  tag: ann.fingerprint.tagName,
2158
- text: (_a = ann.fingerprint.textContent) != null ? _a : "",
2347
+ text: (_a3 = ann.fingerprint.textContent) != null ? _a3 : "",
2159
2348
  comment: ann.comment,
2160
2349
  status: ann.status
2161
2350
  },
@@ -2179,6 +2368,21 @@ var WebRemarq = (() => {
2179
2368
  onEdit: (newComment) => {
2180
2369
  storage.update(ann.id, { comment: newComment });
2181
2370
  refreshMarkers();
2371
+ },
2372
+ onCopy: () => {
2373
+ const fp = ann.fingerprint;
2374
+ const lines = [
2375
+ `[${ann.status}] "${ann.comment}"`,
2376
+ `Element: <${fp.tagName}>${fp.textContent ? ` "${fp.textContent}"` : ""}`,
2377
+ `Route: ${ann.route}`,
2378
+ `Viewport: ${ann.viewportBucket}px`
2379
+ ];
2380
+ if (fp.sourceLocation) lines.push(`Source: ${fp.sourceLocation}`);
2381
+ navigator.clipboard.writeText(lines.join("\n")).then(() => {
2382
+ showToast(themeManager.container, "Annotation copied");
2383
+ }).catch(() => {
2384
+ console.warn("[web-remarq] Clipboard write failed");
2385
+ });
2182
2386
  }
2183
2387
  }
2184
2388
  );
@@ -2189,7 +2393,7 @@ var WebRemarq = (() => {
2189
2393
  if (!anns.length) return "";
2190
2394
  const lines = [`## Annotations \u2014 ${route} (${anns.length})`, ""];
2191
2395
  anns.forEach((ann, i) => {
2192
- var _a, _b;
2396
+ var _a3, _b;
2193
2397
  const fp = ann.fingerprint;
2194
2398
  lines.push(`### ${i + 1}. [${ann.status}] "${ann.comment}"`);
2195
2399
  let elDesc = `Element: <${fp.tagName}>`;
@@ -2220,7 +2424,7 @@ var WebRemarq = (() => {
2220
2424
  if (fp.textContent) {
2221
2425
  lines.push(`- \`"${fp.textContent}"\` \u2014 text content in templates`);
2222
2426
  }
2223
- if ((_a = fp.cssModules) == null ? void 0 : _a.length) {
2427
+ if ((_a3 = fp.cssModules) == null ? void 0 : _a3.length) {
2224
2428
  for (const mod of fp.cssModules) {
2225
2429
  lines.push(`- \`.${mod.localName}\` \u2014 in CSS Module file (likely \`${mod.moduleHint}.module.*\`)`);
2226
2430
  lines.push(`- \`styles.${mod.localName}\` \u2014 in component JS/TS`);
@@ -2299,6 +2503,7 @@ var WebRemarq = (() => {
2299
2503
  }
2300
2504
  var WebRemarq = {
2301
2505
  init(opts) {
2506
+ var _a3;
2302
2507
  if (initialized) return;
2303
2508
  options = opts != null ? opts : {};
2304
2509
  try {
@@ -2309,11 +2514,12 @@ var WebRemarq = (() => {
2309
2514
  spacingOverlay = new SpacingOverlay(themeManager.container);
2310
2515
  popup = new Popup(themeManager.container);
2311
2516
  markers = new MarkerManager(themeManager.container, handleMarkerClick);
2517
+ const position = (_a3 = options.position) != null ? _a3 : "bottom-right";
2312
2518
  detachedPanel = new DetachedPanel(themeManager.container, (id) => {
2313
2519
  elementCache.delete(id);
2314
2520
  storage.remove(id);
2315
2521
  refreshMarkers();
2316
- });
2522
+ }, position);
2317
2523
  toolbar = new Toolbar(themeManager.container, {
2318
2524
  onInspect: () => setInspecting(!inspecting),
2319
2525
  onSpacingToggle: () => {
@@ -2326,8 +2532,8 @@ var WebRemarq = (() => {
2326
2532
  onExportMd: exportMarkdown,
2327
2533
  onExportJson: exportJSON,
2328
2534
  onImport: () => {
2329
- var _a;
2330
- const file = (_a = toolbar.getFileInput().files) == null ? void 0 : _a[0];
2535
+ var _a4;
2536
+ const file = (_a4 = toolbar.getFileInput().files) == null ? void 0 : _a4[0];
2331
2537
  if (file) {
2332
2538
  WebRemarq.import(file);
2333
2539
  }
@@ -2338,8 +2544,9 @@ var WebRemarq = (() => {
2338
2544
  refreshMarkers();
2339
2545
  showToast(themeManager.container, "All annotations cleared");
2340
2546
  },
2341
- onThemeToggle: () => themeManager.toggle()
2342
- });
2547
+ onThemeToggle: () => themeManager.toggle(),
2548
+ onHelp: () => showShortcutsModal(themeManager.container)
2549
+ }, position);
2343
2550
  if (storage.isMemoryOnly) {
2344
2551
  toolbar.setMemoryWarning(true);
2345
2552
  }
@@ -2369,6 +2576,7 @@ var WebRemarq = (() => {
2369
2576
  document.body.style.cursor = savedCursor;
2370
2577
  }
2371
2578
  hideToast();
2579
+ hideShortcutsModal();
2372
2580
  destroyViewportListener();
2373
2581
  unsubRoute == null ? void 0 : unsubRoute();
2374
2582
  routeObserver == null ? void 0 : routeObserver.destroy();