@ohhwells/bridge 0.1.39 → 0.1.40

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 })
5566
- }
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
- ] })
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
+ )
6138
+ }
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,104 @@ 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
+ }
6164
7017
  function collectSections() {
6165
7018
  return collectSectionsFromDom();
6166
7019
  }
@@ -6308,7 +7161,7 @@ function EditGlowChrome({
6308
7161
  dragDisabled = false
6309
7162
  }) {
6310
7163
  const GAP = 6;
6311
- return /* @__PURE__ */ jsxs12(
7164
+ return /* @__PURE__ */ jsxs13(
6312
7165
  "div",
6313
7166
  {
6314
7167
  ref: elRef,
@@ -6323,7 +7176,7 @@ function EditGlowChrome({
6323
7176
  zIndex: 2147483646
6324
7177
  },
6325
7178
  children: [
6326
- /* @__PURE__ */ jsx22(
7179
+ /* @__PURE__ */ jsx23(
6327
7180
  "div",
6328
7181
  {
6329
7182
  style: {
@@ -6336,7 +7189,7 @@ function EditGlowChrome({
6336
7189
  }
6337
7190
  }
6338
7191
  ),
6339
- reorderHrefKey && /* @__PURE__ */ jsx22(
7192
+ reorderHrefKey && /* @__PURE__ */ jsx23(
6340
7193
  "div",
6341
7194
  {
6342
7195
  "data-ohw-drag-handle-container": "",
@@ -6348,7 +7201,7 @@ function EditGlowChrome({
6348
7201
  transform: "translate(calc(-100% - 7px), -50%)",
6349
7202
  pointerEvents: dragDisabled ? "none" : "auto"
6350
7203
  },
6351
- children: /* @__PURE__ */ jsx22(
7204
+ children: /* @__PURE__ */ jsx23(
6352
7205
  DragHandle,
6353
7206
  {
6354
7207
  "aria-label": `Reorder ${reorderHrefKey}`,
@@ -6452,7 +7305,7 @@ function FloatingToolbar({
6452
7305
  onEditLink
6453
7306
  }) {
6454
7307
  const { top, left, transform } = calcToolbarPos(rect, parentScroll, 330);
6455
- return /* @__PURE__ */ jsx22(
7308
+ return /* @__PURE__ */ jsx23(
6456
7309
  "div",
6457
7310
  {
6458
7311
  ref: elRef,
@@ -6464,12 +7317,12 @@ function FloatingToolbar({
6464
7317
  zIndex: 2147483647,
6465
7318
  pointerEvents: "auto"
6466
7319
  },
6467
- children: /* @__PURE__ */ jsxs12(CustomToolbar, { children: [
6468
- TOOLBAR_GROUPS.map((btns, gi) => /* @__PURE__ */ jsxs12(React9.Fragment, { children: [
6469
- gi > 0 && /* @__PURE__ */ jsx22(CustomToolbarDivider, {}),
7320
+ children: /* @__PURE__ */ jsxs13(CustomToolbar, { children: [
7321
+ TOOLBAR_GROUPS.map((btns, gi) => /* @__PURE__ */ jsxs13(React9.Fragment, { children: [
7322
+ gi > 0 && /* @__PURE__ */ jsx23(CustomToolbarDivider, {}),
6470
7323
  btns.map((btn) => {
6471
7324
  const isActive = activeCommands.has(btn.cmd);
6472
- return /* @__PURE__ */ jsx22(
7325
+ return /* @__PURE__ */ jsx23(
6473
7326
  CustomToolbarButton,
6474
7327
  {
6475
7328
  title: btn.title,
@@ -6478,7 +7331,7 @@ function FloatingToolbar({
6478
7331
  e.preventDefault();
6479
7332
  onCommand(btn.cmd);
6480
7333
  },
6481
- children: /* @__PURE__ */ jsx22(
7334
+ children: /* @__PURE__ */ jsx23(
6482
7335
  "svg",
6483
7336
  {
6484
7337
  width: "16",
@@ -6499,7 +7352,7 @@ function FloatingToolbar({
6499
7352
  );
6500
7353
  })
6501
7354
  ] }, gi)),
6502
- showEditLink ? /* @__PURE__ */ jsx22(
7355
+ showEditLink ? /* @__PURE__ */ jsx23(
6503
7356
  CustomToolbarButton,
6504
7357
  {
6505
7358
  type: "button",
@@ -6513,7 +7366,7 @@ function FloatingToolbar({
6513
7366
  e.preventDefault();
6514
7367
  e.stopPropagation();
6515
7368
  },
6516
- children: /* @__PURE__ */ jsx22(Link2, { className: "size-4 shrink-0", "aria-hidden": true })
7369
+ children: /* @__PURE__ */ jsx23(Link2, { className: "size-4 shrink-0", "aria-hidden": true })
6517
7370
  }
6518
7371
  ) : null
6519
7372
  ] })
@@ -6530,7 +7383,7 @@ function StateToggle({
6530
7383
  states,
6531
7384
  onStateChange
6532
7385
  }) {
6533
- return /* @__PURE__ */ jsx22(
7386
+ return /* @__PURE__ */ jsx23(
6534
7387
  ToggleGroup,
6535
7388
  {
6536
7389
  "data-ohw-state-toggle": "",
@@ -6544,7 +7397,7 @@ function StateToggle({
6544
7397
  left: rect.right - 8,
6545
7398
  transform: "translateX(-100%)"
6546
7399
  },
6547
- children: states.map((state) => /* @__PURE__ */ jsx22(ToggleGroupItem, { value: state, size: "sm", children: state }, state))
7400
+ children: states.map((state) => /* @__PURE__ */ jsx23(ToggleGroupItem, { value: state, size: "sm", children: state }, state))
6548
7401
  }
6549
7402
  );
6550
7403
  }
@@ -6567,12 +7420,12 @@ function resolveSubdomain(subdomainFromQuery) {
6567
7420
  return "";
6568
7421
  }
6569
7422
  function OhhwellsBridge() {
6570
- const pathname = usePathname();
6571
- const router = useRouter();
7423
+ const pathname = usePathname2();
7424
+ const router = useRouter2();
6572
7425
  const searchParams = useSearchParams();
6573
7426
  const isEditMode = isEditSessionActive();
6574
- const [bridgeRoot, setBridgeRoot] = useState5(null);
6575
- useEffect4(() => {
7427
+ const [bridgeRoot, setBridgeRoot] = useState6(null);
7428
+ useEffect7(() => {
6576
7429
  const figtreeFontId = "ohw-figtree-font";
6577
7430
  if (!document.getElementById(figtreeFontId)) {
6578
7431
  const preconnect1 = Object.assign(document.createElement("link"), { rel: "preconnect", href: "https://fonts.googleapis.com" });
@@ -6600,83 +7453,91 @@ function OhhwellsBridge() {
6600
7453
  const subdomainFromQuery = searchParams.get("subdomain");
6601
7454
  const subdomain = resolveSubdomain(subdomainFromQuery);
6602
7455
  useLinkHrefGuardian(pathname, subdomain, isEditMode);
6603
- const postToParent = useCallback2((data) => {
7456
+ const postToParent2 = useCallback3((data) => {
6604
7457
  if (typeof window !== "undefined" && window.parent !== window) {
6605
7458
  window.parent.postMessage(data, "*");
6606
7459
  }
6607
7460
  }, []);
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) => {
7461
+ const [fetchState, setFetchState] = useState6("idle");
7462
+ const autoSaveTimers = useRef5(/* @__PURE__ */ new Map());
7463
+ const activeElRef = useRef5(null);
7464
+ const selectedElRef = useRef5(null);
7465
+ const originalContentRef = useRef5(null);
7466
+ const activeStateElRef = useRef5(null);
7467
+ const parentScrollRef = useRef5(null);
7468
+ const visibleViewportRef = useRef5(null);
7469
+ const [dialogPortalContainer, setDialogPortalContainer] = useState6(null);
7470
+ const attachVisibleViewport = useCallback3((node) => {
6618
7471
  visibleViewportRef.current = node;
6619
7472
  setDialogPortalContainer(node);
6620
7473
  if (node) applyVisibleViewport(node, parentScrollRef.current);
6621
7474
  }, []);
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(() => {
7475
+ const toolbarElRef = useRef5(null);
7476
+ const glowElRef = useRef5(null);
7477
+ const hoveredImageRef = useRef5(null);
7478
+ const hoveredImageHasTextOverlapRef = useRef5(false);
7479
+ const dragOverElRef = useRef5(null);
7480
+ const [mediaHover, setMediaHover] = useState6(null);
7481
+ const [uploadingRects, setUploadingRects] = useState6({});
7482
+ const hoveredGapRef = useRef5(null);
7483
+ const imageUnhoverTimerRef = useRef5(null);
7484
+ const imageShowTimerRef = useRef5(null);
7485
+ const editStylesRef = useRef5(null);
7486
+ const activateRef = useRef5(() => {
7487
+ });
7488
+ const deactivateRef = useRef5(() => {
6634
7489
  });
6635
- const deactivateRef = useRef4(() => {
7490
+ const selectRef = useRef5(() => {
6636
7491
  });
6637
- const selectRef = useRef4(() => {
7492
+ const selectFrameRef = useRef5(() => {
6638
7493
  });
6639
- const deselectRef = useRef4(() => {
7494
+ const deselectRef = useRef5(() => {
6640
7495
  });
6641
- const reselectNavigationItemRef = useRef4(() => {
7496
+ const reselectNavigationItemRef = useRef5(() => {
6642
7497
  });
6643
- const commitNavigationTextEditRef = useRef4(() => {
7498
+ const commitNavigationTextEditRef = useRef5(() => {
6644
7499
  });
6645
- const refreshActiveCommandsRef = useRef4(() => {
7500
+ const refreshActiveCommandsRef = useRef5(() => {
6646
7501
  });
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");
7502
+ const postToParentRef = useRef5(postToParent2);
7503
+ postToParentRef.current = postToParent2;
7504
+ const sectionsLoadedRef = useRef5(false);
7505
+ const pendingScheduleConfigRequests = useRef5([]);
7506
+ const [toolbarRect, setToolbarRect] = useState6(null);
7507
+ const [toolbarVariant, setToolbarVariant] = useState6("none");
7508
+ const toolbarVariantRef = useRef5("none");
6654
7509
  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);
7510
+ const [reorderHrefKey, setReorderHrefKey] = useState6(null);
7511
+ const [reorderDragDisabled, setReorderDragDisabled] = useState6(false);
7512
+ const [toggleState, setToggleState] = useState6(null);
7513
+ const [maxBadge, setMaxBadge] = useState6(null);
7514
+ const [activeCommands, setActiveCommands] = useState6(/* @__PURE__ */ new Set());
7515
+ const [sectionGap, setSectionGap] = useState6(null);
7516
+ const [toolbarShowEditLink, setToolbarShowEditLink] = useState6(false);
7517
+ const hoveredNavContainerRef = useRef5(null);
7518
+ const [hoveredNavContainerRect, setHoveredNavContainerRect] = useState6(null);
7519
+ const hoveredItemElRef = useRef5(null);
7520
+ const [hoveredItemRect, setHoveredItemRect] = useState6(null);
7521
+ const siblingHintElRef = useRef5(null);
7522
+ const [siblingHintRect, setSiblingHintRect] = useState6(null);
7523
+ const [isItemDragging, setIsItemDragging] = useState6(false);
7524
+ const [linkPopover, setLinkPopover] = useState6(null);
7525
+ const linkPopoverSessionRef = useRef5(null);
7526
+ const addNavAfterAnchorRef = useRef5(null);
7527
+ const editContentRef = useRef5({});
7528
+ const [sitePages, setSitePages] = useState6([]);
7529
+ const [sectionsByPath, setSectionsByPath] = useState6({});
7530
+ const sectionsPrefetchGenRef = useRef5(0);
7531
+ const setLinkPopoverRef = useRef5(setLinkPopover);
7532
+ const linkPopoverPanelRef = useRef5(null);
7533
+ const linkPopoverOpenRef = useRef5(false);
7534
+ const linkPopoverGraceUntilRef = useRef5(0);
6675
7535
  setLinkPopoverRef.current = setLinkPopover;
7536
+ linkPopoverSessionRef.current = linkPopover;
6676
7537
  const bumpLinkPopoverGrace = () => {
6677
7538
  linkPopoverGraceUntilRef.current = Date.now() + 350;
6678
7539
  };
6679
- const runSectionsPrefetch = useCallback2((pages) => {
7540
+ const runSectionsPrefetch = useCallback3((pages) => {
6680
7541
  if (!isEditMode || shouldUseDevFixtures() || pages.length === 0) return;
6681
7542
  const gen = ++sectionsPrefetchGenRef.current;
6682
7543
  const paths = pages.map((p) => p.path);
@@ -6695,9 +7556,9 @@ function OhhwellsBridge() {
6695
7556
  );
6696
7557
  });
6697
7558
  }, [isEditMode, pathname]);
6698
- const runSectionsPrefetchRef = useRef4(runSectionsPrefetch);
7559
+ const runSectionsPrefetchRef = useRef5(runSectionsPrefetch);
6699
7560
  runSectionsPrefetchRef.current = runSectionsPrefetch;
6700
- useEffect4(() => {
7561
+ useEffect7(() => {
6701
7562
  if (!linkPopover) return;
6702
7563
  if (hoveredImageRef.current) {
6703
7564
  hoveredImageRef.current = null;
@@ -6706,7 +7567,7 @@ function OhhwellsBridge() {
6706
7567
  hoveredGapRef.current = null;
6707
7568
  setSectionGap(null);
6708
7569
  setMediaHover(null);
6709
- postToParent({ type: "ow:link-modal-lock", locked: true });
7570
+ postToParent2({ type: "ow:link-modal-lock", locked: true });
6710
7571
  const html = document.documentElement;
6711
7572
  const body = document.body;
6712
7573
  const prevHtmlOverflow = html.style.overflow;
@@ -6723,15 +7584,16 @@ function OhhwellsBridge() {
6723
7584
  const scrollOpts = { passive: false, capture: true };
6724
7585
  document.addEventListener("wheel", preventBackgroundScroll, scrollOpts);
6725
7586
  document.addEventListener("touchmove", preventBackgroundScroll, scrollOpts);
7587
+ postToParent2({ type: "ow:image-unhover" });
6726
7588
  return () => {
6727
- postToParent({ type: "ow:link-modal-lock", locked: false });
7589
+ postToParent2({ type: "ow:link-modal-lock", locked: false });
6728
7590
  html.style.overflow = prevHtmlOverflow;
6729
7591
  body.style.overflow = prevBodyOverflow;
6730
7592
  document.removeEventListener("wheel", preventBackgroundScroll, scrollOpts);
6731
7593
  document.removeEventListener("touchmove", preventBackgroundScroll, scrollOpts);
6732
7594
  };
6733
- }, [linkPopover, postToParent]);
6734
- useEffect4(() => {
7595
+ }, [linkPopover, postToParent2]);
7596
+ useEffect7(() => {
6735
7597
  if (!isEditMode) return;
6736
7598
  const useFixtures = shouldUseDevFixtures();
6737
7599
  if (useFixtures) {
@@ -6752,17 +7614,17 @@ function OhhwellsBridge() {
6752
7614
  runSectionsPrefetchRef.current(mapped);
6753
7615
  };
6754
7616
  window.addEventListener("message", onSitePages);
6755
- if (!useFixtures) postToParent({ type: "ow:request-site-pages" });
7617
+ if (!useFixtures) postToParent2({ type: "ow:request-site-pages" });
6756
7618
  return () => window.removeEventListener("message", onSitePages);
6757
- }, [isEditMode, postToParent]);
6758
- useEffect4(() => {
7619
+ }, [isEditMode, postToParent2]);
7620
+ useEffect7(() => {
6759
7621
  if (!isEditMode || shouldUseDevFixtures()) return;
6760
7622
  void loadAllSectionsManifest().then((manifest) => {
6761
7623
  if (Object.keys(manifest).length === 0) return;
6762
7624
  setSectionsByPath((prev) => ({ ...manifest, ...prev }));
6763
7625
  });
6764
7626
  }, [isEditMode]);
6765
- useEffect4(() => {
7627
+ useEffect7(() => {
6766
7628
  const update = () => {
6767
7629
  const el = activeElRef.current ?? selectedElRef.current;
6768
7630
  if (el) setToolbarRect(getEditMeasureEl(el).getBoundingClientRect());
@@ -6786,10 +7648,10 @@ function OhhwellsBridge() {
6786
7648
  vvp.removeEventListener("resize", update);
6787
7649
  };
6788
7650
  }, []);
6789
- const refreshStateRules = useCallback2(() => {
7651
+ const refreshStateRules = useCallback3(() => {
6790
7652
  editStylesRef.current?.forceHover && (editStylesRef.current.forceHover.textContent = collectStateRules());
6791
7653
  }, []);
6792
- const processConfigRequest = useCallback2((insertAfterVal) => {
7654
+ const processConfigRequest = useCallback3((insertAfterVal) => {
6793
7655
  const tracker = getSectionsTracker();
6794
7656
  let entries = [];
6795
7657
  try {
@@ -6812,7 +7674,7 @@ function OhhwellsBridge() {
6812
7674
  }
6813
7675
  window.postMessage({ type: "ow:schedule-config", insertAfter: insertAfterVal, scheduleId: null }, "*");
6814
7676
  }, [isEditMode]);
6815
- const deactivate = useCallback2(() => {
7677
+ const deactivate = useCallback3(() => {
6816
7678
  const el = activeElRef.current;
6817
7679
  if (!el) return;
6818
7680
  const key = el.dataset.ohwKey;
@@ -6841,21 +7703,23 @@ function OhhwellsBridge() {
6841
7703
  setMaxBadge(null);
6842
7704
  setActiveCommands(/* @__PURE__ */ new Set());
6843
7705
  setToolbarShowEditLink(false);
6844
- postToParent({ type: "ow:exit-edit" });
6845
- }, [postToParent]);
6846
- const deselect = useCallback2(() => {
7706
+ postToParent2({ type: "ow:exit-edit" });
7707
+ }, [postToParent2]);
7708
+ const deselect = useCallback3(() => {
6847
7709
  selectedElRef.current = null;
6848
7710
  setReorderHrefKey(null);
6849
7711
  setReorderDragDisabled(false);
6850
7712
  siblingHintElRef.current = null;
6851
7713
  setSiblingHintRect(null);
6852
7714
  setIsItemDragging(false);
7715
+ hoveredNavContainerRef.current = null;
7716
+ setHoveredNavContainerRect(null);
6853
7717
  if (!activeElRef.current) {
6854
7718
  setToolbarRect(null);
6855
7719
  setToolbarVariant("none");
6856
7720
  }
6857
7721
  }, []);
6858
- const reselectNavigationItem = useCallback2((navAnchor) => {
7722
+ const reselectNavigationItem = useCallback3((navAnchor) => {
6859
7723
  selectedElRef.current = navAnchor;
6860
7724
  const { key, disabled } = getNavigationItemReorderState(navAnchor);
6861
7725
  setReorderHrefKey(key);
@@ -6865,7 +7729,7 @@ function OhhwellsBridge() {
6865
7729
  setToolbarShowEditLink(false);
6866
7730
  setActiveCommands(/* @__PURE__ */ new Set());
6867
7731
  }, []);
6868
- const commitNavigationTextEdit = useCallback2((navAnchor) => {
7732
+ const commitNavigationTextEdit = useCallback3((navAnchor) => {
6869
7733
  const el = activeElRef.current;
6870
7734
  if (!el) return;
6871
7735
  const key = el.dataset.ohwKey;
@@ -6878,9 +7742,9 @@ function OhhwellsBridge() {
6878
7742
  const html = sanitizeHtml(el.innerHTML);
6879
7743
  const original = originalContentRef.current ?? "";
6880
7744
  if (html !== sanitizeHtml(original)) {
6881
- postToParent({ type: "ow:change", nodes: [{ key, text: html }] });
7745
+ postToParent2({ type: "ow:change", nodes: [{ key, text: html }] });
6882
7746
  const h = document.documentElement.scrollHeight;
6883
- if (h > 50) postToParent({ type: "ow:height", height: h });
7747
+ if (h > 50) postToParent2({ type: "ow:height", height: h });
6884
7748
  }
6885
7749
  }
6886
7750
  el.removeAttribute("contenteditable");
@@ -6888,31 +7752,38 @@ function OhhwellsBridge() {
6888
7752
  setMaxBadge(null);
6889
7753
  setActiveCommands(/* @__PURE__ */ new Set());
6890
7754
  setToolbarShowEditLink(false);
6891
- postToParent({ type: "ow:exit-edit" });
7755
+ postToParent2({ type: "ow:exit-edit" });
6892
7756
  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());
7757
+ }, [postToParent2, reselectNavigationItem]);
7758
+ const handleAddTopLevelNavItem = useCallback3(() => {
7759
+ const items = listNavbarItems();
7760
+ addNavAfterAnchorRef.current = items[items.length - 1] ?? null;
7761
+ deselectRef.current();
7762
+ deactivateRef.current();
7763
+ bumpLinkPopoverGrace();
7764
+ setLinkPopover({
7765
+ key: "__add-nav__",
7766
+ mode: "create",
7767
+ intent: "add-nav"
7768
+ });
6900
7769
  }, []);
6901
- const handleItemDragStart = useCallback2(() => {
7770
+ const handleItemDragStart = useCallback3(() => {
6902
7771
  siblingHintElRef.current = null;
6903
7772
  setSiblingHintRect(null);
6904
7773
  setIsItemDragging(true);
6905
7774
  }, []);
6906
- const handleItemDragEnd = useCallback2(() => {
7775
+ const handleItemDragEnd = useCallback3(() => {
6907
7776
  setIsItemDragging(false);
6908
7777
  }, []);
6909
7778
  reselectNavigationItemRef.current = reselectNavigationItem;
6910
7779
  commitNavigationTextEditRef.current = commitNavigationTextEdit;
6911
- const select = useCallback2((anchor) => {
7780
+ const select = useCallback3((anchor) => {
6912
7781
  if (!isNavigationItem(anchor)) return;
6913
7782
  if (activeElRef.current) deactivate();
6914
7783
  selectedElRef.current = anchor;
6915
7784
  clearHrefKeyHover(anchor);
7785
+ hoveredNavContainerRef.current = null;
7786
+ setHoveredNavContainerRect(null);
6916
7787
  setHoveredItemRect(null);
6917
7788
  hoveredItemElRef.current = null;
6918
7789
  siblingHintElRef.current = null;
@@ -6926,7 +7797,26 @@ function OhhwellsBridge() {
6926
7797
  setToolbarShowEditLink(false);
6927
7798
  setActiveCommands(/* @__PURE__ */ new Set());
6928
7799
  }, [deactivate]);
6929
- const activate = useCallback2((el) => {
7800
+ const selectFrame = useCallback3((el) => {
7801
+ if (!isNavigationContainer(el)) return;
7802
+ if (activeElRef.current) deactivate();
7803
+ selectedElRef.current = el;
7804
+ clearHrefKeyHover(el);
7805
+ hoveredNavContainerRef.current = null;
7806
+ setHoveredNavContainerRect(null);
7807
+ setHoveredItemRect(null);
7808
+ hoveredItemElRef.current = null;
7809
+ siblingHintElRef.current = null;
7810
+ setSiblingHintRect(null);
7811
+ setIsItemDragging(false);
7812
+ setReorderHrefKey(null);
7813
+ setReorderDragDisabled(false);
7814
+ setToolbarVariant("select-frame");
7815
+ setToolbarRect(el.getBoundingClientRect());
7816
+ setToolbarShowEditLink(false);
7817
+ setActiveCommands(/* @__PURE__ */ new Set());
7818
+ }, [deactivate]);
7819
+ const activate = useCallback3((el, options) => {
6930
7820
  if (activeElRef.current === el) return;
6931
7821
  selectedElRef.current = null;
6932
7822
  deactivate();
@@ -6944,6 +7834,9 @@ function OhhwellsBridge() {
6944
7834
  activeElRef.current = el;
6945
7835
  originalContentRef.current = el.innerHTML;
6946
7836
  el.focus();
7837
+ if (options?.caretX !== void 0 && options?.caretY !== void 0) {
7838
+ placeCaretAtPoint(el, options.caretX, options.caretY);
7839
+ }
6947
7840
  setToolbarShowEditLink(Boolean(getHrefKeyFromElement(el)));
6948
7841
  const navAnchor = getNavigationItemAnchor(el);
6949
7842
  if (navAnchor) {
@@ -6955,12 +7848,13 @@ function OhhwellsBridge() {
6955
7848
  setReorderDragDisabled(reorderDisabled);
6956
7849
  }
6957
7850
  setToolbarRect(getEditMeasureEl(el).getBoundingClientRect());
6958
- postToParent({ type: "ow:enter-edit", key: el.dataset.ohwKey });
7851
+ postToParent2({ type: "ow:enter-edit", key: el.dataset.ohwKey });
6959
7852
  requestAnimationFrame(() => refreshActiveCommandsRef.current());
6960
- }, [deactivate, postToParent]);
7853
+ }, [deactivate, postToParent2]);
6961
7854
  activateRef.current = activate;
6962
7855
  deactivateRef.current = deactivate;
6963
7856
  selectRef.current = select;
7857
+ selectFrameRef.current = selectFrame;
6964
7858
  deselectRef.current = deselect;
6965
7859
  useLayoutEffect2(() => {
6966
7860
  if (!subdomain || isEditMode) {
@@ -7002,6 +7896,7 @@ function OhhwellsBridge() {
7002
7896
  });
7003
7897
  applyLinkByKey(key, val);
7004
7898
  }
7899
+ reconcileNavbarItemsFromContent(content);
7005
7900
  enforceLinkHrefs();
7006
7901
  initSectionsFromContent(content, true);
7007
7902
  sectionsLoadedRef.current = true;
@@ -7030,7 +7925,7 @@ function OhhwellsBridge() {
7030
7925
  cancelled = true;
7031
7926
  };
7032
7927
  }, [subdomain, isEditMode]);
7033
- useEffect4(() => {
7928
+ useEffect7(() => {
7034
7929
  if (!subdomain || isEditMode) return;
7035
7930
  let debounceTimer = null;
7036
7931
  let observer = null;
@@ -7061,6 +7956,7 @@ function OhhwellsBridge() {
7061
7956
  });
7062
7957
  applyLinkByKey(key, val);
7063
7958
  }
7959
+ reconcileNavbarItemsFromContent(content);
7064
7960
  } finally {
7065
7961
  observer?.observe(document.body, { childList: true, subtree: true });
7066
7962
  }
@@ -7084,20 +7980,38 @@ function OhhwellsBridge() {
7084
7980
  const visible = Boolean(subdomain) && fetchState !== "done";
7085
7981
  el.style.display = visible ? "flex" : "none";
7086
7982
  }, [subdomain, fetchState]);
7087
- useEffect4(() => {
7088
- postToParent({ type: "ow:navigation", path: pathname });
7089
- }, [pathname, postToParent]);
7090
- useEffect4(() => {
7983
+ useEffect7(() => {
7984
+ postToParent2({ type: "ow:navigation", path: pathname });
7985
+ }, [pathname, postToParent2]);
7986
+ useEffect7(() => {
7091
7987
  if (!isEditMode) return;
7988
+ if (linkPopoverSessionRef.current?.intent === "add-nav") return;
7989
+ if (document.querySelector("[data-ohw-section-picker]")) return;
7092
7990
  setLinkPopover(null);
7093
7991
  deselectRef.current();
7094
7992
  deactivateRef.current();
7095
7993
  }, [pathname, isEditMode]);
7096
- useEffect4(() => {
7994
+ useEffect7(() => {
7995
+ const contentForNav = () => {
7996
+ if (isEditMode) return editContentRef.current;
7997
+ if (!subdomain) return {};
7998
+ return contentCache.get(subdomain) ?? {};
7999
+ };
8000
+ const run = () => reconcileNavbarItemsFromContent(contentForNav());
8001
+ run();
8002
+ const nav = document.querySelector("nav");
8003
+ if (!nav) return;
8004
+ const observer = new MutationObserver(() => {
8005
+ requestAnimationFrame(run);
8006
+ });
8007
+ observer.observe(nav, { childList: true, subtree: true });
8008
+ return () => observer.disconnect();
8009
+ }, [isEditMode, pathname, subdomain, fetchState]);
8010
+ useEffect7(() => {
7097
8011
  if (!isEditMode) return;
7098
8012
  const measure = () => {
7099
8013
  const h = document.body.scrollHeight;
7100
- if (h > 50) postToParent({ type: "ow:height", height: h });
8014
+ if (h > 50) postToParent2({ type: "ow:height", height: h });
7101
8015
  };
7102
8016
  const t1 = setTimeout(measure, 50);
7103
8017
  const t2 = setTimeout(measure, 500);
@@ -7116,8 +8030,8 @@ function OhhwellsBridge() {
7116
8030
  if (resizeTimer) clearTimeout(resizeTimer);
7117
8031
  window.removeEventListener("resize", handleResize);
7118
8032
  };
7119
- }, [pathname, isEditMode, postToParent]);
7120
- useEffect4(() => {
8033
+ }, [pathname, isEditMode, postToParent2]);
8034
+ useEffect7(() => {
7121
8035
  if (!subdomainFromQuery || isEditMode) return;
7122
8036
  const handleClick = (e) => {
7123
8037
  const anchor = e.target.closest("a");
@@ -7133,7 +8047,7 @@ function OhhwellsBridge() {
7133
8047
  document.addEventListener("click", handleClick, true);
7134
8048
  return () => document.removeEventListener("click", handleClick, true);
7135
8049
  }, [subdomainFromQuery, isEditMode, router]);
7136
- useEffect4(() => {
8050
+ useEffect7(() => {
7137
8051
  if (!isEditMode) {
7138
8052
  editStylesRef.current?.base.remove();
7139
8053
  editStylesRef.current?.forceHover.remove();
@@ -7206,6 +8120,13 @@ function OhhwellsBridge() {
7206
8120
  if (target.closest("[data-ohw-max-badge]")) return;
7207
8121
  if (isInsideLinkEditor(target)) return;
7208
8122
  if (target.closest('[data-ohw-edit-chrome], [data-ohw-item-interaction], [data-ohw-drag-handle-container], [data-slot="drag-handle"]')) {
8123
+ const navFromChrome = resolveNavContainerSelectionTarget(target, e.clientX, e.clientY);
8124
+ if (navFromChrome) {
8125
+ e.preventDefault();
8126
+ e.stopPropagation();
8127
+ selectFrameRef.current(navFromChrome);
8128
+ return;
8129
+ }
7209
8130
  e.preventDefault();
7210
8131
  e.stopPropagation();
7211
8132
  return;
@@ -7220,7 +8141,8 @@ function OhhwellsBridge() {
7220
8141
  bumpLinkPopoverGrace();
7221
8142
  setLinkPopoverRef.current({
7222
8143
  key: editable.dataset.ohwKey ?? "",
7223
- target: getLinkHref(editable)
8144
+ mode: "edit",
8145
+ target: getLinkHref2(editable)
7224
8146
  });
7225
8147
  return;
7226
8148
  }
@@ -7236,7 +8158,7 @@ function OhhwellsBridge() {
7236
8158
  e.preventDefault();
7237
8159
  e.stopPropagation();
7238
8160
  if (selectedElRef.current === navAnchor) {
7239
- activateRef.current(editable);
8161
+ activateRef.current(editable, { caretX: e.clientX, caretY: e.clientY });
7240
8162
  return;
7241
8163
  }
7242
8164
  selectRef.current(navAnchor);
@@ -7255,6 +8177,22 @@ function OhhwellsBridge() {
7255
8177
  selectRef.current(hrefAnchor);
7256
8178
  return;
7257
8179
  }
8180
+ const navContainerToSelect = resolveNavContainerSelectionTarget(target, e.clientX, e.clientY);
8181
+ if (navContainerToSelect) {
8182
+ e.preventDefault();
8183
+ e.stopPropagation();
8184
+ selectFrameRef.current(navContainerToSelect);
8185
+ return;
8186
+ }
8187
+ const selectedContainer = selectedElRef.current;
8188
+ if (selectedContainer && isNavigationContainer(selectedContainer)) {
8189
+ const navItem = getNavigationItemAnchor(target);
8190
+ if (!navItem && (selectedContainer === target || selectedContainer.contains(target))) {
8191
+ e.preventDefault();
8192
+ e.stopPropagation();
8193
+ return;
8194
+ }
8195
+ }
7258
8196
  if (activeElRef.current) {
7259
8197
  const sel = window.getSelection();
7260
8198
  if (sel && !sel.isCollapsed) {
@@ -7281,9 +8219,26 @@ function OhhwellsBridge() {
7281
8219
  deactivateRef.current();
7282
8220
  };
7283
8221
  const handleMouseOver = (e) => {
8222
+ if (document.documentElement.hasAttribute("data-ohw-section-picking")) return;
7284
8223
  const target = e.target;
8224
+ if (toolbarVariantRef.current !== "select-frame") {
8225
+ const navContainer = target.closest("[data-ohw-nav-container]");
8226
+ if (navContainer && !getNavigationItemAnchor(target)) {
8227
+ hoveredNavContainerRef.current = navContainer;
8228
+ setHoveredNavContainerRect(navContainer.getBoundingClientRect());
8229
+ hoveredItemElRef.current = null;
8230
+ setHoveredItemRect(null);
8231
+ return;
8232
+ }
8233
+ if (!target.closest("[data-ohw-nav-container]")) {
8234
+ hoveredNavContainerRef.current = null;
8235
+ setHoveredNavContainerRect(null);
8236
+ }
8237
+ }
7285
8238
  const navAnchor = getNavigationItemAnchor(target);
7286
8239
  if (navAnchor) {
8240
+ hoveredNavContainerRef.current = null;
8241
+ setHoveredNavContainerRect(null);
7287
8242
  const selected2 = selectedElRef.current;
7288
8243
  if (selected2 === navAnchor || selected2?.contains(navAnchor)) return;
7289
8244
  clearHrefKeyHover(navAnchor);
@@ -7308,6 +8263,14 @@ function OhhwellsBridge() {
7308
8263
  };
7309
8264
  const handleMouseOut = (e) => {
7310
8265
  const target = e.target;
8266
+ if (target.closest("[data-ohw-nav-container]")) {
8267
+ const related2 = e.relatedTarget instanceof Element ? e.relatedTarget : null;
8268
+ if (related2?.closest("[data-ohw-nav-container], [data-ohw-navbar-container-chrome], [data-ohw-navbar-add-button]")) {
8269
+ return;
8270
+ }
8271
+ hoveredNavContainerRef.current = null;
8272
+ setHoveredNavContainerRect(null);
8273
+ }
7311
8274
  const navAnchor = getNavigationItemAnchor(target);
7312
8275
  if (navAnchor) {
7313
8276
  const related2 = e.relatedTarget instanceof Element ? e.relatedTarget : null;
@@ -7429,6 +8392,56 @@ function OhhwellsBridge() {
7429
8392
  }
7430
8393
  return smallest;
7431
8394
  };
8395
+ const dismissImageHover = () => {
8396
+ if (hoveredImageRef.current) {
8397
+ hoveredImageRef.current = null;
8398
+ hoveredImageHasTextOverlapRef.current = false;
8399
+ resumeAnimTracks();
8400
+ postToParentRef.current({ type: "ow:image-unhover" });
8401
+ }
8402
+ };
8403
+ const probeNavigationHoverAt = (x, y) => {
8404
+ if (toolbarVariantRef.current === "select-frame") {
8405
+ hoveredNavContainerRef.current = null;
8406
+ setHoveredNavContainerRect(null);
8407
+ return;
8408
+ }
8409
+ const navContainer = document.querySelector("[data-ohw-nav-container]");
8410
+ if (!navContainer) {
8411
+ hoveredNavContainerRef.current = null;
8412
+ setHoveredNavContainerRect(null);
8413
+ return;
8414
+ }
8415
+ const containerRect = navContainer.getBoundingClientRect();
8416
+ const overContainer = x >= containerRect.left && x <= containerRect.right && y >= containerRect.top && y <= containerRect.bottom;
8417
+ if (!overContainer) {
8418
+ hoveredNavContainerRef.current = null;
8419
+ setHoveredNavContainerRect(null);
8420
+ return;
8421
+ }
8422
+ const navItemHit = Array.from(
8423
+ navContainer.querySelectorAll("[data-ohw-href-key]")
8424
+ ).find((el) => {
8425
+ if (!isNavigationItem(el) || isNavbarButton2(el)) return false;
8426
+ const itemRect = el.getBoundingClientRect();
8427
+ return x >= itemRect.left && x <= itemRect.right && y >= itemRect.top && y <= itemRect.bottom;
8428
+ });
8429
+ if (navItemHit) {
8430
+ hoveredNavContainerRef.current = null;
8431
+ setHoveredNavContainerRect(null);
8432
+ const selected = selectedElRef.current;
8433
+ if (selected !== navItemHit && !selected?.contains(navItemHit)) {
8434
+ clearHrefKeyHover(navItemHit);
8435
+ hoveredItemElRef.current = navItemHit;
8436
+ setHoveredItemRect(navItemHit.getBoundingClientRect());
8437
+ }
8438
+ return;
8439
+ }
8440
+ hoveredNavContainerRef.current = navContainer;
8441
+ setHoveredNavContainerRect(containerRect);
8442
+ hoveredItemElRef.current = null;
8443
+ setHoveredItemRect(null);
8444
+ };
7432
8445
  const probeImageAt = (clientX, clientY, isDragOver = false, fromParentViewport = false) => {
7433
8446
  if (linkPopoverOpenRef.current) {
7434
8447
  if (hoveredImageRef.current) {
@@ -7437,18 +8450,22 @@ function OhhwellsBridge() {
7437
8450
  resumeAnimTracks();
7438
8451
  clearImageHover();
7439
8452
  }
8453
+ dismissImageHover();
8454
+ return;
8455
+ }
8456
+ const { x, y } = toProbeCoords(clientX, clientY, fromParentViewport);
8457
+ if (isPointOverNavigation(x, y)) {
8458
+ dismissImageHover();
8459
+ probeNavigationHoverAt(x, y);
7440
8460
  return;
7441
8461
  }
8462
+ hoveredNavContainerRef.current = null;
8463
+ setHoveredNavContainerRect(null);
7442
8464
  const toggleEl = document.querySelector("[data-ohw-state-toggle]");
7443
8465
  if (toggleEl) {
7444
- const { x, y } = toProbeCoords(clientX, clientY, fromParentViewport);
7445
8466
  const tr = toggleEl.getBoundingClientRect();
7446
8467
  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
- }
8468
+ dismissImageHover();
7452
8469
  return;
7453
8470
  }
7454
8471
  }
@@ -7461,15 +8478,18 @@ function OhhwellsBridge() {
7461
8478
  resumeAnimTracks();
7462
8479
  clearImageHover();
7463
8480
  }
7464
- return;
8481
+ if (activeElRef.current) {
8482
+ dismissImageHover();
8483
+ return;
8484
+ }
7465
8485
  }
7466
8486
  if (imgEl) {
7467
- const { x, y } = toProbeCoords(clientX, clientY, fromParentViewport);
8487
+ const { x: x2, y: y2 } = toProbeCoords(clientX, clientY, fromParentViewport);
7468
8488
  const overMediaChrome = Array.from(
7469
8489
  document.querySelectorAll("[data-ohw-media-chrome]")
7470
8490
  ).some((el) => {
7471
8491
  const r2 = el.getBoundingClientRect();
7472
- return x >= r2.left && x <= r2.right && y >= r2.top && y <= r2.bottom;
8492
+ return x2 >= r2.left && x2 <= r2.right && y2 >= r2.top && y2 <= r2.bottom;
7473
8493
  });
7474
8494
  if (overMediaChrome) {
7475
8495
  if (hoveredImageRef.current) {
@@ -7480,7 +8500,7 @@ function OhhwellsBridge() {
7480
8500
  }
7481
8501
  return;
7482
8502
  }
7483
- const topEl = document.elementFromPoint(x, y);
8503
+ const topEl = document.elementFromPoint(x2, y2);
7484
8504
  if (topEl?.closest('[data-ohw-toolbar], [data-ohw-edit-chrome], [data-ohw-item-interaction], [data-ohw-drag-handle-container], [data-slot="drag-handle"]')) {
7485
8505
  if (hoveredImageRef.current) {
7486
8506
  hoveredImageRef.current = null;
@@ -7503,7 +8523,7 @@ function OhhwellsBridge() {
7503
8523
  document.querySelectorAll(NON_MEDIA_SELECTOR)
7504
8524
  ).find((el) => {
7505
8525
  const er = el.getBoundingClientRect();
7506
- return x >= er.left && x <= er.right && y >= er.top && y <= er.bottom;
8526
+ return x2 >= er.left && x2 <= er.right && y2 >= er.top && y2 <= er.bottom;
7507
8527
  });
7508
8528
  if (imageUnhoverTimerRef.current) {
7509
8529
  clearTimeout(imageUnhoverTimerRef.current);
@@ -7579,12 +8599,12 @@ function OhhwellsBridge() {
7579
8599
  resumeAnimTracks();
7580
8600
  clearImageHover();
7581
8601
  }
7582
- const { x, y } = toProbeCoords(clientX, clientY, fromParentViewport);
8602
+ const { x: x2, y: y2 } = toProbeCoords(clientX, clientY, fromParentViewport);
7583
8603
  const textEl = Array.from(
7584
8604
  document.querySelectorAll(NON_MEDIA_SELECTOR)
7585
8605
  ).find((el) => {
7586
8606
  const er = el.getBoundingClientRect();
7587
- return x >= er.left && x <= er.right && y >= er.top && y <= er.bottom;
8607
+ return x2 >= er.left && x2 <= er.right && y2 >= er.top && y2 <= er.bottom;
7588
8608
  });
7589
8609
  document.querySelectorAll("[data-ohw-hovered]").forEach((el) => el.removeAttribute("data-ohw-hovered"));
7590
8610
  if (textEl && !textEl.hasAttribute("contenteditable")) {
@@ -7608,6 +8628,14 @@ function OhhwellsBridge() {
7608
8628
  }
7609
8629
  };
7610
8630
  const probeHoverCardsAt = (clientX, clientY, fromParentViewport = false) => {
8631
+ if (linkPopoverOpenRef.current || document.documentElement.hasAttribute("data-ohw-section-picking")) {
8632
+ if (activeStateElRef.current) {
8633
+ activeStateElRef.current.removeAttribute("data-ohw-state-hovered");
8634
+ activeStateElRef.current = null;
8635
+ setToggleState(null);
8636
+ }
8637
+ return;
8638
+ }
7611
8639
  const { x, y } = toProbeCoords(clientX, clientY, fromParentViewport);
7612
8640
  const toggleEl = document.querySelector("[data-ohw-state-toggle]");
7613
8641
  if (toggleEl) {
@@ -7884,6 +8912,8 @@ function OhhwellsBridge() {
7884
8912
  sectionsLoadedRef.current = true;
7885
8913
  pendingScheduleConfigRequests.current.splice(0).forEach(processConfigRequest);
7886
8914
  }
8915
+ editContentRef.current = { ...editContentRef.current, ...content };
8916
+ reconcileNavbarItemsFromContent(editContentRef.current);
7887
8917
  enforceLinkHrefs();
7888
8918
  postToParentRef.current({ type: "ow:hydrate-done" });
7889
8919
  };
@@ -7891,6 +8921,7 @@ function OhhwellsBridge() {
7891
8921
  const handleDeactivate = (e) => {
7892
8922
  if (e.data?.type !== "ow:deactivate") return;
7893
8923
  if (Date.now() < linkPopoverGraceUntilRef.current) return;
8924
+ if (document.querySelector("[data-ohw-section-picker]")) return;
7894
8925
  if (linkPopoverOpenRef.current) {
7895
8926
  setLinkPopoverRef.current(null);
7896
8927
  return;
@@ -7899,13 +8930,31 @@ function OhhwellsBridge() {
7899
8930
  deactivateRef.current();
7900
8931
  };
7901
8932
  window.addEventListener("message", handleDeactivate);
8933
+ const handleUiEscape = (e) => {
8934
+ if (e.data?.type !== "ui:escape") return;
8935
+ if (linkPopoverOpenRef.current) {
8936
+ setLinkPopoverRef.current(null);
8937
+ }
8938
+ };
8939
+ window.addEventListener("message", handleUiEscape);
7902
8940
  const handleKeyDown = (e) => {
8941
+ if (e.key === "Escape" && document.querySelector("[data-ohw-section-picker]")) return;
7903
8942
  if (e.key === "Escape" && linkPopoverOpenRef.current) {
7904
8943
  setLinkPopoverRef.current(null);
7905
8944
  return;
7906
8945
  }
7907
8946
  if (e.key === "Escape" && selectedElRef.current && !activeElRef.current) {
7908
- deselectRef.current();
8947
+ if (toolbarVariantRef.current === "select-frame") {
8948
+ deselectRef.current();
8949
+ return;
8950
+ }
8951
+ const parent = getNavigationSelectionParent(selectedElRef.current);
8952
+ if (parent) {
8953
+ e.preventDefault();
8954
+ selectFrameRef.current(parent);
8955
+ } else {
8956
+ deselectRef.current();
8957
+ }
7909
8958
  return;
7910
8959
  }
7911
8960
  if (e.key === "Escape" && activeElRef.current) {
@@ -7963,6 +9012,9 @@ function OhhwellsBridge() {
7963
9012
  if (hoveredItemElRef.current) {
7964
9013
  setHoveredItemRect(hoveredItemElRef.current.getBoundingClientRect());
7965
9014
  }
9015
+ if (hoveredNavContainerRef.current) {
9016
+ setHoveredNavContainerRect(hoveredNavContainerRef.current.getBoundingClientRect());
9017
+ }
7966
9018
  if (siblingHintElRef.current) {
7967
9019
  setSiblingHintRect(siblingHintElRef.current.getBoundingClientRect());
7968
9020
  }
@@ -8159,9 +9211,45 @@ function OhhwellsBridge() {
8159
9211
  return clientX >= r2.left && clientX <= r2.right && clientY >= r2.top && clientY <= r2.bottom;
8160
9212
  });
8161
9213
  if (textEditable) {
8162
- activateRef.current(textEditable);
9214
+ const hrefCtx = getHrefKeyFromElement(textEditable);
9215
+ const navAnchor = hrefCtx ? getNavigationItemAnchor(hrefCtx.anchor) : null;
9216
+ if (navAnchor) {
9217
+ if (selectedElRef.current === navAnchor) {
9218
+ activateRef.current(textEditable, { caretX: clientX, caretY: clientY });
9219
+ } else {
9220
+ selectRef.current(navAnchor);
9221
+ }
9222
+ return;
9223
+ }
9224
+ activateRef.current(textEditable, { caretX: clientX, caretY: clientY });
8163
9225
  return;
8164
9226
  }
9227
+ const navContainer = document.querySelector("[data-ohw-nav-container]");
9228
+ if (navContainer) {
9229
+ const r2 = navContainer.getBoundingClientRect();
9230
+ const pad = 8;
9231
+ const over = clientX >= r2.left - pad && clientX <= r2.right + pad && clientY >= r2.top - pad && clientY <= r2.bottom + pad;
9232
+ if (over && !isPointOverNavItem(navContainer, clientX, clientY)) {
9233
+ selectFrameRef.current(navContainer);
9234
+ return;
9235
+ }
9236
+ }
9237
+ const navRoot = document.querySelector("[data-ohw-nav-root]");
9238
+ if (navRoot && navContainer) {
9239
+ const rr = navRoot.getBoundingClientRect();
9240
+ if (clientX >= rr.left && clientX <= rr.right && clientY >= rr.top && clientY <= rr.bottom && !isPointOverNavItem(navContainer, clientX, clientY)) {
9241
+ const book = navRoot.querySelector('[data-ohw-role="navbar-button"]');
9242
+ if (book) {
9243
+ const br = book.getBoundingClientRect();
9244
+ if (clientX >= br.left && clientX <= br.right && clientY >= br.top && clientY <= br.bottom) {
9245
+ deactivateRef.current();
9246
+ return;
9247
+ }
9248
+ }
9249
+ selectFrameRef.current(navContainer);
9250
+ return;
9251
+ }
9252
+ }
8165
9253
  deactivateRef.current();
8166
9254
  };
8167
9255
  window.addEventListener("message", handleSave);
@@ -8224,13 +9312,14 @@ function OhhwellsBridge() {
8224
9312
  window.removeEventListener("message", handleClickAt);
8225
9313
  window.removeEventListener("message", handleHydrate);
8226
9314
  window.removeEventListener("message", handleDeactivate);
9315
+ window.removeEventListener("message", handleUiEscape);
8227
9316
  autoSaveTimers.current.forEach(clearTimeout);
8228
9317
  autoSaveTimers.current.clear();
8229
9318
  if (imageUnhoverTimerRef.current) clearTimeout(imageUnhoverTimerRef.current);
8230
9319
  if (imageShowTimerRef.current) clearTimeout(imageShowTimerRef.current);
8231
9320
  };
8232
9321
  }, [isEditMode, refreshStateRules]);
8233
- useEffect4(() => {
9322
+ useEffect7(() => {
8234
9323
  const handler = (e) => {
8235
9324
  if (e.data?.type !== "ow:request-schedule-config") return;
8236
9325
  const insertAfterVal = e.data.insertAfter;
@@ -8246,7 +9335,7 @@ function OhhwellsBridge() {
8246
9335
  window.addEventListener("message", handler);
8247
9336
  return () => window.removeEventListener("message", handler);
8248
9337
  }, [processConfigRequest]);
8249
- useEffect4(() => {
9338
+ useEffect7(() => {
8250
9339
  if (!isEditMode) return;
8251
9340
  document.querySelectorAll("[data-ohw-active-state]").forEach((el) => {
8252
9341
  el.removeAttribute("data-ohw-active-state");
@@ -8267,27 +9356,27 @@ function OhhwellsBridge() {
8267
9356
  const next = { ...prev, [pathKey]: sections };
8268
9357
  return shouldUseDevFixtures() ? { ...DEV_SECTIONS_BY_PATH, ...next } : next;
8269
9358
  });
8270
- postToParent({ type: "ow:ready", version: "1", path: pathname, nodes: collectEditableNodes(), sections });
8271
- postToParent({ type: "ow:request-site-pages" });
9359
+ postToParent2({ type: "ow:ready", version: "1", path: pathname, nodes: collectEditableNodes(), sections });
9360
+ postToParent2({ type: "ow:request-site-pages" });
8272
9361
  }, 150);
8273
9362
  return () => {
8274
9363
  cancelAnimationFrame(raf);
8275
9364
  clearTimeout(timer);
8276
9365
  };
8277
- }, [pathname, isEditMode, refreshStateRules, postToParent]);
8278
- useEffect4(() => {
9366
+ }, [pathname, isEditMode, refreshStateRules, postToParent2]);
9367
+ useEffect7(() => {
8279
9368
  scrollToHashSectionWhenReady();
8280
9369
  const onHashChange = () => scrollToHashSectionWhenReady();
8281
9370
  window.addEventListener("hashchange", onHashChange);
8282
9371
  return () => window.removeEventListener("hashchange", onHashChange);
8283
9372
  }, [pathname]);
8284
- const handleCommand = useCallback2((cmd) => {
9373
+ const handleCommand = useCallback3((cmd) => {
8285
9374
  document.execCommand(cmd, false);
8286
9375
  activeElRef.current?.focus();
8287
9376
  if (activeElRef.current) setToolbarRect(getEditMeasureEl(activeElRef.current).getBoundingClientRect());
8288
9377
  refreshActiveCommandsRef.current();
8289
9378
  }, []);
8290
- const handleStateChange = useCallback2((state) => {
9379
+ const handleStateChange = useCallback3((state) => {
8291
9380
  if (!activeStateElRef.current) return;
8292
9381
  const el = activeStateElRef.current;
8293
9382
  if (state === "Default") {
@@ -8300,18 +9389,22 @@ function OhhwellsBridge() {
8300
9389
  }
8301
9390
  setToggleState((prev) => prev ? { ...prev, activeState: state } : null);
8302
9391
  }, [deactivate]);
8303
- const closeLinkPopover = useCallback2(() => setLinkPopover(null), []);
8304
- const openLinkPopoverForActive = useCallback2(() => {
9392
+ const closeLinkPopover = useCallback3(() => {
9393
+ addNavAfterAnchorRef.current = null;
9394
+ setLinkPopover(null);
9395
+ }, []);
9396
+ const openLinkPopoverForActive = useCallback3(() => {
8305
9397
  const hrefCtx = getHrefKeyFromElement(activeElRef.current);
8306
9398
  if (!hrefCtx) return;
8307
9399
  bumpLinkPopoverGrace();
8308
9400
  setLinkPopover({
8309
9401
  key: hrefCtx.key,
8310
- target: getLinkHref(hrefCtx.anchor)
9402
+ mode: "edit",
9403
+ target: getLinkHref2(hrefCtx.anchor)
8311
9404
  });
8312
9405
  deactivate();
8313
9406
  }, [deactivate]);
8314
- const openLinkPopoverForSelected = useCallback2(() => {
9407
+ const openLinkPopoverForSelected = useCallback3(() => {
8315
9408
  const anchor = selectedElRef.current;
8316
9409
  if (!anchor) return;
8317
9410
  const key = anchor.getAttribute("data-ohw-href-key");
@@ -8319,30 +9412,77 @@ function OhhwellsBridge() {
8319
9412
  bumpLinkPopoverGrace();
8320
9413
  setLinkPopover({
8321
9414
  key,
8322
- target: getLinkHref(anchor)
9415
+ mode: "edit",
9416
+ target: getLinkHref2(anchor)
8323
9417
  });
8324
9418
  deselect();
8325
9419
  }, [deselect]);
8326
- const handleLinkPopoverSubmit = useCallback2(
9420
+ const handleLinkPopoverSubmit = useCallback3(
8327
9421
  (target) => {
8328
- if (!linkPopover) return;
8329
- const { key } = linkPopover;
9422
+ const session = linkPopoverSessionRef.current;
9423
+ if (!session) return;
9424
+ if (session.intent === "add-nav") {
9425
+ const trimmed = target.trim();
9426
+ let href = trimmed;
9427
+ let label = "Untitled";
9428
+ if (trimmed) {
9429
+ const { pageRoute, sectionId } = parseTarget(trimmed);
9430
+ const page = resolvePage(pageRoute, sitePages);
9431
+ const pageSections = getSectionsForPath(sectionsByPath, pageRoute);
9432
+ const section = sectionId ? pageSections.find((s) => s.id === sectionId) : void 0;
9433
+ label = section?.label ?? page.title;
9434
+ href = trimmed;
9435
+ }
9436
+ const afterAnchor = addNavAfterAnchorRef.current;
9437
+ const { anchor, hrefKey, labelKey, index, order } = insertNavbarItem(href, label, afterAnchor);
9438
+ applyLinkByKey(hrefKey, href);
9439
+ document.querySelectorAll(`[data-ohw-key="${labelKey}"]`).forEach((el) => {
9440
+ el.textContent = label;
9441
+ });
9442
+ const navCount = String(index + 1);
9443
+ const orderJson = JSON.stringify(order);
9444
+ editContentRef.current = {
9445
+ ...editContentRef.current,
9446
+ [hrefKey]: href,
9447
+ [labelKey]: label,
9448
+ [NAV_COUNT_KEY]: navCount,
9449
+ [NAV_ORDER_KEY]: orderJson
9450
+ };
9451
+ postToParent2({
9452
+ type: "ow:change",
9453
+ nodes: [
9454
+ { key: hrefKey, text: href },
9455
+ { key: labelKey, text: label },
9456
+ { key: NAV_COUNT_KEY, text: navCount },
9457
+ { key: NAV_ORDER_KEY, text: orderJson }
9458
+ ]
9459
+ });
9460
+ postToParent2({ type: "ow:toast", title: "Link added", toastType: "success" });
9461
+ addNavAfterAnchorRef.current = null;
9462
+ setLinkPopover(null);
9463
+ enforceLinkHrefs();
9464
+ requestAnimationFrame(() => {
9465
+ selectRef.current(anchor);
9466
+ });
9467
+ return;
9468
+ }
9469
+ const { key } = session;
8330
9470
  applyLinkByKey(key, target);
8331
- postToParent({ type: "ow:change", nodes: [{ key, text: target }] });
9471
+ postToParent2({ type: "ow:change", nodes: [{ key, text: target }] });
8332
9472
  setLinkPopover(null);
8333
9473
  },
8334
- [linkPopover, postToParent]
9474
+ [postToParent2, sitePages, sectionsByPath]
8335
9475
  );
8336
9476
  const showEditLink = toolbarShowEditLink;
8337
9477
  const currentSections = sectionsByPath[pathname] ?? [];
8338
9478
  linkPopoverOpenRef.current = linkPopover !== null;
8339
- const handleMediaReplace = useCallback2(
9479
+ const handleMediaReplace = useCallback3(
8340
9480
  (key) => {
8341
- postToParent({ type: "ow:image-pick", key, elementType: mediaHover?.elementType ?? "image" });
9481
+ postToParent2({ type: "ow:image-pick", key, elementType: mediaHover?.elementType ?? "image" });
8342
9482
  },
8343
- [postToParent, mediaHover?.elementType]
9483
+ [postToParent2, mediaHover?.elementType]
8344
9484
  );
8345
- const handleMediaFadeOutComplete = useCallback2((key) => {
9485
+ const handleMediaFadeOutComplete = useCallback3((key) => {
8346
9486
  setUploadingRects((prev) => {
8347
9487
  if (!(key in prev)) return prev;
8348
9488
  const next = { ...prev };
@@ -8350,7 +9490,7 @@ function OhhwellsBridge() {
8350
9490
  return next;
8351
9491
  });
8352
9492
  }, []);
8353
- const handleVideoSettingsChange = useCallback2(
9493
+ const handleVideoSettingsChange = useCallback3(
8354
9494
  (key, settings) => {
8355
9495
  document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
8356
9496
  const video = getVideoEl(el);
@@ -8358,7 +9498,7 @@ function OhhwellsBridge() {
8358
9498
  if (typeof settings.autoplay === "boolean") video.autoplay = settings.autoplay;
8359
9499
  if (typeof settings.muted === "boolean") video.muted = settings.muted;
8360
9500
  syncVideoPlayback(video);
8361
- postToParent({
9501
+ postToParent2({
8362
9502
  type: "ow:change",
8363
9503
  nodes: [
8364
9504
  { key: `${key}${VIDEO_AUTOPLAY_SUFFIX}`, text: String(video.autoplay) },
@@ -8370,12 +9510,12 @@ function OhhwellsBridge() {
8370
9510
  );
8371
9511
  });
8372
9512
  },
8373
- [postToParent]
9513
+ [postToParent2]
8374
9514
  );
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(
9515
+ return bridgeRoot ? createPortal2(
9516
+ /* @__PURE__ */ jsxs13(Fragment5, { children: [
9517
+ /* @__PURE__ */ jsx23("div", { ref: attachVisibleViewport, "data-ohw-visible-viewport": "", "aria-hidden": true }),
9518
+ Object.entries(uploadingRects).map(([key, { rect, fadingOut }]) => /* @__PURE__ */ jsx23(
8379
9519
  MediaOverlay,
8380
9520
  {
8381
9521
  hover: { key, rect, elementType: "image", isDragOver: false, hasTextOverlap: false },
@@ -8386,7 +9526,7 @@ function OhhwellsBridge() {
8386
9526
  },
8387
9527
  `uploading-${key}`
8388
9528
  )),
8389
- mediaHover && !(mediaHover.key in uploadingRects) && /* @__PURE__ */ jsx22(
9529
+ mediaHover && !(mediaHover.key in uploadingRects) && /* @__PURE__ */ jsx23(
8390
9530
  MediaOverlay,
8391
9531
  {
8392
9532
  hover: mediaHover,
@@ -8395,31 +9535,40 @@ function OhhwellsBridge() {
8395
9535
  onVideoSettingsChange: handleVideoSettingsChange
8396
9536
  }
8397
9537
  ),
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(
9538
+ siblingHintRect && !isItemDragging && /* @__PURE__ */ jsx23(ItemInteractionLayer, { rect: siblingHintRect, state: "sibling-hint" }),
9539
+ hoveredNavContainerRect && toolbarVariant !== "select-frame" && !linkPopover && !isItemDragging && !siblingHintRect && /* @__PURE__ */ jsx23(ItemInteractionLayer, { rect: hoveredNavContainerRect, state: "hover" }),
9540
+ toolbarVariant === "select-frame" && toolbarRect && !linkPopover && !isItemDragging && /* @__PURE__ */ jsx23(
9541
+ NavbarContainerChrome,
9542
+ {
9543
+ rect: toolbarRect,
9544
+ onAdd: handleAddTopLevelNavItem
9545
+ }
9546
+ ),
9547
+ hoveredItemRect && !hoveredNavContainerRect && !siblingHintRect && !isItemDragging && hoveredItemElRef.current !== selectedElRef.current && /* @__PURE__ */ jsx23(ItemInteractionLayer, { rect: hoveredItemRect, state: "hover" }),
9548
+ toolbarRect && (toolbarVariant === "link-action" || toolbarVariant === "select-frame") && /* @__PURE__ */ jsx23(
8401
9549
  ItemInteractionLayer,
8402
9550
  {
8403
9551
  rect: toolbarRect,
8404
9552
  elRef: glowElRef,
8405
9553
  state: isItemDragging ? "dragging" : resolveItemInteractionState(toolbarRect, parentScrollRef.current),
8406
- showHandle: Boolean(reorderHrefKey),
9554
+ showHandle: toolbarVariant === "link-action" && Boolean(reorderHrefKey),
8407
9555
  dragDisabled: reorderDragDisabled,
8408
9556
  dragHandleLabel: reorderHrefKey ? `Reorder ${reorderHrefKey}` : "Reorder item",
8409
9557
  onDragHandleDragStart: handleItemDragStart,
8410
9558
  onDragHandleDragEnd: handleItemDragEnd,
8411
- toolbar: isItemDragging ? void 0 : /* @__PURE__ */ jsx22(
9559
+ toolbar: toolbarVariant === "link-action" && !isItemDragging ? /* @__PURE__ */ jsx23(
8412
9560
  ItemActionToolbar,
8413
9561
  {
8414
9562
  onEditLink: openLinkPopoverForSelected,
8415
- onAddItem: handleAddNavigationItem,
8416
- addItemDisabled: false
9563
+ addItemDisabled: true,
9564
+ editLinkDisabled: false,
9565
+ moreDisabled: true
8417
9566
  }
8418
- )
9567
+ ) : void 0
8419
9568
  }
8420
9569
  ),
8421
- toolbarRect && toolbarVariant === "rich-text" && /* @__PURE__ */ jsxs12(Fragment4, { children: [
8422
- /* @__PURE__ */ jsx22(
9570
+ toolbarRect && toolbarVariant === "rich-text" && /* @__PURE__ */ jsxs13(Fragment5, { children: [
9571
+ /* @__PURE__ */ jsx23(
8423
9572
  EditGlowChrome,
8424
9573
  {
8425
9574
  rect: toolbarRect,
@@ -8428,7 +9577,7 @@ function OhhwellsBridge() {
8428
9577
  dragDisabled: reorderDragDisabled
8429
9578
  }
8430
9579
  ),
8431
- /* @__PURE__ */ jsx22(
9580
+ /* @__PURE__ */ jsx23(
8432
9581
  FloatingToolbar,
8433
9582
  {
8434
9583
  rect: toolbarRect,
@@ -8441,7 +9590,7 @@ function OhhwellsBridge() {
8441
9590
  }
8442
9591
  )
8443
9592
  ] }),
8444
- maxBadge && /* @__PURE__ */ jsxs12(
9593
+ maxBadge && /* @__PURE__ */ jsxs13(
8445
9594
  "div",
8446
9595
  {
8447
9596
  "data-ohw-max-badge": "",
@@ -8467,7 +9616,7 @@ function OhhwellsBridge() {
8467
9616
  ]
8468
9617
  }
8469
9618
  ),
8470
- toggleState && /* @__PURE__ */ jsx22(
9619
+ toggleState && !linkPopover && /* @__PURE__ */ jsx23(
8471
9620
  StateToggle,
8472
9621
  {
8473
9622
  rect: toggleState.rect,
@@ -8476,15 +9625,15 @@ function OhhwellsBridge() {
8476
9625
  onStateChange: handleStateChange
8477
9626
  }
8478
9627
  ),
8479
- sectionGap && /* @__PURE__ */ jsxs12(
9628
+ sectionGap && !linkPopover && /* @__PURE__ */ jsxs13(
8480
9629
  "div",
8481
9630
  {
8482
9631
  "data-ohw-section-insert-line": "",
8483
9632
  className: "fixed left-0 w-full z-2147483646 flex items-center pointer-events-none",
8484
9633
  style: { top: sectionGap.y, transform: "translateY(-50%)" },
8485
9634
  children: [
8486
- /* @__PURE__ */ jsx22("div", { className: "flex-1 bg-primary", style: { height: 3 } }),
8487
- /* @__PURE__ */ jsx22(
9635
+ /* @__PURE__ */ jsx23("div", { className: "flex-1 bg-primary", style: { height: 3 } }),
9636
+ /* @__PURE__ */ jsx23(
8488
9637
  Badge,
8489
9638
  {
8490
9639
  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 +9646,26 @@ function OhhwellsBridge() {
8497
9646
  children: "Add Section"
8498
9647
  }
8499
9648
  ),
8500
- /* @__PURE__ */ jsx22("div", { className: "flex-1 bg-primary", style: { height: 3 } })
9649
+ /* @__PURE__ */ jsx23("div", { className: "flex-1 bg-primary", style: { height: 3 } })
8501
9650
  ]
8502
9651
  }
8503
9652
  ),
8504
- linkPopover && dialogPortalContainer ? /* @__PURE__ */ jsx22(
9653
+ linkPopover && dialogPortalContainer ? /* @__PURE__ */ jsx23(
8505
9654
  LinkPopover,
8506
9655
  {
8507
9656
  panelRef: linkPopoverPanelRef,
8508
9657
  portalContainer: dialogPortalContainer,
8509
9658
  open: true,
8510
- mode: "edit",
9659
+ mode: linkPopover.mode ?? "edit",
8511
9660
  pages: sitePages,
8512
9661
  sections: currentSections,
8513
9662
  sectionsByPath,
8514
9663
  initialTarget: linkPopover.target,
9664
+ existingTargets: linkPopover.intent === "add-nav" ? getNavbarExistingTargets() : [],
8515
9665
  onClose: closeLinkPopover,
8516
9666
  onSubmit: handleLinkPopoverSubmit
8517
9667
  },
8518
- `${linkPopover.key}-${pathname}`
9668
+ linkPopover.key
8519
9669
  ) : null
8520
9670
  ] }),
8521
9671
  bridgeRoot