@ohhwells/bridge 0.1.42 → 0.1.44

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
 
3
3
  // src/OhhwellsBridge.tsx
4
- import React9, { useCallback as useCallback3, useEffect as useEffect7, useLayoutEffect as useLayoutEffect2, useRef as useRef5, useState as useState6 } from "react";
4
+ import React10, { useCallback as useCallback3, useEffect as useEffect7, useLayoutEffect as useLayoutEffect3, useRef as useRef6, useState as useState6 } from "react";
5
5
  import { createRoot } from "react-dom/client";
6
6
  import { flushSync } from "react-dom";
7
7
 
@@ -4242,7 +4242,8 @@ var CustomToolbar = React4.forwardRef(({ className, onMouseDown, ...props }, ref
4242
4242
  ref,
4243
4243
  "data-ohw-toolbar": "",
4244
4244
  className: cn(
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)]",
4245
+ // Figma: bg background, radius 8, gap-1, p-0.5, shadow-md no border
4246
+ "inline-flex h-8 items-center gap-1 rounded-[var(--radius,0.5rem)] bg-background p-0.5 font-sans whitespace-nowrap shadow-md",
4246
4247
  className
4247
4248
  ),
4248
4249
  onMouseDown: (e) => {
@@ -4270,8 +4271,8 @@ var CustomToolbarButton = React4.forwardRef(
4270
4271
  ref,
4271
4272
  type,
4272
4273
  className: cn(
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",
4274
+ "inline-flex size-7 shrink-0 items-center justify-center rounded-[calc(var(--radius,0.5rem)-2px)] text-foreground transition-colors",
4275
+ active ? "bg-primary text-primary-foreground" : "bg-transparent hover:bg-muted disabled:cursor-not-allowed disabled:text-muted-foreground disabled:opacity-60",
4275
4276
  className
4276
4277
  ),
4277
4278
  ...props
@@ -4285,7 +4286,7 @@ import { Link, MoreHorizontal, Plus } from "lucide-react";
4285
4286
 
4286
4287
  // src/ui/tooltip.tsx
4287
4288
  import { Tooltip as TooltipPrimitive } from "radix-ui";
4288
- import { jsx as jsx7 } from "react/jsx-runtime";
4289
+ import { jsx as jsx7, jsxs as jsxs3 } from "react/jsx-runtime";
4289
4290
  function TooltipProvider({
4290
4291
  delayDuration = 0,
4291
4292
  ...props
@@ -4298,43 +4299,39 @@ function Tooltip({ ...props }) {
4298
4299
  function TooltipTrigger({ ...props }) {
4299
4300
  return /* @__PURE__ */ jsx7(TooltipPrimitive.Trigger, { "data-slot": "tooltip-trigger", ...props });
4300
4301
  }
4301
- var arrowClassBySide = {
4302
- top: "before:bottom-0 before:left-1/2 before:-translate-x-1/2 before:translate-y-1/2",
4303
- bottom: "before:top-0 before:left-1/2 before:-translate-x-1/2 before:-translate-y-1/2",
4304
- left: "before:right-0 before:top-1/2 before:-translate-y-1/2 before:translate-x-1/2",
4305
- right: "before:left-0 before:top-1/2 before:-translate-y-1/2 before:-translate-x-1/2"
4306
- };
4307
4302
  function TooltipContent({
4308
4303
  className,
4309
- sideOffset = 6,
4304
+ sideOffset = 3,
4310
4305
  side = "bottom",
4311
4306
  children,
4307
+ hideArrow,
4312
4308
  ...props
4313
4309
  }) {
4314
- const resolvedSide = side ?? "bottom";
4315
- return /* @__PURE__ */ jsx7(TooltipPrimitive.Portal, { children: /* @__PURE__ */ jsx7(
4310
+ return /* @__PURE__ */ jsx7(TooltipPrimitive.Portal, { children: /* @__PURE__ */ jsxs3(
4316
4311
  TooltipPrimitive.Content,
4317
4312
  {
4318
4313
  "data-slot": "tooltip-content",
4319
4314
  side,
4320
4315
  sideOffset,
4321
4316
  className: cn(
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",
4317
+ "z-[2147483647] inline-flex w-fit max-w-xs items-center rounded-md bg-foreground px-3 py-1.5 text-xs text-background",
4318
+ "origin-[var(--radix-tooltip-content-transform-origin)] animate-in fade-in-0 zoom-in-95",
4319
+ "data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95",
4326
4320
  "data-[side=bottom]:slide-in-from-top-2 data-[side=top]:slide-in-from-bottom-2",
4327
4321
  "data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2",
4328
4322
  className
4329
4323
  ),
4330
4324
  ...props,
4331
- children
4325
+ children: [
4326
+ children,
4327
+ !hideArrow && /* @__PURE__ */ jsx7(TooltipPrimitive.Arrow, { className: "z-[2147483647] size-2.5 translate-y-[calc(-50%-2px)] rotate-45 rounded-[2px] bg-foreground fill-foreground" })
4328
+ ]
4332
4329
  }
4333
4330
  ) });
4334
4331
  }
4335
4332
 
4336
4333
  // src/ui/item-action-toolbar.tsx
4337
- import { jsx as jsx8, jsxs as jsxs3 } from "react/jsx-runtime";
4334
+ import { jsx as jsx8, jsxs as jsxs4 } from "react/jsx-runtime";
4338
4335
  function ToolbarActionTooltip({
4339
4336
  label,
4340
4337
  side = "bottom",
@@ -4343,9 +4340,9 @@ function ToolbarActionTooltip({
4343
4340
  children
4344
4341
  }) {
4345
4342
  const button = /* @__PURE__ */ jsx8(CustomToolbarButton, { type: "button", "aria-label": label, disabled, ...buttonProps, children });
4346
- return /* @__PURE__ */ jsxs3(Tooltip, { children: [
4343
+ return /* @__PURE__ */ jsxs4(Tooltip, { children: [
4347
4344
  /* @__PURE__ */ jsx8(TooltipTrigger, { asChild: true, children: disabled ? /* @__PURE__ */ jsx8("span", { className: "inline-flex", children: button }) : button }),
4348
- /* @__PURE__ */ jsx8(TooltipContent, { side, children: label })
4345
+ /* @__PURE__ */ jsx8(TooltipContent, { side, sideOffset: 3, children: label })
4349
4346
  ] });
4350
4347
  }
4351
4348
  function ItemActionToolbar({
@@ -4355,9 +4352,11 @@ function ItemActionToolbar({
4355
4352
  editLinkDisabled = false,
4356
4353
  addItemDisabled = true,
4357
4354
  moreDisabled = true,
4355
+ showAddItem = true,
4356
+ showMore = true,
4358
4357
  tooltipSide = "bottom"
4359
4358
  }) {
4360
- return /* @__PURE__ */ jsx8(TooltipProvider, { delayDuration: 0, children: /* @__PURE__ */ jsxs3(CustomToolbar, { children: [
4359
+ return /* @__PURE__ */ jsx8(TooltipProvider, { delayDuration: 0, children: /* @__PURE__ */ jsxs4(CustomToolbar, { children: [
4361
4360
  /* @__PURE__ */ jsx8(
4362
4361
  ToolbarActionTooltip,
4363
4362
  {
@@ -4379,8 +4378,7 @@ function ItemActionToolbar({
4379
4378
  children: /* @__PURE__ */ jsx8(Link, { className: "size-4 shrink-0", "aria-hidden": true })
4380
4379
  }
4381
4380
  ),
4382
- /* @__PURE__ */ jsx8(CustomToolbarDivider, {}),
4383
- /* @__PURE__ */ jsx8(
4381
+ showAddItem ? /* @__PURE__ */ jsx8(
4384
4382
  ToolbarActionTooltip,
4385
4383
  {
4386
4384
  label: "Add item",
@@ -4396,9 +4394,8 @@ function ItemActionToolbar({
4396
4394
  },
4397
4395
  children: /* @__PURE__ */ jsx8(Plus, { className: "size-4 shrink-0", "aria-hidden": true })
4398
4396
  }
4399
- ),
4400
- /* @__PURE__ */ jsx8(CustomToolbarDivider, {}),
4401
- /* @__PURE__ */ jsx8(
4397
+ ) : null,
4398
+ showMore ? /* @__PURE__ */ jsx8(
4402
4399
  ToolbarActionTooltip,
4403
4400
  {
4404
4401
  label: "More",
@@ -4414,15 +4411,19 @@ function ItemActionToolbar({
4414
4411
  },
4415
4412
  children: /* @__PURE__ */ jsx8(MoreHorizontal, { className: "size-4 shrink-0", "aria-hidden": true })
4416
4413
  }
4417
- )
4414
+ ) : null
4418
4415
  ] }) });
4419
4416
  }
4420
4417
 
4421
4418
  // src/ui/item-interaction-layer.tsx
4422
- import { jsx as jsx9, jsxs as jsxs4 } from "react/jsx-runtime";
4419
+ import * as React5 from "react";
4420
+ import { jsx as jsx9, jsxs as jsxs5 } from "react/jsx-runtime";
4423
4421
  var PRIMARY = "var(--ohw-primary, #0885FE)";
4424
- var FOCUS_RING = "0 0 0 4px rgba(8, 133, 254, 0.12)";
4422
+ var FOCUS_RING = `0 0 0 4px color-mix(in srgb, ${PRIMARY} 12%, transparent)`;
4425
4423
  var DRAG_SHADOW = "0px 4px 6px -1px rgba(0, 0, 0, 0.1), 0px 2px 4px -2px rgba(0, 0, 0, 0.1)";
4424
+ var TOOLBAR_EDGE_MARGIN = 4;
4425
+ var SELECTION_CHROME_GAP = 4;
4426
+ var TOOLBAR_STROKE_GAP = 4;
4426
4427
  function getChromeStyle(state) {
4427
4428
  switch (state) {
4428
4429
  case "hover":
@@ -4450,6 +4451,59 @@ function getChromeStyle(state) {
4450
4451
  return {};
4451
4452
  }
4452
4453
  }
4454
+ function ClampedToolbarSlot({
4455
+ placement,
4456
+ children
4457
+ }) {
4458
+ const slotRef = React5.useRef(null);
4459
+ React5.useLayoutEffect(() => {
4460
+ const slot = slotRef.current;
4461
+ if (!slot) return;
4462
+ const clamp = () => {
4463
+ const toolbar = slot.firstElementChild;
4464
+ if (!toolbar) return;
4465
+ const w = toolbar.offsetWidth;
4466
+ if (w <= 0) return;
4467
+ const parent = slot.offsetParent;
4468
+ if (!parent) return;
4469
+ const parentRect = parent.getBoundingClientRect();
4470
+ const centerX = parentRect.left + parentRect.width / 2;
4471
+ const half = w / 2;
4472
+ const clampedCenter = Math.max(
4473
+ TOOLBAR_EDGE_MARGIN + half,
4474
+ Math.min(centerX, window.innerWidth - TOOLBAR_EDGE_MARGIN - half)
4475
+ );
4476
+ const offsetX = clampedCenter - centerX;
4477
+ slot.style.transform = `translateX(calc(-50% + ${offsetX}px))`;
4478
+ };
4479
+ clamp();
4480
+ const ro = new ResizeObserver(clamp);
4481
+ ro.observe(slot);
4482
+ if (slot.firstElementChild) ro.observe(slot.firstElementChild);
4483
+ window.addEventListener("resize", clamp);
4484
+ return () => {
4485
+ ro.disconnect();
4486
+ window.removeEventListener("resize", clamp);
4487
+ };
4488
+ }, [placement, children]);
4489
+ return /* @__PURE__ */ jsx9(
4490
+ "div",
4491
+ {
4492
+ ref: slotRef,
4493
+ className: cn(
4494
+ "pointer-events-auto absolute left-1/2",
4495
+ placement === "top" ? "bottom-full" : "top-full"
4496
+ ),
4497
+ style: {
4498
+ marginBottom: placement === "top" ? TOOLBAR_STROKE_GAP : void 0,
4499
+ marginTop: placement === "bottom" ? TOOLBAR_STROKE_GAP : void 0,
4500
+ transform: "translateX(-50%)"
4501
+ },
4502
+ "data-ohw-item-toolbar-anchor": placement,
4503
+ children
4504
+ }
4505
+ );
4506
+ }
4453
4507
  function ItemInteractionLayer({
4454
4508
  rect,
4455
4509
  state,
@@ -4460,7 +4514,7 @@ function ItemInteractionLayer({
4460
4514
  dragHandleLabel = "Reorder item",
4461
4515
  onDragHandleDragStart,
4462
4516
  onDragHandleDragEnd,
4463
- chromeGap = 6,
4517
+ chromeGap = SELECTION_CHROME_GAP,
4464
4518
  className
4465
4519
  }) {
4466
4520
  if (state === "default") return null;
@@ -4468,7 +4522,7 @@ function ItemInteractionLayer({
4468
4522
  const isDragging = state === "dragging";
4469
4523
  const showToolbar = isActive && toolbar;
4470
4524
  const showDragHandle = isActive && showHandle && !isDragging;
4471
- return /* @__PURE__ */ jsxs4(
4525
+ return /* @__PURE__ */ jsxs5(
4472
4526
  "div",
4473
4527
  {
4474
4528
  ref: elRef,
@@ -4517,33 +4571,19 @@ function ItemInteractionLayer({
4517
4571
  )
4518
4572
  }
4519
4573
  ),
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
- )
4574
+ showToolbar && state === "active-top" && /* @__PURE__ */ jsx9(ClampedToolbarSlot, { placement: "top", children: toolbar }),
4575
+ showToolbar && state === "active-bottom" && /* @__PURE__ */ jsx9(ClampedToolbarSlot, { placement: "bottom", children: toolbar })
4536
4576
  ]
4537
4577
  }
4538
4578
  );
4539
4579
  }
4540
4580
 
4541
4581
  // src/ui/MediaOverlay.tsx
4542
- import * as React6 from "react";
4582
+ import * as React7 from "react";
4543
4583
  import { Film, ImageIcon, Pause, Play, Volume2, VolumeX } from "lucide-react";
4544
4584
 
4545
4585
  // src/ui/button.tsx
4546
- import * as React5 from "react";
4586
+ import * as React6 from "react";
4547
4587
  import { Slot } from "radix-ui";
4548
4588
  import { jsx as jsx10 } from "react/jsx-runtime";
4549
4589
  var buttonVariants = cva(
@@ -4566,7 +4606,7 @@ var buttonVariants = cva(
4566
4606
  }
4567
4607
  }
4568
4608
  );
4569
- var Button = React5.forwardRef(
4609
+ var Button = React6.forwardRef(
4570
4610
  ({ className, variant, size, asChild = false, ...props }, ref) => {
4571
4611
  const Comp = asChild ? Slot.Root : "button";
4572
4612
  return /* @__PURE__ */ jsx10(
@@ -4583,7 +4623,7 @@ var Button = React5.forwardRef(
4583
4623
  Button.displayName = "Button";
4584
4624
 
4585
4625
  // src/ui/MediaOverlay.tsx
4586
- import { Fragment as Fragment2, jsx as jsx11, jsxs as jsxs5 } from "react/jsx-runtime";
4626
+ import { Fragment as Fragment2, jsx as jsx11, jsxs as jsxs6 } from "react/jsx-runtime";
4587
4627
  var MEDIA_UPLOAD_FADE_MS = 300;
4588
4628
  var VIDEO_SETTINGS_BAR_INSET = 8;
4589
4629
  var OVERLAY_BUTTON_STYLE = {
@@ -4618,7 +4658,7 @@ function MediaOverlay({
4618
4658
  onVideoSettingsChange
4619
4659
  }) {
4620
4660
  const { rect } = hover;
4621
- const skeletonRef = React6.useRef(null);
4661
+ const skeletonRef = React7.useRef(null);
4622
4662
  const isVideo = hover.elementType === "video";
4623
4663
  const autoplay = hover.videoAutoplay ?? true;
4624
4664
  const muted = hover.videoMuted ?? true;
@@ -4630,7 +4670,7 @@ function MediaOverlay({
4630
4670
  height: rect.height,
4631
4671
  zIndex: 2147483646
4632
4672
  };
4633
- React6.useEffect(() => {
4673
+ React7.useEffect(() => {
4634
4674
  if (!isUploading || !fadingOut || !skeletonRef.current) return;
4635
4675
  const anim = skeletonRef.current.animate([{ opacity: 1 }, { opacity: 0 }], {
4636
4676
  duration: MEDIA_UPLOAD_FADE_MS,
@@ -4654,7 +4694,7 @@ function MediaOverlay({
4654
4694
  }
4655
4695
  );
4656
4696
  }
4657
- const settingsBar = isVideo && !hover.isDragOver ? /* @__PURE__ */ jsxs5(
4697
+ const settingsBar = isVideo && !hover.isDragOver ? /* @__PURE__ */ jsxs6(
4658
4698
  "div",
4659
4699
  {
4660
4700
  "data-ohw-bridge": "",
@@ -4709,7 +4749,7 @@ function MediaOverlay({
4709
4749
  ]
4710
4750
  }
4711
4751
  ) : null;
4712
- return /* @__PURE__ */ jsxs5(Fragment2, { children: [
4752
+ return /* @__PURE__ */ jsxs6(Fragment2, { children: [
4713
4753
  settingsBar,
4714
4754
  /* @__PURE__ */ jsx11(
4715
4755
  "div",
@@ -4728,7 +4768,7 @@ function MediaOverlay({
4728
4768
  background: "color-mix(in srgb, var(--color-primary) 20%, transparent)"
4729
4769
  },
4730
4770
  onClick: () => onReplace(hover.key),
4731
- children: /* @__PURE__ */ jsxs5(
4771
+ children: /* @__PURE__ */ jsxs6(
4732
4772
  Button,
4733
4773
  {
4734
4774
  "data-ohw-media-overlay": "",
@@ -5035,10 +5075,10 @@ function scrollToHashSectionWhenReady(behavior = "smooth") {
5035
5075
  import { useEffect as useEffect6 } from "react";
5036
5076
 
5037
5077
  // src/ui/dialog.tsx
5038
- import * as React7 from "react";
5078
+ import * as React8 from "react";
5039
5079
  import { Dialog as DialogPrimitive } from "radix-ui";
5040
5080
  import { X } from "lucide-react";
5041
- import { jsx as jsx12, jsxs as jsxs6 } from "react/jsx-runtime";
5081
+ import { jsx as jsx12, jsxs as jsxs7 } from "react/jsx-runtime";
5042
5082
  function Dialog2({
5043
5083
  ...props
5044
5084
  }) {
@@ -5063,12 +5103,12 @@ function DialogOverlay({
5063
5103
  }
5064
5104
  );
5065
5105
  }
5066
- var DialogContent = React7.forwardRef(
5106
+ var DialogContent = React8.forwardRef(
5067
5107
  ({ className, children, showCloseButton = true, container, ...props }, ref) => {
5068
5108
  const positionMode = container ? "absolute" : "fixed";
5069
- return /* @__PURE__ */ jsxs6(DialogPortal, { container: container ?? void 0, children: [
5109
+ return /* @__PURE__ */ jsxs7(DialogPortal, { container: container ?? void 0, children: [
5070
5110
  /* @__PURE__ */ jsx12(DialogOverlay, { className: cn(positionMode, "inset-0") }),
5071
- /* @__PURE__ */ jsxs6(
5111
+ /* @__PURE__ */ jsxs7(
5072
5112
  DialogPrimitive.Content,
5073
5113
  {
5074
5114
  ref,
@@ -5118,7 +5158,7 @@ function DialogFooter({
5118
5158
  }
5119
5159
  );
5120
5160
  }
5121
- var DialogTitle = React7.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx12(
5161
+ var DialogTitle = React8.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx12(
5122
5162
  DialogPrimitive.Title,
5123
5163
  {
5124
5164
  ref,
@@ -5130,7 +5170,7 @@ var DialogTitle = React7.forwardRef(({ className, ...props }, ref) => /* @__PURE
5130
5170
  }
5131
5171
  ));
5132
5172
  DialogTitle.displayName = DialogPrimitive.Title.displayName;
5133
- var DialogDescription = React7.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx12(
5173
+ var DialogDescription = React8.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx12(
5134
5174
  DialogPrimitive.Description,
5135
5175
  {
5136
5176
  ref,
@@ -5146,15 +5186,15 @@ import { Info, X as X3 } from "lucide-react";
5146
5186
 
5147
5187
  // src/ui/link-modal/DestinationBreadcrumb.tsx
5148
5188
  import { ArrowRight, File, GalleryVertical } from "lucide-react";
5149
- import { jsx as jsx13, jsxs as jsxs7 } from "react/jsx-runtime";
5189
+ import { jsx as jsx13, jsxs as jsxs8 } from "react/jsx-runtime";
5150
5190
  function DestinationBreadcrumb({
5151
5191
  pageTitle,
5152
5192
  sectionLabel
5153
5193
  }) {
5154
- return /* @__PURE__ */ jsxs7("div", { className: "flex w-full flex-col gap-2", children: [
5194
+ return /* @__PURE__ */ jsxs8("div", { className: "flex w-full flex-col gap-2", children: [
5155
5195
  /* @__PURE__ */ jsx13("p", { className: "text-sm font-medium! text-foreground m-0", children: "Destination" }),
5156
- /* @__PURE__ */ jsxs7("div", { className: "flex items-center gap-3", children: [
5157
- /* @__PURE__ */ jsxs7("div", { className: "flex items-center gap-2", children: [
5196
+ /* @__PURE__ */ jsxs8("div", { className: "flex items-center gap-3", children: [
5197
+ /* @__PURE__ */ jsxs8("div", { className: "flex items-center gap-2", children: [
5158
5198
  /* @__PURE__ */ jsx13(File, { size: 16, className: "shrink-0 text-foreground", "aria-hidden": true }),
5159
5199
  /* @__PURE__ */ jsx13("span", { className: "text-sm font-medium leading-none text-foreground!", children: pageTitle })
5160
5200
  ] }),
@@ -5166,7 +5206,7 @@ function DestinationBreadcrumb({
5166
5206
  "aria-hidden": true
5167
5207
  }
5168
5208
  ),
5169
- /* @__PURE__ */ jsxs7("div", { className: "flex min-w-0 flex-1 items-center gap-2", children: [
5209
+ /* @__PURE__ */ jsxs8("div", { className: "flex min-w-0 flex-1 items-center gap-2", children: [
5170
5210
  /* @__PURE__ */ jsx13(
5171
5211
  GalleryVertical,
5172
5212
  {
@@ -5183,14 +5223,14 @@ function DestinationBreadcrumb({
5183
5223
 
5184
5224
  // src/ui/link-modal/SectionTreeItem.tsx
5185
5225
  import { GalleryVertical as GalleryVertical2 } from "lucide-react";
5186
- import { jsx as jsx14, jsxs as jsxs8 } from "react/jsx-runtime";
5226
+ import { jsx as jsx14, jsxs as jsxs9 } from "react/jsx-runtime";
5187
5227
  function SectionTreeItem({
5188
5228
  section,
5189
5229
  onSelect,
5190
5230
  selected
5191
5231
  }) {
5192
5232
  const interactive = Boolean(onSelect);
5193
- return /* @__PURE__ */ jsxs8("div", { className: "flex h-9 w-full items-end pl-3", children: [
5233
+ return /* @__PURE__ */ jsxs9("div", { className: "flex h-9 w-full items-end pl-3", children: [
5194
5234
  /* @__PURE__ */ jsx14(
5195
5235
  "div",
5196
5236
  {
@@ -5198,7 +5238,7 @@ function SectionTreeItem({
5198
5238
  "aria-hidden": true
5199
5239
  }
5200
5240
  ),
5201
- /* @__PURE__ */ jsxs8(
5241
+ /* @__PURE__ */ jsxs9(
5202
5242
  "div",
5203
5243
  {
5204
5244
  role: interactive ? "button" : void 0,
@@ -5232,12 +5272,12 @@ function SectionTreeItem({
5232
5272
  }
5233
5273
 
5234
5274
  // src/ui/link-modal/UrlOrPageInput.tsx
5235
- import { useEffect as useEffect3, useId as useId2, useRef as useRef3, useState as useState3 } from "react";
5275
+ import { useEffect as useEffect3, useId as useId2, useRef as useRef4, useState as useState3 } from "react";
5236
5276
 
5237
5277
  // src/ui/input.tsx
5238
- import * as React8 from "react";
5278
+ import * as React9 from "react";
5239
5279
  import { jsx as jsx15 } from "react/jsx-runtime";
5240
- var Input = React8.forwardRef(
5280
+ var Input = React9.forwardRef(
5241
5281
  ({ className, type, ...props }, ref) => {
5242
5282
  return /* @__PURE__ */ jsx15(
5243
5283
  "input",
@@ -5272,7 +5312,7 @@ function Label({ className, ...props }) {
5272
5312
 
5273
5313
  // src/ui/link-modal/UrlOrPageInput.tsx
5274
5314
  import { ChevronDown, File as File2, X as X2 } from "lucide-react";
5275
- import { jsx as jsx17, jsxs as jsxs9 } from "react/jsx-runtime";
5315
+ import { jsx as jsx17, jsxs as jsxs10 } from "react/jsx-runtime";
5276
5316
  function FieldChevron({
5277
5317
  onClick
5278
5318
  }) {
@@ -5301,8 +5341,8 @@ function UrlOrPageInput({
5301
5341
  urlError
5302
5342
  }) {
5303
5343
  const inputId = useId2();
5304
- const inputRef = useRef3(null);
5305
- const rootRef = useRef3(null);
5344
+ const inputRef = useRef4(null);
5345
+ const rootRef = useRef4(null);
5306
5346
  const [isFocused, setIsFocused] = useState3(false);
5307
5347
  useEffect3(() => {
5308
5348
  if (!dropdownOpen) return;
@@ -5345,10 +5385,10 @@ function UrlOrPageInput({
5345
5385
  "data-ohw-link-field flex h-[36px] w-full items-center overflow-hidden rounded-md border bg-background pl-3 pr-3 py-2 outline-none transition-[border-color,box-shadow]",
5346
5386
  urlError ? "border-destructive shadow-[0_0_0_1px_var(--ohw-destructive)]" : isFocused ? "border-primary shadow-[0_0_0_1px_var(--ohw-primary)]" : "border-input"
5347
5387
  );
5348
- return /* @__PURE__ */ jsxs9("div", { className: "flex w-full flex-col gap-2 p-0", children: [
5388
+ return /* @__PURE__ */ jsxs10("div", { className: "flex w-full flex-col gap-2 p-0", children: [
5349
5389
  /* @__PURE__ */ jsx17(Label, { htmlFor: inputId, className: cn(urlError && "text-destructive"), children: "Destination" }),
5350
- /* @__PURE__ */ jsxs9("div", { ref: rootRef, className: "relative w-full", children: [
5351
- /* @__PURE__ */ jsxs9("div", { "data-ohw-link-field": true, className: fieldClassName, children: [
5390
+ /* @__PURE__ */ jsxs10("div", { ref: rootRef, className: "relative w-full", children: [
5391
+ /* @__PURE__ */ jsxs10("div", { "data-ohw-link-field": true, className: fieldClassName, children: [
5352
5392
  selectedPage ? /* @__PURE__ */ jsx17("div", { className: "flex shrink-0 items-center pr-2", children: /* @__PURE__ */ jsx17(
5353
5393
  File2,
5354
5394
  {
@@ -5402,7 +5442,7 @@ function UrlOrPageInput({
5402
5442
  "data-ohw-link-page-dropdown": "",
5403
5443
  className: "absolute left-0 right-0 top-[calc(100%+4px)] z-50 max-h-48 overflow-auto rounded-lg border border-border bg-popover py-1 shadow-lg",
5404
5444
  onMouseDown: (e) => e.preventDefault(),
5405
- children: filteredPages.map((page) => /* @__PURE__ */ jsxs9(
5445
+ children: filteredPages.map((page) => /* @__PURE__ */ jsxs10(
5406
5446
  "button",
5407
5447
  {
5408
5448
  type: "button",
@@ -5423,10 +5463,10 @@ function UrlOrPageInput({
5423
5463
  }
5424
5464
 
5425
5465
  // src/ui/link-modal/LinkEditorPanel.tsx
5426
- import { Fragment as Fragment3, jsx as jsx18, jsxs as jsxs10 } from "react/jsx-runtime";
5466
+ import { Fragment as Fragment3, jsx as jsx18, jsxs as jsxs11 } from "react/jsx-runtime";
5427
5467
  function LinkEditorPanel({ state, onClose }) {
5428
5468
  const isCancel = state.secondaryLabel === "Cancel" || state.secondaryLabel === "Back to sections";
5429
- return /* @__PURE__ */ jsxs10(Fragment3, { children: [
5469
+ return /* @__PURE__ */ jsxs11(Fragment3, { children: [
5430
5470
  /* @__PURE__ */ jsx18(DialogClose, { asChild: true, children: /* @__PURE__ */ jsx18(
5431
5471
  "button",
5432
5472
  {
@@ -5438,7 +5478,7 @@ function LinkEditorPanel({ state, onClose }) {
5438
5478
  }
5439
5479
  ) }),
5440
5480
  /* @__PURE__ */ jsx18(DialogHeader, { className: "w-full gap-1.5 p-6 pr-12", children: /* @__PURE__ */ jsx18(DialogTitle, { className: "m-0 w-full break-words", children: state.title }) }),
5441
- /* @__PURE__ */ jsxs10("div", { className: "flex w-full flex-col gap-3 px-6 pb-8 pt-1", children: [
5481
+ /* @__PURE__ */ jsxs11("div", { className: "flex w-full flex-col gap-3 px-6 pb-8 pt-1", children: [
5442
5482
  state.showBreadcrumb && state.selectedPage && state.selectedSection ? /* @__PURE__ */ jsx18(
5443
5483
  DestinationBreadcrumb,
5444
5484
  {
@@ -5458,7 +5498,7 @@ function LinkEditorPanel({ state, onClose }) {
5458
5498
  urlError: state.urlError
5459
5499
  }
5460
5500
  ),
5461
- state.showChooseSection ? /* @__PURE__ */ jsxs10("div", { className: "flex flex-col justify-center gap-2", children: [
5501
+ state.showChooseSection ? /* @__PURE__ */ jsxs11("div", { className: "flex flex-col justify-center gap-2", children: [
5462
5502
  /* @__PURE__ */ jsx18(
5463
5503
  Button,
5464
5504
  {
@@ -5470,14 +5510,14 @@ function LinkEditorPanel({ state, onClose }) {
5470
5510
  children: "Choose a section"
5471
5511
  }
5472
5512
  ),
5473
- /* @__PURE__ */ jsxs10("div", { className: "flex items-center gap-1 text-sm text-muted-foreground", children: [
5513
+ /* @__PURE__ */ jsxs11("div", { className: "flex items-center gap-1 text-sm text-muted-foreground", children: [
5474
5514
  /* @__PURE__ */ jsx18(Info, { size: 16, className: "shrink-0", "aria-hidden": true }),
5475
5515
  /* @__PURE__ */ jsx18("span", { children: "Pick a section this link should scroll to." })
5476
5516
  ] })
5477
5517
  ] }) : null,
5478
5518
  state.showSectionRow && state.selectedSection ? /* @__PURE__ */ jsx18(SectionTreeItem, { section: state.selectedSection, selected: true }) : null
5479
5519
  ] }),
5480
- /* @__PURE__ */ jsxs10(DialogFooter, { className: "w-full px-6 pb-6", children: [
5520
+ /* @__PURE__ */ jsxs11(DialogFooter, { className: "w-full px-6 pb-6", children: [
5481
5521
  /* @__PURE__ */ jsx18(
5482
5522
  Button,
5483
5523
  {
@@ -5512,11 +5552,11 @@ function LinkEditorPanel({ state, onClose }) {
5512
5552
  }
5513
5553
 
5514
5554
  // src/ui/link-modal/SectionPickerOverlay.tsx
5515
- import { useCallback, useEffect as useEffect4, useMemo as useMemo2, useRef as useRef4, useState as useState4 } from "react";
5555
+ import { useCallback, useEffect as useEffect4, useMemo as useMemo2, useRef as useRef5, useState as useState4 } from "react";
5516
5556
  import { createPortal } from "react-dom";
5517
5557
  import { ArrowLeft, Check } from "lucide-react";
5518
5558
  import { usePathname, useRouter } from "next/navigation";
5519
- import { jsx as jsx19, jsxs as jsxs11 } from "react/jsx-runtime";
5559
+ import { jsx as jsx19, jsxs as jsxs12 } from "react/jsx-runtime";
5520
5560
  var DIM_OVERLAY = "rgba(0, 0, 0, 0.45)";
5521
5561
  function rectsEqual(a, b) {
5522
5562
  if (a.size !== b.size) return false;
@@ -5597,10 +5637,10 @@ function SectionPickerOverlay({
5597
5637
  const pathname = usePathname();
5598
5638
  const [selectedId, setSelectedId] = useState4(null);
5599
5639
  const [hoveredId, setHoveredId] = useState4(null);
5600
- const pointerRef = useRef4(null);
5601
- const pointerScreenRef = useRef4(null);
5602
- const iframeOffsetTopRef = useRef4(null);
5603
- const sectionIdsRef = useRef4([]);
5640
+ const pointerRef = useRef5(null);
5641
+ const pointerScreenRef = useRef5(null);
5642
+ const iframeOffsetTopRef = useRef5(null);
5643
+ const sectionIdsRef = useRef5([]);
5604
5644
  const [chromeClip, setChromeClip] = useState4(
5605
5645
  () => ({
5606
5646
  top: 0,
@@ -5745,7 +5785,7 @@ function SectionPickerOverlay({
5745
5785
  const portalRoot = typeof document !== "undefined" ? document.querySelector("[data-ohw-bridge-root]") ?? document.body : null;
5746
5786
  if (!portalRoot) return null;
5747
5787
  return createPortal(
5748
- /* @__PURE__ */ jsxs11(
5788
+ /* @__PURE__ */ jsxs12(
5749
5789
  "div",
5750
5790
  {
5751
5791
  "data-ohw-section-picker": "",
@@ -5760,7 +5800,7 @@ function SectionPickerOverlay({
5760
5800
  {
5761
5801
  className: "pointer-events-auto fixed left-5 z-[2]",
5762
5802
  style: { top: chromeClip.top + 20 },
5763
- children: /* @__PURE__ */ jsxs11(
5803
+ children: /* @__PURE__ */ jsxs12(
5764
5804
  Button,
5765
5805
  {
5766
5806
  type: "button",
@@ -5792,8 +5832,8 @@ function SectionPickerOverlay({
5792
5832
  !isOnTargetPage ? /* @__PURE__ */ jsx19(
5793
5833
  "div",
5794
5834
  {
5795
- className: "pointer-events-none fixed left-1/2 z-[1] -translate-x-1/2 rounded-md bg-background/90 px-3 py-2 text-sm text-muted-foreground shadow-sm",
5796
- style: { top: chromeClip.top + 64 },
5835
+ className: "pointer-events-none fixed left-1/2 z-[1] -translate-x-1/2 rounded-md px-3 py-2 text-sm text-muted-foreground shadow-sm",
5836
+ style: { top: chromeClip.top + 64, backgroundColor: "var(--ohw-background, #ffffff)" },
5797
5837
  children: "Loading page preview\u2026"
5798
5838
  }
5799
5839
  ) : null,
@@ -5804,7 +5844,7 @@ function SectionPickerOverlay({
5804
5844
  const isSelected = selectedId === section.id;
5805
5845
  const isHovered = hoveredId === section.id;
5806
5846
  const isLit = isSelected || isHovered;
5807
- return /* @__PURE__ */ jsxs11(
5847
+ return /* @__PURE__ */ jsxs12(
5808
5848
  "button",
5809
5849
  {
5810
5850
  type: "button",
@@ -6018,7 +6058,7 @@ function useLinkModalState({
6018
6058
  }
6019
6059
 
6020
6060
  // src/ui/link-modal/LinkPopover.tsx
6021
- import { Fragment as Fragment4, jsx as jsx20, jsxs as jsxs12 } from "react/jsx-runtime";
6061
+ import { Fragment as Fragment4, jsx as jsx20, jsxs as jsxs13 } from "react/jsx-runtime";
6022
6062
  function postToParent(data) {
6023
6063
  window.parent?.postMessage(data, "*");
6024
6064
  }
@@ -6114,7 +6154,7 @@ function LinkPopover({
6114
6154
  );
6115
6155
  };
6116
6156
  }, [open, sectionPickerActive]);
6117
- return /* @__PURE__ */ jsxs12(Fragment4, { children: [
6157
+ return /* @__PURE__ */ jsxs13(Fragment4, { children: [
6118
6158
  /* @__PURE__ */ jsx20(
6119
6159
  Dialog2,
6120
6160
  {
@@ -6354,9 +6394,19 @@ function insertNavbarItemDom(index, href, label, afterAnchor) {
6354
6394
  }
6355
6395
  return primary;
6356
6396
  }
6397
+ function getNavbarLinkKeysInContainer(container) {
6398
+ return Array.from(container.querySelectorAll("[data-ohw-href-key]")).filter(isNavbarLinkItem).map((el) => el.getAttribute("data-ohw-href-key")).filter((key) => Boolean(key));
6399
+ }
6357
6400
  function applyNavOrderToContainer(container, order) {
6401
+ const desired = order.filter((key, i) => order.indexOf(key) === i);
6402
+ const current = getNavbarLinkKeysInContainer(container);
6403
+ const extras = current.filter((key) => !desired.includes(key));
6404
+ const expected = [...desired.filter((key) => current.includes(key)), ...extras];
6405
+ if (current.length === expected.length && current.every((key, i) => key === expected[i])) {
6406
+ return;
6407
+ }
6358
6408
  const seen = /* @__PURE__ */ new Set();
6359
- for (const hrefKey of order) {
6409
+ for (const hrefKey of desired) {
6360
6410
  if (seen.has(hrefKey)) continue;
6361
6411
  seen.add(hrefKey);
6362
6412
  const el = findCounterpartByHrefKey(hrefKey, container);
@@ -6511,7 +6561,7 @@ function Badge({ className, variant, ...props }) {
6511
6561
 
6512
6562
  // src/OhhwellsBridge.tsx
6513
6563
  import { Link as Link2 } from "lucide-react";
6514
- import { Fragment as Fragment5, jsx as jsx23, jsxs as jsxs13 } from "react/jsx-runtime";
6564
+ import { Fragment as Fragment5, jsx as jsx23, jsxs as jsxs14 } from "react/jsx-runtime";
6515
6565
  var PRIMARY2 = "#0885FE";
6516
6566
  var IMAGE_FADE_MS = 300;
6517
6567
  function runOpacityFade(el, onDone) {
@@ -6752,6 +6802,19 @@ function isDragHandleDisabled(el) {
6752
6802
  return raw === "true" || raw === "";
6753
6803
  });
6754
6804
  }
6805
+ function canDragNavigationItem(anchor) {
6806
+ const { key, disabled } = getNavigationItemReorderState(anchor);
6807
+ return Boolean(key) && !disabled;
6808
+ }
6809
+ function syncNavigationDragCursorAttrs() {
6810
+ document.querySelectorAll("nav [data-ohw-href-key], footer [data-ohw-href-key]").forEach((el) => {
6811
+ if (!isNavigationItem(el) || !canDragNavigationItem(el)) {
6812
+ el.removeAttribute("data-ohw-can-drag");
6813
+ return;
6814
+ }
6815
+ el.setAttribute("data-ohw-can-drag", "");
6816
+ });
6817
+ }
6755
6818
  function collectEditableNodes() {
6756
6819
  const nodes = Array.from(document.querySelectorAll("[data-ohw-editable]")).map((el) => {
6757
6820
  if (el.dataset.ohwEditable === "image") {
@@ -6921,18 +6984,20 @@ function getNavigationRoot(el) {
6921
6984
  if (explicit) return explicit;
6922
6985
  return el.closest("nav, footer, aside");
6923
6986
  }
6987
+ function countFooterNavItems(el) {
6988
+ return Array.from(el.querySelectorAll("[data-ohw-href-key]")).filter(isNavigationItem).length;
6989
+ }
6924
6990
  function findFooterItemGroup(item) {
6991
+ const explicit = item.closest("[data-ohw-footer-column]");
6992
+ if (explicit) return explicit;
6925
6993
  const footer = item.closest("footer");
6926
6994
  if (!footer) return null;
6927
6995
  let node = item.parentElement;
6928
6996
  while (node && node !== footer) {
6929
- const hasDirectNavItems = Array.from(
6930
- node.querySelectorAll(":scope > [data-ohw-href-key]")
6931
- ).some(isNavigationItem);
6932
- if (hasDirectNavItems) return node;
6997
+ if (countFooterNavItems(node) >= 2) return node;
6933
6998
  node = node.parentElement;
6934
6999
  }
6935
- return null;
7000
+ return footer;
6936
7001
  }
6937
7002
  function isNavigationRoot(el) {
6938
7003
  return el.hasAttribute("data-ohw-nav-root") || el.matches("nav, footer, aside");
@@ -6940,16 +7005,49 @@ function isNavigationRoot(el) {
6940
7005
  function isInferredFooterGroup(el) {
6941
7006
  const footer = el.closest("footer");
6942
7007
  if (!footer || el === footer) return false;
6943
- return Array.from(el.querySelectorAll(":scope > [data-ohw-href-key]")).some(isNavigationItem);
7008
+ if (el.hasAttribute("data-ohw-footer-column")) return true;
7009
+ return countFooterNavItems(el) >= 2;
6944
7010
  }
6945
7011
  function isNavigationContainer(el) {
6946
- return el.hasAttribute("data-ohw-nav-container") || isNavigationRoot(el) || isInferredFooterGroup(el);
7012
+ return el.hasAttribute("data-ohw-nav-container") || el.hasAttribute("data-ohw-footer-column") || isNavigationRoot(el) || isInferredFooterGroup(el);
7013
+ }
7014
+ function isNavbarLinksContainer(el) {
7015
+ return el.hasAttribute("data-ohw-nav-container");
7016
+ }
7017
+ function getFooterColumn(el) {
7018
+ return el.closest("[data-ohw-footer-column]");
7019
+ }
7020
+ function resolveFooterColumnSelectionTarget(target, clientX, clientY) {
7021
+ if (getNavigationItemAnchor(target)) return null;
7022
+ const column = getFooterColumn(target);
7023
+ if (!column) return null;
7024
+ if (isPointOverNavItem(column, clientX, clientY)) return null;
7025
+ return column;
6947
7026
  }
6948
7027
  function isPointOverNavigation(x, y) {
7028
+ const roots = /* @__PURE__ */ new Set();
6949
7029
  const navRoot = document.querySelector("[data-ohw-nav-root]") ?? document.querySelector("nav");
6950
- if (!navRoot) return false;
6951
- const r2 = navRoot.getBoundingClientRect();
6952
- return x >= r2.left && x <= r2.right && y >= r2.top && y <= r2.bottom;
7030
+ if (navRoot) roots.add(navRoot);
7031
+ const footer = document.querySelector("footer");
7032
+ if (footer) roots.add(footer);
7033
+ for (const root of roots) {
7034
+ const r2 = root.getBoundingClientRect();
7035
+ if (x >= r2.left && x <= r2.right && y >= r2.top && y <= r2.bottom) return true;
7036
+ }
7037
+ return false;
7038
+ }
7039
+ function findHoveredNavOrFooterContainer(x, y) {
7040
+ const candidates = [
7041
+ ...document.querySelectorAll("[data-ohw-nav-container]"),
7042
+ ...document.querySelectorAll("[data-ohw-footer-column]")
7043
+ ];
7044
+ for (const container of candidates) {
7045
+ const r2 = container.getBoundingClientRect();
7046
+ if (x < r2.left || x > r2.right || y < r2.top || y > r2.bottom) continue;
7047
+ if (isPointOverNavItem(container, x, y)) continue;
7048
+ return container;
7049
+ }
7050
+ return null;
6953
7051
  }
6954
7052
  function isPointOverNavItem(container, x, y) {
6955
7053
  return Array.from(container.querySelectorAll("[data-ohw-href-key]")).some((el) => {
@@ -6982,11 +7080,13 @@ function getNavigationSelectionParent(el) {
6982
7080
  if (isNavigationItem(el)) {
6983
7081
  const explicit = el.closest("[data-ohw-nav-container]");
6984
7082
  if (explicit) return explicit;
7083
+ const footerColumn = getFooterColumn(el);
7084
+ if (footerColumn) return footerColumn;
6985
7085
  const footerGroup = findFooterItemGroup(el);
6986
7086
  if (footerGroup) return footerGroup;
6987
7087
  return getNavigationRoot(el);
6988
7088
  }
6989
- if (el.hasAttribute("data-ohw-nav-container") || isInferredFooterGroup(el)) {
7089
+ if (el.hasAttribute("data-ohw-nav-container") || el.hasAttribute("data-ohw-footer-column") || isInferredFooterGroup(el)) {
6990
7090
  return getNavigationRoot(el);
6991
7091
  }
6992
7092
  return null;
@@ -7153,6 +7253,9 @@ var ICONS = {
7153
7253
  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"/>',
7154
7254
  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"/>'
7155
7255
  };
7256
+ var SELECTION_CHROME_GAP2 = 4;
7257
+ var TOOLBAR_STROKE_GAP2 = 4;
7258
+ var TOOLBAR_OFFSET_FROM_ELEMENT = SELECTION_CHROME_GAP2 + TOOLBAR_STROKE_GAP2;
7156
7259
  var TOOLBAR_GROUPS = [
7157
7260
  [
7158
7261
  { cmd: "bold", title: "Bold" },
@@ -7176,8 +7279,8 @@ function EditGlowChrome({
7176
7279
  reorderHrefKey,
7177
7280
  dragDisabled = false
7178
7281
  }) {
7179
- const GAP = 6;
7180
- return /* @__PURE__ */ jsxs13(
7282
+ const GAP = SELECTION_CHROME_GAP2;
7283
+ return /* @__PURE__ */ jsxs14(
7181
7284
  "div",
7182
7285
  {
7183
7286
  ref: elRef,
@@ -7200,7 +7303,7 @@ function EditGlowChrome({
7200
7303
  inset: 0,
7201
7304
  border: "2px solid var(--ohw-primary, #0885FE)",
7202
7305
  borderRadius: 8,
7203
- boxShadow: "0 0 0 4px rgba(8, 133, 254, 0.12)",
7306
+ boxShadow: "0 0 0 4px color-mix(in srgb, var(--ohw-primary, #0885FE) 12%, transparent)",
7204
7307
  pointerEvents: "none"
7205
7308
  }
7206
7309
  }
@@ -7248,6 +7351,7 @@ function applyVisibleViewport(el, parentScroll) {
7248
7351
  el.style.top = `${top}px`;
7249
7352
  el.style.height = `${height}px`;
7250
7353
  }
7354
+ var TOOLBAR_EDGE_MARGIN2 = 4;
7251
7355
  function clampToolbarToClip(top, transform, rect, clip, approxH, gap) {
7252
7356
  const isAbove = transform.includes("translateY(-100%)");
7253
7357
  let visualTop = isAbove ? top - approxH : top;
@@ -7271,10 +7375,16 @@ function clampToolbarToClip(top, transform, rect, clip, approxH, gap) {
7271
7375
  const pinnedTop = Math.min(maxBottom - approxH, Math.max(minTop, clip.top + gap));
7272
7376
  return { top: pinnedTop + approxH, transform: "translateX(-50%) translateY(-100%)" };
7273
7377
  }
7274
- function calcToolbarPos(rect, parentScroll, approxW = 306) {
7275
- const GAP = 8;
7378
+ function clampToolbarLeft(centerX, toolbarWidth) {
7379
+ const half = toolbarWidth / 2;
7380
+ return Math.max(
7381
+ TOOLBAR_EDGE_MARGIN2 + half,
7382
+ Math.min(centerX, window.innerWidth - TOOLBAR_EDGE_MARGIN2 - half)
7383
+ );
7384
+ }
7385
+ function calcToolbarPos(rect, parentScroll, toolbarW = 306) {
7386
+ const GAP = TOOLBAR_OFFSET_FROM_ELEMENT;
7276
7387
  const APPROX_H = 32;
7277
- const APPROX_W = approxW;
7278
7388
  const clip = getIframeVisibleClip(parentScroll);
7279
7389
  const clipTop = clip?.top ?? 0;
7280
7390
  const canvasTopInIframe = parentScroll != null ? parentScroll.headerH - parentScroll.iframeOffsetTop : 0;
@@ -7304,7 +7414,7 @@ function calcToolbarPos(rect, parentScroll, approxW = 306) {
7304
7414
  transform = clamped.transform;
7305
7415
  }
7306
7416
  const rawLeft = rect.left + rect.width / 2;
7307
- const left = Math.max(GAP + APPROX_W / 2, Math.min(rawLeft, window.innerWidth - GAP - APPROX_W / 2));
7417
+ const left = clampToolbarLeft(rawLeft, toolbarW);
7308
7418
  return { top, left, transform };
7309
7419
  }
7310
7420
  function resolveItemInteractionState(rect, parentScroll) {
@@ -7320,11 +7430,37 @@ function FloatingToolbar({
7320
7430
  showEditLink,
7321
7431
  onEditLink
7322
7432
  }) {
7323
- const { top, left, transform } = calcToolbarPos(rect, parentScroll, 330);
7433
+ const localRef = React10.useRef(null);
7434
+ const [measuredW, setMeasuredW] = React10.useState(330);
7435
+ const setRefs = React10.useCallback(
7436
+ (node) => {
7437
+ localRef.current = node;
7438
+ if (typeof elRef === "function") elRef(node);
7439
+ else if (elRef) elRef.current = node;
7440
+ if (node) {
7441
+ const w = node.offsetWidth;
7442
+ if (w > 0) setMeasuredW(w);
7443
+ }
7444
+ },
7445
+ [elRef]
7446
+ );
7447
+ React10.useLayoutEffect(() => {
7448
+ const node = localRef.current;
7449
+ if (!node) return;
7450
+ const update = () => {
7451
+ const w = node.offsetWidth;
7452
+ if (w > 0) setMeasuredW(w);
7453
+ };
7454
+ update();
7455
+ const ro = new ResizeObserver(update);
7456
+ ro.observe(node);
7457
+ return () => ro.disconnect();
7458
+ }, [showEditLink, activeCommands]);
7459
+ const { top, left, transform } = calcToolbarPos(rect, parentScroll, measuredW);
7324
7460
  return /* @__PURE__ */ jsx23(
7325
7461
  "div",
7326
7462
  {
7327
- ref: elRef,
7463
+ ref: setRefs,
7328
7464
  style: {
7329
7465
  position: "fixed",
7330
7466
  top,
@@ -7333,8 +7469,8 @@ function FloatingToolbar({
7333
7469
  zIndex: 2147483647,
7334
7470
  pointerEvents: "auto"
7335
7471
  },
7336
- children: /* @__PURE__ */ jsxs13(CustomToolbar, { children: [
7337
- TOOLBAR_GROUPS.map((btns, gi) => /* @__PURE__ */ jsxs13(React9.Fragment, { children: [
7472
+ children: /* @__PURE__ */ jsxs14(CustomToolbar, { children: [
7473
+ TOOLBAR_GROUPS.map((btns, gi) => /* @__PURE__ */ jsxs14(React10.Fragment, { children: [
7338
7474
  gi > 0 && /* @__PURE__ */ jsx23(CustomToolbarDivider, {}),
7339
7475
  btns.map((btn) => {
7340
7476
  const isActive = activeCommands.has(btn.cmd);
@@ -7475,54 +7611,55 @@ function OhhwellsBridge() {
7475
7611
  }
7476
7612
  }, []);
7477
7613
  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);
7614
+ const autoSaveTimers = useRef6(/* @__PURE__ */ new Map());
7615
+ const activeElRef = useRef6(null);
7616
+ const selectedElRef = useRef6(null);
7617
+ const originalContentRef = useRef6(null);
7618
+ const activeStateElRef = useRef6(null);
7619
+ const parentScrollRef = useRef6(null);
7620
+ const visibleViewportRef = useRef6(null);
7485
7621
  const [dialogPortalContainer, setDialogPortalContainer] = useState6(null);
7486
7622
  const attachVisibleViewport = useCallback3((node) => {
7487
7623
  visibleViewportRef.current = node;
7488
7624
  setDialogPortalContainer(node);
7489
7625
  if (node) applyVisibleViewport(node, parentScrollRef.current);
7490
7626
  }, []);
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);
7627
+ const toolbarElRef = useRef6(null);
7628
+ const glowElRef = useRef6(null);
7629
+ const hoveredImageRef = useRef6(null);
7630
+ const hoveredImageHasTextOverlapRef = useRef6(false);
7631
+ const dragOverElRef = useRef6(null);
7496
7632
  const [mediaHover, setMediaHover] = useState6(null);
7497
7633
  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(() => {
7634
+ const hoveredGapRef = useRef6(null);
7635
+ const imageUnhoverTimerRef = useRef6(null);
7636
+ const imageShowTimerRef = useRef6(null);
7637
+ const editStylesRef = useRef6(null);
7638
+ const activateRef = useRef6(() => {
7503
7639
  });
7504
- const deactivateRef = useRef5(() => {
7640
+ const deactivateRef = useRef6(() => {
7505
7641
  });
7506
- const selectRef = useRef5(() => {
7642
+ const selectRef = useRef6(() => {
7507
7643
  });
7508
- const selectFrameRef = useRef5(() => {
7644
+ const selectFrameRef = useRef6(() => {
7509
7645
  });
7510
- const deselectRef = useRef5(() => {
7646
+ const deselectRef = useRef6(() => {
7511
7647
  });
7512
- const reselectNavigationItemRef = useRef5(() => {
7648
+ const reselectNavigationItemRef = useRef6(() => {
7513
7649
  });
7514
- const commitNavigationTextEditRef = useRef5(() => {
7650
+ const commitNavigationTextEditRef = useRef6(() => {
7515
7651
  });
7516
- const refreshActiveCommandsRef = useRef5(() => {
7652
+ const refreshActiveCommandsRef = useRef6(() => {
7517
7653
  });
7518
- const postToParentRef = useRef5(postToParent2);
7654
+ const postToParentRef = useRef6(postToParent2);
7519
7655
  postToParentRef.current = postToParent2;
7520
- const sectionsLoadedRef = useRef5(false);
7521
- const pendingScheduleConfigRequests = useRef5([]);
7656
+ const sectionsLoadedRef = useRef6(false);
7657
+ const pendingScheduleConfigRequests = useRef6([]);
7522
7658
  const [toolbarRect, setToolbarRect] = useState6(null);
7523
7659
  const [toolbarVariant, setToolbarVariant] = useState6("none");
7524
- const toolbarVariantRef = useRef5("none");
7660
+ const toolbarVariantRef = useRef6("none");
7525
7661
  toolbarVariantRef.current = toolbarVariant;
7662
+ const [selectedIsCta, setSelectedIsCta] = useState6(false);
7526
7663
  const [reorderHrefKey, setReorderHrefKey] = useState6(null);
7527
7664
  const [reorderDragDisabled, setReorderDragDisabled] = useState6(false);
7528
7665
  const [toggleState, setToggleState] = useState6(null);
@@ -7530,24 +7667,24 @@ function OhhwellsBridge() {
7530
7667
  const [activeCommands, setActiveCommands] = useState6(/* @__PURE__ */ new Set());
7531
7668
  const [sectionGap, setSectionGap] = useState6(null);
7532
7669
  const [toolbarShowEditLink, setToolbarShowEditLink] = useState6(false);
7533
- const hoveredNavContainerRef = useRef5(null);
7670
+ const hoveredNavContainerRef = useRef6(null);
7534
7671
  const [hoveredNavContainerRect, setHoveredNavContainerRect] = useState6(null);
7535
- const hoveredItemElRef = useRef5(null);
7672
+ const hoveredItemElRef = useRef6(null);
7536
7673
  const [hoveredItemRect, setHoveredItemRect] = useState6(null);
7537
- const siblingHintElRef = useRef5(null);
7674
+ const siblingHintElRef = useRef6(null);
7538
7675
  const [siblingHintRect, setSiblingHintRect] = useState6(null);
7539
7676
  const [isItemDragging, setIsItemDragging] = useState6(false);
7540
7677
  const [linkPopover, setLinkPopover] = useState6(null);
7541
- const linkPopoverSessionRef = useRef5(null);
7542
- const addNavAfterAnchorRef = useRef5(null);
7543
- const editContentRef = useRef5({});
7678
+ const linkPopoverSessionRef = useRef6(null);
7679
+ const addNavAfterAnchorRef = useRef6(null);
7680
+ const editContentRef = useRef6({});
7544
7681
  const [sitePages, setSitePages] = useState6([]);
7545
7682
  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);
7683
+ const sectionsPrefetchGenRef = useRef6(0);
7684
+ const setLinkPopoverRef = useRef6(setLinkPopover);
7685
+ const linkPopoverPanelRef = useRef6(null);
7686
+ const linkPopoverOpenRef = useRef6(false);
7687
+ const linkPopoverGraceUntilRef = useRef6(0);
7551
7688
  setLinkPopoverRef.current = setLinkPopover;
7552
7689
  linkPopoverSessionRef.current = linkPopover;
7553
7690
  const bumpLinkPopoverGrace = () => {
@@ -7572,7 +7709,7 @@ function OhhwellsBridge() {
7572
7709
  );
7573
7710
  });
7574
7711
  }, [isEditMode, pathname]);
7575
- const runSectionsPrefetchRef = useRef5(runSectionsPrefetch);
7712
+ const runSectionsPrefetchRef = useRef6(runSectionsPrefetch);
7576
7713
  runSectionsPrefetchRef.current = runSectionsPrefetch;
7577
7714
  useEffect7(() => {
7578
7715
  if (!linkPopover) return;
@@ -7709,6 +7846,7 @@ function OhhwellsBridge() {
7709
7846
  }
7710
7847
  }
7711
7848
  el.removeAttribute("contenteditable");
7849
+ el.removeAttribute("data-ohw-editing");
7712
7850
  activeElRef.current = null;
7713
7851
  setReorderHrefKey(null);
7714
7852
  setReorderDragDisabled(false);
@@ -7721,8 +7859,15 @@ function OhhwellsBridge() {
7721
7859
  setToolbarShowEditLink(false);
7722
7860
  postToParent2({ type: "ow:exit-edit" });
7723
7861
  }, [postToParent2]);
7862
+ const clearSelectedAttr = useCallback3(() => {
7863
+ document.querySelectorAll("[data-ohw-selected]").forEach((el) => {
7864
+ el.removeAttribute("data-ohw-selected");
7865
+ });
7866
+ }, []);
7724
7867
  const deselect = useCallback3(() => {
7868
+ clearSelectedAttr();
7725
7869
  selectedElRef.current = null;
7870
+ setSelectedIsCta(false);
7726
7871
  setReorderHrefKey(null);
7727
7872
  setReorderDragDisabled(false);
7728
7873
  siblingHintElRef.current = null;
@@ -7734,9 +7879,15 @@ function OhhwellsBridge() {
7734
7879
  setToolbarRect(null);
7735
7880
  setToolbarVariant("none");
7736
7881
  }
7737
- }, []);
7882
+ }, [clearSelectedAttr]);
7883
+ const markSelected = useCallback3((el) => {
7884
+ clearSelectedAttr();
7885
+ el.setAttribute("data-ohw-selected", "");
7886
+ }, [clearSelectedAttr]);
7738
7887
  const reselectNavigationItem = useCallback3((navAnchor) => {
7739
7888
  selectedElRef.current = navAnchor;
7889
+ markSelected(navAnchor);
7890
+ setSelectedIsCta(isNavbarButton2(navAnchor));
7740
7891
  const { key, disabled } = getNavigationItemReorderState(navAnchor);
7741
7892
  setReorderHrefKey(key);
7742
7893
  setReorderDragDisabled(disabled);
@@ -7744,7 +7895,7 @@ function OhhwellsBridge() {
7744
7895
  setToolbarRect(navAnchor.getBoundingClientRect());
7745
7896
  setToolbarShowEditLink(false);
7746
7897
  setActiveCommands(/* @__PURE__ */ new Set());
7747
- }, []);
7898
+ }, [markSelected]);
7748
7899
  const commitNavigationTextEdit = useCallback3((navAnchor) => {
7749
7900
  const el = activeElRef.current;
7750
7901
  if (!el) return;
@@ -7764,6 +7915,7 @@ function OhhwellsBridge() {
7764
7915
  }
7765
7916
  }
7766
7917
  el.removeAttribute("contenteditable");
7918
+ el.removeAttribute("data-ohw-editing");
7767
7919
  activeElRef.current = null;
7768
7920
  setMaxBadge(null);
7769
7921
  setActiveCommands(/* @__PURE__ */ new Set());
@@ -7797,6 +7949,8 @@ function OhhwellsBridge() {
7797
7949
  if (!isNavigationItem(anchor)) return;
7798
7950
  if (activeElRef.current) deactivate();
7799
7951
  selectedElRef.current = anchor;
7952
+ markSelected(anchor);
7953
+ setSelectedIsCta(isNavbarButton2(anchor));
7800
7954
  clearHrefKeyHover(anchor);
7801
7955
  hoveredNavContainerRef.current = null;
7802
7956
  setHoveredNavContainerRect(null);
@@ -7812,11 +7966,13 @@ function OhhwellsBridge() {
7812
7966
  setToolbarRect(anchor.getBoundingClientRect());
7813
7967
  setToolbarShowEditLink(false);
7814
7968
  setActiveCommands(/* @__PURE__ */ new Set());
7815
- }, [deactivate]);
7969
+ }, [deactivate, markSelected]);
7816
7970
  const selectFrame = useCallback3((el) => {
7817
7971
  if (!isNavigationContainer(el)) return;
7818
7972
  if (activeElRef.current) deactivate();
7819
7973
  selectedElRef.current = el;
7974
+ markSelected(el);
7975
+ setSelectedIsCta(false);
7820
7976
  clearHrefKeyHover(el);
7821
7977
  hoveredNavContainerRef.current = null;
7822
7978
  setHoveredNavContainerRect(null);
@@ -7831,10 +7987,12 @@ function OhhwellsBridge() {
7831
7987
  setToolbarRect(el.getBoundingClientRect());
7832
7988
  setToolbarShowEditLink(false);
7833
7989
  setActiveCommands(/* @__PURE__ */ new Set());
7834
- }, [deactivate]);
7990
+ }, [deactivate, markSelected]);
7835
7991
  const activate = useCallback3((el, options) => {
7836
7992
  if (activeElRef.current === el) return;
7993
+ clearSelectedAttr();
7837
7994
  selectedElRef.current = null;
7995
+ setSelectedIsCta(false);
7838
7996
  deactivate();
7839
7997
  if (hoveredImageRef.current) {
7840
7998
  hoveredImageRef.current = null;
@@ -7845,13 +8003,23 @@ function OhhwellsBridge() {
7845
8003
  setSiblingHintRect(null);
7846
8004
  setIsItemDragging(false);
7847
8005
  el.setAttribute("contenteditable", "true");
8006
+ el.setAttribute("data-ohw-editing", "");
7848
8007
  el.removeAttribute("data-ohw-hovered");
7849
8008
  el.closest("[data-ohw-href-key]")?.removeAttribute("data-ohw-hovered");
7850
8009
  activeElRef.current = el;
7851
8010
  originalContentRef.current = el.innerHTML;
7852
- el.focus();
8011
+ el.focus({ preventScroll: true });
7853
8012
  if (options?.caretX !== void 0 && options?.caretY !== void 0) {
7854
8013
  placeCaretAtPoint(el, options.caretX, options.caretY);
8014
+ } else {
8015
+ const selection = window.getSelection();
8016
+ if (selection && selection.rangeCount === 0) {
8017
+ const range = document.createRange();
8018
+ range.selectNodeContents(el);
8019
+ range.collapse(false);
8020
+ selection.removeAllRanges();
8021
+ selection.addRange(range);
8022
+ }
7855
8023
  }
7856
8024
  setToolbarShowEditLink(Boolean(getHrefKeyFromElement(el)));
7857
8025
  const navAnchor = getNavigationItemAnchor(el);
@@ -7866,13 +8034,13 @@ function OhhwellsBridge() {
7866
8034
  setToolbarRect(getEditMeasureEl(el).getBoundingClientRect());
7867
8035
  postToParent2({ type: "ow:enter-edit", key: el.dataset.ohwKey });
7868
8036
  requestAnimationFrame(() => refreshActiveCommandsRef.current());
7869
- }, [deactivate, postToParent2]);
8037
+ }, [clearSelectedAttr, deactivate, postToParent2]);
7870
8038
  activateRef.current = activate;
7871
8039
  deactivateRef.current = deactivate;
7872
8040
  selectRef.current = select;
7873
8041
  selectFrameRef.current = selectFrame;
7874
8042
  deselectRef.current = deselect;
7875
- useLayoutEffect2(() => {
8043
+ useLayoutEffect3(() => {
7876
8044
  if (!subdomain || isEditMode) {
7877
8045
  setFetchState("done");
7878
8046
  return;
@@ -7990,7 +8158,7 @@ function OhhwellsBridge() {
7990
8158
  if (debounceTimer) clearTimeout(debounceTimer);
7991
8159
  };
7992
8160
  }, [subdomain, isEditMode, pathname]);
7993
- useLayoutEffect2(() => {
8161
+ useLayoutEffect3(() => {
7994
8162
  const el = document.getElementById("ohw-loader");
7995
8163
  if (!el) return;
7996
8164
  const visible = Boolean(subdomain) && fetchState !== "done";
@@ -8013,15 +8181,48 @@ function OhhwellsBridge() {
8013
8181
  if (!subdomain) return {};
8014
8182
  return contentCache.get(subdomain) ?? {};
8015
8183
  };
8016
- const run = () => reconcileNavbarItemsFromContent(contentForNav());
8184
+ const observeRoots = () => [document.querySelector("nav"), document.querySelector("footer")].filter(
8185
+ (el) => Boolean(el)
8186
+ );
8187
+ const roots = observeRoots();
8188
+ if (roots.length === 0) {
8189
+ reconcileNavbarItemsFromContent(contentForNav());
8190
+ if (isEditMode) syncNavigationDragCursorAttrs();
8191
+ return;
8192
+ }
8193
+ let rafId = null;
8194
+ let applying = false;
8195
+ let observer = null;
8196
+ const attach = () => {
8197
+ for (const root of observeRoots()) {
8198
+ observer?.observe(root, { childList: true, subtree: true });
8199
+ }
8200
+ };
8201
+ const run = () => {
8202
+ if (applying) return;
8203
+ applying = true;
8204
+ observer?.disconnect();
8205
+ try {
8206
+ reconcileNavbarItemsFromContent(contentForNav());
8207
+ if (isEditMode) syncNavigationDragCursorAttrs();
8208
+ } finally {
8209
+ applying = false;
8210
+ attach();
8211
+ }
8212
+ };
8213
+ const schedule = () => {
8214
+ if (applying || rafId != null) return;
8215
+ rafId = requestAnimationFrame(() => {
8216
+ rafId = null;
8217
+ run();
8218
+ });
8219
+ };
8220
+ observer = new MutationObserver(schedule);
8017
8221
  run();
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();
8222
+ return () => {
8223
+ if (rafId != null) cancelAnimationFrame(rafId);
8224
+ observer?.disconnect();
8225
+ };
8025
8226
  }, [isEditMode, pathname, subdomain, fetchState]);
8026
8227
  useEffect7(() => {
8027
8228
  if (!isEditMode) return;
@@ -8069,13 +8270,18 @@ function OhhwellsBridge() {
8069
8270
  editStylesRef.current?.forceHover.remove();
8070
8271
  editStylesRef.current?.stateViews.remove();
8071
8272
  editStylesRef.current = null;
8273
+ document.querySelectorAll("[data-ohw-can-drag]").forEach((el) => {
8274
+ el.removeAttribute("data-ohw-can-drag");
8275
+ });
8072
8276
  return;
8073
8277
  }
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 = `
8278
+ const existing = editStylesRef.current;
8279
+ let initialVh = window.innerHeight;
8280
+ if (existing?.base.textContent) {
8281
+ const match = existing.base.textContent.match(/\.min-h-screen[^{]*\{[^}]*min-height:\s*(\d+)px/);
8282
+ if (match) initialVh = parseInt(match[1], 10);
8283
+ }
8284
+ const baseCss = `
8079
8285
  html { height: auto !important; }
8080
8286
  body { height: auto !important; min-height: 0 !important; overflow: hidden !important; }
8081
8287
  .min-h-screen, .min-h-svh, .min-h-dvh { min-height: ${initialVh}px !important; }
@@ -8086,7 +8292,25 @@ function OhhwellsBridge() {
8086
8292
  [data-ohw-editable] {
8087
8293
  display: block;
8088
8294
  }
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; }
8295
+ /* Body text (no item-action toolbar) \u2014 first click enters text edit \u2192 I-beam.
8296
+ Exclude select-first nav/footer labels so grab/default on [data-ohw-href-key] can win. */
8297
+ [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] *)) {
8298
+ cursor: text !important;
8299
+ }
8300
+ /* Select-first items: default arrow; grab only when drag-start is allowed (data-ohw-can-drag). */
8301
+ [data-ohw-href-key]:not([data-ohw-selected]),
8302
+ [data-ohw-href-key]:not([data-ohw-selected]) * {
8303
+ cursor: default !important;
8304
+ }
8305
+ [data-ohw-href-key][data-ohw-can-drag]:not([data-ohw-selected]),
8306
+ [data-ohw-href-key][data-ohw-can-drag]:not([data-ohw-selected]) * {
8307
+ cursor: grab !important;
8308
+ }
8309
+ /* Selected (action-toolbar) \u2014 next click enters text edit at caret. */
8310
+ [data-ohw-href-key][data-ohw-selected],
8311
+ [data-ohw-href-key][data-ohw-selected] * {
8312
+ cursor: text !important;
8313
+ }
8090
8314
  [data-ohw-editable="image"], [data-ohw-editable="image"] *,
8091
8315
  [data-ohw-editable="video"], [data-ohw-editable="video"] *,
8092
8316
  [data-ohw-editable="bg-image"], [data-ohw-editable="bg-image"] * { cursor: pointer !important; }
@@ -8102,9 +8326,17 @@ function OhhwellsBridge() {
8102
8326
  outline: none !important;
8103
8327
  outline-offset: 0 !important;
8104
8328
  }
8105
- [data-ohw-editable][contenteditable] {
8329
+ /* Text edit wins over grab/default (must beat [data-ohw-can-drag] *). */
8330
+ [data-ohw-editing],
8331
+ [data-ohw-editing] *,
8332
+ [data-ohw-editable][contenteditable],
8333
+ [data-ohw-editable][contenteditable] *,
8334
+ nav [data-ohw-href-key] [data-ohw-editable][contenteditable],
8335
+ nav [data-ohw-href-key] [data-ohw-editable][contenteditable] *,
8336
+ footer [data-ohw-href-key] [data-ohw-editable][contenteditable],
8337
+ footer [data-ohw-href-key] [data-ohw-editable][contenteditable] * {
8106
8338
  outline: none !important;
8107
- caret-color: ${PRIMARY2};
8339
+ caret-color: ${PRIMARY2} !important;
8108
8340
  cursor: text !important;
8109
8341
  }
8110
8342
  [data-ohw-editable][contenteditable]::selection,
@@ -8113,6 +8345,9 @@ function OhhwellsBridge() {
8113
8345
  [data-ohw-editable-state][data-ohw-active-state] [data-ohw-editable] { pointer-events: auto !important; }
8114
8346
  [data-ohw-editable-state][data-ohw-active-state][data-ohw-editable] { pointer-events: auto !important; }
8115
8347
  `;
8348
+ if (!existing) {
8349
+ const base = document.createElement("style");
8350
+ base.setAttribute("data-ohw-edit-style", "");
8116
8351
  const forceHover = document.createElement("style");
8117
8352
  forceHover.setAttribute("data-ohw-active-state-style", "");
8118
8353
  const stateViews = document.createElement("style");
@@ -8128,6 +8363,8 @@ function OhhwellsBridge() {
8128
8363
  document.head.appendChild(stateViews);
8129
8364
  editStylesRef.current = { base, forceHover, stateViews };
8130
8365
  }
8366
+ editStylesRef.current.base.textContent = baseCss;
8367
+ syncNavigationDragCursorAttrs();
8131
8368
  refreshStateRules();
8132
8369
  const handleClick = (e) => {
8133
8370
  const target = e.target;
@@ -8143,10 +8380,34 @@ function OhhwellsBridge() {
8143
8380
  selectFrameRef.current(navFromChrome);
8144
8381
  return;
8145
8382
  }
8383
+ const footerFromChrome = resolveFooterColumnSelectionTarget(target, e.clientX, e.clientY);
8384
+ if (footerFromChrome) {
8385
+ e.preventDefault();
8386
+ e.stopPropagation();
8387
+ selectFrameRef.current(footerFromChrome);
8388
+ return;
8389
+ }
8146
8390
  e.preventDefault();
8147
8391
  e.stopPropagation();
8148
8392
  return;
8149
8393
  }
8394
+ if (activeElRef.current) {
8395
+ const active = activeElRef.current;
8396
+ const inActive = active === target || active.contains(target);
8397
+ const navAnchor = getNavigationItemAnchor(active);
8398
+ const inActiveNav = Boolean(
8399
+ navAnchor && (navAnchor === target || navAnchor.contains(target))
8400
+ );
8401
+ if (inActive || inActiveNav) {
8402
+ e.preventDefault();
8403
+ e.stopPropagation();
8404
+ if (e.detail < 2) {
8405
+ placeCaretAtPoint(active, e.clientX, e.clientY);
8406
+ refreshActiveCommandsRef.current();
8407
+ }
8408
+ return;
8409
+ }
8410
+ }
8150
8411
  const editable = target.closest("[data-ohw-editable]");
8151
8412
  if (editable) {
8152
8413
  if (editable.dataset.ohwEditable === "link") {
@@ -8201,6 +8462,13 @@ function OhhwellsBridge() {
8201
8462
  selectFrameRef.current(navContainerToSelect);
8202
8463
  return;
8203
8464
  }
8465
+ const footerColumnToSelect = resolveFooterColumnSelectionTarget(target, e.clientX, e.clientY);
8466
+ if (footerColumnToSelect) {
8467
+ e.preventDefault();
8468
+ e.stopPropagation();
8469
+ selectFrameRef.current(footerColumnToSelect);
8470
+ return;
8471
+ }
8204
8472
  const selectedContainer = selectedElRef.current;
8205
8473
  if (selectedContainer && isNavigationContainer(selectedContainer)) {
8206
8474
  const navItem = getNavigationItemAnchor(target);
@@ -8261,15 +8529,15 @@ function OhhwellsBridge() {
8261
8529
  if (document.documentElement.hasAttribute("data-ohw-section-picking")) return;
8262
8530
  const target = e.target;
8263
8531
  if (toolbarVariantRef.current !== "select-frame") {
8264
- const navContainer = target.closest("[data-ohw-nav-container]");
8265
- if (navContainer && !getNavigationItemAnchor(target)) {
8266
- hoveredNavContainerRef.current = navContainer;
8267
- setHoveredNavContainerRect(navContainer.getBoundingClientRect());
8532
+ const frameContainer = target.closest("[data-ohw-nav-container]") ?? target.closest("[data-ohw-footer-column]");
8533
+ if (frameContainer && !getNavigationItemAnchor(target)) {
8534
+ hoveredNavContainerRef.current = frameContainer;
8535
+ setHoveredNavContainerRect(frameContainer.getBoundingClientRect());
8268
8536
  hoveredItemElRef.current = null;
8269
8537
  setHoveredItemRect(null);
8270
8538
  return;
8271
8539
  }
8272
- if (!target.closest("[data-ohw-nav-container]")) {
8540
+ if (!target.closest("[data-ohw-nav-container], [data-ohw-footer-column]")) {
8273
8541
  hoveredNavContainerRef.current = null;
8274
8542
  setHoveredNavContainerRect(null);
8275
8543
  }
@@ -8302,9 +8570,9 @@ function OhhwellsBridge() {
8302
8570
  };
8303
8571
  const handleMouseOut = (e) => {
8304
8572
  const target = e.target;
8305
- if (target.closest("[data-ohw-nav-container]")) {
8573
+ if (target.closest("[data-ohw-nav-container], [data-ohw-footer-column]")) {
8306
8574
  const related2 = e.relatedTarget instanceof Element ? e.relatedTarget : null;
8307
- if (related2?.closest("[data-ohw-nav-container], [data-ohw-navbar-container-chrome], [data-ohw-navbar-add-button]")) {
8575
+ if (related2?.closest("[data-ohw-nav-container], [data-ohw-footer-column], [data-ohw-navbar-container-chrome], [data-ohw-navbar-add-button]")) {
8308
8576
  return;
8309
8577
  }
8310
8578
  hoveredNavContainerRef.current = null;
@@ -8445,39 +8713,43 @@ function OhhwellsBridge() {
8445
8713
  setHoveredNavContainerRect(null);
8446
8714
  return;
8447
8715
  }
8448
- const navContainer = document.querySelector("[data-ohw-nav-container]");
8449
- if (!navContainer) {
8450
- hoveredNavContainerRef.current = null;
8451
- setHoveredNavContainerRect(null);
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) {
8457
- hoveredNavContainerRef.current = null;
8458
- setHoveredNavContainerRect(null);
8459
- return;
8716
+ const frameContainers = [
8717
+ ...document.querySelectorAll("[data-ohw-nav-container]"),
8718
+ ...document.querySelectorAll("[data-ohw-footer-column]")
8719
+ ];
8720
+ let overFrame = null;
8721
+ for (const container of frameContainers) {
8722
+ const containerRect = container.getBoundingClientRect();
8723
+ const overContainer = x >= containerRect.left && x <= containerRect.right && y >= containerRect.top && y <= containerRect.bottom;
8724
+ if (!overContainer) continue;
8725
+ const navItemHit = Array.from(
8726
+ container.querySelectorAll("[data-ohw-href-key]")
8727
+ ).find((el) => {
8728
+ if (!isNavigationItem(el) || isNavbarButton2(el)) return false;
8729
+ const itemRect = el.getBoundingClientRect();
8730
+ return x >= itemRect.left && x <= itemRect.right && y >= itemRect.top && y <= itemRect.bottom;
8731
+ });
8732
+ if (navItemHit) {
8733
+ hoveredNavContainerRef.current = null;
8734
+ setHoveredNavContainerRect(null);
8735
+ const selected = selectedElRef.current;
8736
+ if (selected !== navItemHit && !selected?.contains(navItemHit)) {
8737
+ clearHrefKeyHover(navItemHit);
8738
+ hoveredItemElRef.current = navItemHit;
8739
+ setHoveredItemRect(navItemHit.getBoundingClientRect());
8740
+ }
8741
+ return;
8742
+ }
8743
+ overFrame = container;
8744
+ break;
8460
8745
  }
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) {
8746
+ if (!overFrame) {
8469
8747
  hoveredNavContainerRef.current = null;
8470
8748
  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());
8476
- }
8477
8749
  return;
8478
8750
  }
8479
- hoveredNavContainerRef.current = navContainer;
8480
- setHoveredNavContainerRect(containerRect);
8751
+ hoveredNavContainerRef.current = overFrame;
8752
+ setHoveredNavContainerRect(overFrame.getBoundingClientRect());
8481
8753
  hoveredItemElRef.current = null;
8482
8754
  setHoveredItemRect(null);
8483
8755
  };
@@ -8909,8 +9181,8 @@ function OhhwellsBridge() {
8909
9181
  if (sibling !== el) sibling.innerHTML = html;
8910
9182
  });
8911
9183
  const timers = autoSaveTimers.current;
8912
- const existing = timers.get(key);
8913
- if (existing) clearTimeout(existing);
9184
+ const existing2 = timers.get(key);
9185
+ if (existing2) clearTimeout(existing2);
8914
9186
  timers.set(key, setTimeout(() => {
8915
9187
  timers.delete(key);
8916
9188
  postToParentRef.current({ type: "ow:change", nodes: [{ key, text: html }] });
@@ -8953,6 +9225,7 @@ function OhhwellsBridge() {
8953
9225
  }
8954
9226
  editContentRef.current = { ...editContentRef.current, ...content };
8955
9227
  reconcileNavbarItemsFromContent(editContentRef.current);
9228
+ syncNavigationDragCursorAttrs();
8956
9229
  enforceLinkHrefs();
8957
9230
  postToParentRef.current({ type: "ow:hydrate-done" });
8958
9231
  };
@@ -8973,6 +9246,48 @@ function OhhwellsBridge() {
8973
9246
  if (e.data?.type !== "ui:escape") return;
8974
9247
  if (linkPopoverOpenRef.current) {
8975
9248
  setLinkPopoverRef.current(null);
9249
+ return;
9250
+ }
9251
+ if (activeElRef.current) {
9252
+ const hrefCtx = getHrefKeyFromElement(activeElRef.current);
9253
+ const navAnchor = hrefCtx ? getNavigationItemAnchor(hrefCtx.anchor) : null;
9254
+ if (navAnchor) {
9255
+ const el = activeElRef.current;
9256
+ if (originalContentRef.current !== null) {
9257
+ el.innerHTML = originalContentRef.current;
9258
+ }
9259
+ el.removeAttribute("contenteditable");
9260
+ el.removeAttribute("data-ohw-editing");
9261
+ activeElRef.current = null;
9262
+ setMaxBadge(null);
9263
+ setActiveCommands(/* @__PURE__ */ new Set());
9264
+ setToolbarShowEditLink(false);
9265
+ postToParentRef.current({ type: "ow:exit-edit" });
9266
+ reselectNavigationItemRef.current(navAnchor);
9267
+ return;
9268
+ }
9269
+ if (originalContentRef.current !== null) {
9270
+ activeElRef.current.innerHTML = originalContentRef.current;
9271
+ const key = activeElRef.current.dataset.ohwKey;
9272
+ if (key) {
9273
+ postToParentRef.current({ type: "ow:change", nodes: [{ key, text: originalContentRef.current }] });
9274
+ }
9275
+ }
9276
+ deselectRef.current();
9277
+ deactivateRef.current();
9278
+ return;
9279
+ }
9280
+ if (selectedElRef.current) {
9281
+ if (toolbarVariantRef.current === "select-frame") {
9282
+ deselectRef.current();
9283
+ return;
9284
+ }
9285
+ const parent = getNavigationSelectionParent(selectedElRef.current);
9286
+ if (parent && isNavigationContainer(parent)) {
9287
+ selectFrameRef.current(parent);
9288
+ } else {
9289
+ deselectRef.current();
9290
+ }
8976
9291
  }
8977
9292
  };
8978
9293
  window.addEventListener("message", handleUiEscape);
@@ -8997,7 +9312,7 @@ function OhhwellsBridge() {
8997
9312
  return;
8998
9313
  }
8999
9314
  const parent = getNavigationSelectionParent(selectedElRef.current);
9000
- if (parent) {
9315
+ if (parent && isNavigationContainer(parent)) {
9001
9316
  e.preventDefault();
9002
9317
  selectFrameRef.current(parent);
9003
9318
  } else {
@@ -9015,6 +9330,7 @@ function OhhwellsBridge() {
9015
9330
  el2.innerHTML = originalContentRef.current;
9016
9331
  }
9017
9332
  el2.removeAttribute("contenteditable");
9333
+ el2.removeAttribute("data-ohw-editing");
9018
9334
  activeElRef.current = null;
9019
9335
  setMaxBadge(null);
9020
9336
  setActiveCommands(/* @__PURE__ */ new Set());
@@ -9202,15 +9518,15 @@ function OhhwellsBridge() {
9202
9518
  };
9203
9519
  const applyToolbarPos = (rect) => {
9204
9520
  const ps = parentScrollRef.current;
9205
- const approxW = 330;
9521
+ const measuredW = toolbarElRef.current?.offsetWidth || 330;
9206
9522
  if (toolbarElRef.current) {
9207
- const { top, left, transform } = calcToolbarPos(rect, ps, approxW);
9523
+ const { top, left, transform } = calcToolbarPos(rect, ps, measuredW);
9208
9524
  toolbarElRef.current.style.top = `${top}px`;
9209
9525
  toolbarElRef.current.style.left = `${left}px`;
9210
9526
  toolbarElRef.current.style.transform = transform;
9211
9527
  }
9212
9528
  if (glowElRef.current) {
9213
- const GAP = 6;
9529
+ const GAP = SELECTION_CHROME_GAP2;
9214
9530
  glowElRef.current.style.top = `${rect.top - GAP}px`;
9215
9531
  glowElRef.current.style.left = `${rect.left - GAP}px`;
9216
9532
  glowElRef.current.style.width = `${rect.width + GAP * 2}px`;
@@ -9298,6 +9614,11 @@ function OhhwellsBridge() {
9298
9614
  return;
9299
9615
  }
9300
9616
  }
9617
+ const footerColumn = findHoveredNavOrFooterContainer(clientX, clientY);
9618
+ if (footerColumn?.hasAttribute("data-ohw-footer-column")) {
9619
+ selectFrameRef.current(footerColumn);
9620
+ return;
9621
+ }
9301
9622
  deactivateRef.current();
9302
9623
  };
9303
9624
  window.addEventListener("message", handleSave);
@@ -9563,7 +9884,7 @@ function OhhwellsBridge() {
9563
9884
  [postToParent2]
9564
9885
  );
9565
9886
  return bridgeRoot ? createPortal2(
9566
- /* @__PURE__ */ jsxs13(Fragment5, { children: [
9887
+ /* @__PURE__ */ jsxs14(Fragment5, { children: [
9567
9888
  /* @__PURE__ */ jsx23("div", { ref: attachVisibleViewport, "data-ohw-visible-viewport": "", "aria-hidden": true }),
9568
9889
  Object.entries(uploadingRects).map(([key, { rect, fadingOut }]) => /* @__PURE__ */ jsx23(
9569
9890
  MediaOverlay,
@@ -9587,7 +9908,7 @@ function OhhwellsBridge() {
9587
9908
  ),
9588
9909
  siblingHintRect && !isItemDragging && /* @__PURE__ */ jsx23(ItemInteractionLayer, { rect: siblingHintRect, state: "sibling-hint" }),
9589
9910
  hoveredNavContainerRect && toolbarVariant !== "select-frame" && !linkPopover && !isItemDragging && !siblingHintRect && /* @__PURE__ */ jsx23(ItemInteractionLayer, { rect: hoveredNavContainerRect, state: "hover" }),
9590
- toolbarVariant === "select-frame" && toolbarRect && !linkPopover && !isItemDragging && /* @__PURE__ */ jsx23(
9911
+ toolbarVariant === "select-frame" && toolbarRect && !linkPopover && !isItemDragging && selectedElRef.current && isNavbarLinksContainer(selectedElRef.current) && /* @__PURE__ */ jsx23(
9591
9912
  NavbarContainerChrome,
9592
9913
  {
9593
9914
  rect: toolbarRect,
@@ -9612,12 +9933,14 @@ function OhhwellsBridge() {
9612
9933
  onEditLink: openLinkPopoverForSelected,
9613
9934
  addItemDisabled: true,
9614
9935
  editLinkDisabled: false,
9615
- moreDisabled: true
9936
+ moreDisabled: true,
9937
+ showAddItem: !selectedIsCta,
9938
+ showMore: !selectedIsCta
9616
9939
  }
9617
9940
  ) : void 0
9618
9941
  }
9619
9942
  ),
9620
- toolbarRect && toolbarVariant === "rich-text" && /* @__PURE__ */ jsxs13(Fragment5, { children: [
9943
+ toolbarRect && toolbarVariant === "rich-text" && /* @__PURE__ */ jsxs14(Fragment5, { children: [
9621
9944
  /* @__PURE__ */ jsx23(
9622
9945
  EditGlowChrome,
9623
9946
  {
@@ -9640,7 +9963,7 @@ function OhhwellsBridge() {
9640
9963
  }
9641
9964
  )
9642
9965
  ] }),
9643
- maxBadge && /* @__PURE__ */ jsxs13(
9966
+ maxBadge && /* @__PURE__ */ jsxs14(
9644
9967
  "div",
9645
9968
  {
9646
9969
  "data-ohw-max-badge": "",
@@ -9675,7 +9998,7 @@ function OhhwellsBridge() {
9675
9998
  onStateChange: handleStateChange
9676
9999
  }
9677
10000
  ),
9678
- sectionGap && !linkPopover && /* @__PURE__ */ jsxs13(
10001
+ sectionGap && !linkPopover && /* @__PURE__ */ jsxs14(
9679
10002
  "div",
9680
10003
  {
9681
10004
  "data-ohw-section-insert-line": "",
@@ -9723,7 +10046,7 @@ function OhhwellsBridge() {
9723
10046
  }
9724
10047
 
9725
10048
  // src/ui/drop-indicator.tsx
9726
- import * as React10 from "react";
10049
+ import * as React11 from "react";
9727
10050
  import { jsx as jsx24 } from "react/jsx-runtime";
9728
10051
  var dropIndicatorVariants = cva(
9729
10052
  "ov-gap-line pointer-events-none shrink-0 transition-opacity duration-150",
@@ -9746,7 +10069,7 @@ var dropIndicatorVariants = cva(
9746
10069
  }
9747
10070
  }
9748
10071
  );
9749
- var DropIndicator = React10.forwardRef(
10072
+ var DropIndicator = React11.forwardRef(
9750
10073
  ({ className, direction, state, ...props }, ref) => {
9751
10074
  return /* @__PURE__ */ jsx24(
9752
10075
  "div",