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.js CHANGED
@@ -712,6 +712,61 @@ var CSS = `
712
712
  .remarq-toast-fade {
713
713
  opacity: 0;
714
714
  }
715
+
716
+ .remarq-spacing {
717
+ position: fixed;
718
+ top: 0;
719
+ left: 0;
720
+ pointer-events: none;
721
+ z-index: 2147483646;
722
+ }
723
+
724
+ .remarq-spacing-margin {
725
+ position: fixed;
726
+ background: rgba(249, 115, 22, 0.2);
727
+ pointer-events: none;
728
+ }
729
+
730
+ .remarq-spacing-padding {
731
+ position: fixed;
732
+ background: rgba(34, 197, 94, 0.2);
733
+ pointer-events: none;
734
+ }
735
+
736
+ .remarq-spacing-content {
737
+ position: fixed;
738
+ background: rgba(59, 130, 246, 0.15);
739
+ border: 1px dashed rgba(59, 130, 246, 0.5);
740
+ pointer-events: none;
741
+ }
742
+
743
+ .remarq-spacing-gap {
744
+ position: fixed;
745
+ background: rgba(168, 85, 247, 0.25);
746
+ border: 1px dashed rgba(168, 85, 247, 0.5);
747
+ pointer-events: none;
748
+ display: flex;
749
+ align-items: center;
750
+ justify-content: center;
751
+ }
752
+
753
+ .remarq-spacing-label {
754
+ position: fixed;
755
+ font-size: 11px;
756
+ font-weight: 700;
757
+ pointer-events: none;
758
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
759
+ line-height: 1;
760
+ text-shadow: 0 0 3px var(--remarq-bg), 0 0 3px var(--remarq-bg);
761
+ }
762
+
763
+ .remarq-spacing-label-margin { color: #f97316; }
764
+ .remarq-spacing-label-padding { color: #22c55e; }
765
+ .remarq-spacing-label-content { color: #3b82f6; font-size: 10px; }
766
+ .remarq-spacing-label-gap { color: #a855f7; font-size: 10px; }
767
+
768
+ .remarq-toolbar-btn:disabled { opacity: 0.3; cursor: default; }
769
+ .remarq-toolbar-btn:disabled:hover { background: transparent; }
715
770
  `;
716
771
  function injectStyles() {
717
772
  if (document.querySelector(`style[${STYLES_ID}]`)) return;
@@ -783,6 +838,7 @@ var ThemeManager = class {
783
838
  // src/ui/toolbar.ts
784
839
  var ICONS = {
785
840
  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>',
841
+ 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>',
786
842
  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>',
787
843
  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>',
788
844
  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>',
@@ -804,6 +860,8 @@ var Toolbar = class {
804
860
  this.badgeEl.className = "remarq-badge";
805
861
  this.badgeEl.style.display = "none";
806
862
  this.inspectBtn.appendChild(this.badgeEl);
863
+ this.spacingBtn = this.createButton("spacing", ICONS.spacing, () => callbacks.onSpacingToggle());
864
+ this.spacingBtn.disabled = true;
807
865
  const copyBtn = this.createButton("copy", ICONS.copy, () => callbacks.onCopy());
808
866
  const exportBtn = this.createButton("export", ICONS.export, (e) => this.toggleExportMenu(e));
809
867
  this.fileInput = document.createElement("input");
@@ -818,8 +876,9 @@ var Toolbar = class {
818
876
  const clearBtn = this.createButton("clear", ICONS.clear, () => callbacks.onClear());
819
877
  const themeBtn = this.createButton("theme", ICONS.theme, () => callbacks.onThemeToggle());
820
878
  const minimizeBtn = this.createButton("minimize", ICONS.minimize, () => this.toggleMinimize());
821
- this.buttons = [this.inspectBtn, copyBtn, exportBtn, importBtn, clearBtn, themeBtn];
879
+ this.buttons = [this.inspectBtn, this.spacingBtn, copyBtn, exportBtn, importBtn, clearBtn, themeBtn];
822
880
  this.toolbarEl.appendChild(this.inspectBtn);
881
+ this.toolbarEl.appendChild(this.spacingBtn);
823
882
  this.toolbarEl.appendChild(copyBtn);
824
883
  this.toolbarEl.appendChild(exportBtn);
825
884
  this.toolbarEl.appendChild(importBtn);
@@ -832,6 +891,15 @@ var Toolbar = class {
832
891
  setInspectActive(active) {
833
892
  this.inspectBtn.classList.toggle("remarq-active", active);
834
893
  }
894
+ setSpacingActive(active) {
895
+ this.spacingBtn.classList.toggle("remarq-active", active);
896
+ }
897
+ setSpacingEnabled(enabled) {
898
+ this.spacingBtn.disabled = !enabled;
899
+ if (!enabled) {
900
+ this.spacingBtn.classList.remove("remarq-active");
901
+ }
902
+ }
835
903
  setBadgeCount(count) {
836
904
  this.badgeEl.textContent = String(count);
837
905
  this.badgeEl.style.display = count > 0 ? "flex" : "none";
@@ -927,6 +995,9 @@ var Overlay = class {
927
995
  this.tooltipEl.style.left = `${x + 12}px`;
928
996
  this.tooltipEl.style.top = `${y - 28}px`;
929
997
  }
998
+ hideHighlight() {
999
+ this.overlayEl.style.display = "none";
1000
+ }
930
1001
  hide() {
931
1002
  this.overlayEl.style.display = "none";
932
1003
  this.tooltipEl.style.display = "none";
@@ -974,6 +1045,229 @@ function getDirectText(el) {
974
1045
  return text.slice(0, 30);
975
1046
  }
976
1047
 
1048
+ // src/ui/spacing-overlay.ts
1049
+ function parsePx(value) {
1050
+ return parseFloat(value) || 0;
1051
+ }
1052
+ var SpacingOverlay = class {
1053
+ constructor(parent) {
1054
+ this.parent = parent;
1055
+ this.labels = [];
1056
+ this.gapEls = [];
1057
+ this.lastTarget = null;
1058
+ this.containerEl = document.createElement("div");
1059
+ this.containerEl.className = "remarq-spacing";
1060
+ this.containerEl.style.display = "none";
1061
+ this.marginEl = document.createElement("div");
1062
+ this.marginEl.className = "remarq-spacing-margin";
1063
+ this.paddingEl = document.createElement("div");
1064
+ this.paddingEl.className = "remarq-spacing-padding";
1065
+ this.contentEl = document.createElement("div");
1066
+ this.contentEl.className = "remarq-spacing-content";
1067
+ this.containerEl.appendChild(this.marginEl);
1068
+ this.containerEl.appendChild(this.paddingEl);
1069
+ this.containerEl.appendChild(this.contentEl);
1070
+ parent.appendChild(this.containerEl);
1071
+ }
1072
+ show(target) {
1073
+ if (target === this.lastTarget) return;
1074
+ this.lastTarget = target;
1075
+ try {
1076
+ const rect = target.getBoundingClientRect();
1077
+ const cs = window.getComputedStyle(target);
1078
+ const margin = this.readSides(cs, "margin");
1079
+ const padding = this.readSides(cs, "padding");
1080
+ const border = this.readBorderSides(cs);
1081
+ const marginBox = {
1082
+ top: rect.top - margin.top,
1083
+ left: rect.left - margin.left,
1084
+ width: rect.width + margin.left + margin.right,
1085
+ height: rect.height + margin.top + margin.bottom
1086
+ };
1087
+ const paddingBox = {
1088
+ top: rect.top,
1089
+ left: rect.left,
1090
+ width: rect.width,
1091
+ height: rect.height
1092
+ };
1093
+ const contentBox = {
1094
+ top: rect.top + border.top + padding.top,
1095
+ left: rect.left + border.left + padding.left,
1096
+ width: rect.width - border.left - border.right - padding.left - padding.right,
1097
+ height: rect.height - border.top - border.bottom - padding.top - padding.bottom
1098
+ };
1099
+ this.positionEl(this.marginEl, marginBox);
1100
+ this.positionEl(this.paddingEl, paddingBox);
1101
+ this.positionEl(this.contentEl, contentBox);
1102
+ this.clearLabels();
1103
+ this.clearGaps();
1104
+ this.addSideLabels(margin, marginBox, paddingBox, "margin");
1105
+ this.addSideLabels(padding, paddingBox, contentBox, "padding");
1106
+ if (contentBox.width > 40 && contentBox.height > 14) {
1107
+ this.addLabel(
1108
+ `${Math.round(contentBox.width)} \xD7 ${Math.round(contentBox.height)}`,
1109
+ contentBox.top + contentBox.height / 2 - 6,
1110
+ contentBox.left + contentBox.width / 2,
1111
+ "content"
1112
+ );
1113
+ }
1114
+ this.showGaps(target);
1115
+ this.containerEl.style.display = "block";
1116
+ } catch (e) {
1117
+ this.hide();
1118
+ }
1119
+ }
1120
+ hide() {
1121
+ this.containerEl.style.display = "none";
1122
+ this.lastTarget = null;
1123
+ this.clearLabels();
1124
+ this.clearGaps();
1125
+ }
1126
+ destroy() {
1127
+ this.clearLabels();
1128
+ this.clearGaps();
1129
+ this.containerEl.remove();
1130
+ }
1131
+ readSides(cs, prop) {
1132
+ return {
1133
+ top: parsePx(cs[`${prop}Top`]),
1134
+ right: parsePx(cs[`${prop}Right`]),
1135
+ bottom: parsePx(cs[`${prop}Bottom`]),
1136
+ left: parsePx(cs[`${prop}Left`])
1137
+ };
1138
+ }
1139
+ readBorderSides(cs) {
1140
+ return {
1141
+ top: parsePx(cs.borderTopWidth),
1142
+ right: parsePx(cs.borderRightWidth),
1143
+ bottom: parsePx(cs.borderBottomWidth),
1144
+ left: parsePx(cs.borderLeftWidth)
1145
+ };
1146
+ }
1147
+ positionEl(el, box) {
1148
+ el.style.top = `${box.top}px`;
1149
+ el.style.left = `${box.left}px`;
1150
+ el.style.width = `${Math.max(0, box.width)}px`;
1151
+ el.style.height = `${Math.max(0, box.height)}px`;
1152
+ }
1153
+ addSideLabels(sides, outerBox, innerBox, type) {
1154
+ if (sides.top > 0) {
1155
+ const y = outerBox.top + (innerBox.top - outerBox.top) / 2 - 6;
1156
+ const x = outerBox.left + outerBox.width / 2;
1157
+ this.addLabel(String(Math.round(sides.top)), y, x, type);
1158
+ }
1159
+ if (sides.bottom > 0) {
1160
+ const innerBottom = innerBox.top + innerBox.height;
1161
+ const outerBottom = outerBox.top + outerBox.height;
1162
+ const y = innerBottom + (outerBottom - innerBottom) / 2 - 6;
1163
+ const x = outerBox.left + outerBox.width / 2;
1164
+ this.addLabel(String(Math.round(sides.bottom)), y, x, type);
1165
+ }
1166
+ if (sides.left > 0) {
1167
+ const y = outerBox.top + outerBox.height / 2 - 6;
1168
+ const x = outerBox.left + (innerBox.left - outerBox.left) / 2;
1169
+ this.addLabel(String(Math.round(sides.left)), y, x, type);
1170
+ }
1171
+ if (sides.right > 0) {
1172
+ const innerRight = innerBox.left + innerBox.width;
1173
+ const outerRight = outerBox.left + outerBox.width;
1174
+ const y = outerBox.top + outerBox.height / 2 - 6;
1175
+ const x = innerRight + (outerRight - innerRight) / 2;
1176
+ this.addLabel(String(Math.round(sides.right)), y, x, type);
1177
+ }
1178
+ }
1179
+ addLabel(text, top, left, type) {
1180
+ const label = document.createElement("div");
1181
+ label.className = `remarq-spacing-label remarq-spacing-label-${type}`;
1182
+ label.textContent = text;
1183
+ label.style.top = `${top}px`;
1184
+ label.style.left = `${left}px`;
1185
+ label.style.transform = "translateX(-50%)";
1186
+ this.containerEl.appendChild(label);
1187
+ this.labels.push(label);
1188
+ }
1189
+ clearLabels() {
1190
+ for (const label of this.labels) label.remove();
1191
+ this.labels = [];
1192
+ }
1193
+ showGaps(target) {
1194
+ const targetCs = window.getComputedStyle(target);
1195
+ if (targetCs.display.includes("flex")) {
1196
+ this.showContainerGaps(target, targetCs);
1197
+ return;
1198
+ }
1199
+ const parent = target.parentElement;
1200
+ if (!parent) return;
1201
+ const parentCs = window.getComputedStyle(parent);
1202
+ if (!parentCs.display.includes("flex")) return;
1203
+ const rowGap = parsePx(parentCs.rowGap);
1204
+ const columnGap = parsePx(parentCs.columnGap);
1205
+ const direction = parentCs.flexDirection;
1206
+ const isRow = direction === "row" || direction === "row-reverse";
1207
+ const gap = isRow ? columnGap : rowGap;
1208
+ if (gap <= 0) return;
1209
+ const children = Array.from(parent.children);
1210
+ const targetIndex = children.indexOf(target);
1211
+ if (targetIndex === -1) return;
1212
+ if (targetIndex > 0) {
1213
+ this.renderGap(children[targetIndex - 1], target, gap, isRow);
1214
+ }
1215
+ if (targetIndex < children.length - 1) {
1216
+ this.renderGap(target, children[targetIndex + 1], gap, isRow);
1217
+ }
1218
+ }
1219
+ showContainerGaps(container, cs) {
1220
+ const rowGap = parsePx(cs.rowGap);
1221
+ const columnGap = parsePx(cs.columnGap);
1222
+ const direction = cs.flexDirection;
1223
+ const isRow = direction === "row" || direction === "row-reverse";
1224
+ const gap = isRow ? columnGap : rowGap;
1225
+ if (gap <= 0) return;
1226
+ const children = Array.from(container.children);
1227
+ for (let i = 0; i < children.length - 1; i++) {
1228
+ this.renderGap(children[i], children[i + 1], gap, isRow);
1229
+ }
1230
+ }
1231
+ renderGap(before, after, gap, isRow) {
1232
+ const rectBefore = before.getBoundingClientRect();
1233
+ const rectAfter = after.getBoundingClientRect();
1234
+ const gapEl = document.createElement("div");
1235
+ gapEl.className = "remarq-spacing-gap";
1236
+ if (isRow) {
1237
+ const left = Math.min(rectBefore.right, rectAfter.right);
1238
+ const right = Math.max(rectBefore.left, rectAfter.left);
1239
+ const top = Math.min(rectBefore.top, rectAfter.top);
1240
+ const height = Math.max(rectBefore.height, rectAfter.height);
1241
+ gapEl.style.top = `${top}px`;
1242
+ gapEl.style.left = `${left}px`;
1243
+ gapEl.style.width = `${Math.abs(right - left)}px`;
1244
+ gapEl.style.height = `${height}px`;
1245
+ } else {
1246
+ const top = Math.min(rectBefore.bottom, rectAfter.bottom);
1247
+ const bottom = Math.max(rectBefore.top, rectAfter.top);
1248
+ const left = Math.min(rectBefore.left, rectAfter.left);
1249
+ const width = Math.max(rectBefore.width, rectAfter.width);
1250
+ gapEl.style.top = `${top}px`;
1251
+ gapEl.style.left = `${left}px`;
1252
+ gapEl.style.width = `${width}px`;
1253
+ gapEl.style.height = `${Math.abs(bottom - top)}px`;
1254
+ }
1255
+ if (gap >= 10) {
1256
+ const label = document.createElement("span");
1257
+ label.className = "remarq-spacing-label-gap";
1258
+ label.textContent = `gap: ${Math.round(gap)}`;
1259
+ label.style.cssText = "font-size:10px;font-weight:700;pointer-events:none;";
1260
+ gapEl.appendChild(label);
1261
+ }
1262
+ this.containerEl.appendChild(gapEl);
1263
+ this.gapEls.push(gapEl);
1264
+ }
1265
+ clearGaps() {
1266
+ for (const el of this.gapEls) el.remove();
1267
+ this.gapEls = [];
1268
+ }
1269
+ };
1270
+
977
1271
  // src/ui/popup.ts
978
1272
  var POPUP_WIDTH = 300;
979
1273
  var POPUP_MARGIN = 8;
@@ -1360,6 +1654,8 @@ var markers;
1360
1654
  var detachedPanel;
1361
1655
  var routeObserver;
1362
1656
  var inspecting = false;
1657
+ var spacingMode = false;
1658
+ var spacingOverlay;
1363
1659
  var mutationObserver = null;
1364
1660
  var unsubRoute = null;
1365
1661
  var refreshScheduled = false;
@@ -1453,7 +1749,6 @@ function handleInspectClick(e) {
1453
1749
  if (!target || target.closest("[data-remarq-theme]")) return;
1454
1750
  e.preventDefault();
1455
1751
  e.stopPropagation();
1456
- overlay.hide();
1457
1752
  setInspecting(false);
1458
1753
  const rect = target.getBoundingClientRect();
1459
1754
  popup.show(
@@ -1493,19 +1788,40 @@ function handleInspectHover(e) {
1493
1788
  if (!inspecting) return;
1494
1789
  const target = e.target;
1495
1790
  if (!target || target.closest("[data-remarq-theme]")) return;
1496
- overlay.show(target);
1791
+ if (spacingMode) {
1792
+ overlay.show(target);
1793
+ overlay.hideHighlight();
1794
+ spacingOverlay.show(target);
1795
+ } else {
1796
+ overlay.show(target);
1797
+ }
1497
1798
  overlay.updateTooltipPosition(e.clientX, e.clientY);
1498
1799
  }
1499
1800
  function handleInspectKeydown(e) {
1801
+ var _a, _b;
1802
+ const tag = (_a = e.target) == null ? void 0 : _a.tagName;
1803
+ if (tag === "INPUT" || tag === "TEXTAREA" || ((_b = e.target) == null ? void 0 : _b.isContentEditable)) return;
1500
1804
  if (e.key === "Escape" && inspecting) {
1501
1805
  setInspecting(false);
1502
1806
  overlay.hide();
1807
+ spacingOverlay.hide();
1808
+ }
1809
+ if (e.key === "s" && inspecting) {
1810
+ spacingMode = !spacingMode;
1811
+ toolbar.setSpacingActive(spacingMode);
1812
+ if (!spacingMode) spacingOverlay.hide();
1503
1813
  }
1504
1814
  }
1505
1815
  function setInspecting(value) {
1506
1816
  inspecting = value;
1507
1817
  toolbar.setInspectActive(value);
1508
- if (!value) overlay.hide();
1818
+ toolbar.setSpacingEnabled(value);
1819
+ if (!value) {
1820
+ overlay.hide();
1821
+ spacingOverlay == null ? void 0 : spacingOverlay.hide();
1822
+ spacingMode = false;
1823
+ toolbar.setSpacingActive(false);
1824
+ }
1509
1825
  }
1510
1826
  function handleMarkerClick(annotationId) {
1511
1827
  var _a;
@@ -1637,6 +1953,7 @@ var WebRemarq = {
1637
1953
  storage = new AnnotationStorage();
1638
1954
  themeManager = new ThemeManager(document.body, options.theme);
1639
1955
  overlay = new Overlay(themeManager.container);
1956
+ spacingOverlay = new SpacingOverlay(themeManager.container);
1640
1957
  popup = new Popup(themeManager.container);
1641
1958
  markers = new MarkerManager(themeManager.container, handleMarkerClick);
1642
1959
  detachedPanel = new DetachedPanel(themeManager.container, (id) => {
@@ -1646,6 +1963,12 @@ var WebRemarq = {
1646
1963
  });
1647
1964
  toolbar = new Toolbar(themeManager.container, {
1648
1965
  onInspect: () => setInspecting(!inspecting),
1966
+ onSpacingToggle: () => {
1967
+ if (!inspecting) return;
1968
+ spacingMode = !spacingMode;
1969
+ toolbar.setSpacingActive(spacingMode);
1970
+ if (!spacingMode) spacingOverlay.hide();
1971
+ },
1649
1972
  onCopy: copyToClipboard,
1650
1973
  onExportMd: exportMarkdown,
1651
1974
  onExportJson: exportJSON,
@@ -1695,11 +2018,13 @@ var WebRemarq = {
1695
2018
  detachedPanel == null ? void 0 : detachedPanel.destroy();
1696
2019
  popup == null ? void 0 : popup.destroy();
1697
2020
  overlay == null ? void 0 : overlay.destroy();
2021
+ spacingOverlay == null ? void 0 : spacingOverlay.destroy();
1698
2022
  toolbar == null ? void 0 : toolbar.destroy();
1699
2023
  themeManager == null ? void 0 : themeManager.destroy();
1700
2024
  removeStyles();
1701
2025
  elementCache.clear();
1702
2026
  inspecting = false;
2027
+ spacingMode = false;
1703
2028
  initialized = false;
1704
2029
  } catch (err) {
1705
2030
  console.error("[web-remarq] Destroy failed:", err);