@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.cjs CHANGED
@@ -60,15 +60,18 @@ __export(index_exports, {
60
60
  module.exports = __toCommonJS(index_exports);
61
61
 
62
62
  // src/OhhwellsBridge.tsx
63
- var import_react6 = __toESM(require("react"), 1);
63
+ var import_react8 = __toESM(require("react"), 1);
64
64
  var import_client = require("react-dom/client");
65
- var import_react_dom = require("react-dom");
65
+ var import_react_dom2 = require("react-dom");
66
66
 
67
67
  // src/linkHrefStore.ts
68
68
  var linkHrefStore = /* @__PURE__ */ new Map();
69
69
  function setStoredLinkHref(key, href) {
70
70
  linkHrefStore.set(key, href);
71
71
  }
72
+ function getStoredLinkHref(key) {
73
+ return linkHrefStore.get(key);
74
+ }
72
75
  function enforceLinkHrefs() {
73
76
  document.querySelectorAll("[data-ohw-href-key]").forEach((el) => {
74
77
  const key = el.getAttribute("data-ohw-href-key") ?? "";
@@ -4408,6 +4411,7 @@ function ItemActionToolbar({
4408
4411
  onEditLink,
4409
4412
  onAddItem,
4410
4413
  onMore,
4414
+ editLinkDisabled = false,
4411
4415
  addItemDisabled = true,
4412
4416
  moreDisabled = true,
4413
4417
  tooltipSide = "bottom"
@@ -4416,10 +4420,12 @@ function ItemActionToolbar({
4416
4420
  /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
4417
4421
  ToolbarActionTooltip,
4418
4422
  {
4419
- label: "Add link",
4423
+ label: "Edit link",
4420
4424
  side: tooltipSide,
4425
+ disabled: editLinkDisabled,
4421
4426
  buttonProps: {
4422
4427
  onMouseDown: (e) => {
4428
+ if (editLinkDisabled) return;
4423
4429
  e.preventDefault();
4424
4430
  e.stopPropagation();
4425
4431
  onEditLink?.();
@@ -4806,8 +4812,8 @@ function MediaOverlay({
4806
4812
  }
4807
4813
 
4808
4814
  // src/OhhwellsBridge.tsx
4809
- var import_react_dom2 = require("react-dom");
4810
- var import_navigation = require("next/navigation");
4815
+ var import_react_dom3 = require("react-dom");
4816
+ var import_navigation2 = require("next/navigation");
4811
4817
 
4812
4818
  // src/lib/session-search.ts
4813
4819
  var STORAGE_KEY = "ohw-preserved-search";
@@ -4837,6 +4843,7 @@ function parseSectionsFromRoot(root) {
4837
4843
  for (const el of root.querySelectorAll("[data-ohw-section]")) {
4838
4844
  const id = el.getAttribute("data-ohw-section") ?? "";
4839
4845
  if (!id || seen.has(id)) continue;
4846
+ if (el.parentElement?.closest("[data-ohw-section]")) continue;
4840
4847
  seen.add(id);
4841
4848
  const label = el.getAttribute("data-ohw-section-label") ?? titleCaseSectionId(id);
4842
4849
  sections.push({ id, label });
@@ -5083,61 +5090,29 @@ function scrollToHashSectionWhenReady(behavior = "smooth") {
5083
5090
  tick();
5084
5091
  }
5085
5092
 
5093
+ // src/ui/link-modal/LinkPopover.tsx
5094
+ var import_react7 = require("react");
5095
+
5086
5096
  // src/ui/dialog.tsx
5087
5097
  var React7 = __toESM(require("react"), 1);
5088
5098
  var import_radix_ui5 = require("radix-ui");
5089
-
5090
- // src/ui/icons.tsx
5099
+ var import_lucide_react4 = require("lucide-react");
5091
5100
  var import_jsx_runtime12 = require("react/jsx-runtime");
5092
- function IconX({ className, ...props }) {
5093
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, ...props, children: [
5094
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("path", { d: "M18 6 6 18" }),
5095
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("path", { d: "m6 6 12 12" })
5096
- ] });
5097
- }
5098
- function IconChevronDown({ className, ...props }) {
5099
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, ...props, children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("path", { d: "m6 9 6 6 6-6" }) });
5100
- }
5101
- function IconFile({ className, ...props }) {
5102
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, ...props, children: [
5103
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("path", { d: "M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z" }),
5104
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("path", { d: "M14 2v4a2 2 0 0 0 2 2h4" })
5105
- ] });
5106
- }
5107
- function IconInfo({ className, ...props }) {
5108
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, ...props, children: [
5109
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("circle", { cx: "12", cy: "12", r: "10" }),
5110
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("path", { d: "M12 16v-4" }),
5111
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("path", { d: "M12 8h.01" })
5112
- ] });
5113
- }
5114
- function IconArrowRight({ className, ...props }) {
5115
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, ...props, children: [
5116
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("path", { d: "M5 12h14" }),
5117
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("path", { d: "m12 5 7 7-7 7" })
5118
- ] });
5119
- }
5120
- function IconSection({ className, ...props }) {
5121
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, ...props, children: [
5122
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("rect", { width: "18", height: "7", x: "3", y: "3", rx: "1" }),
5123
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("rect", { width: "9", height: "7", x: "3", y: "14", rx: "1" }),
5124
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("rect", { width: "5", height: "7", x: "16", y: "14", rx: "1" })
5125
- ] });
5126
- }
5127
-
5128
- // src/ui/dialog.tsx
5129
- var import_jsx_runtime13 = require("react/jsx-runtime");
5130
- function Dialog2({ ...props }) {
5131
- return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_radix_ui5.Dialog.Root, { "data-slot": "dialog", ...props });
5101
+ function Dialog2({
5102
+ ...props
5103
+ }) {
5104
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_radix_ui5.Dialog.Root, { "data-slot": "dialog", ...props });
5132
5105
  }
5133
- function DialogPortal({ ...props }) {
5134
- return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_radix_ui5.Dialog.Portal, { ...props });
5106
+ function DialogPortal({
5107
+ ...props
5108
+ }) {
5109
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_radix_ui5.Dialog.Portal, { ...props });
5135
5110
  }
5136
5111
  function DialogOverlay({
5137
5112
  className,
5138
5113
  ...props
5139
5114
  }) {
5140
- return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
5115
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
5141
5116
  import_radix_ui5.Dialog.Overlay,
5142
5117
  {
5143
5118
  "data-slot": "dialog-overlay",
@@ -5147,57 +5122,74 @@ function DialogOverlay({
5147
5122
  }
5148
5123
  );
5149
5124
  }
5150
- var DialogContent = React7.forwardRef(({ className, children, showCloseButton = true, container, ...props }, ref) => {
5151
- const positionMode = container ? "absolute" : "fixed";
5152
- return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(DialogPortal, { container: container ?? void 0, children: [
5153
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(DialogOverlay, { className: cn(positionMode, "inset-0") }),
5154
- /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
5155
- import_radix_ui5.Dialog.Content,
5156
- {
5157
- ref,
5158
- "data-slot": "dialog-content",
5159
- className: cn(
5160
- positionMode,
5161
- "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",
5162
- "rounded-xl border border-border bg-background font-sans shadow-lg outline-none",
5163
- container ? "max-h-[calc(100%-32px)] max-w-[calc(100%-16px)]" : "max-h-[calc(100vh-32px)] max-w-[calc(100vw-16px)]",
5164
- className
5165
- ),
5166
- onMouseDown: (e) => e.stopPropagation(),
5167
- ...props,
5168
- children: [
5169
- children,
5170
- showCloseButton ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
5171
- import_radix_ui5.Dialog.Close,
5172
- {
5173
- type: "button",
5174
- className: "absolute right-[9px] top-[9px] rounded-sm p-1.5 text-foreground hover:bg-muted/50",
5175
- "aria-label": "Close",
5176
- children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(IconX, { "aria-hidden": true })
5177
- }
5178
- ) : null
5179
- ]
5180
- }
5181
- )
5182
- ] });
5183
- });
5125
+ var DialogContent = React7.forwardRef(
5126
+ ({ className, children, showCloseButton = true, container, ...props }, ref) => {
5127
+ const positionMode = container ? "absolute" : "fixed";
5128
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(DialogPortal, { container: container ?? void 0, children: [
5129
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(DialogOverlay, { className: cn(positionMode, "inset-0") }),
5130
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
5131
+ import_radix_ui5.Dialog.Content,
5132
+ {
5133
+ ref,
5134
+ "data-slot": "dialog-content",
5135
+ className: cn(
5136
+ positionMode,
5137
+ "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",
5138
+ "rounded-xl border border-border bg-background font-sans shadow-lg outline-none",
5139
+ container ? "max-h-[calc(100%-32px)]" : "max-h-[calc(100vh-32px)]",
5140
+ className
5141
+ ),
5142
+ onMouseDown: (e) => e.stopPropagation(),
5143
+ ...props,
5144
+ children: [
5145
+ children,
5146
+ showCloseButton ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
5147
+ import_radix_ui5.Dialog.Close,
5148
+ {
5149
+ type: "button",
5150
+ className: "absolute right-[9px] top-[9px] rounded-sm p-1.5 text-foreground hover:bg-muted/50",
5151
+ "aria-label": "Close",
5152
+ children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_lucide_react4.X, { size: 16, "aria-hidden": true })
5153
+ }
5154
+ ) : null
5155
+ ]
5156
+ }
5157
+ )
5158
+ ] });
5159
+ }
5160
+ );
5184
5161
  DialogContent.displayName = import_radix_ui5.Dialog.Content.displayName;
5185
- function DialogHeader({ className, ...props }) {
5186
- return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: cn("flex flex-col gap-1.5", className), ...props });
5162
+ function DialogHeader({
5163
+ className,
5164
+ ...props
5165
+ }) {
5166
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: cn("flex flex-col gap-1.5", className), ...props });
5187
5167
  }
5188
- function DialogFooter({ className, ...props }) {
5189
- return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: cn("flex items-center justify-end gap-2", className), ...props });
5168
+ function DialogFooter({
5169
+ className,
5170
+ ...props
5171
+ }) {
5172
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
5173
+ "div",
5174
+ {
5175
+ className: cn("flex items-center justify-end gap-2", className),
5176
+ ...props
5177
+ }
5178
+ );
5190
5179
  }
5191
- var DialogTitle = React7.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
5180
+ var DialogTitle = React7.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
5192
5181
  import_radix_ui5.Dialog.Title,
5193
5182
  {
5194
5183
  ref,
5195
- className: cn("text-lg font-semibold leading-none tracking-tight text-card-foreground", className),
5184
+ className: cn(
5185
+ "text-lg font-semibold leading-none tracking-tight text-card-foreground",
5186
+ className
5187
+ ),
5196
5188
  ...props
5197
5189
  }
5198
5190
  ));
5199
5191
  DialogTitle.displayName = import_radix_ui5.Dialog.Title.displayName;
5200
- var DialogDescription = React7.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
5192
+ var DialogDescription = React7.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
5201
5193
  import_radix_ui5.Dialog.Description,
5202
5194
  {
5203
5195
  ref,
@@ -5208,38 +5200,64 @@ var DialogDescription = React7.forwardRef(({ className, ...props }, ref) => /* @
5208
5200
  DialogDescription.displayName = import_radix_ui5.Dialog.Description.displayName;
5209
5201
  var DialogClose = import_radix_ui5.Dialog.Close;
5210
5202
 
5203
+ // src/ui/link-modal/LinkEditorPanel.tsx
5204
+ var import_lucide_react8 = require("lucide-react");
5205
+
5211
5206
  // src/ui/link-modal/DestinationBreadcrumb.tsx
5212
- var import_jsx_runtime14 = require("react/jsx-runtime");
5213
- function DestinationBreadcrumb({ pageTitle, sectionLabel }) {
5214
- return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "flex w-full flex-col gap-2", children: [
5215
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("p", { className: "text-sm font-medium text-foreground", children: "Destination" }),
5216
- /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "flex items-center gap-3", children: [
5217
- /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "flex items-center gap-2", children: [
5218
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(IconFile, { className: "shrink-0 text-foreground", "aria-hidden": true }),
5219
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "text-sm font-medium leading-none text-foreground", children: pageTitle })
5207
+ var import_lucide_react5 = require("lucide-react");
5208
+ var import_jsx_runtime13 = require("react/jsx-runtime");
5209
+ function DestinationBreadcrumb({
5210
+ pageTitle,
5211
+ sectionLabel
5212
+ }) {
5213
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex w-full flex-col gap-2", children: [
5214
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("p", { className: "text-sm font-medium! text-foreground m-0", children: "Destination" }),
5215
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex items-center gap-3", children: [
5216
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex items-center gap-2", children: [
5217
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_lucide_react5.File, { size: 16, className: "shrink-0 text-foreground", "aria-hidden": true }),
5218
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "text-sm font-medium leading-none text-foreground!", children: pageTitle })
5220
5219
  ] }),
5221
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(IconArrowRight, { className: "shrink-0 text-muted-foreground", "aria-hidden": true }),
5222
- /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "flex min-w-0 flex-1 items-center gap-2", children: [
5223
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(IconSection, { className: "shrink-0 text-foreground", "aria-hidden": true }),
5224
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "truncate text-sm font-medium leading-none text-foreground", children: sectionLabel })
5220
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
5221
+ import_lucide_react5.ArrowRight,
5222
+ {
5223
+ size: 16,
5224
+ className: "shrink-0 text-muted-foreground",
5225
+ "aria-hidden": true
5226
+ }
5227
+ ),
5228
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex min-w-0 flex-1 items-center gap-2", children: [
5229
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
5230
+ import_lucide_react5.GalleryVertical,
5231
+ {
5232
+ size: 16,
5233
+ className: "shrink-0 text-foreground",
5234
+ "aria-hidden": true
5235
+ }
5236
+ ),
5237
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "truncate text-sm font-medium leading-none text-foreground", children: sectionLabel })
5225
5238
  ] })
5226
5239
  ] })
5227
5240
  ] });
5228
5241
  }
5229
5242
 
5230
5243
  // src/ui/link-modal/SectionTreeItem.tsx
5231
- var import_jsx_runtime15 = require("react/jsx-runtime");
5232
- function SectionTreeItem({ section, onSelect, selected }) {
5244
+ var import_lucide_react6 = require("lucide-react");
5245
+ var import_jsx_runtime14 = require("react/jsx-runtime");
5246
+ function SectionTreeItem({
5247
+ section,
5248
+ onSelect,
5249
+ selected
5250
+ }) {
5233
5251
  const interactive = Boolean(onSelect);
5234
- return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "flex h-9 w-full items-end pl-3", children: [
5235
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
5252
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "flex h-9 w-full items-end pl-3", children: [
5253
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
5236
5254
  "div",
5237
5255
  {
5238
5256
  className: "mr-[-1px] h-9 w-2 shrink-0 rounded-bl-sm border-b border-l border-border mb-4",
5239
5257
  "aria-hidden": true
5240
5258
  }
5241
5259
  ),
5242
- /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
5260
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(
5243
5261
  "div",
5244
5262
  {
5245
5263
  role: interactive ? "button" : void 0,
@@ -5257,32 +5275,30 @@ function SectionTreeItem({ section, onSelect, selected }) {
5257
5275
  interactive && selected && "border-primary"
5258
5276
  ),
5259
5277
  children: [
5260
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(IconSection, { className: "shrink-0 text-foreground", "aria-hidden": true }),
5261
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "truncate text-sm font-normal leading-5 text-foreground", children: section.label })
5278
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
5279
+ import_lucide_react6.GalleryVertical,
5280
+ {
5281
+ size: 16,
5282
+ className: "shrink-0 text-foreground",
5283
+ "aria-hidden": true
5284
+ }
5285
+ ),
5286
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "truncate text-sm font-normal leading-5 text-foreground", children: section.label })
5262
5287
  ]
5263
5288
  }
5264
5289
  )
5265
5290
  ] });
5266
5291
  }
5267
- function SectionPickerList({ sections, onSelect }) {
5268
- if (sections.length === 0) {
5269
- return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("p", { className: "text-sm text-muted-foreground", children: "No sections found on this page." });
5270
- }
5271
- return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "flex flex-col gap-1", children: [
5272
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("p", { className: "text-sm text-muted-foreground", children: "Pick a section this link should scroll to." }),
5273
- sections.map((section) => /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(SectionTreeItem, { section, onSelect }, section.id))
5274
- ] });
5275
- }
5276
5292
 
5277
5293
  // src/ui/link-modal/UrlOrPageInput.tsx
5278
5294
  var import_react4 = require("react");
5279
5295
 
5280
5296
  // src/ui/input.tsx
5281
5297
  var React8 = __toESM(require("react"), 1);
5282
- var import_jsx_runtime16 = require("react/jsx-runtime");
5298
+ var import_jsx_runtime15 = require("react/jsx-runtime");
5283
5299
  var Input = React8.forwardRef(
5284
5300
  ({ className, type, ...props }, ref) => {
5285
- return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
5301
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
5286
5302
  "input",
5287
5303
  {
5288
5304
  type,
@@ -5301,9 +5317,9 @@ Input.displayName = "Input";
5301
5317
 
5302
5318
  // src/ui/label.tsx
5303
5319
  var import_radix_ui6 = require("radix-ui");
5304
- var import_jsx_runtime17 = require("react/jsx-runtime");
5320
+ var import_jsx_runtime16 = require("react/jsx-runtime");
5305
5321
  function Label({ className, ...props }) {
5306
- return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
5322
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
5307
5323
  import_radix_ui6.Label.Root,
5308
5324
  {
5309
5325
  "data-slot": "label",
@@ -5314,9 +5330,12 @@ function Label({ className, ...props }) {
5314
5330
  }
5315
5331
 
5316
5332
  // src/ui/link-modal/UrlOrPageInput.tsx
5317
- var import_jsx_runtime18 = require("react/jsx-runtime");
5318
- function FieldChevron({ onClick }) {
5319
- return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
5333
+ var import_lucide_react7 = require("lucide-react");
5334
+ var import_jsx_runtime17 = require("react/jsx-runtime");
5335
+ function FieldChevron({
5336
+ onClick
5337
+ }) {
5338
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
5320
5339
  "button",
5321
5340
  {
5322
5341
  type: "button",
@@ -5324,7 +5343,7 @@ function FieldChevron({ onClick }) {
5324
5343
  onClick,
5325
5344
  "aria-label": "Open page list",
5326
5345
  tabIndex: -1,
5327
- children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(IconChevronDown, {})
5346
+ children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_lucide_react7.ChevronDown, { size: 16 })
5328
5347
  }
5329
5348
  );
5330
5349
  }
@@ -5342,7 +5361,29 @@ function UrlOrPageInput({
5342
5361
  }) {
5343
5362
  const inputId = (0, import_react4.useId)();
5344
5363
  const inputRef = (0, import_react4.useRef)(null);
5364
+ const rootRef = (0, import_react4.useRef)(null);
5345
5365
  const [isFocused, setIsFocused] = (0, import_react4.useState)(false);
5366
+ (0, import_react4.useEffect)(() => {
5367
+ if (!dropdownOpen) return;
5368
+ const isOutside = (e) => {
5369
+ const root = rootRef.current;
5370
+ if (!root) return true;
5371
+ const path = typeof e.composedPath === "function" ? e.composedPath() : [];
5372
+ if (path.includes(root)) return false;
5373
+ const target = e.target;
5374
+ return !(target instanceof Node && root.contains(target));
5375
+ };
5376
+ const closeIfOutside = (e) => {
5377
+ if (!isOutside(e)) return;
5378
+ onDropdownOpenChange(false);
5379
+ };
5380
+ document.addEventListener("pointerdown", closeIfOutside, true);
5381
+ document.addEventListener("mousedown", closeIfOutside, true);
5382
+ return () => {
5383
+ document.removeEventListener("pointerdown", closeIfOutside, true);
5384
+ document.removeEventListener("mousedown", closeIfOutside, true);
5385
+ };
5386
+ }, [dropdownOpen, onDropdownOpenChange]);
5346
5387
  const openDropdown = () => {
5347
5388
  if (readOnly || filteredPages.length === 0) return;
5348
5389
  onDropdownOpenChange(true);
@@ -5360,15 +5401,22 @@ function UrlOrPageInput({
5360
5401
  requestAnimationFrame(() => inputRef.current?.focus());
5361
5402
  };
5362
5403
  const fieldClassName = cn(
5363
- "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]",
5404
+ "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]",
5364
5405
  urlError ? "border-destructive shadow-[0_0_0_1px_var(--ohw-destructive)]" : isFocused ? "border-primary shadow-[0_0_0_1px_var(--ohw-primary)]" : "border-input"
5365
5406
  );
5366
- return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "flex w-full flex-col gap-2 p-0", children: [
5367
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(Label, { htmlFor: inputId, className: cn(urlError && "text-destructive"), children: "Destination" }),
5368
- /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "relative w-full", children: [
5369
- /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { "data-ohw-link-field": true, className: fieldClassName, children: [
5370
- selectedPage ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { className: "flex shrink-0 items-center pr-2", children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(IconFile, { className: "text-foreground", "aria-hidden": true }) }) : null,
5371
- readOnly ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "min-w-0 flex-1 truncate text-sm leading-5 text-foreground", children: selectedPage?.title ?? value }) : /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
5407
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "flex w-full flex-col gap-2 p-0", children: [
5408
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(Label, { htmlFor: inputId, className: cn(urlError && "text-destructive"), children: "Destination" }),
5409
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { ref: rootRef, className: "relative w-full", children: [
5410
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { "data-ohw-link-field": true, className: fieldClassName, children: [
5411
+ selectedPage ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { className: "flex shrink-0 items-center pr-2", children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
5412
+ import_lucide_react7.File,
5413
+ {
5414
+ size: 16,
5415
+ className: "shrink-0 text-foreground",
5416
+ "aria-hidden": true
5417
+ }
5418
+ ) }) : null,
5419
+ readOnly ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { className: "min-w-0 flex-1 truncate text-sm leading-5 text-foreground", children: selectedPage?.title ?? value }) : /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
5372
5420
  Input,
5373
5421
  {
5374
5422
  ref: inputRef,
@@ -5379,7 +5427,14 @@ function UrlOrPageInput({
5379
5427
  setIsFocused(true);
5380
5428
  if (!selectedPage) openDropdown();
5381
5429
  },
5382
- onBlur: () => setIsFocused(false),
5430
+ onBlur: () => {
5431
+ setIsFocused(false);
5432
+ requestAnimationFrame(() => {
5433
+ if (!rootRef.current?.contains(document.activeElement)) {
5434
+ onDropdownOpenChange(false);
5435
+ }
5436
+ });
5437
+ },
5383
5438
  placeholder,
5384
5439
  className: cn(
5385
5440
  "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",
@@ -5387,7 +5442,7 @@ function UrlOrPageInput({
5387
5442
  )
5388
5443
  }
5389
5444
  ),
5390
- selectedPage && !readOnly ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
5445
+ selectedPage && !readOnly ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
5391
5446
  "button",
5392
5447
  {
5393
5448
  type: "button",
@@ -5395,26 +5450,26 @@ function UrlOrPageInput({
5395
5450
  onMouseDown: clearSelection,
5396
5451
  "aria-label": "Clear selected page",
5397
5452
  tabIndex: -1,
5398
- children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(IconX, { "aria-hidden": true })
5453
+ children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_lucide_react7.X, { size: 16, "aria-hidden": true })
5399
5454
  }
5400
5455
  ) : null,
5401
- !readOnly ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(FieldChevron, { onClick: toggleDropdown }) : null
5456
+ !readOnly ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(FieldChevron, { onClick: toggleDropdown }) : null
5402
5457
  ] }),
5403
- dropdownOpen && !readOnly && filteredPages.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
5458
+ dropdownOpen && !readOnly && filteredPages.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
5404
5459
  "div",
5405
5460
  {
5406
5461
  "data-ohw-link-page-dropdown": "",
5407
- 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",
5462
+ 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",
5408
5463
  onMouseDown: (e) => e.preventDefault(),
5409
- children: filteredPages.map((page) => /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(
5464
+ children: filteredPages.map((page) => /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
5410
5465
  "button",
5411
5466
  {
5412
5467
  type: "button",
5413
5468
  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",
5414
5469
  onClick: () => onPageSelect(page),
5415
5470
  children: [
5416
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(IconFile, { className: "shrink-0", "aria-hidden": true }),
5417
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "truncate", children: page.title })
5471
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_lucide_react7.File, { size: 16, className: "shrink-0", "aria-hidden": true }),
5472
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { className: "truncate", children: page.title })
5418
5473
  ]
5419
5474
  },
5420
5475
  page.path
@@ -5422,108 +5477,539 @@ function UrlOrPageInput({
5422
5477
  }
5423
5478
  ) : null
5424
5479
  ] }),
5425
- urlError ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("p", { className: "text-sm font-medium text-destructive", children: urlError }) : null
5480
+ urlError ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("p", { className: "text-sm font-medium text-destructive", children: urlError }) : null
5426
5481
  ] });
5427
5482
  }
5428
5483
 
5429
- // src/ui/link-modal/useLinkModalState.ts
5484
+ // src/ui/link-modal/LinkEditorPanel.tsx
5485
+ var import_jsx_runtime18 = require("react/jsx-runtime");
5486
+ function LinkEditorPanel({ state, onClose }) {
5487
+ const isCancel = state.secondaryLabel === "Cancel" || state.secondaryLabel === "Back to sections";
5488
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(import_jsx_runtime18.Fragment, { children: [
5489
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(DialogClose, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
5490
+ "button",
5491
+ {
5492
+ type: "button",
5493
+ className: "absolute right-[9px] top-[9px] rounded-sm p-1.5 text-foreground hover:bg-muted/50 h-7",
5494
+ "aria-label": "Close",
5495
+ onClick: onClose,
5496
+ children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_lucide_react8.X, { size: 16, "aria-hidden": true })
5497
+ }
5498
+ ) }),
5499
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(DialogHeader, { className: "w-full gap-1.5 p-6 pr-12", children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(DialogTitle, { className: "m-0 w-full break-words", children: state.title }) }),
5500
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "flex w-full flex-col gap-3 px-6 pb-8 pt-1", children: [
5501
+ state.showBreadcrumb && state.selectedPage && state.selectedSection ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
5502
+ DestinationBreadcrumb,
5503
+ {
5504
+ pageTitle: state.selectedPage.title,
5505
+ sectionLabel: state.selectedSection.label
5506
+ }
5507
+ ) : /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
5508
+ UrlOrPageInput,
5509
+ {
5510
+ value: state.searchValue,
5511
+ selectedPage: state.selectedPage,
5512
+ dropdownOpen: state.dropdownOpen,
5513
+ onDropdownOpenChange: state.setDropdownOpen,
5514
+ filteredPages: state.filteredPages,
5515
+ onInputChange: state.handleInputChange,
5516
+ onPageSelect: state.handlePageSelect,
5517
+ urlError: state.urlError
5518
+ }
5519
+ ),
5520
+ state.showChooseSection ? /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "flex flex-col justify-center gap-2", children: [
5521
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
5522
+ Button,
5523
+ {
5524
+ type: "button",
5525
+ variant: "outline",
5526
+ className: "w-fit cursor-pointer",
5527
+ size: "sm",
5528
+ onClick: state.handleChooseSection,
5529
+ children: "Choose a section"
5530
+ }
5531
+ ),
5532
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "flex items-center gap-1 text-sm text-muted-foreground", children: [
5533
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_lucide_react8.Info, { size: 16, className: "shrink-0", "aria-hidden": true }),
5534
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { children: "Pick a section this link should scroll to." })
5535
+ ] })
5536
+ ] }) : null,
5537
+ state.showSectionRow && state.selectedSection ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(SectionTreeItem, { section: state.selectedSection, selected: true }) : null
5538
+ ] }),
5539
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(DialogFooter, { className: "w-full px-6 pb-6", children: [
5540
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
5541
+ Button,
5542
+ {
5543
+ type: "button",
5544
+ variant: isCancel ? "outline" : "ghost",
5545
+ className: "leading-6 shadow-none cursor-pointer",
5546
+ style: isCancel ? {
5547
+ backgroundColor: "var(--ohw-background)",
5548
+ borderColor: "var(--ohw-border)",
5549
+ color: "var(--ohw-foreground)"
5550
+ } : void 0,
5551
+ onClick: state.handleSecondary,
5552
+ children: state.secondaryLabel
5553
+ }
5554
+ ),
5555
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
5556
+ Button,
5557
+ {
5558
+ type: "button",
5559
+ className: "border-0 leading-6 shadow-none hover:opacity-90 disabled:opacity-50 cursor-pointer",
5560
+ style: {
5561
+ backgroundColor: "var(--ohw-primary)",
5562
+ color: "var(--ohw-primary-foreground)"
5563
+ },
5564
+ disabled: !state.isValid,
5565
+ onClick: state.handleSubmit,
5566
+ children: state.submitLabel
5567
+ }
5568
+ )
5569
+ ] })
5570
+ ] });
5571
+ }
5572
+
5573
+ // src/ui/link-modal/SectionPickerOverlay.tsx
5430
5574
  var import_react5 = require("react");
5431
- function useLinkModalState({
5432
- open,
5433
- mode,
5434
- pages,
5435
- sections: _sections,
5436
- sectionsByPath,
5437
- initialTarget,
5438
- existingTargets,
5439
- onClose,
5440
- onSubmit
5441
- }) {
5442
- const availablePages = (0, import_react5.useMemo)(
5443
- () => mode === "create" ? filterAvailablePages(pages, existingTargets) : pages,
5444
- [mode, pages, existingTargets]
5575
+ var import_react_dom = require("react-dom");
5576
+ var import_lucide_react9 = require("lucide-react");
5577
+ var import_navigation = require("next/navigation");
5578
+ var import_jsx_runtime19 = require("react/jsx-runtime");
5579
+ var DIM_OVERLAY = "rgba(0, 0, 0, 0.45)";
5580
+ function rectsEqual(a, b) {
5581
+ if (a.size !== b.size) return false;
5582
+ for (const [id, rect] of a) {
5583
+ const other = b.get(id);
5584
+ if (!other || rect.top !== other.top || rect.left !== other.left || rect.width !== other.width || rect.height !== other.height) {
5585
+ return false;
5586
+ }
5587
+ }
5588
+ return true;
5589
+ }
5590
+ function readSectionRects(sectionIds) {
5591
+ const next = /* @__PURE__ */ new Map();
5592
+ for (const id of sectionIds) {
5593
+ const el = document.querySelector(
5594
+ `[data-ohw-section="${CSS.escape(id)}"]`
5595
+ );
5596
+ if (el) next.set(id, el.getBoundingClientRect());
5597
+ }
5598
+ return next;
5599
+ }
5600
+ function hitTestSectionId(x, y, sectionIds, rects) {
5601
+ for (const id of sectionIds) {
5602
+ const rect = rects.get(id);
5603
+ if (!rect || rect.width <= 0 || rect.height <= 0) continue;
5604
+ if (x >= rect.left && x <= rect.right && y >= rect.top && y <= rect.bottom) {
5605
+ return id;
5606
+ }
5607
+ }
5608
+ return null;
5609
+ }
5610
+ function useSectionRects(sectionIdsKey, enabled) {
5611
+ const [rects, setRects] = (0, import_react5.useState)(/* @__PURE__ */ new Map());
5612
+ const sectionIds = (0, import_react5.useMemo)(
5613
+ () => sectionIdsKey ? sectionIdsKey.split("\0") : [],
5614
+ [sectionIdsKey]
5445
5615
  );
5446
- const [searchValue, setSearchValue] = (0, import_react5.useState)("");
5447
- const [selectedPage, setSelectedPage] = (0, import_react5.useState)(null);
5448
- const [selectedSection, setSelectedSection] = (0, import_react5.useState)(null);
5449
- const [step, setStep] = (0, import_react5.useState)("input");
5450
- const [dropdownOpen, setDropdownOpen] = (0, import_react5.useState)(false);
5451
- const [urlError, setUrlError] = (0, import_react5.useState)("");
5452
- const reset = (0, import_react5.useCallback)(() => {
5453
- setSearchValue("");
5454
- setSelectedPage(null);
5455
- setSelectedSection(null);
5456
- setStep("input");
5457
- setDropdownOpen(false);
5458
- setUrlError("");
5459
- }, []);
5460
5616
  (0, import_react5.useEffect)(() => {
5461
- if (!open) return;
5462
- if (mode === "edit" && initialTarget) {
5463
- const { pageRoute } = parseTarget(initialTarget);
5464
- const targetSections = getSectionsForPath(sectionsByPath, pageRoute);
5465
- const init = getEditModeInitialState(initialTarget, pages, targetSections);
5466
- setSearchValue(init.searchValue);
5467
- setSelectedPage(init.selectedPage);
5468
- setSelectedSection(init.selectedSection);
5469
- setStep(init.step);
5470
- } else {
5471
- reset();
5617
+ if (!enabled || sectionIds.length === 0) {
5618
+ setRects((prev) => prev.size === 0 ? prev : /* @__PURE__ */ new Map());
5619
+ return;
5472
5620
  }
5473
- setDropdownOpen(false);
5474
- setUrlError("");
5475
- }, [open, mode, initialTarget, pages, sectionsByPath, reset]);
5476
- const filteredPages = (0, import_react5.useMemo)(() => {
5477
- if (!searchValue.trim()) return availablePages;
5478
- return availablePages.filter((p) => p.title.toLowerCase().startsWith(searchValue.toLowerCase()));
5479
- }, [availablePages, searchValue]);
5480
- const activeSections = (0, import_react5.useMemo)(() => {
5481
- if (!selectedPage) return [];
5482
- return getSectionsForPath(sectionsByPath, selectedPage.path);
5483
- }, [selectedPage, sectionsByPath]);
5484
- const showBreadcrumb = step === "confirmed" && selectedPage && selectedSection;
5485
- const showSectionPicker = step === "sectionPicker" && selectedPage;
5486
- const showSectionRow = mode === "edit" && selectedPage && selectedSection && step === "input";
5487
- const showChooseSection = step === "input" && selectedPage && !selectedSection && !showSectionRow && activeSections.length > 0;
5488
- const handleInputChange = (value) => {
5489
- setSearchValue(value);
5490
- setSelectedPage(null);
5491
- setSelectedSection(null);
5492
- setStep("input");
5493
- setUrlError("");
5494
- const filtered = value.trim() ? availablePages.filter((p) => p.title.toLowerCase().startsWith(value.toLowerCase())) : availablePages;
5495
- setDropdownOpen(filtered.length > 0);
5496
- if (value.trim()) {
5497
- setUrlError(validateUrlInput(value, availablePages, pages));
5621
+ const update = () => {
5622
+ const next = readSectionRects(sectionIds);
5623
+ setRects((prev) => rectsEqual(prev, next) ? prev : next);
5624
+ };
5625
+ update();
5626
+ const opts = { capture: true, passive: true };
5627
+ window.addEventListener("scroll", update, opts);
5628
+ window.addEventListener("resize", update);
5629
+ const vv = window.visualViewport;
5630
+ vv?.addEventListener("resize", update);
5631
+ vv?.addEventListener("scroll", update);
5632
+ const ro = new ResizeObserver(update);
5633
+ for (const id of sectionIds) {
5634
+ const el = document.querySelector(
5635
+ `[data-ohw-section="${CSS.escape(id)}"]`
5636
+ );
5637
+ if (el) ro.observe(el);
5498
5638
  }
5499
- };
5500
- const handlePageSelect = (page) => {
5501
- setSelectedPage({ path: page.path, title: page.title });
5502
- setSelectedSection(null);
5503
- setSearchValue(page.title);
5504
- setStep("input");
5505
- setDropdownOpen(false);
5506
- setUrlError("");
5507
- };
5508
- const handleChooseSection = () => {
5509
- setStep("sectionPicker");
5510
- };
5511
- const handleSectionSelect = (section) => {
5512
- setSelectedSection({ id: section.id, label: section.label });
5513
- if (mode === "create") {
5514
- setStep("confirmed");
5515
- } else {
5639
+ return () => {
5640
+ window.removeEventListener("scroll", update, opts);
5641
+ window.removeEventListener("resize", update);
5642
+ vv?.removeEventListener("resize", update);
5643
+ vv?.removeEventListener("scroll", update);
5644
+ ro.disconnect();
5645
+ };
5646
+ }, [sectionIds, enabled]);
5647
+ return rects;
5648
+ }
5649
+ function SectionPickerOverlay({
5650
+ pagePath,
5651
+ sections,
5652
+ onBack,
5653
+ onSelect
5654
+ }) {
5655
+ const router = (0, import_navigation.useRouter)();
5656
+ const pathname = (0, import_navigation.usePathname)();
5657
+ const [selectedId, setSelectedId] = (0, import_react5.useState)(null);
5658
+ const [hoveredId, setHoveredId] = (0, import_react5.useState)(null);
5659
+ const pointerRef = (0, import_react5.useRef)(null);
5660
+ const pointerScreenRef = (0, import_react5.useRef)(null);
5661
+ const iframeOffsetTopRef = (0, import_react5.useRef)(null);
5662
+ const sectionIdsRef = (0, import_react5.useRef)([]);
5663
+ const [chromeClip, setChromeClip] = (0, import_react5.useState)(
5664
+ () => ({
5665
+ top: 0,
5666
+ bottom: typeof window !== "undefined" ? window.innerHeight : 0
5667
+ })
5668
+ );
5669
+ const normalizedTarget = normalizePath(pagePath);
5670
+ const isOnTargetPage = normalizePath(pathname) === normalizedTarget;
5671
+ (0, import_react5.useEffect)(() => {
5672
+ if (isOnTargetPage) return;
5673
+ const search = readPreservedSearch();
5674
+ router.push(`${normalizedTarget}${search}`);
5675
+ }, [isOnTargetPage, normalizedTarget, router]);
5676
+ (0, import_react5.useEffect)(() => {
5677
+ document.documentElement.setAttribute("data-ohw-section-picking", "");
5678
+ document.querySelectorAll("[data-ohw-hovered]").forEach((el) => {
5679
+ el.removeAttribute("data-ohw-hovered");
5680
+ });
5681
+ document.querySelectorAll("[data-ohw-state-hovered]").forEach((el) => {
5682
+ el.removeAttribute("data-ohw-state-hovered");
5683
+ });
5684
+ return () => {
5685
+ document.documentElement.removeAttribute("data-ohw-section-picking");
5686
+ };
5687
+ }, []);
5688
+ const liveSections = (0, import_react5.useMemo)(() => {
5689
+ if (!isOnTargetPage) return sections;
5690
+ const live = collectSectionsFromDom();
5691
+ if (live.length === 0) return sections;
5692
+ const labels = new Map(sections.map((s) => [s.id, s.label]));
5693
+ return live.map((s) => ({ id: s.id, label: labels.get(s.id) ?? s.label }));
5694
+ }, [isOnTargetPage, sections, pathname]);
5695
+ const sectionIdsKey = (0, import_react5.useMemo)(
5696
+ () => liveSections.map((s) => s.id).join("\0"),
5697
+ [liveSections]
5698
+ );
5699
+ const sectionIds = (0, import_react5.useMemo)(
5700
+ () => liveSections.map((s) => s.id),
5701
+ [liveSections]
5702
+ );
5703
+ sectionIdsRef.current = sectionIds;
5704
+ const rects = useSectionRects(sectionIdsKey, isOnTargetPage);
5705
+ const applyHoverAt = (0, import_react5.useCallback)(
5706
+ (x, y, liveRects) => {
5707
+ const ids = sectionIdsRef.current;
5708
+ const map = liveRects ?? readSectionRects(ids);
5709
+ const next = hitTestSectionId(x, y, ids, map);
5710
+ setHoveredId((prev) => prev === next ? prev : next);
5711
+ },
5712
+ []
5713
+ );
5714
+ (0, import_react5.useEffect)(() => {
5715
+ const applyClip = (iframeOffsetTop, visibleCanvasTop, canvasH) => {
5716
+ const top = Math.max(0, visibleCanvasTop - iframeOffsetTop);
5717
+ const bottom = Math.min(
5718
+ window.innerHeight,
5719
+ visibleCanvasTop + canvasH - iframeOffsetTop
5720
+ );
5721
+ setChromeClip(
5722
+ (prev) => prev.top === top && prev.bottom === bottom ? prev : { top, bottom: Math.max(top, bottom) }
5723
+ );
5724
+ };
5725
+ const onParentScroll = (e) => {
5726
+ if (e.data?.type !== "ow:parent-scroll") return;
5727
+ const { iframeOffsetTop, headerH, canvasH } = e.data;
5728
+ applyClip(iframeOffsetTop, headerH, canvasH);
5729
+ iframeOffsetTopRef.current = iframeOffsetTop;
5730
+ if (!pointerScreenRef.current && pointerRef.current) {
5731
+ pointerScreenRef.current = {
5732
+ x: pointerRef.current.x,
5733
+ y: pointerRef.current.y + iframeOffsetTop
5734
+ };
5735
+ }
5736
+ const screen = pointerScreenRef.current;
5737
+ if (screen) {
5738
+ const next = { x: screen.x, y: screen.y - iframeOffsetTop };
5739
+ pointerRef.current = next;
5740
+ applyHoverAt(next.x, next.y);
5741
+ }
5742
+ };
5743
+ const onResize = () => {
5744
+ setChromeClip((prev) => {
5745
+ const bottom = Math.max(prev.top, window.innerHeight);
5746
+ return prev.bottom === bottom ? prev : { ...prev, bottom };
5747
+ });
5748
+ };
5749
+ window.addEventListener("message", onParentScroll);
5750
+ window.addEventListener("resize", onResize);
5751
+ return () => {
5752
+ window.removeEventListener("message", onParentScroll);
5753
+ window.removeEventListener("resize", onResize);
5754
+ };
5755
+ }, [applyHoverAt]);
5756
+ (0, import_react5.useEffect)(() => {
5757
+ const ptr = pointerRef.current;
5758
+ if (!ptr) return;
5759
+ applyHoverAt(ptr.x, ptr.y, rects);
5760
+ }, [rects, applyHoverAt]);
5761
+ (0, import_react5.useEffect)(() => {
5762
+ const rememberPointer = (clientX, clientY) => {
5763
+ pointerRef.current = { x: clientX, y: clientY };
5764
+ const top = iframeOffsetTopRef.current;
5765
+ if (top != null) {
5766
+ pointerScreenRef.current = { x: clientX, y: clientY + top };
5767
+ }
5768
+ applyHoverAt(clientX, clientY, rects);
5769
+ };
5770
+ const onPointerMove = (e) => {
5771
+ rememberPointer(e.clientX, e.clientY);
5772
+ };
5773
+ const onPointerSync = (e) => {
5774
+ if (e.data?.type !== "ow:pointer-sync") return;
5775
+ const { clientX, clientY } = e.data;
5776
+ rememberPointer(clientX, clientY);
5777
+ };
5778
+ window.addEventListener("pointermove", onPointerMove, { passive: true });
5779
+ window.addEventListener("message", onPointerSync);
5780
+ return () => {
5781
+ window.removeEventListener("pointermove", onPointerMove);
5782
+ window.removeEventListener("message", onPointerSync);
5783
+ };
5784
+ }, [rects, applyHoverAt]);
5785
+ const handleSelect = (0, import_react5.useCallback)(
5786
+ (section) => {
5787
+ if (selectedId) return;
5788
+ setSelectedId(section.id);
5789
+ onSelect(section);
5790
+ },
5791
+ [selectedId, onSelect]
5792
+ );
5793
+ (0, import_react5.useEffect)(() => {
5794
+ const onKeyDown = (e) => {
5795
+ if (e.key === "Escape") {
5796
+ e.preventDefault();
5797
+ e.stopPropagation();
5798
+ onBack();
5799
+ }
5800
+ };
5801
+ window.addEventListener("keydown", onKeyDown, true);
5802
+ return () => window.removeEventListener("keydown", onKeyDown, true);
5803
+ }, [onBack]);
5804
+ const portalRoot = typeof document !== "undefined" ? document.querySelector("[data-ohw-bridge-root]") ?? document.body : null;
5805
+ if (!portalRoot) return null;
5806
+ return (0, import_react_dom.createPortal)(
5807
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(
5808
+ "div",
5809
+ {
5810
+ "data-ohw-section-picker": "",
5811
+ "data-ohw-bridge": "",
5812
+ className: `pointer-events-none fixed inset-0 z-[2147483647] transition-opacity duration-200 ${"opacity-100"}`,
5813
+ "aria-modal": true,
5814
+ role: "dialog",
5815
+ "aria-label": "Choose a section",
5816
+ children: [
5817
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
5818
+ "div",
5819
+ {
5820
+ className: "pointer-events-auto fixed left-5 z-[2]",
5821
+ style: { top: chromeClip.top + 20 },
5822
+ children: /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(
5823
+ Button,
5824
+ {
5825
+ type: "button",
5826
+ variant: "outline",
5827
+ size: "sm",
5828
+ className: "h-8 min-w-0 gap-1 border-border bg-background px-2 py-1.5 shadow-sm hover:bg-muted cursor-pointer",
5829
+ onClick: onBack,
5830
+ children: [
5831
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_lucide_react9.ArrowLeft, { className: "size-4 shrink-0", "aria-hidden": true }),
5832
+ "Back"
5833
+ ]
5834
+ }
5835
+ )
5836
+ }
5837
+ ),
5838
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
5839
+ "div",
5840
+ {
5841
+ 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",
5842
+ style: {
5843
+ top: chromeClip.bottom - 20,
5844
+ transform: "translate(-50%, -100%)",
5845
+ backgroundColor: "var(--ohw-popover-foreground, #0c0a09)"
5846
+ },
5847
+ "data-ohw-section-picker-hint": "",
5848
+ children: "Click on section to select"
5849
+ }
5850
+ ),
5851
+ !isOnTargetPage ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
5852
+ "div",
5853
+ {
5854
+ 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",
5855
+ style: { top: chromeClip.top + 64 },
5856
+ children: "Loading page preview\u2026"
5857
+ }
5858
+ ) : null,
5859
+ isOnTargetPage && liveSections.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("div", { className: "pointer-events-auto fixed inset-0 z-[1] flex items-center justify-center bg-muted/40", children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("p", { className: "text-sm text-muted-foreground", children: "No sections found on this page." }) }) : null,
5860
+ isOnTargetPage ? liveSections.map((section) => {
5861
+ const rect = rects.get(section.id);
5862
+ if (!rect || rect.width <= 0 || rect.height <= 0) return null;
5863
+ const isSelected = selectedId === section.id;
5864
+ const isHovered = hoveredId === section.id;
5865
+ const isLit = isSelected || isHovered;
5866
+ return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(
5867
+ "button",
5868
+ {
5869
+ type: "button",
5870
+ className: "pointer-events-auto fixed z-[1] cursor-pointer border-0 bg-transparent p-0 transition-[background-color] duration-150",
5871
+ style: {
5872
+ top: rect.top,
5873
+ left: rect.left,
5874
+ width: rect.width,
5875
+ height: rect.height,
5876
+ backgroundColor: isLit ? "transparent" : DIM_OVERLAY
5877
+ },
5878
+ "aria-label": `Select section ${section.label}`,
5879
+ onClick: () => handleSelect(section),
5880
+ children: [
5881
+ isLit ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
5882
+ "span",
5883
+ {
5884
+ className: "pointer-events-none absolute",
5885
+ style: {
5886
+ inset: 4,
5887
+ borderRadius: "var(--radius, 6px)",
5888
+ border: "1.5px dashed var(--primary, #0885FE)",
5889
+ boxSizing: "border-box"
5890
+ },
5891
+ "aria-hidden": true
5892
+ }
5893
+ ) : null,
5894
+ isSelected ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
5895
+ "span",
5896
+ {
5897
+ className: "absolute right-3 top-3 flex size-8 items-center justify-center rounded-full text-white",
5898
+ style: { backgroundColor: "var(--ohw-primary, #0885fe)" },
5899
+ "aria-hidden": true,
5900
+ children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_lucide_react9.Check, { className: "size-5" })
5901
+ }
5902
+ ) : null
5903
+ ]
5904
+ },
5905
+ section.id
5906
+ );
5907
+ }) : null
5908
+ ]
5909
+ }
5910
+ ),
5911
+ portalRoot
5912
+ );
5913
+ }
5914
+
5915
+ // src/ui/link-modal/useLinkModalState.ts
5916
+ var import_react6 = require("react");
5917
+ function useLinkModalState({
5918
+ open,
5919
+ mode,
5920
+ pages,
5921
+ sections: _sections,
5922
+ sectionsByPath,
5923
+ initialTarget,
5924
+ existingTargets: _existingTargets,
5925
+ onClose,
5926
+ onSubmit
5927
+ }) {
5928
+ const availablePages = (0, import_react6.useMemo)(() => pages, [pages]);
5929
+ const [searchValue, setSearchValue] = (0, import_react6.useState)("");
5930
+ const [selectedPage, setSelectedPage] = (0, import_react6.useState)(null);
5931
+ const [selectedSection, setSelectedSection] = (0, import_react6.useState)(null);
5932
+ const [step, setStep] = (0, import_react6.useState)("input");
5933
+ const [dropdownOpen, setDropdownOpen] = (0, import_react6.useState)(false);
5934
+ const [urlError, setUrlError] = (0, import_react6.useState)("");
5935
+ const reset = (0, import_react6.useCallback)(() => {
5936
+ setSearchValue("");
5937
+ setSelectedPage(null);
5938
+ setSelectedSection(null);
5939
+ setStep("input");
5940
+ setDropdownOpen(false);
5941
+ setUrlError("");
5942
+ }, []);
5943
+ (0, import_react6.useEffect)(() => {
5944
+ if (!open) return;
5945
+ if (mode === "edit" && initialTarget) {
5946
+ const { pageRoute } = parseTarget(initialTarget);
5947
+ const targetSections = getSectionsForPath(sectionsByPath, pageRoute);
5948
+ const init = getEditModeInitialState(initialTarget, pages, targetSections);
5949
+ setSearchValue(init.searchValue);
5950
+ setSelectedPage(init.selectedPage);
5951
+ setSelectedSection(init.selectedSection);
5952
+ setStep(init.step);
5953
+ } else {
5954
+ reset();
5955
+ }
5956
+ setDropdownOpen(false);
5957
+ setUrlError("");
5958
+ }, [open, mode, initialTarget, reset]);
5959
+ const filteredPages = (0, import_react6.useMemo)(() => {
5960
+ if (!searchValue.trim()) return availablePages;
5961
+ return availablePages.filter((p) => p.title.toLowerCase().startsWith(searchValue.toLowerCase()));
5962
+ }, [availablePages, searchValue]);
5963
+ const activeSections = (0, import_react6.useMemo)(() => {
5964
+ if (!selectedPage) return [];
5965
+ return getSectionsForPath(sectionsByPath, selectedPage.path);
5966
+ }, [selectedPage, sectionsByPath]);
5967
+ const showBreadcrumb = step === "confirmed" && selectedPage && selectedSection;
5968
+ const showSectionPicker = step === "sectionPicker" && selectedPage;
5969
+ const showSectionRow = mode === "edit" && selectedPage && selectedSection && step === "input";
5970
+ const showChooseSection = step === "input" && selectedPage && !selectedSection && !showSectionRow && activeSections.length > 0;
5971
+ const handleInputChange = (value) => {
5972
+ setSearchValue(value);
5973
+ setSelectedPage(null);
5974
+ setSelectedSection(null);
5975
+ setStep("input");
5976
+ setUrlError("");
5977
+ const filtered = value.trim() ? availablePages.filter((p) => p.title.toLowerCase().startsWith(value.toLowerCase())) : availablePages;
5978
+ setDropdownOpen(filtered.length > 0);
5979
+ if (value.trim()) {
5980
+ setUrlError(validateUrlInput(value, availablePages, pages));
5981
+ }
5982
+ };
5983
+ const handlePageSelect = (page) => {
5984
+ setSelectedPage({ path: page.path, title: page.title });
5985
+ setSelectedSection(null);
5986
+ setSearchValue(page.title);
5987
+ setStep("input");
5988
+ setDropdownOpen(false);
5989
+ setUrlError("");
5990
+ };
5991
+ const handleChooseSection = () => {
5992
+ setStep("sectionPicker");
5993
+ };
5994
+ const handleSectionSelect = (section) => {
5995
+ setSelectedSection({ id: section.id, label: section.label });
5996
+ if (mode === "create") {
5997
+ setStep("confirmed");
5998
+ } else {
5516
5999
  setStep("input");
5517
6000
  }
5518
6001
  };
5519
6002
  const handleBackToSections = () => {
5520
6003
  setStep("sectionPicker");
5521
6004
  };
6005
+ const handleSectionPickerBack = () => {
6006
+ setStep("input");
6007
+ };
5522
6008
  const handleClose = () => {
5523
6009
  reset();
5524
6010
  onClose();
5525
6011
  };
5526
- const isValid = (0, import_react5.useMemo)(() => {
6012
+ const isValid = (0, import_react6.useMemo)(() => {
5527
6013
  if (urlError) return false;
5528
6014
  if (showBreadcrumb) return true;
5529
6015
  if (selectedPage) return true;
@@ -5556,7 +6042,7 @@ function useLinkModalState({
5556
6042
  onSubmit(normalizeUrl(searchValue));
5557
6043
  handleClose();
5558
6044
  };
5559
- const submitLabel = mode === "edit" ? "Save" : "Create";
6045
+ const submitLabel = showBreadcrumb ? "Save" : mode === "edit" ? "Save" : "Create";
5560
6046
  const title = mode === "edit" ? "Edit navigation item" : "Create navigation item";
5561
6047
  const secondaryLabel = showBreadcrumb ? "Back to sections" : "Cancel";
5562
6048
  const handleSecondary = showBreadcrumb ? handleBackToSections : handleClose;
@@ -5583,23 +6069,29 @@ function useLinkModalState({
5583
6069
  handleChooseSection,
5584
6070
  handleSectionSelect,
5585
6071
  handleBackToSections,
6072
+ handleSectionPickerBack,
5586
6073
  handleClose,
5587
6074
  handleSecondary,
5588
6075
  handleSubmit
5589
6076
  };
5590
6077
  }
5591
6078
 
5592
- // src/ui/link-modal/LinkEditorPanel.tsx
5593
- var import_jsx_runtime19 = require("react/jsx-runtime");
5594
- function LinkEditorPanel({
6079
+ // src/ui/link-modal/LinkPopover.tsx
6080
+ var import_jsx_runtime20 = require("react/jsx-runtime");
6081
+ function postToParent(data) {
6082
+ window.parent?.postMessage(data, "*");
6083
+ }
6084
+ function LinkPopover({
5595
6085
  open = true,
6086
+ panelRef,
6087
+ portalContainer,
6088
+ onClose,
5596
6089
  mode = "create",
5597
6090
  pages,
5598
6091
  sections = [],
5599
6092
  sectionsByPath,
5600
6093
  initialTarget,
5601
6094
  existingTargets = [],
5602
- onClose,
5603
6095
  onSubmit
5604
6096
  }) {
5605
6097
  const state = useLinkModalState({
@@ -5613,115 +6105,109 @@ function LinkEditorPanel({
5613
6105
  onClose,
5614
6106
  onSubmit
5615
6107
  });
5616
- const isCancel = state.secondaryLabel === "Cancel";
5617
- return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(import_jsx_runtime19.Fragment, { children: [
5618
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(DialogClose, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
5619
- "button",
6108
+ const sectionPickerActive = state.showSectionPicker && Boolean(state.selectedPage);
6109
+ (0, import_react7.useEffect)(() => {
6110
+ if (!open) return;
6111
+ if (sectionPickerActive) {
6112
+ postToParent({ type: "ui:unlock" });
6113
+ postToParent({ type: "ow:section-picker", active: true });
6114
+ document.documentElement.style.overflow = "";
6115
+ document.body.style.overflow = "";
6116
+ return () => {
6117
+ postToParent({ type: "ow:section-picker", active: false });
6118
+ };
6119
+ }
6120
+ postToParent({ type: "ow:section-picker", active: false });
6121
+ postToParent({ type: "ui:lock" });
6122
+ const html = document.documentElement;
6123
+ const body = document.body;
6124
+ const prevHtmlOverflow = html.style.overflow;
6125
+ const prevBodyOverflow = body.style.overflow;
6126
+ const prevHtmlOverscroll = html.style.overscrollBehavior;
6127
+ const prevBodyOverscroll = body.style.overscrollBehavior;
6128
+ html.style.overflow = "hidden";
6129
+ body.style.overflow = "hidden";
6130
+ html.style.overscrollBehavior = "none";
6131
+ body.style.overscrollBehavior = "none";
6132
+ const canScrollElement = (el, deltaY) => {
6133
+ const { overflowY } = getComputedStyle(el);
6134
+ if (overflowY !== "auto" && overflowY !== "scroll") return false;
6135
+ if (el.scrollHeight <= el.clientHeight + 1) return false;
6136
+ if (deltaY < 0) return el.scrollTop > 0;
6137
+ if (deltaY > 0)
6138
+ return el.scrollTop + el.clientHeight < el.scrollHeight - 1;
6139
+ return true;
6140
+ };
6141
+ const allowsInternalScroll = (e) => {
6142
+ const target = e.target;
6143
+ if (!(target instanceof Element)) return false;
6144
+ const scrollRoot = target.closest(
6145
+ "[data-ohw-link-page-dropdown], [data-ohw-allow-scroll]"
6146
+ );
6147
+ if (!scrollRoot) return false;
6148
+ const deltaY = e instanceof WheelEvent ? e.deltaY : 0;
6149
+ return canScrollElement(scrollRoot, deltaY);
6150
+ };
6151
+ const preventBackgroundScroll = (e) => {
6152
+ if (allowsInternalScroll(e)) return;
6153
+ e.preventDefault();
6154
+ };
6155
+ const scrollOpts = { passive: false, capture: true };
6156
+ document.addEventListener("wheel", preventBackgroundScroll, scrollOpts);
6157
+ document.addEventListener("touchmove", preventBackgroundScroll, scrollOpts);
6158
+ return () => {
6159
+ postToParent({ type: "ui:unlock" });
6160
+ html.style.overflow = prevHtmlOverflow;
6161
+ body.style.overflow = prevBodyOverflow;
6162
+ html.style.overscrollBehavior = prevHtmlOverscroll;
6163
+ body.style.overscrollBehavior = prevBodyOverscroll;
6164
+ document.removeEventListener(
6165
+ "wheel",
6166
+ preventBackgroundScroll,
6167
+ scrollOpts
6168
+ );
6169
+ document.removeEventListener(
6170
+ "touchmove",
6171
+ preventBackgroundScroll,
6172
+ scrollOpts
6173
+ );
6174
+ };
6175
+ }, [open, sectionPickerActive]);
6176
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(import_jsx_runtime20.Fragment, { children: [
6177
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
6178
+ Dialog2,
5620
6179
  {
5621
- type: "button",
5622
- className: "absolute right-[9px] top-[9px] rounded-sm p-1.5 text-foreground hover:bg-muted/50",
5623
- "aria-label": "Close",
5624
- children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(IconX, { "aria-hidden": true })
6180
+ open: open && !sectionPickerActive,
6181
+ onOpenChange: (next) => {
6182
+ if (!next) onClose?.();
6183
+ },
6184
+ children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
6185
+ DialogContent,
6186
+ {
6187
+ ref: panelRef,
6188
+ container: portalContainer,
6189
+ "data-ohw-link-popover-root": "",
6190
+ "data-ohw-link-modal-root": "",
6191
+ "data-ohw-bridge": "",
6192
+ showCloseButton: false,
6193
+ className: "gap-0 p-0 w-full max-w-[448px] pointer-events-auto z-[2147483646] overflow-visible",
6194
+ children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(LinkEditorPanel, { state, onClose })
6195
+ }
6196
+ )
5625
6197
  }
5626
- ) }),
5627
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(DialogHeader, { className: "w-full gap-1.5 p-6 pr-12", children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(DialogTitle, { className: "m-0 w-full break-words", children: state.title }) }),
5628
- /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "flex w-full flex-col gap-3 px-6 pb-8 pt-1", children: [
5629
- state.showBreadcrumb && state.selectedPage && state.selectedSection ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
5630
- DestinationBreadcrumb,
5631
- {
5632
- pageTitle: state.selectedPage.title,
5633
- sectionLabel: state.selectedSection.label
5634
- }
5635
- ) : /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
5636
- UrlOrPageInput,
5637
- {
5638
- value: state.searchValue,
5639
- selectedPage: state.selectedPage,
5640
- dropdownOpen: state.dropdownOpen,
5641
- onDropdownOpenChange: state.setDropdownOpen,
5642
- filteredPages: state.filteredPages,
5643
- onInputChange: state.handleInputChange,
5644
- onPageSelect: state.handlePageSelect,
5645
- urlError: state.urlError
5646
- }
5647
- ),
5648
- state.showChooseSection ? /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "flex flex-col justify-center gap-2", children: [
5649
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(Button, { type: "button", variant: "outline", className: "w-fit cursor-pointer", size: "sm", onClick: state.handleChooseSection, children: "Choose a section" }),
5650
- /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "flex items-center gap-1 text-sm text-muted-foreground", children: [
5651
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(IconInfo, { className: "shrink-0", "aria-hidden": true }),
5652
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { children: "Pick a section this link should scroll to." })
5653
- ] })
5654
- ] }) : null,
5655
- state.showSectionPicker ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(SectionPickerList, { sections: state.activeSections, onSelect: state.handleSectionSelect }) : null,
5656
- state.showSectionRow && state.selectedSection ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(SectionTreeItem, { section: state.selectedSection, selected: true }) : null
5657
- ] }),
5658
- /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(DialogFooter, { className: "w-full px-6 pb-6", children: [
5659
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
5660
- Button,
5661
- {
5662
- type: "button",
5663
- variant: isCancel ? "outline" : "ghost",
5664
- className: "leading-6 shadow-none cursor-pointer",
5665
- style: isCancel ? {
5666
- backgroundColor: "var(--ohw-background)",
5667
- borderColor: "var(--ohw-border)",
5668
- color: "var(--ohw-foreground)"
5669
- } : void 0,
5670
- onClick: state.handleSecondary,
5671
- children: state.secondaryLabel
5672
- }
5673
- ),
5674
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
5675
- Button,
5676
- {
5677
- type: "button",
5678
- className: "border-0 leading-6 shadow-none hover:opacity-90 disabled:opacity-50 cursor-pointer",
5679
- style: {
5680
- backgroundColor: "var(--ohw-primary)",
5681
- color: "var(--ohw-primary-foreground)"
5682
- },
5683
- disabled: !state.isValid,
5684
- onClick: state.handleSubmit,
5685
- children: state.submitLabel
5686
- }
5687
- )
5688
- ] })
6198
+ ),
6199
+ sectionPickerActive && state.selectedPage ? /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
6200
+ SectionPickerOverlay,
6201
+ {
6202
+ pagePath: state.selectedPage.path,
6203
+ sections: state.activeSections,
6204
+ onBack: state.handleSectionPickerBack,
6205
+ onSelect: state.handleSectionSelect
6206
+ }
6207
+ ) : null
5689
6208
  ] });
5690
6209
  }
5691
6210
 
5692
- // src/ui/link-modal/LinkPopover.tsx
5693
- var import_jsx_runtime20 = require("react/jsx-runtime");
5694
- function LinkPopover({
5695
- open = true,
5696
- panelRef,
5697
- portalContainer,
5698
- onClose,
5699
- ...editorProps
5700
- }) {
5701
- return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
5702
- Dialog2,
5703
- {
5704
- open,
5705
- onOpenChange: (next) => {
5706
- if (!next) onClose?.();
5707
- },
5708
- children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
5709
- DialogContent,
5710
- {
5711
- ref: panelRef,
5712
- container: portalContainer,
5713
- "data-ohw-link-popover-root": "",
5714
- "data-ohw-link-modal-root": "",
5715
- "data-ohw-bridge": "",
5716
- showCloseButton: false,
5717
- className: "gap-0 p-0 w-full md:w-md pointer-events-auto",
5718
- children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(LinkEditorPanel, { ...editorProps, open, onClose })
5719
- }
5720
- )
5721
- }
5722
- );
5723
- }
5724
-
5725
6211
  // src/ui/link-modal/devFixtures.ts
5726
6212
  var DEV_SITE_PAGES = [
5727
6213
  { path: "/", title: "Home" },
@@ -5741,16 +6227,21 @@ var DEV_SECTIONS_BY_PATH = {
5741
6227
  { id: "founder-teaser", label: "Founder" },
5742
6228
  { id: "plan-form", label: "Plan Form" },
5743
6229
  { id: "testimonials", label: "Testimonials" },
5744
- { id: "wordmark", label: "Wordmark" }
6230
+ { id: "wordmark", label: "Wordmark" },
6231
+ { id: "footer", label: "Footer" }
5745
6232
  ],
5746
6233
  "/about": [
5747
6234
  { id: "manifesto", label: "Manifesto" },
5748
6235
  { id: "story-letter", label: "Our Story" },
5749
6236
  { id: "lagree-explainer", label: "Lagree Explainer" },
5750
6237
  { id: "waitlist-cta", label: "Waitlist" },
5751
- { id: "personal-training", label: "Personal training" }
6238
+ { id: "personal-training", label: "Personal training" },
6239
+ { id: "footer", label: "Footer" }
6240
+ ],
6241
+ "/classes": [
6242
+ { id: "class-library", label: "Class Library" },
6243
+ { id: "footer", label: "Footer" }
5752
6244
  ],
5753
- "/classes": [{ id: "class-library", label: "Class Library" }],
5754
6245
  "/pricing": [
5755
6246
  { id: "page-header", label: "Page Header" },
5756
6247
  { id: "free-class-cta", label: "Free Class" },
@@ -5758,19 +6249,25 @@ var DEV_SECTIONS_BY_PATH = {
5758
6249
  { id: "monthly-memberships", label: "Memberships" },
5759
6250
  { id: "specialty-programs", label: "Specialty Programs" },
5760
6251
  { id: "package-matcher", label: "Packages" },
5761
- { id: "wordmark", label: "Wordmark" }
6252
+ { id: "wordmark", label: "Wordmark" },
6253
+ { id: "footer", label: "Footer" }
5762
6254
  ],
5763
6255
  "/studio": [
5764
6256
  { id: "studio-intro", label: "Studio Intro" },
5765
6257
  { id: "studio-features", label: "Studio Features" },
5766
6258
  { id: "studio-gallery", label: "Studio Gallery" },
5767
- { id: "studio-visit", label: "Visit Studio" }
6259
+ { id: "studio-visit", label: "Visit Studio" },
6260
+ { id: "footer", label: "Footer" }
6261
+ ],
6262
+ "/instructors": [
6263
+ { id: "instructor-grid", label: "Instructors" },
6264
+ { id: "footer", label: "Footer" }
5768
6265
  ],
5769
- "/instructors": [{ id: "instructor-grid", label: "Instructors" }],
5770
6266
  "/contact": [
5771
6267
  { id: "hello-hero", label: "Hello" },
5772
6268
  { id: "contact-form", label: "Contact Form" },
5773
- { id: "faq", label: "FAQ" }
6269
+ { id: "faq", label: "FAQ" },
6270
+ { id: "footer", label: "Footer" }
5774
6271
  ]
5775
6272
  };
5776
6273
  function shouldUseDevFixtures() {
@@ -5780,8 +6277,277 @@ function shouldUseDevFixtures() {
5780
6277
  return new URLSearchParams(q).get("ohw-fixtures") === "1";
5781
6278
  }
5782
6279
 
5783
- // src/ui/badge.tsx
6280
+ // src/lib/nav-items.ts
6281
+ var NAV_COUNT_KEY = "__ohw_nav_count";
6282
+ var NAV_ORDER_KEY = "__ohw_nav_order";
6283
+ function getLinkHref(el) {
6284
+ const key = el.getAttribute("data-ohw-href-key");
6285
+ if (key) {
6286
+ const stored = getStoredLinkHref(key);
6287
+ if (stored !== void 0) return stored;
6288
+ }
6289
+ return el.getAttribute("href") ?? "";
6290
+ }
6291
+ function isNavbarButton(el) {
6292
+ return el.hasAttribute("data-ohw-role") && el.getAttribute("data-ohw-role") === "navbar-button";
6293
+ }
6294
+ function isNavbarLinkItem(el) {
6295
+ if (!el.matches("[data-ohw-href-key]")) return false;
6296
+ if (isNavbarButton(el)) return false;
6297
+ if (!el.querySelector('[data-ohw-editable="text"]')) return false;
6298
+ return Boolean(el.closest("nav, [data-ohw-nav-container], [data-ohw-nav-drawer]"));
6299
+ }
6300
+ function getNavbarDesktopContainer() {
6301
+ return document.querySelector("[data-ohw-nav-container]");
6302
+ }
6303
+ function getNavbarDrawerContainer() {
6304
+ return document.querySelector("[data-ohw-nav-drawer]");
6305
+ }
6306
+ function listNavbarItems() {
6307
+ const desktop = getNavbarDesktopContainer();
6308
+ if (desktop) {
6309
+ return Array.from(desktop.querySelectorAll("[data-ohw-href-key]")).filter(isNavbarLinkItem);
6310
+ }
6311
+ const drawer = getNavbarDrawerContainer();
6312
+ if (drawer) {
6313
+ return Array.from(drawer.querySelectorAll("[data-ohw-href-key]")).filter(isNavbarLinkItem);
6314
+ }
6315
+ return Array.from(document.querySelectorAll("nav [data-ohw-href-key]")).filter(isNavbarLinkItem);
6316
+ }
6317
+ function parseNavIndexFromKey(key) {
6318
+ if (!key) return null;
6319
+ const match = key.match(/^nav-(\d+)-href$/);
6320
+ if (!match) return null;
6321
+ return parseInt(match[1], 10);
6322
+ }
6323
+ function getNavbarIndicesInDom() {
6324
+ const indices = /* @__PURE__ */ new Set();
6325
+ for (const item of listNavbarItems()) {
6326
+ const idx = parseNavIndexFromKey(item.getAttribute("data-ohw-href-key"));
6327
+ if (idx !== null) indices.add(idx);
6328
+ }
6329
+ return [...indices].sort((a, b) => a - b);
6330
+ }
6331
+ function getNextNavbarIndex() {
6332
+ const indices = getNavbarIndicesInDom();
6333
+ if (indices.length === 0) return 0;
6334
+ return Math.max(...indices) + 1;
6335
+ }
6336
+ function getNavbarExistingTargets() {
6337
+ return listNavbarItems().map((el) => getLinkHref(el)).filter(Boolean);
6338
+ }
6339
+ function getNavOrderFromDom() {
6340
+ return listNavbarItems().map((el) => el.getAttribute("data-ohw-href-key")).filter((key) => Boolean(key));
6341
+ }
6342
+ function parseNavOrder(content) {
6343
+ const raw = content[NAV_ORDER_KEY];
6344
+ if (!raw) return null;
6345
+ try {
6346
+ const parsed = JSON.parse(raw);
6347
+ if (!Array.isArray(parsed)) return null;
6348
+ return parsed.filter((key) => typeof key === "string" && key.length > 0);
6349
+ } catch {
6350
+ return null;
6351
+ }
6352
+ }
6353
+ function findCounterpartByHrefKey(hrefKey, root) {
6354
+ return root.querySelector(`[data-ohw-href-key="${CSS.escape(hrefKey)}"]`);
6355
+ }
6356
+ function cloneNavLinkShell(template) {
6357
+ const anchor = document.createElement("a");
6358
+ if (template) {
6359
+ anchor.style.cssText = template.style.cssText;
6360
+ if (template.className) anchor.className = template.className;
6361
+ }
6362
+ anchor.style.cursor = "pointer";
6363
+ anchor.style.textDecoration = "none";
6364
+ return anchor;
6365
+ }
6366
+ function buildNavLink(index, href, label, template) {
6367
+ const anchor = cloneNavLinkShell(template);
6368
+ anchor.href = href;
6369
+ anchor.setAttribute("data-ohw-href-key", `nav-${index}-href`);
6370
+ const span = document.createElement("span");
6371
+ span.setAttribute("data-ohw-editable", "text");
6372
+ span.setAttribute("data-ohw-key", `nav-${index}-label`);
6373
+ span.textContent = label;
6374
+ anchor.appendChild(span);
6375
+ return anchor;
6376
+ }
6377
+ function insertNavLinkInContainer(container, anchor, afterHrefKey) {
6378
+ if (!afterHrefKey) {
6379
+ container.appendChild(anchor);
6380
+ return;
6381
+ }
6382
+ const afterEl = findCounterpartByHrefKey(afterHrefKey, container);
6383
+ if (afterEl?.parentElement === container) {
6384
+ afterEl.insertAdjacentElement("afterend", anchor);
6385
+ return;
6386
+ }
6387
+ container.appendChild(anchor);
6388
+ }
6389
+ function insertNavbarItemDom(index, href, label, afterAnchor) {
6390
+ const afterHrefKey = afterAnchor?.getAttribute("data-ohw-href-key") ?? null;
6391
+ const desktopItems = getNavbarDesktopContainer()?.querySelectorAll("[data-ohw-href-key]") ?? [];
6392
+ const drawerItems = getNavbarDrawerContainer()?.querySelectorAll("[data-ohw-href-key]") ?? [];
6393
+ const desktopTemplate = Array.from(desktopItems).find(isNavbarLinkItem) ?? null;
6394
+ const drawerTemplate = Array.from(drawerItems).find(isNavbarLinkItem) ?? null;
6395
+ let primary = null;
6396
+ const desktopContainer = getNavbarDesktopContainer();
6397
+ if (desktopContainer) {
6398
+ const desktopAnchor = buildNavLink(index, href, label, desktopTemplate);
6399
+ insertNavLinkInContainer(desktopContainer, desktopAnchor, afterHrefKey);
6400
+ primary = desktopAnchor;
6401
+ }
6402
+ const drawerContainer = getNavbarDrawerContainer();
6403
+ if (drawerContainer) {
6404
+ const drawerAnchor = buildNavLink(index, href, label, drawerTemplate);
6405
+ insertNavLinkInContainer(drawerContainer, drawerAnchor, afterHrefKey);
6406
+ if (!primary) primary = drawerAnchor;
6407
+ }
6408
+ if (!primary) {
6409
+ const fallback = buildNavLink(index, href, label, listNavbarItems()[0] ?? null);
6410
+ const nav = document.querySelector("nav");
6411
+ nav?.appendChild(fallback);
6412
+ primary = fallback;
6413
+ }
6414
+ return primary;
6415
+ }
6416
+ function applyNavOrderToContainer(container, order) {
6417
+ const seen = /* @__PURE__ */ new Set();
6418
+ for (const hrefKey of order) {
6419
+ if (seen.has(hrefKey)) continue;
6420
+ seen.add(hrefKey);
6421
+ const el = findCounterpartByHrefKey(hrefKey, container);
6422
+ if (el) container.appendChild(el);
6423
+ }
6424
+ for (const el of container.querySelectorAll("[data-ohw-href-key]")) {
6425
+ if (!isNavbarLinkItem(el)) continue;
6426
+ const key = el.getAttribute("data-ohw-href-key");
6427
+ if (!key || seen.has(key)) continue;
6428
+ container.appendChild(el);
6429
+ }
6430
+ }
6431
+ function applyNavOrder(order) {
6432
+ const desktop = getNavbarDesktopContainer();
6433
+ if (desktop) applyNavOrderToContainer(desktop, order);
6434
+ const drawer = getNavbarDrawerContainer();
6435
+ if (drawer) applyNavOrderToContainer(drawer, order);
6436
+ }
6437
+ function collectNavbarIndicesFromContent(content) {
6438
+ const indices = /* @__PURE__ */ new Set();
6439
+ for (const key of Object.keys(content)) {
6440
+ const idx = parseNavIndexFromKey(key);
6441
+ if (idx !== null) indices.add(idx);
6442
+ }
6443
+ const countRaw = content[NAV_COUNT_KEY];
6444
+ if (countRaw) {
6445
+ const count = parseInt(countRaw, 10);
6446
+ if (Number.isFinite(count) && count > 0) {
6447
+ for (let i = 0; i < count; i++) indices.add(i);
6448
+ }
6449
+ }
6450
+ return [...indices].sort((a, b) => a - b);
6451
+ }
6452
+ function navbarIndexExistsInDom(index) {
6453
+ return document.querySelector(`[data-ohw-href-key="nav-${index}-href"]`) !== null;
6454
+ }
6455
+ function reconcileNavbarItemsFromContent(content) {
6456
+ const indices = collectNavbarIndicesFromContent(content);
6457
+ for (const index of indices) {
6458
+ if (navbarIndexExistsInDom(index)) continue;
6459
+ const href = content[`nav-${index}-href`];
6460
+ const label = content[`nav-${index}-label`];
6461
+ if (!href && !label) continue;
6462
+ insertNavbarItemDom(index, href ?? "/", label ?? "Untitled", null);
6463
+ }
6464
+ const order = parseNavOrder(content);
6465
+ if (order?.length) applyNavOrder(order);
6466
+ }
6467
+ function buildNavOrderAfterInsert(afterAnchor, newHrefKey) {
6468
+ const order = getNavOrderFromDom();
6469
+ if (!afterAnchor) {
6470
+ if (!order.includes(newHrefKey)) order.push(newHrefKey);
6471
+ return order;
6472
+ }
6473
+ const afterKey = afterAnchor.getAttribute("data-ohw-href-key");
6474
+ if (!afterKey) {
6475
+ if (!order.includes(newHrefKey)) order.push(newHrefKey);
6476
+ return order;
6477
+ }
6478
+ const withoutNew = order.filter((key) => key !== newHrefKey);
6479
+ const pos = withoutNew.indexOf(afterKey);
6480
+ if (pos >= 0) {
6481
+ withoutNew.splice(pos + 1, 0, newHrefKey);
6482
+ return withoutNew;
6483
+ }
6484
+ withoutNew.push(newHrefKey);
6485
+ return withoutNew;
6486
+ }
6487
+ function insertNavbarItem(href, label, afterAnchor = null) {
6488
+ const index = getNextNavbarIndex();
6489
+ const anchor = insertNavbarItemDom(index, href, label, afterAnchor);
6490
+ if (!anchor) throw new Error("Failed to insert navbar item");
6491
+ const hrefKey = `nav-${index}-href`;
6492
+ const order = buildNavOrderAfterInsert(afterAnchor, hrefKey);
6493
+ applyNavOrder(order);
6494
+ return {
6495
+ anchor,
6496
+ index,
6497
+ hrefKey,
6498
+ labelKey: `nav-${index}-label`,
6499
+ href,
6500
+ label,
6501
+ order
6502
+ };
6503
+ }
6504
+
6505
+ // src/ui/navbar-container-chrome.tsx
6506
+ var import_lucide_react10 = require("lucide-react");
5784
6507
  var import_jsx_runtime21 = require("react/jsx-runtime");
6508
+ function NavbarContainerChrome({
6509
+ rect,
6510
+ onAdd
6511
+ }) {
6512
+ const chromeGap = 6;
6513
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
6514
+ "div",
6515
+ {
6516
+ "data-ohw-navbar-container-chrome": "",
6517
+ "data-ohw-bridge": "",
6518
+ className: "pointer-events-none fixed z-[2147483647]",
6519
+ style: {
6520
+ top: rect.top - chromeGap,
6521
+ left: rect.left - chromeGap,
6522
+ width: rect.width + chromeGap * 2,
6523
+ height: rect.height + chromeGap * 2
6524
+ },
6525
+ children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
6526
+ "button",
6527
+ {
6528
+ type: "button",
6529
+ "data-ohw-navbar-add-button": "",
6530
+ 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",
6531
+ style: { top: "70%" },
6532
+ "aria-label": "Add item",
6533
+ onMouseDown: (e) => {
6534
+ e.preventDefault();
6535
+ e.stopPropagation();
6536
+ },
6537
+ onClick: (e) => {
6538
+ e.preventDefault();
6539
+ e.stopPropagation();
6540
+ onAdd();
6541
+ },
6542
+ children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react10.Plus, { className: "size-4 shrink-0 text-foreground", "aria-hidden": true })
6543
+ }
6544
+ )
6545
+ }
6546
+ );
6547
+ }
6548
+
6549
+ // src/ui/badge.tsx
6550
+ var import_jsx_runtime22 = require("react/jsx-runtime");
5785
6551
  var badgeVariants = cva(
5786
6552
  "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",
5787
6553
  {
@@ -5799,12 +6565,12 @@ var badgeVariants = cva(
5799
6565
  }
5800
6566
  );
5801
6567
  function Badge({ className, variant, ...props }) {
5802
- return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { className: cn(badgeVariants({ variant }), className), ...props });
6568
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { className: cn(badgeVariants({ variant }), className), ...props });
5803
6569
  }
5804
6570
 
5805
6571
  // src/OhhwellsBridge.tsx
5806
- var import_lucide_react4 = require("lucide-react");
5807
- var import_jsx_runtime22 = require("react/jsx-runtime");
6572
+ var import_lucide_react11 = require("lucide-react");
6573
+ var import_jsx_runtime23 = require("react/jsx-runtime");
5808
6574
  var PRIMARY2 = "#0885FE";
5809
6575
  var IMAGE_FADE_MS = 300;
5810
6576
  function runOpacityFade(el, onDone) {
@@ -5981,9 +6747,9 @@ function mountSchedulingWidget(insertAfter, notifyOnConnect = false, scheduleId,
5981
6747
  tail.insertAdjacentElement("afterend", container);
5982
6748
  }
5983
6749
  const root = (0, import_client.createRoot)(container);
5984
- (0, import_react_dom.flushSync)(() => {
6750
+ (0, import_react_dom2.flushSync)(() => {
5985
6751
  root.render(
5986
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
6752
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
5987
6753
  SchedulingWidget,
5988
6754
  {
5989
6755
  notifyOnConnect,
@@ -6023,7 +6789,7 @@ function mountSchedulingEntries(entries, notifyOnConnect = false) {
6023
6789
  }
6024
6790
  }
6025
6791
  }
6026
- function getLinkHref(el) {
6792
+ function getLinkHref2(el) {
6027
6793
  const anchor = el instanceof HTMLAnchorElement ? el : el.querySelector("a");
6028
6794
  return anchor?.getAttribute("href") ?? "";
6029
6795
  }
@@ -6060,7 +6826,7 @@ function collectEditableNodes() {
6060
6826
  return { key: el.dataset.ohwKey ?? "", type: "video", text: getVideoEl(el)?.src ?? "" };
6061
6827
  }
6062
6828
  if (el.dataset.ohwEditable === "link") {
6063
- return { key: el.dataset.ohwKey ?? "", type: "link", text: getLinkHref(el) };
6829
+ return { key: el.dataset.ohwKey ?? "", type: "link", text: getLinkHref2(el) };
6064
6830
  }
6065
6831
  return {
6066
6832
  key: el.dataset.ohwKey ?? "",
@@ -6071,7 +6837,7 @@ function collectEditableNodes() {
6071
6837
  document.querySelectorAll("[data-ohw-href-key]").forEach((el) => {
6072
6838
  const key = el.getAttribute("data-ohw-href-key") ?? "";
6073
6839
  if (!key) return;
6074
- nodes.push({ key, type: "link", text: getLinkHref(el) });
6840
+ nodes.push({ key, type: "link", text: getLinkHref2(el) });
6075
6841
  });
6076
6842
  document.querySelectorAll('[data-ohw-editable="video"]').forEach((el) => {
6077
6843
  const key = el.dataset.ohwKey ?? "";
@@ -6163,7 +6929,7 @@ function applyLinkByKey(key, val) {
6163
6929
  }
6164
6930
  function isInsideLinkEditor(target) {
6165
6931
  return Boolean(
6166
- 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"]')
6932
+ 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"]')
6167
6933
  );
6168
6934
  }
6169
6935
  function getHrefKeyFromElement(el) {
@@ -6174,7 +6940,7 @@ function getHrefKeyFromElement(el) {
6174
6940
  if (!key) return null;
6175
6941
  return { anchor, key };
6176
6942
  }
6177
- function isNavbarButton(el) {
6943
+ function isNavbarButton2(el) {
6178
6944
  return Boolean(el.closest('[data-ohw-role="navbar-button"]'));
6179
6945
  }
6180
6946
  function getNavigationItemAnchor(el) {
@@ -6186,19 +6952,8 @@ function getNavigationItemAnchor(el) {
6186
6952
  function isNavigationItem(el) {
6187
6953
  return getNavigationItemAnchor(el) !== null;
6188
6954
  }
6189
- function getNavigationItemAddHintTarget(anchor) {
6190
- const items = listNavigationItems();
6191
- const idx = items.indexOf(anchor);
6192
- if (idx >= 0 && idx < items.length - 1) return items[idx + 1];
6193
- return anchor;
6194
- }
6195
- function listNavigationItems() {
6196
- return Array.from(
6197
- document.querySelectorAll("nav [data-ohw-href-key], footer [data-ohw-href-key]")
6198
- ).filter((el) => isNavigationItem(el));
6199
- }
6200
6955
  function getNavigationItemReorderState(anchor) {
6201
- if (isNavbarButton(anchor)) return { key: null, disabled: false };
6956
+ if (isNavbarButton2(anchor)) return { key: null, disabled: false };
6202
6957
  return getReorderHandleStateFromAnchor(anchor);
6203
6958
  }
6204
6959
  function getReorderHandleState(el) {
@@ -6220,6 +6975,104 @@ function clearHrefKeyHover(anchor) {
6220
6975
  function isInsideNavigationItem(el) {
6221
6976
  return getNavigationItemAnchor(el) !== null;
6222
6977
  }
6978
+ function getNavigationRoot(el) {
6979
+ const explicit = el.closest("[data-ohw-nav-root]");
6980
+ if (explicit) return explicit;
6981
+ return el.closest("nav, footer, aside");
6982
+ }
6983
+ function findFooterItemGroup(item) {
6984
+ const footer = item.closest("footer");
6985
+ if (!footer) return null;
6986
+ let node = item.parentElement;
6987
+ while (node && node !== footer) {
6988
+ const hasDirectNavItems = Array.from(
6989
+ node.querySelectorAll(":scope > [data-ohw-href-key]")
6990
+ ).some(isNavigationItem);
6991
+ if (hasDirectNavItems) return node;
6992
+ node = node.parentElement;
6993
+ }
6994
+ return null;
6995
+ }
6996
+ function isNavigationRoot(el) {
6997
+ return el.hasAttribute("data-ohw-nav-root") || el.matches("nav, footer, aside");
6998
+ }
6999
+ function isInferredFooterGroup(el) {
7000
+ const footer = el.closest("footer");
7001
+ if (!footer || el === footer) return false;
7002
+ return Array.from(el.querySelectorAll(":scope > [data-ohw-href-key]")).some(isNavigationItem);
7003
+ }
7004
+ function isNavigationContainer(el) {
7005
+ return el.hasAttribute("data-ohw-nav-container") || isNavigationRoot(el) || isInferredFooterGroup(el);
7006
+ }
7007
+ function isPointOverNavigation(x, y) {
7008
+ const navRoot = document.querySelector("[data-ohw-nav-root]") ?? document.querySelector("nav");
7009
+ if (!navRoot) return false;
7010
+ const r2 = navRoot.getBoundingClientRect();
7011
+ return x >= r2.left && x <= r2.right && y >= r2.top && y <= r2.bottom;
7012
+ }
7013
+ function isPointOverNavItem(container, x, y) {
7014
+ return Array.from(container.querySelectorAll("[data-ohw-href-key]")).some((el) => {
7015
+ if (!isNavigationItem(el) || isNavbarButton2(el)) return false;
7016
+ const itemRect = el.getBoundingClientRect();
7017
+ return x >= itemRect.left && x <= itemRect.right && y >= itemRect.top && y <= itemRect.bottom;
7018
+ });
7019
+ }
7020
+ function resolveNavContainerSelectionTarget(target, clientX, clientY) {
7021
+ if (getNavigationItemAnchor(target)) return null;
7022
+ if (target.closest('[data-ohw-role="navbar-button"]')) return null;
7023
+ const plainLink = target.closest("a");
7024
+ if (plainLink && !plainLink.hasAttribute("data-ohw-href-key") && !plainLink.closest("[data-ohw-nav-container]")) {
7025
+ return null;
7026
+ }
7027
+ const fromClosest = target.closest("[data-ohw-nav-container]");
7028
+ if (fromClosest && !isPointOverNavItem(fromClosest, clientX, clientY)) {
7029
+ return fromClosest;
7030
+ }
7031
+ const navRoot = target.closest("[data-ohw-nav-root]");
7032
+ if (!navRoot) return null;
7033
+ const container = navRoot.querySelector("[data-ohw-nav-container]");
7034
+ if (!container || isPointOverNavItem(container, clientX, clientY)) return null;
7035
+ if (target === navRoot || target.hasAttribute("data-ohw-nav-root")) {
7036
+ return container;
7037
+ }
7038
+ return null;
7039
+ }
7040
+ function getNavigationSelectionParent(el) {
7041
+ if (isNavigationItem(el)) {
7042
+ const explicit = el.closest("[data-ohw-nav-container]");
7043
+ if (explicit) return explicit;
7044
+ const footerGroup = findFooterItemGroup(el);
7045
+ if (footerGroup) return footerGroup;
7046
+ return getNavigationRoot(el);
7047
+ }
7048
+ if (el.hasAttribute("data-ohw-nav-container") || isInferredFooterGroup(el)) {
7049
+ return getNavigationRoot(el);
7050
+ }
7051
+ return null;
7052
+ }
7053
+ function placeCaretAtPoint(el, x, y) {
7054
+ const selection = window.getSelection();
7055
+ if (!selection) return;
7056
+ if (typeof document.caretRangeFromPoint === "function") {
7057
+ const range = document.caretRangeFromPoint(x, y);
7058
+ if (range && el.contains(range.startContainer)) {
7059
+ selection.removeAllRanges();
7060
+ selection.addRange(range);
7061
+ return;
7062
+ }
7063
+ }
7064
+ const caretPositionFromPoint = document.caretPositionFromPoint;
7065
+ if (typeof caretPositionFromPoint === "function") {
7066
+ const position = caretPositionFromPoint(x, y);
7067
+ if (position && el.contains(position.offsetNode)) {
7068
+ const range = document.createRange();
7069
+ range.setStart(position.offsetNode, position.offset);
7070
+ range.collapse(true);
7071
+ selection.removeAllRanges();
7072
+ selection.addRange(range);
7073
+ }
7074
+ }
7075
+ }
6223
7076
  function collectSections() {
6224
7077
  return collectSectionsFromDom();
6225
7078
  }
@@ -6367,7 +7220,7 @@ function EditGlowChrome({
6367
7220
  dragDisabled = false
6368
7221
  }) {
6369
7222
  const GAP = 6;
6370
- return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(
7223
+ return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(
6371
7224
  "div",
6372
7225
  {
6373
7226
  ref: elRef,
@@ -6382,7 +7235,7 @@ function EditGlowChrome({
6382
7235
  zIndex: 2147483646
6383
7236
  },
6384
7237
  children: [
6385
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
7238
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
6386
7239
  "div",
6387
7240
  {
6388
7241
  style: {
@@ -6395,7 +7248,7 @@ function EditGlowChrome({
6395
7248
  }
6396
7249
  }
6397
7250
  ),
6398
- reorderHrefKey && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
7251
+ reorderHrefKey && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
6399
7252
  "div",
6400
7253
  {
6401
7254
  "data-ohw-drag-handle-container": "",
@@ -6407,7 +7260,7 @@ function EditGlowChrome({
6407
7260
  transform: "translate(calc(-100% - 7px), -50%)",
6408
7261
  pointerEvents: dragDisabled ? "none" : "auto"
6409
7262
  },
6410
- children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
7263
+ children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
6411
7264
  DragHandle,
6412
7265
  {
6413
7266
  "aria-label": `Reorder ${reorderHrefKey}`,
@@ -6511,7 +7364,7 @@ function FloatingToolbar({
6511
7364
  onEditLink
6512
7365
  }) {
6513
7366
  const { top, left, transform } = calcToolbarPos(rect, parentScroll, 330);
6514
- return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
7367
+ return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
6515
7368
  "div",
6516
7369
  {
6517
7370
  ref: elRef,
@@ -6523,12 +7376,12 @@ function FloatingToolbar({
6523
7376
  zIndex: 2147483647,
6524
7377
  pointerEvents: "auto"
6525
7378
  },
6526
- children: /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(CustomToolbar, { children: [
6527
- TOOLBAR_GROUPS.map((btns, gi) => /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(import_react6.default.Fragment, { children: [
6528
- gi > 0 && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(CustomToolbarDivider, {}),
7379
+ children: /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(CustomToolbar, { children: [
7380
+ TOOLBAR_GROUPS.map((btns, gi) => /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(import_react8.default.Fragment, { children: [
7381
+ gi > 0 && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(CustomToolbarDivider, {}),
6529
7382
  btns.map((btn) => {
6530
7383
  const isActive = activeCommands.has(btn.cmd);
6531
- return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
7384
+ return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
6532
7385
  CustomToolbarButton,
6533
7386
  {
6534
7387
  title: btn.title,
@@ -6537,7 +7390,7 @@ function FloatingToolbar({
6537
7390
  e.preventDefault();
6538
7391
  onCommand(btn.cmd);
6539
7392
  },
6540
- children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
7393
+ children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
6541
7394
  "svg",
6542
7395
  {
6543
7396
  width: "16",
@@ -6558,7 +7411,7 @@ function FloatingToolbar({
6558
7411
  );
6559
7412
  })
6560
7413
  ] }, gi)),
6561
- showEditLink ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
7414
+ showEditLink ? /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
6562
7415
  CustomToolbarButton,
6563
7416
  {
6564
7417
  type: "button",
@@ -6572,7 +7425,7 @@ function FloatingToolbar({
6572
7425
  e.preventDefault();
6573
7426
  e.stopPropagation();
6574
7427
  },
6575
- children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_lucide_react4.Link, { className: "size-4 shrink-0", "aria-hidden": true })
7428
+ children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_lucide_react11.Link, { className: "size-4 shrink-0", "aria-hidden": true })
6576
7429
  }
6577
7430
  ) : null
6578
7431
  ] })
@@ -6589,7 +7442,7 @@ function StateToggle({
6589
7442
  states,
6590
7443
  onStateChange
6591
7444
  }) {
6592
- return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
7445
+ return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
6593
7446
  ToggleGroup,
6594
7447
  {
6595
7448
  "data-ohw-state-toggle": "",
@@ -6603,7 +7456,7 @@ function StateToggle({
6603
7456
  left: rect.right - 8,
6604
7457
  transform: "translateX(-100%)"
6605
7458
  },
6606
- children: states.map((state) => /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(ToggleGroupItem, { value: state, size: "sm", children: state }, state))
7459
+ children: states.map((state) => /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(ToggleGroupItem, { value: state, size: "sm", children: state }, state))
6607
7460
  }
6608
7461
  );
6609
7462
  }
@@ -6626,12 +7479,12 @@ function resolveSubdomain(subdomainFromQuery) {
6626
7479
  return "";
6627
7480
  }
6628
7481
  function OhhwellsBridge() {
6629
- const pathname = (0, import_navigation.usePathname)();
6630
- const router = (0, import_navigation.useRouter)();
6631
- const searchParams = (0, import_navigation.useSearchParams)();
7482
+ const pathname = (0, import_navigation2.usePathname)();
7483
+ const router = (0, import_navigation2.useRouter)();
7484
+ const searchParams = (0, import_navigation2.useSearchParams)();
6632
7485
  const isEditMode = isEditSessionActive();
6633
- const [bridgeRoot, setBridgeRoot] = (0, import_react6.useState)(null);
6634
- (0, import_react6.useEffect)(() => {
7486
+ const [bridgeRoot, setBridgeRoot] = (0, import_react8.useState)(null);
7487
+ (0, import_react8.useEffect)(() => {
6635
7488
  const figtreeFontId = "ohw-figtree-font";
6636
7489
  if (!document.getElementById(figtreeFontId)) {
6637
7490
  const preconnect1 = Object.assign(document.createElement("link"), { rel: "preconnect", href: "https://fonts.googleapis.com" });
@@ -6659,83 +7512,91 @@ function OhhwellsBridge() {
6659
7512
  const subdomainFromQuery = searchParams.get("subdomain");
6660
7513
  const subdomain = resolveSubdomain(subdomainFromQuery);
6661
7514
  useLinkHrefGuardian(pathname, subdomain, isEditMode);
6662
- const postToParent = (0, import_react6.useCallback)((data) => {
7515
+ const postToParent2 = (0, import_react8.useCallback)((data) => {
6663
7516
  if (typeof window !== "undefined" && window.parent !== window) {
6664
7517
  window.parent.postMessage(data, "*");
6665
7518
  }
6666
7519
  }, []);
6667
- const [fetchState, setFetchState] = (0, import_react6.useState)("idle");
6668
- const autoSaveTimers = (0, import_react6.useRef)(/* @__PURE__ */ new Map());
6669
- const activeElRef = (0, import_react6.useRef)(null);
6670
- const selectedElRef = (0, import_react6.useRef)(null);
6671
- const originalContentRef = (0, import_react6.useRef)(null);
6672
- const activeStateElRef = (0, import_react6.useRef)(null);
6673
- const parentScrollRef = (0, import_react6.useRef)(null);
6674
- const visibleViewportRef = (0, import_react6.useRef)(null);
6675
- const [dialogPortalContainer, setDialogPortalContainer] = (0, import_react6.useState)(null);
6676
- const attachVisibleViewport = (0, import_react6.useCallback)((node) => {
7520
+ const [fetchState, setFetchState] = (0, import_react8.useState)("idle");
7521
+ const autoSaveTimers = (0, import_react8.useRef)(/* @__PURE__ */ new Map());
7522
+ const activeElRef = (0, import_react8.useRef)(null);
7523
+ const selectedElRef = (0, import_react8.useRef)(null);
7524
+ const originalContentRef = (0, import_react8.useRef)(null);
7525
+ const activeStateElRef = (0, import_react8.useRef)(null);
7526
+ const parentScrollRef = (0, import_react8.useRef)(null);
7527
+ const visibleViewportRef = (0, import_react8.useRef)(null);
7528
+ const [dialogPortalContainer, setDialogPortalContainer] = (0, import_react8.useState)(null);
7529
+ const attachVisibleViewport = (0, import_react8.useCallback)((node) => {
6677
7530
  visibleViewportRef.current = node;
6678
7531
  setDialogPortalContainer(node);
6679
7532
  if (node) applyVisibleViewport(node, parentScrollRef.current);
6680
7533
  }, []);
6681
- const toolbarElRef = (0, import_react6.useRef)(null);
6682
- const glowElRef = (0, import_react6.useRef)(null);
6683
- const hoveredImageRef = (0, import_react6.useRef)(null);
6684
- const hoveredImageHasTextOverlapRef = (0, import_react6.useRef)(false);
6685
- const dragOverElRef = (0, import_react6.useRef)(null);
6686
- const [mediaHover, setMediaHover] = (0, import_react6.useState)(null);
6687
- const [uploadingRects, setUploadingRects] = (0, import_react6.useState)({});
6688
- const hoveredGapRef = (0, import_react6.useRef)(null);
6689
- const imageUnhoverTimerRef = (0, import_react6.useRef)(null);
6690
- const imageShowTimerRef = (0, import_react6.useRef)(null);
6691
- const editStylesRef = (0, import_react6.useRef)(null);
6692
- const activateRef = (0, import_react6.useRef)(() => {
7534
+ const toolbarElRef = (0, import_react8.useRef)(null);
7535
+ const glowElRef = (0, import_react8.useRef)(null);
7536
+ const hoveredImageRef = (0, import_react8.useRef)(null);
7537
+ const hoveredImageHasTextOverlapRef = (0, import_react8.useRef)(false);
7538
+ const dragOverElRef = (0, import_react8.useRef)(null);
7539
+ const [mediaHover, setMediaHover] = (0, import_react8.useState)(null);
7540
+ const [uploadingRects, setUploadingRects] = (0, import_react8.useState)({});
7541
+ const hoveredGapRef = (0, import_react8.useRef)(null);
7542
+ const imageUnhoverTimerRef = (0, import_react8.useRef)(null);
7543
+ const imageShowTimerRef = (0, import_react8.useRef)(null);
7544
+ const editStylesRef = (0, import_react8.useRef)(null);
7545
+ const activateRef = (0, import_react8.useRef)(() => {
6693
7546
  });
6694
- const deactivateRef = (0, import_react6.useRef)(() => {
7547
+ const deactivateRef = (0, import_react8.useRef)(() => {
6695
7548
  });
6696
- const selectRef = (0, import_react6.useRef)(() => {
7549
+ const selectRef = (0, import_react8.useRef)(() => {
6697
7550
  });
6698
- const deselectRef = (0, import_react6.useRef)(() => {
7551
+ const selectFrameRef = (0, import_react8.useRef)(() => {
6699
7552
  });
6700
- const reselectNavigationItemRef = (0, import_react6.useRef)(() => {
7553
+ const deselectRef = (0, import_react8.useRef)(() => {
6701
7554
  });
6702
- const commitNavigationTextEditRef = (0, import_react6.useRef)(() => {
7555
+ const reselectNavigationItemRef = (0, import_react8.useRef)(() => {
6703
7556
  });
6704
- const refreshActiveCommandsRef = (0, import_react6.useRef)(() => {
7557
+ const commitNavigationTextEditRef = (0, import_react8.useRef)(() => {
6705
7558
  });
6706
- const postToParentRef = (0, import_react6.useRef)(postToParent);
6707
- postToParentRef.current = postToParent;
6708
- const sectionsLoadedRef = (0, import_react6.useRef)(false);
6709
- const pendingScheduleConfigRequests = (0, import_react6.useRef)([]);
6710
- const [toolbarRect, setToolbarRect] = (0, import_react6.useState)(null);
6711
- const [toolbarVariant, setToolbarVariant] = (0, import_react6.useState)("none");
6712
- const toolbarVariantRef = (0, import_react6.useRef)("none");
7559
+ const refreshActiveCommandsRef = (0, import_react8.useRef)(() => {
7560
+ });
7561
+ const postToParentRef = (0, import_react8.useRef)(postToParent2);
7562
+ postToParentRef.current = postToParent2;
7563
+ const sectionsLoadedRef = (0, import_react8.useRef)(false);
7564
+ const pendingScheduleConfigRequests = (0, import_react8.useRef)([]);
7565
+ const [toolbarRect, setToolbarRect] = (0, import_react8.useState)(null);
7566
+ const [toolbarVariant, setToolbarVariant] = (0, import_react8.useState)("none");
7567
+ const toolbarVariantRef = (0, import_react8.useRef)("none");
6713
7568
  toolbarVariantRef.current = toolbarVariant;
6714
- const [reorderHrefKey, setReorderHrefKey] = (0, import_react6.useState)(null);
6715
- const [reorderDragDisabled, setReorderDragDisabled] = (0, import_react6.useState)(false);
6716
- const [toggleState, setToggleState] = (0, import_react6.useState)(null);
6717
- const [maxBadge, setMaxBadge] = (0, import_react6.useState)(null);
6718
- const [activeCommands, setActiveCommands] = (0, import_react6.useState)(/* @__PURE__ */ new Set());
6719
- const [sectionGap, setSectionGap] = (0, import_react6.useState)(null);
6720
- const [toolbarShowEditLink, setToolbarShowEditLink] = (0, import_react6.useState)(false);
6721
- const hoveredItemElRef = (0, import_react6.useRef)(null);
6722
- const [hoveredItemRect, setHoveredItemRect] = (0, import_react6.useState)(null);
6723
- const siblingHintElRef = (0, import_react6.useRef)(null);
6724
- const [siblingHintRect, setSiblingHintRect] = (0, import_react6.useState)(null);
6725
- const [isItemDragging, setIsItemDragging] = (0, import_react6.useState)(false);
6726
- const [linkPopover, setLinkPopover] = (0, import_react6.useState)(null);
6727
- const [sitePages, setSitePages] = (0, import_react6.useState)([]);
6728
- const [sectionsByPath, setSectionsByPath] = (0, import_react6.useState)({});
6729
- const sectionsPrefetchGenRef = (0, import_react6.useRef)(0);
6730
- const setLinkPopoverRef = (0, import_react6.useRef)(setLinkPopover);
6731
- const linkPopoverPanelRef = (0, import_react6.useRef)(null);
6732
- const linkPopoverOpenRef = (0, import_react6.useRef)(false);
6733
- const linkPopoverGraceUntilRef = (0, import_react6.useRef)(0);
7569
+ const [reorderHrefKey, setReorderHrefKey] = (0, import_react8.useState)(null);
7570
+ const [reorderDragDisabled, setReorderDragDisabled] = (0, import_react8.useState)(false);
7571
+ const [toggleState, setToggleState] = (0, import_react8.useState)(null);
7572
+ const [maxBadge, setMaxBadge] = (0, import_react8.useState)(null);
7573
+ const [activeCommands, setActiveCommands] = (0, import_react8.useState)(/* @__PURE__ */ new Set());
7574
+ const [sectionGap, setSectionGap] = (0, import_react8.useState)(null);
7575
+ const [toolbarShowEditLink, setToolbarShowEditLink] = (0, import_react8.useState)(false);
7576
+ const hoveredNavContainerRef = (0, import_react8.useRef)(null);
7577
+ const [hoveredNavContainerRect, setHoveredNavContainerRect] = (0, import_react8.useState)(null);
7578
+ const hoveredItemElRef = (0, import_react8.useRef)(null);
7579
+ const [hoveredItemRect, setHoveredItemRect] = (0, import_react8.useState)(null);
7580
+ const siblingHintElRef = (0, import_react8.useRef)(null);
7581
+ const [siblingHintRect, setSiblingHintRect] = (0, import_react8.useState)(null);
7582
+ const [isItemDragging, setIsItemDragging] = (0, import_react8.useState)(false);
7583
+ const [linkPopover, setLinkPopover] = (0, import_react8.useState)(null);
7584
+ const linkPopoverSessionRef = (0, import_react8.useRef)(null);
7585
+ const addNavAfterAnchorRef = (0, import_react8.useRef)(null);
7586
+ const editContentRef = (0, import_react8.useRef)({});
7587
+ const [sitePages, setSitePages] = (0, import_react8.useState)([]);
7588
+ const [sectionsByPath, setSectionsByPath] = (0, import_react8.useState)({});
7589
+ const sectionsPrefetchGenRef = (0, import_react8.useRef)(0);
7590
+ const setLinkPopoverRef = (0, import_react8.useRef)(setLinkPopover);
7591
+ const linkPopoverPanelRef = (0, import_react8.useRef)(null);
7592
+ const linkPopoverOpenRef = (0, import_react8.useRef)(false);
7593
+ const linkPopoverGraceUntilRef = (0, import_react8.useRef)(0);
6734
7594
  setLinkPopoverRef.current = setLinkPopover;
7595
+ linkPopoverSessionRef.current = linkPopover;
6735
7596
  const bumpLinkPopoverGrace = () => {
6736
7597
  linkPopoverGraceUntilRef.current = Date.now() + 350;
6737
7598
  };
6738
- const runSectionsPrefetch = (0, import_react6.useCallback)((pages) => {
7599
+ const runSectionsPrefetch = (0, import_react8.useCallback)((pages) => {
6739
7600
  if (!isEditMode || shouldUseDevFixtures() || pages.length === 0) return;
6740
7601
  const gen = ++sectionsPrefetchGenRef.current;
6741
7602
  const paths = pages.map((p) => p.path);
@@ -6754,9 +7615,9 @@ function OhhwellsBridge() {
6754
7615
  );
6755
7616
  });
6756
7617
  }, [isEditMode, pathname]);
6757
- const runSectionsPrefetchRef = (0, import_react6.useRef)(runSectionsPrefetch);
7618
+ const runSectionsPrefetchRef = (0, import_react8.useRef)(runSectionsPrefetch);
6758
7619
  runSectionsPrefetchRef.current = runSectionsPrefetch;
6759
- (0, import_react6.useEffect)(() => {
7620
+ (0, import_react8.useEffect)(() => {
6760
7621
  if (!linkPopover) return;
6761
7622
  if (hoveredImageRef.current) {
6762
7623
  hoveredImageRef.current = null;
@@ -6765,7 +7626,7 @@ function OhhwellsBridge() {
6765
7626
  hoveredGapRef.current = null;
6766
7627
  setSectionGap(null);
6767
7628
  setMediaHover(null);
6768
- postToParent({ type: "ow:link-modal-lock", locked: true });
7629
+ postToParent2({ type: "ow:link-modal-lock", locked: true });
6769
7630
  const html = document.documentElement;
6770
7631
  const body = document.body;
6771
7632
  const prevHtmlOverflow = html.style.overflow;
@@ -6782,15 +7643,16 @@ function OhhwellsBridge() {
6782
7643
  const scrollOpts = { passive: false, capture: true };
6783
7644
  document.addEventListener("wheel", preventBackgroundScroll, scrollOpts);
6784
7645
  document.addEventListener("touchmove", preventBackgroundScroll, scrollOpts);
7646
+ postToParent2({ type: "ow:image-unhover" });
6785
7647
  return () => {
6786
- postToParent({ type: "ow:link-modal-lock", locked: false });
7648
+ postToParent2({ type: "ow:link-modal-lock", locked: false });
6787
7649
  html.style.overflow = prevHtmlOverflow;
6788
7650
  body.style.overflow = prevBodyOverflow;
6789
7651
  document.removeEventListener("wheel", preventBackgroundScroll, scrollOpts);
6790
7652
  document.removeEventListener("touchmove", preventBackgroundScroll, scrollOpts);
6791
7653
  };
6792
- }, [linkPopover, postToParent]);
6793
- (0, import_react6.useEffect)(() => {
7654
+ }, [linkPopover, postToParent2]);
7655
+ (0, import_react8.useEffect)(() => {
6794
7656
  if (!isEditMode) return;
6795
7657
  const useFixtures = shouldUseDevFixtures();
6796
7658
  if (useFixtures) {
@@ -6811,17 +7673,17 @@ function OhhwellsBridge() {
6811
7673
  runSectionsPrefetchRef.current(mapped);
6812
7674
  };
6813
7675
  window.addEventListener("message", onSitePages);
6814
- if (!useFixtures) postToParent({ type: "ow:request-site-pages" });
7676
+ if (!useFixtures) postToParent2({ type: "ow:request-site-pages" });
6815
7677
  return () => window.removeEventListener("message", onSitePages);
6816
- }, [isEditMode, postToParent]);
6817
- (0, import_react6.useEffect)(() => {
7678
+ }, [isEditMode, postToParent2]);
7679
+ (0, import_react8.useEffect)(() => {
6818
7680
  if (!isEditMode || shouldUseDevFixtures()) return;
6819
7681
  void loadAllSectionsManifest().then((manifest) => {
6820
7682
  if (Object.keys(manifest).length === 0) return;
6821
7683
  setSectionsByPath((prev) => ({ ...manifest, ...prev }));
6822
7684
  });
6823
7685
  }, [isEditMode]);
6824
- (0, import_react6.useEffect)(() => {
7686
+ (0, import_react8.useEffect)(() => {
6825
7687
  const update = () => {
6826
7688
  const el = activeElRef.current ?? selectedElRef.current;
6827
7689
  if (el) setToolbarRect(getEditMeasureEl(el).getBoundingClientRect());
@@ -6845,10 +7707,10 @@ function OhhwellsBridge() {
6845
7707
  vvp.removeEventListener("resize", update);
6846
7708
  };
6847
7709
  }, []);
6848
- const refreshStateRules = (0, import_react6.useCallback)(() => {
7710
+ const refreshStateRules = (0, import_react8.useCallback)(() => {
6849
7711
  editStylesRef.current?.forceHover && (editStylesRef.current.forceHover.textContent = collectStateRules());
6850
7712
  }, []);
6851
- const processConfigRequest = (0, import_react6.useCallback)((insertAfterVal) => {
7713
+ const processConfigRequest = (0, import_react8.useCallback)((insertAfterVal) => {
6852
7714
  const tracker = getSectionsTracker();
6853
7715
  let entries = [];
6854
7716
  try {
@@ -6871,7 +7733,7 @@ function OhhwellsBridge() {
6871
7733
  }
6872
7734
  window.postMessage({ type: "ow:schedule-config", insertAfter: insertAfterVal, scheduleId: null }, "*");
6873
7735
  }, [isEditMode]);
6874
- const deactivate = (0, import_react6.useCallback)(() => {
7736
+ const deactivate = (0, import_react8.useCallback)(() => {
6875
7737
  const el = activeElRef.current;
6876
7738
  if (!el) return;
6877
7739
  const key = el.dataset.ohwKey;
@@ -6900,21 +7762,23 @@ function OhhwellsBridge() {
6900
7762
  setMaxBadge(null);
6901
7763
  setActiveCommands(/* @__PURE__ */ new Set());
6902
7764
  setToolbarShowEditLink(false);
6903
- postToParent({ type: "ow:exit-edit" });
6904
- }, [postToParent]);
6905
- const deselect = (0, import_react6.useCallback)(() => {
7765
+ postToParent2({ type: "ow:exit-edit" });
7766
+ }, [postToParent2]);
7767
+ const deselect = (0, import_react8.useCallback)(() => {
6906
7768
  selectedElRef.current = null;
6907
7769
  setReorderHrefKey(null);
6908
7770
  setReorderDragDisabled(false);
6909
7771
  siblingHintElRef.current = null;
6910
7772
  setSiblingHintRect(null);
6911
7773
  setIsItemDragging(false);
7774
+ hoveredNavContainerRef.current = null;
7775
+ setHoveredNavContainerRect(null);
6912
7776
  if (!activeElRef.current) {
6913
7777
  setToolbarRect(null);
6914
7778
  setToolbarVariant("none");
6915
7779
  }
6916
7780
  }, []);
6917
- const reselectNavigationItem = (0, import_react6.useCallback)((navAnchor) => {
7781
+ const reselectNavigationItem = (0, import_react8.useCallback)((navAnchor) => {
6918
7782
  selectedElRef.current = navAnchor;
6919
7783
  const { key, disabled } = getNavigationItemReorderState(navAnchor);
6920
7784
  setReorderHrefKey(key);
@@ -6924,7 +7788,7 @@ function OhhwellsBridge() {
6924
7788
  setToolbarShowEditLink(false);
6925
7789
  setActiveCommands(/* @__PURE__ */ new Set());
6926
7790
  }, []);
6927
- const commitNavigationTextEdit = (0, import_react6.useCallback)((navAnchor) => {
7791
+ const commitNavigationTextEdit = (0, import_react8.useCallback)((navAnchor) => {
6928
7792
  const el = activeElRef.current;
6929
7793
  if (!el) return;
6930
7794
  const key = el.dataset.ohwKey;
@@ -6937,9 +7801,9 @@ function OhhwellsBridge() {
6937
7801
  const html = sanitizeHtml(el.innerHTML);
6938
7802
  const original = originalContentRef.current ?? "";
6939
7803
  if (html !== sanitizeHtml(original)) {
6940
- postToParent({ type: "ow:change", nodes: [{ key, text: html }] });
7804
+ postToParent2({ type: "ow:change", nodes: [{ key, text: html }] });
6941
7805
  const h = document.documentElement.scrollHeight;
6942
- if (h > 50) postToParent({ type: "ow:height", height: h });
7806
+ if (h > 50) postToParent2({ type: "ow:height", height: h });
6943
7807
  }
6944
7808
  }
6945
7809
  el.removeAttribute("contenteditable");
@@ -6947,31 +7811,38 @@ function OhhwellsBridge() {
6947
7811
  setMaxBadge(null);
6948
7812
  setActiveCommands(/* @__PURE__ */ new Set());
6949
7813
  setToolbarShowEditLink(false);
6950
- postToParent({ type: "ow:exit-edit" });
7814
+ postToParent2({ type: "ow:exit-edit" });
6951
7815
  reselectNavigationItem(navAnchor);
6952
- }, [postToParent, reselectNavigationItem]);
6953
- const handleAddNavigationItem = (0, import_react6.useCallback)(() => {
6954
- const selected = selectedElRef.current;
6955
- if (!selected) return;
6956
- const target = getNavigationItemAddHintTarget(selected);
6957
- siblingHintElRef.current = target;
6958
- setSiblingHintRect(target.getBoundingClientRect());
7816
+ }, [postToParent2, reselectNavigationItem]);
7817
+ const handleAddTopLevelNavItem = (0, import_react8.useCallback)(() => {
7818
+ const items = listNavbarItems();
7819
+ addNavAfterAnchorRef.current = items[items.length - 1] ?? null;
7820
+ deselectRef.current();
7821
+ deactivateRef.current();
7822
+ bumpLinkPopoverGrace();
7823
+ setLinkPopover({
7824
+ key: "__add-nav__",
7825
+ mode: "create",
7826
+ intent: "add-nav"
7827
+ });
6959
7828
  }, []);
6960
- const handleItemDragStart = (0, import_react6.useCallback)(() => {
7829
+ const handleItemDragStart = (0, import_react8.useCallback)(() => {
6961
7830
  siblingHintElRef.current = null;
6962
7831
  setSiblingHintRect(null);
6963
7832
  setIsItemDragging(true);
6964
7833
  }, []);
6965
- const handleItemDragEnd = (0, import_react6.useCallback)(() => {
7834
+ const handleItemDragEnd = (0, import_react8.useCallback)(() => {
6966
7835
  setIsItemDragging(false);
6967
7836
  }, []);
6968
7837
  reselectNavigationItemRef.current = reselectNavigationItem;
6969
7838
  commitNavigationTextEditRef.current = commitNavigationTextEdit;
6970
- const select = (0, import_react6.useCallback)((anchor) => {
7839
+ const select = (0, import_react8.useCallback)((anchor) => {
6971
7840
  if (!isNavigationItem(anchor)) return;
6972
7841
  if (activeElRef.current) deactivate();
6973
7842
  selectedElRef.current = anchor;
6974
7843
  clearHrefKeyHover(anchor);
7844
+ hoveredNavContainerRef.current = null;
7845
+ setHoveredNavContainerRect(null);
6975
7846
  setHoveredItemRect(null);
6976
7847
  hoveredItemElRef.current = null;
6977
7848
  siblingHintElRef.current = null;
@@ -6985,7 +7856,26 @@ function OhhwellsBridge() {
6985
7856
  setToolbarShowEditLink(false);
6986
7857
  setActiveCommands(/* @__PURE__ */ new Set());
6987
7858
  }, [deactivate]);
6988
- const activate = (0, import_react6.useCallback)((el) => {
7859
+ const selectFrame = (0, import_react8.useCallback)((el) => {
7860
+ if (!isNavigationContainer(el)) return;
7861
+ if (activeElRef.current) deactivate();
7862
+ selectedElRef.current = el;
7863
+ clearHrefKeyHover(el);
7864
+ hoveredNavContainerRef.current = null;
7865
+ setHoveredNavContainerRect(null);
7866
+ setHoveredItemRect(null);
7867
+ hoveredItemElRef.current = null;
7868
+ siblingHintElRef.current = null;
7869
+ setSiblingHintRect(null);
7870
+ setIsItemDragging(false);
7871
+ setReorderHrefKey(null);
7872
+ setReorderDragDisabled(false);
7873
+ setToolbarVariant("select-frame");
7874
+ setToolbarRect(el.getBoundingClientRect());
7875
+ setToolbarShowEditLink(false);
7876
+ setActiveCommands(/* @__PURE__ */ new Set());
7877
+ }, [deactivate]);
7878
+ const activate = (0, import_react8.useCallback)((el, options) => {
6989
7879
  if (activeElRef.current === el) return;
6990
7880
  selectedElRef.current = null;
6991
7881
  deactivate();
@@ -7003,6 +7893,9 @@ function OhhwellsBridge() {
7003
7893
  activeElRef.current = el;
7004
7894
  originalContentRef.current = el.innerHTML;
7005
7895
  el.focus();
7896
+ if (options?.caretX !== void 0 && options?.caretY !== void 0) {
7897
+ placeCaretAtPoint(el, options.caretX, options.caretY);
7898
+ }
7006
7899
  setToolbarShowEditLink(Boolean(getHrefKeyFromElement(el)));
7007
7900
  const navAnchor = getNavigationItemAnchor(el);
7008
7901
  if (navAnchor) {
@@ -7014,14 +7907,15 @@ function OhhwellsBridge() {
7014
7907
  setReorderDragDisabled(reorderDisabled);
7015
7908
  }
7016
7909
  setToolbarRect(getEditMeasureEl(el).getBoundingClientRect());
7017
- postToParent({ type: "ow:enter-edit", key: el.dataset.ohwKey });
7910
+ postToParent2({ type: "ow:enter-edit", key: el.dataset.ohwKey });
7018
7911
  requestAnimationFrame(() => refreshActiveCommandsRef.current());
7019
- }, [deactivate, postToParent]);
7912
+ }, [deactivate, postToParent2]);
7020
7913
  activateRef.current = activate;
7021
7914
  deactivateRef.current = deactivate;
7022
7915
  selectRef.current = select;
7916
+ selectFrameRef.current = selectFrame;
7023
7917
  deselectRef.current = deselect;
7024
- (0, import_react6.useLayoutEffect)(() => {
7918
+ (0, import_react8.useLayoutEffect)(() => {
7025
7919
  if (!subdomain || isEditMode) {
7026
7920
  setFetchState("done");
7027
7921
  return;
@@ -7061,6 +7955,7 @@ function OhhwellsBridge() {
7061
7955
  });
7062
7956
  applyLinkByKey(key, val);
7063
7957
  }
7958
+ reconcileNavbarItemsFromContent(content);
7064
7959
  enforceLinkHrefs();
7065
7960
  initSectionsFromContent(content, true);
7066
7961
  sectionsLoadedRef.current = true;
@@ -7089,7 +7984,7 @@ function OhhwellsBridge() {
7089
7984
  cancelled = true;
7090
7985
  };
7091
7986
  }, [subdomain, isEditMode]);
7092
- (0, import_react6.useEffect)(() => {
7987
+ (0, import_react8.useEffect)(() => {
7093
7988
  if (!subdomain || isEditMode) return;
7094
7989
  let debounceTimer = null;
7095
7990
  let observer = null;
@@ -7120,6 +8015,7 @@ function OhhwellsBridge() {
7120
8015
  });
7121
8016
  applyLinkByKey(key, val);
7122
8017
  }
8018
+ reconcileNavbarItemsFromContent(content);
7123
8019
  } finally {
7124
8020
  observer?.observe(document.body, { childList: true, subtree: true });
7125
8021
  }
@@ -7137,26 +8033,44 @@ function OhhwellsBridge() {
7137
8033
  if (debounceTimer) clearTimeout(debounceTimer);
7138
8034
  };
7139
8035
  }, [subdomain, isEditMode, pathname]);
7140
- (0, import_react6.useLayoutEffect)(() => {
8036
+ (0, import_react8.useLayoutEffect)(() => {
7141
8037
  const el = document.getElementById("ohw-loader");
7142
8038
  if (!el) return;
7143
8039
  const visible = Boolean(subdomain) && fetchState !== "done";
7144
8040
  el.style.display = visible ? "flex" : "none";
7145
8041
  }, [subdomain, fetchState]);
7146
- (0, import_react6.useEffect)(() => {
7147
- postToParent({ type: "ow:navigation", path: pathname });
7148
- }, [pathname, postToParent]);
7149
- (0, import_react6.useEffect)(() => {
8042
+ (0, import_react8.useEffect)(() => {
8043
+ postToParent2({ type: "ow:navigation", path: pathname });
8044
+ }, [pathname, postToParent2]);
8045
+ (0, import_react8.useEffect)(() => {
7150
8046
  if (!isEditMode) return;
8047
+ if (linkPopoverSessionRef.current?.intent === "add-nav") return;
8048
+ if (document.querySelector("[data-ohw-section-picker]")) return;
7151
8049
  setLinkPopover(null);
7152
8050
  deselectRef.current();
7153
8051
  deactivateRef.current();
7154
8052
  }, [pathname, isEditMode]);
7155
- (0, import_react6.useEffect)(() => {
8053
+ (0, import_react8.useEffect)(() => {
8054
+ const contentForNav = () => {
8055
+ if (isEditMode) return editContentRef.current;
8056
+ if (!subdomain) return {};
8057
+ return contentCache.get(subdomain) ?? {};
8058
+ };
8059
+ const run = () => reconcileNavbarItemsFromContent(contentForNav());
8060
+ run();
8061
+ const nav = document.querySelector("nav");
8062
+ if (!nav) return;
8063
+ const observer = new MutationObserver(() => {
8064
+ requestAnimationFrame(run);
8065
+ });
8066
+ observer.observe(nav, { childList: true, subtree: true });
8067
+ return () => observer.disconnect();
8068
+ }, [isEditMode, pathname, subdomain, fetchState]);
8069
+ (0, import_react8.useEffect)(() => {
7156
8070
  if (!isEditMode) return;
7157
8071
  const measure = () => {
7158
8072
  const h = document.body.scrollHeight;
7159
- if (h > 50) postToParent({ type: "ow:height", height: h });
8073
+ if (h > 50) postToParent2({ type: "ow:height", height: h });
7160
8074
  };
7161
8075
  const t1 = setTimeout(measure, 50);
7162
8076
  const t2 = setTimeout(measure, 500);
@@ -7175,8 +8089,8 @@ function OhhwellsBridge() {
7175
8089
  if (resizeTimer) clearTimeout(resizeTimer);
7176
8090
  window.removeEventListener("resize", handleResize);
7177
8091
  };
7178
- }, [pathname, isEditMode, postToParent]);
7179
- (0, import_react6.useEffect)(() => {
8092
+ }, [pathname, isEditMode, postToParent2]);
8093
+ (0, import_react8.useEffect)(() => {
7180
8094
  if (!subdomainFromQuery || isEditMode) return;
7181
8095
  const handleClick = (e) => {
7182
8096
  const anchor = e.target.closest("a");
@@ -7192,7 +8106,7 @@ function OhhwellsBridge() {
7192
8106
  document.addEventListener("click", handleClick, true);
7193
8107
  return () => document.removeEventListener("click", handleClick, true);
7194
8108
  }, [subdomainFromQuery, isEditMode, router]);
7195
- (0, import_react6.useEffect)(() => {
8109
+ (0, import_react8.useEffect)(() => {
7196
8110
  if (!isEditMode) {
7197
8111
  editStylesRef.current?.base.remove();
7198
8112
  editStylesRef.current?.forceHover.remove();
@@ -7265,6 +8179,13 @@ function OhhwellsBridge() {
7265
8179
  if (target.closest("[data-ohw-max-badge]")) return;
7266
8180
  if (isInsideLinkEditor(target)) return;
7267
8181
  if (target.closest('[data-ohw-edit-chrome], [data-ohw-item-interaction], [data-ohw-drag-handle-container], [data-slot="drag-handle"]')) {
8182
+ const navFromChrome = resolveNavContainerSelectionTarget(target, e.clientX, e.clientY);
8183
+ if (navFromChrome) {
8184
+ e.preventDefault();
8185
+ e.stopPropagation();
8186
+ selectFrameRef.current(navFromChrome);
8187
+ return;
8188
+ }
7268
8189
  e.preventDefault();
7269
8190
  e.stopPropagation();
7270
8191
  return;
@@ -7279,7 +8200,8 @@ function OhhwellsBridge() {
7279
8200
  bumpLinkPopoverGrace();
7280
8201
  setLinkPopoverRef.current({
7281
8202
  key: editable.dataset.ohwKey ?? "",
7282
- target: getLinkHref(editable)
8203
+ mode: "edit",
8204
+ target: getLinkHref2(editable)
7283
8205
  });
7284
8206
  return;
7285
8207
  }
@@ -7295,7 +8217,7 @@ function OhhwellsBridge() {
7295
8217
  e.preventDefault();
7296
8218
  e.stopPropagation();
7297
8219
  if (selectedElRef.current === navAnchor) {
7298
- activateRef.current(editable);
8220
+ activateRef.current(editable, { caretX: e.clientX, caretY: e.clientY });
7299
8221
  return;
7300
8222
  }
7301
8223
  selectRef.current(navAnchor);
@@ -7314,6 +8236,22 @@ function OhhwellsBridge() {
7314
8236
  selectRef.current(hrefAnchor);
7315
8237
  return;
7316
8238
  }
8239
+ const navContainerToSelect = resolveNavContainerSelectionTarget(target, e.clientX, e.clientY);
8240
+ if (navContainerToSelect) {
8241
+ e.preventDefault();
8242
+ e.stopPropagation();
8243
+ selectFrameRef.current(navContainerToSelect);
8244
+ return;
8245
+ }
8246
+ const selectedContainer = selectedElRef.current;
8247
+ if (selectedContainer && isNavigationContainer(selectedContainer)) {
8248
+ const navItem = getNavigationItemAnchor(target);
8249
+ if (!navItem && (selectedContainer === target || selectedContainer.contains(target))) {
8250
+ e.preventDefault();
8251
+ e.stopPropagation();
8252
+ return;
8253
+ }
8254
+ }
7317
8255
  if (activeElRef.current) {
7318
8256
  const sel = window.getSelection();
7319
8257
  if (sel && !sel.isCollapsed) {
@@ -7340,9 +8278,26 @@ function OhhwellsBridge() {
7340
8278
  deactivateRef.current();
7341
8279
  };
7342
8280
  const handleMouseOver = (e) => {
8281
+ if (document.documentElement.hasAttribute("data-ohw-section-picking")) return;
7343
8282
  const target = e.target;
8283
+ if (toolbarVariantRef.current !== "select-frame") {
8284
+ const navContainer = target.closest("[data-ohw-nav-container]");
8285
+ if (navContainer && !getNavigationItemAnchor(target)) {
8286
+ hoveredNavContainerRef.current = navContainer;
8287
+ setHoveredNavContainerRect(navContainer.getBoundingClientRect());
8288
+ hoveredItemElRef.current = null;
8289
+ setHoveredItemRect(null);
8290
+ return;
8291
+ }
8292
+ if (!target.closest("[data-ohw-nav-container]")) {
8293
+ hoveredNavContainerRef.current = null;
8294
+ setHoveredNavContainerRect(null);
8295
+ }
8296
+ }
7344
8297
  const navAnchor = getNavigationItemAnchor(target);
7345
8298
  if (navAnchor) {
8299
+ hoveredNavContainerRef.current = null;
8300
+ setHoveredNavContainerRect(null);
7346
8301
  const selected2 = selectedElRef.current;
7347
8302
  if (selected2 === navAnchor || selected2?.contains(navAnchor)) return;
7348
8303
  clearHrefKeyHover(navAnchor);
@@ -7367,6 +8322,14 @@ function OhhwellsBridge() {
7367
8322
  };
7368
8323
  const handleMouseOut = (e) => {
7369
8324
  const target = e.target;
8325
+ if (target.closest("[data-ohw-nav-container]")) {
8326
+ const related2 = e.relatedTarget instanceof Element ? e.relatedTarget : null;
8327
+ if (related2?.closest("[data-ohw-nav-container], [data-ohw-navbar-container-chrome], [data-ohw-navbar-add-button]")) {
8328
+ return;
8329
+ }
8330
+ hoveredNavContainerRef.current = null;
8331
+ setHoveredNavContainerRect(null);
8332
+ }
7370
8333
  const navAnchor = getNavigationItemAnchor(target);
7371
8334
  if (navAnchor) {
7372
8335
  const related2 = e.relatedTarget instanceof Element ? e.relatedTarget : null;
@@ -7488,6 +8451,56 @@ function OhhwellsBridge() {
7488
8451
  }
7489
8452
  return smallest;
7490
8453
  };
8454
+ const dismissImageHover = () => {
8455
+ if (hoveredImageRef.current) {
8456
+ hoveredImageRef.current = null;
8457
+ hoveredImageHasTextOverlapRef.current = false;
8458
+ resumeAnimTracks();
8459
+ postToParentRef.current({ type: "ow:image-unhover" });
8460
+ }
8461
+ };
8462
+ const probeNavigationHoverAt = (x, y) => {
8463
+ if (toolbarVariantRef.current === "select-frame") {
8464
+ hoveredNavContainerRef.current = null;
8465
+ setHoveredNavContainerRect(null);
8466
+ return;
8467
+ }
8468
+ const navContainer = document.querySelector("[data-ohw-nav-container]");
8469
+ if (!navContainer) {
8470
+ hoveredNavContainerRef.current = null;
8471
+ setHoveredNavContainerRect(null);
8472
+ return;
8473
+ }
8474
+ const containerRect = navContainer.getBoundingClientRect();
8475
+ const overContainer = x >= containerRect.left && x <= containerRect.right && y >= containerRect.top && y <= containerRect.bottom;
8476
+ if (!overContainer) {
8477
+ hoveredNavContainerRef.current = null;
8478
+ setHoveredNavContainerRect(null);
8479
+ return;
8480
+ }
8481
+ const navItemHit = Array.from(
8482
+ navContainer.querySelectorAll("[data-ohw-href-key]")
8483
+ ).find((el) => {
8484
+ if (!isNavigationItem(el) || isNavbarButton2(el)) return false;
8485
+ const itemRect = el.getBoundingClientRect();
8486
+ return x >= itemRect.left && x <= itemRect.right && y >= itemRect.top && y <= itemRect.bottom;
8487
+ });
8488
+ if (navItemHit) {
8489
+ hoveredNavContainerRef.current = null;
8490
+ setHoveredNavContainerRect(null);
8491
+ const selected = selectedElRef.current;
8492
+ if (selected !== navItemHit && !selected?.contains(navItemHit)) {
8493
+ clearHrefKeyHover(navItemHit);
8494
+ hoveredItemElRef.current = navItemHit;
8495
+ setHoveredItemRect(navItemHit.getBoundingClientRect());
8496
+ }
8497
+ return;
8498
+ }
8499
+ hoveredNavContainerRef.current = navContainer;
8500
+ setHoveredNavContainerRect(containerRect);
8501
+ hoveredItemElRef.current = null;
8502
+ setHoveredItemRect(null);
8503
+ };
7491
8504
  const probeImageAt = (clientX, clientY, isDragOver = false, fromParentViewport = false) => {
7492
8505
  if (linkPopoverOpenRef.current) {
7493
8506
  if (hoveredImageRef.current) {
@@ -7496,18 +8509,22 @@ function OhhwellsBridge() {
7496
8509
  resumeAnimTracks();
7497
8510
  clearImageHover();
7498
8511
  }
8512
+ dismissImageHover();
8513
+ return;
8514
+ }
8515
+ const { x, y } = toProbeCoords(clientX, clientY, fromParentViewport);
8516
+ if (isPointOverNavigation(x, y)) {
8517
+ dismissImageHover();
8518
+ probeNavigationHoverAt(x, y);
7499
8519
  return;
7500
8520
  }
8521
+ hoveredNavContainerRef.current = null;
8522
+ setHoveredNavContainerRect(null);
7501
8523
  const toggleEl = document.querySelector("[data-ohw-state-toggle]");
7502
8524
  if (toggleEl) {
7503
- const { x, y } = toProbeCoords(clientX, clientY, fromParentViewport);
7504
8525
  const tr = toggleEl.getBoundingClientRect();
7505
8526
  if (x >= tr.left && x <= tr.right && y >= tr.top && y <= tr.bottom) {
7506
- if (hoveredImageRef.current) {
7507
- hoveredImageRef.current = null;
7508
- resumeAnimTracks();
7509
- clearImageHover();
7510
- }
8527
+ dismissImageHover();
7511
8528
  return;
7512
8529
  }
7513
8530
  }
@@ -7520,15 +8537,18 @@ function OhhwellsBridge() {
7520
8537
  resumeAnimTracks();
7521
8538
  clearImageHover();
7522
8539
  }
7523
- return;
8540
+ if (activeElRef.current) {
8541
+ dismissImageHover();
8542
+ return;
8543
+ }
7524
8544
  }
7525
8545
  if (imgEl) {
7526
- const { x, y } = toProbeCoords(clientX, clientY, fromParentViewport);
8546
+ const { x: x2, y: y2 } = toProbeCoords(clientX, clientY, fromParentViewport);
7527
8547
  const overMediaChrome = Array.from(
7528
8548
  document.querySelectorAll("[data-ohw-media-chrome]")
7529
8549
  ).some((el) => {
7530
8550
  const r2 = el.getBoundingClientRect();
7531
- return x >= r2.left && x <= r2.right && y >= r2.top && y <= r2.bottom;
8551
+ return x2 >= r2.left && x2 <= r2.right && y2 >= r2.top && y2 <= r2.bottom;
7532
8552
  });
7533
8553
  if (overMediaChrome) {
7534
8554
  if (hoveredImageRef.current) {
@@ -7539,7 +8559,7 @@ function OhhwellsBridge() {
7539
8559
  }
7540
8560
  return;
7541
8561
  }
7542
- const topEl = document.elementFromPoint(x, y);
8562
+ const topEl = document.elementFromPoint(x2, y2);
7543
8563
  if (topEl?.closest('[data-ohw-toolbar], [data-ohw-edit-chrome], [data-ohw-item-interaction], [data-ohw-drag-handle-container], [data-slot="drag-handle"]')) {
7544
8564
  if (hoveredImageRef.current) {
7545
8565
  hoveredImageRef.current = null;
@@ -7562,7 +8582,7 @@ function OhhwellsBridge() {
7562
8582
  document.querySelectorAll(NON_MEDIA_SELECTOR)
7563
8583
  ).find((el) => {
7564
8584
  const er = el.getBoundingClientRect();
7565
- return x >= er.left && x <= er.right && y >= er.top && y <= er.bottom;
8585
+ return x2 >= er.left && x2 <= er.right && y2 >= er.top && y2 <= er.bottom;
7566
8586
  });
7567
8587
  if (imageUnhoverTimerRef.current) {
7568
8588
  clearTimeout(imageUnhoverTimerRef.current);
@@ -7638,12 +8658,12 @@ function OhhwellsBridge() {
7638
8658
  resumeAnimTracks();
7639
8659
  clearImageHover();
7640
8660
  }
7641
- const { x, y } = toProbeCoords(clientX, clientY, fromParentViewport);
8661
+ const { x: x2, y: y2 } = toProbeCoords(clientX, clientY, fromParentViewport);
7642
8662
  const textEl = Array.from(
7643
8663
  document.querySelectorAll(NON_MEDIA_SELECTOR)
7644
8664
  ).find((el) => {
7645
8665
  const er = el.getBoundingClientRect();
7646
- return x >= er.left && x <= er.right && y >= er.top && y <= er.bottom;
8666
+ return x2 >= er.left && x2 <= er.right && y2 >= er.top && y2 <= er.bottom;
7647
8667
  });
7648
8668
  document.querySelectorAll("[data-ohw-hovered]").forEach((el) => el.removeAttribute("data-ohw-hovered"));
7649
8669
  if (textEl && !textEl.hasAttribute("contenteditable")) {
@@ -7667,6 +8687,14 @@ function OhhwellsBridge() {
7667
8687
  }
7668
8688
  };
7669
8689
  const probeHoverCardsAt = (clientX, clientY, fromParentViewport = false) => {
8690
+ if (linkPopoverOpenRef.current || document.documentElement.hasAttribute("data-ohw-section-picking")) {
8691
+ if (activeStateElRef.current) {
8692
+ activeStateElRef.current.removeAttribute("data-ohw-state-hovered");
8693
+ activeStateElRef.current = null;
8694
+ setToggleState(null);
8695
+ }
8696
+ return;
8697
+ }
7670
8698
  const { x, y } = toProbeCoords(clientX, clientY, fromParentViewport);
7671
8699
  const toggleEl = document.querySelector("[data-ohw-state-toggle]");
7672
8700
  if (toggleEl) {
@@ -7943,6 +8971,8 @@ function OhhwellsBridge() {
7943
8971
  sectionsLoadedRef.current = true;
7944
8972
  pendingScheduleConfigRequests.current.splice(0).forEach(processConfigRequest);
7945
8973
  }
8974
+ editContentRef.current = { ...editContentRef.current, ...content };
8975
+ reconcileNavbarItemsFromContent(editContentRef.current);
7946
8976
  enforceLinkHrefs();
7947
8977
  postToParentRef.current({ type: "ow:hydrate-done" });
7948
8978
  };
@@ -7950,6 +8980,7 @@ function OhhwellsBridge() {
7950
8980
  const handleDeactivate = (e) => {
7951
8981
  if (e.data?.type !== "ow:deactivate") return;
7952
8982
  if (Date.now() < linkPopoverGraceUntilRef.current) return;
8983
+ if (document.querySelector("[data-ohw-section-picker]")) return;
7953
8984
  if (linkPopoverOpenRef.current) {
7954
8985
  setLinkPopoverRef.current(null);
7955
8986
  return;
@@ -7958,13 +8989,31 @@ function OhhwellsBridge() {
7958
8989
  deactivateRef.current();
7959
8990
  };
7960
8991
  window.addEventListener("message", handleDeactivate);
8992
+ const handleUiEscape = (e) => {
8993
+ if (e.data?.type !== "ui:escape") return;
8994
+ if (linkPopoverOpenRef.current) {
8995
+ setLinkPopoverRef.current(null);
8996
+ }
8997
+ };
8998
+ window.addEventListener("message", handleUiEscape);
7961
8999
  const handleKeyDown = (e) => {
9000
+ if (e.key === "Escape" && document.querySelector("[data-ohw-section-picker]")) return;
7962
9001
  if (e.key === "Escape" && linkPopoverOpenRef.current) {
7963
9002
  setLinkPopoverRef.current(null);
7964
9003
  return;
7965
9004
  }
7966
9005
  if (e.key === "Escape" && selectedElRef.current && !activeElRef.current) {
7967
- deselectRef.current();
9006
+ if (toolbarVariantRef.current === "select-frame") {
9007
+ deselectRef.current();
9008
+ return;
9009
+ }
9010
+ const parent = getNavigationSelectionParent(selectedElRef.current);
9011
+ if (parent) {
9012
+ e.preventDefault();
9013
+ selectFrameRef.current(parent);
9014
+ } else {
9015
+ deselectRef.current();
9016
+ }
7968
9017
  return;
7969
9018
  }
7970
9019
  if (e.key === "Escape" && activeElRef.current) {
@@ -8022,6 +9071,9 @@ function OhhwellsBridge() {
8022
9071
  if (hoveredItemElRef.current) {
8023
9072
  setHoveredItemRect(hoveredItemElRef.current.getBoundingClientRect());
8024
9073
  }
9074
+ if (hoveredNavContainerRef.current) {
9075
+ setHoveredNavContainerRect(hoveredNavContainerRef.current.getBoundingClientRect());
9076
+ }
8025
9077
  if (siblingHintElRef.current) {
8026
9078
  setSiblingHintRect(siblingHintElRef.current.getBoundingClientRect());
8027
9079
  }
@@ -8218,9 +9270,45 @@ function OhhwellsBridge() {
8218
9270
  return clientX >= r2.left && clientX <= r2.right && clientY >= r2.top && clientY <= r2.bottom;
8219
9271
  });
8220
9272
  if (textEditable) {
8221
- activateRef.current(textEditable);
9273
+ const hrefCtx = getHrefKeyFromElement(textEditable);
9274
+ const navAnchor = hrefCtx ? getNavigationItemAnchor(hrefCtx.anchor) : null;
9275
+ if (navAnchor) {
9276
+ if (selectedElRef.current === navAnchor) {
9277
+ activateRef.current(textEditable, { caretX: clientX, caretY: clientY });
9278
+ } else {
9279
+ selectRef.current(navAnchor);
9280
+ }
9281
+ return;
9282
+ }
9283
+ activateRef.current(textEditable, { caretX: clientX, caretY: clientY });
8222
9284
  return;
8223
9285
  }
9286
+ const navContainer = document.querySelector("[data-ohw-nav-container]");
9287
+ if (navContainer) {
9288
+ const r2 = navContainer.getBoundingClientRect();
9289
+ const pad = 8;
9290
+ const over = clientX >= r2.left - pad && clientX <= r2.right + pad && clientY >= r2.top - pad && clientY <= r2.bottom + pad;
9291
+ if (over && !isPointOverNavItem(navContainer, clientX, clientY)) {
9292
+ selectFrameRef.current(navContainer);
9293
+ return;
9294
+ }
9295
+ }
9296
+ const navRoot = document.querySelector("[data-ohw-nav-root]");
9297
+ if (navRoot && navContainer) {
9298
+ const rr = navRoot.getBoundingClientRect();
9299
+ if (clientX >= rr.left && clientX <= rr.right && clientY >= rr.top && clientY <= rr.bottom && !isPointOverNavItem(navContainer, clientX, clientY)) {
9300
+ const book = navRoot.querySelector('[data-ohw-role="navbar-button"]');
9301
+ if (book) {
9302
+ const br = book.getBoundingClientRect();
9303
+ if (clientX >= br.left && clientX <= br.right && clientY >= br.top && clientY <= br.bottom) {
9304
+ deactivateRef.current();
9305
+ return;
9306
+ }
9307
+ }
9308
+ selectFrameRef.current(navContainer);
9309
+ return;
9310
+ }
9311
+ }
8224
9312
  deactivateRef.current();
8225
9313
  };
8226
9314
  window.addEventListener("message", handleSave);
@@ -8283,13 +9371,14 @@ function OhhwellsBridge() {
8283
9371
  window.removeEventListener("message", handleClickAt);
8284
9372
  window.removeEventListener("message", handleHydrate);
8285
9373
  window.removeEventListener("message", handleDeactivate);
9374
+ window.removeEventListener("message", handleUiEscape);
8286
9375
  autoSaveTimers.current.forEach(clearTimeout);
8287
9376
  autoSaveTimers.current.clear();
8288
9377
  if (imageUnhoverTimerRef.current) clearTimeout(imageUnhoverTimerRef.current);
8289
9378
  if (imageShowTimerRef.current) clearTimeout(imageShowTimerRef.current);
8290
9379
  };
8291
9380
  }, [isEditMode, refreshStateRules]);
8292
- (0, import_react6.useEffect)(() => {
9381
+ (0, import_react8.useEffect)(() => {
8293
9382
  const handler = (e) => {
8294
9383
  if (e.data?.type !== "ow:request-schedule-config") return;
8295
9384
  const insertAfterVal = e.data.insertAfter;
@@ -8305,7 +9394,7 @@ function OhhwellsBridge() {
8305
9394
  window.addEventListener("message", handler);
8306
9395
  return () => window.removeEventListener("message", handler);
8307
9396
  }, [processConfigRequest]);
8308
- (0, import_react6.useEffect)(() => {
9397
+ (0, import_react8.useEffect)(() => {
8309
9398
  if (!isEditMode) return;
8310
9399
  document.querySelectorAll("[data-ohw-active-state]").forEach((el) => {
8311
9400
  el.removeAttribute("data-ohw-active-state");
@@ -8326,27 +9415,27 @@ function OhhwellsBridge() {
8326
9415
  const next = { ...prev, [pathKey]: sections };
8327
9416
  return shouldUseDevFixtures() ? { ...DEV_SECTIONS_BY_PATH, ...next } : next;
8328
9417
  });
8329
- postToParent({ type: "ow:ready", version: "1", path: pathname, nodes: collectEditableNodes(), sections });
8330
- postToParent({ type: "ow:request-site-pages" });
9418
+ postToParent2({ type: "ow:ready", version: "1", path: pathname, nodes: collectEditableNodes(), sections });
9419
+ postToParent2({ type: "ow:request-site-pages" });
8331
9420
  }, 150);
8332
9421
  return () => {
8333
9422
  cancelAnimationFrame(raf);
8334
9423
  clearTimeout(timer);
8335
9424
  };
8336
- }, [pathname, isEditMode, refreshStateRules, postToParent]);
8337
- (0, import_react6.useEffect)(() => {
9425
+ }, [pathname, isEditMode, refreshStateRules, postToParent2]);
9426
+ (0, import_react8.useEffect)(() => {
8338
9427
  scrollToHashSectionWhenReady();
8339
9428
  const onHashChange = () => scrollToHashSectionWhenReady();
8340
9429
  window.addEventListener("hashchange", onHashChange);
8341
9430
  return () => window.removeEventListener("hashchange", onHashChange);
8342
9431
  }, [pathname]);
8343
- const handleCommand = (0, import_react6.useCallback)((cmd) => {
9432
+ const handleCommand = (0, import_react8.useCallback)((cmd) => {
8344
9433
  document.execCommand(cmd, false);
8345
9434
  activeElRef.current?.focus();
8346
9435
  if (activeElRef.current) setToolbarRect(getEditMeasureEl(activeElRef.current).getBoundingClientRect());
8347
9436
  refreshActiveCommandsRef.current();
8348
9437
  }, []);
8349
- const handleStateChange = (0, import_react6.useCallback)((state) => {
9438
+ const handleStateChange = (0, import_react8.useCallback)((state) => {
8350
9439
  if (!activeStateElRef.current) return;
8351
9440
  const el = activeStateElRef.current;
8352
9441
  if (state === "Default") {
@@ -8359,18 +9448,22 @@ function OhhwellsBridge() {
8359
9448
  }
8360
9449
  setToggleState((prev) => prev ? { ...prev, activeState: state } : null);
8361
9450
  }, [deactivate]);
8362
- const closeLinkPopover = (0, import_react6.useCallback)(() => setLinkPopover(null), []);
8363
- const openLinkPopoverForActive = (0, import_react6.useCallback)(() => {
9451
+ const closeLinkPopover = (0, import_react8.useCallback)(() => {
9452
+ addNavAfterAnchorRef.current = null;
9453
+ setLinkPopover(null);
9454
+ }, []);
9455
+ const openLinkPopoverForActive = (0, import_react8.useCallback)(() => {
8364
9456
  const hrefCtx = getHrefKeyFromElement(activeElRef.current);
8365
9457
  if (!hrefCtx) return;
8366
9458
  bumpLinkPopoverGrace();
8367
9459
  setLinkPopover({
8368
9460
  key: hrefCtx.key,
8369
- target: getLinkHref(hrefCtx.anchor)
9461
+ mode: "edit",
9462
+ target: getLinkHref2(hrefCtx.anchor)
8370
9463
  });
8371
9464
  deactivate();
8372
9465
  }, [deactivate]);
8373
- const openLinkPopoverForSelected = (0, import_react6.useCallback)(() => {
9466
+ const openLinkPopoverForSelected = (0, import_react8.useCallback)(() => {
8374
9467
  const anchor = selectedElRef.current;
8375
9468
  if (!anchor) return;
8376
9469
  const key = anchor.getAttribute("data-ohw-href-key");
@@ -8378,30 +9471,77 @@ function OhhwellsBridge() {
8378
9471
  bumpLinkPopoverGrace();
8379
9472
  setLinkPopover({
8380
9473
  key,
8381
- target: getLinkHref(anchor)
9474
+ mode: "edit",
9475
+ target: getLinkHref2(anchor)
8382
9476
  });
8383
9477
  deselect();
8384
9478
  }, [deselect]);
8385
- const handleLinkPopoverSubmit = (0, import_react6.useCallback)(
9479
+ const handleLinkPopoverSubmit = (0, import_react8.useCallback)(
8386
9480
  (target) => {
8387
- if (!linkPopover) return;
8388
- const { key } = linkPopover;
9481
+ const session = linkPopoverSessionRef.current;
9482
+ if (!session) return;
9483
+ if (session.intent === "add-nav") {
9484
+ const trimmed = target.trim();
9485
+ let href = trimmed;
9486
+ let label = "Untitled";
9487
+ if (trimmed) {
9488
+ const { pageRoute, sectionId } = parseTarget(trimmed);
9489
+ const page = resolvePage(pageRoute, sitePages);
9490
+ const pageSections = getSectionsForPath(sectionsByPath, pageRoute);
9491
+ const section = sectionId ? pageSections.find((s) => s.id === sectionId) : void 0;
9492
+ label = section?.label ?? page.title;
9493
+ href = trimmed;
9494
+ }
9495
+ const afterAnchor = addNavAfterAnchorRef.current;
9496
+ const { anchor, hrefKey, labelKey, index, order } = insertNavbarItem(href, label, afterAnchor);
9497
+ applyLinkByKey(hrefKey, href);
9498
+ document.querySelectorAll(`[data-ohw-key="${labelKey}"]`).forEach((el) => {
9499
+ el.textContent = label;
9500
+ });
9501
+ const navCount = String(index + 1);
9502
+ const orderJson = JSON.stringify(order);
9503
+ editContentRef.current = {
9504
+ ...editContentRef.current,
9505
+ [hrefKey]: href,
9506
+ [labelKey]: label,
9507
+ [NAV_COUNT_KEY]: navCount,
9508
+ [NAV_ORDER_KEY]: orderJson
9509
+ };
9510
+ postToParent2({
9511
+ type: "ow:change",
9512
+ nodes: [
9513
+ { key: hrefKey, text: href },
9514
+ { key: labelKey, text: label },
9515
+ { key: NAV_COUNT_KEY, text: navCount },
9516
+ { key: NAV_ORDER_KEY, text: orderJson }
9517
+ ]
9518
+ });
9519
+ postToParent2({ type: "ow:toast", title: "Link added", toastType: "success" });
9520
+ addNavAfterAnchorRef.current = null;
9521
+ setLinkPopover(null);
9522
+ enforceLinkHrefs();
9523
+ requestAnimationFrame(() => {
9524
+ selectRef.current(anchor);
9525
+ });
9526
+ return;
9527
+ }
9528
+ const { key } = session;
8389
9529
  applyLinkByKey(key, target);
8390
- postToParent({ type: "ow:change", nodes: [{ key, text: target }] });
9530
+ postToParent2({ type: "ow:change", nodes: [{ key, text: target }] });
8391
9531
  setLinkPopover(null);
8392
9532
  },
8393
- [linkPopover, postToParent]
9533
+ [postToParent2, sitePages, sectionsByPath]
8394
9534
  );
8395
9535
  const showEditLink = toolbarShowEditLink;
8396
9536
  const currentSections = sectionsByPath[pathname] ?? [];
8397
9537
  linkPopoverOpenRef.current = linkPopover !== null;
8398
- const handleMediaReplace = (0, import_react6.useCallback)(
9538
+ const handleMediaReplace = (0, import_react8.useCallback)(
8399
9539
  (key) => {
8400
- postToParent({ type: "ow:image-pick", key, elementType: mediaHover?.elementType ?? "image" });
9540
+ postToParent2({ type: "ow:image-pick", key, elementType: mediaHover?.elementType ?? "image" });
8401
9541
  },
8402
- [postToParent, mediaHover?.elementType]
9542
+ [postToParent2, mediaHover?.elementType]
8403
9543
  );
8404
- const handleMediaFadeOutComplete = (0, import_react6.useCallback)((key) => {
9544
+ const handleMediaFadeOutComplete = (0, import_react8.useCallback)((key) => {
8405
9545
  setUploadingRects((prev) => {
8406
9546
  if (!(key in prev)) return prev;
8407
9547
  const next = { ...prev };
@@ -8409,7 +9549,7 @@ function OhhwellsBridge() {
8409
9549
  return next;
8410
9550
  });
8411
9551
  }, []);
8412
- const handleVideoSettingsChange = (0, import_react6.useCallback)(
9552
+ const handleVideoSettingsChange = (0, import_react8.useCallback)(
8413
9553
  (key, settings) => {
8414
9554
  document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
8415
9555
  const video = getVideoEl(el);
@@ -8417,7 +9557,7 @@ function OhhwellsBridge() {
8417
9557
  if (typeof settings.autoplay === "boolean") video.autoplay = settings.autoplay;
8418
9558
  if (typeof settings.muted === "boolean") video.muted = settings.muted;
8419
9559
  syncVideoPlayback(video);
8420
- postToParent({
9560
+ postToParent2({
8421
9561
  type: "ow:change",
8422
9562
  nodes: [
8423
9563
  { key: `${key}${VIDEO_AUTOPLAY_SUFFIX}`, text: String(video.autoplay) },
@@ -8429,12 +9569,12 @@ function OhhwellsBridge() {
8429
9569
  );
8430
9570
  });
8431
9571
  },
8432
- [postToParent]
9572
+ [postToParent2]
8433
9573
  );
8434
- return bridgeRoot ? (0, import_react_dom2.createPortal)(
8435
- /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(import_jsx_runtime22.Fragment, { children: [
8436
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { ref: attachVisibleViewport, "data-ohw-visible-viewport": "", "aria-hidden": true }),
8437
- Object.entries(uploadingRects).map(([key, { rect, fadingOut }]) => /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
9574
+ return bridgeRoot ? (0, import_react_dom3.createPortal)(
9575
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(import_jsx_runtime23.Fragment, { children: [
9576
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { ref: attachVisibleViewport, "data-ohw-visible-viewport": "", "aria-hidden": true }),
9577
+ Object.entries(uploadingRects).map(([key, { rect, fadingOut }]) => /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
8438
9578
  MediaOverlay,
8439
9579
  {
8440
9580
  hover: { key, rect, elementType: "image", isDragOver: false, hasTextOverlap: false },
@@ -8445,7 +9585,7 @@ function OhhwellsBridge() {
8445
9585
  },
8446
9586
  `uploading-${key}`
8447
9587
  )),
8448
- mediaHover && !(mediaHover.key in uploadingRects) && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
9588
+ mediaHover && !(mediaHover.key in uploadingRects) && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
8449
9589
  MediaOverlay,
8450
9590
  {
8451
9591
  hover: mediaHover,
@@ -8454,31 +9594,40 @@ function OhhwellsBridge() {
8454
9594
  onVideoSettingsChange: handleVideoSettingsChange
8455
9595
  }
8456
9596
  ),
8457
- siblingHintRect && !isItemDragging && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(ItemInteractionLayer, { rect: siblingHintRect, state: "sibling-hint" }),
8458
- hoveredItemRect && !siblingHintRect && !isItemDragging && hoveredItemElRef.current !== selectedElRef.current && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(ItemInteractionLayer, { rect: hoveredItemRect, state: "hover" }),
8459
- toolbarRect && toolbarVariant === "link-action" && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
9597
+ siblingHintRect && !isItemDragging && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(ItemInteractionLayer, { rect: siblingHintRect, state: "sibling-hint" }),
9598
+ hoveredNavContainerRect && toolbarVariant !== "select-frame" && !linkPopover && !isItemDragging && !siblingHintRect && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(ItemInteractionLayer, { rect: hoveredNavContainerRect, state: "hover" }),
9599
+ toolbarVariant === "select-frame" && toolbarRect && !linkPopover && !isItemDragging && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
9600
+ NavbarContainerChrome,
9601
+ {
9602
+ rect: toolbarRect,
9603
+ onAdd: handleAddTopLevelNavItem
9604
+ }
9605
+ ),
9606
+ hoveredItemRect && !hoveredNavContainerRect && !siblingHintRect && !isItemDragging && hoveredItemElRef.current !== selectedElRef.current && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(ItemInteractionLayer, { rect: hoveredItemRect, state: "hover" }),
9607
+ toolbarRect && (toolbarVariant === "link-action" || toolbarVariant === "select-frame") && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
8460
9608
  ItemInteractionLayer,
8461
9609
  {
8462
9610
  rect: toolbarRect,
8463
9611
  elRef: glowElRef,
8464
9612
  state: isItemDragging ? "dragging" : resolveItemInteractionState(toolbarRect, parentScrollRef.current),
8465
- showHandle: Boolean(reorderHrefKey),
9613
+ showHandle: toolbarVariant === "link-action" && Boolean(reorderHrefKey),
8466
9614
  dragDisabled: reorderDragDisabled,
8467
9615
  dragHandleLabel: reorderHrefKey ? `Reorder ${reorderHrefKey}` : "Reorder item",
8468
9616
  onDragHandleDragStart: handleItemDragStart,
8469
9617
  onDragHandleDragEnd: handleItemDragEnd,
8470
- toolbar: isItemDragging ? void 0 : /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
9618
+ toolbar: toolbarVariant === "link-action" && !isItemDragging ? /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
8471
9619
  ItemActionToolbar,
8472
9620
  {
8473
9621
  onEditLink: openLinkPopoverForSelected,
8474
- onAddItem: handleAddNavigationItem,
8475
- addItemDisabled: false
9622
+ addItemDisabled: true,
9623
+ editLinkDisabled: false,
9624
+ moreDisabled: true
8476
9625
  }
8477
- )
9626
+ ) : void 0
8478
9627
  }
8479
9628
  ),
8480
- toolbarRect && toolbarVariant === "rich-text" && /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(import_jsx_runtime22.Fragment, { children: [
8481
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
9629
+ toolbarRect && toolbarVariant === "rich-text" && /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(import_jsx_runtime23.Fragment, { children: [
9630
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
8482
9631
  EditGlowChrome,
8483
9632
  {
8484
9633
  rect: toolbarRect,
@@ -8487,7 +9636,7 @@ function OhhwellsBridge() {
8487
9636
  dragDisabled: reorderDragDisabled
8488
9637
  }
8489
9638
  ),
8490
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
9639
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
8491
9640
  FloatingToolbar,
8492
9641
  {
8493
9642
  rect: toolbarRect,
@@ -8500,7 +9649,7 @@ function OhhwellsBridge() {
8500
9649
  }
8501
9650
  )
8502
9651
  ] }),
8503
- maxBadge && /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(
9652
+ maxBadge && /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(
8504
9653
  "div",
8505
9654
  {
8506
9655
  "data-ohw-max-badge": "",
@@ -8526,7 +9675,7 @@ function OhhwellsBridge() {
8526
9675
  ]
8527
9676
  }
8528
9677
  ),
8529
- toggleState && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
9678
+ toggleState && !linkPopover && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
8530
9679
  StateToggle,
8531
9680
  {
8532
9681
  rect: toggleState.rect,
@@ -8535,15 +9684,15 @@ function OhhwellsBridge() {
8535
9684
  onStateChange: handleStateChange
8536
9685
  }
8537
9686
  ),
8538
- sectionGap && /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(
9687
+ sectionGap && !linkPopover && /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(
8539
9688
  "div",
8540
9689
  {
8541
9690
  "data-ohw-section-insert-line": "",
8542
9691
  className: "fixed left-0 w-full z-2147483646 flex items-center pointer-events-none",
8543
9692
  style: { top: sectionGap.y, transform: "translateY(-50%)" },
8544
9693
  children: [
8545
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { className: "flex-1 bg-primary", style: { height: 3 } }),
8546
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
9694
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { className: "flex-1 bg-primary", style: { height: 3 } }),
9695
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
8547
9696
  Badge,
8548
9697
  {
8549
9698
  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",
@@ -8556,25 +9705,26 @@ function OhhwellsBridge() {
8556
9705
  children: "Add Section"
8557
9706
  }
8558
9707
  ),
8559
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { className: "flex-1 bg-primary", style: { height: 3 } })
9708
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { className: "flex-1 bg-primary", style: { height: 3 } })
8560
9709
  ]
8561
9710
  }
8562
9711
  ),
8563
- linkPopover && dialogPortalContainer ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
9712
+ linkPopover && dialogPortalContainer ? /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
8564
9713
  LinkPopover,
8565
9714
  {
8566
9715
  panelRef: linkPopoverPanelRef,
8567
9716
  portalContainer: dialogPortalContainer,
8568
9717
  open: true,
8569
- mode: "edit",
9718
+ mode: linkPopover.mode ?? "edit",
8570
9719
  pages: sitePages,
8571
9720
  sections: currentSections,
8572
9721
  sectionsByPath,
8573
9722
  initialTarget: linkPopover.target,
9723
+ existingTargets: linkPopover.intent === "add-nav" ? getNavbarExistingTargets() : [],
8574
9724
  onClose: closeLinkPopover,
8575
9725
  onSubmit: handleLinkPopoverSubmit
8576
9726
  },
8577
- `${linkPopover.key}-${pathname}`
9727
+ linkPopover.key
8578
9728
  ) : null
8579
9729
  ] }),
8580
9730
  bridgeRoot