@ohhwells/bridge 0.1.42-next.97 → 0.1.43

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -62,7 +62,7 @@ __export(index_exports, {
62
62
  module.exports = __toCommonJS(index_exports);
63
63
 
64
64
  // src/OhhwellsBridge.tsx
65
- var import_react9 = __toESM(require("react"), 1);
65
+ var import_react8 = __toESM(require("react"), 1);
66
66
  var import_client = require("react-dom/client");
67
67
  var import_react_dom2 = require("react-dom");
68
68
 
@@ -115,7 +115,6 @@ var import_react3 = require("react");
115
115
  var import_react2 = require("react");
116
116
  var import_radix_ui = require("radix-ui");
117
117
  var import_jsx_runtime = require("react/jsx-runtime");
118
- var isValidEmail = (value) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value);
119
118
  function Spinner() {
120
119
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
121
120
  "svg",
@@ -149,17 +148,12 @@ function EmailCaptureModal({ title, subtitle, onSubmit, onClose }) {
149
148
  const [error, setError] = (0, import_react2.useState)(null);
150
149
  const isBook = title === "Confirm your spot";
151
150
  const handleSubmit = async () => {
152
- const trimmed = email.trim();
153
- if (!trimmed) return;
154
- if (!isValidEmail(trimmed)) {
155
- setError("Please enter a valid email address.");
156
- return;
157
- }
151
+ if (!email.trim()) return;
158
152
  setLoading(true);
159
153
  setError(null);
160
154
  try {
161
- await onSubmit(trimmed);
162
- setSubmittedEmail(trimmed);
155
+ await onSubmit(email.trim());
156
+ setSubmittedEmail(email.trim());
163
157
  setSuccess(true);
164
158
  } catch (e) {
165
159
  setError(e instanceof Error ? e.message : "Something went wrong. Please try again.");
@@ -234,10 +228,7 @@ function EmailCaptureModal({ title, subtitle, onSubmit, onClose }) {
234
228
  {
235
229
  type: "email",
236
230
  value: email,
237
- onChange: (e) => {
238
- setEmail(e.target.value);
239
- if (error) setError(null);
240
- },
231
+ onChange: (e) => setEmail(e.target.value),
241
232
  onKeyDown: handleKeyDown,
242
233
  placeholder: "hello@gmail.com",
243
234
  autoFocus: true,
@@ -294,20 +285,12 @@ function formatClassTime(cls) {
294
285
  const em = endTotal % 60;
295
286
  return `${h}:${cls.startTime.minutes}-${eh}:${String(em).padStart(2, "0")}`;
296
287
  }
297
- function formatBookingLabel(cls) {
298
- const price = cls.price;
299
- const isFree = price === null || price === void 0 || `${price}` === "" || `${price}` === "0";
300
- return isFree ? "Book for free" : `Book for ${cls.currency ?? ""} ${price}`.trim();
301
- }
302
288
  function getBookingsOnDate(cls, date) {
303
289
  if (!cls.bookings?.length) return 0;
304
- const target = new Date(date);
305
- target.setHours(0, 0, 0, 0);
290
+ const ds = date.toISOString().split("T")[0];
306
291
  return cls.bookings.filter((b) => {
307
292
  try {
308
- const bookingDate = new Date(b.classDate);
309
- bookingDate.setHours(0, 0, 0, 0);
310
- return bookingDate.getTime() === target.getTime();
293
+ return new Date(b.classDate).toISOString().split("T")[0] === ds;
311
294
  } catch {
312
295
  return false;
313
296
  }
@@ -572,7 +555,7 @@ function ScheduleView({ schedule, dates, selectedIdx, onSelectDate, onOpenModal
572
555
  }
573
556
  )
574
557
  ] }),
575
- cls.showAvailability !== false && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "sm:hidden flex flex-col gap-px", children: isFull ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "font-body text-sm text-(--color-dark,#200C02) opacity-80", children: "Full" }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
558
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "sm:hidden flex flex-col gap-px", children: isFull ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "font-body text-sm text-(--color-dark,#200C02) opacity-80", children: "Full" }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
576
559
  /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "font-body text-sm text-(--color-dark,#200C02) opacity-80", children: [
577
560
  available,
578
561
  "/",
@@ -584,17 +567,9 @@ function ScheduleView({ schedule, dates, selectedIdx, onSelectDate, onOpenModal
584
567
  /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex flex-1 flex-col gap-2 min-w-0 sm:contents", children: [
585
568
  /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex-1 flex flex-col gap-2 min-w-0", children: [
586
569
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "font-body text-base font-bold text-(--color-dark,#200C02) block truncate", children: cls.name }),
587
- cls.hostName && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "font-body text-base font-normal text-(--color-dark,#200C02) opacity-80 block truncate", children: cls.hostName }),
588
- cls.description && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
589
- "span",
590
- {
591
- className: "font-body text-base font-normal text-(--color-dark,#200C02) opacity-80 block",
592
- style: { wordBreak: "break-word" },
593
- children: cls.description
594
- }
595
- )
570
+ cls.hostName && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "font-body text-base font-normal text-(--color-dark,#200C02) opacity-80 block truncate", children: cls.hostName })
596
571
  ] }),
597
- cls.showAvailability !== false && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "hidden sm:flex w-14 shrink-0 flex-col gap-px", children: isFull ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "font-body text-sm text-(--color-dark,#200C02) opacity-80", children: "Full" }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
572
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "hidden sm:flex w-14 shrink-0 flex-col gap-px", children: isFull ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "font-body text-sm text-(--color-dark,#200C02) opacity-80", children: "Full" }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
598
573
  /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "font-body text-sm text-(--color-dark,#200C02) opacity-80", children: [
599
574
  available,
600
575
  "/",
@@ -619,7 +594,7 @@ function ScheduleView({ schedule, dates, selectedIdx, onSelectDate, onOpenModal
619
594
  if (!cls.id) return;
620
595
  onOpenModal?.(isFull ? "waitlist" : "book", cls.id, selectedDate);
621
596
  },
622
- children: isFull ? "Join Waitlist" : formatBookingLabel(cls)
597
+ children: isFull ? "Join Waitlist" : "Book Now"
623
598
  }
624
599
  )
625
600
  ] })
@@ -661,17 +636,6 @@ function SchedulingWidget({ notifyOnConnect = false, initialScheduleId, insertAf
661
636
  const [isHovered, setIsHovered] = (0, import_react3.useState)(false);
662
637
  const [modalState, setModalState] = (0, import_react3.useState)(null);
663
638
  const switchScheduleIdRef = (0, import_react3.useRef)(null);
664
- const liveScheduleIdRef = (0, import_react3.useRef)(null);
665
- const refetchLiveSchedule = (0, import_react3.useCallback)(async () => {
666
- const id = liveScheduleIdRef.current;
667
- if (!id) return;
668
- try {
669
- const res = await fetch(`${API_URL}/api/schedule/id/${id}`);
670
- const data = await res.json();
671
- if (data?.id) setSchedule(data);
672
- } catch {
673
- }
674
- }, []);
675
639
  const dates = (0, import_react3.useMemo)(() => {
676
640
  const today = /* @__PURE__ */ new Date();
677
641
  today.setHours(0, 0, 0, 0);
@@ -691,18 +655,6 @@ function SchedulingWidget({ notifyOnConnect = false, initialScheduleId, insertAf
691
655
  }
692
656
  }
693
657
  }, [schedule, dates]);
694
- (0, import_react3.useEffect)(() => {
695
- if (typeof window === "undefined") return;
696
- const onVisible = () => {
697
- if (!document.hidden) void refetchLiveSchedule();
698
- };
699
- window.addEventListener("focus", refetchLiveSchedule);
700
- document.addEventListener("visibilitychange", onVisible);
701
- return () => {
702
- window.removeEventListener("focus", refetchLiveSchedule);
703
- document.removeEventListener("visibilitychange", onVisible);
704
- };
705
- }, [refetchLiveSchedule]);
706
658
  (0, import_react3.useEffect)(() => {
707
659
  if (typeof window === "undefined") return;
708
660
  const isInEditor = window.self !== window.top;
@@ -764,7 +716,7 @@ function SchedulingWidget({ notifyOnConnect = false, initialScheduleId, insertAf
764
716
  setLoading(false);
765
717
  return;
766
718
  }
767
- liveScheduleIdRef.current = effectiveId;
719
+ ;
768
720
  (async () => {
769
721
  try {
770
722
  const res = await fetch(`${API_URL}/api/schedule/id/${effectiveId}`);
@@ -821,14 +773,18 @@ function SchedulingWidget({ notifyOnConnect = false, initialScheduleId, insertAf
821
773
  const data = await res.json().catch(() => ({}));
822
774
  throw new Error(data?.message ?? "Something went wrong. Please try again.");
823
775
  }
824
- await refetchLiveSchedule();
825
776
  };
826
777
  const handleReplaceSchedule = () => {
827
778
  setIsHovered(false);
828
- window.parent.postMessage(
829
- { type: "ow:scheduling-not-connected", insertAfter, currentScheduleId: schedule?.id },
830
- "*"
831
- );
779
+ if (schedule) {
780
+ window.parent.postMessage({
781
+ type: "ow:schedule-connected",
782
+ schedule: { id: schedule.id, name: schedule.name },
783
+ insertAfter
784
+ }, "*");
785
+ } else {
786
+ window.parent.postMessage({ type: "ow:scheduling-not-connected", insertAfter }, "*");
787
+ }
832
788
  };
833
789
  if (!inEditor && !loading && !schedule) return null;
834
790
  const sectionId = `scheduling-${insertAfter}`;
@@ -871,19 +827,7 @@ function SchedulingWidget({ notifyOnConnect = false, initialScheduleId, insertAf
871
827
  ),
872
828
  /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "max-w-[1280px] mx-auto flex flex-col items-center gap-16", children: [
873
829
  /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex flex-col items-center gap-4", children: [
874
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
875
- "h2",
876
- {
877
- className: "font-display text-center m-0 text-(--color-dark,#200C02)",
878
- style: {
879
- fontSize: "var(--fs-section-h2, clamp(40px, 4.4vw, 64px))",
880
- fontWeight: "var(--font-weight-heading, 400)",
881
- lineHeight: 1.05,
882
- letterSpacing: "-0.02em"
883
- },
884
- children: schedule?.name ?? "Book an appointment"
885
- }
886
- ),
830
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("h2", { className: "font-display text-4xl sm:text-5xl font-bold text-center m-0 text-(--color-dark,#200C02)", children: schedule?.name ?? "Book an appointment" }),
887
831
  schedule?.description && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "font-body text-body text-center m-0 text-(--color-accent,#A89B83)", children: schedule.description })
888
832
  ] }),
889
833
  /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "w-full flex flex-col items-end gap-3", children: [
@@ -4359,8 +4303,7 @@ var CustomToolbar = React4.forwardRef(({ className, onMouseDown, ...props }, ref
4359
4303
  ref,
4360
4304
  "data-ohw-toolbar": "",
4361
4305
  className: cn(
4362
- // Figma: bg background, radius 8, gap-1, p-0.5, shadow-md no border
4363
- "inline-flex h-8 items-center gap-1 rounded-[var(--radius,0.5rem)] bg-background p-0.5 font-sans whitespace-nowrap shadow-md",
4306
+ "inline-flex items-center gap-1 rounded-lg border border-border bg-background p-0.5 font-sans whitespace-nowrap shadow-[0px_2px_4px_-2px_rgba(0,0,0,0.1),0px_4px_6px_-1px_rgba(0,0,0,0.1)]",
4364
4307
  className
4365
4308
  ),
4366
4309
  onMouseDown: (e) => {
@@ -4388,8 +4331,8 @@ var CustomToolbarButton = React4.forwardRef(
4388
4331
  ref,
4389
4332
  type,
4390
4333
  className: cn(
4391
- "inline-flex size-7 shrink-0 items-center justify-center rounded-[calc(var(--radius,0.5rem)-2px)] text-foreground transition-colors",
4392
- active ? "bg-primary text-primary-foreground" : "bg-transparent hover:bg-muted disabled:cursor-not-allowed disabled:text-muted-foreground disabled:opacity-60",
4334
+ "inline-flex shrink-0 items-center justify-center rounded p-1.5 transition-colors",
4335
+ active ? "bg-primary text-primary-foreground" : "bg-transparent text-foreground hover:bg-muted disabled:cursor-not-allowed disabled:text-muted-foreground disabled:opacity-60",
4393
4336
  className
4394
4337
  ),
4395
4338
  ...props
@@ -4424,10 +4367,9 @@ var arrowClassBySide = {
4424
4367
  };
4425
4368
  function TooltipContent({
4426
4369
  className,
4427
- sideOffset = 3,
4370
+ sideOffset = 6,
4428
4371
  side = "bottom",
4429
4372
  children,
4430
- hideArrow,
4431
4373
  ...props
4432
4374
  }) {
4433
4375
  const resolvedSide = side ?? "bottom";
@@ -4438,13 +4380,12 @@ function TooltipContent({
4438
4380
  side,
4439
4381
  sideOffset,
4440
4382
  className: cn(
4441
- "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",
4442
- "origin-[var(--radix-tooltip-content-transform-origin)] animate-in fade-in-0 zoom-in-95",
4443
- "data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95",
4383
+ "relative z-[2147483647] w-fit max-w-xs rounded-md bg-foreground px-3 py-1.5 text-xs text-background shadow-md",
4384
+ 'before:pointer-events-none before:absolute before:size-2 before:rotate-45 before:bg-foreground before:content-[""]',
4385
+ arrowClassBySide[resolvedSide],
4386
+ "animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95",
4444
4387
  "data-[side=bottom]:slide-in-from-top-2 data-[side=top]:slide-in-from-bottom-2",
4445
4388
  "data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2",
4446
- !hideArrow && 'before:pointer-events-none before:absolute before:size-2 before:rotate-45 before:bg-foreground before:content-[""]',
4447
- !hideArrow && arrowClassBySide[resolvedSide],
4448
4389
  className
4449
4390
  ),
4450
4391
  ...props,
@@ -4465,7 +4406,7 @@ function ToolbarActionTooltip({
4465
4406
  const button = /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(CustomToolbarButton, { type: "button", "aria-label": label, disabled, ...buttonProps, children });
4466
4407
  return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(Tooltip, { children: [
4467
4408
  /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(TooltipTrigger, { asChild: true, children: disabled ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "inline-flex", children: button }) : button }),
4468
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(TooltipContent, { side, sideOffset: 3, children: label })
4409
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(TooltipContent, { side, children: label })
4469
4410
  ] });
4470
4411
  }
4471
4412
  function ItemActionToolbar({
@@ -4475,8 +4416,6 @@ function ItemActionToolbar({
4475
4416
  editLinkDisabled = false,
4476
4417
  addItemDisabled = true,
4477
4418
  moreDisabled = true,
4478
- showAddItem = true,
4479
- showMore = true,
4480
4419
  tooltipSide = "bottom"
4481
4420
  }) {
4482
4421
  return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(TooltipProvider, { delayDuration: 0, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(CustomToolbar, { children: [
@@ -4501,7 +4440,8 @@ function ItemActionToolbar({
4501
4440
  children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_lucide_react2.Link, { className: "size-4 shrink-0", "aria-hidden": true })
4502
4441
  }
4503
4442
  ),
4504
- showAddItem ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
4443
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(CustomToolbarDivider, {}),
4444
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
4505
4445
  ToolbarActionTooltip,
4506
4446
  {
4507
4447
  label: "Add item",
@@ -4517,8 +4457,9 @@ function ItemActionToolbar({
4517
4457
  },
4518
4458
  children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_lucide_react2.Plus, { className: "size-4 shrink-0", "aria-hidden": true })
4519
4459
  }
4520
- ) : null,
4521
- showMore ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
4460
+ ),
4461
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(CustomToolbarDivider, {}),
4462
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
4522
4463
  ToolbarActionTooltip,
4523
4464
  {
4524
4465
  label: "More",
@@ -4534,32 +4475,15 @@ function ItemActionToolbar({
4534
4475
  },
4535
4476
  children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_lucide_react2.MoreHorizontal, { className: "size-4 shrink-0", "aria-hidden": true })
4536
4477
  }
4537
- ) : null
4478
+ )
4538
4479
  ] }) });
4539
4480
  }
4540
4481
 
4541
4482
  // src/ui/item-interaction-layer.tsx
4542
- var React5 = __toESM(require("react"), 1);
4543
4483
  var import_jsx_runtime9 = require("react/jsx-runtime");
4544
4484
  var PRIMARY = "var(--ohw-primary, #0885FE)";
4545
- var FOCUS_RING = `0 0 0 4px color-mix(in srgb, ${PRIMARY} 12%, transparent)`;
4485
+ var FOCUS_RING = "0 0 0 4px rgba(8, 133, 254, 0.12)";
4546
4486
  var DRAG_SHADOW = "0px 4px 6px -1px rgba(0, 0, 0, 0.1), 0px 2px 4px -2px rgba(0, 0, 0, 0.1)";
4547
- var TOOLBAR_EDGE_MARGIN = 4;
4548
- var SELECTION_CHROME_GAP = 4;
4549
- var TOOLBAR_STROKE_GAP = 4;
4550
- function getChromeZIndex(state) {
4551
- switch (state) {
4552
- case "active-top":
4553
- case "active-bottom":
4554
- case "dragging":
4555
- return 2147483644;
4556
- case "hover":
4557
- case "sibling-hint":
4558
- return 2147483643;
4559
- default:
4560
- return 2147483642;
4561
- }
4562
- }
4563
4487
  function getChromeStyle(state) {
4564
4488
  switch (state) {
4565
4489
  case "hover":
@@ -4581,65 +4505,12 @@ function getChromeStyle(state) {
4581
4505
  case "dragging":
4582
4506
  return {
4583
4507
  border: `2px solid ${PRIMARY}`,
4584
- boxShadow: `${FOCUS_RING}, ${DRAG_SHADOW}`
4508
+ boxShadow: DRAG_SHADOW
4585
4509
  };
4586
4510
  default:
4587
4511
  return {};
4588
4512
  }
4589
4513
  }
4590
- function ClampedToolbarSlot({
4591
- placement,
4592
- children
4593
- }) {
4594
- const slotRef = React5.useRef(null);
4595
- React5.useLayoutEffect(() => {
4596
- const slot = slotRef.current;
4597
- if (!slot) return;
4598
- const clamp = () => {
4599
- const toolbar = slot.firstElementChild;
4600
- if (!toolbar) return;
4601
- const w = toolbar.offsetWidth;
4602
- if (w <= 0) return;
4603
- const parent = slot.offsetParent;
4604
- if (!parent) return;
4605
- const parentRect = parent.getBoundingClientRect();
4606
- const centerX = parentRect.left + parentRect.width / 2;
4607
- const half = w / 2;
4608
- const clampedCenter = Math.max(
4609
- TOOLBAR_EDGE_MARGIN + half,
4610
- Math.min(centerX, window.innerWidth - TOOLBAR_EDGE_MARGIN - half)
4611
- );
4612
- const offsetX = clampedCenter - centerX;
4613
- slot.style.transform = `translateX(calc(-50% + ${offsetX}px))`;
4614
- };
4615
- clamp();
4616
- const ro = new ResizeObserver(clamp);
4617
- ro.observe(slot);
4618
- if (slot.firstElementChild) ro.observe(slot.firstElementChild);
4619
- window.addEventListener("resize", clamp);
4620
- return () => {
4621
- ro.disconnect();
4622
- window.removeEventListener("resize", clamp);
4623
- };
4624
- }, [placement, children]);
4625
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
4626
- "div",
4627
- {
4628
- ref: slotRef,
4629
- className: cn(
4630
- "pointer-events-auto absolute left-1/2",
4631
- placement === "top" ? "bottom-full" : "top-full"
4632
- ),
4633
- style: {
4634
- marginBottom: placement === "top" ? TOOLBAR_STROKE_GAP : void 0,
4635
- marginTop: placement === "bottom" ? TOOLBAR_STROKE_GAP : void 0,
4636
- transform: "translateX(-50%)"
4637
- },
4638
- "data-ohw-item-toolbar-anchor": placement,
4639
- children
4640
- }
4641
- );
4642
- }
4643
4514
  function ItemInteractionLayer({
4644
4515
  rect,
4645
4516
  state,
@@ -4650,18 +4521,14 @@ function ItemInteractionLayer({
4650
4521
  dragHandleLabel = "Reorder item",
4651
4522
  onDragHandleDragStart,
4652
4523
  onDragHandleDragEnd,
4653
- onItemPointerDown,
4654
- onItemClick,
4655
- itemDragSurface = true,
4656
- chromeGap = SELECTION_CHROME_GAP,
4524
+ chromeGap = 6,
4657
4525
  className
4658
4526
  }) {
4659
4527
  if (state === "default") return null;
4660
4528
  const isActive = state === "active-top" || state === "active-bottom";
4661
4529
  const isDragging = state === "dragging";
4662
4530
  const showToolbar = isActive && toolbar;
4663
- const showDragHandle = (isActive || isDragging) && showHandle;
4664
- const itemDragEnabled = itemDragSurface && isActive && showHandle && !isDragging && !dragDisabled;
4531
+ const showDragHandle = isActive && showHandle && !isDragging;
4665
4532
  return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
4666
4533
  "div",
4667
4534
  {
@@ -4675,7 +4542,7 @@ function ItemInteractionLayer({
4675
4542
  left: rect.left - chromeGap,
4676
4543
  width: rect.width + chromeGap * 2,
4677
4544
  height: rect.height + chromeGap * 2,
4678
- zIndex: getChromeZIndex(state)
4545
+ zIndex: 2147483646
4679
4546
  },
4680
4547
  children: [
4681
4548
  /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
@@ -4686,29 +4553,11 @@ function ItemInteractionLayer({
4686
4553
  style: getChromeStyle(state)
4687
4554
  }
4688
4555
  ),
4689
- itemDragEnabled && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
4690
- "div",
4691
- {
4692
- "data-ohw-item-drag-surface": "",
4693
- className: "pointer-events-auto absolute inset-0 cursor-text rounded-lg",
4694
- onPointerDown: (e) => {
4695
- if (e.button !== 0) return;
4696
- onItemPointerDown?.(e);
4697
- },
4698
- onClick: (e) => {
4699
- e.preventDefault();
4700
- e.stopPropagation();
4701
- onItemClick?.(e.clientX, e.clientY);
4702
- }
4703
- }
4704
- ),
4705
4556
  showDragHandle && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
4706
4557
  "div",
4707
4558
  {
4708
4559
  "data-ohw-drag-handle-container": "",
4709
- className: "pointer-events-auto absolute left-0 top-1/2 z-10 -translate-x-[calc(100%+7px)] -translate-y-1/2",
4710
- style: isDragging ? { opacity: 0 } : void 0,
4711
- "aria-hidden": isDragging || void 0,
4560
+ className: "pointer-events-auto absolute left-0 top-1/2 -translate-x-[calc(100%+7px)] -translate-y-1/2",
4712
4561
  children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
4713
4562
  DragHandle,
4714
4563
  {
@@ -4720,17 +4569,7 @@ function ItemInteractionLayer({
4720
4569
  e.preventDefault();
4721
4570
  return;
4722
4571
  }
4723
- const blank = document.createElement("canvas");
4724
- blank.width = 1;
4725
- blank.height = 1;
4726
- blank.style.cssText = "position:fixed;left:-9999px;top:-9999px;width:1px;height:1px;pointer-events:none";
4727
- document.body.appendChild(blank);
4728
- try {
4729
- e.dataTransfer.setDragImage(blank, 0, 0);
4730
- } finally {
4731
- requestAnimationFrame(() => blank.remove());
4732
- }
4733
- e.dataTransfer.setData("text/plain", dragHandleLabel);
4572
+ e.dataTransfer?.setData("text/plain", dragHandleLabel);
4734
4573
  e.dataTransfer.effectAllowed = "move";
4735
4574
  onDragHandleDragStart?.(e);
4736
4575
  },
@@ -4739,19 +4578,33 @@ function ItemInteractionLayer({
4739
4578
  )
4740
4579
  }
4741
4580
  ),
4742
- showToolbar && state === "active-top" && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(ClampedToolbarSlot, { placement: "top", children: toolbar }),
4743
- showToolbar && state === "active-bottom" && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(ClampedToolbarSlot, { placement: "bottom", children: toolbar })
4581
+ showToolbar && state === "active-top" && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
4582
+ "div",
4583
+ {
4584
+ className: "pointer-events-auto absolute bottom-full left-1/2 mb-2 -translate-x-1/2",
4585
+ "data-ohw-item-toolbar-anchor": "top",
4586
+ children: toolbar
4587
+ }
4588
+ ),
4589
+ showToolbar && state === "active-bottom" && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
4590
+ "div",
4591
+ {
4592
+ className: "pointer-events-auto absolute left-1/2 top-full mt-2 -translate-x-1/2",
4593
+ "data-ohw-item-toolbar-anchor": "bottom",
4594
+ children: toolbar
4595
+ }
4596
+ )
4744
4597
  ]
4745
4598
  }
4746
4599
  );
4747
4600
  }
4748
4601
 
4749
4602
  // src/ui/MediaOverlay.tsx
4750
- var React7 = __toESM(require("react"), 1);
4603
+ var React6 = __toESM(require("react"), 1);
4751
4604
  var import_lucide_react3 = require("lucide-react");
4752
4605
 
4753
4606
  // src/ui/button.tsx
4754
- var React6 = __toESM(require("react"), 1);
4607
+ var React5 = __toESM(require("react"), 1);
4755
4608
  var import_radix_ui4 = require("radix-ui");
4756
4609
  var import_jsx_runtime10 = require("react/jsx-runtime");
4757
4610
  var buttonVariants = cva(
@@ -4774,7 +4627,7 @@ var buttonVariants = cva(
4774
4627
  }
4775
4628
  }
4776
4629
  );
4777
- var Button = React6.forwardRef(
4630
+ var Button = React5.forwardRef(
4778
4631
  ({ className, variant, size, asChild = false, ...props }, ref) => {
4779
4632
  const Comp = asChild ? import_radix_ui4.Slot.Root : "button";
4780
4633
  return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
@@ -4826,7 +4679,7 @@ function MediaOverlay({
4826
4679
  onVideoSettingsChange
4827
4680
  }) {
4828
4681
  const { rect } = hover;
4829
- const skeletonRef = React7.useRef(null);
4682
+ const skeletonRef = React6.useRef(null);
4830
4683
  const isVideo = hover.elementType === "video";
4831
4684
  const autoplay = hover.videoAutoplay ?? true;
4832
4685
  const muted = hover.videoMuted ?? true;
@@ -4838,7 +4691,7 @@ function MediaOverlay({
4838
4691
  height: rect.height,
4839
4692
  zIndex: 2147483646
4840
4693
  };
4841
- React7.useEffect(() => {
4694
+ React6.useEffect(() => {
4842
4695
  if (!isUploading || !fadingOut || !skeletonRef.current) return;
4843
4696
  const anim = skeletonRef.current.animate([{ opacity: 1 }, { opacity: 0 }], {
4844
4697
  duration: MEDIA_UPLOAD_FADE_MS,
@@ -5243,7 +5096,7 @@ function scrollToHashSectionWhenReady(behavior = "smooth") {
5243
5096
  var import_react7 = require("react");
5244
5097
 
5245
5098
  // src/ui/dialog.tsx
5246
- var React8 = __toESM(require("react"), 1);
5099
+ var React7 = __toESM(require("react"), 1);
5247
5100
  var import_radix_ui5 = require("radix-ui");
5248
5101
  var import_lucide_react4 = require("lucide-react");
5249
5102
  var import_jsx_runtime12 = require("react/jsx-runtime");
@@ -5271,7 +5124,7 @@ function DialogOverlay({
5271
5124
  }
5272
5125
  );
5273
5126
  }
5274
- var DialogContent = React8.forwardRef(
5127
+ var DialogContent = React7.forwardRef(
5275
5128
  ({ className, children, showCloseButton = true, container, ...props }, ref) => {
5276
5129
  const positionMode = container ? "absolute" : "fixed";
5277
5130
  return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(DialogPortal, { container: container ?? void 0, children: [
@@ -5326,7 +5179,7 @@ function DialogFooter({
5326
5179
  }
5327
5180
  );
5328
5181
  }
5329
- var DialogTitle = React8.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
5182
+ var DialogTitle = React7.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
5330
5183
  import_radix_ui5.Dialog.Title,
5331
5184
  {
5332
5185
  ref,
@@ -5338,7 +5191,7 @@ var DialogTitle = React8.forwardRef(({ className, ...props }, ref) => /* @__PURE
5338
5191
  }
5339
5192
  ));
5340
5193
  DialogTitle.displayName = import_radix_ui5.Dialog.Title.displayName;
5341
- var DialogDescription = React8.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
5194
+ var DialogDescription = React7.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
5342
5195
  import_radix_ui5.Dialog.Description,
5343
5196
  {
5344
5197
  ref,
@@ -5443,9 +5296,9 @@ function SectionTreeItem({
5443
5296
  var import_react4 = require("react");
5444
5297
 
5445
5298
  // src/ui/input.tsx
5446
- var React9 = __toESM(require("react"), 1);
5299
+ var React8 = __toESM(require("react"), 1);
5447
5300
  var import_jsx_runtime15 = require("react/jsx-runtime");
5448
- var Input = React9.forwardRef(
5301
+ var Input = React8.forwardRef(
5449
5302
  ({ className, type, ...props }, ref) => {
5450
5303
  return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
5451
5304
  "input",
@@ -6426,155 +6279,9 @@ function shouldUseDevFixtures() {
6426
6279
  return new URLSearchParams(q).get("ohw-fixtures") === "1";
6427
6280
  }
6428
6281
 
6429
- // src/lib/nav-tree.ts
6430
- var MAX_LEVEL = { nav: 1 };
6431
- var ROOT = null;
6432
- function cloneForest(nodes) {
6433
- return nodes.map((n) => ({ id: n.id, children: cloneForest(n.children) }));
6434
- }
6435
- function findNode(nodes, id, parentId = ROOT) {
6436
- for (let i = 0; i < nodes.length; i++) {
6437
- const node = nodes[i];
6438
- if (node.id === id) return { node, parentId, index: i, siblings: nodes };
6439
- const nested = findNode(node.children, id, node.id);
6440
- if (nested) return nested;
6441
- }
6442
- return null;
6443
- }
6444
- function getChildrenOf(nodes, parentId) {
6445
- if (parentId === ROOT) return nodes;
6446
- const found = findNode(nodes, parentId);
6447
- return found ? found.node.children : null;
6448
- }
6449
- function isDescendant(nodes, ancestorId, maybeDescendantId) {
6450
- const found = findNode(nodes, ancestorId);
6451
- if (!found) return false;
6452
- return findNode(found.node.children, maybeDescendantId) !== null;
6453
- }
6454
- function levelOf(nodes, id) {
6455
- const loc = findNode(nodes, id);
6456
- if (!loc) return -1;
6457
- if (loc.parentId === ROOT) return 0;
6458
- return 1 + Math.max(0, levelOf(nodes, loc.parentId));
6459
- }
6460
- function insertedLevel(nodes, parentId) {
6461
- if (parentId === ROOT) return 0;
6462
- const parentLevel = levelOf(nodes, parentId);
6463
- if (parentLevel < 0) return -1;
6464
- return parentLevel + 1;
6465
- }
6466
- function takeNode(nodes, id) {
6467
- const forest = cloneForest(nodes);
6468
- const loc = findNode(forest, id);
6469
- if (!loc) return null;
6470
- const [taken] = loc.siblings.splice(loc.index, 1);
6471
- if (!taken) return null;
6472
- return { forest, taken };
6473
- }
6474
- function insertInto(forest, node, parentId, index) {
6475
- const siblings = getChildrenOf(forest, parentId);
6476
- if (!siblings) return false;
6477
- const clamped = Math.max(0, Math.min(index, siblings.length));
6478
- siblings.splice(clamped, 0, node);
6479
- return true;
6480
- }
6481
- function normalize(nodes) {
6482
- const walk = (list) => list.map((n) => {
6483
- const children = walk(n.children);
6484
- return { id: n.id, children };
6485
- });
6486
- return walk(nodes);
6487
- }
6488
- function moveNode(model, draggedId, target) {
6489
- const { parentId, index: targetIndex } = target;
6490
- if (parentId === draggedId) return null;
6491
- if (parentId !== ROOT && isDescendant(model, draggedId, parentId)) return null;
6492
- const nextLevel = insertedLevel(model, parentId);
6493
- if (nextLevel < 0 || nextLevel > MAX_LEVEL.nav) return null;
6494
- const draggedLoc = findNode(model, draggedId);
6495
- if (!draggedLoc) return null;
6496
- if (parentId !== ROOT && draggedLoc.node.children.length > 0) return null;
6497
- const taken = takeNode(model, draggedId);
6498
- if (!taken) return null;
6499
- let insertIndex = targetIndex;
6500
- if (draggedLoc.parentId === parentId && draggedLoc.index < targetIndex) {
6501
- insertIndex -= 1;
6502
- }
6503
- if (!insertInto(taken.forest, taken.taken, parentId, insertIndex)) return null;
6504
- const next = normalize(taken.forest);
6505
- if (forestsEqual(model, next)) return null;
6506
- return next;
6507
- }
6508
- function forestsEqual(a, b) {
6509
- if (a.length !== b.length) return false;
6510
- for (let i = 0; i < a.length; i++) {
6511
- const left = a[i];
6512
- const right = b[i];
6513
- if (left.id !== right.id) return false;
6514
- if (!forestsEqual(left.children, right.children)) return false;
6515
- }
6516
- return true;
6517
- }
6518
- function forestFromFlatOrder(order) {
6519
- return order.map((id) => ({ id, children: [] }));
6520
- }
6521
- function serializeNavOrder(nodes) {
6522
- const hasNesting = nodes.some((n) => n.children.length > 0);
6523
- if (!hasNesting) {
6524
- return JSON.stringify(nodes.map((n) => n.id));
6525
- }
6526
- return JSON.stringify(nodes);
6527
- }
6528
- function parseNavOrderJson(raw) {
6529
- if (!raw) return null;
6530
- try {
6531
- const parsed = JSON.parse(raw);
6532
- if (!Array.isArray(parsed)) return null;
6533
- if (parsed.every((x) => typeof x === "string")) {
6534
- return forestFromFlatOrder(parsed.filter((x) => typeof x === "string" && x.length > 0));
6535
- }
6536
- return parseNavNodeList(parsed);
6537
- } catch {
6538
- return null;
6539
- }
6540
- }
6541
- function parseNavNodeList(raw) {
6542
- const out = [];
6543
- for (const item of raw) {
6544
- if (typeof item === "string" && item.length > 0) {
6545
- out.push({ id: item, children: [] });
6546
- continue;
6547
- }
6548
- if (!item || typeof item !== "object") return null;
6549
- const rec = item;
6550
- if (typeof rec.id !== "string" || !rec.id) return null;
6551
- const children = Array.isArray(rec.children) ? parseNavNodeList(rec.children) : [];
6552
- if (!children) return null;
6553
- out.push({ id: rec.id, children });
6554
- }
6555
- return out;
6556
- }
6557
- function flattenNavOrder(nodes) {
6558
- const out = [];
6559
- const walk = (list) => {
6560
- for (const n of list) {
6561
- out.push(n.id);
6562
- walk(n.children);
6563
- }
6564
- };
6565
- walk(nodes);
6566
- return out;
6567
- }
6568
-
6569
6282
  // src/lib/nav-items.ts
6570
6283
  var NAV_COUNT_KEY = "__ohw_nav_count";
6571
6284
  var NAV_ORDER_KEY = "__ohw_nav_order";
6572
- var NAV_HREF_RE = /^nav-(\d+)-href$/;
6573
- function isNavbarHrefKey(key) {
6574
- if (!key) return false;
6575
- if (key === "nav-book-href") return false;
6576
- return NAV_HREF_RE.test(key);
6577
- }
6578
6285
  function getLinkHref(el) {
6579
6286
  const key = el.getAttribute("data-ohw-href-key");
6580
6287
  if (key) {
@@ -6611,7 +6318,7 @@ function listNavbarItems() {
6611
6318
  }
6612
6319
  function parseNavIndexFromKey(key) {
6613
6320
  if (!key) return null;
6614
- const match = key.match(NAV_HREF_RE);
6321
+ const match = key.match(/^nav-(\d+)-href$/);
6615
6322
  if (!match) return null;
6616
6323
  return parseInt(match[1], 10);
6617
6324
  }
@@ -6634,6 +6341,17 @@ function getNavbarExistingTargets() {
6634
6341
  function getNavOrderFromDom() {
6635
6342
  return listNavbarItems().map((el) => el.getAttribute("data-ohw-href-key")).filter((key) => Boolean(key));
6636
6343
  }
6344
+ function parseNavOrder(content) {
6345
+ const raw = content[NAV_ORDER_KEY];
6346
+ if (!raw) return null;
6347
+ try {
6348
+ const parsed = JSON.parse(raw);
6349
+ if (!Array.isArray(parsed)) return null;
6350
+ return parsed.filter((key) => typeof key === "string" && key.length > 0);
6351
+ } catch {
6352
+ return null;
6353
+ }
6354
+ }
6637
6355
  function findCounterpartByHrefKey(hrefKey, root) {
6638
6356
  return root.querySelector(`[data-ohw-href-key="${CSS.escape(hrefKey)}"]`);
6639
6357
  }
@@ -6697,47 +6415,19 @@ function insertNavbarItemDom(index, href, label, afterAnchor) {
6697
6415
  }
6698
6416
  return primary;
6699
6417
  }
6700
- function navOrderKeysEqual(a, b) {
6701
- if (a.length !== b.length) return false;
6702
- for (let i = 0; i < a.length; i++) {
6703
- if (a[i] !== b[i]) return false;
6704
- }
6705
- return true;
6706
- }
6707
- function getNavbarLinkKeysInContainer(container) {
6708
- return Array.from(container.querySelectorAll("[data-ohw-href-key]")).filter(isNavbarLinkItem).map((el) => el.getAttribute("data-ohw-href-key")).filter((key) => Boolean(key));
6709
- }
6710
6418
  function applyNavOrderToContainer(container, order) {
6711
- const desired = order.filter((key, i) => order.indexOf(key) === i);
6712
- const current = getNavbarLinkKeysInContainer(container);
6713
- const extras = current.filter((key) => !desired.includes(key));
6714
- const expected = [...desired.filter((key) => current.includes(key)), ...extras];
6715
- const orderedEls = [];
6716
- if (navOrderKeysEqual(current, expected)) return;
6717
- if (current.length === expected.length && current.every((key, i) => key === expected[i])) {
6718
- return;
6719
- }
6720
- const lastCurrentLinkEl = current.length > 0 ? findCounterpartByHrefKey(current[current.length - 1], container) : null;
6721
- const anchor = lastCurrentLinkEl?.parentElement === container ? lastCurrentLinkEl.nextElementSibling : null;
6722
6419
  const seen = /* @__PURE__ */ new Set();
6723
- for (const hrefKey of desired) {
6420
+ for (const hrefKey of order) {
6724
6421
  if (seen.has(hrefKey)) continue;
6725
6422
  seen.add(hrefKey);
6726
6423
  const el = findCounterpartByHrefKey(hrefKey, container);
6727
- if (el && isNavbarLinkItem(el)) orderedEls.push(el);
6424
+ if (el) container.appendChild(el);
6728
6425
  }
6729
6426
  for (const el of container.querySelectorAll("[data-ohw-href-key]")) {
6730
6427
  if (!isNavbarLinkItem(el)) continue;
6731
6428
  const key = el.getAttribute("data-ohw-href-key");
6732
6429
  if (!key || seen.has(key)) continue;
6733
- orderedEls.push(el);
6734
- }
6735
- for (const el of orderedEls) {
6736
- if (anchor) {
6737
- if (el.nextSibling !== anchor) container.insertBefore(el, anchor);
6738
- } else if (container.lastElementChild !== el) {
6739
- container.appendChild(el);
6740
- }
6430
+ container.appendChild(el);
6741
6431
  }
6742
6432
  }
6743
6433
  function applyNavOrder(order) {
@@ -6746,75 +6436,6 @@ function applyNavOrder(order) {
6746
6436
  const drawer = getNavbarDrawerContainer();
6747
6437
  if (drawer) applyNavOrderToContainer(drawer, order);
6748
6438
  }
6749
- function applyChildrenOrder(parentHrefKey, childKeys, root) {
6750
- const parent = findCounterpartByHrefKey(parentHrefKey, root);
6751
- if (!parent) return;
6752
- const group = parent.closest("[data-ohw-nav-group]");
6753
- const childrenRoot = group?.querySelector(":scope > [data-ohw-nav-children]");
6754
- if (!childrenRoot) return;
6755
- const current = Array.from(childrenRoot.querySelectorAll(":scope > [data-ohw-href-key]")).map((el) => el.getAttribute("data-ohw-href-key")).filter((key) => Boolean(key));
6756
- if (navOrderKeysEqual(current, childKeys)) return;
6757
- for (const key of childKeys) {
6758
- const el = findCounterpartByHrefKey(key, root);
6759
- if (!el) continue;
6760
- if (childrenRoot.lastElementChild !== el) {
6761
- childrenRoot.appendChild(el);
6762
- }
6763
- }
6764
- }
6765
- function applyNavForest(forest) {
6766
- if (forestsEqual(forest, getNavForestFromDom())) return;
6767
- const rootKeys = forest.map((n) => n.id);
6768
- applyNavOrder(rootKeys);
6769
- const desktop = getNavbarDesktopContainer();
6770
- const drawer = getNavbarDrawerContainer();
6771
- for (const node of forest) {
6772
- if (node.children.length === 0) continue;
6773
- const childKeys = node.children.map((c) => c.id);
6774
- if (desktop) applyChildrenOrder(node.id, childKeys, desktop);
6775
- if (drawer) applyChildrenOrder(node.id, childKeys, drawer);
6776
- }
6777
- }
6778
- function getNavForestFromDom() {
6779
- const items = listNavbarItems();
6780
- const nestedChildKeys = /* @__PURE__ */ new Set();
6781
- const roots = [];
6782
- for (const item of items) {
6783
- const key = item.getAttribute("data-ohw-href-key");
6784
- if (!key || !isNavbarHrefKey(key)) continue;
6785
- if (item.closest("[data-ohw-nav-children]")) continue;
6786
- const group = item.closest("[data-ohw-nav-group]");
6787
- const childrenRoot = group?.querySelector(":scope > [data-ohw-nav-children]");
6788
- const children = [];
6789
- if (childrenRoot) {
6790
- for (const child of childrenRoot.querySelectorAll(":scope > [data-ohw-href-key]")) {
6791
- if (!isNavbarLinkItem(child)) continue;
6792
- const childKey = child.getAttribute("data-ohw-href-key");
6793
- if (!childKey || !isNavbarHrefKey(childKey)) continue;
6794
- nestedChildKeys.add(childKey);
6795
- children.push({ id: childKey, children: [] });
6796
- }
6797
- }
6798
- roots.push({ id: key, children });
6799
- }
6800
- if (roots.length === 0) {
6801
- return forestFromFlatOrder(
6802
- getNavOrderFromDom().filter((k) => isNavbarHrefKey(k) && !nestedChildKeys.has(k))
6803
- );
6804
- }
6805
- return roots;
6806
- }
6807
- function planNavItemMove(hrefKey, parentId, insertIndex) {
6808
- const model = getNavForestFromDom();
6809
- const next = moveNode(model, hrefKey, { parentId, index: insertIndex });
6810
- if (!next) return null;
6811
- if (forestsEqual(model, next)) return null;
6812
- return {
6813
- forest: next,
6814
- orderJson: serializeNavOrder(next),
6815
- flatOrder: flattenNavOrder(next)
6816
- };
6817
- }
6818
6439
  function collectNavbarIndicesFromContent(content) {
6819
6440
  const indices = /* @__PURE__ */ new Set();
6820
6441
  for (const key of Object.keys(content)) {
@@ -6842,10 +6463,8 @@ function reconcileNavbarItemsFromContent(content) {
6842
6463
  if (!href && !label) continue;
6843
6464
  insertNavbarItemDom(index, href ?? "/", label ?? "Untitled", null);
6844
6465
  }
6845
- const orderForest = parseNavOrderJson(content[NAV_ORDER_KEY]);
6846
- if (orderForest?.length) {
6847
- applyNavForest(orderForest);
6848
- }
6466
+ const order = parseNavOrder(content);
6467
+ if (order?.length) applyNavOrder(order);
6849
6468
  }
6850
6469
  function buildNavOrderAfterInsert(afterAnchor, newHrefKey) {
6851
6470
  const order = getNavOrderFromDom();
@@ -6885,982 +6504,75 @@ function insertNavbarItem(href, label, afterAnchor = null) {
6885
6504
  };
6886
6505
  }
6887
6506
 
6888
- // src/lib/footer-items.ts
6889
- var FOOTER_ORDER_KEY = "__ohw_footer_order";
6890
- var FOOTER_HREF_RE = /^footer-(\d+)-(\d+)-href$/;
6891
- function parseFooterHrefKey(key) {
6892
- if (!key) return null;
6893
- const match = key.match(FOOTER_HREF_RE);
6894
- if (!match) return null;
6895
- return { col: parseInt(match[1], 10), item: parseInt(match[2], 10) };
6896
- }
6897
- function isFooterHrefKey(key) {
6898
- return parseFooterHrefKey(key) !== null;
6899
- }
6900
- function getFooterRoot() {
6901
- return document.querySelector('footer[data-ohw-section="footer"], footer');
6902
- }
6903
- function getFooterLinksContainer() {
6904
- return document.querySelector("[data-ohw-footer-links]") ?? document.querySelector(".rb-footer-links");
6905
- }
6906
- function isFooterLinkAnchor(el) {
6907
- if (!el.matches("[data-ohw-href-key]")) return false;
6908
- if (!isFooterHrefKey(el.getAttribute("data-ohw-href-key"))) return false;
6909
- return Boolean(el.querySelector('[data-ohw-editable="text"]'));
6910
- }
6911
- function listFooterColumns() {
6912
- const root = getFooterLinksContainer() ?? getFooterRoot()?.querySelector(".rb-footer-links") ?? null;
6913
- if (!root) return [];
6914
- const explicit = Array.from(root.querySelectorAll(":scope > [data-ohw-footer-col]"));
6915
- if (explicit.length > 0) return explicit;
6916
- return Array.from(root.children).filter((el) => {
6917
- if (!(el instanceof HTMLElement)) return false;
6918
- return Array.from(el.querySelectorAll(":scope > [data-ohw-href-key]")).some(
6919
- isFooterLinkAnchor
6920
- );
6921
- });
6922
- }
6923
- function listFooterLinksInColumn(column) {
6924
- return Array.from(column.querySelectorAll(":scope > [data-ohw-href-key]")).filter(
6925
- isFooterLinkAnchor
6926
- );
6927
- }
6928
- function findFooterColumnForLink(anchor) {
6929
- const columns = listFooterColumns();
6930
- return columns.find((col) => col.contains(anchor)) ?? null;
6931
- }
6932
- function getFooterOrderFromDom() {
6933
- return listFooterColumns().map(
6934
- (col) => listFooterLinksInColumn(col).map((el) => el.getAttribute("data-ohw-href-key")).filter((key) => Boolean(key))
6935
- );
6936
- }
6937
- function findFooterLinkByKey(hrefKey) {
6938
- return document.querySelector(
6939
- `footer [data-ohw-href-key="${CSS.escape(hrefKey)}"]`
6940
- );
6941
- }
6942
- function syncFooterColumnIndices(columns) {
6943
- columns.forEach((col, index) => {
6944
- const next = String(index);
6945
- if (col.getAttribute("data-ohw-footer-col") !== next) {
6946
- col.setAttribute("data-ohw-footer-col", next);
6507
+ // src/ui/navbar-container-chrome.tsx
6508
+ var import_lucide_react10 = require("lucide-react");
6509
+ var import_jsx_runtime21 = require("react/jsx-runtime");
6510
+ function NavbarContainerChrome({
6511
+ rect,
6512
+ onAdd
6513
+ }) {
6514
+ const chromeGap = 6;
6515
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
6516
+ "div",
6517
+ {
6518
+ "data-ohw-navbar-container-chrome": "",
6519
+ "data-ohw-bridge": "",
6520
+ className: "pointer-events-none fixed z-[2147483647]",
6521
+ style: {
6522
+ top: rect.top - chromeGap,
6523
+ left: rect.left - chromeGap,
6524
+ width: rect.width + chromeGap * 2,
6525
+ height: rect.height + chromeGap * 2
6526
+ },
6527
+ children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
6528
+ "button",
6529
+ {
6530
+ type: "button",
6531
+ "data-ohw-navbar-add-button": "",
6532
+ 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",
6533
+ style: { top: "70%" },
6534
+ "aria-label": "Add item",
6535
+ onMouseDown: (e) => {
6536
+ e.preventDefault();
6537
+ e.stopPropagation();
6538
+ },
6539
+ onClick: (e) => {
6540
+ e.preventDefault();
6541
+ e.stopPropagation();
6542
+ onAdd();
6543
+ },
6544
+ children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react10.Plus, { className: "size-4 shrink-0 text-foreground", "aria-hidden": true })
6545
+ }
6546
+ )
6947
6547
  }
6948
- });
6949
- }
6950
- function appendChildIfNeeded(parent, child) {
6951
- if (parent.lastElementChild !== child) {
6952
- parent.appendChild(child);
6953
- }
6548
+ );
6954
6549
  }
6955
- function applyFooterOrder(order) {
6956
- const container = getFooterLinksContainer();
6957
- if (!container) return;
6958
- const columns = listFooterColumns();
6959
- if (columns.length === 0) return;
6960
- const currentOrder = getFooterOrderFromDom();
6961
- if (ordersEqual(order, currentOrder)) {
6962
- syncFooterColumnIndices(columns);
6963
- return;
6964
- }
6965
- const used = /* @__PURE__ */ new Set();
6966
- const orderedCols = [];
6967
- for (const colOrder of order) {
6968
- let matched = null;
6969
- for (const hrefKey of colOrder) {
6970
- const link = findFooterLinkByKey(hrefKey);
6971
- if (!link) continue;
6972
- const owner = findFooterColumnForLink(link);
6973
- if (owner && !used.has(owner)) {
6974
- matched = owner;
6975
- break;
6976
- }
6977
- }
6978
- if (!matched) {
6979
- matched = columns.find((col) => !used.has(col)) ?? null;
6980
- }
6981
- if (matched) {
6982
- used.add(matched);
6983
- orderedCols.push(matched);
6984
- }
6985
- }
6986
- for (const col of columns) {
6987
- if (!used.has(col)) orderedCols.push(col);
6988
- }
6989
- for (const col of orderedCols) {
6990
- appendChildIfNeeded(container, col);
6991
- }
6992
- const freshColumns = listFooterColumns();
6993
- syncFooterColumnIndices(freshColumns);
6994
- for (let c = 0; c < order.length; c++) {
6995
- const col = freshColumns[c];
6996
- if (!col) continue;
6997
- const colOrder = order[c];
6998
- for (const hrefKey of colOrder) {
6999
- const el = findFooterLinkByKey(hrefKey);
7000
- if (!el) continue;
7001
- if (el.parentElement !== col || col.lastElementChild !== el) {
7002
- col.appendChild(el);
6550
+
6551
+ // src/ui/badge.tsx
6552
+ var import_jsx_runtime22 = require("react/jsx-runtime");
6553
+ var badgeVariants = cva(
6554
+ "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",
6555
+ {
6556
+ variants: {
6557
+ variant: {
6558
+ default: "border-transparent bg-primary text-primary-foreground",
6559
+ secondary: "border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80",
6560
+ destructive: "border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80",
6561
+ outline: "text-foreground"
7003
6562
  }
6563
+ },
6564
+ defaultVariants: {
6565
+ variant: "default"
7004
6566
  }
7005
6567
  }
7006
- }
7007
- function ordersEqual(a, b) {
7008
- if (a.length !== b.length) return false;
7009
- for (let i = 0; i < a.length; i++) {
7010
- const left = a[i];
7011
- const right = b[i];
7012
- if (left.length !== right.length) return false;
7013
- for (let j = 0; j < left.length; j++) {
7014
- if (left[j] !== right[j]) return false;
7015
- }
7016
- }
7017
- return true;
7018
- }
7019
- function planFooterLinkMove(hrefKey, targetColIndex, insertIndex) {
7020
- const order = getFooterOrderFromDom();
7021
- if (targetColIndex < 0 || targetColIndex >= order.length) return null;
7022
- let fromCol = -1;
7023
- let fromIdx = -1;
7024
- for (let c = 0; c < order.length; c++) {
7025
- const idx = order[c].indexOf(hrefKey);
7026
- if (idx >= 0) {
7027
- fromCol = c;
7028
- fromIdx = idx;
7029
- break;
7030
- }
7031
- }
7032
- if (fromCol < 0) return null;
7033
- const next = order.map((col) => [...col]);
7034
- next[fromCol].splice(fromIdx, 1);
7035
- let adjusted = insertIndex;
7036
- if (fromCol === targetColIndex && fromIdx < insertIndex) adjusted -= 1;
7037
- adjusted = Math.max(0, Math.min(adjusted, next[targetColIndex].length));
7038
- next[targetColIndex].splice(adjusted, 0, hrefKey);
7039
- if (ordersEqual(next, order)) return null;
7040
- return next;
7041
- }
7042
- function planFooterColumnMove(fromIndex, toIndex) {
7043
- const order = getFooterOrderFromDom();
7044
- const columns = listFooterColumns();
7045
- if (fromIndex < 0 || fromIndex >= columns.length || toIndex < 0 || toIndex > columns.length) {
7046
- return null;
7047
- }
7048
- if (toIndex === fromIndex || toIndex === fromIndex + 1) return null;
7049
- const nextOrder = order.map((col) => [...col]);
7050
- const [movedOrder] = nextOrder.splice(fromIndex, 1);
7051
- if (!movedOrder) return null;
7052
- let adjusted = toIndex;
7053
- if (fromIndex < toIndex) adjusted -= 1;
7054
- adjusted = Math.max(0, Math.min(adjusted, nextOrder.length));
7055
- nextOrder.splice(adjusted, 0, movedOrder);
7056
- if (ordersEqual(nextOrder, order)) return null;
7057
- return nextOrder;
7058
- }
7059
- function parseFooterOrder(content) {
7060
- const raw = content[FOOTER_ORDER_KEY];
7061
- if (!raw) return null;
7062
- try {
7063
- const parsed = JSON.parse(raw);
7064
- if (!Array.isArray(parsed)) return null;
7065
- return parsed.filter((col) => Array.isArray(col)).map((col) => col.filter((key) => typeof key === "string" && key.length > 0));
7066
- } catch {
7067
- return null;
7068
- }
7069
- }
7070
- function reconcileFooterOrderFromContent(content) {
7071
- const order = parseFooterOrder(content);
7072
- if (!order?.length) return;
7073
- const current = getFooterOrderFromDom();
7074
- if (ordersEqual(order, current)) {
7075
- syncFooterColumnIndices(listFooterColumns());
7076
- return;
7077
- }
7078
- applyFooterOrder(order);
7079
- }
7080
- function isRowLayoutColumn(links) {
7081
- if (links.length < 2) return false;
7082
- const firstTop = links[0].getBoundingClientRect().top;
7083
- return links.every((link) => Math.abs(link.getBoundingClientRect().top - firstTop) < 4);
7084
- }
7085
- function buildLinkDropSlots(column, columnIndex) {
7086
- const links = listFooterLinksInColumn(column);
7087
- const colRect = column.getBoundingClientRect();
7088
- const slots = [];
7089
- const barThickness = 3;
7090
- if (links.length === 0) {
7091
- slots.push({
7092
- insertIndex: 0,
7093
- columnIndex,
7094
- left: colRect.left,
7095
- top: colRect.top + colRect.height / 2 - barThickness / 2,
7096
- width: colRect.width,
7097
- height: barThickness,
7098
- direction: "horizontal"
7099
- });
7100
- return slots;
7101
- }
7102
- if (isRowLayoutColumn(links)) {
7103
- for (let i = 0; i <= links.length; i++) {
7104
- let left;
7105
- if (i === 0) {
7106
- left = links[0].getBoundingClientRect().left - barThickness / 2;
7107
- } else if (i === links.length) {
7108
- const last = links[links.length - 1].getBoundingClientRect();
7109
- left = last.right - barThickness / 2;
7110
- } else {
7111
- const prev = links[i - 1].getBoundingClientRect();
7112
- const next = links[i].getBoundingClientRect();
7113
- left = (prev.right + next.left) / 2 - barThickness / 2;
7114
- }
7115
- const heightRef = i === 0 ? links[0].getBoundingClientRect() : i === links.length ? links[links.length - 1].getBoundingClientRect() : links[i].getBoundingClientRect();
7116
- slots.push({
7117
- insertIndex: i,
7118
- columnIndex,
7119
- left,
7120
- top: heightRef.top,
7121
- width: barThickness,
7122
- height: Math.max(heightRef.height, colRect.height * 0.8),
7123
- direction: "vertical"
7124
- });
7125
- }
7126
- return slots;
7127
- }
7128
- for (let i = 0; i <= links.length; i++) {
7129
- let top;
7130
- if (i === 0) {
7131
- top = links[0].getBoundingClientRect().top - barThickness / 2;
7132
- } else if (i === links.length) {
7133
- const last = links[links.length - 1].getBoundingClientRect();
7134
- top = last.bottom - barThickness / 2;
7135
- } else {
7136
- const prev = links[i - 1].getBoundingClientRect();
7137
- const next = links[i].getBoundingClientRect();
7138
- top = (prev.bottom + next.top) / 2 - barThickness / 2;
7139
- }
7140
- const widthRef = i === 0 ? links[0].getBoundingClientRect() : i === links.length ? links[links.length - 1].getBoundingClientRect() : links[i].getBoundingClientRect();
7141
- slots.push({
7142
- insertIndex: i,
7143
- columnIndex,
7144
- left: widthRef.left,
7145
- top,
7146
- width: Math.max(widthRef.width, colRect.width * 0.8),
7147
- height: barThickness,
7148
- direction: "horizontal"
7149
- });
7150
- }
7151
- return slots;
7152
- }
7153
- function buildColumnDropSlots() {
7154
- const columns = listFooterColumns();
7155
- const slots = [];
7156
- const barThickness = 3;
7157
- if (columns.length === 0) return slots;
7158
- for (let i = 0; i <= columns.length; i++) {
7159
- let left;
7160
- let height;
7161
- let top;
7162
- if (i === 0) {
7163
- const first = columns[0].getBoundingClientRect();
7164
- left = first.left - barThickness / 2;
7165
- top = first.top;
7166
- height = first.height;
7167
- } else if (i === columns.length) {
7168
- const last = columns[columns.length - 1].getBoundingClientRect();
7169
- left = last.right - barThickness / 2;
7170
- top = last.top;
7171
- height = last.height;
7172
- } else {
7173
- const prev = columns[i - 1].getBoundingClientRect();
7174
- const next = columns[i].getBoundingClientRect();
7175
- left = (prev.right + next.left) / 2 - barThickness / 2;
7176
- top = Math.min(prev.top, next.top);
7177
- height = Math.max(prev.bottom, next.bottom) - top;
7178
- }
7179
- slots.push({
7180
- insertIndex: i,
7181
- columnIndex: i,
7182
- left,
7183
- top,
7184
- width: barThickness,
7185
- height: Math.max(height, 24),
7186
- direction: "vertical"
7187
- });
7188
- }
7189
- return slots;
7190
- }
7191
- function hitTestLinkDropSlot(clientX, clientY, draggedHrefKey) {
7192
- const columns = listFooterColumns();
7193
- let best = null;
7194
- for (let c = 0; c < columns.length; c++) {
7195
- const col = columns[c];
7196
- const colRect = col.getBoundingClientRect();
7197
- const inColX = clientX >= colRect.left - 24 && clientX <= colRect.right + 24;
7198
- if (!inColX) continue;
7199
- const slots = buildLinkDropSlots(col, c).filter((slot) => {
7200
- const links = listFooterLinksInColumn(col);
7201
- const fromIdx = links.findIndex(
7202
- (el) => el.getAttribute("data-ohw-href-key") === draggedHrefKey
7203
- );
7204
- if (fromIdx < 0) return true;
7205
- if (c === findColumnIndexForKey(draggedHrefKey) && (slot.insertIndex === fromIdx || slot.insertIndex === fromIdx + 1)) {
7206
- return true;
7207
- }
7208
- return true;
7209
- });
7210
- for (const slot of slots) {
7211
- const dist = slot.direction === "vertical" ? Math.abs(clientX - (slot.left + slot.width / 2)) : Math.abs(clientY - (slot.top + slot.height / 2));
7212
- if (!best || dist < best.dist) best = { slot, dist };
7213
- }
7214
- }
7215
- return best?.slot ?? null;
7216
- }
7217
- function findColumnIndexForKey(hrefKey) {
7218
- const order = getFooterOrderFromDom();
7219
- for (let c = 0; c < order.length; c++) {
7220
- if (order[c].includes(hrefKey)) return c;
7221
- }
7222
- return -1;
7223
- }
7224
- function hitTestColumnDropSlot(clientX, _clientY) {
7225
- const slots = buildColumnDropSlots();
7226
- let best = null;
7227
- for (const slot of slots) {
7228
- const cx2 = slot.left + slot.width / 2;
7229
- const dist = Math.abs(clientX - cx2);
7230
- if (!best || dist < best.dist) best = { slot, dist };
7231
- }
7232
- return best?.slot ?? null;
7233
- }
7234
-
7235
- // src/lib/item-drag-interaction.ts
7236
- function disableNativeHrefDrag(el) {
7237
- if (el.draggable) el.draggable = false;
7238
- if (el.getAttribute("draggable") !== "false") {
7239
- el.setAttribute("draggable", "false");
7240
- }
7241
- }
7242
- function clearTextSelection() {
7243
- const sel = window.getSelection();
7244
- if (sel && !sel.isCollapsed) sel.removeAllRanges();
7245
- }
7246
- function armItemPressDrag() {
7247
- document.documentElement.setAttribute("data-ohw-footer-press-drag", "");
7248
- }
7249
- function lockItemDuringDrag() {
7250
- document.documentElement.removeAttribute("data-ohw-footer-press-drag");
7251
- document.documentElement.setAttribute("data-ohw-item-dragging", "");
7252
- clearTextSelection();
7253
- }
7254
- function unlockItemDragInteraction() {
7255
- document.documentElement.removeAttribute("data-ohw-footer-press-drag");
7256
- document.documentElement.removeAttribute("data-ohw-item-dragging");
7257
- clearTextSelection();
7258
- }
7259
- var armFooterPressDrag = armItemPressDrag;
7260
- var lockFooterDuringDrag = lockItemDuringDrag;
7261
- var unlockFooterDragInteraction = unlockItemDragInteraction;
7262
-
7263
- // src/lib/nav-dnd.ts
7264
- function listReorderableNavItems() {
7265
- return listNavbarItems().filter((el) => {
7266
- const key = el.getAttribute("data-ohw-href-key");
7267
- return isNavbarHrefKey(key) && !el.closest("[data-ohw-nav-children]");
7268
- });
7269
- }
7270
- function listNavChildren(parentHrefKey) {
7271
- const items = listNavbarItems();
7272
- const parent = items.find((el) => el.getAttribute("data-ohw-href-key") === parentHrefKey);
7273
- if (!parent) return [];
7274
- const group = parent.closest("[data-ohw-nav-group]");
7275
- const childrenRoot = group?.querySelector(":scope > [data-ohw-nav-children]");
7276
- if (!childrenRoot) return [];
7277
- return Array.from(childrenRoot.querySelectorAll(":scope > [data-ohw-href-key]")).filter(
7278
- (el) => isNavbarHrefKey(el.getAttribute("data-ohw-href-key"))
7279
- );
7280
- }
7281
- function activeNavListContainer() {
7282
- const desktop = getNavbarDesktopContainer();
7283
- if (desktop && desktop.getClientRects().length > 0) {
7284
- const style = window.getComputedStyle(desktop);
7285
- if (style.display !== "none" && style.visibility !== "hidden") return desktop;
7286
- }
7287
- return getNavbarDrawerContainer();
7288
- }
7289
- function buildRootNavDropSlots() {
7290
- const items = listReorderableNavItems();
7291
- const slots = [];
7292
- const barThickness = 3;
7293
- if (items.length === 0) {
7294
- const container = activeNavListContainer();
7295
- if (!container) return slots;
7296
- const rect = container.getBoundingClientRect();
7297
- slots.push({
7298
- insertIndex: 0,
7299
- parentId: null,
7300
- left: rect.left + rect.width / 2 - barThickness / 2,
7301
- top: rect.top,
7302
- width: barThickness,
7303
- height: Math.max(rect.height, 24),
7304
- direction: "vertical"
7305
- });
7306
- return slots;
7307
- }
7308
- for (let i = 0; i <= items.length; i++) {
7309
- let left;
7310
- let top;
7311
- let height;
7312
- if (i === 0) {
7313
- const first = items[0].getBoundingClientRect();
7314
- left = first.left - barThickness / 2;
7315
- top = first.top;
7316
- height = first.height;
7317
- } else if (i === items.length) {
7318
- const last = items[items.length - 1].getBoundingClientRect();
7319
- left = last.right - barThickness / 2;
7320
- top = last.top;
7321
- height = last.height;
7322
- } else {
7323
- const prev = items[i - 1].getBoundingClientRect();
7324
- const next = items[i].getBoundingClientRect();
7325
- left = (prev.right + next.left) / 2 - barThickness / 2;
7326
- top = Math.min(prev.top, next.top);
7327
- height = Math.max(prev.bottom, next.bottom) - top;
7328
- }
7329
- slots.push({
7330
- insertIndex: i,
7331
- parentId: null,
7332
- left,
7333
- top,
7334
- width: barThickness,
7335
- height: Math.max(height, 24),
7336
- direction: "vertical"
7337
- });
7338
- }
7339
- return slots;
7340
- }
7341
- function buildChildNavDropSlots(parentHrefKey) {
7342
- const children = listNavChildren(parentHrefKey);
7343
- const slots = [];
7344
- const barThickness = 3;
7345
- if (children.length === 0) return slots;
7346
- for (let i = 0; i <= children.length; i++) {
7347
- let top;
7348
- let width;
7349
- let left;
7350
- if (i === 0) {
7351
- const first = children[0].getBoundingClientRect();
7352
- top = first.top - barThickness / 2;
7353
- left = first.left;
7354
- width = first.width;
7355
- } else if (i === children.length) {
7356
- const last = children[children.length - 1].getBoundingClientRect();
7357
- top = last.bottom - barThickness / 2;
7358
- left = last.left;
7359
- width = last.width;
7360
- } else {
7361
- const prev = children[i - 1].getBoundingClientRect();
7362
- const next = children[i].getBoundingClientRect();
7363
- top = (prev.bottom + next.top) / 2 - barThickness / 2;
7364
- left = Math.min(prev.left, next.left);
7365
- width = Math.max(prev.right, next.right) - left;
7366
- }
7367
- slots.push({
7368
- insertIndex: i,
7369
- parentId: parentHrefKey,
7370
- left,
7371
- top,
7372
- width: Math.max(width, 40),
7373
- height: barThickness,
7374
- direction: "horizontal"
7375
- });
7376
- }
7377
- return slots;
7378
- }
7379
- function buildAllNavDropSlots(draggedHrefKey) {
7380
- const slots = [...buildRootNavDropSlots()];
7381
- for (const item of listReorderableNavItems()) {
7382
- const key = item.getAttribute("data-ohw-href-key");
7383
- if (!key || key === draggedHrefKey) continue;
7384
- const group = item.closest("[data-ohw-nav-group]");
7385
- if (!group?.querySelector(":scope > [data-ohw-nav-children]")) continue;
7386
- slots.push(...buildChildNavDropSlots(key));
7387
- }
7388
- const dragged = listNavbarItems().find((el) => el.getAttribute("data-ohw-href-key") === draggedHrefKey);
7389
- const parentGroup = dragged?.closest("[data-ohw-nav-children]")?.closest("[data-ohw-nav-group]");
7390
- const parentTrigger = parentGroup?.querySelector(":scope > [data-ohw-href-key]");
7391
- const parentKey = parentTrigger?.getAttribute("data-ohw-href-key");
7392
- if (parentKey && isNavbarHrefKey(parentKey)) {
7393
- const existing = slots.some((s) => s.parentId === parentKey);
7394
- if (!existing) slots.push(...buildChildNavDropSlots(parentKey));
7395
- }
7396
- return slots;
7397
- }
7398
- function hitTestNavDropSlot(clientX, clientY, draggedHrefKey) {
7399
- const slots = buildAllNavDropSlots(draggedHrefKey);
7400
- let best = null;
7401
- for (const slot of slots) {
7402
- const cx2 = slot.left + slot.width / 2;
7403
- const cy = slot.top + slot.height / 2;
7404
- 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;
7405
- if (!best || dist < best.dist) best = { slot, dist };
7406
- }
7407
- return best?.slot ?? null;
7408
- }
7409
- function siblingRectsForNavDrag(draggedEl, activeParentId) {
7410
- if (activeParentId) {
7411
- return listNavChildren(activeParentId).filter((el) => el !== draggedEl).map((el) => el.getBoundingClientRect());
7412
- }
7413
- return listReorderableNavItems().filter((el) => el !== draggedEl).map((el) => el.getBoundingClientRect());
7414
- }
7415
-
7416
- // src/useNavItemDrag.ts
7417
- var import_react8 = require("react");
7418
- function useNavItemDrag({
7419
- isEditMode,
7420
- editContentRef,
7421
- selectedElRef,
7422
- activeElRef,
7423
- footerDragRef,
7424
- suppressNextClickRef,
7425
- suppressClickUntilRef,
7426
- siblingHintElRef,
7427
- postToParentRef,
7428
- deselectRef,
7429
- selectRef,
7430
- deactivateRef,
7431
- setLinkPopover,
7432
- linkPopoverOpenRef,
7433
- setIsItemDragging,
7434
- setDraggedItemRect,
7435
- setSiblingHintRect,
7436
- setSiblingHintRects,
7437
- setToolbarRect,
7438
- getNavigationItemAnchor: getNavigationItemAnchor2,
7439
- isDragHandleDisabled: isDragHandleDisabled2,
7440
- isNavbarButton: isNavbarButton3
7441
- }) {
7442
- const navDragRef = (0, import_react8.useRef)(null);
7443
- const [navDropSlots, setNavDropSlots] = (0, import_react8.useState)([]);
7444
- const [activeNavDropIndex, setActiveNavDropIndex] = (0, import_react8.useState)(null);
7445
- const navPointerDragRef = (0, import_react8.useRef)(null);
7446
- const clearNavDragVisuals = (0, import_react8.useCallback)(() => {
7447
- navDragRef.current = null;
7448
- setNavDropSlots([]);
7449
- setActiveNavDropIndex(null);
7450
- setDraggedItemRect(null);
7451
- setSiblingHintRects([]);
7452
- setIsItemDragging(false);
7453
- unlockItemDragInteraction();
7454
- }, [setDraggedItemRect, setIsItemDragging, setSiblingHintRects]);
7455
- const refreshNavDragVisuals = (0, import_react8.useCallback)(
7456
- (session, activeSlot, clientX, clientY) => {
7457
- setDraggedItemRect(session.draggedEl.getBoundingClientRect());
7458
- if (typeof clientX === "number" && typeof clientY === "number") {
7459
- session.lastClientX = clientX;
7460
- session.lastClientY = clientY;
7461
- }
7462
- session.activeSlot = activeSlot;
7463
- const parentId = activeSlot?.parentId ?? null;
7464
- setSiblingHintRects(siblingRectsForNavDrag(session.draggedEl, parentId));
7465
- const slots = buildAllNavDropSlots(session.hrefKey);
7466
- setNavDropSlots(slots);
7467
- const activeIdx = activeSlot ? slots.findIndex(
7468
- (s) => s.parentId === activeSlot.parentId && s.insertIndex === activeSlot.insertIndex
7469
- ) : -1;
7470
- setActiveNavDropIndex(activeIdx >= 0 ? activeIdx : null);
7471
- },
7472
- [setDraggedItemRect, setSiblingHintRects]
7473
- );
7474
- const refreshNavDragVisualsRef = (0, import_react8.useRef)(refreshNavDragVisuals);
7475
- refreshNavDragVisualsRef.current = refreshNavDragVisuals;
7476
- const commitNavDragRef = (0, import_react8.useRef)(() => {
7477
- });
7478
- const beginNavDragRef = (0, import_react8.useRef)(() => {
7479
- });
7480
- const beginNavDrag = (0, import_react8.useCallback)(
7481
- (session) => {
7482
- const rect = session.draggedEl.getBoundingClientRect();
7483
- session.lastClientX = session.lastClientX || rect.left + rect.width / 2;
7484
- session.lastClientY = session.lastClientY || rect.top + rect.height / 2;
7485
- session.activeSlot = session.activeSlot ?? null;
7486
- navDragRef.current = session;
7487
- setIsItemDragging(true);
7488
- lockItemDuringDrag();
7489
- siblingHintElRef.current = null;
7490
- setSiblingHintRect(null);
7491
- if (session.wasSelected && selectedElRef.current === session.draggedEl) {
7492
- setToolbarRect(rect);
7493
- }
7494
- const initialSlot = hitTestNavDropSlot(session.lastClientX, session.lastClientY, session.hrefKey);
7495
- refreshNavDragVisuals(session, initialSlot, session.lastClientX, session.lastClientY);
7496
- },
7497
- [
7498
- refreshNavDragVisuals,
7499
- selectedElRef,
7500
- setIsItemDragging,
7501
- setSiblingHintRect,
7502
- setToolbarRect,
7503
- siblingHintElRef
7504
- ]
7505
- );
7506
- beginNavDragRef.current = beginNavDrag;
7507
- const commitNavDrag = (0, import_react8.useCallback)(
7508
- (clientX, clientY) => {
7509
- const session = navDragRef.current;
7510
- if (!session) {
7511
- clearNavDragVisuals();
7512
- return;
7513
- }
7514
- const x = typeof clientX === "number" && (clientX !== 0 || clientY !== 0) ? clientX : session.lastClientX;
7515
- const y = typeof clientY === "number" && (clientX !== 0 || clientY !== 0) ? clientY : session.lastClientY;
7516
- const slot = session.activeSlot ?? hitTestNavDropSlot(x, y, session.hrefKey);
7517
- const planned = slot != null ? planNavItemMove(session.hrefKey, slot.parentId, slot.insertIndex) : null;
7518
- const wasSelected = session.wasSelected;
7519
- const hrefKey = session.hrefKey;
7520
- const applySelectionAfterDrop = () => {
7521
- if (!wasSelected) {
7522
- deselectRef.current();
7523
- return;
7524
- }
7525
- const desktop = document.querySelector("[data-ohw-nav-container]");
7526
- const drawer = document.querySelector("[data-ohw-nav-drawer]");
7527
- 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)}"]`);
7528
- if (link) {
7529
- selectRef.current(link);
7530
- return;
7531
- }
7532
- deselectRef.current();
7533
- };
7534
- if (planned) {
7535
- editContentRef.current = {
7536
- ...editContentRef.current,
7537
- [NAV_ORDER_KEY]: planned.orderJson
7538
- };
7539
- applyNavForest(planned.forest);
7540
- document.querySelectorAll(
7541
- "nav [data-ohw-href-key], [data-ohw-nav-container] [data-ohw-href-key], [data-ohw-nav-drawer] [data-ohw-href-key]"
7542
- ).forEach((el) => {
7543
- if (isNavbarHrefKey(el.getAttribute("data-ohw-href-key"))) {
7544
- disableNativeHrefDrag(el);
7545
- }
7546
- });
7547
- postToParentRef.current({
7548
- type: "ow:change",
7549
- nodes: [{ key: NAV_ORDER_KEY, text: planned.orderJson }]
7550
- });
7551
- applySelectionAfterDrop();
7552
- clearNavDragVisuals();
7553
- requestAnimationFrame(() => {
7554
- if (editContentRef.current[NAV_ORDER_KEY] === planned.orderJson) {
7555
- applyNavForest(planned.forest);
7556
- }
7557
- applySelectionAfterDrop();
7558
- requestAnimationFrame(applySelectionAfterDrop);
7559
- });
7560
- return;
7561
- }
7562
- applySelectionAfterDrop();
7563
- clearNavDragVisuals();
7564
- },
7565
- [clearNavDragVisuals, deselectRef, editContentRef, postToParentRef, selectRef]
7566
- );
7567
- commitNavDragRef.current = commitNavDrag;
7568
- const startNavLinkDrag = (0, import_react8.useCallback)(
7569
- (anchor, clientX, clientY, wasSelected) => {
7570
- if (footerDragRef.current) return false;
7571
- const hrefKey = anchor.getAttribute("data-ohw-href-key");
7572
- if (!hrefKey || !isNavbarHrefKey(hrefKey)) return false;
7573
- if (isNavbarButton3(anchor) || isDragHandleDisabled2(anchor)) return false;
7574
- beginNavDrag({
7575
- hrefKey,
7576
- wasSelected,
7577
- draggedEl: anchor,
7578
- lastClientX: clientX,
7579
- lastClientY: clientY,
7580
- activeSlot: null
7581
- });
7582
- return true;
7583
- },
7584
- [beginNavDrag, footerDragRef, isDragHandleDisabled2, isNavbarButton3]
7585
- );
7586
- const onNavDragOver = (0, import_react8.useCallback)(
7587
- (e) => {
7588
- const session = navDragRef.current;
7589
- if (!session) return false;
7590
- e.preventDefault();
7591
- if (e.dataTransfer) e.dataTransfer.dropEffect = "move";
7592
- const slot = hitTestNavDropSlot(e.clientX, e.clientY, session.hrefKey);
7593
- refreshNavDragVisualsRef.current(session, slot, e.clientX, e.clientY);
7594
- return true;
7595
- },
7596
- []
7597
- );
7598
- (0, import_react8.useEffect)(() => {
7599
- if (!isEditMode) return;
7600
- const THRESHOLD = 10;
7601
- const resolveWasSelected = (el) => {
7602
- if (selectedElRef.current === el) return true;
7603
- const activeAnchor = activeElRef.current ? getNavigationItemAnchor2(activeElRef.current) : null;
7604
- return activeAnchor === el;
7605
- };
7606
- const onPointerDown = (e) => {
7607
- if (e.button !== 0) return;
7608
- if (navDragRef.current || footerDragRef.current) return;
7609
- const target = e.target;
7610
- if (!target) return;
7611
- if (target.closest(
7612
- '[data-ohw-drag-handle-container], [data-slot="drag-handle"], [data-ohw-toolbar], [data-ohw-item-toolbar-anchor], [data-ohw-link-popover-root]'
7613
- )) {
7614
- return;
7615
- }
7616
- if (target.closest("[data-ohw-item-drag-surface]")) return;
7617
- const anchor = getNavigationItemAnchor2(target);
7618
- const hrefKey = anchor?.getAttribute("data-ohw-href-key") ?? null;
7619
- if (!anchor || !isNavbarHrefKey(hrefKey)) return;
7620
- if (isNavbarButton3(anchor) || isDragHandleDisabled2(anchor)) return;
7621
- navPointerDragRef.current = {
7622
- el: anchor,
7623
- startX: e.clientX,
7624
- startY: e.clientY,
7625
- pointerId: e.pointerId,
7626
- wasSelected: resolveWasSelected(anchor),
7627
- started: false
7628
- };
7629
- };
7630
- const onPointerMove = (e) => {
7631
- const pending = navPointerDragRef.current;
7632
- if (!pending) return;
7633
- if (pending.started) {
7634
- e.preventDefault();
7635
- clearTextSelection();
7636
- const session = navDragRef.current;
7637
- if (!session) return;
7638
- const slot = hitTestNavDropSlot(e.clientX, e.clientY, session.hrefKey);
7639
- refreshNavDragVisualsRef.current(session, slot, e.clientX, e.clientY);
7640
- return;
7641
- }
7642
- const dx = e.clientX - pending.startX;
7643
- const dy = e.clientY - pending.startY;
7644
- if (dx * dx + dy * dy < THRESHOLD * THRESHOLD) return;
7645
- e.preventDefault();
7646
- pending.started = true;
7647
- armItemPressDrag();
7648
- clearTextSelection();
7649
- try {
7650
- document.body.setPointerCapture(pending.pointerId);
7651
- } catch {
7652
- }
7653
- if (linkPopoverOpenRef.current) setLinkPopover(null);
7654
- if (activeElRef.current) deactivateRef.current();
7655
- const key = pending.el.getAttribute("data-ohw-href-key");
7656
- if (!key) return;
7657
- beginNavDragRef.current({
7658
- hrefKey: key,
7659
- wasSelected: pending.wasSelected,
7660
- draggedEl: pending.el,
7661
- lastClientX: e.clientX,
7662
- lastClientY: e.clientY,
7663
- activeSlot: null
7664
- });
7665
- };
7666
- const endPointerDrag = (e) => {
7667
- const pending = navPointerDragRef.current;
7668
- navPointerDragRef.current = null;
7669
- try {
7670
- if (document.body.hasPointerCapture(e.pointerId)) {
7671
- document.body.releasePointerCapture(e.pointerId);
7672
- }
7673
- } catch {
7674
- }
7675
- if (!pending) return;
7676
- if (!pending.started) {
7677
- unlockItemDragInteraction();
7678
- return;
7679
- }
7680
- suppressNextClickRef.current = true;
7681
- suppressClickUntilRef.current = Date.now() + 500;
7682
- commitNavDragRef.current(e.clientX, e.clientY);
7683
- };
7684
- const blockSelectStart = (e) => {
7685
- if (navDragRef.current || navPointerDragRef.current?.started || document.documentElement.hasAttribute("data-ohw-footer-press-drag")) {
7686
- e.preventDefault();
7687
- }
7688
- };
7689
- const onDragEnd = () => {
7690
- if (!navDragRef.current) return;
7691
- suppressNextClickRef.current = true;
7692
- suppressClickUntilRef.current = Date.now() + 500;
7693
- commitNavDragRef.current();
7694
- };
7695
- document.addEventListener("pointerdown", onPointerDown, true);
7696
- document.addEventListener("pointermove", onPointerMove, true);
7697
- document.addEventListener("pointerup", endPointerDrag, true);
7698
- document.addEventListener("pointercancel", endPointerDrag, true);
7699
- document.addEventListener("selectstart", blockSelectStart, true);
7700
- document.addEventListener("dragend", onDragEnd, true);
7701
- return () => {
7702
- document.removeEventListener("pointerdown", onPointerDown, true);
7703
- document.removeEventListener("pointermove", onPointerMove, true);
7704
- document.removeEventListener("pointerup", endPointerDrag, true);
7705
- document.removeEventListener("pointercancel", endPointerDrag, true);
7706
- document.removeEventListener("selectstart", blockSelectStart, true);
7707
- document.removeEventListener("dragend", onDragEnd, true);
7708
- unlockItemDragInteraction();
7709
- };
7710
- }, [
7711
- activeElRef,
7712
- deactivateRef,
7713
- footerDragRef,
7714
- getNavigationItemAnchor2,
7715
- isDragHandleDisabled2,
7716
- isEditMode,
7717
- isNavbarButton3,
7718
- linkPopoverOpenRef,
7719
- selectedElRef,
7720
- setLinkPopover,
7721
- suppressNextClickRef
7722
- ]);
7723
- const armNavPressFromChrome = (0, import_react8.useCallback)(
7724
- (selected, clientX, clientY, pointerId) => {
7725
- const hrefKey = selected.getAttribute("data-ohw-href-key");
7726
- if (!hrefKey || !isNavbarHrefKey(hrefKey)) return false;
7727
- if (isNavbarButton3(selected) || isDragHandleDisabled2(selected)) return false;
7728
- navPointerDragRef.current = {
7729
- el: selected,
7730
- startX: clientX,
7731
- startY: clientY,
7732
- pointerId,
7733
- wasSelected: true,
7734
- started: false
7735
- };
7736
- return true;
7737
- },
7738
- [isDragHandleDisabled2, isNavbarButton3]
7739
- );
7740
- return {
7741
- navDragRef,
7742
- navDropSlots,
7743
- activeNavDropIndex,
7744
- startNavLinkDrag,
7745
- commitNavDrag,
7746
- onNavDragOver,
7747
- armNavPressFromChrome,
7748
- clearNavDragVisuals,
7749
- refreshNavDragVisualsRef
7750
- };
7751
- }
7752
-
7753
- // src/ui/navbar-container-chrome.tsx
7754
- var import_lucide_react10 = require("lucide-react");
7755
- var import_jsx_runtime21 = require("react/jsx-runtime");
7756
- function NavbarContainerChrome({
7757
- rect,
7758
- onAdd
7759
- }) {
7760
- const chromeGap = 6;
7761
- return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
7762
- "div",
7763
- {
7764
- "data-ohw-navbar-container-chrome": "",
7765
- "data-ohw-bridge": "",
7766
- className: "pointer-events-none fixed z-[2147483647]",
7767
- style: {
7768
- top: rect.top - chromeGap,
7769
- left: rect.left - chromeGap,
7770
- width: rect.width + chromeGap * 2,
7771
- height: rect.height + chromeGap * 2
7772
- },
7773
- children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
7774
- "button",
7775
- {
7776
- type: "button",
7777
- "data-ohw-navbar-add-button": "",
7778
- 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",
7779
- style: { top: "70%" },
7780
- "aria-label": "Add item",
7781
- onMouseDown: (e) => {
7782
- e.preventDefault();
7783
- e.stopPropagation();
7784
- },
7785
- onClick: (e) => {
7786
- e.preventDefault();
7787
- e.stopPropagation();
7788
- onAdd();
7789
- },
7790
- children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react10.Plus, { className: "size-4 shrink-0 text-foreground", "aria-hidden": true })
7791
- }
7792
- )
7793
- }
7794
- );
7795
- }
7796
-
7797
- // src/ui/drop-indicator.tsx
7798
- var React10 = __toESM(require("react"), 1);
7799
- var import_jsx_runtime22 = require("react/jsx-runtime");
7800
- var dropIndicatorVariants = cva(
7801
- "ov-gap-line pointer-events-none shrink-0 transition-opacity duration-150",
7802
- {
7803
- variants: {
7804
- direction: {
7805
- vertical: "h-6 w-[3px]",
7806
- horizontal: "h-[3px] w-[200px]"
7807
- },
7808
- state: {
7809
- default: "opacity-0",
7810
- hover: "opacity-100",
7811
- dragIdle: "opacity-40",
7812
- dragActive: "opacity-100"
7813
- }
7814
- },
7815
- defaultVariants: {
7816
- direction: "vertical",
7817
- state: "default"
7818
- }
7819
- }
7820
- );
7821
- var DropIndicator = React10.forwardRef(
7822
- ({ className, direction, state, ...props }, ref) => {
7823
- return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
7824
- "div",
7825
- {
7826
- ref,
7827
- "data-slot": "drop-indicator",
7828
- "data-direction": direction ?? "vertical",
7829
- "data-state": state ?? "default",
7830
- "aria-hidden": "true",
7831
- className: cn(dropIndicatorVariants({ direction, state }), className),
7832
- ...props
7833
- }
7834
- );
7835
- }
7836
- );
7837
- DropIndicator.displayName = "DropIndicator";
7838
-
7839
- // src/ui/badge.tsx
7840
- var import_jsx_runtime23 = require("react/jsx-runtime");
7841
- var badgeVariants = cva(
7842
- "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",
7843
- {
7844
- variants: {
7845
- variant: {
7846
- default: "border-transparent bg-primary text-primary-foreground",
7847
- secondary: "border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80",
7848
- destructive: "border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80",
7849
- outline: "text-foreground"
7850
- }
7851
- },
7852
- defaultVariants: {
7853
- variant: "default"
7854
- }
7855
- }
7856
- );
7857
- function Badge({ className, variant, ...props }) {
7858
- return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { className: cn(badgeVariants({ variant }), className), ...props });
6568
+ );
6569
+ function Badge({ className, variant, ...props }) {
6570
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { className: cn(badgeVariants({ variant }), className), ...props });
7859
6571
  }
7860
6572
 
7861
6573
  // src/OhhwellsBridge.tsx
7862
6574
  var import_lucide_react11 = require("lucide-react");
7863
- var import_jsx_runtime24 = require("react/jsx-runtime");
6575
+ var import_jsx_runtime23 = require("react/jsx-runtime");
7864
6576
  var PRIMARY2 = "#0885FE";
7865
6577
  var IMAGE_FADE_MS = 300;
7866
6578
  function runOpacityFade(el, onDone) {
@@ -8039,7 +6751,7 @@ function mountSchedulingWidget(insertAfter, notifyOnConnect = false, scheduleId,
8039
6751
  const root = (0, import_client.createRoot)(container);
8040
6752
  (0, import_react_dom2.flushSync)(() => {
8041
6753
  root.render(
8042
- /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
6754
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
8043
6755
  SchedulingWidget,
8044
6756
  {
8045
6757
  notifyOnConnect,
@@ -8101,20 +6813,7 @@ function isDragHandleDisabled(el) {
8101
6813
  return raw === "true" || raw === "";
8102
6814
  });
8103
6815
  }
8104
- function canDragNavigationItem(anchor) {
8105
- const { key, disabled } = getNavigationItemReorderState(anchor);
8106
- return Boolean(key) && !disabled;
8107
- }
8108
- function syncNavigationDragCursorAttrs() {
8109
- document.querySelectorAll("nav [data-ohw-href-key], footer [data-ohw-href-key]").forEach((el) => {
8110
- if (!isNavigationItem(el) || !canDragNavigationItem(el)) {
8111
- el.removeAttribute("data-ohw-can-drag");
8112
- return;
8113
- }
8114
- el.setAttribute("data-ohw-can-drag", "");
8115
- });
8116
- }
8117
- function collectEditableNodes(extraContent) {
6816
+ function collectEditableNodes() {
8118
6817
  const nodes = Array.from(document.querySelectorAll("[data-ohw-editable]")).map((el) => {
8119
6818
  if (el.dataset.ohwEditable === "image") {
8120
6819
  const img = el instanceof HTMLImageElement ? el : el.querySelector("img");
@@ -8142,14 +6841,6 @@ function collectEditableNodes(extraContent) {
8142
6841
  if (!key) return;
8143
6842
  nodes.push({ key, type: "link", text: getLinkHref2(el) });
8144
6843
  });
8145
- if (extraContent) {
8146
- for (const key of [NAV_ORDER_KEY, FOOTER_ORDER_KEY, NAV_COUNT_KEY]) {
8147
- const text = extraContent[key];
8148
- if (typeof text === "string" && text.length > 0) {
8149
- nodes.push({ key, type: "meta", text });
8150
- }
8151
- }
8152
- }
8153
6844
  document.querySelectorAll('[data-ohw-editable="video"]').forEach((el) => {
8154
6845
  const key = el.dataset.ohwKey ?? "";
8155
6846
  const video = getVideoEl(el);
@@ -8263,11 +6954,6 @@ function getNavigationItemAnchor(el) {
8263
6954
  function isNavigationItem(el) {
8264
6955
  return getNavigationItemAnchor(el) !== null;
8265
6956
  }
8266
- function listNavigationItems() {
8267
- return Array.from(
8268
- document.querySelectorAll("nav [data-ohw-href-key], footer [data-ohw-href-key]")
8269
- ).filter((el) => isNavigationItem(el));
8270
- }
8271
6957
  function getNavigationItemReorderState(anchor) {
8272
6958
  if (isNavbarButton2(anchor)) return { key: null, disabled: false };
8273
6959
  return getReorderHandleStateFromAnchor(anchor);
@@ -8296,20 +6982,18 @@ function getNavigationRoot(el) {
8296
6982
  if (explicit) return explicit;
8297
6983
  return el.closest("nav, footer, aside");
8298
6984
  }
8299
- function countFooterNavItems(el) {
8300
- return Array.from(el.querySelectorAll("[data-ohw-href-key]")).filter(isNavigationItem).length;
8301
- }
8302
6985
  function findFooterItemGroup(item) {
8303
- const explicit = item.closest("[data-ohw-footer-col], [data-ohw-footer-column]");
8304
- if (explicit) return explicit;
8305
6986
  const footer = item.closest("footer");
8306
6987
  if (!footer) return null;
8307
6988
  let node = item.parentElement;
8308
6989
  while (node && node !== footer) {
8309
- if (countFooterNavItems(node) >= 2) return node;
6990
+ const hasDirectNavItems = Array.from(
6991
+ node.querySelectorAll(":scope > [data-ohw-href-key]")
6992
+ ).some(isNavigationItem);
6993
+ if (hasDirectNavItems) return node;
8310
6994
  node = node.parentElement;
8311
6995
  }
8312
- return footer;
6996
+ return null;
8313
6997
  }
8314
6998
  function isNavigationRoot(el) {
8315
6999
  return el.hasAttribute("data-ohw-nav-root") || el.matches("nav, footer, aside");
@@ -8317,49 +7001,16 @@ function isNavigationRoot(el) {
8317
7001
  function isInferredFooterGroup(el) {
8318
7002
  const footer = el.closest("footer");
8319
7003
  if (!footer || el === footer) return false;
8320
- if (el.hasAttribute("data-ohw-footer-col") || el.hasAttribute("data-ohw-footer-column")) return true;
8321
- return countFooterNavItems(el) >= 2;
7004
+ return Array.from(el.querySelectorAll(":scope > [data-ohw-href-key]")).some(isNavigationItem);
8322
7005
  }
8323
7006
  function isNavigationContainer(el) {
8324
- return el.hasAttribute("data-ohw-nav-container") || el.hasAttribute("data-ohw-footer-col") || el.hasAttribute("data-ohw-footer-column") || isNavigationRoot(el) || isInferredFooterGroup(el);
8325
- }
8326
- function isNavbarLinksContainer(el) {
8327
- return el.hasAttribute("data-ohw-nav-container");
8328
- }
8329
- function getFooterColumn(el) {
8330
- return el.closest("[data-ohw-footer-col], [data-ohw-footer-column]");
8331
- }
8332
- function resolveFooterColumnSelectionTarget(target, clientX, clientY) {
8333
- if (getNavigationItemAnchor(target)) return null;
8334
- const column = getFooterColumn(target);
8335
- if (!column) return null;
8336
- if (isPointOverNavItem(column, clientX, clientY)) return null;
8337
- return column;
7007
+ return el.hasAttribute("data-ohw-nav-container") || isNavigationRoot(el) || isInferredFooterGroup(el);
8338
7008
  }
8339
7009
  function isPointOverNavigation(x, y) {
8340
- const roots = /* @__PURE__ */ new Set();
8341
7010
  const navRoot = document.querySelector("[data-ohw-nav-root]") ?? document.querySelector("nav");
8342
- if (navRoot) roots.add(navRoot);
8343
- const footer = document.querySelector("footer");
8344
- if (footer) roots.add(footer);
8345
- for (const root of roots) {
8346
- const r2 = root.getBoundingClientRect();
8347
- if (x >= r2.left && x <= r2.right && y >= r2.top && y <= r2.bottom) return true;
8348
- }
8349
- return false;
8350
- }
8351
- function findHoveredNavOrFooterContainer(x, y) {
8352
- const candidates = [
8353
- ...document.querySelectorAll("[data-ohw-nav-container]"),
8354
- ...listFooterColumns()
8355
- ];
8356
- for (const container of candidates) {
8357
- const r2 = container.getBoundingClientRect();
8358
- if (x < r2.left || x > r2.right || y < r2.top || y > r2.bottom) continue;
8359
- if (isPointOverNavItem(container, x, y)) continue;
8360
- return container;
8361
- }
8362
- return null;
7011
+ if (!navRoot) return false;
7012
+ const r2 = navRoot.getBoundingClientRect();
7013
+ return x >= r2.left && x <= r2.right && y >= r2.top && y <= r2.bottom;
8363
7014
  }
8364
7015
  function isPointOverNavItem(container, x, y) {
8365
7016
  return Array.from(container.querySelectorAll("[data-ohw-href-key]")).some((el) => {
@@ -8392,63 +7043,36 @@ function getNavigationSelectionParent(el) {
8392
7043
  if (isNavigationItem(el)) {
8393
7044
  const explicit = el.closest("[data-ohw-nav-container]");
8394
7045
  if (explicit) return explicit;
8395
- const footerColumn = getFooterColumn(el);
8396
- if (footerColumn) return footerColumn;
8397
7046
  const footerGroup = findFooterItemGroup(el);
8398
7047
  if (footerGroup) return footerGroup;
8399
7048
  return getNavigationRoot(el);
8400
7049
  }
8401
- if (el.hasAttribute("data-ohw-nav-container") || el.hasAttribute("data-ohw-footer-col") || el.hasAttribute("data-ohw-footer-column") || isInferredFooterGroup(el)) {
7050
+ if (el.hasAttribute("data-ohw-nav-container") || isInferredFooterGroup(el)) {
8402
7051
  return getNavigationRoot(el);
8403
7052
  }
8404
7053
  return null;
8405
7054
  }
8406
- function collectNavigationItemSiblingHintRects(selected) {
8407
- if (!isNavigationItem(selected)) return [];
8408
- const footerColumn = getFooterColumn(selected);
8409
- if (footerColumn) {
8410
- return listFooterLinksInColumn(footerColumn).filter((link) => link !== selected).map((link) => link.getBoundingClientRect());
8411
- }
8412
- const navContainer = selected.closest("[data-ohw-nav-container], [data-ohw-nav-drawer]");
8413
- if (navContainer) {
8414
- return Array.from(navContainer.querySelectorAll("[data-ohw-href-key]")).filter((el) => isNavigationItem(el) && el !== selected).map((el) => el.getBoundingClientRect());
8415
- }
8416
- return listNavigationItems().filter((el) => el !== selected).map((el) => el.getBoundingClientRect());
8417
- }
8418
7055
  function placeCaretAtPoint(el, x, y) {
8419
7056
  const selection = window.getSelection();
8420
7057
  if (!selection) return;
8421
- const overlays = Array.from(
8422
- document.querySelectorAll(
8423
- "[data-ohw-item-drag-surface], [data-ohw-item-interaction], [data-ohw-drag-handle-container], [data-ohw-item-toolbar-anchor]"
8424
- )
8425
- );
8426
- const prevPointerEvents = overlays.map((node) => node.style.pointerEvents);
8427
- for (const node of overlays) node.style.pointerEvents = "none";
8428
- try {
8429
- if (typeof document.caretRangeFromPoint === "function") {
8430
- const range = document.caretRangeFromPoint(x, y);
8431
- if (range && el.contains(range.startContainer)) {
8432
- selection.removeAllRanges();
8433
- selection.addRange(range);
8434
- return;
8435
- }
7058
+ if (typeof document.caretRangeFromPoint === "function") {
7059
+ const range = document.caretRangeFromPoint(x, y);
7060
+ if (range && el.contains(range.startContainer)) {
7061
+ selection.removeAllRanges();
7062
+ selection.addRange(range);
7063
+ return;
8436
7064
  }
8437
- const caretPositionFromPoint = document.caretPositionFromPoint;
8438
- if (typeof caretPositionFromPoint === "function") {
8439
- const position = caretPositionFromPoint(x, y);
8440
- if (position && el.contains(position.offsetNode)) {
8441
- const range = document.createRange();
8442
- range.setStart(position.offsetNode, position.offset);
8443
- range.collapse(true);
8444
- selection.removeAllRanges();
8445
- selection.addRange(range);
8446
- }
7065
+ }
7066
+ const caretPositionFromPoint = document.caretPositionFromPoint;
7067
+ if (typeof caretPositionFromPoint === "function") {
7068
+ const position = caretPositionFromPoint(x, y);
7069
+ if (position && el.contains(position.offsetNode)) {
7070
+ const range = document.createRange();
7071
+ range.setStart(position.offsetNode, position.offset);
7072
+ range.collapse(true);
7073
+ selection.removeAllRanges();
7074
+ selection.addRange(range);
8447
7075
  }
8448
- } finally {
8449
- overlays.forEach((node, i) => {
8450
- node.style.pointerEvents = prevPointerEvents[i] ?? "";
8451
- });
8452
7076
  }
8453
7077
  }
8454
7078
  function selectAllTextInEditable(el) {
@@ -8590,9 +7214,6 @@ var ICONS = {
8590
7214
  insertUnorderedList: '<line x1="8" y1="6" x2="21" y2="6"/><line x1="8" y1="12" x2="21" y2="12"/><line x1="8" y1="18" x2="21" y2="18"/><line x1="3" y1="6" x2="3.01" y2="6"/><line x1="3" y1="12" x2="3.01" y2="12"/><line x1="3" y1="18" x2="3.01" y2="18"/>',
8591
7215
  insertOrderedList: '<line x1="10" y1="6" x2="21" y2="6"/><line x1="10" y1="12" x2="21" y2="12"/><line x1="10" y1="18" x2="21" y2="18"/><path d="M4 6h1v4"/><path d="M4 10h2"/><path d="M6 18H4c0-1 2-2 2-3s-1-1.5-2-1"/>'
8592
7216
  };
8593
- var SELECTION_CHROME_GAP2 = 4;
8594
- var TOOLBAR_STROKE_GAP2 = 4;
8595
- var TOOLBAR_OFFSET_FROM_ELEMENT = SELECTION_CHROME_GAP2 + TOOLBAR_STROKE_GAP2;
8596
7217
  var TOOLBAR_GROUPS = [
8597
7218
  [
8598
7219
  { cmd: "bold", title: "Bold" },
@@ -8614,11 +7235,10 @@ function EditGlowChrome({
8614
7235
  rect,
8615
7236
  elRef,
8616
7237
  reorderHrefKey,
8617
- dragDisabled = false,
8618
- hideHandle = false
7238
+ dragDisabled = false
8619
7239
  }) {
8620
- const GAP = SELECTION_CHROME_GAP2;
8621
- return /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(
7240
+ const GAP = 6;
7241
+ return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(
8622
7242
  "div",
8623
7243
  {
8624
7244
  ref: elRef,
@@ -8633,7 +7253,7 @@ function EditGlowChrome({
8633
7253
  zIndex: 2147483646
8634
7254
  },
8635
7255
  children: [
8636
- /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
7256
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
8637
7257
  "div",
8638
7258
  {
8639
7259
  style: {
@@ -8641,12 +7261,12 @@ function EditGlowChrome({
8641
7261
  inset: 0,
8642
7262
  border: "2px solid var(--ohw-primary, #0885FE)",
8643
7263
  borderRadius: 8,
8644
- boxShadow: "0 0 0 4px color-mix(in srgb, var(--ohw-primary, #0885FE) 12%, transparent)",
7264
+ boxShadow: "0 0 0 4px rgba(8, 133, 254, 0.12)",
8645
7265
  pointerEvents: "none"
8646
7266
  }
8647
7267
  }
8648
7268
  ),
8649
- reorderHrefKey && !hideHandle && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
7269
+ reorderHrefKey && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
8650
7270
  "div",
8651
7271
  {
8652
7272
  "data-ohw-drag-handle-container": "",
@@ -8658,7 +7278,7 @@ function EditGlowChrome({
8658
7278
  transform: "translate(calc(-100% - 7px), -50%)",
8659
7279
  pointerEvents: dragDisabled ? "none" : "auto"
8660
7280
  },
8661
- children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
7281
+ children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
8662
7282
  DragHandle,
8663
7283
  {
8664
7284
  "aria-label": `Reorder ${reorderHrefKey}`,
@@ -8689,7 +7309,6 @@ function applyVisibleViewport(el, parentScroll) {
8689
7309
  el.style.top = `${top}px`;
8690
7310
  el.style.height = `${height}px`;
8691
7311
  }
8692
- var TOOLBAR_EDGE_MARGIN2 = 4;
8693
7312
  function clampToolbarToClip(top, transform, rect, clip, approxH, gap) {
8694
7313
  const isAbove = transform.includes("translateY(-100%)");
8695
7314
  let visualTop = isAbove ? top - approxH : top;
@@ -8713,16 +7332,10 @@ function clampToolbarToClip(top, transform, rect, clip, approxH, gap) {
8713
7332
  const pinnedTop = Math.min(maxBottom - approxH, Math.max(minTop, clip.top + gap));
8714
7333
  return { top: pinnedTop + approxH, transform: "translateX(-50%) translateY(-100%)" };
8715
7334
  }
8716
- function clampToolbarLeft(centerX, toolbarWidth) {
8717
- const half = toolbarWidth / 2;
8718
- return Math.max(
8719
- TOOLBAR_EDGE_MARGIN2 + half,
8720
- Math.min(centerX, window.innerWidth - TOOLBAR_EDGE_MARGIN2 - half)
8721
- );
8722
- }
8723
- function calcToolbarPos(rect, parentScroll, toolbarW = 306) {
8724
- const GAP = TOOLBAR_OFFSET_FROM_ELEMENT;
7335
+ function calcToolbarPos(rect, parentScroll, approxW = 306) {
7336
+ const GAP = 8;
8725
7337
  const APPROX_H = 32;
7338
+ const APPROX_W = approxW;
8726
7339
  const clip = getIframeVisibleClip(parentScroll);
8727
7340
  const clipTop = clip?.top ?? 0;
8728
7341
  const canvasTopInIframe = parentScroll != null ? parentScroll.headerH - parentScroll.iframeOffsetTop : 0;
@@ -8752,7 +7365,7 @@ function calcToolbarPos(rect, parentScroll, toolbarW = 306) {
8752
7365
  transform = clamped.transform;
8753
7366
  }
8754
7367
  const rawLeft = rect.left + rect.width / 2;
8755
- const left = clampToolbarLeft(rawLeft, toolbarW);
7368
+ const left = Math.max(GAP + APPROX_W / 2, Math.min(rawLeft, window.innerWidth - GAP - APPROX_W / 2));
8756
7369
  return { top, left, transform };
8757
7370
  }
8758
7371
  function resolveItemInteractionState(rect, parentScroll) {
@@ -8768,37 +7381,11 @@ function FloatingToolbar({
8768
7381
  showEditLink,
8769
7382
  onEditLink
8770
7383
  }) {
8771
- const localRef = import_react9.default.useRef(null);
8772
- const [measuredW, setMeasuredW] = import_react9.default.useState(330);
8773
- const setRefs = import_react9.default.useCallback(
8774
- (node) => {
8775
- localRef.current = node;
8776
- if (typeof elRef === "function") elRef(node);
8777
- else if (elRef) elRef.current = node;
8778
- if (node) {
8779
- const w = node.offsetWidth;
8780
- if (w > 0) setMeasuredW(w);
8781
- }
8782
- },
8783
- [elRef]
8784
- );
8785
- import_react9.default.useLayoutEffect(() => {
8786
- const node = localRef.current;
8787
- if (!node) return;
8788
- const update = () => {
8789
- const w = node.offsetWidth;
8790
- if (w > 0) setMeasuredW(w);
8791
- };
8792
- update();
8793
- const ro = new ResizeObserver(update);
8794
- ro.observe(node);
8795
- return () => ro.disconnect();
8796
- }, [showEditLink, activeCommands]);
8797
- const { top, left, transform } = calcToolbarPos(rect, parentScroll, measuredW);
8798
- return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
7384
+ const { top, left, transform } = calcToolbarPos(rect, parentScroll, 330);
7385
+ return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
8799
7386
  "div",
8800
7387
  {
8801
- ref: setRefs,
7388
+ ref: elRef,
8802
7389
  style: {
8803
7390
  position: "fixed",
8804
7391
  top,
@@ -8807,12 +7394,12 @@ function FloatingToolbar({
8807
7394
  zIndex: 2147483647,
8808
7395
  pointerEvents: "auto"
8809
7396
  },
8810
- children: /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(CustomToolbar, { children: [
8811
- TOOLBAR_GROUPS.map((btns, gi) => /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(import_react9.default.Fragment, { children: [
8812
- gi > 0 && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(CustomToolbarDivider, {}),
7397
+ children: /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(CustomToolbar, { children: [
7398
+ TOOLBAR_GROUPS.map((btns, gi) => /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(import_react8.default.Fragment, { children: [
7399
+ gi > 0 && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(CustomToolbarDivider, {}),
8813
7400
  btns.map((btn) => {
8814
7401
  const isActive = activeCommands.has(btn.cmd);
8815
- return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
7402
+ return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
8816
7403
  CustomToolbarButton,
8817
7404
  {
8818
7405
  title: btn.title,
@@ -8821,7 +7408,7 @@ function FloatingToolbar({
8821
7408
  e.preventDefault();
8822
7409
  onCommand(btn.cmd);
8823
7410
  },
8824
- children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
7411
+ children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
8825
7412
  "svg",
8826
7413
  {
8827
7414
  width: "16",
@@ -8842,7 +7429,7 @@ function FloatingToolbar({
8842
7429
  );
8843
7430
  })
8844
7431
  ] }, gi)),
8845
- showEditLink ? /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
7432
+ showEditLink ? /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
8846
7433
  CustomToolbarButton,
8847
7434
  {
8848
7435
  type: "button",
@@ -8856,7 +7443,7 @@ function FloatingToolbar({
8856
7443
  e.preventDefault();
8857
7444
  e.stopPropagation();
8858
7445
  },
8859
- children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_lucide_react11.Link, { className: "size-4 shrink-0", "aria-hidden": true })
7446
+ children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_lucide_react11.Link, { className: "size-4 shrink-0", "aria-hidden": true })
8860
7447
  }
8861
7448
  ) : null
8862
7449
  ] })
@@ -8873,7 +7460,7 @@ function StateToggle({
8873
7460
  states,
8874
7461
  onStateChange
8875
7462
  }) {
8876
- return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
7463
+ return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
8877
7464
  ToggleGroup,
8878
7465
  {
8879
7466
  "data-ohw-state-toggle": "",
@@ -8887,7 +7474,7 @@ function StateToggle({
8887
7474
  left: rect.right - 8,
8888
7475
  transform: "translateX(-100%)"
8889
7476
  },
8890
- children: states.map((state) => /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(ToggleGroupItem, { value: state, size: "sm", children: state }, state))
7477
+ children: states.map((state) => /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(ToggleGroupItem, { value: state, size: "sm", children: state }, state))
8891
7478
  }
8892
7479
  );
8893
7480
  }
@@ -8914,8 +7501,8 @@ function OhhwellsBridge() {
8914
7501
  const router = (0, import_navigation2.useRouter)();
8915
7502
  const searchParams = (0, import_navigation2.useSearchParams)();
8916
7503
  const isEditMode = isEditSessionActive();
8917
- const [bridgeRoot, setBridgeRoot] = (0, import_react9.useState)(null);
8918
- (0, import_react9.useEffect)(() => {
7504
+ const [bridgeRoot, setBridgeRoot] = (0, import_react8.useState)(null);
7505
+ (0, import_react8.useEffect)(() => {
8919
7506
  const figtreeFontId = "ohw-figtree-font";
8920
7507
  if (!document.getElementById(figtreeFontId)) {
8921
7508
  const preconnect1 = Object.assign(document.createElement("link"), { rel: "preconnect", href: "https://fonts.googleapis.com" });
@@ -8943,137 +7530,91 @@ function OhhwellsBridge() {
8943
7530
  const subdomainFromQuery = searchParams.get("subdomain");
8944
7531
  const subdomain = resolveSubdomain(subdomainFromQuery);
8945
7532
  useLinkHrefGuardian(pathname, subdomain, isEditMode);
8946
- const postToParent2 = (0, import_react9.useCallback)((data) => {
7533
+ const postToParent2 = (0, import_react8.useCallback)((data) => {
8947
7534
  if (typeof window !== "undefined" && window.parent !== window) {
8948
7535
  window.parent.postMessage(data, "*");
8949
7536
  }
8950
7537
  }, []);
8951
- const [fetchState, setFetchState] = (0, import_react9.useState)("idle");
8952
- const autoSaveTimers = (0, import_react9.useRef)(/* @__PURE__ */ new Map());
8953
- const activeElRef = (0, import_react9.useRef)(null);
8954
- const pointerHeldRef = (0, import_react9.useRef)(false);
8955
- const selectedElRef = (0, import_react9.useRef)(null);
8956
- const selectedHrefKeyRef = (0, import_react9.useRef)(null);
8957
- const selectedFooterColAttrRef = (0, import_react9.useRef)(null);
8958
- const originalContentRef = (0, import_react9.useRef)(null);
8959
- const activeStateElRef = (0, import_react9.useRef)(null);
8960
- const parentScrollRef = (0, import_react9.useRef)(null);
8961
- const visibleViewportRef = (0, import_react9.useRef)(null);
8962
- const [dialogPortalContainer, setDialogPortalContainer] = (0, import_react9.useState)(null);
8963
- const attachVisibleViewport = (0, import_react9.useCallback)((node) => {
7538
+ const [fetchState, setFetchState] = (0, import_react8.useState)("idle");
7539
+ const autoSaveTimers = (0, import_react8.useRef)(/* @__PURE__ */ new Map());
7540
+ const activeElRef = (0, import_react8.useRef)(null);
7541
+ const selectedElRef = (0, import_react8.useRef)(null);
7542
+ const originalContentRef = (0, import_react8.useRef)(null);
7543
+ const activeStateElRef = (0, import_react8.useRef)(null);
7544
+ const parentScrollRef = (0, import_react8.useRef)(null);
7545
+ const visibleViewportRef = (0, import_react8.useRef)(null);
7546
+ const [dialogPortalContainer, setDialogPortalContainer] = (0, import_react8.useState)(null);
7547
+ const attachVisibleViewport = (0, import_react8.useCallback)((node) => {
8964
7548
  visibleViewportRef.current = node;
8965
7549
  setDialogPortalContainer(node);
8966
7550
  if (node) applyVisibleViewport(node, parentScrollRef.current);
8967
7551
  }, []);
8968
- const toolbarElRef = (0, import_react9.useRef)(null);
8969
- const glowElRef = (0, import_react9.useRef)(null);
8970
- const hoveredImageRef = (0, import_react9.useRef)(null);
8971
- const hoveredImageHasTextOverlapRef = (0, import_react9.useRef)(false);
8972
- const dragOverElRef = (0, import_react9.useRef)(null);
8973
- const [mediaHover, setMediaHover] = (0, import_react9.useState)(null);
8974
- const [uploadingRects, setUploadingRects] = (0, import_react9.useState)({});
8975
- const hoveredGapRef = (0, import_react9.useRef)(null);
8976
- const imageUnhoverTimerRef = (0, import_react9.useRef)(null);
8977
- const imageShowTimerRef = (0, import_react9.useRef)(null);
8978
- const editStylesRef = (0, import_react9.useRef)(null);
8979
- const activateRef = (0, import_react9.useRef)(() => {
7552
+ const toolbarElRef = (0, import_react8.useRef)(null);
7553
+ const glowElRef = (0, import_react8.useRef)(null);
7554
+ const hoveredImageRef = (0, import_react8.useRef)(null);
7555
+ const hoveredImageHasTextOverlapRef = (0, import_react8.useRef)(false);
7556
+ const dragOverElRef = (0, import_react8.useRef)(null);
7557
+ const [mediaHover, setMediaHover] = (0, import_react8.useState)(null);
7558
+ const [uploadingRects, setUploadingRects] = (0, import_react8.useState)({});
7559
+ const hoveredGapRef = (0, import_react8.useRef)(null);
7560
+ const imageUnhoverTimerRef = (0, import_react8.useRef)(null);
7561
+ const imageShowTimerRef = (0, import_react8.useRef)(null);
7562
+ const editStylesRef = (0, import_react8.useRef)(null);
7563
+ const activateRef = (0, import_react8.useRef)(() => {
8980
7564
  });
8981
- const deactivateRef = (0, import_react9.useRef)(() => {
7565
+ const deactivateRef = (0, import_react8.useRef)(() => {
8982
7566
  });
8983
- const selectRef = (0, import_react9.useRef)(() => {
7567
+ const selectRef = (0, import_react8.useRef)(() => {
8984
7568
  });
8985
- const selectFrameRef = (0, import_react9.useRef)(() => {
7569
+ const selectFrameRef = (0, import_react8.useRef)(() => {
8986
7570
  });
8987
- const deselectRef = (0, import_react9.useRef)(() => {
7571
+ const deselectRef = (0, import_react8.useRef)(() => {
8988
7572
  });
8989
- const reselectNavigationItemRef = (0, import_react9.useRef)(() => {
7573
+ const reselectNavigationItemRef = (0, import_react8.useRef)(() => {
8990
7574
  });
8991
- const commitNavigationTextEditRef = (0, import_react9.useRef)(() => {
7575
+ const commitNavigationTextEditRef = (0, import_react8.useRef)(() => {
8992
7576
  });
8993
- const refreshActiveCommandsRef = (0, import_react9.useRef)(() => {
7577
+ const refreshActiveCommandsRef = (0, import_react8.useRef)(() => {
8994
7578
  });
8995
- const postToParentRef = (0, import_react9.useRef)(postToParent2);
7579
+ const postToParentRef = (0, import_react8.useRef)(postToParent2);
8996
7580
  postToParentRef.current = postToParent2;
8997
- const sectionsLoadedRef = (0, import_react9.useRef)(false);
8998
- const pendingScheduleConfigRequests = (0, import_react9.useRef)([]);
8999
- const [toolbarRect, setToolbarRect] = (0, import_react9.useState)(null);
9000
- const [toolbarVariant, setToolbarVariant] = (0, import_react9.useState)("none");
9001
- const toolbarVariantRef = (0, import_react9.useRef)("none");
7581
+ const sectionsLoadedRef = (0, import_react8.useRef)(false);
7582
+ const pendingScheduleConfigRequests = (0, import_react8.useRef)([]);
7583
+ const [toolbarRect, setToolbarRect] = (0, import_react8.useState)(null);
7584
+ const [toolbarVariant, setToolbarVariant] = (0, import_react8.useState)("none");
7585
+ const toolbarVariantRef = (0, import_react8.useRef)("none");
9002
7586
  toolbarVariantRef.current = toolbarVariant;
9003
- const [selectedIsCta, setSelectedIsCta] = (0, import_react9.useState)(false);
9004
- const [reorderHrefKey, setReorderHrefKey] = (0, import_react9.useState)(null);
9005
- const [reorderDragDisabled, setReorderDragDisabled] = (0, import_react9.useState)(false);
9006
- const [toggleState, setToggleState] = (0, import_react9.useState)(null);
9007
- const [maxBadge, setMaxBadge] = (0, import_react9.useState)(null);
9008
- const [activeCommands, setActiveCommands] = (0, import_react9.useState)(/* @__PURE__ */ new Set());
9009
- const [sectionGap, setSectionGap] = (0, import_react9.useState)(null);
9010
- const [toolbarShowEditLink, setToolbarShowEditLink] = (0, import_react9.useState)(false);
9011
- const hoveredNavContainerRef = (0, import_react9.useRef)(null);
9012
- const [hoveredNavContainerRect, setHoveredNavContainerRect] = (0, import_react9.useState)(null);
9013
- const hoveredItemElRef = (0, import_react9.useRef)(null);
9014
- const [hoveredItemRect, setHoveredItemRect] = (0, import_react9.useState)(null);
9015
- const siblingHintElRef = (0, import_react9.useRef)(null);
9016
- const [siblingHintRect, setSiblingHintRect] = (0, import_react9.useState)(null);
9017
- const [siblingHintRects, setSiblingHintRects] = (0, import_react9.useState)([]);
9018
- const [isItemDragging, setIsItemDragging] = (0, import_react9.useState)(false);
9019
- const [isFooterFrameSelection, setIsFooterFrameSelection] = (0, import_react9.useState)(false);
9020
- const footerDragRef = (0, import_react9.useRef)(null);
9021
- const [footerDropSlots, setFooterDropSlots] = (0, import_react9.useState)([]);
9022
- const [activeFooterDropIndex, setActiveFooterDropIndex] = (0, import_react9.useState)(null);
9023
- const [draggedItemRect, setDraggedItemRect] = (0, import_react9.useState)(null);
9024
- const footerPointerDragRef = (0, import_react9.useRef)(null);
9025
- const suppressNextClickRef = (0, import_react9.useRef)(false);
9026
- const suppressClickUntilRef = (0, import_react9.useRef)(0);
9027
- const [linkPopover, setLinkPopover] = (0, import_react9.useState)(null);
9028
- const linkPopoverSessionRef = (0, import_react9.useRef)(null);
9029
- const addNavAfterAnchorRef = (0, import_react9.useRef)(null);
9030
- const editContentRef = (0, import_react9.useRef)({});
9031
- const [sitePages, setSitePages] = (0, import_react9.useState)([]);
9032
- const [sectionsByPath, setSectionsByPath] = (0, import_react9.useState)({});
9033
- const sectionsPrefetchGenRef = (0, import_react9.useRef)(0);
9034
- const setLinkPopoverRef = (0, import_react9.useRef)(setLinkPopover);
9035
- const linkPopoverPanelRef = (0, import_react9.useRef)(null);
9036
- const linkPopoverOpenRef = (0, import_react9.useRef)(false);
9037
- const linkPopoverGraceUntilRef = (0, import_react9.useRef)(0);
7587
+ const [reorderHrefKey, setReorderHrefKey] = (0, import_react8.useState)(null);
7588
+ const [reorderDragDisabled, setReorderDragDisabled] = (0, import_react8.useState)(false);
7589
+ const [toggleState, setToggleState] = (0, import_react8.useState)(null);
7590
+ const [maxBadge, setMaxBadge] = (0, import_react8.useState)(null);
7591
+ const [activeCommands, setActiveCommands] = (0, import_react8.useState)(/* @__PURE__ */ new Set());
7592
+ const [sectionGap, setSectionGap] = (0, import_react8.useState)(null);
7593
+ const [toolbarShowEditLink, setToolbarShowEditLink] = (0, import_react8.useState)(false);
7594
+ const hoveredNavContainerRef = (0, import_react8.useRef)(null);
7595
+ const [hoveredNavContainerRect, setHoveredNavContainerRect] = (0, import_react8.useState)(null);
7596
+ const hoveredItemElRef = (0, import_react8.useRef)(null);
7597
+ const [hoveredItemRect, setHoveredItemRect] = (0, import_react8.useState)(null);
7598
+ const siblingHintElRef = (0, import_react8.useRef)(null);
7599
+ const [siblingHintRect, setSiblingHintRect] = (0, import_react8.useState)(null);
7600
+ const [isItemDragging, setIsItemDragging] = (0, import_react8.useState)(false);
7601
+ const [linkPopover, setLinkPopover] = (0, import_react8.useState)(null);
7602
+ const linkPopoverSessionRef = (0, import_react8.useRef)(null);
7603
+ const addNavAfterAnchorRef = (0, import_react8.useRef)(null);
7604
+ const editContentRef = (0, import_react8.useRef)({});
7605
+ const [sitePages, setSitePages] = (0, import_react8.useState)([]);
7606
+ const [sectionsByPath, setSectionsByPath] = (0, import_react8.useState)({});
7607
+ const sectionsPrefetchGenRef = (0, import_react8.useRef)(0);
7608
+ const setLinkPopoverRef = (0, import_react8.useRef)(setLinkPopover);
7609
+ const linkPopoverPanelRef = (0, import_react8.useRef)(null);
7610
+ const linkPopoverOpenRef = (0, import_react8.useRef)(false);
7611
+ const linkPopoverGraceUntilRef = (0, import_react8.useRef)(0);
9038
7612
  setLinkPopoverRef.current = setLinkPopover;
9039
7613
  linkPopoverSessionRef.current = linkPopover;
9040
- const {
9041
- navDragRef,
9042
- navDropSlots,
9043
- activeNavDropIndex,
9044
- startNavLinkDrag,
9045
- commitNavDrag,
9046
- onNavDragOver,
9047
- armNavPressFromChrome,
9048
- refreshNavDragVisualsRef
9049
- } = useNavItemDrag({
9050
- isEditMode,
9051
- editContentRef,
9052
- selectedElRef,
9053
- activeElRef,
9054
- footerDragRef,
9055
- suppressNextClickRef,
9056
- suppressClickUntilRef,
9057
- siblingHintElRef,
9058
- postToParentRef,
9059
- deselectRef,
9060
- selectRef,
9061
- deactivateRef,
9062
- setLinkPopover: () => setLinkPopover(null),
9063
- linkPopoverOpenRef,
9064
- setIsItemDragging,
9065
- setDraggedItemRect,
9066
- setSiblingHintRect,
9067
- setSiblingHintRects,
9068
- setToolbarRect,
9069
- getNavigationItemAnchor,
9070
- isDragHandleDisabled,
9071
- isNavbarButton: isNavbarButton2
9072
- });
9073
7614
  const bumpLinkPopoverGrace = () => {
9074
7615
  linkPopoverGraceUntilRef.current = Date.now() + 350;
9075
7616
  };
9076
- const runSectionsPrefetch = (0, import_react9.useCallback)((pages) => {
7617
+ const runSectionsPrefetch = (0, import_react8.useCallback)((pages) => {
9077
7618
  if (!isEditMode || shouldUseDevFixtures() || pages.length === 0) return;
9078
7619
  const gen = ++sectionsPrefetchGenRef.current;
9079
7620
  const paths = pages.map((p) => p.path);
@@ -9092,24 +7633,10 @@ function OhhwellsBridge() {
9092
7633
  );
9093
7634
  });
9094
7635
  }, [isEditMode, pathname]);
9095
- const runSectionsPrefetchRef = (0, import_react9.useRef)(runSectionsPrefetch);
7636
+ const runSectionsPrefetchRef = (0, import_react8.useRef)(runSectionsPrefetch);
9096
7637
  runSectionsPrefetchRef.current = runSectionsPrefetch;
9097
- (0, import_react9.useEffect)(() => {
9098
- if (!linkPopover) {
9099
- document.documentElement.removeAttribute("data-ohw-link-popover-open");
9100
- return;
9101
- }
9102
- document.documentElement.setAttribute("data-ohw-link-popover-open", "");
9103
- hoveredItemElRef.current = null;
9104
- setHoveredItemRect(null);
9105
- hoveredNavContainerRef.current = null;
9106
- setHoveredNavContainerRect(null);
9107
- siblingHintElRef.current = null;
9108
- setSiblingHintRect(null);
9109
- setSiblingHintRects([]);
9110
- if (selectedElRef.current || toolbarVariantRef.current !== "none") {
9111
- deselectRef.current();
9112
- }
7638
+ (0, import_react8.useEffect)(() => {
7639
+ if (!linkPopover) return;
9113
7640
  if (hoveredImageRef.current) {
9114
7641
  hoveredImageRef.current = null;
9115
7642
  hoveredImageHasTextOverlapRef.current = false;
@@ -9143,7 +7670,7 @@ function OhhwellsBridge() {
9143
7670
  document.removeEventListener("touchmove", preventBackgroundScroll, scrollOpts);
9144
7671
  };
9145
7672
  }, [linkPopover, postToParent2]);
9146
- (0, import_react9.useEffect)(() => {
7673
+ (0, import_react8.useEffect)(() => {
9147
7674
  if (!isEditMode) return;
9148
7675
  const useFixtures = shouldUseDevFixtures();
9149
7676
  if (useFixtures) {
@@ -9167,14 +7694,14 @@ function OhhwellsBridge() {
9167
7694
  if (!useFixtures) postToParent2({ type: "ow:request-site-pages" });
9168
7695
  return () => window.removeEventListener("message", onSitePages);
9169
7696
  }, [isEditMode, postToParent2]);
9170
- (0, import_react9.useEffect)(() => {
7697
+ (0, import_react8.useEffect)(() => {
9171
7698
  if (!isEditMode || shouldUseDevFixtures()) return;
9172
7699
  void loadAllSectionsManifest().then((manifest) => {
9173
7700
  if (Object.keys(manifest).length === 0) return;
9174
7701
  setSectionsByPath((prev) => ({ ...manifest, ...prev }));
9175
7702
  });
9176
7703
  }, [isEditMode]);
9177
- (0, import_react9.useEffect)(() => {
7704
+ (0, import_react8.useEffect)(() => {
9178
7705
  const update = () => {
9179
7706
  const el = activeElRef.current ?? selectedElRef.current;
9180
7707
  if (el) setToolbarRect(getEditMeasureEl(el).getBoundingClientRect());
@@ -9198,10 +7725,10 @@ function OhhwellsBridge() {
9198
7725
  vvp.removeEventListener("resize", update);
9199
7726
  };
9200
7727
  }, []);
9201
- const refreshStateRules = (0, import_react9.useCallback)(() => {
7728
+ const refreshStateRules = (0, import_react8.useCallback)(() => {
9202
7729
  editStylesRef.current?.forceHover && (editStylesRef.current.forceHover.textContent = collectStateRules());
9203
7730
  }, []);
9204
- const processConfigRequest = (0, import_react9.useCallback)((insertAfterVal) => {
7731
+ const processConfigRequest = (0, import_react8.useCallback)((insertAfterVal) => {
9205
7732
  const tracker = getSectionsTracker();
9206
7733
  let entries = [];
9207
7734
  try {
@@ -9224,7 +7751,7 @@ function OhhwellsBridge() {
9224
7751
  }
9225
7752
  window.postMessage({ type: "ow:schedule-config", insertAfter: insertAfterVal, scheduleId: null }, "*");
9226
7753
  }, [isEditMode]);
9227
- const deactivate = (0, import_react9.useCallback)(() => {
7754
+ const deactivate = (0, import_react8.useCallback)(() => {
9228
7755
  const el = activeElRef.current;
9229
7756
  if (!el) return;
9230
7757
  const key = el.dataset.ohwKey;
@@ -9243,7 +7770,6 @@ function OhhwellsBridge() {
9243
7770
  }
9244
7771
  }
9245
7772
  el.removeAttribute("contenteditable");
9246
- el.removeAttribute("data-ohw-editing");
9247
7773
  activeElRef.current = null;
9248
7774
  setReorderHrefKey(null);
9249
7775
  setReorderDragDisabled(false);
@@ -9256,23 +7782,12 @@ function OhhwellsBridge() {
9256
7782
  setToolbarShowEditLink(false);
9257
7783
  postToParent2({ type: "ow:exit-edit" });
9258
7784
  }, [postToParent2]);
9259
- const clearSelectedAttr = (0, import_react9.useCallback)(() => {
9260
- document.querySelectorAll("[data-ohw-selected]").forEach((el) => {
9261
- el.removeAttribute("data-ohw-selected");
9262
- });
9263
- }, []);
9264
- const deselect = (0, import_react9.useCallback)(() => {
9265
- clearSelectedAttr();
7785
+ const deselect = (0, import_react8.useCallback)(() => {
9266
7786
  selectedElRef.current = null;
9267
- selectedHrefKeyRef.current = null;
9268
- selectedFooterColAttrRef.current = null;
9269
- setSelectedIsCta(false);
9270
7787
  setReorderHrefKey(null);
9271
7788
  setReorderDragDisabled(false);
9272
- setIsFooterFrameSelection(false);
9273
7789
  siblingHintElRef.current = null;
9274
7790
  setSiblingHintRect(null);
9275
- setSiblingHintRects([]);
9276
7791
  setIsItemDragging(false);
9277
7792
  hoveredNavContainerRef.current = null;
9278
7793
  setHoveredNavContainerRect(null);
@@ -9280,70 +7795,18 @@ function OhhwellsBridge() {
9280
7795
  setToolbarRect(null);
9281
7796
  setToolbarVariant("none");
9282
7797
  }
9283
- }, [clearSelectedAttr]);
9284
- const markSelected = (0, import_react9.useCallback)((el) => {
9285
- clearSelectedAttr();
9286
- el.setAttribute("data-ohw-selected", "");
9287
- }, [clearSelectedAttr]);
9288
- const resolveHrefKeyElement = (0, import_react9.useCallback)((hrefKey) => {
9289
- if (isFooterHrefKey(hrefKey)) {
9290
- return document.querySelector(
9291
- `footer [data-ohw-href-key="${CSS.escape(hrefKey)}"]`
9292
- );
9293
- }
9294
- if (isNavbarHrefKey(hrefKey)) {
9295
- const desktop = document.querySelector("[data-ohw-nav-container]");
9296
- const drawer = document.querySelector("[data-ohw-nav-drawer]");
9297
- 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)}"]`);
9298
- }
9299
- return document.querySelector(
9300
- `[data-ohw-href-key="${CSS.escape(hrefKey)}"]`
9301
- );
9302
7798
  }, []);
9303
- const resyncSelectedNavigationItem = (0, import_react9.useCallback)(() => {
9304
- const hrefKey = selectedHrefKeyRef.current;
9305
- if (hrefKey) {
9306
- const link = resolveHrefKeyElement(hrefKey);
9307
- if (!link || !isNavigationItem(link)) return;
9308
- selectedElRef.current = link;
9309
- const { key, disabled } = getNavigationItemReorderState(link);
9310
- setReorderHrefKey(key);
9311
- setReorderDragDisabled(disabled);
9312
- setIsFooterFrameSelection(false);
9313
- setToolbarVariant("link-action");
9314
- setToolbarRect(link.getBoundingClientRect());
9315
- setSiblingHintRects(collectNavigationItemSiblingHintRects(link));
9316
- return;
9317
- }
9318
- const colAttr = selectedFooterColAttrRef.current;
9319
- if (colAttr != null) {
9320
- const column = document.querySelector(`[data-ohw-footer-col="${CSS.escape(colAttr)}"]`) ?? listFooterColumns()[Number(colAttr)] ?? null;
9321
- if (!column || !isNavigationContainer(column)) return;
9322
- selectedElRef.current = column;
9323
- setIsFooterFrameSelection(true);
9324
- setToolbarVariant("select-frame");
9325
- setToolbarRect(column.getBoundingClientRect());
9326
- setSiblingHintRects(
9327
- listFooterColumns().filter((c) => c !== column).map((c) => c.getBoundingClientRect())
9328
- );
9329
- }
9330
- }, [resolveHrefKeyElement]);
9331
- const reselectNavigationItem = (0, import_react9.useCallback)((navAnchor) => {
7799
+ const reselectNavigationItem = (0, import_react8.useCallback)((navAnchor) => {
9332
7800
  selectedElRef.current = navAnchor;
9333
- selectedHrefKeyRef.current = navAnchor.getAttribute("data-ohw-href-key");
9334
- selectedFooterColAttrRef.current = null;
9335
- markSelected(navAnchor);
9336
- setSelectedIsCta(isNavbarButton2(navAnchor));
9337
7801
  const { key, disabled } = getNavigationItemReorderState(navAnchor);
9338
7802
  setReorderHrefKey(key);
9339
7803
  setReorderDragDisabled(disabled);
9340
7804
  setToolbarVariant("link-action");
9341
7805
  setToolbarRect(navAnchor.getBoundingClientRect());
9342
- setSiblingHintRects(collectNavigationItemSiblingHintRects(navAnchor));
9343
7806
  setToolbarShowEditLink(false);
9344
7807
  setActiveCommands(/* @__PURE__ */ new Set());
9345
- }, [markSelected]);
9346
- const commitNavigationTextEdit = (0, import_react9.useCallback)((navAnchor) => {
7808
+ }, []);
7809
+ const commitNavigationTextEdit = (0, import_react8.useCallback)((navAnchor) => {
9347
7810
  const el = activeElRef.current;
9348
7811
  if (!el) return;
9349
7812
  const key = el.dataset.ohwKey;
@@ -9362,7 +7825,6 @@ function OhhwellsBridge() {
9362
7825
  }
9363
7826
  }
9364
7827
  el.removeAttribute("contenteditable");
9365
- el.removeAttribute("data-ohw-editing");
9366
7828
  activeElRef.current = null;
9367
7829
  setMaxBadge(null);
9368
7830
  setActiveCommands(/* @__PURE__ */ new Set());
@@ -9370,7 +7832,7 @@ function OhhwellsBridge() {
9370
7832
  postToParent2({ type: "ow:exit-edit" });
9371
7833
  reselectNavigationItem(navAnchor);
9372
7834
  }, [postToParent2, reselectNavigationItem]);
9373
- const handleAddTopLevelNavItem = (0, import_react9.useCallback)(() => {
7835
+ const handleAddTopLevelNavItem = (0, import_react8.useCallback)(() => {
9374
7836
  const items = listNavbarItems();
9375
7837
  addNavAfterAnchorRef.current = items[items.length - 1] ?? null;
9376
7838
  deselectRef.current();
@@ -9382,321 +7844,20 @@ function OhhwellsBridge() {
9382
7844
  intent: "add-nav"
9383
7845
  });
9384
7846
  }, []);
9385
- const clearFooterDragVisuals = (0, import_react9.useCallback)(() => {
9386
- footerDragRef.current = null;
9387
- setSiblingHintRects([]);
9388
- setFooterDropSlots([]);
9389
- setActiveFooterDropIndex(null);
9390
- setDraggedItemRect(null);
9391
- setIsItemDragging(false);
9392
- unlockFooterDragInteraction();
9393
- }, []);
9394
- const refreshFooterDragVisuals = (0, import_react9.useCallback)((session, activeSlot, clientX, clientY) => {
9395
- const dragged = session.draggedEl;
9396
- setDraggedItemRect(dragged.getBoundingClientRect());
9397
- if (typeof clientX === "number" && typeof clientY === "number") {
9398
- session.lastClientX = clientX;
9399
- session.lastClientY = clientY;
9400
- }
9401
- session.activeSlot = activeSlot;
9402
- if (session.kind === "link") {
9403
- const columns2 = listFooterColumns();
9404
- const focusCols = /* @__PURE__ */ new Set([session.sourceColumnIndex]);
9405
- if (activeSlot) focusCols.add(activeSlot.columnIndex);
9406
- const siblingRects = [];
9407
- columns2.forEach((col, i) => {
9408
- if (!focusCols.has(i)) return;
9409
- for (const link of listFooterLinksInColumn(col)) {
9410
- if (link === dragged) continue;
9411
- siblingRects.push(link.getBoundingClientRect());
9412
- }
9413
- });
9414
- setSiblingHintRects(siblingRects);
9415
- const slots2 = [];
9416
- columns2.forEach((col, i) => {
9417
- slots2.push(...buildLinkDropSlots(col, i));
9418
- });
9419
- setFooterDropSlots(slots2);
9420
- const activeIdx2 = activeSlot ? slots2.findIndex((s) => s.columnIndex === activeSlot.columnIndex && s.insertIndex === activeSlot.insertIndex) : -1;
9421
- setActiveFooterDropIndex(activeIdx2 >= 0 ? activeIdx2 : null);
9422
- return;
9423
- }
9424
- const columns = listFooterColumns();
9425
- setSiblingHintRects(columns.filter((col) => col !== dragged).map((col) => col.getBoundingClientRect()));
9426
- const slots = buildColumnDropSlots();
9427
- setFooterDropSlots(slots);
9428
- const activeIdx = activeSlot ? slots.findIndex((s) => s.insertIndex === activeSlot.insertIndex) : -1;
9429
- setActiveFooterDropIndex(activeIdx >= 0 ? activeIdx : null);
7847
+ const handleItemDragStart = (0, import_react8.useCallback)(() => {
7848
+ siblingHintElRef.current = null;
7849
+ setSiblingHintRect(null);
7850
+ setIsItemDragging(true);
9430
7851
  }, []);
9431
- const refreshFooterDragVisualsRef = (0, import_react9.useRef)(refreshFooterDragVisuals);
9432
- refreshFooterDragVisualsRef.current = refreshFooterDragVisuals;
9433
- const commitFooterDragRef = (0, import_react9.useRef)(() => {
9434
- });
9435
- const beginFooterDragRef = (0, import_react9.useRef)(() => {
9436
- });
9437
- const beginFooterDrag = (0, import_react9.useCallback)(
9438
- (session) => {
9439
- const rect = session.draggedEl.getBoundingClientRect();
9440
- session.lastClientX = session.lastClientX || rect.left + rect.width / 2;
9441
- session.lastClientY = session.lastClientY || rect.top + rect.height / 2;
9442
- session.activeSlot = session.activeSlot ?? null;
9443
- footerDragRef.current = session;
9444
- setIsItemDragging(true);
9445
- lockFooterDuringDrag();
9446
- siblingHintElRef.current = null;
9447
- setSiblingHintRect(null);
9448
- if (session.wasSelected && selectedElRef.current === session.draggedEl) {
9449
- setToolbarRect(rect);
9450
- }
9451
- const initialSlot = session.kind === "link" && session.hrefKey ? hitTestLinkDropSlot(session.lastClientX, session.lastClientY, session.hrefKey) : hitTestColumnDropSlot(session.lastClientX, session.lastClientY);
9452
- refreshFooterDragVisuals(session, initialSlot, session.lastClientX, session.lastClientY);
9453
- },
9454
- [refreshFooterDragVisuals]
9455
- );
9456
- beginFooterDragRef.current = beginFooterDrag;
9457
- const commitFooterDrag = (0, import_react9.useCallback)(
9458
- (clientX, clientY) => {
9459
- const session = footerDragRef.current;
9460
- if (!session) {
9461
- clearFooterDragVisuals();
9462
- return;
9463
- }
9464
- const x = typeof clientX === "number" && (clientX !== 0 || clientY !== 0) ? clientX : session.lastClientX;
9465
- const y = typeof clientY === "number" && (clientX !== 0 || clientY !== 0) ? clientY : session.lastClientY;
9466
- let nextOrder = null;
9467
- const slot = session.activeSlot ?? (session.kind === "link" && session.hrefKey ? hitTestLinkDropSlot(x, y, session.hrefKey) : session.kind === "column" ? hitTestColumnDropSlot(x, y) : null);
9468
- if (session.kind === "link" && session.hrefKey && slot) {
9469
- nextOrder = planFooterLinkMove(session.hrefKey, slot.columnIndex, slot.insertIndex);
9470
- } else if (session.kind === "column" && slot) {
9471
- nextOrder = planFooterColumnMove(session.sourceColumnIndex, slot.insertIndex);
9472
- }
9473
- const wasSelected = session.wasSelected;
9474
- const draggedEl = session.draggedEl;
9475
- const hrefKey = session.hrefKey;
9476
- let movedColumnIndex = null;
9477
- if (session.kind === "column" && slot && nextOrder) {
9478
- let adjusted = slot.insertIndex;
9479
- if (session.sourceColumnIndex < slot.insertIndex) adjusted -= 1;
9480
- movedColumnIndex = Math.max(0, Math.min(adjusted, nextOrder.length - 1));
9481
- }
9482
- const applySelectionAfterDrop = () => {
9483
- if (!wasSelected) {
9484
- deselectRef.current();
9485
- return;
9486
- }
9487
- if (hrefKey) {
9488
- selectedHrefKeyRef.current = hrefKey;
9489
- selectedFooterColAttrRef.current = null;
9490
- const link = resolveHrefKeyElement(hrefKey);
9491
- if (link && isNavigationItem(link)) {
9492
- selectRef.current(link);
9493
- return;
9494
- }
9495
- }
9496
- if (movedColumnIndex !== null && nextOrder) {
9497
- const colKeys = nextOrder[movedColumnIndex] ?? [];
9498
- let column = null;
9499
- for (const key of colKeys) {
9500
- const link = document.querySelector(
9501
- `footer [data-ohw-href-key="${CSS.escape(key)}"]`
9502
- );
9503
- if (link) {
9504
- column = findFooterColumnForLink(link);
9505
- if (column) break;
9506
- }
9507
- }
9508
- if (!column) column = listFooterColumns()[movedColumnIndex] ?? null;
9509
- if (column && isNavigationContainer(column)) {
9510
- selectFrameRef.current(column);
9511
- return;
9512
- }
9513
- }
9514
- if (document.body.contains(draggedEl)) {
9515
- if (isNavigationItem(draggedEl)) {
9516
- selectRef.current(draggedEl);
9517
- return;
9518
- }
9519
- if (isNavigationContainer(draggedEl)) {
9520
- selectFrameRef.current(draggedEl);
9521
- return;
9522
- }
9523
- }
9524
- deselectRef.current();
9525
- };
9526
- if (nextOrder) {
9527
- const orderJson = JSON.stringify(nextOrder);
9528
- editContentRef.current = {
9529
- ...editContentRef.current,
9530
- [FOOTER_ORDER_KEY]: orderJson
9531
- };
9532
- applyFooterOrder(nextOrder);
9533
- document.querySelectorAll("footer [data-ohw-href-key]").forEach(disableNativeHrefDrag);
9534
- postToParentRef.current({
9535
- type: "ow:change",
9536
- nodes: [{ key: FOOTER_ORDER_KEY, text: orderJson }]
9537
- });
9538
- applySelectionAfterDrop();
9539
- clearFooterDragVisuals();
9540
- requestAnimationFrame(() => {
9541
- if (editContentRef.current[FOOTER_ORDER_KEY] === orderJson) {
9542
- applyFooterOrder(nextOrder);
9543
- document.querySelectorAll("footer [data-ohw-href-key]").forEach(disableNativeHrefDrag);
9544
- }
9545
- applySelectionAfterDrop();
9546
- requestAnimationFrame(() => {
9547
- if (editContentRef.current[FOOTER_ORDER_KEY] === orderJson) {
9548
- applyFooterOrder(nextOrder);
9549
- }
9550
- resyncSelectedNavigationItem();
9551
- });
9552
- });
9553
- return;
9554
- }
9555
- applySelectionAfterDrop();
9556
- clearFooterDragVisuals();
9557
- },
9558
- [clearFooterDragVisuals, resolveHrefKeyElement, resyncSelectedNavigationItem]
9559
- );
9560
- commitFooterDragRef.current = commitFooterDrag;
9561
- const startFooterLinkDrag = (0, import_react9.useCallback)(
9562
- (anchor, clientX, clientY, wasSelected) => {
9563
- const hrefKey = anchor.getAttribute("data-ohw-href-key");
9564
- if (!hrefKey || !isFooterHrefKey(hrefKey)) return false;
9565
- const column = findFooterColumnForLink(anchor);
9566
- const columns = listFooterColumns();
9567
- beginFooterDrag({
9568
- kind: "link",
9569
- hrefKey,
9570
- columnEl: column,
9571
- sourceColumnIndex: column ? columns.indexOf(column) : 0,
9572
- wasSelected,
9573
- draggedEl: anchor,
9574
- lastClientX: clientX,
9575
- lastClientY: clientY,
9576
- activeSlot: null
9577
- });
9578
- return true;
9579
- },
9580
- [beginFooterDrag]
9581
- );
9582
- const startFooterColumnDrag = (0, import_react9.useCallback)(
9583
- (columnEl, clientX, clientY, wasSelected) => {
9584
- const columns = listFooterColumns();
9585
- const idx = columns.indexOf(columnEl);
9586
- if (idx < 0) return false;
9587
- beginFooterDrag({
9588
- kind: "column",
9589
- hrefKey: null,
9590
- columnEl,
9591
- sourceColumnIndex: idx,
9592
- wasSelected,
9593
- draggedEl: columnEl,
9594
- lastClientX: clientX,
9595
- lastClientY: clientY,
9596
- activeSlot: null
9597
- });
9598
- return true;
9599
- },
9600
- [beginFooterDrag]
9601
- );
9602
- const handleItemDragStart = (0, import_react9.useCallback)(
9603
- (e) => {
9604
- const selected = selectedElRef.current;
9605
- if (!selected) {
9606
- setIsItemDragging(true);
9607
- return;
9608
- }
9609
- const clientX = e?.clientX ?? selected.getBoundingClientRect().left + 8;
9610
- const clientY = e?.clientY ?? selected.getBoundingClientRect().top + 8;
9611
- if (startFooterLinkDrag(selected, clientX, clientY, true)) return;
9612
- if (selected.hasAttribute("data-ohw-footer-col") || isInferredFooterGroup(selected) && selected.closest("footer")) {
9613
- if (startFooterColumnDrag(selected, clientX, clientY, true)) return;
9614
- }
9615
- if (startNavLinkDrag(selected, clientX, clientY, true)) return;
9616
- siblingHintElRef.current = null;
9617
- setSiblingHintRect(null);
9618
- setIsItemDragging(true);
9619
- },
9620
- [startFooterColumnDrag, startFooterLinkDrag, startNavLinkDrag]
9621
- );
9622
- const handleItemDragEnd = (0, import_react9.useCallback)(
9623
- (e) => {
9624
- if (footerDragRef.current) {
9625
- const x = e?.clientX;
9626
- const y = e?.clientY;
9627
- if (typeof x === "number" && typeof y === "number" && (x !== 0 || y !== 0)) {
9628
- commitFooterDrag(x, y);
9629
- } else {
9630
- commitFooterDrag();
9631
- }
9632
- return;
9633
- }
9634
- if (navDragRef.current) {
9635
- const x = e?.clientX;
9636
- const y = e?.clientY;
9637
- if (typeof x === "number" && typeof y === "number" && (x !== 0 || y !== 0)) {
9638
- commitNavDrag(x, y);
9639
- } else {
9640
- commitNavDrag();
9641
- }
9642
- return;
9643
- }
9644
- setIsItemDragging(false);
9645
- },
9646
- [commitFooterDrag, commitNavDrag, navDragRef]
9647
- );
9648
- const handleItemChromePointerDown = (0, import_react9.useCallback)((e) => {
9649
- if (e.button !== 0) return;
9650
- const selected = selectedElRef.current;
9651
- if (!selected) return;
9652
- const hrefKey = selected.getAttribute("data-ohw-href-key");
9653
- if (hrefKey && isFooterHrefKey(hrefKey)) {
9654
- footerPointerDragRef.current = {
9655
- el: selected,
9656
- kind: "link",
9657
- startX: e.clientX,
9658
- startY: e.clientY,
9659
- pointerId: e.pointerId,
9660
- wasSelected: true,
9661
- started: false
9662
- };
9663
- return;
9664
- }
9665
- if (selected.hasAttribute("data-ohw-footer-col") || isInferredFooterGroup(selected) && selected.closest("footer")) {
9666
- footerPointerDragRef.current = {
9667
- el: selected,
9668
- kind: "column",
9669
- startX: e.clientX,
9670
- startY: e.clientY,
9671
- pointerId: e.pointerId,
9672
- wasSelected: true,
9673
- started: false
9674
- };
9675
- return;
9676
- }
9677
- if (armNavPressFromChrome(selected, e.clientX, e.clientY, e.pointerId)) return;
9678
- }, [armNavPressFromChrome]);
9679
- const handleItemChromeClick = (0, import_react9.useCallback)((clientX, clientY) => {
9680
- if (suppressNextClickRef.current || Date.now() < suppressClickUntilRef.current) {
9681
- suppressNextClickRef.current = false;
9682
- return;
9683
- }
9684
- const selected = selectedElRef.current;
9685
- if (!selected || !isNavigationItem(selected)) return;
9686
- const editable = selected.querySelector('[data-ohw-editable="text"]') ?? selected.querySelector("[data-ohw-editable]");
9687
- if (!editable) return;
9688
- activateRef.current(editable, { caretX: clientX, caretY: clientY });
7852
+ const handleItemDragEnd = (0, import_react8.useCallback)(() => {
7853
+ setIsItemDragging(false);
9689
7854
  }, []);
9690
7855
  reselectNavigationItemRef.current = reselectNavigationItem;
9691
7856
  commitNavigationTextEditRef.current = commitNavigationTextEdit;
9692
- const select = (0, import_react9.useCallback)((anchor) => {
7857
+ const select = (0, import_react8.useCallback)((anchor) => {
9693
7858
  if (!isNavigationItem(anchor)) return;
9694
7859
  if (activeElRef.current) deactivate();
9695
7860
  selectedElRef.current = anchor;
9696
- selectedHrefKeyRef.current = anchor.getAttribute("data-ohw-href-key");
9697
- selectedFooterColAttrRef.current = null;
9698
- markSelected(anchor);
9699
- setSelectedIsCta(isNavbarButton2(anchor));
9700
7861
  clearHrefKeyHover(anchor);
9701
7862
  hoveredNavContainerRef.current = null;
9702
7863
  setHoveredNavContainerRect(null);
@@ -9704,26 +7865,19 @@ function OhhwellsBridge() {
9704
7865
  hoveredItemElRef.current = null;
9705
7866
  siblingHintElRef.current = null;
9706
7867
  setSiblingHintRect(null);
9707
- setSiblingHintRects(collectNavigationItemSiblingHintRects(anchor));
9708
7868
  setIsItemDragging(false);
9709
7869
  const { key, disabled } = getNavigationItemReorderState(anchor);
9710
7870
  setReorderHrefKey(key);
9711
7871
  setReorderDragDisabled(disabled);
9712
- setIsFooterFrameSelection(false);
9713
7872
  setToolbarVariant("link-action");
9714
7873
  setToolbarRect(anchor.getBoundingClientRect());
9715
7874
  setToolbarShowEditLink(false);
9716
7875
  setActiveCommands(/* @__PURE__ */ new Set());
9717
- }, [deactivate, markSelected]);
9718
- const selectFrame = (0, import_react9.useCallback)((el) => {
7876
+ }, [deactivate]);
7877
+ const selectFrame = (0, import_react8.useCallback)((el) => {
9719
7878
  if (!isNavigationContainer(el)) return;
9720
7879
  if (activeElRef.current) deactivate();
9721
- const isFooterColumn = el.hasAttribute("data-ohw-footer-col") || Boolean(el.closest("footer") && isInferredFooterGroup(el));
9722
7880
  selectedElRef.current = el;
9723
- selectedHrefKeyRef.current = null;
9724
- selectedFooterColAttrRef.current = isFooterColumn ? el.getAttribute("data-ohw-footer-col") ?? String(listFooterColumns().indexOf(el)) : null;
9725
- markSelected(el);
9726
- setSelectedIsCta(false);
9727
7881
  clearHrefKeyHover(el);
9728
7882
  hoveredNavContainerRef.current = null;
9729
7883
  setHoveredNavContainerRect(null);
@@ -9731,25 +7885,17 @@ function OhhwellsBridge() {
9731
7885
  hoveredItemElRef.current = null;
9732
7886
  siblingHintElRef.current = null;
9733
7887
  setSiblingHintRect(null);
9734
- setSiblingHintRects(
9735
- isFooterColumn ? listFooterColumns().filter((column) => column !== el).map((column) => column.getBoundingClientRect()) : []
9736
- );
9737
7888
  setIsItemDragging(false);
9738
7889
  setReorderHrefKey(null);
9739
7890
  setReorderDragDisabled(false);
9740
- setIsFooterFrameSelection(isFooterColumn);
9741
7891
  setToolbarVariant("select-frame");
9742
7892
  setToolbarRect(el.getBoundingClientRect());
9743
7893
  setToolbarShowEditLink(false);
9744
7894
  setActiveCommands(/* @__PURE__ */ new Set());
9745
- }, [deactivate, markSelected]);
9746
- const activate = (0, import_react9.useCallback)((el, options) => {
7895
+ }, [deactivate]);
7896
+ const activate = (0, import_react8.useCallback)((el, options) => {
9747
7897
  if (activeElRef.current === el) return;
9748
- clearSelectedAttr();
9749
7898
  selectedElRef.current = null;
9750
- selectedHrefKeyRef.current = null;
9751
- selectedFooterColAttrRef.current = null;
9752
- setSelectedIsCta(false);
9753
7899
  deactivate();
9754
7900
  if (hoveredImageRef.current) {
9755
7901
  hoveredImageRef.current = null;
@@ -9759,34 +7905,14 @@ function OhhwellsBridge() {
9759
7905
  siblingHintElRef.current = null;
9760
7906
  setSiblingHintRect(null);
9761
7907
  setIsItemDragging(false);
9762
- const preActivationSelection = window.getSelection();
9763
- const preservedRange = preActivationSelection && preActivationSelection.rangeCount > 0 && !preActivationSelection.isCollapsed && el.contains(preActivationSelection.getRangeAt(0).commonAncestorContainer) ? preActivationSelection.getRangeAt(0).cloneRange() : null;
9764
7908
  el.setAttribute("contenteditable", "true");
9765
- el.setAttribute("data-ohw-editing", "");
9766
7909
  el.removeAttribute("data-ohw-hovered");
9767
7910
  el.closest("[data-ohw-href-key]")?.removeAttribute("data-ohw-hovered");
9768
7911
  activeElRef.current = el;
9769
7912
  originalContentRef.current = el.innerHTML;
9770
- el.focus({ preventScroll: true });
9771
- if (preservedRange) {
9772
- const selection = window.getSelection();
9773
- selection?.removeAllRanges();
9774
- selection?.addRange(preservedRange);
9775
- } else if (options?.caretX !== void 0 && options?.caretY !== void 0) {
9776
- const { caretX, caretY } = options;
9777
- placeCaretAtPoint(el, caretX, caretY);
9778
- requestAnimationFrame(() => {
9779
- if (activeElRef.current === el) placeCaretAtPoint(el, caretX, caretY);
9780
- });
9781
- } else {
9782
- const selection = window.getSelection();
9783
- if (selection && selection.rangeCount === 0) {
9784
- const range = document.createRange();
9785
- range.selectNodeContents(el);
9786
- range.collapse(false);
9787
- selection.removeAllRanges();
9788
- selection.addRange(range);
9789
- }
7913
+ el.focus();
7914
+ if (options?.caretX !== void 0 && options?.caretY !== void 0) {
7915
+ placeCaretAtPoint(el, options.caretX, options.caretY);
9790
7916
  }
9791
7917
  setToolbarShowEditLink(Boolean(getHrefKeyFromElement(el)));
9792
7918
  const navAnchor = getNavigationItemAnchor(el);
@@ -9801,13 +7927,13 @@ function OhhwellsBridge() {
9801
7927
  setToolbarRect(getEditMeasureEl(el).getBoundingClientRect());
9802
7928
  postToParent2({ type: "ow:enter-edit", key: el.dataset.ohwKey });
9803
7929
  requestAnimationFrame(() => refreshActiveCommandsRef.current());
9804
- }, [clearSelectedAttr, deactivate, postToParent2]);
7930
+ }, [deactivate, postToParent2]);
9805
7931
  activateRef.current = activate;
9806
7932
  deactivateRef.current = deactivate;
9807
7933
  selectRef.current = select;
9808
7934
  selectFrameRef.current = selectFrame;
9809
7935
  deselectRef.current = deselect;
9810
- (0, import_react9.useLayoutEffect)(() => {
7936
+ (0, import_react8.useLayoutEffect)(() => {
9811
7937
  if (!subdomain || isEditMode) {
9812
7938
  setFetchState("done");
9813
7939
  return;
@@ -9848,7 +7974,6 @@ function OhhwellsBridge() {
9848
7974
  applyLinkByKey(key, val);
9849
7975
  }
9850
7976
  reconcileNavbarItemsFromContent(content);
9851
- reconcileFooterOrderFromContent(content);
9852
7977
  enforceLinkHrefs();
9853
7978
  initSectionsFromContent(content, true);
9854
7979
  sectionsLoadedRef.current = true;
@@ -9877,7 +8002,7 @@ function OhhwellsBridge() {
9877
8002
  cancelled = true;
9878
8003
  };
9879
8004
  }, [subdomain, isEditMode]);
9880
- (0, import_react9.useEffect)(() => {
8005
+ (0, import_react8.useEffect)(() => {
9881
8006
  if (!subdomain || isEditMode) return;
9882
8007
  let debounceTimer = null;
9883
8008
  let observer = null;
@@ -9909,7 +8034,6 @@ function OhhwellsBridge() {
9909
8034
  applyLinkByKey(key, val);
9910
8035
  }
9911
8036
  reconcileNavbarItemsFromContent(content);
9912
- reconcileFooterOrderFromContent(content);
9913
8037
  } finally {
9914
8038
  observer?.observe(document.body, { childList: true, subtree: true });
9915
8039
  }
@@ -9927,16 +8051,16 @@ function OhhwellsBridge() {
9927
8051
  if (debounceTimer) clearTimeout(debounceTimer);
9928
8052
  };
9929
8053
  }, [subdomain, isEditMode, pathname]);
9930
- (0, import_react9.useLayoutEffect)(() => {
8054
+ (0, import_react8.useLayoutEffect)(() => {
9931
8055
  const el = document.getElementById("ohw-loader");
9932
8056
  if (!el) return;
9933
8057
  const visible = Boolean(subdomain) && fetchState !== "done";
9934
8058
  el.style.display = visible ? "flex" : "none";
9935
8059
  }, [subdomain, fetchState]);
9936
- (0, import_react9.useEffect)(() => {
8060
+ (0, import_react8.useEffect)(() => {
9937
8061
  postToParent2({ type: "ow:navigation", path: pathname });
9938
8062
  }, [pathname, postToParent2]);
9939
- (0, import_react9.useEffect)(() => {
8063
+ (0, import_react8.useEffect)(() => {
9940
8064
  if (!isEditMode) return;
9941
8065
  if (linkPopoverSessionRef.current?.intent === "add-nav") return;
9942
8066
  if (document.querySelector("[data-ohw-section-picker]")) return;
@@ -9944,72 +8068,23 @@ function OhhwellsBridge() {
9944
8068
  deselectRef.current();
9945
8069
  deactivateRef.current();
9946
8070
  }, [pathname, isEditMode]);
9947
- (0, import_react9.useEffect)(() => {
8071
+ (0, import_react8.useEffect)(() => {
9948
8072
  const contentForNav = () => {
9949
8073
  if (isEditMode) return editContentRef.current;
9950
8074
  if (!subdomain) return {};
9951
8075
  return contentCache.get(subdomain) ?? {};
9952
8076
  };
9953
- const observeRoots = () => [document.querySelector("nav"), document.querySelector("footer")].filter(
9954
- (el) => Boolean(el)
9955
- );
9956
- const roots = observeRoots();
9957
- if (roots.length === 0) {
9958
- reconcileNavbarItemsFromContent(contentForNav());
9959
- reconcileFooterOrderFromContent(contentForNav());
9960
- if (isEditMode) syncNavigationDragCursorAttrs();
9961
- return;
9962
- }
9963
- let rafId = null;
9964
- let applying = false;
9965
- let observer = null;
9966
- const attach = () => {
9967
- for (const root of observeRoots()) {
9968
- observer?.observe(root, { childList: true, subtree: true });
9969
- }
9970
- };
9971
- const run = () => {
9972
- if (footerDragRef.current || navDragRef.current || applying) return;
9973
- applying = true;
9974
- observer?.disconnect();
9975
- try {
9976
- const content = contentForNav();
9977
- reconcileNavbarItemsFromContent(content);
9978
- reconcileFooterOrderFromContent(content);
9979
- document.querySelectorAll("footer [data-ohw-href-key]").forEach((el) => {
9980
- if (isFooterHrefKey(el.getAttribute("data-ohw-href-key"))) {
9981
- disableNativeHrefDrag(el);
9982
- }
9983
- });
9984
- document.querySelectorAll(
9985
- "nav [data-ohw-href-key], [data-ohw-nav-container] [data-ohw-href-key], [data-ohw-nav-drawer] [data-ohw-href-key]"
9986
- ).forEach((el) => {
9987
- if (isNavbarHrefKey(el.getAttribute("data-ohw-href-key"))) {
9988
- disableNativeHrefDrag(el);
9989
- }
9990
- });
9991
- resyncSelectedNavigationItem();
9992
- if (isEditMode) syncNavigationDragCursorAttrs();
9993
- } finally {
9994
- applying = false;
9995
- attach();
9996
- }
9997
- };
9998
- const schedule = () => {
9999
- if (applying || rafId != null) return;
10000
- rafId = requestAnimationFrame(() => {
10001
- rafId = null;
10002
- run();
10003
- });
10004
- };
10005
- observer = new MutationObserver(schedule);
8077
+ const run = () => reconcileNavbarItemsFromContent(contentForNav());
10006
8078
  run();
10007
- return () => {
10008
- if (rafId != null) cancelAnimationFrame(rafId);
10009
- observer?.disconnect();
10010
- };
10011
- }, [isEditMode, pathname, subdomain, fetchState, resyncSelectedNavigationItem]);
10012
- (0, import_react9.useEffect)(() => {
8079
+ const nav = document.querySelector("nav");
8080
+ if (!nav) return;
8081
+ const observer = new MutationObserver(() => {
8082
+ requestAnimationFrame(run);
8083
+ });
8084
+ observer.observe(nav, { childList: true, subtree: true });
8085
+ return () => observer.disconnect();
8086
+ }, [isEditMode, pathname, subdomain, fetchState]);
8087
+ (0, import_react8.useEffect)(() => {
10013
8088
  if (!isEditMode) return;
10014
8089
  const measure = () => {
10015
8090
  const h = document.body.scrollHeight;
@@ -10033,7 +8108,7 @@ function OhhwellsBridge() {
10033
8108
  window.removeEventListener("resize", handleResize);
10034
8109
  };
10035
8110
  }, [pathname, isEditMode, postToParent2]);
10036
- (0, import_react9.useEffect)(() => {
8111
+ (0, import_react8.useEffect)(() => {
10037
8112
  if (!subdomainFromQuery || isEditMode) return;
10038
8113
  const handleClick = (e) => {
10039
8114
  const anchor = e.target.closest("a");
@@ -10049,53 +8124,30 @@ function OhhwellsBridge() {
10049
8124
  document.addEventListener("click", handleClick, true);
10050
8125
  return () => document.removeEventListener("click", handleClick, true);
10051
8126
  }, [subdomainFromQuery, isEditMode, router]);
10052
- (0, import_react9.useEffect)(() => {
8127
+ (0, import_react8.useEffect)(() => {
10053
8128
  if (!isEditMode) {
10054
8129
  editStylesRef.current?.base.remove();
10055
8130
  editStylesRef.current?.forceHover.remove();
10056
8131
  editStylesRef.current?.stateViews.remove();
10057
8132
  editStylesRef.current = null;
10058
- document.querySelectorAll("[data-ohw-can-drag]").forEach((el) => {
10059
- el.removeAttribute("data-ohw-can-drag");
10060
- });
10061
8133
  return;
10062
8134
  }
10063
- const existing = editStylesRef.current;
10064
- let initialVh = window.innerHeight;
10065
- if (existing?.base.textContent) {
10066
- const match = existing.base.textContent.match(/\.min-h-screen[^{]*\{[^}]*min-height:\s*(\d+)px/);
10067
- if (match) initialVh = parseInt(match[1], 10);
10068
- }
10069
- const baseCss = `
8135
+ if (!editStylesRef.current) {
8136
+ const initialVh = window.innerHeight;
8137
+ const base = document.createElement("style");
8138
+ base.setAttribute("data-ohw-edit-style", "");
8139
+ base.textContent = `
10070
8140
  html { height: auto !important; }
10071
8141
  body { height: auto !important; min-height: 0 !important; overflow: hidden !important; }
10072
8142
  .min-h-screen, .min-h-svh, .min-h-dvh { min-height: ${initialVh}px !important; }
10073
8143
  .h-screen, .h-svh, .h-dvh { height: ${initialVh}px !important; }
10074
- [style*="100vh"] { min-height: ${initialVh}px !important; height: ${initialVh}px !important; }
10075
- [style*="100svh"] { min-height: ${initialVh}px !important; height: ${initialVh}px !important; }
10076
- [style*="100dvh"] { min-height: ${initialVh}px !important; height: ${initialVh}px !important; }
10077
- [data-ohw-editable] {
10078
- display: block;
10079
- }
10080
- /* Body text (no item-action toolbar) \u2014 first click enters text edit \u2192 I-beam.
10081
- Exclude select-first nav/footer labels so grab/default on [data-ohw-href-key] can win. */
10082
- [data-ohw-editable]:not([contenteditable]):not([data-ohw-editable="image"]):not([data-ohw-editable="bg-image"]):not([data-ohw-editable="video"]):not([data-ohw-editable="link"]):not(:is([data-ohw-href-key] *)) {
10083
- cursor: text !important;
10084
- }
10085
- /* Select-first items: default arrow; grab only when drag-start is allowed (data-ohw-can-drag). */
10086
- [data-ohw-href-key]:not([data-ohw-selected]),
10087
- [data-ohw-href-key]:not([data-ohw-selected]) * {
10088
- cursor: default !important;
10089
- }
10090
- [data-ohw-href-key][data-ohw-can-drag]:not([data-ohw-selected]),
10091
- [data-ohw-href-key][data-ohw-can-drag]:not([data-ohw-selected]) * {
10092
- cursor: grab !important;
10093
- }
10094
- /* Selected (action-toolbar) \u2014 next click enters text edit at caret. */
10095
- [data-ohw-href-key][data-ohw-selected],
10096
- [data-ohw-href-key][data-ohw-selected] * {
10097
- cursor: text !important;
8144
+ [style*="100vh"] { min-height: ${initialVh}px !important; height: ${initialVh}px !important; }
8145
+ [style*="100svh"] { min-height: ${initialVh}px !important; height: ${initialVh}px !important; }
8146
+ [style*="100dvh"] { min-height: ${initialVh}px !important; height: ${initialVh}px !important; }
8147
+ [data-ohw-editable] {
8148
+ display: block;
10098
8149
  }
8150
+ [data-ohw-editable]:not([contenteditable]):not([data-ohw-editable="image"]):not([data-ohw-editable="bg-image"]):not([data-ohw-editable="video"]) { cursor: text !important; }
10099
8151
  [data-ohw-editable="image"], [data-ohw-editable="image"] *,
10100
8152
  [data-ohw-editable="video"], [data-ohw-editable="video"] *,
10101
8153
  [data-ohw-editable="bg-image"], [data-ohw-editable="bg-image"] * { cursor: pointer !important; }
@@ -10111,32 +8163,9 @@ function OhhwellsBridge() {
10111
8163
  outline: none !important;
10112
8164
  outline-offset: 0 !important;
10113
8165
  }
10114
- /* Editor chrome owns selection \u2014 suppress native focus rings on nav/footer items. */
10115
- nav [data-ohw-href-key]:focus,
10116
- nav [data-ohw-href-key]:focus-visible,
10117
- nav [data-ohw-href-key]:focus-within,
10118
- footer [data-ohw-href-key]:focus,
10119
- footer [data-ohw-href-key]:focus-visible,
10120
- footer [data-ohw-href-key]:focus-within,
10121
- nav [data-ohw-href-key] [data-ohw-editable]:focus,
10122
- nav [data-ohw-href-key] [data-ohw-editable]:focus-visible,
10123
- footer [data-ohw-href-key] [data-ohw-editable]:focus,
10124
- footer [data-ohw-href-key] [data-ohw-editable]:focus-visible {
10125
- outline: none !important;
10126
- outline-offset: 0 !important;
10127
- box-shadow: none !important;
10128
- }
10129
- /* Text edit wins over grab/default (must beat [data-ohw-can-drag] *). */
10130
- [data-ohw-editing],
10131
- [data-ohw-editing] *,
10132
- [data-ohw-editable][contenteditable],
10133
- [data-ohw-editable][contenteditable] *,
10134
- nav [data-ohw-href-key] [data-ohw-editable][contenteditable],
10135
- nav [data-ohw-href-key] [data-ohw-editable][contenteditable] *,
10136
- footer [data-ohw-href-key] [data-ohw-editable][contenteditable],
10137
- footer [data-ohw-href-key] [data-ohw-editable][contenteditable] * {
8166
+ [data-ohw-editable][contenteditable] {
10138
8167
  outline: none !important;
10139
- caret-color: ${PRIMARY2} !important;
8168
+ caret-color: ${PRIMARY2};
10140
8169
  cursor: text !important;
10141
8170
  }
10142
8171
  [data-ohw-editable][contenteditable]::selection,
@@ -10145,9 +8174,6 @@ function OhhwellsBridge() {
10145
8174
  [data-ohw-editable-state][data-ohw-active-state] [data-ohw-editable] { pointer-events: auto !important; }
10146
8175
  [data-ohw-editable-state][data-ohw-active-state][data-ohw-editable] { pointer-events: auto !important; }
10147
8176
  `;
10148
- if (!existing) {
10149
- const base = document.createElement("style");
10150
- base.setAttribute("data-ohw-edit-style", "");
10151
8177
  const forceHover = document.createElement("style");
10152
8178
  forceHover.setAttribute("data-ohw-active-state-style", "");
10153
8179
  const stateViews = document.createElement("style");
@@ -10163,43 +8189,14 @@ function OhhwellsBridge() {
10163
8189
  document.head.appendChild(stateViews);
10164
8190
  editStylesRef.current = { base, forceHover, stateViews };
10165
8191
  }
10166
- editStylesRef.current.base.textContent = baseCss;
10167
- syncNavigationDragCursorAttrs();
10168
8192
  refreshStateRules();
10169
8193
  const handleClick = (e) => {
10170
- if (suppressNextClickRef.current || Date.now() < suppressClickUntilRef.current) {
10171
- suppressNextClickRef.current = false;
10172
- e.preventDefault();
10173
- e.stopPropagation();
10174
- return;
10175
- }
10176
8194
  const target = e.target;
10177
8195
  if (target.closest("[data-ohw-toolbar]")) return;
10178
8196
  if (target.closest("[data-ohw-state-toggle]")) return;
10179
8197
  if (target.closest("[data-ohw-max-badge]")) return;
10180
8198
  if (isInsideLinkEditor(target)) return;
10181
8199
  if (target.closest('[data-ohw-edit-chrome], [data-ohw-item-interaction], [data-ohw-drag-handle-container], [data-slot="drag-handle"]')) {
10182
- if (target.closest("[data-ohw-item-drag-surface]")) {
10183
- e.preventDefault();
10184
- e.stopPropagation();
10185
- const selected = selectedElRef.current;
10186
- if (selected && isNavigationItem(selected)) {
10187
- const editable2 = selected.querySelector('[data-ohw-editable="text"]') ?? selected.querySelector("[data-ohw-editable]");
10188
- if (editable2) {
10189
- activateRef.current(editable2, {
10190
- caretX: e.clientX,
10191
- caretY: e.clientY
10192
- });
10193
- }
10194
- } else if (selected?.hasAttribute("data-ohw-footer-col")) {
10195
- const link = listFooterLinksInColumn(selected).find((el) => {
10196
- const r2 = el.getBoundingClientRect();
10197
- return e.clientX >= r2.left && e.clientX <= r2.right && e.clientY >= r2.top && e.clientY <= r2.bottom;
10198
- });
10199
- if (link) selectRef.current(link);
10200
- }
10201
- return;
10202
- }
10203
8200
  const navFromChrome = resolveNavContainerSelectionTarget(target, e.clientX, e.clientY);
10204
8201
  if (navFromChrome) {
10205
8202
  e.preventDefault();
@@ -10207,45 +8204,10 @@ function OhhwellsBridge() {
10207
8204
  selectFrameRef.current(navFromChrome);
10208
8205
  return;
10209
8206
  }
10210
- const footerFromChrome = resolveFooterColumnSelectionTarget(target, e.clientX, e.clientY);
10211
- if (footerFromChrome) {
10212
- e.preventDefault();
10213
- e.stopPropagation();
10214
- selectFrameRef.current(footerFromChrome);
10215
- return;
10216
- }
10217
8207
  e.preventDefault();
10218
8208
  e.stopPropagation();
10219
8209
  return;
10220
8210
  }
10221
- if (activeElRef.current) {
10222
- const active = activeElRef.current;
10223
- const inActive = active === target || active.contains(target);
10224
- const navAnchor = getNavigationItemAnchor(active);
10225
- const inActiveNav = Boolean(
10226
- navAnchor && (navAnchor === target || navAnchor.contains(target))
10227
- );
10228
- if (inActive || inActiveNav) {
10229
- e.preventDefault();
10230
- e.stopPropagation();
10231
- const selection = window.getSelection();
10232
- const hasRangeSelection = Boolean(selection && !selection.isCollapsed);
10233
- console.log(
10234
- "[OHW DEBUG handleClick] already-active branch",
10235
- "detail=",
10236
- e.detail,
10237
- "hasRangeSelection=",
10238
- hasRangeSelection,
10239
- "willPlaceCaret=",
10240
- e.detail < 2 && !hasRangeSelection
10241
- );
10242
- if (e.detail < 2 && !hasRangeSelection) {
10243
- placeCaretAtPoint(active, e.clientX, e.clientY);
10244
- refreshActiveCommandsRef.current();
10245
- }
10246
- return;
10247
- }
10248
- }
10249
8211
  const editable = target.closest("[data-ohw-editable]");
10250
8212
  if (editable) {
10251
8213
  if (editable.dataset.ohwEditable === "link") {
@@ -10282,15 +8244,6 @@ function OhhwellsBridge() {
10282
8244
  }
10283
8245
  e.preventDefault();
10284
8246
  e.stopPropagation();
10285
- console.log(
10286
- "[OHW DEBUG handleClick] first-activation branch",
10287
- "detail=",
10288
- e.detail,
10289
- "selectionAtClick=",
10290
- JSON.stringify(window.getSelection()?.toString() ?? ""),
10291
- "collapsedAtClick=",
10292
- window.getSelection()?.isCollapsed
10293
- );
10294
8247
  activateRef.current(editable);
10295
8248
  return;
10296
8249
  }
@@ -10298,26 +8251,10 @@ function OhhwellsBridge() {
10298
8251
  if (hrefAnchor) {
10299
8252
  e.preventDefault();
10300
8253
  e.stopPropagation();
10301
- if (selectedElRef.current === hrefAnchor) {
10302
- const textEditable = hrefAnchor.querySelector('[data-ohw-editable="text"]') ?? hrefAnchor.querySelector("[data-ohw-editable]");
10303
- if (textEditable) {
10304
- activateRef.current(textEditable, {
10305
- caretX: e.clientX,
10306
- caretY: e.clientY
10307
- });
10308
- }
10309
- return;
10310
- }
8254
+ if (selectedElRef.current === hrefAnchor) return;
10311
8255
  selectRef.current(hrefAnchor);
10312
8256
  return;
10313
8257
  }
10314
- const footerColClick = target.closest("[data-ohw-footer-col]");
10315
- if (footerColClick) {
10316
- e.preventDefault();
10317
- e.stopPropagation();
10318
- selectFrameRef.current(footerColClick);
10319
- return;
10320
- }
10321
8258
  const navContainerToSelect = resolveNavContainerSelectionTarget(target, e.clientX, e.clientY);
10322
8259
  if (navContainerToSelect) {
10323
8260
  e.preventDefault();
@@ -10325,13 +8262,6 @@ function OhhwellsBridge() {
10325
8262
  selectFrameRef.current(navContainerToSelect);
10326
8263
  return;
10327
8264
  }
10328
- const footerColumnToSelect = resolveFooterColumnSelectionTarget(target, e.clientX, e.clientY);
10329
- if (footerColumnToSelect) {
10330
- e.preventDefault();
10331
- e.stopPropagation();
10332
- selectFrameRef.current(footerColumnToSelect);
10333
- return;
10334
- }
10335
8265
  const selectedContainer = selectedElRef.current;
10336
8266
  if (selectedContainer && isNavigationContainer(selectedContainer)) {
10337
8267
  const navItem = getNavigationItemAnchor(target);
@@ -10391,13 +8321,6 @@ function OhhwellsBridge() {
10391
8321
  const handleMouseOver = (e) => {
10392
8322
  if (document.documentElement.hasAttribute("data-ohw-section-picking")) return;
10393
8323
  const target = e.target;
10394
- if (linkPopoverOpenRef.current) {
10395
- hoveredItemElRef.current = null;
10396
- setHoveredItemRect(null);
10397
- hoveredNavContainerRef.current = null;
10398
- setHoveredNavContainerRect(null);
10399
- return;
10400
- }
10401
8324
  if (toolbarVariantRef.current !== "select-frame") {
10402
8325
  const navContainer = target.closest("[data-ohw-nav-container]");
10403
8326
  if (navContainer && !getNavigationItemAnchor(target)) {
@@ -10417,21 +8340,12 @@ function OhhwellsBridge() {
10417
8340
  hoveredNavContainerRef.current = null;
10418
8341
  setHoveredNavContainerRect(null);
10419
8342
  const selected2 = selectedElRef.current;
10420
- if (selected2 === navAnchor) return;
8343
+ if (selected2 === navAnchor || selected2?.contains(navAnchor)) return;
10421
8344
  clearHrefKeyHover(navAnchor);
10422
8345
  hoveredItemElRef.current = navAnchor;
10423
8346
  setHoveredItemRect(navAnchor.getBoundingClientRect());
10424
8347
  return;
10425
8348
  }
10426
- const footerCol = target.closest("[data-ohw-footer-col]") ?? target.closest("[data-ohw-footer-column]");
10427
- if (footerCol) {
10428
- hoveredNavContainerRef.current = null;
10429
- setHoveredNavContainerRect(null);
10430
- if (selectedElRef.current === footerCol) return;
10431
- hoveredItemElRef.current = footerCol;
10432
- setHoveredItemRect(footerCol.getBoundingClientRect());
10433
- return;
10434
- }
10435
8349
  const editable = target.closest("[data-ohw-editable]");
10436
8350
  if (!editable) return;
10437
8351
  const selected = selectedElRef.current;
@@ -10449,25 +8363,14 @@ function OhhwellsBridge() {
10449
8363
  };
10450
8364
  const handleMouseOut = (e) => {
10451
8365
  const target = e.target;
10452
- if (target.closest("[data-ohw-nav-container], [data-ohw-footer-col], [data-ohw-footer-column]")) {
8366
+ if (target.closest("[data-ohw-nav-container]")) {
10453
8367
  const related2 = e.relatedTarget instanceof Element ? e.relatedTarget : null;
10454
- 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]")) {
8368
+ if (related2?.closest("[data-ohw-nav-container], [data-ohw-navbar-container-chrome], [data-ohw-navbar-add-button]")) {
10455
8369
  return;
10456
8370
  }
10457
8371
  hoveredNavContainerRef.current = null;
10458
8372
  setHoveredNavContainerRect(null);
10459
8373
  }
10460
- const footerCol = target.closest("[data-ohw-footer-col]") ?? target.closest("[data-ohw-footer-column]");
10461
- if (footerCol && hoveredItemElRef.current === footerCol) {
10462
- const related2 = e.relatedTarget instanceof Element ? e.relatedTarget : null;
10463
- if (related2?.closest("[data-ohw-drag-handle-container], [data-ohw-item-interaction]")) return;
10464
- if (related2?.closest("[data-ohw-footer-col], [data-ohw-footer-column]") === footerCol) {
10465
- return;
10466
- }
10467
- hoveredItemElRef.current = null;
10468
- setHoveredItemRect(null);
10469
- return;
10470
- }
10471
8374
  const navAnchor = getNavigationItemAnchor(target);
10472
8375
  if (navAnchor) {
10473
8376
  const related2 = e.relatedTarget instanceof Element ? e.relatedTarget : null;
@@ -10601,75 +8504,43 @@ function OhhwellsBridge() {
10601
8504
  if (toolbarVariantRef.current === "select-frame") {
10602
8505
  hoveredNavContainerRef.current = null;
10603
8506
  setHoveredNavContainerRect(null);
8507
+ return;
10604
8508
  }
10605
- const navContainers = Array.from(
10606
- document.querySelectorAll("[data-ohw-nav-container]")
10607
- );
10608
- const footerColumns = listFooterColumns();
10609
- for (const container of [...navContainers, ...footerColumns]) {
10610
- const containerRect = container.getBoundingClientRect();
10611
- const overContainer = x >= containerRect.left && x <= containerRect.right && y >= containerRect.top && y <= containerRect.bottom;
10612
- if (!overContainer) continue;
10613
- const navItemHit = Array.from(
10614
- container.querySelectorAll("[data-ohw-href-key]")
10615
- ).find((el) => {
10616
- if (!isNavigationItem(el) || isNavbarButton2(el)) return false;
10617
- const itemRect = el.getBoundingClientRect();
10618
- return x >= itemRect.left && x <= itemRect.right && y >= itemRect.top && y <= itemRect.bottom;
10619
- });
10620
- if (navItemHit) {
10621
- hoveredNavContainerRef.current = null;
10622
- setHoveredNavContainerRect(null);
10623
- const selected = selectedElRef.current;
10624
- if (selected !== navItemHit) {
10625
- clearHrefKeyHover(navItemHit);
10626
- hoveredItemElRef.current = navItemHit;
10627
- setHoveredItemRect(navItemHit.getBoundingClientRect());
10628
- } else if (hoveredItemElRef.current === navItemHit) {
10629
- hoveredItemElRef.current = null;
10630
- setHoveredItemRect(null);
10631
- }
10632
- return;
10633
- }
10634
- }
10635
- if (toolbarVariantRef.current !== "select-frame") {
10636
- for (const container of navContainers) {
10637
- const containerRect = container.getBoundingClientRect();
10638
- const overContainer = x >= containerRect.left && x <= containerRect.right && y >= containerRect.top && y <= containerRect.bottom;
10639
- if (!overContainer) continue;
10640
- if (isPointOverNavItem(container, x, y)) continue;
10641
- hoveredNavContainerRef.current = container;
10642
- setHoveredNavContainerRect(container.getBoundingClientRect());
10643
- hoveredItemElRef.current = null;
10644
- setHoveredItemRect(null);
10645
- return;
10646
- }
8509
+ const navContainer = document.querySelector("[data-ohw-nav-container]");
8510
+ if (!navContainer) {
10647
8511
  hoveredNavContainerRef.current = null;
10648
8512
  setHoveredNavContainerRect(null);
10649
- } else {
8513
+ return;
8514
+ }
8515
+ const containerRect = navContainer.getBoundingClientRect();
8516
+ const overContainer = x >= containerRect.left && x <= containerRect.right && y >= containerRect.top && y <= containerRect.bottom;
8517
+ if (!overContainer) {
10650
8518
  hoveredNavContainerRef.current = null;
10651
8519
  setHoveredNavContainerRect(null);
8520
+ return;
10652
8521
  }
10653
- for (const column of footerColumns) {
10654
- const containerRect = column.getBoundingClientRect();
10655
- const overContainer = x >= containerRect.left && x <= containerRect.right && y >= containerRect.top && y <= containerRect.bottom;
10656
- if (!overContainer) continue;
10657
- if (isPointOverNavItem(column, x, y)) continue;
10658
- if (selectedElRef.current === column) {
10659
- if (hoveredItemElRef.current === column) {
10660
- hoveredItemElRef.current = null;
10661
- setHoveredItemRect(null);
10662
- }
10663
- return;
8522
+ const navItemHit = Array.from(
8523
+ navContainer.querySelectorAll("[data-ohw-href-key]")
8524
+ ).find((el) => {
8525
+ if (!isNavigationItem(el) || isNavbarButton2(el)) return false;
8526
+ const itemRect = el.getBoundingClientRect();
8527
+ return x >= itemRect.left && x <= itemRect.right && y >= itemRect.top && y <= itemRect.bottom;
8528
+ });
8529
+ if (navItemHit) {
8530
+ hoveredNavContainerRef.current = null;
8531
+ setHoveredNavContainerRect(null);
8532
+ const selected = selectedElRef.current;
8533
+ if (selected !== navItemHit && !selected?.contains(navItemHit)) {
8534
+ clearHrefKeyHover(navItemHit);
8535
+ hoveredItemElRef.current = navItemHit;
8536
+ setHoveredItemRect(navItemHit.getBoundingClientRect());
10664
8537
  }
10665
- hoveredItemElRef.current = column;
10666
- setHoveredItemRect(column.getBoundingClientRect());
10667
8538
  return;
10668
8539
  }
10669
- if (hoveredItemElRef.current?.hasAttribute("data-ohw-footer-col") || hoveredItemElRef.current?.hasAttribute("data-ohw-footer-column")) {
10670
- hoveredItemElRef.current = null;
10671
- setHoveredItemRect(null);
10672
- }
8540
+ hoveredNavContainerRef.current = navContainer;
8541
+ setHoveredNavContainerRect(containerRect);
8542
+ hoveredItemElRef.current = null;
8543
+ setHoveredItemRect(null);
10673
8544
  };
10674
8545
  const probeImageAt = (clientX, clientY, isDragOver = false, fromParentViewport = false) => {
10675
8546
  if (linkPopoverOpenRef.current) {
@@ -10690,10 +8561,6 @@ function OhhwellsBridge() {
10690
8561
  }
10691
8562
  hoveredNavContainerRef.current = null;
10692
8563
  setHoveredNavContainerRect(null);
10693
- if (hoveredItemElRef.current?.hasAttribute("data-ohw-footer-col") || hoveredItemElRef.current?.hasAttribute("data-ohw-footer-column")) {
10694
- hoveredItemElRef.current = null;
10695
- setHoveredItemRect(null);
10696
- }
10697
8564
  const toggleEl = document.querySelector("[data-ohw-state-toggle]");
10698
8565
  if (toggleEl) {
10699
8566
  const tr = toggleEl.getBoundingClientRect();
@@ -10941,15 +8808,6 @@ function OhhwellsBridge() {
10941
8808
  probeHoverCardsAt(clientX, clientY);
10942
8809
  };
10943
8810
  const handleDragOver = (e) => {
10944
- const footerSession = footerDragRef.current;
10945
- if (footerSession) {
10946
- e.preventDefault();
10947
- if (e.dataTransfer) e.dataTransfer.dropEffect = "move";
10948
- const slot = footerSession.kind === "link" && footerSession.hrefKey ? hitTestLinkDropSlot(e.clientX, e.clientY, footerSession.hrefKey) : hitTestColumnDropSlot(e.clientX, e.clientY);
10949
- refreshFooterDragVisualsRef.current(footerSession, slot, e.clientX, e.clientY);
10950
- return;
10951
- }
10952
- if (onNavDragOver(e)) return;
10953
8811
  e.preventDefault();
10954
8812
  const el = findImageAtPoint(e.clientX, e.clientY, false);
10955
8813
  if (!el) {
@@ -11112,8 +8970,8 @@ function OhhwellsBridge() {
11112
8970
  if (sibling !== el) sibling.innerHTML = html;
11113
8971
  });
11114
8972
  const timers = autoSaveTimers.current;
11115
- const existing2 = timers.get(key);
11116
- if (existing2) clearTimeout(existing2);
8973
+ const existing = timers.get(key);
8974
+ if (existing) clearTimeout(existing);
11117
8975
  timers.set(key, setTimeout(() => {
11118
8976
  timers.delete(key);
11119
8977
  postToParentRef.current({ type: "ow:change", nodes: [{ key, text: html }] });
@@ -11156,7 +9014,6 @@ function OhhwellsBridge() {
11156
9014
  }
11157
9015
  editContentRef.current = { ...editContentRef.current, ...content };
11158
9016
  reconcileNavbarItemsFromContent(editContentRef.current);
11159
- syncNavigationDragCursorAttrs();
11160
9017
  enforceLinkHrefs();
11161
9018
  postToParentRef.current({ type: "ow:hydrate-done" });
11162
9019
  };
@@ -11177,48 +9034,6 @@ function OhhwellsBridge() {
11177
9034
  if (e.data?.type !== "ui:escape") return;
11178
9035
  if (linkPopoverOpenRef.current) {
11179
9036
  setLinkPopoverRef.current(null);
11180
- return;
11181
- }
11182
- if (activeElRef.current) {
11183
- const hrefCtx = getHrefKeyFromElement(activeElRef.current);
11184
- const navAnchor = hrefCtx ? getNavigationItemAnchor(hrefCtx.anchor) : null;
11185
- if (navAnchor) {
11186
- const el = activeElRef.current;
11187
- if (originalContentRef.current !== null) {
11188
- el.innerHTML = originalContentRef.current;
11189
- }
11190
- el.removeAttribute("contenteditable");
11191
- el.removeAttribute("data-ohw-editing");
11192
- activeElRef.current = null;
11193
- setMaxBadge(null);
11194
- setActiveCommands(/* @__PURE__ */ new Set());
11195
- setToolbarShowEditLink(false);
11196
- postToParentRef.current({ type: "ow:exit-edit" });
11197
- reselectNavigationItemRef.current(navAnchor);
11198
- return;
11199
- }
11200
- if (originalContentRef.current !== null) {
11201
- activeElRef.current.innerHTML = originalContentRef.current;
11202
- const key = activeElRef.current.dataset.ohwKey;
11203
- if (key) {
11204
- postToParentRef.current({ type: "ow:change", nodes: [{ key, text: originalContentRef.current }] });
11205
- }
11206
- }
11207
- deselectRef.current();
11208
- deactivateRef.current();
11209
- return;
11210
- }
11211
- if (selectedElRef.current) {
11212
- if (toolbarVariantRef.current === "select-frame") {
11213
- deselectRef.current();
11214
- return;
11215
- }
11216
- const parent = getNavigationSelectionParent(selectedElRef.current);
11217
- if (parent && isNavigationContainer(parent)) {
11218
- selectFrameRef.current(parent);
11219
- } else {
11220
- deselectRef.current();
11221
- }
11222
9037
  }
11223
9038
  };
11224
9039
  window.addEventListener("message", handleUiEscape);
@@ -11243,7 +9058,7 @@ function OhhwellsBridge() {
11243
9058
  return;
11244
9059
  }
11245
9060
  const parent = getNavigationSelectionParent(selectedElRef.current);
11246
- if (parent && isNavigationContainer(parent)) {
9061
+ if (parent) {
11247
9062
  e.preventDefault();
11248
9063
  selectFrameRef.current(parent);
11249
9064
  } else {
@@ -11261,7 +9076,6 @@ function OhhwellsBridge() {
11261
9076
  el2.innerHTML = originalContentRef.current;
11262
9077
  }
11263
9078
  el2.removeAttribute("contenteditable");
11264
- el2.removeAttribute("data-ohw-editing");
11265
9079
  activeElRef.current = null;
11266
9080
  setMaxBadge(null);
11267
9081
  setActiveCommands(/* @__PURE__ */ new Set());
@@ -11313,26 +9127,6 @@ function OhhwellsBridge() {
11313
9127
  if (siblingHintElRef.current) {
11314
9128
  setSiblingHintRect(siblingHintElRef.current.getBoundingClientRect());
11315
9129
  }
11316
- const selected = selectedElRef.current;
11317
- if (selected && !footerDragRef.current && !navDragRef.current) {
11318
- if (selected.hasAttribute("data-ohw-footer-col") || Boolean(selected.closest("footer") && isInferredFooterGroup(selected))) {
11319
- setSiblingHintRects(
11320
- listFooterColumns().filter((column) => column !== selected).map((column) => column.getBoundingClientRect())
11321
- );
11322
- } else if (isNavigationItem(selected)) {
11323
- setSiblingHintRects(collectNavigationItemSiblingHintRects(selected));
11324
- }
11325
- }
11326
- if (footerDragRef.current) {
11327
- const session = footerDragRef.current;
11328
- const slot = session.kind === "link" && session.hrefKey ? hitTestLinkDropSlot(session.lastClientX, session.lastClientY, session.hrefKey) : hitTestColumnDropSlot(session.lastClientX, session.lastClientY);
11329
- refreshFooterDragVisualsRef.current(session, slot, session.lastClientX, session.lastClientY);
11330
- }
11331
- if (navDragRef.current) {
11332
- const session = navDragRef.current;
11333
- const slot = hitTestNavDropSlot(session.lastClientX, session.lastClientY, session.hrefKey);
11334
- refreshNavDragVisualsRef.current(session, slot, session.lastClientX, session.lastClientY);
11335
- }
11336
9130
  if (hoveredImageRef.current) {
11337
9131
  const el = hoveredImageRef.current;
11338
9132
  const r2 = el.getBoundingClientRect();
@@ -11343,7 +9137,7 @@ function OhhwellsBridge() {
11343
9137
  };
11344
9138
  const handleSave = (e) => {
11345
9139
  if (e.data?.type !== "ow:save") return;
11346
- const nodes = collectEditableNodes(editContentRef.current);
9140
+ const nodes = collectEditableNodes();
11347
9141
  const tracker = document.querySelector("[data-ohw-sections-tracker]");
11348
9142
  if (tracker?.textContent) nodes.push({ key: "__ohw_sections", type: "sections", text: tracker.textContent });
11349
9143
  postToParentRef.current({ type: "ow:save-result", nodes });
@@ -11401,83 +9195,30 @@ function OhhwellsBridge() {
11401
9195
  const h = document.documentElement.scrollHeight;
11402
9196
  if (h > 50) postToParentRef.current({ type: "ow:height", height: h });
11403
9197
  };
11404
- let selectionChangeRaf = null;
11405
- const runSelectionChange = () => {
11406
- selectionChangeRaf = null;
11407
- const activeRoot = activeElRef.current;
11408
- if (!activeRoot) return;
9198
+ const handleSelectionChange = () => {
9199
+ if (!activeElRef.current) return;
11409
9200
  const next = /* @__PURE__ */ new Set();
11410
- const sel = window.getSelection();
11411
- let startNode = null;
11412
- if (sel && sel.rangeCount > 0) {
11413
- const range = sel.getRangeAt(0);
11414
- startNode = range.startContainer;
11415
- if (startNode.nodeType === Node.ELEMENT_NODE) {
11416
- const el = startNode;
11417
- startNode = el.childNodes[range.startOffset] ?? el.childNodes[range.startOffset - 1] ?? el;
9201
+ for (const cmd of ["bold", "italic", "underline", "strikeThrough", "insertUnorderedList", "insertOrderedList"]) {
9202
+ try {
9203
+ if (document.queryCommandState(cmd)) next.add(cmd);
9204
+ } catch {
11418
9205
  }
11419
9206
  }
11420
- const startEl = startNode ? startNode.nodeType === Node.TEXT_NODE ? startNode.parentElement : startNode : null;
11421
- if (startEl) {
11422
- const cs = getComputedStyle(startEl);
11423
- const weight = parseInt(cs.fontWeight, 10);
11424
- let hasBold = cs.fontWeight === "bold" || !Number.isNaN(weight) && weight >= 600;
11425
- let hasItalic = cs.fontStyle === "italic" || cs.fontStyle === "oblique";
11426
- let hasUnderline = false;
11427
- let hasStrike = false;
11428
- let hasUl = false;
11429
- let hasOl = false;
11430
- for (let el = startEl; el; el = el.parentElement) {
11431
- if (el.tagName === "B" || el.tagName === "STRONG") hasBold = true;
11432
- if (el.tagName === "I" || el.tagName === "EM") hasItalic = true;
11433
- const decoration = getComputedStyle(el).textDecorationLine;
11434
- if (decoration.includes("underline") || el.tagName === "U" || el.tagName === "INS") hasUnderline = true;
11435
- if (decoration.includes("line-through") || el.tagName === "S" || el.tagName === "STRIKE" || el.tagName === "DEL") hasStrike = true;
11436
- if (el.tagName === "UL") hasUl = true;
11437
- if (el.tagName === "OL") hasOl = true;
11438
- if (el === activeRoot) break;
11439
- }
11440
- if (hasBold) next.add("bold");
11441
- if (hasItalic) next.add("italic");
11442
- if (hasUnderline) next.add("underline");
11443
- if (hasStrike) next.add("strikeThrough");
11444
- if (hasUl) next.add("insertUnorderedList");
11445
- if (hasOl) next.add("insertOrderedList");
11446
- const block = startEl.closest("div, p, h1, h2, h3, h4, h5, h6, li, td, th") ?? startEl;
11447
- const align = getComputedStyle(block).textAlign;
11448
- if (align === "center") next.add("justifyCenter");
11449
- else if (align === "right" || align === "end") next.add("justifyRight");
11450
- else next.add("justifyLeft");
11451
- }
11452
- setActiveCommands((prev) => {
11453
- if (prev.size === next.size) {
11454
- let same = true;
11455
- for (const cmd of prev) {
11456
- if (!next.has(cmd)) {
11457
- same = false;
11458
- break;
11459
- }
11460
- }
11461
- if (same) return prev;
9207
+ const sel = window.getSelection();
9208
+ const anchor = sel?.anchorNode;
9209
+ if (anchor) {
9210
+ const el = anchor.nodeType === Node.TEXT_NODE ? anchor.parentElement : anchor;
9211
+ const block = el?.closest("div, p, h1, h2, h3, h4, h5, h6, li, td, th") ?? el;
9212
+ if (block) {
9213
+ const align = getComputedStyle(block).textAlign;
9214
+ if (align === "center") next.add("justifyCenter");
9215
+ else if (align === "right" || align === "end") next.add("justifyRight");
9216
+ else next.add("justifyLeft");
11462
9217
  }
11463
- return next;
11464
- });
11465
- };
11466
- const handleSelectionChange = () => {
11467
- if (pointerHeldRef.current) return;
11468
- if (selectionChangeRaf !== null) return;
11469
- selectionChangeRaf = requestAnimationFrame(runSelectionChange);
11470
- };
11471
- const markPointerHeld = (e) => {
11472
- if (e.button !== 0) return;
11473
- pointerHeldRef.current = true;
11474
- };
11475
- const markPointerReleased = () => {
11476
- if (!pointerHeldRef.current) return;
11477
- pointerHeldRef.current = false;
11478
- handleSelectionChange();
9218
+ }
9219
+ setActiveCommands(next);
11479
9220
  };
11480
- refreshActiveCommandsRef.current = runSelectionChange;
9221
+ refreshActiveCommandsRef.current = handleSelectionChange;
11481
9222
  const handleDocMouseLeave = () => {
11482
9223
  hoveredImageRef.current = null;
11483
9224
  setMediaHover(null);
@@ -11522,15 +9263,15 @@ function OhhwellsBridge() {
11522
9263
  };
11523
9264
  const applyToolbarPos = (rect) => {
11524
9265
  const ps = parentScrollRef.current;
11525
- const measuredW = toolbarElRef.current?.offsetWidth || 330;
9266
+ const approxW = 330;
11526
9267
  if (toolbarElRef.current) {
11527
- const { top, left, transform } = calcToolbarPos(rect, ps, measuredW);
9268
+ const { top, left, transform } = calcToolbarPos(rect, ps, approxW);
11528
9269
  toolbarElRef.current.style.top = `${top}px`;
11529
9270
  toolbarElRef.current.style.left = `${left}px`;
11530
9271
  toolbarElRef.current.style.transform = transform;
11531
9272
  }
11532
9273
  if (glowElRef.current) {
11533
- const GAP = SELECTION_CHROME_GAP2;
9274
+ const GAP = 6;
11534
9275
  glowElRef.current.style.top = `${rect.top - GAP}px`;
11535
9276
  glowElRef.current.style.left = `${rect.left - GAP}px`;
11536
9277
  glowElRef.current.style.width = `${rect.width + GAP * 2}px`;
@@ -11618,11 +9359,6 @@ function OhhwellsBridge() {
11618
9359
  return;
11619
9360
  }
11620
9361
  }
11621
- const footerColumn = findHoveredNavOrFooterContainer(clientX, clientY);
11622
- if (footerColumn?.hasAttribute("data-ohw-footer-col") || footerColumn?.hasAttribute("data-ohw-footer-column")) {
11623
- selectFrameRef.current(footerColumn);
11624
- return;
11625
- }
11626
9362
  deactivateRef.current();
11627
9363
  };
11628
9364
  window.addEventListener("message", handleSave);
@@ -11694,177 +9430,7 @@ function OhhwellsBridge() {
11694
9430
  if (imageShowTimerRef.current) clearTimeout(imageShowTimerRef.current);
11695
9431
  };
11696
9432
  }, [isEditMode, refreshStateRules]);
11697
- (0, import_react9.useEffect)(() => {
11698
- if (!isEditMode) return;
11699
- const THRESHOLD = 10;
11700
- const resolveWasSelected = (el) => {
11701
- if (selectedElRef.current === el) return true;
11702
- const activeAnchor = activeElRef.current ? getNavigationItemAnchor(activeElRef.current) : null;
11703
- return activeAnchor === el;
11704
- };
11705
- const onPointerDown = (e) => {
11706
- if (e.button !== 0) return;
11707
- if (suppressNextClickRef.current && Date.now() >= suppressClickUntilRef.current && !footerDragRef.current && !footerPointerDragRef.current?.started) {
11708
- suppressNextClickRef.current = false;
11709
- }
11710
- if (footerDragRef.current) return;
11711
- const target = e.target;
11712
- if (!target) return;
11713
- 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]')) {
11714
- return;
11715
- }
11716
- if (target.closest("[data-ohw-item-drag-surface]")) return;
11717
- const anchor = getNavigationItemAnchor(target);
11718
- const hrefKey = anchor?.getAttribute("data-ohw-href-key") ?? null;
11719
- if (anchor && isFooterHrefKey(hrefKey)) {
11720
- footerPointerDragRef.current = {
11721
- el: anchor,
11722
- kind: "link",
11723
- startX: e.clientX,
11724
- startY: e.clientY,
11725
- pointerId: e.pointerId,
11726
- wasSelected: resolveWasSelected(anchor),
11727
- started: false
11728
- };
11729
- return;
11730
- }
11731
- const col = target.closest("[data-ohw-footer-col]");
11732
- if (col && !getNavigationItemAnchor(target)) {
11733
- footerPointerDragRef.current = {
11734
- el: col,
11735
- kind: "column",
11736
- startX: e.clientX,
11737
- startY: e.clientY,
11738
- pointerId: e.pointerId,
11739
- wasSelected: resolveWasSelected(col),
11740
- started: false
11741
- };
11742
- }
11743
- };
11744
- const onPointerMove = (e) => {
11745
- const pending = footerPointerDragRef.current;
11746
- if (!pending) return;
11747
- if (pending.started) {
11748
- e.preventDefault();
11749
- clearTextSelection();
11750
- const session = footerDragRef.current;
11751
- if (!session) return;
11752
- const slot = session.kind === "link" && session.hrefKey ? hitTestLinkDropSlot(e.clientX, e.clientY, session.hrefKey) : hitTestColumnDropSlot(e.clientX, e.clientY);
11753
- refreshFooterDragVisualsRef.current(session, slot, e.clientX, e.clientY);
11754
- return;
11755
- }
11756
- const dx = e.clientX - pending.startX;
11757
- const dy = e.clientY - pending.startY;
11758
- if (dx * dx + dy * dy < THRESHOLD * THRESHOLD) return;
11759
- e.preventDefault();
11760
- pending.started = true;
11761
- armFooterPressDrag();
11762
- clearTextSelection();
11763
- try {
11764
- document.body.setPointerCapture(pending.pointerId);
11765
- } catch {
11766
- }
11767
- if (linkPopoverOpenRef.current) {
11768
- setLinkPopoverRef.current(null);
11769
- }
11770
- if (activeElRef.current) {
11771
- deactivateRef.current();
11772
- }
11773
- if (pending.kind === "link") {
11774
- const key = pending.el.getAttribute("data-ohw-href-key");
11775
- if (!key) return;
11776
- const column = findFooterColumnForLink(pending.el);
11777
- const columns2 = listFooterColumns();
11778
- beginFooterDragRef.current({
11779
- kind: "link",
11780
- hrefKey: key,
11781
- columnEl: column,
11782
- sourceColumnIndex: column ? columns2.indexOf(column) : 0,
11783
- wasSelected: pending.wasSelected,
11784
- draggedEl: pending.el,
11785
- lastClientX: e.clientX,
11786
- lastClientY: e.clientY,
11787
- activeSlot: null
11788
- });
11789
- return;
11790
- }
11791
- const columns = listFooterColumns();
11792
- const idx = columns.indexOf(pending.el);
11793
- if (idx < 0) return;
11794
- beginFooterDragRef.current({
11795
- kind: "column",
11796
- hrefKey: null,
11797
- columnEl: pending.el,
11798
- sourceColumnIndex: idx,
11799
- wasSelected: pending.wasSelected,
11800
- draggedEl: pending.el,
11801
- lastClientX: e.clientX,
11802
- lastClientY: e.clientY,
11803
- activeSlot: null
11804
- });
11805
- };
11806
- const endPointerDrag = (e) => {
11807
- const pending = footerPointerDragRef.current;
11808
- footerPointerDragRef.current = null;
11809
- try {
11810
- if (document.body.hasPointerCapture(e.pointerId)) {
11811
- document.body.releasePointerCapture(e.pointerId);
11812
- }
11813
- } catch {
11814
- }
11815
- if (!pending) return;
11816
- if (!pending.started) {
11817
- unlockFooterDragInteraction();
11818
- return;
11819
- }
11820
- suppressNextClickRef.current = true;
11821
- suppressClickUntilRef.current = Date.now() + 500;
11822
- commitFooterDragRef.current(e.clientX, e.clientY);
11823
- };
11824
- const blockSelectStart = (e) => {
11825
- if (footerDragRef.current || footerPointerDragRef.current?.started || document.documentElement.hasAttribute("data-ohw-footer-press-drag")) {
11826
- e.preventDefault();
11827
- }
11828
- };
11829
- const onDragEnd = (_e) => {
11830
- if (!footerDragRef.current) return;
11831
- suppressNextClickRef.current = true;
11832
- suppressClickUntilRef.current = Date.now() + 500;
11833
- commitFooterDragRef.current();
11834
- };
11835
- document.addEventListener("pointerdown", onPointerDown, true);
11836
- document.addEventListener("pointermove", onPointerMove, true);
11837
- document.addEventListener("pointerup", endPointerDrag, true);
11838
- document.addEventListener("pointercancel", endPointerDrag, true);
11839
- document.addEventListener("selectstart", blockSelectStart, true);
11840
- document.addEventListener("dragend", onDragEnd, true);
11841
- return () => {
11842
- document.removeEventListener("pointerdown", onPointerDown, true);
11843
- document.removeEventListener("pointermove", onPointerMove, true);
11844
- document.removeEventListener("pointerup", endPointerDrag, true);
11845
- document.removeEventListener("pointercancel", endPointerDrag, true);
11846
- document.removeEventListener("selectstart", blockSelectStart, true);
11847
- document.removeEventListener("dragend", onDragEnd, true);
11848
- unlockFooterDragInteraction();
11849
- };
11850
- }, [isEditMode]);
11851
- (0, import_react9.useEffect)(() => {
11852
- if (!isEditMode) return;
11853
- const debugSelectionChange = () => {
11854
- const sel = window.getSelection();
11855
- console.log(
11856
- "[OHW DEBUG selectionchange]",
11857
- JSON.stringify(sel?.toString() ?? ""),
11858
- "collapsed=",
11859
- sel?.isCollapsed,
11860
- "anchorNode=",
11861
- sel?.anchorNode,
11862
- "activeEl=",
11863
- activeElRef.current
11864
- );
11865
- };
11866
- }, [isEditMode]);
11867
- (0, import_react9.useEffect)(() => {
9433
+ (0, import_react8.useEffect)(() => {
11868
9434
  const handler = (e) => {
11869
9435
  if (e.data?.type !== "ow:request-schedule-config") return;
11870
9436
  const insertAfterVal = e.data.insertAfter;
@@ -11880,7 +9446,7 @@ function OhhwellsBridge() {
11880
9446
  window.addEventListener("message", handler);
11881
9447
  return () => window.removeEventListener("message", handler);
11882
9448
  }, [processConfigRequest]);
11883
- (0, import_react9.useEffect)(() => {
9449
+ (0, import_react8.useEffect)(() => {
11884
9450
  if (!isEditMode) return;
11885
9451
  document.querySelectorAll("[data-ohw-active-state]").forEach((el) => {
11886
9452
  el.removeAttribute("data-ohw-active-state");
@@ -11901,13 +9467,7 @@ function OhhwellsBridge() {
11901
9467
  const next = { ...prev, [pathKey]: sections };
11902
9468
  return shouldUseDevFixtures() ? { ...DEV_SECTIONS_BY_PATH, ...next } : next;
11903
9469
  });
11904
- postToParent2({
11905
- type: "ow:ready",
11906
- version: "1",
11907
- path: pathname,
11908
- nodes: collectEditableNodes(editContentRef.current),
11909
- sections
11910
- });
9470
+ postToParent2({ type: "ow:ready", version: "1", path: pathname, nodes: collectEditableNodes(), sections });
11911
9471
  postToParent2({ type: "ow:request-site-pages" });
11912
9472
  }, 150);
11913
9473
  return () => {
@@ -11915,20 +9475,19 @@ function OhhwellsBridge() {
11915
9475
  clearTimeout(timer);
11916
9476
  };
11917
9477
  }, [pathname, isEditMode, refreshStateRules, postToParent2]);
11918
- (0, import_react9.useEffect)(() => {
9478
+ (0, import_react8.useEffect)(() => {
11919
9479
  scrollToHashSectionWhenReady();
11920
9480
  const onHashChange = () => scrollToHashSectionWhenReady();
11921
9481
  window.addEventListener("hashchange", onHashChange);
11922
9482
  return () => window.removeEventListener("hashchange", onHashChange);
11923
9483
  }, [pathname]);
11924
- const handleCommand = (0, import_react9.useCallback)((cmd) => {
11925
- document.execCommand("styleWithCSS", false, true);
9484
+ const handleCommand = (0, import_react8.useCallback)((cmd) => {
11926
9485
  document.execCommand(cmd, false);
11927
9486
  activeElRef.current?.focus();
11928
9487
  if (activeElRef.current) setToolbarRect(getEditMeasureEl(activeElRef.current).getBoundingClientRect());
11929
9488
  refreshActiveCommandsRef.current();
11930
9489
  }, []);
11931
- const handleStateChange = (0, import_react9.useCallback)((state) => {
9490
+ const handleStateChange = (0, import_react8.useCallback)((state) => {
11932
9491
  if (!activeStateElRef.current) return;
11933
9492
  const el = activeStateElRef.current;
11934
9493
  if (state === "Default") {
@@ -11941,11 +9500,11 @@ function OhhwellsBridge() {
11941
9500
  }
11942
9501
  setToggleState((prev) => prev ? { ...prev, activeState: state } : null);
11943
9502
  }, [deactivate]);
11944
- const closeLinkPopover = (0, import_react9.useCallback)(() => {
9503
+ const closeLinkPopover = (0, import_react8.useCallback)(() => {
11945
9504
  addNavAfterAnchorRef.current = null;
11946
9505
  setLinkPopover(null);
11947
9506
  }, []);
11948
- const openLinkPopoverForActive = (0, import_react9.useCallback)(() => {
9507
+ const openLinkPopoverForActive = (0, import_react8.useCallback)(() => {
11949
9508
  const hrefCtx = getHrefKeyFromElement(activeElRef.current);
11950
9509
  if (!hrefCtx) return;
11951
9510
  bumpLinkPopoverGrace();
@@ -11956,7 +9515,7 @@ function OhhwellsBridge() {
11956
9515
  });
11957
9516
  deactivate();
11958
9517
  }, [deactivate]);
11959
- const openLinkPopoverForSelected = (0, import_react9.useCallback)(() => {
9518
+ const openLinkPopoverForSelected = (0, import_react8.useCallback)(() => {
11960
9519
  const anchor = selectedElRef.current;
11961
9520
  if (!anchor) return;
11962
9521
  const key = anchor.getAttribute("data-ohw-href-key");
@@ -11969,7 +9528,7 @@ function OhhwellsBridge() {
11969
9528
  });
11970
9529
  deselect();
11971
9530
  }, [deselect]);
11972
- const handleLinkPopoverSubmit = (0, import_react9.useCallback)(
9531
+ const handleLinkPopoverSubmit = (0, import_react8.useCallback)(
11973
9532
  (target) => {
11974
9533
  const session = linkPopoverSessionRef.current;
11975
9534
  if (!session) return;
@@ -12028,13 +9587,13 @@ function OhhwellsBridge() {
12028
9587
  const showEditLink = toolbarShowEditLink;
12029
9588
  const currentSections = sectionsByPath[pathname] ?? [];
12030
9589
  linkPopoverOpenRef.current = linkPopover !== null;
12031
- const handleMediaReplace = (0, import_react9.useCallback)(
9590
+ const handleMediaReplace = (0, import_react8.useCallback)(
12032
9591
  (key) => {
12033
9592
  postToParent2({ type: "ow:image-pick", key, elementType: mediaHover?.elementType ?? "image" });
12034
9593
  },
12035
9594
  [postToParent2, mediaHover?.elementType]
12036
9595
  );
12037
- const handleMediaFadeOutComplete = (0, import_react9.useCallback)((key) => {
9596
+ const handleMediaFadeOutComplete = (0, import_react8.useCallback)((key) => {
12038
9597
  setUploadingRects((prev) => {
12039
9598
  if (!(key in prev)) return prev;
12040
9599
  const next = { ...prev };
@@ -12042,7 +9601,7 @@ function OhhwellsBridge() {
12042
9601
  return next;
12043
9602
  });
12044
9603
  }, []);
12045
- const handleVideoSettingsChange = (0, import_react9.useCallback)(
9604
+ const handleVideoSettingsChange = (0, import_react8.useCallback)(
12046
9605
  (key, settings) => {
12047
9606
  document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
12048
9607
  const video = getVideoEl(el);
@@ -12065,9 +9624,9 @@ function OhhwellsBridge() {
12065
9624
  [postToParent2]
12066
9625
  );
12067
9626
  return bridgeRoot ? (0, import_react_dom3.createPortal)(
12068
- /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(import_jsx_runtime24.Fragment, { children: [
12069
- /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { ref: attachVisibleViewport, "data-ohw-visible-viewport": "", "aria-hidden": true }),
12070
- Object.entries(uploadingRects).map(([key, { rect, fadingOut }]) => /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
9627
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(import_jsx_runtime23.Fragment, { children: [
9628
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { ref: attachVisibleViewport, "data-ohw-visible-viewport": "", "aria-hidden": true }),
9629
+ Object.entries(uploadingRects).map(([key, { rect, fadingOut }]) => /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
12071
9630
  MediaOverlay,
12072
9631
  {
12073
9632
  hover: { key, rect, elementType: "image", isDragOver: false, hasTextOverlap: false },
@@ -12078,7 +9637,7 @@ function OhhwellsBridge() {
12078
9637
  },
12079
9638
  `uploading-${key}`
12080
9639
  )),
12081
- mediaHover && !(mediaHover.key in uploadingRects) && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
9640
+ mediaHover && !(mediaHover.key in uploadingRects) && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
12082
9641
  MediaOverlay,
12083
9642
  {
12084
9643
  hover: mediaHover,
@@ -12087,93 +9646,49 @@ function OhhwellsBridge() {
12087
9646
  onVideoSettingsChange: handleVideoSettingsChange
12088
9647
  }
12089
9648
  ),
12090
- siblingHintRect && !linkPopover && !isItemDragging && siblingHintRects.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(ItemInteractionLayer, { rect: siblingHintRect, state: "sibling-hint" }),
12091
- siblingHintRects.length > 0 && !linkPopover && siblingHintRects.map((rect, i) => /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(ItemInteractionLayer, { rect, state: "sibling-hint" }, `sibling-hint-${i}`)),
12092
- isItemDragging && draggedItemRect && selectedElRef.current !== footerDragRef.current?.draggedEl && selectedElRef.current !== navDragRef.current?.draggedEl && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(ItemInteractionLayer, { rect: draggedItemRect, state: "dragging" }),
12093
- isItemDragging && footerDropSlots.map((slot, i) => /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
12094
- "div",
12095
- {
12096
- className: "pointer-events-none fixed z-2147483646",
12097
- style: {
12098
- left: slot.left,
12099
- top: slot.top,
12100
- width: slot.width,
12101
- height: slot.height
12102
- },
12103
- children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
12104
- DropIndicator,
12105
- {
12106
- direction: slot.direction,
12107
- state: activeFooterDropIndex === i ? "dragActive" : "dragIdle",
12108
- className: "!h-full !w-full"
12109
- }
12110
- )
12111
- },
12112
- `footer-drop-${slot.direction}-${slot.columnIndex}-${slot.insertIndex}-${i}`
12113
- )),
12114
- isItemDragging && navDropSlots.map((slot, i) => /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
12115
- "div",
9649
+ siblingHintRect && !isItemDragging && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(ItemInteractionLayer, { rect: siblingHintRect, state: "sibling-hint" }),
9650
+ hoveredNavContainerRect && toolbarVariant !== "select-frame" && !linkPopover && !isItemDragging && !siblingHintRect && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(ItemInteractionLayer, { rect: hoveredNavContainerRect, state: "hover" }),
9651
+ toolbarVariant === "select-frame" && toolbarRect && !linkPopover && !isItemDragging && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
9652
+ NavbarContainerChrome,
12116
9653
  {
12117
- className: "pointer-events-none fixed z-2147483646",
12118
- style: {
12119
- left: slot.left,
12120
- top: slot.top,
12121
- width: slot.width,
12122
- height: slot.height
12123
- },
12124
- children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
12125
- DropIndicator,
12126
- {
12127
- direction: slot.direction,
12128
- state: activeNavDropIndex === i ? "dragActive" : "dragIdle",
12129
- className: "!h-full !w-full"
12130
- }
12131
- )
12132
- },
12133
- `nav-drop-${slot.direction}-${slot.parentId ?? "root"}-${slot.insertIndex}-${i}`
12134
- )),
12135
- hoveredNavContainerRect && toolbarVariant !== "select-frame" && !linkPopover && !isItemDragging && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(ItemInteractionLayer, { rect: hoveredNavContainerRect, state: "hover" }),
12136
- hoveredItemRect && !linkPopover && !hoveredNavContainerRect && !isItemDragging && hoveredItemElRef.current !== selectedElRef.current && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(ItemInteractionLayer, { rect: hoveredItemRect, state: "hover" }),
12137
- toolbarVariant === "select-frame" && toolbarRect && !linkPopover && !isItemDragging && !isFooterFrameSelection && selectedElRef.current && isNavbarLinksContainer(selectedElRef.current) && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(NavbarContainerChrome, { rect: toolbarRect, onAdd: handleAddTopLevelNavItem }),
12138
- toolbarRect && !linkPopover && (toolbarVariant === "link-action" || toolbarVariant === "select-frame") && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
9654
+ rect: toolbarRect,
9655
+ onAdd: handleAddTopLevelNavItem
9656
+ }
9657
+ ),
9658
+ hoveredItemRect && !hoveredNavContainerRect && !siblingHintRect && !isItemDragging && hoveredItemElRef.current !== selectedElRef.current && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(ItemInteractionLayer, { rect: hoveredItemRect, state: "hover" }),
9659
+ toolbarRect && (toolbarVariant === "link-action" || toolbarVariant === "select-frame") && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
12139
9660
  ItemInteractionLayer,
12140
9661
  {
12141
- rect: isItemDragging && draggedItemRect && (footerDragRef.current?.wasSelected || navDragRef.current?.wasSelected) ? draggedItemRect : toolbarRect,
9662
+ rect: toolbarRect,
12142
9663
  elRef: glowElRef,
12143
9664
  state: isItemDragging ? "dragging" : resolveItemInteractionState(toolbarRect, parentScrollRef.current),
12144
- showHandle: toolbarVariant === "link-action" && Boolean(reorderHrefKey) || toolbarVariant === "select-frame" && isFooterFrameSelection,
9665
+ showHandle: toolbarVariant === "link-action" && Boolean(reorderHrefKey),
12145
9666
  dragDisabled: reorderDragDisabled,
12146
9667
  dragHandleLabel: reorderHrefKey ? `Reorder ${reorderHrefKey}` : "Reorder item",
12147
9668
  onDragHandleDragStart: handleItemDragStart,
12148
9669
  onDragHandleDragEnd: handleItemDragEnd,
12149
- onItemPointerDown: handleItemChromePointerDown,
12150
- onItemClick: handleItemChromeClick,
12151
- itemDragSurface: !isFooterFrameSelection,
12152
- toolbar: toolbarVariant === "link-action" && !isItemDragging ? /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
9670
+ toolbar: toolbarVariant === "link-action" && !isItemDragging ? /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
12153
9671
  ItemActionToolbar,
12154
9672
  {
12155
9673
  onEditLink: openLinkPopoverForSelected,
12156
9674
  addItemDisabled: true,
12157
9675
  editLinkDisabled: false,
12158
- moreDisabled: true,
12159
- showAddItem: !selectedIsCta,
12160
- showMore: !selectedIsCta
9676
+ moreDisabled: true
12161
9677
  }
12162
9678
  ) : void 0
12163
9679
  }
12164
9680
  ),
12165
- toolbarRect && toolbarVariant === "rich-text" && !linkPopover && /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(import_jsx_runtime24.Fragment, { children: [
12166
- /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
9681
+ toolbarRect && toolbarVariant === "rich-text" && /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(import_jsx_runtime23.Fragment, { children: [
9682
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
12167
9683
  EditGlowChrome,
12168
9684
  {
12169
9685
  rect: toolbarRect,
12170
9686
  elRef: glowElRef,
12171
9687
  reorderHrefKey,
12172
- dragDisabled: reorderDragDisabled,
12173
- hideHandle: isItemDragging
9688
+ dragDisabled: reorderDragDisabled
12174
9689
  }
12175
9690
  ),
12176
- /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
9691
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
12177
9692
  FloatingToolbar,
12178
9693
  {
12179
9694
  rect: toolbarRect,
@@ -12186,7 +9701,7 @@ function OhhwellsBridge() {
12186
9701
  }
12187
9702
  )
12188
9703
  ] }),
12189
- maxBadge && /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(
9704
+ maxBadge && /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(
12190
9705
  "div",
12191
9706
  {
12192
9707
  "data-ohw-max-badge": "",
@@ -12212,7 +9727,7 @@ function OhhwellsBridge() {
12212
9727
  ]
12213
9728
  }
12214
9729
  ),
12215
- toggleState && !linkPopover && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
9730
+ toggleState && !linkPopover && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
12216
9731
  StateToggle,
12217
9732
  {
12218
9733
  rect: toggleState.rect,
@@ -12221,36 +9736,32 @@ function OhhwellsBridge() {
12221
9736
  onStateChange: handleStateChange
12222
9737
  }
12223
9738
  ),
12224
- sectionGap && !linkPopover && /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(
9739
+ sectionGap && !linkPopover && /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(
12225
9740
  "div",
12226
9741
  {
12227
9742
  "data-ohw-section-insert-line": "",
12228
9743
  className: "fixed left-0 w-full z-2147483646 flex items-center pointer-events-none",
12229
9744
  style: { top: sectionGap.y, transform: "translateY(-50%)" },
12230
9745
  children: [
12231
- /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { className: "flex-1 bg-primary", style: { height: 3 } }),
12232
- /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
9746
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { className: "flex-1 bg-primary", style: { height: 3 } }),
9747
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
12233
9748
  Badge,
12234
9749
  {
12235
9750
  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",
12236
9751
  onClick: () => {
12237
9752
  window.parent.postMessage(
12238
- {
12239
- type: "ow:add-section",
12240
- insertAfter: sectionGap.insertAfter,
12241
- insertBefore: sectionGap.insertBefore
12242
- },
9753
+ { type: "ow:add-section", insertAfter: sectionGap.insertAfter, insertBefore: sectionGap.insertBefore },
12243
9754
  "*"
12244
9755
  );
12245
9756
  },
12246
9757
  children: "Add Section"
12247
9758
  }
12248
9759
  ),
12249
- /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { className: "flex-1 bg-primary", style: { height: 3 } })
9760
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { className: "flex-1 bg-primary", style: { height: 3 } })
12250
9761
  ]
12251
9762
  }
12252
9763
  ),
12253
- linkPopover && dialogPortalContainer ? /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
9764
+ linkPopover && dialogPortalContainer ? /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
12254
9765
  LinkPopover,
12255
9766
  {
12256
9767
  panelRef: linkPopoverPanelRef,
@@ -12271,6 +9782,48 @@ function OhhwellsBridge() {
12271
9782
  bridgeRoot
12272
9783
  ) : null;
12273
9784
  }
9785
+
9786
+ // src/ui/drop-indicator.tsx
9787
+ var React10 = __toESM(require("react"), 1);
9788
+ var import_jsx_runtime24 = require("react/jsx-runtime");
9789
+ var dropIndicatorVariants = cva(
9790
+ "ov-gap-line pointer-events-none shrink-0 transition-opacity duration-150",
9791
+ {
9792
+ variants: {
9793
+ direction: {
9794
+ vertical: "h-6 w-[3px]",
9795
+ horizontal: "h-[3px] w-[200px]"
9796
+ },
9797
+ state: {
9798
+ default: "opacity-0",
9799
+ hover: "opacity-100",
9800
+ dragIdle: "opacity-40",
9801
+ dragActive: "opacity-100"
9802
+ }
9803
+ },
9804
+ defaultVariants: {
9805
+ direction: "vertical",
9806
+ state: "default"
9807
+ }
9808
+ }
9809
+ );
9810
+ var DropIndicator = React10.forwardRef(
9811
+ ({ className, direction, state, ...props }, ref) => {
9812
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
9813
+ "div",
9814
+ {
9815
+ ref,
9816
+ "data-slot": "drop-indicator",
9817
+ "data-direction": direction ?? "vertical",
9818
+ "data-state": state ?? "default",
9819
+ "aria-hidden": "true",
9820
+ className: cn(dropIndicatorVariants({ direction, state }), className),
9821
+ ...props
9822
+ }
9823
+ );
9824
+ }
9825
+ );
9826
+ DropIndicator.displayName = "DropIndicator";
12274
9827
  // Annotate the CommonJS export names for ESM import in node:
12275
9828
  0 && (module.exports = {
12276
9829
  CustomToolbar,