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.js CHANGED
@@ -913,6 +913,12 @@ var CSS = `
913
913
 
914
914
  .remarq-toolbar-btn:disabled { opacity: 0.3; cursor: default; }
915
915
  .remarq-toolbar-btn:disabled:hover { background: transparent; }
916
+
917
+ .remarq-popup-hint {
918
+ font-size: 11px;
919
+ color: var(--remarq-text-secondary);
920
+ margin-top: 4px;
921
+ }
916
922
  `;
917
923
  function injectStyles() {
918
924
  if (document.querySelector(`style[${STYLES_ID}]`)) return;
@@ -1143,15 +1149,13 @@ var Overlay = class {
1143
1149
  this.overlayEl.style.height = `${rect.height}px`;
1144
1150
  this.tooltipEl.textContent = describeElement(target);
1145
1151
  this.tooltipEl.style.display = "block";
1146
- this.tooltipEl.style.top = `${rect.top - 28}px`;
1147
- this.tooltipEl.style.left = `${rect.left}px`;
1152
+ this.positionTooltip(rect.left, rect.top - 28);
1148
1153
  } catch (e) {
1149
1154
  this.hide();
1150
1155
  }
1151
1156
  }
1152
1157
  updateTooltipPosition(x, y) {
1153
- this.tooltipEl.style.left = `${x + 12}px`;
1154
- this.tooltipEl.style.top = `${y - 28}px`;
1158
+ this.positionTooltip(x + 12, y - 28);
1155
1159
  }
1156
1160
  hideHighlight() {
1157
1161
  this.overlayEl.style.display = "none";
@@ -1164,6 +1168,16 @@ var Overlay = class {
1164
1168
  this.overlayEl.remove();
1165
1169
  this.tooltipEl.remove();
1166
1170
  }
1171
+ positionTooltip(left, top) {
1172
+ this.tooltipEl.style.left = "0px";
1173
+ this.tooltipEl.style.top = "0px";
1174
+ const tooltipWidth = this.tooltipEl.offsetWidth;
1175
+ const tooltipHeight = this.tooltipEl.offsetHeight;
1176
+ const maxLeft = window.innerWidth - tooltipWidth - 8;
1177
+ const maxTop = window.innerHeight - tooltipHeight - 8;
1178
+ this.tooltipEl.style.left = `${Math.max(8, Math.min(left, maxLeft))}px`;
1179
+ this.tooltipEl.style.top = `${Math.max(8, Math.min(top, maxTop))}px`;
1180
+ }
1167
1181
  };
1168
1182
  function describeElement(el) {
1169
1183
  const tag = el.tagName.toLowerCase();
@@ -1443,6 +1457,7 @@ var Popup = class {
1443
1457
  this.container = container;
1444
1458
  this.popupEl = null;
1445
1459
  this.keyHandler = null;
1460
+ this.outsideClickHandler = null;
1446
1461
  }
1447
1462
  show(info, position, onSubmit, onCancel) {
1448
1463
  this.hide();
@@ -1455,7 +1470,11 @@ var Popup = class {
1455
1470
  body.className = "remarq-popup-body";
1456
1471
  const textarea = document.createElement("textarea");
1457
1472
  textarea.placeholder = "Add your comment...";
1473
+ const hint = document.createElement("div");
1474
+ hint.className = "remarq-popup-hint";
1475
+ hint.textContent = "Enter to submit \xB7 Shift+Enter for new line";
1458
1476
  body.appendChild(textarea);
1477
+ body.appendChild(hint);
1459
1478
  const actions = document.createElement("div");
1460
1479
  actions.className = "remarq-popup-actions";
1461
1480
  const cancelBtn = document.createElement("button");
@@ -1485,18 +1504,37 @@ var Popup = class {
1485
1504
  textarea.focus();
1486
1505
  });
1487
1506
  this.keyHandler = (e) => {
1488
- if (e.key === "Enter" && (e.metaKey || e.ctrlKey)) {
1507
+ if (e.key === "Escape") {
1508
+ this.hide();
1509
+ onCancel();
1510
+ return;
1511
+ }
1512
+ if (e.key === "Enter" && !e.shiftKey && e.target === textarea) {
1513
+ e.preventDefault();
1489
1514
  const comment = textarea.value.trim();
1490
1515
  if (!comment) return;
1491
1516
  this.hide();
1492
1517
  onSubmit(comment);
1518
+ return;
1493
1519
  }
1494
- if (e.key === "Escape") {
1520
+ if (e.key === "Enter" && (e.metaKey || e.ctrlKey)) {
1521
+ const comment = textarea.value.trim();
1522
+ if (!comment) return;
1495
1523
  this.hide();
1496
- onCancel();
1524
+ onSubmit(comment);
1497
1525
  }
1498
1526
  };
1499
1527
  document.addEventListener("keydown", this.keyHandler);
1528
+ setTimeout(() => {
1529
+ this.outsideClickHandler = (e) => {
1530
+ const target = e.target;
1531
+ if (target && !target.closest(".remarq-popup")) {
1532
+ this.hide();
1533
+ onCancel();
1534
+ }
1535
+ };
1536
+ document.addEventListener("mousedown", this.outsideClickHandler);
1537
+ }, 0);
1500
1538
  }
1501
1539
  showDetail(info, position, callbacks) {
1502
1540
  this.hide();
@@ -1507,7 +1545,15 @@ var Popup = class {
1507
1545
  header.textContent = `<${info.tag}>${info.text ? ` "${info.text}"` : ""} [${info.status}]`;
1508
1546
  const body = document.createElement("div");
1509
1547
  body.className = "remarq-popup-body";
1510
- body.textContent = info.comment;
1548
+ const makeCommentEl = () => {
1549
+ const el = document.createElement("div");
1550
+ el.textContent = info.comment;
1551
+ el.style.cursor = "pointer";
1552
+ el.title = "Click to edit";
1553
+ el.addEventListener("click", () => this.enterEditMode(el, info, callbacks));
1554
+ return el;
1555
+ };
1556
+ body.appendChild(makeCommentEl());
1511
1557
  const actions = document.createElement("div");
1512
1558
  actions.className = "remarq-popup-actions";
1513
1559
  if (info.status === "pending") {
@@ -1549,6 +1595,16 @@ var Popup = class {
1549
1595
  }
1550
1596
  };
1551
1597
  document.addEventListener("keydown", this.keyHandler);
1598
+ setTimeout(() => {
1599
+ this.outsideClickHandler = (e) => {
1600
+ const target = e.target;
1601
+ if (target && !target.closest(".remarq-popup")) {
1602
+ this.hide();
1603
+ callbacks.onClose();
1604
+ }
1605
+ };
1606
+ document.addEventListener("mousedown", this.outsideClickHandler);
1607
+ }, 0);
1552
1608
  }
1553
1609
  hide() {
1554
1610
  if (this.popupEl) {
@@ -1559,10 +1615,68 @@ var Popup = class {
1559
1615
  document.removeEventListener("keydown", this.keyHandler);
1560
1616
  this.keyHandler = null;
1561
1617
  }
1618
+ if (this.outsideClickHandler) {
1619
+ document.removeEventListener("mousedown", this.outsideClickHandler);
1620
+ this.outsideClickHandler = null;
1621
+ }
1562
1622
  }
1563
1623
  destroy() {
1564
1624
  this.hide();
1565
1625
  }
1626
+ enterEditMode(commentEl, info, callbacks) {
1627
+ const textarea = document.createElement("textarea");
1628
+ textarea.value = info.comment;
1629
+ textarea.className = "remarq-popup-edit-textarea";
1630
+ textarea.style.width = "100%";
1631
+ textarea.style.minHeight = "60px";
1632
+ textarea.style.padding = "8px";
1633
+ textarea.style.border = "1px solid var(--remarq-border)";
1634
+ textarea.style.borderRadius = "4px";
1635
+ textarea.style.background = "var(--remarq-bg-secondary)";
1636
+ textarea.style.color = "var(--remarq-text)";
1637
+ textarea.style.fontFamily = "inherit";
1638
+ textarea.style.fontSize = "13px";
1639
+ textarea.style.resize = "vertical";
1640
+ textarea.style.boxSizing = "border-box";
1641
+ commentEl.replaceWith(textarea);
1642
+ textarea.focus();
1643
+ textarea.selectionStart = textarea.value.length;
1644
+ const saveEdit = () => {
1645
+ const newComment = textarea.value.trim();
1646
+ if (newComment && newComment !== info.comment) {
1647
+ info.comment = newComment;
1648
+ callbacks.onEdit(newComment);
1649
+ }
1650
+ const restored = document.createElement("div");
1651
+ restored.textContent = info.comment;
1652
+ restored.style.cursor = "pointer";
1653
+ restored.title = "Click to edit";
1654
+ restored.addEventListener("click", () => this.enterEditMode(restored, info, callbacks));
1655
+ textarea.replaceWith(restored);
1656
+ };
1657
+ textarea.addEventListener("keydown", (e) => {
1658
+ if (e.key === "Enter" && !e.shiftKey) {
1659
+ e.preventDefault();
1660
+ saveEdit();
1661
+ }
1662
+ if (e.key === "Escape") {
1663
+ e.stopPropagation();
1664
+ const restored = document.createElement("div");
1665
+ restored.textContent = info.comment;
1666
+ restored.style.cursor = "pointer";
1667
+ restored.title = "Click to edit";
1668
+ restored.addEventListener("click", () => this.enterEditMode(restored, info, callbacks));
1669
+ textarea.replaceWith(restored);
1670
+ }
1671
+ });
1672
+ textarea.addEventListener("blur", () => {
1673
+ setTimeout(() => {
1674
+ if (textarea.isConnected) {
1675
+ saveEdit();
1676
+ }
1677
+ }, 50);
1678
+ });
1679
+ }
1566
1680
  adjustPosition(popup2, position) {
1567
1681
  const popupHeight = popup2.offsetHeight;
1568
1682
  const viewportBottom = window.scrollY + window.innerHeight;
@@ -1661,14 +1775,40 @@ var MarkerManager = class {
1661
1775
  }
1662
1776
  };
1663
1777
 
1778
+ // src/ui/toast.ts
1779
+ var currentToast = null;
1780
+ var currentTimer = null;
1781
+ function showToast(container, message, duration = 3e3) {
1782
+ hideToast();
1783
+ const toast = document.createElement("div");
1784
+ toast.className = "remarq-toast";
1785
+ toast.textContent = message;
1786
+ container.appendChild(toast);
1787
+ currentToast = toast;
1788
+ currentTimer = setTimeout(() => {
1789
+ if (currentToast) {
1790
+ currentToast.classList.add("remarq-toast-fade");
1791
+ setTimeout(() => hideToast(), 300);
1792
+ }
1793
+ }, duration);
1794
+ }
1795
+ function hideToast() {
1796
+ if (currentTimer) {
1797
+ clearTimeout(currentTimer);
1798
+ currentTimer = null;
1799
+ }
1800
+ if (currentToast) {
1801
+ currentToast.remove();
1802
+ currentToast = null;
1803
+ }
1804
+ }
1805
+
1664
1806
  // src/ui/detached-panel.ts
1665
1807
  var DetachedPanel = class {
1666
1808
  constructor(container, onDelete) {
1667
1809
  this.container = container;
1668
1810
  this.onDelete = onDelete;
1669
1811
  this.panelEl = null;
1670
- this.toastEl = null;
1671
- this.toastTimer = null;
1672
1812
  }
1673
1813
  update(otherBreakpoint, detached) {
1674
1814
  this.remove();
@@ -1686,7 +1826,7 @@ var DetachedPanel = class {
1686
1826
  }
1687
1827
  destroy() {
1688
1828
  this.remove();
1689
- this.hideToast();
1829
+ hideToast();
1690
1830
  }
1691
1831
  renderSection(panel, title, annotations, type) {
1692
1832
  const header = document.createElement("div");
@@ -1715,7 +1855,7 @@ var DetachedPanel = class {
1715
1855
  if (type === "other") {
1716
1856
  item.style.cursor = "pointer";
1717
1857
  item.addEventListener("click", () => {
1718
- this.showToast(`Annotation created at ${ann.viewportBucket}px width. Resize viewport to view.`);
1858
+ showToast(this.container, `Annotation created at ${ann.viewportBucket}px width. Resize viewport to view.`);
1719
1859
  });
1720
1860
  } else {
1721
1861
  const deleteBtn = document.createElement("button");
@@ -1730,30 +1870,6 @@ var DetachedPanel = class {
1730
1870
  panel.appendChild(item);
1731
1871
  }
1732
1872
  }
1733
- showToast(message) {
1734
- this.hideToast();
1735
- const toast = document.createElement("div");
1736
- toast.className = "remarq-toast";
1737
- toast.textContent = message;
1738
- this.container.appendChild(toast);
1739
- this.toastEl = toast;
1740
- this.toastTimer = setTimeout(() => {
1741
- if (this.toastEl) {
1742
- this.toastEl.classList.add("remarq-toast-fade");
1743
- setTimeout(() => this.hideToast(), 300);
1744
- }
1745
- }, 3e3);
1746
- }
1747
- hideToast() {
1748
- if (this.toastTimer) {
1749
- clearTimeout(this.toastTimer);
1750
- this.toastTimer = null;
1751
- }
1752
- if (this.toastEl) {
1753
- this.toastEl.remove();
1754
- this.toastEl = null;
1755
- }
1756
- }
1757
1873
  remove() {
1758
1874
  if (this.panelEl) {
1759
1875
  this.panelEl.remove();
@@ -1826,6 +1942,7 @@ var spacingOverlay;
1826
1942
  var mutationObserver = null;
1827
1943
  var unsubRoute = null;
1828
1944
  var refreshScheduled = false;
1945
+ var savedCursor = "";
1829
1946
  var elementCache = /* @__PURE__ */ new Map();
1830
1947
  function describeTarget(el) {
1831
1948
  var _a, _b, _c, _d;
@@ -1946,6 +2063,7 @@ function handleInspectClick(e) {
1946
2063
  cacheElement(ann.id, target);
1947
2064
  storage.add(ann);
1948
2065
  refreshMarkers();
2066
+ showToast(themeManager.container, "Annotation added");
1949
2067
  },
1950
2068
  () => {
1951
2069
  }
@@ -1978,8 +2096,22 @@ function handleInspectKeydown(e) {
1978
2096
  toolbar.setSpacingActive(spacingMode);
1979
2097
  if (!spacingMode) spacingOverlay.hide();
1980
2098
  }
2099
+ if (e.key === "i") {
2100
+ setInspecting(!inspecting);
2101
+ if (!inspecting) {
2102
+ overlay.hide();
2103
+ spacingOverlay.hide();
2104
+ }
2105
+ }
1981
2106
  }
1982
2107
  function setInspecting(value) {
2108
+ if (value && !inspecting) {
2109
+ savedCursor = document.body.style.cursor;
2110
+ document.body.style.cursor = "crosshair";
2111
+ }
2112
+ if (!value && inspecting) {
2113
+ document.body.style.cursor = savedCursor;
2114
+ }
1983
2115
  inspecting = value;
1984
2116
  toolbar.setInspectActive(value);
1985
2117
  toolbar.setSpacingEnabled(value);
@@ -2020,6 +2152,10 @@ function handleMarkerClick(annotationId) {
2020
2152
  refreshMarkers();
2021
2153
  },
2022
2154
  onClose: () => {
2155
+ },
2156
+ onEdit: (newComment) => {
2157
+ storage.update(ann.id, { comment: newComment });
2158
+ refreshMarkers();
2023
2159
  }
2024
2160
  }
2025
2161
  );
@@ -2038,6 +2174,13 @@ function generateMarkdown() {
2038
2174
  lines.push(elDesc);
2039
2175
  lines.push(`Viewport: ${ann.viewportBucket}px`);
2040
2176
  lines.push("");
2177
+ if (fp.sourceLocation) {
2178
+ lines.push(`Source: \`${fp.sourceLocation}\`${fp.componentName ? ` (${fp.componentName})` : ""}`);
2179
+ lines.push("");
2180
+ } else if (fp.detectedSource) {
2181
+ lines.push(`Source (detected): \`${fp.detectedSource}\`${fp.detectedComponent ? ` (${fp.detectedComponent})` : ""}`);
2182
+ lines.push("");
2183
+ }
2041
2184
  lines.push("Search hints:");
2042
2185
  if (fp.dataAnnotate) {
2043
2186
  lines.push(`- \`data-annotate="${fp.dataAnnotate}"\` \u2014 in template files`);
@@ -2084,16 +2227,20 @@ function exportMarkdown() {
2084
2227
  const md = generateMarkdown();
2085
2228
  if (!md) return;
2086
2229
  downloadFile(md, `remarq-annotations-${Date.now()}.md`, "text/markdown");
2230
+ showToast(themeManager.container, "Exported as Markdown");
2087
2231
  }
2088
2232
  function exportJSON() {
2089
2233
  const data = storage.exportJSON();
2090
2234
  const json = JSON.stringify(data, null, 2);
2091
2235
  downloadFile(json, `remarq-annotations-${Date.now()}.json`, "application/json");
2236
+ showToast(themeManager.container, "Exported as JSON");
2092
2237
  }
2093
2238
  function copyToClipboard() {
2094
2239
  const md = generateMarkdown();
2095
2240
  if (!md) return;
2096
- navigator.clipboard.writeText(md).catch(() => {
2241
+ navigator.clipboard.writeText(md).then(() => {
2242
+ showToast(themeManager.container, "Copied to clipboard");
2243
+ }).catch(() => {
2097
2244
  console.warn("[web-remarq] Clipboard write failed");
2098
2245
  });
2099
2246
  }
@@ -2166,6 +2313,7 @@ var WebRemarq = {
2166
2313
  elementCache.clear();
2167
2314
  storage.clearAll();
2168
2315
  refreshMarkers();
2316
+ showToast(themeManager.container, "All annotations cleared");
2169
2317
  },
2170
2318
  onThemeToggle: () => themeManager.toggle()
2171
2319
  });
@@ -2194,6 +2342,10 @@ var WebRemarq = {
2194
2342
  document.removeEventListener("keydown", handleInspectKeydown);
2195
2343
  mutationObserver == null ? void 0 : mutationObserver.disconnect();
2196
2344
  mutationObserver = null;
2345
+ if (inspecting) {
2346
+ document.body.style.cursor = savedCursor;
2347
+ }
2348
+ hideToast();
2197
2349
  destroyViewportListener();
2198
2350
  unsubRoute == null ? void 0 : unsubRoute();
2199
2351
  routeObserver == null ? void 0 : routeObserver.destroy();