web-remarq 0.3.2 → 0.4.2

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.cjs CHANGED
@@ -936,6 +936,12 @@ var CSS = `
936
936
 
937
937
  .remarq-toolbar-btn:disabled { opacity: 0.3; cursor: default; }
938
938
  .remarq-toolbar-btn:disabled:hover { background: transparent; }
939
+
940
+ .remarq-popup-hint {
941
+ font-size: 11px;
942
+ color: var(--remarq-text-secondary);
943
+ margin-top: 4px;
944
+ }
939
945
  `;
940
946
  function injectStyles() {
941
947
  if (document.querySelector(`style[${STYLES_ID}]`)) return;
@@ -1166,15 +1172,13 @@ var Overlay = class {
1166
1172
  this.overlayEl.style.height = `${rect.height}px`;
1167
1173
  this.tooltipEl.textContent = describeElement(target);
1168
1174
  this.tooltipEl.style.display = "block";
1169
- this.tooltipEl.style.top = `${rect.top - 28}px`;
1170
- this.tooltipEl.style.left = `${rect.left}px`;
1175
+ this.positionTooltip(rect.left, rect.top - 28);
1171
1176
  } catch (e) {
1172
1177
  this.hide();
1173
1178
  }
1174
1179
  }
1175
1180
  updateTooltipPosition(x, y) {
1176
- this.tooltipEl.style.left = `${x + 12}px`;
1177
- this.tooltipEl.style.top = `${y - 28}px`;
1181
+ this.positionTooltip(x + 12, y - 28);
1178
1182
  }
1179
1183
  hideHighlight() {
1180
1184
  this.overlayEl.style.display = "none";
@@ -1187,6 +1191,16 @@ var Overlay = class {
1187
1191
  this.overlayEl.remove();
1188
1192
  this.tooltipEl.remove();
1189
1193
  }
1194
+ positionTooltip(left, top) {
1195
+ this.tooltipEl.style.left = "0px";
1196
+ this.tooltipEl.style.top = "0px";
1197
+ const tooltipWidth = this.tooltipEl.offsetWidth;
1198
+ const tooltipHeight = this.tooltipEl.offsetHeight;
1199
+ const maxLeft = window.innerWidth - tooltipWidth - 8;
1200
+ const maxTop = window.innerHeight - tooltipHeight - 8;
1201
+ this.tooltipEl.style.left = `${Math.max(8, Math.min(left, maxLeft))}px`;
1202
+ this.tooltipEl.style.top = `${Math.max(8, Math.min(top, maxTop))}px`;
1203
+ }
1190
1204
  };
1191
1205
  function describeElement(el) {
1192
1206
  const tag = el.tagName.toLowerCase();
@@ -1466,6 +1480,7 @@ var Popup = class {
1466
1480
  this.container = container;
1467
1481
  this.popupEl = null;
1468
1482
  this.keyHandler = null;
1483
+ this.outsideClickHandler = null;
1469
1484
  }
1470
1485
  show(info, position, onSubmit, onCancel) {
1471
1486
  this.hide();
@@ -1478,7 +1493,11 @@ var Popup = class {
1478
1493
  body.className = "remarq-popup-body";
1479
1494
  const textarea = document.createElement("textarea");
1480
1495
  textarea.placeholder = "Add your comment...";
1496
+ const hint = document.createElement("div");
1497
+ hint.className = "remarq-popup-hint";
1498
+ hint.textContent = "Enter to submit \xB7 Shift+Enter for new line";
1481
1499
  body.appendChild(textarea);
1500
+ body.appendChild(hint);
1482
1501
  const actions = document.createElement("div");
1483
1502
  actions.className = "remarq-popup-actions";
1484
1503
  const cancelBtn = document.createElement("button");
@@ -1508,18 +1527,37 @@ var Popup = class {
1508
1527
  textarea.focus();
1509
1528
  });
1510
1529
  this.keyHandler = (e) => {
1511
- if (e.key === "Enter" && (e.metaKey || e.ctrlKey)) {
1530
+ if (e.key === "Escape") {
1531
+ this.hide();
1532
+ onCancel();
1533
+ return;
1534
+ }
1535
+ if (e.key === "Enter" && !e.shiftKey && e.target === textarea) {
1536
+ e.preventDefault();
1512
1537
  const comment = textarea.value.trim();
1513
1538
  if (!comment) return;
1514
1539
  this.hide();
1515
1540
  onSubmit(comment);
1541
+ return;
1516
1542
  }
1517
- if (e.key === "Escape") {
1543
+ if (e.key === "Enter" && (e.metaKey || e.ctrlKey)) {
1544
+ const comment = textarea.value.trim();
1545
+ if (!comment) return;
1518
1546
  this.hide();
1519
- onCancel();
1547
+ onSubmit(comment);
1520
1548
  }
1521
1549
  };
1522
1550
  document.addEventListener("keydown", this.keyHandler);
1551
+ setTimeout(() => {
1552
+ this.outsideClickHandler = (e) => {
1553
+ const target = e.target;
1554
+ if (target && !target.closest(".remarq-popup")) {
1555
+ this.hide();
1556
+ onCancel();
1557
+ }
1558
+ };
1559
+ document.addEventListener("mousedown", this.outsideClickHandler);
1560
+ }, 0);
1523
1561
  }
1524
1562
  showDetail(info, position, callbacks) {
1525
1563
  this.hide();
@@ -1530,7 +1568,15 @@ var Popup = class {
1530
1568
  header.textContent = `<${info.tag}>${info.text ? ` "${info.text}"` : ""} [${info.status}]`;
1531
1569
  const body = document.createElement("div");
1532
1570
  body.className = "remarq-popup-body";
1533
- body.textContent = info.comment;
1571
+ const makeCommentEl = () => {
1572
+ const el = document.createElement("div");
1573
+ el.textContent = info.comment;
1574
+ el.style.cursor = "pointer";
1575
+ el.title = "Click to edit";
1576
+ el.addEventListener("click", () => this.enterEditMode(el, info, callbacks));
1577
+ return el;
1578
+ };
1579
+ body.appendChild(makeCommentEl());
1534
1580
  const actions = document.createElement("div");
1535
1581
  actions.className = "remarq-popup-actions";
1536
1582
  if (info.status === "pending") {
@@ -1543,6 +1589,12 @@ var Popup = class {
1543
1589
  });
1544
1590
  actions.appendChild(resolveBtn);
1545
1591
  }
1592
+ const copyBtn = document.createElement("button");
1593
+ copyBtn.textContent = "Copy";
1594
+ copyBtn.addEventListener("click", () => {
1595
+ callbacks.onCopy();
1596
+ });
1597
+ actions.appendChild(copyBtn);
1546
1598
  const deleteBtn = document.createElement("button");
1547
1599
  deleteBtn.textContent = "Delete";
1548
1600
  deleteBtn.addEventListener("click", () => {
@@ -1572,6 +1624,16 @@ var Popup = class {
1572
1624
  }
1573
1625
  };
1574
1626
  document.addEventListener("keydown", this.keyHandler);
1627
+ setTimeout(() => {
1628
+ this.outsideClickHandler = (e) => {
1629
+ const target = e.target;
1630
+ if (target && !target.closest(".remarq-popup")) {
1631
+ this.hide();
1632
+ callbacks.onClose();
1633
+ }
1634
+ };
1635
+ document.addEventListener("mousedown", this.outsideClickHandler);
1636
+ }, 0);
1575
1637
  }
1576
1638
  hide() {
1577
1639
  if (this.popupEl) {
@@ -1582,10 +1644,68 @@ var Popup = class {
1582
1644
  document.removeEventListener("keydown", this.keyHandler);
1583
1645
  this.keyHandler = null;
1584
1646
  }
1647
+ if (this.outsideClickHandler) {
1648
+ document.removeEventListener("mousedown", this.outsideClickHandler);
1649
+ this.outsideClickHandler = null;
1650
+ }
1585
1651
  }
1586
1652
  destroy() {
1587
1653
  this.hide();
1588
1654
  }
1655
+ enterEditMode(commentEl, info, callbacks) {
1656
+ const textarea = document.createElement("textarea");
1657
+ textarea.value = info.comment;
1658
+ textarea.className = "remarq-popup-edit-textarea";
1659
+ textarea.style.width = "100%";
1660
+ textarea.style.minHeight = "60px";
1661
+ textarea.style.padding = "8px";
1662
+ textarea.style.border = "1px solid var(--remarq-border)";
1663
+ textarea.style.borderRadius = "4px";
1664
+ textarea.style.background = "var(--remarq-bg-secondary)";
1665
+ textarea.style.color = "var(--remarq-text)";
1666
+ textarea.style.fontFamily = "inherit";
1667
+ textarea.style.fontSize = "13px";
1668
+ textarea.style.resize = "vertical";
1669
+ textarea.style.boxSizing = "border-box";
1670
+ commentEl.replaceWith(textarea);
1671
+ textarea.focus();
1672
+ textarea.selectionStart = textarea.value.length;
1673
+ const saveEdit = () => {
1674
+ const newComment = textarea.value.trim();
1675
+ if (newComment && newComment !== info.comment) {
1676
+ info.comment = newComment;
1677
+ callbacks.onEdit(newComment);
1678
+ }
1679
+ const restored = document.createElement("div");
1680
+ restored.textContent = info.comment;
1681
+ restored.style.cursor = "pointer";
1682
+ restored.title = "Click to edit";
1683
+ restored.addEventListener("click", () => this.enterEditMode(restored, info, callbacks));
1684
+ textarea.replaceWith(restored);
1685
+ };
1686
+ textarea.addEventListener("keydown", (e) => {
1687
+ if (e.key === "Enter" && !e.shiftKey) {
1688
+ e.preventDefault();
1689
+ saveEdit();
1690
+ }
1691
+ if (e.key === "Escape") {
1692
+ e.stopPropagation();
1693
+ const restored = document.createElement("div");
1694
+ restored.textContent = info.comment;
1695
+ restored.style.cursor = "pointer";
1696
+ restored.title = "Click to edit";
1697
+ restored.addEventListener("click", () => this.enterEditMode(restored, info, callbacks));
1698
+ textarea.replaceWith(restored);
1699
+ }
1700
+ });
1701
+ textarea.addEventListener("blur", () => {
1702
+ setTimeout(() => {
1703
+ if (textarea.isConnected) {
1704
+ saveEdit();
1705
+ }
1706
+ }, 50);
1707
+ });
1708
+ }
1589
1709
  adjustPosition(popup2, position) {
1590
1710
  const popupHeight = popup2.offsetHeight;
1591
1711
  const viewportBottom = window.scrollY + window.innerHeight;
@@ -1633,12 +1753,14 @@ var MarkerManager = class {
1633
1753
  });
1634
1754
  this.container.appendChild(markerEl);
1635
1755
  this.markers.set(annotation.id, { annotation, target, markerEl });
1756
+ this.applyOutline(target, annotation.status);
1636
1757
  this.updatePosition(annotation.id);
1637
1758
  }
1638
1759
  removeMarker(id) {
1639
1760
  const entry = this.markers.get(id);
1640
1761
  if (entry) {
1641
1762
  entry.markerEl.remove();
1763
+ this.removeOutline(entry.target);
1642
1764
  this.markers.delete(id);
1643
1765
  }
1644
1766
  }
@@ -1652,6 +1774,7 @@ var MarkerManager = class {
1652
1774
  clear() {
1653
1775
  for (const entry of this.markers.values()) {
1654
1776
  entry.markerEl.remove();
1777
+ this.removeOutline(entry.target);
1655
1778
  }
1656
1779
  this.markers.clear();
1657
1780
  this.counter = 0;
@@ -1663,6 +1786,15 @@ var MarkerManager = class {
1663
1786
  }
1664
1787
  this.clear();
1665
1788
  }
1789
+ applyOutline(target, status) {
1790
+ const color = status === "pending" ? "#f97316" : "rgba(34, 197, 94, 0.5)";
1791
+ target.style.outline = `2px solid ${color}`;
1792
+ target.style.outlineOffset = "2px";
1793
+ }
1794
+ removeOutline(target) {
1795
+ target.style.outline = "";
1796
+ target.style.outlineOffset = "";
1797
+ }
1666
1798
  updatePosition(id) {
1667
1799
  const entry = this.markers.get(id);
1668
1800
  if (!entry) return;
@@ -1684,14 +1816,40 @@ var MarkerManager = class {
1684
1816
  }
1685
1817
  };
1686
1818
 
1819
+ // src/ui/toast.ts
1820
+ var currentToast = null;
1821
+ var currentTimer = null;
1822
+ function showToast(container, message, duration = 3e3) {
1823
+ hideToast();
1824
+ const toast = document.createElement("div");
1825
+ toast.className = "remarq-toast";
1826
+ toast.textContent = message;
1827
+ container.appendChild(toast);
1828
+ currentToast = toast;
1829
+ currentTimer = setTimeout(() => {
1830
+ if (currentToast) {
1831
+ currentToast.classList.add("remarq-toast-fade");
1832
+ setTimeout(() => hideToast(), 300);
1833
+ }
1834
+ }, duration);
1835
+ }
1836
+ function hideToast() {
1837
+ if (currentTimer) {
1838
+ clearTimeout(currentTimer);
1839
+ currentTimer = null;
1840
+ }
1841
+ if (currentToast) {
1842
+ currentToast.remove();
1843
+ currentToast = null;
1844
+ }
1845
+ }
1846
+
1687
1847
  // src/ui/detached-panel.ts
1688
1848
  var DetachedPanel = class {
1689
1849
  constructor(container, onDelete) {
1690
1850
  this.container = container;
1691
1851
  this.onDelete = onDelete;
1692
1852
  this.panelEl = null;
1693
- this.toastEl = null;
1694
- this.toastTimer = null;
1695
1853
  }
1696
1854
  update(otherBreakpoint, detached) {
1697
1855
  this.remove();
@@ -1709,7 +1867,7 @@ var DetachedPanel = class {
1709
1867
  }
1710
1868
  destroy() {
1711
1869
  this.remove();
1712
- this.hideToast();
1870
+ hideToast();
1713
1871
  }
1714
1872
  renderSection(panel, title, annotations, type) {
1715
1873
  const header = document.createElement("div");
@@ -1738,7 +1896,7 @@ var DetachedPanel = class {
1738
1896
  if (type === "other") {
1739
1897
  item.style.cursor = "pointer";
1740
1898
  item.addEventListener("click", () => {
1741
- this.showToast(`Annotation created at ${ann.viewportBucket}px width. Resize viewport to view.`);
1899
+ showToast(this.container, `Annotation created at ${ann.viewportBucket}px width. Resize viewport to view.`);
1742
1900
  });
1743
1901
  } else {
1744
1902
  const deleteBtn = document.createElement("button");
@@ -1753,30 +1911,6 @@ var DetachedPanel = class {
1753
1911
  panel.appendChild(item);
1754
1912
  }
1755
1913
  }
1756
- showToast(message) {
1757
- this.hideToast();
1758
- const toast = document.createElement("div");
1759
- toast.className = "remarq-toast";
1760
- toast.textContent = message;
1761
- this.container.appendChild(toast);
1762
- this.toastEl = toast;
1763
- this.toastTimer = setTimeout(() => {
1764
- if (this.toastEl) {
1765
- this.toastEl.classList.add("remarq-toast-fade");
1766
- setTimeout(() => this.hideToast(), 300);
1767
- }
1768
- }, 3e3);
1769
- }
1770
- hideToast() {
1771
- if (this.toastTimer) {
1772
- clearTimeout(this.toastTimer);
1773
- this.toastTimer = null;
1774
- }
1775
- if (this.toastEl) {
1776
- this.toastEl.remove();
1777
- this.toastEl = null;
1778
- }
1779
- }
1780
1914
  remove() {
1781
1915
  if (this.panelEl) {
1782
1916
  this.panelEl.remove();
@@ -1849,6 +1983,7 @@ var spacingOverlay;
1849
1983
  var mutationObserver = null;
1850
1984
  var unsubRoute = null;
1851
1985
  var refreshScheduled = false;
1986
+ var savedCursor = "";
1852
1987
  var elementCache = /* @__PURE__ */ new Map();
1853
1988
  function describeTarget(el) {
1854
1989
  var _a, _b, _c, _d;
@@ -1969,6 +2104,7 @@ function handleInspectClick(e) {
1969
2104
  cacheElement(ann.id, target);
1970
2105
  storage.add(ann);
1971
2106
  refreshMarkers();
2107
+ showToast(themeManager.container, "Annotation added");
1972
2108
  },
1973
2109
  () => {
1974
2110
  }
@@ -2001,8 +2137,22 @@ function handleInspectKeydown(e) {
2001
2137
  toolbar.setSpacingActive(spacingMode);
2002
2138
  if (!spacingMode) spacingOverlay.hide();
2003
2139
  }
2140
+ if (e.key === "i") {
2141
+ setInspecting(!inspecting);
2142
+ if (!inspecting) {
2143
+ overlay.hide();
2144
+ spacingOverlay.hide();
2145
+ }
2146
+ }
2004
2147
  }
2005
2148
  function setInspecting(value) {
2149
+ if (value && !inspecting) {
2150
+ savedCursor = document.body.style.cursor;
2151
+ document.body.style.cursor = "crosshair";
2152
+ }
2153
+ if (!value && inspecting) {
2154
+ document.body.style.cursor = savedCursor;
2155
+ }
2006
2156
  inspecting = value;
2007
2157
  toolbar.setInspectActive(value);
2008
2158
  toolbar.setSpacingEnabled(value);
@@ -2043,6 +2193,25 @@ function handleMarkerClick(annotationId) {
2043
2193
  refreshMarkers();
2044
2194
  },
2045
2195
  onClose: () => {
2196
+ },
2197
+ onEdit: (newComment) => {
2198
+ storage.update(ann.id, { comment: newComment });
2199
+ refreshMarkers();
2200
+ },
2201
+ onCopy: () => {
2202
+ const fp = ann.fingerprint;
2203
+ const lines = [
2204
+ `[${ann.status}] "${ann.comment}"`,
2205
+ `Element: <${fp.tagName}>${fp.textContent ? ` "${fp.textContent}"` : ""}`,
2206
+ `Route: ${ann.route}`,
2207
+ `Viewport: ${ann.viewportBucket}px`
2208
+ ];
2209
+ if (fp.sourceLocation) lines.push(`Source: ${fp.sourceLocation}`);
2210
+ navigator.clipboard.writeText(lines.join("\n")).then(() => {
2211
+ showToast(themeManager.container, "Annotation copied");
2212
+ }).catch(() => {
2213
+ console.warn("[web-remarq] Clipboard write failed");
2214
+ });
2046
2215
  }
2047
2216
  }
2048
2217
  );
@@ -2114,16 +2283,20 @@ function exportMarkdown() {
2114
2283
  const md = generateMarkdown();
2115
2284
  if (!md) return;
2116
2285
  downloadFile(md, `remarq-annotations-${Date.now()}.md`, "text/markdown");
2286
+ showToast(themeManager.container, "Exported as Markdown");
2117
2287
  }
2118
2288
  function exportJSON() {
2119
2289
  const data = storage.exportJSON();
2120
2290
  const json = JSON.stringify(data, null, 2);
2121
2291
  downloadFile(json, `remarq-annotations-${Date.now()}.json`, "application/json");
2292
+ showToast(themeManager.container, "Exported as JSON");
2122
2293
  }
2123
2294
  function copyToClipboard() {
2124
2295
  const md = generateMarkdown();
2125
2296
  if (!md) return;
2126
- navigator.clipboard.writeText(md).catch(() => {
2297
+ navigator.clipboard.writeText(md).then(() => {
2298
+ showToast(themeManager.container, "Copied to clipboard");
2299
+ }).catch(() => {
2127
2300
  console.warn("[web-remarq] Clipboard write failed");
2128
2301
  });
2129
2302
  }
@@ -2196,6 +2369,7 @@ var WebRemarq = {
2196
2369
  elementCache.clear();
2197
2370
  storage.clearAll();
2198
2371
  refreshMarkers();
2372
+ showToast(themeManager.container, "All annotations cleared");
2199
2373
  },
2200
2374
  onThemeToggle: () => themeManager.toggle()
2201
2375
  });
@@ -2224,6 +2398,10 @@ var WebRemarq = {
2224
2398
  document.removeEventListener("keydown", handleInspectKeydown);
2225
2399
  mutationObserver == null ? void 0 : mutationObserver.disconnect();
2226
2400
  mutationObserver = null;
2401
+ if (inspecting) {
2402
+ document.body.style.cursor = savedCursor;
2403
+ }
2404
+ hideToast();
2227
2405
  destroyViewportListener();
2228
2406
  unsubRoute == null ? void 0 : unsubRoute();
2229
2407
  routeObserver == null ? void 0 : routeObserver.destroy();