@ohhwells/bridge 0.1.43 → 0.1.44

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
@@ -4303,7 +4303,8 @@ var CustomToolbar = React4.forwardRef(({ className, onMouseDown, ...props }, ref
4303
4303
  ref,
4304
4304
  "data-ohw-toolbar": "",
4305
4305
  className: cn(
4306
- "inline-flex items-center gap-1 rounded-lg border border-border bg-background p-0.5 font-sans whitespace-nowrap shadow-[0px_2px_4px_-2px_rgba(0,0,0,0.1),0px_4px_6px_-1px_rgba(0,0,0,0.1)]",
4306
+ // Figma: bg background, radius 8, gap-1, p-0.5, shadow-md no border
4307
+ "inline-flex h-8 items-center gap-1 rounded-[var(--radius,0.5rem)] bg-background p-0.5 font-sans whitespace-nowrap shadow-md",
4307
4308
  className
4308
4309
  ),
4309
4310
  onMouseDown: (e) => {
@@ -4331,8 +4332,8 @@ var CustomToolbarButton = React4.forwardRef(
4331
4332
  ref,
4332
4333
  type,
4333
4334
  className: cn(
4334
- "inline-flex shrink-0 items-center justify-center rounded p-1.5 transition-colors",
4335
- active ? "bg-primary text-primary-foreground" : "bg-transparent text-foreground hover:bg-muted disabled:cursor-not-allowed disabled:text-muted-foreground disabled:opacity-60",
4335
+ "inline-flex size-7 shrink-0 items-center justify-center rounded-[calc(var(--radius,0.5rem)-2px)] text-foreground transition-colors",
4336
+ active ? "bg-primary text-primary-foreground" : "bg-transparent hover:bg-muted disabled:cursor-not-allowed disabled:text-muted-foreground disabled:opacity-60",
4336
4337
  className
4337
4338
  ),
4338
4339
  ...props
@@ -4359,37 +4360,33 @@ function Tooltip({ ...props }) {
4359
4360
  function TooltipTrigger({ ...props }) {
4360
4361
  return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_radix_ui3.Tooltip.Trigger, { "data-slot": "tooltip-trigger", ...props });
4361
4362
  }
4362
- var arrowClassBySide = {
4363
- top: "before:bottom-0 before:left-1/2 before:-translate-x-1/2 before:translate-y-1/2",
4364
- bottom: "before:top-0 before:left-1/2 before:-translate-x-1/2 before:-translate-y-1/2",
4365
- left: "before:right-0 before:top-1/2 before:-translate-y-1/2 before:translate-x-1/2",
4366
- right: "before:left-0 before:top-1/2 before:-translate-y-1/2 before:-translate-x-1/2"
4367
- };
4368
4363
  function TooltipContent({
4369
4364
  className,
4370
- sideOffset = 6,
4365
+ sideOffset = 3,
4371
4366
  side = "bottom",
4372
4367
  children,
4368
+ hideArrow,
4373
4369
  ...props
4374
4370
  }) {
4375
- const resolvedSide = side ?? "bottom";
4376
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_radix_ui3.Tooltip.Portal, { children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
4371
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_radix_ui3.Tooltip.Portal, { children: /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
4377
4372
  import_radix_ui3.Tooltip.Content,
4378
4373
  {
4379
4374
  "data-slot": "tooltip-content",
4380
4375
  side,
4381
4376
  sideOffset,
4382
4377
  className: cn(
4383
- "relative z-[2147483647] w-fit max-w-xs rounded-md bg-foreground px-3 py-1.5 text-xs text-background shadow-md",
4384
- 'before:pointer-events-none before:absolute before:size-2 before:rotate-45 before:bg-foreground before:content-[""]',
4385
- arrowClassBySide[resolvedSide],
4386
- "animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95",
4378
+ "z-[2147483647] inline-flex w-fit max-w-xs items-center rounded-md bg-foreground px-3 py-1.5 text-xs text-background",
4379
+ "origin-[var(--radix-tooltip-content-transform-origin)] animate-in fade-in-0 zoom-in-95",
4380
+ "data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95",
4387
4381
  "data-[side=bottom]:slide-in-from-top-2 data-[side=top]:slide-in-from-bottom-2",
4388
4382
  "data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2",
4389
4383
  className
4390
4384
  ),
4391
4385
  ...props,
4392
- children
4386
+ children: [
4387
+ children,
4388
+ !hideArrow && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_radix_ui3.Tooltip.Arrow, { className: "z-[2147483647] size-2.5 translate-y-[calc(-50%-2px)] rotate-45 rounded-[2px] bg-foreground fill-foreground" })
4389
+ ]
4393
4390
  }
4394
4391
  ) });
4395
4392
  }
@@ -4406,7 +4403,7 @@ function ToolbarActionTooltip({
4406
4403
  const button = /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(CustomToolbarButton, { type: "button", "aria-label": label, disabled, ...buttonProps, children });
4407
4404
  return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(Tooltip, { children: [
4408
4405
  /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(TooltipTrigger, { asChild: true, children: disabled ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "inline-flex", children: button }) : button }),
4409
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(TooltipContent, { side, children: label })
4406
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(TooltipContent, { side, sideOffset: 3, children: label })
4410
4407
  ] });
4411
4408
  }
4412
4409
  function ItemActionToolbar({
@@ -4416,6 +4413,8 @@ function ItemActionToolbar({
4416
4413
  editLinkDisabled = false,
4417
4414
  addItemDisabled = true,
4418
4415
  moreDisabled = true,
4416
+ showAddItem = true,
4417
+ showMore = true,
4419
4418
  tooltipSide = "bottom"
4420
4419
  }) {
4421
4420
  return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(TooltipProvider, { delayDuration: 0, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(CustomToolbar, { children: [
@@ -4440,8 +4439,7 @@ function ItemActionToolbar({
4440
4439
  children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_lucide_react2.Link, { className: "size-4 shrink-0", "aria-hidden": true })
4441
4440
  }
4442
4441
  ),
4443
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(CustomToolbarDivider, {}),
4444
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
4442
+ showAddItem ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
4445
4443
  ToolbarActionTooltip,
4446
4444
  {
4447
4445
  label: "Add item",
@@ -4457,9 +4455,8 @@ function ItemActionToolbar({
4457
4455
  },
4458
4456
  children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_lucide_react2.Plus, { className: "size-4 shrink-0", "aria-hidden": true })
4459
4457
  }
4460
- ),
4461
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(CustomToolbarDivider, {}),
4462
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
4458
+ ) : null,
4459
+ showMore ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
4463
4460
  ToolbarActionTooltip,
4464
4461
  {
4465
4462
  label: "More",
@@ -4475,15 +4472,19 @@ function ItemActionToolbar({
4475
4472
  },
4476
4473
  children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_lucide_react2.MoreHorizontal, { className: "size-4 shrink-0", "aria-hidden": true })
4477
4474
  }
4478
- )
4475
+ ) : null
4479
4476
  ] }) });
4480
4477
  }
4481
4478
 
4482
4479
  // src/ui/item-interaction-layer.tsx
4480
+ var React5 = __toESM(require("react"), 1);
4483
4481
  var import_jsx_runtime9 = require("react/jsx-runtime");
4484
4482
  var PRIMARY = "var(--ohw-primary, #0885FE)";
4485
- var FOCUS_RING = "0 0 0 4px rgba(8, 133, 254, 0.12)";
4483
+ var FOCUS_RING = `0 0 0 4px color-mix(in srgb, ${PRIMARY} 12%, transparent)`;
4486
4484
  var DRAG_SHADOW = "0px 4px 6px -1px rgba(0, 0, 0, 0.1), 0px 2px 4px -2px rgba(0, 0, 0, 0.1)";
4485
+ var TOOLBAR_EDGE_MARGIN = 4;
4486
+ var SELECTION_CHROME_GAP = 4;
4487
+ var TOOLBAR_STROKE_GAP = 4;
4487
4488
  function getChromeStyle(state) {
4488
4489
  switch (state) {
4489
4490
  case "hover":
@@ -4511,6 +4512,59 @@ function getChromeStyle(state) {
4511
4512
  return {};
4512
4513
  }
4513
4514
  }
4515
+ function ClampedToolbarSlot({
4516
+ placement,
4517
+ children
4518
+ }) {
4519
+ const slotRef = React5.useRef(null);
4520
+ React5.useLayoutEffect(() => {
4521
+ const slot = slotRef.current;
4522
+ if (!slot) return;
4523
+ const clamp = () => {
4524
+ const toolbar = slot.firstElementChild;
4525
+ if (!toolbar) return;
4526
+ const w = toolbar.offsetWidth;
4527
+ if (w <= 0) return;
4528
+ const parent = slot.offsetParent;
4529
+ if (!parent) return;
4530
+ const parentRect = parent.getBoundingClientRect();
4531
+ const centerX = parentRect.left + parentRect.width / 2;
4532
+ const half = w / 2;
4533
+ const clampedCenter = Math.max(
4534
+ TOOLBAR_EDGE_MARGIN + half,
4535
+ Math.min(centerX, window.innerWidth - TOOLBAR_EDGE_MARGIN - half)
4536
+ );
4537
+ const offsetX = clampedCenter - centerX;
4538
+ slot.style.transform = `translateX(calc(-50% + ${offsetX}px))`;
4539
+ };
4540
+ clamp();
4541
+ const ro = new ResizeObserver(clamp);
4542
+ ro.observe(slot);
4543
+ if (slot.firstElementChild) ro.observe(slot.firstElementChild);
4544
+ window.addEventListener("resize", clamp);
4545
+ return () => {
4546
+ ro.disconnect();
4547
+ window.removeEventListener("resize", clamp);
4548
+ };
4549
+ }, [placement, children]);
4550
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
4551
+ "div",
4552
+ {
4553
+ ref: slotRef,
4554
+ className: cn(
4555
+ "pointer-events-auto absolute left-1/2",
4556
+ placement === "top" ? "bottom-full" : "top-full"
4557
+ ),
4558
+ style: {
4559
+ marginBottom: placement === "top" ? TOOLBAR_STROKE_GAP : void 0,
4560
+ marginTop: placement === "bottom" ? TOOLBAR_STROKE_GAP : void 0,
4561
+ transform: "translateX(-50%)"
4562
+ },
4563
+ "data-ohw-item-toolbar-anchor": placement,
4564
+ children
4565
+ }
4566
+ );
4567
+ }
4514
4568
  function ItemInteractionLayer({
4515
4569
  rect,
4516
4570
  state,
@@ -4521,7 +4575,7 @@ function ItemInteractionLayer({
4521
4575
  dragHandleLabel = "Reorder item",
4522
4576
  onDragHandleDragStart,
4523
4577
  onDragHandleDragEnd,
4524
- chromeGap = 6,
4578
+ chromeGap = SELECTION_CHROME_GAP,
4525
4579
  className
4526
4580
  }) {
4527
4581
  if (state === "default") return null;
@@ -4578,33 +4632,19 @@ function ItemInteractionLayer({
4578
4632
  )
4579
4633
  }
4580
4634
  ),
4581
- showToolbar && state === "active-top" && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
4582
- "div",
4583
- {
4584
- className: "pointer-events-auto absolute bottom-full left-1/2 mb-2 -translate-x-1/2",
4585
- "data-ohw-item-toolbar-anchor": "top",
4586
- children: toolbar
4587
- }
4588
- ),
4589
- showToolbar && state === "active-bottom" && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
4590
- "div",
4591
- {
4592
- className: "pointer-events-auto absolute left-1/2 top-full mt-2 -translate-x-1/2",
4593
- "data-ohw-item-toolbar-anchor": "bottom",
4594
- children: toolbar
4595
- }
4596
- )
4635
+ showToolbar && state === "active-top" && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(ClampedToolbarSlot, { placement: "top", children: toolbar }),
4636
+ showToolbar && state === "active-bottom" && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(ClampedToolbarSlot, { placement: "bottom", children: toolbar })
4597
4637
  ]
4598
4638
  }
4599
4639
  );
4600
4640
  }
4601
4641
 
4602
4642
  // src/ui/MediaOverlay.tsx
4603
- var React6 = __toESM(require("react"), 1);
4643
+ var React7 = __toESM(require("react"), 1);
4604
4644
  var import_lucide_react3 = require("lucide-react");
4605
4645
 
4606
4646
  // src/ui/button.tsx
4607
- var React5 = __toESM(require("react"), 1);
4647
+ var React6 = __toESM(require("react"), 1);
4608
4648
  var import_radix_ui4 = require("radix-ui");
4609
4649
  var import_jsx_runtime10 = require("react/jsx-runtime");
4610
4650
  var buttonVariants = cva(
@@ -4627,7 +4667,7 @@ var buttonVariants = cva(
4627
4667
  }
4628
4668
  }
4629
4669
  );
4630
- var Button = React5.forwardRef(
4670
+ var Button = React6.forwardRef(
4631
4671
  ({ className, variant, size, asChild = false, ...props }, ref) => {
4632
4672
  const Comp = asChild ? import_radix_ui4.Slot.Root : "button";
4633
4673
  return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
@@ -4679,7 +4719,7 @@ function MediaOverlay({
4679
4719
  onVideoSettingsChange
4680
4720
  }) {
4681
4721
  const { rect } = hover;
4682
- const skeletonRef = React6.useRef(null);
4722
+ const skeletonRef = React7.useRef(null);
4683
4723
  const isVideo = hover.elementType === "video";
4684
4724
  const autoplay = hover.videoAutoplay ?? true;
4685
4725
  const muted = hover.videoMuted ?? true;
@@ -4691,7 +4731,7 @@ function MediaOverlay({
4691
4731
  height: rect.height,
4692
4732
  zIndex: 2147483646
4693
4733
  };
4694
- React6.useEffect(() => {
4734
+ React7.useEffect(() => {
4695
4735
  if (!isUploading || !fadingOut || !skeletonRef.current) return;
4696
4736
  const anim = skeletonRef.current.animate([{ opacity: 1 }, { opacity: 0 }], {
4697
4737
  duration: MEDIA_UPLOAD_FADE_MS,
@@ -5096,7 +5136,7 @@ function scrollToHashSectionWhenReady(behavior = "smooth") {
5096
5136
  var import_react7 = require("react");
5097
5137
 
5098
5138
  // src/ui/dialog.tsx
5099
- var React7 = __toESM(require("react"), 1);
5139
+ var React8 = __toESM(require("react"), 1);
5100
5140
  var import_radix_ui5 = require("radix-ui");
5101
5141
  var import_lucide_react4 = require("lucide-react");
5102
5142
  var import_jsx_runtime12 = require("react/jsx-runtime");
@@ -5124,7 +5164,7 @@ function DialogOverlay({
5124
5164
  }
5125
5165
  );
5126
5166
  }
5127
- var DialogContent = React7.forwardRef(
5167
+ var DialogContent = React8.forwardRef(
5128
5168
  ({ className, children, showCloseButton = true, container, ...props }, ref) => {
5129
5169
  const positionMode = container ? "absolute" : "fixed";
5130
5170
  return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(DialogPortal, { container: container ?? void 0, children: [
@@ -5179,7 +5219,7 @@ function DialogFooter({
5179
5219
  }
5180
5220
  );
5181
5221
  }
5182
- var DialogTitle = React7.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
5222
+ var DialogTitle = React8.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
5183
5223
  import_radix_ui5.Dialog.Title,
5184
5224
  {
5185
5225
  ref,
@@ -5191,7 +5231,7 @@ var DialogTitle = React7.forwardRef(({ className, ...props }, ref) => /* @__PURE
5191
5231
  }
5192
5232
  ));
5193
5233
  DialogTitle.displayName = import_radix_ui5.Dialog.Title.displayName;
5194
- var DialogDescription = React7.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
5234
+ var DialogDescription = React8.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
5195
5235
  import_radix_ui5.Dialog.Description,
5196
5236
  {
5197
5237
  ref,
@@ -5296,9 +5336,9 @@ function SectionTreeItem({
5296
5336
  var import_react4 = require("react");
5297
5337
 
5298
5338
  // src/ui/input.tsx
5299
- var React8 = __toESM(require("react"), 1);
5339
+ var React9 = __toESM(require("react"), 1);
5300
5340
  var import_jsx_runtime15 = require("react/jsx-runtime");
5301
- var Input = React8.forwardRef(
5341
+ var Input = React9.forwardRef(
5302
5342
  ({ className, type, ...props }, ref) => {
5303
5343
  return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
5304
5344
  "input",
@@ -6415,9 +6455,19 @@ function insertNavbarItemDom(index, href, label, afterAnchor) {
6415
6455
  }
6416
6456
  return primary;
6417
6457
  }
6458
+ function getNavbarLinkKeysInContainer(container) {
6459
+ return Array.from(container.querySelectorAll("[data-ohw-href-key]")).filter(isNavbarLinkItem).map((el) => el.getAttribute("data-ohw-href-key")).filter((key) => Boolean(key));
6460
+ }
6418
6461
  function applyNavOrderToContainer(container, order) {
6462
+ const desired = order.filter((key, i) => order.indexOf(key) === i);
6463
+ const current = getNavbarLinkKeysInContainer(container);
6464
+ const extras = current.filter((key) => !desired.includes(key));
6465
+ const expected = [...desired.filter((key) => current.includes(key)), ...extras];
6466
+ if (current.length === expected.length && current.every((key, i) => key === expected[i])) {
6467
+ return;
6468
+ }
6419
6469
  const seen = /* @__PURE__ */ new Set();
6420
- for (const hrefKey of order) {
6470
+ for (const hrefKey of desired) {
6421
6471
  if (seen.has(hrefKey)) continue;
6422
6472
  seen.add(hrefKey);
6423
6473
  const el = findCounterpartByHrefKey(hrefKey, container);
@@ -6813,6 +6863,19 @@ function isDragHandleDisabled(el) {
6813
6863
  return raw === "true" || raw === "";
6814
6864
  });
6815
6865
  }
6866
+ function canDragNavigationItem(anchor) {
6867
+ const { key, disabled } = getNavigationItemReorderState(anchor);
6868
+ return Boolean(key) && !disabled;
6869
+ }
6870
+ function syncNavigationDragCursorAttrs() {
6871
+ document.querySelectorAll("nav [data-ohw-href-key], footer [data-ohw-href-key]").forEach((el) => {
6872
+ if (!isNavigationItem(el) || !canDragNavigationItem(el)) {
6873
+ el.removeAttribute("data-ohw-can-drag");
6874
+ return;
6875
+ }
6876
+ el.setAttribute("data-ohw-can-drag", "");
6877
+ });
6878
+ }
6816
6879
  function collectEditableNodes() {
6817
6880
  const nodes = Array.from(document.querySelectorAll("[data-ohw-editable]")).map((el) => {
6818
6881
  if (el.dataset.ohwEditable === "image") {
@@ -6982,18 +7045,20 @@ function getNavigationRoot(el) {
6982
7045
  if (explicit) return explicit;
6983
7046
  return el.closest("nav, footer, aside");
6984
7047
  }
7048
+ function countFooterNavItems(el) {
7049
+ return Array.from(el.querySelectorAll("[data-ohw-href-key]")).filter(isNavigationItem).length;
7050
+ }
6985
7051
  function findFooterItemGroup(item) {
7052
+ const explicit = item.closest("[data-ohw-footer-column]");
7053
+ if (explicit) return explicit;
6986
7054
  const footer = item.closest("footer");
6987
7055
  if (!footer) return null;
6988
7056
  let node = item.parentElement;
6989
7057
  while (node && node !== footer) {
6990
- const hasDirectNavItems = Array.from(
6991
- node.querySelectorAll(":scope > [data-ohw-href-key]")
6992
- ).some(isNavigationItem);
6993
- if (hasDirectNavItems) return node;
7058
+ if (countFooterNavItems(node) >= 2) return node;
6994
7059
  node = node.parentElement;
6995
7060
  }
6996
- return null;
7061
+ return footer;
6997
7062
  }
6998
7063
  function isNavigationRoot(el) {
6999
7064
  return el.hasAttribute("data-ohw-nav-root") || el.matches("nav, footer, aside");
@@ -7001,16 +7066,49 @@ function isNavigationRoot(el) {
7001
7066
  function isInferredFooterGroup(el) {
7002
7067
  const footer = el.closest("footer");
7003
7068
  if (!footer || el === footer) return false;
7004
- return Array.from(el.querySelectorAll(":scope > [data-ohw-href-key]")).some(isNavigationItem);
7069
+ if (el.hasAttribute("data-ohw-footer-column")) return true;
7070
+ return countFooterNavItems(el) >= 2;
7005
7071
  }
7006
7072
  function isNavigationContainer(el) {
7007
- return el.hasAttribute("data-ohw-nav-container") || isNavigationRoot(el) || isInferredFooterGroup(el);
7073
+ return el.hasAttribute("data-ohw-nav-container") || el.hasAttribute("data-ohw-footer-column") || isNavigationRoot(el) || isInferredFooterGroup(el);
7074
+ }
7075
+ function isNavbarLinksContainer(el) {
7076
+ return el.hasAttribute("data-ohw-nav-container");
7077
+ }
7078
+ function getFooterColumn(el) {
7079
+ return el.closest("[data-ohw-footer-column]");
7080
+ }
7081
+ function resolveFooterColumnSelectionTarget(target, clientX, clientY) {
7082
+ if (getNavigationItemAnchor(target)) return null;
7083
+ const column = getFooterColumn(target);
7084
+ if (!column) return null;
7085
+ if (isPointOverNavItem(column, clientX, clientY)) return null;
7086
+ return column;
7008
7087
  }
7009
7088
  function isPointOverNavigation(x, y) {
7089
+ const roots = /* @__PURE__ */ new Set();
7010
7090
  const navRoot = document.querySelector("[data-ohw-nav-root]") ?? document.querySelector("nav");
7011
- if (!navRoot) return false;
7012
- const r2 = navRoot.getBoundingClientRect();
7013
- return x >= r2.left && x <= r2.right && y >= r2.top && y <= r2.bottom;
7091
+ if (navRoot) roots.add(navRoot);
7092
+ const footer = document.querySelector("footer");
7093
+ if (footer) roots.add(footer);
7094
+ for (const root of roots) {
7095
+ const r2 = root.getBoundingClientRect();
7096
+ if (x >= r2.left && x <= r2.right && y >= r2.top && y <= r2.bottom) return true;
7097
+ }
7098
+ return false;
7099
+ }
7100
+ function findHoveredNavOrFooterContainer(x, y) {
7101
+ const candidates = [
7102
+ ...document.querySelectorAll("[data-ohw-nav-container]"),
7103
+ ...document.querySelectorAll("[data-ohw-footer-column]")
7104
+ ];
7105
+ for (const container of candidates) {
7106
+ const r2 = container.getBoundingClientRect();
7107
+ if (x < r2.left || x > r2.right || y < r2.top || y > r2.bottom) continue;
7108
+ if (isPointOverNavItem(container, x, y)) continue;
7109
+ return container;
7110
+ }
7111
+ return null;
7014
7112
  }
7015
7113
  function isPointOverNavItem(container, x, y) {
7016
7114
  return Array.from(container.querySelectorAll("[data-ohw-href-key]")).some((el) => {
@@ -7043,11 +7141,13 @@ function getNavigationSelectionParent(el) {
7043
7141
  if (isNavigationItem(el)) {
7044
7142
  const explicit = el.closest("[data-ohw-nav-container]");
7045
7143
  if (explicit) return explicit;
7144
+ const footerColumn = getFooterColumn(el);
7145
+ if (footerColumn) return footerColumn;
7046
7146
  const footerGroup = findFooterItemGroup(el);
7047
7147
  if (footerGroup) return footerGroup;
7048
7148
  return getNavigationRoot(el);
7049
7149
  }
7050
- if (el.hasAttribute("data-ohw-nav-container") || isInferredFooterGroup(el)) {
7150
+ if (el.hasAttribute("data-ohw-nav-container") || el.hasAttribute("data-ohw-footer-column") || isInferredFooterGroup(el)) {
7051
7151
  return getNavigationRoot(el);
7052
7152
  }
7053
7153
  return null;
@@ -7214,6 +7314,9 @@ var ICONS = {
7214
7314
  insertUnorderedList: '<line x1="8" y1="6" x2="21" y2="6"/><line x1="8" y1="12" x2="21" y2="12"/><line x1="8" y1="18" x2="21" y2="18"/><line x1="3" y1="6" x2="3.01" y2="6"/><line x1="3" y1="12" x2="3.01" y2="12"/><line x1="3" y1="18" x2="3.01" y2="18"/>',
7215
7315
  insertOrderedList: '<line x1="10" y1="6" x2="21" y2="6"/><line x1="10" y1="12" x2="21" y2="12"/><line x1="10" y1="18" x2="21" y2="18"/><path d="M4 6h1v4"/><path d="M4 10h2"/><path d="M6 18H4c0-1 2-2 2-3s-1-1.5-2-1"/>'
7216
7316
  };
7317
+ var SELECTION_CHROME_GAP2 = 4;
7318
+ var TOOLBAR_STROKE_GAP2 = 4;
7319
+ var TOOLBAR_OFFSET_FROM_ELEMENT = SELECTION_CHROME_GAP2 + TOOLBAR_STROKE_GAP2;
7217
7320
  var TOOLBAR_GROUPS = [
7218
7321
  [
7219
7322
  { cmd: "bold", title: "Bold" },
@@ -7237,7 +7340,7 @@ function EditGlowChrome({
7237
7340
  reorderHrefKey,
7238
7341
  dragDisabled = false
7239
7342
  }) {
7240
- const GAP = 6;
7343
+ const GAP = SELECTION_CHROME_GAP2;
7241
7344
  return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(
7242
7345
  "div",
7243
7346
  {
@@ -7261,7 +7364,7 @@ function EditGlowChrome({
7261
7364
  inset: 0,
7262
7365
  border: "2px solid var(--ohw-primary, #0885FE)",
7263
7366
  borderRadius: 8,
7264
- boxShadow: "0 0 0 4px rgba(8, 133, 254, 0.12)",
7367
+ boxShadow: "0 0 0 4px color-mix(in srgb, var(--ohw-primary, #0885FE) 12%, transparent)",
7265
7368
  pointerEvents: "none"
7266
7369
  }
7267
7370
  }
@@ -7309,6 +7412,7 @@ function applyVisibleViewport(el, parentScroll) {
7309
7412
  el.style.top = `${top}px`;
7310
7413
  el.style.height = `${height}px`;
7311
7414
  }
7415
+ var TOOLBAR_EDGE_MARGIN2 = 4;
7312
7416
  function clampToolbarToClip(top, transform, rect, clip, approxH, gap) {
7313
7417
  const isAbove = transform.includes("translateY(-100%)");
7314
7418
  let visualTop = isAbove ? top - approxH : top;
@@ -7332,10 +7436,16 @@ function clampToolbarToClip(top, transform, rect, clip, approxH, gap) {
7332
7436
  const pinnedTop = Math.min(maxBottom - approxH, Math.max(minTop, clip.top + gap));
7333
7437
  return { top: pinnedTop + approxH, transform: "translateX(-50%) translateY(-100%)" };
7334
7438
  }
7335
- function calcToolbarPos(rect, parentScroll, approxW = 306) {
7336
- const GAP = 8;
7439
+ function clampToolbarLeft(centerX, toolbarWidth) {
7440
+ const half = toolbarWidth / 2;
7441
+ return Math.max(
7442
+ TOOLBAR_EDGE_MARGIN2 + half,
7443
+ Math.min(centerX, window.innerWidth - TOOLBAR_EDGE_MARGIN2 - half)
7444
+ );
7445
+ }
7446
+ function calcToolbarPos(rect, parentScroll, toolbarW = 306) {
7447
+ const GAP = TOOLBAR_OFFSET_FROM_ELEMENT;
7337
7448
  const APPROX_H = 32;
7338
- const APPROX_W = approxW;
7339
7449
  const clip = getIframeVisibleClip(parentScroll);
7340
7450
  const clipTop = clip?.top ?? 0;
7341
7451
  const canvasTopInIframe = parentScroll != null ? parentScroll.headerH - parentScroll.iframeOffsetTop : 0;
@@ -7365,7 +7475,7 @@ function calcToolbarPos(rect, parentScroll, approxW = 306) {
7365
7475
  transform = clamped.transform;
7366
7476
  }
7367
7477
  const rawLeft = rect.left + rect.width / 2;
7368
- const left = Math.max(GAP + APPROX_W / 2, Math.min(rawLeft, window.innerWidth - GAP - APPROX_W / 2));
7478
+ const left = clampToolbarLeft(rawLeft, toolbarW);
7369
7479
  return { top, left, transform };
7370
7480
  }
7371
7481
  function resolveItemInteractionState(rect, parentScroll) {
@@ -7381,11 +7491,37 @@ function FloatingToolbar({
7381
7491
  showEditLink,
7382
7492
  onEditLink
7383
7493
  }) {
7384
- const { top, left, transform } = calcToolbarPos(rect, parentScroll, 330);
7494
+ const localRef = import_react8.default.useRef(null);
7495
+ const [measuredW, setMeasuredW] = import_react8.default.useState(330);
7496
+ const setRefs = import_react8.default.useCallback(
7497
+ (node) => {
7498
+ localRef.current = node;
7499
+ if (typeof elRef === "function") elRef(node);
7500
+ else if (elRef) elRef.current = node;
7501
+ if (node) {
7502
+ const w = node.offsetWidth;
7503
+ if (w > 0) setMeasuredW(w);
7504
+ }
7505
+ },
7506
+ [elRef]
7507
+ );
7508
+ import_react8.default.useLayoutEffect(() => {
7509
+ const node = localRef.current;
7510
+ if (!node) return;
7511
+ const update = () => {
7512
+ const w = node.offsetWidth;
7513
+ if (w > 0) setMeasuredW(w);
7514
+ };
7515
+ update();
7516
+ const ro = new ResizeObserver(update);
7517
+ ro.observe(node);
7518
+ return () => ro.disconnect();
7519
+ }, [showEditLink, activeCommands]);
7520
+ const { top, left, transform } = calcToolbarPos(rect, parentScroll, measuredW);
7385
7521
  return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
7386
7522
  "div",
7387
7523
  {
7388
- ref: elRef,
7524
+ ref: setRefs,
7389
7525
  style: {
7390
7526
  position: "fixed",
7391
7527
  top,
@@ -7584,6 +7720,7 @@ function OhhwellsBridge() {
7584
7720
  const [toolbarVariant, setToolbarVariant] = (0, import_react8.useState)("none");
7585
7721
  const toolbarVariantRef = (0, import_react8.useRef)("none");
7586
7722
  toolbarVariantRef.current = toolbarVariant;
7723
+ const [selectedIsCta, setSelectedIsCta] = (0, import_react8.useState)(false);
7587
7724
  const [reorderHrefKey, setReorderHrefKey] = (0, import_react8.useState)(null);
7588
7725
  const [reorderDragDisabled, setReorderDragDisabled] = (0, import_react8.useState)(false);
7589
7726
  const [toggleState, setToggleState] = (0, import_react8.useState)(null);
@@ -7770,6 +7907,7 @@ function OhhwellsBridge() {
7770
7907
  }
7771
7908
  }
7772
7909
  el.removeAttribute("contenteditable");
7910
+ el.removeAttribute("data-ohw-editing");
7773
7911
  activeElRef.current = null;
7774
7912
  setReorderHrefKey(null);
7775
7913
  setReorderDragDisabled(false);
@@ -7782,8 +7920,15 @@ function OhhwellsBridge() {
7782
7920
  setToolbarShowEditLink(false);
7783
7921
  postToParent2({ type: "ow:exit-edit" });
7784
7922
  }, [postToParent2]);
7923
+ const clearSelectedAttr = (0, import_react8.useCallback)(() => {
7924
+ document.querySelectorAll("[data-ohw-selected]").forEach((el) => {
7925
+ el.removeAttribute("data-ohw-selected");
7926
+ });
7927
+ }, []);
7785
7928
  const deselect = (0, import_react8.useCallback)(() => {
7929
+ clearSelectedAttr();
7786
7930
  selectedElRef.current = null;
7931
+ setSelectedIsCta(false);
7787
7932
  setReorderHrefKey(null);
7788
7933
  setReorderDragDisabled(false);
7789
7934
  siblingHintElRef.current = null;
@@ -7795,9 +7940,15 @@ function OhhwellsBridge() {
7795
7940
  setToolbarRect(null);
7796
7941
  setToolbarVariant("none");
7797
7942
  }
7798
- }, []);
7943
+ }, [clearSelectedAttr]);
7944
+ const markSelected = (0, import_react8.useCallback)((el) => {
7945
+ clearSelectedAttr();
7946
+ el.setAttribute("data-ohw-selected", "");
7947
+ }, [clearSelectedAttr]);
7799
7948
  const reselectNavigationItem = (0, import_react8.useCallback)((navAnchor) => {
7800
7949
  selectedElRef.current = navAnchor;
7950
+ markSelected(navAnchor);
7951
+ setSelectedIsCta(isNavbarButton2(navAnchor));
7801
7952
  const { key, disabled } = getNavigationItemReorderState(navAnchor);
7802
7953
  setReorderHrefKey(key);
7803
7954
  setReorderDragDisabled(disabled);
@@ -7805,7 +7956,7 @@ function OhhwellsBridge() {
7805
7956
  setToolbarRect(navAnchor.getBoundingClientRect());
7806
7957
  setToolbarShowEditLink(false);
7807
7958
  setActiveCommands(/* @__PURE__ */ new Set());
7808
- }, []);
7959
+ }, [markSelected]);
7809
7960
  const commitNavigationTextEdit = (0, import_react8.useCallback)((navAnchor) => {
7810
7961
  const el = activeElRef.current;
7811
7962
  if (!el) return;
@@ -7825,6 +7976,7 @@ function OhhwellsBridge() {
7825
7976
  }
7826
7977
  }
7827
7978
  el.removeAttribute("contenteditable");
7979
+ el.removeAttribute("data-ohw-editing");
7828
7980
  activeElRef.current = null;
7829
7981
  setMaxBadge(null);
7830
7982
  setActiveCommands(/* @__PURE__ */ new Set());
@@ -7858,6 +8010,8 @@ function OhhwellsBridge() {
7858
8010
  if (!isNavigationItem(anchor)) return;
7859
8011
  if (activeElRef.current) deactivate();
7860
8012
  selectedElRef.current = anchor;
8013
+ markSelected(anchor);
8014
+ setSelectedIsCta(isNavbarButton2(anchor));
7861
8015
  clearHrefKeyHover(anchor);
7862
8016
  hoveredNavContainerRef.current = null;
7863
8017
  setHoveredNavContainerRect(null);
@@ -7873,11 +8027,13 @@ function OhhwellsBridge() {
7873
8027
  setToolbarRect(anchor.getBoundingClientRect());
7874
8028
  setToolbarShowEditLink(false);
7875
8029
  setActiveCommands(/* @__PURE__ */ new Set());
7876
- }, [deactivate]);
8030
+ }, [deactivate, markSelected]);
7877
8031
  const selectFrame = (0, import_react8.useCallback)((el) => {
7878
8032
  if (!isNavigationContainer(el)) return;
7879
8033
  if (activeElRef.current) deactivate();
7880
8034
  selectedElRef.current = el;
8035
+ markSelected(el);
8036
+ setSelectedIsCta(false);
7881
8037
  clearHrefKeyHover(el);
7882
8038
  hoveredNavContainerRef.current = null;
7883
8039
  setHoveredNavContainerRect(null);
@@ -7892,10 +8048,12 @@ function OhhwellsBridge() {
7892
8048
  setToolbarRect(el.getBoundingClientRect());
7893
8049
  setToolbarShowEditLink(false);
7894
8050
  setActiveCommands(/* @__PURE__ */ new Set());
7895
- }, [deactivate]);
8051
+ }, [deactivate, markSelected]);
7896
8052
  const activate = (0, import_react8.useCallback)((el, options) => {
7897
8053
  if (activeElRef.current === el) return;
8054
+ clearSelectedAttr();
7898
8055
  selectedElRef.current = null;
8056
+ setSelectedIsCta(false);
7899
8057
  deactivate();
7900
8058
  if (hoveredImageRef.current) {
7901
8059
  hoveredImageRef.current = null;
@@ -7906,13 +8064,23 @@ function OhhwellsBridge() {
7906
8064
  setSiblingHintRect(null);
7907
8065
  setIsItemDragging(false);
7908
8066
  el.setAttribute("contenteditable", "true");
8067
+ el.setAttribute("data-ohw-editing", "");
7909
8068
  el.removeAttribute("data-ohw-hovered");
7910
8069
  el.closest("[data-ohw-href-key]")?.removeAttribute("data-ohw-hovered");
7911
8070
  activeElRef.current = el;
7912
8071
  originalContentRef.current = el.innerHTML;
7913
- el.focus();
8072
+ el.focus({ preventScroll: true });
7914
8073
  if (options?.caretX !== void 0 && options?.caretY !== void 0) {
7915
8074
  placeCaretAtPoint(el, options.caretX, options.caretY);
8075
+ } else {
8076
+ const selection = window.getSelection();
8077
+ if (selection && selection.rangeCount === 0) {
8078
+ const range = document.createRange();
8079
+ range.selectNodeContents(el);
8080
+ range.collapse(false);
8081
+ selection.removeAllRanges();
8082
+ selection.addRange(range);
8083
+ }
7916
8084
  }
7917
8085
  setToolbarShowEditLink(Boolean(getHrefKeyFromElement(el)));
7918
8086
  const navAnchor = getNavigationItemAnchor(el);
@@ -7927,7 +8095,7 @@ function OhhwellsBridge() {
7927
8095
  setToolbarRect(getEditMeasureEl(el).getBoundingClientRect());
7928
8096
  postToParent2({ type: "ow:enter-edit", key: el.dataset.ohwKey });
7929
8097
  requestAnimationFrame(() => refreshActiveCommandsRef.current());
7930
- }, [deactivate, postToParent2]);
8098
+ }, [clearSelectedAttr, deactivate, postToParent2]);
7931
8099
  activateRef.current = activate;
7932
8100
  deactivateRef.current = deactivate;
7933
8101
  selectRef.current = select;
@@ -8074,15 +8242,48 @@ function OhhwellsBridge() {
8074
8242
  if (!subdomain) return {};
8075
8243
  return contentCache.get(subdomain) ?? {};
8076
8244
  };
8077
- const run = () => reconcileNavbarItemsFromContent(contentForNav());
8245
+ const observeRoots = () => [document.querySelector("nav"), document.querySelector("footer")].filter(
8246
+ (el) => Boolean(el)
8247
+ );
8248
+ const roots = observeRoots();
8249
+ if (roots.length === 0) {
8250
+ reconcileNavbarItemsFromContent(contentForNav());
8251
+ if (isEditMode) syncNavigationDragCursorAttrs();
8252
+ return;
8253
+ }
8254
+ let rafId = null;
8255
+ let applying = false;
8256
+ let observer = null;
8257
+ const attach = () => {
8258
+ for (const root of observeRoots()) {
8259
+ observer?.observe(root, { childList: true, subtree: true });
8260
+ }
8261
+ };
8262
+ const run = () => {
8263
+ if (applying) return;
8264
+ applying = true;
8265
+ observer?.disconnect();
8266
+ try {
8267
+ reconcileNavbarItemsFromContent(contentForNav());
8268
+ if (isEditMode) syncNavigationDragCursorAttrs();
8269
+ } finally {
8270
+ applying = false;
8271
+ attach();
8272
+ }
8273
+ };
8274
+ const schedule = () => {
8275
+ if (applying || rafId != null) return;
8276
+ rafId = requestAnimationFrame(() => {
8277
+ rafId = null;
8278
+ run();
8279
+ });
8280
+ };
8281
+ observer = new MutationObserver(schedule);
8078
8282
  run();
8079
- const nav = document.querySelector("nav");
8080
- if (!nav) return;
8081
- const observer = new MutationObserver(() => {
8082
- requestAnimationFrame(run);
8083
- });
8084
- observer.observe(nav, { childList: true, subtree: true });
8085
- return () => observer.disconnect();
8283
+ return () => {
8284
+ if (rafId != null) cancelAnimationFrame(rafId);
8285
+ observer?.disconnect();
8286
+ };
8086
8287
  }, [isEditMode, pathname, subdomain, fetchState]);
8087
8288
  (0, import_react8.useEffect)(() => {
8088
8289
  if (!isEditMode) return;
@@ -8130,13 +8331,18 @@ function OhhwellsBridge() {
8130
8331
  editStylesRef.current?.forceHover.remove();
8131
8332
  editStylesRef.current?.stateViews.remove();
8132
8333
  editStylesRef.current = null;
8334
+ document.querySelectorAll("[data-ohw-can-drag]").forEach((el) => {
8335
+ el.removeAttribute("data-ohw-can-drag");
8336
+ });
8133
8337
  return;
8134
8338
  }
8135
- if (!editStylesRef.current) {
8136
- const initialVh = window.innerHeight;
8137
- const base = document.createElement("style");
8138
- base.setAttribute("data-ohw-edit-style", "");
8139
- base.textContent = `
8339
+ const existing = editStylesRef.current;
8340
+ let initialVh = window.innerHeight;
8341
+ if (existing?.base.textContent) {
8342
+ const match = existing.base.textContent.match(/\.min-h-screen[^{]*\{[^}]*min-height:\s*(\d+)px/);
8343
+ if (match) initialVh = parseInt(match[1], 10);
8344
+ }
8345
+ const baseCss = `
8140
8346
  html { height: auto !important; }
8141
8347
  body { height: auto !important; min-height: 0 !important; overflow: hidden !important; }
8142
8348
  .min-h-screen, .min-h-svh, .min-h-dvh { min-height: ${initialVh}px !important; }
@@ -8147,7 +8353,25 @@ function OhhwellsBridge() {
8147
8353
  [data-ohw-editable] {
8148
8354
  display: block;
8149
8355
  }
8150
- [data-ohw-editable]:not([contenteditable]):not([data-ohw-editable="image"]):not([data-ohw-editable="bg-image"]):not([data-ohw-editable="video"]) { cursor: text !important; }
8356
+ /* Body text (no item-action toolbar) \u2014 first click enters text edit \u2192 I-beam.
8357
+ Exclude select-first nav/footer labels so grab/default on [data-ohw-href-key] can win. */
8358
+ [data-ohw-editable]:not([contenteditable]):not([data-ohw-editable="image"]):not([data-ohw-editable="bg-image"]):not([data-ohw-editable="video"]):not([data-ohw-editable="link"]):not(:is([data-ohw-href-key] *)) {
8359
+ cursor: text !important;
8360
+ }
8361
+ /* Select-first items: default arrow; grab only when drag-start is allowed (data-ohw-can-drag). */
8362
+ [data-ohw-href-key]:not([data-ohw-selected]),
8363
+ [data-ohw-href-key]:not([data-ohw-selected]) * {
8364
+ cursor: default !important;
8365
+ }
8366
+ [data-ohw-href-key][data-ohw-can-drag]:not([data-ohw-selected]),
8367
+ [data-ohw-href-key][data-ohw-can-drag]:not([data-ohw-selected]) * {
8368
+ cursor: grab !important;
8369
+ }
8370
+ /* Selected (action-toolbar) \u2014 next click enters text edit at caret. */
8371
+ [data-ohw-href-key][data-ohw-selected],
8372
+ [data-ohw-href-key][data-ohw-selected] * {
8373
+ cursor: text !important;
8374
+ }
8151
8375
  [data-ohw-editable="image"], [data-ohw-editable="image"] *,
8152
8376
  [data-ohw-editable="video"], [data-ohw-editable="video"] *,
8153
8377
  [data-ohw-editable="bg-image"], [data-ohw-editable="bg-image"] * { cursor: pointer !important; }
@@ -8163,9 +8387,17 @@ function OhhwellsBridge() {
8163
8387
  outline: none !important;
8164
8388
  outline-offset: 0 !important;
8165
8389
  }
8166
- [data-ohw-editable][contenteditable] {
8390
+ /* Text edit wins over grab/default (must beat [data-ohw-can-drag] *). */
8391
+ [data-ohw-editing],
8392
+ [data-ohw-editing] *,
8393
+ [data-ohw-editable][contenteditable],
8394
+ [data-ohw-editable][contenteditable] *,
8395
+ nav [data-ohw-href-key] [data-ohw-editable][contenteditable],
8396
+ nav [data-ohw-href-key] [data-ohw-editable][contenteditable] *,
8397
+ footer [data-ohw-href-key] [data-ohw-editable][contenteditable],
8398
+ footer [data-ohw-href-key] [data-ohw-editable][contenteditable] * {
8167
8399
  outline: none !important;
8168
- caret-color: ${PRIMARY2};
8400
+ caret-color: ${PRIMARY2} !important;
8169
8401
  cursor: text !important;
8170
8402
  }
8171
8403
  [data-ohw-editable][contenteditable]::selection,
@@ -8174,6 +8406,9 @@ function OhhwellsBridge() {
8174
8406
  [data-ohw-editable-state][data-ohw-active-state] [data-ohw-editable] { pointer-events: auto !important; }
8175
8407
  [data-ohw-editable-state][data-ohw-active-state][data-ohw-editable] { pointer-events: auto !important; }
8176
8408
  `;
8409
+ if (!existing) {
8410
+ const base = document.createElement("style");
8411
+ base.setAttribute("data-ohw-edit-style", "");
8177
8412
  const forceHover = document.createElement("style");
8178
8413
  forceHover.setAttribute("data-ohw-active-state-style", "");
8179
8414
  const stateViews = document.createElement("style");
@@ -8189,6 +8424,8 @@ function OhhwellsBridge() {
8189
8424
  document.head.appendChild(stateViews);
8190
8425
  editStylesRef.current = { base, forceHover, stateViews };
8191
8426
  }
8427
+ editStylesRef.current.base.textContent = baseCss;
8428
+ syncNavigationDragCursorAttrs();
8192
8429
  refreshStateRules();
8193
8430
  const handleClick = (e) => {
8194
8431
  const target = e.target;
@@ -8204,10 +8441,34 @@ function OhhwellsBridge() {
8204
8441
  selectFrameRef.current(navFromChrome);
8205
8442
  return;
8206
8443
  }
8444
+ const footerFromChrome = resolveFooterColumnSelectionTarget(target, e.clientX, e.clientY);
8445
+ if (footerFromChrome) {
8446
+ e.preventDefault();
8447
+ e.stopPropagation();
8448
+ selectFrameRef.current(footerFromChrome);
8449
+ return;
8450
+ }
8207
8451
  e.preventDefault();
8208
8452
  e.stopPropagation();
8209
8453
  return;
8210
8454
  }
8455
+ if (activeElRef.current) {
8456
+ const active = activeElRef.current;
8457
+ const inActive = active === target || active.contains(target);
8458
+ const navAnchor = getNavigationItemAnchor(active);
8459
+ const inActiveNav = Boolean(
8460
+ navAnchor && (navAnchor === target || navAnchor.contains(target))
8461
+ );
8462
+ if (inActive || inActiveNav) {
8463
+ e.preventDefault();
8464
+ e.stopPropagation();
8465
+ if (e.detail < 2) {
8466
+ placeCaretAtPoint(active, e.clientX, e.clientY);
8467
+ refreshActiveCommandsRef.current();
8468
+ }
8469
+ return;
8470
+ }
8471
+ }
8211
8472
  const editable = target.closest("[data-ohw-editable]");
8212
8473
  if (editable) {
8213
8474
  if (editable.dataset.ohwEditable === "link") {
@@ -8262,6 +8523,13 @@ function OhhwellsBridge() {
8262
8523
  selectFrameRef.current(navContainerToSelect);
8263
8524
  return;
8264
8525
  }
8526
+ const footerColumnToSelect = resolveFooterColumnSelectionTarget(target, e.clientX, e.clientY);
8527
+ if (footerColumnToSelect) {
8528
+ e.preventDefault();
8529
+ e.stopPropagation();
8530
+ selectFrameRef.current(footerColumnToSelect);
8531
+ return;
8532
+ }
8265
8533
  const selectedContainer = selectedElRef.current;
8266
8534
  if (selectedContainer && isNavigationContainer(selectedContainer)) {
8267
8535
  const navItem = getNavigationItemAnchor(target);
@@ -8322,15 +8590,15 @@ function OhhwellsBridge() {
8322
8590
  if (document.documentElement.hasAttribute("data-ohw-section-picking")) return;
8323
8591
  const target = e.target;
8324
8592
  if (toolbarVariantRef.current !== "select-frame") {
8325
- const navContainer = target.closest("[data-ohw-nav-container]");
8326
- if (navContainer && !getNavigationItemAnchor(target)) {
8327
- hoveredNavContainerRef.current = navContainer;
8328
- setHoveredNavContainerRect(navContainer.getBoundingClientRect());
8593
+ const frameContainer = target.closest("[data-ohw-nav-container]") ?? target.closest("[data-ohw-footer-column]");
8594
+ if (frameContainer && !getNavigationItemAnchor(target)) {
8595
+ hoveredNavContainerRef.current = frameContainer;
8596
+ setHoveredNavContainerRect(frameContainer.getBoundingClientRect());
8329
8597
  hoveredItemElRef.current = null;
8330
8598
  setHoveredItemRect(null);
8331
8599
  return;
8332
8600
  }
8333
- if (!target.closest("[data-ohw-nav-container]")) {
8601
+ if (!target.closest("[data-ohw-nav-container], [data-ohw-footer-column]")) {
8334
8602
  hoveredNavContainerRef.current = null;
8335
8603
  setHoveredNavContainerRect(null);
8336
8604
  }
@@ -8363,9 +8631,9 @@ function OhhwellsBridge() {
8363
8631
  };
8364
8632
  const handleMouseOut = (e) => {
8365
8633
  const target = e.target;
8366
- if (target.closest("[data-ohw-nav-container]")) {
8634
+ if (target.closest("[data-ohw-nav-container], [data-ohw-footer-column]")) {
8367
8635
  const related2 = e.relatedTarget instanceof Element ? e.relatedTarget : null;
8368
- if (related2?.closest("[data-ohw-nav-container], [data-ohw-navbar-container-chrome], [data-ohw-navbar-add-button]")) {
8636
+ if (related2?.closest("[data-ohw-nav-container], [data-ohw-footer-column], [data-ohw-navbar-container-chrome], [data-ohw-navbar-add-button]")) {
8369
8637
  return;
8370
8638
  }
8371
8639
  hoveredNavContainerRef.current = null;
@@ -8506,39 +8774,43 @@ function OhhwellsBridge() {
8506
8774
  setHoveredNavContainerRect(null);
8507
8775
  return;
8508
8776
  }
8509
- const navContainer = document.querySelector("[data-ohw-nav-container]");
8510
- if (!navContainer) {
8511
- hoveredNavContainerRef.current = null;
8512
- setHoveredNavContainerRect(null);
8513
- return;
8514
- }
8515
- const containerRect = navContainer.getBoundingClientRect();
8516
- const overContainer = x >= containerRect.left && x <= containerRect.right && y >= containerRect.top && y <= containerRect.bottom;
8517
- if (!overContainer) {
8518
- hoveredNavContainerRef.current = null;
8519
- setHoveredNavContainerRect(null);
8520
- return;
8777
+ const frameContainers = [
8778
+ ...document.querySelectorAll("[data-ohw-nav-container]"),
8779
+ ...document.querySelectorAll("[data-ohw-footer-column]")
8780
+ ];
8781
+ let overFrame = null;
8782
+ for (const container of frameContainers) {
8783
+ const containerRect = container.getBoundingClientRect();
8784
+ const overContainer = x >= containerRect.left && x <= containerRect.right && y >= containerRect.top && y <= containerRect.bottom;
8785
+ if (!overContainer) continue;
8786
+ const navItemHit = Array.from(
8787
+ container.querySelectorAll("[data-ohw-href-key]")
8788
+ ).find((el) => {
8789
+ if (!isNavigationItem(el) || isNavbarButton2(el)) return false;
8790
+ const itemRect = el.getBoundingClientRect();
8791
+ return x >= itemRect.left && x <= itemRect.right && y >= itemRect.top && y <= itemRect.bottom;
8792
+ });
8793
+ if (navItemHit) {
8794
+ hoveredNavContainerRef.current = null;
8795
+ setHoveredNavContainerRect(null);
8796
+ const selected = selectedElRef.current;
8797
+ if (selected !== navItemHit && !selected?.contains(navItemHit)) {
8798
+ clearHrefKeyHover(navItemHit);
8799
+ hoveredItemElRef.current = navItemHit;
8800
+ setHoveredItemRect(navItemHit.getBoundingClientRect());
8801
+ }
8802
+ return;
8803
+ }
8804
+ overFrame = container;
8805
+ break;
8521
8806
  }
8522
- const navItemHit = Array.from(
8523
- navContainer.querySelectorAll("[data-ohw-href-key]")
8524
- ).find((el) => {
8525
- if (!isNavigationItem(el) || isNavbarButton2(el)) return false;
8526
- const itemRect = el.getBoundingClientRect();
8527
- return x >= itemRect.left && x <= itemRect.right && y >= itemRect.top && y <= itemRect.bottom;
8528
- });
8529
- if (navItemHit) {
8807
+ if (!overFrame) {
8530
8808
  hoveredNavContainerRef.current = null;
8531
8809
  setHoveredNavContainerRect(null);
8532
- const selected = selectedElRef.current;
8533
- if (selected !== navItemHit && !selected?.contains(navItemHit)) {
8534
- clearHrefKeyHover(navItemHit);
8535
- hoveredItemElRef.current = navItemHit;
8536
- setHoveredItemRect(navItemHit.getBoundingClientRect());
8537
- }
8538
8810
  return;
8539
8811
  }
8540
- hoveredNavContainerRef.current = navContainer;
8541
- setHoveredNavContainerRect(containerRect);
8812
+ hoveredNavContainerRef.current = overFrame;
8813
+ setHoveredNavContainerRect(overFrame.getBoundingClientRect());
8542
8814
  hoveredItemElRef.current = null;
8543
8815
  setHoveredItemRect(null);
8544
8816
  };
@@ -8970,8 +9242,8 @@ function OhhwellsBridge() {
8970
9242
  if (sibling !== el) sibling.innerHTML = html;
8971
9243
  });
8972
9244
  const timers = autoSaveTimers.current;
8973
- const existing = timers.get(key);
8974
- if (existing) clearTimeout(existing);
9245
+ const existing2 = timers.get(key);
9246
+ if (existing2) clearTimeout(existing2);
8975
9247
  timers.set(key, setTimeout(() => {
8976
9248
  timers.delete(key);
8977
9249
  postToParentRef.current({ type: "ow:change", nodes: [{ key, text: html }] });
@@ -9014,6 +9286,7 @@ function OhhwellsBridge() {
9014
9286
  }
9015
9287
  editContentRef.current = { ...editContentRef.current, ...content };
9016
9288
  reconcileNavbarItemsFromContent(editContentRef.current);
9289
+ syncNavigationDragCursorAttrs();
9017
9290
  enforceLinkHrefs();
9018
9291
  postToParentRef.current({ type: "ow:hydrate-done" });
9019
9292
  };
@@ -9034,6 +9307,48 @@ function OhhwellsBridge() {
9034
9307
  if (e.data?.type !== "ui:escape") return;
9035
9308
  if (linkPopoverOpenRef.current) {
9036
9309
  setLinkPopoverRef.current(null);
9310
+ return;
9311
+ }
9312
+ if (activeElRef.current) {
9313
+ const hrefCtx = getHrefKeyFromElement(activeElRef.current);
9314
+ const navAnchor = hrefCtx ? getNavigationItemAnchor(hrefCtx.anchor) : null;
9315
+ if (navAnchor) {
9316
+ const el = activeElRef.current;
9317
+ if (originalContentRef.current !== null) {
9318
+ el.innerHTML = originalContentRef.current;
9319
+ }
9320
+ el.removeAttribute("contenteditable");
9321
+ el.removeAttribute("data-ohw-editing");
9322
+ activeElRef.current = null;
9323
+ setMaxBadge(null);
9324
+ setActiveCommands(/* @__PURE__ */ new Set());
9325
+ setToolbarShowEditLink(false);
9326
+ postToParentRef.current({ type: "ow:exit-edit" });
9327
+ reselectNavigationItemRef.current(navAnchor);
9328
+ return;
9329
+ }
9330
+ if (originalContentRef.current !== null) {
9331
+ activeElRef.current.innerHTML = originalContentRef.current;
9332
+ const key = activeElRef.current.dataset.ohwKey;
9333
+ if (key) {
9334
+ postToParentRef.current({ type: "ow:change", nodes: [{ key, text: originalContentRef.current }] });
9335
+ }
9336
+ }
9337
+ deselectRef.current();
9338
+ deactivateRef.current();
9339
+ return;
9340
+ }
9341
+ if (selectedElRef.current) {
9342
+ if (toolbarVariantRef.current === "select-frame") {
9343
+ deselectRef.current();
9344
+ return;
9345
+ }
9346
+ const parent = getNavigationSelectionParent(selectedElRef.current);
9347
+ if (parent && isNavigationContainer(parent)) {
9348
+ selectFrameRef.current(parent);
9349
+ } else {
9350
+ deselectRef.current();
9351
+ }
9037
9352
  }
9038
9353
  };
9039
9354
  window.addEventListener("message", handleUiEscape);
@@ -9058,7 +9373,7 @@ function OhhwellsBridge() {
9058
9373
  return;
9059
9374
  }
9060
9375
  const parent = getNavigationSelectionParent(selectedElRef.current);
9061
- if (parent) {
9376
+ if (parent && isNavigationContainer(parent)) {
9062
9377
  e.preventDefault();
9063
9378
  selectFrameRef.current(parent);
9064
9379
  } else {
@@ -9076,6 +9391,7 @@ function OhhwellsBridge() {
9076
9391
  el2.innerHTML = originalContentRef.current;
9077
9392
  }
9078
9393
  el2.removeAttribute("contenteditable");
9394
+ el2.removeAttribute("data-ohw-editing");
9079
9395
  activeElRef.current = null;
9080
9396
  setMaxBadge(null);
9081
9397
  setActiveCommands(/* @__PURE__ */ new Set());
@@ -9263,15 +9579,15 @@ function OhhwellsBridge() {
9263
9579
  };
9264
9580
  const applyToolbarPos = (rect) => {
9265
9581
  const ps = parentScrollRef.current;
9266
- const approxW = 330;
9582
+ const measuredW = toolbarElRef.current?.offsetWidth || 330;
9267
9583
  if (toolbarElRef.current) {
9268
- const { top, left, transform } = calcToolbarPos(rect, ps, approxW);
9584
+ const { top, left, transform } = calcToolbarPos(rect, ps, measuredW);
9269
9585
  toolbarElRef.current.style.top = `${top}px`;
9270
9586
  toolbarElRef.current.style.left = `${left}px`;
9271
9587
  toolbarElRef.current.style.transform = transform;
9272
9588
  }
9273
9589
  if (glowElRef.current) {
9274
- const GAP = 6;
9590
+ const GAP = SELECTION_CHROME_GAP2;
9275
9591
  glowElRef.current.style.top = `${rect.top - GAP}px`;
9276
9592
  glowElRef.current.style.left = `${rect.left - GAP}px`;
9277
9593
  glowElRef.current.style.width = `${rect.width + GAP * 2}px`;
@@ -9359,6 +9675,11 @@ function OhhwellsBridge() {
9359
9675
  return;
9360
9676
  }
9361
9677
  }
9678
+ const footerColumn = findHoveredNavOrFooterContainer(clientX, clientY);
9679
+ if (footerColumn?.hasAttribute("data-ohw-footer-column")) {
9680
+ selectFrameRef.current(footerColumn);
9681
+ return;
9682
+ }
9362
9683
  deactivateRef.current();
9363
9684
  };
9364
9685
  window.addEventListener("message", handleSave);
@@ -9648,7 +9969,7 @@ function OhhwellsBridge() {
9648
9969
  ),
9649
9970
  siblingHintRect && !isItemDragging && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(ItemInteractionLayer, { rect: siblingHintRect, state: "sibling-hint" }),
9650
9971
  hoveredNavContainerRect && toolbarVariant !== "select-frame" && !linkPopover && !isItemDragging && !siblingHintRect && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(ItemInteractionLayer, { rect: hoveredNavContainerRect, state: "hover" }),
9651
- toolbarVariant === "select-frame" && toolbarRect && !linkPopover && !isItemDragging && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
9972
+ toolbarVariant === "select-frame" && toolbarRect && !linkPopover && !isItemDragging && selectedElRef.current && isNavbarLinksContainer(selectedElRef.current) && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
9652
9973
  NavbarContainerChrome,
9653
9974
  {
9654
9975
  rect: toolbarRect,
@@ -9673,7 +9994,9 @@ function OhhwellsBridge() {
9673
9994
  onEditLink: openLinkPopoverForSelected,
9674
9995
  addItemDisabled: true,
9675
9996
  editLinkDisabled: false,
9676
- moreDisabled: true
9997
+ moreDisabled: true,
9998
+ showAddItem: !selectedIsCta,
9999
+ showMore: !selectedIsCta
9677
10000
  }
9678
10001
  ) : void 0
9679
10002
  }
@@ -9784,7 +10107,7 @@ function OhhwellsBridge() {
9784
10107
  }
9785
10108
 
9786
10109
  // src/ui/drop-indicator.tsx
9787
- var React10 = __toESM(require("react"), 1);
10110
+ var React11 = __toESM(require("react"), 1);
9788
10111
  var import_jsx_runtime24 = require("react/jsx-runtime");
9789
10112
  var dropIndicatorVariants = cva(
9790
10113
  "ov-gap-line pointer-events-none shrink-0 transition-opacity duration-150",
@@ -9807,7 +10130,7 @@ var dropIndicatorVariants = cva(
9807
10130
  }
9808
10131
  }
9809
10132
  );
9810
- var DropIndicator = React10.forwardRef(
10133
+ var DropIndicator = React11.forwardRef(
9811
10134
  ({ className, direction, state, ...props }, ref) => {
9812
10135
  return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
9813
10136
  "div",