web-remarq 0.1.10 → 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/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);