@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.js CHANGED
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
 
3
3
  // src/OhhwellsBridge.tsx
4
- import React11, { useCallback as useCallback5, useEffect as useEffect8, useLayoutEffect as useLayoutEffect3, useRef as useRef7, useState as useState7 } from "react";
4
+ import React9, { useCallback as useCallback3, useEffect as useEffect7, useLayoutEffect as useLayoutEffect2, useRef as useRef5, useState as useState6 } from "react";
5
5
  import { createRoot } from "react-dom/client";
6
6
  import { flushSync } from "react-dom";
7
7
 
@@ -48,13 +48,12 @@ function useLinkHrefGuardian(...deps) {
48
48
  }
49
49
 
50
50
  // src/ui/SchedulingWidget.tsx
51
- import { useCallback, useEffect, useId, useMemo, useRef, useState as useState2 } from "react";
51
+ import { useEffect, useId, useMemo, useRef, useState as useState2 } from "react";
52
52
 
53
53
  // src/ui/EmailCaptureModal.tsx
54
54
  import { useState } from "react";
55
55
  import { Dialog } from "radix-ui";
56
56
  import { jsx, jsxs } from "react/jsx-runtime";
57
- var isValidEmail = (value) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value);
58
57
  function Spinner() {
59
58
  return /* @__PURE__ */ jsxs(
60
59
  "svg",
@@ -88,17 +87,12 @@ function EmailCaptureModal({ title, subtitle, onSubmit, onClose }) {
88
87
  const [error, setError] = useState(null);
89
88
  const isBook = title === "Confirm your spot";
90
89
  const handleSubmit = async () => {
91
- const trimmed = email.trim();
92
- if (!trimmed) return;
93
- if (!isValidEmail(trimmed)) {
94
- setError("Please enter a valid email address.");
95
- return;
96
- }
90
+ if (!email.trim()) return;
97
91
  setLoading(true);
98
92
  setError(null);
99
93
  try {
100
- await onSubmit(trimmed);
101
- setSubmittedEmail(trimmed);
94
+ await onSubmit(email.trim());
95
+ setSubmittedEmail(email.trim());
102
96
  setSuccess(true);
103
97
  } catch (e) {
104
98
  setError(e instanceof Error ? e.message : "Something went wrong. Please try again.");
@@ -173,10 +167,7 @@ function EmailCaptureModal({ title, subtitle, onSubmit, onClose }) {
173
167
  {
174
168
  type: "email",
175
169
  value: email,
176
- onChange: (e) => {
177
- setEmail(e.target.value);
178
- if (error) setError(null);
179
- },
170
+ onChange: (e) => setEmail(e.target.value),
180
171
  onKeyDown: handleKeyDown,
181
172
  placeholder: "hello@gmail.com",
182
173
  autoFocus: true,
@@ -233,20 +224,12 @@ function formatClassTime(cls) {
233
224
  const em = endTotal % 60;
234
225
  return `${h}:${cls.startTime.minutes}-${eh}:${String(em).padStart(2, "0")}`;
235
226
  }
236
- function formatBookingLabel(cls) {
237
- const price = cls.price;
238
- const isFree = price === null || price === void 0 || `${price}` === "" || `${price}` === "0";
239
- return isFree ? "Book for free" : `Book for ${cls.currency ?? ""} ${price}`.trim();
240
- }
241
227
  function getBookingsOnDate(cls, date) {
242
228
  if (!cls.bookings?.length) return 0;
243
- const target = new Date(date);
244
- target.setHours(0, 0, 0, 0);
229
+ const ds = date.toISOString().split("T")[0];
245
230
  return cls.bookings.filter((b) => {
246
231
  try {
247
- const bookingDate = new Date(b.classDate);
248
- bookingDate.setHours(0, 0, 0, 0);
249
- return bookingDate.getTime() === target.getTime();
232
+ return new Date(b.classDate).toISOString().split("T")[0] === ds;
250
233
  } catch {
251
234
  return false;
252
235
  }
@@ -511,7 +494,7 @@ function ScheduleView({ schedule, dates, selectedIdx, onSelectDate, onOpenModal
511
494
  }
512
495
  )
513
496
  ] }),
514
- cls.showAvailability !== false && /* @__PURE__ */ jsx2("div", { className: "sm:hidden flex flex-col gap-px", children: isFull ? /* @__PURE__ */ jsx2("span", { className: "font-body text-sm text-(--color-dark,#200C02) opacity-80", children: "Full" }) : /* @__PURE__ */ jsxs2(Fragment, { children: [
497
+ /* @__PURE__ */ jsx2("div", { className: "sm:hidden flex flex-col gap-px", children: isFull ? /* @__PURE__ */ jsx2("span", { className: "font-body text-sm text-(--color-dark,#200C02) opacity-80", children: "Full" }) : /* @__PURE__ */ jsxs2(Fragment, { children: [
515
498
  /* @__PURE__ */ jsxs2("span", { className: "font-body text-sm text-(--color-dark,#200C02) opacity-80", children: [
516
499
  available,
517
500
  "/",
@@ -523,17 +506,9 @@ function ScheduleView({ schedule, dates, selectedIdx, onSelectDate, onOpenModal
523
506
  /* @__PURE__ */ jsxs2("div", { className: "flex flex-1 flex-col gap-2 min-w-0 sm:contents", children: [
524
507
  /* @__PURE__ */ jsxs2("div", { className: "flex-1 flex flex-col gap-2 min-w-0", children: [
525
508
  /* @__PURE__ */ jsx2("span", { className: "font-body text-base font-bold text-(--color-dark,#200C02) block truncate", children: cls.name }),
526
- cls.hostName && /* @__PURE__ */ jsx2("span", { className: "font-body text-base font-normal text-(--color-dark,#200C02) opacity-80 block truncate", children: cls.hostName }),
527
- cls.description && /* @__PURE__ */ jsx2(
528
- "span",
529
- {
530
- className: "font-body text-base font-normal text-(--color-dark,#200C02) opacity-80 block",
531
- style: { wordBreak: "break-word" },
532
- children: cls.description
533
- }
534
- )
509
+ cls.hostName && /* @__PURE__ */ jsx2("span", { className: "font-body text-base font-normal text-(--color-dark,#200C02) opacity-80 block truncate", children: cls.hostName })
535
510
  ] }),
536
- cls.showAvailability !== false && /* @__PURE__ */ jsx2("div", { className: "hidden sm:flex w-14 shrink-0 flex-col gap-px", children: isFull ? /* @__PURE__ */ jsx2("span", { className: "font-body text-sm text-(--color-dark,#200C02) opacity-80", children: "Full" }) : /* @__PURE__ */ jsxs2(Fragment, { children: [
511
+ /* @__PURE__ */ jsx2("div", { className: "hidden sm:flex w-14 shrink-0 flex-col gap-px", children: isFull ? /* @__PURE__ */ jsx2("span", { className: "font-body text-sm text-(--color-dark,#200C02) opacity-80", children: "Full" }) : /* @__PURE__ */ jsxs2(Fragment, { children: [
537
512
  /* @__PURE__ */ jsxs2("span", { className: "font-body text-sm text-(--color-dark,#200C02) opacity-80", children: [
538
513
  available,
539
514
  "/",
@@ -558,7 +533,7 @@ function ScheduleView({ schedule, dates, selectedIdx, onSelectDate, onOpenModal
558
533
  if (!cls.id) return;
559
534
  onOpenModal?.(isFull ? "waitlist" : "book", cls.id, selectedDate);
560
535
  },
561
- children: isFull ? "Join Waitlist" : formatBookingLabel(cls)
536
+ children: isFull ? "Join Waitlist" : "Book Now"
562
537
  }
563
538
  )
564
539
  ] })
@@ -600,17 +575,6 @@ function SchedulingWidget({ notifyOnConnect = false, initialScheduleId, insertAf
600
575
  const [isHovered, setIsHovered] = useState2(false);
601
576
  const [modalState, setModalState] = useState2(null);
602
577
  const switchScheduleIdRef = useRef(null);
603
- const liveScheduleIdRef = useRef(null);
604
- const refetchLiveSchedule = useCallback(async () => {
605
- const id = liveScheduleIdRef.current;
606
- if (!id) return;
607
- try {
608
- const res = await fetch(`${API_URL}/api/schedule/id/${id}`);
609
- const data = await res.json();
610
- if (data?.id) setSchedule(data);
611
- } catch {
612
- }
613
- }, []);
614
578
  const dates = useMemo(() => {
615
579
  const today = /* @__PURE__ */ new Date();
616
580
  today.setHours(0, 0, 0, 0);
@@ -630,18 +594,6 @@ function SchedulingWidget({ notifyOnConnect = false, initialScheduleId, insertAf
630
594
  }
631
595
  }
632
596
  }, [schedule, dates]);
633
- useEffect(() => {
634
- if (typeof window === "undefined") return;
635
- const onVisible = () => {
636
- if (!document.hidden) void refetchLiveSchedule();
637
- };
638
- window.addEventListener("focus", refetchLiveSchedule);
639
- document.addEventListener("visibilitychange", onVisible);
640
- return () => {
641
- window.removeEventListener("focus", refetchLiveSchedule);
642
- document.removeEventListener("visibilitychange", onVisible);
643
- };
644
- }, [refetchLiveSchedule]);
645
597
  useEffect(() => {
646
598
  if (typeof window === "undefined") return;
647
599
  const isInEditor = window.self !== window.top;
@@ -703,7 +655,7 @@ function SchedulingWidget({ notifyOnConnect = false, initialScheduleId, insertAf
703
655
  setLoading(false);
704
656
  return;
705
657
  }
706
- liveScheduleIdRef.current = effectiveId;
658
+ ;
707
659
  (async () => {
708
660
  try {
709
661
  const res = await fetch(`${API_URL}/api/schedule/id/${effectiveId}`);
@@ -760,14 +712,18 @@ function SchedulingWidget({ notifyOnConnect = false, initialScheduleId, insertAf
760
712
  const data = await res.json().catch(() => ({}));
761
713
  throw new Error(data?.message ?? "Something went wrong. Please try again.");
762
714
  }
763
- await refetchLiveSchedule();
764
715
  };
765
716
  const handleReplaceSchedule = () => {
766
717
  setIsHovered(false);
767
- window.parent.postMessage(
768
- { type: "ow:scheduling-not-connected", insertAfter, currentScheduleId: schedule?.id },
769
- "*"
770
- );
718
+ if (schedule) {
719
+ window.parent.postMessage({
720
+ type: "ow:schedule-connected",
721
+ schedule: { id: schedule.id, name: schedule.name },
722
+ insertAfter
723
+ }, "*");
724
+ } else {
725
+ window.parent.postMessage({ type: "ow:scheduling-not-connected", insertAfter }, "*");
726
+ }
771
727
  };
772
728
  if (!inEditor && !loading && !schedule) return null;
773
729
  const sectionId = `scheduling-${insertAfter}`;
@@ -810,19 +766,7 @@ function SchedulingWidget({ notifyOnConnect = false, initialScheduleId, insertAf
810
766
  ),
811
767
  /* @__PURE__ */ jsxs2("div", { className: "max-w-[1280px] mx-auto flex flex-col items-center gap-16", children: [
812
768
  /* @__PURE__ */ jsxs2("div", { className: "flex flex-col items-center gap-4", children: [
813
- /* @__PURE__ */ jsx2(
814
- "h2",
815
- {
816
- className: "font-display text-center m-0 text-(--color-dark,#200C02)",
817
- style: {
818
- fontSize: "var(--fs-section-h2, clamp(40px, 4.4vw, 64px))",
819
- fontWeight: "var(--font-weight-heading, 400)",
820
- lineHeight: 1.05,
821
- letterSpacing: "-0.02em"
822
- },
823
- children: schedule?.name ?? "Book an appointment"
824
- }
825
- ),
769
+ /* @__PURE__ */ jsx2("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" }),
826
770
  schedule?.description && /* @__PURE__ */ jsx2("p", { className: "font-body text-body text-center m-0 text-(--color-accent,#A89B83)", children: schedule.description })
827
771
  ] }),
828
772
  /* @__PURE__ */ jsxs2("div", { className: "w-full flex flex-col items-end gap-3", children: [
@@ -4298,8 +4242,7 @@ var CustomToolbar = React4.forwardRef(({ className, onMouseDown, ...props }, ref
4298
4242
  ref,
4299
4243
  "data-ohw-toolbar": "",
4300
4244
  className: cn(
4301
- // Figma: bg background, radius 8, gap-1, p-0.5, shadow-md no border
4302
- "inline-flex h-8 items-center gap-1 rounded-[var(--radius,0.5rem)] bg-background p-0.5 font-sans whitespace-nowrap shadow-md",
4245
+ "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)]",
4303
4246
  className
4304
4247
  ),
4305
4248
  onMouseDown: (e) => {
@@ -4327,8 +4270,8 @@ var CustomToolbarButton = React4.forwardRef(
4327
4270
  ref,
4328
4271
  type,
4329
4272
  className: cn(
4330
- "inline-flex size-7 shrink-0 items-center justify-center rounded-[calc(var(--radius,0.5rem)-2px)] text-foreground transition-colors",
4331
- active ? "bg-primary text-primary-foreground" : "bg-transparent hover:bg-muted disabled:cursor-not-allowed disabled:text-muted-foreground disabled:opacity-60",
4273
+ "inline-flex shrink-0 items-center justify-center rounded p-1.5 transition-colors",
4274
+ active ? "bg-primary text-primary-foreground" : "bg-transparent text-foreground hover:bg-muted disabled:cursor-not-allowed disabled:text-muted-foreground disabled:opacity-60",
4332
4275
  className
4333
4276
  ),
4334
4277
  ...props
@@ -4363,10 +4306,9 @@ var arrowClassBySide = {
4363
4306
  };
4364
4307
  function TooltipContent({
4365
4308
  className,
4366
- sideOffset = 3,
4309
+ sideOffset = 6,
4367
4310
  side = "bottom",
4368
4311
  children,
4369
- hideArrow,
4370
4312
  ...props
4371
4313
  }) {
4372
4314
  const resolvedSide = side ?? "bottom";
@@ -4377,13 +4319,12 @@ function TooltipContent({
4377
4319
  side,
4378
4320
  sideOffset,
4379
4321
  className: cn(
4380
- "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",
4381
- "origin-[var(--radix-tooltip-content-transform-origin)] animate-in fade-in-0 zoom-in-95",
4382
- "data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95",
4322
+ "relative z-[2147483647] w-fit max-w-xs rounded-md bg-foreground px-3 py-1.5 text-xs text-background shadow-md",
4323
+ 'before:pointer-events-none before:absolute before:size-2 before:rotate-45 before:bg-foreground before:content-[""]',
4324
+ arrowClassBySide[resolvedSide],
4325
+ "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",
4383
4326
  "data-[side=bottom]:slide-in-from-top-2 data-[side=top]:slide-in-from-bottom-2",
4384
4327
  "data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2",
4385
- !hideArrow && 'before:pointer-events-none before:absolute before:size-2 before:rotate-45 before:bg-foreground before:content-[""]',
4386
- !hideArrow && arrowClassBySide[resolvedSide],
4387
4328
  className
4388
4329
  ),
4389
4330
  ...props,
@@ -4404,7 +4345,7 @@ function ToolbarActionTooltip({
4404
4345
  const button = /* @__PURE__ */ jsx8(CustomToolbarButton, { type: "button", "aria-label": label, disabled, ...buttonProps, children });
4405
4346
  return /* @__PURE__ */ jsxs3(Tooltip, { children: [
4406
4347
  /* @__PURE__ */ jsx8(TooltipTrigger, { asChild: true, children: disabled ? /* @__PURE__ */ jsx8("span", { className: "inline-flex", children: button }) : button }),
4407
- /* @__PURE__ */ jsx8(TooltipContent, { side, sideOffset: 3, children: label })
4348
+ /* @__PURE__ */ jsx8(TooltipContent, { side, children: label })
4408
4349
  ] });
4409
4350
  }
4410
4351
  function ItemActionToolbar({
@@ -4414,8 +4355,6 @@ function ItemActionToolbar({
4414
4355
  editLinkDisabled = false,
4415
4356
  addItemDisabled = true,
4416
4357
  moreDisabled = true,
4417
- showAddItem = true,
4418
- showMore = true,
4419
4358
  tooltipSide = "bottom"
4420
4359
  }) {
4421
4360
  return /* @__PURE__ */ jsx8(TooltipProvider, { delayDuration: 0, children: /* @__PURE__ */ jsxs3(CustomToolbar, { children: [
@@ -4440,7 +4379,8 @@ function ItemActionToolbar({
4440
4379
  children: /* @__PURE__ */ jsx8(Link, { className: "size-4 shrink-0", "aria-hidden": true })
4441
4380
  }
4442
4381
  ),
4443
- showAddItem ? /* @__PURE__ */ jsx8(
4382
+ /* @__PURE__ */ jsx8(CustomToolbarDivider, {}),
4383
+ /* @__PURE__ */ jsx8(
4444
4384
  ToolbarActionTooltip,
4445
4385
  {
4446
4386
  label: "Add item",
@@ -4456,8 +4396,9 @@ function ItemActionToolbar({
4456
4396
  },
4457
4397
  children: /* @__PURE__ */ jsx8(Plus, { className: "size-4 shrink-0", "aria-hidden": true })
4458
4398
  }
4459
- ) : null,
4460
- showMore ? /* @__PURE__ */ jsx8(
4399
+ ),
4400
+ /* @__PURE__ */ jsx8(CustomToolbarDivider, {}),
4401
+ /* @__PURE__ */ jsx8(
4461
4402
  ToolbarActionTooltip,
4462
4403
  {
4463
4404
  label: "More",
@@ -4473,32 +4414,15 @@ function ItemActionToolbar({
4473
4414
  },
4474
4415
  children: /* @__PURE__ */ jsx8(MoreHorizontal, { className: "size-4 shrink-0", "aria-hidden": true })
4475
4416
  }
4476
- ) : null
4417
+ )
4477
4418
  ] }) });
4478
4419
  }
4479
4420
 
4480
4421
  // src/ui/item-interaction-layer.tsx
4481
- import * as React5 from "react";
4482
4422
  import { jsx as jsx9, jsxs as jsxs4 } from "react/jsx-runtime";
4483
4423
  var PRIMARY = "var(--ohw-primary, #0885FE)";
4484
- var FOCUS_RING = `0 0 0 4px color-mix(in srgb, ${PRIMARY} 12%, transparent)`;
4424
+ var FOCUS_RING = "0 0 0 4px rgba(8, 133, 254, 0.12)";
4485
4425
  var DRAG_SHADOW = "0px 4px 6px -1px rgba(0, 0, 0, 0.1), 0px 2px 4px -2px rgba(0, 0, 0, 0.1)";
4486
- var TOOLBAR_EDGE_MARGIN = 4;
4487
- var SELECTION_CHROME_GAP = 4;
4488
- var TOOLBAR_STROKE_GAP = 4;
4489
- function getChromeZIndex(state) {
4490
- switch (state) {
4491
- case "active-top":
4492
- case "active-bottom":
4493
- case "dragging":
4494
- return 2147483644;
4495
- case "hover":
4496
- case "sibling-hint":
4497
- return 2147483643;
4498
- default:
4499
- return 2147483642;
4500
- }
4501
- }
4502
4426
  function getChromeStyle(state) {
4503
4427
  switch (state) {
4504
4428
  case "hover":
@@ -4520,65 +4444,12 @@ function getChromeStyle(state) {
4520
4444
  case "dragging":
4521
4445
  return {
4522
4446
  border: `2px solid ${PRIMARY}`,
4523
- boxShadow: `${FOCUS_RING}, ${DRAG_SHADOW}`
4447
+ boxShadow: DRAG_SHADOW
4524
4448
  };
4525
4449
  default:
4526
4450
  return {};
4527
4451
  }
4528
4452
  }
4529
- function ClampedToolbarSlot({
4530
- placement,
4531
- children
4532
- }) {
4533
- const slotRef = React5.useRef(null);
4534
- React5.useLayoutEffect(() => {
4535
- const slot = slotRef.current;
4536
- if (!slot) return;
4537
- const clamp = () => {
4538
- const toolbar = slot.firstElementChild;
4539
- if (!toolbar) return;
4540
- const w = toolbar.offsetWidth;
4541
- if (w <= 0) return;
4542
- const parent = slot.offsetParent;
4543
- if (!parent) return;
4544
- const parentRect = parent.getBoundingClientRect();
4545
- const centerX = parentRect.left + parentRect.width / 2;
4546
- const half = w / 2;
4547
- const clampedCenter = Math.max(
4548
- TOOLBAR_EDGE_MARGIN + half,
4549
- Math.min(centerX, window.innerWidth - TOOLBAR_EDGE_MARGIN - half)
4550
- );
4551
- const offsetX = clampedCenter - centerX;
4552
- slot.style.transform = `translateX(calc(-50% + ${offsetX}px))`;
4553
- };
4554
- clamp();
4555
- const ro = new ResizeObserver(clamp);
4556
- ro.observe(slot);
4557
- if (slot.firstElementChild) ro.observe(slot.firstElementChild);
4558
- window.addEventListener("resize", clamp);
4559
- return () => {
4560
- ro.disconnect();
4561
- window.removeEventListener("resize", clamp);
4562
- };
4563
- }, [placement, children]);
4564
- return /* @__PURE__ */ jsx9(
4565
- "div",
4566
- {
4567
- ref: slotRef,
4568
- className: cn(
4569
- "pointer-events-auto absolute left-1/2",
4570
- placement === "top" ? "bottom-full" : "top-full"
4571
- ),
4572
- style: {
4573
- marginBottom: placement === "top" ? TOOLBAR_STROKE_GAP : void 0,
4574
- marginTop: placement === "bottom" ? TOOLBAR_STROKE_GAP : void 0,
4575
- transform: "translateX(-50%)"
4576
- },
4577
- "data-ohw-item-toolbar-anchor": placement,
4578
- children
4579
- }
4580
- );
4581
- }
4582
4453
  function ItemInteractionLayer({
4583
4454
  rect,
4584
4455
  state,
@@ -4589,18 +4460,14 @@ function ItemInteractionLayer({
4589
4460
  dragHandleLabel = "Reorder item",
4590
4461
  onDragHandleDragStart,
4591
4462
  onDragHandleDragEnd,
4592
- onItemPointerDown,
4593
- onItemClick,
4594
- itemDragSurface = true,
4595
- chromeGap = SELECTION_CHROME_GAP,
4463
+ chromeGap = 6,
4596
4464
  className
4597
4465
  }) {
4598
4466
  if (state === "default") return null;
4599
4467
  const isActive = state === "active-top" || state === "active-bottom";
4600
4468
  const isDragging = state === "dragging";
4601
4469
  const showToolbar = isActive && toolbar;
4602
- const showDragHandle = (isActive || isDragging) && showHandle;
4603
- const itemDragEnabled = itemDragSurface && isActive && showHandle && !isDragging && !dragDisabled;
4470
+ const showDragHandle = isActive && showHandle && !isDragging;
4604
4471
  return /* @__PURE__ */ jsxs4(
4605
4472
  "div",
4606
4473
  {
@@ -4614,7 +4481,7 @@ function ItemInteractionLayer({
4614
4481
  left: rect.left - chromeGap,
4615
4482
  width: rect.width + chromeGap * 2,
4616
4483
  height: rect.height + chromeGap * 2,
4617
- zIndex: getChromeZIndex(state)
4484
+ zIndex: 2147483646
4618
4485
  },
4619
4486
  children: [
4620
4487
  /* @__PURE__ */ jsx9(
@@ -4625,29 +4492,11 @@ function ItemInteractionLayer({
4625
4492
  style: getChromeStyle(state)
4626
4493
  }
4627
4494
  ),
4628
- itemDragEnabled && /* @__PURE__ */ jsx9(
4629
- "div",
4630
- {
4631
- "data-ohw-item-drag-surface": "",
4632
- className: "pointer-events-auto absolute inset-0 cursor-text rounded-lg",
4633
- onPointerDown: (e) => {
4634
- if (e.button !== 0) return;
4635
- onItemPointerDown?.(e);
4636
- },
4637
- onClick: (e) => {
4638
- e.preventDefault();
4639
- e.stopPropagation();
4640
- onItemClick?.(e.clientX, e.clientY);
4641
- }
4642
- }
4643
- ),
4644
4495
  showDragHandle && /* @__PURE__ */ jsx9(
4645
4496
  "div",
4646
4497
  {
4647
4498
  "data-ohw-drag-handle-container": "",
4648
- className: "pointer-events-auto absolute left-0 top-1/2 z-10 -translate-x-[calc(100%+7px)] -translate-y-1/2",
4649
- style: isDragging ? { opacity: 0 } : void 0,
4650
- "aria-hidden": isDragging || void 0,
4499
+ className: "pointer-events-auto absolute left-0 top-1/2 -translate-x-[calc(100%+7px)] -translate-y-1/2",
4651
4500
  children: /* @__PURE__ */ jsx9(
4652
4501
  DragHandle,
4653
4502
  {
@@ -4659,17 +4508,7 @@ function ItemInteractionLayer({
4659
4508
  e.preventDefault();
4660
4509
  return;
4661
4510
  }
4662
- const blank = document.createElement("canvas");
4663
- blank.width = 1;
4664
- blank.height = 1;
4665
- blank.style.cssText = "position:fixed;left:-9999px;top:-9999px;width:1px;height:1px;pointer-events:none";
4666
- document.body.appendChild(blank);
4667
- try {
4668
- e.dataTransfer.setDragImage(blank, 0, 0);
4669
- } finally {
4670
- requestAnimationFrame(() => blank.remove());
4671
- }
4672
- e.dataTransfer.setData("text/plain", dragHandleLabel);
4511
+ e.dataTransfer?.setData("text/plain", dragHandleLabel);
4673
4512
  e.dataTransfer.effectAllowed = "move";
4674
4513
  onDragHandleDragStart?.(e);
4675
4514
  },
@@ -4678,19 +4517,33 @@ function ItemInteractionLayer({
4678
4517
  )
4679
4518
  }
4680
4519
  ),
4681
- showToolbar && state === "active-top" && /* @__PURE__ */ jsx9(ClampedToolbarSlot, { placement: "top", children: toolbar }),
4682
- showToolbar && state === "active-bottom" && /* @__PURE__ */ jsx9(ClampedToolbarSlot, { placement: "bottom", children: toolbar })
4520
+ showToolbar && state === "active-top" && /* @__PURE__ */ jsx9(
4521
+ "div",
4522
+ {
4523
+ className: "pointer-events-auto absolute bottom-full left-1/2 mb-2 -translate-x-1/2",
4524
+ "data-ohw-item-toolbar-anchor": "top",
4525
+ children: toolbar
4526
+ }
4527
+ ),
4528
+ showToolbar && state === "active-bottom" && /* @__PURE__ */ jsx9(
4529
+ "div",
4530
+ {
4531
+ className: "pointer-events-auto absolute left-1/2 top-full mt-2 -translate-x-1/2",
4532
+ "data-ohw-item-toolbar-anchor": "bottom",
4533
+ children: toolbar
4534
+ }
4535
+ )
4683
4536
  ]
4684
4537
  }
4685
4538
  );
4686
4539
  }
4687
4540
 
4688
4541
  // src/ui/MediaOverlay.tsx
4689
- import * as React7 from "react";
4542
+ import * as React6 from "react";
4690
4543
  import { Film, ImageIcon, Pause, Play, Volume2, VolumeX } from "lucide-react";
4691
4544
 
4692
4545
  // src/ui/button.tsx
4693
- import * as React6 from "react";
4546
+ import * as React5 from "react";
4694
4547
  import { Slot } from "radix-ui";
4695
4548
  import { jsx as jsx10 } from "react/jsx-runtime";
4696
4549
  var buttonVariants = cva(
@@ -4713,7 +4566,7 @@ var buttonVariants = cva(
4713
4566
  }
4714
4567
  }
4715
4568
  );
4716
- var Button = React6.forwardRef(
4569
+ var Button = React5.forwardRef(
4717
4570
  ({ className, variant, size, asChild = false, ...props }, ref) => {
4718
4571
  const Comp = asChild ? Slot.Root : "button";
4719
4572
  return /* @__PURE__ */ jsx10(
@@ -4765,7 +4618,7 @@ function MediaOverlay({
4765
4618
  onVideoSettingsChange
4766
4619
  }) {
4767
4620
  const { rect } = hover;
4768
- const skeletonRef = React7.useRef(null);
4621
+ const skeletonRef = React6.useRef(null);
4769
4622
  const isVideo = hover.elementType === "video";
4770
4623
  const autoplay = hover.videoAutoplay ?? true;
4771
4624
  const muted = hover.videoMuted ?? true;
@@ -4777,7 +4630,7 @@ function MediaOverlay({
4777
4630
  height: rect.height,
4778
4631
  zIndex: 2147483646
4779
4632
  };
4780
- React7.useEffect(() => {
4633
+ React6.useEffect(() => {
4781
4634
  if (!isUploading || !fadingOut || !skeletonRef.current) return;
4782
4635
  const anim = skeletonRef.current.animate([{ opacity: 1 }, { opacity: 0 }], {
4783
4636
  duration: MEDIA_UPLOAD_FADE_MS,
@@ -5182,7 +5035,7 @@ function scrollToHashSectionWhenReady(behavior = "smooth") {
5182
5035
  import { useEffect as useEffect6 } from "react";
5183
5036
 
5184
5037
  // src/ui/dialog.tsx
5185
- import * as React8 from "react";
5038
+ import * as React7 from "react";
5186
5039
  import { Dialog as DialogPrimitive } from "radix-ui";
5187
5040
  import { X } from "lucide-react";
5188
5041
  import { jsx as jsx12, jsxs as jsxs6 } from "react/jsx-runtime";
@@ -5210,7 +5063,7 @@ function DialogOverlay({
5210
5063
  }
5211
5064
  );
5212
5065
  }
5213
- var DialogContent = React8.forwardRef(
5066
+ var DialogContent = React7.forwardRef(
5214
5067
  ({ className, children, showCloseButton = true, container, ...props }, ref) => {
5215
5068
  const positionMode = container ? "absolute" : "fixed";
5216
5069
  return /* @__PURE__ */ jsxs6(DialogPortal, { container: container ?? void 0, children: [
@@ -5265,7 +5118,7 @@ function DialogFooter({
5265
5118
  }
5266
5119
  );
5267
5120
  }
5268
- var DialogTitle = React8.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx12(
5121
+ var DialogTitle = React7.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx12(
5269
5122
  DialogPrimitive.Title,
5270
5123
  {
5271
5124
  ref,
@@ -5277,7 +5130,7 @@ var DialogTitle = React8.forwardRef(({ className, ...props }, ref) => /* @__PURE
5277
5130
  }
5278
5131
  ));
5279
5132
  DialogTitle.displayName = DialogPrimitive.Title.displayName;
5280
- var DialogDescription = React8.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx12(
5133
+ var DialogDescription = React7.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx12(
5281
5134
  DialogPrimitive.Description,
5282
5135
  {
5283
5136
  ref,
@@ -5379,12 +5232,12 @@ function SectionTreeItem({
5379
5232
  }
5380
5233
 
5381
5234
  // src/ui/link-modal/UrlOrPageInput.tsx
5382
- import { useEffect as useEffect3, useId as useId2, useRef as useRef4, useState as useState3 } from "react";
5235
+ import { useEffect as useEffect3, useId as useId2, useRef as useRef3, useState as useState3 } from "react";
5383
5236
 
5384
5237
  // src/ui/input.tsx
5385
- import * as React9 from "react";
5238
+ import * as React8 from "react";
5386
5239
  import { jsx as jsx15 } from "react/jsx-runtime";
5387
- var Input = React9.forwardRef(
5240
+ var Input = React8.forwardRef(
5388
5241
  ({ className, type, ...props }, ref) => {
5389
5242
  return /* @__PURE__ */ jsx15(
5390
5243
  "input",
@@ -5448,8 +5301,8 @@ function UrlOrPageInput({
5448
5301
  urlError
5449
5302
  }) {
5450
5303
  const inputId = useId2();
5451
- const inputRef = useRef4(null);
5452
- const rootRef = useRef4(null);
5304
+ const inputRef = useRef3(null);
5305
+ const rootRef = useRef3(null);
5453
5306
  const [isFocused, setIsFocused] = useState3(false);
5454
5307
  useEffect3(() => {
5455
5308
  if (!dropdownOpen) return;
@@ -5659,7 +5512,7 @@ function LinkEditorPanel({ state, onClose }) {
5659
5512
  }
5660
5513
 
5661
5514
  // src/ui/link-modal/SectionPickerOverlay.tsx
5662
- import { useCallback as useCallback2, useEffect as useEffect4, useMemo as useMemo2, useRef as useRef5, useState as useState4 } from "react";
5515
+ import { useCallback, useEffect as useEffect4, useMemo as useMemo2, useRef as useRef4, useState as useState4 } from "react";
5663
5516
  import { createPortal } from "react-dom";
5664
5517
  import { ArrowLeft, Check } from "lucide-react";
5665
5518
  import { usePathname, useRouter } from "next/navigation";
@@ -5744,10 +5597,10 @@ function SectionPickerOverlay({
5744
5597
  const pathname = usePathname();
5745
5598
  const [selectedId, setSelectedId] = useState4(null);
5746
5599
  const [hoveredId, setHoveredId] = useState4(null);
5747
- const pointerRef = useRef5(null);
5748
- const pointerScreenRef = useRef5(null);
5749
- const iframeOffsetTopRef = useRef5(null);
5750
- const sectionIdsRef = useRef5([]);
5600
+ const pointerRef = useRef4(null);
5601
+ const pointerScreenRef = useRef4(null);
5602
+ const iframeOffsetTopRef = useRef4(null);
5603
+ const sectionIdsRef = useRef4([]);
5751
5604
  const [chromeClip, setChromeClip] = useState4(
5752
5605
  () => ({
5753
5606
  top: 0,
@@ -5790,7 +5643,7 @@ function SectionPickerOverlay({
5790
5643
  );
5791
5644
  sectionIdsRef.current = sectionIds;
5792
5645
  const rects = useSectionRects(sectionIdsKey, isOnTargetPage);
5793
- const applyHoverAt = useCallback2(
5646
+ const applyHoverAt = useCallback(
5794
5647
  (x, y, liveRects) => {
5795
5648
  const ids = sectionIdsRef.current;
5796
5649
  const map = liveRects ?? readSectionRects(ids);
@@ -5870,7 +5723,7 @@ function SectionPickerOverlay({
5870
5723
  window.removeEventListener("message", onPointerSync);
5871
5724
  };
5872
5725
  }, [rects, applyHoverAt]);
5873
- const handleSelect = useCallback2(
5726
+ const handleSelect = useCallback(
5874
5727
  (section) => {
5875
5728
  if (selectedId) return;
5876
5729
  setSelectedId(section.id);
@@ -6001,7 +5854,7 @@ function SectionPickerOverlay({
6001
5854
  }
6002
5855
 
6003
5856
  // src/ui/link-modal/useLinkModalState.ts
6004
- import { useCallback as useCallback3, useEffect as useEffect5, useMemo as useMemo3, useState as useState5 } from "react";
5857
+ import { useCallback as useCallback2, useEffect as useEffect5, useMemo as useMemo3, useState as useState5 } from "react";
6005
5858
  function useLinkModalState({
6006
5859
  open,
6007
5860
  mode,
@@ -6020,7 +5873,7 @@ function useLinkModalState({
6020
5873
  const [step, setStep] = useState5("input");
6021
5874
  const [dropdownOpen, setDropdownOpen] = useState5(false);
6022
5875
  const [urlError, setUrlError] = useState5("");
6023
- const reset = useCallback3(() => {
5876
+ const reset = useCallback2(() => {
6024
5877
  setSearchValue("");
6025
5878
  setSelectedPage(null);
6026
5879
  setSelectedSection(null);
@@ -6365,155 +6218,9 @@ function shouldUseDevFixtures() {
6365
6218
  return new URLSearchParams(q).get("ohw-fixtures") === "1";
6366
6219
  }
6367
6220
 
6368
- // src/lib/nav-tree.ts
6369
- var MAX_LEVEL = { nav: 1 };
6370
- var ROOT = null;
6371
- function cloneForest(nodes) {
6372
- return nodes.map((n) => ({ id: n.id, children: cloneForest(n.children) }));
6373
- }
6374
- function findNode(nodes, id, parentId = ROOT) {
6375
- for (let i = 0; i < nodes.length; i++) {
6376
- const node = nodes[i];
6377
- if (node.id === id) return { node, parentId, index: i, siblings: nodes };
6378
- const nested = findNode(node.children, id, node.id);
6379
- if (nested) return nested;
6380
- }
6381
- return null;
6382
- }
6383
- function getChildrenOf(nodes, parentId) {
6384
- if (parentId === ROOT) return nodes;
6385
- const found = findNode(nodes, parentId);
6386
- return found ? found.node.children : null;
6387
- }
6388
- function isDescendant(nodes, ancestorId, maybeDescendantId) {
6389
- const found = findNode(nodes, ancestorId);
6390
- if (!found) return false;
6391
- return findNode(found.node.children, maybeDescendantId) !== null;
6392
- }
6393
- function levelOf(nodes, id) {
6394
- const loc = findNode(nodes, id);
6395
- if (!loc) return -1;
6396
- if (loc.parentId === ROOT) return 0;
6397
- return 1 + Math.max(0, levelOf(nodes, loc.parentId));
6398
- }
6399
- function insertedLevel(nodes, parentId) {
6400
- if (parentId === ROOT) return 0;
6401
- const parentLevel = levelOf(nodes, parentId);
6402
- if (parentLevel < 0) return -1;
6403
- return parentLevel + 1;
6404
- }
6405
- function takeNode(nodes, id) {
6406
- const forest = cloneForest(nodes);
6407
- const loc = findNode(forest, id);
6408
- if (!loc) return null;
6409
- const [taken] = loc.siblings.splice(loc.index, 1);
6410
- if (!taken) return null;
6411
- return { forest, taken };
6412
- }
6413
- function insertInto(forest, node, parentId, index) {
6414
- const siblings = getChildrenOf(forest, parentId);
6415
- if (!siblings) return false;
6416
- const clamped = Math.max(0, Math.min(index, siblings.length));
6417
- siblings.splice(clamped, 0, node);
6418
- return true;
6419
- }
6420
- function normalize(nodes) {
6421
- const walk = (list) => list.map((n) => {
6422
- const children = walk(n.children);
6423
- return { id: n.id, children };
6424
- });
6425
- return walk(nodes);
6426
- }
6427
- function moveNode(model, draggedId, target) {
6428
- const { parentId, index: targetIndex } = target;
6429
- if (parentId === draggedId) return null;
6430
- if (parentId !== ROOT && isDescendant(model, draggedId, parentId)) return null;
6431
- const nextLevel = insertedLevel(model, parentId);
6432
- if (nextLevel < 0 || nextLevel > MAX_LEVEL.nav) return null;
6433
- const draggedLoc = findNode(model, draggedId);
6434
- if (!draggedLoc) return null;
6435
- if (parentId !== ROOT && draggedLoc.node.children.length > 0) return null;
6436
- const taken = takeNode(model, draggedId);
6437
- if (!taken) return null;
6438
- let insertIndex = targetIndex;
6439
- if (draggedLoc.parentId === parentId && draggedLoc.index < targetIndex) {
6440
- insertIndex -= 1;
6441
- }
6442
- if (!insertInto(taken.forest, taken.taken, parentId, insertIndex)) return null;
6443
- const next = normalize(taken.forest);
6444
- if (forestsEqual(model, next)) return null;
6445
- return next;
6446
- }
6447
- function forestsEqual(a, b) {
6448
- if (a.length !== b.length) return false;
6449
- for (let i = 0; i < a.length; i++) {
6450
- const left = a[i];
6451
- const right = b[i];
6452
- if (left.id !== right.id) return false;
6453
- if (!forestsEqual(left.children, right.children)) return false;
6454
- }
6455
- return true;
6456
- }
6457
- function forestFromFlatOrder(order) {
6458
- return order.map((id) => ({ id, children: [] }));
6459
- }
6460
- function serializeNavOrder(nodes) {
6461
- const hasNesting = nodes.some((n) => n.children.length > 0);
6462
- if (!hasNesting) {
6463
- return JSON.stringify(nodes.map((n) => n.id));
6464
- }
6465
- return JSON.stringify(nodes);
6466
- }
6467
- function parseNavOrderJson(raw) {
6468
- if (!raw) return null;
6469
- try {
6470
- const parsed = JSON.parse(raw);
6471
- if (!Array.isArray(parsed)) return null;
6472
- if (parsed.every((x) => typeof x === "string")) {
6473
- return forestFromFlatOrder(parsed.filter((x) => typeof x === "string" && x.length > 0));
6474
- }
6475
- return parseNavNodeList(parsed);
6476
- } catch {
6477
- return null;
6478
- }
6479
- }
6480
- function parseNavNodeList(raw) {
6481
- const out = [];
6482
- for (const item of raw) {
6483
- if (typeof item === "string" && item.length > 0) {
6484
- out.push({ id: item, children: [] });
6485
- continue;
6486
- }
6487
- if (!item || typeof item !== "object") return null;
6488
- const rec = item;
6489
- if (typeof rec.id !== "string" || !rec.id) return null;
6490
- const children = Array.isArray(rec.children) ? parseNavNodeList(rec.children) : [];
6491
- if (!children) return null;
6492
- out.push({ id: rec.id, children });
6493
- }
6494
- return out;
6495
- }
6496
- function flattenNavOrder(nodes) {
6497
- const out = [];
6498
- const walk = (list) => {
6499
- for (const n of list) {
6500
- out.push(n.id);
6501
- walk(n.children);
6502
- }
6503
- };
6504
- walk(nodes);
6505
- return out;
6506
- }
6507
-
6508
6221
  // src/lib/nav-items.ts
6509
6222
  var NAV_COUNT_KEY = "__ohw_nav_count";
6510
6223
  var NAV_ORDER_KEY = "__ohw_nav_order";
6511
- var NAV_HREF_RE = /^nav-(\d+)-href$/;
6512
- function isNavbarHrefKey(key) {
6513
- if (!key) return false;
6514
- if (key === "nav-book-href") return false;
6515
- return NAV_HREF_RE.test(key);
6516
- }
6517
6224
  function getLinkHref(el) {
6518
6225
  const key = el.getAttribute("data-ohw-href-key");
6519
6226
  if (key) {
@@ -6550,7 +6257,7 @@ function listNavbarItems() {
6550
6257
  }
6551
6258
  function parseNavIndexFromKey(key) {
6552
6259
  if (!key) return null;
6553
- const match = key.match(NAV_HREF_RE);
6260
+ const match = key.match(/^nav-(\d+)-href$/);
6554
6261
  if (!match) return null;
6555
6262
  return parseInt(match[1], 10);
6556
6263
  }
@@ -6573,6 +6280,17 @@ function getNavbarExistingTargets() {
6573
6280
  function getNavOrderFromDom() {
6574
6281
  return listNavbarItems().map((el) => el.getAttribute("data-ohw-href-key")).filter((key) => Boolean(key));
6575
6282
  }
6283
+ function parseNavOrder(content) {
6284
+ const raw = content[NAV_ORDER_KEY];
6285
+ if (!raw) return null;
6286
+ try {
6287
+ const parsed = JSON.parse(raw);
6288
+ if (!Array.isArray(parsed)) return null;
6289
+ return parsed.filter((key) => typeof key === "string" && key.length > 0);
6290
+ } catch {
6291
+ return null;
6292
+ }
6293
+ }
6576
6294
  function findCounterpartByHrefKey(hrefKey, root) {
6577
6295
  return root.querySelector(`[data-ohw-href-key="${CSS.escape(hrefKey)}"]`);
6578
6296
  }
@@ -6636,47 +6354,19 @@ function insertNavbarItemDom(index, href, label, afterAnchor) {
6636
6354
  }
6637
6355
  return primary;
6638
6356
  }
6639
- function navOrderKeysEqual(a, b) {
6640
- if (a.length !== b.length) return false;
6641
- for (let i = 0; i < a.length; i++) {
6642
- if (a[i] !== b[i]) return false;
6643
- }
6644
- return true;
6645
- }
6646
- function getNavbarLinkKeysInContainer(container) {
6647
- return Array.from(container.querySelectorAll("[data-ohw-href-key]")).filter(isNavbarLinkItem).map((el) => el.getAttribute("data-ohw-href-key")).filter((key) => Boolean(key));
6648
- }
6649
6357
  function applyNavOrderToContainer(container, order) {
6650
- const desired = order.filter((key, i) => order.indexOf(key) === i);
6651
- const current = getNavbarLinkKeysInContainer(container);
6652
- const extras = current.filter((key) => !desired.includes(key));
6653
- const expected = [...desired.filter((key) => current.includes(key)), ...extras];
6654
- const orderedEls = [];
6655
- if (navOrderKeysEqual(current, expected)) return;
6656
- if (current.length === expected.length && current.every((key, i) => key === expected[i])) {
6657
- return;
6658
- }
6659
- const lastCurrentLinkEl = current.length > 0 ? findCounterpartByHrefKey(current[current.length - 1], container) : null;
6660
- const anchor = lastCurrentLinkEl?.parentElement === container ? lastCurrentLinkEl.nextElementSibling : null;
6661
6358
  const seen = /* @__PURE__ */ new Set();
6662
- for (const hrefKey of desired) {
6359
+ for (const hrefKey of order) {
6663
6360
  if (seen.has(hrefKey)) continue;
6664
6361
  seen.add(hrefKey);
6665
6362
  const el = findCounterpartByHrefKey(hrefKey, container);
6666
- if (el && isNavbarLinkItem(el)) orderedEls.push(el);
6363
+ if (el) container.appendChild(el);
6667
6364
  }
6668
6365
  for (const el of container.querySelectorAll("[data-ohw-href-key]")) {
6669
6366
  if (!isNavbarLinkItem(el)) continue;
6670
6367
  const key = el.getAttribute("data-ohw-href-key");
6671
6368
  if (!key || seen.has(key)) continue;
6672
- orderedEls.push(el);
6673
- }
6674
- for (const el of orderedEls) {
6675
- if (anchor) {
6676
- if (el.nextSibling !== anchor) container.insertBefore(el, anchor);
6677
- } else if (container.lastElementChild !== el) {
6678
- container.appendChild(el);
6679
- }
6369
+ container.appendChild(el);
6680
6370
  }
6681
6371
  }
6682
6372
  function applyNavOrder(order) {
@@ -6685,75 +6375,6 @@ function applyNavOrder(order) {
6685
6375
  const drawer = getNavbarDrawerContainer();
6686
6376
  if (drawer) applyNavOrderToContainer(drawer, order);
6687
6377
  }
6688
- function applyChildrenOrder(parentHrefKey, childKeys, root) {
6689
- const parent = findCounterpartByHrefKey(parentHrefKey, root);
6690
- if (!parent) return;
6691
- const group = parent.closest("[data-ohw-nav-group]");
6692
- const childrenRoot = group?.querySelector(":scope > [data-ohw-nav-children]");
6693
- if (!childrenRoot) return;
6694
- const current = Array.from(childrenRoot.querySelectorAll(":scope > [data-ohw-href-key]")).map((el) => el.getAttribute("data-ohw-href-key")).filter((key) => Boolean(key));
6695
- if (navOrderKeysEqual(current, childKeys)) return;
6696
- for (const key of childKeys) {
6697
- const el = findCounterpartByHrefKey(key, root);
6698
- if (!el) continue;
6699
- if (childrenRoot.lastElementChild !== el) {
6700
- childrenRoot.appendChild(el);
6701
- }
6702
- }
6703
- }
6704
- function applyNavForest(forest) {
6705
- if (forestsEqual(forest, getNavForestFromDom())) return;
6706
- const rootKeys = forest.map((n) => n.id);
6707
- applyNavOrder(rootKeys);
6708
- const desktop = getNavbarDesktopContainer();
6709
- const drawer = getNavbarDrawerContainer();
6710
- for (const node of forest) {
6711
- if (node.children.length === 0) continue;
6712
- const childKeys = node.children.map((c) => c.id);
6713
- if (desktop) applyChildrenOrder(node.id, childKeys, desktop);
6714
- if (drawer) applyChildrenOrder(node.id, childKeys, drawer);
6715
- }
6716
- }
6717
- function getNavForestFromDom() {
6718
- const items = listNavbarItems();
6719
- const nestedChildKeys = /* @__PURE__ */ new Set();
6720
- const roots = [];
6721
- for (const item of items) {
6722
- const key = item.getAttribute("data-ohw-href-key");
6723
- if (!key || !isNavbarHrefKey(key)) continue;
6724
- if (item.closest("[data-ohw-nav-children]")) continue;
6725
- const group = item.closest("[data-ohw-nav-group]");
6726
- const childrenRoot = group?.querySelector(":scope > [data-ohw-nav-children]");
6727
- const children = [];
6728
- if (childrenRoot) {
6729
- for (const child of childrenRoot.querySelectorAll(":scope > [data-ohw-href-key]")) {
6730
- if (!isNavbarLinkItem(child)) continue;
6731
- const childKey = child.getAttribute("data-ohw-href-key");
6732
- if (!childKey || !isNavbarHrefKey(childKey)) continue;
6733
- nestedChildKeys.add(childKey);
6734
- children.push({ id: childKey, children: [] });
6735
- }
6736
- }
6737
- roots.push({ id: key, children });
6738
- }
6739
- if (roots.length === 0) {
6740
- return forestFromFlatOrder(
6741
- getNavOrderFromDom().filter((k) => isNavbarHrefKey(k) && !nestedChildKeys.has(k))
6742
- );
6743
- }
6744
- return roots;
6745
- }
6746
- function planNavItemMove(hrefKey, parentId, insertIndex) {
6747
- const model = getNavForestFromDom();
6748
- const next = moveNode(model, hrefKey, { parentId, index: insertIndex });
6749
- if (!next) return null;
6750
- if (forestsEqual(model, next)) return null;
6751
- return {
6752
- forest: next,
6753
- orderJson: serializeNavOrder(next),
6754
- flatOrder: flattenNavOrder(next)
6755
- };
6756
- }
6757
6378
  function collectNavbarIndicesFromContent(content) {
6758
6379
  const indices = /* @__PURE__ */ new Set();
6759
6380
  for (const key of Object.keys(content)) {
@@ -6781,10 +6402,8 @@ function reconcileNavbarItemsFromContent(content) {
6781
6402
  if (!href && !label) continue;
6782
6403
  insertNavbarItemDom(index, href ?? "/", label ?? "Untitled", null);
6783
6404
  }
6784
- const orderForest = parseNavOrderJson(content[NAV_ORDER_KEY]);
6785
- if (orderForest?.length) {
6786
- applyNavForest(orderForest);
6787
- }
6405
+ const order = parseNavOrder(content);
6406
+ if (order?.length) applyNavOrder(order);
6788
6407
  }
6789
6408
  function buildNavOrderAfterInsert(afterAnchor, newHrefKey) {
6790
6409
  const order = getNavOrderFromDom();
@@ -6824,982 +6443,75 @@ function insertNavbarItem(href, label, afterAnchor = null) {
6824
6443
  };
6825
6444
  }
6826
6445
 
6827
- // src/lib/footer-items.ts
6828
- var FOOTER_ORDER_KEY = "__ohw_footer_order";
6829
- var FOOTER_HREF_RE = /^footer-(\d+)-(\d+)-href$/;
6830
- function parseFooterHrefKey(key) {
6831
- if (!key) return null;
6832
- const match = key.match(FOOTER_HREF_RE);
6833
- if (!match) return null;
6834
- return { col: parseInt(match[1], 10), item: parseInt(match[2], 10) };
6835
- }
6836
- function isFooterHrefKey(key) {
6837
- return parseFooterHrefKey(key) !== null;
6838
- }
6839
- function getFooterRoot() {
6840
- return document.querySelector('footer[data-ohw-section="footer"], footer');
6841
- }
6842
- function getFooterLinksContainer() {
6843
- return document.querySelector("[data-ohw-footer-links]") ?? document.querySelector(".rb-footer-links");
6844
- }
6845
- function isFooterLinkAnchor(el) {
6846
- if (!el.matches("[data-ohw-href-key]")) return false;
6847
- if (!isFooterHrefKey(el.getAttribute("data-ohw-href-key"))) return false;
6848
- return Boolean(el.querySelector('[data-ohw-editable="text"]'));
6849
- }
6850
- function listFooterColumns() {
6851
- const root = getFooterLinksContainer() ?? getFooterRoot()?.querySelector(".rb-footer-links") ?? null;
6852
- if (!root) return [];
6853
- const explicit = Array.from(root.querySelectorAll(":scope > [data-ohw-footer-col]"));
6854
- if (explicit.length > 0) return explicit;
6855
- return Array.from(root.children).filter((el) => {
6856
- if (!(el instanceof HTMLElement)) return false;
6857
- return Array.from(el.querySelectorAll(":scope > [data-ohw-href-key]")).some(
6858
- isFooterLinkAnchor
6859
- );
6860
- });
6861
- }
6862
- function listFooterLinksInColumn(column) {
6863
- return Array.from(column.querySelectorAll(":scope > [data-ohw-href-key]")).filter(
6864
- isFooterLinkAnchor
6865
- );
6866
- }
6867
- function findFooterColumnForLink(anchor) {
6868
- const columns = listFooterColumns();
6869
- return columns.find((col) => col.contains(anchor)) ?? null;
6870
- }
6871
- function getFooterOrderFromDom() {
6872
- return listFooterColumns().map(
6873
- (col) => listFooterLinksInColumn(col).map((el) => el.getAttribute("data-ohw-href-key")).filter((key) => Boolean(key))
6874
- );
6875
- }
6876
- function findFooterLinkByKey(hrefKey) {
6877
- return document.querySelector(
6878
- `footer [data-ohw-href-key="${CSS.escape(hrefKey)}"]`
6879
- );
6880
- }
6881
- function syncFooterColumnIndices(columns) {
6882
- columns.forEach((col, index) => {
6883
- const next = String(index);
6884
- if (col.getAttribute("data-ohw-footer-col") !== next) {
6885
- col.setAttribute("data-ohw-footer-col", next);
6446
+ // src/ui/navbar-container-chrome.tsx
6447
+ import { Plus as Plus2 } from "lucide-react";
6448
+ import { jsx as jsx21 } from "react/jsx-runtime";
6449
+ function NavbarContainerChrome({
6450
+ rect,
6451
+ onAdd
6452
+ }) {
6453
+ const chromeGap = 6;
6454
+ return /* @__PURE__ */ jsx21(
6455
+ "div",
6456
+ {
6457
+ "data-ohw-navbar-container-chrome": "",
6458
+ "data-ohw-bridge": "",
6459
+ className: "pointer-events-none fixed z-[2147483647]",
6460
+ style: {
6461
+ top: rect.top - chromeGap,
6462
+ left: rect.left - chromeGap,
6463
+ width: rect.width + chromeGap * 2,
6464
+ height: rect.height + chromeGap * 2
6465
+ },
6466
+ children: /* @__PURE__ */ jsx21(
6467
+ "button",
6468
+ {
6469
+ type: "button",
6470
+ "data-ohw-navbar-add-button": "",
6471
+ 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",
6472
+ style: { top: "70%" },
6473
+ "aria-label": "Add item",
6474
+ onMouseDown: (e) => {
6475
+ e.preventDefault();
6476
+ e.stopPropagation();
6477
+ },
6478
+ onClick: (e) => {
6479
+ e.preventDefault();
6480
+ e.stopPropagation();
6481
+ onAdd();
6482
+ },
6483
+ children: /* @__PURE__ */ jsx21(Plus2, { className: "size-4 shrink-0 text-foreground", "aria-hidden": true })
6484
+ }
6485
+ )
6886
6486
  }
6887
- });
6888
- }
6889
- function appendChildIfNeeded(parent, child) {
6890
- if (parent.lastElementChild !== child) {
6891
- parent.appendChild(child);
6892
- }
6487
+ );
6893
6488
  }
6894
- function applyFooterOrder(order) {
6895
- const container = getFooterLinksContainer();
6896
- if (!container) return;
6897
- const columns = listFooterColumns();
6898
- if (columns.length === 0) return;
6899
- const currentOrder = getFooterOrderFromDom();
6900
- if (ordersEqual(order, currentOrder)) {
6901
- syncFooterColumnIndices(columns);
6902
- return;
6903
- }
6904
- const used = /* @__PURE__ */ new Set();
6905
- const orderedCols = [];
6906
- for (const colOrder of order) {
6907
- let matched = null;
6908
- for (const hrefKey of colOrder) {
6909
- const link = findFooterLinkByKey(hrefKey);
6910
- if (!link) continue;
6911
- const owner = findFooterColumnForLink(link);
6912
- if (owner && !used.has(owner)) {
6913
- matched = owner;
6914
- break;
6915
- }
6916
- }
6917
- if (!matched) {
6918
- matched = columns.find((col) => !used.has(col)) ?? null;
6919
- }
6920
- if (matched) {
6921
- used.add(matched);
6922
- orderedCols.push(matched);
6923
- }
6924
- }
6925
- for (const col of columns) {
6926
- if (!used.has(col)) orderedCols.push(col);
6927
- }
6928
- for (const col of orderedCols) {
6929
- appendChildIfNeeded(container, col);
6930
- }
6931
- const freshColumns = listFooterColumns();
6932
- syncFooterColumnIndices(freshColumns);
6933
- for (let c = 0; c < order.length; c++) {
6934
- const col = freshColumns[c];
6935
- if (!col) continue;
6936
- const colOrder = order[c];
6937
- for (const hrefKey of colOrder) {
6938
- const el = findFooterLinkByKey(hrefKey);
6939
- if (!el) continue;
6940
- if (el.parentElement !== col || col.lastElementChild !== el) {
6941
- col.appendChild(el);
6489
+
6490
+ // src/ui/badge.tsx
6491
+ import { jsx as jsx22 } from "react/jsx-runtime";
6492
+ var badgeVariants = cva(
6493
+ "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",
6494
+ {
6495
+ variants: {
6496
+ variant: {
6497
+ default: "border-transparent bg-primary text-primary-foreground",
6498
+ secondary: "border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80",
6499
+ destructive: "border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80",
6500
+ outline: "text-foreground"
6942
6501
  }
6502
+ },
6503
+ defaultVariants: {
6504
+ variant: "default"
6943
6505
  }
6944
6506
  }
6945
- }
6946
- function ordersEqual(a, b) {
6947
- if (a.length !== b.length) return false;
6948
- for (let i = 0; i < a.length; i++) {
6949
- const left = a[i];
6950
- const right = b[i];
6951
- if (left.length !== right.length) return false;
6952
- for (let j = 0; j < left.length; j++) {
6953
- if (left[j] !== right[j]) return false;
6954
- }
6955
- }
6956
- return true;
6957
- }
6958
- function planFooterLinkMove(hrefKey, targetColIndex, insertIndex) {
6959
- const order = getFooterOrderFromDom();
6960
- if (targetColIndex < 0 || targetColIndex >= order.length) return null;
6961
- let fromCol = -1;
6962
- let fromIdx = -1;
6963
- for (let c = 0; c < order.length; c++) {
6964
- const idx = order[c].indexOf(hrefKey);
6965
- if (idx >= 0) {
6966
- fromCol = c;
6967
- fromIdx = idx;
6968
- break;
6969
- }
6970
- }
6971
- if (fromCol < 0) return null;
6972
- const next = order.map((col) => [...col]);
6973
- next[fromCol].splice(fromIdx, 1);
6974
- let adjusted = insertIndex;
6975
- if (fromCol === targetColIndex && fromIdx < insertIndex) adjusted -= 1;
6976
- adjusted = Math.max(0, Math.min(adjusted, next[targetColIndex].length));
6977
- next[targetColIndex].splice(adjusted, 0, hrefKey);
6978
- if (ordersEqual(next, order)) return null;
6979
- return next;
6980
- }
6981
- function planFooterColumnMove(fromIndex, toIndex) {
6982
- const order = getFooterOrderFromDom();
6983
- const columns = listFooterColumns();
6984
- if (fromIndex < 0 || fromIndex >= columns.length || toIndex < 0 || toIndex > columns.length) {
6985
- return null;
6986
- }
6987
- if (toIndex === fromIndex || toIndex === fromIndex + 1) return null;
6988
- const nextOrder = order.map((col) => [...col]);
6989
- const [movedOrder] = nextOrder.splice(fromIndex, 1);
6990
- if (!movedOrder) return null;
6991
- let adjusted = toIndex;
6992
- if (fromIndex < toIndex) adjusted -= 1;
6993
- adjusted = Math.max(0, Math.min(adjusted, nextOrder.length));
6994
- nextOrder.splice(adjusted, 0, movedOrder);
6995
- if (ordersEqual(nextOrder, order)) return null;
6996
- return nextOrder;
6997
- }
6998
- function parseFooterOrder(content) {
6999
- const raw = content[FOOTER_ORDER_KEY];
7000
- if (!raw) return null;
7001
- try {
7002
- const parsed = JSON.parse(raw);
7003
- if (!Array.isArray(parsed)) return null;
7004
- return parsed.filter((col) => Array.isArray(col)).map((col) => col.filter((key) => typeof key === "string" && key.length > 0));
7005
- } catch {
7006
- return null;
7007
- }
7008
- }
7009
- function reconcileFooterOrderFromContent(content) {
7010
- const order = parseFooterOrder(content);
7011
- if (!order?.length) return;
7012
- const current = getFooterOrderFromDom();
7013
- if (ordersEqual(order, current)) {
7014
- syncFooterColumnIndices(listFooterColumns());
7015
- return;
7016
- }
7017
- applyFooterOrder(order);
7018
- }
7019
- function isRowLayoutColumn(links) {
7020
- if (links.length < 2) return false;
7021
- const firstTop = links[0].getBoundingClientRect().top;
7022
- return links.every((link) => Math.abs(link.getBoundingClientRect().top - firstTop) < 4);
7023
- }
7024
- function buildLinkDropSlots(column, columnIndex) {
7025
- const links = listFooterLinksInColumn(column);
7026
- const colRect = column.getBoundingClientRect();
7027
- const slots = [];
7028
- const barThickness = 3;
7029
- if (links.length === 0) {
7030
- slots.push({
7031
- insertIndex: 0,
7032
- columnIndex,
7033
- left: colRect.left,
7034
- top: colRect.top + colRect.height / 2 - barThickness / 2,
7035
- width: colRect.width,
7036
- height: barThickness,
7037
- direction: "horizontal"
7038
- });
7039
- return slots;
7040
- }
7041
- if (isRowLayoutColumn(links)) {
7042
- for (let i = 0; i <= links.length; i++) {
7043
- let left;
7044
- if (i === 0) {
7045
- left = links[0].getBoundingClientRect().left - barThickness / 2;
7046
- } else if (i === links.length) {
7047
- const last = links[links.length - 1].getBoundingClientRect();
7048
- left = last.right - barThickness / 2;
7049
- } else {
7050
- const prev = links[i - 1].getBoundingClientRect();
7051
- const next = links[i].getBoundingClientRect();
7052
- left = (prev.right + next.left) / 2 - barThickness / 2;
7053
- }
7054
- const heightRef = i === 0 ? links[0].getBoundingClientRect() : i === links.length ? links[links.length - 1].getBoundingClientRect() : links[i].getBoundingClientRect();
7055
- slots.push({
7056
- insertIndex: i,
7057
- columnIndex,
7058
- left,
7059
- top: heightRef.top,
7060
- width: barThickness,
7061
- height: Math.max(heightRef.height, colRect.height * 0.8),
7062
- direction: "vertical"
7063
- });
7064
- }
7065
- return slots;
7066
- }
7067
- for (let i = 0; i <= links.length; i++) {
7068
- let top;
7069
- if (i === 0) {
7070
- top = links[0].getBoundingClientRect().top - barThickness / 2;
7071
- } else if (i === links.length) {
7072
- const last = links[links.length - 1].getBoundingClientRect();
7073
- top = last.bottom - barThickness / 2;
7074
- } else {
7075
- const prev = links[i - 1].getBoundingClientRect();
7076
- const next = links[i].getBoundingClientRect();
7077
- top = (prev.bottom + next.top) / 2 - barThickness / 2;
7078
- }
7079
- const widthRef = i === 0 ? links[0].getBoundingClientRect() : i === links.length ? links[links.length - 1].getBoundingClientRect() : links[i].getBoundingClientRect();
7080
- slots.push({
7081
- insertIndex: i,
7082
- columnIndex,
7083
- left: widthRef.left,
7084
- top,
7085
- width: Math.max(widthRef.width, colRect.width * 0.8),
7086
- height: barThickness,
7087
- direction: "horizontal"
7088
- });
7089
- }
7090
- return slots;
7091
- }
7092
- function buildColumnDropSlots() {
7093
- const columns = listFooterColumns();
7094
- const slots = [];
7095
- const barThickness = 3;
7096
- if (columns.length === 0) return slots;
7097
- for (let i = 0; i <= columns.length; i++) {
7098
- let left;
7099
- let height;
7100
- let top;
7101
- if (i === 0) {
7102
- const first = columns[0].getBoundingClientRect();
7103
- left = first.left - barThickness / 2;
7104
- top = first.top;
7105
- height = first.height;
7106
- } else if (i === columns.length) {
7107
- const last = columns[columns.length - 1].getBoundingClientRect();
7108
- left = last.right - barThickness / 2;
7109
- top = last.top;
7110
- height = last.height;
7111
- } else {
7112
- const prev = columns[i - 1].getBoundingClientRect();
7113
- const next = columns[i].getBoundingClientRect();
7114
- left = (prev.right + next.left) / 2 - barThickness / 2;
7115
- top = Math.min(prev.top, next.top);
7116
- height = Math.max(prev.bottom, next.bottom) - top;
7117
- }
7118
- slots.push({
7119
- insertIndex: i,
7120
- columnIndex: i,
7121
- left,
7122
- top,
7123
- width: barThickness,
7124
- height: Math.max(height, 24),
7125
- direction: "vertical"
7126
- });
7127
- }
7128
- return slots;
7129
- }
7130
- function hitTestLinkDropSlot(clientX, clientY, draggedHrefKey) {
7131
- const columns = listFooterColumns();
7132
- let best = null;
7133
- for (let c = 0; c < columns.length; c++) {
7134
- const col = columns[c];
7135
- const colRect = col.getBoundingClientRect();
7136
- const inColX = clientX >= colRect.left - 24 && clientX <= colRect.right + 24;
7137
- if (!inColX) continue;
7138
- const slots = buildLinkDropSlots(col, c).filter((slot) => {
7139
- const links = listFooterLinksInColumn(col);
7140
- const fromIdx = links.findIndex(
7141
- (el) => el.getAttribute("data-ohw-href-key") === draggedHrefKey
7142
- );
7143
- if (fromIdx < 0) return true;
7144
- if (c === findColumnIndexForKey(draggedHrefKey) && (slot.insertIndex === fromIdx || slot.insertIndex === fromIdx + 1)) {
7145
- return true;
7146
- }
7147
- return true;
7148
- });
7149
- for (const slot of slots) {
7150
- const dist = slot.direction === "vertical" ? Math.abs(clientX - (slot.left + slot.width / 2)) : Math.abs(clientY - (slot.top + slot.height / 2));
7151
- if (!best || dist < best.dist) best = { slot, dist };
7152
- }
7153
- }
7154
- return best?.slot ?? null;
7155
- }
7156
- function findColumnIndexForKey(hrefKey) {
7157
- const order = getFooterOrderFromDom();
7158
- for (let c = 0; c < order.length; c++) {
7159
- if (order[c].includes(hrefKey)) return c;
7160
- }
7161
- return -1;
7162
- }
7163
- function hitTestColumnDropSlot(clientX, _clientY) {
7164
- const slots = buildColumnDropSlots();
7165
- let best = null;
7166
- for (const slot of slots) {
7167
- const cx2 = slot.left + slot.width / 2;
7168
- const dist = Math.abs(clientX - cx2);
7169
- if (!best || dist < best.dist) best = { slot, dist };
7170
- }
7171
- return best?.slot ?? null;
7172
- }
7173
-
7174
- // src/lib/item-drag-interaction.ts
7175
- function disableNativeHrefDrag(el) {
7176
- if (el.draggable) el.draggable = false;
7177
- if (el.getAttribute("draggable") !== "false") {
7178
- el.setAttribute("draggable", "false");
7179
- }
7180
- }
7181
- function clearTextSelection() {
7182
- const sel = window.getSelection();
7183
- if (sel && !sel.isCollapsed) sel.removeAllRanges();
7184
- }
7185
- function armItemPressDrag() {
7186
- document.documentElement.setAttribute("data-ohw-footer-press-drag", "");
7187
- }
7188
- function lockItemDuringDrag() {
7189
- document.documentElement.removeAttribute("data-ohw-footer-press-drag");
7190
- document.documentElement.setAttribute("data-ohw-item-dragging", "");
7191
- clearTextSelection();
7192
- }
7193
- function unlockItemDragInteraction() {
7194
- document.documentElement.removeAttribute("data-ohw-footer-press-drag");
7195
- document.documentElement.removeAttribute("data-ohw-item-dragging");
7196
- clearTextSelection();
7197
- }
7198
- var armFooterPressDrag = armItemPressDrag;
7199
- var lockFooterDuringDrag = lockItemDuringDrag;
7200
- var unlockFooterDragInteraction = unlockItemDragInteraction;
7201
-
7202
- // src/lib/nav-dnd.ts
7203
- function listReorderableNavItems() {
7204
- return listNavbarItems().filter((el) => {
7205
- const key = el.getAttribute("data-ohw-href-key");
7206
- return isNavbarHrefKey(key) && !el.closest("[data-ohw-nav-children]");
7207
- });
7208
- }
7209
- function listNavChildren(parentHrefKey) {
7210
- const items = listNavbarItems();
7211
- const parent = items.find((el) => el.getAttribute("data-ohw-href-key") === parentHrefKey);
7212
- if (!parent) return [];
7213
- const group = parent.closest("[data-ohw-nav-group]");
7214
- const childrenRoot = group?.querySelector(":scope > [data-ohw-nav-children]");
7215
- if (!childrenRoot) return [];
7216
- return Array.from(childrenRoot.querySelectorAll(":scope > [data-ohw-href-key]")).filter(
7217
- (el) => isNavbarHrefKey(el.getAttribute("data-ohw-href-key"))
7218
- );
7219
- }
7220
- function activeNavListContainer() {
7221
- const desktop = getNavbarDesktopContainer();
7222
- if (desktop && desktop.getClientRects().length > 0) {
7223
- const style = window.getComputedStyle(desktop);
7224
- if (style.display !== "none" && style.visibility !== "hidden") return desktop;
7225
- }
7226
- return getNavbarDrawerContainer();
7227
- }
7228
- function buildRootNavDropSlots() {
7229
- const items = listReorderableNavItems();
7230
- const slots = [];
7231
- const barThickness = 3;
7232
- if (items.length === 0) {
7233
- const container = activeNavListContainer();
7234
- if (!container) return slots;
7235
- const rect = container.getBoundingClientRect();
7236
- slots.push({
7237
- insertIndex: 0,
7238
- parentId: null,
7239
- left: rect.left + rect.width / 2 - barThickness / 2,
7240
- top: rect.top,
7241
- width: barThickness,
7242
- height: Math.max(rect.height, 24),
7243
- direction: "vertical"
7244
- });
7245
- return slots;
7246
- }
7247
- for (let i = 0; i <= items.length; i++) {
7248
- let left;
7249
- let top;
7250
- let height;
7251
- if (i === 0) {
7252
- const first = items[0].getBoundingClientRect();
7253
- left = first.left - barThickness / 2;
7254
- top = first.top;
7255
- height = first.height;
7256
- } else if (i === items.length) {
7257
- const last = items[items.length - 1].getBoundingClientRect();
7258
- left = last.right - barThickness / 2;
7259
- top = last.top;
7260
- height = last.height;
7261
- } else {
7262
- const prev = items[i - 1].getBoundingClientRect();
7263
- const next = items[i].getBoundingClientRect();
7264
- left = (prev.right + next.left) / 2 - barThickness / 2;
7265
- top = Math.min(prev.top, next.top);
7266
- height = Math.max(prev.bottom, next.bottom) - top;
7267
- }
7268
- slots.push({
7269
- insertIndex: i,
7270
- parentId: null,
7271
- left,
7272
- top,
7273
- width: barThickness,
7274
- height: Math.max(height, 24),
7275
- direction: "vertical"
7276
- });
7277
- }
7278
- return slots;
7279
- }
7280
- function buildChildNavDropSlots(parentHrefKey) {
7281
- const children = listNavChildren(parentHrefKey);
7282
- const slots = [];
7283
- const barThickness = 3;
7284
- if (children.length === 0) return slots;
7285
- for (let i = 0; i <= children.length; i++) {
7286
- let top;
7287
- let width;
7288
- let left;
7289
- if (i === 0) {
7290
- const first = children[0].getBoundingClientRect();
7291
- top = first.top - barThickness / 2;
7292
- left = first.left;
7293
- width = first.width;
7294
- } else if (i === children.length) {
7295
- const last = children[children.length - 1].getBoundingClientRect();
7296
- top = last.bottom - barThickness / 2;
7297
- left = last.left;
7298
- width = last.width;
7299
- } else {
7300
- const prev = children[i - 1].getBoundingClientRect();
7301
- const next = children[i].getBoundingClientRect();
7302
- top = (prev.bottom + next.top) / 2 - barThickness / 2;
7303
- left = Math.min(prev.left, next.left);
7304
- width = Math.max(prev.right, next.right) - left;
7305
- }
7306
- slots.push({
7307
- insertIndex: i,
7308
- parentId: parentHrefKey,
7309
- left,
7310
- top,
7311
- width: Math.max(width, 40),
7312
- height: barThickness,
7313
- direction: "horizontal"
7314
- });
7315
- }
7316
- return slots;
7317
- }
7318
- function buildAllNavDropSlots(draggedHrefKey) {
7319
- const slots = [...buildRootNavDropSlots()];
7320
- for (const item of listReorderableNavItems()) {
7321
- const key = item.getAttribute("data-ohw-href-key");
7322
- if (!key || key === draggedHrefKey) continue;
7323
- const group = item.closest("[data-ohw-nav-group]");
7324
- if (!group?.querySelector(":scope > [data-ohw-nav-children]")) continue;
7325
- slots.push(...buildChildNavDropSlots(key));
7326
- }
7327
- const dragged = listNavbarItems().find((el) => el.getAttribute("data-ohw-href-key") === draggedHrefKey);
7328
- const parentGroup = dragged?.closest("[data-ohw-nav-children]")?.closest("[data-ohw-nav-group]");
7329
- const parentTrigger = parentGroup?.querySelector(":scope > [data-ohw-href-key]");
7330
- const parentKey = parentTrigger?.getAttribute("data-ohw-href-key");
7331
- if (parentKey && isNavbarHrefKey(parentKey)) {
7332
- const existing = slots.some((s) => s.parentId === parentKey);
7333
- if (!existing) slots.push(...buildChildNavDropSlots(parentKey));
7334
- }
7335
- return slots;
7336
- }
7337
- function hitTestNavDropSlot(clientX, clientY, draggedHrefKey) {
7338
- const slots = buildAllNavDropSlots(draggedHrefKey);
7339
- let best = null;
7340
- for (const slot of slots) {
7341
- const cx2 = slot.left + slot.width / 2;
7342
- const cy = slot.top + slot.height / 2;
7343
- 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;
7344
- if (!best || dist < best.dist) best = { slot, dist };
7345
- }
7346
- return best?.slot ?? null;
7347
- }
7348
- function siblingRectsForNavDrag(draggedEl, activeParentId) {
7349
- if (activeParentId) {
7350
- return listNavChildren(activeParentId).filter((el) => el !== draggedEl).map((el) => el.getBoundingClientRect());
7351
- }
7352
- return listReorderableNavItems().filter((el) => el !== draggedEl).map((el) => el.getBoundingClientRect());
7353
- }
7354
-
7355
- // src/useNavItemDrag.ts
7356
- import { useCallback as useCallback4, useEffect as useEffect7, useRef as useRef6, useState as useState6 } from "react";
7357
- function useNavItemDrag({
7358
- isEditMode,
7359
- editContentRef,
7360
- selectedElRef,
7361
- activeElRef,
7362
- footerDragRef,
7363
- suppressNextClickRef,
7364
- suppressClickUntilRef,
7365
- siblingHintElRef,
7366
- postToParentRef,
7367
- deselectRef,
7368
- selectRef,
7369
- deactivateRef,
7370
- setLinkPopover,
7371
- linkPopoverOpenRef,
7372
- setIsItemDragging,
7373
- setDraggedItemRect,
7374
- setSiblingHintRect,
7375
- setSiblingHintRects,
7376
- setToolbarRect,
7377
- getNavigationItemAnchor: getNavigationItemAnchor2,
7378
- isDragHandleDisabled: isDragHandleDisabled2,
7379
- isNavbarButton: isNavbarButton3
7380
- }) {
7381
- const navDragRef = useRef6(null);
7382
- const [navDropSlots, setNavDropSlots] = useState6([]);
7383
- const [activeNavDropIndex, setActiveNavDropIndex] = useState6(null);
7384
- const navPointerDragRef = useRef6(null);
7385
- const clearNavDragVisuals = useCallback4(() => {
7386
- navDragRef.current = null;
7387
- setNavDropSlots([]);
7388
- setActiveNavDropIndex(null);
7389
- setDraggedItemRect(null);
7390
- setSiblingHintRects([]);
7391
- setIsItemDragging(false);
7392
- unlockItemDragInteraction();
7393
- }, [setDraggedItemRect, setIsItemDragging, setSiblingHintRects]);
7394
- const refreshNavDragVisuals = useCallback4(
7395
- (session, activeSlot, clientX, clientY) => {
7396
- setDraggedItemRect(session.draggedEl.getBoundingClientRect());
7397
- if (typeof clientX === "number" && typeof clientY === "number") {
7398
- session.lastClientX = clientX;
7399
- session.lastClientY = clientY;
7400
- }
7401
- session.activeSlot = activeSlot;
7402
- const parentId = activeSlot?.parentId ?? null;
7403
- setSiblingHintRects(siblingRectsForNavDrag(session.draggedEl, parentId));
7404
- const slots = buildAllNavDropSlots(session.hrefKey);
7405
- setNavDropSlots(slots);
7406
- const activeIdx = activeSlot ? slots.findIndex(
7407
- (s) => s.parentId === activeSlot.parentId && s.insertIndex === activeSlot.insertIndex
7408
- ) : -1;
7409
- setActiveNavDropIndex(activeIdx >= 0 ? activeIdx : null);
7410
- },
7411
- [setDraggedItemRect, setSiblingHintRects]
7412
- );
7413
- const refreshNavDragVisualsRef = useRef6(refreshNavDragVisuals);
7414
- refreshNavDragVisualsRef.current = refreshNavDragVisuals;
7415
- const commitNavDragRef = useRef6(() => {
7416
- });
7417
- const beginNavDragRef = useRef6(() => {
7418
- });
7419
- const beginNavDrag = useCallback4(
7420
- (session) => {
7421
- const rect = session.draggedEl.getBoundingClientRect();
7422
- session.lastClientX = session.lastClientX || rect.left + rect.width / 2;
7423
- session.lastClientY = session.lastClientY || rect.top + rect.height / 2;
7424
- session.activeSlot = session.activeSlot ?? null;
7425
- navDragRef.current = session;
7426
- setIsItemDragging(true);
7427
- lockItemDuringDrag();
7428
- siblingHintElRef.current = null;
7429
- setSiblingHintRect(null);
7430
- if (session.wasSelected && selectedElRef.current === session.draggedEl) {
7431
- setToolbarRect(rect);
7432
- }
7433
- const initialSlot = hitTestNavDropSlot(session.lastClientX, session.lastClientY, session.hrefKey);
7434
- refreshNavDragVisuals(session, initialSlot, session.lastClientX, session.lastClientY);
7435
- },
7436
- [
7437
- refreshNavDragVisuals,
7438
- selectedElRef,
7439
- setIsItemDragging,
7440
- setSiblingHintRect,
7441
- setToolbarRect,
7442
- siblingHintElRef
7443
- ]
7444
- );
7445
- beginNavDragRef.current = beginNavDrag;
7446
- const commitNavDrag = useCallback4(
7447
- (clientX, clientY) => {
7448
- const session = navDragRef.current;
7449
- if (!session) {
7450
- clearNavDragVisuals();
7451
- return;
7452
- }
7453
- const x = typeof clientX === "number" && (clientX !== 0 || clientY !== 0) ? clientX : session.lastClientX;
7454
- const y = typeof clientY === "number" && (clientX !== 0 || clientY !== 0) ? clientY : session.lastClientY;
7455
- const slot = session.activeSlot ?? hitTestNavDropSlot(x, y, session.hrefKey);
7456
- const planned = slot != null ? planNavItemMove(session.hrefKey, slot.parentId, slot.insertIndex) : null;
7457
- const wasSelected = session.wasSelected;
7458
- const hrefKey = session.hrefKey;
7459
- const applySelectionAfterDrop = () => {
7460
- if (!wasSelected) {
7461
- deselectRef.current();
7462
- return;
7463
- }
7464
- const desktop = document.querySelector("[data-ohw-nav-container]");
7465
- const drawer = document.querySelector("[data-ohw-nav-drawer]");
7466
- 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)}"]`);
7467
- if (link) {
7468
- selectRef.current(link);
7469
- return;
7470
- }
7471
- deselectRef.current();
7472
- };
7473
- if (planned) {
7474
- editContentRef.current = {
7475
- ...editContentRef.current,
7476
- [NAV_ORDER_KEY]: planned.orderJson
7477
- };
7478
- applyNavForest(planned.forest);
7479
- document.querySelectorAll(
7480
- "nav [data-ohw-href-key], [data-ohw-nav-container] [data-ohw-href-key], [data-ohw-nav-drawer] [data-ohw-href-key]"
7481
- ).forEach((el) => {
7482
- if (isNavbarHrefKey(el.getAttribute("data-ohw-href-key"))) {
7483
- disableNativeHrefDrag(el);
7484
- }
7485
- });
7486
- postToParentRef.current({
7487
- type: "ow:change",
7488
- nodes: [{ key: NAV_ORDER_KEY, text: planned.orderJson }]
7489
- });
7490
- applySelectionAfterDrop();
7491
- clearNavDragVisuals();
7492
- requestAnimationFrame(() => {
7493
- if (editContentRef.current[NAV_ORDER_KEY] === planned.orderJson) {
7494
- applyNavForest(planned.forest);
7495
- }
7496
- applySelectionAfterDrop();
7497
- requestAnimationFrame(applySelectionAfterDrop);
7498
- });
7499
- return;
7500
- }
7501
- applySelectionAfterDrop();
7502
- clearNavDragVisuals();
7503
- },
7504
- [clearNavDragVisuals, deselectRef, editContentRef, postToParentRef, selectRef]
7505
- );
7506
- commitNavDragRef.current = commitNavDrag;
7507
- const startNavLinkDrag = useCallback4(
7508
- (anchor, clientX, clientY, wasSelected) => {
7509
- if (footerDragRef.current) return false;
7510
- const hrefKey = anchor.getAttribute("data-ohw-href-key");
7511
- if (!hrefKey || !isNavbarHrefKey(hrefKey)) return false;
7512
- if (isNavbarButton3(anchor) || isDragHandleDisabled2(anchor)) return false;
7513
- beginNavDrag({
7514
- hrefKey,
7515
- wasSelected,
7516
- draggedEl: anchor,
7517
- lastClientX: clientX,
7518
- lastClientY: clientY,
7519
- activeSlot: null
7520
- });
7521
- return true;
7522
- },
7523
- [beginNavDrag, footerDragRef, isDragHandleDisabled2, isNavbarButton3]
7524
- );
7525
- const onNavDragOver = useCallback4(
7526
- (e) => {
7527
- const session = navDragRef.current;
7528
- if (!session) return false;
7529
- e.preventDefault();
7530
- if (e.dataTransfer) e.dataTransfer.dropEffect = "move";
7531
- const slot = hitTestNavDropSlot(e.clientX, e.clientY, session.hrefKey);
7532
- refreshNavDragVisualsRef.current(session, slot, e.clientX, e.clientY);
7533
- return true;
7534
- },
7535
- []
7536
- );
7537
- useEffect7(() => {
7538
- if (!isEditMode) return;
7539
- const THRESHOLD = 10;
7540
- const resolveWasSelected = (el) => {
7541
- if (selectedElRef.current === el) return true;
7542
- const activeAnchor = activeElRef.current ? getNavigationItemAnchor2(activeElRef.current) : null;
7543
- return activeAnchor === el;
7544
- };
7545
- const onPointerDown = (e) => {
7546
- if (e.button !== 0) return;
7547
- if (navDragRef.current || footerDragRef.current) return;
7548
- const target = e.target;
7549
- if (!target) return;
7550
- if (target.closest(
7551
- '[data-ohw-drag-handle-container], [data-slot="drag-handle"], [data-ohw-toolbar], [data-ohw-item-toolbar-anchor], [data-ohw-link-popover-root]'
7552
- )) {
7553
- return;
7554
- }
7555
- if (target.closest("[data-ohw-item-drag-surface]")) return;
7556
- const anchor = getNavigationItemAnchor2(target);
7557
- const hrefKey = anchor?.getAttribute("data-ohw-href-key") ?? null;
7558
- if (!anchor || !isNavbarHrefKey(hrefKey)) return;
7559
- if (isNavbarButton3(anchor) || isDragHandleDisabled2(anchor)) return;
7560
- navPointerDragRef.current = {
7561
- el: anchor,
7562
- startX: e.clientX,
7563
- startY: e.clientY,
7564
- pointerId: e.pointerId,
7565
- wasSelected: resolveWasSelected(anchor),
7566
- started: false
7567
- };
7568
- };
7569
- const onPointerMove = (e) => {
7570
- const pending = navPointerDragRef.current;
7571
- if (!pending) return;
7572
- if (pending.started) {
7573
- e.preventDefault();
7574
- clearTextSelection();
7575
- const session = navDragRef.current;
7576
- if (!session) return;
7577
- const slot = hitTestNavDropSlot(e.clientX, e.clientY, session.hrefKey);
7578
- refreshNavDragVisualsRef.current(session, slot, e.clientX, e.clientY);
7579
- return;
7580
- }
7581
- const dx = e.clientX - pending.startX;
7582
- const dy = e.clientY - pending.startY;
7583
- if (dx * dx + dy * dy < THRESHOLD * THRESHOLD) return;
7584
- e.preventDefault();
7585
- pending.started = true;
7586
- armItemPressDrag();
7587
- clearTextSelection();
7588
- try {
7589
- document.body.setPointerCapture(pending.pointerId);
7590
- } catch {
7591
- }
7592
- if (linkPopoverOpenRef.current) setLinkPopover(null);
7593
- if (activeElRef.current) deactivateRef.current();
7594
- const key = pending.el.getAttribute("data-ohw-href-key");
7595
- if (!key) return;
7596
- beginNavDragRef.current({
7597
- hrefKey: key,
7598
- wasSelected: pending.wasSelected,
7599
- draggedEl: pending.el,
7600
- lastClientX: e.clientX,
7601
- lastClientY: e.clientY,
7602
- activeSlot: null
7603
- });
7604
- };
7605
- const endPointerDrag = (e) => {
7606
- const pending = navPointerDragRef.current;
7607
- navPointerDragRef.current = null;
7608
- try {
7609
- if (document.body.hasPointerCapture(e.pointerId)) {
7610
- document.body.releasePointerCapture(e.pointerId);
7611
- }
7612
- } catch {
7613
- }
7614
- if (!pending) return;
7615
- if (!pending.started) {
7616
- unlockItemDragInteraction();
7617
- return;
7618
- }
7619
- suppressNextClickRef.current = true;
7620
- suppressClickUntilRef.current = Date.now() + 500;
7621
- commitNavDragRef.current(e.clientX, e.clientY);
7622
- };
7623
- const blockSelectStart = (e) => {
7624
- if (navDragRef.current || navPointerDragRef.current?.started || document.documentElement.hasAttribute("data-ohw-footer-press-drag")) {
7625
- e.preventDefault();
7626
- }
7627
- };
7628
- const onDragEnd = () => {
7629
- if (!navDragRef.current) return;
7630
- suppressNextClickRef.current = true;
7631
- suppressClickUntilRef.current = Date.now() + 500;
7632
- commitNavDragRef.current();
7633
- };
7634
- document.addEventListener("pointerdown", onPointerDown, true);
7635
- document.addEventListener("pointermove", onPointerMove, true);
7636
- document.addEventListener("pointerup", endPointerDrag, true);
7637
- document.addEventListener("pointercancel", endPointerDrag, true);
7638
- document.addEventListener("selectstart", blockSelectStart, true);
7639
- document.addEventListener("dragend", onDragEnd, true);
7640
- return () => {
7641
- document.removeEventListener("pointerdown", onPointerDown, true);
7642
- document.removeEventListener("pointermove", onPointerMove, true);
7643
- document.removeEventListener("pointerup", endPointerDrag, true);
7644
- document.removeEventListener("pointercancel", endPointerDrag, true);
7645
- document.removeEventListener("selectstart", blockSelectStart, true);
7646
- document.removeEventListener("dragend", onDragEnd, true);
7647
- unlockItemDragInteraction();
7648
- };
7649
- }, [
7650
- activeElRef,
7651
- deactivateRef,
7652
- footerDragRef,
7653
- getNavigationItemAnchor2,
7654
- isDragHandleDisabled2,
7655
- isEditMode,
7656
- isNavbarButton3,
7657
- linkPopoverOpenRef,
7658
- selectedElRef,
7659
- setLinkPopover,
7660
- suppressNextClickRef
7661
- ]);
7662
- const armNavPressFromChrome = useCallback4(
7663
- (selected, clientX, clientY, pointerId) => {
7664
- const hrefKey = selected.getAttribute("data-ohw-href-key");
7665
- if (!hrefKey || !isNavbarHrefKey(hrefKey)) return false;
7666
- if (isNavbarButton3(selected) || isDragHandleDisabled2(selected)) return false;
7667
- navPointerDragRef.current = {
7668
- el: selected,
7669
- startX: clientX,
7670
- startY: clientY,
7671
- pointerId,
7672
- wasSelected: true,
7673
- started: false
7674
- };
7675
- return true;
7676
- },
7677
- [isDragHandleDisabled2, isNavbarButton3]
7678
- );
7679
- return {
7680
- navDragRef,
7681
- navDropSlots,
7682
- activeNavDropIndex,
7683
- startNavLinkDrag,
7684
- commitNavDrag,
7685
- onNavDragOver,
7686
- armNavPressFromChrome,
7687
- clearNavDragVisuals,
7688
- refreshNavDragVisualsRef
7689
- };
7690
- }
7691
-
7692
- // src/ui/navbar-container-chrome.tsx
7693
- import { Plus as Plus2 } from "lucide-react";
7694
- import { jsx as jsx21 } from "react/jsx-runtime";
7695
- function NavbarContainerChrome({
7696
- rect,
7697
- onAdd
7698
- }) {
7699
- const chromeGap = 6;
7700
- return /* @__PURE__ */ jsx21(
7701
- "div",
7702
- {
7703
- "data-ohw-navbar-container-chrome": "",
7704
- "data-ohw-bridge": "",
7705
- className: "pointer-events-none fixed z-[2147483647]",
7706
- style: {
7707
- top: rect.top - chromeGap,
7708
- left: rect.left - chromeGap,
7709
- width: rect.width + chromeGap * 2,
7710
- height: rect.height + chromeGap * 2
7711
- },
7712
- children: /* @__PURE__ */ jsx21(
7713
- "button",
7714
- {
7715
- type: "button",
7716
- "data-ohw-navbar-add-button": "",
7717
- 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",
7718
- style: { top: "70%" },
7719
- "aria-label": "Add item",
7720
- onMouseDown: (e) => {
7721
- e.preventDefault();
7722
- e.stopPropagation();
7723
- },
7724
- onClick: (e) => {
7725
- e.preventDefault();
7726
- e.stopPropagation();
7727
- onAdd();
7728
- },
7729
- children: /* @__PURE__ */ jsx21(Plus2, { className: "size-4 shrink-0 text-foreground", "aria-hidden": true })
7730
- }
7731
- )
7732
- }
7733
- );
7734
- }
7735
-
7736
- // src/ui/drop-indicator.tsx
7737
- import * as React10 from "react";
7738
- import { jsx as jsx22 } from "react/jsx-runtime";
7739
- var dropIndicatorVariants = cva(
7740
- "ov-gap-line pointer-events-none shrink-0 transition-opacity duration-150",
7741
- {
7742
- variants: {
7743
- direction: {
7744
- vertical: "h-6 w-[3px]",
7745
- horizontal: "h-[3px] w-[200px]"
7746
- },
7747
- state: {
7748
- default: "opacity-0",
7749
- hover: "opacity-100",
7750
- dragIdle: "opacity-40",
7751
- dragActive: "opacity-100"
7752
- }
7753
- },
7754
- defaultVariants: {
7755
- direction: "vertical",
7756
- state: "default"
7757
- }
7758
- }
7759
- );
7760
- var DropIndicator = React10.forwardRef(
7761
- ({ className, direction, state, ...props }, ref) => {
7762
- return /* @__PURE__ */ jsx22(
7763
- "div",
7764
- {
7765
- ref,
7766
- "data-slot": "drop-indicator",
7767
- "data-direction": direction ?? "vertical",
7768
- "data-state": state ?? "default",
7769
- "aria-hidden": "true",
7770
- className: cn(dropIndicatorVariants({ direction, state }), className),
7771
- ...props
7772
- }
7773
- );
7774
- }
7775
- );
7776
- DropIndicator.displayName = "DropIndicator";
7777
-
7778
- // src/ui/badge.tsx
7779
- import { jsx as jsx23 } from "react/jsx-runtime";
7780
- var badgeVariants = cva(
7781
- "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",
7782
- {
7783
- variants: {
7784
- variant: {
7785
- default: "border-transparent bg-primary text-primary-foreground",
7786
- secondary: "border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80",
7787
- destructive: "border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80",
7788
- outline: "text-foreground"
7789
- }
7790
- },
7791
- defaultVariants: {
7792
- variant: "default"
7793
- }
7794
- }
7795
- );
7796
- function Badge({ className, variant, ...props }) {
7797
- return /* @__PURE__ */ jsx23("div", { className: cn(badgeVariants({ variant }), className), ...props });
6507
+ );
6508
+ function Badge({ className, variant, ...props }) {
6509
+ return /* @__PURE__ */ jsx22("div", { className: cn(badgeVariants({ variant }), className), ...props });
7798
6510
  }
7799
6511
 
7800
6512
  // src/OhhwellsBridge.tsx
7801
6513
  import { Link as Link2 } from "lucide-react";
7802
- import { Fragment as Fragment5, jsx as jsx24, jsxs as jsxs13 } from "react/jsx-runtime";
6514
+ import { Fragment as Fragment5, jsx as jsx23, jsxs as jsxs13 } from "react/jsx-runtime";
7803
6515
  var PRIMARY2 = "#0885FE";
7804
6516
  var IMAGE_FADE_MS = 300;
7805
6517
  function runOpacityFade(el, onDone) {
@@ -7978,7 +6690,7 @@ function mountSchedulingWidget(insertAfter, notifyOnConnect = false, scheduleId,
7978
6690
  const root = createRoot(container);
7979
6691
  flushSync(() => {
7980
6692
  root.render(
7981
- /* @__PURE__ */ jsx24(
6693
+ /* @__PURE__ */ jsx23(
7982
6694
  SchedulingWidget,
7983
6695
  {
7984
6696
  notifyOnConnect,
@@ -8040,20 +6752,7 @@ function isDragHandleDisabled(el) {
8040
6752
  return raw === "true" || raw === "";
8041
6753
  });
8042
6754
  }
8043
- function canDragNavigationItem(anchor) {
8044
- const { key, disabled } = getNavigationItemReorderState(anchor);
8045
- return Boolean(key) && !disabled;
8046
- }
8047
- function syncNavigationDragCursorAttrs() {
8048
- document.querySelectorAll("nav [data-ohw-href-key], footer [data-ohw-href-key]").forEach((el) => {
8049
- if (!isNavigationItem(el) || !canDragNavigationItem(el)) {
8050
- el.removeAttribute("data-ohw-can-drag");
8051
- return;
8052
- }
8053
- el.setAttribute("data-ohw-can-drag", "");
8054
- });
8055
- }
8056
- function collectEditableNodes(extraContent) {
6755
+ function collectEditableNodes() {
8057
6756
  const nodes = Array.from(document.querySelectorAll("[data-ohw-editable]")).map((el) => {
8058
6757
  if (el.dataset.ohwEditable === "image") {
8059
6758
  const img = el instanceof HTMLImageElement ? el : el.querySelector("img");
@@ -8081,14 +6780,6 @@ function collectEditableNodes(extraContent) {
8081
6780
  if (!key) return;
8082
6781
  nodes.push({ key, type: "link", text: getLinkHref2(el) });
8083
6782
  });
8084
- if (extraContent) {
8085
- for (const key of [NAV_ORDER_KEY, FOOTER_ORDER_KEY, NAV_COUNT_KEY]) {
8086
- const text = extraContent[key];
8087
- if (typeof text === "string" && text.length > 0) {
8088
- nodes.push({ key, type: "meta", text });
8089
- }
8090
- }
8091
- }
8092
6783
  document.querySelectorAll('[data-ohw-editable="video"]').forEach((el) => {
8093
6784
  const key = el.dataset.ohwKey ?? "";
8094
6785
  const video = getVideoEl(el);
@@ -8202,11 +6893,6 @@ function getNavigationItemAnchor(el) {
8202
6893
  function isNavigationItem(el) {
8203
6894
  return getNavigationItemAnchor(el) !== null;
8204
6895
  }
8205
- function listNavigationItems() {
8206
- return Array.from(
8207
- document.querySelectorAll("nav [data-ohw-href-key], footer [data-ohw-href-key]")
8208
- ).filter((el) => isNavigationItem(el));
8209
- }
8210
6896
  function getNavigationItemReorderState(anchor) {
8211
6897
  if (isNavbarButton2(anchor)) return { key: null, disabled: false };
8212
6898
  return getReorderHandleStateFromAnchor(anchor);
@@ -8235,20 +6921,18 @@ function getNavigationRoot(el) {
8235
6921
  if (explicit) return explicit;
8236
6922
  return el.closest("nav, footer, aside");
8237
6923
  }
8238
- function countFooterNavItems(el) {
8239
- return Array.from(el.querySelectorAll("[data-ohw-href-key]")).filter(isNavigationItem).length;
8240
- }
8241
6924
  function findFooterItemGroup(item) {
8242
- const explicit = item.closest("[data-ohw-footer-col], [data-ohw-footer-column]");
8243
- if (explicit) return explicit;
8244
6925
  const footer = item.closest("footer");
8245
6926
  if (!footer) return null;
8246
6927
  let node = item.parentElement;
8247
6928
  while (node && node !== footer) {
8248
- if (countFooterNavItems(node) >= 2) return node;
6929
+ const hasDirectNavItems = Array.from(
6930
+ node.querySelectorAll(":scope > [data-ohw-href-key]")
6931
+ ).some(isNavigationItem);
6932
+ if (hasDirectNavItems) return node;
8249
6933
  node = node.parentElement;
8250
6934
  }
8251
- return footer;
6935
+ return null;
8252
6936
  }
8253
6937
  function isNavigationRoot(el) {
8254
6938
  return el.hasAttribute("data-ohw-nav-root") || el.matches("nav, footer, aside");
@@ -8256,49 +6940,16 @@ function isNavigationRoot(el) {
8256
6940
  function isInferredFooterGroup(el) {
8257
6941
  const footer = el.closest("footer");
8258
6942
  if (!footer || el === footer) return false;
8259
- if (el.hasAttribute("data-ohw-footer-col") || el.hasAttribute("data-ohw-footer-column")) return true;
8260
- return countFooterNavItems(el) >= 2;
6943
+ return Array.from(el.querySelectorAll(":scope > [data-ohw-href-key]")).some(isNavigationItem);
8261
6944
  }
8262
6945
  function isNavigationContainer(el) {
8263
- return el.hasAttribute("data-ohw-nav-container") || el.hasAttribute("data-ohw-footer-col") || el.hasAttribute("data-ohw-footer-column") || isNavigationRoot(el) || isInferredFooterGroup(el);
8264
- }
8265
- function isNavbarLinksContainer(el) {
8266
- return el.hasAttribute("data-ohw-nav-container");
8267
- }
8268
- function getFooterColumn(el) {
8269
- return el.closest("[data-ohw-footer-col], [data-ohw-footer-column]");
8270
- }
8271
- function resolveFooterColumnSelectionTarget(target, clientX, clientY) {
8272
- if (getNavigationItemAnchor(target)) return null;
8273
- const column = getFooterColumn(target);
8274
- if (!column) return null;
8275
- if (isPointOverNavItem(column, clientX, clientY)) return null;
8276
- return column;
6946
+ return el.hasAttribute("data-ohw-nav-container") || isNavigationRoot(el) || isInferredFooterGroup(el);
8277
6947
  }
8278
6948
  function isPointOverNavigation(x, y) {
8279
- const roots = /* @__PURE__ */ new Set();
8280
6949
  const navRoot = document.querySelector("[data-ohw-nav-root]") ?? document.querySelector("nav");
8281
- if (navRoot) roots.add(navRoot);
8282
- const footer = document.querySelector("footer");
8283
- if (footer) roots.add(footer);
8284
- for (const root of roots) {
8285
- const r2 = root.getBoundingClientRect();
8286
- if (x >= r2.left && x <= r2.right && y >= r2.top && y <= r2.bottom) return true;
8287
- }
8288
- return false;
8289
- }
8290
- function findHoveredNavOrFooterContainer(x, y) {
8291
- const candidates = [
8292
- ...document.querySelectorAll("[data-ohw-nav-container]"),
8293
- ...listFooterColumns()
8294
- ];
8295
- for (const container of candidates) {
8296
- const r2 = container.getBoundingClientRect();
8297
- if (x < r2.left || x > r2.right || y < r2.top || y > r2.bottom) continue;
8298
- if (isPointOverNavItem(container, x, y)) continue;
8299
- return container;
8300
- }
8301
- return null;
6950
+ if (!navRoot) return false;
6951
+ const r2 = navRoot.getBoundingClientRect();
6952
+ return x >= r2.left && x <= r2.right && y >= r2.top && y <= r2.bottom;
8302
6953
  }
8303
6954
  function isPointOverNavItem(container, x, y) {
8304
6955
  return Array.from(container.querySelectorAll("[data-ohw-href-key]")).some((el) => {
@@ -8331,63 +6982,36 @@ function getNavigationSelectionParent(el) {
8331
6982
  if (isNavigationItem(el)) {
8332
6983
  const explicit = el.closest("[data-ohw-nav-container]");
8333
6984
  if (explicit) return explicit;
8334
- const footerColumn = getFooterColumn(el);
8335
- if (footerColumn) return footerColumn;
8336
6985
  const footerGroup = findFooterItemGroup(el);
8337
6986
  if (footerGroup) return footerGroup;
8338
6987
  return getNavigationRoot(el);
8339
6988
  }
8340
- if (el.hasAttribute("data-ohw-nav-container") || el.hasAttribute("data-ohw-footer-col") || el.hasAttribute("data-ohw-footer-column") || isInferredFooterGroup(el)) {
6989
+ if (el.hasAttribute("data-ohw-nav-container") || isInferredFooterGroup(el)) {
8341
6990
  return getNavigationRoot(el);
8342
6991
  }
8343
6992
  return null;
8344
6993
  }
8345
- function collectNavigationItemSiblingHintRects(selected) {
8346
- if (!isNavigationItem(selected)) return [];
8347
- const footerColumn = getFooterColumn(selected);
8348
- if (footerColumn) {
8349
- return listFooterLinksInColumn(footerColumn).filter((link) => link !== selected).map((link) => link.getBoundingClientRect());
8350
- }
8351
- const navContainer = selected.closest("[data-ohw-nav-container], [data-ohw-nav-drawer]");
8352
- if (navContainer) {
8353
- return Array.from(navContainer.querySelectorAll("[data-ohw-href-key]")).filter((el) => isNavigationItem(el) && el !== selected).map((el) => el.getBoundingClientRect());
8354
- }
8355
- return listNavigationItems().filter((el) => el !== selected).map((el) => el.getBoundingClientRect());
8356
- }
8357
6994
  function placeCaretAtPoint(el, x, y) {
8358
6995
  const selection = window.getSelection();
8359
6996
  if (!selection) return;
8360
- const overlays = Array.from(
8361
- document.querySelectorAll(
8362
- "[data-ohw-item-drag-surface], [data-ohw-item-interaction], [data-ohw-drag-handle-container], [data-ohw-item-toolbar-anchor]"
8363
- )
8364
- );
8365
- const prevPointerEvents = overlays.map((node) => node.style.pointerEvents);
8366
- for (const node of overlays) node.style.pointerEvents = "none";
8367
- try {
8368
- if (typeof document.caretRangeFromPoint === "function") {
8369
- const range = document.caretRangeFromPoint(x, y);
8370
- if (range && el.contains(range.startContainer)) {
8371
- selection.removeAllRanges();
8372
- selection.addRange(range);
8373
- return;
8374
- }
6997
+ if (typeof document.caretRangeFromPoint === "function") {
6998
+ const range = document.caretRangeFromPoint(x, y);
6999
+ if (range && el.contains(range.startContainer)) {
7000
+ selection.removeAllRanges();
7001
+ selection.addRange(range);
7002
+ return;
8375
7003
  }
8376
- const caretPositionFromPoint = document.caretPositionFromPoint;
8377
- if (typeof caretPositionFromPoint === "function") {
8378
- const position = caretPositionFromPoint(x, y);
8379
- if (position && el.contains(position.offsetNode)) {
8380
- const range = document.createRange();
8381
- range.setStart(position.offsetNode, position.offset);
8382
- range.collapse(true);
8383
- selection.removeAllRanges();
8384
- selection.addRange(range);
8385
- }
7004
+ }
7005
+ const caretPositionFromPoint = document.caretPositionFromPoint;
7006
+ if (typeof caretPositionFromPoint === "function") {
7007
+ const position = caretPositionFromPoint(x, y);
7008
+ if (position && el.contains(position.offsetNode)) {
7009
+ const range = document.createRange();
7010
+ range.setStart(position.offsetNode, position.offset);
7011
+ range.collapse(true);
7012
+ selection.removeAllRanges();
7013
+ selection.addRange(range);
8386
7014
  }
8387
- } finally {
8388
- overlays.forEach((node, i) => {
8389
- node.style.pointerEvents = prevPointerEvents[i] ?? "";
8390
- });
8391
7015
  }
8392
7016
  }
8393
7017
  function selectAllTextInEditable(el) {
@@ -8529,9 +7153,6 @@ var ICONS = {
8529
7153
  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"/>',
8530
7154
  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"/>'
8531
7155
  };
8532
- var SELECTION_CHROME_GAP2 = 4;
8533
- var TOOLBAR_STROKE_GAP2 = 4;
8534
- var TOOLBAR_OFFSET_FROM_ELEMENT = SELECTION_CHROME_GAP2 + TOOLBAR_STROKE_GAP2;
8535
7156
  var TOOLBAR_GROUPS = [
8536
7157
  [
8537
7158
  { cmd: "bold", title: "Bold" },
@@ -8553,10 +7174,9 @@ function EditGlowChrome({
8553
7174
  rect,
8554
7175
  elRef,
8555
7176
  reorderHrefKey,
8556
- dragDisabled = false,
8557
- hideHandle = false
7177
+ dragDisabled = false
8558
7178
  }) {
8559
- const GAP = SELECTION_CHROME_GAP2;
7179
+ const GAP = 6;
8560
7180
  return /* @__PURE__ */ jsxs13(
8561
7181
  "div",
8562
7182
  {
@@ -8572,7 +7192,7 @@ function EditGlowChrome({
8572
7192
  zIndex: 2147483646
8573
7193
  },
8574
7194
  children: [
8575
- /* @__PURE__ */ jsx24(
7195
+ /* @__PURE__ */ jsx23(
8576
7196
  "div",
8577
7197
  {
8578
7198
  style: {
@@ -8580,12 +7200,12 @@ function EditGlowChrome({
8580
7200
  inset: 0,
8581
7201
  border: "2px solid var(--ohw-primary, #0885FE)",
8582
7202
  borderRadius: 8,
8583
- boxShadow: "0 0 0 4px color-mix(in srgb, var(--ohw-primary, #0885FE) 12%, transparent)",
7203
+ boxShadow: "0 0 0 4px rgba(8, 133, 254, 0.12)",
8584
7204
  pointerEvents: "none"
8585
7205
  }
8586
7206
  }
8587
7207
  ),
8588
- reorderHrefKey && !hideHandle && /* @__PURE__ */ jsx24(
7208
+ reorderHrefKey && /* @__PURE__ */ jsx23(
8589
7209
  "div",
8590
7210
  {
8591
7211
  "data-ohw-drag-handle-container": "",
@@ -8597,7 +7217,7 @@ function EditGlowChrome({
8597
7217
  transform: "translate(calc(-100% - 7px), -50%)",
8598
7218
  pointerEvents: dragDisabled ? "none" : "auto"
8599
7219
  },
8600
- children: /* @__PURE__ */ jsx24(
7220
+ children: /* @__PURE__ */ jsx23(
8601
7221
  DragHandle,
8602
7222
  {
8603
7223
  "aria-label": `Reorder ${reorderHrefKey}`,
@@ -8628,7 +7248,6 @@ function applyVisibleViewport(el, parentScroll) {
8628
7248
  el.style.top = `${top}px`;
8629
7249
  el.style.height = `${height}px`;
8630
7250
  }
8631
- var TOOLBAR_EDGE_MARGIN2 = 4;
8632
7251
  function clampToolbarToClip(top, transform, rect, clip, approxH, gap) {
8633
7252
  const isAbove = transform.includes("translateY(-100%)");
8634
7253
  let visualTop = isAbove ? top - approxH : top;
@@ -8652,16 +7271,10 @@ function clampToolbarToClip(top, transform, rect, clip, approxH, gap) {
8652
7271
  const pinnedTop = Math.min(maxBottom - approxH, Math.max(minTop, clip.top + gap));
8653
7272
  return { top: pinnedTop + approxH, transform: "translateX(-50%) translateY(-100%)" };
8654
7273
  }
8655
- function clampToolbarLeft(centerX, toolbarWidth) {
8656
- const half = toolbarWidth / 2;
8657
- return Math.max(
8658
- TOOLBAR_EDGE_MARGIN2 + half,
8659
- Math.min(centerX, window.innerWidth - TOOLBAR_EDGE_MARGIN2 - half)
8660
- );
8661
- }
8662
- function calcToolbarPos(rect, parentScroll, toolbarW = 306) {
8663
- const GAP = TOOLBAR_OFFSET_FROM_ELEMENT;
7274
+ function calcToolbarPos(rect, parentScroll, approxW = 306) {
7275
+ const GAP = 8;
8664
7276
  const APPROX_H = 32;
7277
+ const APPROX_W = approxW;
8665
7278
  const clip = getIframeVisibleClip(parentScroll);
8666
7279
  const clipTop = clip?.top ?? 0;
8667
7280
  const canvasTopInIframe = parentScroll != null ? parentScroll.headerH - parentScroll.iframeOffsetTop : 0;
@@ -8691,7 +7304,7 @@ function calcToolbarPos(rect, parentScroll, toolbarW = 306) {
8691
7304
  transform = clamped.transform;
8692
7305
  }
8693
7306
  const rawLeft = rect.left + rect.width / 2;
8694
- const left = clampToolbarLeft(rawLeft, toolbarW);
7307
+ const left = Math.max(GAP + APPROX_W / 2, Math.min(rawLeft, window.innerWidth - GAP - APPROX_W / 2));
8695
7308
  return { top, left, transform };
8696
7309
  }
8697
7310
  function resolveItemInteractionState(rect, parentScroll) {
@@ -8707,37 +7320,11 @@ function FloatingToolbar({
8707
7320
  showEditLink,
8708
7321
  onEditLink
8709
7322
  }) {
8710
- const localRef = React11.useRef(null);
8711
- const [measuredW, setMeasuredW] = React11.useState(330);
8712
- const setRefs = React11.useCallback(
8713
- (node) => {
8714
- localRef.current = node;
8715
- if (typeof elRef === "function") elRef(node);
8716
- else if (elRef) elRef.current = node;
8717
- if (node) {
8718
- const w = node.offsetWidth;
8719
- if (w > 0) setMeasuredW(w);
8720
- }
8721
- },
8722
- [elRef]
8723
- );
8724
- React11.useLayoutEffect(() => {
8725
- const node = localRef.current;
8726
- if (!node) return;
8727
- const update = () => {
8728
- const w = node.offsetWidth;
8729
- if (w > 0) setMeasuredW(w);
8730
- };
8731
- update();
8732
- const ro = new ResizeObserver(update);
8733
- ro.observe(node);
8734
- return () => ro.disconnect();
8735
- }, [showEditLink, activeCommands]);
8736
- const { top, left, transform } = calcToolbarPos(rect, parentScroll, measuredW);
8737
- return /* @__PURE__ */ jsx24(
7323
+ const { top, left, transform } = calcToolbarPos(rect, parentScroll, 330);
7324
+ return /* @__PURE__ */ jsx23(
8738
7325
  "div",
8739
7326
  {
8740
- ref: setRefs,
7327
+ ref: elRef,
8741
7328
  style: {
8742
7329
  position: "fixed",
8743
7330
  top,
@@ -8747,11 +7334,11 @@ function FloatingToolbar({
8747
7334
  pointerEvents: "auto"
8748
7335
  },
8749
7336
  children: /* @__PURE__ */ jsxs13(CustomToolbar, { children: [
8750
- TOOLBAR_GROUPS.map((btns, gi) => /* @__PURE__ */ jsxs13(React11.Fragment, { children: [
8751
- gi > 0 && /* @__PURE__ */ jsx24(CustomToolbarDivider, {}),
7337
+ TOOLBAR_GROUPS.map((btns, gi) => /* @__PURE__ */ jsxs13(React9.Fragment, { children: [
7338
+ gi > 0 && /* @__PURE__ */ jsx23(CustomToolbarDivider, {}),
8752
7339
  btns.map((btn) => {
8753
7340
  const isActive = activeCommands.has(btn.cmd);
8754
- return /* @__PURE__ */ jsx24(
7341
+ return /* @__PURE__ */ jsx23(
8755
7342
  CustomToolbarButton,
8756
7343
  {
8757
7344
  title: btn.title,
@@ -8760,7 +7347,7 @@ function FloatingToolbar({
8760
7347
  e.preventDefault();
8761
7348
  onCommand(btn.cmd);
8762
7349
  },
8763
- children: /* @__PURE__ */ jsx24(
7350
+ children: /* @__PURE__ */ jsx23(
8764
7351
  "svg",
8765
7352
  {
8766
7353
  width: "16",
@@ -8781,7 +7368,7 @@ function FloatingToolbar({
8781
7368
  );
8782
7369
  })
8783
7370
  ] }, gi)),
8784
- showEditLink ? /* @__PURE__ */ jsx24(
7371
+ showEditLink ? /* @__PURE__ */ jsx23(
8785
7372
  CustomToolbarButton,
8786
7373
  {
8787
7374
  type: "button",
@@ -8795,7 +7382,7 @@ function FloatingToolbar({
8795
7382
  e.preventDefault();
8796
7383
  e.stopPropagation();
8797
7384
  },
8798
- children: /* @__PURE__ */ jsx24(Link2, { className: "size-4 shrink-0", "aria-hidden": true })
7385
+ children: /* @__PURE__ */ jsx23(Link2, { className: "size-4 shrink-0", "aria-hidden": true })
8799
7386
  }
8800
7387
  ) : null
8801
7388
  ] })
@@ -8812,7 +7399,7 @@ function StateToggle({
8812
7399
  states,
8813
7400
  onStateChange
8814
7401
  }) {
8815
- return /* @__PURE__ */ jsx24(
7402
+ return /* @__PURE__ */ jsx23(
8816
7403
  ToggleGroup,
8817
7404
  {
8818
7405
  "data-ohw-state-toggle": "",
@@ -8826,7 +7413,7 @@ function StateToggle({
8826
7413
  left: rect.right - 8,
8827
7414
  transform: "translateX(-100%)"
8828
7415
  },
8829
- children: states.map((state) => /* @__PURE__ */ jsx24(ToggleGroupItem, { value: state, size: "sm", children: state }, state))
7416
+ children: states.map((state) => /* @__PURE__ */ jsx23(ToggleGroupItem, { value: state, size: "sm", children: state }, state))
8830
7417
  }
8831
7418
  );
8832
7419
  }
@@ -8853,8 +7440,8 @@ function OhhwellsBridge() {
8853
7440
  const router = useRouter2();
8854
7441
  const searchParams = useSearchParams();
8855
7442
  const isEditMode = isEditSessionActive();
8856
- const [bridgeRoot, setBridgeRoot] = useState7(null);
8857
- useEffect8(() => {
7443
+ const [bridgeRoot, setBridgeRoot] = useState6(null);
7444
+ useEffect7(() => {
8858
7445
  const figtreeFontId = "ohw-figtree-font";
8859
7446
  if (!document.getElementById(figtreeFontId)) {
8860
7447
  const preconnect1 = Object.assign(document.createElement("link"), { rel: "preconnect", href: "https://fonts.googleapis.com" });
@@ -8882,137 +7469,91 @@ function OhhwellsBridge() {
8882
7469
  const subdomainFromQuery = searchParams.get("subdomain");
8883
7470
  const subdomain = resolveSubdomain(subdomainFromQuery);
8884
7471
  useLinkHrefGuardian(pathname, subdomain, isEditMode);
8885
- const postToParent2 = useCallback5((data) => {
7472
+ const postToParent2 = useCallback3((data) => {
8886
7473
  if (typeof window !== "undefined" && window.parent !== window) {
8887
7474
  window.parent.postMessage(data, "*");
8888
7475
  }
8889
7476
  }, []);
8890
- const [fetchState, setFetchState] = useState7("idle");
8891
- const autoSaveTimers = useRef7(/* @__PURE__ */ new Map());
8892
- const activeElRef = useRef7(null);
8893
- const pointerHeldRef = useRef7(false);
8894
- const selectedElRef = useRef7(null);
8895
- const selectedHrefKeyRef = useRef7(null);
8896
- const selectedFooterColAttrRef = useRef7(null);
8897
- const originalContentRef = useRef7(null);
8898
- const activeStateElRef = useRef7(null);
8899
- const parentScrollRef = useRef7(null);
8900
- const visibleViewportRef = useRef7(null);
8901
- const [dialogPortalContainer, setDialogPortalContainer] = useState7(null);
8902
- const attachVisibleViewport = useCallback5((node) => {
7477
+ const [fetchState, setFetchState] = useState6("idle");
7478
+ const autoSaveTimers = useRef5(/* @__PURE__ */ new Map());
7479
+ const activeElRef = useRef5(null);
7480
+ const selectedElRef = useRef5(null);
7481
+ const originalContentRef = useRef5(null);
7482
+ const activeStateElRef = useRef5(null);
7483
+ const parentScrollRef = useRef5(null);
7484
+ const visibleViewportRef = useRef5(null);
7485
+ const [dialogPortalContainer, setDialogPortalContainer] = useState6(null);
7486
+ const attachVisibleViewport = useCallback3((node) => {
8903
7487
  visibleViewportRef.current = node;
8904
7488
  setDialogPortalContainer(node);
8905
7489
  if (node) applyVisibleViewport(node, parentScrollRef.current);
8906
7490
  }, []);
8907
- const toolbarElRef = useRef7(null);
8908
- const glowElRef = useRef7(null);
8909
- const hoveredImageRef = useRef7(null);
8910
- const hoveredImageHasTextOverlapRef = useRef7(false);
8911
- const dragOverElRef = useRef7(null);
8912
- const [mediaHover, setMediaHover] = useState7(null);
8913
- const [uploadingRects, setUploadingRects] = useState7({});
8914
- const hoveredGapRef = useRef7(null);
8915
- const imageUnhoverTimerRef = useRef7(null);
8916
- const imageShowTimerRef = useRef7(null);
8917
- const editStylesRef = useRef7(null);
8918
- const activateRef = useRef7(() => {
7491
+ const toolbarElRef = useRef5(null);
7492
+ const glowElRef = useRef5(null);
7493
+ const hoveredImageRef = useRef5(null);
7494
+ const hoveredImageHasTextOverlapRef = useRef5(false);
7495
+ const dragOverElRef = useRef5(null);
7496
+ const [mediaHover, setMediaHover] = useState6(null);
7497
+ const [uploadingRects, setUploadingRects] = useState6({});
7498
+ const hoveredGapRef = useRef5(null);
7499
+ const imageUnhoverTimerRef = useRef5(null);
7500
+ const imageShowTimerRef = useRef5(null);
7501
+ const editStylesRef = useRef5(null);
7502
+ const activateRef = useRef5(() => {
8919
7503
  });
8920
- const deactivateRef = useRef7(() => {
7504
+ const deactivateRef = useRef5(() => {
8921
7505
  });
8922
- const selectRef = useRef7(() => {
7506
+ const selectRef = useRef5(() => {
8923
7507
  });
8924
- const selectFrameRef = useRef7(() => {
7508
+ const selectFrameRef = useRef5(() => {
8925
7509
  });
8926
- const deselectRef = useRef7(() => {
7510
+ const deselectRef = useRef5(() => {
8927
7511
  });
8928
- const reselectNavigationItemRef = useRef7(() => {
7512
+ const reselectNavigationItemRef = useRef5(() => {
8929
7513
  });
8930
- const commitNavigationTextEditRef = useRef7(() => {
7514
+ const commitNavigationTextEditRef = useRef5(() => {
8931
7515
  });
8932
- const refreshActiveCommandsRef = useRef7(() => {
7516
+ const refreshActiveCommandsRef = useRef5(() => {
8933
7517
  });
8934
- const postToParentRef = useRef7(postToParent2);
7518
+ const postToParentRef = useRef5(postToParent2);
8935
7519
  postToParentRef.current = postToParent2;
8936
- const sectionsLoadedRef = useRef7(false);
8937
- const pendingScheduleConfigRequests = useRef7([]);
8938
- const [toolbarRect, setToolbarRect] = useState7(null);
8939
- const [toolbarVariant, setToolbarVariant] = useState7("none");
8940
- const toolbarVariantRef = useRef7("none");
7520
+ const sectionsLoadedRef = useRef5(false);
7521
+ const pendingScheduleConfigRequests = useRef5([]);
7522
+ const [toolbarRect, setToolbarRect] = useState6(null);
7523
+ const [toolbarVariant, setToolbarVariant] = useState6("none");
7524
+ const toolbarVariantRef = useRef5("none");
8941
7525
  toolbarVariantRef.current = toolbarVariant;
8942
- const [selectedIsCta, setSelectedIsCta] = useState7(false);
8943
- const [reorderHrefKey, setReorderHrefKey] = useState7(null);
8944
- const [reorderDragDisabled, setReorderDragDisabled] = useState7(false);
8945
- const [toggleState, setToggleState] = useState7(null);
8946
- const [maxBadge, setMaxBadge] = useState7(null);
8947
- const [activeCommands, setActiveCommands] = useState7(/* @__PURE__ */ new Set());
8948
- const [sectionGap, setSectionGap] = useState7(null);
8949
- const [toolbarShowEditLink, setToolbarShowEditLink] = useState7(false);
8950
- const hoveredNavContainerRef = useRef7(null);
8951
- const [hoveredNavContainerRect, setHoveredNavContainerRect] = useState7(null);
8952
- const hoveredItemElRef = useRef7(null);
8953
- const [hoveredItemRect, setHoveredItemRect] = useState7(null);
8954
- const siblingHintElRef = useRef7(null);
8955
- const [siblingHintRect, setSiblingHintRect] = useState7(null);
8956
- const [siblingHintRects, setSiblingHintRects] = useState7([]);
8957
- const [isItemDragging, setIsItemDragging] = useState7(false);
8958
- const [isFooterFrameSelection, setIsFooterFrameSelection] = useState7(false);
8959
- const footerDragRef = useRef7(null);
8960
- const [footerDropSlots, setFooterDropSlots] = useState7([]);
8961
- const [activeFooterDropIndex, setActiveFooterDropIndex] = useState7(null);
8962
- const [draggedItemRect, setDraggedItemRect] = useState7(null);
8963
- const footerPointerDragRef = useRef7(null);
8964
- const suppressNextClickRef = useRef7(false);
8965
- const suppressClickUntilRef = useRef7(0);
8966
- const [linkPopover, setLinkPopover] = useState7(null);
8967
- const linkPopoverSessionRef = useRef7(null);
8968
- const addNavAfterAnchorRef = useRef7(null);
8969
- const editContentRef = useRef7({});
8970
- const [sitePages, setSitePages] = useState7([]);
8971
- const [sectionsByPath, setSectionsByPath] = useState7({});
8972
- const sectionsPrefetchGenRef = useRef7(0);
8973
- const setLinkPopoverRef = useRef7(setLinkPopover);
8974
- const linkPopoverPanelRef = useRef7(null);
8975
- const linkPopoverOpenRef = useRef7(false);
8976
- const linkPopoverGraceUntilRef = useRef7(0);
7526
+ const [reorderHrefKey, setReorderHrefKey] = useState6(null);
7527
+ const [reorderDragDisabled, setReorderDragDisabled] = useState6(false);
7528
+ const [toggleState, setToggleState] = useState6(null);
7529
+ const [maxBadge, setMaxBadge] = useState6(null);
7530
+ const [activeCommands, setActiveCommands] = useState6(/* @__PURE__ */ new Set());
7531
+ const [sectionGap, setSectionGap] = useState6(null);
7532
+ const [toolbarShowEditLink, setToolbarShowEditLink] = useState6(false);
7533
+ const hoveredNavContainerRef = useRef5(null);
7534
+ const [hoveredNavContainerRect, setHoveredNavContainerRect] = useState6(null);
7535
+ const hoveredItemElRef = useRef5(null);
7536
+ const [hoveredItemRect, setHoveredItemRect] = useState6(null);
7537
+ const siblingHintElRef = useRef5(null);
7538
+ const [siblingHintRect, setSiblingHintRect] = useState6(null);
7539
+ const [isItemDragging, setIsItemDragging] = useState6(false);
7540
+ const [linkPopover, setLinkPopover] = useState6(null);
7541
+ const linkPopoverSessionRef = useRef5(null);
7542
+ const addNavAfterAnchorRef = useRef5(null);
7543
+ const editContentRef = useRef5({});
7544
+ const [sitePages, setSitePages] = useState6([]);
7545
+ const [sectionsByPath, setSectionsByPath] = useState6({});
7546
+ const sectionsPrefetchGenRef = useRef5(0);
7547
+ const setLinkPopoverRef = useRef5(setLinkPopover);
7548
+ const linkPopoverPanelRef = useRef5(null);
7549
+ const linkPopoverOpenRef = useRef5(false);
7550
+ const linkPopoverGraceUntilRef = useRef5(0);
8977
7551
  setLinkPopoverRef.current = setLinkPopover;
8978
7552
  linkPopoverSessionRef.current = linkPopover;
8979
- const {
8980
- navDragRef,
8981
- navDropSlots,
8982
- activeNavDropIndex,
8983
- startNavLinkDrag,
8984
- commitNavDrag,
8985
- onNavDragOver,
8986
- armNavPressFromChrome,
8987
- refreshNavDragVisualsRef
8988
- } = useNavItemDrag({
8989
- isEditMode,
8990
- editContentRef,
8991
- selectedElRef,
8992
- activeElRef,
8993
- footerDragRef,
8994
- suppressNextClickRef,
8995
- suppressClickUntilRef,
8996
- siblingHintElRef,
8997
- postToParentRef,
8998
- deselectRef,
8999
- selectRef,
9000
- deactivateRef,
9001
- setLinkPopover: () => setLinkPopover(null),
9002
- linkPopoverOpenRef,
9003
- setIsItemDragging,
9004
- setDraggedItemRect,
9005
- setSiblingHintRect,
9006
- setSiblingHintRects,
9007
- setToolbarRect,
9008
- getNavigationItemAnchor,
9009
- isDragHandleDisabled,
9010
- isNavbarButton: isNavbarButton2
9011
- });
9012
7553
  const bumpLinkPopoverGrace = () => {
9013
7554
  linkPopoverGraceUntilRef.current = Date.now() + 350;
9014
7555
  };
9015
- const runSectionsPrefetch = useCallback5((pages) => {
7556
+ const runSectionsPrefetch = useCallback3((pages) => {
9016
7557
  if (!isEditMode || shouldUseDevFixtures() || pages.length === 0) return;
9017
7558
  const gen = ++sectionsPrefetchGenRef.current;
9018
7559
  const paths = pages.map((p) => p.path);
@@ -9031,24 +7572,10 @@ function OhhwellsBridge() {
9031
7572
  );
9032
7573
  });
9033
7574
  }, [isEditMode, pathname]);
9034
- const runSectionsPrefetchRef = useRef7(runSectionsPrefetch);
7575
+ const runSectionsPrefetchRef = useRef5(runSectionsPrefetch);
9035
7576
  runSectionsPrefetchRef.current = runSectionsPrefetch;
9036
- useEffect8(() => {
9037
- if (!linkPopover) {
9038
- document.documentElement.removeAttribute("data-ohw-link-popover-open");
9039
- return;
9040
- }
9041
- document.documentElement.setAttribute("data-ohw-link-popover-open", "");
9042
- hoveredItemElRef.current = null;
9043
- setHoveredItemRect(null);
9044
- hoveredNavContainerRef.current = null;
9045
- setHoveredNavContainerRect(null);
9046
- siblingHintElRef.current = null;
9047
- setSiblingHintRect(null);
9048
- setSiblingHintRects([]);
9049
- if (selectedElRef.current || toolbarVariantRef.current !== "none") {
9050
- deselectRef.current();
9051
- }
7577
+ useEffect7(() => {
7578
+ if (!linkPopover) return;
9052
7579
  if (hoveredImageRef.current) {
9053
7580
  hoveredImageRef.current = null;
9054
7581
  hoveredImageHasTextOverlapRef.current = false;
@@ -9082,7 +7609,7 @@ function OhhwellsBridge() {
9082
7609
  document.removeEventListener("touchmove", preventBackgroundScroll, scrollOpts);
9083
7610
  };
9084
7611
  }, [linkPopover, postToParent2]);
9085
- useEffect8(() => {
7612
+ useEffect7(() => {
9086
7613
  if (!isEditMode) return;
9087
7614
  const useFixtures = shouldUseDevFixtures();
9088
7615
  if (useFixtures) {
@@ -9106,14 +7633,14 @@ function OhhwellsBridge() {
9106
7633
  if (!useFixtures) postToParent2({ type: "ow:request-site-pages" });
9107
7634
  return () => window.removeEventListener("message", onSitePages);
9108
7635
  }, [isEditMode, postToParent2]);
9109
- useEffect8(() => {
7636
+ useEffect7(() => {
9110
7637
  if (!isEditMode || shouldUseDevFixtures()) return;
9111
7638
  void loadAllSectionsManifest().then((manifest) => {
9112
7639
  if (Object.keys(manifest).length === 0) return;
9113
7640
  setSectionsByPath((prev) => ({ ...manifest, ...prev }));
9114
7641
  });
9115
7642
  }, [isEditMode]);
9116
- useEffect8(() => {
7643
+ useEffect7(() => {
9117
7644
  const update = () => {
9118
7645
  const el = activeElRef.current ?? selectedElRef.current;
9119
7646
  if (el) setToolbarRect(getEditMeasureEl(el).getBoundingClientRect());
@@ -9137,10 +7664,10 @@ function OhhwellsBridge() {
9137
7664
  vvp.removeEventListener("resize", update);
9138
7665
  };
9139
7666
  }, []);
9140
- const refreshStateRules = useCallback5(() => {
7667
+ const refreshStateRules = useCallback3(() => {
9141
7668
  editStylesRef.current?.forceHover && (editStylesRef.current.forceHover.textContent = collectStateRules());
9142
7669
  }, []);
9143
- const processConfigRequest = useCallback5((insertAfterVal) => {
7670
+ const processConfigRequest = useCallback3((insertAfterVal) => {
9144
7671
  const tracker = getSectionsTracker();
9145
7672
  let entries = [];
9146
7673
  try {
@@ -9163,7 +7690,7 @@ function OhhwellsBridge() {
9163
7690
  }
9164
7691
  window.postMessage({ type: "ow:schedule-config", insertAfter: insertAfterVal, scheduleId: null }, "*");
9165
7692
  }, [isEditMode]);
9166
- const deactivate = useCallback5(() => {
7693
+ const deactivate = useCallback3(() => {
9167
7694
  const el = activeElRef.current;
9168
7695
  if (!el) return;
9169
7696
  const key = el.dataset.ohwKey;
@@ -9182,7 +7709,6 @@ function OhhwellsBridge() {
9182
7709
  }
9183
7710
  }
9184
7711
  el.removeAttribute("contenteditable");
9185
- el.removeAttribute("data-ohw-editing");
9186
7712
  activeElRef.current = null;
9187
7713
  setReorderHrefKey(null);
9188
7714
  setReorderDragDisabled(false);
@@ -9195,23 +7721,12 @@ function OhhwellsBridge() {
9195
7721
  setToolbarShowEditLink(false);
9196
7722
  postToParent2({ type: "ow:exit-edit" });
9197
7723
  }, [postToParent2]);
9198
- const clearSelectedAttr = useCallback5(() => {
9199
- document.querySelectorAll("[data-ohw-selected]").forEach((el) => {
9200
- el.removeAttribute("data-ohw-selected");
9201
- });
9202
- }, []);
9203
- const deselect = useCallback5(() => {
9204
- clearSelectedAttr();
7724
+ const deselect = useCallback3(() => {
9205
7725
  selectedElRef.current = null;
9206
- selectedHrefKeyRef.current = null;
9207
- selectedFooterColAttrRef.current = null;
9208
- setSelectedIsCta(false);
9209
7726
  setReorderHrefKey(null);
9210
7727
  setReorderDragDisabled(false);
9211
- setIsFooterFrameSelection(false);
9212
7728
  siblingHintElRef.current = null;
9213
7729
  setSiblingHintRect(null);
9214
- setSiblingHintRects([]);
9215
7730
  setIsItemDragging(false);
9216
7731
  hoveredNavContainerRef.current = null;
9217
7732
  setHoveredNavContainerRect(null);
@@ -9219,70 +7734,18 @@ function OhhwellsBridge() {
9219
7734
  setToolbarRect(null);
9220
7735
  setToolbarVariant("none");
9221
7736
  }
9222
- }, [clearSelectedAttr]);
9223
- const markSelected = useCallback5((el) => {
9224
- clearSelectedAttr();
9225
- el.setAttribute("data-ohw-selected", "");
9226
- }, [clearSelectedAttr]);
9227
- const resolveHrefKeyElement = useCallback5((hrefKey) => {
9228
- if (isFooterHrefKey(hrefKey)) {
9229
- return document.querySelector(
9230
- `footer [data-ohw-href-key="${CSS.escape(hrefKey)}"]`
9231
- );
9232
- }
9233
- if (isNavbarHrefKey(hrefKey)) {
9234
- const desktop = document.querySelector("[data-ohw-nav-container]");
9235
- const drawer = document.querySelector("[data-ohw-nav-drawer]");
9236
- 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)}"]`);
9237
- }
9238
- return document.querySelector(
9239
- `[data-ohw-href-key="${CSS.escape(hrefKey)}"]`
9240
- );
9241
7737
  }, []);
9242
- const resyncSelectedNavigationItem = useCallback5(() => {
9243
- const hrefKey = selectedHrefKeyRef.current;
9244
- if (hrefKey) {
9245
- const link = resolveHrefKeyElement(hrefKey);
9246
- if (!link || !isNavigationItem(link)) return;
9247
- selectedElRef.current = link;
9248
- const { key, disabled } = getNavigationItemReorderState(link);
9249
- setReorderHrefKey(key);
9250
- setReorderDragDisabled(disabled);
9251
- setIsFooterFrameSelection(false);
9252
- setToolbarVariant("link-action");
9253
- setToolbarRect(link.getBoundingClientRect());
9254
- setSiblingHintRects(collectNavigationItemSiblingHintRects(link));
9255
- return;
9256
- }
9257
- const colAttr = selectedFooterColAttrRef.current;
9258
- if (colAttr != null) {
9259
- const column = document.querySelector(`[data-ohw-footer-col="${CSS.escape(colAttr)}"]`) ?? listFooterColumns()[Number(colAttr)] ?? null;
9260
- if (!column || !isNavigationContainer(column)) return;
9261
- selectedElRef.current = column;
9262
- setIsFooterFrameSelection(true);
9263
- setToolbarVariant("select-frame");
9264
- setToolbarRect(column.getBoundingClientRect());
9265
- setSiblingHintRects(
9266
- listFooterColumns().filter((c) => c !== column).map((c) => c.getBoundingClientRect())
9267
- );
9268
- }
9269
- }, [resolveHrefKeyElement]);
9270
- const reselectNavigationItem = useCallback5((navAnchor) => {
7738
+ const reselectNavigationItem = useCallback3((navAnchor) => {
9271
7739
  selectedElRef.current = navAnchor;
9272
- selectedHrefKeyRef.current = navAnchor.getAttribute("data-ohw-href-key");
9273
- selectedFooterColAttrRef.current = null;
9274
- markSelected(navAnchor);
9275
- setSelectedIsCta(isNavbarButton2(navAnchor));
9276
7740
  const { key, disabled } = getNavigationItemReorderState(navAnchor);
9277
7741
  setReorderHrefKey(key);
9278
7742
  setReorderDragDisabled(disabled);
9279
7743
  setToolbarVariant("link-action");
9280
7744
  setToolbarRect(navAnchor.getBoundingClientRect());
9281
- setSiblingHintRects(collectNavigationItemSiblingHintRects(navAnchor));
9282
7745
  setToolbarShowEditLink(false);
9283
7746
  setActiveCommands(/* @__PURE__ */ new Set());
9284
- }, [markSelected]);
9285
- const commitNavigationTextEdit = useCallback5((navAnchor) => {
7747
+ }, []);
7748
+ const commitNavigationTextEdit = useCallback3((navAnchor) => {
9286
7749
  const el = activeElRef.current;
9287
7750
  if (!el) return;
9288
7751
  const key = el.dataset.ohwKey;
@@ -9301,7 +7764,6 @@ function OhhwellsBridge() {
9301
7764
  }
9302
7765
  }
9303
7766
  el.removeAttribute("contenteditable");
9304
- el.removeAttribute("data-ohw-editing");
9305
7767
  activeElRef.current = null;
9306
7768
  setMaxBadge(null);
9307
7769
  setActiveCommands(/* @__PURE__ */ new Set());
@@ -9309,7 +7771,7 @@ function OhhwellsBridge() {
9309
7771
  postToParent2({ type: "ow:exit-edit" });
9310
7772
  reselectNavigationItem(navAnchor);
9311
7773
  }, [postToParent2, reselectNavigationItem]);
9312
- const handleAddTopLevelNavItem = useCallback5(() => {
7774
+ const handleAddTopLevelNavItem = useCallback3(() => {
9313
7775
  const items = listNavbarItems();
9314
7776
  addNavAfterAnchorRef.current = items[items.length - 1] ?? null;
9315
7777
  deselectRef.current();
@@ -9321,321 +7783,20 @@ function OhhwellsBridge() {
9321
7783
  intent: "add-nav"
9322
7784
  });
9323
7785
  }, []);
9324
- const clearFooterDragVisuals = useCallback5(() => {
9325
- footerDragRef.current = null;
9326
- setSiblingHintRects([]);
9327
- setFooterDropSlots([]);
9328
- setActiveFooterDropIndex(null);
9329
- setDraggedItemRect(null);
9330
- setIsItemDragging(false);
9331
- unlockFooterDragInteraction();
9332
- }, []);
9333
- const refreshFooterDragVisuals = useCallback5((session, activeSlot, clientX, clientY) => {
9334
- const dragged = session.draggedEl;
9335
- setDraggedItemRect(dragged.getBoundingClientRect());
9336
- if (typeof clientX === "number" && typeof clientY === "number") {
9337
- session.lastClientX = clientX;
9338
- session.lastClientY = clientY;
9339
- }
9340
- session.activeSlot = activeSlot;
9341
- if (session.kind === "link") {
9342
- const columns2 = listFooterColumns();
9343
- const focusCols = /* @__PURE__ */ new Set([session.sourceColumnIndex]);
9344
- if (activeSlot) focusCols.add(activeSlot.columnIndex);
9345
- const siblingRects = [];
9346
- columns2.forEach((col, i) => {
9347
- if (!focusCols.has(i)) return;
9348
- for (const link of listFooterLinksInColumn(col)) {
9349
- if (link === dragged) continue;
9350
- siblingRects.push(link.getBoundingClientRect());
9351
- }
9352
- });
9353
- setSiblingHintRects(siblingRects);
9354
- const slots2 = [];
9355
- columns2.forEach((col, i) => {
9356
- slots2.push(...buildLinkDropSlots(col, i));
9357
- });
9358
- setFooterDropSlots(slots2);
9359
- const activeIdx2 = activeSlot ? slots2.findIndex((s) => s.columnIndex === activeSlot.columnIndex && s.insertIndex === activeSlot.insertIndex) : -1;
9360
- setActiveFooterDropIndex(activeIdx2 >= 0 ? activeIdx2 : null);
9361
- return;
9362
- }
9363
- const columns = listFooterColumns();
9364
- setSiblingHintRects(columns.filter((col) => col !== dragged).map((col) => col.getBoundingClientRect()));
9365
- const slots = buildColumnDropSlots();
9366
- setFooterDropSlots(slots);
9367
- const activeIdx = activeSlot ? slots.findIndex((s) => s.insertIndex === activeSlot.insertIndex) : -1;
9368
- setActiveFooterDropIndex(activeIdx >= 0 ? activeIdx : null);
7786
+ const handleItemDragStart = useCallback3(() => {
7787
+ siblingHintElRef.current = null;
7788
+ setSiblingHintRect(null);
7789
+ setIsItemDragging(true);
9369
7790
  }, []);
9370
- const refreshFooterDragVisualsRef = useRef7(refreshFooterDragVisuals);
9371
- refreshFooterDragVisualsRef.current = refreshFooterDragVisuals;
9372
- const commitFooterDragRef = useRef7(() => {
9373
- });
9374
- const beginFooterDragRef = useRef7(() => {
9375
- });
9376
- const beginFooterDrag = useCallback5(
9377
- (session) => {
9378
- const rect = session.draggedEl.getBoundingClientRect();
9379
- session.lastClientX = session.lastClientX || rect.left + rect.width / 2;
9380
- session.lastClientY = session.lastClientY || rect.top + rect.height / 2;
9381
- session.activeSlot = session.activeSlot ?? null;
9382
- footerDragRef.current = session;
9383
- setIsItemDragging(true);
9384
- lockFooterDuringDrag();
9385
- siblingHintElRef.current = null;
9386
- setSiblingHintRect(null);
9387
- if (session.wasSelected && selectedElRef.current === session.draggedEl) {
9388
- setToolbarRect(rect);
9389
- }
9390
- const initialSlot = session.kind === "link" && session.hrefKey ? hitTestLinkDropSlot(session.lastClientX, session.lastClientY, session.hrefKey) : hitTestColumnDropSlot(session.lastClientX, session.lastClientY);
9391
- refreshFooterDragVisuals(session, initialSlot, session.lastClientX, session.lastClientY);
9392
- },
9393
- [refreshFooterDragVisuals]
9394
- );
9395
- beginFooterDragRef.current = beginFooterDrag;
9396
- const commitFooterDrag = useCallback5(
9397
- (clientX, clientY) => {
9398
- const session = footerDragRef.current;
9399
- if (!session) {
9400
- clearFooterDragVisuals();
9401
- return;
9402
- }
9403
- const x = typeof clientX === "number" && (clientX !== 0 || clientY !== 0) ? clientX : session.lastClientX;
9404
- const y = typeof clientY === "number" && (clientX !== 0 || clientY !== 0) ? clientY : session.lastClientY;
9405
- let nextOrder = null;
9406
- const slot = session.activeSlot ?? (session.kind === "link" && session.hrefKey ? hitTestLinkDropSlot(x, y, session.hrefKey) : session.kind === "column" ? hitTestColumnDropSlot(x, y) : null);
9407
- if (session.kind === "link" && session.hrefKey && slot) {
9408
- nextOrder = planFooterLinkMove(session.hrefKey, slot.columnIndex, slot.insertIndex);
9409
- } else if (session.kind === "column" && slot) {
9410
- nextOrder = planFooterColumnMove(session.sourceColumnIndex, slot.insertIndex);
9411
- }
9412
- const wasSelected = session.wasSelected;
9413
- const draggedEl = session.draggedEl;
9414
- const hrefKey = session.hrefKey;
9415
- let movedColumnIndex = null;
9416
- if (session.kind === "column" && slot && nextOrder) {
9417
- let adjusted = slot.insertIndex;
9418
- if (session.sourceColumnIndex < slot.insertIndex) adjusted -= 1;
9419
- movedColumnIndex = Math.max(0, Math.min(adjusted, nextOrder.length - 1));
9420
- }
9421
- const applySelectionAfterDrop = () => {
9422
- if (!wasSelected) {
9423
- deselectRef.current();
9424
- return;
9425
- }
9426
- if (hrefKey) {
9427
- selectedHrefKeyRef.current = hrefKey;
9428
- selectedFooterColAttrRef.current = null;
9429
- const link = resolveHrefKeyElement(hrefKey);
9430
- if (link && isNavigationItem(link)) {
9431
- selectRef.current(link);
9432
- return;
9433
- }
9434
- }
9435
- if (movedColumnIndex !== null && nextOrder) {
9436
- const colKeys = nextOrder[movedColumnIndex] ?? [];
9437
- let column = null;
9438
- for (const key of colKeys) {
9439
- const link = document.querySelector(
9440
- `footer [data-ohw-href-key="${CSS.escape(key)}"]`
9441
- );
9442
- if (link) {
9443
- column = findFooterColumnForLink(link);
9444
- if (column) break;
9445
- }
9446
- }
9447
- if (!column) column = listFooterColumns()[movedColumnIndex] ?? null;
9448
- if (column && isNavigationContainer(column)) {
9449
- selectFrameRef.current(column);
9450
- return;
9451
- }
9452
- }
9453
- if (document.body.contains(draggedEl)) {
9454
- if (isNavigationItem(draggedEl)) {
9455
- selectRef.current(draggedEl);
9456
- return;
9457
- }
9458
- if (isNavigationContainer(draggedEl)) {
9459
- selectFrameRef.current(draggedEl);
9460
- return;
9461
- }
9462
- }
9463
- deselectRef.current();
9464
- };
9465
- if (nextOrder) {
9466
- const orderJson = JSON.stringify(nextOrder);
9467
- editContentRef.current = {
9468
- ...editContentRef.current,
9469
- [FOOTER_ORDER_KEY]: orderJson
9470
- };
9471
- applyFooterOrder(nextOrder);
9472
- document.querySelectorAll("footer [data-ohw-href-key]").forEach(disableNativeHrefDrag);
9473
- postToParentRef.current({
9474
- type: "ow:change",
9475
- nodes: [{ key: FOOTER_ORDER_KEY, text: orderJson }]
9476
- });
9477
- applySelectionAfterDrop();
9478
- clearFooterDragVisuals();
9479
- requestAnimationFrame(() => {
9480
- if (editContentRef.current[FOOTER_ORDER_KEY] === orderJson) {
9481
- applyFooterOrder(nextOrder);
9482
- document.querySelectorAll("footer [data-ohw-href-key]").forEach(disableNativeHrefDrag);
9483
- }
9484
- applySelectionAfterDrop();
9485
- requestAnimationFrame(() => {
9486
- if (editContentRef.current[FOOTER_ORDER_KEY] === orderJson) {
9487
- applyFooterOrder(nextOrder);
9488
- }
9489
- resyncSelectedNavigationItem();
9490
- });
9491
- });
9492
- return;
9493
- }
9494
- applySelectionAfterDrop();
9495
- clearFooterDragVisuals();
9496
- },
9497
- [clearFooterDragVisuals, resolveHrefKeyElement, resyncSelectedNavigationItem]
9498
- );
9499
- commitFooterDragRef.current = commitFooterDrag;
9500
- const startFooterLinkDrag = useCallback5(
9501
- (anchor, clientX, clientY, wasSelected) => {
9502
- const hrefKey = anchor.getAttribute("data-ohw-href-key");
9503
- if (!hrefKey || !isFooterHrefKey(hrefKey)) return false;
9504
- const column = findFooterColumnForLink(anchor);
9505
- const columns = listFooterColumns();
9506
- beginFooterDrag({
9507
- kind: "link",
9508
- hrefKey,
9509
- columnEl: column,
9510
- sourceColumnIndex: column ? columns.indexOf(column) : 0,
9511
- wasSelected,
9512
- draggedEl: anchor,
9513
- lastClientX: clientX,
9514
- lastClientY: clientY,
9515
- activeSlot: null
9516
- });
9517
- return true;
9518
- },
9519
- [beginFooterDrag]
9520
- );
9521
- const startFooterColumnDrag = useCallback5(
9522
- (columnEl, clientX, clientY, wasSelected) => {
9523
- const columns = listFooterColumns();
9524
- const idx = columns.indexOf(columnEl);
9525
- if (idx < 0) return false;
9526
- beginFooterDrag({
9527
- kind: "column",
9528
- hrefKey: null,
9529
- columnEl,
9530
- sourceColumnIndex: idx,
9531
- wasSelected,
9532
- draggedEl: columnEl,
9533
- lastClientX: clientX,
9534
- lastClientY: clientY,
9535
- activeSlot: null
9536
- });
9537
- return true;
9538
- },
9539
- [beginFooterDrag]
9540
- );
9541
- const handleItemDragStart = useCallback5(
9542
- (e) => {
9543
- const selected = selectedElRef.current;
9544
- if (!selected) {
9545
- setIsItemDragging(true);
9546
- return;
9547
- }
9548
- const clientX = e?.clientX ?? selected.getBoundingClientRect().left + 8;
9549
- const clientY = e?.clientY ?? selected.getBoundingClientRect().top + 8;
9550
- if (startFooterLinkDrag(selected, clientX, clientY, true)) return;
9551
- if (selected.hasAttribute("data-ohw-footer-col") || isInferredFooterGroup(selected) && selected.closest("footer")) {
9552
- if (startFooterColumnDrag(selected, clientX, clientY, true)) return;
9553
- }
9554
- if (startNavLinkDrag(selected, clientX, clientY, true)) return;
9555
- siblingHintElRef.current = null;
9556
- setSiblingHintRect(null);
9557
- setIsItemDragging(true);
9558
- },
9559
- [startFooterColumnDrag, startFooterLinkDrag, startNavLinkDrag]
9560
- );
9561
- const handleItemDragEnd = useCallback5(
9562
- (e) => {
9563
- if (footerDragRef.current) {
9564
- const x = e?.clientX;
9565
- const y = e?.clientY;
9566
- if (typeof x === "number" && typeof y === "number" && (x !== 0 || y !== 0)) {
9567
- commitFooterDrag(x, y);
9568
- } else {
9569
- commitFooterDrag();
9570
- }
9571
- return;
9572
- }
9573
- if (navDragRef.current) {
9574
- const x = e?.clientX;
9575
- const y = e?.clientY;
9576
- if (typeof x === "number" && typeof y === "number" && (x !== 0 || y !== 0)) {
9577
- commitNavDrag(x, y);
9578
- } else {
9579
- commitNavDrag();
9580
- }
9581
- return;
9582
- }
9583
- setIsItemDragging(false);
9584
- },
9585
- [commitFooterDrag, commitNavDrag, navDragRef]
9586
- );
9587
- const handleItemChromePointerDown = useCallback5((e) => {
9588
- if (e.button !== 0) return;
9589
- const selected = selectedElRef.current;
9590
- if (!selected) return;
9591
- const hrefKey = selected.getAttribute("data-ohw-href-key");
9592
- if (hrefKey && isFooterHrefKey(hrefKey)) {
9593
- footerPointerDragRef.current = {
9594
- el: selected,
9595
- kind: "link",
9596
- startX: e.clientX,
9597
- startY: e.clientY,
9598
- pointerId: e.pointerId,
9599
- wasSelected: true,
9600
- started: false
9601
- };
9602
- return;
9603
- }
9604
- if (selected.hasAttribute("data-ohw-footer-col") || isInferredFooterGroup(selected) && selected.closest("footer")) {
9605
- footerPointerDragRef.current = {
9606
- el: selected,
9607
- kind: "column",
9608
- startX: e.clientX,
9609
- startY: e.clientY,
9610
- pointerId: e.pointerId,
9611
- wasSelected: true,
9612
- started: false
9613
- };
9614
- return;
9615
- }
9616
- if (armNavPressFromChrome(selected, e.clientX, e.clientY, e.pointerId)) return;
9617
- }, [armNavPressFromChrome]);
9618
- const handleItemChromeClick = useCallback5((clientX, clientY) => {
9619
- if (suppressNextClickRef.current || Date.now() < suppressClickUntilRef.current) {
9620
- suppressNextClickRef.current = false;
9621
- return;
9622
- }
9623
- const selected = selectedElRef.current;
9624
- if (!selected || !isNavigationItem(selected)) return;
9625
- const editable = selected.querySelector('[data-ohw-editable="text"]') ?? selected.querySelector("[data-ohw-editable]");
9626
- if (!editable) return;
9627
- activateRef.current(editable, { caretX: clientX, caretY: clientY });
7791
+ const handleItemDragEnd = useCallback3(() => {
7792
+ setIsItemDragging(false);
9628
7793
  }, []);
9629
7794
  reselectNavigationItemRef.current = reselectNavigationItem;
9630
7795
  commitNavigationTextEditRef.current = commitNavigationTextEdit;
9631
- const select = useCallback5((anchor) => {
7796
+ const select = useCallback3((anchor) => {
9632
7797
  if (!isNavigationItem(anchor)) return;
9633
7798
  if (activeElRef.current) deactivate();
9634
7799
  selectedElRef.current = anchor;
9635
- selectedHrefKeyRef.current = anchor.getAttribute("data-ohw-href-key");
9636
- selectedFooterColAttrRef.current = null;
9637
- markSelected(anchor);
9638
- setSelectedIsCta(isNavbarButton2(anchor));
9639
7800
  clearHrefKeyHover(anchor);
9640
7801
  hoveredNavContainerRef.current = null;
9641
7802
  setHoveredNavContainerRect(null);
@@ -9643,26 +7804,19 @@ function OhhwellsBridge() {
9643
7804
  hoveredItemElRef.current = null;
9644
7805
  siblingHintElRef.current = null;
9645
7806
  setSiblingHintRect(null);
9646
- setSiblingHintRects(collectNavigationItemSiblingHintRects(anchor));
9647
7807
  setIsItemDragging(false);
9648
7808
  const { key, disabled } = getNavigationItemReorderState(anchor);
9649
7809
  setReorderHrefKey(key);
9650
7810
  setReorderDragDisabled(disabled);
9651
- setIsFooterFrameSelection(false);
9652
7811
  setToolbarVariant("link-action");
9653
7812
  setToolbarRect(anchor.getBoundingClientRect());
9654
7813
  setToolbarShowEditLink(false);
9655
7814
  setActiveCommands(/* @__PURE__ */ new Set());
9656
- }, [deactivate, markSelected]);
9657
- const selectFrame = useCallback5((el) => {
7815
+ }, [deactivate]);
7816
+ const selectFrame = useCallback3((el) => {
9658
7817
  if (!isNavigationContainer(el)) return;
9659
7818
  if (activeElRef.current) deactivate();
9660
- const isFooterColumn = el.hasAttribute("data-ohw-footer-col") || Boolean(el.closest("footer") && isInferredFooterGroup(el));
9661
7819
  selectedElRef.current = el;
9662
- selectedHrefKeyRef.current = null;
9663
- selectedFooterColAttrRef.current = isFooterColumn ? el.getAttribute("data-ohw-footer-col") ?? String(listFooterColumns().indexOf(el)) : null;
9664
- markSelected(el);
9665
- setSelectedIsCta(false);
9666
7820
  clearHrefKeyHover(el);
9667
7821
  hoveredNavContainerRef.current = null;
9668
7822
  setHoveredNavContainerRect(null);
@@ -9670,25 +7824,17 @@ function OhhwellsBridge() {
9670
7824
  hoveredItemElRef.current = null;
9671
7825
  siblingHintElRef.current = null;
9672
7826
  setSiblingHintRect(null);
9673
- setSiblingHintRects(
9674
- isFooterColumn ? listFooterColumns().filter((column) => column !== el).map((column) => column.getBoundingClientRect()) : []
9675
- );
9676
7827
  setIsItemDragging(false);
9677
7828
  setReorderHrefKey(null);
9678
7829
  setReorderDragDisabled(false);
9679
- setIsFooterFrameSelection(isFooterColumn);
9680
7830
  setToolbarVariant("select-frame");
9681
7831
  setToolbarRect(el.getBoundingClientRect());
9682
7832
  setToolbarShowEditLink(false);
9683
7833
  setActiveCommands(/* @__PURE__ */ new Set());
9684
- }, [deactivate, markSelected]);
9685
- const activate = useCallback5((el, options) => {
7834
+ }, [deactivate]);
7835
+ const activate = useCallback3((el, options) => {
9686
7836
  if (activeElRef.current === el) return;
9687
- clearSelectedAttr();
9688
7837
  selectedElRef.current = null;
9689
- selectedHrefKeyRef.current = null;
9690
- selectedFooterColAttrRef.current = null;
9691
- setSelectedIsCta(false);
9692
7838
  deactivate();
9693
7839
  if (hoveredImageRef.current) {
9694
7840
  hoveredImageRef.current = null;
@@ -9698,34 +7844,14 @@ function OhhwellsBridge() {
9698
7844
  siblingHintElRef.current = null;
9699
7845
  setSiblingHintRect(null);
9700
7846
  setIsItemDragging(false);
9701
- const preActivationSelection = window.getSelection();
9702
- const preservedRange = preActivationSelection && preActivationSelection.rangeCount > 0 && !preActivationSelection.isCollapsed && el.contains(preActivationSelection.getRangeAt(0).commonAncestorContainer) ? preActivationSelection.getRangeAt(0).cloneRange() : null;
9703
7847
  el.setAttribute("contenteditable", "true");
9704
- el.setAttribute("data-ohw-editing", "");
9705
7848
  el.removeAttribute("data-ohw-hovered");
9706
7849
  el.closest("[data-ohw-href-key]")?.removeAttribute("data-ohw-hovered");
9707
7850
  activeElRef.current = el;
9708
7851
  originalContentRef.current = el.innerHTML;
9709
- el.focus({ preventScroll: true });
9710
- if (preservedRange) {
9711
- const selection = window.getSelection();
9712
- selection?.removeAllRanges();
9713
- selection?.addRange(preservedRange);
9714
- } else if (options?.caretX !== void 0 && options?.caretY !== void 0) {
9715
- const { caretX, caretY } = options;
9716
- placeCaretAtPoint(el, caretX, caretY);
9717
- requestAnimationFrame(() => {
9718
- if (activeElRef.current === el) placeCaretAtPoint(el, caretX, caretY);
9719
- });
9720
- } else {
9721
- const selection = window.getSelection();
9722
- if (selection && selection.rangeCount === 0) {
9723
- const range = document.createRange();
9724
- range.selectNodeContents(el);
9725
- range.collapse(false);
9726
- selection.removeAllRanges();
9727
- selection.addRange(range);
9728
- }
7852
+ el.focus();
7853
+ if (options?.caretX !== void 0 && options?.caretY !== void 0) {
7854
+ placeCaretAtPoint(el, options.caretX, options.caretY);
9729
7855
  }
9730
7856
  setToolbarShowEditLink(Boolean(getHrefKeyFromElement(el)));
9731
7857
  const navAnchor = getNavigationItemAnchor(el);
@@ -9740,13 +7866,13 @@ function OhhwellsBridge() {
9740
7866
  setToolbarRect(getEditMeasureEl(el).getBoundingClientRect());
9741
7867
  postToParent2({ type: "ow:enter-edit", key: el.dataset.ohwKey });
9742
7868
  requestAnimationFrame(() => refreshActiveCommandsRef.current());
9743
- }, [clearSelectedAttr, deactivate, postToParent2]);
7869
+ }, [deactivate, postToParent2]);
9744
7870
  activateRef.current = activate;
9745
7871
  deactivateRef.current = deactivate;
9746
7872
  selectRef.current = select;
9747
7873
  selectFrameRef.current = selectFrame;
9748
7874
  deselectRef.current = deselect;
9749
- useLayoutEffect3(() => {
7875
+ useLayoutEffect2(() => {
9750
7876
  if (!subdomain || isEditMode) {
9751
7877
  setFetchState("done");
9752
7878
  return;
@@ -9787,7 +7913,6 @@ function OhhwellsBridge() {
9787
7913
  applyLinkByKey(key, val);
9788
7914
  }
9789
7915
  reconcileNavbarItemsFromContent(content);
9790
- reconcileFooterOrderFromContent(content);
9791
7916
  enforceLinkHrefs();
9792
7917
  initSectionsFromContent(content, true);
9793
7918
  sectionsLoadedRef.current = true;
@@ -9816,7 +7941,7 @@ function OhhwellsBridge() {
9816
7941
  cancelled = true;
9817
7942
  };
9818
7943
  }, [subdomain, isEditMode]);
9819
- useEffect8(() => {
7944
+ useEffect7(() => {
9820
7945
  if (!subdomain || isEditMode) return;
9821
7946
  let debounceTimer = null;
9822
7947
  let observer = null;
@@ -9848,7 +7973,6 @@ function OhhwellsBridge() {
9848
7973
  applyLinkByKey(key, val);
9849
7974
  }
9850
7975
  reconcileNavbarItemsFromContent(content);
9851
- reconcileFooterOrderFromContent(content);
9852
7976
  } finally {
9853
7977
  observer?.observe(document.body, { childList: true, subtree: true });
9854
7978
  }
@@ -9866,16 +7990,16 @@ function OhhwellsBridge() {
9866
7990
  if (debounceTimer) clearTimeout(debounceTimer);
9867
7991
  };
9868
7992
  }, [subdomain, isEditMode, pathname]);
9869
- useLayoutEffect3(() => {
7993
+ useLayoutEffect2(() => {
9870
7994
  const el = document.getElementById("ohw-loader");
9871
7995
  if (!el) return;
9872
7996
  const visible = Boolean(subdomain) && fetchState !== "done";
9873
7997
  el.style.display = visible ? "flex" : "none";
9874
7998
  }, [subdomain, fetchState]);
9875
- useEffect8(() => {
7999
+ useEffect7(() => {
9876
8000
  postToParent2({ type: "ow:navigation", path: pathname });
9877
8001
  }, [pathname, postToParent2]);
9878
- useEffect8(() => {
8002
+ useEffect7(() => {
9879
8003
  if (!isEditMode) return;
9880
8004
  if (linkPopoverSessionRef.current?.intent === "add-nav") return;
9881
8005
  if (document.querySelector("[data-ohw-section-picker]")) return;
@@ -9883,72 +8007,23 @@ function OhhwellsBridge() {
9883
8007
  deselectRef.current();
9884
8008
  deactivateRef.current();
9885
8009
  }, [pathname, isEditMode]);
9886
- useEffect8(() => {
8010
+ useEffect7(() => {
9887
8011
  const contentForNav = () => {
9888
8012
  if (isEditMode) return editContentRef.current;
9889
8013
  if (!subdomain) return {};
9890
8014
  return contentCache.get(subdomain) ?? {};
9891
8015
  };
9892
- const observeRoots = () => [document.querySelector("nav"), document.querySelector("footer")].filter(
9893
- (el) => Boolean(el)
9894
- );
9895
- const roots = observeRoots();
9896
- if (roots.length === 0) {
9897
- reconcileNavbarItemsFromContent(contentForNav());
9898
- reconcileFooterOrderFromContent(contentForNav());
9899
- if (isEditMode) syncNavigationDragCursorAttrs();
9900
- return;
9901
- }
9902
- let rafId = null;
9903
- let applying = false;
9904
- let observer = null;
9905
- const attach = () => {
9906
- for (const root of observeRoots()) {
9907
- observer?.observe(root, { childList: true, subtree: true });
9908
- }
9909
- };
9910
- const run = () => {
9911
- if (footerDragRef.current || navDragRef.current || applying) return;
9912
- applying = true;
9913
- observer?.disconnect();
9914
- try {
9915
- const content = contentForNav();
9916
- reconcileNavbarItemsFromContent(content);
9917
- reconcileFooterOrderFromContent(content);
9918
- document.querySelectorAll("footer [data-ohw-href-key]").forEach((el) => {
9919
- if (isFooterHrefKey(el.getAttribute("data-ohw-href-key"))) {
9920
- disableNativeHrefDrag(el);
9921
- }
9922
- });
9923
- document.querySelectorAll(
9924
- "nav [data-ohw-href-key], [data-ohw-nav-container] [data-ohw-href-key], [data-ohw-nav-drawer] [data-ohw-href-key]"
9925
- ).forEach((el) => {
9926
- if (isNavbarHrefKey(el.getAttribute("data-ohw-href-key"))) {
9927
- disableNativeHrefDrag(el);
9928
- }
9929
- });
9930
- resyncSelectedNavigationItem();
9931
- if (isEditMode) syncNavigationDragCursorAttrs();
9932
- } finally {
9933
- applying = false;
9934
- attach();
9935
- }
9936
- };
9937
- const schedule = () => {
9938
- if (applying || rafId != null) return;
9939
- rafId = requestAnimationFrame(() => {
9940
- rafId = null;
9941
- run();
9942
- });
9943
- };
9944
- observer = new MutationObserver(schedule);
8016
+ const run = () => reconcileNavbarItemsFromContent(contentForNav());
9945
8017
  run();
9946
- return () => {
9947
- if (rafId != null) cancelAnimationFrame(rafId);
9948
- observer?.disconnect();
9949
- };
9950
- }, [isEditMode, pathname, subdomain, fetchState, resyncSelectedNavigationItem]);
9951
- useEffect8(() => {
8018
+ const nav = document.querySelector("nav");
8019
+ if (!nav) return;
8020
+ const observer = new MutationObserver(() => {
8021
+ requestAnimationFrame(run);
8022
+ });
8023
+ observer.observe(nav, { childList: true, subtree: true });
8024
+ return () => observer.disconnect();
8025
+ }, [isEditMode, pathname, subdomain, fetchState]);
8026
+ useEffect7(() => {
9952
8027
  if (!isEditMode) return;
9953
8028
  const measure = () => {
9954
8029
  const h = document.body.scrollHeight;
@@ -9972,7 +8047,7 @@ function OhhwellsBridge() {
9972
8047
  window.removeEventListener("resize", handleResize);
9973
8048
  };
9974
8049
  }, [pathname, isEditMode, postToParent2]);
9975
- useEffect8(() => {
8050
+ useEffect7(() => {
9976
8051
  if (!subdomainFromQuery || isEditMode) return;
9977
8052
  const handleClick = (e) => {
9978
8053
  const anchor = e.target.closest("a");
@@ -9988,24 +8063,19 @@ function OhhwellsBridge() {
9988
8063
  document.addEventListener("click", handleClick, true);
9989
8064
  return () => document.removeEventListener("click", handleClick, true);
9990
8065
  }, [subdomainFromQuery, isEditMode, router]);
9991
- useEffect8(() => {
8066
+ useEffect7(() => {
9992
8067
  if (!isEditMode) {
9993
8068
  editStylesRef.current?.base.remove();
9994
8069
  editStylesRef.current?.forceHover.remove();
9995
8070
  editStylesRef.current?.stateViews.remove();
9996
8071
  editStylesRef.current = null;
9997
- document.querySelectorAll("[data-ohw-can-drag]").forEach((el) => {
9998
- el.removeAttribute("data-ohw-can-drag");
9999
- });
10000
8072
  return;
10001
8073
  }
10002
- const existing = editStylesRef.current;
10003
- let initialVh = window.innerHeight;
10004
- if (existing?.base.textContent) {
10005
- const match = existing.base.textContent.match(/\.min-h-screen[^{]*\{[^}]*min-height:\s*(\d+)px/);
10006
- if (match) initialVh = parseInt(match[1], 10);
10007
- }
10008
- const baseCss = `
8074
+ if (!editStylesRef.current) {
8075
+ const initialVh = window.innerHeight;
8076
+ const base = document.createElement("style");
8077
+ base.setAttribute("data-ohw-edit-style", "");
8078
+ base.textContent = `
10009
8079
  html { height: auto !important; }
10010
8080
  body { height: auto !important; min-height: 0 !important; overflow: hidden !important; }
10011
8081
  .min-h-screen, .min-h-svh, .min-h-dvh { min-height: ${initialVh}px !important; }
@@ -10016,25 +8086,7 @@ function OhhwellsBridge() {
10016
8086
  [data-ohw-editable] {
10017
8087
  display: block;
10018
8088
  }
10019
- /* Body text (no item-action toolbar) \u2014 first click enters text edit \u2192 I-beam.
10020
- Exclude select-first nav/footer labels so grab/default on [data-ohw-href-key] can win. */
10021
- [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] *)) {
10022
- cursor: text !important;
10023
- }
10024
- /* Select-first items: default arrow; grab only when drag-start is allowed (data-ohw-can-drag). */
10025
- [data-ohw-href-key]:not([data-ohw-selected]),
10026
- [data-ohw-href-key]:not([data-ohw-selected]) * {
10027
- cursor: default !important;
10028
- }
10029
- [data-ohw-href-key][data-ohw-can-drag]:not([data-ohw-selected]),
10030
- [data-ohw-href-key][data-ohw-can-drag]:not([data-ohw-selected]) * {
10031
- cursor: grab !important;
10032
- }
10033
- /* Selected (action-toolbar) \u2014 next click enters text edit at caret. */
10034
- [data-ohw-href-key][data-ohw-selected],
10035
- [data-ohw-href-key][data-ohw-selected] * {
10036
- cursor: text !important;
10037
- }
8089
+ [data-ohw-editable]:not([contenteditable]):not([data-ohw-editable="image"]):not([data-ohw-editable="bg-image"]):not([data-ohw-editable="video"]) { cursor: text !important; }
10038
8090
  [data-ohw-editable="image"], [data-ohw-editable="image"] *,
10039
8091
  [data-ohw-editable="video"], [data-ohw-editable="video"] *,
10040
8092
  [data-ohw-editable="bg-image"], [data-ohw-editable="bg-image"] * { cursor: pointer !important; }
@@ -10050,32 +8102,9 @@ function OhhwellsBridge() {
10050
8102
  outline: none !important;
10051
8103
  outline-offset: 0 !important;
10052
8104
  }
10053
- /* Editor chrome owns selection \u2014 suppress native focus rings on nav/footer items. */
10054
- nav [data-ohw-href-key]:focus,
10055
- nav [data-ohw-href-key]:focus-visible,
10056
- nav [data-ohw-href-key]:focus-within,
10057
- footer [data-ohw-href-key]:focus,
10058
- footer [data-ohw-href-key]:focus-visible,
10059
- footer [data-ohw-href-key]:focus-within,
10060
- nav [data-ohw-href-key] [data-ohw-editable]:focus,
10061
- nav [data-ohw-href-key] [data-ohw-editable]:focus-visible,
10062
- footer [data-ohw-href-key] [data-ohw-editable]:focus,
10063
- footer [data-ohw-href-key] [data-ohw-editable]:focus-visible {
10064
- outline: none !important;
10065
- outline-offset: 0 !important;
10066
- box-shadow: none !important;
10067
- }
10068
- /* Text edit wins over grab/default (must beat [data-ohw-can-drag] *). */
10069
- [data-ohw-editing],
10070
- [data-ohw-editing] *,
10071
- [data-ohw-editable][contenteditable],
10072
- [data-ohw-editable][contenteditable] *,
10073
- nav [data-ohw-href-key] [data-ohw-editable][contenteditable],
10074
- nav [data-ohw-href-key] [data-ohw-editable][contenteditable] *,
10075
- footer [data-ohw-href-key] [data-ohw-editable][contenteditable],
10076
- footer [data-ohw-href-key] [data-ohw-editable][contenteditable] * {
8105
+ [data-ohw-editable][contenteditable] {
10077
8106
  outline: none !important;
10078
- caret-color: ${PRIMARY2} !important;
8107
+ caret-color: ${PRIMARY2};
10079
8108
  cursor: text !important;
10080
8109
  }
10081
8110
  [data-ohw-editable][contenteditable]::selection,
@@ -10084,9 +8113,6 @@ function OhhwellsBridge() {
10084
8113
  [data-ohw-editable-state][data-ohw-active-state] [data-ohw-editable] { pointer-events: auto !important; }
10085
8114
  [data-ohw-editable-state][data-ohw-active-state][data-ohw-editable] { pointer-events: auto !important; }
10086
8115
  `;
10087
- if (!existing) {
10088
- const base = document.createElement("style");
10089
- base.setAttribute("data-ohw-edit-style", "");
10090
8116
  const forceHover = document.createElement("style");
10091
8117
  forceHover.setAttribute("data-ohw-active-state-style", "");
10092
8118
  const stateViews = document.createElement("style");
@@ -10102,43 +8128,14 @@ function OhhwellsBridge() {
10102
8128
  document.head.appendChild(stateViews);
10103
8129
  editStylesRef.current = { base, forceHover, stateViews };
10104
8130
  }
10105
- editStylesRef.current.base.textContent = baseCss;
10106
- syncNavigationDragCursorAttrs();
10107
8131
  refreshStateRules();
10108
8132
  const handleClick = (e) => {
10109
- if (suppressNextClickRef.current || Date.now() < suppressClickUntilRef.current) {
10110
- suppressNextClickRef.current = false;
10111
- e.preventDefault();
10112
- e.stopPropagation();
10113
- return;
10114
- }
10115
8133
  const target = e.target;
10116
8134
  if (target.closest("[data-ohw-toolbar]")) return;
10117
8135
  if (target.closest("[data-ohw-state-toggle]")) return;
10118
8136
  if (target.closest("[data-ohw-max-badge]")) return;
10119
8137
  if (isInsideLinkEditor(target)) return;
10120
8138
  if (target.closest('[data-ohw-edit-chrome], [data-ohw-item-interaction], [data-ohw-drag-handle-container], [data-slot="drag-handle"]')) {
10121
- if (target.closest("[data-ohw-item-drag-surface]")) {
10122
- e.preventDefault();
10123
- e.stopPropagation();
10124
- const selected = selectedElRef.current;
10125
- if (selected && isNavigationItem(selected)) {
10126
- const editable2 = selected.querySelector('[data-ohw-editable="text"]') ?? selected.querySelector("[data-ohw-editable]");
10127
- if (editable2) {
10128
- activateRef.current(editable2, {
10129
- caretX: e.clientX,
10130
- caretY: e.clientY
10131
- });
10132
- }
10133
- } else if (selected?.hasAttribute("data-ohw-footer-col")) {
10134
- const link = listFooterLinksInColumn(selected).find((el) => {
10135
- const r2 = el.getBoundingClientRect();
10136
- return e.clientX >= r2.left && e.clientX <= r2.right && e.clientY >= r2.top && e.clientY <= r2.bottom;
10137
- });
10138
- if (link) selectRef.current(link);
10139
- }
10140
- return;
10141
- }
10142
8139
  const navFromChrome = resolveNavContainerSelectionTarget(target, e.clientX, e.clientY);
10143
8140
  if (navFromChrome) {
10144
8141
  e.preventDefault();
@@ -10146,45 +8143,10 @@ function OhhwellsBridge() {
10146
8143
  selectFrameRef.current(navFromChrome);
10147
8144
  return;
10148
8145
  }
10149
- const footerFromChrome = resolveFooterColumnSelectionTarget(target, e.clientX, e.clientY);
10150
- if (footerFromChrome) {
10151
- e.preventDefault();
10152
- e.stopPropagation();
10153
- selectFrameRef.current(footerFromChrome);
10154
- return;
10155
- }
10156
8146
  e.preventDefault();
10157
8147
  e.stopPropagation();
10158
8148
  return;
10159
8149
  }
10160
- if (activeElRef.current) {
10161
- const active = activeElRef.current;
10162
- const inActive = active === target || active.contains(target);
10163
- const navAnchor = getNavigationItemAnchor(active);
10164
- const inActiveNav = Boolean(
10165
- navAnchor && (navAnchor === target || navAnchor.contains(target))
10166
- );
10167
- if (inActive || inActiveNav) {
10168
- e.preventDefault();
10169
- e.stopPropagation();
10170
- const selection = window.getSelection();
10171
- const hasRangeSelection = Boolean(selection && !selection.isCollapsed);
10172
- console.log(
10173
- "[OHW DEBUG handleClick] already-active branch",
10174
- "detail=",
10175
- e.detail,
10176
- "hasRangeSelection=",
10177
- hasRangeSelection,
10178
- "willPlaceCaret=",
10179
- e.detail < 2 && !hasRangeSelection
10180
- );
10181
- if (e.detail < 2 && !hasRangeSelection) {
10182
- placeCaretAtPoint(active, e.clientX, e.clientY);
10183
- refreshActiveCommandsRef.current();
10184
- }
10185
- return;
10186
- }
10187
- }
10188
8150
  const editable = target.closest("[data-ohw-editable]");
10189
8151
  if (editable) {
10190
8152
  if (editable.dataset.ohwEditable === "link") {
@@ -10221,15 +8183,6 @@ function OhhwellsBridge() {
10221
8183
  }
10222
8184
  e.preventDefault();
10223
8185
  e.stopPropagation();
10224
- console.log(
10225
- "[OHW DEBUG handleClick] first-activation branch",
10226
- "detail=",
10227
- e.detail,
10228
- "selectionAtClick=",
10229
- JSON.stringify(window.getSelection()?.toString() ?? ""),
10230
- "collapsedAtClick=",
10231
- window.getSelection()?.isCollapsed
10232
- );
10233
8186
  activateRef.current(editable);
10234
8187
  return;
10235
8188
  }
@@ -10237,26 +8190,10 @@ function OhhwellsBridge() {
10237
8190
  if (hrefAnchor) {
10238
8191
  e.preventDefault();
10239
8192
  e.stopPropagation();
10240
- if (selectedElRef.current === hrefAnchor) {
10241
- const textEditable = hrefAnchor.querySelector('[data-ohw-editable="text"]') ?? hrefAnchor.querySelector("[data-ohw-editable]");
10242
- if (textEditable) {
10243
- activateRef.current(textEditable, {
10244
- caretX: e.clientX,
10245
- caretY: e.clientY
10246
- });
10247
- }
10248
- return;
10249
- }
8193
+ if (selectedElRef.current === hrefAnchor) return;
10250
8194
  selectRef.current(hrefAnchor);
10251
8195
  return;
10252
8196
  }
10253
- const footerColClick = target.closest("[data-ohw-footer-col]");
10254
- if (footerColClick) {
10255
- e.preventDefault();
10256
- e.stopPropagation();
10257
- selectFrameRef.current(footerColClick);
10258
- return;
10259
- }
10260
8197
  const navContainerToSelect = resolveNavContainerSelectionTarget(target, e.clientX, e.clientY);
10261
8198
  if (navContainerToSelect) {
10262
8199
  e.preventDefault();
@@ -10264,13 +8201,6 @@ function OhhwellsBridge() {
10264
8201
  selectFrameRef.current(navContainerToSelect);
10265
8202
  return;
10266
8203
  }
10267
- const footerColumnToSelect = resolveFooterColumnSelectionTarget(target, e.clientX, e.clientY);
10268
- if (footerColumnToSelect) {
10269
- e.preventDefault();
10270
- e.stopPropagation();
10271
- selectFrameRef.current(footerColumnToSelect);
10272
- return;
10273
- }
10274
8204
  const selectedContainer = selectedElRef.current;
10275
8205
  if (selectedContainer && isNavigationContainer(selectedContainer)) {
10276
8206
  const navItem = getNavigationItemAnchor(target);
@@ -10330,13 +8260,6 @@ function OhhwellsBridge() {
10330
8260
  const handleMouseOver = (e) => {
10331
8261
  if (document.documentElement.hasAttribute("data-ohw-section-picking")) return;
10332
8262
  const target = e.target;
10333
- if (linkPopoverOpenRef.current) {
10334
- hoveredItemElRef.current = null;
10335
- setHoveredItemRect(null);
10336
- hoveredNavContainerRef.current = null;
10337
- setHoveredNavContainerRect(null);
10338
- return;
10339
- }
10340
8263
  if (toolbarVariantRef.current !== "select-frame") {
10341
8264
  const navContainer = target.closest("[data-ohw-nav-container]");
10342
8265
  if (navContainer && !getNavigationItemAnchor(target)) {
@@ -10356,21 +8279,12 @@ function OhhwellsBridge() {
10356
8279
  hoveredNavContainerRef.current = null;
10357
8280
  setHoveredNavContainerRect(null);
10358
8281
  const selected2 = selectedElRef.current;
10359
- if (selected2 === navAnchor) return;
8282
+ if (selected2 === navAnchor || selected2?.contains(navAnchor)) return;
10360
8283
  clearHrefKeyHover(navAnchor);
10361
8284
  hoveredItemElRef.current = navAnchor;
10362
8285
  setHoveredItemRect(navAnchor.getBoundingClientRect());
10363
8286
  return;
10364
8287
  }
10365
- const footerCol = target.closest("[data-ohw-footer-col]") ?? target.closest("[data-ohw-footer-column]");
10366
- if (footerCol) {
10367
- hoveredNavContainerRef.current = null;
10368
- setHoveredNavContainerRect(null);
10369
- if (selectedElRef.current === footerCol) return;
10370
- hoveredItemElRef.current = footerCol;
10371
- setHoveredItemRect(footerCol.getBoundingClientRect());
10372
- return;
10373
- }
10374
8288
  const editable = target.closest("[data-ohw-editable]");
10375
8289
  if (!editable) return;
10376
8290
  const selected = selectedElRef.current;
@@ -10388,25 +8302,14 @@ function OhhwellsBridge() {
10388
8302
  };
10389
8303
  const handleMouseOut = (e) => {
10390
8304
  const target = e.target;
10391
- if (target.closest("[data-ohw-nav-container], [data-ohw-footer-col], [data-ohw-footer-column]")) {
8305
+ if (target.closest("[data-ohw-nav-container]")) {
10392
8306
  const related2 = e.relatedTarget instanceof Element ? e.relatedTarget : null;
10393
- 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]")) {
8307
+ if (related2?.closest("[data-ohw-nav-container], [data-ohw-navbar-container-chrome], [data-ohw-navbar-add-button]")) {
10394
8308
  return;
10395
8309
  }
10396
8310
  hoveredNavContainerRef.current = null;
10397
8311
  setHoveredNavContainerRect(null);
10398
8312
  }
10399
- const footerCol = target.closest("[data-ohw-footer-col]") ?? target.closest("[data-ohw-footer-column]");
10400
- if (footerCol && hoveredItemElRef.current === footerCol) {
10401
- const related2 = e.relatedTarget instanceof Element ? e.relatedTarget : null;
10402
- if (related2?.closest("[data-ohw-drag-handle-container], [data-ohw-item-interaction]")) return;
10403
- if (related2?.closest("[data-ohw-footer-col], [data-ohw-footer-column]") === footerCol) {
10404
- return;
10405
- }
10406
- hoveredItemElRef.current = null;
10407
- setHoveredItemRect(null);
10408
- return;
10409
- }
10410
8313
  const navAnchor = getNavigationItemAnchor(target);
10411
8314
  if (navAnchor) {
10412
8315
  const related2 = e.relatedTarget instanceof Element ? e.relatedTarget : null;
@@ -10540,75 +8443,43 @@ function OhhwellsBridge() {
10540
8443
  if (toolbarVariantRef.current === "select-frame") {
10541
8444
  hoveredNavContainerRef.current = null;
10542
8445
  setHoveredNavContainerRect(null);
8446
+ return;
10543
8447
  }
10544
- const navContainers = Array.from(
10545
- document.querySelectorAll("[data-ohw-nav-container]")
10546
- );
10547
- const footerColumns = listFooterColumns();
10548
- for (const container of [...navContainers, ...footerColumns]) {
10549
- const containerRect = container.getBoundingClientRect();
10550
- const overContainer = x >= containerRect.left && x <= containerRect.right && y >= containerRect.top && y <= containerRect.bottom;
10551
- if (!overContainer) continue;
10552
- const navItemHit = Array.from(
10553
- container.querySelectorAll("[data-ohw-href-key]")
10554
- ).find((el) => {
10555
- if (!isNavigationItem(el) || isNavbarButton2(el)) return false;
10556
- const itemRect = el.getBoundingClientRect();
10557
- return x >= itemRect.left && x <= itemRect.right && y >= itemRect.top && y <= itemRect.bottom;
10558
- });
10559
- if (navItemHit) {
10560
- hoveredNavContainerRef.current = null;
10561
- setHoveredNavContainerRect(null);
10562
- const selected = selectedElRef.current;
10563
- if (selected !== navItemHit) {
10564
- clearHrefKeyHover(navItemHit);
10565
- hoveredItemElRef.current = navItemHit;
10566
- setHoveredItemRect(navItemHit.getBoundingClientRect());
10567
- } else if (hoveredItemElRef.current === navItemHit) {
10568
- hoveredItemElRef.current = null;
10569
- setHoveredItemRect(null);
10570
- }
10571
- return;
10572
- }
10573
- }
10574
- if (toolbarVariantRef.current !== "select-frame") {
10575
- for (const container of navContainers) {
10576
- const containerRect = container.getBoundingClientRect();
10577
- const overContainer = x >= containerRect.left && x <= containerRect.right && y >= containerRect.top && y <= containerRect.bottom;
10578
- if (!overContainer) continue;
10579
- if (isPointOverNavItem(container, x, y)) continue;
10580
- hoveredNavContainerRef.current = container;
10581
- setHoveredNavContainerRect(container.getBoundingClientRect());
10582
- hoveredItemElRef.current = null;
10583
- setHoveredItemRect(null);
10584
- return;
10585
- }
8448
+ const navContainer = document.querySelector("[data-ohw-nav-container]");
8449
+ if (!navContainer) {
10586
8450
  hoveredNavContainerRef.current = null;
10587
8451
  setHoveredNavContainerRect(null);
10588
- } else {
8452
+ return;
8453
+ }
8454
+ const containerRect = navContainer.getBoundingClientRect();
8455
+ const overContainer = x >= containerRect.left && x <= containerRect.right && y >= containerRect.top && y <= containerRect.bottom;
8456
+ if (!overContainer) {
10589
8457
  hoveredNavContainerRef.current = null;
10590
8458
  setHoveredNavContainerRect(null);
8459
+ return;
10591
8460
  }
10592
- for (const column of footerColumns) {
10593
- const containerRect = column.getBoundingClientRect();
10594
- const overContainer = x >= containerRect.left && x <= containerRect.right && y >= containerRect.top && y <= containerRect.bottom;
10595
- if (!overContainer) continue;
10596
- if (isPointOverNavItem(column, x, y)) continue;
10597
- if (selectedElRef.current === column) {
10598
- if (hoveredItemElRef.current === column) {
10599
- hoveredItemElRef.current = null;
10600
- setHoveredItemRect(null);
10601
- }
10602
- return;
8461
+ const navItemHit = Array.from(
8462
+ navContainer.querySelectorAll("[data-ohw-href-key]")
8463
+ ).find((el) => {
8464
+ if (!isNavigationItem(el) || isNavbarButton2(el)) return false;
8465
+ const itemRect = el.getBoundingClientRect();
8466
+ return x >= itemRect.left && x <= itemRect.right && y >= itemRect.top && y <= itemRect.bottom;
8467
+ });
8468
+ if (navItemHit) {
8469
+ hoveredNavContainerRef.current = null;
8470
+ setHoveredNavContainerRect(null);
8471
+ const selected = selectedElRef.current;
8472
+ if (selected !== navItemHit && !selected?.contains(navItemHit)) {
8473
+ clearHrefKeyHover(navItemHit);
8474
+ hoveredItemElRef.current = navItemHit;
8475
+ setHoveredItemRect(navItemHit.getBoundingClientRect());
10603
8476
  }
10604
- hoveredItemElRef.current = column;
10605
- setHoveredItemRect(column.getBoundingClientRect());
10606
8477
  return;
10607
8478
  }
10608
- if (hoveredItemElRef.current?.hasAttribute("data-ohw-footer-col") || hoveredItemElRef.current?.hasAttribute("data-ohw-footer-column")) {
10609
- hoveredItemElRef.current = null;
10610
- setHoveredItemRect(null);
10611
- }
8479
+ hoveredNavContainerRef.current = navContainer;
8480
+ setHoveredNavContainerRect(containerRect);
8481
+ hoveredItemElRef.current = null;
8482
+ setHoveredItemRect(null);
10612
8483
  };
10613
8484
  const probeImageAt = (clientX, clientY, isDragOver = false, fromParentViewport = false) => {
10614
8485
  if (linkPopoverOpenRef.current) {
@@ -10629,10 +8500,6 @@ function OhhwellsBridge() {
10629
8500
  }
10630
8501
  hoveredNavContainerRef.current = null;
10631
8502
  setHoveredNavContainerRect(null);
10632
- if (hoveredItemElRef.current?.hasAttribute("data-ohw-footer-col") || hoveredItemElRef.current?.hasAttribute("data-ohw-footer-column")) {
10633
- hoveredItemElRef.current = null;
10634
- setHoveredItemRect(null);
10635
- }
10636
8503
  const toggleEl = document.querySelector("[data-ohw-state-toggle]");
10637
8504
  if (toggleEl) {
10638
8505
  const tr = toggleEl.getBoundingClientRect();
@@ -10880,15 +8747,6 @@ function OhhwellsBridge() {
10880
8747
  probeHoverCardsAt(clientX, clientY);
10881
8748
  };
10882
8749
  const handleDragOver = (e) => {
10883
- const footerSession = footerDragRef.current;
10884
- if (footerSession) {
10885
- e.preventDefault();
10886
- if (e.dataTransfer) e.dataTransfer.dropEffect = "move";
10887
- const slot = footerSession.kind === "link" && footerSession.hrefKey ? hitTestLinkDropSlot(e.clientX, e.clientY, footerSession.hrefKey) : hitTestColumnDropSlot(e.clientX, e.clientY);
10888
- refreshFooterDragVisualsRef.current(footerSession, slot, e.clientX, e.clientY);
10889
- return;
10890
- }
10891
- if (onNavDragOver(e)) return;
10892
8750
  e.preventDefault();
10893
8751
  const el = findImageAtPoint(e.clientX, e.clientY, false);
10894
8752
  if (!el) {
@@ -11051,8 +8909,8 @@ function OhhwellsBridge() {
11051
8909
  if (sibling !== el) sibling.innerHTML = html;
11052
8910
  });
11053
8911
  const timers = autoSaveTimers.current;
11054
- const existing2 = timers.get(key);
11055
- if (existing2) clearTimeout(existing2);
8912
+ const existing = timers.get(key);
8913
+ if (existing) clearTimeout(existing);
11056
8914
  timers.set(key, setTimeout(() => {
11057
8915
  timers.delete(key);
11058
8916
  postToParentRef.current({ type: "ow:change", nodes: [{ key, text: html }] });
@@ -11095,7 +8953,6 @@ function OhhwellsBridge() {
11095
8953
  }
11096
8954
  editContentRef.current = { ...editContentRef.current, ...content };
11097
8955
  reconcileNavbarItemsFromContent(editContentRef.current);
11098
- syncNavigationDragCursorAttrs();
11099
8956
  enforceLinkHrefs();
11100
8957
  postToParentRef.current({ type: "ow:hydrate-done" });
11101
8958
  };
@@ -11116,48 +8973,6 @@ function OhhwellsBridge() {
11116
8973
  if (e.data?.type !== "ui:escape") return;
11117
8974
  if (linkPopoverOpenRef.current) {
11118
8975
  setLinkPopoverRef.current(null);
11119
- return;
11120
- }
11121
- if (activeElRef.current) {
11122
- const hrefCtx = getHrefKeyFromElement(activeElRef.current);
11123
- const navAnchor = hrefCtx ? getNavigationItemAnchor(hrefCtx.anchor) : null;
11124
- if (navAnchor) {
11125
- const el = activeElRef.current;
11126
- if (originalContentRef.current !== null) {
11127
- el.innerHTML = originalContentRef.current;
11128
- }
11129
- el.removeAttribute("contenteditable");
11130
- el.removeAttribute("data-ohw-editing");
11131
- activeElRef.current = null;
11132
- setMaxBadge(null);
11133
- setActiveCommands(/* @__PURE__ */ new Set());
11134
- setToolbarShowEditLink(false);
11135
- postToParentRef.current({ type: "ow:exit-edit" });
11136
- reselectNavigationItemRef.current(navAnchor);
11137
- return;
11138
- }
11139
- if (originalContentRef.current !== null) {
11140
- activeElRef.current.innerHTML = originalContentRef.current;
11141
- const key = activeElRef.current.dataset.ohwKey;
11142
- if (key) {
11143
- postToParentRef.current({ type: "ow:change", nodes: [{ key, text: originalContentRef.current }] });
11144
- }
11145
- }
11146
- deselectRef.current();
11147
- deactivateRef.current();
11148
- return;
11149
- }
11150
- if (selectedElRef.current) {
11151
- if (toolbarVariantRef.current === "select-frame") {
11152
- deselectRef.current();
11153
- return;
11154
- }
11155
- const parent = getNavigationSelectionParent(selectedElRef.current);
11156
- if (parent && isNavigationContainer(parent)) {
11157
- selectFrameRef.current(parent);
11158
- } else {
11159
- deselectRef.current();
11160
- }
11161
8976
  }
11162
8977
  };
11163
8978
  window.addEventListener("message", handleUiEscape);
@@ -11182,7 +8997,7 @@ function OhhwellsBridge() {
11182
8997
  return;
11183
8998
  }
11184
8999
  const parent = getNavigationSelectionParent(selectedElRef.current);
11185
- if (parent && isNavigationContainer(parent)) {
9000
+ if (parent) {
11186
9001
  e.preventDefault();
11187
9002
  selectFrameRef.current(parent);
11188
9003
  } else {
@@ -11200,7 +9015,6 @@ function OhhwellsBridge() {
11200
9015
  el2.innerHTML = originalContentRef.current;
11201
9016
  }
11202
9017
  el2.removeAttribute("contenteditable");
11203
- el2.removeAttribute("data-ohw-editing");
11204
9018
  activeElRef.current = null;
11205
9019
  setMaxBadge(null);
11206
9020
  setActiveCommands(/* @__PURE__ */ new Set());
@@ -11252,26 +9066,6 @@ function OhhwellsBridge() {
11252
9066
  if (siblingHintElRef.current) {
11253
9067
  setSiblingHintRect(siblingHintElRef.current.getBoundingClientRect());
11254
9068
  }
11255
- const selected = selectedElRef.current;
11256
- if (selected && !footerDragRef.current && !navDragRef.current) {
11257
- if (selected.hasAttribute("data-ohw-footer-col") || Boolean(selected.closest("footer") && isInferredFooterGroup(selected))) {
11258
- setSiblingHintRects(
11259
- listFooterColumns().filter((column) => column !== selected).map((column) => column.getBoundingClientRect())
11260
- );
11261
- } else if (isNavigationItem(selected)) {
11262
- setSiblingHintRects(collectNavigationItemSiblingHintRects(selected));
11263
- }
11264
- }
11265
- if (footerDragRef.current) {
11266
- const session = footerDragRef.current;
11267
- const slot = session.kind === "link" && session.hrefKey ? hitTestLinkDropSlot(session.lastClientX, session.lastClientY, session.hrefKey) : hitTestColumnDropSlot(session.lastClientX, session.lastClientY);
11268
- refreshFooterDragVisualsRef.current(session, slot, session.lastClientX, session.lastClientY);
11269
- }
11270
- if (navDragRef.current) {
11271
- const session = navDragRef.current;
11272
- const slot = hitTestNavDropSlot(session.lastClientX, session.lastClientY, session.hrefKey);
11273
- refreshNavDragVisualsRef.current(session, slot, session.lastClientX, session.lastClientY);
11274
- }
11275
9069
  if (hoveredImageRef.current) {
11276
9070
  const el = hoveredImageRef.current;
11277
9071
  const r2 = el.getBoundingClientRect();
@@ -11282,7 +9076,7 @@ function OhhwellsBridge() {
11282
9076
  };
11283
9077
  const handleSave = (e) => {
11284
9078
  if (e.data?.type !== "ow:save") return;
11285
- const nodes = collectEditableNodes(editContentRef.current);
9079
+ const nodes = collectEditableNodes();
11286
9080
  const tracker = document.querySelector("[data-ohw-sections-tracker]");
11287
9081
  if (tracker?.textContent) nodes.push({ key: "__ohw_sections", type: "sections", text: tracker.textContent });
11288
9082
  postToParentRef.current({ type: "ow:save-result", nodes });
@@ -11340,83 +9134,30 @@ function OhhwellsBridge() {
11340
9134
  const h = document.documentElement.scrollHeight;
11341
9135
  if (h > 50) postToParentRef.current({ type: "ow:height", height: h });
11342
9136
  };
11343
- let selectionChangeRaf = null;
11344
- const runSelectionChange = () => {
11345
- selectionChangeRaf = null;
11346
- const activeRoot = activeElRef.current;
11347
- if (!activeRoot) return;
9137
+ const handleSelectionChange = () => {
9138
+ if (!activeElRef.current) return;
11348
9139
  const next = /* @__PURE__ */ new Set();
11349
- const sel = window.getSelection();
11350
- let startNode = null;
11351
- if (sel && sel.rangeCount > 0) {
11352
- const range = sel.getRangeAt(0);
11353
- startNode = range.startContainer;
11354
- if (startNode.nodeType === Node.ELEMENT_NODE) {
11355
- const el = startNode;
11356
- startNode = el.childNodes[range.startOffset] ?? el.childNodes[range.startOffset - 1] ?? el;
9140
+ for (const cmd of ["bold", "italic", "underline", "strikeThrough", "insertUnorderedList", "insertOrderedList"]) {
9141
+ try {
9142
+ if (document.queryCommandState(cmd)) next.add(cmd);
9143
+ } catch {
11357
9144
  }
11358
9145
  }
11359
- const startEl = startNode ? startNode.nodeType === Node.TEXT_NODE ? startNode.parentElement : startNode : null;
11360
- if (startEl) {
11361
- const cs = getComputedStyle(startEl);
11362
- const weight = parseInt(cs.fontWeight, 10);
11363
- let hasBold = cs.fontWeight === "bold" || !Number.isNaN(weight) && weight >= 600;
11364
- let hasItalic = cs.fontStyle === "italic" || cs.fontStyle === "oblique";
11365
- let hasUnderline = false;
11366
- let hasStrike = false;
11367
- let hasUl = false;
11368
- let hasOl = false;
11369
- for (let el = startEl; el; el = el.parentElement) {
11370
- if (el.tagName === "B" || el.tagName === "STRONG") hasBold = true;
11371
- if (el.tagName === "I" || el.tagName === "EM") hasItalic = true;
11372
- const decoration = getComputedStyle(el).textDecorationLine;
11373
- if (decoration.includes("underline") || el.tagName === "U" || el.tagName === "INS") hasUnderline = true;
11374
- if (decoration.includes("line-through") || el.tagName === "S" || el.tagName === "STRIKE" || el.tagName === "DEL") hasStrike = true;
11375
- if (el.tagName === "UL") hasUl = true;
11376
- if (el.tagName === "OL") hasOl = true;
11377
- if (el === activeRoot) break;
11378
- }
11379
- if (hasBold) next.add("bold");
11380
- if (hasItalic) next.add("italic");
11381
- if (hasUnderline) next.add("underline");
11382
- if (hasStrike) next.add("strikeThrough");
11383
- if (hasUl) next.add("insertUnorderedList");
11384
- if (hasOl) next.add("insertOrderedList");
11385
- const block = startEl.closest("div, p, h1, h2, h3, h4, h5, h6, li, td, th") ?? startEl;
11386
- const align = getComputedStyle(block).textAlign;
11387
- if (align === "center") next.add("justifyCenter");
11388
- else if (align === "right" || align === "end") next.add("justifyRight");
11389
- else next.add("justifyLeft");
11390
- }
11391
- setActiveCommands((prev) => {
11392
- if (prev.size === next.size) {
11393
- let same = true;
11394
- for (const cmd of prev) {
11395
- if (!next.has(cmd)) {
11396
- same = false;
11397
- break;
11398
- }
11399
- }
11400
- if (same) return prev;
9146
+ const sel = window.getSelection();
9147
+ const anchor = sel?.anchorNode;
9148
+ if (anchor) {
9149
+ const el = anchor.nodeType === Node.TEXT_NODE ? anchor.parentElement : anchor;
9150
+ const block = el?.closest("div, p, h1, h2, h3, h4, h5, h6, li, td, th") ?? el;
9151
+ if (block) {
9152
+ const align = getComputedStyle(block).textAlign;
9153
+ if (align === "center") next.add("justifyCenter");
9154
+ else if (align === "right" || align === "end") next.add("justifyRight");
9155
+ else next.add("justifyLeft");
11401
9156
  }
11402
- return next;
11403
- });
11404
- };
11405
- const handleSelectionChange = () => {
11406
- if (pointerHeldRef.current) return;
11407
- if (selectionChangeRaf !== null) return;
11408
- selectionChangeRaf = requestAnimationFrame(runSelectionChange);
11409
- };
11410
- const markPointerHeld = (e) => {
11411
- if (e.button !== 0) return;
11412
- pointerHeldRef.current = true;
11413
- };
11414
- const markPointerReleased = () => {
11415
- if (!pointerHeldRef.current) return;
11416
- pointerHeldRef.current = false;
11417
- handleSelectionChange();
9157
+ }
9158
+ setActiveCommands(next);
11418
9159
  };
11419
- refreshActiveCommandsRef.current = runSelectionChange;
9160
+ refreshActiveCommandsRef.current = handleSelectionChange;
11420
9161
  const handleDocMouseLeave = () => {
11421
9162
  hoveredImageRef.current = null;
11422
9163
  setMediaHover(null);
@@ -11461,15 +9202,15 @@ function OhhwellsBridge() {
11461
9202
  };
11462
9203
  const applyToolbarPos = (rect) => {
11463
9204
  const ps = parentScrollRef.current;
11464
- const measuredW = toolbarElRef.current?.offsetWidth || 330;
9205
+ const approxW = 330;
11465
9206
  if (toolbarElRef.current) {
11466
- const { top, left, transform } = calcToolbarPos(rect, ps, measuredW);
9207
+ const { top, left, transform } = calcToolbarPos(rect, ps, approxW);
11467
9208
  toolbarElRef.current.style.top = `${top}px`;
11468
9209
  toolbarElRef.current.style.left = `${left}px`;
11469
9210
  toolbarElRef.current.style.transform = transform;
11470
9211
  }
11471
9212
  if (glowElRef.current) {
11472
- const GAP = SELECTION_CHROME_GAP2;
9213
+ const GAP = 6;
11473
9214
  glowElRef.current.style.top = `${rect.top - GAP}px`;
11474
9215
  glowElRef.current.style.left = `${rect.left - GAP}px`;
11475
9216
  glowElRef.current.style.width = `${rect.width + GAP * 2}px`;
@@ -11557,11 +9298,6 @@ function OhhwellsBridge() {
11557
9298
  return;
11558
9299
  }
11559
9300
  }
11560
- const footerColumn = findHoveredNavOrFooterContainer(clientX, clientY);
11561
- if (footerColumn?.hasAttribute("data-ohw-footer-col") || footerColumn?.hasAttribute("data-ohw-footer-column")) {
11562
- selectFrameRef.current(footerColumn);
11563
- return;
11564
- }
11565
9301
  deactivateRef.current();
11566
9302
  };
11567
9303
  window.addEventListener("message", handleSave);
@@ -11633,177 +9369,7 @@ function OhhwellsBridge() {
11633
9369
  if (imageShowTimerRef.current) clearTimeout(imageShowTimerRef.current);
11634
9370
  };
11635
9371
  }, [isEditMode, refreshStateRules]);
11636
- useEffect8(() => {
11637
- if (!isEditMode) return;
11638
- const THRESHOLD = 10;
11639
- const resolveWasSelected = (el) => {
11640
- if (selectedElRef.current === el) return true;
11641
- const activeAnchor = activeElRef.current ? getNavigationItemAnchor(activeElRef.current) : null;
11642
- return activeAnchor === el;
11643
- };
11644
- const onPointerDown = (e) => {
11645
- if (e.button !== 0) return;
11646
- if (suppressNextClickRef.current && Date.now() >= suppressClickUntilRef.current && !footerDragRef.current && !footerPointerDragRef.current?.started) {
11647
- suppressNextClickRef.current = false;
11648
- }
11649
- if (footerDragRef.current) return;
11650
- const target = e.target;
11651
- if (!target) return;
11652
- 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]')) {
11653
- return;
11654
- }
11655
- if (target.closest("[data-ohw-item-drag-surface]")) return;
11656
- const anchor = getNavigationItemAnchor(target);
11657
- const hrefKey = anchor?.getAttribute("data-ohw-href-key") ?? null;
11658
- if (anchor && isFooterHrefKey(hrefKey)) {
11659
- footerPointerDragRef.current = {
11660
- el: anchor,
11661
- kind: "link",
11662
- startX: e.clientX,
11663
- startY: e.clientY,
11664
- pointerId: e.pointerId,
11665
- wasSelected: resolveWasSelected(anchor),
11666
- started: false
11667
- };
11668
- return;
11669
- }
11670
- const col = target.closest("[data-ohw-footer-col]");
11671
- if (col && !getNavigationItemAnchor(target)) {
11672
- footerPointerDragRef.current = {
11673
- el: col,
11674
- kind: "column",
11675
- startX: e.clientX,
11676
- startY: e.clientY,
11677
- pointerId: e.pointerId,
11678
- wasSelected: resolveWasSelected(col),
11679
- started: false
11680
- };
11681
- }
11682
- };
11683
- const onPointerMove = (e) => {
11684
- const pending = footerPointerDragRef.current;
11685
- if (!pending) return;
11686
- if (pending.started) {
11687
- e.preventDefault();
11688
- clearTextSelection();
11689
- const session = footerDragRef.current;
11690
- if (!session) return;
11691
- const slot = session.kind === "link" && session.hrefKey ? hitTestLinkDropSlot(e.clientX, e.clientY, session.hrefKey) : hitTestColumnDropSlot(e.clientX, e.clientY);
11692
- refreshFooterDragVisualsRef.current(session, slot, e.clientX, e.clientY);
11693
- return;
11694
- }
11695
- const dx = e.clientX - pending.startX;
11696
- const dy = e.clientY - pending.startY;
11697
- if (dx * dx + dy * dy < THRESHOLD * THRESHOLD) return;
11698
- e.preventDefault();
11699
- pending.started = true;
11700
- armFooterPressDrag();
11701
- clearTextSelection();
11702
- try {
11703
- document.body.setPointerCapture(pending.pointerId);
11704
- } catch {
11705
- }
11706
- if (linkPopoverOpenRef.current) {
11707
- setLinkPopoverRef.current(null);
11708
- }
11709
- if (activeElRef.current) {
11710
- deactivateRef.current();
11711
- }
11712
- if (pending.kind === "link") {
11713
- const key = pending.el.getAttribute("data-ohw-href-key");
11714
- if (!key) return;
11715
- const column = findFooterColumnForLink(pending.el);
11716
- const columns2 = listFooterColumns();
11717
- beginFooterDragRef.current({
11718
- kind: "link",
11719
- hrefKey: key,
11720
- columnEl: column,
11721
- sourceColumnIndex: column ? columns2.indexOf(column) : 0,
11722
- wasSelected: pending.wasSelected,
11723
- draggedEl: pending.el,
11724
- lastClientX: e.clientX,
11725
- lastClientY: e.clientY,
11726
- activeSlot: null
11727
- });
11728
- return;
11729
- }
11730
- const columns = listFooterColumns();
11731
- const idx = columns.indexOf(pending.el);
11732
- if (idx < 0) return;
11733
- beginFooterDragRef.current({
11734
- kind: "column",
11735
- hrefKey: null,
11736
- columnEl: pending.el,
11737
- sourceColumnIndex: idx,
11738
- wasSelected: pending.wasSelected,
11739
- draggedEl: pending.el,
11740
- lastClientX: e.clientX,
11741
- lastClientY: e.clientY,
11742
- activeSlot: null
11743
- });
11744
- };
11745
- const endPointerDrag = (e) => {
11746
- const pending = footerPointerDragRef.current;
11747
- footerPointerDragRef.current = null;
11748
- try {
11749
- if (document.body.hasPointerCapture(e.pointerId)) {
11750
- document.body.releasePointerCapture(e.pointerId);
11751
- }
11752
- } catch {
11753
- }
11754
- if (!pending) return;
11755
- if (!pending.started) {
11756
- unlockFooterDragInteraction();
11757
- return;
11758
- }
11759
- suppressNextClickRef.current = true;
11760
- suppressClickUntilRef.current = Date.now() + 500;
11761
- commitFooterDragRef.current(e.clientX, e.clientY);
11762
- };
11763
- const blockSelectStart = (e) => {
11764
- if (footerDragRef.current || footerPointerDragRef.current?.started || document.documentElement.hasAttribute("data-ohw-footer-press-drag")) {
11765
- e.preventDefault();
11766
- }
11767
- };
11768
- const onDragEnd = (_e) => {
11769
- if (!footerDragRef.current) return;
11770
- suppressNextClickRef.current = true;
11771
- suppressClickUntilRef.current = Date.now() + 500;
11772
- commitFooterDragRef.current();
11773
- };
11774
- document.addEventListener("pointerdown", onPointerDown, true);
11775
- document.addEventListener("pointermove", onPointerMove, true);
11776
- document.addEventListener("pointerup", endPointerDrag, true);
11777
- document.addEventListener("pointercancel", endPointerDrag, true);
11778
- document.addEventListener("selectstart", blockSelectStart, true);
11779
- document.addEventListener("dragend", onDragEnd, true);
11780
- return () => {
11781
- document.removeEventListener("pointerdown", onPointerDown, true);
11782
- document.removeEventListener("pointermove", onPointerMove, true);
11783
- document.removeEventListener("pointerup", endPointerDrag, true);
11784
- document.removeEventListener("pointercancel", endPointerDrag, true);
11785
- document.removeEventListener("selectstart", blockSelectStart, true);
11786
- document.removeEventListener("dragend", onDragEnd, true);
11787
- unlockFooterDragInteraction();
11788
- };
11789
- }, [isEditMode]);
11790
- useEffect8(() => {
11791
- if (!isEditMode) return;
11792
- const debugSelectionChange = () => {
11793
- const sel = window.getSelection();
11794
- console.log(
11795
- "[OHW DEBUG selectionchange]",
11796
- JSON.stringify(sel?.toString() ?? ""),
11797
- "collapsed=",
11798
- sel?.isCollapsed,
11799
- "anchorNode=",
11800
- sel?.anchorNode,
11801
- "activeEl=",
11802
- activeElRef.current
11803
- );
11804
- };
11805
- }, [isEditMode]);
11806
- useEffect8(() => {
9372
+ useEffect7(() => {
11807
9373
  const handler = (e) => {
11808
9374
  if (e.data?.type !== "ow:request-schedule-config") return;
11809
9375
  const insertAfterVal = e.data.insertAfter;
@@ -11819,7 +9385,7 @@ function OhhwellsBridge() {
11819
9385
  window.addEventListener("message", handler);
11820
9386
  return () => window.removeEventListener("message", handler);
11821
9387
  }, [processConfigRequest]);
11822
- useEffect8(() => {
9388
+ useEffect7(() => {
11823
9389
  if (!isEditMode) return;
11824
9390
  document.querySelectorAll("[data-ohw-active-state]").forEach((el) => {
11825
9391
  el.removeAttribute("data-ohw-active-state");
@@ -11840,13 +9406,7 @@ function OhhwellsBridge() {
11840
9406
  const next = { ...prev, [pathKey]: sections };
11841
9407
  return shouldUseDevFixtures() ? { ...DEV_SECTIONS_BY_PATH, ...next } : next;
11842
9408
  });
11843
- postToParent2({
11844
- type: "ow:ready",
11845
- version: "1",
11846
- path: pathname,
11847
- nodes: collectEditableNodes(editContentRef.current),
11848
- sections
11849
- });
9409
+ postToParent2({ type: "ow:ready", version: "1", path: pathname, nodes: collectEditableNodes(), sections });
11850
9410
  postToParent2({ type: "ow:request-site-pages" });
11851
9411
  }, 150);
11852
9412
  return () => {
@@ -11854,20 +9414,19 @@ function OhhwellsBridge() {
11854
9414
  clearTimeout(timer);
11855
9415
  };
11856
9416
  }, [pathname, isEditMode, refreshStateRules, postToParent2]);
11857
- useEffect8(() => {
9417
+ useEffect7(() => {
11858
9418
  scrollToHashSectionWhenReady();
11859
9419
  const onHashChange = () => scrollToHashSectionWhenReady();
11860
9420
  window.addEventListener("hashchange", onHashChange);
11861
9421
  return () => window.removeEventListener("hashchange", onHashChange);
11862
9422
  }, [pathname]);
11863
- const handleCommand = useCallback5((cmd) => {
11864
- document.execCommand("styleWithCSS", false, true);
9423
+ const handleCommand = useCallback3((cmd) => {
11865
9424
  document.execCommand(cmd, false);
11866
9425
  activeElRef.current?.focus();
11867
9426
  if (activeElRef.current) setToolbarRect(getEditMeasureEl(activeElRef.current).getBoundingClientRect());
11868
9427
  refreshActiveCommandsRef.current();
11869
9428
  }, []);
11870
- const handleStateChange = useCallback5((state) => {
9429
+ const handleStateChange = useCallback3((state) => {
11871
9430
  if (!activeStateElRef.current) return;
11872
9431
  const el = activeStateElRef.current;
11873
9432
  if (state === "Default") {
@@ -11880,11 +9439,11 @@ function OhhwellsBridge() {
11880
9439
  }
11881
9440
  setToggleState((prev) => prev ? { ...prev, activeState: state } : null);
11882
9441
  }, [deactivate]);
11883
- const closeLinkPopover = useCallback5(() => {
9442
+ const closeLinkPopover = useCallback3(() => {
11884
9443
  addNavAfterAnchorRef.current = null;
11885
9444
  setLinkPopover(null);
11886
9445
  }, []);
11887
- const openLinkPopoverForActive = useCallback5(() => {
9446
+ const openLinkPopoverForActive = useCallback3(() => {
11888
9447
  const hrefCtx = getHrefKeyFromElement(activeElRef.current);
11889
9448
  if (!hrefCtx) return;
11890
9449
  bumpLinkPopoverGrace();
@@ -11895,7 +9454,7 @@ function OhhwellsBridge() {
11895
9454
  });
11896
9455
  deactivate();
11897
9456
  }, [deactivate]);
11898
- const openLinkPopoverForSelected = useCallback5(() => {
9457
+ const openLinkPopoverForSelected = useCallback3(() => {
11899
9458
  const anchor = selectedElRef.current;
11900
9459
  if (!anchor) return;
11901
9460
  const key = anchor.getAttribute("data-ohw-href-key");
@@ -11908,7 +9467,7 @@ function OhhwellsBridge() {
11908
9467
  });
11909
9468
  deselect();
11910
9469
  }, [deselect]);
11911
- const handleLinkPopoverSubmit = useCallback5(
9470
+ const handleLinkPopoverSubmit = useCallback3(
11912
9471
  (target) => {
11913
9472
  const session = linkPopoverSessionRef.current;
11914
9473
  if (!session) return;
@@ -11967,13 +9526,13 @@ function OhhwellsBridge() {
11967
9526
  const showEditLink = toolbarShowEditLink;
11968
9527
  const currentSections = sectionsByPath[pathname] ?? [];
11969
9528
  linkPopoverOpenRef.current = linkPopover !== null;
11970
- const handleMediaReplace = useCallback5(
9529
+ const handleMediaReplace = useCallback3(
11971
9530
  (key) => {
11972
9531
  postToParent2({ type: "ow:image-pick", key, elementType: mediaHover?.elementType ?? "image" });
11973
9532
  },
11974
9533
  [postToParent2, mediaHover?.elementType]
11975
9534
  );
11976
- const handleMediaFadeOutComplete = useCallback5((key) => {
9535
+ const handleMediaFadeOutComplete = useCallback3((key) => {
11977
9536
  setUploadingRects((prev) => {
11978
9537
  if (!(key in prev)) return prev;
11979
9538
  const next = { ...prev };
@@ -11981,7 +9540,7 @@ function OhhwellsBridge() {
11981
9540
  return next;
11982
9541
  });
11983
9542
  }, []);
11984
- const handleVideoSettingsChange = useCallback5(
9543
+ const handleVideoSettingsChange = useCallback3(
11985
9544
  (key, settings) => {
11986
9545
  document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
11987
9546
  const video = getVideoEl(el);
@@ -12005,8 +9564,8 @@ function OhhwellsBridge() {
12005
9564
  );
12006
9565
  return bridgeRoot ? createPortal2(
12007
9566
  /* @__PURE__ */ jsxs13(Fragment5, { children: [
12008
- /* @__PURE__ */ jsx24("div", { ref: attachVisibleViewport, "data-ohw-visible-viewport": "", "aria-hidden": true }),
12009
- Object.entries(uploadingRects).map(([key, { rect, fadingOut }]) => /* @__PURE__ */ jsx24(
9567
+ /* @__PURE__ */ jsx23("div", { ref: attachVisibleViewport, "data-ohw-visible-viewport": "", "aria-hidden": true }),
9568
+ Object.entries(uploadingRects).map(([key, { rect, fadingOut }]) => /* @__PURE__ */ jsx23(
12010
9569
  MediaOverlay,
12011
9570
  {
12012
9571
  hover: { key, rect, elementType: "image", isDragOver: false, hasTextOverlap: false },
@@ -12017,7 +9576,7 @@ function OhhwellsBridge() {
12017
9576
  },
12018
9577
  `uploading-${key}`
12019
9578
  )),
12020
- mediaHover && !(mediaHover.key in uploadingRects) && /* @__PURE__ */ jsx24(
9579
+ mediaHover && !(mediaHover.key in uploadingRects) && /* @__PURE__ */ jsx23(
12021
9580
  MediaOverlay,
12022
9581
  {
12023
9582
  hover: mediaHover,
@@ -12026,93 +9585,49 @@ function OhhwellsBridge() {
12026
9585
  onVideoSettingsChange: handleVideoSettingsChange
12027
9586
  }
12028
9587
  ),
12029
- siblingHintRect && !linkPopover && !isItemDragging && siblingHintRects.length === 0 && /* @__PURE__ */ jsx24(ItemInteractionLayer, { rect: siblingHintRect, state: "sibling-hint" }),
12030
- siblingHintRects.length > 0 && !linkPopover && siblingHintRects.map((rect, i) => /* @__PURE__ */ jsx24(ItemInteractionLayer, { rect, state: "sibling-hint" }, `sibling-hint-${i}`)),
12031
- isItemDragging && draggedItemRect && selectedElRef.current !== footerDragRef.current?.draggedEl && selectedElRef.current !== navDragRef.current?.draggedEl && /* @__PURE__ */ jsx24(ItemInteractionLayer, { rect: draggedItemRect, state: "dragging" }),
12032
- isItemDragging && footerDropSlots.map((slot, i) => /* @__PURE__ */ jsx24(
12033
- "div",
12034
- {
12035
- className: "pointer-events-none fixed z-2147483646",
12036
- style: {
12037
- left: slot.left,
12038
- top: slot.top,
12039
- width: slot.width,
12040
- height: slot.height
12041
- },
12042
- children: /* @__PURE__ */ jsx24(
12043
- DropIndicator,
12044
- {
12045
- direction: slot.direction,
12046
- state: activeFooterDropIndex === i ? "dragActive" : "dragIdle",
12047
- className: "!h-full !w-full"
12048
- }
12049
- )
12050
- },
12051
- `footer-drop-${slot.direction}-${slot.columnIndex}-${slot.insertIndex}-${i}`
12052
- )),
12053
- isItemDragging && navDropSlots.map((slot, i) => /* @__PURE__ */ jsx24(
12054
- "div",
9588
+ siblingHintRect && !isItemDragging && /* @__PURE__ */ jsx23(ItemInteractionLayer, { rect: siblingHintRect, state: "sibling-hint" }),
9589
+ hoveredNavContainerRect && toolbarVariant !== "select-frame" && !linkPopover && !isItemDragging && !siblingHintRect && /* @__PURE__ */ jsx23(ItemInteractionLayer, { rect: hoveredNavContainerRect, state: "hover" }),
9590
+ toolbarVariant === "select-frame" && toolbarRect && !linkPopover && !isItemDragging && /* @__PURE__ */ jsx23(
9591
+ NavbarContainerChrome,
12055
9592
  {
12056
- className: "pointer-events-none fixed z-2147483646",
12057
- style: {
12058
- left: slot.left,
12059
- top: slot.top,
12060
- width: slot.width,
12061
- height: slot.height
12062
- },
12063
- children: /* @__PURE__ */ jsx24(
12064
- DropIndicator,
12065
- {
12066
- direction: slot.direction,
12067
- state: activeNavDropIndex === i ? "dragActive" : "dragIdle",
12068
- className: "!h-full !w-full"
12069
- }
12070
- )
12071
- },
12072
- `nav-drop-${slot.direction}-${slot.parentId ?? "root"}-${slot.insertIndex}-${i}`
12073
- )),
12074
- hoveredNavContainerRect && toolbarVariant !== "select-frame" && !linkPopover && !isItemDragging && /* @__PURE__ */ jsx24(ItemInteractionLayer, { rect: hoveredNavContainerRect, state: "hover" }),
12075
- hoveredItemRect && !linkPopover && !hoveredNavContainerRect && !isItemDragging && hoveredItemElRef.current !== selectedElRef.current && /* @__PURE__ */ jsx24(ItemInteractionLayer, { rect: hoveredItemRect, state: "hover" }),
12076
- toolbarVariant === "select-frame" && toolbarRect && !linkPopover && !isItemDragging && !isFooterFrameSelection && selectedElRef.current && isNavbarLinksContainer(selectedElRef.current) && /* @__PURE__ */ jsx24(NavbarContainerChrome, { rect: toolbarRect, onAdd: handleAddTopLevelNavItem }),
12077
- toolbarRect && !linkPopover && (toolbarVariant === "link-action" || toolbarVariant === "select-frame") && /* @__PURE__ */ jsx24(
9593
+ rect: toolbarRect,
9594
+ onAdd: handleAddTopLevelNavItem
9595
+ }
9596
+ ),
9597
+ hoveredItemRect && !hoveredNavContainerRect && !siblingHintRect && !isItemDragging && hoveredItemElRef.current !== selectedElRef.current && /* @__PURE__ */ jsx23(ItemInteractionLayer, { rect: hoveredItemRect, state: "hover" }),
9598
+ toolbarRect && (toolbarVariant === "link-action" || toolbarVariant === "select-frame") && /* @__PURE__ */ jsx23(
12078
9599
  ItemInteractionLayer,
12079
9600
  {
12080
- rect: isItemDragging && draggedItemRect && (footerDragRef.current?.wasSelected || navDragRef.current?.wasSelected) ? draggedItemRect : toolbarRect,
9601
+ rect: toolbarRect,
12081
9602
  elRef: glowElRef,
12082
9603
  state: isItemDragging ? "dragging" : resolveItemInteractionState(toolbarRect, parentScrollRef.current),
12083
- showHandle: toolbarVariant === "link-action" && Boolean(reorderHrefKey) || toolbarVariant === "select-frame" && isFooterFrameSelection,
9604
+ showHandle: toolbarVariant === "link-action" && Boolean(reorderHrefKey),
12084
9605
  dragDisabled: reorderDragDisabled,
12085
9606
  dragHandleLabel: reorderHrefKey ? `Reorder ${reorderHrefKey}` : "Reorder item",
12086
9607
  onDragHandleDragStart: handleItemDragStart,
12087
9608
  onDragHandleDragEnd: handleItemDragEnd,
12088
- onItemPointerDown: handleItemChromePointerDown,
12089
- onItemClick: handleItemChromeClick,
12090
- itemDragSurface: !isFooterFrameSelection,
12091
- toolbar: toolbarVariant === "link-action" && !isItemDragging ? /* @__PURE__ */ jsx24(
9609
+ toolbar: toolbarVariant === "link-action" && !isItemDragging ? /* @__PURE__ */ jsx23(
12092
9610
  ItemActionToolbar,
12093
9611
  {
12094
9612
  onEditLink: openLinkPopoverForSelected,
12095
9613
  addItemDisabled: true,
12096
9614
  editLinkDisabled: false,
12097
- moreDisabled: true,
12098
- showAddItem: !selectedIsCta,
12099
- showMore: !selectedIsCta
9615
+ moreDisabled: true
12100
9616
  }
12101
9617
  ) : void 0
12102
9618
  }
12103
9619
  ),
12104
- toolbarRect && toolbarVariant === "rich-text" && !linkPopover && /* @__PURE__ */ jsxs13(Fragment5, { children: [
12105
- /* @__PURE__ */ jsx24(
9620
+ toolbarRect && toolbarVariant === "rich-text" && /* @__PURE__ */ jsxs13(Fragment5, { children: [
9621
+ /* @__PURE__ */ jsx23(
12106
9622
  EditGlowChrome,
12107
9623
  {
12108
9624
  rect: toolbarRect,
12109
9625
  elRef: glowElRef,
12110
9626
  reorderHrefKey,
12111
- dragDisabled: reorderDragDisabled,
12112
- hideHandle: isItemDragging
9627
+ dragDisabled: reorderDragDisabled
12113
9628
  }
12114
9629
  ),
12115
- /* @__PURE__ */ jsx24(
9630
+ /* @__PURE__ */ jsx23(
12116
9631
  FloatingToolbar,
12117
9632
  {
12118
9633
  rect: toolbarRect,
@@ -12151,7 +9666,7 @@ function OhhwellsBridge() {
12151
9666
  ]
12152
9667
  }
12153
9668
  ),
12154
- toggleState && !linkPopover && /* @__PURE__ */ jsx24(
9669
+ toggleState && !linkPopover && /* @__PURE__ */ jsx23(
12155
9670
  StateToggle,
12156
9671
  {
12157
9672
  rect: toggleState.rect,
@@ -12167,29 +9682,25 @@ function OhhwellsBridge() {
12167
9682
  className: "fixed left-0 w-full z-2147483646 flex items-center pointer-events-none",
12168
9683
  style: { top: sectionGap.y, transform: "translateY(-50%)" },
12169
9684
  children: [
12170
- /* @__PURE__ */ jsx24("div", { className: "flex-1 bg-primary", style: { height: 3 } }),
12171
- /* @__PURE__ */ jsx24(
9685
+ /* @__PURE__ */ jsx23("div", { className: "flex-1 bg-primary", style: { height: 3 } }),
9686
+ /* @__PURE__ */ jsx23(
12172
9687
  Badge,
12173
9688
  {
12174
9689
  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",
12175
9690
  onClick: () => {
12176
9691
  window.parent.postMessage(
12177
- {
12178
- type: "ow:add-section",
12179
- insertAfter: sectionGap.insertAfter,
12180
- insertBefore: sectionGap.insertBefore
12181
- },
9692
+ { type: "ow:add-section", insertAfter: sectionGap.insertAfter, insertBefore: sectionGap.insertBefore },
12182
9693
  "*"
12183
9694
  );
12184
9695
  },
12185
9696
  children: "Add Section"
12186
9697
  }
12187
9698
  ),
12188
- /* @__PURE__ */ jsx24("div", { className: "flex-1 bg-primary", style: { height: 3 } })
9699
+ /* @__PURE__ */ jsx23("div", { className: "flex-1 bg-primary", style: { height: 3 } })
12189
9700
  ]
12190
9701
  }
12191
9702
  ),
12192
- linkPopover && dialogPortalContainer ? /* @__PURE__ */ jsx24(
9703
+ linkPopover && dialogPortalContainer ? /* @__PURE__ */ jsx23(
12193
9704
  LinkPopover,
12194
9705
  {
12195
9706
  panelRef: linkPopoverPanelRef,
@@ -12210,6 +9721,48 @@ function OhhwellsBridge() {
12210
9721
  bridgeRoot
12211
9722
  ) : null;
12212
9723
  }
9724
+
9725
+ // src/ui/drop-indicator.tsx
9726
+ import * as React10 from "react";
9727
+ import { jsx as jsx24 } from "react/jsx-runtime";
9728
+ var dropIndicatorVariants = cva(
9729
+ "ov-gap-line pointer-events-none shrink-0 transition-opacity duration-150",
9730
+ {
9731
+ variants: {
9732
+ direction: {
9733
+ vertical: "h-6 w-[3px]",
9734
+ horizontal: "h-[3px] w-[200px]"
9735
+ },
9736
+ state: {
9737
+ default: "opacity-0",
9738
+ hover: "opacity-100",
9739
+ dragIdle: "opacity-40",
9740
+ dragActive: "opacity-100"
9741
+ }
9742
+ },
9743
+ defaultVariants: {
9744
+ direction: "vertical",
9745
+ state: "default"
9746
+ }
9747
+ }
9748
+ );
9749
+ var DropIndicator = React10.forwardRef(
9750
+ ({ className, direction, state, ...props }, ref) => {
9751
+ return /* @__PURE__ */ jsx24(
9752
+ "div",
9753
+ {
9754
+ ref,
9755
+ "data-slot": "drop-indicator",
9756
+ "data-direction": direction ?? "vertical",
9757
+ "data-state": state ?? "default",
9758
+ "aria-hidden": "true",
9759
+ className: cn(dropIndicatorVariants({ direction, state }), className),
9760
+ ...props
9761
+ }
9762
+ );
9763
+ }
9764
+ );
9765
+ DropIndicator.displayName = "DropIndicator";
12213
9766
  export {
12214
9767
  CustomToolbar,
12215
9768
  CustomToolbarButton,