@ohhwells/bridge 0.1.44 → 0.1.45

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
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
 
3
3
  // src/OhhwellsBridge.tsx
4
- import React10, { useCallback as useCallback3, useEffect as useEffect7, useLayoutEffect as useLayoutEffect3, useRef as useRef6, useState as useState6 } from "react";
4
+ import React11, { useCallback as useCallback4, useEffect as useEffect8, useLayoutEffect as useLayoutEffect3, useRef as useRef7, useState as useState7 } from "react";
5
5
  import { createRoot } from "react-dom/client";
6
6
  import { flushSync } from "react-dom";
7
7
 
@@ -4286,7 +4286,7 @@ import { Link, MoreHorizontal, Plus } from "lucide-react";
4286
4286
 
4287
4287
  // src/ui/tooltip.tsx
4288
4288
  import { Tooltip as TooltipPrimitive } from "radix-ui";
4289
- import { jsx as jsx7, jsxs as jsxs3 } from "react/jsx-runtime";
4289
+ import { jsx as jsx7 } from "react/jsx-runtime";
4290
4290
  function TooltipProvider({
4291
4291
  delayDuration = 0,
4292
4292
  ...props
@@ -4299,39 +4299,45 @@ function Tooltip({ ...props }) {
4299
4299
  function TooltipTrigger({ ...props }) {
4300
4300
  return /* @__PURE__ */ jsx7(TooltipPrimitive.Trigger, { "data-slot": "tooltip-trigger", ...props });
4301
4301
  }
4302
+ var arrowClassBySide = {
4303
+ top: "before:bottom-0 before:left-1/2 before:-translate-x-1/2 before:translate-y-1/2",
4304
+ bottom: "before:top-0 before:left-1/2 before:-translate-x-1/2 before:-translate-y-1/2",
4305
+ left: "before:right-0 before:top-1/2 before:-translate-y-1/2 before:translate-x-1/2",
4306
+ right: "before:left-0 before:top-1/2 before:-translate-y-1/2 before:-translate-x-1/2"
4307
+ };
4302
4308
  function TooltipContent({
4303
4309
  className,
4304
- sideOffset = 3,
4310
+ sideOffset = 9,
4305
4311
  side = "bottom",
4306
4312
  children,
4307
4313
  hideArrow,
4308
4314
  ...props
4309
4315
  }) {
4310
- return /* @__PURE__ */ jsx7(TooltipPrimitive.Portal, { children: /* @__PURE__ */ jsxs3(
4316
+ const resolvedSide = side ?? "bottom";
4317
+ return /* @__PURE__ */ jsx7(TooltipPrimitive.Portal, { children: /* @__PURE__ */ jsx7(
4311
4318
  TooltipPrimitive.Content,
4312
4319
  {
4313
4320
  "data-slot": "tooltip-content",
4314
4321
  side,
4315
4322
  sideOffset,
4316
4323
  className: cn(
4317
- "z-[2147483647] inline-flex w-fit max-w-xs items-center rounded-md bg-foreground px-3 py-1.5 text-xs text-background",
4324
+ "relative z-[2147483647] inline-flex w-fit max-w-xs items-center rounded-md bg-foreground px-3 py-1.5 text-xs text-background",
4318
4325
  "origin-[var(--radix-tooltip-content-transform-origin)] animate-in fade-in-0 zoom-in-95",
4319
4326
  "data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95",
4320
4327
  "data-[side=bottom]:slide-in-from-top-2 data-[side=top]:slide-in-from-bottom-2",
4321
4328
  "data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2",
4329
+ !hideArrow && 'before:pointer-events-none before:absolute before:size-2 before:rotate-45 before:bg-foreground before:content-[""]',
4330
+ !hideArrow && arrowClassBySide[resolvedSide],
4322
4331
  className
4323
4332
  ),
4324
4333
  ...props,
4325
- children: [
4326
- children,
4327
- !hideArrow && /* @__PURE__ */ jsx7(TooltipPrimitive.Arrow, { className: "z-[2147483647] size-2.5 translate-y-[calc(-50%-2px)] rotate-45 rounded-[2px] bg-foreground fill-foreground" })
4328
- ]
4334
+ children
4329
4335
  }
4330
4336
  ) });
4331
4337
  }
4332
4338
 
4333
4339
  // src/ui/item-action-toolbar.tsx
4334
- import { jsx as jsx8, jsxs as jsxs4 } from "react/jsx-runtime";
4340
+ import { jsx as jsx8, jsxs as jsxs3 } from "react/jsx-runtime";
4335
4341
  function ToolbarActionTooltip({
4336
4342
  label,
4337
4343
  side = "bottom",
@@ -4340,9 +4346,9 @@ function ToolbarActionTooltip({
4340
4346
  children
4341
4347
  }) {
4342
4348
  const button = /* @__PURE__ */ jsx8(CustomToolbarButton, { type: "button", "aria-label": label, disabled, ...buttonProps, children });
4343
- return /* @__PURE__ */ jsxs4(Tooltip, { children: [
4349
+ return /* @__PURE__ */ jsxs3(Tooltip, { children: [
4344
4350
  /* @__PURE__ */ jsx8(TooltipTrigger, { asChild: true, children: disabled ? /* @__PURE__ */ jsx8("span", { className: "inline-flex", children: button }) : button }),
4345
- /* @__PURE__ */ jsx8(TooltipContent, { side, sideOffset: 3, children: label })
4351
+ /* @__PURE__ */ jsx8(TooltipContent, { side, sideOffset: 9, children: label })
4346
4352
  ] });
4347
4353
  }
4348
4354
  function ItemActionToolbar({
@@ -4356,7 +4362,7 @@ function ItemActionToolbar({
4356
4362
  showMore = true,
4357
4363
  tooltipSide = "bottom"
4358
4364
  }) {
4359
- return /* @__PURE__ */ jsx8(TooltipProvider, { delayDuration: 0, children: /* @__PURE__ */ jsxs4(CustomToolbar, { children: [
4365
+ return /* @__PURE__ */ jsx8(TooltipProvider, { delayDuration: 0, children: /* @__PURE__ */ jsxs3(CustomToolbar, { children: [
4360
4366
  /* @__PURE__ */ jsx8(
4361
4367
  ToolbarActionTooltip,
4362
4368
  {
@@ -4417,13 +4423,26 @@ function ItemActionToolbar({
4417
4423
 
4418
4424
  // src/ui/item-interaction-layer.tsx
4419
4425
  import * as React5 from "react";
4420
- import { jsx as jsx9, jsxs as jsxs5 } from "react/jsx-runtime";
4426
+ import { jsx as jsx9, jsxs as jsxs4 } from "react/jsx-runtime";
4421
4427
  var PRIMARY = "var(--ohw-primary, #0885FE)";
4422
4428
  var FOCUS_RING = `0 0 0 4px color-mix(in srgb, ${PRIMARY} 12%, transparent)`;
4423
4429
  var DRAG_SHADOW = "0px 4px 6px -1px rgba(0, 0, 0, 0.1), 0px 2px 4px -2px rgba(0, 0, 0, 0.1)";
4424
4430
  var TOOLBAR_EDGE_MARGIN = 4;
4425
4431
  var SELECTION_CHROME_GAP = 4;
4426
4432
  var TOOLBAR_STROKE_GAP = 4;
4433
+ function getChromeZIndex(state) {
4434
+ switch (state) {
4435
+ case "active-top":
4436
+ case "active-bottom":
4437
+ case "dragging":
4438
+ return 2147483644;
4439
+ case "hover":
4440
+ case "sibling-hint":
4441
+ return 2147483643;
4442
+ default:
4443
+ return 2147483642;
4444
+ }
4445
+ }
4427
4446
  function getChromeStyle(state) {
4428
4447
  switch (state) {
4429
4448
  case "hover":
@@ -4445,7 +4464,7 @@ function getChromeStyle(state) {
4445
4464
  case "dragging":
4446
4465
  return {
4447
4466
  border: `2px solid ${PRIMARY}`,
4448
- boxShadow: DRAG_SHADOW
4467
+ boxShadow: `${FOCUS_RING}, ${DRAG_SHADOW}`
4449
4468
  };
4450
4469
  default:
4451
4470
  return {};
@@ -4514,6 +4533,9 @@ function ItemInteractionLayer({
4514
4533
  dragHandleLabel = "Reorder item",
4515
4534
  onDragHandleDragStart,
4516
4535
  onDragHandleDragEnd,
4536
+ onItemPointerDown,
4537
+ onItemClick,
4538
+ itemDragSurface = true,
4517
4539
  chromeGap = SELECTION_CHROME_GAP,
4518
4540
  className
4519
4541
  }) {
@@ -4521,8 +4543,9 @@ function ItemInteractionLayer({
4521
4543
  const isActive = state === "active-top" || state === "active-bottom";
4522
4544
  const isDragging = state === "dragging";
4523
4545
  const showToolbar = isActive && toolbar;
4524
- const showDragHandle = isActive && showHandle && !isDragging;
4525
- return /* @__PURE__ */ jsxs5(
4546
+ const showDragHandle = (isActive || isDragging) && showHandle;
4547
+ const itemDragEnabled = itemDragSurface && isActive && showHandle && !isDragging && !dragDisabled;
4548
+ return /* @__PURE__ */ jsxs4(
4526
4549
  "div",
4527
4550
  {
4528
4551
  ref: elRef,
@@ -4535,7 +4558,7 @@ function ItemInteractionLayer({
4535
4558
  left: rect.left - chromeGap,
4536
4559
  width: rect.width + chromeGap * 2,
4537
4560
  height: rect.height + chromeGap * 2,
4538
- zIndex: 2147483646
4561
+ zIndex: getChromeZIndex(state)
4539
4562
  },
4540
4563
  children: [
4541
4564
  /* @__PURE__ */ jsx9(
@@ -4546,11 +4569,29 @@ function ItemInteractionLayer({
4546
4569
  style: getChromeStyle(state)
4547
4570
  }
4548
4571
  ),
4572
+ itemDragEnabled && /* @__PURE__ */ jsx9(
4573
+ "div",
4574
+ {
4575
+ "data-ohw-item-drag-surface": "",
4576
+ className: "pointer-events-auto absolute inset-0 cursor-text rounded-lg",
4577
+ onPointerDown: (e) => {
4578
+ if (e.button !== 0) return;
4579
+ onItemPointerDown?.(e);
4580
+ },
4581
+ onClick: (e) => {
4582
+ e.preventDefault();
4583
+ e.stopPropagation();
4584
+ onItemClick?.(e.clientX, e.clientY);
4585
+ }
4586
+ }
4587
+ ),
4549
4588
  showDragHandle && /* @__PURE__ */ jsx9(
4550
4589
  "div",
4551
4590
  {
4552
4591
  "data-ohw-drag-handle-container": "",
4553
- className: "pointer-events-auto absolute left-0 top-1/2 -translate-x-[calc(100%+7px)] -translate-y-1/2",
4592
+ className: "pointer-events-auto absolute left-0 top-1/2 z-10 -translate-x-[calc(100%+7px)] -translate-y-1/2",
4593
+ style: isDragging ? { opacity: 0 } : void 0,
4594
+ "aria-hidden": isDragging || void 0,
4554
4595
  children: /* @__PURE__ */ jsx9(
4555
4596
  DragHandle,
4556
4597
  {
@@ -4562,7 +4603,17 @@ function ItemInteractionLayer({
4562
4603
  e.preventDefault();
4563
4604
  return;
4564
4605
  }
4565
- e.dataTransfer?.setData("text/plain", dragHandleLabel);
4606
+ const blank = document.createElement("canvas");
4607
+ blank.width = 1;
4608
+ blank.height = 1;
4609
+ blank.style.cssText = "position:fixed;left:-9999px;top:-9999px;width:1px;height:1px;pointer-events:none";
4610
+ document.body.appendChild(blank);
4611
+ try {
4612
+ e.dataTransfer.setDragImage(blank, 0, 0);
4613
+ } finally {
4614
+ requestAnimationFrame(() => blank.remove());
4615
+ }
4616
+ e.dataTransfer.setData("text/plain", dragHandleLabel);
4566
4617
  e.dataTransfer.effectAllowed = "move";
4567
4618
  onDragHandleDragStart?.(e);
4568
4619
  },
@@ -4623,7 +4674,7 @@ var Button = React6.forwardRef(
4623
4674
  Button.displayName = "Button";
4624
4675
 
4625
4676
  // src/ui/MediaOverlay.tsx
4626
- import { Fragment as Fragment2, jsx as jsx11, jsxs as jsxs6 } from "react/jsx-runtime";
4677
+ import { Fragment as Fragment2, jsx as jsx11, jsxs as jsxs5 } from "react/jsx-runtime";
4627
4678
  var MEDIA_UPLOAD_FADE_MS = 300;
4628
4679
  var VIDEO_SETTINGS_BAR_INSET = 8;
4629
4680
  var OVERLAY_BUTTON_STYLE = {
@@ -4694,7 +4745,7 @@ function MediaOverlay({
4694
4745
  }
4695
4746
  );
4696
4747
  }
4697
- const settingsBar = isVideo && !hover.isDragOver ? /* @__PURE__ */ jsxs6(
4748
+ const settingsBar = isVideo && !hover.isDragOver ? /* @__PURE__ */ jsxs5(
4698
4749
  "div",
4699
4750
  {
4700
4751
  "data-ohw-bridge": "",
@@ -4749,7 +4800,7 @@ function MediaOverlay({
4749
4800
  ]
4750
4801
  }
4751
4802
  ) : null;
4752
- return /* @__PURE__ */ jsxs6(Fragment2, { children: [
4803
+ return /* @__PURE__ */ jsxs5(Fragment2, { children: [
4753
4804
  settingsBar,
4754
4805
  /* @__PURE__ */ jsx11(
4755
4806
  "div",
@@ -4768,7 +4819,7 @@ function MediaOverlay({
4768
4819
  background: "color-mix(in srgb, var(--color-primary) 20%, transparent)"
4769
4820
  },
4770
4821
  onClick: () => onReplace(hover.key),
4771
- children: /* @__PURE__ */ jsxs6(
4822
+ children: /* @__PURE__ */ jsxs5(
4772
4823
  Button,
4773
4824
  {
4774
4825
  "data-ohw-media-overlay": "",
@@ -5078,7 +5129,7 @@ import { useEffect as useEffect6 } from "react";
5078
5129
  import * as React8 from "react";
5079
5130
  import { Dialog as DialogPrimitive } from "radix-ui";
5080
5131
  import { X } from "lucide-react";
5081
- import { jsx as jsx12, jsxs as jsxs7 } from "react/jsx-runtime";
5132
+ import { jsx as jsx12, jsxs as jsxs6 } from "react/jsx-runtime";
5082
5133
  function Dialog2({
5083
5134
  ...props
5084
5135
  }) {
@@ -5106,9 +5157,9 @@ function DialogOverlay({
5106
5157
  var DialogContent = React8.forwardRef(
5107
5158
  ({ className, children, showCloseButton = true, container, ...props }, ref) => {
5108
5159
  const positionMode = container ? "absolute" : "fixed";
5109
- return /* @__PURE__ */ jsxs7(DialogPortal, { container: container ?? void 0, children: [
5160
+ return /* @__PURE__ */ jsxs6(DialogPortal, { container: container ?? void 0, children: [
5110
5161
  /* @__PURE__ */ jsx12(DialogOverlay, { className: cn(positionMode, "inset-0") }),
5111
- /* @__PURE__ */ jsxs7(
5162
+ /* @__PURE__ */ jsxs6(
5112
5163
  DialogPrimitive.Content,
5113
5164
  {
5114
5165
  ref,
@@ -5186,15 +5237,15 @@ import { Info, X as X3 } from "lucide-react";
5186
5237
 
5187
5238
  // src/ui/link-modal/DestinationBreadcrumb.tsx
5188
5239
  import { ArrowRight, File, GalleryVertical } from "lucide-react";
5189
- import { jsx as jsx13, jsxs as jsxs8 } from "react/jsx-runtime";
5240
+ import { jsx as jsx13, jsxs as jsxs7 } from "react/jsx-runtime";
5190
5241
  function DestinationBreadcrumb({
5191
5242
  pageTitle,
5192
5243
  sectionLabel
5193
5244
  }) {
5194
- return /* @__PURE__ */ jsxs8("div", { className: "flex w-full flex-col gap-2", children: [
5245
+ return /* @__PURE__ */ jsxs7("div", { className: "flex w-full flex-col gap-2", children: [
5195
5246
  /* @__PURE__ */ jsx13("p", { className: "text-sm font-medium! text-foreground m-0", children: "Destination" }),
5196
- /* @__PURE__ */ jsxs8("div", { className: "flex items-center gap-3", children: [
5197
- /* @__PURE__ */ jsxs8("div", { className: "flex items-center gap-2", children: [
5247
+ /* @__PURE__ */ jsxs7("div", { className: "flex items-center gap-3", children: [
5248
+ /* @__PURE__ */ jsxs7("div", { className: "flex items-center gap-2", children: [
5198
5249
  /* @__PURE__ */ jsx13(File, { size: 16, className: "shrink-0 text-foreground", "aria-hidden": true }),
5199
5250
  /* @__PURE__ */ jsx13("span", { className: "text-sm font-medium leading-none text-foreground!", children: pageTitle })
5200
5251
  ] }),
@@ -5206,7 +5257,7 @@ function DestinationBreadcrumb({
5206
5257
  "aria-hidden": true
5207
5258
  }
5208
5259
  ),
5209
- /* @__PURE__ */ jsxs8("div", { className: "flex min-w-0 flex-1 items-center gap-2", children: [
5260
+ /* @__PURE__ */ jsxs7("div", { className: "flex min-w-0 flex-1 items-center gap-2", children: [
5210
5261
  /* @__PURE__ */ jsx13(
5211
5262
  GalleryVertical,
5212
5263
  {
@@ -5223,14 +5274,14 @@ function DestinationBreadcrumb({
5223
5274
 
5224
5275
  // src/ui/link-modal/SectionTreeItem.tsx
5225
5276
  import { GalleryVertical as GalleryVertical2 } from "lucide-react";
5226
- import { jsx as jsx14, jsxs as jsxs9 } from "react/jsx-runtime";
5277
+ import { jsx as jsx14, jsxs as jsxs8 } from "react/jsx-runtime";
5227
5278
  function SectionTreeItem({
5228
5279
  section,
5229
5280
  onSelect,
5230
5281
  selected
5231
5282
  }) {
5232
5283
  const interactive = Boolean(onSelect);
5233
- return /* @__PURE__ */ jsxs9("div", { className: "flex h-9 w-full items-end pl-3", children: [
5284
+ return /* @__PURE__ */ jsxs8("div", { className: "flex h-9 w-full items-end pl-3", children: [
5234
5285
  /* @__PURE__ */ jsx14(
5235
5286
  "div",
5236
5287
  {
@@ -5238,7 +5289,7 @@ function SectionTreeItem({
5238
5289
  "aria-hidden": true
5239
5290
  }
5240
5291
  ),
5241
- /* @__PURE__ */ jsxs9(
5292
+ /* @__PURE__ */ jsxs8(
5242
5293
  "div",
5243
5294
  {
5244
5295
  role: interactive ? "button" : void 0,
@@ -5312,7 +5363,7 @@ function Label({ className, ...props }) {
5312
5363
 
5313
5364
  // src/ui/link-modal/UrlOrPageInput.tsx
5314
5365
  import { ChevronDown, File as File2, X as X2 } from "lucide-react";
5315
- import { jsx as jsx17, jsxs as jsxs10 } from "react/jsx-runtime";
5366
+ import { jsx as jsx17, jsxs as jsxs9 } from "react/jsx-runtime";
5316
5367
  function FieldChevron({
5317
5368
  onClick
5318
5369
  }) {
@@ -5385,10 +5436,10 @@ function UrlOrPageInput({
5385
5436
  "data-ohw-link-field flex h-[36px] w-full items-center overflow-hidden rounded-md border bg-background pl-3 pr-3 py-2 outline-none transition-[border-color,box-shadow]",
5386
5437
  urlError ? "border-destructive shadow-[0_0_0_1px_var(--ohw-destructive)]" : isFocused ? "border-primary shadow-[0_0_0_1px_var(--ohw-primary)]" : "border-input"
5387
5438
  );
5388
- return /* @__PURE__ */ jsxs10("div", { className: "flex w-full flex-col gap-2 p-0", children: [
5439
+ return /* @__PURE__ */ jsxs9("div", { className: "flex w-full flex-col gap-2 p-0", children: [
5389
5440
  /* @__PURE__ */ jsx17(Label, { htmlFor: inputId, className: cn(urlError && "text-destructive"), children: "Destination" }),
5390
- /* @__PURE__ */ jsxs10("div", { ref: rootRef, className: "relative w-full", children: [
5391
- /* @__PURE__ */ jsxs10("div", { "data-ohw-link-field": true, className: fieldClassName, children: [
5441
+ /* @__PURE__ */ jsxs9("div", { ref: rootRef, className: "relative w-full", children: [
5442
+ /* @__PURE__ */ jsxs9("div", { "data-ohw-link-field": true, className: fieldClassName, children: [
5392
5443
  selectedPage ? /* @__PURE__ */ jsx17("div", { className: "flex shrink-0 items-center pr-2", children: /* @__PURE__ */ jsx17(
5393
5444
  File2,
5394
5445
  {
@@ -5442,7 +5493,7 @@ function UrlOrPageInput({
5442
5493
  "data-ohw-link-page-dropdown": "",
5443
5494
  className: "absolute left-0 right-0 top-[calc(100%+4px)] z-50 max-h-48 overflow-auto rounded-lg border border-border bg-popover py-1 shadow-lg",
5444
5495
  onMouseDown: (e) => e.preventDefault(),
5445
- children: filteredPages.map((page) => /* @__PURE__ */ jsxs10(
5496
+ children: filteredPages.map((page) => /* @__PURE__ */ jsxs9(
5446
5497
  "button",
5447
5498
  {
5448
5499
  type: "button",
@@ -5463,10 +5514,10 @@ function UrlOrPageInput({
5463
5514
  }
5464
5515
 
5465
5516
  // src/ui/link-modal/LinkEditorPanel.tsx
5466
- import { Fragment as Fragment3, jsx as jsx18, jsxs as jsxs11 } from "react/jsx-runtime";
5517
+ import { Fragment as Fragment3, jsx as jsx18, jsxs as jsxs10 } from "react/jsx-runtime";
5467
5518
  function LinkEditorPanel({ state, onClose }) {
5468
5519
  const isCancel = state.secondaryLabel === "Cancel" || state.secondaryLabel === "Back to sections";
5469
- return /* @__PURE__ */ jsxs11(Fragment3, { children: [
5520
+ return /* @__PURE__ */ jsxs10(Fragment3, { children: [
5470
5521
  /* @__PURE__ */ jsx18(DialogClose, { asChild: true, children: /* @__PURE__ */ jsx18(
5471
5522
  "button",
5472
5523
  {
@@ -5478,7 +5529,7 @@ function LinkEditorPanel({ state, onClose }) {
5478
5529
  }
5479
5530
  ) }),
5480
5531
  /* @__PURE__ */ jsx18(DialogHeader, { className: "w-full gap-1.5 p-6 pr-12", children: /* @__PURE__ */ jsx18(DialogTitle, { className: "m-0 w-full break-words", children: state.title }) }),
5481
- /* @__PURE__ */ jsxs11("div", { className: "flex w-full flex-col gap-3 px-6 pb-8 pt-1", children: [
5532
+ /* @__PURE__ */ jsxs10("div", { className: "flex w-full flex-col gap-3 px-6 pb-8 pt-1", children: [
5482
5533
  state.showBreadcrumb && state.selectedPage && state.selectedSection ? /* @__PURE__ */ jsx18(
5483
5534
  DestinationBreadcrumb,
5484
5535
  {
@@ -5498,7 +5549,7 @@ function LinkEditorPanel({ state, onClose }) {
5498
5549
  urlError: state.urlError
5499
5550
  }
5500
5551
  ),
5501
- state.showChooseSection ? /* @__PURE__ */ jsxs11("div", { className: "flex flex-col justify-center gap-2", children: [
5552
+ state.showChooseSection ? /* @__PURE__ */ jsxs10("div", { className: "flex flex-col justify-center gap-2", children: [
5502
5553
  /* @__PURE__ */ jsx18(
5503
5554
  Button,
5504
5555
  {
@@ -5510,14 +5561,14 @@ function LinkEditorPanel({ state, onClose }) {
5510
5561
  children: "Choose a section"
5511
5562
  }
5512
5563
  ),
5513
- /* @__PURE__ */ jsxs11("div", { className: "flex items-center gap-1 text-sm text-muted-foreground", children: [
5564
+ /* @__PURE__ */ jsxs10("div", { className: "flex items-center gap-1 text-sm text-muted-foreground", children: [
5514
5565
  /* @__PURE__ */ jsx18(Info, { size: 16, className: "shrink-0", "aria-hidden": true }),
5515
5566
  /* @__PURE__ */ jsx18("span", { children: "Pick a section this link should scroll to." })
5516
5567
  ] })
5517
5568
  ] }) : null,
5518
5569
  state.showSectionRow && state.selectedSection ? /* @__PURE__ */ jsx18(SectionTreeItem, { section: state.selectedSection, selected: true }) : null
5519
5570
  ] }),
5520
- /* @__PURE__ */ jsxs11(DialogFooter, { className: "w-full px-6 pb-6", children: [
5571
+ /* @__PURE__ */ jsxs10(DialogFooter, { className: "w-full px-6 pb-6", children: [
5521
5572
  /* @__PURE__ */ jsx18(
5522
5573
  Button,
5523
5574
  {
@@ -5556,7 +5607,7 @@ import { useCallback, useEffect as useEffect4, useMemo as useMemo2, useRef as us
5556
5607
  import { createPortal } from "react-dom";
5557
5608
  import { ArrowLeft, Check } from "lucide-react";
5558
5609
  import { usePathname, useRouter } from "next/navigation";
5559
- import { jsx as jsx19, jsxs as jsxs12 } from "react/jsx-runtime";
5610
+ import { jsx as jsx19, jsxs as jsxs11 } from "react/jsx-runtime";
5560
5611
  var DIM_OVERLAY = "rgba(0, 0, 0, 0.45)";
5561
5612
  function rectsEqual(a, b) {
5562
5613
  if (a.size !== b.size) return false;
@@ -5785,7 +5836,7 @@ function SectionPickerOverlay({
5785
5836
  const portalRoot = typeof document !== "undefined" ? document.querySelector("[data-ohw-bridge-root]") ?? document.body : null;
5786
5837
  if (!portalRoot) return null;
5787
5838
  return createPortal(
5788
- /* @__PURE__ */ jsxs12(
5839
+ /* @__PURE__ */ jsxs11(
5789
5840
  "div",
5790
5841
  {
5791
5842
  "data-ohw-section-picker": "",
@@ -5800,7 +5851,7 @@ function SectionPickerOverlay({
5800
5851
  {
5801
5852
  className: "pointer-events-auto fixed left-5 z-[2]",
5802
5853
  style: { top: chromeClip.top + 20 },
5803
- children: /* @__PURE__ */ jsxs12(
5854
+ children: /* @__PURE__ */ jsxs11(
5804
5855
  Button,
5805
5856
  {
5806
5857
  type: "button",
@@ -5844,7 +5895,7 @@ function SectionPickerOverlay({
5844
5895
  const isSelected = selectedId === section.id;
5845
5896
  const isHovered = hoveredId === section.id;
5846
5897
  const isLit = isSelected || isHovered;
5847
- return /* @__PURE__ */ jsxs12(
5898
+ return /* @__PURE__ */ jsxs11(
5848
5899
  "button",
5849
5900
  {
5850
5901
  type: "button",
@@ -6058,7 +6109,7 @@ function useLinkModalState({
6058
6109
  }
6059
6110
 
6060
6111
  // src/ui/link-modal/LinkPopover.tsx
6061
- import { Fragment as Fragment4, jsx as jsx20, jsxs as jsxs13 } from "react/jsx-runtime";
6112
+ import { Fragment as Fragment4, jsx as jsx20, jsxs as jsxs12 } from "react/jsx-runtime";
6062
6113
  function postToParent(data) {
6063
6114
  window.parent?.postMessage(data, "*");
6064
6115
  }
@@ -6154,7 +6205,7 @@ function LinkPopover({
6154
6205
  );
6155
6206
  };
6156
6207
  }, [open, sectionPickerActive]);
6157
- return /* @__PURE__ */ jsxs13(Fragment4, { children: [
6208
+ return /* @__PURE__ */ jsxs12(Fragment4, { children: [
6158
6209
  /* @__PURE__ */ jsx20(
6159
6210
  Dialog2,
6160
6211
  {
@@ -6258,9 +6309,155 @@ function shouldUseDevFixtures() {
6258
6309
  return new URLSearchParams(q).get("ohw-fixtures") === "1";
6259
6310
  }
6260
6311
 
6312
+ // src/lib/nav-tree.ts
6313
+ var MAX_LEVEL = { nav: 1 };
6314
+ var ROOT = null;
6315
+ function cloneForest(nodes) {
6316
+ return nodes.map((n) => ({ id: n.id, children: cloneForest(n.children) }));
6317
+ }
6318
+ function findNode(nodes, id, parentId = ROOT) {
6319
+ for (let i = 0; i < nodes.length; i++) {
6320
+ const node = nodes[i];
6321
+ if (node.id === id) return { node, parentId, index: i, siblings: nodes };
6322
+ const nested = findNode(node.children, id, node.id);
6323
+ if (nested) return nested;
6324
+ }
6325
+ return null;
6326
+ }
6327
+ function getChildrenOf(nodes, parentId) {
6328
+ if (parentId === ROOT) return nodes;
6329
+ const found = findNode(nodes, parentId);
6330
+ return found ? found.node.children : null;
6331
+ }
6332
+ function isDescendant(nodes, ancestorId, maybeDescendantId) {
6333
+ const found = findNode(nodes, ancestorId);
6334
+ if (!found) return false;
6335
+ return findNode(found.node.children, maybeDescendantId) !== null;
6336
+ }
6337
+ function levelOf(nodes, id) {
6338
+ const loc = findNode(nodes, id);
6339
+ if (!loc) return -1;
6340
+ if (loc.parentId === ROOT) return 0;
6341
+ return 1 + Math.max(0, levelOf(nodes, loc.parentId));
6342
+ }
6343
+ function insertedLevel(nodes, parentId) {
6344
+ if (parentId === ROOT) return 0;
6345
+ const parentLevel = levelOf(nodes, parentId);
6346
+ if (parentLevel < 0) return -1;
6347
+ return parentLevel + 1;
6348
+ }
6349
+ function takeNode(nodes, id) {
6350
+ const forest = cloneForest(nodes);
6351
+ const loc = findNode(forest, id);
6352
+ if (!loc) return null;
6353
+ const [taken] = loc.siblings.splice(loc.index, 1);
6354
+ if (!taken) return null;
6355
+ return { forest, taken };
6356
+ }
6357
+ function insertInto(forest, node, parentId, index) {
6358
+ const siblings = getChildrenOf(forest, parentId);
6359
+ if (!siblings) return false;
6360
+ const clamped = Math.max(0, Math.min(index, siblings.length));
6361
+ siblings.splice(clamped, 0, node);
6362
+ return true;
6363
+ }
6364
+ function normalize(nodes) {
6365
+ const walk = (list) => list.map((n) => {
6366
+ const children = walk(n.children);
6367
+ return { id: n.id, children };
6368
+ });
6369
+ return walk(nodes);
6370
+ }
6371
+ function moveNode(model, draggedId, target) {
6372
+ const { parentId, index: targetIndex } = target;
6373
+ if (parentId === draggedId) return null;
6374
+ if (parentId !== ROOT && isDescendant(model, draggedId, parentId)) return null;
6375
+ const nextLevel = insertedLevel(model, parentId);
6376
+ if (nextLevel < 0 || nextLevel > MAX_LEVEL.nav) return null;
6377
+ const draggedLoc = findNode(model, draggedId);
6378
+ if (!draggedLoc) return null;
6379
+ if (parentId !== ROOT && draggedLoc.node.children.length > 0) return null;
6380
+ const taken = takeNode(model, draggedId);
6381
+ if (!taken) return null;
6382
+ let insertIndex = targetIndex;
6383
+ if (draggedLoc.parentId === parentId && draggedLoc.index < targetIndex) {
6384
+ insertIndex -= 1;
6385
+ }
6386
+ if (!insertInto(taken.forest, taken.taken, parentId, insertIndex)) return null;
6387
+ const next = normalize(taken.forest);
6388
+ if (forestsEqual(model, next)) return null;
6389
+ return next;
6390
+ }
6391
+ function forestsEqual(a, b) {
6392
+ if (a.length !== b.length) return false;
6393
+ for (let i = 0; i < a.length; i++) {
6394
+ const left = a[i];
6395
+ const right = b[i];
6396
+ if (left.id !== right.id) return false;
6397
+ if (!forestsEqual(left.children, right.children)) return false;
6398
+ }
6399
+ return true;
6400
+ }
6401
+ function forestFromFlatOrder(order) {
6402
+ return order.map((id) => ({ id, children: [] }));
6403
+ }
6404
+ function serializeNavOrder(nodes) {
6405
+ const hasNesting = nodes.some((n) => n.children.length > 0);
6406
+ if (!hasNesting) {
6407
+ return JSON.stringify(nodes.map((n) => n.id));
6408
+ }
6409
+ return JSON.stringify(nodes);
6410
+ }
6411
+ function parseNavOrderJson(raw) {
6412
+ if (!raw) return null;
6413
+ try {
6414
+ const parsed = JSON.parse(raw);
6415
+ if (!Array.isArray(parsed)) return null;
6416
+ if (parsed.every((x) => typeof x === "string")) {
6417
+ return forestFromFlatOrder(parsed.filter((x) => typeof x === "string" && x.length > 0));
6418
+ }
6419
+ return parseNavNodeList(parsed);
6420
+ } catch {
6421
+ return null;
6422
+ }
6423
+ }
6424
+ function parseNavNodeList(raw) {
6425
+ const out = [];
6426
+ for (const item of raw) {
6427
+ if (typeof item === "string" && item.length > 0) {
6428
+ out.push({ id: item, children: [] });
6429
+ continue;
6430
+ }
6431
+ if (!item || typeof item !== "object") return null;
6432
+ const rec = item;
6433
+ if (typeof rec.id !== "string" || !rec.id) return null;
6434
+ const children = Array.isArray(rec.children) ? parseNavNodeList(rec.children) : [];
6435
+ if (!children) return null;
6436
+ out.push({ id: rec.id, children });
6437
+ }
6438
+ return out;
6439
+ }
6440
+ function flattenNavOrder(nodes) {
6441
+ const out = [];
6442
+ const walk = (list) => {
6443
+ for (const n of list) {
6444
+ out.push(n.id);
6445
+ walk(n.children);
6446
+ }
6447
+ };
6448
+ walk(nodes);
6449
+ return out;
6450
+ }
6451
+
6261
6452
  // src/lib/nav-items.ts
6262
6453
  var NAV_COUNT_KEY = "__ohw_nav_count";
6263
6454
  var NAV_ORDER_KEY = "__ohw_nav_order";
6455
+ var NAV_HREF_RE = /^nav-(\d+)-href$/;
6456
+ function isNavbarHrefKey(key) {
6457
+ if (!key) return false;
6458
+ if (key === "nav-book-href") return false;
6459
+ return NAV_HREF_RE.test(key);
6460
+ }
6264
6461
  function getLinkHref(el) {
6265
6462
  const key = el.getAttribute("data-ohw-href-key");
6266
6463
  if (key) {
@@ -6297,7 +6494,7 @@ function listNavbarItems() {
6297
6494
  }
6298
6495
  function parseNavIndexFromKey(key) {
6299
6496
  if (!key) return null;
6300
- const match = key.match(/^nav-(\d+)-href$/);
6497
+ const match = key.match(NAV_HREF_RE);
6301
6498
  if (!match) return null;
6302
6499
  return parseInt(match[1], 10);
6303
6500
  }
@@ -6320,17 +6517,6 @@ function getNavbarExistingTargets() {
6320
6517
  function getNavOrderFromDom() {
6321
6518
  return listNavbarItems().map((el) => el.getAttribute("data-ohw-href-key")).filter((key) => Boolean(key));
6322
6519
  }
6323
- function parseNavOrder(content) {
6324
- const raw = content[NAV_ORDER_KEY];
6325
- if (!raw) return null;
6326
- try {
6327
- const parsed = JSON.parse(raw);
6328
- if (!Array.isArray(parsed)) return null;
6329
- return parsed.filter((key) => typeof key === "string" && key.length > 0);
6330
- } catch {
6331
- return null;
6332
- }
6333
- }
6334
6520
  function findCounterpartByHrefKey(hrefKey, root) {
6335
6521
  return root.querySelector(`[data-ohw-href-key="${CSS.escape(hrefKey)}"]`);
6336
6522
  }
@@ -6394,6 +6580,13 @@ function insertNavbarItemDom(index, href, label, afterAnchor) {
6394
6580
  }
6395
6581
  return primary;
6396
6582
  }
6583
+ function navOrderKeysEqual(a, b) {
6584
+ if (a.length !== b.length) return false;
6585
+ for (let i = 0; i < a.length; i++) {
6586
+ if (a[i] !== b[i]) return false;
6587
+ }
6588
+ return true;
6589
+ }
6397
6590
  function getNavbarLinkKeysInContainer(container) {
6398
6591
  return Array.from(container.querySelectorAll("[data-ohw-href-key]")).filter(isNavbarLinkItem).map((el) => el.getAttribute("data-ohw-href-key")).filter((key) => Boolean(key));
6399
6592
  }
@@ -6402,6 +6595,8 @@ function applyNavOrderToContainer(container, order) {
6402
6595
  const current = getNavbarLinkKeysInContainer(container);
6403
6596
  const extras = current.filter((key) => !desired.includes(key));
6404
6597
  const expected = [...desired.filter((key) => current.includes(key)), ...extras];
6598
+ const orderedEls = [];
6599
+ if (navOrderKeysEqual(current, expected)) return;
6405
6600
  if (current.length === expected.length && current.every((key, i) => key === expected[i])) {
6406
6601
  return;
6407
6602
  }
@@ -6410,13 +6605,18 @@ function applyNavOrderToContainer(container, order) {
6410
6605
  if (seen.has(hrefKey)) continue;
6411
6606
  seen.add(hrefKey);
6412
6607
  const el = findCounterpartByHrefKey(hrefKey, container);
6413
- if (el) container.appendChild(el);
6608
+ if (el && isNavbarLinkItem(el)) orderedEls.push(el);
6414
6609
  }
6415
6610
  for (const el of container.querySelectorAll("[data-ohw-href-key]")) {
6416
6611
  if (!isNavbarLinkItem(el)) continue;
6417
6612
  const key = el.getAttribute("data-ohw-href-key");
6418
6613
  if (!key || seen.has(key)) continue;
6419
- container.appendChild(el);
6614
+ orderedEls.push(el);
6615
+ }
6616
+ for (const el of orderedEls) {
6617
+ if (container.lastElementChild !== el) {
6618
+ container.appendChild(el);
6619
+ }
6420
6620
  }
6421
6621
  }
6422
6622
  function applyNavOrder(order) {
@@ -6425,6 +6625,75 @@ function applyNavOrder(order) {
6425
6625
  const drawer = getNavbarDrawerContainer();
6426
6626
  if (drawer) applyNavOrderToContainer(drawer, order);
6427
6627
  }
6628
+ function applyChildrenOrder(parentHrefKey, childKeys, root) {
6629
+ const parent = findCounterpartByHrefKey(parentHrefKey, root);
6630
+ if (!parent) return;
6631
+ const group = parent.closest("[data-ohw-nav-group]");
6632
+ const childrenRoot = group?.querySelector(":scope > [data-ohw-nav-children]");
6633
+ if (!childrenRoot) return;
6634
+ const current = Array.from(childrenRoot.querySelectorAll(":scope > [data-ohw-href-key]")).map((el) => el.getAttribute("data-ohw-href-key")).filter((key) => Boolean(key));
6635
+ if (navOrderKeysEqual(current, childKeys)) return;
6636
+ for (const key of childKeys) {
6637
+ const el = findCounterpartByHrefKey(key, root);
6638
+ if (!el) continue;
6639
+ if (childrenRoot.lastElementChild !== el) {
6640
+ childrenRoot.appendChild(el);
6641
+ }
6642
+ }
6643
+ }
6644
+ function applyNavForest(forest) {
6645
+ if (forestsEqual(forest, getNavForestFromDom())) return;
6646
+ const rootKeys = forest.map((n) => n.id);
6647
+ applyNavOrder(rootKeys);
6648
+ const desktop = getNavbarDesktopContainer();
6649
+ const drawer = getNavbarDrawerContainer();
6650
+ for (const node of forest) {
6651
+ if (node.children.length === 0) continue;
6652
+ const childKeys = node.children.map((c) => c.id);
6653
+ if (desktop) applyChildrenOrder(node.id, childKeys, desktop);
6654
+ if (drawer) applyChildrenOrder(node.id, childKeys, drawer);
6655
+ }
6656
+ }
6657
+ function getNavForestFromDom() {
6658
+ const items = listNavbarItems();
6659
+ const nestedChildKeys = /* @__PURE__ */ new Set();
6660
+ const roots = [];
6661
+ for (const item of items) {
6662
+ const key = item.getAttribute("data-ohw-href-key");
6663
+ if (!key || !isNavbarHrefKey(key)) continue;
6664
+ if (item.closest("[data-ohw-nav-children]")) continue;
6665
+ const group = item.closest("[data-ohw-nav-group]");
6666
+ const childrenRoot = group?.querySelector(":scope > [data-ohw-nav-children]");
6667
+ const children = [];
6668
+ if (childrenRoot) {
6669
+ for (const child of childrenRoot.querySelectorAll(":scope > [data-ohw-href-key]")) {
6670
+ if (!isNavbarLinkItem(child)) continue;
6671
+ const childKey = child.getAttribute("data-ohw-href-key");
6672
+ if (!childKey || !isNavbarHrefKey(childKey)) continue;
6673
+ nestedChildKeys.add(childKey);
6674
+ children.push({ id: childKey, children: [] });
6675
+ }
6676
+ }
6677
+ roots.push({ id: key, children });
6678
+ }
6679
+ if (roots.length === 0) {
6680
+ return forestFromFlatOrder(
6681
+ getNavOrderFromDom().filter((k) => isNavbarHrefKey(k) && !nestedChildKeys.has(k))
6682
+ );
6683
+ }
6684
+ return roots;
6685
+ }
6686
+ function planNavItemMove(hrefKey, parentId, insertIndex) {
6687
+ const model = getNavForestFromDom();
6688
+ const next = moveNode(model, hrefKey, { parentId, index: insertIndex });
6689
+ if (!next) return null;
6690
+ if (forestsEqual(model, next)) return null;
6691
+ return {
6692
+ forest: next,
6693
+ orderJson: serializeNavOrder(next),
6694
+ flatOrder: flattenNavOrder(next)
6695
+ };
6696
+ }
6428
6697
  function collectNavbarIndicesFromContent(content) {
6429
6698
  const indices = /* @__PURE__ */ new Set();
6430
6699
  for (const key of Object.keys(content)) {
@@ -6452,8 +6721,10 @@ function reconcileNavbarItemsFromContent(content) {
6452
6721
  if (!href && !label) continue;
6453
6722
  insertNavbarItemDom(index, href ?? "/", label ?? "Untitled", null);
6454
6723
  }
6455
- const order = parseNavOrder(content);
6456
- if (order?.length) applyNavOrder(order);
6724
+ const orderForest = parseNavOrderJson(content[NAV_ORDER_KEY]);
6725
+ if (orderForest?.length) {
6726
+ applyNavForest(orderForest);
6727
+ }
6457
6728
  }
6458
6729
  function buildNavOrderAfterInsert(afterAnchor, newHrefKey) {
6459
6730
  const order = getNavOrderFromDom();
@@ -6493,161 +6764,1051 @@ function insertNavbarItem(href, label, afterAnchor = null) {
6493
6764
  };
6494
6765
  }
6495
6766
 
6496
- // src/ui/navbar-container-chrome.tsx
6497
- import { Plus as Plus2 } from "lucide-react";
6498
- import { jsx as jsx21 } from "react/jsx-runtime";
6499
- function NavbarContainerChrome({
6500
- rect,
6501
- onAdd
6502
- }) {
6503
- const chromeGap = 6;
6504
- return /* @__PURE__ */ jsx21(
6505
- "div",
6506
- {
6507
- "data-ohw-navbar-container-chrome": "",
6508
- "data-ohw-bridge": "",
6509
- className: "pointer-events-none fixed z-[2147483647]",
6510
- style: {
6511
- top: rect.top - chromeGap,
6512
- left: rect.left - chromeGap,
6513
- width: rect.width + chromeGap * 2,
6514
- height: rect.height + chromeGap * 2
6515
- },
6516
- children: /* @__PURE__ */ jsx21(
6517
- "button",
6518
- {
6519
- type: "button",
6520
- "data-ohw-navbar-add-button": "",
6521
- className: "pointer-events-auto absolute left-1/2 flex p-0.5 rounded-[10px] size-7 -translate-x-1/2 translate-y-1/2 items-center justify-center border border-border bg-background shadow-sm transition-colors hover:bg-muted/80",
6522
- style: { top: "70%" },
6523
- "aria-label": "Add item",
6524
- onMouseDown: (e) => {
6525
- e.preventDefault();
6526
- e.stopPropagation();
6527
- },
6528
- onClick: (e) => {
6529
- e.preventDefault();
6530
- e.stopPropagation();
6531
- onAdd();
6532
- },
6533
- children: /* @__PURE__ */ jsx21(Plus2, { className: "size-4 shrink-0 text-foreground", "aria-hidden": true })
6534
- }
6535
- )
6536
- }
6537
- );
6767
+ // src/lib/footer-items.ts
6768
+ var FOOTER_ORDER_KEY = "__ohw_footer_order";
6769
+ var FOOTER_HREF_RE = /^footer-(\d+)-(\d+)-href$/;
6770
+ function parseFooterHrefKey(key) {
6771
+ if (!key) return null;
6772
+ const match = key.match(FOOTER_HREF_RE);
6773
+ if (!match) return null;
6774
+ return { col: parseInt(match[1], 10), item: parseInt(match[2], 10) };
6538
6775
  }
6539
-
6540
- // src/ui/badge.tsx
6541
- import { jsx as jsx22 } from "react/jsx-runtime";
6542
- var badgeVariants = cva(
6543
- "inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
6544
- {
6545
- variants: {
6546
- variant: {
6547
- default: "border-transparent bg-primary text-primary-foreground",
6548
- secondary: "border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80",
6549
- destructive: "border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80",
6550
- outline: "text-foreground"
6551
- }
6552
- },
6553
- defaultVariants: {
6554
- variant: "default"
6555
- }
6556
- }
6557
- );
6558
- function Badge({ className, variant, ...props }) {
6559
- return /* @__PURE__ */ jsx22("div", { className: cn(badgeVariants({ variant }), className), ...props });
6776
+ function isFooterHrefKey(key) {
6777
+ return parseFooterHrefKey(key) !== null;
6560
6778
  }
6561
-
6562
- // src/OhhwellsBridge.tsx
6563
- import { Link as Link2 } from "lucide-react";
6564
- import { Fragment as Fragment5, jsx as jsx23, jsxs as jsxs14 } from "react/jsx-runtime";
6565
- var PRIMARY2 = "#0885FE";
6566
- var IMAGE_FADE_MS = 300;
6567
- function runOpacityFade(el, onDone) {
6568
- const anim = el.animate([{ opacity: 0 }, { opacity: 1 }], {
6569
- duration: IMAGE_FADE_MS,
6570
- easing: "ease",
6571
- fill: "forwards"
6572
- });
6573
- let finished = false;
6574
- const finish = () => {
6575
- if (finished) return;
6576
- finished = true;
6577
- anim.cancel();
6578
- el.style.opacity = "";
6579
- onDone();
6580
- };
6581
- anim.finished.then(finish).catch(finish);
6582
- window.setTimeout(finish, IMAGE_FADE_MS + 100);
6779
+ function getFooterRoot() {
6780
+ return document.querySelector('footer[data-ohw-section="footer"], footer');
6583
6781
  }
6584
- function fadeInImageElement(img, onReady) {
6585
- onReady();
6586
- img.style.opacity = "0";
6587
- runOpacityFade(img, () => {
6588
- img.style.opacity = "";
6782
+ function getFooterLinksContainer() {
6783
+ return document.querySelector("[data-ohw-footer-links]") ?? document.querySelector(".rb-footer-links");
6784
+ }
6785
+ function isFooterLinkAnchor(el) {
6786
+ if (!el.matches("[data-ohw-href-key]")) return false;
6787
+ if (!isFooterHrefKey(el.getAttribute("data-ohw-href-key"))) return false;
6788
+ return Boolean(el.querySelector('[data-ohw-editable="text"]'));
6789
+ }
6790
+ function listFooterColumns() {
6791
+ const root = getFooterLinksContainer() ?? getFooterRoot()?.querySelector(".rb-footer-links") ?? null;
6792
+ if (!root) return [];
6793
+ const explicit = Array.from(root.querySelectorAll(":scope > [data-ohw-footer-col]"));
6794
+ if (explicit.length > 0) return explicit;
6795
+ return Array.from(root.children).filter((el) => {
6796
+ if (!(el instanceof HTMLElement)) return false;
6797
+ return Array.from(el.querySelectorAll(":scope > [data-ohw-href-key]")).some(
6798
+ isFooterLinkAnchor
6799
+ );
6589
6800
  });
6590
6801
  }
6591
- function applyEditableImageSrc(img, url) {
6592
- img.removeAttribute("srcset");
6593
- img.removeAttribute("sizes");
6594
- img.src = url;
6802
+ function listFooterLinksInColumn(column) {
6803
+ return Array.from(column.querySelectorAll(":scope > [data-ohw-href-key]")).filter(
6804
+ isFooterLinkAnchor
6805
+ );
6595
6806
  }
6596
- function fadeInBgImage(el, url, onReady) {
6597
- const prevPos = el.style.position;
6598
- if (!prevPos || prevPos === "static") el.style.position = "relative";
6599
- const layer = document.createElement("div");
6600
- layer.setAttribute("data-ohw-bg-fade-layer", "");
6601
- Object.assign(layer.style, {
6602
- position: "absolute",
6603
- inset: "0",
6604
- zIndex: "0",
6605
- backgroundImage: `url('${url}')`,
6606
- backgroundSize: "cover",
6607
- backgroundPosition: "center",
6608
- backgroundRepeat: "no-repeat",
6609
- opacity: "0",
6610
- pointerEvents: "none"
6611
- });
6612
- el.prepend(layer);
6613
- onReady();
6614
- runOpacityFade(layer, () => {
6615
- el.style.backgroundImage = `url('${url}')`;
6616
- layer.remove();
6617
- if (!prevPos || prevPos === "static") el.style.position = prevPos;
6807
+ function findFooterColumnForLink(anchor) {
6808
+ const columns = listFooterColumns();
6809
+ return columns.find((col) => col.contains(anchor)) ?? null;
6810
+ }
6811
+ function getFooterOrderFromDom() {
6812
+ return listFooterColumns().map(
6813
+ (col) => listFooterLinksInColumn(col).map((el) => el.getAttribute("data-ohw-href-key")).filter((key) => Boolean(key))
6814
+ );
6815
+ }
6816
+ function findFooterLinkByKey(hrefKey) {
6817
+ return document.querySelector(
6818
+ `footer [data-ohw-href-key="${CSS.escape(hrefKey)}"]`
6819
+ );
6820
+ }
6821
+ function syncFooterColumnIndices(columns) {
6822
+ columns.forEach((col, index) => {
6823
+ const next = String(index);
6824
+ if (col.getAttribute("data-ohw-footer-col") !== next) {
6825
+ col.setAttribute("data-ohw-footer-col", next);
6826
+ }
6618
6827
  });
6619
6828
  }
6620
- function getSectionsTracker() {
6621
- let el = document.querySelector("[data-ohw-sections-tracker]");
6622
- if (!el) {
6623
- el = document.createElement("div");
6624
- el.setAttribute("data-ohw-sections-tracker", "");
6625
- el.style.display = "none";
6626
- document.body.appendChild(el);
6829
+ function appendChildIfNeeded(parent, child) {
6830
+ if (parent.lastElementChild !== child) {
6831
+ parent.appendChild(child);
6627
6832
  }
6628
- return el;
6629
6833
  }
6630
- function updateSectionScheduleId(insertAfter, scheduleId) {
6631
- const tracker = getSectionsTracker();
6632
- let sections = [];
6633
- try {
6634
- sections = JSON.parse(tracker.textContent || "[]");
6635
- } catch {
6834
+ function applyFooterOrder(order) {
6835
+ const container = getFooterLinksContainer();
6836
+ if (!container) return;
6837
+ const columns = listFooterColumns();
6838
+ if (columns.length === 0) return;
6839
+ const currentOrder = getFooterOrderFromDom();
6840
+ if (ordersEqual(order, currentOrder)) {
6841
+ syncFooterColumnIndices(columns);
6842
+ return;
6843
+ }
6844
+ const used = /* @__PURE__ */ new Set();
6845
+ const orderedCols = [];
6846
+ for (const colOrder of order) {
6847
+ let matched = null;
6848
+ for (const hrefKey of colOrder) {
6849
+ const link = findFooterLinkByKey(hrefKey);
6850
+ if (!link) continue;
6851
+ const owner = findFooterColumnForLink(link);
6852
+ if (owner && !used.has(owner)) {
6853
+ matched = owner;
6854
+ break;
6855
+ }
6856
+ }
6857
+ if (!matched) {
6858
+ matched = columns.find((col) => !used.has(col)) ?? null;
6859
+ }
6860
+ if (matched) {
6861
+ used.add(matched);
6862
+ orderedCols.push(matched);
6863
+ }
6864
+ }
6865
+ for (const col of columns) {
6866
+ if (!used.has(col)) orderedCols.push(col);
6867
+ }
6868
+ for (const col of orderedCols) {
6869
+ appendChildIfNeeded(container, col);
6870
+ }
6871
+ const freshColumns = listFooterColumns();
6872
+ syncFooterColumnIndices(freshColumns);
6873
+ for (let c = 0; c < order.length; c++) {
6874
+ const col = freshColumns[c];
6875
+ if (!col) continue;
6876
+ const colOrder = order[c];
6877
+ for (const hrefKey of colOrder) {
6878
+ const el = findFooterLinkByKey(hrefKey);
6879
+ if (!el) continue;
6880
+ if (el.parentElement !== col || col.lastElementChild !== el) {
6881
+ col.appendChild(el);
6882
+ }
6883
+ }
6636
6884
  }
6637
- const currentPath = window.location.pathname;
6638
- const updated = sections.map((s) => {
6639
- if (s.type !== "scheduling" || s.insertAfter !== insertAfter) return s;
6640
- if (s.pagePath && s.pagePath !== currentPath) return s;
6641
- return { ...s, scheduleId };
6642
- });
6643
- tracker.textContent = JSON.stringify(updated);
6644
- return tracker.textContent ?? "[]";
6645
6885
  }
6646
- function schedulingSectionId(insertAfter) {
6647
- return `scheduling-${insertAfter}`;
6886
+ function ordersEqual(a, b) {
6887
+ if (a.length !== b.length) return false;
6888
+ for (let i = 0; i < a.length; i++) {
6889
+ const left = a[i];
6890
+ const right = b[i];
6891
+ if (left.length !== right.length) return false;
6892
+ for (let j = 0; j < left.length; j++) {
6893
+ if (left[j] !== right[j]) return false;
6894
+ }
6895
+ }
6896
+ return true;
6648
6897
  }
6649
- var INSERT_BEFORE_MARKER = ":before:";
6650
- function parseSchedulingInsertAfter(insertAfter) {
6898
+ function planFooterLinkMove(hrefKey, targetColIndex, insertIndex) {
6899
+ const order = getFooterOrderFromDom();
6900
+ if (targetColIndex < 0 || targetColIndex >= order.length) return null;
6901
+ let fromCol = -1;
6902
+ let fromIdx = -1;
6903
+ for (let c = 0; c < order.length; c++) {
6904
+ const idx = order[c].indexOf(hrefKey);
6905
+ if (idx >= 0) {
6906
+ fromCol = c;
6907
+ fromIdx = idx;
6908
+ break;
6909
+ }
6910
+ }
6911
+ if (fromCol < 0) return null;
6912
+ const next = order.map((col) => [...col]);
6913
+ next[fromCol].splice(fromIdx, 1);
6914
+ let adjusted = insertIndex;
6915
+ if (fromCol === targetColIndex && fromIdx < insertIndex) adjusted -= 1;
6916
+ adjusted = Math.max(0, Math.min(adjusted, next[targetColIndex].length));
6917
+ next[targetColIndex].splice(adjusted, 0, hrefKey);
6918
+ if (ordersEqual(next, order)) return null;
6919
+ return next;
6920
+ }
6921
+ function planFooterColumnMove(fromIndex, toIndex) {
6922
+ const order = getFooterOrderFromDom();
6923
+ const columns = listFooterColumns();
6924
+ if (fromIndex < 0 || fromIndex >= columns.length || toIndex < 0 || toIndex > columns.length) {
6925
+ return null;
6926
+ }
6927
+ if (toIndex === fromIndex || toIndex === fromIndex + 1) return null;
6928
+ const nextOrder = order.map((col) => [...col]);
6929
+ const [movedOrder] = nextOrder.splice(fromIndex, 1);
6930
+ if (!movedOrder) return null;
6931
+ let adjusted = toIndex;
6932
+ if (fromIndex < toIndex) adjusted -= 1;
6933
+ adjusted = Math.max(0, Math.min(adjusted, nextOrder.length));
6934
+ nextOrder.splice(adjusted, 0, movedOrder);
6935
+ if (ordersEqual(nextOrder, order)) return null;
6936
+ return nextOrder;
6937
+ }
6938
+ function parseFooterOrder(content) {
6939
+ const raw = content[FOOTER_ORDER_KEY];
6940
+ if (!raw) return null;
6941
+ try {
6942
+ const parsed = JSON.parse(raw);
6943
+ if (!Array.isArray(parsed)) return null;
6944
+ return parsed.filter((col) => Array.isArray(col)).map((col) => col.filter((key) => typeof key === "string" && key.length > 0));
6945
+ } catch {
6946
+ return null;
6947
+ }
6948
+ }
6949
+ function reconcileFooterOrderFromContent(content) {
6950
+ const order = parseFooterOrder(content);
6951
+ if (!order?.length) return;
6952
+ const current = getFooterOrderFromDom();
6953
+ if (ordersEqual(order, current)) {
6954
+ syncFooterColumnIndices(listFooterColumns());
6955
+ return;
6956
+ }
6957
+ applyFooterOrder(order);
6958
+ }
6959
+ function buildLinkDropSlots(column, columnIndex) {
6960
+ const links = listFooterLinksInColumn(column);
6961
+ const colRect = column.getBoundingClientRect();
6962
+ const slots = [];
6963
+ const barThickness = 3;
6964
+ if (links.length === 0) {
6965
+ slots.push({
6966
+ insertIndex: 0,
6967
+ columnIndex,
6968
+ left: colRect.left,
6969
+ top: colRect.top + colRect.height / 2 - barThickness / 2,
6970
+ width: colRect.width,
6971
+ height: barThickness,
6972
+ direction: "horizontal"
6973
+ });
6974
+ return slots;
6975
+ }
6976
+ for (let i = 0; i <= links.length; i++) {
6977
+ let top;
6978
+ if (i === 0) {
6979
+ top = links[0].getBoundingClientRect().top - barThickness / 2;
6980
+ } else if (i === links.length) {
6981
+ const last = links[links.length - 1].getBoundingClientRect();
6982
+ top = last.bottom - barThickness / 2;
6983
+ } else {
6984
+ const prev = links[i - 1].getBoundingClientRect();
6985
+ const next = links[i].getBoundingClientRect();
6986
+ top = (prev.bottom + next.top) / 2 - barThickness / 2;
6987
+ }
6988
+ const widthRef = i === 0 ? links[0].getBoundingClientRect() : i === links.length ? links[links.length - 1].getBoundingClientRect() : links[i].getBoundingClientRect();
6989
+ slots.push({
6990
+ insertIndex: i,
6991
+ columnIndex,
6992
+ left: widthRef.left,
6993
+ top,
6994
+ width: Math.max(widthRef.width, colRect.width * 0.8),
6995
+ height: barThickness,
6996
+ direction: "horizontal"
6997
+ });
6998
+ }
6999
+ return slots;
7000
+ }
7001
+ function buildColumnDropSlots() {
7002
+ const columns = listFooterColumns();
7003
+ const slots = [];
7004
+ const barThickness = 3;
7005
+ if (columns.length === 0) return slots;
7006
+ for (let i = 0; i <= columns.length; i++) {
7007
+ let left;
7008
+ let height;
7009
+ let top;
7010
+ if (i === 0) {
7011
+ const first = columns[0].getBoundingClientRect();
7012
+ left = first.left - barThickness / 2;
7013
+ top = first.top;
7014
+ height = first.height;
7015
+ } else if (i === columns.length) {
7016
+ const last = columns[columns.length - 1].getBoundingClientRect();
7017
+ left = last.right - barThickness / 2;
7018
+ top = last.top;
7019
+ height = last.height;
7020
+ } else {
7021
+ const prev = columns[i - 1].getBoundingClientRect();
7022
+ const next = columns[i].getBoundingClientRect();
7023
+ left = (prev.right + next.left) / 2 - barThickness / 2;
7024
+ top = Math.min(prev.top, next.top);
7025
+ height = Math.max(prev.bottom, next.bottom) - top;
7026
+ }
7027
+ slots.push({
7028
+ insertIndex: i,
7029
+ columnIndex: i,
7030
+ left,
7031
+ top,
7032
+ width: barThickness,
7033
+ height: Math.max(height, 24),
7034
+ direction: "vertical"
7035
+ });
7036
+ }
7037
+ return slots;
7038
+ }
7039
+ function hitTestLinkDropSlot(clientX, clientY, draggedHrefKey) {
7040
+ const columns = listFooterColumns();
7041
+ let best = null;
7042
+ for (let c = 0; c < columns.length; c++) {
7043
+ const col = columns[c];
7044
+ const colRect = col.getBoundingClientRect();
7045
+ const inColX = clientX >= colRect.left - 24 && clientX <= colRect.right + 24;
7046
+ if (!inColX) continue;
7047
+ const slots = buildLinkDropSlots(col, c).filter((slot) => {
7048
+ const links = listFooterLinksInColumn(col);
7049
+ const fromIdx = links.findIndex(
7050
+ (el) => el.getAttribute("data-ohw-href-key") === draggedHrefKey
7051
+ );
7052
+ if (fromIdx < 0) return true;
7053
+ if (c === findColumnIndexForKey(draggedHrefKey) && (slot.insertIndex === fromIdx || slot.insertIndex === fromIdx + 1)) {
7054
+ return true;
7055
+ }
7056
+ return true;
7057
+ });
7058
+ for (const slot of slots) {
7059
+ const cy = slot.top + slot.height / 2;
7060
+ const dist = Math.abs(clientY - cy) + (inColX ? 0 : 1e3);
7061
+ if (!best || dist < best.dist) best = { slot, dist };
7062
+ }
7063
+ }
7064
+ return best?.slot ?? null;
7065
+ }
7066
+ function findColumnIndexForKey(hrefKey) {
7067
+ const order = getFooterOrderFromDom();
7068
+ for (let c = 0; c < order.length; c++) {
7069
+ if (order[c].includes(hrefKey)) return c;
7070
+ }
7071
+ return -1;
7072
+ }
7073
+ function hitTestColumnDropSlot(clientX, _clientY) {
7074
+ const slots = buildColumnDropSlots();
7075
+ let best = null;
7076
+ for (const slot of slots) {
7077
+ const cx2 = slot.left + slot.width / 2;
7078
+ const dist = Math.abs(clientX - cx2);
7079
+ if (!best || dist < best.dist) best = { slot, dist };
7080
+ }
7081
+ return best?.slot ?? null;
7082
+ }
7083
+
7084
+ // src/lib/item-drag-interaction.ts
7085
+ function disableNativeHrefDrag(el) {
7086
+ if (el.draggable) el.draggable = false;
7087
+ if (el.getAttribute("draggable") !== "false") {
7088
+ el.setAttribute("draggable", "false");
7089
+ }
7090
+ }
7091
+ function clearTextSelection() {
7092
+ const sel = window.getSelection();
7093
+ if (sel && !sel.isCollapsed) sel.removeAllRanges();
7094
+ }
7095
+ function armItemPressDrag() {
7096
+ document.documentElement.setAttribute("data-ohw-footer-press-drag", "");
7097
+ }
7098
+ function lockItemDuringDrag() {
7099
+ document.documentElement.removeAttribute("data-ohw-footer-press-drag");
7100
+ document.documentElement.setAttribute("data-ohw-item-dragging", "");
7101
+ clearTextSelection();
7102
+ }
7103
+ function unlockItemDragInteraction() {
7104
+ document.documentElement.removeAttribute("data-ohw-footer-press-drag");
7105
+ document.documentElement.removeAttribute("data-ohw-item-dragging");
7106
+ clearTextSelection();
7107
+ }
7108
+ var armFooterPressDrag = armItemPressDrag;
7109
+ var lockFooterDuringDrag = lockItemDuringDrag;
7110
+ var unlockFooterDragInteraction = unlockItemDragInteraction;
7111
+
7112
+ // src/lib/nav-dnd.ts
7113
+ function listReorderableNavItems() {
7114
+ return listNavbarItems().filter((el) => {
7115
+ const key = el.getAttribute("data-ohw-href-key");
7116
+ return isNavbarHrefKey(key) && !el.closest("[data-ohw-nav-children]");
7117
+ });
7118
+ }
7119
+ function listNavChildren(parentHrefKey) {
7120
+ const items = listNavbarItems();
7121
+ const parent = items.find((el) => el.getAttribute("data-ohw-href-key") === parentHrefKey);
7122
+ if (!parent) return [];
7123
+ const group = parent.closest("[data-ohw-nav-group]");
7124
+ const childrenRoot = group?.querySelector(":scope > [data-ohw-nav-children]");
7125
+ if (!childrenRoot) return [];
7126
+ return Array.from(childrenRoot.querySelectorAll(":scope > [data-ohw-href-key]")).filter(
7127
+ (el) => isNavbarHrefKey(el.getAttribute("data-ohw-href-key"))
7128
+ );
7129
+ }
7130
+ function activeNavListContainer() {
7131
+ const desktop = getNavbarDesktopContainer();
7132
+ if (desktop && desktop.getClientRects().length > 0) {
7133
+ const style = window.getComputedStyle(desktop);
7134
+ if (style.display !== "none" && style.visibility !== "hidden") return desktop;
7135
+ }
7136
+ return getNavbarDrawerContainer();
7137
+ }
7138
+ function buildRootNavDropSlots() {
7139
+ const items = listReorderableNavItems();
7140
+ const slots = [];
7141
+ const barThickness = 3;
7142
+ if (items.length === 0) {
7143
+ const container = activeNavListContainer();
7144
+ if (!container) return slots;
7145
+ const rect = container.getBoundingClientRect();
7146
+ slots.push({
7147
+ insertIndex: 0,
7148
+ parentId: null,
7149
+ left: rect.left + rect.width / 2 - barThickness / 2,
7150
+ top: rect.top,
7151
+ width: barThickness,
7152
+ height: Math.max(rect.height, 24),
7153
+ direction: "vertical"
7154
+ });
7155
+ return slots;
7156
+ }
7157
+ const edgeGap = (() => {
7158
+ if (items.length < 2) return 32;
7159
+ const a = items[0].getBoundingClientRect();
7160
+ const b = items[1].getBoundingClientRect();
7161
+ return Math.max(0, b.left - a.right);
7162
+ })();
7163
+ for (let i = 0; i <= items.length; i++) {
7164
+ let left;
7165
+ let top;
7166
+ let height;
7167
+ if (i === 0) {
7168
+ const first = items[0].getBoundingClientRect();
7169
+ left = first.left - edgeGap / 2 - barThickness / 2;
7170
+ top = first.top;
7171
+ height = first.height;
7172
+ } else if (i === items.length) {
7173
+ const last = items[items.length - 1].getBoundingClientRect();
7174
+ const lastGap = items.length >= 2 ? Math.max(
7175
+ 0,
7176
+ last.left - items[items.length - 2].getBoundingClientRect().right
7177
+ ) : edgeGap;
7178
+ left = last.right + lastGap / 2 - barThickness / 2;
7179
+ top = last.top;
7180
+ height = last.height;
7181
+ } else {
7182
+ const prev = items[i - 1].getBoundingClientRect();
7183
+ const next = items[i].getBoundingClientRect();
7184
+ left = (prev.right + next.left) / 2 - barThickness / 2;
7185
+ top = Math.min(prev.top, next.top);
7186
+ height = Math.max(prev.bottom, next.bottom) - top;
7187
+ }
7188
+ slots.push({
7189
+ insertIndex: i,
7190
+ parentId: null,
7191
+ left,
7192
+ top,
7193
+ width: barThickness,
7194
+ height: Math.max(height, 24),
7195
+ direction: "vertical"
7196
+ });
7197
+ }
7198
+ return slots;
7199
+ }
7200
+ function buildChildNavDropSlots(parentHrefKey) {
7201
+ const children = listNavChildren(parentHrefKey);
7202
+ const slots = [];
7203
+ const barThickness = 3;
7204
+ if (children.length === 0) return slots;
7205
+ const edgeGap = (() => {
7206
+ if (children.length < 2) return 16;
7207
+ const a = children[0].getBoundingClientRect();
7208
+ const b = children[1].getBoundingClientRect();
7209
+ return Math.max(0, b.top - a.bottom);
7210
+ })();
7211
+ for (let i = 0; i <= children.length; i++) {
7212
+ let top;
7213
+ let width;
7214
+ let left;
7215
+ if (i === 0) {
7216
+ const first = children[0].getBoundingClientRect();
7217
+ top = first.top - edgeGap / 2 - barThickness / 2;
7218
+ left = first.left;
7219
+ width = first.width;
7220
+ } else if (i === children.length) {
7221
+ const last = children[children.length - 1].getBoundingClientRect();
7222
+ const lastGap = children.length >= 2 ? Math.max(
7223
+ 0,
7224
+ last.top - children[children.length - 2].getBoundingClientRect().bottom
7225
+ ) : edgeGap;
7226
+ top = last.bottom + lastGap / 2 - barThickness / 2;
7227
+ left = last.left;
7228
+ width = last.width;
7229
+ } else {
7230
+ const prev = children[i - 1].getBoundingClientRect();
7231
+ const next = children[i].getBoundingClientRect();
7232
+ top = (prev.bottom + next.top) / 2 - barThickness / 2;
7233
+ left = Math.min(prev.left, next.left);
7234
+ width = Math.max(prev.right, next.right) - left;
7235
+ }
7236
+ slots.push({
7237
+ insertIndex: i,
7238
+ parentId: parentHrefKey,
7239
+ left,
7240
+ top,
7241
+ width: Math.max(width, 40),
7242
+ height: barThickness,
7243
+ direction: "horizontal"
7244
+ });
7245
+ }
7246
+ return slots;
7247
+ }
7248
+ function buildAllNavDropSlots(draggedHrefKey) {
7249
+ const slots = [...buildRootNavDropSlots()];
7250
+ for (const item of listReorderableNavItems()) {
7251
+ const key = item.getAttribute("data-ohw-href-key");
7252
+ if (!key || key === draggedHrefKey) continue;
7253
+ const group = item.closest("[data-ohw-nav-group]");
7254
+ if (!group?.querySelector(":scope > [data-ohw-nav-children]")) continue;
7255
+ slots.push(...buildChildNavDropSlots(key));
7256
+ }
7257
+ const dragged = listNavbarItems().find((el) => el.getAttribute("data-ohw-href-key") === draggedHrefKey);
7258
+ const parentGroup = dragged?.closest("[data-ohw-nav-children]")?.closest("[data-ohw-nav-group]");
7259
+ const parentTrigger = parentGroup?.querySelector(":scope > [data-ohw-href-key]");
7260
+ const parentKey = parentTrigger?.getAttribute("data-ohw-href-key");
7261
+ if (parentKey && isNavbarHrefKey(parentKey)) {
7262
+ const existing = slots.some((s) => s.parentId === parentKey);
7263
+ if (!existing) slots.push(...buildChildNavDropSlots(parentKey));
7264
+ }
7265
+ return slots;
7266
+ }
7267
+ function hitTestNavDropSlot(clientX, clientY, draggedHrefKey) {
7268
+ const slots = buildAllNavDropSlots(draggedHrefKey);
7269
+ let best = null;
7270
+ for (const slot of slots) {
7271
+ const cx2 = slot.left + slot.width / 2;
7272
+ const cy = slot.top + slot.height / 2;
7273
+ const dist = slot.direction === "vertical" ? Math.abs(clientX - cx2) + Math.abs(clientY - cy) * 0.25 : Math.abs(clientY - cy) + Math.abs(clientX - cx2) * 0.25;
7274
+ if (!best || dist < best.dist) best = { slot, dist };
7275
+ }
7276
+ return best?.slot ?? null;
7277
+ }
7278
+
7279
+ // src/useNavItemDrag.ts
7280
+ import { useCallback as useCallback3, useEffect as useEffect7, useRef as useRef6, useState as useState6 } from "react";
7281
+ function useNavItemDrag({
7282
+ isEditMode,
7283
+ editContentRef,
7284
+ selectedElRef,
7285
+ activeElRef,
7286
+ footerDragRef,
7287
+ suppressNextClickRef,
7288
+ suppressClickUntilRef,
7289
+ siblingHintElRef,
7290
+ postToParentRef,
7291
+ deselectRef,
7292
+ selectRef,
7293
+ deactivateRef,
7294
+ setLinkPopover,
7295
+ linkPopoverOpenRef,
7296
+ setIsItemDragging,
7297
+ setDraggedItemRect,
7298
+ setSiblingHintRect,
7299
+ setSiblingHintRects,
7300
+ setToolbarRect,
7301
+ getNavigationItemAnchor: getNavigationItemAnchor2,
7302
+ isDragHandleDisabled: isDragHandleDisabled2,
7303
+ isNavbarButton: isNavbarButton3
7304
+ }) {
7305
+ const navDragRef = useRef6(null);
7306
+ const [navDropSlots, setNavDropSlots] = useState6([]);
7307
+ const [activeNavDropIndex, setActiveNavDropIndex] = useState6(null);
7308
+ const navPointerDragRef = useRef6(null);
7309
+ const clearNavDragVisuals = useCallback3(() => {
7310
+ navDragRef.current = null;
7311
+ setNavDropSlots([]);
7312
+ setActiveNavDropIndex(null);
7313
+ setDraggedItemRect(null);
7314
+ setSiblingHintRects([]);
7315
+ setIsItemDragging(false);
7316
+ unlockItemDragInteraction();
7317
+ }, [setDraggedItemRect, setIsItemDragging, setSiblingHintRects]);
7318
+ const refreshNavDragVisuals = useCallback3(
7319
+ (session, activeSlot, clientX, clientY) => {
7320
+ setDraggedItemRect(session.draggedEl.getBoundingClientRect());
7321
+ if (typeof clientX === "number" && typeof clientY === "number") {
7322
+ session.lastClientX = clientX;
7323
+ session.lastClientY = clientY;
7324
+ }
7325
+ session.activeSlot = activeSlot;
7326
+ setSiblingHintRects([]);
7327
+ const slots = buildAllNavDropSlots(session.hrefKey);
7328
+ setNavDropSlots(slots);
7329
+ const activeIdx = activeSlot ? slots.findIndex(
7330
+ (s) => s.parentId === activeSlot.parentId && s.insertIndex === activeSlot.insertIndex
7331
+ ) : -1;
7332
+ setActiveNavDropIndex(activeIdx >= 0 ? activeIdx : null);
7333
+ },
7334
+ [setDraggedItemRect, setSiblingHintRects]
7335
+ );
7336
+ const refreshNavDragVisualsRef = useRef6(refreshNavDragVisuals);
7337
+ refreshNavDragVisualsRef.current = refreshNavDragVisuals;
7338
+ const commitNavDragRef = useRef6(() => {
7339
+ });
7340
+ const beginNavDragRef = useRef6(() => {
7341
+ });
7342
+ const beginNavDrag = useCallback3(
7343
+ (session) => {
7344
+ const rect = session.draggedEl.getBoundingClientRect();
7345
+ session.lastClientX = session.lastClientX || rect.left + rect.width / 2;
7346
+ session.lastClientY = session.lastClientY || rect.top + rect.height / 2;
7347
+ session.activeSlot = session.activeSlot ?? null;
7348
+ navDragRef.current = session;
7349
+ setIsItemDragging(true);
7350
+ lockItemDuringDrag();
7351
+ siblingHintElRef.current = null;
7352
+ setSiblingHintRect(null);
7353
+ if (session.wasSelected && selectedElRef.current === session.draggedEl) {
7354
+ setToolbarRect(rect);
7355
+ }
7356
+ const initialSlot = hitTestNavDropSlot(session.lastClientX, session.lastClientY, session.hrefKey);
7357
+ refreshNavDragVisuals(session, initialSlot, session.lastClientX, session.lastClientY);
7358
+ },
7359
+ [
7360
+ refreshNavDragVisuals,
7361
+ selectedElRef,
7362
+ setIsItemDragging,
7363
+ setSiblingHintRect,
7364
+ setToolbarRect,
7365
+ siblingHintElRef
7366
+ ]
7367
+ );
7368
+ beginNavDragRef.current = beginNavDrag;
7369
+ const commitNavDrag = useCallback3(
7370
+ (clientX, clientY) => {
7371
+ const session = navDragRef.current;
7372
+ if (!session) {
7373
+ clearNavDragVisuals();
7374
+ return;
7375
+ }
7376
+ const x = typeof clientX === "number" && (clientX !== 0 || clientY !== 0) ? clientX : session.lastClientX;
7377
+ const y = typeof clientY === "number" && (clientX !== 0 || clientY !== 0) ? clientY : session.lastClientY;
7378
+ const slot = session.activeSlot ?? hitTestNavDropSlot(x, y, session.hrefKey);
7379
+ const planned = slot != null ? planNavItemMove(session.hrefKey, slot.parentId, slot.insertIndex) : null;
7380
+ const wasSelected = session.wasSelected;
7381
+ const hrefKey = session.hrefKey;
7382
+ const applySelectionAfterDrop = () => {
7383
+ if (!wasSelected) {
7384
+ deselectRef.current();
7385
+ return;
7386
+ }
7387
+ const desktop = document.querySelector("[data-ohw-nav-container]");
7388
+ const drawer = document.querySelector("[data-ohw-nav-drawer]");
7389
+ const link = desktop?.querySelector(`[data-ohw-href-key="${CSS.escape(hrefKey)}"]`) ?? drawer?.querySelector(`[data-ohw-href-key="${CSS.escape(hrefKey)}"]`) ?? document.querySelector(`[data-ohw-href-key="${CSS.escape(hrefKey)}"]`);
7390
+ if (link) {
7391
+ selectRef.current(link);
7392
+ return;
7393
+ }
7394
+ deselectRef.current();
7395
+ };
7396
+ if (planned) {
7397
+ editContentRef.current = {
7398
+ ...editContentRef.current,
7399
+ [NAV_ORDER_KEY]: planned.orderJson
7400
+ };
7401
+ applyNavForest(planned.forest);
7402
+ document.querySelectorAll(
7403
+ "nav [data-ohw-href-key], [data-ohw-nav-container] [data-ohw-href-key], [data-ohw-nav-drawer] [data-ohw-href-key]"
7404
+ ).forEach((el) => {
7405
+ if (isNavbarHrefKey(el.getAttribute("data-ohw-href-key"))) {
7406
+ disableNativeHrefDrag(el);
7407
+ }
7408
+ });
7409
+ postToParentRef.current({
7410
+ type: "ow:change",
7411
+ nodes: [{ key: NAV_ORDER_KEY, text: planned.orderJson }]
7412
+ });
7413
+ applySelectionAfterDrop();
7414
+ clearNavDragVisuals();
7415
+ requestAnimationFrame(() => {
7416
+ if (editContentRef.current[NAV_ORDER_KEY] === planned.orderJson) {
7417
+ applyNavForest(planned.forest);
7418
+ }
7419
+ applySelectionAfterDrop();
7420
+ requestAnimationFrame(applySelectionAfterDrop);
7421
+ });
7422
+ return;
7423
+ }
7424
+ applySelectionAfterDrop();
7425
+ clearNavDragVisuals();
7426
+ },
7427
+ [clearNavDragVisuals, deselectRef, editContentRef, postToParentRef, selectRef]
7428
+ );
7429
+ commitNavDragRef.current = commitNavDrag;
7430
+ const startNavLinkDrag = useCallback3(
7431
+ (anchor, clientX, clientY, wasSelected) => {
7432
+ if (footerDragRef.current) return false;
7433
+ const hrefKey = anchor.getAttribute("data-ohw-href-key");
7434
+ if (!hrefKey || !isNavbarHrefKey(hrefKey)) return false;
7435
+ if (isNavbarButton3(anchor) || isDragHandleDisabled2(anchor)) return false;
7436
+ beginNavDrag({
7437
+ hrefKey,
7438
+ wasSelected,
7439
+ draggedEl: anchor,
7440
+ lastClientX: clientX,
7441
+ lastClientY: clientY,
7442
+ activeSlot: null
7443
+ });
7444
+ return true;
7445
+ },
7446
+ [beginNavDrag, footerDragRef, isDragHandleDisabled2, isNavbarButton3]
7447
+ );
7448
+ const onNavDragOver = useCallback3(
7449
+ (e) => {
7450
+ const session = navDragRef.current;
7451
+ if (!session) return false;
7452
+ e.preventDefault();
7453
+ if (e.dataTransfer) e.dataTransfer.dropEffect = "move";
7454
+ const slot = hitTestNavDropSlot(e.clientX, e.clientY, session.hrefKey);
7455
+ refreshNavDragVisualsRef.current(session, slot, e.clientX, e.clientY);
7456
+ return true;
7457
+ },
7458
+ []
7459
+ );
7460
+ useEffect7(() => {
7461
+ if (!isEditMode) return;
7462
+ const THRESHOLD = 10;
7463
+ const resolveWasSelected = (el) => {
7464
+ if (selectedElRef.current === el) return true;
7465
+ const activeAnchor = activeElRef.current ? getNavigationItemAnchor2(activeElRef.current) : null;
7466
+ return activeAnchor === el;
7467
+ };
7468
+ const onPointerDown = (e) => {
7469
+ if (e.button !== 0) return;
7470
+ if (navDragRef.current || footerDragRef.current) return;
7471
+ const target = e.target;
7472
+ if (!target) return;
7473
+ if (target.closest(
7474
+ '[data-ohw-drag-handle-container], [data-slot="drag-handle"], [data-ohw-toolbar], [data-ohw-item-toolbar-anchor], [data-ohw-link-popover-root]'
7475
+ )) {
7476
+ return;
7477
+ }
7478
+ if (target.closest("[data-ohw-item-drag-surface]")) return;
7479
+ const anchor = getNavigationItemAnchor2(target);
7480
+ const hrefKey = anchor?.getAttribute("data-ohw-href-key") ?? null;
7481
+ if (!anchor || !isNavbarHrefKey(hrefKey)) return;
7482
+ if (isNavbarButton3(anchor) || isDragHandleDisabled2(anchor)) return;
7483
+ armItemPressDrag();
7484
+ navPointerDragRef.current = {
7485
+ el: anchor,
7486
+ startX: e.clientX,
7487
+ startY: e.clientY,
7488
+ pointerId: e.pointerId,
7489
+ wasSelected: resolveWasSelected(anchor),
7490
+ started: false
7491
+ };
7492
+ };
7493
+ const onPointerMove = (e) => {
7494
+ const pending = navPointerDragRef.current;
7495
+ if (!pending) return;
7496
+ if (pending.started) {
7497
+ e.preventDefault();
7498
+ clearTextSelection();
7499
+ const session = navDragRef.current;
7500
+ if (!session) return;
7501
+ const slot = hitTestNavDropSlot(e.clientX, e.clientY, session.hrefKey);
7502
+ refreshNavDragVisualsRef.current(session, slot, e.clientX, e.clientY);
7503
+ return;
7504
+ }
7505
+ const dx = e.clientX - pending.startX;
7506
+ const dy = e.clientY - pending.startY;
7507
+ if (dx * dx + dy * dy < THRESHOLD * THRESHOLD) return;
7508
+ e.preventDefault();
7509
+ pending.started = true;
7510
+ clearTextSelection();
7511
+ try {
7512
+ document.body.setPointerCapture(pending.pointerId);
7513
+ } catch {
7514
+ }
7515
+ if (linkPopoverOpenRef.current) setLinkPopover(null);
7516
+ if (activeElRef.current) deactivateRef.current();
7517
+ const key = pending.el.getAttribute("data-ohw-href-key");
7518
+ if (!key) return;
7519
+ beginNavDragRef.current({
7520
+ hrefKey: key,
7521
+ wasSelected: pending.wasSelected,
7522
+ draggedEl: pending.el,
7523
+ lastClientX: e.clientX,
7524
+ lastClientY: e.clientY,
7525
+ activeSlot: null
7526
+ });
7527
+ };
7528
+ const endPointerDrag = (e) => {
7529
+ const pending = navPointerDragRef.current;
7530
+ navPointerDragRef.current = null;
7531
+ try {
7532
+ if (document.body.hasPointerCapture(e.pointerId)) {
7533
+ document.body.releasePointerCapture(e.pointerId);
7534
+ }
7535
+ } catch {
7536
+ }
7537
+ if (!pending?.started) {
7538
+ unlockItemDragInteraction();
7539
+ return;
7540
+ }
7541
+ suppressNextClickRef.current = true;
7542
+ suppressClickUntilRef.current = Date.now() + 500;
7543
+ commitNavDragRef.current(e.clientX, e.clientY);
7544
+ };
7545
+ const blockSelectStart = (e) => {
7546
+ if (navDragRef.current || navPointerDragRef.current?.started || document.documentElement.hasAttribute("data-ohw-footer-press-drag")) {
7547
+ e.preventDefault();
7548
+ }
7549
+ };
7550
+ const onDragEnd = () => {
7551
+ if (!navDragRef.current) return;
7552
+ suppressNextClickRef.current = true;
7553
+ suppressClickUntilRef.current = Date.now() + 500;
7554
+ commitNavDragRef.current();
7555
+ };
7556
+ document.addEventListener("pointerdown", onPointerDown, true);
7557
+ document.addEventListener("pointermove", onPointerMove, true);
7558
+ document.addEventListener("pointerup", endPointerDrag, true);
7559
+ document.addEventListener("pointercancel", endPointerDrag, true);
7560
+ document.addEventListener("selectstart", blockSelectStart, true);
7561
+ document.addEventListener("dragend", onDragEnd, true);
7562
+ return () => {
7563
+ document.removeEventListener("pointerdown", onPointerDown, true);
7564
+ document.removeEventListener("pointermove", onPointerMove, true);
7565
+ document.removeEventListener("pointerup", endPointerDrag, true);
7566
+ document.removeEventListener("pointercancel", endPointerDrag, true);
7567
+ document.removeEventListener("selectstart", blockSelectStart, true);
7568
+ document.removeEventListener("dragend", onDragEnd, true);
7569
+ unlockItemDragInteraction();
7570
+ };
7571
+ }, [
7572
+ activeElRef,
7573
+ deactivateRef,
7574
+ footerDragRef,
7575
+ getNavigationItemAnchor2,
7576
+ isDragHandleDisabled2,
7577
+ isEditMode,
7578
+ isNavbarButton3,
7579
+ linkPopoverOpenRef,
7580
+ selectedElRef,
7581
+ setLinkPopover,
7582
+ suppressNextClickRef
7583
+ ]);
7584
+ const armNavPressFromChrome = useCallback3(
7585
+ (selected, clientX, clientY, pointerId) => {
7586
+ const hrefKey = selected.getAttribute("data-ohw-href-key");
7587
+ if (!hrefKey || !isNavbarHrefKey(hrefKey)) return false;
7588
+ if (isNavbarButton3(selected) || isDragHandleDisabled2(selected)) return false;
7589
+ armItemPressDrag();
7590
+ navPointerDragRef.current = {
7591
+ el: selected,
7592
+ startX: clientX,
7593
+ startY: clientY,
7594
+ pointerId,
7595
+ wasSelected: true,
7596
+ started: false
7597
+ };
7598
+ return true;
7599
+ },
7600
+ [isDragHandleDisabled2, isNavbarButton3]
7601
+ );
7602
+ return {
7603
+ navDragRef,
7604
+ navDropSlots,
7605
+ activeNavDropIndex,
7606
+ startNavLinkDrag,
7607
+ commitNavDrag,
7608
+ onNavDragOver,
7609
+ armNavPressFromChrome,
7610
+ clearNavDragVisuals,
7611
+ refreshNavDragVisualsRef
7612
+ };
7613
+ }
7614
+
7615
+ // src/ui/navbar-container-chrome.tsx
7616
+ import { Plus as Plus2 } from "lucide-react";
7617
+ import { jsx as jsx21 } from "react/jsx-runtime";
7618
+ function NavbarContainerChrome({
7619
+ rect,
7620
+ onAdd
7621
+ }) {
7622
+ const chromeGap = 6;
7623
+ return /* @__PURE__ */ jsx21(
7624
+ "div",
7625
+ {
7626
+ "data-ohw-navbar-container-chrome": "",
7627
+ "data-ohw-bridge": "",
7628
+ className: "pointer-events-none fixed z-[2147483647]",
7629
+ style: {
7630
+ top: rect.top - chromeGap,
7631
+ left: rect.left - chromeGap,
7632
+ width: rect.width + chromeGap * 2,
7633
+ height: rect.height + chromeGap * 2
7634
+ },
7635
+ children: /* @__PURE__ */ jsx21(
7636
+ "button",
7637
+ {
7638
+ type: "button",
7639
+ "data-ohw-navbar-add-button": "",
7640
+ className: "pointer-events-auto absolute left-1/2 flex p-0.5 rounded-[10px] size-7 -translate-x-1/2 translate-y-1/2 items-center justify-center border border-border bg-background shadow-sm transition-colors hover:bg-muted/80",
7641
+ style: { top: "70%" },
7642
+ "aria-label": "Add item",
7643
+ onMouseDown: (e) => {
7644
+ e.preventDefault();
7645
+ e.stopPropagation();
7646
+ },
7647
+ onClick: (e) => {
7648
+ e.preventDefault();
7649
+ e.stopPropagation();
7650
+ onAdd();
7651
+ },
7652
+ children: /* @__PURE__ */ jsx21(Plus2, { className: "size-4 shrink-0 text-foreground", "aria-hidden": true })
7653
+ }
7654
+ )
7655
+ }
7656
+ );
7657
+ }
7658
+
7659
+ // src/ui/drop-indicator.tsx
7660
+ import * as React10 from "react";
7661
+ import { jsx as jsx22 } from "react/jsx-runtime";
7662
+ var dropIndicatorVariants = cva(
7663
+ "ov-gap-line pointer-events-none shrink-0 transition-opacity duration-150",
7664
+ {
7665
+ variants: {
7666
+ direction: {
7667
+ vertical: "h-6 w-[3px]",
7668
+ horizontal: "h-[3px] w-[200px]"
7669
+ },
7670
+ state: {
7671
+ default: "opacity-0",
7672
+ hover: "opacity-100",
7673
+ dragIdle: "opacity-40",
7674
+ dragActive: "opacity-100"
7675
+ }
7676
+ },
7677
+ defaultVariants: {
7678
+ direction: "vertical",
7679
+ state: "default"
7680
+ }
7681
+ }
7682
+ );
7683
+ var DropIndicator = React10.forwardRef(
7684
+ ({ className, direction, state, ...props }, ref) => {
7685
+ return /* @__PURE__ */ jsx22(
7686
+ "div",
7687
+ {
7688
+ ref,
7689
+ "data-slot": "drop-indicator",
7690
+ "data-direction": direction ?? "vertical",
7691
+ "data-state": state ?? "default",
7692
+ "aria-hidden": "true",
7693
+ className: cn(dropIndicatorVariants({ direction, state }), className),
7694
+ ...props
7695
+ }
7696
+ );
7697
+ }
7698
+ );
7699
+ DropIndicator.displayName = "DropIndicator";
7700
+
7701
+ // src/ui/badge.tsx
7702
+ import { jsx as jsx23 } from "react/jsx-runtime";
7703
+ var badgeVariants = cva(
7704
+ "inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
7705
+ {
7706
+ variants: {
7707
+ variant: {
7708
+ default: "border-transparent bg-primary text-primary-foreground",
7709
+ secondary: "border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80",
7710
+ destructive: "border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80",
7711
+ outline: "text-foreground"
7712
+ }
7713
+ },
7714
+ defaultVariants: {
7715
+ variant: "default"
7716
+ }
7717
+ }
7718
+ );
7719
+ function Badge({ className, variant, ...props }) {
7720
+ return /* @__PURE__ */ jsx23("div", { className: cn(badgeVariants({ variant }), className), ...props });
7721
+ }
7722
+
7723
+ // src/OhhwellsBridge.tsx
7724
+ import { Link as Link2 } from "lucide-react";
7725
+ import { Fragment as Fragment5, jsx as jsx24, jsxs as jsxs13 } from "react/jsx-runtime";
7726
+ var PRIMARY2 = "#0885FE";
7727
+ var IMAGE_FADE_MS = 300;
7728
+ function runOpacityFade(el, onDone) {
7729
+ const anim = el.animate([{ opacity: 0 }, { opacity: 1 }], {
7730
+ duration: IMAGE_FADE_MS,
7731
+ easing: "ease",
7732
+ fill: "forwards"
7733
+ });
7734
+ let finished = false;
7735
+ const finish = () => {
7736
+ if (finished) return;
7737
+ finished = true;
7738
+ anim.cancel();
7739
+ el.style.opacity = "";
7740
+ onDone();
7741
+ };
7742
+ anim.finished.then(finish).catch(finish);
7743
+ window.setTimeout(finish, IMAGE_FADE_MS + 100);
7744
+ }
7745
+ function fadeInImageElement(img, onReady) {
7746
+ onReady();
7747
+ img.style.opacity = "0";
7748
+ runOpacityFade(img, () => {
7749
+ img.style.opacity = "";
7750
+ });
7751
+ }
7752
+ function applyEditableImageSrc(img, url) {
7753
+ img.removeAttribute("srcset");
7754
+ img.removeAttribute("sizes");
7755
+ img.src = url;
7756
+ }
7757
+ function fadeInBgImage(el, url, onReady) {
7758
+ const prevPos = el.style.position;
7759
+ if (!prevPos || prevPos === "static") el.style.position = "relative";
7760
+ const layer = document.createElement("div");
7761
+ layer.setAttribute("data-ohw-bg-fade-layer", "");
7762
+ Object.assign(layer.style, {
7763
+ position: "absolute",
7764
+ inset: "0",
7765
+ zIndex: "0",
7766
+ backgroundImage: `url('${url}')`,
7767
+ backgroundSize: "cover",
7768
+ backgroundPosition: "center",
7769
+ backgroundRepeat: "no-repeat",
7770
+ opacity: "0",
7771
+ pointerEvents: "none"
7772
+ });
7773
+ el.prepend(layer);
7774
+ onReady();
7775
+ runOpacityFade(layer, () => {
7776
+ el.style.backgroundImage = `url('${url}')`;
7777
+ layer.remove();
7778
+ if (!prevPos || prevPos === "static") el.style.position = prevPos;
7779
+ });
7780
+ }
7781
+ function getSectionsTracker() {
7782
+ let el = document.querySelector("[data-ohw-sections-tracker]");
7783
+ if (!el) {
7784
+ el = document.createElement("div");
7785
+ el.setAttribute("data-ohw-sections-tracker", "");
7786
+ el.style.display = "none";
7787
+ document.body.appendChild(el);
7788
+ }
7789
+ return el;
7790
+ }
7791
+ function updateSectionScheduleId(insertAfter, scheduleId) {
7792
+ const tracker = getSectionsTracker();
7793
+ let sections = [];
7794
+ try {
7795
+ sections = JSON.parse(tracker.textContent || "[]");
7796
+ } catch {
7797
+ }
7798
+ const currentPath = window.location.pathname;
7799
+ const updated = sections.map((s) => {
7800
+ if (s.type !== "scheduling" || s.insertAfter !== insertAfter) return s;
7801
+ if (s.pagePath && s.pagePath !== currentPath) return s;
7802
+ return { ...s, scheduleId };
7803
+ });
7804
+ tracker.textContent = JSON.stringify(updated);
7805
+ return tracker.textContent ?? "[]";
7806
+ }
7807
+ function schedulingSectionId(insertAfter) {
7808
+ return `scheduling-${insertAfter}`;
7809
+ }
7810
+ var INSERT_BEFORE_MARKER = ":before:";
7811
+ function parseSchedulingInsertAfter(insertAfter) {
6651
7812
  const idx = insertAfter.indexOf(INSERT_BEFORE_MARKER);
6652
7813
  if (idx < 0) return { anchor: insertAfter, insertBefore: null };
6653
7814
  return {
@@ -6740,7 +7901,7 @@ function mountSchedulingWidget(insertAfter, notifyOnConnect = false, scheduleId,
6740
7901
  const root = createRoot(container);
6741
7902
  flushSync(() => {
6742
7903
  root.render(
6743
- /* @__PURE__ */ jsx23(
7904
+ /* @__PURE__ */ jsx24(
6744
7905
  SchedulingWidget,
6745
7906
  {
6746
7907
  notifyOnConnect,
@@ -6815,7 +7976,7 @@ function syncNavigationDragCursorAttrs() {
6815
7976
  el.setAttribute("data-ohw-can-drag", "");
6816
7977
  });
6817
7978
  }
6818
- function collectEditableNodes() {
7979
+ function collectEditableNodes(extraContent) {
6819
7980
  const nodes = Array.from(document.querySelectorAll("[data-ohw-editable]")).map((el) => {
6820
7981
  if (el.dataset.ohwEditable === "image") {
6821
7982
  const img = el instanceof HTMLImageElement ? el : el.querySelector("img");
@@ -6843,6 +8004,14 @@ function collectEditableNodes() {
6843
8004
  if (!key) return;
6844
8005
  nodes.push({ key, type: "link", text: getLinkHref2(el) });
6845
8006
  });
8007
+ if (extraContent) {
8008
+ for (const key of [NAV_ORDER_KEY, FOOTER_ORDER_KEY, NAV_COUNT_KEY]) {
8009
+ const text = extraContent[key];
8010
+ if (typeof text === "string" && text.length > 0) {
8011
+ nodes.push({ key, type: "meta", text });
8012
+ }
8013
+ }
8014
+ }
6846
8015
  document.querySelectorAll('[data-ohw-editable="video"]').forEach((el) => {
6847
8016
  const key = el.dataset.ohwKey ?? "";
6848
8017
  const video = getVideoEl(el);
@@ -6956,6 +8125,11 @@ function getNavigationItemAnchor(el) {
6956
8125
  function isNavigationItem(el) {
6957
8126
  return getNavigationItemAnchor(el) !== null;
6958
8127
  }
8128
+ function listNavigationItems() {
8129
+ return Array.from(
8130
+ document.querySelectorAll("nav [data-ohw-href-key], footer [data-ohw-href-key]")
8131
+ ).filter((el) => isNavigationItem(el));
8132
+ }
6959
8133
  function getNavigationItemReorderState(anchor) {
6960
8134
  if (isNavbarButton2(anchor)) return { key: null, disabled: false };
6961
8135
  return getReorderHandleStateFromAnchor(anchor);
@@ -6988,7 +8162,7 @@ function countFooterNavItems(el) {
6988
8162
  return Array.from(el.querySelectorAll("[data-ohw-href-key]")).filter(isNavigationItem).length;
6989
8163
  }
6990
8164
  function findFooterItemGroup(item) {
6991
- const explicit = item.closest("[data-ohw-footer-column]");
8165
+ const explicit = item.closest("[data-ohw-footer-col], [data-ohw-footer-column]");
6992
8166
  if (explicit) return explicit;
6993
8167
  const footer = item.closest("footer");
6994
8168
  if (!footer) return null;
@@ -7005,17 +8179,17 @@ function isNavigationRoot(el) {
7005
8179
  function isInferredFooterGroup(el) {
7006
8180
  const footer = el.closest("footer");
7007
8181
  if (!footer || el === footer) return false;
7008
- if (el.hasAttribute("data-ohw-footer-column")) return true;
8182
+ if (el.hasAttribute("data-ohw-footer-col") || el.hasAttribute("data-ohw-footer-column")) return true;
7009
8183
  return countFooterNavItems(el) >= 2;
7010
8184
  }
7011
8185
  function isNavigationContainer(el) {
7012
- return el.hasAttribute("data-ohw-nav-container") || el.hasAttribute("data-ohw-footer-column") || isNavigationRoot(el) || isInferredFooterGroup(el);
8186
+ return el.hasAttribute("data-ohw-nav-container") || el.hasAttribute("data-ohw-footer-col") || el.hasAttribute("data-ohw-footer-column") || isNavigationRoot(el) || isInferredFooterGroup(el);
7013
8187
  }
7014
8188
  function isNavbarLinksContainer(el) {
7015
8189
  return el.hasAttribute("data-ohw-nav-container");
7016
8190
  }
7017
8191
  function getFooterColumn(el) {
7018
- return el.closest("[data-ohw-footer-column]");
8192
+ return el.closest("[data-ohw-footer-col], [data-ohw-footer-column]");
7019
8193
  }
7020
8194
  function resolveFooterColumnSelectionTarget(target, clientX, clientY) {
7021
8195
  if (getNavigationItemAnchor(target)) return null;
@@ -7039,7 +8213,7 @@ function isPointOverNavigation(x, y) {
7039
8213
  function findHoveredNavOrFooterContainer(x, y) {
7040
8214
  const candidates = [
7041
8215
  ...document.querySelectorAll("[data-ohw-nav-container]"),
7042
- ...document.querySelectorAll("[data-ohw-footer-column]")
8216
+ ...listFooterColumns()
7043
8217
  ];
7044
8218
  for (const container of candidates) {
7045
8219
  const r2 = container.getBoundingClientRect();
@@ -7086,32 +8260,57 @@ function getNavigationSelectionParent(el) {
7086
8260
  if (footerGroup) return footerGroup;
7087
8261
  return getNavigationRoot(el);
7088
8262
  }
7089
- if (el.hasAttribute("data-ohw-nav-container") || el.hasAttribute("data-ohw-footer-column") || isInferredFooterGroup(el)) {
8263
+ if (el.hasAttribute("data-ohw-nav-container") || el.hasAttribute("data-ohw-footer-col") || el.hasAttribute("data-ohw-footer-column") || isInferredFooterGroup(el)) {
7090
8264
  return getNavigationRoot(el);
7091
8265
  }
7092
8266
  return null;
7093
8267
  }
8268
+ function collectNavigationItemSiblingHintRects(selected) {
8269
+ if (!isNavigationItem(selected)) return [];
8270
+ const footerColumn = getFooterColumn(selected);
8271
+ if (footerColumn) {
8272
+ return listFooterLinksInColumn(footerColumn).filter((link) => link !== selected).map((link) => link.getBoundingClientRect());
8273
+ }
8274
+ const navContainer = selected.closest("[data-ohw-nav-container], [data-ohw-nav-drawer]");
8275
+ if (navContainer) {
8276
+ return Array.from(navContainer.querySelectorAll("[data-ohw-href-key]")).filter((el) => isNavigationItem(el) && el !== selected).map((el) => el.getBoundingClientRect());
8277
+ }
8278
+ return listNavigationItems().filter((el) => el !== selected).map((el) => el.getBoundingClientRect());
8279
+ }
7094
8280
  function placeCaretAtPoint(el, x, y) {
7095
8281
  const selection = window.getSelection();
7096
8282
  if (!selection) return;
7097
- if (typeof document.caretRangeFromPoint === "function") {
7098
- const range = document.caretRangeFromPoint(x, y);
7099
- if (range && el.contains(range.startContainer)) {
7100
- selection.removeAllRanges();
7101
- selection.addRange(range);
7102
- return;
8283
+ const overlays = Array.from(
8284
+ document.querySelectorAll(
8285
+ "[data-ohw-item-drag-surface], [data-ohw-item-interaction], [data-ohw-drag-handle-container], [data-ohw-item-toolbar-anchor]"
8286
+ )
8287
+ );
8288
+ const prevPointerEvents = overlays.map((node) => node.style.pointerEvents);
8289
+ for (const node of overlays) node.style.pointerEvents = "none";
8290
+ try {
8291
+ if (typeof document.caretRangeFromPoint === "function") {
8292
+ const range = document.caretRangeFromPoint(x, y);
8293
+ if (range && el.contains(range.startContainer)) {
8294
+ selection.removeAllRanges();
8295
+ selection.addRange(range);
8296
+ return;
8297
+ }
7103
8298
  }
7104
- }
7105
- const caretPositionFromPoint = document.caretPositionFromPoint;
7106
- if (typeof caretPositionFromPoint === "function") {
7107
- const position = caretPositionFromPoint(x, y);
7108
- if (position && el.contains(position.offsetNode)) {
7109
- const range = document.createRange();
7110
- range.setStart(position.offsetNode, position.offset);
7111
- range.collapse(true);
7112
- selection.removeAllRanges();
7113
- selection.addRange(range);
8299
+ const caretPositionFromPoint = document.caretPositionFromPoint;
8300
+ if (typeof caretPositionFromPoint === "function") {
8301
+ const position = caretPositionFromPoint(x, y);
8302
+ if (position && el.contains(position.offsetNode)) {
8303
+ const range = document.createRange();
8304
+ range.setStart(position.offsetNode, position.offset);
8305
+ range.collapse(true);
8306
+ selection.removeAllRanges();
8307
+ selection.addRange(range);
8308
+ }
7114
8309
  }
8310
+ } finally {
8311
+ overlays.forEach((node, i) => {
8312
+ node.style.pointerEvents = prevPointerEvents[i] ?? "";
8313
+ });
7115
8314
  }
7116
8315
  }
7117
8316
  function selectAllTextInEditable(el) {
@@ -7277,10 +8476,11 @@ function EditGlowChrome({
7277
8476
  rect,
7278
8477
  elRef,
7279
8478
  reorderHrefKey,
7280
- dragDisabled = false
8479
+ dragDisabled = false,
8480
+ hideHandle = false
7281
8481
  }) {
7282
8482
  const GAP = SELECTION_CHROME_GAP2;
7283
- return /* @__PURE__ */ jsxs14(
8483
+ return /* @__PURE__ */ jsxs13(
7284
8484
  "div",
7285
8485
  {
7286
8486
  ref: elRef,
@@ -7295,7 +8495,7 @@ function EditGlowChrome({
7295
8495
  zIndex: 2147483646
7296
8496
  },
7297
8497
  children: [
7298
- /* @__PURE__ */ jsx23(
8498
+ /* @__PURE__ */ jsx24(
7299
8499
  "div",
7300
8500
  {
7301
8501
  style: {
@@ -7308,7 +8508,7 @@ function EditGlowChrome({
7308
8508
  }
7309
8509
  }
7310
8510
  ),
7311
- reorderHrefKey && /* @__PURE__ */ jsx23(
8511
+ reorderHrefKey && !hideHandle && /* @__PURE__ */ jsx24(
7312
8512
  "div",
7313
8513
  {
7314
8514
  "data-ohw-drag-handle-container": "",
@@ -7320,7 +8520,7 @@ function EditGlowChrome({
7320
8520
  transform: "translate(calc(-100% - 7px), -50%)",
7321
8521
  pointerEvents: dragDisabled ? "none" : "auto"
7322
8522
  },
7323
- children: /* @__PURE__ */ jsx23(
8523
+ children: /* @__PURE__ */ jsx24(
7324
8524
  DragHandle,
7325
8525
  {
7326
8526
  "aria-label": `Reorder ${reorderHrefKey}`,
@@ -7430,9 +8630,9 @@ function FloatingToolbar({
7430
8630
  showEditLink,
7431
8631
  onEditLink
7432
8632
  }) {
7433
- const localRef = React10.useRef(null);
7434
- const [measuredW, setMeasuredW] = React10.useState(330);
7435
- const setRefs = React10.useCallback(
8633
+ const localRef = React11.useRef(null);
8634
+ const [measuredW, setMeasuredW] = React11.useState(330);
8635
+ const setRefs = React11.useCallback(
7436
8636
  (node) => {
7437
8637
  localRef.current = node;
7438
8638
  if (typeof elRef === "function") elRef(node);
@@ -7444,7 +8644,7 @@ function FloatingToolbar({
7444
8644
  },
7445
8645
  [elRef]
7446
8646
  );
7447
- React10.useLayoutEffect(() => {
8647
+ React11.useLayoutEffect(() => {
7448
8648
  const node = localRef.current;
7449
8649
  if (!node) return;
7450
8650
  const update = () => {
@@ -7457,7 +8657,7 @@ function FloatingToolbar({
7457
8657
  return () => ro.disconnect();
7458
8658
  }, [showEditLink, activeCommands]);
7459
8659
  const { top, left, transform } = calcToolbarPos(rect, parentScroll, measuredW);
7460
- return /* @__PURE__ */ jsx23(
8660
+ return /* @__PURE__ */ jsx24(
7461
8661
  "div",
7462
8662
  {
7463
8663
  ref: setRefs,
@@ -7469,12 +8669,12 @@ function FloatingToolbar({
7469
8669
  zIndex: 2147483647,
7470
8670
  pointerEvents: "auto"
7471
8671
  },
7472
- children: /* @__PURE__ */ jsxs14(CustomToolbar, { children: [
7473
- TOOLBAR_GROUPS.map((btns, gi) => /* @__PURE__ */ jsxs14(React10.Fragment, { children: [
7474
- gi > 0 && /* @__PURE__ */ jsx23(CustomToolbarDivider, {}),
8672
+ children: /* @__PURE__ */ jsxs13(CustomToolbar, { children: [
8673
+ TOOLBAR_GROUPS.map((btns, gi) => /* @__PURE__ */ jsxs13(React11.Fragment, { children: [
8674
+ gi > 0 && /* @__PURE__ */ jsx24(CustomToolbarDivider, {}),
7475
8675
  btns.map((btn) => {
7476
8676
  const isActive = activeCommands.has(btn.cmd);
7477
- return /* @__PURE__ */ jsx23(
8677
+ return /* @__PURE__ */ jsx24(
7478
8678
  CustomToolbarButton,
7479
8679
  {
7480
8680
  title: btn.title,
@@ -7483,7 +8683,7 @@ function FloatingToolbar({
7483
8683
  e.preventDefault();
7484
8684
  onCommand(btn.cmd);
7485
8685
  },
7486
- children: /* @__PURE__ */ jsx23(
8686
+ children: /* @__PURE__ */ jsx24(
7487
8687
  "svg",
7488
8688
  {
7489
8689
  width: "16",
@@ -7504,7 +8704,7 @@ function FloatingToolbar({
7504
8704
  );
7505
8705
  })
7506
8706
  ] }, gi)),
7507
- showEditLink ? /* @__PURE__ */ jsx23(
8707
+ showEditLink ? /* @__PURE__ */ jsx24(
7508
8708
  CustomToolbarButton,
7509
8709
  {
7510
8710
  type: "button",
@@ -7518,7 +8718,7 @@ function FloatingToolbar({
7518
8718
  e.preventDefault();
7519
8719
  e.stopPropagation();
7520
8720
  },
7521
- children: /* @__PURE__ */ jsx23(Link2, { className: "size-4 shrink-0", "aria-hidden": true })
8721
+ children: /* @__PURE__ */ jsx24(Link2, { className: "size-4 shrink-0", "aria-hidden": true })
7522
8722
  }
7523
8723
  ) : null
7524
8724
  ] })
@@ -7535,7 +8735,7 @@ function StateToggle({
7535
8735
  states,
7536
8736
  onStateChange
7537
8737
  }) {
7538
- return /* @__PURE__ */ jsx23(
8738
+ return /* @__PURE__ */ jsx24(
7539
8739
  ToggleGroup,
7540
8740
  {
7541
8741
  "data-ohw-state-toggle": "",
@@ -7549,7 +8749,7 @@ function StateToggle({
7549
8749
  left: rect.right - 8,
7550
8750
  transform: "translateX(-100%)"
7551
8751
  },
7552
- children: states.map((state) => /* @__PURE__ */ jsx23(ToggleGroupItem, { value: state, size: "sm", children: state }, state))
8752
+ children: states.map((state) => /* @__PURE__ */ jsx24(ToggleGroupItem, { value: state, size: "sm", children: state }, state))
7553
8753
  }
7554
8754
  );
7555
8755
  }
@@ -7576,8 +8776,8 @@ function OhhwellsBridge() {
7576
8776
  const router = useRouter2();
7577
8777
  const searchParams = useSearchParams();
7578
8778
  const isEditMode = isEditSessionActive();
7579
- const [bridgeRoot, setBridgeRoot] = useState6(null);
7580
- useEffect7(() => {
8779
+ const [bridgeRoot, setBridgeRoot] = useState7(null);
8780
+ useEffect8(() => {
7581
8781
  const figtreeFontId = "ohw-figtree-font";
7582
8782
  if (!document.getElementById(figtreeFontId)) {
7583
8783
  const preconnect1 = Object.assign(document.createElement("link"), { rel: "preconnect", href: "https://fonts.googleapis.com" });
@@ -7605,92 +8805,136 @@ function OhhwellsBridge() {
7605
8805
  const subdomainFromQuery = searchParams.get("subdomain");
7606
8806
  const subdomain = resolveSubdomain(subdomainFromQuery);
7607
8807
  useLinkHrefGuardian(pathname, subdomain, isEditMode);
7608
- const postToParent2 = useCallback3((data) => {
8808
+ const postToParent2 = useCallback4((data) => {
7609
8809
  if (typeof window !== "undefined" && window.parent !== window) {
7610
8810
  window.parent.postMessage(data, "*");
7611
8811
  }
7612
8812
  }, []);
7613
- const [fetchState, setFetchState] = useState6("idle");
7614
- const autoSaveTimers = useRef6(/* @__PURE__ */ new Map());
7615
- const activeElRef = useRef6(null);
7616
- const selectedElRef = useRef6(null);
7617
- const originalContentRef = useRef6(null);
7618
- const activeStateElRef = useRef6(null);
7619
- const parentScrollRef = useRef6(null);
7620
- const visibleViewportRef = useRef6(null);
7621
- const [dialogPortalContainer, setDialogPortalContainer] = useState6(null);
7622
- const attachVisibleViewport = useCallback3((node) => {
8813
+ const [fetchState, setFetchState] = useState7("idle");
8814
+ const autoSaveTimers = useRef7(/* @__PURE__ */ new Map());
8815
+ const activeElRef = useRef7(null);
8816
+ const selectedElRef = useRef7(null);
8817
+ const selectedHrefKeyRef = useRef7(null);
8818
+ const selectedFooterColAttrRef = useRef7(null);
8819
+ const originalContentRef = useRef7(null);
8820
+ const activeStateElRef = useRef7(null);
8821
+ const parentScrollRef = useRef7(null);
8822
+ const visibleViewportRef = useRef7(null);
8823
+ const [dialogPortalContainer, setDialogPortalContainer] = useState7(null);
8824
+ const attachVisibleViewport = useCallback4((node) => {
7623
8825
  visibleViewportRef.current = node;
7624
8826
  setDialogPortalContainer(node);
7625
8827
  if (node) applyVisibleViewport(node, parentScrollRef.current);
7626
8828
  }, []);
7627
- const toolbarElRef = useRef6(null);
7628
- const glowElRef = useRef6(null);
7629
- const hoveredImageRef = useRef6(null);
7630
- const hoveredImageHasTextOverlapRef = useRef6(false);
7631
- const dragOverElRef = useRef6(null);
7632
- const [mediaHover, setMediaHover] = useState6(null);
7633
- const [uploadingRects, setUploadingRects] = useState6({});
7634
- const hoveredGapRef = useRef6(null);
7635
- const imageUnhoverTimerRef = useRef6(null);
7636
- const imageShowTimerRef = useRef6(null);
7637
- const editStylesRef = useRef6(null);
7638
- const activateRef = useRef6(() => {
8829
+ const toolbarElRef = useRef7(null);
8830
+ const glowElRef = useRef7(null);
8831
+ const hoveredImageRef = useRef7(null);
8832
+ const hoveredImageHasTextOverlapRef = useRef7(false);
8833
+ const dragOverElRef = useRef7(null);
8834
+ const [mediaHover, setMediaHover] = useState7(null);
8835
+ const [uploadingRects, setUploadingRects] = useState7({});
8836
+ const hoveredGapRef = useRef7(null);
8837
+ const imageUnhoverTimerRef = useRef7(null);
8838
+ const imageShowTimerRef = useRef7(null);
8839
+ const editStylesRef = useRef7(null);
8840
+ const activateRef = useRef7(() => {
7639
8841
  });
7640
- const deactivateRef = useRef6(() => {
8842
+ const deactivateRef = useRef7(() => {
7641
8843
  });
7642
- const selectRef = useRef6(() => {
8844
+ const selectRef = useRef7(() => {
7643
8845
  });
7644
- const selectFrameRef = useRef6(() => {
8846
+ const selectFrameRef = useRef7(() => {
7645
8847
  });
7646
- const deselectRef = useRef6(() => {
8848
+ const deselectRef = useRef7(() => {
7647
8849
  });
7648
- const reselectNavigationItemRef = useRef6(() => {
8850
+ const reselectNavigationItemRef = useRef7(() => {
7649
8851
  });
7650
- const commitNavigationTextEditRef = useRef6(() => {
8852
+ const commitNavigationTextEditRef = useRef7(() => {
7651
8853
  });
7652
- const refreshActiveCommandsRef = useRef6(() => {
8854
+ const refreshActiveCommandsRef = useRef7(() => {
7653
8855
  });
7654
- const postToParentRef = useRef6(postToParent2);
8856
+ const postToParentRef = useRef7(postToParent2);
7655
8857
  postToParentRef.current = postToParent2;
7656
- const sectionsLoadedRef = useRef6(false);
7657
- const pendingScheduleConfigRequests = useRef6([]);
7658
- const [toolbarRect, setToolbarRect] = useState6(null);
7659
- const [toolbarVariant, setToolbarVariant] = useState6("none");
7660
- const toolbarVariantRef = useRef6("none");
8858
+ const sectionsLoadedRef = useRef7(false);
8859
+ const pendingScheduleConfigRequests = useRef7([]);
8860
+ const [toolbarRect, setToolbarRect] = useState7(null);
8861
+ const [toolbarVariant, setToolbarVariant] = useState7("none");
8862
+ const toolbarVariantRef = useRef7("none");
7661
8863
  toolbarVariantRef.current = toolbarVariant;
7662
- const [selectedIsCta, setSelectedIsCta] = useState6(false);
7663
- const [reorderHrefKey, setReorderHrefKey] = useState6(null);
7664
- const [reorderDragDisabled, setReorderDragDisabled] = useState6(false);
7665
- const [toggleState, setToggleState] = useState6(null);
7666
- const [maxBadge, setMaxBadge] = useState6(null);
7667
- const [activeCommands, setActiveCommands] = useState6(/* @__PURE__ */ new Set());
7668
- const [sectionGap, setSectionGap] = useState6(null);
7669
- const [toolbarShowEditLink, setToolbarShowEditLink] = useState6(false);
7670
- const hoveredNavContainerRef = useRef6(null);
7671
- const [hoveredNavContainerRect, setHoveredNavContainerRect] = useState6(null);
7672
- const hoveredItemElRef = useRef6(null);
7673
- const [hoveredItemRect, setHoveredItemRect] = useState6(null);
7674
- const siblingHintElRef = useRef6(null);
7675
- const [siblingHintRect, setSiblingHintRect] = useState6(null);
7676
- const [isItemDragging, setIsItemDragging] = useState6(false);
7677
- const [linkPopover, setLinkPopover] = useState6(null);
7678
- const linkPopoverSessionRef = useRef6(null);
7679
- const addNavAfterAnchorRef = useRef6(null);
7680
- const editContentRef = useRef6({});
7681
- const [sitePages, setSitePages] = useState6([]);
7682
- const [sectionsByPath, setSectionsByPath] = useState6({});
7683
- const sectionsPrefetchGenRef = useRef6(0);
7684
- const setLinkPopoverRef = useRef6(setLinkPopover);
7685
- const linkPopoverPanelRef = useRef6(null);
7686
- const linkPopoverOpenRef = useRef6(false);
7687
- const linkPopoverGraceUntilRef = useRef6(0);
8864
+ const [selectedIsCta, setSelectedIsCta] = useState7(false);
8865
+ const [reorderHrefKey, setReorderHrefKey] = useState7(null);
8866
+ const [reorderDragDisabled, setReorderDragDisabled] = useState7(false);
8867
+ const [toggleState, setToggleState] = useState7(null);
8868
+ const [maxBadge, setMaxBadge] = useState7(null);
8869
+ const [activeCommands, setActiveCommands] = useState7(/* @__PURE__ */ new Set());
8870
+ const [sectionGap, setSectionGap] = useState7(null);
8871
+ const [toolbarShowEditLink, setToolbarShowEditLink] = useState7(false);
8872
+ const hoveredNavContainerRef = useRef7(null);
8873
+ const [hoveredNavContainerRect, setHoveredNavContainerRect] = useState7(null);
8874
+ const hoveredItemElRef = useRef7(null);
8875
+ const [hoveredItemRect, setHoveredItemRect] = useState7(null);
8876
+ const siblingHintElRef = useRef7(null);
8877
+ const [siblingHintRect, setSiblingHintRect] = useState7(null);
8878
+ const [siblingHintRects, setSiblingHintRects] = useState7([]);
8879
+ const [isItemDragging, setIsItemDragging] = useState7(false);
8880
+ const [isFooterFrameSelection, setIsFooterFrameSelection] = useState7(false);
8881
+ const footerDragRef = useRef7(null);
8882
+ const [footerDropSlots, setFooterDropSlots] = useState7([]);
8883
+ const [activeFooterDropIndex, setActiveFooterDropIndex] = useState7(null);
8884
+ const [draggedItemRect, setDraggedItemRect] = useState7(null);
8885
+ const footerPointerDragRef = useRef7(null);
8886
+ const suppressNextClickRef = useRef7(false);
8887
+ const suppressClickUntilRef = useRef7(0);
8888
+ const [linkPopover, setLinkPopover] = useState7(null);
8889
+ const linkPopoverSessionRef = useRef7(null);
8890
+ const addNavAfterAnchorRef = useRef7(null);
8891
+ const editContentRef = useRef7({});
8892
+ const [sitePages, setSitePages] = useState7([]);
8893
+ const [sectionsByPath, setSectionsByPath] = useState7({});
8894
+ const sectionsPrefetchGenRef = useRef7(0);
8895
+ const setLinkPopoverRef = useRef7(setLinkPopover);
8896
+ const linkPopoverPanelRef = useRef7(null);
8897
+ const linkPopoverOpenRef = useRef7(false);
8898
+ const linkPopoverGraceUntilRef = useRef7(0);
7688
8899
  setLinkPopoverRef.current = setLinkPopover;
7689
8900
  linkPopoverSessionRef.current = linkPopover;
8901
+ const {
8902
+ navDragRef,
8903
+ navDropSlots,
8904
+ activeNavDropIndex,
8905
+ startNavLinkDrag,
8906
+ commitNavDrag,
8907
+ onNavDragOver,
8908
+ armNavPressFromChrome,
8909
+ refreshNavDragVisualsRef
8910
+ } = useNavItemDrag({
8911
+ isEditMode,
8912
+ editContentRef,
8913
+ selectedElRef,
8914
+ activeElRef,
8915
+ footerDragRef,
8916
+ suppressNextClickRef,
8917
+ suppressClickUntilRef,
8918
+ siblingHintElRef,
8919
+ postToParentRef,
8920
+ deselectRef,
8921
+ selectRef,
8922
+ deactivateRef,
8923
+ setLinkPopover: () => setLinkPopover(null),
8924
+ linkPopoverOpenRef,
8925
+ setIsItemDragging,
8926
+ setDraggedItemRect,
8927
+ setSiblingHintRect,
8928
+ setSiblingHintRects,
8929
+ setToolbarRect,
8930
+ getNavigationItemAnchor,
8931
+ isDragHandleDisabled,
8932
+ isNavbarButton: isNavbarButton2
8933
+ });
7690
8934
  const bumpLinkPopoverGrace = () => {
7691
8935
  linkPopoverGraceUntilRef.current = Date.now() + 350;
7692
8936
  };
7693
- const runSectionsPrefetch = useCallback3((pages) => {
8937
+ const runSectionsPrefetch = useCallback4((pages) => {
7694
8938
  if (!isEditMode || shouldUseDevFixtures() || pages.length === 0) return;
7695
8939
  const gen = ++sectionsPrefetchGenRef.current;
7696
8940
  const paths = pages.map((p) => p.path);
@@ -7709,10 +8953,24 @@ function OhhwellsBridge() {
7709
8953
  );
7710
8954
  });
7711
8955
  }, [isEditMode, pathname]);
7712
- const runSectionsPrefetchRef = useRef6(runSectionsPrefetch);
8956
+ const runSectionsPrefetchRef = useRef7(runSectionsPrefetch);
7713
8957
  runSectionsPrefetchRef.current = runSectionsPrefetch;
7714
- useEffect7(() => {
7715
- if (!linkPopover) return;
8958
+ useEffect8(() => {
8959
+ if (!linkPopover) {
8960
+ document.documentElement.removeAttribute("data-ohw-link-popover-open");
8961
+ return;
8962
+ }
8963
+ document.documentElement.setAttribute("data-ohw-link-popover-open", "");
8964
+ hoveredItemElRef.current = null;
8965
+ setHoveredItemRect(null);
8966
+ hoveredNavContainerRef.current = null;
8967
+ setHoveredNavContainerRect(null);
8968
+ siblingHintElRef.current = null;
8969
+ setSiblingHintRect(null);
8970
+ setSiblingHintRects([]);
8971
+ if (selectedElRef.current || toolbarVariantRef.current !== "none") {
8972
+ deselectRef.current();
8973
+ }
7716
8974
  if (hoveredImageRef.current) {
7717
8975
  hoveredImageRef.current = null;
7718
8976
  hoveredImageHasTextOverlapRef.current = false;
@@ -7746,7 +9004,7 @@ function OhhwellsBridge() {
7746
9004
  document.removeEventListener("touchmove", preventBackgroundScroll, scrollOpts);
7747
9005
  };
7748
9006
  }, [linkPopover, postToParent2]);
7749
- useEffect7(() => {
9007
+ useEffect8(() => {
7750
9008
  if (!isEditMode) return;
7751
9009
  const useFixtures = shouldUseDevFixtures();
7752
9010
  if (useFixtures) {
@@ -7770,14 +9028,14 @@ function OhhwellsBridge() {
7770
9028
  if (!useFixtures) postToParent2({ type: "ow:request-site-pages" });
7771
9029
  return () => window.removeEventListener("message", onSitePages);
7772
9030
  }, [isEditMode, postToParent2]);
7773
- useEffect7(() => {
9031
+ useEffect8(() => {
7774
9032
  if (!isEditMode || shouldUseDevFixtures()) return;
7775
9033
  void loadAllSectionsManifest().then((manifest) => {
7776
9034
  if (Object.keys(manifest).length === 0) return;
7777
9035
  setSectionsByPath((prev) => ({ ...manifest, ...prev }));
7778
9036
  });
7779
9037
  }, [isEditMode]);
7780
- useEffect7(() => {
9038
+ useEffect8(() => {
7781
9039
  const update = () => {
7782
9040
  const el = activeElRef.current ?? selectedElRef.current;
7783
9041
  if (el) setToolbarRect(getEditMeasureEl(el).getBoundingClientRect());
@@ -7801,10 +9059,10 @@ function OhhwellsBridge() {
7801
9059
  vvp.removeEventListener("resize", update);
7802
9060
  };
7803
9061
  }, []);
7804
- const refreshStateRules = useCallback3(() => {
9062
+ const refreshStateRules = useCallback4(() => {
7805
9063
  editStylesRef.current?.forceHover && (editStylesRef.current.forceHover.textContent = collectStateRules());
7806
9064
  }, []);
7807
- const processConfigRequest = useCallback3((insertAfterVal) => {
9065
+ const processConfigRequest = useCallback4((insertAfterVal) => {
7808
9066
  const tracker = getSectionsTracker();
7809
9067
  let entries = [];
7810
9068
  try {
@@ -7827,7 +9085,7 @@ function OhhwellsBridge() {
7827
9085
  }
7828
9086
  window.postMessage({ type: "ow:schedule-config", insertAfter: insertAfterVal, scheduleId: null }, "*");
7829
9087
  }, [isEditMode]);
7830
- const deactivate = useCallback3(() => {
9088
+ const deactivate = useCallback4(() => {
7831
9089
  const el = activeElRef.current;
7832
9090
  if (!el) return;
7833
9091
  const key = el.dataset.ohwKey;
@@ -7859,19 +9117,23 @@ function OhhwellsBridge() {
7859
9117
  setToolbarShowEditLink(false);
7860
9118
  postToParent2({ type: "ow:exit-edit" });
7861
9119
  }, [postToParent2]);
7862
- const clearSelectedAttr = useCallback3(() => {
9120
+ const clearSelectedAttr = useCallback4(() => {
7863
9121
  document.querySelectorAll("[data-ohw-selected]").forEach((el) => {
7864
9122
  el.removeAttribute("data-ohw-selected");
7865
9123
  });
7866
9124
  }, []);
7867
- const deselect = useCallback3(() => {
9125
+ const deselect = useCallback4(() => {
7868
9126
  clearSelectedAttr();
7869
9127
  selectedElRef.current = null;
9128
+ selectedHrefKeyRef.current = null;
9129
+ selectedFooterColAttrRef.current = null;
7870
9130
  setSelectedIsCta(false);
7871
9131
  setReorderHrefKey(null);
7872
9132
  setReorderDragDisabled(false);
9133
+ setIsFooterFrameSelection(false);
7873
9134
  siblingHintElRef.current = null;
7874
9135
  setSiblingHintRect(null);
9136
+ setSiblingHintRects([]);
7875
9137
  setIsItemDragging(false);
7876
9138
  hoveredNavContainerRef.current = null;
7877
9139
  setHoveredNavContainerRect(null);
@@ -7880,12 +9142,57 @@ function OhhwellsBridge() {
7880
9142
  setToolbarVariant("none");
7881
9143
  }
7882
9144
  }, [clearSelectedAttr]);
7883
- const markSelected = useCallback3((el) => {
9145
+ const markSelected = useCallback4((el) => {
7884
9146
  clearSelectedAttr();
7885
9147
  el.setAttribute("data-ohw-selected", "");
7886
9148
  }, [clearSelectedAttr]);
7887
- const reselectNavigationItem = useCallback3((navAnchor) => {
9149
+ const resolveHrefKeyElement = useCallback4((hrefKey) => {
9150
+ if (isFooterHrefKey(hrefKey)) {
9151
+ return document.querySelector(
9152
+ `footer [data-ohw-href-key="${CSS.escape(hrefKey)}"]`
9153
+ );
9154
+ }
9155
+ if (isNavbarHrefKey(hrefKey)) {
9156
+ const desktop = document.querySelector("[data-ohw-nav-container]");
9157
+ const drawer = document.querySelector("[data-ohw-nav-drawer]");
9158
+ return desktop?.querySelector(`[data-ohw-href-key="${CSS.escape(hrefKey)}"]`) ?? drawer?.querySelector(`[data-ohw-href-key="${CSS.escape(hrefKey)}"]`) ?? document.querySelector(`[data-ohw-href-key="${CSS.escape(hrefKey)}"]`);
9159
+ }
9160
+ return document.querySelector(
9161
+ `[data-ohw-href-key="${CSS.escape(hrefKey)}"]`
9162
+ );
9163
+ }, []);
9164
+ const resyncSelectedNavigationItem = useCallback4(() => {
9165
+ const hrefKey = selectedHrefKeyRef.current;
9166
+ if (hrefKey) {
9167
+ const link = resolveHrefKeyElement(hrefKey);
9168
+ if (!link || !isNavigationItem(link)) return;
9169
+ selectedElRef.current = link;
9170
+ const { key, disabled } = getNavigationItemReorderState(link);
9171
+ setReorderHrefKey(key);
9172
+ setReorderDragDisabled(disabled);
9173
+ setIsFooterFrameSelection(false);
9174
+ setToolbarVariant("link-action");
9175
+ setToolbarRect(link.getBoundingClientRect());
9176
+ setSiblingHintRects(collectNavigationItemSiblingHintRects(link));
9177
+ return;
9178
+ }
9179
+ const colAttr = selectedFooterColAttrRef.current;
9180
+ if (colAttr != null) {
9181
+ const column = document.querySelector(`[data-ohw-footer-col="${CSS.escape(colAttr)}"]`) ?? listFooterColumns()[Number(colAttr)] ?? null;
9182
+ if (!column || !isNavigationContainer(column)) return;
9183
+ selectedElRef.current = column;
9184
+ setIsFooterFrameSelection(true);
9185
+ setToolbarVariant("select-frame");
9186
+ setToolbarRect(column.getBoundingClientRect());
9187
+ setSiblingHintRects(
9188
+ listFooterColumns().filter((c) => c !== column).map((c) => c.getBoundingClientRect())
9189
+ );
9190
+ }
9191
+ }, [resolveHrefKeyElement]);
9192
+ const reselectNavigationItem = useCallback4((navAnchor) => {
7888
9193
  selectedElRef.current = navAnchor;
9194
+ selectedHrefKeyRef.current = navAnchor.getAttribute("data-ohw-href-key");
9195
+ selectedFooterColAttrRef.current = null;
7889
9196
  markSelected(navAnchor);
7890
9197
  setSelectedIsCta(isNavbarButton2(navAnchor));
7891
9198
  const { key, disabled } = getNavigationItemReorderState(navAnchor);
@@ -7893,10 +9200,11 @@ function OhhwellsBridge() {
7893
9200
  setReorderDragDisabled(disabled);
7894
9201
  setToolbarVariant("link-action");
7895
9202
  setToolbarRect(navAnchor.getBoundingClientRect());
9203
+ setSiblingHintRects(collectNavigationItemSiblingHintRects(navAnchor));
7896
9204
  setToolbarShowEditLink(false);
7897
9205
  setActiveCommands(/* @__PURE__ */ new Set());
7898
9206
  }, [markSelected]);
7899
- const commitNavigationTextEdit = useCallback3((navAnchor) => {
9207
+ const commitNavigationTextEdit = useCallback4((navAnchor) => {
7900
9208
  const el = activeElRef.current;
7901
9209
  if (!el) return;
7902
9210
  const key = el.dataset.ohwKey;
@@ -7906,49 +9214,337 @@ function OhhwellsBridge() {
7906
9214
  clearTimeout(timer);
7907
9215
  autoSaveTimers.current.delete(key);
7908
9216
  }
7909
- const html = sanitizeHtml(el.innerHTML);
7910
- const original = originalContentRef.current ?? "";
7911
- if (html !== sanitizeHtml(original)) {
7912
- postToParent2({ type: "ow:change", nodes: [{ key, text: html }] });
7913
- const h = document.documentElement.scrollHeight;
7914
- if (h > 50) postToParent2({ type: "ow:height", height: h });
9217
+ const html = sanitizeHtml(el.innerHTML);
9218
+ const original = originalContentRef.current ?? "";
9219
+ if (html !== sanitizeHtml(original)) {
9220
+ postToParent2({ type: "ow:change", nodes: [{ key, text: html }] });
9221
+ const h = document.documentElement.scrollHeight;
9222
+ if (h > 50) postToParent2({ type: "ow:height", height: h });
9223
+ }
9224
+ }
9225
+ el.removeAttribute("contenteditable");
9226
+ el.removeAttribute("data-ohw-editing");
9227
+ activeElRef.current = null;
9228
+ setMaxBadge(null);
9229
+ setActiveCommands(/* @__PURE__ */ new Set());
9230
+ setToolbarShowEditLink(false);
9231
+ postToParent2({ type: "ow:exit-edit" });
9232
+ reselectNavigationItem(navAnchor);
9233
+ }, [postToParent2, reselectNavigationItem]);
9234
+ const handleAddTopLevelNavItem = useCallback4(() => {
9235
+ const items = listNavbarItems();
9236
+ addNavAfterAnchorRef.current = items[items.length - 1] ?? null;
9237
+ deselectRef.current();
9238
+ deactivateRef.current();
9239
+ bumpLinkPopoverGrace();
9240
+ setLinkPopover({
9241
+ key: "__add-nav__",
9242
+ mode: "create",
9243
+ intent: "add-nav"
9244
+ });
9245
+ }, []);
9246
+ const clearFooterDragVisuals = useCallback4(() => {
9247
+ footerDragRef.current = null;
9248
+ setSiblingHintRects([]);
9249
+ setFooterDropSlots([]);
9250
+ setActiveFooterDropIndex(null);
9251
+ setDraggedItemRect(null);
9252
+ setIsItemDragging(false);
9253
+ unlockFooterDragInteraction();
9254
+ }, []);
9255
+ const refreshFooterDragVisuals = useCallback4((session, activeSlot, clientX, clientY) => {
9256
+ const dragged = session.draggedEl;
9257
+ setDraggedItemRect(dragged.getBoundingClientRect());
9258
+ if (typeof clientX === "number" && typeof clientY === "number") {
9259
+ session.lastClientX = clientX;
9260
+ session.lastClientY = clientY;
9261
+ }
9262
+ session.activeSlot = activeSlot;
9263
+ setSiblingHintRects([]);
9264
+ if (session.kind === "link") {
9265
+ const columns = listFooterColumns();
9266
+ const slots2 = [];
9267
+ columns.forEach((col, i) => {
9268
+ slots2.push(...buildLinkDropSlots(col, i));
9269
+ });
9270
+ setFooterDropSlots(slots2);
9271
+ const activeIdx2 = activeSlot ? slots2.findIndex((s) => s.columnIndex === activeSlot.columnIndex && s.insertIndex === activeSlot.insertIndex) : -1;
9272
+ setActiveFooterDropIndex(activeIdx2 >= 0 ? activeIdx2 : null);
9273
+ return;
9274
+ }
9275
+ const slots = buildColumnDropSlots();
9276
+ setFooterDropSlots(slots);
9277
+ const activeIdx = activeSlot ? slots.findIndex((s) => s.insertIndex === activeSlot.insertIndex) : -1;
9278
+ setActiveFooterDropIndex(activeIdx >= 0 ? activeIdx : null);
9279
+ }, []);
9280
+ const refreshFooterDragVisualsRef = useRef7(refreshFooterDragVisuals);
9281
+ refreshFooterDragVisualsRef.current = refreshFooterDragVisuals;
9282
+ const commitFooterDragRef = useRef7(() => {
9283
+ });
9284
+ const beginFooterDragRef = useRef7(() => {
9285
+ });
9286
+ const beginFooterDrag = useCallback4(
9287
+ (session) => {
9288
+ const rect = session.draggedEl.getBoundingClientRect();
9289
+ session.lastClientX = session.lastClientX || rect.left + rect.width / 2;
9290
+ session.lastClientY = session.lastClientY || rect.top + rect.height / 2;
9291
+ session.activeSlot = session.activeSlot ?? null;
9292
+ footerDragRef.current = session;
9293
+ setIsItemDragging(true);
9294
+ lockFooterDuringDrag();
9295
+ siblingHintElRef.current = null;
9296
+ setSiblingHintRect(null);
9297
+ if (session.wasSelected && selectedElRef.current === session.draggedEl) {
9298
+ setToolbarRect(rect);
9299
+ }
9300
+ const initialSlot = session.kind === "link" && session.hrefKey ? hitTestLinkDropSlot(session.lastClientX, session.lastClientY, session.hrefKey) : hitTestColumnDropSlot(session.lastClientX, session.lastClientY);
9301
+ refreshFooterDragVisuals(session, initialSlot, session.lastClientX, session.lastClientY);
9302
+ },
9303
+ [refreshFooterDragVisuals]
9304
+ );
9305
+ beginFooterDragRef.current = beginFooterDrag;
9306
+ const commitFooterDrag = useCallback4(
9307
+ (clientX, clientY) => {
9308
+ const session = footerDragRef.current;
9309
+ if (!session) {
9310
+ clearFooterDragVisuals();
9311
+ return;
9312
+ }
9313
+ const x = typeof clientX === "number" && (clientX !== 0 || clientY !== 0) ? clientX : session.lastClientX;
9314
+ const y = typeof clientY === "number" && (clientX !== 0 || clientY !== 0) ? clientY : session.lastClientY;
9315
+ let nextOrder = null;
9316
+ const slot = session.activeSlot ?? (session.kind === "link" && session.hrefKey ? hitTestLinkDropSlot(x, y, session.hrefKey) : session.kind === "column" ? hitTestColumnDropSlot(x, y) : null);
9317
+ if (session.kind === "link" && session.hrefKey && slot) {
9318
+ nextOrder = planFooterLinkMove(session.hrefKey, slot.columnIndex, slot.insertIndex);
9319
+ } else if (session.kind === "column" && slot) {
9320
+ nextOrder = planFooterColumnMove(session.sourceColumnIndex, slot.insertIndex);
9321
+ }
9322
+ const wasSelected = session.wasSelected;
9323
+ const draggedEl = session.draggedEl;
9324
+ const hrefKey = session.hrefKey;
9325
+ let movedColumnIndex = null;
9326
+ if (session.kind === "column" && slot && nextOrder) {
9327
+ let adjusted = slot.insertIndex;
9328
+ if (session.sourceColumnIndex < slot.insertIndex) adjusted -= 1;
9329
+ movedColumnIndex = Math.max(0, Math.min(adjusted, nextOrder.length - 1));
9330
+ }
9331
+ const applySelectionAfterDrop = () => {
9332
+ if (!wasSelected) {
9333
+ deselectRef.current();
9334
+ return;
9335
+ }
9336
+ if (hrefKey) {
9337
+ selectedHrefKeyRef.current = hrefKey;
9338
+ selectedFooterColAttrRef.current = null;
9339
+ const link = resolveHrefKeyElement(hrefKey);
9340
+ if (link && isNavigationItem(link)) {
9341
+ selectRef.current(link);
9342
+ return;
9343
+ }
9344
+ }
9345
+ if (movedColumnIndex !== null && nextOrder) {
9346
+ const colKeys = nextOrder[movedColumnIndex] ?? [];
9347
+ let column = null;
9348
+ for (const key of colKeys) {
9349
+ const link = document.querySelector(
9350
+ `footer [data-ohw-href-key="${CSS.escape(key)}"]`
9351
+ );
9352
+ if (link) {
9353
+ column = findFooterColumnForLink(link);
9354
+ if (column) break;
9355
+ }
9356
+ }
9357
+ if (!column) column = listFooterColumns()[movedColumnIndex] ?? null;
9358
+ if (column && isNavigationContainer(column)) {
9359
+ selectFrameRef.current(column);
9360
+ return;
9361
+ }
9362
+ }
9363
+ if (document.body.contains(draggedEl)) {
9364
+ if (isNavigationItem(draggedEl)) {
9365
+ selectRef.current(draggedEl);
9366
+ return;
9367
+ }
9368
+ if (isNavigationContainer(draggedEl)) {
9369
+ selectFrameRef.current(draggedEl);
9370
+ return;
9371
+ }
9372
+ }
9373
+ deselectRef.current();
9374
+ };
9375
+ if (nextOrder) {
9376
+ const orderJson = JSON.stringify(nextOrder);
9377
+ editContentRef.current = {
9378
+ ...editContentRef.current,
9379
+ [FOOTER_ORDER_KEY]: orderJson
9380
+ };
9381
+ applyFooterOrder(nextOrder);
9382
+ document.querySelectorAll("footer [data-ohw-href-key]").forEach(disableNativeHrefDrag);
9383
+ postToParentRef.current({
9384
+ type: "ow:change",
9385
+ nodes: [{ key: FOOTER_ORDER_KEY, text: orderJson }]
9386
+ });
9387
+ applySelectionAfterDrop();
9388
+ clearFooterDragVisuals();
9389
+ requestAnimationFrame(() => {
9390
+ if (editContentRef.current[FOOTER_ORDER_KEY] === orderJson) {
9391
+ applyFooterOrder(nextOrder);
9392
+ document.querySelectorAll("footer [data-ohw-href-key]").forEach(disableNativeHrefDrag);
9393
+ }
9394
+ applySelectionAfterDrop();
9395
+ requestAnimationFrame(() => {
9396
+ if (editContentRef.current[FOOTER_ORDER_KEY] === orderJson) {
9397
+ applyFooterOrder(nextOrder);
9398
+ }
9399
+ resyncSelectedNavigationItem();
9400
+ });
9401
+ });
9402
+ return;
9403
+ }
9404
+ applySelectionAfterDrop();
9405
+ clearFooterDragVisuals();
9406
+ },
9407
+ [clearFooterDragVisuals, resolveHrefKeyElement, resyncSelectedNavigationItem]
9408
+ );
9409
+ commitFooterDragRef.current = commitFooterDrag;
9410
+ const startFooterLinkDrag = useCallback4(
9411
+ (anchor, clientX, clientY, wasSelected) => {
9412
+ const hrefKey = anchor.getAttribute("data-ohw-href-key");
9413
+ if (!hrefKey || !isFooterHrefKey(hrefKey)) return false;
9414
+ const column = findFooterColumnForLink(anchor);
9415
+ const columns = listFooterColumns();
9416
+ beginFooterDrag({
9417
+ kind: "link",
9418
+ hrefKey,
9419
+ columnEl: column,
9420
+ sourceColumnIndex: column ? columns.indexOf(column) : 0,
9421
+ wasSelected,
9422
+ draggedEl: anchor,
9423
+ lastClientX: clientX,
9424
+ lastClientY: clientY,
9425
+ activeSlot: null
9426
+ });
9427
+ return true;
9428
+ },
9429
+ [beginFooterDrag]
9430
+ );
9431
+ const startFooterColumnDrag = useCallback4(
9432
+ (columnEl, clientX, clientY, wasSelected) => {
9433
+ const columns = listFooterColumns();
9434
+ const idx = columns.indexOf(columnEl);
9435
+ if (idx < 0) return false;
9436
+ beginFooterDrag({
9437
+ kind: "column",
9438
+ hrefKey: null,
9439
+ columnEl,
9440
+ sourceColumnIndex: idx,
9441
+ wasSelected,
9442
+ draggedEl: columnEl,
9443
+ lastClientX: clientX,
9444
+ lastClientY: clientY,
9445
+ activeSlot: null
9446
+ });
9447
+ return true;
9448
+ },
9449
+ [beginFooterDrag]
9450
+ );
9451
+ const handleItemDragStart = useCallback4(
9452
+ (e) => {
9453
+ const selected = selectedElRef.current;
9454
+ if (!selected) {
9455
+ setIsItemDragging(true);
9456
+ return;
9457
+ }
9458
+ const clientX = e?.clientX ?? selected.getBoundingClientRect().left + 8;
9459
+ const clientY = e?.clientY ?? selected.getBoundingClientRect().top + 8;
9460
+ if (startFooterLinkDrag(selected, clientX, clientY, true)) return;
9461
+ if (selected.hasAttribute("data-ohw-footer-col") || isInferredFooterGroup(selected) && selected.closest("footer")) {
9462
+ if (startFooterColumnDrag(selected, clientX, clientY, true)) return;
9463
+ }
9464
+ if (startNavLinkDrag(selected, clientX, clientY, true)) return;
9465
+ siblingHintElRef.current = null;
9466
+ setSiblingHintRect(null);
9467
+ setIsItemDragging(true);
9468
+ },
9469
+ [startFooterColumnDrag, startFooterLinkDrag, startNavLinkDrag]
9470
+ );
9471
+ const handleItemDragEnd = useCallback4(
9472
+ (e) => {
9473
+ if (footerDragRef.current) {
9474
+ const x = e?.clientX;
9475
+ const y = e?.clientY;
9476
+ if (typeof x === "number" && typeof y === "number" && (x !== 0 || y !== 0)) {
9477
+ commitFooterDrag(x, y);
9478
+ } else {
9479
+ commitFooterDrag();
9480
+ }
9481
+ return;
9482
+ }
9483
+ if (navDragRef.current) {
9484
+ const x = e?.clientX;
9485
+ const y = e?.clientY;
9486
+ if (typeof x === "number" && typeof y === "number" && (x !== 0 || y !== 0)) {
9487
+ commitNavDrag(x, y);
9488
+ } else {
9489
+ commitNavDrag();
9490
+ }
9491
+ return;
7915
9492
  }
9493
+ setIsItemDragging(false);
9494
+ },
9495
+ [commitFooterDrag, commitNavDrag, navDragRef]
9496
+ );
9497
+ const handleItemChromePointerDown = useCallback4((e) => {
9498
+ if (e.button !== 0) return;
9499
+ const selected = selectedElRef.current;
9500
+ if (!selected) return;
9501
+ armFooterPressDrag();
9502
+ const hrefKey = selected.getAttribute("data-ohw-href-key");
9503
+ if (hrefKey && isFooterHrefKey(hrefKey)) {
9504
+ footerPointerDragRef.current = {
9505
+ el: selected,
9506
+ kind: "link",
9507
+ startX: e.clientX,
9508
+ startY: e.clientY,
9509
+ pointerId: e.pointerId,
9510
+ wasSelected: true,
9511
+ started: false
9512
+ };
9513
+ return;
7916
9514
  }
7917
- el.removeAttribute("contenteditable");
7918
- el.removeAttribute("data-ohw-editing");
7919
- activeElRef.current = null;
7920
- setMaxBadge(null);
7921
- setActiveCommands(/* @__PURE__ */ new Set());
7922
- setToolbarShowEditLink(false);
7923
- postToParent2({ type: "ow:exit-edit" });
7924
- reselectNavigationItem(navAnchor);
7925
- }, [postToParent2, reselectNavigationItem]);
7926
- const handleAddTopLevelNavItem = useCallback3(() => {
7927
- const items = listNavbarItems();
7928
- addNavAfterAnchorRef.current = items[items.length - 1] ?? null;
7929
- deselectRef.current();
7930
- deactivateRef.current();
7931
- bumpLinkPopoverGrace();
7932
- setLinkPopover({
7933
- key: "__add-nav__",
7934
- mode: "create",
7935
- intent: "add-nav"
7936
- });
7937
- }, []);
7938
- const handleItemDragStart = useCallback3(() => {
7939
- siblingHintElRef.current = null;
7940
- setSiblingHintRect(null);
7941
- setIsItemDragging(true);
7942
- }, []);
7943
- const handleItemDragEnd = useCallback3(() => {
7944
- setIsItemDragging(false);
9515
+ if (selected.hasAttribute("data-ohw-footer-col") || isInferredFooterGroup(selected) && selected.closest("footer")) {
9516
+ footerPointerDragRef.current = {
9517
+ el: selected,
9518
+ kind: "column",
9519
+ startX: e.clientX,
9520
+ startY: e.clientY,
9521
+ pointerId: e.pointerId,
9522
+ wasSelected: true,
9523
+ started: false
9524
+ };
9525
+ return;
9526
+ }
9527
+ if (armNavPressFromChrome(selected, e.clientX, e.clientY, e.pointerId)) return;
9528
+ }, [armNavPressFromChrome]);
9529
+ const handleItemChromeClick = useCallback4((clientX, clientY) => {
9530
+ if (suppressNextClickRef.current || Date.now() < suppressClickUntilRef.current) {
9531
+ suppressNextClickRef.current = false;
9532
+ return;
9533
+ }
9534
+ const selected = selectedElRef.current;
9535
+ if (!selected || !isNavigationItem(selected)) return;
9536
+ const editable = selected.querySelector('[data-ohw-editable="text"]') ?? selected.querySelector("[data-ohw-editable]");
9537
+ if (!editable) return;
9538
+ activateRef.current(editable, { caretX: clientX, caretY: clientY });
7945
9539
  }, []);
7946
9540
  reselectNavigationItemRef.current = reselectNavigationItem;
7947
9541
  commitNavigationTextEditRef.current = commitNavigationTextEdit;
7948
- const select = useCallback3((anchor) => {
9542
+ const select = useCallback4((anchor) => {
7949
9543
  if (!isNavigationItem(anchor)) return;
7950
9544
  if (activeElRef.current) deactivate();
7951
9545
  selectedElRef.current = anchor;
9546
+ selectedHrefKeyRef.current = anchor.getAttribute("data-ohw-href-key");
9547
+ selectedFooterColAttrRef.current = null;
7952
9548
  markSelected(anchor);
7953
9549
  setSelectedIsCta(isNavbarButton2(anchor));
7954
9550
  clearHrefKeyHover(anchor);
@@ -7958,19 +9554,24 @@ function OhhwellsBridge() {
7958
9554
  hoveredItemElRef.current = null;
7959
9555
  siblingHintElRef.current = null;
7960
9556
  setSiblingHintRect(null);
9557
+ setSiblingHintRects(collectNavigationItemSiblingHintRects(anchor));
7961
9558
  setIsItemDragging(false);
7962
9559
  const { key, disabled } = getNavigationItemReorderState(anchor);
7963
9560
  setReorderHrefKey(key);
7964
9561
  setReorderDragDisabled(disabled);
9562
+ setIsFooterFrameSelection(false);
7965
9563
  setToolbarVariant("link-action");
7966
9564
  setToolbarRect(anchor.getBoundingClientRect());
7967
9565
  setToolbarShowEditLink(false);
7968
9566
  setActiveCommands(/* @__PURE__ */ new Set());
7969
9567
  }, [deactivate, markSelected]);
7970
- const selectFrame = useCallback3((el) => {
9568
+ const selectFrame = useCallback4((el) => {
7971
9569
  if (!isNavigationContainer(el)) return;
7972
9570
  if (activeElRef.current) deactivate();
9571
+ const isFooterColumn = el.hasAttribute("data-ohw-footer-col") || Boolean(el.closest("footer") && isInferredFooterGroup(el));
7973
9572
  selectedElRef.current = el;
9573
+ selectedHrefKeyRef.current = null;
9574
+ selectedFooterColAttrRef.current = isFooterColumn ? el.getAttribute("data-ohw-footer-col") ?? String(listFooterColumns().indexOf(el)) : null;
7974
9575
  markSelected(el);
7975
9576
  setSelectedIsCta(false);
7976
9577
  clearHrefKeyHover(el);
@@ -7980,18 +9581,24 @@ function OhhwellsBridge() {
7980
9581
  hoveredItemElRef.current = null;
7981
9582
  siblingHintElRef.current = null;
7982
9583
  setSiblingHintRect(null);
9584
+ setSiblingHintRects(
9585
+ isFooterColumn ? listFooterColumns().filter((column) => column !== el).map((column) => column.getBoundingClientRect()) : []
9586
+ );
7983
9587
  setIsItemDragging(false);
7984
9588
  setReorderHrefKey(null);
7985
9589
  setReorderDragDisabled(false);
9590
+ setIsFooterFrameSelection(isFooterColumn);
7986
9591
  setToolbarVariant("select-frame");
7987
9592
  setToolbarRect(el.getBoundingClientRect());
7988
9593
  setToolbarShowEditLink(false);
7989
9594
  setActiveCommands(/* @__PURE__ */ new Set());
7990
9595
  }, [deactivate, markSelected]);
7991
- const activate = useCallback3((el, options) => {
9596
+ const activate = useCallback4((el, options) => {
7992
9597
  if (activeElRef.current === el) return;
7993
9598
  clearSelectedAttr();
7994
9599
  selectedElRef.current = null;
9600
+ selectedHrefKeyRef.current = null;
9601
+ selectedFooterColAttrRef.current = null;
7995
9602
  setSelectedIsCta(false);
7996
9603
  deactivate();
7997
9604
  if (hoveredImageRef.current) {
@@ -8010,7 +9617,11 @@ function OhhwellsBridge() {
8010
9617
  originalContentRef.current = el.innerHTML;
8011
9618
  el.focus({ preventScroll: true });
8012
9619
  if (options?.caretX !== void 0 && options?.caretY !== void 0) {
8013
- placeCaretAtPoint(el, options.caretX, options.caretY);
9620
+ const { caretX, caretY } = options;
9621
+ placeCaretAtPoint(el, caretX, caretY);
9622
+ requestAnimationFrame(() => {
9623
+ if (activeElRef.current === el) placeCaretAtPoint(el, caretX, caretY);
9624
+ });
8014
9625
  } else {
8015
9626
  const selection = window.getSelection();
8016
9627
  if (selection && selection.rangeCount === 0) {
@@ -8081,6 +9692,7 @@ function OhhwellsBridge() {
8081
9692
  applyLinkByKey(key, val);
8082
9693
  }
8083
9694
  reconcileNavbarItemsFromContent(content);
9695
+ reconcileFooterOrderFromContent(content);
8084
9696
  enforceLinkHrefs();
8085
9697
  initSectionsFromContent(content, true);
8086
9698
  sectionsLoadedRef.current = true;
@@ -8109,7 +9721,7 @@ function OhhwellsBridge() {
8109
9721
  cancelled = true;
8110
9722
  };
8111
9723
  }, [subdomain, isEditMode]);
8112
- useEffect7(() => {
9724
+ useEffect8(() => {
8113
9725
  if (!subdomain || isEditMode) return;
8114
9726
  let debounceTimer = null;
8115
9727
  let observer = null;
@@ -8141,6 +9753,7 @@ function OhhwellsBridge() {
8141
9753
  applyLinkByKey(key, val);
8142
9754
  }
8143
9755
  reconcileNavbarItemsFromContent(content);
9756
+ reconcileFooterOrderFromContent(content);
8144
9757
  } finally {
8145
9758
  observer?.observe(document.body, { childList: true, subtree: true });
8146
9759
  }
@@ -8164,10 +9777,10 @@ function OhhwellsBridge() {
8164
9777
  const visible = Boolean(subdomain) && fetchState !== "done";
8165
9778
  el.style.display = visible ? "flex" : "none";
8166
9779
  }, [subdomain, fetchState]);
8167
- useEffect7(() => {
9780
+ useEffect8(() => {
8168
9781
  postToParent2({ type: "ow:navigation", path: pathname });
8169
9782
  }, [pathname, postToParent2]);
8170
- useEffect7(() => {
9783
+ useEffect8(() => {
8171
9784
  if (!isEditMode) return;
8172
9785
  if (linkPopoverSessionRef.current?.intent === "add-nav") return;
8173
9786
  if (document.querySelector("[data-ohw-section-picker]")) return;
@@ -8175,7 +9788,7 @@ function OhhwellsBridge() {
8175
9788
  deselectRef.current();
8176
9789
  deactivateRef.current();
8177
9790
  }, [pathname, isEditMode]);
8178
- useEffect7(() => {
9791
+ useEffect8(() => {
8179
9792
  const contentForNav = () => {
8180
9793
  if (isEditMode) return editContentRef.current;
8181
9794
  if (!subdomain) return {};
@@ -8187,6 +9800,7 @@ function OhhwellsBridge() {
8187
9800
  const roots = observeRoots();
8188
9801
  if (roots.length === 0) {
8189
9802
  reconcileNavbarItemsFromContent(contentForNav());
9803
+ reconcileFooterOrderFromContent(contentForNav());
8190
9804
  if (isEditMode) syncNavigationDragCursorAttrs();
8191
9805
  return;
8192
9806
  }
@@ -8199,11 +9813,26 @@ function OhhwellsBridge() {
8199
9813
  }
8200
9814
  };
8201
9815
  const run = () => {
8202
- if (applying) return;
9816
+ if (footerDragRef.current || navDragRef.current || applying) return;
8203
9817
  applying = true;
8204
9818
  observer?.disconnect();
8205
9819
  try {
8206
- reconcileNavbarItemsFromContent(contentForNav());
9820
+ const content = contentForNav();
9821
+ reconcileNavbarItemsFromContent(content);
9822
+ reconcileFooterOrderFromContent(content);
9823
+ document.querySelectorAll("footer [data-ohw-href-key]").forEach((el) => {
9824
+ if (isFooterHrefKey(el.getAttribute("data-ohw-href-key"))) {
9825
+ disableNativeHrefDrag(el);
9826
+ }
9827
+ });
9828
+ document.querySelectorAll(
9829
+ "nav [data-ohw-href-key], [data-ohw-nav-container] [data-ohw-href-key], [data-ohw-nav-drawer] [data-ohw-href-key]"
9830
+ ).forEach((el) => {
9831
+ if (isNavbarHrefKey(el.getAttribute("data-ohw-href-key"))) {
9832
+ disableNativeHrefDrag(el);
9833
+ }
9834
+ });
9835
+ resyncSelectedNavigationItem();
8207
9836
  if (isEditMode) syncNavigationDragCursorAttrs();
8208
9837
  } finally {
8209
9838
  applying = false;
@@ -8223,8 +9852,8 @@ function OhhwellsBridge() {
8223
9852
  if (rafId != null) cancelAnimationFrame(rafId);
8224
9853
  observer?.disconnect();
8225
9854
  };
8226
- }, [isEditMode, pathname, subdomain, fetchState]);
8227
- useEffect7(() => {
9855
+ }, [isEditMode, pathname, subdomain, fetchState, resyncSelectedNavigationItem]);
9856
+ useEffect8(() => {
8228
9857
  if (!isEditMode) return;
8229
9858
  const measure = () => {
8230
9859
  const h = document.body.scrollHeight;
@@ -8248,7 +9877,7 @@ function OhhwellsBridge() {
8248
9877
  window.removeEventListener("resize", handleResize);
8249
9878
  };
8250
9879
  }, [pathname, isEditMode, postToParent2]);
8251
- useEffect7(() => {
9880
+ useEffect8(() => {
8252
9881
  if (!subdomainFromQuery || isEditMode) return;
8253
9882
  const handleClick = (e) => {
8254
9883
  const anchor = e.target.closest("a");
@@ -8264,7 +9893,7 @@ function OhhwellsBridge() {
8264
9893
  document.addEventListener("click", handleClick, true);
8265
9894
  return () => document.removeEventListener("click", handleClick, true);
8266
9895
  }, [subdomainFromQuery, isEditMode, router]);
8267
- useEffect7(() => {
9896
+ useEffect8(() => {
8268
9897
  if (!isEditMode) {
8269
9898
  editStylesRef.current?.base.remove();
8270
9899
  editStylesRef.current?.forceHover.remove();
@@ -8326,6 +9955,21 @@ function OhhwellsBridge() {
8326
9955
  outline: none !important;
8327
9956
  outline-offset: 0 !important;
8328
9957
  }
9958
+ /* Editor chrome owns selection \u2014 suppress native focus rings on nav/footer items. */
9959
+ nav [data-ohw-href-key]:focus,
9960
+ nav [data-ohw-href-key]:focus-visible,
9961
+ nav [data-ohw-href-key]:focus-within,
9962
+ footer [data-ohw-href-key]:focus,
9963
+ footer [data-ohw-href-key]:focus-visible,
9964
+ footer [data-ohw-href-key]:focus-within,
9965
+ nav [data-ohw-href-key] [data-ohw-editable]:focus,
9966
+ nav [data-ohw-href-key] [data-ohw-editable]:focus-visible,
9967
+ footer [data-ohw-href-key] [data-ohw-editable]:focus,
9968
+ footer [data-ohw-href-key] [data-ohw-editable]:focus-visible {
9969
+ outline: none !important;
9970
+ outline-offset: 0 !important;
9971
+ box-shadow: none !important;
9972
+ }
8329
9973
  /* Text edit wins over grab/default (must beat [data-ohw-can-drag] *). */
8330
9974
  [data-ohw-editing],
8331
9975
  [data-ohw-editing] *,
@@ -8367,12 +10011,39 @@ function OhhwellsBridge() {
8367
10011
  syncNavigationDragCursorAttrs();
8368
10012
  refreshStateRules();
8369
10013
  const handleClick = (e) => {
10014
+ if (suppressNextClickRef.current || Date.now() < suppressClickUntilRef.current) {
10015
+ suppressNextClickRef.current = false;
10016
+ e.preventDefault();
10017
+ e.stopPropagation();
10018
+ return;
10019
+ }
8370
10020
  const target = e.target;
8371
10021
  if (target.closest("[data-ohw-toolbar]")) return;
8372
10022
  if (target.closest("[data-ohw-state-toggle]")) return;
8373
10023
  if (target.closest("[data-ohw-max-badge]")) return;
8374
10024
  if (isInsideLinkEditor(target)) return;
8375
10025
  if (target.closest('[data-ohw-edit-chrome], [data-ohw-item-interaction], [data-ohw-drag-handle-container], [data-slot="drag-handle"]')) {
10026
+ if (target.closest("[data-ohw-item-drag-surface]")) {
10027
+ e.preventDefault();
10028
+ e.stopPropagation();
10029
+ const selected = selectedElRef.current;
10030
+ if (selected && isNavigationItem(selected)) {
10031
+ const editable2 = selected.querySelector('[data-ohw-editable="text"]') ?? selected.querySelector("[data-ohw-editable]");
10032
+ if (editable2) {
10033
+ activateRef.current(editable2, {
10034
+ caretX: e.clientX,
10035
+ caretY: e.clientY
10036
+ });
10037
+ }
10038
+ } else if (selected?.hasAttribute("data-ohw-footer-col")) {
10039
+ const link = listFooterLinksInColumn(selected).find((el) => {
10040
+ const r2 = el.getBoundingClientRect();
10041
+ return e.clientX >= r2.left && e.clientX <= r2.right && e.clientY >= r2.top && e.clientY <= r2.bottom;
10042
+ });
10043
+ if (link) selectRef.current(link);
10044
+ }
10045
+ return;
10046
+ }
8376
10047
  const navFromChrome = resolveNavContainerSelectionTarget(target, e.clientX, e.clientY);
8377
10048
  if (navFromChrome) {
8378
10049
  e.preventDefault();
@@ -8451,10 +10122,26 @@ function OhhwellsBridge() {
8451
10122
  if (hrefAnchor) {
8452
10123
  e.preventDefault();
8453
10124
  e.stopPropagation();
8454
- if (selectedElRef.current === hrefAnchor) return;
10125
+ if (selectedElRef.current === hrefAnchor) {
10126
+ const textEditable = hrefAnchor.querySelector('[data-ohw-editable="text"]') ?? hrefAnchor.querySelector("[data-ohw-editable]");
10127
+ if (textEditable) {
10128
+ activateRef.current(textEditable, {
10129
+ caretX: e.clientX,
10130
+ caretY: e.clientY
10131
+ });
10132
+ }
10133
+ return;
10134
+ }
8455
10135
  selectRef.current(hrefAnchor);
8456
10136
  return;
8457
10137
  }
10138
+ const footerColClick = target.closest("[data-ohw-footer-col]");
10139
+ if (footerColClick) {
10140
+ e.preventDefault();
10141
+ e.stopPropagation();
10142
+ selectFrameRef.current(footerColClick);
10143
+ return;
10144
+ }
8458
10145
  const navContainerToSelect = resolveNavContainerSelectionTarget(target, e.clientX, e.clientY);
8459
10146
  if (navContainerToSelect) {
8460
10147
  e.preventDefault();
@@ -8528,16 +10215,23 @@ function OhhwellsBridge() {
8528
10215
  const handleMouseOver = (e) => {
8529
10216
  if (document.documentElement.hasAttribute("data-ohw-section-picking")) return;
8530
10217
  const target = e.target;
10218
+ if (linkPopoverOpenRef.current) {
10219
+ hoveredItemElRef.current = null;
10220
+ setHoveredItemRect(null);
10221
+ hoveredNavContainerRef.current = null;
10222
+ setHoveredNavContainerRect(null);
10223
+ return;
10224
+ }
8531
10225
  if (toolbarVariantRef.current !== "select-frame") {
8532
- const frameContainer = target.closest("[data-ohw-nav-container]") ?? target.closest("[data-ohw-footer-column]");
8533
- if (frameContainer && !getNavigationItemAnchor(target)) {
8534
- hoveredNavContainerRef.current = frameContainer;
8535
- setHoveredNavContainerRect(frameContainer.getBoundingClientRect());
10226
+ const navContainer = target.closest("[data-ohw-nav-container]");
10227
+ if (navContainer && !getNavigationItemAnchor(target)) {
10228
+ hoveredNavContainerRef.current = navContainer;
10229
+ setHoveredNavContainerRect(navContainer.getBoundingClientRect());
8536
10230
  hoveredItemElRef.current = null;
8537
10231
  setHoveredItemRect(null);
8538
10232
  return;
8539
10233
  }
8540
- if (!target.closest("[data-ohw-nav-container], [data-ohw-footer-column]")) {
10234
+ if (!target.closest("[data-ohw-nav-container]")) {
8541
10235
  hoveredNavContainerRef.current = null;
8542
10236
  setHoveredNavContainerRect(null);
8543
10237
  }
@@ -8547,12 +10241,21 @@ function OhhwellsBridge() {
8547
10241
  hoveredNavContainerRef.current = null;
8548
10242
  setHoveredNavContainerRect(null);
8549
10243
  const selected2 = selectedElRef.current;
8550
- if (selected2 === navAnchor || selected2?.contains(navAnchor)) return;
10244
+ if (selected2 === navAnchor) return;
8551
10245
  clearHrefKeyHover(navAnchor);
8552
10246
  hoveredItemElRef.current = navAnchor;
8553
10247
  setHoveredItemRect(navAnchor.getBoundingClientRect());
8554
10248
  return;
8555
10249
  }
10250
+ const footerCol = target.closest("[data-ohw-footer-col]") ?? target.closest("[data-ohw-footer-column]");
10251
+ if (footerCol) {
10252
+ hoveredNavContainerRef.current = null;
10253
+ setHoveredNavContainerRect(null);
10254
+ if (selectedElRef.current === footerCol) return;
10255
+ hoveredItemElRef.current = footerCol;
10256
+ setHoveredItemRect(footerCol.getBoundingClientRect());
10257
+ return;
10258
+ }
8556
10259
  const editable = target.closest("[data-ohw-editable]");
8557
10260
  if (!editable) return;
8558
10261
  const selected = selectedElRef.current;
@@ -8570,14 +10273,25 @@ function OhhwellsBridge() {
8570
10273
  };
8571
10274
  const handleMouseOut = (e) => {
8572
10275
  const target = e.target;
8573
- if (target.closest("[data-ohw-nav-container], [data-ohw-footer-column]")) {
10276
+ if (target.closest("[data-ohw-nav-container], [data-ohw-footer-col], [data-ohw-footer-column]")) {
8574
10277
  const related2 = e.relatedTarget instanceof Element ? e.relatedTarget : null;
8575
- if (related2?.closest("[data-ohw-nav-container], [data-ohw-footer-column], [data-ohw-navbar-container-chrome], [data-ohw-navbar-add-button]")) {
10278
+ if (related2?.closest("[data-ohw-nav-container], [data-ohw-footer-col], [data-ohw-footer-column], [data-ohw-navbar-container-chrome], [data-ohw-navbar-add-button]")) {
8576
10279
  return;
8577
10280
  }
8578
10281
  hoveredNavContainerRef.current = null;
8579
10282
  setHoveredNavContainerRect(null);
8580
10283
  }
10284
+ const footerCol = target.closest("[data-ohw-footer-col]") ?? target.closest("[data-ohw-footer-column]");
10285
+ if (footerCol && hoveredItemElRef.current === footerCol) {
10286
+ const related2 = e.relatedTarget instanceof Element ? e.relatedTarget : null;
10287
+ if (related2?.closest("[data-ohw-drag-handle-container], [data-ohw-item-interaction]")) return;
10288
+ if (related2?.closest("[data-ohw-footer-col], [data-ohw-footer-column]") === footerCol) {
10289
+ return;
10290
+ }
10291
+ hoveredItemElRef.current = null;
10292
+ setHoveredItemRect(null);
10293
+ return;
10294
+ }
8581
10295
  const navAnchor = getNavigationItemAnchor(target);
8582
10296
  if (navAnchor) {
8583
10297
  const related2 = e.relatedTarget instanceof Element ? e.relatedTarget : null;
@@ -8711,14 +10425,12 @@ function OhhwellsBridge() {
8711
10425
  if (toolbarVariantRef.current === "select-frame") {
8712
10426
  hoveredNavContainerRef.current = null;
8713
10427
  setHoveredNavContainerRect(null);
8714
- return;
8715
10428
  }
8716
- const frameContainers = [
8717
- ...document.querySelectorAll("[data-ohw-nav-container]"),
8718
- ...document.querySelectorAll("[data-ohw-footer-column]")
8719
- ];
8720
- let overFrame = null;
8721
- for (const container of frameContainers) {
10429
+ const navContainers = Array.from(
10430
+ document.querySelectorAll("[data-ohw-nav-container]")
10431
+ );
10432
+ const footerColumns = listFooterColumns();
10433
+ for (const container of [...navContainers, ...footerColumns]) {
8722
10434
  const containerRect = container.getBoundingClientRect();
8723
10435
  const overContainer = x >= containerRect.left && x <= containerRect.right && y >= containerRect.top && y <= containerRect.bottom;
8724
10436
  if (!overContainer) continue;
@@ -8733,25 +10445,55 @@ function OhhwellsBridge() {
8733
10445
  hoveredNavContainerRef.current = null;
8734
10446
  setHoveredNavContainerRect(null);
8735
10447
  const selected = selectedElRef.current;
8736
- if (selected !== navItemHit && !selected?.contains(navItemHit)) {
10448
+ if (selected !== navItemHit) {
8737
10449
  clearHrefKeyHover(navItemHit);
8738
10450
  hoveredItemElRef.current = navItemHit;
8739
10451
  setHoveredItemRect(navItemHit.getBoundingClientRect());
10452
+ } else if (hoveredItemElRef.current === navItemHit) {
10453
+ hoveredItemElRef.current = null;
10454
+ setHoveredItemRect(null);
8740
10455
  }
8741
10456
  return;
8742
10457
  }
8743
- overFrame = container;
8744
- break;
8745
10458
  }
8746
- if (!overFrame) {
10459
+ if (toolbarVariantRef.current !== "select-frame") {
10460
+ for (const container of navContainers) {
10461
+ const containerRect = container.getBoundingClientRect();
10462
+ const overContainer = x >= containerRect.left && x <= containerRect.right && y >= containerRect.top && y <= containerRect.bottom;
10463
+ if (!overContainer) continue;
10464
+ if (isPointOverNavItem(container, x, y)) continue;
10465
+ hoveredNavContainerRef.current = container;
10466
+ setHoveredNavContainerRect(container.getBoundingClientRect());
10467
+ hoveredItemElRef.current = null;
10468
+ setHoveredItemRect(null);
10469
+ return;
10470
+ }
10471
+ hoveredNavContainerRef.current = null;
10472
+ setHoveredNavContainerRect(null);
10473
+ } else {
8747
10474
  hoveredNavContainerRef.current = null;
8748
10475
  setHoveredNavContainerRect(null);
10476
+ }
10477
+ for (const column of footerColumns) {
10478
+ const containerRect = column.getBoundingClientRect();
10479
+ const overContainer = x >= containerRect.left && x <= containerRect.right && y >= containerRect.top && y <= containerRect.bottom;
10480
+ if (!overContainer) continue;
10481
+ if (isPointOverNavItem(column, x, y)) continue;
10482
+ if (selectedElRef.current === column) {
10483
+ if (hoveredItemElRef.current === column) {
10484
+ hoveredItemElRef.current = null;
10485
+ setHoveredItemRect(null);
10486
+ }
10487
+ return;
10488
+ }
10489
+ hoveredItemElRef.current = column;
10490
+ setHoveredItemRect(column.getBoundingClientRect());
8749
10491
  return;
8750
10492
  }
8751
- hoveredNavContainerRef.current = overFrame;
8752
- setHoveredNavContainerRect(overFrame.getBoundingClientRect());
8753
- hoveredItemElRef.current = null;
8754
- setHoveredItemRect(null);
10493
+ if (hoveredItemElRef.current?.hasAttribute("data-ohw-footer-col") || hoveredItemElRef.current?.hasAttribute("data-ohw-footer-column")) {
10494
+ hoveredItemElRef.current = null;
10495
+ setHoveredItemRect(null);
10496
+ }
8755
10497
  };
8756
10498
  const probeImageAt = (clientX, clientY, isDragOver = false, fromParentViewport = false) => {
8757
10499
  if (linkPopoverOpenRef.current) {
@@ -8772,6 +10514,10 @@ function OhhwellsBridge() {
8772
10514
  }
8773
10515
  hoveredNavContainerRef.current = null;
8774
10516
  setHoveredNavContainerRect(null);
10517
+ if (hoveredItemElRef.current?.hasAttribute("data-ohw-footer-col") || hoveredItemElRef.current?.hasAttribute("data-ohw-footer-column")) {
10518
+ hoveredItemElRef.current = null;
10519
+ setHoveredItemRect(null);
10520
+ }
8775
10521
  const toggleEl = document.querySelector("[data-ohw-state-toggle]");
8776
10522
  if (toggleEl) {
8777
10523
  const tr = toggleEl.getBoundingClientRect();
@@ -9019,6 +10765,15 @@ function OhhwellsBridge() {
9019
10765
  probeHoverCardsAt(clientX, clientY);
9020
10766
  };
9021
10767
  const handleDragOver = (e) => {
10768
+ const footerSession = footerDragRef.current;
10769
+ if (footerSession) {
10770
+ e.preventDefault();
10771
+ if (e.dataTransfer) e.dataTransfer.dropEffect = "move";
10772
+ const slot = footerSession.kind === "link" && footerSession.hrefKey ? hitTestLinkDropSlot(e.clientX, e.clientY, footerSession.hrefKey) : hitTestColumnDropSlot(e.clientX, e.clientY);
10773
+ refreshFooterDragVisualsRef.current(footerSession, slot, e.clientX, e.clientY);
10774
+ return;
10775
+ }
10776
+ if (onNavDragOver(e)) return;
9022
10777
  e.preventDefault();
9023
10778
  const el = findImageAtPoint(e.clientX, e.clientY, false);
9024
10779
  if (!el) {
@@ -9382,6 +11137,26 @@ function OhhwellsBridge() {
9382
11137
  if (siblingHintElRef.current) {
9383
11138
  setSiblingHintRect(siblingHintElRef.current.getBoundingClientRect());
9384
11139
  }
11140
+ const selected = selectedElRef.current;
11141
+ if (selected && !footerDragRef.current && !navDragRef.current) {
11142
+ if (selected.hasAttribute("data-ohw-footer-col") || Boolean(selected.closest("footer") && isInferredFooterGroup(selected))) {
11143
+ setSiblingHintRects(
11144
+ listFooterColumns().filter((column) => column !== selected).map((column) => column.getBoundingClientRect())
11145
+ );
11146
+ } else if (isNavigationItem(selected)) {
11147
+ setSiblingHintRects(collectNavigationItemSiblingHintRects(selected));
11148
+ }
11149
+ }
11150
+ if (footerDragRef.current) {
11151
+ const session = footerDragRef.current;
11152
+ const slot = session.kind === "link" && session.hrefKey ? hitTestLinkDropSlot(session.lastClientX, session.lastClientY, session.hrefKey) : hitTestColumnDropSlot(session.lastClientX, session.lastClientY);
11153
+ refreshFooterDragVisualsRef.current(session, slot, session.lastClientX, session.lastClientY);
11154
+ }
11155
+ if (navDragRef.current) {
11156
+ const session = navDragRef.current;
11157
+ const slot = hitTestNavDropSlot(session.lastClientX, session.lastClientY, session.hrefKey);
11158
+ refreshNavDragVisualsRef.current(session, slot, session.lastClientX, session.lastClientY);
11159
+ }
9385
11160
  if (hoveredImageRef.current) {
9386
11161
  const el = hoveredImageRef.current;
9387
11162
  const r2 = el.getBoundingClientRect();
@@ -9392,7 +11167,7 @@ function OhhwellsBridge() {
9392
11167
  };
9393
11168
  const handleSave = (e) => {
9394
11169
  if (e.data?.type !== "ow:save") return;
9395
- const nodes = collectEditableNodes();
11170
+ const nodes = collectEditableNodes(editContentRef.current);
9396
11171
  const tracker = document.querySelector("[data-ohw-sections-tracker]");
9397
11172
  if (tracker?.textContent) nodes.push({ key: "__ohw_sections", type: "sections", text: tracker.textContent });
9398
11173
  postToParentRef.current({ type: "ow:save-result", nodes });
@@ -9615,7 +11390,7 @@ function OhhwellsBridge() {
9615
11390
  }
9616
11391
  }
9617
11392
  const footerColumn = findHoveredNavOrFooterContainer(clientX, clientY);
9618
- if (footerColumn?.hasAttribute("data-ohw-footer-column")) {
11393
+ if (footerColumn?.hasAttribute("data-ohw-footer-col") || footerColumn?.hasAttribute("data-ohw-footer-column")) {
9619
11394
  selectFrameRef.current(footerColumn);
9620
11395
  return;
9621
11396
  }
@@ -9690,7 +11465,161 @@ function OhhwellsBridge() {
9690
11465
  if (imageShowTimerRef.current) clearTimeout(imageShowTimerRef.current);
9691
11466
  };
9692
11467
  }, [isEditMode, refreshStateRules]);
9693
- useEffect7(() => {
11468
+ useEffect8(() => {
11469
+ if (!isEditMode) return;
11470
+ const THRESHOLD = 10;
11471
+ const resolveWasSelected = (el) => {
11472
+ if (selectedElRef.current === el) return true;
11473
+ const activeAnchor = activeElRef.current ? getNavigationItemAnchor(activeElRef.current) : null;
11474
+ return activeAnchor === el;
11475
+ };
11476
+ const onPointerDown = (e) => {
11477
+ if (e.button !== 0) return;
11478
+ if (suppressNextClickRef.current && Date.now() >= suppressClickUntilRef.current && !footerDragRef.current && !footerPointerDragRef.current?.started) {
11479
+ suppressNextClickRef.current = false;
11480
+ }
11481
+ if (footerDragRef.current) return;
11482
+ const target = e.target;
11483
+ if (!target) return;
11484
+ 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]')) {
11485
+ return;
11486
+ }
11487
+ if (target.closest("[data-ohw-item-drag-surface]")) return;
11488
+ const anchor = getNavigationItemAnchor(target);
11489
+ const hrefKey = anchor?.getAttribute("data-ohw-href-key") ?? null;
11490
+ if (anchor && isFooterHrefKey(hrefKey)) {
11491
+ armFooterPressDrag();
11492
+ footerPointerDragRef.current = {
11493
+ el: anchor,
11494
+ kind: "link",
11495
+ startX: e.clientX,
11496
+ startY: e.clientY,
11497
+ pointerId: e.pointerId,
11498
+ wasSelected: resolveWasSelected(anchor),
11499
+ started: false
11500
+ };
11501
+ return;
11502
+ }
11503
+ const col = target.closest("[data-ohw-footer-col]");
11504
+ if (col && !getNavigationItemAnchor(target)) {
11505
+ armFooterPressDrag();
11506
+ footerPointerDragRef.current = {
11507
+ el: col,
11508
+ kind: "column",
11509
+ startX: e.clientX,
11510
+ startY: e.clientY,
11511
+ pointerId: e.pointerId,
11512
+ wasSelected: resolveWasSelected(col),
11513
+ started: false
11514
+ };
11515
+ }
11516
+ };
11517
+ const onPointerMove = (e) => {
11518
+ const pending = footerPointerDragRef.current;
11519
+ if (!pending) return;
11520
+ if (pending.started) {
11521
+ e.preventDefault();
11522
+ clearTextSelection();
11523
+ const session = footerDragRef.current;
11524
+ if (!session) return;
11525
+ const slot = session.kind === "link" && session.hrefKey ? hitTestLinkDropSlot(e.clientX, e.clientY, session.hrefKey) : hitTestColumnDropSlot(e.clientX, e.clientY);
11526
+ refreshFooterDragVisualsRef.current(session, slot, e.clientX, e.clientY);
11527
+ return;
11528
+ }
11529
+ const dx = e.clientX - pending.startX;
11530
+ const dy = e.clientY - pending.startY;
11531
+ if (dx * dx + dy * dy < THRESHOLD * THRESHOLD) return;
11532
+ e.preventDefault();
11533
+ pending.started = true;
11534
+ clearTextSelection();
11535
+ try {
11536
+ document.body.setPointerCapture(pending.pointerId);
11537
+ } catch {
11538
+ }
11539
+ if (linkPopoverOpenRef.current) {
11540
+ setLinkPopoverRef.current(null);
11541
+ }
11542
+ if (activeElRef.current) {
11543
+ deactivateRef.current();
11544
+ }
11545
+ if (pending.kind === "link") {
11546
+ const key = pending.el.getAttribute("data-ohw-href-key");
11547
+ if (!key) return;
11548
+ const column = findFooterColumnForLink(pending.el);
11549
+ const columns2 = listFooterColumns();
11550
+ beginFooterDragRef.current({
11551
+ kind: "link",
11552
+ hrefKey: key,
11553
+ columnEl: column,
11554
+ sourceColumnIndex: column ? columns2.indexOf(column) : 0,
11555
+ wasSelected: pending.wasSelected,
11556
+ draggedEl: pending.el,
11557
+ lastClientX: e.clientX,
11558
+ lastClientY: e.clientY,
11559
+ activeSlot: null
11560
+ });
11561
+ return;
11562
+ }
11563
+ const columns = listFooterColumns();
11564
+ const idx = columns.indexOf(pending.el);
11565
+ if (idx < 0) return;
11566
+ beginFooterDragRef.current({
11567
+ kind: "column",
11568
+ hrefKey: null,
11569
+ columnEl: pending.el,
11570
+ sourceColumnIndex: idx,
11571
+ wasSelected: pending.wasSelected,
11572
+ draggedEl: pending.el,
11573
+ lastClientX: e.clientX,
11574
+ lastClientY: e.clientY,
11575
+ activeSlot: null
11576
+ });
11577
+ };
11578
+ const endPointerDrag = (e) => {
11579
+ const pending = footerPointerDragRef.current;
11580
+ footerPointerDragRef.current = null;
11581
+ try {
11582
+ if (document.body.hasPointerCapture(e.pointerId)) {
11583
+ document.body.releasePointerCapture(e.pointerId);
11584
+ }
11585
+ } catch {
11586
+ }
11587
+ if (!pending?.started) {
11588
+ unlockFooterDragInteraction();
11589
+ return;
11590
+ }
11591
+ suppressNextClickRef.current = true;
11592
+ suppressClickUntilRef.current = Date.now() + 500;
11593
+ commitFooterDragRef.current(e.clientX, e.clientY);
11594
+ };
11595
+ const blockSelectStart = (e) => {
11596
+ if (footerDragRef.current || footerPointerDragRef.current?.started || document.documentElement.hasAttribute("data-ohw-footer-press-drag")) {
11597
+ e.preventDefault();
11598
+ }
11599
+ };
11600
+ const onDragEnd = (_e) => {
11601
+ if (!footerDragRef.current) return;
11602
+ suppressNextClickRef.current = true;
11603
+ suppressClickUntilRef.current = Date.now() + 500;
11604
+ commitFooterDragRef.current();
11605
+ };
11606
+ document.addEventListener("pointerdown", onPointerDown, true);
11607
+ document.addEventListener("pointermove", onPointerMove, true);
11608
+ document.addEventListener("pointerup", endPointerDrag, true);
11609
+ document.addEventListener("pointercancel", endPointerDrag, true);
11610
+ document.addEventListener("selectstart", blockSelectStart, true);
11611
+ document.addEventListener("dragend", onDragEnd, true);
11612
+ return () => {
11613
+ document.removeEventListener("pointerdown", onPointerDown, true);
11614
+ document.removeEventListener("pointermove", onPointerMove, true);
11615
+ document.removeEventListener("pointerup", endPointerDrag, true);
11616
+ document.removeEventListener("pointercancel", endPointerDrag, true);
11617
+ document.removeEventListener("selectstart", blockSelectStart, true);
11618
+ document.removeEventListener("dragend", onDragEnd, true);
11619
+ unlockFooterDragInteraction();
11620
+ };
11621
+ }, [isEditMode]);
11622
+ useEffect8(() => {
9694
11623
  const handler = (e) => {
9695
11624
  if (e.data?.type !== "ow:request-schedule-config") return;
9696
11625
  const insertAfterVal = e.data.insertAfter;
@@ -9706,7 +11635,7 @@ function OhhwellsBridge() {
9706
11635
  window.addEventListener("message", handler);
9707
11636
  return () => window.removeEventListener("message", handler);
9708
11637
  }, [processConfigRequest]);
9709
- useEffect7(() => {
11638
+ useEffect8(() => {
9710
11639
  if (!isEditMode) return;
9711
11640
  document.querySelectorAll("[data-ohw-active-state]").forEach((el) => {
9712
11641
  el.removeAttribute("data-ohw-active-state");
@@ -9727,7 +11656,13 @@ function OhhwellsBridge() {
9727
11656
  const next = { ...prev, [pathKey]: sections };
9728
11657
  return shouldUseDevFixtures() ? { ...DEV_SECTIONS_BY_PATH, ...next } : next;
9729
11658
  });
9730
- postToParent2({ type: "ow:ready", version: "1", path: pathname, nodes: collectEditableNodes(), sections });
11659
+ postToParent2({
11660
+ type: "ow:ready",
11661
+ version: "1",
11662
+ path: pathname,
11663
+ nodes: collectEditableNodes(editContentRef.current),
11664
+ sections
11665
+ });
9731
11666
  postToParent2({ type: "ow:request-site-pages" });
9732
11667
  }, 150);
9733
11668
  return () => {
@@ -9735,19 +11670,19 @@ function OhhwellsBridge() {
9735
11670
  clearTimeout(timer);
9736
11671
  };
9737
11672
  }, [pathname, isEditMode, refreshStateRules, postToParent2]);
9738
- useEffect7(() => {
11673
+ useEffect8(() => {
9739
11674
  scrollToHashSectionWhenReady();
9740
11675
  const onHashChange = () => scrollToHashSectionWhenReady();
9741
11676
  window.addEventListener("hashchange", onHashChange);
9742
11677
  return () => window.removeEventListener("hashchange", onHashChange);
9743
11678
  }, [pathname]);
9744
- const handleCommand = useCallback3((cmd) => {
11679
+ const handleCommand = useCallback4((cmd) => {
9745
11680
  document.execCommand(cmd, false);
9746
11681
  activeElRef.current?.focus();
9747
11682
  if (activeElRef.current) setToolbarRect(getEditMeasureEl(activeElRef.current).getBoundingClientRect());
9748
11683
  refreshActiveCommandsRef.current();
9749
11684
  }, []);
9750
- const handleStateChange = useCallback3((state) => {
11685
+ const handleStateChange = useCallback4((state) => {
9751
11686
  if (!activeStateElRef.current) return;
9752
11687
  const el = activeStateElRef.current;
9753
11688
  if (state === "Default") {
@@ -9760,11 +11695,11 @@ function OhhwellsBridge() {
9760
11695
  }
9761
11696
  setToggleState((prev) => prev ? { ...prev, activeState: state } : null);
9762
11697
  }, [deactivate]);
9763
- const closeLinkPopover = useCallback3(() => {
11698
+ const closeLinkPopover = useCallback4(() => {
9764
11699
  addNavAfterAnchorRef.current = null;
9765
11700
  setLinkPopover(null);
9766
11701
  }, []);
9767
- const openLinkPopoverForActive = useCallback3(() => {
11702
+ const openLinkPopoverForActive = useCallback4(() => {
9768
11703
  const hrefCtx = getHrefKeyFromElement(activeElRef.current);
9769
11704
  if (!hrefCtx) return;
9770
11705
  bumpLinkPopoverGrace();
@@ -9775,7 +11710,7 @@ function OhhwellsBridge() {
9775
11710
  });
9776
11711
  deactivate();
9777
11712
  }, [deactivate]);
9778
- const openLinkPopoverForSelected = useCallback3(() => {
11713
+ const openLinkPopoverForSelected = useCallback4(() => {
9779
11714
  const anchor = selectedElRef.current;
9780
11715
  if (!anchor) return;
9781
11716
  const key = anchor.getAttribute("data-ohw-href-key");
@@ -9788,7 +11723,7 @@ function OhhwellsBridge() {
9788
11723
  });
9789
11724
  deselect();
9790
11725
  }, [deselect]);
9791
- const handleLinkPopoverSubmit = useCallback3(
11726
+ const handleLinkPopoverSubmit = useCallback4(
9792
11727
  (target) => {
9793
11728
  const session = linkPopoverSessionRef.current;
9794
11729
  if (!session) return;
@@ -9847,13 +11782,13 @@ function OhhwellsBridge() {
9847
11782
  const showEditLink = toolbarShowEditLink;
9848
11783
  const currentSections = sectionsByPath[pathname] ?? [];
9849
11784
  linkPopoverOpenRef.current = linkPopover !== null;
9850
- const handleMediaReplace = useCallback3(
11785
+ const handleMediaReplace = useCallback4(
9851
11786
  (key) => {
9852
11787
  postToParent2({ type: "ow:image-pick", key, elementType: mediaHover?.elementType ?? "image" });
9853
11788
  },
9854
11789
  [postToParent2, mediaHover?.elementType]
9855
11790
  );
9856
- const handleMediaFadeOutComplete = useCallback3((key) => {
11791
+ const handleMediaFadeOutComplete = useCallback4((key) => {
9857
11792
  setUploadingRects((prev) => {
9858
11793
  if (!(key in prev)) return prev;
9859
11794
  const next = { ...prev };
@@ -9861,7 +11796,7 @@ function OhhwellsBridge() {
9861
11796
  return next;
9862
11797
  });
9863
11798
  }, []);
9864
- const handleVideoSettingsChange = useCallback3(
11799
+ const handleVideoSettingsChange = useCallback4(
9865
11800
  (key, settings) => {
9866
11801
  document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
9867
11802
  const video = getVideoEl(el);
@@ -9884,9 +11819,9 @@ function OhhwellsBridge() {
9884
11819
  [postToParent2]
9885
11820
  );
9886
11821
  return bridgeRoot ? createPortal2(
9887
- /* @__PURE__ */ jsxs14(Fragment5, { children: [
9888
- /* @__PURE__ */ jsx23("div", { ref: attachVisibleViewport, "data-ohw-visible-viewport": "", "aria-hidden": true }),
9889
- Object.entries(uploadingRects).map(([key, { rect, fadingOut }]) => /* @__PURE__ */ jsx23(
11822
+ /* @__PURE__ */ jsxs13(Fragment5, { children: [
11823
+ /* @__PURE__ */ jsx24("div", { ref: attachVisibleViewport, "data-ohw-visible-viewport": "", "aria-hidden": true }),
11824
+ Object.entries(uploadingRects).map(([key, { rect, fadingOut }]) => /* @__PURE__ */ jsx24(
9890
11825
  MediaOverlay,
9891
11826
  {
9892
11827
  hover: { key, rect, elementType: "image", isDragOver: false, hasTextOverlap: false },
@@ -9897,7 +11832,7 @@ function OhhwellsBridge() {
9897
11832
  },
9898
11833
  `uploading-${key}`
9899
11834
  )),
9900
- mediaHover && !(mediaHover.key in uploadingRects) && /* @__PURE__ */ jsx23(
11835
+ mediaHover && !(mediaHover.key in uploadingRects) && /* @__PURE__ */ jsx24(
9901
11836
  MediaOverlay,
9902
11837
  {
9903
11838
  hover: mediaHover,
@@ -9906,28 +11841,69 @@ function OhhwellsBridge() {
9906
11841
  onVideoSettingsChange: handleVideoSettingsChange
9907
11842
  }
9908
11843
  ),
9909
- siblingHintRect && !isItemDragging && /* @__PURE__ */ jsx23(ItemInteractionLayer, { rect: siblingHintRect, state: "sibling-hint" }),
9910
- hoveredNavContainerRect && toolbarVariant !== "select-frame" && !linkPopover && !isItemDragging && !siblingHintRect && /* @__PURE__ */ jsx23(ItemInteractionLayer, { rect: hoveredNavContainerRect, state: "hover" }),
9911
- toolbarVariant === "select-frame" && toolbarRect && !linkPopover && !isItemDragging && selectedElRef.current && isNavbarLinksContainer(selectedElRef.current) && /* @__PURE__ */ jsx23(
9912
- NavbarContainerChrome,
11844
+ siblingHintRect && !linkPopover && !isItemDragging && siblingHintRects.length === 0 && /* @__PURE__ */ jsx24(ItemInteractionLayer, { rect: siblingHintRect, state: "sibling-hint" }),
11845
+ siblingHintRects.length > 0 && !linkPopover && !isItemDragging && siblingHintRects.map((rect, i) => /* @__PURE__ */ jsx24(ItemInteractionLayer, { rect, state: "sibling-hint" }, `sibling-hint-${i}`)),
11846
+ isItemDragging && draggedItemRect && selectedElRef.current !== footerDragRef.current?.draggedEl && selectedElRef.current !== navDragRef.current?.draggedEl && /* @__PURE__ */ jsx24(ItemInteractionLayer, { rect: draggedItemRect, state: "dragging" }),
11847
+ isItemDragging && footerDropSlots.map((slot, i) => /* @__PURE__ */ jsx24(
11848
+ "div",
9913
11849
  {
9914
- rect: toolbarRect,
9915
- onAdd: handleAddTopLevelNavItem
9916
- }
9917
- ),
9918
- hoveredItemRect && !hoveredNavContainerRect && !siblingHintRect && !isItemDragging && hoveredItemElRef.current !== selectedElRef.current && /* @__PURE__ */ jsx23(ItemInteractionLayer, { rect: hoveredItemRect, state: "hover" }),
9919
- toolbarRect && (toolbarVariant === "link-action" || toolbarVariant === "select-frame") && /* @__PURE__ */ jsx23(
11850
+ className: "pointer-events-none fixed z-2147483646",
11851
+ style: {
11852
+ left: slot.left,
11853
+ top: slot.top,
11854
+ width: slot.width,
11855
+ height: slot.height
11856
+ },
11857
+ children: /* @__PURE__ */ jsx24(
11858
+ DropIndicator,
11859
+ {
11860
+ direction: slot.direction,
11861
+ state: activeFooterDropIndex === i ? "dragActive" : "dragIdle",
11862
+ className: "!h-full !w-full"
11863
+ }
11864
+ )
11865
+ },
11866
+ `footer-drop-${slot.direction}-${slot.columnIndex}-${slot.insertIndex}-${i}`
11867
+ )),
11868
+ isItemDragging && navDropSlots.map((slot, i) => /* @__PURE__ */ jsx24(
11869
+ "div",
11870
+ {
11871
+ className: "pointer-events-none fixed z-2147483646",
11872
+ style: {
11873
+ left: slot.left,
11874
+ top: slot.top,
11875
+ width: slot.width,
11876
+ height: slot.height
11877
+ },
11878
+ children: /* @__PURE__ */ jsx24(
11879
+ DropIndicator,
11880
+ {
11881
+ direction: slot.direction,
11882
+ state: activeNavDropIndex === i ? "dragActive" : "dragIdle",
11883
+ className: "!h-full !w-full"
11884
+ }
11885
+ )
11886
+ },
11887
+ `nav-drop-${slot.direction}-${slot.parentId ?? "root"}-${slot.insertIndex}-${i}`
11888
+ )),
11889
+ hoveredNavContainerRect && toolbarVariant !== "select-frame" && !linkPopover && !isItemDragging && /* @__PURE__ */ jsx24(ItemInteractionLayer, { rect: hoveredNavContainerRect, state: "hover" }),
11890
+ hoveredItemRect && !linkPopover && !hoveredNavContainerRect && !isItemDragging && hoveredItemElRef.current !== selectedElRef.current && /* @__PURE__ */ jsx24(ItemInteractionLayer, { rect: hoveredItemRect, state: "hover" }),
11891
+ toolbarVariant === "select-frame" && toolbarRect && !linkPopover && !isItemDragging && !isFooterFrameSelection && selectedElRef.current && isNavbarLinksContainer(selectedElRef.current) && /* @__PURE__ */ jsx24(NavbarContainerChrome, { rect: toolbarRect, onAdd: handleAddTopLevelNavItem }),
11892
+ toolbarRect && !linkPopover && (toolbarVariant === "link-action" || toolbarVariant === "select-frame") && /* @__PURE__ */ jsx24(
9920
11893
  ItemInteractionLayer,
9921
11894
  {
9922
- rect: toolbarRect,
11895
+ rect: isItemDragging && draggedItemRect && (footerDragRef.current?.wasSelected || navDragRef.current?.wasSelected) ? draggedItemRect : toolbarRect,
9923
11896
  elRef: glowElRef,
9924
11897
  state: isItemDragging ? "dragging" : resolveItemInteractionState(toolbarRect, parentScrollRef.current),
9925
- showHandle: toolbarVariant === "link-action" && Boolean(reorderHrefKey),
11898
+ showHandle: toolbarVariant === "link-action" && Boolean(reorderHrefKey) || toolbarVariant === "select-frame" && isFooterFrameSelection,
9926
11899
  dragDisabled: reorderDragDisabled,
9927
11900
  dragHandleLabel: reorderHrefKey ? `Reorder ${reorderHrefKey}` : "Reorder item",
9928
11901
  onDragHandleDragStart: handleItemDragStart,
9929
11902
  onDragHandleDragEnd: handleItemDragEnd,
9930
- toolbar: toolbarVariant === "link-action" && !isItemDragging ? /* @__PURE__ */ jsx23(
11903
+ onItemPointerDown: handleItemChromePointerDown,
11904
+ onItemClick: handleItemChromeClick,
11905
+ itemDragSurface: !isFooterFrameSelection,
11906
+ toolbar: toolbarVariant === "link-action" && !isItemDragging ? /* @__PURE__ */ jsx24(
9931
11907
  ItemActionToolbar,
9932
11908
  {
9933
11909
  onEditLink: openLinkPopoverForSelected,
@@ -9940,17 +11916,18 @@ function OhhwellsBridge() {
9940
11916
  ) : void 0
9941
11917
  }
9942
11918
  ),
9943
- toolbarRect && toolbarVariant === "rich-text" && /* @__PURE__ */ jsxs14(Fragment5, { children: [
9944
- /* @__PURE__ */ jsx23(
11919
+ toolbarRect && toolbarVariant === "rich-text" && !linkPopover && /* @__PURE__ */ jsxs13(Fragment5, { children: [
11920
+ /* @__PURE__ */ jsx24(
9945
11921
  EditGlowChrome,
9946
11922
  {
9947
11923
  rect: toolbarRect,
9948
11924
  elRef: glowElRef,
9949
11925
  reorderHrefKey,
9950
- dragDisabled: reorderDragDisabled
11926
+ dragDisabled: reorderDragDisabled,
11927
+ hideHandle: isItemDragging
9951
11928
  }
9952
11929
  ),
9953
- /* @__PURE__ */ jsx23(
11930
+ /* @__PURE__ */ jsx24(
9954
11931
  FloatingToolbar,
9955
11932
  {
9956
11933
  rect: toolbarRect,
@@ -9963,7 +11940,7 @@ function OhhwellsBridge() {
9963
11940
  }
9964
11941
  )
9965
11942
  ] }),
9966
- maxBadge && /* @__PURE__ */ jsxs14(
11943
+ maxBadge && /* @__PURE__ */ jsxs13(
9967
11944
  "div",
9968
11945
  {
9969
11946
  "data-ohw-max-badge": "",
@@ -9989,7 +11966,7 @@ function OhhwellsBridge() {
9989
11966
  ]
9990
11967
  }
9991
11968
  ),
9992
- toggleState && !linkPopover && /* @__PURE__ */ jsx23(
11969
+ toggleState && !linkPopover && /* @__PURE__ */ jsx24(
9993
11970
  StateToggle,
9994
11971
  {
9995
11972
  rect: toggleState.rect,
@@ -9998,32 +11975,36 @@ function OhhwellsBridge() {
9998
11975
  onStateChange: handleStateChange
9999
11976
  }
10000
11977
  ),
10001
- sectionGap && !linkPopover && /* @__PURE__ */ jsxs14(
11978
+ sectionGap && !linkPopover && /* @__PURE__ */ jsxs13(
10002
11979
  "div",
10003
11980
  {
10004
11981
  "data-ohw-section-insert-line": "",
10005
11982
  className: "fixed left-0 w-full z-2147483646 flex items-center pointer-events-none",
10006
11983
  style: { top: sectionGap.y, transform: "translateY(-50%)" },
10007
11984
  children: [
10008
- /* @__PURE__ */ jsx23("div", { className: "flex-1 bg-primary", style: { height: 3 } }),
10009
- /* @__PURE__ */ jsx23(
11985
+ /* @__PURE__ */ jsx24("div", { className: "flex-1 bg-primary", style: { height: 3 } }),
11986
+ /* @__PURE__ */ jsx24(
10010
11987
  Badge,
10011
11988
  {
10012
11989
  className: "px-8 py-1 bg-primary hover:bg-primary text-primary-foreground text-xs font-medium shrink-0 rounded-full cursor-pointer pointer-events-auto",
10013
11990
  onClick: () => {
10014
11991
  window.parent.postMessage(
10015
- { type: "ow:add-section", insertAfter: sectionGap.insertAfter, insertBefore: sectionGap.insertBefore },
11992
+ {
11993
+ type: "ow:add-section",
11994
+ insertAfter: sectionGap.insertAfter,
11995
+ insertBefore: sectionGap.insertBefore
11996
+ },
10016
11997
  "*"
10017
11998
  );
10018
11999
  },
10019
12000
  children: "Add Section"
10020
12001
  }
10021
12002
  ),
10022
- /* @__PURE__ */ jsx23("div", { className: "flex-1 bg-primary", style: { height: 3 } })
12003
+ /* @__PURE__ */ jsx24("div", { className: "flex-1 bg-primary", style: { height: 3 } })
10023
12004
  ]
10024
12005
  }
10025
12006
  ),
10026
- linkPopover && dialogPortalContainer ? /* @__PURE__ */ jsx23(
12007
+ linkPopover && dialogPortalContainer ? /* @__PURE__ */ jsx24(
10027
12008
  LinkPopover,
10028
12009
  {
10029
12010
  panelRef: linkPopoverPanelRef,
@@ -10044,48 +12025,6 @@ function OhhwellsBridge() {
10044
12025
  bridgeRoot
10045
12026
  ) : null;
10046
12027
  }
10047
-
10048
- // src/ui/drop-indicator.tsx
10049
- import * as React11 from "react";
10050
- import { jsx as jsx24 } from "react/jsx-runtime";
10051
- var dropIndicatorVariants = cva(
10052
- "ov-gap-line pointer-events-none shrink-0 transition-opacity duration-150",
10053
- {
10054
- variants: {
10055
- direction: {
10056
- vertical: "h-6 w-[3px]",
10057
- horizontal: "h-[3px] w-[200px]"
10058
- },
10059
- state: {
10060
- default: "opacity-0",
10061
- hover: "opacity-100",
10062
- dragIdle: "opacity-40",
10063
- dragActive: "opacity-100"
10064
- }
10065
- },
10066
- defaultVariants: {
10067
- direction: "vertical",
10068
- state: "default"
10069
- }
10070
- }
10071
- );
10072
- var DropIndicator = React11.forwardRef(
10073
- ({ className, direction, state, ...props }, ref) => {
10074
- return /* @__PURE__ */ jsx24(
10075
- "div",
10076
- {
10077
- ref,
10078
- "data-slot": "drop-indicator",
10079
- "data-direction": direction ?? "vertical",
10080
- "data-state": state ?? "default",
10081
- "aria-hidden": "true",
10082
- className: cn(dropIndicatorVariants({ direction, state }), className),
10083
- ...props
10084
- }
10085
- );
10086
- }
10087
- );
10088
- DropIndicator.displayName = "DropIndicator";
10089
12028
  export {
10090
12029
  CustomToolbar,
10091
12030
  CustomToolbarButton,