web-remarq 0.3.1 → 0.4.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/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") {
@@ -1572,6 +1618,16 @@ var Popup = class {
1572
1618
  }
1573
1619
  };
1574
1620
  document.addEventListener("keydown", this.keyHandler);
1621
+ setTimeout(() => {
1622
+ this.outsideClickHandler = (e) => {
1623
+ const target = e.target;
1624
+ if (target && !target.closest(".remarq-popup")) {
1625
+ this.hide();
1626
+ callbacks.onClose();
1627
+ }
1628
+ };
1629
+ document.addEventListener("mousedown", this.outsideClickHandler);
1630
+ }, 0);
1575
1631
  }
1576
1632
  hide() {
1577
1633
  if (this.popupEl) {
@@ -1582,10 +1638,68 @@ var Popup = class {
1582
1638
  document.removeEventListener("keydown", this.keyHandler);
1583
1639
  this.keyHandler = null;
1584
1640
  }
1641
+ if (this.outsideClickHandler) {
1642
+ document.removeEventListener("mousedown", this.outsideClickHandler);
1643
+ this.outsideClickHandler = null;
1644
+ }
1585
1645
  }
1586
1646
  destroy() {
1587
1647
  this.hide();
1588
1648
  }
1649
+ enterEditMode(commentEl, info, callbacks) {
1650
+ const textarea = document.createElement("textarea");
1651
+ textarea.value = info.comment;
1652
+ textarea.className = "remarq-popup-edit-textarea";
1653
+ textarea.style.width = "100%";
1654
+ textarea.style.minHeight = "60px";
1655
+ textarea.style.padding = "8px";
1656
+ textarea.style.border = "1px solid var(--remarq-border)";
1657
+ textarea.style.borderRadius = "4px";
1658
+ textarea.style.background = "var(--remarq-bg-secondary)";
1659
+ textarea.style.color = "var(--remarq-text)";
1660
+ textarea.style.fontFamily = "inherit";
1661
+ textarea.style.fontSize = "13px";
1662
+ textarea.style.resize = "vertical";
1663
+ textarea.style.boxSizing = "border-box";
1664
+ commentEl.replaceWith(textarea);
1665
+ textarea.focus();
1666
+ textarea.selectionStart = textarea.value.length;
1667
+ const saveEdit = () => {
1668
+ const newComment = textarea.value.trim();
1669
+ if (newComment && newComment !== info.comment) {
1670
+ info.comment = newComment;
1671
+ callbacks.onEdit(newComment);
1672
+ }
1673
+ const restored = document.createElement("div");
1674
+ restored.textContent = info.comment;
1675
+ restored.style.cursor = "pointer";
1676
+ restored.title = "Click to edit";
1677
+ restored.addEventListener("click", () => this.enterEditMode(restored, info, callbacks));
1678
+ textarea.replaceWith(restored);
1679
+ };
1680
+ textarea.addEventListener("keydown", (e) => {
1681
+ if (e.key === "Enter" && !e.shiftKey) {
1682
+ e.preventDefault();
1683
+ saveEdit();
1684
+ }
1685
+ if (e.key === "Escape") {
1686
+ e.stopPropagation();
1687
+ const restored = document.createElement("div");
1688
+ restored.textContent = info.comment;
1689
+ restored.style.cursor = "pointer";
1690
+ restored.title = "Click to edit";
1691
+ restored.addEventListener("click", () => this.enterEditMode(restored, info, callbacks));
1692
+ textarea.replaceWith(restored);
1693
+ }
1694
+ });
1695
+ textarea.addEventListener("blur", () => {
1696
+ setTimeout(() => {
1697
+ if (textarea.isConnected) {
1698
+ saveEdit();
1699
+ }
1700
+ }, 50);
1701
+ });
1702
+ }
1589
1703
  adjustPosition(popup2, position) {
1590
1704
  const popupHeight = popup2.offsetHeight;
1591
1705
  const viewportBottom = window.scrollY + window.innerHeight;
@@ -1684,14 +1798,40 @@ var MarkerManager = class {
1684
1798
  }
1685
1799
  };
1686
1800
 
1801
+ // src/ui/toast.ts
1802
+ var currentToast = null;
1803
+ var currentTimer = null;
1804
+ function showToast(container, message, duration = 3e3) {
1805
+ hideToast();
1806
+ const toast = document.createElement("div");
1807
+ toast.className = "remarq-toast";
1808
+ toast.textContent = message;
1809
+ container.appendChild(toast);
1810
+ currentToast = toast;
1811
+ currentTimer = setTimeout(() => {
1812
+ if (currentToast) {
1813
+ currentToast.classList.add("remarq-toast-fade");
1814
+ setTimeout(() => hideToast(), 300);
1815
+ }
1816
+ }, duration);
1817
+ }
1818
+ function hideToast() {
1819
+ if (currentTimer) {
1820
+ clearTimeout(currentTimer);
1821
+ currentTimer = null;
1822
+ }
1823
+ if (currentToast) {
1824
+ currentToast.remove();
1825
+ currentToast = null;
1826
+ }
1827
+ }
1828
+
1687
1829
  // src/ui/detached-panel.ts
1688
1830
  var DetachedPanel = class {
1689
1831
  constructor(container, onDelete) {
1690
1832
  this.container = container;
1691
1833
  this.onDelete = onDelete;
1692
1834
  this.panelEl = null;
1693
- this.toastEl = null;
1694
- this.toastTimer = null;
1695
1835
  }
1696
1836
  update(otherBreakpoint, detached) {
1697
1837
  this.remove();
@@ -1709,7 +1849,7 @@ var DetachedPanel = class {
1709
1849
  }
1710
1850
  destroy() {
1711
1851
  this.remove();
1712
- this.hideToast();
1852
+ hideToast();
1713
1853
  }
1714
1854
  renderSection(panel, title, annotations, type) {
1715
1855
  const header = document.createElement("div");
@@ -1738,7 +1878,7 @@ var DetachedPanel = class {
1738
1878
  if (type === "other") {
1739
1879
  item.style.cursor = "pointer";
1740
1880
  item.addEventListener("click", () => {
1741
- this.showToast(`Annotation created at ${ann.viewportBucket}px width. Resize viewport to view.`);
1881
+ showToast(this.container, `Annotation created at ${ann.viewportBucket}px width. Resize viewport to view.`);
1742
1882
  });
1743
1883
  } else {
1744
1884
  const deleteBtn = document.createElement("button");
@@ -1753,30 +1893,6 @@ var DetachedPanel = class {
1753
1893
  panel.appendChild(item);
1754
1894
  }
1755
1895
  }
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
1896
  remove() {
1781
1897
  if (this.panelEl) {
1782
1898
  this.panelEl.remove();
@@ -1849,6 +1965,7 @@ var spacingOverlay;
1849
1965
  var mutationObserver = null;
1850
1966
  var unsubRoute = null;
1851
1967
  var refreshScheduled = false;
1968
+ var savedCursor = "";
1852
1969
  var elementCache = /* @__PURE__ */ new Map();
1853
1970
  function describeTarget(el) {
1854
1971
  var _a, _b, _c, _d;
@@ -1969,6 +2086,7 @@ function handleInspectClick(e) {
1969
2086
  cacheElement(ann.id, target);
1970
2087
  storage.add(ann);
1971
2088
  refreshMarkers();
2089
+ showToast(themeManager.container, "Annotation added");
1972
2090
  },
1973
2091
  () => {
1974
2092
  }
@@ -2001,8 +2119,22 @@ function handleInspectKeydown(e) {
2001
2119
  toolbar.setSpacingActive(spacingMode);
2002
2120
  if (!spacingMode) spacingOverlay.hide();
2003
2121
  }
2122
+ if (e.key === "i") {
2123
+ setInspecting(!inspecting);
2124
+ if (!inspecting) {
2125
+ overlay.hide();
2126
+ spacingOverlay.hide();
2127
+ }
2128
+ }
2004
2129
  }
2005
2130
  function setInspecting(value) {
2131
+ if (value && !inspecting) {
2132
+ savedCursor = document.body.style.cursor;
2133
+ document.body.style.cursor = "crosshair";
2134
+ }
2135
+ if (!value && inspecting) {
2136
+ document.body.style.cursor = savedCursor;
2137
+ }
2006
2138
  inspecting = value;
2007
2139
  toolbar.setInspectActive(value);
2008
2140
  toolbar.setSpacingEnabled(value);
@@ -2043,6 +2175,10 @@ function handleMarkerClick(annotationId) {
2043
2175
  refreshMarkers();
2044
2176
  },
2045
2177
  onClose: () => {
2178
+ },
2179
+ onEdit: (newComment) => {
2180
+ storage.update(ann.id, { comment: newComment });
2181
+ refreshMarkers();
2046
2182
  }
2047
2183
  }
2048
2184
  );
@@ -2061,6 +2197,13 @@ function generateMarkdown() {
2061
2197
  lines.push(elDesc);
2062
2198
  lines.push(`Viewport: ${ann.viewportBucket}px`);
2063
2199
  lines.push("");
2200
+ if (fp.sourceLocation) {
2201
+ lines.push(`Source: \`${fp.sourceLocation}\`${fp.componentName ? ` (${fp.componentName})` : ""}`);
2202
+ lines.push("");
2203
+ } else if (fp.detectedSource) {
2204
+ lines.push(`Source (detected): \`${fp.detectedSource}\`${fp.detectedComponent ? ` (${fp.detectedComponent})` : ""}`);
2205
+ lines.push("");
2206
+ }
2064
2207
  lines.push("Search hints:");
2065
2208
  if (fp.dataAnnotate) {
2066
2209
  lines.push(`- \`data-annotate="${fp.dataAnnotate}"\` \u2014 in template files`);
@@ -2107,16 +2250,20 @@ function exportMarkdown() {
2107
2250
  const md = generateMarkdown();
2108
2251
  if (!md) return;
2109
2252
  downloadFile(md, `remarq-annotations-${Date.now()}.md`, "text/markdown");
2253
+ showToast(themeManager.container, "Exported as Markdown");
2110
2254
  }
2111
2255
  function exportJSON() {
2112
2256
  const data = storage.exportJSON();
2113
2257
  const json = JSON.stringify(data, null, 2);
2114
2258
  downloadFile(json, `remarq-annotations-${Date.now()}.json`, "application/json");
2259
+ showToast(themeManager.container, "Exported as JSON");
2115
2260
  }
2116
2261
  function copyToClipboard() {
2117
2262
  const md = generateMarkdown();
2118
2263
  if (!md) return;
2119
- navigator.clipboard.writeText(md).catch(() => {
2264
+ navigator.clipboard.writeText(md).then(() => {
2265
+ showToast(themeManager.container, "Copied to clipboard");
2266
+ }).catch(() => {
2120
2267
  console.warn("[web-remarq] Clipboard write failed");
2121
2268
  });
2122
2269
  }
@@ -2189,6 +2336,7 @@ var WebRemarq = {
2189
2336
  elementCache.clear();
2190
2337
  storage.clearAll();
2191
2338
  refreshMarkers();
2339
+ showToast(themeManager.container, "All annotations cleared");
2192
2340
  },
2193
2341
  onThemeToggle: () => themeManager.toggle()
2194
2342
  });
@@ -2217,6 +2365,10 @@ var WebRemarq = {
2217
2365
  document.removeEventListener("keydown", handleInspectKeydown);
2218
2366
  mutationObserver == null ? void 0 : mutationObserver.disconnect();
2219
2367
  mutationObserver = null;
2368
+ if (inspecting) {
2369
+ document.body.style.cursor = savedCursor;
2370
+ }
2371
+ hideToast();
2220
2372
  destroyViewportListener();
2221
2373
  unsubRoute == null ? void 0 : unsubRoute();
2222
2374
  routeObserver == null ? void 0 : routeObserver.destroy();