web-remarq 0.1.10 → 0.2.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
@@ -735,6 +735,62 @@ 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
+ padding: 1px 3px;
784
+ border-radius: 2px;
785
+ }
786
+
787
+ .remarq-spacing-label-margin { color: #fff; background: rgba(249, 115, 22, 0.85); }
788
+ .remarq-spacing-label-padding { color: #fff; background: rgba(34, 197, 94, 0.85); }
789
+ .remarq-spacing-label-content { color: #fff; background: rgba(59, 130, 246, 0.85); font-size: 10px; }
790
+ .remarq-spacing-label-gap { color: #fff; background: rgba(168, 85, 247, 0.85); font-size: 10px; }
791
+
792
+ .remarq-toolbar-btn:disabled { opacity: 0.3; cursor: default; }
793
+ .remarq-toolbar-btn:disabled:hover { background: transparent; }
738
794
  `;
739
795
  function injectStyles() {
740
796
  if (document.querySelector(`style[${STYLES_ID}]`)) return;
@@ -806,6 +862,7 @@ var ThemeManager = class {
806
862
  // src/ui/toolbar.ts
807
863
  var ICONS = {
808
864
  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>',
865
+ 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
866
  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
867
  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
868
  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 +884,8 @@ var Toolbar = class {
827
884
  this.badgeEl.className = "remarq-badge";
828
885
  this.badgeEl.style.display = "none";
829
886
  this.inspectBtn.appendChild(this.badgeEl);
887
+ this.spacingBtn = this.createButton("spacing", ICONS.spacing, () => callbacks.onSpacingToggle());
888
+ this.spacingBtn.disabled = true;
830
889
  const copyBtn = this.createButton("copy", ICONS.copy, () => callbacks.onCopy());
831
890
  const exportBtn = this.createButton("export", ICONS.export, (e) => this.toggleExportMenu(e));
832
891
  this.fileInput = document.createElement("input");
@@ -841,8 +900,9 @@ var Toolbar = class {
841
900
  const clearBtn = this.createButton("clear", ICONS.clear, () => callbacks.onClear());
842
901
  const themeBtn = this.createButton("theme", ICONS.theme, () => callbacks.onThemeToggle());
843
902
  const minimizeBtn = this.createButton("minimize", ICONS.minimize, () => this.toggleMinimize());
844
- this.buttons = [this.inspectBtn, copyBtn, exportBtn, importBtn, clearBtn, themeBtn];
903
+ this.buttons = [this.inspectBtn, this.spacingBtn, copyBtn, exportBtn, importBtn, clearBtn, themeBtn];
845
904
  this.toolbarEl.appendChild(this.inspectBtn);
905
+ this.toolbarEl.appendChild(this.spacingBtn);
846
906
  this.toolbarEl.appendChild(copyBtn);
847
907
  this.toolbarEl.appendChild(exportBtn);
848
908
  this.toolbarEl.appendChild(importBtn);
@@ -855,6 +915,15 @@ var Toolbar = class {
855
915
  setInspectActive(active) {
856
916
  this.inspectBtn.classList.toggle("remarq-active", active);
857
917
  }
918
+ setSpacingActive(active) {
919
+ this.spacingBtn.classList.toggle("remarq-active", active);
920
+ }
921
+ setSpacingEnabled(enabled) {
922
+ this.spacingBtn.disabled = !enabled;
923
+ if (!enabled) {
924
+ this.spacingBtn.classList.remove("remarq-active");
925
+ }
926
+ }
858
927
  setBadgeCount(count) {
859
928
  this.badgeEl.textContent = String(count);
860
929
  this.badgeEl.style.display = count > 0 ? "flex" : "none";
@@ -950,6 +1019,9 @@ var Overlay = class {
950
1019
  this.tooltipEl.style.left = `${x + 12}px`;
951
1020
  this.tooltipEl.style.top = `${y - 28}px`;
952
1021
  }
1022
+ hideHighlight() {
1023
+ this.overlayEl.style.display = "none";
1024
+ }
953
1025
  hide() {
954
1026
  this.overlayEl.style.display = "none";
955
1027
  this.tooltipEl.style.display = "none";
@@ -997,6 +1069,229 @@ function getDirectText(el) {
997
1069
  return text.slice(0, 30);
998
1070
  }
999
1071
 
1072
+ // src/ui/spacing-overlay.ts
1073
+ function parsePx(value) {
1074
+ return parseFloat(value) || 0;
1075
+ }
1076
+ var SpacingOverlay = class {
1077
+ constructor(parent) {
1078
+ this.parent = parent;
1079
+ this.labels = [];
1080
+ this.gapEls = [];
1081
+ this.lastTarget = null;
1082
+ this.containerEl = document.createElement("div");
1083
+ this.containerEl.className = "remarq-spacing";
1084
+ this.containerEl.style.display = "none";
1085
+ this.marginEl = document.createElement("div");
1086
+ this.marginEl.className = "remarq-spacing-margin";
1087
+ this.paddingEl = document.createElement("div");
1088
+ this.paddingEl.className = "remarq-spacing-padding";
1089
+ this.contentEl = document.createElement("div");
1090
+ this.contentEl.className = "remarq-spacing-content";
1091
+ this.containerEl.appendChild(this.marginEl);
1092
+ this.containerEl.appendChild(this.paddingEl);
1093
+ this.containerEl.appendChild(this.contentEl);
1094
+ parent.appendChild(this.containerEl);
1095
+ }
1096
+ show(target) {
1097
+ if (target === this.lastTarget) return;
1098
+ this.lastTarget = target;
1099
+ try {
1100
+ const rect = target.getBoundingClientRect();
1101
+ const cs = window.getComputedStyle(target);
1102
+ const margin = this.readSides(cs, "margin");
1103
+ const padding = this.readSides(cs, "padding");
1104
+ const border = this.readBorderSides(cs);
1105
+ const marginBox = {
1106
+ top: rect.top - margin.top,
1107
+ left: rect.left - margin.left,
1108
+ width: rect.width + margin.left + margin.right,
1109
+ height: rect.height + margin.top + margin.bottom
1110
+ };
1111
+ const paddingBox = {
1112
+ top: rect.top,
1113
+ left: rect.left,
1114
+ width: rect.width,
1115
+ height: rect.height
1116
+ };
1117
+ const contentBox = {
1118
+ top: rect.top + border.top + padding.top,
1119
+ left: rect.left + border.left + padding.left,
1120
+ width: rect.width - border.left - border.right - padding.left - padding.right,
1121
+ height: rect.height - border.top - border.bottom - padding.top - padding.bottom
1122
+ };
1123
+ this.positionEl(this.marginEl, marginBox);
1124
+ this.positionEl(this.paddingEl, paddingBox);
1125
+ this.positionEl(this.contentEl, contentBox);
1126
+ this.clearLabels();
1127
+ this.clearGaps();
1128
+ this.addSideLabels(margin, marginBox, paddingBox, "margin");
1129
+ this.addSideLabels(padding, paddingBox, contentBox, "padding");
1130
+ if (contentBox.width > 40 && contentBox.height > 14) {
1131
+ this.addLabel(
1132
+ `${Math.round(contentBox.width)} \xD7 ${Math.round(contentBox.height)}`,
1133
+ contentBox.top + contentBox.height / 2 - 6,
1134
+ contentBox.left + contentBox.width / 2,
1135
+ "content"
1136
+ );
1137
+ }
1138
+ this.showGaps(target);
1139
+ this.containerEl.style.display = "block";
1140
+ } catch (e) {
1141
+ this.hide();
1142
+ }
1143
+ }
1144
+ hide() {
1145
+ this.containerEl.style.display = "none";
1146
+ this.lastTarget = null;
1147
+ this.clearLabels();
1148
+ this.clearGaps();
1149
+ }
1150
+ destroy() {
1151
+ this.clearLabels();
1152
+ this.clearGaps();
1153
+ this.containerEl.remove();
1154
+ }
1155
+ readSides(cs, prop) {
1156
+ return {
1157
+ top: parsePx(cs[`${prop}Top`]),
1158
+ right: parsePx(cs[`${prop}Right`]),
1159
+ bottom: parsePx(cs[`${prop}Bottom`]),
1160
+ left: parsePx(cs[`${prop}Left`])
1161
+ };
1162
+ }
1163
+ readBorderSides(cs) {
1164
+ return {
1165
+ top: parsePx(cs.borderTopWidth),
1166
+ right: parsePx(cs.borderRightWidth),
1167
+ bottom: parsePx(cs.borderBottomWidth),
1168
+ left: parsePx(cs.borderLeftWidth)
1169
+ };
1170
+ }
1171
+ positionEl(el, box) {
1172
+ el.style.top = `${box.top}px`;
1173
+ el.style.left = `${box.left}px`;
1174
+ el.style.width = `${Math.max(0, box.width)}px`;
1175
+ el.style.height = `${Math.max(0, box.height)}px`;
1176
+ }
1177
+ addSideLabels(sides, outerBox, innerBox, type) {
1178
+ if (sides.top > 0) {
1179
+ const y = outerBox.top + (innerBox.top - outerBox.top) / 2 - 6;
1180
+ const x = outerBox.left + outerBox.width / 2;
1181
+ this.addLabel(String(Math.round(sides.top)), y, x, type);
1182
+ }
1183
+ if (sides.bottom > 0) {
1184
+ const innerBottom = innerBox.top + innerBox.height;
1185
+ const outerBottom = outerBox.top + outerBox.height;
1186
+ const y = innerBottom + (outerBottom - innerBottom) / 2 - 6;
1187
+ const x = outerBox.left + outerBox.width / 2;
1188
+ this.addLabel(String(Math.round(sides.bottom)), y, x, type);
1189
+ }
1190
+ if (sides.left > 0) {
1191
+ const y = outerBox.top + outerBox.height / 2 - 6;
1192
+ const x = outerBox.left + (innerBox.left - outerBox.left) / 2;
1193
+ this.addLabel(String(Math.round(sides.left)), y, x, type);
1194
+ }
1195
+ if (sides.right > 0) {
1196
+ const innerRight = innerBox.left + innerBox.width;
1197
+ const outerRight = outerBox.left + outerBox.width;
1198
+ const y = outerBox.top + outerBox.height / 2 - 6;
1199
+ const x = innerRight + (outerRight - innerRight) / 2;
1200
+ this.addLabel(String(Math.round(sides.right)), y, x, type);
1201
+ }
1202
+ }
1203
+ addLabel(text, top, left, type) {
1204
+ const label = document.createElement("div");
1205
+ label.className = `remarq-spacing-label remarq-spacing-label-${type}`;
1206
+ label.textContent = text;
1207
+ label.style.top = `${top}px`;
1208
+ label.style.left = `${left}px`;
1209
+ label.style.transform = "translateX(-50%)";
1210
+ this.containerEl.appendChild(label);
1211
+ this.labels.push(label);
1212
+ }
1213
+ clearLabels() {
1214
+ for (const label of this.labels) label.remove();
1215
+ this.labels = [];
1216
+ }
1217
+ showGaps(target) {
1218
+ const targetCs = window.getComputedStyle(target);
1219
+ if (targetCs.display.includes("flex")) {
1220
+ this.showContainerGaps(target, targetCs);
1221
+ return;
1222
+ }
1223
+ const parent = target.parentElement;
1224
+ if (!parent) return;
1225
+ const parentCs = window.getComputedStyle(parent);
1226
+ if (!parentCs.display.includes("flex")) return;
1227
+ const rowGap = parsePx(parentCs.rowGap);
1228
+ const columnGap = parsePx(parentCs.columnGap);
1229
+ const direction = parentCs.flexDirection;
1230
+ const isRow = direction === "row" || direction === "row-reverse";
1231
+ const gap = isRow ? columnGap : rowGap;
1232
+ if (gap <= 0) return;
1233
+ const children = Array.from(parent.children);
1234
+ const targetIndex = children.indexOf(target);
1235
+ if (targetIndex === -1) return;
1236
+ if (targetIndex > 0) {
1237
+ this.renderGap(children[targetIndex - 1], target, gap, isRow);
1238
+ }
1239
+ if (targetIndex < children.length - 1) {
1240
+ this.renderGap(target, children[targetIndex + 1], gap, isRow);
1241
+ }
1242
+ }
1243
+ showContainerGaps(container, cs) {
1244
+ const rowGap = parsePx(cs.rowGap);
1245
+ const columnGap = parsePx(cs.columnGap);
1246
+ const direction = cs.flexDirection;
1247
+ const isRow = direction === "row" || direction === "row-reverse";
1248
+ const gap = isRow ? columnGap : rowGap;
1249
+ if (gap <= 0) return;
1250
+ const children = Array.from(container.children);
1251
+ for (let i = 0; i < children.length - 1; i++) {
1252
+ this.renderGap(children[i], children[i + 1], gap, isRow);
1253
+ }
1254
+ }
1255
+ renderGap(before, after, gap, isRow) {
1256
+ const rectBefore = before.getBoundingClientRect();
1257
+ const rectAfter = after.getBoundingClientRect();
1258
+ const gapEl = document.createElement("div");
1259
+ gapEl.className = "remarq-spacing-gap";
1260
+ if (isRow) {
1261
+ const left = Math.min(rectBefore.right, rectAfter.right);
1262
+ const right = Math.max(rectBefore.left, rectAfter.left);
1263
+ const top = Math.min(rectBefore.top, rectAfter.top);
1264
+ const height = Math.max(rectBefore.height, rectAfter.height);
1265
+ gapEl.style.top = `${top}px`;
1266
+ gapEl.style.left = `${left}px`;
1267
+ gapEl.style.width = `${Math.abs(right - left)}px`;
1268
+ gapEl.style.height = `${height}px`;
1269
+ } else {
1270
+ const top = Math.min(rectBefore.bottom, rectAfter.bottom);
1271
+ const bottom = Math.max(rectBefore.top, rectAfter.top);
1272
+ const left = Math.min(rectBefore.left, rectAfter.left);
1273
+ const width = Math.max(rectBefore.width, rectAfter.width);
1274
+ gapEl.style.top = `${top}px`;
1275
+ gapEl.style.left = `${left}px`;
1276
+ gapEl.style.width = `${width}px`;
1277
+ gapEl.style.height = `${Math.abs(bottom - top)}px`;
1278
+ }
1279
+ if (gap >= 10) {
1280
+ const label = document.createElement("span");
1281
+ label.className = "remarq-spacing-label-gap";
1282
+ label.textContent = `gap: ${Math.round(gap)}`;
1283
+ label.style.cssText = "font-size:10px;font-weight:700;pointer-events:none;";
1284
+ gapEl.appendChild(label);
1285
+ }
1286
+ this.containerEl.appendChild(gapEl);
1287
+ this.gapEls.push(gapEl);
1288
+ }
1289
+ clearGaps() {
1290
+ for (const el of this.gapEls) el.remove();
1291
+ this.gapEls = [];
1292
+ }
1293
+ };
1294
+
1000
1295
  // src/ui/popup.ts
1001
1296
  var POPUP_WIDTH = 300;
1002
1297
  var POPUP_MARGIN = 8;
@@ -1383,6 +1678,8 @@ var markers;
1383
1678
  var detachedPanel;
1384
1679
  var routeObserver;
1385
1680
  var inspecting = false;
1681
+ var spacingMode = false;
1682
+ var spacingOverlay;
1386
1683
  var mutationObserver = null;
1387
1684
  var unsubRoute = null;
1388
1685
  var refreshScheduled = false;
@@ -1476,7 +1773,6 @@ function handleInspectClick(e) {
1476
1773
  if (!target || target.closest("[data-remarq-theme]")) return;
1477
1774
  e.preventDefault();
1478
1775
  e.stopPropagation();
1479
- overlay.hide();
1480
1776
  setInspecting(false);
1481
1777
  const rect = target.getBoundingClientRect();
1482
1778
  popup.show(
@@ -1516,19 +1812,40 @@ function handleInspectHover(e) {
1516
1812
  if (!inspecting) return;
1517
1813
  const target = e.target;
1518
1814
  if (!target || target.closest("[data-remarq-theme]")) return;
1519
- overlay.show(target);
1815
+ if (spacingMode) {
1816
+ overlay.show(target);
1817
+ overlay.hideHighlight();
1818
+ spacingOverlay.show(target);
1819
+ } else {
1820
+ overlay.show(target);
1821
+ }
1520
1822
  overlay.updateTooltipPosition(e.clientX, e.clientY);
1521
1823
  }
1522
1824
  function handleInspectKeydown(e) {
1825
+ var _a, _b;
1826
+ const tag = (_a = e.target) == null ? void 0 : _a.tagName;
1827
+ if (tag === "INPUT" || tag === "TEXTAREA" || ((_b = e.target) == null ? void 0 : _b.isContentEditable)) return;
1523
1828
  if (e.key === "Escape" && inspecting) {
1524
1829
  setInspecting(false);
1525
1830
  overlay.hide();
1831
+ spacingOverlay.hide();
1832
+ }
1833
+ if (e.key === "s" && inspecting) {
1834
+ spacingMode = !spacingMode;
1835
+ toolbar.setSpacingActive(spacingMode);
1836
+ if (!spacingMode) spacingOverlay.hide();
1526
1837
  }
1527
1838
  }
1528
1839
  function setInspecting(value) {
1529
1840
  inspecting = value;
1530
1841
  toolbar.setInspectActive(value);
1531
- if (!value) overlay.hide();
1842
+ toolbar.setSpacingEnabled(value);
1843
+ if (!value) {
1844
+ overlay.hide();
1845
+ spacingOverlay == null ? void 0 : spacingOverlay.hide();
1846
+ spacingMode = false;
1847
+ toolbar.setSpacingActive(false);
1848
+ }
1532
1849
  }
1533
1850
  function handleMarkerClick(annotationId) {
1534
1851
  var _a;
@@ -1660,6 +1977,7 @@ var WebRemarq = {
1660
1977
  storage = new AnnotationStorage();
1661
1978
  themeManager = new ThemeManager(document.body, options.theme);
1662
1979
  overlay = new Overlay(themeManager.container);
1980
+ spacingOverlay = new SpacingOverlay(themeManager.container);
1663
1981
  popup = new Popup(themeManager.container);
1664
1982
  markers = new MarkerManager(themeManager.container, handleMarkerClick);
1665
1983
  detachedPanel = new DetachedPanel(themeManager.container, (id) => {
@@ -1669,6 +1987,12 @@ var WebRemarq = {
1669
1987
  });
1670
1988
  toolbar = new Toolbar(themeManager.container, {
1671
1989
  onInspect: () => setInspecting(!inspecting),
1990
+ onSpacingToggle: () => {
1991
+ if (!inspecting) return;
1992
+ spacingMode = !spacingMode;
1993
+ toolbar.setSpacingActive(spacingMode);
1994
+ if (!spacingMode) spacingOverlay.hide();
1995
+ },
1672
1996
  onCopy: copyToClipboard,
1673
1997
  onExportMd: exportMarkdown,
1674
1998
  onExportJson: exportJSON,
@@ -1718,11 +2042,13 @@ var WebRemarq = {
1718
2042
  detachedPanel == null ? void 0 : detachedPanel.destroy();
1719
2043
  popup == null ? void 0 : popup.destroy();
1720
2044
  overlay == null ? void 0 : overlay.destroy();
2045
+ spacingOverlay == null ? void 0 : spacingOverlay.destroy();
1721
2046
  toolbar == null ? void 0 : toolbar.destroy();
1722
2047
  themeManager == null ? void 0 : themeManager.destroy();
1723
2048
  removeStyles();
1724
2049
  elementCache.clear();
1725
2050
  inspecting = false;
2051
+ spacingMode = false;
1726
2052
  initialized = false;
1727
2053
  } catch (err) {
1728
2054
  console.error("[web-remarq] Destroy failed:", err);