web-remarq 0.1.9 → 0.2.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/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  Visual annotation tool for design review workflows. Framework-agnostic, zero dependencies.
4
4
 
5
- Designer annotates UI elements on staging/dev, exports a report. Developer imports the report and sees markers on the exact elements.
5
+ Designer annotates UI elements on staging/dev, exports a report. Developer imports the report and sees markers on the exact elements. Copy annotations as agent-friendly markdown with search hints for AI coding agents.
6
6
 
7
7
  ## Install
8
8
 
@@ -47,19 +47,23 @@ Remove all DOM nodes, event listeners, and observers. Full cleanup.
47
47
 
48
48
  Switch between `'light'` and `'dark'` themes.
49
49
 
50
+ ### `WebRemarq.copy()`
51
+
52
+ Copy annotations as agent-friendly markdown to clipboard. Includes ranked search hints (CSS selectors, class names, text content, DOM path) so AI coding agents can grep and locate the source code.
53
+
50
54
  ### `WebRemarq.export(format)`
51
55
 
52
- - `'md'` — copies markdown report to clipboard
56
+ - `'md'` — downloads `.md` file with search hints (same content as `copy()`)
53
57
  - `'json'` — downloads `.json` file with full annotation data
54
58
 
55
59
  ### `WebRemarq.import(file)`
56
60
 
57
- Import annotations from a JSON file. Returns `Promise<{ total, matched, detached }>`.
61
+ Import annotations from a JSON file. Returns `Promise<{ total, matched, otherBreakpoint, detached }>`.
58
62
 
59
63
  ```ts
60
64
  const input = document.querySelector('input[type="file"]')
61
65
  const result = await WebRemarq.import(input.files[0])
62
- // { total: 12, matched: 10, detached: 2 }
66
+ // { total: 12, matched: 10, otherBreakpoint: 1, detached: 1 }
63
67
  ```
64
68
 
65
69
  ### `WebRemarq.getAnnotations(route?)`
@@ -91,8 +95,9 @@ When a user clicks an element, a multi-signal fingerprint is captured:
91
95
 
92
96
  - **Stable anchors** — `data-annotate`, `data-testid`, `id`
93
97
  - **Semantics** — tag name, text content, ARIA role/label
94
- - **Structure** — stable CSS classes (hashes stripped), DOM path, sibling index
98
+ - **Structure** — stable CSS classes (hashes stripped), DOM path with parent classes, sibling index
95
99
  - **Parent context** — nearest ancestor's `data-annotate` value
100
+ - **Agent export** — raw classes, CSS Module decomposition (module hint + local class name)
96
101
 
97
102
  ### Matching
98
103
 
@@ -101,7 +106,34 @@ When loading annotations, elements are found via a fallback chain:
101
106
  1. Exact match by `data-annotate` or `data-testid`
102
107
  2. Exact match by `id`
103
108
  3. Fuzzy match using weighted scoring (text similarity, ARIA, classes, DOM path)
104
- 4. Unmatched annotations go to a "detached" panel
109
+ 4. Unmatched annotations sorted into "other viewport" or "detached" panels
110
+
111
+ ### Viewport Breakpoints
112
+
113
+ Annotations are tagged with a viewport bucket (width rounded to 100px). When resizing:
114
+
115
+ - **Attached** — element found in current viewport
116
+ - **Other viewport** — element not found, but annotation belongs to a different breakpoint (not an error)
117
+ - **Detached** — element not found even in its native breakpoint (real problem)
118
+
119
+ Automatic reconnection when returning to the annotation's native viewport.
120
+
121
+ ### Agent-Friendly Copy
122
+
123
+ The Copy button produces markdown with ranked search hints:
124
+
125
+ ```markdown
126
+ ### 1. [pending] "Button too small on mobile"
127
+ Element: <button> "Submit"
128
+ Viewport: 300px
129
+
130
+ Search hints:
131
+ - `data-testid="submit-btn"` — in template files
132
+ - `"Submit"` — text content in templates
133
+ - `.submitButton` — in CSS Module file (likely `form.module.*`)
134
+ - DOM: div.form-wrapper > form > button.submit
135
+ - Classes: form__submitButton__cEqts flex items-center
136
+ ```
105
137
 
106
138
  ### Hash Detection
107
139
 
@@ -122,11 +154,12 @@ Works without any markup changes, but for guaranteed stable matching add `data-a
122
154
 
123
155
  ## UI Components
124
156
 
125
- - **Toolbar** — fixed bottom-right panel with inspect, export, import, clear, theme, minimize
157
+ - **Toolbar** — fixed bottom-right panel with inspect, copy, export, import, clear, theme, minimize
126
158
  - **Inspect mode** — hover to highlight, click to annotate
127
159
  - **Markers** — numbered circles (orange = pending, green = resolved)
128
160
  - **Popup** — comment input for new annotations, detail view with Resolve/Delete for existing
129
- - **Detached panel** — shows annotations whose elements can't be found
161
+ - **Other viewport panel** — annotations from different breakpoints, click to see required viewport
162
+ - **Detached panel** — annotations whose elements can't be found in their native viewport
130
163
 
131
164
  ## Build Outputs
132
165
 
package/dist/index.cjs CHANGED
@@ -735,6 +735,61 @@ var CSS = `
735
735
  .remarq-toast-fade {
736
736
  opacity: 0;
737
737
  }
738
+
739
+ .remarq-spacing {
740
+ position: fixed;
741
+ top: 0;
742
+ left: 0;
743
+ pointer-events: none;
744
+ z-index: 2147483646;
745
+ }
746
+
747
+ .remarq-spacing-margin {
748
+ position: fixed;
749
+ background: rgba(249, 115, 22, 0.2);
750
+ pointer-events: none;
751
+ }
752
+
753
+ .remarq-spacing-padding {
754
+ position: fixed;
755
+ background: rgba(34, 197, 94, 0.2);
756
+ pointer-events: none;
757
+ }
758
+
759
+ .remarq-spacing-content {
760
+ position: fixed;
761
+ background: rgba(59, 130, 246, 0.15);
762
+ border: 1px dashed rgba(59, 130, 246, 0.5);
763
+ pointer-events: none;
764
+ }
765
+
766
+ .remarq-spacing-gap {
767
+ position: fixed;
768
+ background: rgba(168, 85, 247, 0.25);
769
+ border: 1px dashed rgba(168, 85, 247, 0.5);
770
+ pointer-events: none;
771
+ display: flex;
772
+ align-items: center;
773
+ justify-content: center;
774
+ }
775
+
776
+ .remarq-spacing-label {
777
+ position: fixed;
778
+ font-size: 11px;
779
+ font-weight: 700;
780
+ pointer-events: none;
781
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
782
+ line-height: 1;
783
+ text-shadow: 0 0 3px var(--remarq-bg), 0 0 3px var(--remarq-bg);
784
+ }
785
+
786
+ .remarq-spacing-label-margin { color: #f97316; }
787
+ .remarq-spacing-label-padding { color: #22c55e; }
788
+ .remarq-spacing-label-content { color: #3b82f6; font-size: 10px; }
789
+ .remarq-spacing-label-gap { color: #a855f7; font-size: 10px; }
790
+
791
+ .remarq-toolbar-btn:disabled { opacity: 0.3; cursor: default; }
792
+ .remarq-toolbar-btn:disabled:hover { background: transparent; }
738
793
  `;
739
794
  function injectStyles() {
740
795
  if (document.querySelector(`style[${STYLES_ID}]`)) return;
@@ -806,6 +861,7 @@ var ThemeManager = class {
806
861
  // src/ui/toolbar.ts
807
862
  var ICONS = {
808
863
  inspect: '<svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5"><circle cx="7" cy="7" r="4"/><line x1="10" y1="10" x2="14" y2="14"/></svg>',
864
+ spacing: '<svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M2 2h12M2 14h12M2 2v12M14 2v12"/><path d="M5 5h6v6H5z" stroke-dasharray="2 1"/></svg>',
809
865
  copy: '<svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5"><rect x="5" y="5" width="8" height="9" rx="1"/><path d="M3 11V3a1 1 0 0 1 1-1h6"/></svg>',
810
866
  export: '<svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M8 2v8M4 6l4-4 4 4M2 12h12"/></svg>',
811
867
  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>',
@@ -827,6 +883,8 @@ var Toolbar = class {
827
883
  this.badgeEl.className = "remarq-badge";
828
884
  this.badgeEl.style.display = "none";
829
885
  this.inspectBtn.appendChild(this.badgeEl);
886
+ this.spacingBtn = this.createButton("spacing", ICONS.spacing, () => callbacks.onSpacingToggle());
887
+ this.spacingBtn.disabled = true;
830
888
  const copyBtn = this.createButton("copy", ICONS.copy, () => callbacks.onCopy());
831
889
  const exportBtn = this.createButton("export", ICONS.export, (e) => this.toggleExportMenu(e));
832
890
  this.fileInput = document.createElement("input");
@@ -841,8 +899,9 @@ var Toolbar = class {
841
899
  const clearBtn = this.createButton("clear", ICONS.clear, () => callbacks.onClear());
842
900
  const themeBtn = this.createButton("theme", ICONS.theme, () => callbacks.onThemeToggle());
843
901
  const minimizeBtn = this.createButton("minimize", ICONS.minimize, () => this.toggleMinimize());
844
- this.buttons = [this.inspectBtn, copyBtn, exportBtn, importBtn, clearBtn, themeBtn];
902
+ this.buttons = [this.inspectBtn, this.spacingBtn, copyBtn, exportBtn, importBtn, clearBtn, themeBtn];
845
903
  this.toolbarEl.appendChild(this.inspectBtn);
904
+ this.toolbarEl.appendChild(this.spacingBtn);
846
905
  this.toolbarEl.appendChild(copyBtn);
847
906
  this.toolbarEl.appendChild(exportBtn);
848
907
  this.toolbarEl.appendChild(importBtn);
@@ -855,6 +914,15 @@ var Toolbar = class {
855
914
  setInspectActive(active) {
856
915
  this.inspectBtn.classList.toggle("remarq-active", active);
857
916
  }
917
+ setSpacingActive(active) {
918
+ this.spacingBtn.classList.toggle("remarq-active", active);
919
+ }
920
+ setSpacingEnabled(enabled) {
921
+ this.spacingBtn.disabled = !enabled;
922
+ if (!enabled) {
923
+ this.spacingBtn.classList.remove("remarq-active");
924
+ }
925
+ }
858
926
  setBadgeCount(count) {
859
927
  this.badgeEl.textContent = String(count);
860
928
  this.badgeEl.style.display = count > 0 ? "flex" : "none";
@@ -950,6 +1018,9 @@ var Overlay = class {
950
1018
  this.tooltipEl.style.left = `${x + 12}px`;
951
1019
  this.tooltipEl.style.top = `${y - 28}px`;
952
1020
  }
1021
+ hideHighlight() {
1022
+ this.overlayEl.style.display = "none";
1023
+ }
953
1024
  hide() {
954
1025
  this.overlayEl.style.display = "none";
955
1026
  this.tooltipEl.style.display = "none";
@@ -997,6 +1068,229 @@ function getDirectText(el) {
997
1068
  return text.slice(0, 30);
998
1069
  }
999
1070
 
1071
+ // src/ui/spacing-overlay.ts
1072
+ function parsePx(value) {
1073
+ return parseFloat(value) || 0;
1074
+ }
1075
+ var SpacingOverlay = class {
1076
+ constructor(parent) {
1077
+ this.parent = parent;
1078
+ this.labels = [];
1079
+ this.gapEls = [];
1080
+ this.lastTarget = null;
1081
+ this.containerEl = document.createElement("div");
1082
+ this.containerEl.className = "remarq-spacing";
1083
+ this.containerEl.style.display = "none";
1084
+ this.marginEl = document.createElement("div");
1085
+ this.marginEl.className = "remarq-spacing-margin";
1086
+ this.paddingEl = document.createElement("div");
1087
+ this.paddingEl.className = "remarq-spacing-padding";
1088
+ this.contentEl = document.createElement("div");
1089
+ this.contentEl.className = "remarq-spacing-content";
1090
+ this.containerEl.appendChild(this.marginEl);
1091
+ this.containerEl.appendChild(this.paddingEl);
1092
+ this.containerEl.appendChild(this.contentEl);
1093
+ parent.appendChild(this.containerEl);
1094
+ }
1095
+ show(target) {
1096
+ if (target === this.lastTarget) return;
1097
+ this.lastTarget = target;
1098
+ try {
1099
+ const rect = target.getBoundingClientRect();
1100
+ const cs = window.getComputedStyle(target);
1101
+ const margin = this.readSides(cs, "margin");
1102
+ const padding = this.readSides(cs, "padding");
1103
+ const border = this.readBorderSides(cs);
1104
+ const marginBox = {
1105
+ top: rect.top - margin.top,
1106
+ left: rect.left - margin.left,
1107
+ width: rect.width + margin.left + margin.right,
1108
+ height: rect.height + margin.top + margin.bottom
1109
+ };
1110
+ const paddingBox = {
1111
+ top: rect.top,
1112
+ left: rect.left,
1113
+ width: rect.width,
1114
+ height: rect.height
1115
+ };
1116
+ const contentBox = {
1117
+ top: rect.top + border.top + padding.top,
1118
+ left: rect.left + border.left + padding.left,
1119
+ width: rect.width - border.left - border.right - padding.left - padding.right,
1120
+ height: rect.height - border.top - border.bottom - padding.top - padding.bottom
1121
+ };
1122
+ this.positionEl(this.marginEl, marginBox);
1123
+ this.positionEl(this.paddingEl, paddingBox);
1124
+ this.positionEl(this.contentEl, contentBox);
1125
+ this.clearLabels();
1126
+ this.clearGaps();
1127
+ this.addSideLabels(margin, marginBox, paddingBox, "margin");
1128
+ this.addSideLabels(padding, paddingBox, contentBox, "padding");
1129
+ if (contentBox.width > 40 && contentBox.height > 14) {
1130
+ this.addLabel(
1131
+ `${Math.round(contentBox.width)} \xD7 ${Math.round(contentBox.height)}`,
1132
+ contentBox.top + contentBox.height / 2 - 6,
1133
+ contentBox.left + contentBox.width / 2,
1134
+ "content"
1135
+ );
1136
+ }
1137
+ this.showGaps(target);
1138
+ this.containerEl.style.display = "block";
1139
+ } catch (e) {
1140
+ this.hide();
1141
+ }
1142
+ }
1143
+ hide() {
1144
+ this.containerEl.style.display = "none";
1145
+ this.lastTarget = null;
1146
+ this.clearLabels();
1147
+ this.clearGaps();
1148
+ }
1149
+ destroy() {
1150
+ this.clearLabels();
1151
+ this.clearGaps();
1152
+ this.containerEl.remove();
1153
+ }
1154
+ readSides(cs, prop) {
1155
+ return {
1156
+ top: parsePx(cs[`${prop}Top`]),
1157
+ right: parsePx(cs[`${prop}Right`]),
1158
+ bottom: parsePx(cs[`${prop}Bottom`]),
1159
+ left: parsePx(cs[`${prop}Left`])
1160
+ };
1161
+ }
1162
+ readBorderSides(cs) {
1163
+ return {
1164
+ top: parsePx(cs.borderTopWidth),
1165
+ right: parsePx(cs.borderRightWidth),
1166
+ bottom: parsePx(cs.borderBottomWidth),
1167
+ left: parsePx(cs.borderLeftWidth)
1168
+ };
1169
+ }
1170
+ positionEl(el, box) {
1171
+ el.style.top = `${box.top}px`;
1172
+ el.style.left = `${box.left}px`;
1173
+ el.style.width = `${Math.max(0, box.width)}px`;
1174
+ el.style.height = `${Math.max(0, box.height)}px`;
1175
+ }
1176
+ addSideLabels(sides, outerBox, innerBox, type) {
1177
+ if (sides.top > 0) {
1178
+ const y = outerBox.top + (innerBox.top - outerBox.top) / 2 - 6;
1179
+ const x = outerBox.left + outerBox.width / 2;
1180
+ this.addLabel(String(Math.round(sides.top)), y, x, type);
1181
+ }
1182
+ if (sides.bottom > 0) {
1183
+ const innerBottom = innerBox.top + innerBox.height;
1184
+ const outerBottom = outerBox.top + outerBox.height;
1185
+ const y = innerBottom + (outerBottom - innerBottom) / 2 - 6;
1186
+ const x = outerBox.left + outerBox.width / 2;
1187
+ this.addLabel(String(Math.round(sides.bottom)), y, x, type);
1188
+ }
1189
+ if (sides.left > 0) {
1190
+ const y = outerBox.top + outerBox.height / 2 - 6;
1191
+ const x = outerBox.left + (innerBox.left - outerBox.left) / 2;
1192
+ this.addLabel(String(Math.round(sides.left)), y, x, type);
1193
+ }
1194
+ if (sides.right > 0) {
1195
+ const innerRight = innerBox.left + innerBox.width;
1196
+ const outerRight = outerBox.left + outerBox.width;
1197
+ const y = outerBox.top + outerBox.height / 2 - 6;
1198
+ const x = innerRight + (outerRight - innerRight) / 2;
1199
+ this.addLabel(String(Math.round(sides.right)), y, x, type);
1200
+ }
1201
+ }
1202
+ addLabel(text, top, left, type) {
1203
+ const label = document.createElement("div");
1204
+ label.className = `remarq-spacing-label remarq-spacing-label-${type}`;
1205
+ label.textContent = text;
1206
+ label.style.top = `${top}px`;
1207
+ label.style.left = `${left}px`;
1208
+ label.style.transform = "translateX(-50%)";
1209
+ this.containerEl.appendChild(label);
1210
+ this.labels.push(label);
1211
+ }
1212
+ clearLabels() {
1213
+ for (const label of this.labels) label.remove();
1214
+ this.labels = [];
1215
+ }
1216
+ showGaps(target) {
1217
+ const targetCs = window.getComputedStyle(target);
1218
+ if (targetCs.display.includes("flex")) {
1219
+ this.showContainerGaps(target, targetCs);
1220
+ return;
1221
+ }
1222
+ const parent = target.parentElement;
1223
+ if (!parent) return;
1224
+ const parentCs = window.getComputedStyle(parent);
1225
+ if (!parentCs.display.includes("flex")) return;
1226
+ const rowGap = parsePx(parentCs.rowGap);
1227
+ const columnGap = parsePx(parentCs.columnGap);
1228
+ const direction = parentCs.flexDirection;
1229
+ const isRow = direction === "row" || direction === "row-reverse";
1230
+ const gap = isRow ? columnGap : rowGap;
1231
+ if (gap <= 0) return;
1232
+ const children = Array.from(parent.children);
1233
+ const targetIndex = children.indexOf(target);
1234
+ if (targetIndex === -1) return;
1235
+ if (targetIndex > 0) {
1236
+ this.renderGap(children[targetIndex - 1], target, gap, isRow);
1237
+ }
1238
+ if (targetIndex < children.length - 1) {
1239
+ this.renderGap(target, children[targetIndex + 1], gap, isRow);
1240
+ }
1241
+ }
1242
+ showContainerGaps(container, cs) {
1243
+ const rowGap = parsePx(cs.rowGap);
1244
+ const columnGap = parsePx(cs.columnGap);
1245
+ const direction = cs.flexDirection;
1246
+ const isRow = direction === "row" || direction === "row-reverse";
1247
+ const gap = isRow ? columnGap : rowGap;
1248
+ if (gap <= 0) return;
1249
+ const children = Array.from(container.children);
1250
+ for (let i = 0; i < children.length - 1; i++) {
1251
+ this.renderGap(children[i], children[i + 1], gap, isRow);
1252
+ }
1253
+ }
1254
+ renderGap(before, after, gap, isRow) {
1255
+ const rectBefore = before.getBoundingClientRect();
1256
+ const rectAfter = after.getBoundingClientRect();
1257
+ const gapEl = document.createElement("div");
1258
+ gapEl.className = "remarq-spacing-gap";
1259
+ if (isRow) {
1260
+ const left = Math.min(rectBefore.right, rectAfter.right);
1261
+ const right = Math.max(rectBefore.left, rectAfter.left);
1262
+ const top = Math.min(rectBefore.top, rectAfter.top);
1263
+ const height = Math.max(rectBefore.height, rectAfter.height);
1264
+ gapEl.style.top = `${top}px`;
1265
+ gapEl.style.left = `${left}px`;
1266
+ gapEl.style.width = `${Math.abs(right - left)}px`;
1267
+ gapEl.style.height = `${height}px`;
1268
+ } else {
1269
+ const top = Math.min(rectBefore.bottom, rectAfter.bottom);
1270
+ const bottom = Math.max(rectBefore.top, rectAfter.top);
1271
+ const left = Math.min(rectBefore.left, rectAfter.left);
1272
+ const width = Math.max(rectBefore.width, rectAfter.width);
1273
+ gapEl.style.top = `${top}px`;
1274
+ gapEl.style.left = `${left}px`;
1275
+ gapEl.style.width = `${width}px`;
1276
+ gapEl.style.height = `${Math.abs(bottom - top)}px`;
1277
+ }
1278
+ if (gap >= 10) {
1279
+ const label = document.createElement("span");
1280
+ label.className = "remarq-spacing-label-gap";
1281
+ label.textContent = `gap: ${Math.round(gap)}`;
1282
+ label.style.cssText = "font-size:10px;font-weight:700;pointer-events:none;";
1283
+ gapEl.appendChild(label);
1284
+ }
1285
+ this.containerEl.appendChild(gapEl);
1286
+ this.gapEls.push(gapEl);
1287
+ }
1288
+ clearGaps() {
1289
+ for (const el of this.gapEls) el.remove();
1290
+ this.gapEls = [];
1291
+ }
1292
+ };
1293
+
1000
1294
  // src/ui/popup.ts
1001
1295
  var POPUP_WIDTH = 300;
1002
1296
  var POPUP_MARGIN = 8;
@@ -1383,6 +1677,8 @@ var markers;
1383
1677
  var detachedPanel;
1384
1678
  var routeObserver;
1385
1679
  var inspecting = false;
1680
+ var spacingMode = false;
1681
+ var spacingOverlay;
1386
1682
  var mutationObserver = null;
1387
1683
  var unsubRoute = null;
1388
1684
  var refreshScheduled = false;
@@ -1476,7 +1772,6 @@ function handleInspectClick(e) {
1476
1772
  if (!target || target.closest("[data-remarq-theme]")) return;
1477
1773
  e.preventDefault();
1478
1774
  e.stopPropagation();
1479
- overlay.hide();
1480
1775
  setInspecting(false);
1481
1776
  const rect = target.getBoundingClientRect();
1482
1777
  popup.show(
@@ -1516,19 +1811,40 @@ function handleInspectHover(e) {
1516
1811
  if (!inspecting) return;
1517
1812
  const target = e.target;
1518
1813
  if (!target || target.closest("[data-remarq-theme]")) return;
1519
- overlay.show(target);
1814
+ if (spacingMode) {
1815
+ overlay.show(target);
1816
+ overlay.hideHighlight();
1817
+ spacingOverlay.show(target);
1818
+ } else {
1819
+ overlay.show(target);
1820
+ }
1520
1821
  overlay.updateTooltipPosition(e.clientX, e.clientY);
1521
1822
  }
1522
1823
  function handleInspectKeydown(e) {
1824
+ var _a, _b;
1825
+ const tag = (_a = e.target) == null ? void 0 : _a.tagName;
1826
+ if (tag === "INPUT" || tag === "TEXTAREA" || ((_b = e.target) == null ? void 0 : _b.isContentEditable)) return;
1523
1827
  if (e.key === "Escape" && inspecting) {
1524
1828
  setInspecting(false);
1525
1829
  overlay.hide();
1830
+ spacingOverlay.hide();
1831
+ }
1832
+ if (e.key === "s" && inspecting) {
1833
+ spacingMode = !spacingMode;
1834
+ toolbar.setSpacingActive(spacingMode);
1835
+ if (!spacingMode) spacingOverlay.hide();
1526
1836
  }
1527
1837
  }
1528
1838
  function setInspecting(value) {
1529
1839
  inspecting = value;
1530
1840
  toolbar.setInspectActive(value);
1531
- if (!value) overlay.hide();
1841
+ toolbar.setSpacingEnabled(value);
1842
+ if (!value) {
1843
+ overlay.hide();
1844
+ spacingOverlay == null ? void 0 : spacingOverlay.hide();
1845
+ spacingMode = false;
1846
+ toolbar.setSpacingActive(false);
1847
+ }
1532
1848
  }
1533
1849
  function handleMarkerClick(annotationId) {
1534
1850
  var _a;
@@ -1660,6 +1976,7 @@ var WebRemarq = {
1660
1976
  storage = new AnnotationStorage();
1661
1977
  themeManager = new ThemeManager(document.body, options.theme);
1662
1978
  overlay = new Overlay(themeManager.container);
1979
+ spacingOverlay = new SpacingOverlay(themeManager.container);
1663
1980
  popup = new Popup(themeManager.container);
1664
1981
  markers = new MarkerManager(themeManager.container, handleMarkerClick);
1665
1982
  detachedPanel = new DetachedPanel(themeManager.container, (id) => {
@@ -1669,6 +1986,12 @@ var WebRemarq = {
1669
1986
  });
1670
1987
  toolbar = new Toolbar(themeManager.container, {
1671
1988
  onInspect: () => setInspecting(!inspecting),
1989
+ onSpacingToggle: () => {
1990
+ if (!inspecting) return;
1991
+ spacingMode = !spacingMode;
1992
+ toolbar.setSpacingActive(spacingMode);
1993
+ if (!spacingMode) spacingOverlay.hide();
1994
+ },
1672
1995
  onCopy: copyToClipboard,
1673
1996
  onExportMd: exportMarkdown,
1674
1997
  onExportJson: exportJSON,
@@ -1718,11 +2041,13 @@ var WebRemarq = {
1718
2041
  detachedPanel == null ? void 0 : detachedPanel.destroy();
1719
2042
  popup == null ? void 0 : popup.destroy();
1720
2043
  overlay == null ? void 0 : overlay.destroy();
2044
+ spacingOverlay == null ? void 0 : spacingOverlay.destroy();
1721
2045
  toolbar == null ? void 0 : toolbar.destroy();
1722
2046
  themeManager == null ? void 0 : themeManager.destroy();
1723
2047
  removeStyles();
1724
2048
  elementCache.clear();
1725
2049
  inspecting = false;
2050
+ spacingMode = false;
1726
2051
  initialized = false;
1727
2052
  } catch (err) {
1728
2053
  console.error("[web-remarq] Destroy failed:", err);