@ohhwells/bridge 0.1.39 → 0.1.41

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 useCallback2, useEffect as useEffect4, useLayoutEffect as useLayoutEffect2, useRef as useRef4, useState as useState5 } 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
 
@@ -10,6 +10,9 @@ var linkHrefStore = /* @__PURE__ */ new Map();
10
10
  function setStoredLinkHref(key, href) {
11
11
  linkHrefStore.set(key, href);
12
12
  }
13
+ function getStoredLinkHref(key) {
14
+ return linkHrefStore.get(key);
15
+ }
13
16
  function enforceLinkHrefs() {
14
17
  document.querySelectorAll("[data-ohw-href-key]").forEach((el) => {
15
18
  const key = el.getAttribute("data-ohw-href-key") ?? "";
@@ -4349,6 +4352,7 @@ function ItemActionToolbar({
4349
4352
  onEditLink,
4350
4353
  onAddItem,
4351
4354
  onMore,
4355
+ editLinkDisabled = false,
4352
4356
  addItemDisabled = true,
4353
4357
  moreDisabled = true,
4354
4358
  tooltipSide = "bottom"
@@ -4357,10 +4361,12 @@ function ItemActionToolbar({
4357
4361
  /* @__PURE__ */ jsx8(
4358
4362
  ToolbarActionTooltip,
4359
4363
  {
4360
- label: "Add link",
4364
+ label: "Edit link",
4361
4365
  side: tooltipSide,
4366
+ disabled: editLinkDisabled,
4362
4367
  buttonProps: {
4363
4368
  onMouseDown: (e) => {
4369
+ if (editLinkDisabled) return;
4364
4370
  e.preventDefault();
4365
4371
  e.stopPropagation();
4366
4372
  onEditLink?.();
@@ -4747,8 +4753,8 @@ function MediaOverlay({
4747
4753
  }
4748
4754
 
4749
4755
  // src/OhhwellsBridge.tsx
4750
- import { createPortal } from "react-dom";
4751
- import { usePathname, useRouter, useSearchParams } from "next/navigation";
4756
+ import { createPortal as createPortal2 } from "react-dom";
4757
+ import { usePathname as usePathname2, useRouter as useRouter2, useSearchParams } from "next/navigation";
4752
4758
 
4753
4759
  // src/lib/session-search.ts
4754
4760
  var STORAGE_KEY = "ohw-preserved-search";
@@ -4778,6 +4784,7 @@ function parseSectionsFromRoot(root) {
4778
4784
  for (const el of root.querySelectorAll("[data-ohw-section]")) {
4779
4785
  const id = el.getAttribute("data-ohw-section") ?? "";
4780
4786
  if (!id || seen.has(id)) continue;
4787
+ if (el.parentElement?.closest("[data-ohw-section]")) continue;
4781
4788
  seen.add(id);
4782
4789
  const label = el.getAttribute("data-ohw-section-label") ?? titleCaseSectionId(id);
4783
4790
  sections.push({ id, label });
@@ -5024,61 +5031,29 @@ function scrollToHashSectionWhenReady(behavior = "smooth") {
5024
5031
  tick();
5025
5032
  }
5026
5033
 
5034
+ // src/ui/link-modal/LinkPopover.tsx
5035
+ import { useEffect as useEffect6 } from "react";
5036
+
5027
5037
  // src/ui/dialog.tsx
5028
5038
  import * as React7 from "react";
5029
5039
  import { Dialog as DialogPrimitive } from "radix-ui";
5030
-
5031
- // src/ui/icons.tsx
5040
+ import { X } from "lucide-react";
5032
5041
  import { jsx as jsx12, jsxs as jsxs6 } from "react/jsx-runtime";
5033
- function IconX({ className, ...props }) {
5034
- return /* @__PURE__ */ jsxs6("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, ...props, children: [
5035
- /* @__PURE__ */ jsx12("path", { d: "M18 6 6 18" }),
5036
- /* @__PURE__ */ jsx12("path", { d: "m6 6 12 12" })
5037
- ] });
5038
- }
5039
- function IconChevronDown({ className, ...props }) {
5040
- return /* @__PURE__ */ jsx12("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, ...props, children: /* @__PURE__ */ jsx12("path", { d: "m6 9 6 6 6-6" }) });
5041
- }
5042
- function IconFile({ className, ...props }) {
5043
- return /* @__PURE__ */ jsxs6("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, ...props, children: [
5044
- /* @__PURE__ */ jsx12("path", { d: "M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z" }),
5045
- /* @__PURE__ */ jsx12("path", { d: "M14 2v4a2 2 0 0 0 2 2h4" })
5046
- ] });
5047
- }
5048
- function IconInfo({ className, ...props }) {
5049
- return /* @__PURE__ */ jsxs6("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, ...props, children: [
5050
- /* @__PURE__ */ jsx12("circle", { cx: "12", cy: "12", r: "10" }),
5051
- /* @__PURE__ */ jsx12("path", { d: "M12 16v-4" }),
5052
- /* @__PURE__ */ jsx12("path", { d: "M12 8h.01" })
5053
- ] });
5054
- }
5055
- function IconArrowRight({ className, ...props }) {
5056
- return /* @__PURE__ */ jsxs6("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, ...props, children: [
5057
- /* @__PURE__ */ jsx12("path", { d: "M5 12h14" }),
5058
- /* @__PURE__ */ jsx12("path", { d: "m12 5 7 7-7 7" })
5059
- ] });
5060
- }
5061
- function IconSection({ className, ...props }) {
5062
- return /* @__PURE__ */ jsxs6("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, ...props, children: [
5063
- /* @__PURE__ */ jsx12("rect", { width: "18", height: "7", x: "3", y: "3", rx: "1" }),
5064
- /* @__PURE__ */ jsx12("rect", { width: "9", height: "7", x: "3", y: "14", rx: "1" }),
5065
- /* @__PURE__ */ jsx12("rect", { width: "5", height: "7", x: "16", y: "14", rx: "1" })
5066
- ] });
5067
- }
5068
-
5069
- // src/ui/dialog.tsx
5070
- import { jsx as jsx13, jsxs as jsxs7 } from "react/jsx-runtime";
5071
- function Dialog2({ ...props }) {
5072
- return /* @__PURE__ */ jsx13(DialogPrimitive.Root, { "data-slot": "dialog", ...props });
5042
+ function Dialog2({
5043
+ ...props
5044
+ }) {
5045
+ return /* @__PURE__ */ jsx12(DialogPrimitive.Root, { "data-slot": "dialog", ...props });
5073
5046
  }
5074
- function DialogPortal({ ...props }) {
5075
- return /* @__PURE__ */ jsx13(DialogPrimitive.Portal, { ...props });
5047
+ function DialogPortal({
5048
+ ...props
5049
+ }) {
5050
+ return /* @__PURE__ */ jsx12(DialogPrimitive.Portal, { ...props });
5076
5051
  }
5077
5052
  function DialogOverlay({
5078
5053
  className,
5079
5054
  ...props
5080
5055
  }) {
5081
- return /* @__PURE__ */ jsx13(
5056
+ return /* @__PURE__ */ jsx12(
5082
5057
  DialogPrimitive.Overlay,
5083
5058
  {
5084
5059
  "data-slot": "dialog-overlay",
@@ -5088,57 +5063,74 @@ function DialogOverlay({
5088
5063
  }
5089
5064
  );
5090
5065
  }
5091
- var DialogContent = React7.forwardRef(({ className, children, showCloseButton = true, container, ...props }, ref) => {
5092
- const positionMode = container ? "absolute" : "fixed";
5093
- return /* @__PURE__ */ jsxs7(DialogPortal, { container: container ?? void 0, children: [
5094
- /* @__PURE__ */ jsx13(DialogOverlay, { className: cn(positionMode, "inset-0") }),
5095
- /* @__PURE__ */ jsxs7(
5096
- DialogPrimitive.Content,
5097
- {
5098
- ref,
5099
- "data-slot": "dialog-content",
5100
- className: cn(
5101
- positionMode,
5102
- "left-1/2 top-1/2 z-[2147483647] flex w-full max-w-[483px] -translate-x-1/2 -translate-y-1/2 flex-col overflow-hidden",
5103
- "rounded-xl border border-border bg-background font-sans shadow-lg outline-none",
5104
- container ? "max-h-[calc(100%-32px)] max-w-[calc(100%-16px)]" : "max-h-[calc(100vh-32px)] max-w-[calc(100vw-16px)]",
5105
- className
5106
- ),
5107
- onMouseDown: (e) => e.stopPropagation(),
5108
- ...props,
5109
- children: [
5110
- children,
5111
- showCloseButton ? /* @__PURE__ */ jsx13(
5112
- DialogPrimitive.Close,
5113
- {
5114
- type: "button",
5115
- className: "absolute right-[9px] top-[9px] rounded-sm p-1.5 text-foreground hover:bg-muted/50",
5116
- "aria-label": "Close",
5117
- children: /* @__PURE__ */ jsx13(IconX, { "aria-hidden": true })
5118
- }
5119
- ) : null
5120
- ]
5121
- }
5122
- )
5123
- ] });
5124
- });
5066
+ var DialogContent = React7.forwardRef(
5067
+ ({ className, children, showCloseButton = true, container, ...props }, ref) => {
5068
+ const positionMode = container ? "absolute" : "fixed";
5069
+ return /* @__PURE__ */ jsxs6(DialogPortal, { container: container ?? void 0, children: [
5070
+ /* @__PURE__ */ jsx12(DialogOverlay, { className: cn(positionMode, "inset-0") }),
5071
+ /* @__PURE__ */ jsxs6(
5072
+ DialogPrimitive.Content,
5073
+ {
5074
+ ref,
5075
+ "data-slot": "dialog-content",
5076
+ className: cn(
5077
+ positionMode,
5078
+ "left-1/2 top-1/2 z-[2147483647] flex w-full max-w-[448px] -translate-x-1/2 -translate-y-1/2 flex-col overflow-visible",
5079
+ "rounded-xl border border-border bg-background font-sans shadow-lg outline-none",
5080
+ container ? "max-h-[calc(100%-32px)]" : "max-h-[calc(100vh-32px)]",
5081
+ className
5082
+ ),
5083
+ onMouseDown: (e) => e.stopPropagation(),
5084
+ ...props,
5085
+ children: [
5086
+ children,
5087
+ showCloseButton ? /* @__PURE__ */ jsx12(
5088
+ DialogPrimitive.Close,
5089
+ {
5090
+ type: "button",
5091
+ className: "absolute right-[9px] top-[9px] rounded-sm p-1.5 text-foreground hover:bg-muted/50",
5092
+ "aria-label": "Close",
5093
+ children: /* @__PURE__ */ jsx12(X, { size: 16, "aria-hidden": true })
5094
+ }
5095
+ ) : null
5096
+ ]
5097
+ }
5098
+ )
5099
+ ] });
5100
+ }
5101
+ );
5125
5102
  DialogContent.displayName = DialogPrimitive.Content.displayName;
5126
- function DialogHeader({ className, ...props }) {
5127
- return /* @__PURE__ */ jsx13("div", { className: cn("flex flex-col gap-1.5", className), ...props });
5103
+ function DialogHeader({
5104
+ className,
5105
+ ...props
5106
+ }) {
5107
+ return /* @__PURE__ */ jsx12("div", { className: cn("flex flex-col gap-1.5", className), ...props });
5128
5108
  }
5129
- function DialogFooter({ className, ...props }) {
5130
- return /* @__PURE__ */ jsx13("div", { className: cn("flex items-center justify-end gap-2", className), ...props });
5109
+ function DialogFooter({
5110
+ className,
5111
+ ...props
5112
+ }) {
5113
+ return /* @__PURE__ */ jsx12(
5114
+ "div",
5115
+ {
5116
+ className: cn("flex items-center justify-end gap-2", className),
5117
+ ...props
5118
+ }
5119
+ );
5131
5120
  }
5132
- var DialogTitle = React7.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx13(
5121
+ var DialogTitle = React7.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx12(
5133
5122
  DialogPrimitive.Title,
5134
5123
  {
5135
5124
  ref,
5136
- className: cn("text-lg font-semibold leading-none tracking-tight text-card-foreground", className),
5125
+ className: cn(
5126
+ "text-lg font-semibold leading-none tracking-tight text-card-foreground",
5127
+ className
5128
+ ),
5137
5129
  ...props
5138
5130
  }
5139
5131
  ));
5140
5132
  DialogTitle.displayName = DialogPrimitive.Title.displayName;
5141
- var DialogDescription = React7.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx13(
5133
+ var DialogDescription = React7.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx12(
5142
5134
  DialogPrimitive.Description,
5143
5135
  {
5144
5136
  ref,
@@ -5149,38 +5141,64 @@ var DialogDescription = React7.forwardRef(({ className, ...props }, ref) => /* @
5149
5141
  DialogDescription.displayName = DialogPrimitive.Description.displayName;
5150
5142
  var DialogClose = DialogPrimitive.Close;
5151
5143
 
5144
+ // src/ui/link-modal/LinkEditorPanel.tsx
5145
+ import { Info, X as X3 } from "lucide-react";
5146
+
5152
5147
  // src/ui/link-modal/DestinationBreadcrumb.tsx
5153
- import { jsx as jsx14, jsxs as jsxs8 } from "react/jsx-runtime";
5154
- function DestinationBreadcrumb({ pageTitle, sectionLabel }) {
5155
- return /* @__PURE__ */ jsxs8("div", { className: "flex w-full flex-col gap-2", children: [
5156
- /* @__PURE__ */ jsx14("p", { className: "text-sm font-medium text-foreground", children: "Destination" }),
5157
- /* @__PURE__ */ jsxs8("div", { className: "flex items-center gap-3", children: [
5158
- /* @__PURE__ */ jsxs8("div", { className: "flex items-center gap-2", children: [
5159
- /* @__PURE__ */ jsx14(IconFile, { className: "shrink-0 text-foreground", "aria-hidden": true }),
5160
- /* @__PURE__ */ jsx14("span", { className: "text-sm font-medium leading-none text-foreground", children: pageTitle })
5148
+ import { ArrowRight, File, GalleryVertical } from "lucide-react";
5149
+ import { jsx as jsx13, jsxs as jsxs7 } from "react/jsx-runtime";
5150
+ function DestinationBreadcrumb({
5151
+ pageTitle,
5152
+ sectionLabel
5153
+ }) {
5154
+ return /* @__PURE__ */ jsxs7("div", { className: "flex w-full flex-col gap-2", children: [
5155
+ /* @__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: [
5158
+ /* @__PURE__ */ jsx13(File, { size: 16, className: "shrink-0 text-foreground", "aria-hidden": true }),
5159
+ /* @__PURE__ */ jsx13("span", { className: "text-sm font-medium leading-none text-foreground!", children: pageTitle })
5161
5160
  ] }),
5162
- /* @__PURE__ */ jsx14(IconArrowRight, { className: "shrink-0 text-muted-foreground", "aria-hidden": true }),
5163
- /* @__PURE__ */ jsxs8("div", { className: "flex min-w-0 flex-1 items-center gap-2", children: [
5164
- /* @__PURE__ */ jsx14(IconSection, { className: "shrink-0 text-foreground", "aria-hidden": true }),
5165
- /* @__PURE__ */ jsx14("span", { className: "truncate text-sm font-medium leading-none text-foreground", children: sectionLabel })
5161
+ /* @__PURE__ */ jsx13(
5162
+ ArrowRight,
5163
+ {
5164
+ size: 16,
5165
+ className: "shrink-0 text-muted-foreground",
5166
+ "aria-hidden": true
5167
+ }
5168
+ ),
5169
+ /* @__PURE__ */ jsxs7("div", { className: "flex min-w-0 flex-1 items-center gap-2", children: [
5170
+ /* @__PURE__ */ jsx13(
5171
+ GalleryVertical,
5172
+ {
5173
+ size: 16,
5174
+ className: "shrink-0 text-foreground",
5175
+ "aria-hidden": true
5176
+ }
5177
+ ),
5178
+ /* @__PURE__ */ jsx13("span", { className: "truncate text-sm font-medium leading-none text-foreground", children: sectionLabel })
5166
5179
  ] })
5167
5180
  ] })
5168
5181
  ] });
5169
5182
  }
5170
5183
 
5171
5184
  // src/ui/link-modal/SectionTreeItem.tsx
5172
- import { jsx as jsx15, jsxs as jsxs9 } from "react/jsx-runtime";
5173
- function SectionTreeItem({ section, onSelect, selected }) {
5185
+ import { GalleryVertical as GalleryVertical2 } from "lucide-react";
5186
+ import { jsx as jsx14, jsxs as jsxs8 } from "react/jsx-runtime";
5187
+ function SectionTreeItem({
5188
+ section,
5189
+ onSelect,
5190
+ selected
5191
+ }) {
5174
5192
  const interactive = Boolean(onSelect);
5175
- return /* @__PURE__ */ jsxs9("div", { className: "flex h-9 w-full items-end pl-3", children: [
5176
- /* @__PURE__ */ jsx15(
5193
+ return /* @__PURE__ */ jsxs8("div", { className: "flex h-9 w-full items-end pl-3", children: [
5194
+ /* @__PURE__ */ jsx14(
5177
5195
  "div",
5178
5196
  {
5179
5197
  className: "mr-[-1px] h-9 w-2 shrink-0 rounded-bl-sm border-b border-l border-border mb-4",
5180
5198
  "aria-hidden": true
5181
5199
  }
5182
5200
  ),
5183
- /* @__PURE__ */ jsxs9(
5201
+ /* @__PURE__ */ jsxs8(
5184
5202
  "div",
5185
5203
  {
5186
5204
  role: interactive ? "button" : void 0,
@@ -5198,32 +5216,30 @@ function SectionTreeItem({ section, onSelect, selected }) {
5198
5216
  interactive && selected && "border-primary"
5199
5217
  ),
5200
5218
  children: [
5201
- /* @__PURE__ */ jsx15(IconSection, { className: "shrink-0 text-foreground", "aria-hidden": true }),
5202
- /* @__PURE__ */ jsx15("span", { className: "truncate text-sm font-normal leading-5 text-foreground", children: section.label })
5219
+ /* @__PURE__ */ jsx14(
5220
+ GalleryVertical2,
5221
+ {
5222
+ size: 16,
5223
+ className: "shrink-0 text-foreground",
5224
+ "aria-hidden": true
5225
+ }
5226
+ ),
5227
+ /* @__PURE__ */ jsx14("span", { className: "truncate text-sm font-normal leading-5 text-foreground", children: section.label })
5203
5228
  ]
5204
5229
  }
5205
5230
  )
5206
5231
  ] });
5207
5232
  }
5208
- function SectionPickerList({ sections, onSelect }) {
5209
- if (sections.length === 0) {
5210
- return /* @__PURE__ */ jsx15("p", { className: "text-sm text-muted-foreground", children: "No sections found on this page." });
5211
- }
5212
- return /* @__PURE__ */ jsxs9("div", { className: "flex flex-col gap-1", children: [
5213
- /* @__PURE__ */ jsx15("p", { className: "text-sm text-muted-foreground", children: "Pick a section this link should scroll to." }),
5214
- sections.map((section) => /* @__PURE__ */ jsx15(SectionTreeItem, { section, onSelect }, section.id))
5215
- ] });
5216
- }
5217
5233
 
5218
5234
  // src/ui/link-modal/UrlOrPageInput.tsx
5219
- import { useId as useId2, useRef as useRef3, useState as useState3 } from "react";
5235
+ import { useEffect as useEffect3, useId as useId2, useRef as useRef3, useState as useState3 } from "react";
5220
5236
 
5221
5237
  // src/ui/input.tsx
5222
5238
  import * as React8 from "react";
5223
- import { jsx as jsx16 } from "react/jsx-runtime";
5239
+ import { jsx as jsx15 } from "react/jsx-runtime";
5224
5240
  var Input = React8.forwardRef(
5225
5241
  ({ className, type, ...props }, ref) => {
5226
- return /* @__PURE__ */ jsx16(
5242
+ return /* @__PURE__ */ jsx15(
5227
5243
  "input",
5228
5244
  {
5229
5245
  type,
@@ -5242,9 +5258,9 @@ Input.displayName = "Input";
5242
5258
 
5243
5259
  // src/ui/label.tsx
5244
5260
  import { Label as LabelPrimitive } from "radix-ui";
5245
- import { jsx as jsx17 } from "react/jsx-runtime";
5261
+ import { jsx as jsx16 } from "react/jsx-runtime";
5246
5262
  function Label({ className, ...props }) {
5247
- return /* @__PURE__ */ jsx17(
5263
+ return /* @__PURE__ */ jsx16(
5248
5264
  LabelPrimitive.Root,
5249
5265
  {
5250
5266
  "data-slot": "label",
@@ -5255,9 +5271,12 @@ function Label({ className, ...props }) {
5255
5271
  }
5256
5272
 
5257
5273
  // src/ui/link-modal/UrlOrPageInput.tsx
5258
- import { jsx as jsx18, jsxs as jsxs10 } from "react/jsx-runtime";
5259
- function FieldChevron({ onClick }) {
5260
- return /* @__PURE__ */ jsx18(
5274
+ import { ChevronDown, File as File2, X as X2 } from "lucide-react";
5275
+ import { jsx as jsx17, jsxs as jsxs9 } from "react/jsx-runtime";
5276
+ function FieldChevron({
5277
+ onClick
5278
+ }) {
5279
+ return /* @__PURE__ */ jsx17(
5261
5280
  "button",
5262
5281
  {
5263
5282
  type: "button",
@@ -5265,7 +5284,7 @@ function FieldChevron({ onClick }) {
5265
5284
  onClick,
5266
5285
  "aria-label": "Open page list",
5267
5286
  tabIndex: -1,
5268
- children: /* @__PURE__ */ jsx18(IconChevronDown, {})
5287
+ children: /* @__PURE__ */ jsx17(ChevronDown, { size: 16 })
5269
5288
  }
5270
5289
  );
5271
5290
  }
@@ -5283,7 +5302,29 @@ function UrlOrPageInput({
5283
5302
  }) {
5284
5303
  const inputId = useId2();
5285
5304
  const inputRef = useRef3(null);
5305
+ const rootRef = useRef3(null);
5286
5306
  const [isFocused, setIsFocused] = useState3(false);
5307
+ useEffect3(() => {
5308
+ if (!dropdownOpen) return;
5309
+ const isOutside = (e) => {
5310
+ const root = rootRef.current;
5311
+ if (!root) return true;
5312
+ const path = typeof e.composedPath === "function" ? e.composedPath() : [];
5313
+ if (path.includes(root)) return false;
5314
+ const target = e.target;
5315
+ return !(target instanceof Node && root.contains(target));
5316
+ };
5317
+ const closeIfOutside = (e) => {
5318
+ if (!isOutside(e)) return;
5319
+ onDropdownOpenChange(false);
5320
+ };
5321
+ document.addEventListener("pointerdown", closeIfOutside, true);
5322
+ document.addEventListener("mousedown", closeIfOutside, true);
5323
+ return () => {
5324
+ document.removeEventListener("pointerdown", closeIfOutside, true);
5325
+ document.removeEventListener("mousedown", closeIfOutside, true);
5326
+ };
5327
+ }, [dropdownOpen, onDropdownOpenChange]);
5287
5328
  const openDropdown = () => {
5288
5329
  if (readOnly || filteredPages.length === 0) return;
5289
5330
  onDropdownOpenChange(true);
@@ -5301,15 +5342,22 @@ function UrlOrPageInput({
5301
5342
  requestAnimationFrame(() => inputRef.current?.focus());
5302
5343
  };
5303
5344
  const fieldClassName = cn(
5304
- "data-ohw-link-field flex h-10 w-full items-center overflow-hidden rounded-md border bg-background pl-3 pr-3 py-2 outline-none transition-[border-color,box-shadow]",
5345
+ "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]",
5305
5346
  urlError ? "border-destructive shadow-[0_0_0_1px_var(--ohw-destructive)]" : isFocused ? "border-primary shadow-[0_0_0_1px_var(--ohw-primary)]" : "border-input"
5306
5347
  );
5307
- return /* @__PURE__ */ jsxs10("div", { className: "flex w-full flex-col gap-2 p-0", children: [
5308
- /* @__PURE__ */ jsx18(Label, { htmlFor: inputId, className: cn(urlError && "text-destructive"), children: "Destination" }),
5309
- /* @__PURE__ */ jsxs10("div", { className: "relative w-full", children: [
5310
- /* @__PURE__ */ jsxs10("div", { "data-ohw-link-field": true, className: fieldClassName, children: [
5311
- selectedPage ? /* @__PURE__ */ jsx18("div", { className: "flex shrink-0 items-center pr-2", children: /* @__PURE__ */ jsx18(IconFile, { className: "text-foreground", "aria-hidden": true }) }) : null,
5312
- readOnly ? /* @__PURE__ */ jsx18("span", { className: "min-w-0 flex-1 truncate text-sm leading-5 text-foreground", children: selectedPage?.title ?? value }) : /* @__PURE__ */ jsx18(
5348
+ return /* @__PURE__ */ jsxs9("div", { className: "flex w-full flex-col gap-2 p-0", children: [
5349
+ /* @__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: [
5352
+ selectedPage ? /* @__PURE__ */ jsx17("div", { className: "flex shrink-0 items-center pr-2", children: /* @__PURE__ */ jsx17(
5353
+ File2,
5354
+ {
5355
+ size: 16,
5356
+ className: "shrink-0 text-foreground",
5357
+ "aria-hidden": true
5358
+ }
5359
+ ) }) : null,
5360
+ readOnly ? /* @__PURE__ */ jsx17("span", { className: "min-w-0 flex-1 truncate text-sm leading-5 text-foreground", children: selectedPage?.title ?? value }) : /* @__PURE__ */ jsx17(
5313
5361
  Input,
5314
5362
  {
5315
5363
  ref: inputRef,
@@ -5320,7 +5368,14 @@ function UrlOrPageInput({
5320
5368
  setIsFocused(true);
5321
5369
  if (!selectedPage) openDropdown();
5322
5370
  },
5323
- onBlur: () => setIsFocused(false),
5371
+ onBlur: () => {
5372
+ setIsFocused(false);
5373
+ requestAnimationFrame(() => {
5374
+ if (!rootRef.current?.contains(document.activeElement)) {
5375
+ onDropdownOpenChange(false);
5376
+ }
5377
+ });
5378
+ },
5324
5379
  placeholder,
5325
5380
  className: cn(
5326
5381
  "min-w-0 flex-1 truncate border-0 bg-transparent p-0 text-sm font-normal leading-5 shadow-none outline-none ring-0 caret-foreground",
@@ -5328,7 +5383,7 @@ function UrlOrPageInput({
5328
5383
  )
5329
5384
  }
5330
5385
  ),
5331
- selectedPage && !readOnly ? /* @__PURE__ */ jsx18(
5386
+ selectedPage && !readOnly ? /* @__PURE__ */ jsx17(
5332
5387
  "button",
5333
5388
  {
5334
5389
  type: "button",
@@ -5336,26 +5391,26 @@ function UrlOrPageInput({
5336
5391
  onMouseDown: clearSelection,
5337
5392
  "aria-label": "Clear selected page",
5338
5393
  tabIndex: -1,
5339
- children: /* @__PURE__ */ jsx18(IconX, { "aria-hidden": true })
5394
+ children: /* @__PURE__ */ jsx17(X2, { size: 16, "aria-hidden": true })
5340
5395
  }
5341
5396
  ) : null,
5342
- !readOnly ? /* @__PURE__ */ jsx18(FieldChevron, { onClick: toggleDropdown }) : null
5397
+ !readOnly ? /* @__PURE__ */ jsx17(FieldChevron, { onClick: toggleDropdown }) : null
5343
5398
  ] }),
5344
- dropdownOpen && !readOnly && filteredPages.length > 0 ? /* @__PURE__ */ jsx18(
5399
+ dropdownOpen && !readOnly && filteredPages.length > 0 ? /* @__PURE__ */ jsx17(
5345
5400
  "div",
5346
5401
  {
5347
5402
  "data-ohw-link-page-dropdown": "",
5348
- className: "absolute left-0 right-0 h-20 top-[calc(100%+4px)] z-10 max-h-48 overflow-auto rounded-lg border border-border bg-popover py-1 shadow-lg",
5403
+ 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",
5349
5404
  onMouseDown: (e) => e.preventDefault(),
5350
- children: filteredPages.map((page) => /* @__PURE__ */ jsxs10(
5405
+ children: filteredPages.map((page) => /* @__PURE__ */ jsxs9(
5351
5406
  "button",
5352
5407
  {
5353
5408
  type: "button",
5354
5409
  className: "flex h-9 w-full items-center gap-2 border-0 bg-transparent px-3 text-left text-sm leading-5 text-foreground outline-none hover:bg-muted",
5355
5410
  onClick: () => onPageSelect(page),
5356
5411
  children: [
5357
- /* @__PURE__ */ jsx18(IconFile, { className: "shrink-0", "aria-hidden": true }),
5358
- /* @__PURE__ */ jsx18("span", { className: "truncate", children: page.title })
5412
+ /* @__PURE__ */ jsx17(File2, { size: 16, className: "shrink-0", "aria-hidden": true }),
5413
+ /* @__PURE__ */ jsx17("span", { className: "truncate", children: page.title })
5359
5414
  ]
5360
5415
  },
5361
5416
  page.path
@@ -5363,111 +5418,542 @@ function UrlOrPageInput({
5363
5418
  }
5364
5419
  ) : null
5365
5420
  ] }),
5366
- urlError ? /* @__PURE__ */ jsx18("p", { className: "text-sm font-medium text-destructive", children: urlError }) : null
5421
+ urlError ? /* @__PURE__ */ jsx17("p", { className: "text-sm font-medium text-destructive", children: urlError }) : null
5367
5422
  ] });
5368
5423
  }
5369
5424
 
5370
- // src/ui/link-modal/useLinkModalState.ts
5371
- import { useCallback, useEffect as useEffect3, useMemo as useMemo2, useState as useState4 } from "react";
5372
- function useLinkModalState({
5373
- open,
5374
- mode,
5375
- pages,
5376
- sections: _sections,
5377
- sectionsByPath,
5378
- initialTarget,
5379
- existingTargets,
5380
- onClose,
5381
- onSubmit
5382
- }) {
5383
- const availablePages = useMemo2(
5384
- () => mode === "create" ? filterAvailablePages(pages, existingTargets) : pages,
5385
- [mode, pages, existingTargets]
5386
- );
5387
- const [searchValue, setSearchValue] = useState4("");
5388
- const [selectedPage, setSelectedPage] = useState4(null);
5389
- const [selectedSection, setSelectedSection] = useState4(null);
5390
- const [step, setStep] = useState4("input");
5391
- const [dropdownOpen, setDropdownOpen] = useState4(false);
5392
- const [urlError, setUrlError] = useState4("");
5393
- const reset = useCallback(() => {
5394
- setSearchValue("");
5395
- setSelectedPage(null);
5396
- setSelectedSection(null);
5397
- setStep("input");
5398
- setDropdownOpen(false);
5399
- setUrlError("");
5400
- }, []);
5401
- useEffect3(() => {
5402
- if (!open) return;
5403
- if (mode === "edit" && initialTarget) {
5404
- const { pageRoute } = parseTarget(initialTarget);
5405
- const targetSections = getSectionsForPath(sectionsByPath, pageRoute);
5406
- const init = getEditModeInitialState(initialTarget, pages, targetSections);
5407
- setSearchValue(init.searchValue);
5408
- setSelectedPage(init.selectedPage);
5409
- setSelectedSection(init.selectedSection);
5410
- setStep(init.step);
5411
- } else {
5412
- reset();
5425
+ // src/ui/link-modal/LinkEditorPanel.tsx
5426
+ import { Fragment as Fragment3, jsx as jsx18, jsxs as jsxs10 } from "react/jsx-runtime";
5427
+ function LinkEditorPanel({ state, onClose }) {
5428
+ const isCancel = state.secondaryLabel === "Cancel" || state.secondaryLabel === "Back to sections";
5429
+ return /* @__PURE__ */ jsxs10(Fragment3, { children: [
5430
+ /* @__PURE__ */ jsx18(DialogClose, { asChild: true, children: /* @__PURE__ */ jsx18(
5431
+ "button",
5432
+ {
5433
+ type: "button",
5434
+ className: "absolute right-[9px] top-[9px] rounded-sm p-1.5 text-foreground hover:bg-muted/50 h-7",
5435
+ "aria-label": "Close",
5436
+ onClick: onClose,
5437
+ children: /* @__PURE__ */ jsx18(X3, { size: 16, "aria-hidden": true })
5438
+ }
5439
+ ) }),
5440
+ /* @__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: [
5442
+ state.showBreadcrumb && state.selectedPage && state.selectedSection ? /* @__PURE__ */ jsx18(
5443
+ DestinationBreadcrumb,
5444
+ {
5445
+ pageTitle: state.selectedPage.title,
5446
+ sectionLabel: state.selectedSection.label
5447
+ }
5448
+ ) : /* @__PURE__ */ jsx18(
5449
+ UrlOrPageInput,
5450
+ {
5451
+ value: state.searchValue,
5452
+ selectedPage: state.selectedPage,
5453
+ dropdownOpen: state.dropdownOpen,
5454
+ onDropdownOpenChange: state.setDropdownOpen,
5455
+ filteredPages: state.filteredPages,
5456
+ onInputChange: state.handleInputChange,
5457
+ onPageSelect: state.handlePageSelect,
5458
+ urlError: state.urlError
5459
+ }
5460
+ ),
5461
+ state.showChooseSection ? /* @__PURE__ */ jsxs10("div", { className: "flex flex-col justify-center gap-2", children: [
5462
+ /* @__PURE__ */ jsx18(
5463
+ Button,
5464
+ {
5465
+ type: "button",
5466
+ variant: "outline",
5467
+ className: "w-fit cursor-pointer",
5468
+ size: "sm",
5469
+ onClick: state.handleChooseSection,
5470
+ children: "Choose a section"
5471
+ }
5472
+ ),
5473
+ /* @__PURE__ */ jsxs10("div", { className: "flex items-center gap-1 text-sm text-muted-foreground", children: [
5474
+ /* @__PURE__ */ jsx18(Info, { size: 16, className: "shrink-0", "aria-hidden": true }),
5475
+ /* @__PURE__ */ jsx18("span", { children: "Pick a section this link should scroll to." })
5476
+ ] })
5477
+ ] }) : null,
5478
+ state.showSectionRow && state.selectedSection ? /* @__PURE__ */ jsx18(SectionTreeItem, { section: state.selectedSection, selected: true }) : null
5479
+ ] }),
5480
+ /* @__PURE__ */ jsxs10(DialogFooter, { className: "w-full px-6 pb-6", children: [
5481
+ /* @__PURE__ */ jsx18(
5482
+ Button,
5483
+ {
5484
+ type: "button",
5485
+ variant: isCancel ? "outline" : "ghost",
5486
+ className: "leading-6 shadow-none cursor-pointer",
5487
+ style: isCancel ? {
5488
+ backgroundColor: "var(--ohw-background)",
5489
+ borderColor: "var(--ohw-border)",
5490
+ color: "var(--ohw-foreground)"
5491
+ } : void 0,
5492
+ onClick: state.handleSecondary,
5493
+ children: state.secondaryLabel
5494
+ }
5495
+ ),
5496
+ /* @__PURE__ */ jsx18(
5497
+ Button,
5498
+ {
5499
+ type: "button",
5500
+ className: "border-0 leading-6 shadow-none hover:opacity-90 disabled:opacity-50 cursor-pointer",
5501
+ style: {
5502
+ backgroundColor: "var(--ohw-primary)",
5503
+ color: "var(--ohw-primary-foreground)"
5504
+ },
5505
+ disabled: !state.isValid,
5506
+ onClick: state.handleSubmit,
5507
+ children: state.submitLabel
5508
+ }
5509
+ )
5510
+ ] })
5511
+ ] });
5512
+ }
5513
+
5514
+ // src/ui/link-modal/SectionPickerOverlay.tsx
5515
+ import { useCallback, useEffect as useEffect4, useMemo as useMemo2, useRef as useRef4, useState as useState4 } from "react";
5516
+ import { createPortal } from "react-dom";
5517
+ import { ArrowLeft, Check } from "lucide-react";
5518
+ import { usePathname, useRouter } from "next/navigation";
5519
+ import { jsx as jsx19, jsxs as jsxs11 } from "react/jsx-runtime";
5520
+ var DIM_OVERLAY = "rgba(0, 0, 0, 0.45)";
5521
+ function rectsEqual(a, b) {
5522
+ if (a.size !== b.size) return false;
5523
+ for (const [id, rect] of a) {
5524
+ const other = b.get(id);
5525
+ if (!other || rect.top !== other.top || rect.left !== other.left || rect.width !== other.width || rect.height !== other.height) {
5526
+ return false;
5413
5527
  }
5414
- setDropdownOpen(false);
5415
- setUrlError("");
5416
- }, [open, mode, initialTarget, pages, sectionsByPath, reset]);
5417
- const filteredPages = useMemo2(() => {
5418
- if (!searchValue.trim()) return availablePages;
5419
- return availablePages.filter((p) => p.title.toLowerCase().startsWith(searchValue.toLowerCase()));
5420
- }, [availablePages, searchValue]);
5421
- const activeSections = useMemo2(() => {
5422
- if (!selectedPage) return [];
5423
- return getSectionsForPath(sectionsByPath, selectedPage.path);
5424
- }, [selectedPage, sectionsByPath]);
5425
- const showBreadcrumb = step === "confirmed" && selectedPage && selectedSection;
5426
- const showSectionPicker = step === "sectionPicker" && selectedPage;
5427
- const showSectionRow = mode === "edit" && selectedPage && selectedSection && step === "input";
5428
- const showChooseSection = step === "input" && selectedPage && !selectedSection && !showSectionRow && activeSections.length > 0;
5429
- const handleInputChange = (value) => {
5430
- setSearchValue(value);
5431
- setSelectedPage(null);
5432
- setSelectedSection(null);
5433
- setStep("input");
5434
- setUrlError("");
5435
- const filtered = value.trim() ? availablePages.filter((p) => p.title.toLowerCase().startsWith(value.toLowerCase())) : availablePages;
5436
- setDropdownOpen(filtered.length > 0);
5437
- if (value.trim()) {
5438
- setUrlError(validateUrlInput(value, availablePages, pages));
5528
+ }
5529
+ return true;
5530
+ }
5531
+ function readSectionRects(sectionIds) {
5532
+ const next = /* @__PURE__ */ new Map();
5533
+ for (const id of sectionIds) {
5534
+ const el = document.querySelector(
5535
+ `[data-ohw-section="${CSS.escape(id)}"]`
5536
+ );
5537
+ if (el) next.set(id, el.getBoundingClientRect());
5538
+ }
5539
+ return next;
5540
+ }
5541
+ function hitTestSectionId(x, y, sectionIds, rects) {
5542
+ for (const id of sectionIds) {
5543
+ const rect = rects.get(id);
5544
+ if (!rect || rect.width <= 0 || rect.height <= 0) continue;
5545
+ if (x >= rect.left && x <= rect.right && y >= rect.top && y <= rect.bottom) {
5546
+ return id;
5439
5547
  }
5440
- };
5441
- const handlePageSelect = (page) => {
5442
- setSelectedPage({ path: page.path, title: page.title });
5443
- setSelectedSection(null);
5444
- setSearchValue(page.title);
5445
- setStep("input");
5446
- setDropdownOpen(false);
5447
- setUrlError("");
5448
- };
5449
- const handleChooseSection = () => {
5450
- setStep("sectionPicker");
5451
- };
5452
- const handleSectionSelect = (section) => {
5453
- setSelectedSection({ id: section.id, label: section.label });
5454
- if (mode === "create") {
5455
- setStep("confirmed");
5456
- } else {
5457
- setStep("input");
5548
+ }
5549
+ return null;
5550
+ }
5551
+ function useSectionRects(sectionIdsKey, enabled) {
5552
+ const [rects, setRects] = useState4(/* @__PURE__ */ new Map());
5553
+ const sectionIds = useMemo2(
5554
+ () => sectionIdsKey ? sectionIdsKey.split("\0") : [],
5555
+ [sectionIdsKey]
5556
+ );
5557
+ useEffect4(() => {
5558
+ if (!enabled || sectionIds.length === 0) {
5559
+ setRects((prev) => prev.size === 0 ? prev : /* @__PURE__ */ new Map());
5560
+ return;
5458
5561
  }
5459
- };
5460
- const handleBackToSections = () => {
5461
- setStep("sectionPicker");
5462
- };
5463
- const handleClose = () => {
5464
- reset();
5465
- onClose();
5466
- };
5467
- const isValid = useMemo2(() => {
5468
- if (urlError) return false;
5469
- if (showBreadcrumb) return true;
5470
- if (selectedPage) return true;
5562
+ const update = () => {
5563
+ const next = readSectionRects(sectionIds);
5564
+ setRects((prev) => rectsEqual(prev, next) ? prev : next);
5565
+ };
5566
+ update();
5567
+ const opts = { capture: true, passive: true };
5568
+ window.addEventListener("scroll", update, opts);
5569
+ window.addEventListener("resize", update);
5570
+ const vv = window.visualViewport;
5571
+ vv?.addEventListener("resize", update);
5572
+ vv?.addEventListener("scroll", update);
5573
+ const ro = new ResizeObserver(update);
5574
+ for (const id of sectionIds) {
5575
+ const el = document.querySelector(
5576
+ `[data-ohw-section="${CSS.escape(id)}"]`
5577
+ );
5578
+ if (el) ro.observe(el);
5579
+ }
5580
+ return () => {
5581
+ window.removeEventListener("scroll", update, opts);
5582
+ window.removeEventListener("resize", update);
5583
+ vv?.removeEventListener("resize", update);
5584
+ vv?.removeEventListener("scroll", update);
5585
+ ro.disconnect();
5586
+ };
5587
+ }, [sectionIds, enabled]);
5588
+ return rects;
5589
+ }
5590
+ function SectionPickerOverlay({
5591
+ pagePath,
5592
+ sections,
5593
+ onBack,
5594
+ onSelect
5595
+ }) {
5596
+ const router = useRouter();
5597
+ const pathname = usePathname();
5598
+ const [selectedId, setSelectedId] = useState4(null);
5599
+ const [hoveredId, setHoveredId] = useState4(null);
5600
+ const pointerRef = useRef4(null);
5601
+ const pointerScreenRef = useRef4(null);
5602
+ const iframeOffsetTopRef = useRef4(null);
5603
+ const sectionIdsRef = useRef4([]);
5604
+ const [chromeClip, setChromeClip] = useState4(
5605
+ () => ({
5606
+ top: 0,
5607
+ bottom: typeof window !== "undefined" ? window.innerHeight : 0
5608
+ })
5609
+ );
5610
+ const normalizedTarget = normalizePath(pagePath);
5611
+ const isOnTargetPage = normalizePath(pathname) === normalizedTarget;
5612
+ useEffect4(() => {
5613
+ if (isOnTargetPage) return;
5614
+ const search = readPreservedSearch();
5615
+ router.push(`${normalizedTarget}${search}`);
5616
+ }, [isOnTargetPage, normalizedTarget, router]);
5617
+ useEffect4(() => {
5618
+ document.documentElement.setAttribute("data-ohw-section-picking", "");
5619
+ document.querySelectorAll("[data-ohw-hovered]").forEach((el) => {
5620
+ el.removeAttribute("data-ohw-hovered");
5621
+ });
5622
+ document.querySelectorAll("[data-ohw-state-hovered]").forEach((el) => {
5623
+ el.removeAttribute("data-ohw-state-hovered");
5624
+ });
5625
+ return () => {
5626
+ document.documentElement.removeAttribute("data-ohw-section-picking");
5627
+ };
5628
+ }, []);
5629
+ const liveSections = useMemo2(() => {
5630
+ if (!isOnTargetPage) return sections;
5631
+ const live = collectSectionsFromDom();
5632
+ if (live.length === 0) return sections;
5633
+ const labels = new Map(sections.map((s) => [s.id, s.label]));
5634
+ return live.map((s) => ({ id: s.id, label: labels.get(s.id) ?? s.label }));
5635
+ }, [isOnTargetPage, sections, pathname]);
5636
+ const sectionIdsKey = useMemo2(
5637
+ () => liveSections.map((s) => s.id).join("\0"),
5638
+ [liveSections]
5639
+ );
5640
+ const sectionIds = useMemo2(
5641
+ () => liveSections.map((s) => s.id),
5642
+ [liveSections]
5643
+ );
5644
+ sectionIdsRef.current = sectionIds;
5645
+ const rects = useSectionRects(sectionIdsKey, isOnTargetPage);
5646
+ const applyHoverAt = useCallback(
5647
+ (x, y, liveRects) => {
5648
+ const ids = sectionIdsRef.current;
5649
+ const map = liveRects ?? readSectionRects(ids);
5650
+ const next = hitTestSectionId(x, y, ids, map);
5651
+ setHoveredId((prev) => prev === next ? prev : next);
5652
+ },
5653
+ []
5654
+ );
5655
+ useEffect4(() => {
5656
+ const applyClip = (iframeOffsetTop, visibleCanvasTop, canvasH) => {
5657
+ const top = Math.max(0, visibleCanvasTop - iframeOffsetTop);
5658
+ const bottom = Math.min(
5659
+ window.innerHeight,
5660
+ visibleCanvasTop + canvasH - iframeOffsetTop
5661
+ );
5662
+ setChromeClip(
5663
+ (prev) => prev.top === top && prev.bottom === bottom ? prev : { top, bottom: Math.max(top, bottom) }
5664
+ );
5665
+ };
5666
+ const onParentScroll = (e) => {
5667
+ if (e.data?.type !== "ow:parent-scroll") return;
5668
+ const { iframeOffsetTop, headerH, canvasH } = e.data;
5669
+ applyClip(iframeOffsetTop, headerH, canvasH);
5670
+ iframeOffsetTopRef.current = iframeOffsetTop;
5671
+ if (!pointerScreenRef.current && pointerRef.current) {
5672
+ pointerScreenRef.current = {
5673
+ x: pointerRef.current.x,
5674
+ y: pointerRef.current.y + iframeOffsetTop
5675
+ };
5676
+ }
5677
+ const screen = pointerScreenRef.current;
5678
+ if (screen) {
5679
+ const next = { x: screen.x, y: screen.y - iframeOffsetTop };
5680
+ pointerRef.current = next;
5681
+ applyHoverAt(next.x, next.y);
5682
+ }
5683
+ };
5684
+ const onResize = () => {
5685
+ setChromeClip((prev) => {
5686
+ const bottom = Math.max(prev.top, window.innerHeight);
5687
+ return prev.bottom === bottom ? prev : { ...prev, bottom };
5688
+ });
5689
+ };
5690
+ window.addEventListener("message", onParentScroll);
5691
+ window.addEventListener("resize", onResize);
5692
+ return () => {
5693
+ window.removeEventListener("message", onParentScroll);
5694
+ window.removeEventListener("resize", onResize);
5695
+ };
5696
+ }, [applyHoverAt]);
5697
+ useEffect4(() => {
5698
+ const ptr = pointerRef.current;
5699
+ if (!ptr) return;
5700
+ applyHoverAt(ptr.x, ptr.y, rects);
5701
+ }, [rects, applyHoverAt]);
5702
+ useEffect4(() => {
5703
+ const rememberPointer = (clientX, clientY) => {
5704
+ pointerRef.current = { x: clientX, y: clientY };
5705
+ const top = iframeOffsetTopRef.current;
5706
+ if (top != null) {
5707
+ pointerScreenRef.current = { x: clientX, y: clientY + top };
5708
+ }
5709
+ applyHoverAt(clientX, clientY, rects);
5710
+ };
5711
+ const onPointerMove = (e) => {
5712
+ rememberPointer(e.clientX, e.clientY);
5713
+ };
5714
+ const onPointerSync = (e) => {
5715
+ if (e.data?.type !== "ow:pointer-sync") return;
5716
+ const { clientX, clientY } = e.data;
5717
+ rememberPointer(clientX, clientY);
5718
+ };
5719
+ window.addEventListener("pointermove", onPointerMove, { passive: true });
5720
+ window.addEventListener("message", onPointerSync);
5721
+ return () => {
5722
+ window.removeEventListener("pointermove", onPointerMove);
5723
+ window.removeEventListener("message", onPointerSync);
5724
+ };
5725
+ }, [rects, applyHoverAt]);
5726
+ const handleSelect = useCallback(
5727
+ (section) => {
5728
+ if (selectedId) return;
5729
+ setSelectedId(section.id);
5730
+ onSelect(section);
5731
+ },
5732
+ [selectedId, onSelect]
5733
+ );
5734
+ useEffect4(() => {
5735
+ const onKeyDown = (e) => {
5736
+ if (e.key === "Escape") {
5737
+ e.preventDefault();
5738
+ e.stopPropagation();
5739
+ onBack();
5740
+ }
5741
+ };
5742
+ window.addEventListener("keydown", onKeyDown, true);
5743
+ return () => window.removeEventListener("keydown", onKeyDown, true);
5744
+ }, [onBack]);
5745
+ const portalRoot = typeof document !== "undefined" ? document.querySelector("[data-ohw-bridge-root]") ?? document.body : null;
5746
+ if (!portalRoot) return null;
5747
+ return createPortal(
5748
+ /* @__PURE__ */ jsxs11(
5749
+ "div",
5750
+ {
5751
+ "data-ohw-section-picker": "",
5752
+ "data-ohw-bridge": "",
5753
+ className: `pointer-events-none fixed inset-0 z-[2147483647] transition-opacity duration-200 ${"opacity-100"}`,
5754
+ "aria-modal": true,
5755
+ role: "dialog",
5756
+ "aria-label": "Choose a section",
5757
+ children: [
5758
+ /* @__PURE__ */ jsx19(
5759
+ "div",
5760
+ {
5761
+ className: "pointer-events-auto fixed left-5 z-[2]",
5762
+ style: { top: chromeClip.top + 20 },
5763
+ children: /* @__PURE__ */ jsxs11(
5764
+ Button,
5765
+ {
5766
+ type: "button",
5767
+ variant: "outline",
5768
+ size: "sm",
5769
+ className: "h-8 min-w-0 gap-1 border-border bg-background px-2 py-1.5 shadow-sm hover:bg-muted cursor-pointer",
5770
+ onClick: onBack,
5771
+ children: [
5772
+ /* @__PURE__ */ jsx19(ArrowLeft, { className: "size-4 shrink-0", "aria-hidden": true }),
5773
+ "Back"
5774
+ ]
5775
+ }
5776
+ )
5777
+ }
5778
+ ),
5779
+ /* @__PURE__ */ jsx19(
5780
+ "div",
5781
+ {
5782
+ className: "pointer-events-none fixed left-1/2 z-[2] rounded-lg px-4 py-3 text-xs leading-4 tracking-[0.18px] text-white shadow-md",
5783
+ style: {
5784
+ top: chromeClip.bottom - 20,
5785
+ transform: "translate(-50%, -100%)",
5786
+ backgroundColor: "var(--ohw-popover-foreground, #0c0a09)"
5787
+ },
5788
+ "data-ohw-section-picker-hint": "",
5789
+ children: "Click on section to select"
5790
+ }
5791
+ ),
5792
+ !isOnTargetPage ? /* @__PURE__ */ jsx19(
5793
+ "div",
5794
+ {
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 },
5797
+ children: "Loading page preview\u2026"
5798
+ }
5799
+ ) : null,
5800
+ isOnTargetPage && liveSections.length === 0 ? /* @__PURE__ */ jsx19("div", { className: "pointer-events-auto fixed inset-0 z-[1] flex items-center justify-center bg-muted/40", children: /* @__PURE__ */ jsx19("p", { className: "text-sm text-muted-foreground", children: "No sections found on this page." }) }) : null,
5801
+ isOnTargetPage ? liveSections.map((section) => {
5802
+ const rect = rects.get(section.id);
5803
+ if (!rect || rect.width <= 0 || rect.height <= 0) return null;
5804
+ const isSelected = selectedId === section.id;
5805
+ const isHovered = hoveredId === section.id;
5806
+ const isLit = isSelected || isHovered;
5807
+ return /* @__PURE__ */ jsxs11(
5808
+ "button",
5809
+ {
5810
+ type: "button",
5811
+ className: "pointer-events-auto fixed z-[1] cursor-pointer border-0 bg-transparent p-0 transition-[background-color] duration-150",
5812
+ style: {
5813
+ top: rect.top,
5814
+ left: rect.left,
5815
+ width: rect.width,
5816
+ height: rect.height,
5817
+ backgroundColor: isLit ? "transparent" : DIM_OVERLAY
5818
+ },
5819
+ "aria-label": `Select section ${section.label}`,
5820
+ onClick: () => handleSelect(section),
5821
+ children: [
5822
+ isLit ? /* @__PURE__ */ jsx19(
5823
+ "span",
5824
+ {
5825
+ className: "pointer-events-none absolute",
5826
+ style: {
5827
+ inset: 4,
5828
+ borderRadius: "var(--radius, 6px)",
5829
+ border: "1.5px dashed var(--primary, #0885FE)",
5830
+ boxSizing: "border-box"
5831
+ },
5832
+ "aria-hidden": true
5833
+ }
5834
+ ) : null,
5835
+ isSelected ? /* @__PURE__ */ jsx19(
5836
+ "span",
5837
+ {
5838
+ className: "absolute right-3 top-3 flex size-8 items-center justify-center rounded-full text-white",
5839
+ style: { backgroundColor: "var(--ohw-primary, #0885fe)" },
5840
+ "aria-hidden": true,
5841
+ children: /* @__PURE__ */ jsx19(Check, { className: "size-5" })
5842
+ }
5843
+ ) : null
5844
+ ]
5845
+ },
5846
+ section.id
5847
+ );
5848
+ }) : null
5849
+ ]
5850
+ }
5851
+ ),
5852
+ portalRoot
5853
+ );
5854
+ }
5855
+
5856
+ // src/ui/link-modal/useLinkModalState.ts
5857
+ import { useCallback as useCallback2, useEffect as useEffect5, useMemo as useMemo3, useState as useState5 } from "react";
5858
+ function useLinkModalState({
5859
+ open,
5860
+ mode,
5861
+ pages,
5862
+ sections: _sections,
5863
+ sectionsByPath,
5864
+ initialTarget,
5865
+ existingTargets: _existingTargets,
5866
+ onClose,
5867
+ onSubmit
5868
+ }) {
5869
+ const availablePages = useMemo3(() => pages, [pages]);
5870
+ const [searchValue, setSearchValue] = useState5("");
5871
+ const [selectedPage, setSelectedPage] = useState5(null);
5872
+ const [selectedSection, setSelectedSection] = useState5(null);
5873
+ const [step, setStep] = useState5("input");
5874
+ const [dropdownOpen, setDropdownOpen] = useState5(false);
5875
+ const [urlError, setUrlError] = useState5("");
5876
+ const reset = useCallback2(() => {
5877
+ setSearchValue("");
5878
+ setSelectedPage(null);
5879
+ setSelectedSection(null);
5880
+ setStep("input");
5881
+ setDropdownOpen(false);
5882
+ setUrlError("");
5883
+ }, []);
5884
+ useEffect5(() => {
5885
+ if (!open) return;
5886
+ if (mode === "edit" && initialTarget) {
5887
+ const { pageRoute } = parseTarget(initialTarget);
5888
+ const targetSections = getSectionsForPath(sectionsByPath, pageRoute);
5889
+ const init = getEditModeInitialState(initialTarget, pages, targetSections);
5890
+ setSearchValue(init.searchValue);
5891
+ setSelectedPage(init.selectedPage);
5892
+ setSelectedSection(init.selectedSection);
5893
+ setStep(init.step);
5894
+ } else {
5895
+ reset();
5896
+ }
5897
+ setDropdownOpen(false);
5898
+ setUrlError("");
5899
+ }, [open, mode, initialTarget, reset]);
5900
+ const filteredPages = useMemo3(() => {
5901
+ if (!searchValue.trim()) return availablePages;
5902
+ return availablePages.filter((p) => p.title.toLowerCase().startsWith(searchValue.toLowerCase()));
5903
+ }, [availablePages, searchValue]);
5904
+ const activeSections = useMemo3(() => {
5905
+ if (!selectedPage) return [];
5906
+ return getSectionsForPath(sectionsByPath, selectedPage.path);
5907
+ }, [selectedPage, sectionsByPath]);
5908
+ const showBreadcrumb = step === "confirmed" && selectedPage && selectedSection;
5909
+ const showSectionPicker = step === "sectionPicker" && selectedPage;
5910
+ const showSectionRow = mode === "edit" && selectedPage && selectedSection && step === "input";
5911
+ const showChooseSection = step === "input" && selectedPage && !selectedSection && !showSectionRow && activeSections.length > 0;
5912
+ const handleInputChange = (value) => {
5913
+ setSearchValue(value);
5914
+ setSelectedPage(null);
5915
+ setSelectedSection(null);
5916
+ setStep("input");
5917
+ setUrlError("");
5918
+ const filtered = value.trim() ? availablePages.filter((p) => p.title.toLowerCase().startsWith(value.toLowerCase())) : availablePages;
5919
+ setDropdownOpen(filtered.length > 0);
5920
+ if (value.trim()) {
5921
+ setUrlError(validateUrlInput(value, availablePages, pages));
5922
+ }
5923
+ };
5924
+ const handlePageSelect = (page) => {
5925
+ setSelectedPage({ path: page.path, title: page.title });
5926
+ setSelectedSection(null);
5927
+ setSearchValue(page.title);
5928
+ setStep("input");
5929
+ setDropdownOpen(false);
5930
+ setUrlError("");
5931
+ };
5932
+ const handleChooseSection = () => {
5933
+ setStep("sectionPicker");
5934
+ };
5935
+ const handleSectionSelect = (section) => {
5936
+ setSelectedSection({ id: section.id, label: section.label });
5937
+ if (mode === "create") {
5938
+ setStep("confirmed");
5939
+ } else {
5940
+ setStep("input");
5941
+ }
5942
+ };
5943
+ const handleBackToSections = () => {
5944
+ setStep("sectionPicker");
5945
+ };
5946
+ const handleSectionPickerBack = () => {
5947
+ setStep("input");
5948
+ };
5949
+ const handleClose = () => {
5950
+ reset();
5951
+ onClose();
5952
+ };
5953
+ const isValid = useMemo3(() => {
5954
+ if (urlError) return false;
5955
+ if (showBreadcrumb) return true;
5956
+ if (selectedPage) return true;
5471
5957
  if (!searchValue.trim()) return false;
5472
5958
  return validateUrlInput(searchValue, availablePages, pages) === "";
5473
5959
  }, [urlError, showBreadcrumb, selectedPage, searchValue, availablePages, pages]);
@@ -5497,7 +5983,7 @@ function useLinkModalState({
5497
5983
  onSubmit(normalizeUrl(searchValue));
5498
5984
  handleClose();
5499
5985
  };
5500
- const submitLabel = mode === "edit" ? "Save" : "Create";
5986
+ const submitLabel = showBreadcrumb ? "Save" : mode === "edit" ? "Save" : "Create";
5501
5987
  const title = mode === "edit" ? "Edit navigation item" : "Create navigation item";
5502
5988
  const secondaryLabel = showBreadcrumb ? "Back to sections" : "Cancel";
5503
5989
  const handleSecondary = showBreadcrumb ? handleBackToSections : handleClose;
@@ -5524,23 +6010,29 @@ function useLinkModalState({
5524
6010
  handleChooseSection,
5525
6011
  handleSectionSelect,
5526
6012
  handleBackToSections,
6013
+ handleSectionPickerBack,
5527
6014
  handleClose,
5528
6015
  handleSecondary,
5529
6016
  handleSubmit
5530
6017
  };
5531
6018
  }
5532
6019
 
5533
- // src/ui/link-modal/LinkEditorPanel.tsx
5534
- import { Fragment as Fragment3, jsx as jsx19, jsxs as jsxs11 } from "react/jsx-runtime";
5535
- function LinkEditorPanel({
6020
+ // src/ui/link-modal/LinkPopover.tsx
6021
+ import { Fragment as Fragment4, jsx as jsx20, jsxs as jsxs12 } from "react/jsx-runtime";
6022
+ function postToParent(data) {
6023
+ window.parent?.postMessage(data, "*");
6024
+ }
6025
+ function LinkPopover({
5536
6026
  open = true,
6027
+ panelRef,
6028
+ portalContainer,
6029
+ onClose,
5537
6030
  mode = "create",
5538
6031
  pages,
5539
6032
  sections = [],
5540
6033
  sectionsByPath,
5541
6034
  initialTarget,
5542
6035
  existingTargets = [],
5543
- onClose,
5544
6036
  onSubmit
5545
6037
  }) {
5546
6038
  const state = useLinkModalState({
@@ -5554,115 +6046,109 @@ function LinkEditorPanel({
5554
6046
  onClose,
5555
6047
  onSubmit
5556
6048
  });
5557
- const isCancel = state.secondaryLabel === "Cancel";
5558
- return /* @__PURE__ */ jsxs11(Fragment3, { children: [
5559
- /* @__PURE__ */ jsx19(DialogClose, { asChild: true, children: /* @__PURE__ */ jsx19(
5560
- "button",
6049
+ const sectionPickerActive = state.showSectionPicker && Boolean(state.selectedPage);
6050
+ useEffect6(() => {
6051
+ if (!open) return;
6052
+ if (sectionPickerActive) {
6053
+ postToParent({ type: "ui:unlock" });
6054
+ postToParent({ type: "ow:section-picker", active: true });
6055
+ document.documentElement.style.overflow = "";
6056
+ document.body.style.overflow = "";
6057
+ return () => {
6058
+ postToParent({ type: "ow:section-picker", active: false });
6059
+ };
6060
+ }
6061
+ postToParent({ type: "ow:section-picker", active: false });
6062
+ postToParent({ type: "ui:lock" });
6063
+ const html = document.documentElement;
6064
+ const body = document.body;
6065
+ const prevHtmlOverflow = html.style.overflow;
6066
+ const prevBodyOverflow = body.style.overflow;
6067
+ const prevHtmlOverscroll = html.style.overscrollBehavior;
6068
+ const prevBodyOverscroll = body.style.overscrollBehavior;
6069
+ html.style.overflow = "hidden";
6070
+ body.style.overflow = "hidden";
6071
+ html.style.overscrollBehavior = "none";
6072
+ body.style.overscrollBehavior = "none";
6073
+ const canScrollElement = (el, deltaY) => {
6074
+ const { overflowY } = getComputedStyle(el);
6075
+ if (overflowY !== "auto" && overflowY !== "scroll") return false;
6076
+ if (el.scrollHeight <= el.clientHeight + 1) return false;
6077
+ if (deltaY < 0) return el.scrollTop > 0;
6078
+ if (deltaY > 0)
6079
+ return el.scrollTop + el.clientHeight < el.scrollHeight - 1;
6080
+ return true;
6081
+ };
6082
+ const allowsInternalScroll = (e) => {
6083
+ const target = e.target;
6084
+ if (!(target instanceof Element)) return false;
6085
+ const scrollRoot = target.closest(
6086
+ "[data-ohw-link-page-dropdown], [data-ohw-allow-scroll]"
6087
+ );
6088
+ if (!scrollRoot) return false;
6089
+ const deltaY = e instanceof WheelEvent ? e.deltaY : 0;
6090
+ return canScrollElement(scrollRoot, deltaY);
6091
+ };
6092
+ const preventBackgroundScroll = (e) => {
6093
+ if (allowsInternalScroll(e)) return;
6094
+ e.preventDefault();
6095
+ };
6096
+ const scrollOpts = { passive: false, capture: true };
6097
+ document.addEventListener("wheel", preventBackgroundScroll, scrollOpts);
6098
+ document.addEventListener("touchmove", preventBackgroundScroll, scrollOpts);
6099
+ return () => {
6100
+ postToParent({ type: "ui:unlock" });
6101
+ html.style.overflow = prevHtmlOverflow;
6102
+ body.style.overflow = prevBodyOverflow;
6103
+ html.style.overscrollBehavior = prevHtmlOverscroll;
6104
+ body.style.overscrollBehavior = prevBodyOverscroll;
6105
+ document.removeEventListener(
6106
+ "wheel",
6107
+ preventBackgroundScroll,
6108
+ scrollOpts
6109
+ );
6110
+ document.removeEventListener(
6111
+ "touchmove",
6112
+ preventBackgroundScroll,
6113
+ scrollOpts
6114
+ );
6115
+ };
6116
+ }, [open, sectionPickerActive]);
6117
+ return /* @__PURE__ */ jsxs12(Fragment4, { children: [
6118
+ /* @__PURE__ */ jsx20(
6119
+ Dialog2,
5561
6120
  {
5562
- type: "button",
5563
- className: "absolute right-[9px] top-[9px] rounded-sm p-1.5 text-foreground hover:bg-muted/50",
5564
- "aria-label": "Close",
5565
- children: /* @__PURE__ */ jsx19(IconX, { "aria-hidden": true })
6121
+ open: open && !sectionPickerActive,
6122
+ onOpenChange: (next) => {
6123
+ if (!next) onClose?.();
6124
+ },
6125
+ children: /* @__PURE__ */ jsx20(
6126
+ DialogContent,
6127
+ {
6128
+ ref: panelRef,
6129
+ container: portalContainer,
6130
+ "data-ohw-link-popover-root": "",
6131
+ "data-ohw-link-modal-root": "",
6132
+ "data-ohw-bridge": "",
6133
+ showCloseButton: false,
6134
+ className: "gap-0 p-0 w-full max-w-[448px] pointer-events-auto z-[2147483646] overflow-visible",
6135
+ children: /* @__PURE__ */ jsx20(LinkEditorPanel, { state, onClose })
6136
+ }
6137
+ )
5566
6138
  }
5567
- ) }),
5568
- /* @__PURE__ */ jsx19(DialogHeader, { className: "w-full gap-1.5 p-6 pr-12", children: /* @__PURE__ */ jsx19(DialogTitle, { className: "m-0 w-full break-words", children: state.title }) }),
5569
- /* @__PURE__ */ jsxs11("div", { className: "flex w-full flex-col gap-3 px-6 pb-8 pt-1", children: [
5570
- state.showBreadcrumb && state.selectedPage && state.selectedSection ? /* @__PURE__ */ jsx19(
5571
- DestinationBreadcrumb,
5572
- {
5573
- pageTitle: state.selectedPage.title,
5574
- sectionLabel: state.selectedSection.label
5575
- }
5576
- ) : /* @__PURE__ */ jsx19(
5577
- UrlOrPageInput,
5578
- {
5579
- value: state.searchValue,
5580
- selectedPage: state.selectedPage,
5581
- dropdownOpen: state.dropdownOpen,
5582
- onDropdownOpenChange: state.setDropdownOpen,
5583
- filteredPages: state.filteredPages,
5584
- onInputChange: state.handleInputChange,
5585
- onPageSelect: state.handlePageSelect,
5586
- urlError: state.urlError
5587
- }
5588
- ),
5589
- state.showChooseSection ? /* @__PURE__ */ jsxs11("div", { className: "flex flex-col justify-center gap-2", children: [
5590
- /* @__PURE__ */ jsx19(Button, { type: "button", variant: "outline", className: "w-fit cursor-pointer", size: "sm", onClick: state.handleChooseSection, children: "Choose a section" }),
5591
- /* @__PURE__ */ jsxs11("div", { className: "flex items-center gap-1 text-sm text-muted-foreground", children: [
5592
- /* @__PURE__ */ jsx19(IconInfo, { className: "shrink-0", "aria-hidden": true }),
5593
- /* @__PURE__ */ jsx19("span", { children: "Pick a section this link should scroll to." })
5594
- ] })
5595
- ] }) : null,
5596
- state.showSectionPicker ? /* @__PURE__ */ jsx19(SectionPickerList, { sections: state.activeSections, onSelect: state.handleSectionSelect }) : null,
5597
- state.showSectionRow && state.selectedSection ? /* @__PURE__ */ jsx19(SectionTreeItem, { section: state.selectedSection, selected: true }) : null
5598
- ] }),
5599
- /* @__PURE__ */ jsxs11(DialogFooter, { className: "w-full px-6 pb-6", children: [
5600
- /* @__PURE__ */ jsx19(
5601
- Button,
5602
- {
5603
- type: "button",
5604
- variant: isCancel ? "outline" : "ghost",
5605
- className: "leading-6 shadow-none cursor-pointer",
5606
- style: isCancel ? {
5607
- backgroundColor: "var(--ohw-background)",
5608
- borderColor: "var(--ohw-border)",
5609
- color: "var(--ohw-foreground)"
5610
- } : void 0,
5611
- onClick: state.handleSecondary,
5612
- children: state.secondaryLabel
5613
- }
5614
- ),
5615
- /* @__PURE__ */ jsx19(
5616
- Button,
5617
- {
5618
- type: "button",
5619
- className: "border-0 leading-6 shadow-none hover:opacity-90 disabled:opacity-50 cursor-pointer",
5620
- style: {
5621
- backgroundColor: "var(--ohw-primary)",
5622
- color: "var(--ohw-primary-foreground)"
5623
- },
5624
- disabled: !state.isValid,
5625
- onClick: state.handleSubmit,
5626
- children: state.submitLabel
5627
- }
5628
- )
5629
- ] })
6139
+ ),
6140
+ sectionPickerActive && state.selectedPage ? /* @__PURE__ */ jsx20(
6141
+ SectionPickerOverlay,
6142
+ {
6143
+ pagePath: state.selectedPage.path,
6144
+ sections: state.activeSections,
6145
+ onBack: state.handleSectionPickerBack,
6146
+ onSelect: state.handleSectionSelect
6147
+ }
6148
+ ) : null
5630
6149
  ] });
5631
6150
  }
5632
6151
 
5633
- // src/ui/link-modal/LinkPopover.tsx
5634
- import { jsx as jsx20 } from "react/jsx-runtime";
5635
- function LinkPopover({
5636
- open = true,
5637
- panelRef,
5638
- portalContainer,
5639
- onClose,
5640
- ...editorProps
5641
- }) {
5642
- return /* @__PURE__ */ jsx20(
5643
- Dialog2,
5644
- {
5645
- open,
5646
- onOpenChange: (next) => {
5647
- if (!next) onClose?.();
5648
- },
5649
- children: /* @__PURE__ */ jsx20(
5650
- DialogContent,
5651
- {
5652
- ref: panelRef,
5653
- container: portalContainer,
5654
- "data-ohw-link-popover-root": "",
5655
- "data-ohw-link-modal-root": "",
5656
- "data-ohw-bridge": "",
5657
- showCloseButton: false,
5658
- className: "gap-0 p-0 w-full md:w-md pointer-events-auto",
5659
- children: /* @__PURE__ */ jsx20(LinkEditorPanel, { ...editorProps, open, onClose })
5660
- }
5661
- )
5662
- }
5663
- );
5664
- }
5665
-
5666
6152
  // src/ui/link-modal/devFixtures.ts
5667
6153
  var DEV_SITE_PAGES = [
5668
6154
  { path: "/", title: "Home" },
@@ -5682,16 +6168,21 @@ var DEV_SECTIONS_BY_PATH = {
5682
6168
  { id: "founder-teaser", label: "Founder" },
5683
6169
  { id: "plan-form", label: "Plan Form" },
5684
6170
  { id: "testimonials", label: "Testimonials" },
5685
- { id: "wordmark", label: "Wordmark" }
6171
+ { id: "wordmark", label: "Wordmark" },
6172
+ { id: "footer", label: "Footer" }
5686
6173
  ],
5687
6174
  "/about": [
5688
6175
  { id: "manifesto", label: "Manifesto" },
5689
6176
  { id: "story-letter", label: "Our Story" },
5690
6177
  { id: "lagree-explainer", label: "Lagree Explainer" },
5691
6178
  { id: "waitlist-cta", label: "Waitlist" },
5692
- { id: "personal-training", label: "Personal training" }
6179
+ { id: "personal-training", label: "Personal training" },
6180
+ { id: "footer", label: "Footer" }
6181
+ ],
6182
+ "/classes": [
6183
+ { id: "class-library", label: "Class Library" },
6184
+ { id: "footer", label: "Footer" }
5693
6185
  ],
5694
- "/classes": [{ id: "class-library", label: "Class Library" }],
5695
6186
  "/pricing": [
5696
6187
  { id: "page-header", label: "Page Header" },
5697
6188
  { id: "free-class-cta", label: "Free Class" },
@@ -5699,19 +6190,25 @@ var DEV_SECTIONS_BY_PATH = {
5699
6190
  { id: "monthly-memberships", label: "Memberships" },
5700
6191
  { id: "specialty-programs", label: "Specialty Programs" },
5701
6192
  { id: "package-matcher", label: "Packages" },
5702
- { id: "wordmark", label: "Wordmark" }
6193
+ { id: "wordmark", label: "Wordmark" },
6194
+ { id: "footer", label: "Footer" }
5703
6195
  ],
5704
6196
  "/studio": [
5705
6197
  { id: "studio-intro", label: "Studio Intro" },
5706
6198
  { id: "studio-features", label: "Studio Features" },
5707
6199
  { id: "studio-gallery", label: "Studio Gallery" },
5708
- { id: "studio-visit", label: "Visit Studio" }
6200
+ { id: "studio-visit", label: "Visit Studio" },
6201
+ { id: "footer", label: "Footer" }
6202
+ ],
6203
+ "/instructors": [
6204
+ { id: "instructor-grid", label: "Instructors" },
6205
+ { id: "footer", label: "Footer" }
5709
6206
  ],
5710
- "/instructors": [{ id: "instructor-grid", label: "Instructors" }],
5711
6207
  "/contact": [
5712
6208
  { id: "hello-hero", label: "Hello" },
5713
6209
  { id: "contact-form", label: "Contact Form" },
5714
- { id: "faq", label: "FAQ" }
6210
+ { id: "faq", label: "FAQ" },
6211
+ { id: "footer", label: "Footer" }
5715
6212
  ]
5716
6213
  };
5717
6214
  function shouldUseDevFixtures() {
@@ -5721,8 +6218,277 @@ function shouldUseDevFixtures() {
5721
6218
  return new URLSearchParams(q).get("ohw-fixtures") === "1";
5722
6219
  }
5723
6220
 
5724
- // src/ui/badge.tsx
6221
+ // src/lib/nav-items.ts
6222
+ var NAV_COUNT_KEY = "__ohw_nav_count";
6223
+ var NAV_ORDER_KEY = "__ohw_nav_order";
6224
+ function getLinkHref(el) {
6225
+ const key = el.getAttribute("data-ohw-href-key");
6226
+ if (key) {
6227
+ const stored = getStoredLinkHref(key);
6228
+ if (stored !== void 0) return stored;
6229
+ }
6230
+ return el.getAttribute("href") ?? "";
6231
+ }
6232
+ function isNavbarButton(el) {
6233
+ return el.hasAttribute("data-ohw-role") && el.getAttribute("data-ohw-role") === "navbar-button";
6234
+ }
6235
+ function isNavbarLinkItem(el) {
6236
+ if (!el.matches("[data-ohw-href-key]")) return false;
6237
+ if (isNavbarButton(el)) return false;
6238
+ if (!el.querySelector('[data-ohw-editable="text"]')) return false;
6239
+ return Boolean(el.closest("nav, [data-ohw-nav-container], [data-ohw-nav-drawer]"));
6240
+ }
6241
+ function getNavbarDesktopContainer() {
6242
+ return document.querySelector("[data-ohw-nav-container]");
6243
+ }
6244
+ function getNavbarDrawerContainer() {
6245
+ return document.querySelector("[data-ohw-nav-drawer]");
6246
+ }
6247
+ function listNavbarItems() {
6248
+ const desktop = getNavbarDesktopContainer();
6249
+ if (desktop) {
6250
+ return Array.from(desktop.querySelectorAll("[data-ohw-href-key]")).filter(isNavbarLinkItem);
6251
+ }
6252
+ const drawer = getNavbarDrawerContainer();
6253
+ if (drawer) {
6254
+ return Array.from(drawer.querySelectorAll("[data-ohw-href-key]")).filter(isNavbarLinkItem);
6255
+ }
6256
+ return Array.from(document.querySelectorAll("nav [data-ohw-href-key]")).filter(isNavbarLinkItem);
6257
+ }
6258
+ function parseNavIndexFromKey(key) {
6259
+ if (!key) return null;
6260
+ const match = key.match(/^nav-(\d+)-href$/);
6261
+ if (!match) return null;
6262
+ return parseInt(match[1], 10);
6263
+ }
6264
+ function getNavbarIndicesInDom() {
6265
+ const indices = /* @__PURE__ */ new Set();
6266
+ for (const item of listNavbarItems()) {
6267
+ const idx = parseNavIndexFromKey(item.getAttribute("data-ohw-href-key"));
6268
+ if (idx !== null) indices.add(idx);
6269
+ }
6270
+ return [...indices].sort((a, b) => a - b);
6271
+ }
6272
+ function getNextNavbarIndex() {
6273
+ const indices = getNavbarIndicesInDom();
6274
+ if (indices.length === 0) return 0;
6275
+ return Math.max(...indices) + 1;
6276
+ }
6277
+ function getNavbarExistingTargets() {
6278
+ return listNavbarItems().map((el) => getLinkHref(el)).filter(Boolean);
6279
+ }
6280
+ function getNavOrderFromDom() {
6281
+ return listNavbarItems().map((el) => el.getAttribute("data-ohw-href-key")).filter((key) => Boolean(key));
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
+ }
6294
+ function findCounterpartByHrefKey(hrefKey, root) {
6295
+ return root.querySelector(`[data-ohw-href-key="${CSS.escape(hrefKey)}"]`);
6296
+ }
6297
+ function cloneNavLinkShell(template) {
6298
+ const anchor = document.createElement("a");
6299
+ if (template) {
6300
+ anchor.style.cssText = template.style.cssText;
6301
+ if (template.className) anchor.className = template.className;
6302
+ }
6303
+ anchor.style.cursor = "pointer";
6304
+ anchor.style.textDecoration = "none";
6305
+ return anchor;
6306
+ }
6307
+ function buildNavLink(index, href, label, template) {
6308
+ const anchor = cloneNavLinkShell(template);
6309
+ anchor.href = href;
6310
+ anchor.setAttribute("data-ohw-href-key", `nav-${index}-href`);
6311
+ const span = document.createElement("span");
6312
+ span.setAttribute("data-ohw-editable", "text");
6313
+ span.setAttribute("data-ohw-key", `nav-${index}-label`);
6314
+ span.textContent = label;
6315
+ anchor.appendChild(span);
6316
+ return anchor;
6317
+ }
6318
+ function insertNavLinkInContainer(container, anchor, afterHrefKey) {
6319
+ if (!afterHrefKey) {
6320
+ container.appendChild(anchor);
6321
+ return;
6322
+ }
6323
+ const afterEl = findCounterpartByHrefKey(afterHrefKey, container);
6324
+ if (afterEl?.parentElement === container) {
6325
+ afterEl.insertAdjacentElement("afterend", anchor);
6326
+ return;
6327
+ }
6328
+ container.appendChild(anchor);
6329
+ }
6330
+ function insertNavbarItemDom(index, href, label, afterAnchor) {
6331
+ const afterHrefKey = afterAnchor?.getAttribute("data-ohw-href-key") ?? null;
6332
+ const desktopItems = getNavbarDesktopContainer()?.querySelectorAll("[data-ohw-href-key]") ?? [];
6333
+ const drawerItems = getNavbarDrawerContainer()?.querySelectorAll("[data-ohw-href-key]") ?? [];
6334
+ const desktopTemplate = Array.from(desktopItems).find(isNavbarLinkItem) ?? null;
6335
+ const drawerTemplate = Array.from(drawerItems).find(isNavbarLinkItem) ?? null;
6336
+ let primary = null;
6337
+ const desktopContainer = getNavbarDesktopContainer();
6338
+ if (desktopContainer) {
6339
+ const desktopAnchor = buildNavLink(index, href, label, desktopTemplate);
6340
+ insertNavLinkInContainer(desktopContainer, desktopAnchor, afterHrefKey);
6341
+ primary = desktopAnchor;
6342
+ }
6343
+ const drawerContainer = getNavbarDrawerContainer();
6344
+ if (drawerContainer) {
6345
+ const drawerAnchor = buildNavLink(index, href, label, drawerTemplate);
6346
+ insertNavLinkInContainer(drawerContainer, drawerAnchor, afterHrefKey);
6347
+ if (!primary) primary = drawerAnchor;
6348
+ }
6349
+ if (!primary) {
6350
+ const fallback = buildNavLink(index, href, label, listNavbarItems()[0] ?? null);
6351
+ const nav = document.querySelector("nav");
6352
+ nav?.appendChild(fallback);
6353
+ primary = fallback;
6354
+ }
6355
+ return primary;
6356
+ }
6357
+ function applyNavOrderToContainer(container, order) {
6358
+ const seen = /* @__PURE__ */ new Set();
6359
+ for (const hrefKey of order) {
6360
+ if (seen.has(hrefKey)) continue;
6361
+ seen.add(hrefKey);
6362
+ const el = findCounterpartByHrefKey(hrefKey, container);
6363
+ if (el) container.appendChild(el);
6364
+ }
6365
+ for (const el of container.querySelectorAll("[data-ohw-href-key]")) {
6366
+ if (!isNavbarLinkItem(el)) continue;
6367
+ const key = el.getAttribute("data-ohw-href-key");
6368
+ if (!key || seen.has(key)) continue;
6369
+ container.appendChild(el);
6370
+ }
6371
+ }
6372
+ function applyNavOrder(order) {
6373
+ const desktop = getNavbarDesktopContainer();
6374
+ if (desktop) applyNavOrderToContainer(desktop, order);
6375
+ const drawer = getNavbarDrawerContainer();
6376
+ if (drawer) applyNavOrderToContainer(drawer, order);
6377
+ }
6378
+ function collectNavbarIndicesFromContent(content) {
6379
+ const indices = /* @__PURE__ */ new Set();
6380
+ for (const key of Object.keys(content)) {
6381
+ const idx = parseNavIndexFromKey(key);
6382
+ if (idx !== null) indices.add(idx);
6383
+ }
6384
+ const countRaw = content[NAV_COUNT_KEY];
6385
+ if (countRaw) {
6386
+ const count = parseInt(countRaw, 10);
6387
+ if (Number.isFinite(count) && count > 0) {
6388
+ for (let i = 0; i < count; i++) indices.add(i);
6389
+ }
6390
+ }
6391
+ return [...indices].sort((a, b) => a - b);
6392
+ }
6393
+ function navbarIndexExistsInDom(index) {
6394
+ return document.querySelector(`[data-ohw-href-key="nav-${index}-href"]`) !== null;
6395
+ }
6396
+ function reconcileNavbarItemsFromContent(content) {
6397
+ const indices = collectNavbarIndicesFromContent(content);
6398
+ for (const index of indices) {
6399
+ if (navbarIndexExistsInDom(index)) continue;
6400
+ const href = content[`nav-${index}-href`];
6401
+ const label = content[`nav-${index}-label`];
6402
+ if (!href && !label) continue;
6403
+ insertNavbarItemDom(index, href ?? "/", label ?? "Untitled", null);
6404
+ }
6405
+ const order = parseNavOrder(content);
6406
+ if (order?.length) applyNavOrder(order);
6407
+ }
6408
+ function buildNavOrderAfterInsert(afterAnchor, newHrefKey) {
6409
+ const order = getNavOrderFromDom();
6410
+ if (!afterAnchor) {
6411
+ if (!order.includes(newHrefKey)) order.push(newHrefKey);
6412
+ return order;
6413
+ }
6414
+ const afterKey = afterAnchor.getAttribute("data-ohw-href-key");
6415
+ if (!afterKey) {
6416
+ if (!order.includes(newHrefKey)) order.push(newHrefKey);
6417
+ return order;
6418
+ }
6419
+ const withoutNew = order.filter((key) => key !== newHrefKey);
6420
+ const pos = withoutNew.indexOf(afterKey);
6421
+ if (pos >= 0) {
6422
+ withoutNew.splice(pos + 1, 0, newHrefKey);
6423
+ return withoutNew;
6424
+ }
6425
+ withoutNew.push(newHrefKey);
6426
+ return withoutNew;
6427
+ }
6428
+ function insertNavbarItem(href, label, afterAnchor = null) {
6429
+ const index = getNextNavbarIndex();
6430
+ const anchor = insertNavbarItemDom(index, href, label, afterAnchor);
6431
+ if (!anchor) throw new Error("Failed to insert navbar item");
6432
+ const hrefKey = `nav-${index}-href`;
6433
+ const order = buildNavOrderAfterInsert(afterAnchor, hrefKey);
6434
+ applyNavOrder(order);
6435
+ return {
6436
+ anchor,
6437
+ index,
6438
+ hrefKey,
6439
+ labelKey: `nav-${index}-label`,
6440
+ href,
6441
+ label,
6442
+ order
6443
+ };
6444
+ }
6445
+
6446
+ // src/ui/navbar-container-chrome.tsx
6447
+ import { Plus as Plus2 } from "lucide-react";
5725
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
+ )
6486
+ }
6487
+ );
6488
+ }
6489
+
6490
+ // src/ui/badge.tsx
6491
+ import { jsx as jsx22 } from "react/jsx-runtime";
5726
6492
  var badgeVariants = cva(
5727
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",
5728
6494
  {
@@ -5740,12 +6506,12 @@ var badgeVariants = cva(
5740
6506
  }
5741
6507
  );
5742
6508
  function Badge({ className, variant, ...props }) {
5743
- return /* @__PURE__ */ jsx21("div", { className: cn(badgeVariants({ variant }), className), ...props });
6509
+ return /* @__PURE__ */ jsx22("div", { className: cn(badgeVariants({ variant }), className), ...props });
5744
6510
  }
5745
6511
 
5746
6512
  // src/OhhwellsBridge.tsx
5747
6513
  import { Link as Link2 } from "lucide-react";
5748
- import { Fragment as Fragment4, jsx as jsx22, jsxs as jsxs12 } from "react/jsx-runtime";
6514
+ import { Fragment as Fragment5, jsx as jsx23, jsxs as jsxs13 } from "react/jsx-runtime";
5749
6515
  var PRIMARY2 = "#0885FE";
5750
6516
  var IMAGE_FADE_MS = 300;
5751
6517
  function runOpacityFade(el, onDone) {
@@ -5924,7 +6690,7 @@ function mountSchedulingWidget(insertAfter, notifyOnConnect = false, scheduleId,
5924
6690
  const root = createRoot(container);
5925
6691
  flushSync(() => {
5926
6692
  root.render(
5927
- /* @__PURE__ */ jsx22(
6693
+ /* @__PURE__ */ jsx23(
5928
6694
  SchedulingWidget,
5929
6695
  {
5930
6696
  notifyOnConnect,
@@ -5964,7 +6730,7 @@ function mountSchedulingEntries(entries, notifyOnConnect = false) {
5964
6730
  }
5965
6731
  }
5966
6732
  }
5967
- function getLinkHref(el) {
6733
+ function getLinkHref2(el) {
5968
6734
  const anchor = el instanceof HTMLAnchorElement ? el : el.querySelector("a");
5969
6735
  return anchor?.getAttribute("href") ?? "";
5970
6736
  }
@@ -6001,7 +6767,7 @@ function collectEditableNodes() {
6001
6767
  return { key: el.dataset.ohwKey ?? "", type: "video", text: getVideoEl(el)?.src ?? "" };
6002
6768
  }
6003
6769
  if (el.dataset.ohwEditable === "link") {
6004
- return { key: el.dataset.ohwKey ?? "", type: "link", text: getLinkHref(el) };
6770
+ return { key: el.dataset.ohwKey ?? "", type: "link", text: getLinkHref2(el) };
6005
6771
  }
6006
6772
  return {
6007
6773
  key: el.dataset.ohwKey ?? "",
@@ -6012,7 +6778,7 @@ function collectEditableNodes() {
6012
6778
  document.querySelectorAll("[data-ohw-href-key]").forEach((el) => {
6013
6779
  const key = el.getAttribute("data-ohw-href-key") ?? "";
6014
6780
  if (!key) return;
6015
- nodes.push({ key, type: "link", text: getLinkHref(el) });
6781
+ nodes.push({ key, type: "link", text: getLinkHref2(el) });
6016
6782
  });
6017
6783
  document.querySelectorAll('[data-ohw-editable="video"]').forEach((el) => {
6018
6784
  const key = el.dataset.ohwKey ?? "";
@@ -6104,7 +6870,7 @@ function applyLinkByKey(key, val) {
6104
6870
  }
6105
6871
  function isInsideLinkEditor(target) {
6106
6872
  return Boolean(
6107
- target.closest("[data-ohw-link-popover-root]") || target.closest("[data-ohw-link-modal-root]") || target.closest("[data-ohw-link-page-dropdown]") || target.closest('[data-slot="popover-content"]') || target.closest('[data-slot="dialog-content"]') || target.closest('[data-slot="dialog-overlay"]')
6873
+ target.closest("[data-ohw-link-popover-root]") || target.closest("[data-ohw-link-modal-root]") || target.closest("[data-ohw-link-page-dropdown]") || target.closest("[data-ohw-section-picker]") || target.closest("[data-ohw-navbar-container-chrome]") || target.closest("[data-ohw-navbar-add-button]") || target.closest('[data-slot="popover-content"]') || target.closest('[data-slot="dialog-content"]') || target.closest('[data-slot="dialog-overlay"]')
6108
6874
  );
6109
6875
  }
6110
6876
  function getHrefKeyFromElement(el) {
@@ -6115,7 +6881,7 @@ function getHrefKeyFromElement(el) {
6115
6881
  if (!key) return null;
6116
6882
  return { anchor, key };
6117
6883
  }
6118
- function isNavbarButton(el) {
6884
+ function isNavbarButton2(el) {
6119
6885
  return Boolean(el.closest('[data-ohw-role="navbar-button"]'));
6120
6886
  }
6121
6887
  function getNavigationItemAnchor(el) {
@@ -6127,19 +6893,8 @@ function getNavigationItemAnchor(el) {
6127
6893
  function isNavigationItem(el) {
6128
6894
  return getNavigationItemAnchor(el) !== null;
6129
6895
  }
6130
- function getNavigationItemAddHintTarget(anchor) {
6131
- const items = listNavigationItems();
6132
- const idx = items.indexOf(anchor);
6133
- if (idx >= 0 && idx < items.length - 1) return items[idx + 1];
6134
- return anchor;
6135
- }
6136
- function listNavigationItems() {
6137
- return Array.from(
6138
- document.querySelectorAll("nav [data-ohw-href-key], footer [data-ohw-href-key]")
6139
- ).filter((el) => isNavigationItem(el));
6140
- }
6141
6896
  function getNavigationItemReorderState(anchor) {
6142
- if (isNavbarButton(anchor)) return { key: null, disabled: false };
6897
+ if (isNavbarButton2(anchor)) return { key: null, disabled: false };
6143
6898
  return getReorderHandleStateFromAnchor(anchor);
6144
6899
  }
6145
6900
  function getReorderHandleState(el) {
@@ -6161,6 +6916,120 @@ function clearHrefKeyHover(anchor) {
6161
6916
  function isInsideNavigationItem(el) {
6162
6917
  return getNavigationItemAnchor(el) !== null;
6163
6918
  }
6919
+ function getNavigationRoot(el) {
6920
+ const explicit = el.closest("[data-ohw-nav-root]");
6921
+ if (explicit) return explicit;
6922
+ return el.closest("nav, footer, aside");
6923
+ }
6924
+ function findFooterItemGroup(item) {
6925
+ const footer = item.closest("footer");
6926
+ if (!footer) return null;
6927
+ let node = item.parentElement;
6928
+ 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;
6933
+ node = node.parentElement;
6934
+ }
6935
+ return null;
6936
+ }
6937
+ function isNavigationRoot(el) {
6938
+ return el.hasAttribute("data-ohw-nav-root") || el.matches("nav, footer, aside");
6939
+ }
6940
+ function isInferredFooterGroup(el) {
6941
+ const footer = el.closest("footer");
6942
+ if (!footer || el === footer) return false;
6943
+ return Array.from(el.querySelectorAll(":scope > [data-ohw-href-key]")).some(isNavigationItem);
6944
+ }
6945
+ function isNavigationContainer(el) {
6946
+ return el.hasAttribute("data-ohw-nav-container") || isNavigationRoot(el) || isInferredFooterGroup(el);
6947
+ }
6948
+ function isPointOverNavigation(x, y) {
6949
+ 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;
6953
+ }
6954
+ function isPointOverNavItem(container, x, y) {
6955
+ return Array.from(container.querySelectorAll("[data-ohw-href-key]")).some((el) => {
6956
+ if (!isNavigationItem(el) || isNavbarButton2(el)) return false;
6957
+ const itemRect = el.getBoundingClientRect();
6958
+ return x >= itemRect.left && x <= itemRect.right && y >= itemRect.top && y <= itemRect.bottom;
6959
+ });
6960
+ }
6961
+ function resolveNavContainerSelectionTarget(target, clientX, clientY) {
6962
+ if (getNavigationItemAnchor(target)) return null;
6963
+ if (target.closest('[data-ohw-role="navbar-button"]')) return null;
6964
+ const plainLink = target.closest("a");
6965
+ if (plainLink && !plainLink.hasAttribute("data-ohw-href-key") && !plainLink.closest("[data-ohw-nav-container]")) {
6966
+ return null;
6967
+ }
6968
+ const fromClosest = target.closest("[data-ohw-nav-container]");
6969
+ if (fromClosest && !isPointOverNavItem(fromClosest, clientX, clientY)) {
6970
+ return fromClosest;
6971
+ }
6972
+ const navRoot = target.closest("[data-ohw-nav-root]");
6973
+ if (!navRoot) return null;
6974
+ const container = navRoot.querySelector("[data-ohw-nav-container]");
6975
+ if (!container || isPointOverNavItem(container, clientX, clientY)) return null;
6976
+ if (target === navRoot || target.hasAttribute("data-ohw-nav-root")) {
6977
+ return container;
6978
+ }
6979
+ return null;
6980
+ }
6981
+ function getNavigationSelectionParent(el) {
6982
+ if (isNavigationItem(el)) {
6983
+ const explicit = el.closest("[data-ohw-nav-container]");
6984
+ if (explicit) return explicit;
6985
+ const footerGroup = findFooterItemGroup(el);
6986
+ if (footerGroup) return footerGroup;
6987
+ return getNavigationRoot(el);
6988
+ }
6989
+ if (el.hasAttribute("data-ohw-nav-container") || isInferredFooterGroup(el)) {
6990
+ return getNavigationRoot(el);
6991
+ }
6992
+ return null;
6993
+ }
6994
+ function placeCaretAtPoint(el, x, y) {
6995
+ const selection = window.getSelection();
6996
+ if (!selection) return;
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;
7003
+ }
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);
7014
+ }
7015
+ }
7016
+ }
7017
+ function selectAllTextInEditable(el) {
7018
+ const selection = window.getSelection();
7019
+ if (!selection) return;
7020
+ const range = document.createRange();
7021
+ range.selectNodeContents(el);
7022
+ selection.removeAllRanges();
7023
+ selection.addRange(range);
7024
+ }
7025
+ function getNavigationLabelEditable(target) {
7026
+ const editable = target.closest('[data-ohw-editable="text"]');
7027
+ if (!editable) return null;
7028
+ const hrefCtx = getHrefKeyFromElement(editable);
7029
+ const navAnchor = hrefCtx ? getNavigationItemAnchor(hrefCtx.anchor) : null;
7030
+ if (!navAnchor) return null;
7031
+ return { editable, navAnchor };
7032
+ }
6164
7033
  function collectSections() {
6165
7034
  return collectSectionsFromDom();
6166
7035
  }
@@ -6308,7 +7177,7 @@ function EditGlowChrome({
6308
7177
  dragDisabled = false
6309
7178
  }) {
6310
7179
  const GAP = 6;
6311
- return /* @__PURE__ */ jsxs12(
7180
+ return /* @__PURE__ */ jsxs13(
6312
7181
  "div",
6313
7182
  {
6314
7183
  ref: elRef,
@@ -6323,7 +7192,7 @@ function EditGlowChrome({
6323
7192
  zIndex: 2147483646
6324
7193
  },
6325
7194
  children: [
6326
- /* @__PURE__ */ jsx22(
7195
+ /* @__PURE__ */ jsx23(
6327
7196
  "div",
6328
7197
  {
6329
7198
  style: {
@@ -6336,7 +7205,7 @@ function EditGlowChrome({
6336
7205
  }
6337
7206
  }
6338
7207
  ),
6339
- reorderHrefKey && /* @__PURE__ */ jsx22(
7208
+ reorderHrefKey && /* @__PURE__ */ jsx23(
6340
7209
  "div",
6341
7210
  {
6342
7211
  "data-ohw-drag-handle-container": "",
@@ -6348,7 +7217,7 @@ function EditGlowChrome({
6348
7217
  transform: "translate(calc(-100% - 7px), -50%)",
6349
7218
  pointerEvents: dragDisabled ? "none" : "auto"
6350
7219
  },
6351
- children: /* @__PURE__ */ jsx22(
7220
+ children: /* @__PURE__ */ jsx23(
6352
7221
  DragHandle,
6353
7222
  {
6354
7223
  "aria-label": `Reorder ${reorderHrefKey}`,
@@ -6452,7 +7321,7 @@ function FloatingToolbar({
6452
7321
  onEditLink
6453
7322
  }) {
6454
7323
  const { top, left, transform } = calcToolbarPos(rect, parentScroll, 330);
6455
- return /* @__PURE__ */ jsx22(
7324
+ return /* @__PURE__ */ jsx23(
6456
7325
  "div",
6457
7326
  {
6458
7327
  ref: elRef,
@@ -6464,12 +7333,12 @@ function FloatingToolbar({
6464
7333
  zIndex: 2147483647,
6465
7334
  pointerEvents: "auto"
6466
7335
  },
6467
- children: /* @__PURE__ */ jsxs12(CustomToolbar, { children: [
6468
- TOOLBAR_GROUPS.map((btns, gi) => /* @__PURE__ */ jsxs12(React9.Fragment, { children: [
6469
- gi > 0 && /* @__PURE__ */ jsx22(CustomToolbarDivider, {}),
7336
+ children: /* @__PURE__ */ jsxs13(CustomToolbar, { children: [
7337
+ TOOLBAR_GROUPS.map((btns, gi) => /* @__PURE__ */ jsxs13(React9.Fragment, { children: [
7338
+ gi > 0 && /* @__PURE__ */ jsx23(CustomToolbarDivider, {}),
6470
7339
  btns.map((btn) => {
6471
7340
  const isActive = activeCommands.has(btn.cmd);
6472
- return /* @__PURE__ */ jsx22(
7341
+ return /* @__PURE__ */ jsx23(
6473
7342
  CustomToolbarButton,
6474
7343
  {
6475
7344
  title: btn.title,
@@ -6478,7 +7347,7 @@ function FloatingToolbar({
6478
7347
  e.preventDefault();
6479
7348
  onCommand(btn.cmd);
6480
7349
  },
6481
- children: /* @__PURE__ */ jsx22(
7350
+ children: /* @__PURE__ */ jsx23(
6482
7351
  "svg",
6483
7352
  {
6484
7353
  width: "16",
@@ -6499,7 +7368,7 @@ function FloatingToolbar({
6499
7368
  );
6500
7369
  })
6501
7370
  ] }, gi)),
6502
- showEditLink ? /* @__PURE__ */ jsx22(
7371
+ showEditLink ? /* @__PURE__ */ jsx23(
6503
7372
  CustomToolbarButton,
6504
7373
  {
6505
7374
  type: "button",
@@ -6513,7 +7382,7 @@ function FloatingToolbar({
6513
7382
  e.preventDefault();
6514
7383
  e.stopPropagation();
6515
7384
  },
6516
- children: /* @__PURE__ */ jsx22(Link2, { className: "size-4 shrink-0", "aria-hidden": true })
7385
+ children: /* @__PURE__ */ jsx23(Link2, { className: "size-4 shrink-0", "aria-hidden": true })
6517
7386
  }
6518
7387
  ) : null
6519
7388
  ] })
@@ -6530,7 +7399,7 @@ function StateToggle({
6530
7399
  states,
6531
7400
  onStateChange
6532
7401
  }) {
6533
- return /* @__PURE__ */ jsx22(
7402
+ return /* @__PURE__ */ jsx23(
6534
7403
  ToggleGroup,
6535
7404
  {
6536
7405
  "data-ohw-state-toggle": "",
@@ -6544,7 +7413,7 @@ function StateToggle({
6544
7413
  left: rect.right - 8,
6545
7414
  transform: "translateX(-100%)"
6546
7415
  },
6547
- children: states.map((state) => /* @__PURE__ */ jsx22(ToggleGroupItem, { value: state, size: "sm", children: state }, state))
7416
+ children: states.map((state) => /* @__PURE__ */ jsx23(ToggleGroupItem, { value: state, size: "sm", children: state }, state))
6548
7417
  }
6549
7418
  );
6550
7419
  }
@@ -6567,12 +7436,12 @@ function resolveSubdomain(subdomainFromQuery) {
6567
7436
  return "";
6568
7437
  }
6569
7438
  function OhhwellsBridge() {
6570
- const pathname = usePathname();
6571
- const router = useRouter();
7439
+ const pathname = usePathname2();
7440
+ const router = useRouter2();
6572
7441
  const searchParams = useSearchParams();
6573
7442
  const isEditMode = isEditSessionActive();
6574
- const [bridgeRoot, setBridgeRoot] = useState5(null);
6575
- useEffect4(() => {
7443
+ const [bridgeRoot, setBridgeRoot] = useState6(null);
7444
+ useEffect7(() => {
6576
7445
  const figtreeFontId = "ohw-figtree-font";
6577
7446
  if (!document.getElementById(figtreeFontId)) {
6578
7447
  const preconnect1 = Object.assign(document.createElement("link"), { rel: "preconnect", href: "https://fonts.googleapis.com" });
@@ -6600,83 +7469,91 @@ function OhhwellsBridge() {
6600
7469
  const subdomainFromQuery = searchParams.get("subdomain");
6601
7470
  const subdomain = resolveSubdomain(subdomainFromQuery);
6602
7471
  useLinkHrefGuardian(pathname, subdomain, isEditMode);
6603
- const postToParent = useCallback2((data) => {
7472
+ const postToParent2 = useCallback3((data) => {
6604
7473
  if (typeof window !== "undefined" && window.parent !== window) {
6605
7474
  window.parent.postMessage(data, "*");
6606
7475
  }
6607
7476
  }, []);
6608
- const [fetchState, setFetchState] = useState5("idle");
6609
- const autoSaveTimers = useRef4(/* @__PURE__ */ new Map());
6610
- const activeElRef = useRef4(null);
6611
- const selectedElRef = useRef4(null);
6612
- const originalContentRef = useRef4(null);
6613
- const activeStateElRef = useRef4(null);
6614
- const parentScrollRef = useRef4(null);
6615
- const visibleViewportRef = useRef4(null);
6616
- const [dialogPortalContainer, setDialogPortalContainer] = useState5(null);
6617
- const attachVisibleViewport = useCallback2((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) => {
6618
7487
  visibleViewportRef.current = node;
6619
7488
  setDialogPortalContainer(node);
6620
7489
  if (node) applyVisibleViewport(node, parentScrollRef.current);
6621
7490
  }, []);
6622
- const toolbarElRef = useRef4(null);
6623
- const glowElRef = useRef4(null);
6624
- const hoveredImageRef = useRef4(null);
6625
- const hoveredImageHasTextOverlapRef = useRef4(false);
6626
- const dragOverElRef = useRef4(null);
6627
- const [mediaHover, setMediaHover] = useState5(null);
6628
- const [uploadingRects, setUploadingRects] = useState5({});
6629
- const hoveredGapRef = useRef4(null);
6630
- const imageUnhoverTimerRef = useRef4(null);
6631
- const imageShowTimerRef = useRef4(null);
6632
- const editStylesRef = useRef4(null);
6633
- const activateRef = useRef4(() => {
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(() => {
7503
+ });
7504
+ const deactivateRef = useRef5(() => {
6634
7505
  });
6635
- const deactivateRef = useRef4(() => {
7506
+ const selectRef = useRef5(() => {
6636
7507
  });
6637
- const selectRef = useRef4(() => {
7508
+ const selectFrameRef = useRef5(() => {
6638
7509
  });
6639
- const deselectRef = useRef4(() => {
7510
+ const deselectRef = useRef5(() => {
6640
7511
  });
6641
- const reselectNavigationItemRef = useRef4(() => {
7512
+ const reselectNavigationItemRef = useRef5(() => {
6642
7513
  });
6643
- const commitNavigationTextEditRef = useRef4(() => {
7514
+ const commitNavigationTextEditRef = useRef5(() => {
6644
7515
  });
6645
- const refreshActiveCommandsRef = useRef4(() => {
7516
+ const refreshActiveCommandsRef = useRef5(() => {
6646
7517
  });
6647
- const postToParentRef = useRef4(postToParent);
6648
- postToParentRef.current = postToParent;
6649
- const sectionsLoadedRef = useRef4(false);
6650
- const pendingScheduleConfigRequests = useRef4([]);
6651
- const [toolbarRect, setToolbarRect] = useState5(null);
6652
- const [toolbarVariant, setToolbarVariant] = useState5("none");
6653
- const toolbarVariantRef = useRef4("none");
7518
+ const postToParentRef = useRef5(postToParent2);
7519
+ postToParentRef.current = postToParent2;
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");
6654
7525
  toolbarVariantRef.current = toolbarVariant;
6655
- const [reorderHrefKey, setReorderHrefKey] = useState5(null);
6656
- const [reorderDragDisabled, setReorderDragDisabled] = useState5(false);
6657
- const [toggleState, setToggleState] = useState5(null);
6658
- const [maxBadge, setMaxBadge] = useState5(null);
6659
- const [activeCommands, setActiveCommands] = useState5(/* @__PURE__ */ new Set());
6660
- const [sectionGap, setSectionGap] = useState5(null);
6661
- const [toolbarShowEditLink, setToolbarShowEditLink] = useState5(false);
6662
- const hoveredItemElRef = useRef4(null);
6663
- const [hoveredItemRect, setHoveredItemRect] = useState5(null);
6664
- const siblingHintElRef = useRef4(null);
6665
- const [siblingHintRect, setSiblingHintRect] = useState5(null);
6666
- const [isItemDragging, setIsItemDragging] = useState5(false);
6667
- const [linkPopover, setLinkPopover] = useState5(null);
6668
- const [sitePages, setSitePages] = useState5([]);
6669
- const [sectionsByPath, setSectionsByPath] = useState5({});
6670
- const sectionsPrefetchGenRef = useRef4(0);
6671
- const setLinkPopoverRef = useRef4(setLinkPopover);
6672
- const linkPopoverPanelRef = useRef4(null);
6673
- const linkPopoverOpenRef = useRef4(false);
6674
- const linkPopoverGraceUntilRef = useRef4(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);
6675
7551
  setLinkPopoverRef.current = setLinkPopover;
7552
+ linkPopoverSessionRef.current = linkPopover;
6676
7553
  const bumpLinkPopoverGrace = () => {
6677
7554
  linkPopoverGraceUntilRef.current = Date.now() + 350;
6678
7555
  };
6679
- const runSectionsPrefetch = useCallback2((pages) => {
7556
+ const runSectionsPrefetch = useCallback3((pages) => {
6680
7557
  if (!isEditMode || shouldUseDevFixtures() || pages.length === 0) return;
6681
7558
  const gen = ++sectionsPrefetchGenRef.current;
6682
7559
  const paths = pages.map((p) => p.path);
@@ -6695,9 +7572,9 @@ function OhhwellsBridge() {
6695
7572
  );
6696
7573
  });
6697
7574
  }, [isEditMode, pathname]);
6698
- const runSectionsPrefetchRef = useRef4(runSectionsPrefetch);
7575
+ const runSectionsPrefetchRef = useRef5(runSectionsPrefetch);
6699
7576
  runSectionsPrefetchRef.current = runSectionsPrefetch;
6700
- useEffect4(() => {
7577
+ useEffect7(() => {
6701
7578
  if (!linkPopover) return;
6702
7579
  if (hoveredImageRef.current) {
6703
7580
  hoveredImageRef.current = null;
@@ -6706,7 +7583,7 @@ function OhhwellsBridge() {
6706
7583
  hoveredGapRef.current = null;
6707
7584
  setSectionGap(null);
6708
7585
  setMediaHover(null);
6709
- postToParent({ type: "ow:link-modal-lock", locked: true });
7586
+ postToParent2({ type: "ow:link-modal-lock", locked: true });
6710
7587
  const html = document.documentElement;
6711
7588
  const body = document.body;
6712
7589
  const prevHtmlOverflow = html.style.overflow;
@@ -6723,15 +7600,16 @@ function OhhwellsBridge() {
6723
7600
  const scrollOpts = { passive: false, capture: true };
6724
7601
  document.addEventListener("wheel", preventBackgroundScroll, scrollOpts);
6725
7602
  document.addEventListener("touchmove", preventBackgroundScroll, scrollOpts);
7603
+ postToParent2({ type: "ow:image-unhover" });
6726
7604
  return () => {
6727
- postToParent({ type: "ow:link-modal-lock", locked: false });
7605
+ postToParent2({ type: "ow:link-modal-lock", locked: false });
6728
7606
  html.style.overflow = prevHtmlOverflow;
6729
7607
  body.style.overflow = prevBodyOverflow;
6730
7608
  document.removeEventListener("wheel", preventBackgroundScroll, scrollOpts);
6731
7609
  document.removeEventListener("touchmove", preventBackgroundScroll, scrollOpts);
6732
7610
  };
6733
- }, [linkPopover, postToParent]);
6734
- useEffect4(() => {
7611
+ }, [linkPopover, postToParent2]);
7612
+ useEffect7(() => {
6735
7613
  if (!isEditMode) return;
6736
7614
  const useFixtures = shouldUseDevFixtures();
6737
7615
  if (useFixtures) {
@@ -6752,17 +7630,17 @@ function OhhwellsBridge() {
6752
7630
  runSectionsPrefetchRef.current(mapped);
6753
7631
  };
6754
7632
  window.addEventListener("message", onSitePages);
6755
- if (!useFixtures) postToParent({ type: "ow:request-site-pages" });
7633
+ if (!useFixtures) postToParent2({ type: "ow:request-site-pages" });
6756
7634
  return () => window.removeEventListener("message", onSitePages);
6757
- }, [isEditMode, postToParent]);
6758
- useEffect4(() => {
7635
+ }, [isEditMode, postToParent2]);
7636
+ useEffect7(() => {
6759
7637
  if (!isEditMode || shouldUseDevFixtures()) return;
6760
7638
  void loadAllSectionsManifest().then((manifest) => {
6761
7639
  if (Object.keys(manifest).length === 0) return;
6762
7640
  setSectionsByPath((prev) => ({ ...manifest, ...prev }));
6763
7641
  });
6764
7642
  }, [isEditMode]);
6765
- useEffect4(() => {
7643
+ useEffect7(() => {
6766
7644
  const update = () => {
6767
7645
  const el = activeElRef.current ?? selectedElRef.current;
6768
7646
  if (el) setToolbarRect(getEditMeasureEl(el).getBoundingClientRect());
@@ -6786,10 +7664,10 @@ function OhhwellsBridge() {
6786
7664
  vvp.removeEventListener("resize", update);
6787
7665
  };
6788
7666
  }, []);
6789
- const refreshStateRules = useCallback2(() => {
7667
+ const refreshStateRules = useCallback3(() => {
6790
7668
  editStylesRef.current?.forceHover && (editStylesRef.current.forceHover.textContent = collectStateRules());
6791
7669
  }, []);
6792
- const processConfigRequest = useCallback2((insertAfterVal) => {
7670
+ const processConfigRequest = useCallback3((insertAfterVal) => {
6793
7671
  const tracker = getSectionsTracker();
6794
7672
  let entries = [];
6795
7673
  try {
@@ -6812,7 +7690,7 @@ function OhhwellsBridge() {
6812
7690
  }
6813
7691
  window.postMessage({ type: "ow:schedule-config", insertAfter: insertAfterVal, scheduleId: null }, "*");
6814
7692
  }, [isEditMode]);
6815
- const deactivate = useCallback2(() => {
7693
+ const deactivate = useCallback3(() => {
6816
7694
  const el = activeElRef.current;
6817
7695
  if (!el) return;
6818
7696
  const key = el.dataset.ohwKey;
@@ -6841,21 +7719,23 @@ function OhhwellsBridge() {
6841
7719
  setMaxBadge(null);
6842
7720
  setActiveCommands(/* @__PURE__ */ new Set());
6843
7721
  setToolbarShowEditLink(false);
6844
- postToParent({ type: "ow:exit-edit" });
6845
- }, [postToParent]);
6846
- const deselect = useCallback2(() => {
7722
+ postToParent2({ type: "ow:exit-edit" });
7723
+ }, [postToParent2]);
7724
+ const deselect = useCallback3(() => {
6847
7725
  selectedElRef.current = null;
6848
7726
  setReorderHrefKey(null);
6849
7727
  setReorderDragDisabled(false);
6850
7728
  siblingHintElRef.current = null;
6851
7729
  setSiblingHintRect(null);
6852
7730
  setIsItemDragging(false);
7731
+ hoveredNavContainerRef.current = null;
7732
+ setHoveredNavContainerRect(null);
6853
7733
  if (!activeElRef.current) {
6854
7734
  setToolbarRect(null);
6855
7735
  setToolbarVariant("none");
6856
7736
  }
6857
7737
  }, []);
6858
- const reselectNavigationItem = useCallback2((navAnchor) => {
7738
+ const reselectNavigationItem = useCallback3((navAnchor) => {
6859
7739
  selectedElRef.current = navAnchor;
6860
7740
  const { key, disabled } = getNavigationItemReorderState(navAnchor);
6861
7741
  setReorderHrefKey(key);
@@ -6865,7 +7745,7 @@ function OhhwellsBridge() {
6865
7745
  setToolbarShowEditLink(false);
6866
7746
  setActiveCommands(/* @__PURE__ */ new Set());
6867
7747
  }, []);
6868
- const commitNavigationTextEdit = useCallback2((navAnchor) => {
7748
+ const commitNavigationTextEdit = useCallback3((navAnchor) => {
6869
7749
  const el = activeElRef.current;
6870
7750
  if (!el) return;
6871
7751
  const key = el.dataset.ohwKey;
@@ -6878,9 +7758,9 @@ function OhhwellsBridge() {
6878
7758
  const html = sanitizeHtml(el.innerHTML);
6879
7759
  const original = originalContentRef.current ?? "";
6880
7760
  if (html !== sanitizeHtml(original)) {
6881
- postToParent({ type: "ow:change", nodes: [{ key, text: html }] });
7761
+ postToParent2({ type: "ow:change", nodes: [{ key, text: html }] });
6882
7762
  const h = document.documentElement.scrollHeight;
6883
- if (h > 50) postToParent({ type: "ow:height", height: h });
7763
+ if (h > 50) postToParent2({ type: "ow:height", height: h });
6884
7764
  }
6885
7765
  }
6886
7766
  el.removeAttribute("contenteditable");
@@ -6888,31 +7768,38 @@ function OhhwellsBridge() {
6888
7768
  setMaxBadge(null);
6889
7769
  setActiveCommands(/* @__PURE__ */ new Set());
6890
7770
  setToolbarShowEditLink(false);
6891
- postToParent({ type: "ow:exit-edit" });
7771
+ postToParent2({ type: "ow:exit-edit" });
6892
7772
  reselectNavigationItem(navAnchor);
6893
- }, [postToParent, reselectNavigationItem]);
6894
- const handleAddNavigationItem = useCallback2(() => {
6895
- const selected = selectedElRef.current;
6896
- if (!selected) return;
6897
- const target = getNavigationItemAddHintTarget(selected);
6898
- siblingHintElRef.current = target;
6899
- setSiblingHintRect(target.getBoundingClientRect());
7773
+ }, [postToParent2, reselectNavigationItem]);
7774
+ const handleAddTopLevelNavItem = useCallback3(() => {
7775
+ const items = listNavbarItems();
7776
+ addNavAfterAnchorRef.current = items[items.length - 1] ?? null;
7777
+ deselectRef.current();
7778
+ deactivateRef.current();
7779
+ bumpLinkPopoverGrace();
7780
+ setLinkPopover({
7781
+ key: "__add-nav__",
7782
+ mode: "create",
7783
+ intent: "add-nav"
7784
+ });
6900
7785
  }, []);
6901
- const handleItemDragStart = useCallback2(() => {
7786
+ const handleItemDragStart = useCallback3(() => {
6902
7787
  siblingHintElRef.current = null;
6903
7788
  setSiblingHintRect(null);
6904
7789
  setIsItemDragging(true);
6905
7790
  }, []);
6906
- const handleItemDragEnd = useCallback2(() => {
7791
+ const handleItemDragEnd = useCallback3(() => {
6907
7792
  setIsItemDragging(false);
6908
7793
  }, []);
6909
7794
  reselectNavigationItemRef.current = reselectNavigationItem;
6910
7795
  commitNavigationTextEditRef.current = commitNavigationTextEdit;
6911
- const select = useCallback2((anchor) => {
7796
+ const select = useCallback3((anchor) => {
6912
7797
  if (!isNavigationItem(anchor)) return;
6913
7798
  if (activeElRef.current) deactivate();
6914
7799
  selectedElRef.current = anchor;
6915
7800
  clearHrefKeyHover(anchor);
7801
+ hoveredNavContainerRef.current = null;
7802
+ setHoveredNavContainerRect(null);
6916
7803
  setHoveredItemRect(null);
6917
7804
  hoveredItemElRef.current = null;
6918
7805
  siblingHintElRef.current = null;
@@ -6926,7 +7813,26 @@ function OhhwellsBridge() {
6926
7813
  setToolbarShowEditLink(false);
6927
7814
  setActiveCommands(/* @__PURE__ */ new Set());
6928
7815
  }, [deactivate]);
6929
- const activate = useCallback2((el) => {
7816
+ const selectFrame = useCallback3((el) => {
7817
+ if (!isNavigationContainer(el)) return;
7818
+ if (activeElRef.current) deactivate();
7819
+ selectedElRef.current = el;
7820
+ clearHrefKeyHover(el);
7821
+ hoveredNavContainerRef.current = null;
7822
+ setHoveredNavContainerRect(null);
7823
+ setHoveredItemRect(null);
7824
+ hoveredItemElRef.current = null;
7825
+ siblingHintElRef.current = null;
7826
+ setSiblingHintRect(null);
7827
+ setIsItemDragging(false);
7828
+ setReorderHrefKey(null);
7829
+ setReorderDragDisabled(false);
7830
+ setToolbarVariant("select-frame");
7831
+ setToolbarRect(el.getBoundingClientRect());
7832
+ setToolbarShowEditLink(false);
7833
+ setActiveCommands(/* @__PURE__ */ new Set());
7834
+ }, [deactivate]);
7835
+ const activate = useCallback3((el, options) => {
6930
7836
  if (activeElRef.current === el) return;
6931
7837
  selectedElRef.current = null;
6932
7838
  deactivate();
@@ -6944,6 +7850,9 @@ function OhhwellsBridge() {
6944
7850
  activeElRef.current = el;
6945
7851
  originalContentRef.current = el.innerHTML;
6946
7852
  el.focus();
7853
+ if (options?.caretX !== void 0 && options?.caretY !== void 0) {
7854
+ placeCaretAtPoint(el, options.caretX, options.caretY);
7855
+ }
6947
7856
  setToolbarShowEditLink(Boolean(getHrefKeyFromElement(el)));
6948
7857
  const navAnchor = getNavigationItemAnchor(el);
6949
7858
  if (navAnchor) {
@@ -6955,12 +7864,13 @@ function OhhwellsBridge() {
6955
7864
  setReorderDragDisabled(reorderDisabled);
6956
7865
  }
6957
7866
  setToolbarRect(getEditMeasureEl(el).getBoundingClientRect());
6958
- postToParent({ type: "ow:enter-edit", key: el.dataset.ohwKey });
7867
+ postToParent2({ type: "ow:enter-edit", key: el.dataset.ohwKey });
6959
7868
  requestAnimationFrame(() => refreshActiveCommandsRef.current());
6960
- }, [deactivate, postToParent]);
7869
+ }, [deactivate, postToParent2]);
6961
7870
  activateRef.current = activate;
6962
7871
  deactivateRef.current = deactivate;
6963
7872
  selectRef.current = select;
7873
+ selectFrameRef.current = selectFrame;
6964
7874
  deselectRef.current = deselect;
6965
7875
  useLayoutEffect2(() => {
6966
7876
  if (!subdomain || isEditMode) {
@@ -7002,6 +7912,7 @@ function OhhwellsBridge() {
7002
7912
  });
7003
7913
  applyLinkByKey(key, val);
7004
7914
  }
7915
+ reconcileNavbarItemsFromContent(content);
7005
7916
  enforceLinkHrefs();
7006
7917
  initSectionsFromContent(content, true);
7007
7918
  sectionsLoadedRef.current = true;
@@ -7030,7 +7941,7 @@ function OhhwellsBridge() {
7030
7941
  cancelled = true;
7031
7942
  };
7032
7943
  }, [subdomain, isEditMode]);
7033
- useEffect4(() => {
7944
+ useEffect7(() => {
7034
7945
  if (!subdomain || isEditMode) return;
7035
7946
  let debounceTimer = null;
7036
7947
  let observer = null;
@@ -7061,6 +7972,7 @@ function OhhwellsBridge() {
7061
7972
  });
7062
7973
  applyLinkByKey(key, val);
7063
7974
  }
7975
+ reconcileNavbarItemsFromContent(content);
7064
7976
  } finally {
7065
7977
  observer?.observe(document.body, { childList: true, subtree: true });
7066
7978
  }
@@ -7084,20 +7996,38 @@ function OhhwellsBridge() {
7084
7996
  const visible = Boolean(subdomain) && fetchState !== "done";
7085
7997
  el.style.display = visible ? "flex" : "none";
7086
7998
  }, [subdomain, fetchState]);
7087
- useEffect4(() => {
7088
- postToParent({ type: "ow:navigation", path: pathname });
7089
- }, [pathname, postToParent]);
7090
- useEffect4(() => {
7999
+ useEffect7(() => {
8000
+ postToParent2({ type: "ow:navigation", path: pathname });
8001
+ }, [pathname, postToParent2]);
8002
+ useEffect7(() => {
7091
8003
  if (!isEditMode) return;
8004
+ if (linkPopoverSessionRef.current?.intent === "add-nav") return;
8005
+ if (document.querySelector("[data-ohw-section-picker]")) return;
7092
8006
  setLinkPopover(null);
7093
8007
  deselectRef.current();
7094
8008
  deactivateRef.current();
7095
8009
  }, [pathname, isEditMode]);
7096
- useEffect4(() => {
8010
+ useEffect7(() => {
8011
+ const contentForNav = () => {
8012
+ if (isEditMode) return editContentRef.current;
8013
+ if (!subdomain) return {};
8014
+ return contentCache.get(subdomain) ?? {};
8015
+ };
8016
+ const run = () => reconcileNavbarItemsFromContent(contentForNav());
8017
+ 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();
8025
+ }, [isEditMode, pathname, subdomain, fetchState]);
8026
+ useEffect7(() => {
7097
8027
  if (!isEditMode) return;
7098
8028
  const measure = () => {
7099
8029
  const h = document.body.scrollHeight;
7100
- if (h > 50) postToParent({ type: "ow:height", height: h });
8030
+ if (h > 50) postToParent2({ type: "ow:height", height: h });
7101
8031
  };
7102
8032
  const t1 = setTimeout(measure, 50);
7103
8033
  const t2 = setTimeout(measure, 500);
@@ -7116,8 +8046,8 @@ function OhhwellsBridge() {
7116
8046
  if (resizeTimer) clearTimeout(resizeTimer);
7117
8047
  window.removeEventListener("resize", handleResize);
7118
8048
  };
7119
- }, [pathname, isEditMode, postToParent]);
7120
- useEffect4(() => {
8049
+ }, [pathname, isEditMode, postToParent2]);
8050
+ useEffect7(() => {
7121
8051
  if (!subdomainFromQuery || isEditMode) return;
7122
8052
  const handleClick = (e) => {
7123
8053
  const anchor = e.target.closest("a");
@@ -7133,7 +8063,7 @@ function OhhwellsBridge() {
7133
8063
  document.addEventListener("click", handleClick, true);
7134
8064
  return () => document.removeEventListener("click", handleClick, true);
7135
8065
  }, [subdomainFromQuery, isEditMode, router]);
7136
- useEffect4(() => {
8066
+ useEffect7(() => {
7137
8067
  if (!isEditMode) {
7138
8068
  editStylesRef.current?.base.remove();
7139
8069
  editStylesRef.current?.forceHover.remove();
@@ -7206,6 +8136,13 @@ function OhhwellsBridge() {
7206
8136
  if (target.closest("[data-ohw-max-badge]")) return;
7207
8137
  if (isInsideLinkEditor(target)) return;
7208
8138
  if (target.closest('[data-ohw-edit-chrome], [data-ohw-item-interaction], [data-ohw-drag-handle-container], [data-slot="drag-handle"]')) {
8139
+ const navFromChrome = resolveNavContainerSelectionTarget(target, e.clientX, e.clientY);
8140
+ if (navFromChrome) {
8141
+ e.preventDefault();
8142
+ e.stopPropagation();
8143
+ selectFrameRef.current(navFromChrome);
8144
+ return;
8145
+ }
7209
8146
  e.preventDefault();
7210
8147
  e.stopPropagation();
7211
8148
  return;
@@ -7220,7 +8157,8 @@ function OhhwellsBridge() {
7220
8157
  bumpLinkPopoverGrace();
7221
8158
  setLinkPopoverRef.current({
7222
8159
  key: editable.dataset.ohwKey ?? "",
7223
- target: getLinkHref(editable)
8160
+ mode: "edit",
8161
+ target: getLinkHref2(editable)
7224
8162
  });
7225
8163
  return;
7226
8164
  }
@@ -7236,7 +8174,8 @@ function OhhwellsBridge() {
7236
8174
  e.preventDefault();
7237
8175
  e.stopPropagation();
7238
8176
  if (selectedElRef.current === navAnchor) {
7239
- activateRef.current(editable);
8177
+ if (e.detail >= 2) return;
8178
+ activateRef.current(editable, { caretX: e.clientX, caretY: e.clientY });
7240
8179
  return;
7241
8180
  }
7242
8181
  selectRef.current(navAnchor);
@@ -7255,6 +8194,22 @@ function OhhwellsBridge() {
7255
8194
  selectRef.current(hrefAnchor);
7256
8195
  return;
7257
8196
  }
8197
+ const navContainerToSelect = resolveNavContainerSelectionTarget(target, e.clientX, e.clientY);
8198
+ if (navContainerToSelect) {
8199
+ e.preventDefault();
8200
+ e.stopPropagation();
8201
+ selectFrameRef.current(navContainerToSelect);
8202
+ return;
8203
+ }
8204
+ const selectedContainer = selectedElRef.current;
8205
+ if (selectedContainer && isNavigationContainer(selectedContainer)) {
8206
+ const navItem = getNavigationItemAnchor(target);
8207
+ if (!navItem && (selectedContainer === target || selectedContainer.contains(target))) {
8208
+ e.preventDefault();
8209
+ e.stopPropagation();
8210
+ return;
8211
+ }
8212
+ }
7258
8213
  if (activeElRef.current) {
7259
8214
  const sel = window.getSelection();
7260
8215
  if (sel && !sel.isCollapsed) {
@@ -7280,10 +8235,49 @@ function OhhwellsBridge() {
7280
8235
  deselectRef.current();
7281
8236
  deactivateRef.current();
7282
8237
  };
8238
+ const handleDblClick = (e) => {
8239
+ const target = e.target;
8240
+ if (target.closest("[data-ohw-toolbar]")) return;
8241
+ if (target.closest("[data-ohw-state-toggle]")) return;
8242
+ if (target.closest("[data-ohw-max-badge]")) return;
8243
+ if (isInsideLinkEditor(target)) return;
8244
+ if (target.closest('[data-ohw-edit-chrome], [data-ohw-item-interaction], [data-ohw-drag-handle-container], [data-slot="drag-handle"]')) {
8245
+ return;
8246
+ }
8247
+ const navLabel = getNavigationLabelEditable(target);
8248
+ if (!navLabel) return;
8249
+ const { editable } = navLabel;
8250
+ e.preventDefault();
8251
+ e.stopPropagation();
8252
+ if (activeElRef.current !== editable) {
8253
+ activateRef.current(editable);
8254
+ }
8255
+ requestAnimationFrame(() => {
8256
+ selectAllTextInEditable(editable);
8257
+ refreshActiveCommandsRef.current();
8258
+ });
8259
+ };
7283
8260
  const handleMouseOver = (e) => {
8261
+ if (document.documentElement.hasAttribute("data-ohw-section-picking")) return;
7284
8262
  const target = e.target;
8263
+ 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());
8268
+ hoveredItemElRef.current = null;
8269
+ setHoveredItemRect(null);
8270
+ return;
8271
+ }
8272
+ if (!target.closest("[data-ohw-nav-container]")) {
8273
+ hoveredNavContainerRef.current = null;
8274
+ setHoveredNavContainerRect(null);
8275
+ }
8276
+ }
7285
8277
  const navAnchor = getNavigationItemAnchor(target);
7286
8278
  if (navAnchor) {
8279
+ hoveredNavContainerRef.current = null;
8280
+ setHoveredNavContainerRect(null);
7287
8281
  const selected2 = selectedElRef.current;
7288
8282
  if (selected2 === navAnchor || selected2?.contains(navAnchor)) return;
7289
8283
  clearHrefKeyHover(navAnchor);
@@ -7308,6 +8302,14 @@ function OhhwellsBridge() {
7308
8302
  };
7309
8303
  const handleMouseOut = (e) => {
7310
8304
  const target = e.target;
8305
+ if (target.closest("[data-ohw-nav-container]")) {
8306
+ 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]")) {
8308
+ return;
8309
+ }
8310
+ hoveredNavContainerRef.current = null;
8311
+ setHoveredNavContainerRect(null);
8312
+ }
7311
8313
  const navAnchor = getNavigationItemAnchor(target);
7312
8314
  if (navAnchor) {
7313
8315
  const related2 = e.relatedTarget instanceof Element ? e.relatedTarget : null;
@@ -7429,6 +8431,56 @@ function OhhwellsBridge() {
7429
8431
  }
7430
8432
  return smallest;
7431
8433
  };
8434
+ const dismissImageHover = () => {
8435
+ if (hoveredImageRef.current) {
8436
+ hoveredImageRef.current = null;
8437
+ hoveredImageHasTextOverlapRef.current = false;
8438
+ resumeAnimTracks();
8439
+ postToParentRef.current({ type: "ow:image-unhover" });
8440
+ }
8441
+ };
8442
+ const probeNavigationHoverAt = (x, y) => {
8443
+ if (toolbarVariantRef.current === "select-frame") {
8444
+ hoveredNavContainerRef.current = null;
8445
+ setHoveredNavContainerRect(null);
8446
+ return;
8447
+ }
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;
8460
+ }
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());
8476
+ }
8477
+ return;
8478
+ }
8479
+ hoveredNavContainerRef.current = navContainer;
8480
+ setHoveredNavContainerRect(containerRect);
8481
+ hoveredItemElRef.current = null;
8482
+ setHoveredItemRect(null);
8483
+ };
7432
8484
  const probeImageAt = (clientX, clientY, isDragOver = false, fromParentViewport = false) => {
7433
8485
  if (linkPopoverOpenRef.current) {
7434
8486
  if (hoveredImageRef.current) {
@@ -7437,18 +8489,22 @@ function OhhwellsBridge() {
7437
8489
  resumeAnimTracks();
7438
8490
  clearImageHover();
7439
8491
  }
8492
+ dismissImageHover();
8493
+ return;
8494
+ }
8495
+ const { x, y } = toProbeCoords(clientX, clientY, fromParentViewport);
8496
+ if (isPointOverNavigation(x, y)) {
8497
+ dismissImageHover();
8498
+ probeNavigationHoverAt(x, y);
7440
8499
  return;
7441
8500
  }
8501
+ hoveredNavContainerRef.current = null;
8502
+ setHoveredNavContainerRect(null);
7442
8503
  const toggleEl = document.querySelector("[data-ohw-state-toggle]");
7443
8504
  if (toggleEl) {
7444
- const { x, y } = toProbeCoords(clientX, clientY, fromParentViewport);
7445
8505
  const tr = toggleEl.getBoundingClientRect();
7446
8506
  if (x >= tr.left && x <= tr.right && y >= tr.top && y <= tr.bottom) {
7447
- if (hoveredImageRef.current) {
7448
- hoveredImageRef.current = null;
7449
- resumeAnimTracks();
7450
- clearImageHover();
7451
- }
8507
+ dismissImageHover();
7452
8508
  return;
7453
8509
  }
7454
8510
  }
@@ -7461,15 +8517,18 @@ function OhhwellsBridge() {
7461
8517
  resumeAnimTracks();
7462
8518
  clearImageHover();
7463
8519
  }
7464
- return;
8520
+ if (activeElRef.current) {
8521
+ dismissImageHover();
8522
+ return;
8523
+ }
7465
8524
  }
7466
8525
  if (imgEl) {
7467
- const { x, y } = toProbeCoords(clientX, clientY, fromParentViewport);
8526
+ const { x: x2, y: y2 } = toProbeCoords(clientX, clientY, fromParentViewport);
7468
8527
  const overMediaChrome = Array.from(
7469
8528
  document.querySelectorAll("[data-ohw-media-chrome]")
7470
8529
  ).some((el) => {
7471
8530
  const r2 = el.getBoundingClientRect();
7472
- return x >= r2.left && x <= r2.right && y >= r2.top && y <= r2.bottom;
8531
+ return x2 >= r2.left && x2 <= r2.right && y2 >= r2.top && y2 <= r2.bottom;
7473
8532
  });
7474
8533
  if (overMediaChrome) {
7475
8534
  if (hoveredImageRef.current) {
@@ -7480,7 +8539,7 @@ function OhhwellsBridge() {
7480
8539
  }
7481
8540
  return;
7482
8541
  }
7483
- const topEl = document.elementFromPoint(x, y);
8542
+ const topEl = document.elementFromPoint(x2, y2);
7484
8543
  if (topEl?.closest('[data-ohw-toolbar], [data-ohw-edit-chrome], [data-ohw-item-interaction], [data-ohw-drag-handle-container], [data-slot="drag-handle"]')) {
7485
8544
  if (hoveredImageRef.current) {
7486
8545
  hoveredImageRef.current = null;
@@ -7503,7 +8562,7 @@ function OhhwellsBridge() {
7503
8562
  document.querySelectorAll(NON_MEDIA_SELECTOR)
7504
8563
  ).find((el) => {
7505
8564
  const er = el.getBoundingClientRect();
7506
- return x >= er.left && x <= er.right && y >= er.top && y <= er.bottom;
8565
+ return x2 >= er.left && x2 <= er.right && y2 >= er.top && y2 <= er.bottom;
7507
8566
  });
7508
8567
  if (imageUnhoverTimerRef.current) {
7509
8568
  clearTimeout(imageUnhoverTimerRef.current);
@@ -7579,12 +8638,12 @@ function OhhwellsBridge() {
7579
8638
  resumeAnimTracks();
7580
8639
  clearImageHover();
7581
8640
  }
7582
- const { x, y } = toProbeCoords(clientX, clientY, fromParentViewport);
8641
+ const { x: x2, y: y2 } = toProbeCoords(clientX, clientY, fromParentViewport);
7583
8642
  const textEl = Array.from(
7584
8643
  document.querySelectorAll(NON_MEDIA_SELECTOR)
7585
8644
  ).find((el) => {
7586
8645
  const er = el.getBoundingClientRect();
7587
- return x >= er.left && x <= er.right && y >= er.top && y <= er.bottom;
8646
+ return x2 >= er.left && x2 <= er.right && y2 >= er.top && y2 <= er.bottom;
7588
8647
  });
7589
8648
  document.querySelectorAll("[data-ohw-hovered]").forEach((el) => el.removeAttribute("data-ohw-hovered"));
7590
8649
  if (textEl && !textEl.hasAttribute("contenteditable")) {
@@ -7608,6 +8667,14 @@ function OhhwellsBridge() {
7608
8667
  }
7609
8668
  };
7610
8669
  const probeHoverCardsAt = (clientX, clientY, fromParentViewport = false) => {
8670
+ if (linkPopoverOpenRef.current || document.documentElement.hasAttribute("data-ohw-section-picking")) {
8671
+ if (activeStateElRef.current) {
8672
+ activeStateElRef.current.removeAttribute("data-ohw-state-hovered");
8673
+ activeStateElRef.current = null;
8674
+ setToggleState(null);
8675
+ }
8676
+ return;
8677
+ }
7611
8678
  const { x, y } = toProbeCoords(clientX, clientY, fromParentViewport);
7612
8679
  const toggleEl = document.querySelector("[data-ohw-state-toggle]");
7613
8680
  if (toggleEl) {
@@ -7884,6 +8951,8 @@ function OhhwellsBridge() {
7884
8951
  sectionsLoadedRef.current = true;
7885
8952
  pendingScheduleConfigRequests.current.splice(0).forEach(processConfigRequest);
7886
8953
  }
8954
+ editContentRef.current = { ...editContentRef.current, ...content };
8955
+ reconcileNavbarItemsFromContent(editContentRef.current);
7887
8956
  enforceLinkHrefs();
7888
8957
  postToParentRef.current({ type: "ow:hydrate-done" });
7889
8958
  };
@@ -7891,6 +8960,7 @@ function OhhwellsBridge() {
7891
8960
  const handleDeactivate = (e) => {
7892
8961
  if (e.data?.type !== "ow:deactivate") return;
7893
8962
  if (Date.now() < linkPopoverGraceUntilRef.current) return;
8963
+ if (document.querySelector("[data-ohw-section-picker]")) return;
7894
8964
  if (linkPopoverOpenRef.current) {
7895
8965
  setLinkPopoverRef.current(null);
7896
8966
  return;
@@ -7899,13 +8969,40 @@ function OhhwellsBridge() {
7899
8969
  deactivateRef.current();
7900
8970
  };
7901
8971
  window.addEventListener("message", handleDeactivate);
8972
+ const handleUiEscape = (e) => {
8973
+ if (e.data?.type !== "ui:escape") return;
8974
+ if (linkPopoverOpenRef.current) {
8975
+ setLinkPopoverRef.current(null);
8976
+ }
8977
+ };
8978
+ window.addEventListener("message", handleUiEscape);
7902
8979
  const handleKeyDown = (e) => {
8980
+ if (e.key === "Escape" && document.querySelector("[data-ohw-section-picker]")) return;
8981
+ if ((e.ctrlKey || e.metaKey) && e.key.toLowerCase() === "a" && activeElRef.current) {
8982
+ const navAnchor = getNavigationItemAnchor(activeElRef.current);
8983
+ if (navAnchor) {
8984
+ e.preventDefault();
8985
+ selectAllTextInEditable(activeElRef.current);
8986
+ refreshActiveCommandsRef.current();
8987
+ return;
8988
+ }
8989
+ }
7903
8990
  if (e.key === "Escape" && linkPopoverOpenRef.current) {
7904
8991
  setLinkPopoverRef.current(null);
7905
8992
  return;
7906
8993
  }
7907
8994
  if (e.key === "Escape" && selectedElRef.current && !activeElRef.current) {
7908
- deselectRef.current();
8995
+ if (toolbarVariantRef.current === "select-frame") {
8996
+ deselectRef.current();
8997
+ return;
8998
+ }
8999
+ const parent = getNavigationSelectionParent(selectedElRef.current);
9000
+ if (parent) {
9001
+ e.preventDefault();
9002
+ selectFrameRef.current(parent);
9003
+ } else {
9004
+ deselectRef.current();
9005
+ }
7909
9006
  return;
7910
9007
  }
7911
9008
  if (e.key === "Escape" && activeElRef.current) {
@@ -7963,6 +9060,9 @@ function OhhwellsBridge() {
7963
9060
  if (hoveredItemElRef.current) {
7964
9061
  setHoveredItemRect(hoveredItemElRef.current.getBoundingClientRect());
7965
9062
  }
9063
+ if (hoveredNavContainerRef.current) {
9064
+ setHoveredNavContainerRect(hoveredNavContainerRef.current.getBoundingClientRect());
9065
+ }
7966
9066
  if (siblingHintElRef.current) {
7967
9067
  setSiblingHintRect(siblingHintElRef.current.getBoundingClientRect());
7968
9068
  }
@@ -8159,9 +9259,45 @@ function OhhwellsBridge() {
8159
9259
  return clientX >= r2.left && clientX <= r2.right && clientY >= r2.top && clientY <= r2.bottom;
8160
9260
  });
8161
9261
  if (textEditable) {
8162
- activateRef.current(textEditable);
9262
+ const hrefCtx = getHrefKeyFromElement(textEditable);
9263
+ const navAnchor = hrefCtx ? getNavigationItemAnchor(hrefCtx.anchor) : null;
9264
+ if (navAnchor) {
9265
+ if (selectedElRef.current === navAnchor) {
9266
+ activateRef.current(textEditable, { caretX: clientX, caretY: clientY });
9267
+ } else {
9268
+ selectRef.current(navAnchor);
9269
+ }
9270
+ return;
9271
+ }
9272
+ activateRef.current(textEditable, { caretX: clientX, caretY: clientY });
8163
9273
  return;
8164
9274
  }
9275
+ const navContainer = document.querySelector("[data-ohw-nav-container]");
9276
+ if (navContainer) {
9277
+ const r2 = navContainer.getBoundingClientRect();
9278
+ const pad = 8;
9279
+ const over = clientX >= r2.left - pad && clientX <= r2.right + pad && clientY >= r2.top - pad && clientY <= r2.bottom + pad;
9280
+ if (over && !isPointOverNavItem(navContainer, clientX, clientY)) {
9281
+ selectFrameRef.current(navContainer);
9282
+ return;
9283
+ }
9284
+ }
9285
+ const navRoot = document.querySelector("[data-ohw-nav-root]");
9286
+ if (navRoot && navContainer) {
9287
+ const rr = navRoot.getBoundingClientRect();
9288
+ if (clientX >= rr.left && clientX <= rr.right && clientY >= rr.top && clientY <= rr.bottom && !isPointOverNavItem(navContainer, clientX, clientY)) {
9289
+ const book = navRoot.querySelector('[data-ohw-role="navbar-button"]');
9290
+ if (book) {
9291
+ const br = book.getBoundingClientRect();
9292
+ if (clientX >= br.left && clientX <= br.right && clientY >= br.top && clientY <= br.bottom) {
9293
+ deactivateRef.current();
9294
+ return;
9295
+ }
9296
+ }
9297
+ selectFrameRef.current(navContainer);
9298
+ return;
9299
+ }
9300
+ }
8165
9301
  deactivateRef.current();
8166
9302
  };
8167
9303
  window.addEventListener("message", handleSave);
@@ -8183,6 +9319,7 @@ function OhhwellsBridge() {
8183
9319
  };
8184
9320
  window.addEventListener("resize", handleViewportResize, { passive: true });
8185
9321
  document.addEventListener("click", handleClick, true);
9322
+ document.addEventListener("dblclick", handleDblClick, true);
8186
9323
  document.addEventListener("paste", handlePaste, true);
8187
9324
  document.addEventListener("input", handleInput, true);
8188
9325
  document.addEventListener("mouseover", handleMouseOver, true);
@@ -8197,6 +9334,7 @@ function OhhwellsBridge() {
8197
9334
  window.addEventListener("scroll", handleScroll, true);
8198
9335
  return () => {
8199
9336
  document.removeEventListener("click", handleClick, true);
9337
+ document.removeEventListener("dblclick", handleDblClick, true);
8200
9338
  document.removeEventListener("paste", handlePaste, true);
8201
9339
  document.removeEventListener("input", handleInput, true);
8202
9340
  document.removeEventListener("mouseover", handleMouseOver, true);
@@ -8224,13 +9362,14 @@ function OhhwellsBridge() {
8224
9362
  window.removeEventListener("message", handleClickAt);
8225
9363
  window.removeEventListener("message", handleHydrate);
8226
9364
  window.removeEventListener("message", handleDeactivate);
9365
+ window.removeEventListener("message", handleUiEscape);
8227
9366
  autoSaveTimers.current.forEach(clearTimeout);
8228
9367
  autoSaveTimers.current.clear();
8229
9368
  if (imageUnhoverTimerRef.current) clearTimeout(imageUnhoverTimerRef.current);
8230
9369
  if (imageShowTimerRef.current) clearTimeout(imageShowTimerRef.current);
8231
9370
  };
8232
9371
  }, [isEditMode, refreshStateRules]);
8233
- useEffect4(() => {
9372
+ useEffect7(() => {
8234
9373
  const handler = (e) => {
8235
9374
  if (e.data?.type !== "ow:request-schedule-config") return;
8236
9375
  const insertAfterVal = e.data.insertAfter;
@@ -8246,7 +9385,7 @@ function OhhwellsBridge() {
8246
9385
  window.addEventListener("message", handler);
8247
9386
  return () => window.removeEventListener("message", handler);
8248
9387
  }, [processConfigRequest]);
8249
- useEffect4(() => {
9388
+ useEffect7(() => {
8250
9389
  if (!isEditMode) return;
8251
9390
  document.querySelectorAll("[data-ohw-active-state]").forEach((el) => {
8252
9391
  el.removeAttribute("data-ohw-active-state");
@@ -8267,27 +9406,27 @@ function OhhwellsBridge() {
8267
9406
  const next = { ...prev, [pathKey]: sections };
8268
9407
  return shouldUseDevFixtures() ? { ...DEV_SECTIONS_BY_PATH, ...next } : next;
8269
9408
  });
8270
- postToParent({ type: "ow:ready", version: "1", path: pathname, nodes: collectEditableNodes(), sections });
8271
- postToParent({ type: "ow:request-site-pages" });
9409
+ postToParent2({ type: "ow:ready", version: "1", path: pathname, nodes: collectEditableNodes(), sections });
9410
+ postToParent2({ type: "ow:request-site-pages" });
8272
9411
  }, 150);
8273
9412
  return () => {
8274
9413
  cancelAnimationFrame(raf);
8275
9414
  clearTimeout(timer);
8276
9415
  };
8277
- }, [pathname, isEditMode, refreshStateRules, postToParent]);
8278
- useEffect4(() => {
9416
+ }, [pathname, isEditMode, refreshStateRules, postToParent2]);
9417
+ useEffect7(() => {
8279
9418
  scrollToHashSectionWhenReady();
8280
9419
  const onHashChange = () => scrollToHashSectionWhenReady();
8281
9420
  window.addEventListener("hashchange", onHashChange);
8282
9421
  return () => window.removeEventListener("hashchange", onHashChange);
8283
9422
  }, [pathname]);
8284
- const handleCommand = useCallback2((cmd) => {
9423
+ const handleCommand = useCallback3((cmd) => {
8285
9424
  document.execCommand(cmd, false);
8286
9425
  activeElRef.current?.focus();
8287
9426
  if (activeElRef.current) setToolbarRect(getEditMeasureEl(activeElRef.current).getBoundingClientRect());
8288
9427
  refreshActiveCommandsRef.current();
8289
9428
  }, []);
8290
- const handleStateChange = useCallback2((state) => {
9429
+ const handleStateChange = useCallback3((state) => {
8291
9430
  if (!activeStateElRef.current) return;
8292
9431
  const el = activeStateElRef.current;
8293
9432
  if (state === "Default") {
@@ -8300,18 +9439,22 @@ function OhhwellsBridge() {
8300
9439
  }
8301
9440
  setToggleState((prev) => prev ? { ...prev, activeState: state } : null);
8302
9441
  }, [deactivate]);
8303
- const closeLinkPopover = useCallback2(() => setLinkPopover(null), []);
8304
- const openLinkPopoverForActive = useCallback2(() => {
9442
+ const closeLinkPopover = useCallback3(() => {
9443
+ addNavAfterAnchorRef.current = null;
9444
+ setLinkPopover(null);
9445
+ }, []);
9446
+ const openLinkPopoverForActive = useCallback3(() => {
8305
9447
  const hrefCtx = getHrefKeyFromElement(activeElRef.current);
8306
9448
  if (!hrefCtx) return;
8307
9449
  bumpLinkPopoverGrace();
8308
9450
  setLinkPopover({
8309
9451
  key: hrefCtx.key,
8310
- target: getLinkHref(hrefCtx.anchor)
9452
+ mode: "edit",
9453
+ target: getLinkHref2(hrefCtx.anchor)
8311
9454
  });
8312
9455
  deactivate();
8313
9456
  }, [deactivate]);
8314
- const openLinkPopoverForSelected = useCallback2(() => {
9457
+ const openLinkPopoverForSelected = useCallback3(() => {
8315
9458
  const anchor = selectedElRef.current;
8316
9459
  if (!anchor) return;
8317
9460
  const key = anchor.getAttribute("data-ohw-href-key");
@@ -8319,30 +9462,77 @@ function OhhwellsBridge() {
8319
9462
  bumpLinkPopoverGrace();
8320
9463
  setLinkPopover({
8321
9464
  key,
8322
- target: getLinkHref(anchor)
9465
+ mode: "edit",
9466
+ target: getLinkHref2(anchor)
8323
9467
  });
8324
9468
  deselect();
8325
9469
  }, [deselect]);
8326
- const handleLinkPopoverSubmit = useCallback2(
9470
+ const handleLinkPopoverSubmit = useCallback3(
8327
9471
  (target) => {
8328
- if (!linkPopover) return;
8329
- const { key } = linkPopover;
9472
+ const session = linkPopoverSessionRef.current;
9473
+ if (!session) return;
9474
+ if (session.intent === "add-nav") {
9475
+ const trimmed = target.trim();
9476
+ let href = trimmed;
9477
+ let label = "Untitled";
9478
+ if (trimmed) {
9479
+ const { pageRoute, sectionId } = parseTarget(trimmed);
9480
+ const page = resolvePage(pageRoute, sitePages);
9481
+ const pageSections = getSectionsForPath(sectionsByPath, pageRoute);
9482
+ const section = sectionId ? pageSections.find((s) => s.id === sectionId) : void 0;
9483
+ label = section?.label ?? page.title;
9484
+ href = trimmed;
9485
+ }
9486
+ const afterAnchor = addNavAfterAnchorRef.current;
9487
+ const { anchor, hrefKey, labelKey, index, order } = insertNavbarItem(href, label, afterAnchor);
9488
+ applyLinkByKey(hrefKey, href);
9489
+ document.querySelectorAll(`[data-ohw-key="${labelKey}"]`).forEach((el) => {
9490
+ el.textContent = label;
9491
+ });
9492
+ const navCount = String(index + 1);
9493
+ const orderJson = JSON.stringify(order);
9494
+ editContentRef.current = {
9495
+ ...editContentRef.current,
9496
+ [hrefKey]: href,
9497
+ [labelKey]: label,
9498
+ [NAV_COUNT_KEY]: navCount,
9499
+ [NAV_ORDER_KEY]: orderJson
9500
+ };
9501
+ postToParent2({
9502
+ type: "ow:change",
9503
+ nodes: [
9504
+ { key: hrefKey, text: href },
9505
+ { key: labelKey, text: label },
9506
+ { key: NAV_COUNT_KEY, text: navCount },
9507
+ { key: NAV_ORDER_KEY, text: orderJson }
9508
+ ]
9509
+ });
9510
+ postToParent2({ type: "ow:toast", title: "Link added", toastType: "success" });
9511
+ addNavAfterAnchorRef.current = null;
9512
+ setLinkPopover(null);
9513
+ enforceLinkHrefs();
9514
+ requestAnimationFrame(() => {
9515
+ selectRef.current(anchor);
9516
+ });
9517
+ return;
9518
+ }
9519
+ const { key } = session;
8330
9520
  applyLinkByKey(key, target);
8331
- postToParent({ type: "ow:change", nodes: [{ key, text: target }] });
9521
+ postToParent2({ type: "ow:change", nodes: [{ key, text: target }] });
8332
9522
  setLinkPopover(null);
8333
9523
  },
8334
- [linkPopover, postToParent]
9524
+ [postToParent2, sitePages, sectionsByPath]
8335
9525
  );
8336
9526
  const showEditLink = toolbarShowEditLink;
8337
9527
  const currentSections = sectionsByPath[pathname] ?? [];
8338
9528
  linkPopoverOpenRef.current = linkPopover !== null;
8339
- const handleMediaReplace = useCallback2(
9529
+ const handleMediaReplace = useCallback3(
8340
9530
  (key) => {
8341
- postToParent({ type: "ow:image-pick", key, elementType: mediaHover?.elementType ?? "image" });
9531
+ postToParent2({ type: "ow:image-pick", key, elementType: mediaHover?.elementType ?? "image" });
8342
9532
  },
8343
- [postToParent, mediaHover?.elementType]
9533
+ [postToParent2, mediaHover?.elementType]
8344
9534
  );
8345
- const handleMediaFadeOutComplete = useCallback2((key) => {
9535
+ const handleMediaFadeOutComplete = useCallback3((key) => {
8346
9536
  setUploadingRects((prev) => {
8347
9537
  if (!(key in prev)) return prev;
8348
9538
  const next = { ...prev };
@@ -8350,7 +9540,7 @@ function OhhwellsBridge() {
8350
9540
  return next;
8351
9541
  });
8352
9542
  }, []);
8353
- const handleVideoSettingsChange = useCallback2(
9543
+ const handleVideoSettingsChange = useCallback3(
8354
9544
  (key, settings) => {
8355
9545
  document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
8356
9546
  const video = getVideoEl(el);
@@ -8358,7 +9548,7 @@ function OhhwellsBridge() {
8358
9548
  if (typeof settings.autoplay === "boolean") video.autoplay = settings.autoplay;
8359
9549
  if (typeof settings.muted === "boolean") video.muted = settings.muted;
8360
9550
  syncVideoPlayback(video);
8361
- postToParent({
9551
+ postToParent2({
8362
9552
  type: "ow:change",
8363
9553
  nodes: [
8364
9554
  { key: `${key}${VIDEO_AUTOPLAY_SUFFIX}`, text: String(video.autoplay) },
@@ -8370,12 +9560,12 @@ function OhhwellsBridge() {
8370
9560
  );
8371
9561
  });
8372
9562
  },
8373
- [postToParent]
9563
+ [postToParent2]
8374
9564
  );
8375
- return bridgeRoot ? createPortal(
8376
- /* @__PURE__ */ jsxs12(Fragment4, { children: [
8377
- /* @__PURE__ */ jsx22("div", { ref: attachVisibleViewport, "data-ohw-visible-viewport": "", "aria-hidden": true }),
8378
- Object.entries(uploadingRects).map(([key, { rect, fadingOut }]) => /* @__PURE__ */ jsx22(
9565
+ return bridgeRoot ? createPortal2(
9566
+ /* @__PURE__ */ jsxs13(Fragment5, { children: [
9567
+ /* @__PURE__ */ jsx23("div", { ref: attachVisibleViewport, "data-ohw-visible-viewport": "", "aria-hidden": true }),
9568
+ Object.entries(uploadingRects).map(([key, { rect, fadingOut }]) => /* @__PURE__ */ jsx23(
8379
9569
  MediaOverlay,
8380
9570
  {
8381
9571
  hover: { key, rect, elementType: "image", isDragOver: false, hasTextOverlap: false },
@@ -8386,7 +9576,7 @@ function OhhwellsBridge() {
8386
9576
  },
8387
9577
  `uploading-${key}`
8388
9578
  )),
8389
- mediaHover && !(mediaHover.key in uploadingRects) && /* @__PURE__ */ jsx22(
9579
+ mediaHover && !(mediaHover.key in uploadingRects) && /* @__PURE__ */ jsx23(
8390
9580
  MediaOverlay,
8391
9581
  {
8392
9582
  hover: mediaHover,
@@ -8395,31 +9585,40 @@ function OhhwellsBridge() {
8395
9585
  onVideoSettingsChange: handleVideoSettingsChange
8396
9586
  }
8397
9587
  ),
8398
- siblingHintRect && !isItemDragging && /* @__PURE__ */ jsx22(ItemInteractionLayer, { rect: siblingHintRect, state: "sibling-hint" }),
8399
- hoveredItemRect && !siblingHintRect && !isItemDragging && hoveredItemElRef.current !== selectedElRef.current && /* @__PURE__ */ jsx22(ItemInteractionLayer, { rect: hoveredItemRect, state: "hover" }),
8400
- toolbarRect && toolbarVariant === "link-action" && /* @__PURE__ */ jsx22(
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,
9592
+ {
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(
8401
9599
  ItemInteractionLayer,
8402
9600
  {
8403
9601
  rect: toolbarRect,
8404
9602
  elRef: glowElRef,
8405
9603
  state: isItemDragging ? "dragging" : resolveItemInteractionState(toolbarRect, parentScrollRef.current),
8406
- showHandle: Boolean(reorderHrefKey),
9604
+ showHandle: toolbarVariant === "link-action" && Boolean(reorderHrefKey),
8407
9605
  dragDisabled: reorderDragDisabled,
8408
9606
  dragHandleLabel: reorderHrefKey ? `Reorder ${reorderHrefKey}` : "Reorder item",
8409
9607
  onDragHandleDragStart: handleItemDragStart,
8410
9608
  onDragHandleDragEnd: handleItemDragEnd,
8411
- toolbar: isItemDragging ? void 0 : /* @__PURE__ */ jsx22(
9609
+ toolbar: toolbarVariant === "link-action" && !isItemDragging ? /* @__PURE__ */ jsx23(
8412
9610
  ItemActionToolbar,
8413
9611
  {
8414
9612
  onEditLink: openLinkPopoverForSelected,
8415
- onAddItem: handleAddNavigationItem,
8416
- addItemDisabled: false
9613
+ addItemDisabled: true,
9614
+ editLinkDisabled: false,
9615
+ moreDisabled: true
8417
9616
  }
8418
- )
9617
+ ) : void 0
8419
9618
  }
8420
9619
  ),
8421
- toolbarRect && toolbarVariant === "rich-text" && /* @__PURE__ */ jsxs12(Fragment4, { children: [
8422
- /* @__PURE__ */ jsx22(
9620
+ toolbarRect && toolbarVariant === "rich-text" && /* @__PURE__ */ jsxs13(Fragment5, { children: [
9621
+ /* @__PURE__ */ jsx23(
8423
9622
  EditGlowChrome,
8424
9623
  {
8425
9624
  rect: toolbarRect,
@@ -8428,7 +9627,7 @@ function OhhwellsBridge() {
8428
9627
  dragDisabled: reorderDragDisabled
8429
9628
  }
8430
9629
  ),
8431
- /* @__PURE__ */ jsx22(
9630
+ /* @__PURE__ */ jsx23(
8432
9631
  FloatingToolbar,
8433
9632
  {
8434
9633
  rect: toolbarRect,
@@ -8441,7 +9640,7 @@ function OhhwellsBridge() {
8441
9640
  }
8442
9641
  )
8443
9642
  ] }),
8444
- maxBadge && /* @__PURE__ */ jsxs12(
9643
+ maxBadge && /* @__PURE__ */ jsxs13(
8445
9644
  "div",
8446
9645
  {
8447
9646
  "data-ohw-max-badge": "",
@@ -8467,7 +9666,7 @@ function OhhwellsBridge() {
8467
9666
  ]
8468
9667
  }
8469
9668
  ),
8470
- toggleState && /* @__PURE__ */ jsx22(
9669
+ toggleState && !linkPopover && /* @__PURE__ */ jsx23(
8471
9670
  StateToggle,
8472
9671
  {
8473
9672
  rect: toggleState.rect,
@@ -8476,15 +9675,15 @@ function OhhwellsBridge() {
8476
9675
  onStateChange: handleStateChange
8477
9676
  }
8478
9677
  ),
8479
- sectionGap && /* @__PURE__ */ jsxs12(
9678
+ sectionGap && !linkPopover && /* @__PURE__ */ jsxs13(
8480
9679
  "div",
8481
9680
  {
8482
9681
  "data-ohw-section-insert-line": "",
8483
9682
  className: "fixed left-0 w-full z-2147483646 flex items-center pointer-events-none",
8484
9683
  style: { top: sectionGap.y, transform: "translateY(-50%)" },
8485
9684
  children: [
8486
- /* @__PURE__ */ jsx22("div", { className: "flex-1 bg-primary", style: { height: 3 } }),
8487
- /* @__PURE__ */ jsx22(
9685
+ /* @__PURE__ */ jsx23("div", { className: "flex-1 bg-primary", style: { height: 3 } }),
9686
+ /* @__PURE__ */ jsx23(
8488
9687
  Badge,
8489
9688
  {
8490
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",
@@ -8497,25 +9696,26 @@ function OhhwellsBridge() {
8497
9696
  children: "Add Section"
8498
9697
  }
8499
9698
  ),
8500
- /* @__PURE__ */ jsx22("div", { className: "flex-1 bg-primary", style: { height: 3 } })
9699
+ /* @__PURE__ */ jsx23("div", { className: "flex-1 bg-primary", style: { height: 3 } })
8501
9700
  ]
8502
9701
  }
8503
9702
  ),
8504
- linkPopover && dialogPortalContainer ? /* @__PURE__ */ jsx22(
9703
+ linkPopover && dialogPortalContainer ? /* @__PURE__ */ jsx23(
8505
9704
  LinkPopover,
8506
9705
  {
8507
9706
  panelRef: linkPopoverPanelRef,
8508
9707
  portalContainer: dialogPortalContainer,
8509
9708
  open: true,
8510
- mode: "edit",
9709
+ mode: linkPopover.mode ?? "edit",
8511
9710
  pages: sitePages,
8512
9711
  sections: currentSections,
8513
9712
  sectionsByPath,
8514
9713
  initialTarget: linkPopover.target,
9714
+ existingTargets: linkPopover.intent === "add-nav" ? getNavbarExistingTargets() : [],
8515
9715
  onClose: closeLinkPopover,
8516
9716
  onSubmit: handleLinkPopoverSubmit
8517
9717
  },
8518
- `${linkPopover.key}-${pathname}`
9718
+ linkPopover.key
8519
9719
  ) : null
8520
9720
  ] }),
8521
9721
  bridgeRoot