@ohhwells/bridge 0.1.55-next.158 → 0.1.55-next.161

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
@@ -347,6 +347,17 @@ var FEATURE_LINE_CSS = [
347
347
  function textAttrs(ctx, path) {
348
348
  return ctx.keyFor ? { "data-ohw-key": ctx.keyFor(path), "data-ohw-editable": "text" } : {};
349
349
  }
350
+ var AI_RESPONSIVE_CSS = [
351
+ "@media (max-width: 960px) {",
352
+ " [data-ai-responsive] [data-ai-grid] { grid-template-columns: repeat(2, 1fr) !important; }",
353
+ ' [data-ai-responsive] [data-ai-grid="1"] { grid-template-columns: 1fr !important; }',
354
+ "}",
355
+ "@media (max-width: 640px) {",
356
+ " [data-ai-responsive] [data-ai-grid] { grid-template-columns: 1fr !important; }",
357
+ " [data-ai-responsive] [data-ai-columns] > * { grid-column: 1 / -1 !important; }",
358
+ " [data-ai-responsive] [data-ai-section-inner] { padding-left: 20px !important; padding-right: 20px !important; }",
359
+ "}"
360
+ ].join("\n");
350
361
  var TRANSPARENT_PX = "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7";
351
362
  function MediaBox({
352
363
  refValue,
@@ -1089,6 +1100,7 @@ function Carousel({ items, itemsPerRow, ctx }) {
1089
1100
  children: pageGroups.map((group, p) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1090
1101
  "div",
1091
1102
  {
1103
+ "data-ai-grid": String(itemsPerRow),
1092
1104
  style: {
1093
1105
  flex: "0 0 100%",
1094
1106
  display: "grid",
@@ -1192,6 +1204,7 @@ function CollectionBlock({ node, ctx, path }) {
1192
1204
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1193
1205
  "div",
1194
1206
  {
1207
+ "data-ai-grid": String(itemsPerRow),
1195
1208
  style: {
1196
1209
  display: "grid",
1197
1210
  gridTemplateColumns: `repeat(${itemsPerRow}, 1fr)`,
@@ -1355,6 +1368,7 @@ function AiTreeRenderer({ tree, brand, resolveMedia, editKeyPrefix }) {
1355
1368
  {
1356
1369
  "data-ai-section": tree.tag ?? "",
1357
1370
  ...bgAttrs,
1371
+ "data-ai-responsive": "",
1358
1372
  style: {
1359
1373
  position: "relative",
1360
1374
  padding: `${pad}px 0`,
@@ -1364,10 +1378,12 @@ function AiTreeRenderer({ tree, brand, resolveMedia, editKeyPrefix }) {
1364
1378
  backgroundPosition: "center"
1365
1379
  },
1366
1380
  children: [
1381
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("style", { children: AI_RESPONSIVE_CSS }),
1367
1382
  isOverlay && backgroundUrl && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { position: "absolute", inset: 0, background: `rgba(255,255,255,${overlayAlpha})` } }),
1368
1383
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1369
1384
  "div",
1370
1385
  {
1386
+ "data-ai-section-inner": "",
1371
1387
  style: {
1372
1388
  position: "relative",
1373
1389
  maxWidth: AI_TREE_TOKENS.sectionMaxWidth,
@@ -1378,6 +1394,7 @@ function AiTreeRenderer({ tree, brand, resolveMedia, editKeyPrefix }) {
1378
1394
  children: tree.rows.map((row, r2) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1379
1395
  "div",
1380
1396
  {
1397
+ "data-ai-columns": "",
1381
1398
  style: {
1382
1399
  display: "grid",
1383
1400
  gridTemplateColumns: "repeat(12, 1fr)",
@@ -6304,6 +6321,7 @@ var FOCUS_RING = `0 0 0 4px color-mix(in srgb, ${PRIMARY} 12%, transparent)`;
6304
6321
  var DRAG_SHADOW = "0px 4px 6px -1px rgba(0, 0, 0, 0.1), 0px 2px 4px -2px rgba(0, 0, 0, 0.1)";
6305
6322
  var TOOLBAR_EDGE_MARGIN = 4;
6306
6323
  var SELECTION_CHROME_GAP = 4;
6324
+ var HOVER_CHROME_GAP = 2;
6307
6325
  var TOOLBAR_STROKE_GAP = 4;
6308
6326
  function getChromeZIndex(state) {
6309
6327
  switch (state) {
@@ -6459,10 +6477,11 @@ function ItemInteractionLayer({
6459
6477
  onItemPointerDown,
6460
6478
  onItemClick,
6461
6479
  itemDragSurface = true,
6462
- chromeGap = SELECTION_CHROME_GAP,
6480
+ chromeGap,
6463
6481
  className
6464
6482
  }) {
6465
6483
  if (state === "default") return null;
6484
+ const gap = chromeGap ?? (state === "hover" ? HOVER_CHROME_GAP : SELECTION_CHROME_GAP);
6466
6485
  const isActive = state === "active-top" || state === "active-bottom";
6467
6486
  const isDragging = state === "dragging";
6468
6487
  const showToolbar = isActive && toolbar;
@@ -6478,10 +6497,10 @@ function ItemInteractionLayer({
6478
6497
  className: cn("pointer-events-none", className),
6479
6498
  style: {
6480
6499
  position: "fixed",
6481
- top: rect.top - chromeGap,
6482
- left: rect.left - chromeGap,
6483
- width: rect.width + chromeGap * 2,
6484
- height: rect.height + chromeGap * 2,
6500
+ top: rect.top - gap,
6501
+ left: rect.left - gap,
6502
+ width: rect.width + gap * 2,
6503
+ height: rect.height + gap * 2,
6485
6504
  zIndex: getChromeZIndex(state)
6486
6505
  },
6487
6506
  children: [
@@ -7141,7 +7160,10 @@ function AiSectionOverlay({
7141
7160
  border: `2px solid ${PRIMARY2}`,
7142
7161
  borderRadius: edgeAwareRadius(reviewRect),
7143
7162
  zIndex: 2147483200,
7144
- // The veil itself: swallows clicks so the section stays locked until decided.
7163
+ // The veil itself: swallows clicks so the section stays locked until decided. This
7164
+ // stopPropagation only guards the bubble phase; the bridge's capture-phase click
7165
+ // handlers additionally bail on `[data-ohw-ai-review]`, without which a click on
7166
+ // Accept/Discard resolves to the media beneath and opens the file picker.
7145
7167
  background: "rgba(8, 133, 254, 0.04)",
7146
7168
  pointerEvents: "auto",
7147
7169
  cursor: "default"
@@ -10139,6 +10161,7 @@ function ensureIconSlot(item) {
10139
10161
  // src/lib/footer-items.ts
10140
10162
  var FOOTER_ORDER_KEY = "__ohw_footer_order";
10141
10163
  var MAX_FOOTER_COLUMNS = 18;
10164
+ var MAX_FOOTER_ITEMS_PER_COLUMN = 7;
10142
10165
  var FOOTER_HREF_RE = /^footer-(\d+)-(\d+)-href$/;
10143
10166
  function parseFooterHrefKey(key) {
10144
10167
  if (!key) return null;
@@ -10356,6 +10379,13 @@ function getNextFooterColumnIndex() {
10356
10379
  function canAddFooterColumn() {
10357
10380
  return listFooterColumns().length < MAX_FOOTER_COLUMNS;
10358
10381
  }
10382
+ function canAddFooterItem(column) {
10383
+ return listFooterLinksInColumn(column).length < MAX_FOOTER_ITEMS_PER_COLUMN;
10384
+ }
10385
+ function resolveFooterColumnForAdd(selected) {
10386
+ if (selected.hasAttribute("data-ohw-footer-col")) return selected;
10387
+ return selected.closest("[data-ohw-footer-col]") ?? (listFooterColumns().includes(selected) ? selected : null);
10388
+ }
10359
10389
  function buildFooterHeading(colIndex, text) {
10360
10390
  const heading = document.createElement("p");
10361
10391
  heading.setAttribute("data-ohw-editable", "text");
@@ -11408,6 +11438,7 @@ function FloatingPanel({
11408
11438
  e.stopPropagation();
11409
11439
  const el = e.currentTarget;
11410
11440
  el.setPointerCapture(e.pointerId);
11441
+ document.documentElement.setAttribute("data-ohw-panel-dragging", "");
11411
11442
  dragRef.current = {
11412
11443
  pointerId: e.pointerId,
11413
11444
  startX: e.clientX,
@@ -11440,11 +11471,17 @@ function FloatingPanel({
11440
11471
  const drag = dragRef.current;
11441
11472
  if (!drag || drag.pointerId !== e.pointerId) return;
11442
11473
  dragRef.current = null;
11474
+ document.documentElement.removeAttribute("data-ohw-panel-dragging");
11443
11475
  try {
11444
11476
  e.currentTarget.releasePointerCapture(e.pointerId);
11445
11477
  } catch {
11446
11478
  }
11447
11479
  }, []);
11480
+ (0, import_react13.useEffect)(() => {
11481
+ if (open) return;
11482
+ document.documentElement.removeAttribute("data-ohw-panel-dragging");
11483
+ }, [open]);
11484
+ (0, import_react13.useEffect)(() => () => document.documentElement.removeAttribute("data-ohw-panel-dragging"), []);
11448
11485
  if (!open) return null;
11449
11486
  return /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(
11450
11487
  "div",
@@ -13351,6 +13388,11 @@ function isNavbarLinksContainer2(el) {
13351
13388
  function getFooterColumn(el) {
13352
13389
  return el.closest("[data-ohw-footer-col], [data-ohw-footer-column]");
13353
13390
  }
13391
+ function isFooterAddItemDisabled(selected) {
13392
+ if (!selected) return false;
13393
+ const column = resolveFooterColumnForAdd(selected);
13394
+ return column ? !canAddFooterItem(column) : false;
13395
+ }
13354
13396
  function resolveFooterColumnSelectionTarget(target, clientX, clientY) {
13355
13397
  if (getNavigationItemAnchor(target)) return null;
13356
13398
  const column = getFooterColumn(target);
@@ -13664,6 +13706,9 @@ var ICONS = {
13664
13706
  var SELECTION_CHROME_GAP2 = 4;
13665
13707
  var TOOLBAR_STROKE_GAP2 = 4;
13666
13708
  var TOOLBAR_OFFSET_FROM_ELEMENT = SELECTION_CHROME_GAP2 + TOOLBAR_STROKE_GAP2;
13709
+ var HOVER_STROKE_WIDTH = 1.5;
13710
+ var TEXT_HOVER_SIDE_PAD = 4;
13711
+ var CHROME_PRIMARY = `var(--ohw-primary, ${PRIMARY3})`;
13667
13712
  var TOOLBAR_GROUPS = [
13668
13713
  [
13669
13714
  { cmd: "bold", title: "Bold" },
@@ -14171,6 +14216,28 @@ function OhhwellsBridge() {
14171
14216
  const [hoveredNavContainerRect, setHoveredNavContainerRect] = (0, import_react16.useState)(null);
14172
14217
  const hoveredItemElRef = (0, import_react16.useRef)(null);
14173
14218
  const [hoveredItemRect, setHoveredItemRect] = (0, import_react16.useState)(null);
14219
+ const [hoveredTextRect, setHoveredTextRect] = (0, import_react16.useState)(null);
14220
+ (0, import_react16.useEffect)(() => {
14221
+ const sync = () => {
14222
+ const el = document.querySelector(
14223
+ "[data-ohw-hovered]:not([contenteditable]):not([data-ohw-href-key])"
14224
+ );
14225
+ const target = el && !el.closest("[data-ohw-href-key]") ? el : null;
14226
+ if (!target) {
14227
+ setHoveredTextRect(null);
14228
+ return;
14229
+ }
14230
+ const r2 = target.getBoundingClientRect();
14231
+ setHoveredTextRect(new DOMRect(r2.x - TEXT_HOVER_SIDE_PAD, r2.y, r2.width + TEXT_HOVER_SIDE_PAD * 2, r2.height));
14232
+ };
14233
+ const observer = new MutationObserver(sync);
14234
+ observer.observe(document.documentElement, {
14235
+ attributes: true,
14236
+ attributeFilter: ["data-ohw-hovered"],
14237
+ subtree: true
14238
+ });
14239
+ return () => observer.disconnect();
14240
+ }, []);
14174
14241
  const siblingHintElRef = (0, import_react16.useRef)(null);
14175
14242
  const [siblingHintRect, setSiblingHintRect] = (0, import_react16.useState)(null);
14176
14243
  const [siblingHintRects, setSiblingHintRects] = (0, import_react16.useState)([]);
@@ -14663,10 +14730,16 @@ function OhhwellsBridge() {
14663
14730
  return;
14664
14731
  }
14665
14732
  if (toolbarVariantRef.current === "select-frame" && isFooterFrameSelection) {
14666
- if (!selected.hasAttribute("data-ohw-footer-col") && !selected.closest("[data-ohw-footer-col]")) {
14667
- }
14668
- const column = (selected.hasAttribute("data-ohw-footer-col") ? selected : null) ?? selected.closest("[data-ohw-footer-col]") ?? (listFooterColumns().includes(selected) ? selected : null);
14733
+ const column = resolveFooterColumnForAdd(selected);
14669
14734
  if (!column) return;
14735
+ if (!canAddFooterItem(column)) {
14736
+ postToParent2({
14737
+ type: "ow:toast",
14738
+ title: `Maximum ${MAX_FOOTER_ITEMS_PER_COLUMN} items per column`,
14739
+ toastType: "error"
14740
+ });
14741
+ return;
14742
+ }
14670
14743
  const result2 = insertFooterItem(column, "/", "New link", null);
14671
14744
  applyLinkByKey(result2.hrefKey, result2.href);
14672
14745
  document.querySelectorAll(`[data-ohw-key="${result2.labelKey}"]`).forEach((el) => {
@@ -15645,7 +15718,7 @@ function OhhwellsBridge() {
15645
15718
  reconcileSocialsFromContent(content);
15646
15719
  applySocialsDisplayFromContent(content);
15647
15720
  document.querySelectorAll("footer [data-ohw-href-key]").forEach((el) => {
15648
- if (isFooterHrefKey(el.getAttribute("data-ohw-href-key"))) {
15721
+ if (isFooterHrefKey(el.getAttribute("data-ohw-href-key")) || getSocialItem(el)) {
15649
15722
  disableNativeHrefDrag(el);
15650
15723
  }
15651
15724
  });
@@ -15773,9 +15846,12 @@ function OhhwellsBridge() {
15773
15846
  [data-ohw-editable="video"], [data-ohw-editable="video"] *,
15774
15847
  [data-ohw-editable="bg-image"], [data-ohw-editable="bg-image"] * { cursor: pointer !important; }
15775
15848
  [data-ohw-editable="link"], [data-ohw-editable="link"] * { cursor: pointer !important; }
15849
+ /* Text hover chrome is drawn by the overlay (see hoveredTextRect) \u2014 the CSS outline
15850
+ that used to draw it dashes denser than the overlay border, so identical specs
15851
+ still read as two different frames (OHH-695). The attribute stays: hover paths
15852
+ and suppression rules key off it. */
15776
15853
  [data-ohw-hovered]:not([contenteditable]):not([data-ohw-href-key]) {
15777
- outline: 2px dashed ${PRIMARY3} !important;
15778
- outline-offset: 4px;
15854
+ outline: none !important;
15779
15855
  }
15780
15856
  [data-ohw-href-key] [data-ohw-hovered],
15781
15857
  [data-ohw-href-key][data-ohw-hovered],
@@ -15828,8 +15904,11 @@ function OhhwellsBridge() {
15828
15904
  stateViews.textContent = `
15829
15905
  [data-ohw-state-view]:not([data-ohw-state-view="default"]) { display: none; }
15830
15906
  [data-ohw-state-view="default"] [data-ohw-editable] { pointer-events: auto !important; }
15831
- [data-ohw-state-hovered] { outline: 2px dashed ${PRIMARY3} !important; outline-offset: 4px; }
15907
+ [data-ohw-state-hovered] { outline: ${HOVER_STROKE_WIDTH}px dashed ${CHROME_PRIMARY} !important; outline-offset: ${HOVER_CHROME_GAP}px; }
15832
15908
  [data-ohw-state-hovered]:has([data-ohw-hovered]) { outline: none !important; }
15909
+ /* :has() only sees descendants \u2014 when the card itself is the hovered text, the overlay
15910
+ already frames it, and the card outline doubled it (OHH-695). */
15911
+ [data-ohw-state-hovered][data-ohw-hovered] { outline: none !important; }
15833
15912
  `;
15834
15913
  document.head.appendChild(base);
15835
15914
  document.head.appendChild(forceHover);
@@ -15847,6 +15926,7 @@ function OhhwellsBridge() {
15847
15926
  return;
15848
15927
  }
15849
15928
  const target = e.target;
15929
+ if (target.closest("[data-ohw-ai-review]")) return;
15850
15930
  if (target.closest("[data-ohw-toolbar]")) return;
15851
15931
  if (target.closest("[data-ohw-state-toggle]")) return;
15852
15932
  if (target.closest("[data-ohw-max-badge]")) return;
@@ -16078,6 +16158,7 @@ function OhhwellsBridge() {
16078
16158
  };
16079
16159
  const handleDblClick = (e) => {
16080
16160
  const target = e.target;
16161
+ if (target.closest("[data-ohw-ai-review]")) return;
16081
16162
  if (target.closest("[data-ohw-toolbar]")) return;
16082
16163
  if (target.closest("[data-ohw-state-toggle]")) return;
16083
16164
  if (target.closest("[data-ohw-max-badge]")) return;
@@ -16110,7 +16191,7 @@ function OhhwellsBridge() {
16110
16191
  setHoveredNavContainerRect(null);
16111
16192
  return;
16112
16193
  }
16113
- if (isInsideFloatingPanel(target) || isPointOverFloatingPanel(e.clientX, e.clientY)) {
16194
+ if (document.documentElement.hasAttribute("data-ohw-panel-dragging") || isInsideFloatingPanel(target) || isPointOverFloatingPanel(e.clientX, e.clientY)) {
16114
16195
  hoveredItemElRef.current = null;
16115
16196
  setHoveredItemRect(null);
16116
16197
  hoveredNavContainerRef.current = null;
@@ -16206,7 +16287,6 @@ function OhhwellsBridge() {
16206
16287
  hoveredNavContainerRef.current = null;
16207
16288
  setHoveredNavContainerRect(null);
16208
16289
  hoveredItemElRef.current = editable;
16209
- setHoveredItemRect(editable.getBoundingClientRect());
16210
16290
  }
16211
16291
  }
16212
16292
  }
@@ -16825,7 +16905,7 @@ function OhhwellsBridge() {
16825
16905
  };
16826
16906
  const handleMouseMove = (e) => {
16827
16907
  const { clientX, clientY } = e;
16828
- if (floatingPanelOpenRef.current && isPointOverFloatingPanel(clientX, clientY)) {
16908
+ if (document.documentElement.hasAttribute("data-ohw-panel-dragging") || floatingPanelOpenRef.current && isPointOverFloatingPanel(clientX, clientY)) {
16829
16909
  hoveredItemElRef.current = null;
16830
16910
  setHoveredItemRect(null);
16831
16911
  hoveredNavContainerRef.current = null;
@@ -16846,7 +16926,7 @@ function OhhwellsBridge() {
16846
16926
  if (e.data?.type !== "ow:pointer-sync") return;
16847
16927
  const { clientX, clientY } = e.data;
16848
16928
  if (typeof clientX !== "number" || typeof clientY !== "number") return;
16849
- if (floatingPanelOpenRef.current && isPointOverFloatingPanel(clientX, clientY)) {
16929
+ if (document.documentElement.hasAttribute("data-ohw-panel-dragging") || floatingPanelOpenRef.current && isPointOverFloatingPanel(clientX, clientY)) {
16850
16930
  dismissImageHover();
16851
16931
  clearImageHover();
16852
16932
  return;
@@ -17949,7 +18029,7 @@ function OhhwellsBridge() {
17949
18029
  if (footerDragRef.current) return;
17950
18030
  const target = e.target;
17951
18031
  if (!target) return;
17952
- if (target.closest('[data-ohw-drag-handle-container], [data-slot="drag-handle"], [data-ohw-toolbar], [data-ohw-item-toolbar-anchor], [data-ohw-link-popover-root]')) {
18032
+ if (target.closest('[data-ohw-drag-handle-container], [data-slot="drag-handle"], [data-ohw-toolbar], [data-ohw-item-toolbar-anchor], [data-ohw-link-popover-root], [data-ohw-floating-panel]')) {
17953
18033
  return;
17954
18034
  }
17955
18035
  if (target.closest("[data-ohw-item-drag-surface]")) return;
@@ -18593,6 +18673,7 @@ function OhhwellsBridge() {
18593
18673
  )),
18594
18674
  hoveredNavContainerRect && (toolbarVariant !== "select-frame" || isFooterFrameSelection) && !linkPopover && !isItemDragging && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(ItemInteractionLayer, { rect: hoveredNavContainerRect, state: "hover" }),
18595
18675
  hoveredItemRect && !linkPopover && !hoveredNavContainerRect && !isItemDragging && hoveredItemElRef.current !== selectedElRef.current && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(ItemInteractionLayer, { rect: hoveredItemRect, state: "hover" }),
18676
+ hoveredTextRect && !isItemDragging && toolbarVariant !== "rich-text" && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(ItemInteractionLayer, { rect: hoveredTextRect, state: "hover" }),
18596
18677
  toolbarVariant === "select-frame" && toolbarRect && !linkPopover && !isItemDragging && !isFooterFrameSelection && selectedElRef.current && isNavbarLinksContainer2(selectedElRef.current) && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(NavbarContainerChrome, { rect: toolbarRect, onAdd: handleAddTopLevelNavItem }),
18597
18678
  toolbarVariant === "select-frame" && toolbarRect && !linkPopover && !isItemDragging && !isFooterFrameSelection && selectedElRef.current && isFooterLinksContainer(selectedElRef.current) && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
18598
18679
  FooterContainerChrome,
@@ -18633,7 +18714,7 @@ function OhhwellsBridge() {
18633
18714
  onSelectParent: handleSelectParent,
18634
18715
  onDuplicate: handleDuplicateSelected,
18635
18716
  onDelete: handleDeleteSelected,
18636
- addItemDisabled: false,
18717
+ addItemDisabled: isFooterFrameSelection && isFooterAddItemDisabled(selectedElRef.current),
18637
18718
  editLinkDisabled: false,
18638
18719
  moreDisabled: false,
18639
18720
  duplicateDisabled: isFooterFrameSelection || selectedIsSocialsRow,