@ohhwells/bridge 0.1.36-next.50 → 0.1.36-next.52

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") ?? "";
@@ -4456,6 +4459,7 @@ function ItemActionToolbar({
4456
4459
  onEditLink,
4457
4460
  onAddItem,
4458
4461
  onMore,
4462
+ editLinkDisabled = false,
4459
4463
  addItemDisabled = true,
4460
4464
  moreDisabled = true,
4461
4465
  tooltipSide = "bottom"
@@ -4466,8 +4470,10 @@ function ItemActionToolbar({
4466
4470
  {
4467
4471
  label: "Edit link",
4468
4472
  side: tooltipSide,
4473
+ disabled: editLinkDisabled,
4469
4474
  buttonProps: {
4470
4475
  onMouseDown: (e) => {
4476
+ if (editLinkDisabled) return;
4471
4477
  e.preventDefault();
4472
4478
  e.stopPropagation();
4473
4479
  onEditLink?.();
@@ -4854,8 +4860,8 @@ function MediaOverlay({
4854
4860
  }
4855
4861
 
4856
4862
  // src/OhhwellsBridge.tsx
4857
- var import_react_dom2 = require("react-dom");
4858
- var import_navigation = require("next/navigation");
4863
+ var import_react_dom3 = require("react-dom");
4864
+ var import_navigation2 = require("next/navigation");
4859
4865
 
4860
4866
  // src/lib/session-search.ts
4861
4867
  var STORAGE_KEY = "ohw-preserved-search";
@@ -5131,61 +5137,29 @@ function scrollToHashSectionWhenReady(behavior = "smooth") {
5131
5137
  tick();
5132
5138
  }
5133
5139
 
5140
+ // src/ui/link-modal/LinkPopover.tsx
5141
+ var import_react7 = require("react");
5142
+
5134
5143
  // src/ui/dialog.tsx
5135
5144
  var React7 = __toESM(require("react"), 1);
5136
5145
  var import_radix_ui5 = require("radix-ui");
5137
-
5138
- // src/ui/icons.tsx
5146
+ var import_lucide_react4 = require("lucide-react");
5139
5147
  var import_jsx_runtime12 = require("react/jsx-runtime");
5140
- function IconX({ className, ...props }) {
5141
- 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: [
5142
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("path", { d: "M18 6 6 18" }),
5143
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("path", { d: "m6 6 12 12" })
5144
- ] });
5145
- }
5146
- function IconChevronDown({ className, ...props }) {
5147
- 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" }) });
5148
- }
5149
- function IconFile({ className, ...props }) {
5150
- 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: [
5151
- /* @__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" }),
5152
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("path", { d: "M14 2v4a2 2 0 0 0 2 2h4" })
5153
- ] });
5154
- }
5155
- function IconInfo({ className, ...props }) {
5156
- 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: [
5157
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("circle", { cx: "12", cy: "12", r: "10" }),
5158
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("path", { d: "M12 16v-4" }),
5159
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("path", { d: "M12 8h.01" })
5160
- ] });
5161
- }
5162
- function IconArrowRight({ className, ...props }) {
5163
- 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: [
5164
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("path", { d: "M5 12h14" }),
5165
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("path", { d: "m12 5 7 7-7 7" })
5166
- ] });
5167
- }
5168
- function IconSection({ className, ...props }) {
5169
- 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: [
5170
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("rect", { width: "18", height: "7", x: "3", y: "3", rx: "1" }),
5171
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("rect", { width: "9", height: "7", x: "3", y: "14", rx: "1" }),
5172
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("rect", { width: "5", height: "7", x: "16", y: "14", rx: "1" })
5173
- ] });
5174
- }
5175
-
5176
- // src/ui/dialog.tsx
5177
- var import_jsx_runtime13 = require("react/jsx-runtime");
5178
- function Dialog2({ ...props }) {
5179
- return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_radix_ui5.Dialog.Root, { "data-slot": "dialog", ...props });
5148
+ function Dialog2({
5149
+ ...props
5150
+ }) {
5151
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_radix_ui5.Dialog.Root, { "data-slot": "dialog", ...props });
5180
5152
  }
5181
- function DialogPortal({ ...props }) {
5182
- return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_radix_ui5.Dialog.Portal, { ...props });
5153
+ function DialogPortal({
5154
+ ...props
5155
+ }) {
5156
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_radix_ui5.Dialog.Portal, { ...props });
5183
5157
  }
5184
5158
  function DialogOverlay({
5185
5159
  className,
5186
5160
  ...props
5187
5161
  }) {
5188
- return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
5162
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
5189
5163
  import_radix_ui5.Dialog.Overlay,
5190
5164
  {
5191
5165
  "data-slot": "dialog-overlay",
@@ -5195,57 +5169,74 @@ function DialogOverlay({
5195
5169
  }
5196
5170
  );
5197
5171
  }
5198
- var DialogContent = React7.forwardRef(({ className, children, showCloseButton = true, container, ...props }, ref) => {
5199
- const positionMode = container ? "absolute" : "fixed";
5200
- return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(DialogPortal, { container: container ?? void 0, children: [
5201
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(DialogOverlay, { className: cn(positionMode, "inset-0") }),
5202
- /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
5203
- import_radix_ui5.Dialog.Content,
5204
- {
5205
- ref,
5206
- "data-slot": "dialog-content",
5207
- className: cn(
5208
- positionMode,
5209
- "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",
5210
- "rounded-xl border border-border bg-background font-sans shadow-lg outline-none",
5211
- container ? "max-h-[calc(100%-32px)] max-w-[calc(100%-16px)]" : "max-h-[calc(100vh-32px)] max-w-[calc(100vw-16px)]",
5212
- className
5213
- ),
5214
- onMouseDown: (e) => e.stopPropagation(),
5215
- ...props,
5216
- children: [
5217
- children,
5218
- showCloseButton ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
5219
- import_radix_ui5.Dialog.Close,
5220
- {
5221
- type: "button",
5222
- className: "absolute right-[9px] top-[9px] rounded-sm p-1.5 text-foreground hover:bg-muted/50",
5223
- "aria-label": "Close",
5224
- children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(IconX, { "aria-hidden": true })
5225
- }
5226
- ) : null
5227
- ]
5228
- }
5229
- )
5230
- ] });
5231
- });
5172
+ var DialogContent = React7.forwardRef(
5173
+ ({ className, children, showCloseButton = true, container, ...props }, ref) => {
5174
+ const positionMode = container ? "absolute" : "fixed";
5175
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(DialogPortal, { container: container ?? void 0, children: [
5176
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(DialogOverlay, { className: cn(positionMode, "inset-0") }),
5177
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
5178
+ import_radix_ui5.Dialog.Content,
5179
+ {
5180
+ ref,
5181
+ "data-slot": "dialog-content",
5182
+ className: cn(
5183
+ positionMode,
5184
+ "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-visible",
5185
+ "rounded-xl border border-border bg-background font-sans shadow-lg outline-none",
5186
+ container ? "max-h-[calc(100%-32px)] max-w-[calc(100%-16px)]" : "max-h-[calc(100vh-32px)] max-w-[calc(100vw-16px)]",
5187
+ className
5188
+ ),
5189
+ onMouseDown: (e) => e.stopPropagation(),
5190
+ ...props,
5191
+ children: [
5192
+ children,
5193
+ showCloseButton ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
5194
+ import_radix_ui5.Dialog.Close,
5195
+ {
5196
+ type: "button",
5197
+ className: "absolute right-[9px] top-[9px] rounded-sm p-1.5 text-foreground hover:bg-muted/50",
5198
+ "aria-label": "Close",
5199
+ children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_lucide_react4.X, { size: 16, "aria-hidden": true })
5200
+ }
5201
+ ) : null
5202
+ ]
5203
+ }
5204
+ )
5205
+ ] });
5206
+ }
5207
+ );
5232
5208
  DialogContent.displayName = import_radix_ui5.Dialog.Content.displayName;
5233
- function DialogHeader({ className, ...props }) {
5234
- return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: cn("flex flex-col gap-1.5", className), ...props });
5209
+ function DialogHeader({
5210
+ className,
5211
+ ...props
5212
+ }) {
5213
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: cn("flex flex-col gap-1.5", className), ...props });
5235
5214
  }
5236
- function DialogFooter({ className, ...props }) {
5237
- return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: cn("flex items-center justify-end gap-2", className), ...props });
5215
+ function DialogFooter({
5216
+ className,
5217
+ ...props
5218
+ }) {
5219
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
5220
+ "div",
5221
+ {
5222
+ className: cn("flex items-center justify-end gap-2", className),
5223
+ ...props
5224
+ }
5225
+ );
5238
5226
  }
5239
- var DialogTitle = React7.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
5227
+ var DialogTitle = React7.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
5240
5228
  import_radix_ui5.Dialog.Title,
5241
5229
  {
5242
5230
  ref,
5243
- className: cn("text-lg font-semibold leading-none tracking-tight text-card-foreground", className),
5231
+ className: cn(
5232
+ "text-lg font-semibold leading-none tracking-tight text-card-foreground",
5233
+ className
5234
+ ),
5244
5235
  ...props
5245
5236
  }
5246
5237
  ));
5247
5238
  DialogTitle.displayName = import_radix_ui5.Dialog.Title.displayName;
5248
- var DialogDescription = React7.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
5239
+ var DialogDescription = React7.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
5249
5240
  import_radix_ui5.Dialog.Description,
5250
5241
  {
5251
5242
  ref,
@@ -5256,38 +5247,64 @@ var DialogDescription = React7.forwardRef(({ className, ...props }, ref) => /* @
5256
5247
  DialogDescription.displayName = import_radix_ui5.Dialog.Description.displayName;
5257
5248
  var DialogClose = import_radix_ui5.Dialog.Close;
5258
5249
 
5250
+ // src/ui/link-modal/LinkEditorPanel.tsx
5251
+ var import_lucide_react8 = require("lucide-react");
5252
+
5259
5253
  // src/ui/link-modal/DestinationBreadcrumb.tsx
5260
- var import_jsx_runtime14 = require("react/jsx-runtime");
5261
- function DestinationBreadcrumb({ pageTitle, sectionLabel }) {
5262
- return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "flex w-full flex-col gap-2", children: [
5263
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("p", { className: "text-sm font-medium text-foreground", children: "Destination" }),
5264
- /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "flex items-center gap-3", children: [
5265
- /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "flex items-center gap-2", children: [
5266
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(IconFile, { className: "shrink-0 text-foreground", "aria-hidden": true }),
5267
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "text-sm font-medium leading-none text-foreground", children: pageTitle })
5254
+ var import_lucide_react5 = require("lucide-react");
5255
+ var import_jsx_runtime13 = require("react/jsx-runtime");
5256
+ function DestinationBreadcrumb({
5257
+ pageTitle,
5258
+ sectionLabel
5259
+ }) {
5260
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex w-full flex-col gap-2", children: [
5261
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("p", { className: "text-sm font-medium! text-foreground m-0", children: "Destination" }),
5262
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex items-center gap-3", children: [
5263
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex items-center gap-2", children: [
5264
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_lucide_react5.File, { size: 16, className: "shrink-0 text-foreground", "aria-hidden": true }),
5265
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "text-sm font-medium leading-none text-foreground!", children: pageTitle })
5268
5266
  ] }),
5269
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(IconArrowRight, { className: "shrink-0 text-muted-foreground", "aria-hidden": true }),
5270
- /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "flex min-w-0 flex-1 items-center gap-2", children: [
5271
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(IconSection, { className: "shrink-0 text-foreground", "aria-hidden": true }),
5272
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "truncate text-sm font-medium leading-none text-foreground", children: sectionLabel })
5267
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
5268
+ import_lucide_react5.ArrowRight,
5269
+ {
5270
+ size: 16,
5271
+ className: "shrink-0 text-muted-foreground",
5272
+ "aria-hidden": true
5273
+ }
5274
+ ),
5275
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex min-w-0 flex-1 items-center gap-2", children: [
5276
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
5277
+ import_lucide_react5.GalleryVertical,
5278
+ {
5279
+ size: 16,
5280
+ className: "shrink-0 text-foreground",
5281
+ "aria-hidden": true
5282
+ }
5283
+ ),
5284
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "truncate text-sm font-medium leading-none text-foreground", children: sectionLabel })
5273
5285
  ] })
5274
5286
  ] })
5275
5287
  ] });
5276
5288
  }
5277
5289
 
5278
5290
  // src/ui/link-modal/SectionTreeItem.tsx
5279
- var import_jsx_runtime15 = require("react/jsx-runtime");
5280
- function SectionTreeItem({ section, onSelect, selected }) {
5291
+ var import_lucide_react6 = require("lucide-react");
5292
+ var import_jsx_runtime14 = require("react/jsx-runtime");
5293
+ function SectionTreeItem({
5294
+ section,
5295
+ onSelect,
5296
+ selected
5297
+ }) {
5281
5298
  const interactive = Boolean(onSelect);
5282
- return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "flex h-9 w-full items-end pl-3", children: [
5283
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
5299
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "flex h-9 w-full items-end pl-3", children: [
5300
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
5284
5301
  "div",
5285
5302
  {
5286
5303
  className: "mr-[-1px] h-9 w-2 shrink-0 rounded-bl-sm border-b border-l border-border mb-4",
5287
5304
  "aria-hidden": true
5288
5305
  }
5289
5306
  ),
5290
- /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
5307
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(
5291
5308
  "div",
5292
5309
  {
5293
5310
  role: interactive ? "button" : void 0,
@@ -5305,32 +5322,30 @@ function SectionTreeItem({ section, onSelect, selected }) {
5305
5322
  interactive && selected && "border-primary"
5306
5323
  ),
5307
5324
  children: [
5308
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(IconSection, { className: "shrink-0 text-foreground", "aria-hidden": true }),
5309
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "truncate text-sm font-normal leading-5 text-foreground", children: section.label })
5325
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
5326
+ import_lucide_react6.GalleryVertical,
5327
+ {
5328
+ size: 16,
5329
+ className: "shrink-0 text-foreground",
5330
+ "aria-hidden": true
5331
+ }
5332
+ ),
5333
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "truncate text-sm font-normal leading-5 text-foreground", children: section.label })
5310
5334
  ]
5311
5335
  }
5312
5336
  )
5313
5337
  ] });
5314
5338
  }
5315
- function SectionPickerList({ sections, onSelect }) {
5316
- if (sections.length === 0) {
5317
- return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("p", { className: "text-sm text-muted-foreground", children: "No sections found on this page." });
5318
- }
5319
- return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "flex flex-col gap-1", children: [
5320
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("p", { className: "text-sm text-muted-foreground", children: "Pick a section this link should scroll to." }),
5321
- sections.map((section) => /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(SectionTreeItem, { section, onSelect }, section.id))
5322
- ] });
5323
- }
5324
5339
 
5325
5340
  // src/ui/link-modal/UrlOrPageInput.tsx
5326
5341
  var import_react4 = require("react");
5327
5342
 
5328
5343
  // src/ui/input.tsx
5329
5344
  var React8 = __toESM(require("react"), 1);
5330
- var import_jsx_runtime16 = require("react/jsx-runtime");
5345
+ var import_jsx_runtime15 = require("react/jsx-runtime");
5331
5346
  var Input = React8.forwardRef(
5332
5347
  ({ className, type, ...props }, ref) => {
5333
- return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
5348
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
5334
5349
  "input",
5335
5350
  {
5336
5351
  type,
@@ -5349,9 +5364,9 @@ Input.displayName = "Input";
5349
5364
 
5350
5365
  // src/ui/label.tsx
5351
5366
  var import_radix_ui6 = require("radix-ui");
5352
- var import_jsx_runtime17 = require("react/jsx-runtime");
5367
+ var import_jsx_runtime16 = require("react/jsx-runtime");
5353
5368
  function Label({ className, ...props }) {
5354
- return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
5369
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
5355
5370
  import_radix_ui6.Label.Root,
5356
5371
  {
5357
5372
  "data-slot": "label",
@@ -5362,9 +5377,12 @@ function Label({ className, ...props }) {
5362
5377
  }
5363
5378
 
5364
5379
  // src/ui/link-modal/UrlOrPageInput.tsx
5365
- var import_jsx_runtime18 = require("react/jsx-runtime");
5366
- function FieldChevron({ onClick }) {
5367
- return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
5380
+ var import_lucide_react7 = require("lucide-react");
5381
+ var import_jsx_runtime17 = require("react/jsx-runtime");
5382
+ function FieldChevron({
5383
+ onClick
5384
+ }) {
5385
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
5368
5386
  "button",
5369
5387
  {
5370
5388
  type: "button",
@@ -5372,7 +5390,7 @@ function FieldChevron({ onClick }) {
5372
5390
  onClick,
5373
5391
  "aria-label": "Open page list",
5374
5392
  tabIndex: -1,
5375
- children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(IconChevronDown, {})
5393
+ children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_lucide_react7.ChevronDown, { size: 16 })
5376
5394
  }
5377
5395
  );
5378
5396
  }
@@ -5390,7 +5408,29 @@ function UrlOrPageInput({
5390
5408
  }) {
5391
5409
  const inputId = (0, import_react4.useId)();
5392
5410
  const inputRef = (0, import_react4.useRef)(null);
5411
+ const rootRef = (0, import_react4.useRef)(null);
5393
5412
  const [isFocused, setIsFocused] = (0, import_react4.useState)(false);
5413
+ (0, import_react4.useEffect)(() => {
5414
+ if (!dropdownOpen) return;
5415
+ const isOutside = (e) => {
5416
+ const root = rootRef.current;
5417
+ if (!root) return true;
5418
+ const path = typeof e.composedPath === "function" ? e.composedPath() : [];
5419
+ if (path.includes(root)) return false;
5420
+ const target = e.target;
5421
+ return !(target instanceof Node && root.contains(target));
5422
+ };
5423
+ const closeIfOutside = (e) => {
5424
+ if (!isOutside(e)) return;
5425
+ onDropdownOpenChange(false);
5426
+ };
5427
+ document.addEventListener("pointerdown", closeIfOutside, true);
5428
+ document.addEventListener("mousedown", closeIfOutside, true);
5429
+ return () => {
5430
+ document.removeEventListener("pointerdown", closeIfOutside, true);
5431
+ document.removeEventListener("mousedown", closeIfOutside, true);
5432
+ };
5433
+ }, [dropdownOpen, onDropdownOpenChange]);
5394
5434
  const openDropdown = () => {
5395
5435
  if (readOnly || filteredPages.length === 0) return;
5396
5436
  onDropdownOpenChange(true);
@@ -5411,12 +5451,19 @@ function UrlOrPageInput({
5411
5451
  "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]",
5412
5452
  urlError ? "border-destructive shadow-[0_0_0_1px_var(--ohw-destructive)]" : isFocused ? "border-primary shadow-[0_0_0_1px_var(--ohw-primary)]" : "border-input"
5413
5453
  );
5414
- return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "flex w-full flex-col gap-2 p-0", children: [
5415
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(Label, { htmlFor: inputId, className: cn(urlError && "text-destructive"), children: "Destination" }),
5416
- /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "relative w-full", children: [
5417
- /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { "data-ohw-link-field": true, className: fieldClassName, children: [
5418
- 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,
5419
- 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)(
5454
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "flex w-full flex-col gap-2 p-0", children: [
5455
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(Label, { htmlFor: inputId, className: cn(urlError && "text-destructive"), children: "Destination" }),
5456
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { ref: rootRef, className: "relative w-full", children: [
5457
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { "data-ohw-link-field": true, className: fieldClassName, children: [
5458
+ selectedPage ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { className: "flex shrink-0 items-center pr-2", children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
5459
+ import_lucide_react7.File,
5460
+ {
5461
+ size: 16,
5462
+ className: "shrink-0 text-foreground",
5463
+ "aria-hidden": true
5464
+ }
5465
+ ) }) : null,
5466
+ 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)(
5420
5467
  Input,
5421
5468
  {
5422
5469
  ref: inputRef,
@@ -5427,7 +5474,14 @@ function UrlOrPageInput({
5427
5474
  setIsFocused(true);
5428
5475
  if (!selectedPage) openDropdown();
5429
5476
  },
5430
- onBlur: () => setIsFocused(false),
5477
+ onBlur: () => {
5478
+ setIsFocused(false);
5479
+ requestAnimationFrame(() => {
5480
+ if (!rootRef.current?.contains(document.activeElement)) {
5481
+ onDropdownOpenChange(false);
5482
+ }
5483
+ });
5484
+ },
5431
5485
  placeholder,
5432
5486
  className: cn(
5433
5487
  "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",
@@ -5435,7 +5489,7 @@ function UrlOrPageInput({
5435
5489
  )
5436
5490
  }
5437
5491
  ),
5438
- selectedPage && !readOnly ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
5492
+ selectedPage && !readOnly ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
5439
5493
  "button",
5440
5494
  {
5441
5495
  type: "button",
@@ -5443,26 +5497,26 @@ function UrlOrPageInput({
5443
5497
  onMouseDown: clearSelection,
5444
5498
  "aria-label": "Clear selected page",
5445
5499
  tabIndex: -1,
5446
- children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(IconX, { "aria-hidden": true })
5500
+ children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_lucide_react7.X, { size: 16, "aria-hidden": true })
5447
5501
  }
5448
5502
  ) : null,
5449
- !readOnly ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(FieldChevron, { onClick: toggleDropdown }) : null
5503
+ !readOnly ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(FieldChevron, { onClick: toggleDropdown }) : null
5450
5504
  ] }),
5451
- dropdownOpen && !readOnly && filteredPages.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
5505
+ dropdownOpen && !readOnly && filteredPages.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
5452
5506
  "div",
5453
5507
  {
5454
5508
  "data-ohw-link-page-dropdown": "",
5455
- 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",
5509
+ 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",
5456
5510
  onMouseDown: (e) => e.preventDefault(),
5457
- children: filteredPages.map((page) => /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(
5511
+ children: filteredPages.map((page) => /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
5458
5512
  "button",
5459
5513
  {
5460
5514
  type: "button",
5461
5515
  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",
5462
5516
  onClick: () => onPageSelect(page),
5463
5517
  children: [
5464
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(IconFile, { className: "shrink-0", "aria-hidden": true }),
5465
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "truncate", children: page.title })
5518
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_lucide_react7.File, { size: 16, className: "shrink-0", "aria-hidden": true }),
5519
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { className: "truncate", children: page.title })
5466
5520
  ]
5467
5521
  },
5468
5522
  page.path
@@ -5470,12 +5524,352 @@ function UrlOrPageInput({
5470
5524
  }
5471
5525
  ) : null
5472
5526
  ] }),
5473
- urlError ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("p", { className: "text-sm font-medium text-destructive", children: urlError }) : null
5527
+ urlError ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("p", { className: "text-sm font-medium text-destructive", children: urlError }) : null
5474
5528
  ] });
5475
5529
  }
5476
5530
 
5477
- // src/ui/link-modal/useLinkModalState.ts
5531
+ // src/ui/link-modal/LinkEditorPanel.tsx
5532
+ var import_jsx_runtime18 = require("react/jsx-runtime");
5533
+ function LinkEditorPanel({ state, onClose }) {
5534
+ const isCancel = state.secondaryLabel === "Cancel" || state.secondaryLabel === "Back to sections";
5535
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(import_jsx_runtime18.Fragment, { children: [
5536
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(DialogClose, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
5537
+ "button",
5538
+ {
5539
+ type: "button",
5540
+ className: "absolute right-[9px] top-[9px] rounded-sm p-1.5 text-foreground hover:bg-muted/50 h-7",
5541
+ "aria-label": "Close",
5542
+ onClick: onClose,
5543
+ children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_lucide_react8.X, { size: 16, "aria-hidden": true })
5544
+ }
5545
+ ) }),
5546
+ /* @__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 }) }),
5547
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "flex w-full flex-col gap-3 px-6 pb-8 pt-1", children: [
5548
+ state.showBreadcrumb && state.selectedPage && state.selectedSection ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
5549
+ DestinationBreadcrumb,
5550
+ {
5551
+ pageTitle: state.selectedPage.title,
5552
+ sectionLabel: state.selectedSection.label
5553
+ }
5554
+ ) : /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
5555
+ UrlOrPageInput,
5556
+ {
5557
+ value: state.searchValue,
5558
+ selectedPage: state.selectedPage,
5559
+ dropdownOpen: state.dropdownOpen,
5560
+ onDropdownOpenChange: state.setDropdownOpen,
5561
+ filteredPages: state.filteredPages,
5562
+ onInputChange: state.handleInputChange,
5563
+ onPageSelect: state.handlePageSelect,
5564
+ urlError: state.urlError
5565
+ }
5566
+ ),
5567
+ state.showChooseSection ? /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "flex flex-col justify-center gap-2", children: [
5568
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
5569
+ Button,
5570
+ {
5571
+ type: "button",
5572
+ variant: "outline",
5573
+ className: "w-fit cursor-pointer",
5574
+ size: "sm",
5575
+ onClick: state.handleChooseSection,
5576
+ children: "Choose a section"
5577
+ }
5578
+ ),
5579
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "flex items-center gap-1 text-sm text-muted-foreground", children: [
5580
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_lucide_react8.Info, { size: 16, className: "shrink-0", "aria-hidden": true }),
5581
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { children: "Pick a section this link should scroll to." })
5582
+ ] })
5583
+ ] }) : null,
5584
+ state.showSectionRow && state.selectedSection ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(SectionTreeItem, { section: state.selectedSection, selected: true }) : null
5585
+ ] }),
5586
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(DialogFooter, { className: "w-full px-6 pb-6", children: [
5587
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
5588
+ Button,
5589
+ {
5590
+ type: "button",
5591
+ variant: isCancel ? "outline" : "ghost",
5592
+ className: "leading-6 shadow-none cursor-pointer",
5593
+ style: isCancel ? {
5594
+ backgroundColor: "var(--ohw-background)",
5595
+ borderColor: "var(--ohw-border)",
5596
+ color: "var(--ohw-foreground)"
5597
+ } : void 0,
5598
+ onClick: state.handleSecondary,
5599
+ children: state.secondaryLabel
5600
+ }
5601
+ ),
5602
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
5603
+ Button,
5604
+ {
5605
+ type: "button",
5606
+ className: "border-0 leading-6 shadow-none hover:opacity-90 disabled:opacity-50 cursor-pointer",
5607
+ style: {
5608
+ backgroundColor: "var(--ohw-primary)",
5609
+ color: "var(--ohw-primary-foreground)"
5610
+ },
5611
+ disabled: !state.isValid,
5612
+ onClick: state.handleSubmit,
5613
+ children: state.submitLabel
5614
+ }
5615
+ )
5616
+ ] })
5617
+ ] });
5618
+ }
5619
+
5620
+ // src/ui/link-modal/SectionPickerOverlay.tsx
5478
5621
  var import_react5 = require("react");
5622
+ var import_react_dom = require("react-dom");
5623
+ var import_lucide_react9 = require("lucide-react");
5624
+ var import_navigation = require("next/navigation");
5625
+ var import_jsx_runtime19 = require("react/jsx-runtime");
5626
+ var DIM_OVERLAY = "rgba(0, 0, 0, 0.45)";
5627
+ function rectsEqual(a, b) {
5628
+ if (a.size !== b.size) return false;
5629
+ for (const [id, rect] of a) {
5630
+ const other = b.get(id);
5631
+ if (!other || rect.top !== other.top || rect.left !== other.left || rect.width !== other.width || rect.height !== other.height) {
5632
+ return false;
5633
+ }
5634
+ }
5635
+ return true;
5636
+ }
5637
+ function useSectionRects(sectionIdsKey, enabled) {
5638
+ const [rects, setRects] = (0, import_react5.useState)(/* @__PURE__ */ new Map());
5639
+ const sectionIds = (0, import_react5.useMemo)(
5640
+ () => sectionIdsKey ? sectionIdsKey.split("\0") : [],
5641
+ [sectionIdsKey]
5642
+ );
5643
+ (0, import_react5.useEffect)(() => {
5644
+ if (!enabled || sectionIds.length === 0) {
5645
+ setRects((prev) => prev.size === 0 ? prev : /* @__PURE__ */ new Map());
5646
+ return;
5647
+ }
5648
+ const update = () => {
5649
+ const next = /* @__PURE__ */ new Map();
5650
+ for (const id of sectionIds) {
5651
+ const el = document.querySelector(
5652
+ `[data-ohw-section="${CSS.escape(id)}"]`
5653
+ );
5654
+ if (el) next.set(id, el.getBoundingClientRect());
5655
+ }
5656
+ setRects((prev) => rectsEqual(prev, next) ? prev : next);
5657
+ };
5658
+ update();
5659
+ const opts = { capture: true, passive: true };
5660
+ window.addEventListener("scroll", update, opts);
5661
+ window.addEventListener("resize", update);
5662
+ const vv = window.visualViewport;
5663
+ vv?.addEventListener("resize", update);
5664
+ vv?.addEventListener("scroll", update);
5665
+ const ro = new ResizeObserver(update);
5666
+ for (const id of sectionIds) {
5667
+ const el = document.querySelector(
5668
+ `[data-ohw-section="${CSS.escape(id)}"]`
5669
+ );
5670
+ if (el) ro.observe(el);
5671
+ }
5672
+ return () => {
5673
+ window.removeEventListener("scroll", update, opts);
5674
+ window.removeEventListener("resize", update);
5675
+ vv?.removeEventListener("resize", update);
5676
+ vv?.removeEventListener("scroll", update);
5677
+ ro.disconnect();
5678
+ };
5679
+ }, [sectionIds, enabled]);
5680
+ return rects;
5681
+ }
5682
+ function SectionPickerOverlay({
5683
+ pagePath,
5684
+ sections,
5685
+ onBack,
5686
+ onSelect
5687
+ }) {
5688
+ const router = (0, import_navigation.useRouter)();
5689
+ const pathname = (0, import_navigation.usePathname)();
5690
+ const [selectedId, setSelectedId] = (0, import_react5.useState)(null);
5691
+ const [hoveredId, setHoveredId] = (0, import_react5.useState)(null);
5692
+ const [chromeClip, setChromeClip] = (0, import_react5.useState)(
5693
+ () => ({
5694
+ top: 0,
5695
+ bottom: typeof window !== "undefined" ? window.innerHeight : 0
5696
+ })
5697
+ );
5698
+ const normalizedTarget = normalizePath(pagePath);
5699
+ const isOnTargetPage = normalizePath(pathname) === normalizedTarget;
5700
+ (0, import_react5.useEffect)(() => {
5701
+ if (isOnTargetPage) return;
5702
+ const search = readPreservedSearch();
5703
+ router.push(`${normalizedTarget}${search}`);
5704
+ }, [isOnTargetPage, normalizedTarget, router]);
5705
+ (0, import_react5.useEffect)(() => {
5706
+ document.documentElement.setAttribute("data-ohw-section-picking", "");
5707
+ document.querySelectorAll("[data-ohw-hovered]").forEach((el) => {
5708
+ el.removeAttribute("data-ohw-hovered");
5709
+ });
5710
+ return () => {
5711
+ document.documentElement.removeAttribute("data-ohw-section-picking");
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
+ };
5730
+ const onResize = () => {
5731
+ setChromeClip((prev) => {
5732
+ const bottom = Math.max(prev.top, window.innerHeight);
5733
+ return prev.bottom === bottom ? prev : { ...prev, bottom };
5734
+ });
5735
+ };
5736
+ window.addEventListener("message", onParentScroll);
5737
+ window.addEventListener("resize", onResize);
5738
+ return () => {
5739
+ window.removeEventListener("message", onParentScroll);
5740
+ window.removeEventListener("resize", onResize);
5741
+ };
5742
+ }, []);
5743
+ const liveSections = (0, import_react5.useMemo)(() => {
5744
+ if (!isOnTargetPage) return sections;
5745
+ const live = collectSectionsFromDom();
5746
+ if (live.length === 0) return sections;
5747
+ const labels = new Map(sections.map((s) => [s.id, s.label]));
5748
+ return live.map((s) => ({ id: s.id, label: labels.get(s.id) ?? s.label }));
5749
+ }, [isOnTargetPage, sections, pathname]);
5750
+ const sectionIdsKey = (0, import_react5.useMemo)(
5751
+ () => liveSections.map((s) => s.id).join("\0"),
5752
+ [liveSections]
5753
+ );
5754
+ const rects = useSectionRects(sectionIdsKey, isOnTargetPage);
5755
+ const handleSelect = (0, import_react5.useCallback)(
5756
+ (section) => {
5757
+ if (selectedId) return;
5758
+ setSelectedId(section.id);
5759
+ onSelect(section);
5760
+ },
5761
+ [selectedId, onSelect]
5762
+ );
5763
+ (0, import_react5.useEffect)(() => {
5764
+ const onKeyDown = (e) => {
5765
+ if (e.key === "Escape") {
5766
+ e.preventDefault();
5767
+ e.stopPropagation();
5768
+ onBack();
5769
+ }
5770
+ };
5771
+ window.addEventListener("keydown", onKeyDown, true);
5772
+ return () => window.removeEventListener("keydown", onKeyDown, true);
5773
+ }, [onBack]);
5774
+ const portalRoot = typeof document !== "undefined" ? document.querySelector("[data-ohw-bridge-root]") ?? document.body : null;
5775
+ if (!portalRoot) return null;
5776
+ return (0, import_react_dom.createPortal)(
5777
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(
5778
+ "div",
5779
+ {
5780
+ "data-ohw-section-picker": "",
5781
+ "data-ohw-bridge": "",
5782
+ className: `pointer-events-none fixed inset-0 z-[2147483647] transition-opacity duration-200 ${"opacity-100"}`,
5783
+ "aria-modal": true,
5784
+ role: "dialog",
5785
+ "aria-label": "Choose a section",
5786
+ children: [
5787
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
5788
+ "div",
5789
+ {
5790
+ className: "pointer-events-auto fixed left-5 z-[2]",
5791
+ style: { top: chromeClip.top + 20 },
5792
+ children: /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(
5793
+ Button,
5794
+ {
5795
+ type: "button",
5796
+ variant: "outline",
5797
+ size: "sm",
5798
+ className: "h-8 min-w-0 gap-1 border-border bg-background px-2 py-1.5 shadow-sm hover:bg-muted cursor-pointer",
5799
+ onClick: onBack,
5800
+ children: [
5801
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_lucide_react9.ArrowLeft, { className: "size-4 shrink-0", "aria-hidden": true }),
5802
+ "Back"
5803
+ ]
5804
+ }
5805
+ )
5806
+ }
5807
+ ),
5808
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
5809
+ "div",
5810
+ {
5811
+ 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",
5812
+ style: {
5813
+ top: chromeClip.bottom - 20,
5814
+ transform: "translate(-50%, -100%)",
5815
+ backgroundColor: "var(--ohw-popover-foreground, #0c0a09)"
5816
+ },
5817
+ "data-ohw-section-picker-hint": "",
5818
+ children: "Click on section to select"
5819
+ }
5820
+ ),
5821
+ !isOnTargetPage ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
5822
+ "div",
5823
+ {
5824
+ 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",
5825
+ style: { top: chromeClip.top + 64 },
5826
+ children: "Loading page preview\u2026"
5827
+ }
5828
+ ) : null,
5829
+ 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,
5830
+ isOnTargetPage ? liveSections.map((section) => {
5831
+ const rect = rects.get(section.id);
5832
+ if (!rect || rect.width <= 0 || rect.height <= 0) return null;
5833
+ const isSelected = selectedId === section.id;
5834
+ const isLit = isSelected || hoveredId === section.id;
5835
+ return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
5836
+ "button",
5837
+ {
5838
+ type: "button",
5839
+ className: "pointer-events-auto fixed z-[1] cursor-pointer border-0 bg-transparent p-0 transition-[background-color] duration-150",
5840
+ style: {
5841
+ top: rect.top,
5842
+ left: rect.left,
5843
+ width: rect.width,
5844
+ height: rect.height,
5845
+ backgroundColor: isLit ? "transparent" : DIM_OVERLAY
5846
+ },
5847
+ "aria-label": `Select section ${section.label}`,
5848
+ onMouseEnter: () => setHoveredId(section.id),
5849
+ onMouseLeave: () => setHoveredId((prev) => prev === section.id ? null : prev),
5850
+ onClick: () => handleSelect(section),
5851
+ children: isSelected ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
5852
+ "span",
5853
+ {
5854
+ className: "absolute right-3 top-3 flex size-8 items-center justify-center rounded-full text-white",
5855
+ style: { backgroundColor: "var(--ohw-primary, #0885fe)" },
5856
+ "aria-hidden": true,
5857
+ children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_lucide_react9.Check, { className: "size-5" })
5858
+ }
5859
+ ) : null
5860
+ },
5861
+ section.id
5862
+ );
5863
+ }) : null
5864
+ ]
5865
+ }
5866
+ ),
5867
+ portalRoot
5868
+ );
5869
+ }
5870
+
5871
+ // src/ui/link-modal/useLinkModalState.ts
5872
+ var import_react6 = require("react");
5479
5873
  function useLinkModalState({
5480
5874
  open,
5481
5875
  mode,
@@ -5483,21 +5877,18 @@ function useLinkModalState({
5483
5877
  sections: _sections,
5484
5878
  sectionsByPath,
5485
5879
  initialTarget,
5486
- existingTargets,
5880
+ existingTargets: _existingTargets,
5487
5881
  onClose,
5488
5882
  onSubmit
5489
5883
  }) {
5490
- const availablePages = (0, import_react5.useMemo)(
5491
- () => mode === "create" ? filterAvailablePages(pages, existingTargets) : pages,
5492
- [mode, pages, existingTargets]
5493
- );
5494
- const [searchValue, setSearchValue] = (0, import_react5.useState)("");
5495
- const [selectedPage, setSelectedPage] = (0, import_react5.useState)(null);
5496
- const [selectedSection, setSelectedSection] = (0, import_react5.useState)(null);
5497
- const [step, setStep] = (0, import_react5.useState)("input");
5498
- const [dropdownOpen, setDropdownOpen] = (0, import_react5.useState)(false);
5499
- const [urlError, setUrlError] = (0, import_react5.useState)("");
5500
- const reset = (0, import_react5.useCallback)(() => {
5884
+ const availablePages = (0, import_react6.useMemo)(() => pages, [pages]);
5885
+ const [searchValue, setSearchValue] = (0, import_react6.useState)("");
5886
+ const [selectedPage, setSelectedPage] = (0, import_react6.useState)(null);
5887
+ const [selectedSection, setSelectedSection] = (0, import_react6.useState)(null);
5888
+ const [step, setStep] = (0, import_react6.useState)("input");
5889
+ const [dropdownOpen, setDropdownOpen] = (0, import_react6.useState)(false);
5890
+ const [urlError, setUrlError] = (0, import_react6.useState)("");
5891
+ const reset = (0, import_react6.useCallback)(() => {
5501
5892
  setSearchValue("");
5502
5893
  setSelectedPage(null);
5503
5894
  setSelectedSection(null);
@@ -5505,7 +5896,7 @@ function useLinkModalState({
5505
5896
  setDropdownOpen(false);
5506
5897
  setUrlError("");
5507
5898
  }, []);
5508
- (0, import_react5.useEffect)(() => {
5899
+ (0, import_react6.useEffect)(() => {
5509
5900
  if (!open) return;
5510
5901
  if (mode === "edit" && initialTarget) {
5511
5902
  const { pageRoute } = parseTarget(initialTarget);
@@ -5520,12 +5911,12 @@ function useLinkModalState({
5520
5911
  }
5521
5912
  setDropdownOpen(false);
5522
5913
  setUrlError("");
5523
- }, [open, mode, initialTarget, pages, sectionsByPath, reset]);
5524
- const filteredPages = (0, import_react5.useMemo)(() => {
5914
+ }, [open, mode, initialTarget, reset]);
5915
+ const filteredPages = (0, import_react6.useMemo)(() => {
5525
5916
  if (!searchValue.trim()) return availablePages;
5526
5917
  return availablePages.filter((p) => p.title.toLowerCase().startsWith(searchValue.toLowerCase()));
5527
5918
  }, [availablePages, searchValue]);
5528
- const activeSections = (0, import_react5.useMemo)(() => {
5919
+ const activeSections = (0, import_react6.useMemo)(() => {
5529
5920
  if (!selectedPage) return [];
5530
5921
  return getSectionsForPath(sectionsByPath, selectedPage.path);
5531
5922
  }, [selectedPage, sectionsByPath]);
@@ -5567,11 +5958,14 @@ function useLinkModalState({
5567
5958
  const handleBackToSections = () => {
5568
5959
  setStep("sectionPicker");
5569
5960
  };
5961
+ const handleSectionPickerBack = () => {
5962
+ setStep("input");
5963
+ };
5570
5964
  const handleClose = () => {
5571
5965
  reset();
5572
5966
  onClose();
5573
5967
  };
5574
- const isValid = (0, import_react5.useMemo)(() => {
5968
+ const isValid = (0, import_react6.useMemo)(() => {
5575
5969
  if (urlError) return false;
5576
5970
  if (showBreadcrumb) return true;
5577
5971
  if (selectedPage) return true;
@@ -5604,7 +5998,7 @@ function useLinkModalState({
5604
5998
  onSubmit(normalizeUrl(searchValue));
5605
5999
  handleClose();
5606
6000
  };
5607
- const submitLabel = mode === "edit" ? "Save" : "Create";
6001
+ const submitLabel = showBreadcrumb ? "Save" : mode === "edit" ? "Save" : "Create";
5608
6002
  const title = mode === "edit" ? "Edit navigation item" : "Create navigation item";
5609
6003
  const secondaryLabel = showBreadcrumb ? "Back to sections" : "Cancel";
5610
6004
  const handleSecondary = showBreadcrumb ? handleBackToSections : handleClose;
@@ -5631,23 +6025,29 @@ function useLinkModalState({
5631
6025
  handleChooseSection,
5632
6026
  handleSectionSelect,
5633
6027
  handleBackToSections,
6028
+ handleSectionPickerBack,
5634
6029
  handleClose,
5635
6030
  handleSecondary,
5636
6031
  handleSubmit
5637
6032
  };
5638
6033
  }
5639
6034
 
5640
- // src/ui/link-modal/LinkEditorPanel.tsx
5641
- var import_jsx_runtime19 = require("react/jsx-runtime");
5642
- function LinkEditorPanel({
6035
+ // src/ui/link-modal/LinkPopover.tsx
6036
+ var import_jsx_runtime20 = require("react/jsx-runtime");
6037
+ function postToParent(data) {
6038
+ window.parent?.postMessage(data, "*");
6039
+ }
6040
+ function LinkPopover({
5643
6041
  open = true,
6042
+ panelRef,
6043
+ portalContainer,
6044
+ onClose,
5644
6045
  mode = "create",
5645
6046
  pages,
5646
6047
  sections = [],
5647
6048
  sectionsByPath,
5648
6049
  initialTarget,
5649
6050
  existingTargets = [],
5650
- onClose,
5651
6051
  onSubmit
5652
6052
  }) {
5653
6053
  const state = useLinkModalState({
@@ -5661,115 +6061,100 @@ function LinkEditorPanel({
5661
6061
  onClose,
5662
6062
  onSubmit
5663
6063
  });
5664
- const isCancel = state.secondaryLabel === "Cancel";
5665
- return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(import_jsx_runtime19.Fragment, { children: [
5666
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(DialogClose, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
5667
- "button",
6064
+ const sectionPickerActive = state.showSectionPicker && Boolean(state.selectedPage);
6065
+ (0, import_react7.useEffect)(() => {
6066
+ if (!open) return;
6067
+ if (sectionPickerActive) {
6068
+ postToParent({ type: "ow:link-modal-lock", locked: false });
6069
+ postToParent({ type: "ow:section-picker", active: true });
6070
+ document.documentElement.style.overflow = "";
6071
+ document.body.style.overflow = "";
6072
+ return () => {
6073
+ postToParent({ type: "ow:section-picker", active: false });
6074
+ };
6075
+ }
6076
+ postToParent({ type: "ow:section-picker", active: false });
6077
+ postToParent({ type: "ow:link-modal-lock", locked: true });
6078
+ const html = document.documentElement;
6079
+ const body = document.body;
6080
+ const prevHtmlOverflow = html.style.overflow;
6081
+ const prevBodyOverflow = body.style.overflow;
6082
+ const prevHtmlOverscroll = html.style.overscrollBehavior;
6083
+ const prevBodyOverscroll = body.style.overscrollBehavior;
6084
+ html.style.overflow = "hidden";
6085
+ body.style.overflow = "hidden";
6086
+ html.style.overscrollBehavior = "none";
6087
+ body.style.overscrollBehavior = "none";
6088
+ const canScrollElement = (el, deltaY) => {
6089
+ const { overflowY } = getComputedStyle(el);
6090
+ if (overflowY !== "auto" && overflowY !== "scroll") return false;
6091
+ if (el.scrollHeight <= el.clientHeight + 1) return false;
6092
+ if (deltaY < 0) return el.scrollTop > 0;
6093
+ if (deltaY > 0) return el.scrollTop + el.clientHeight < el.scrollHeight - 1;
6094
+ return true;
6095
+ };
6096
+ const allowsInternalScroll = (e) => {
6097
+ const target = e.target;
6098
+ if (!(target instanceof Element)) return false;
6099
+ const scrollRoot = target.closest(
6100
+ "[data-ohw-link-page-dropdown], [data-ohw-allow-scroll]"
6101
+ );
6102
+ if (!scrollRoot) return false;
6103
+ const deltaY = e instanceof WheelEvent ? e.deltaY : 0;
6104
+ return canScrollElement(scrollRoot, deltaY);
6105
+ };
6106
+ const preventBackgroundScroll = (e) => {
6107
+ if (allowsInternalScroll(e)) return;
6108
+ e.preventDefault();
6109
+ };
6110
+ const scrollOpts = { passive: false, capture: true };
6111
+ document.addEventListener("wheel", preventBackgroundScroll, scrollOpts);
6112
+ document.addEventListener("touchmove", preventBackgroundScroll, scrollOpts);
6113
+ return () => {
6114
+ postToParent({ type: "ow:link-modal-lock", locked: false });
6115
+ html.style.overflow = prevHtmlOverflow;
6116
+ body.style.overflow = prevBodyOverflow;
6117
+ html.style.overscrollBehavior = prevHtmlOverscroll;
6118
+ body.style.overscrollBehavior = prevBodyOverscroll;
6119
+ document.removeEventListener("wheel", preventBackgroundScroll, scrollOpts);
6120
+ document.removeEventListener("touchmove", preventBackgroundScroll, scrollOpts);
6121
+ };
6122
+ }, [open, sectionPickerActive]);
6123
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(import_jsx_runtime20.Fragment, { children: [
6124
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
6125
+ Dialog2,
5668
6126
  {
5669
- type: "button",
5670
- className: "absolute right-[9px] top-[9px] rounded-sm p-1.5 text-foreground hover:bg-muted/50",
5671
- "aria-label": "Close",
5672
- children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(IconX, { "aria-hidden": true })
6127
+ open: open && !sectionPickerActive,
6128
+ onOpenChange: (next) => {
6129
+ if (!next) onClose?.();
6130
+ },
6131
+ children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
6132
+ DialogContent,
6133
+ {
6134
+ ref: panelRef,
6135
+ container: portalContainer,
6136
+ "data-ohw-link-popover-root": "",
6137
+ "data-ohw-link-modal-root": "",
6138
+ "data-ohw-bridge": "",
6139
+ showCloseButton: false,
6140
+ className: "gap-0 p-0 w-full md:w-md pointer-events-auto z-[2147483646] overflow-visible",
6141
+ children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(LinkEditorPanel, { state, onClose })
6142
+ }
6143
+ )
5673
6144
  }
5674
- ) }),
5675
- /* @__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 }) }),
5676
- /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "flex w-full flex-col gap-3 px-6 pb-8 pt-1", children: [
5677
- state.showBreadcrumb && state.selectedPage && state.selectedSection ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
5678
- DestinationBreadcrumb,
5679
- {
5680
- pageTitle: state.selectedPage.title,
5681
- sectionLabel: state.selectedSection.label
5682
- }
5683
- ) : /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
5684
- UrlOrPageInput,
5685
- {
5686
- value: state.searchValue,
5687
- selectedPage: state.selectedPage,
5688
- dropdownOpen: state.dropdownOpen,
5689
- onDropdownOpenChange: state.setDropdownOpen,
5690
- filteredPages: state.filteredPages,
5691
- onInputChange: state.handleInputChange,
5692
- onPageSelect: state.handlePageSelect,
5693
- urlError: state.urlError
5694
- }
5695
- ),
5696
- state.showChooseSection ? /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "flex flex-col justify-center gap-2", children: [
5697
- /* @__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" }),
5698
- /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "flex items-center gap-1 text-sm text-muted-foreground", children: [
5699
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(IconInfo, { className: "shrink-0", "aria-hidden": true }),
5700
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { children: "Pick a section this link should scroll to." })
5701
- ] })
5702
- ] }) : null,
5703
- state.showSectionPicker ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(SectionPickerList, { sections: state.activeSections, onSelect: state.handleSectionSelect }) : null,
5704
- state.showSectionRow && state.selectedSection ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(SectionTreeItem, { section: state.selectedSection, selected: true }) : null
5705
- ] }),
5706
- /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(DialogFooter, { className: "w-full px-6 pb-6", children: [
5707
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
5708
- Button,
5709
- {
5710
- type: "button",
5711
- variant: isCancel ? "outline" : "ghost",
5712
- className: "leading-6 shadow-none cursor-pointer",
5713
- style: isCancel ? {
5714
- backgroundColor: "var(--ohw-background)",
5715
- borderColor: "var(--ohw-border)",
5716
- color: "var(--ohw-foreground)"
5717
- } : void 0,
5718
- onClick: state.handleSecondary,
5719
- children: state.secondaryLabel
5720
- }
5721
- ),
5722
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
5723
- Button,
5724
- {
5725
- type: "button",
5726
- className: "border-0 leading-6 shadow-none hover:opacity-90 disabled:opacity-50 cursor-pointer",
5727
- style: {
5728
- backgroundColor: "var(--ohw-primary)",
5729
- color: "var(--ohw-primary-foreground)"
5730
- },
5731
- disabled: !state.isValid,
5732
- onClick: state.handleSubmit,
5733
- children: state.submitLabel
5734
- }
5735
- )
5736
- ] })
6145
+ ),
6146
+ sectionPickerActive && state.selectedPage ? /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
6147
+ SectionPickerOverlay,
6148
+ {
6149
+ pagePath: state.selectedPage.path,
6150
+ sections: state.activeSections,
6151
+ onBack: state.handleSectionPickerBack,
6152
+ onSelect: state.handleSectionSelect
6153
+ }
6154
+ ) : null
5737
6155
  ] });
5738
6156
  }
5739
6157
 
5740
- // src/ui/link-modal/LinkPopover.tsx
5741
- var import_jsx_runtime20 = require("react/jsx-runtime");
5742
- function LinkPopover({
5743
- open = true,
5744
- panelRef,
5745
- portalContainer,
5746
- onClose,
5747
- ...editorProps
5748
- }) {
5749
- return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
5750
- Dialog2,
5751
- {
5752
- open,
5753
- onOpenChange: (next) => {
5754
- if (!next) onClose?.();
5755
- },
5756
- children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
5757
- DialogContent,
5758
- {
5759
- ref: panelRef,
5760
- container: portalContainer,
5761
- "data-ohw-link-popover-root": "",
5762
- "data-ohw-link-modal-root": "",
5763
- "data-ohw-bridge": "",
5764
- showCloseButton: false,
5765
- className: "gap-0 p-0 w-full md:w-md pointer-events-auto",
5766
- children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(LinkEditorPanel, { ...editorProps, open, onClose })
5767
- }
5768
- )
5769
- }
5770
- );
5771
- }
5772
-
5773
6158
  // src/ui/link-modal/devFixtures.ts
5774
6159
  var DEV_SITE_PAGES = [
5775
6160
  { path: "/", title: "Home" },
@@ -5789,16 +6174,21 @@ var DEV_SECTIONS_BY_PATH = {
5789
6174
  { id: "founder-teaser", label: "Founder" },
5790
6175
  { id: "plan-form", label: "Plan Form" },
5791
6176
  { id: "testimonials", label: "Testimonials" },
5792
- { id: "wordmark", label: "Wordmark" }
6177
+ { id: "wordmark", label: "Wordmark" },
6178
+ { id: "footer", label: "Footer" }
5793
6179
  ],
5794
6180
  "/about": [
5795
6181
  { id: "manifesto", label: "Manifesto" },
5796
6182
  { id: "story-letter", label: "Our Story" },
5797
6183
  { id: "lagree-explainer", label: "Lagree Explainer" },
5798
6184
  { id: "waitlist-cta", label: "Waitlist" },
5799
- { id: "personal-training", label: "Personal training" }
6185
+ { id: "personal-training", label: "Personal training" },
6186
+ { id: "footer", label: "Footer" }
6187
+ ],
6188
+ "/classes": [
6189
+ { id: "class-library", label: "Class Library" },
6190
+ { id: "footer", label: "Footer" }
5800
6191
  ],
5801
- "/classes": [{ id: "class-library", label: "Class Library" }],
5802
6192
  "/pricing": [
5803
6193
  { id: "page-header", label: "Page Header" },
5804
6194
  { id: "free-class-cta", label: "Free Class" },
@@ -5806,19 +6196,25 @@ var DEV_SECTIONS_BY_PATH = {
5806
6196
  { id: "monthly-memberships", label: "Memberships" },
5807
6197
  { id: "specialty-programs", label: "Specialty Programs" },
5808
6198
  { id: "package-matcher", label: "Packages" },
5809
- { id: "wordmark", label: "Wordmark" }
6199
+ { id: "wordmark", label: "Wordmark" },
6200
+ { id: "footer", label: "Footer" }
5810
6201
  ],
5811
6202
  "/studio": [
5812
6203
  { id: "studio-intro", label: "Studio Intro" },
5813
6204
  { id: "studio-features", label: "Studio Features" },
5814
6205
  { id: "studio-gallery", label: "Studio Gallery" },
5815
- { id: "studio-visit", label: "Visit Studio" }
6206
+ { id: "studio-visit", label: "Visit Studio" },
6207
+ { id: "footer", label: "Footer" }
6208
+ ],
6209
+ "/instructors": [
6210
+ { id: "instructor-grid", label: "Instructors" },
6211
+ { id: "footer", label: "Footer" }
5816
6212
  ],
5817
- "/instructors": [{ id: "instructor-grid", label: "Instructors" }],
5818
6213
  "/contact": [
5819
6214
  { id: "hello-hero", label: "Hello" },
5820
6215
  { id: "contact-form", label: "Contact Form" },
5821
- { id: "faq", label: "FAQ" }
6216
+ { id: "faq", label: "FAQ" },
6217
+ { id: "footer", label: "Footer" }
5822
6218
  ]
5823
6219
  };
5824
6220
  function shouldUseDevFixtures() {
@@ -5828,8 +6224,277 @@ function shouldUseDevFixtures() {
5828
6224
  return new URLSearchParams(q).get("ohw-fixtures") === "1";
5829
6225
  }
5830
6226
 
5831
- // src/ui/badge.tsx
6227
+ // src/lib/nav-items.ts
6228
+ var NAV_COUNT_KEY = "__ohw_nav_count";
6229
+ var NAV_ORDER_KEY = "__ohw_nav_order";
6230
+ function getLinkHref(el) {
6231
+ const key = el.getAttribute("data-ohw-href-key");
6232
+ if (key) {
6233
+ const stored = getStoredLinkHref(key);
6234
+ if (stored !== void 0) return stored;
6235
+ }
6236
+ return el.getAttribute("href") ?? "";
6237
+ }
6238
+ function isNavbarButton(el) {
6239
+ return el.hasAttribute("data-ohw-role") && el.getAttribute("data-ohw-role") === "navbar-button";
6240
+ }
6241
+ function isNavbarLinkItem(el) {
6242
+ if (!el.matches("[data-ohw-href-key]")) return false;
6243
+ if (isNavbarButton(el)) return false;
6244
+ if (!el.querySelector('[data-ohw-editable="text"]')) return false;
6245
+ return Boolean(el.closest("nav, [data-ohw-nav-container], [data-ohw-nav-drawer]"));
6246
+ }
6247
+ function getNavbarDesktopContainer() {
6248
+ return document.querySelector("[data-ohw-nav-container]");
6249
+ }
6250
+ function getNavbarDrawerContainer() {
6251
+ return document.querySelector("[data-ohw-nav-drawer]");
6252
+ }
6253
+ function listNavbarItems() {
6254
+ const desktop = getNavbarDesktopContainer();
6255
+ if (desktop) {
6256
+ return Array.from(desktop.querySelectorAll("[data-ohw-href-key]")).filter(isNavbarLinkItem);
6257
+ }
6258
+ const drawer = getNavbarDrawerContainer();
6259
+ if (drawer) {
6260
+ return Array.from(drawer.querySelectorAll("[data-ohw-href-key]")).filter(isNavbarLinkItem);
6261
+ }
6262
+ return Array.from(document.querySelectorAll("nav [data-ohw-href-key]")).filter(isNavbarLinkItem);
6263
+ }
6264
+ function parseNavIndexFromKey(key) {
6265
+ if (!key) return null;
6266
+ const match = key.match(/^nav-(\d+)-href$/);
6267
+ if (!match) return null;
6268
+ return parseInt(match[1], 10);
6269
+ }
6270
+ function getNavbarIndicesInDom() {
6271
+ const indices = /* @__PURE__ */ new Set();
6272
+ for (const item of listNavbarItems()) {
6273
+ const idx = parseNavIndexFromKey(item.getAttribute("data-ohw-href-key"));
6274
+ if (idx !== null) indices.add(idx);
6275
+ }
6276
+ return [...indices].sort((a, b) => a - b);
6277
+ }
6278
+ function getNextNavbarIndex() {
6279
+ const indices = getNavbarIndicesInDom();
6280
+ if (indices.length === 0) return 0;
6281
+ return Math.max(...indices) + 1;
6282
+ }
6283
+ function getNavbarExistingTargets() {
6284
+ return listNavbarItems().map((el) => getLinkHref(el)).filter(Boolean);
6285
+ }
6286
+ function getNavOrderFromDom() {
6287
+ return listNavbarItems().map((el) => el.getAttribute("data-ohw-href-key")).filter((key) => Boolean(key));
6288
+ }
6289
+ function parseNavOrder(content) {
6290
+ const raw = content[NAV_ORDER_KEY];
6291
+ if (!raw) return null;
6292
+ try {
6293
+ const parsed = JSON.parse(raw);
6294
+ if (!Array.isArray(parsed)) return null;
6295
+ return parsed.filter((key) => typeof key === "string" && key.length > 0);
6296
+ } catch {
6297
+ return null;
6298
+ }
6299
+ }
6300
+ function findCounterpartByHrefKey(hrefKey, root) {
6301
+ return root.querySelector(`[data-ohw-href-key="${CSS.escape(hrefKey)}"]`);
6302
+ }
6303
+ function cloneNavLinkShell(template) {
6304
+ const anchor = document.createElement("a");
6305
+ if (template) {
6306
+ anchor.style.cssText = template.style.cssText;
6307
+ if (template.className) anchor.className = template.className;
6308
+ }
6309
+ anchor.style.cursor = "pointer";
6310
+ anchor.style.textDecoration = "none";
6311
+ return anchor;
6312
+ }
6313
+ function buildNavLink(index, href, label, template) {
6314
+ const anchor = cloneNavLinkShell(template);
6315
+ anchor.href = href;
6316
+ anchor.setAttribute("data-ohw-href-key", `nav-${index}-href`);
6317
+ const span = document.createElement("span");
6318
+ span.setAttribute("data-ohw-editable", "text");
6319
+ span.setAttribute("data-ohw-key", `nav-${index}-label`);
6320
+ span.textContent = label;
6321
+ anchor.appendChild(span);
6322
+ return anchor;
6323
+ }
6324
+ function insertNavLinkInContainer(container, anchor, afterHrefKey) {
6325
+ if (!afterHrefKey) {
6326
+ container.appendChild(anchor);
6327
+ return;
6328
+ }
6329
+ const afterEl = findCounterpartByHrefKey(afterHrefKey, container);
6330
+ if (afterEl?.parentElement === container) {
6331
+ afterEl.insertAdjacentElement("afterend", anchor);
6332
+ return;
6333
+ }
6334
+ container.appendChild(anchor);
6335
+ }
6336
+ function insertNavbarItemDom(index, href, label, afterAnchor) {
6337
+ const afterHrefKey = afterAnchor?.getAttribute("data-ohw-href-key") ?? null;
6338
+ const desktopItems = getNavbarDesktopContainer()?.querySelectorAll("[data-ohw-href-key]") ?? [];
6339
+ const drawerItems = getNavbarDrawerContainer()?.querySelectorAll("[data-ohw-href-key]") ?? [];
6340
+ const desktopTemplate = Array.from(desktopItems).find(isNavbarLinkItem) ?? null;
6341
+ const drawerTemplate = Array.from(drawerItems).find(isNavbarLinkItem) ?? null;
6342
+ let primary = null;
6343
+ const desktopContainer = getNavbarDesktopContainer();
6344
+ if (desktopContainer) {
6345
+ const desktopAnchor = buildNavLink(index, href, label, desktopTemplate);
6346
+ insertNavLinkInContainer(desktopContainer, desktopAnchor, afterHrefKey);
6347
+ primary = desktopAnchor;
6348
+ }
6349
+ const drawerContainer = getNavbarDrawerContainer();
6350
+ if (drawerContainer) {
6351
+ const drawerAnchor = buildNavLink(index, href, label, drawerTemplate);
6352
+ insertNavLinkInContainer(drawerContainer, drawerAnchor, afterHrefKey);
6353
+ if (!primary) primary = drawerAnchor;
6354
+ }
6355
+ if (!primary) {
6356
+ const fallback = buildNavLink(index, href, label, listNavbarItems()[0] ?? null);
6357
+ const nav = document.querySelector("nav");
6358
+ nav?.appendChild(fallback);
6359
+ primary = fallback;
6360
+ }
6361
+ return primary;
6362
+ }
6363
+ function applyNavOrderToContainer(container, order) {
6364
+ const seen = /* @__PURE__ */ new Set();
6365
+ for (const hrefKey of order) {
6366
+ if (seen.has(hrefKey)) continue;
6367
+ seen.add(hrefKey);
6368
+ const el = findCounterpartByHrefKey(hrefKey, container);
6369
+ if (el) container.appendChild(el);
6370
+ }
6371
+ for (const el of container.querySelectorAll("[data-ohw-href-key]")) {
6372
+ if (!isNavbarLinkItem(el)) continue;
6373
+ const key = el.getAttribute("data-ohw-href-key");
6374
+ if (!key || seen.has(key)) continue;
6375
+ container.appendChild(el);
6376
+ }
6377
+ }
6378
+ function applyNavOrder(order) {
6379
+ const desktop = getNavbarDesktopContainer();
6380
+ if (desktop) applyNavOrderToContainer(desktop, order);
6381
+ const drawer = getNavbarDrawerContainer();
6382
+ if (drawer) applyNavOrderToContainer(drawer, order);
6383
+ }
6384
+ function collectNavbarIndicesFromContent(content) {
6385
+ const indices = /* @__PURE__ */ new Set();
6386
+ for (const key of Object.keys(content)) {
6387
+ const idx = parseNavIndexFromKey(key);
6388
+ if (idx !== null) indices.add(idx);
6389
+ }
6390
+ const countRaw = content[NAV_COUNT_KEY];
6391
+ if (countRaw) {
6392
+ const count = parseInt(countRaw, 10);
6393
+ if (Number.isFinite(count) && count > 0) {
6394
+ for (let i = 0; i < count; i++) indices.add(i);
6395
+ }
6396
+ }
6397
+ return [...indices].sort((a, b) => a - b);
6398
+ }
6399
+ function navbarIndexExistsInDom(index) {
6400
+ return document.querySelector(`[data-ohw-href-key="nav-${index}-href"]`) !== null;
6401
+ }
6402
+ function reconcileNavbarItemsFromContent(content) {
6403
+ const indices = collectNavbarIndicesFromContent(content);
6404
+ for (const index of indices) {
6405
+ if (navbarIndexExistsInDom(index)) continue;
6406
+ const href = content[`nav-${index}-href`];
6407
+ const label = content[`nav-${index}-label`];
6408
+ if (!href && !label) continue;
6409
+ insertNavbarItemDom(index, href ?? "/", label ?? "Untitled", null);
6410
+ }
6411
+ const order = parseNavOrder(content);
6412
+ if (order?.length) applyNavOrder(order);
6413
+ }
6414
+ function buildNavOrderAfterInsert(afterAnchor, newHrefKey) {
6415
+ const order = getNavOrderFromDom();
6416
+ if (!afterAnchor) {
6417
+ if (!order.includes(newHrefKey)) order.push(newHrefKey);
6418
+ return order;
6419
+ }
6420
+ const afterKey = afterAnchor.getAttribute("data-ohw-href-key");
6421
+ if (!afterKey) {
6422
+ if (!order.includes(newHrefKey)) order.push(newHrefKey);
6423
+ return order;
6424
+ }
6425
+ const withoutNew = order.filter((key) => key !== newHrefKey);
6426
+ const pos = withoutNew.indexOf(afterKey);
6427
+ if (pos >= 0) {
6428
+ withoutNew.splice(pos + 1, 0, newHrefKey);
6429
+ return withoutNew;
6430
+ }
6431
+ withoutNew.push(newHrefKey);
6432
+ return withoutNew;
6433
+ }
6434
+ function insertNavbarItem(href, label, afterAnchor = null) {
6435
+ const index = getNextNavbarIndex();
6436
+ const anchor = insertNavbarItemDom(index, href, label, afterAnchor);
6437
+ if (!anchor) throw new Error("Failed to insert navbar item");
6438
+ const hrefKey = `nav-${index}-href`;
6439
+ const order = buildNavOrderAfterInsert(afterAnchor, hrefKey);
6440
+ applyNavOrder(order);
6441
+ return {
6442
+ anchor,
6443
+ index,
6444
+ hrefKey,
6445
+ labelKey: `nav-${index}-label`,
6446
+ href,
6447
+ label,
6448
+ order
6449
+ };
6450
+ }
6451
+
6452
+ // src/ui/navbar-container-chrome.tsx
6453
+ var import_lucide_react10 = require("lucide-react");
5832
6454
  var import_jsx_runtime21 = require("react/jsx-runtime");
6455
+ function NavbarContainerChrome({
6456
+ rect,
6457
+ onAdd
6458
+ }) {
6459
+ const chromeGap = 6;
6460
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
6461
+ "div",
6462
+ {
6463
+ "data-ohw-navbar-container-chrome": "",
6464
+ "data-ohw-bridge": "",
6465
+ className: "pointer-events-none fixed z-[2147483647]",
6466
+ style: {
6467
+ top: rect.top - chromeGap,
6468
+ left: rect.left - chromeGap,
6469
+ width: rect.width + chromeGap * 2,
6470
+ height: rect.height + chromeGap * 2
6471
+ },
6472
+ children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
6473
+ "button",
6474
+ {
6475
+ type: "button",
6476
+ "data-ohw-navbar-add-button": "",
6477
+ 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",
6478
+ style: { top: "70%" },
6479
+ "aria-label": "Add item",
6480
+ onMouseDown: (e) => {
6481
+ e.preventDefault();
6482
+ e.stopPropagation();
6483
+ },
6484
+ onClick: (e) => {
6485
+ e.preventDefault();
6486
+ e.stopPropagation();
6487
+ onAdd();
6488
+ },
6489
+ children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react10.Plus, { className: "size-4 shrink-0 text-foreground", "aria-hidden": true })
6490
+ }
6491
+ )
6492
+ }
6493
+ );
6494
+ }
6495
+
6496
+ // src/ui/badge.tsx
6497
+ var import_jsx_runtime22 = require("react/jsx-runtime");
5833
6498
  var badgeVariants = cva(
5834
6499
  "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",
5835
6500
  {
@@ -5847,12 +6512,12 @@ var badgeVariants = cva(
5847
6512
  }
5848
6513
  );
5849
6514
  function Badge({ className, variant, ...props }) {
5850
- return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { className: cn(badgeVariants({ variant }), className), ...props });
6515
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { className: cn(badgeVariants({ variant }), className), ...props });
5851
6516
  }
5852
6517
 
5853
6518
  // src/OhhwellsBridge.tsx
5854
- var import_lucide_react4 = require("lucide-react");
5855
- var import_jsx_runtime22 = require("react/jsx-runtime");
6519
+ var import_lucide_react11 = require("lucide-react");
6520
+ var import_jsx_runtime23 = require("react/jsx-runtime");
5856
6521
  var PRIMARY2 = "#0885FE";
5857
6522
  var IMAGE_FADE_MS = 300;
5858
6523
  function runOpacityFade(el, onDone) {
@@ -6024,9 +6689,9 @@ function mountSchedulingWidget(insertAfter, notifyOnConnect = false, scheduleId,
6024
6689
  tail.insertAdjacentElement("afterend", container);
6025
6690
  }
6026
6691
  const root = (0, import_client.createRoot)(container);
6027
- (0, import_react_dom.flushSync)(() => {
6692
+ (0, import_react_dom2.flushSync)(() => {
6028
6693
  root.render(
6029
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
6694
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
6030
6695
  SchedulingWidget,
6031
6696
  {
6032
6697
  notifyOnConnect,
@@ -6066,7 +6731,7 @@ function mountSchedulingEntries(entries, notifyOnConnect = false) {
6066
6731
  }
6067
6732
  }
6068
6733
  }
6069
- function getLinkHref(el) {
6734
+ function getLinkHref2(el) {
6070
6735
  const anchor = el instanceof HTMLAnchorElement ? el : el.querySelector("a");
6071
6736
  return anchor?.getAttribute("href") ?? "";
6072
6737
  }
@@ -6103,7 +6768,7 @@ function collectEditableNodes() {
6103
6768
  return { key: el.dataset.ohwKey ?? "", type: "video", text: getVideoEl(el)?.src ?? "" };
6104
6769
  }
6105
6770
  if (el.dataset.ohwEditable === "link") {
6106
- return { key: el.dataset.ohwKey ?? "", type: "link", text: getLinkHref(el) };
6771
+ return { key: el.dataset.ohwKey ?? "", type: "link", text: getLinkHref2(el) };
6107
6772
  }
6108
6773
  return {
6109
6774
  key: el.dataset.ohwKey ?? "",
@@ -6114,7 +6779,7 @@ function collectEditableNodes() {
6114
6779
  document.querySelectorAll("[data-ohw-href-key]").forEach((el) => {
6115
6780
  const key = el.getAttribute("data-ohw-href-key") ?? "";
6116
6781
  if (!key) return;
6117
- nodes.push({ key, type: "link", text: getLinkHref(el) });
6782
+ nodes.push({ key, type: "link", text: getLinkHref2(el) });
6118
6783
  });
6119
6784
  document.querySelectorAll('[data-ohw-editable="video"]').forEach((el) => {
6120
6785
  const key = el.dataset.ohwKey ?? "";
@@ -6184,7 +6849,7 @@ function applyLinkByKey(key, val) {
6184
6849
  }
6185
6850
  function isInsideLinkEditor(target) {
6186
6851
  return Boolean(
6187
- 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"]')
6852
+ 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"]')
6188
6853
  );
6189
6854
  }
6190
6855
  function getHrefKeyFromElement(el) {
@@ -6195,7 +6860,7 @@ function getHrefKeyFromElement(el) {
6195
6860
  if (!key) return null;
6196
6861
  return { anchor, key };
6197
6862
  }
6198
- function isNavbarButton(el) {
6863
+ function isNavbarButton2(el) {
6199
6864
  return Boolean(el.closest('[data-ohw-role="navbar-button"]'));
6200
6865
  }
6201
6866
  function getNavigationItemAnchor(el) {
@@ -6207,19 +6872,8 @@ function getNavigationItemAnchor(el) {
6207
6872
  function isNavigationItem(el) {
6208
6873
  return getNavigationItemAnchor(el) !== null;
6209
6874
  }
6210
- function getNavigationItemAddHintTarget(anchor) {
6211
- const items = listNavigationItems();
6212
- const idx = items.indexOf(anchor);
6213
- if (idx >= 0 && idx < items.length - 1) return items[idx + 1];
6214
- return anchor;
6215
- }
6216
- function listNavigationItems() {
6217
- return Array.from(
6218
- document.querySelectorAll("nav [data-ohw-href-key], footer [data-ohw-href-key]")
6219
- ).filter((el) => isNavigationItem(el));
6220
- }
6221
6875
  function getNavigationItemReorderState(anchor) {
6222
- if (isNavbarButton(anchor)) return { key: null, disabled: false };
6876
+ if (isNavbarButton2(anchor)) return { key: null, disabled: false };
6223
6877
  return getReorderHandleStateFromAnchor(anchor);
6224
6878
  }
6225
6879
  function getReorderHandleState(el) {
@@ -6270,6 +6924,39 @@ function isInferredFooterGroup(el) {
6270
6924
  function isNavigationContainer(el) {
6271
6925
  return el.hasAttribute("data-ohw-nav-container") || isNavigationRoot(el) || isInferredFooterGroup(el);
6272
6926
  }
6927
+ function isPointOverNavigation(x, y) {
6928
+ const navRoot = document.querySelector("[data-ohw-nav-root]") ?? document.querySelector("nav");
6929
+ if (!navRoot) return false;
6930
+ const r2 = navRoot.getBoundingClientRect();
6931
+ return x >= r2.left && x <= r2.right && y >= r2.top && y <= r2.bottom;
6932
+ }
6933
+ function isPointOverNavItem(container, x, y) {
6934
+ return Array.from(container.querySelectorAll("[data-ohw-href-key]")).some((el) => {
6935
+ if (!isNavigationItem(el) || isNavbarButton2(el)) return false;
6936
+ const itemRect = el.getBoundingClientRect();
6937
+ return x >= itemRect.left && x <= itemRect.right && y >= itemRect.top && y <= itemRect.bottom;
6938
+ });
6939
+ }
6940
+ function resolveNavContainerSelectionTarget(target, clientX, clientY) {
6941
+ if (getNavigationItemAnchor(target)) return null;
6942
+ if (target.closest('[data-ohw-role="navbar-button"]')) return null;
6943
+ const plainLink = target.closest("a");
6944
+ if (plainLink && !plainLink.hasAttribute("data-ohw-href-key") && !plainLink.closest("[data-ohw-nav-container]")) {
6945
+ return null;
6946
+ }
6947
+ const fromClosest = target.closest("[data-ohw-nav-container]");
6948
+ if (fromClosest && !isPointOverNavItem(fromClosest, clientX, clientY)) {
6949
+ return fromClosest;
6950
+ }
6951
+ const navRoot = target.closest("[data-ohw-nav-root]");
6952
+ if (!navRoot) return null;
6953
+ const container = navRoot.querySelector("[data-ohw-nav-container]");
6954
+ if (!container || isPointOverNavItem(container, clientX, clientY)) return null;
6955
+ if (target === navRoot || target.hasAttribute("data-ohw-nav-root")) {
6956
+ return container;
6957
+ }
6958
+ return null;
6959
+ }
6273
6960
  function getNavigationSelectionParent(el) {
6274
6961
  if (isNavigationItem(el)) {
6275
6962
  const explicit = el.closest("[data-ohw-nav-container]");
@@ -6471,7 +7158,7 @@ function EditGlowChrome({
6471
7158
  dragDisabled = false
6472
7159
  }) {
6473
7160
  const GAP = 6;
6474
- return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(
7161
+ return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(
6475
7162
  "div",
6476
7163
  {
6477
7164
  ref: elRef,
@@ -6486,7 +7173,7 @@ function EditGlowChrome({
6486
7173
  zIndex: 2147483646
6487
7174
  },
6488
7175
  children: [
6489
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
7176
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
6490
7177
  "div",
6491
7178
  {
6492
7179
  style: {
@@ -6499,7 +7186,7 @@ function EditGlowChrome({
6499
7186
  }
6500
7187
  }
6501
7188
  ),
6502
- reorderHrefKey && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
7189
+ reorderHrefKey && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
6503
7190
  "div",
6504
7191
  {
6505
7192
  "data-ohw-drag-handle-container": "",
@@ -6511,7 +7198,7 @@ function EditGlowChrome({
6511
7198
  transform: "translate(calc(-100% - 7px), -50%)",
6512
7199
  pointerEvents: dragDisabled ? "none" : "auto"
6513
7200
  },
6514
- children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
7201
+ children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
6515
7202
  DragHandle,
6516
7203
  {
6517
7204
  "aria-label": `Reorder ${reorderHrefKey}`,
@@ -6615,7 +7302,7 @@ function FloatingToolbar({
6615
7302
  onEditLink
6616
7303
  }) {
6617
7304
  const { top, left, transform } = calcToolbarPos(rect, parentScroll, 330);
6618
- return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
7305
+ return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
6619
7306
  "div",
6620
7307
  {
6621
7308
  ref: elRef,
@@ -6636,12 +7323,12 @@ function FloatingToolbar({
6636
7323
  fontFamily: "sans-serif",
6637
7324
  pointerEvents: "auto"
6638
7325
  },
6639
- children: /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(CustomToolbar, { children: [
6640
- TOOLBAR_GROUPS.map((btns, gi) => /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(import_react6.default.Fragment, { children: [
6641
- gi > 0 && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(CustomToolbarDivider, {}),
7326
+ children: /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(CustomToolbar, { children: [
7327
+ TOOLBAR_GROUPS.map((btns, gi) => /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(import_react8.default.Fragment, { children: [
7328
+ gi > 0 && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(CustomToolbarDivider, {}),
6642
7329
  btns.map((btn) => {
6643
7330
  const isActive = activeCommands.has(btn.cmd);
6644
- return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
7331
+ return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
6645
7332
  CustomToolbarButton,
6646
7333
  {
6647
7334
  title: btn.title,
@@ -6650,7 +7337,7 @@ function FloatingToolbar({
6650
7337
  e.preventDefault();
6651
7338
  onCommand(btn.cmd);
6652
7339
  },
6653
- children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
7340
+ children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
6654
7341
  "svg",
6655
7342
  {
6656
7343
  width: "16",
@@ -6671,7 +7358,7 @@ function FloatingToolbar({
6671
7358
  );
6672
7359
  })
6673
7360
  ] }, gi)),
6674
- showEditLink ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
7361
+ showEditLink ? /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
6675
7362
  CustomToolbarButton,
6676
7363
  {
6677
7364
  type: "button",
@@ -6685,7 +7372,7 @@ function FloatingToolbar({
6685
7372
  e.preventDefault();
6686
7373
  e.stopPropagation();
6687
7374
  },
6688
- children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_lucide_react4.Link, { className: "size-4 shrink-0", "aria-hidden": true })
7375
+ children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_lucide_react11.Link, { className: "size-4 shrink-0", "aria-hidden": true })
6689
7376
  }
6690
7377
  ) : null
6691
7378
  ] })
@@ -6702,7 +7389,7 @@ function StateToggle({
6702
7389
  states,
6703
7390
  onStateChange
6704
7391
  }) {
6705
- return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
7392
+ return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
6706
7393
  ToggleGroup,
6707
7394
  {
6708
7395
  "data-ohw-state-toggle": "",
@@ -6716,7 +7403,7 @@ function StateToggle({
6716
7403
  left: rect.right - 8,
6717
7404
  transform: "translateX(-100%)"
6718
7405
  },
6719
- children: states.map((state) => /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(ToggleGroupItem, { value: state, size: "sm", children: state }, state))
7406
+ children: states.map((state) => /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(ToggleGroupItem, { value: state, size: "sm", children: state }, state))
6720
7407
  }
6721
7408
  );
6722
7409
  }
@@ -6739,12 +7426,12 @@ function resolveSubdomain(subdomainFromQuery) {
6739
7426
  return "";
6740
7427
  }
6741
7428
  function OhhwellsBridge() {
6742
- const pathname = (0, import_navigation.usePathname)();
6743
- const router = (0, import_navigation.useRouter)();
6744
- const searchParams = (0, import_navigation.useSearchParams)();
7429
+ const pathname = (0, import_navigation2.usePathname)();
7430
+ const router = (0, import_navigation2.useRouter)();
7431
+ const searchParams = (0, import_navigation2.useSearchParams)();
6745
7432
  const isEditMode = isEditSessionActive();
6746
- const [bridgeRoot, setBridgeRoot] = (0, import_react6.useState)(null);
6747
- (0, import_react6.useEffect)(() => {
7433
+ const [bridgeRoot, setBridgeRoot] = (0, import_react8.useState)(null);
7434
+ (0, import_react8.useEffect)(() => {
6748
7435
  const figtreeFontId = "ohw-figtree-font";
6749
7436
  if (!document.getElementById(figtreeFontId)) {
6750
7437
  const preconnect1 = Object.assign(document.createElement("link"), { rel: "preconnect", href: "https://fonts.googleapis.com" });
@@ -6772,84 +7459,90 @@ function OhhwellsBridge() {
6772
7459
  const subdomainFromQuery = searchParams.get("subdomain");
6773
7460
  const subdomain = resolveSubdomain(subdomainFromQuery);
6774
7461
  useLinkHrefGuardian(pathname, subdomain, isEditMode);
6775
- const postToParent = (0, import_react6.useCallback)((data) => {
7462
+ const postToParent2 = (0, import_react8.useCallback)((data) => {
6776
7463
  if (typeof window !== "undefined" && window.parent !== window) {
6777
7464
  window.parent.postMessage(data, "*");
6778
7465
  }
6779
7466
  }, []);
6780
- const [fetchState, setFetchState] = (0, import_react6.useState)("idle");
6781
- const autoSaveTimers = (0, import_react6.useRef)(/* @__PURE__ */ new Map());
6782
- const activeElRef = (0, import_react6.useRef)(null);
6783
- const selectedElRef = (0, import_react6.useRef)(null);
6784
- const originalContentRef = (0, import_react6.useRef)(null);
6785
- const activeStateElRef = (0, import_react6.useRef)(null);
6786
- const parentScrollRef = (0, import_react6.useRef)(null);
6787
- const visibleViewportRef = (0, import_react6.useRef)(null);
6788
- const [dialogPortalContainer, setDialogPortalContainer] = (0, import_react6.useState)(null);
6789
- const attachVisibleViewport = (0, import_react6.useCallback)((node) => {
7467
+ const [fetchState, setFetchState] = (0, import_react8.useState)("idle");
7468
+ const autoSaveTimers = (0, import_react8.useRef)(/* @__PURE__ */ new Map());
7469
+ const activeElRef = (0, import_react8.useRef)(null);
7470
+ const selectedElRef = (0, import_react8.useRef)(null);
7471
+ const originalContentRef = (0, import_react8.useRef)(null);
7472
+ const activeStateElRef = (0, import_react8.useRef)(null);
7473
+ const parentScrollRef = (0, import_react8.useRef)(null);
7474
+ const visibleViewportRef = (0, import_react8.useRef)(null);
7475
+ const [dialogPortalContainer, setDialogPortalContainer] = (0, import_react8.useState)(null);
7476
+ const attachVisibleViewport = (0, import_react8.useCallback)((node) => {
6790
7477
  visibleViewportRef.current = node;
6791
7478
  setDialogPortalContainer(node);
6792
7479
  if (node) applyVisibleViewport(node, parentScrollRef.current);
6793
7480
  }, []);
6794
- const toolbarElRef = (0, import_react6.useRef)(null);
6795
- const glowElRef = (0, import_react6.useRef)(null);
6796
- const hoveredImageRef = (0, import_react6.useRef)(null);
6797
- const hoveredImageHasTextOverlapRef = (0, import_react6.useRef)(false);
6798
- const [mediaHover, setMediaHover] = (0, import_react6.useState)(null);
6799
- const [uploadingRects, setUploadingRects] = (0, import_react6.useState)({});
6800
- const hoveredGapRef = (0, import_react6.useRef)(null);
6801
- const imageUnhoverTimerRef = (0, import_react6.useRef)(null);
6802
- const imageShowTimerRef = (0, import_react6.useRef)(null);
6803
- const editStylesRef = (0, import_react6.useRef)(null);
6804
- const activateRef = (0, import_react6.useRef)(() => {
7481
+ const toolbarElRef = (0, import_react8.useRef)(null);
7482
+ const glowElRef = (0, import_react8.useRef)(null);
7483
+ const hoveredImageRef = (0, import_react8.useRef)(null);
7484
+ const hoveredImageHasTextOverlapRef = (0, import_react8.useRef)(false);
7485
+ const [mediaHover, setMediaHover] = (0, import_react8.useState)(null);
7486
+ const [uploadingRects, setUploadingRects] = (0, import_react8.useState)({});
7487
+ const hoveredGapRef = (0, import_react8.useRef)(null);
7488
+ const imageUnhoverTimerRef = (0, import_react8.useRef)(null);
7489
+ const imageShowTimerRef = (0, import_react8.useRef)(null);
7490
+ const editStylesRef = (0, import_react8.useRef)(null);
7491
+ const activateRef = (0, import_react8.useRef)(() => {
6805
7492
  });
6806
- const deactivateRef = (0, import_react6.useRef)(() => {
7493
+ const deactivateRef = (0, import_react8.useRef)(() => {
6807
7494
  });
6808
- const selectRef = (0, import_react6.useRef)(() => {
7495
+ const selectRef = (0, import_react8.useRef)(() => {
6809
7496
  });
6810
- const selectFrameRef = (0, import_react6.useRef)(() => {
7497
+ const selectFrameRef = (0, import_react8.useRef)(() => {
6811
7498
  });
6812
- const deselectRef = (0, import_react6.useRef)(() => {
7499
+ const deselectRef = (0, import_react8.useRef)(() => {
6813
7500
  });
6814
- const reselectNavigationItemRef = (0, import_react6.useRef)(() => {
7501
+ const reselectNavigationItemRef = (0, import_react8.useRef)(() => {
6815
7502
  });
6816
- const commitNavigationTextEditRef = (0, import_react6.useRef)(() => {
7503
+ const commitNavigationTextEditRef = (0, import_react8.useRef)(() => {
6817
7504
  });
6818
- const refreshActiveCommandsRef = (0, import_react6.useRef)(() => {
7505
+ const refreshActiveCommandsRef = (0, import_react8.useRef)(() => {
6819
7506
  });
6820
- const postToParentRef = (0, import_react6.useRef)(postToParent);
6821
- postToParentRef.current = postToParent;
6822
- const sectionsLoadedRef = (0, import_react6.useRef)(false);
6823
- const pendingScheduleConfigRequests = (0, import_react6.useRef)([]);
6824
- const [toolbarRect, setToolbarRect] = (0, import_react6.useState)(null);
6825
- const [toolbarVariant, setToolbarVariant] = (0, import_react6.useState)("none");
6826
- const toolbarVariantRef = (0, import_react6.useRef)("none");
7507
+ const postToParentRef = (0, import_react8.useRef)(postToParent2);
7508
+ postToParentRef.current = postToParent2;
7509
+ const sectionsLoadedRef = (0, import_react8.useRef)(false);
7510
+ const pendingScheduleConfigRequests = (0, import_react8.useRef)([]);
7511
+ const [toolbarRect, setToolbarRect] = (0, import_react8.useState)(null);
7512
+ const [toolbarVariant, setToolbarVariant] = (0, import_react8.useState)("none");
7513
+ const toolbarVariantRef = (0, import_react8.useRef)("none");
6827
7514
  toolbarVariantRef.current = toolbarVariant;
6828
- const [reorderHrefKey, setReorderHrefKey] = (0, import_react6.useState)(null);
6829
- const [reorderDragDisabled, setReorderDragDisabled] = (0, import_react6.useState)(false);
6830
- const [toggleState, setToggleState] = (0, import_react6.useState)(null);
6831
- const [maxBadge, setMaxBadge] = (0, import_react6.useState)(null);
6832
- const [activeCommands, setActiveCommands] = (0, import_react6.useState)(/* @__PURE__ */ new Set());
6833
- const [sectionGap, setSectionGap] = (0, import_react6.useState)(null);
6834
- const [toolbarShowEditLink, setToolbarShowEditLink] = (0, import_react6.useState)(false);
6835
- const hoveredItemElRef = (0, import_react6.useRef)(null);
6836
- const [hoveredItemRect, setHoveredItemRect] = (0, import_react6.useState)(null);
6837
- const siblingHintElRef = (0, import_react6.useRef)(null);
6838
- const [siblingHintRect, setSiblingHintRect] = (0, import_react6.useState)(null);
6839
- const [isItemDragging, setIsItemDragging] = (0, import_react6.useState)(false);
6840
- const [linkPopover, setLinkPopover] = (0, import_react6.useState)(null);
6841
- const [sitePages, setSitePages] = (0, import_react6.useState)([]);
6842
- const [sectionsByPath, setSectionsByPath] = (0, import_react6.useState)({});
6843
- const sectionsPrefetchGenRef = (0, import_react6.useRef)(0);
6844
- const setLinkPopoverRef = (0, import_react6.useRef)(setLinkPopover);
6845
- const linkPopoverPanelRef = (0, import_react6.useRef)(null);
6846
- const linkPopoverOpenRef = (0, import_react6.useRef)(false);
6847
- const linkPopoverGraceUntilRef = (0, import_react6.useRef)(0);
7515
+ const [reorderHrefKey, setReorderHrefKey] = (0, import_react8.useState)(null);
7516
+ const [reorderDragDisabled, setReorderDragDisabled] = (0, import_react8.useState)(false);
7517
+ const [toggleState, setToggleState] = (0, import_react8.useState)(null);
7518
+ const [maxBadge, setMaxBadge] = (0, import_react8.useState)(null);
7519
+ const [activeCommands, setActiveCommands] = (0, import_react8.useState)(/* @__PURE__ */ new Set());
7520
+ const [sectionGap, setSectionGap] = (0, import_react8.useState)(null);
7521
+ const [toolbarShowEditLink, setToolbarShowEditLink] = (0, import_react8.useState)(false);
7522
+ const hoveredNavContainerRef = (0, import_react8.useRef)(null);
7523
+ const [hoveredNavContainerRect, setHoveredNavContainerRect] = (0, import_react8.useState)(null);
7524
+ const hoveredItemElRef = (0, import_react8.useRef)(null);
7525
+ const [hoveredItemRect, setHoveredItemRect] = (0, import_react8.useState)(null);
7526
+ const siblingHintElRef = (0, import_react8.useRef)(null);
7527
+ const [siblingHintRect, setSiblingHintRect] = (0, import_react8.useState)(null);
7528
+ const [isItemDragging, setIsItemDragging] = (0, import_react8.useState)(false);
7529
+ const [linkPopover, setLinkPopover] = (0, import_react8.useState)(null);
7530
+ const linkPopoverSessionRef = (0, import_react8.useRef)(null);
7531
+ const addNavAfterAnchorRef = (0, import_react8.useRef)(null);
7532
+ const editContentRef = (0, import_react8.useRef)({});
7533
+ const [sitePages, setSitePages] = (0, import_react8.useState)([]);
7534
+ const [sectionsByPath, setSectionsByPath] = (0, import_react8.useState)({});
7535
+ const sectionsPrefetchGenRef = (0, import_react8.useRef)(0);
7536
+ const setLinkPopoverRef = (0, import_react8.useRef)(setLinkPopover);
7537
+ const linkPopoverPanelRef = (0, import_react8.useRef)(null);
7538
+ const linkPopoverOpenRef = (0, import_react8.useRef)(false);
7539
+ const linkPopoverGraceUntilRef = (0, import_react8.useRef)(0);
6848
7540
  setLinkPopoverRef.current = setLinkPopover;
7541
+ linkPopoverSessionRef.current = linkPopover;
6849
7542
  const bumpLinkPopoverGrace = () => {
6850
7543
  linkPopoverGraceUntilRef.current = Date.now() + 350;
6851
7544
  };
6852
- const runSectionsPrefetch = (0, import_react6.useCallback)((pages) => {
7545
+ const runSectionsPrefetch = (0, import_react8.useCallback)((pages) => {
6853
7546
  if (!isEditMode || shouldUseDevFixtures() || pages.length === 0) return;
6854
7547
  const gen = ++sectionsPrefetchGenRef.current;
6855
7548
  const paths = pages.map((p) => p.path);
@@ -6868,9 +7561,9 @@ function OhhwellsBridge() {
6868
7561
  );
6869
7562
  });
6870
7563
  }, [isEditMode, pathname]);
6871
- const runSectionsPrefetchRef = (0, import_react6.useRef)(runSectionsPrefetch);
7564
+ const runSectionsPrefetchRef = (0, import_react8.useRef)(runSectionsPrefetch);
6872
7565
  runSectionsPrefetchRef.current = runSectionsPrefetch;
6873
- (0, import_react6.useEffect)(() => {
7566
+ (0, import_react8.useEffect)(() => {
6874
7567
  if (!linkPopover) return;
6875
7568
  if (hoveredImageRef.current) {
6876
7569
  hoveredImageRef.current = null;
@@ -6878,33 +7571,9 @@ function OhhwellsBridge() {
6878
7571
  }
6879
7572
  hoveredGapRef.current = null;
6880
7573
  setSectionGap(null);
6881
- setMediaHover(null);
6882
- postToParent({ type: "ow:link-modal-lock", locked: true });
6883
- const html = document.documentElement;
6884
- const body = document.body;
6885
- const prevHtmlOverflow = html.style.overflow;
6886
- const prevBodyOverflow = body.style.overflow;
6887
- html.style.overflow = "hidden";
6888
- body.style.overflow = "hidden";
6889
- const preventBackgroundScroll = (e) => {
6890
- const target = e.target;
6891
- if (target instanceof Element && target.closest('[data-ohw-link-modal-root], [data-slot="dialog-overlay"]')) {
6892
- return;
6893
- }
6894
- e.preventDefault();
6895
- };
6896
- const scrollOpts = { passive: false, capture: true };
6897
- document.addEventListener("wheel", preventBackgroundScroll, scrollOpts);
6898
- document.addEventListener("touchmove", preventBackgroundScroll, scrollOpts);
6899
- return () => {
6900
- postToParent({ type: "ow:link-modal-lock", locked: false });
6901
- html.style.overflow = prevHtmlOverflow;
6902
- body.style.overflow = prevBodyOverflow;
6903
- document.removeEventListener("wheel", preventBackgroundScroll, scrollOpts);
6904
- document.removeEventListener("touchmove", preventBackgroundScroll, scrollOpts);
6905
- };
6906
- }, [linkPopover, postToParent]);
6907
- (0, import_react6.useEffect)(() => {
7574
+ postToParent2({ type: "ow:image-unhover" });
7575
+ }, [linkPopover, postToParent2]);
7576
+ (0, import_react8.useEffect)(() => {
6908
7577
  if (!isEditMode) return;
6909
7578
  const useFixtures = shouldUseDevFixtures();
6910
7579
  if (useFixtures) {
@@ -6925,17 +7594,17 @@ function OhhwellsBridge() {
6925
7594
  runSectionsPrefetchRef.current(mapped);
6926
7595
  };
6927
7596
  window.addEventListener("message", onSitePages);
6928
- if (!useFixtures) postToParent({ type: "ow:request-site-pages" });
7597
+ if (!useFixtures) postToParent2({ type: "ow:request-site-pages" });
6929
7598
  return () => window.removeEventListener("message", onSitePages);
6930
- }, [isEditMode, postToParent]);
6931
- (0, import_react6.useEffect)(() => {
7599
+ }, [isEditMode, postToParent2]);
7600
+ (0, import_react8.useEffect)(() => {
6932
7601
  if (!isEditMode || shouldUseDevFixtures()) return;
6933
7602
  void loadAllSectionsManifest().then((manifest) => {
6934
7603
  if (Object.keys(manifest).length === 0) return;
6935
7604
  setSectionsByPath((prev) => ({ ...manifest, ...prev }));
6936
7605
  });
6937
7606
  }, [isEditMode]);
6938
- (0, import_react6.useEffect)(() => {
7607
+ (0, import_react8.useEffect)(() => {
6939
7608
  const update = () => {
6940
7609
  const el = activeElRef.current ?? selectedElRef.current;
6941
7610
  if (el) setToolbarRect(getEditMeasureEl(el).getBoundingClientRect());
@@ -6959,10 +7628,10 @@ function OhhwellsBridge() {
6959
7628
  vvp.removeEventListener("resize", update);
6960
7629
  };
6961
7630
  }, []);
6962
- const refreshStateRules = (0, import_react6.useCallback)(() => {
7631
+ const refreshStateRules = (0, import_react8.useCallback)(() => {
6963
7632
  editStylesRef.current?.forceHover && (editStylesRef.current.forceHover.textContent = collectStateRules());
6964
7633
  }, []);
6965
- const processConfigRequest = (0, import_react6.useCallback)((insertAfterVal) => {
7634
+ const processConfigRequest = (0, import_react8.useCallback)((insertAfterVal) => {
6966
7635
  const tracker = getSectionsTracker();
6967
7636
  let entries = [];
6968
7637
  try {
@@ -6985,7 +7654,7 @@ function OhhwellsBridge() {
6985
7654
  }
6986
7655
  window.postMessage({ type: "ow:schedule-config", insertAfter: insertAfterVal, scheduleId: null }, "*");
6987
7656
  }, [isEditMode]);
6988
- const deactivate = (0, import_react6.useCallback)(() => {
7657
+ const deactivate = (0, import_react8.useCallback)(() => {
6989
7658
  const el = activeElRef.current;
6990
7659
  if (!el) return;
6991
7660
  const key = el.dataset.ohwKey;
@@ -7014,21 +7683,23 @@ function OhhwellsBridge() {
7014
7683
  setMaxBadge(null);
7015
7684
  setActiveCommands(/* @__PURE__ */ new Set());
7016
7685
  setToolbarShowEditLink(false);
7017
- postToParent({ type: "ow:exit-edit" });
7018
- }, [postToParent]);
7019
- const deselect = (0, import_react6.useCallback)(() => {
7686
+ postToParent2({ type: "ow:exit-edit" });
7687
+ }, [postToParent2]);
7688
+ const deselect = (0, import_react8.useCallback)(() => {
7020
7689
  selectedElRef.current = null;
7021
7690
  setReorderHrefKey(null);
7022
7691
  setReorderDragDisabled(false);
7023
7692
  siblingHintElRef.current = null;
7024
7693
  setSiblingHintRect(null);
7025
7694
  setIsItemDragging(false);
7695
+ hoveredNavContainerRef.current = null;
7696
+ setHoveredNavContainerRect(null);
7026
7697
  if (!activeElRef.current) {
7027
7698
  setToolbarRect(null);
7028
7699
  setToolbarVariant("none");
7029
7700
  }
7030
7701
  }, []);
7031
- const reselectNavigationItem = (0, import_react6.useCallback)((navAnchor) => {
7702
+ const reselectNavigationItem = (0, import_react8.useCallback)((navAnchor) => {
7032
7703
  selectedElRef.current = navAnchor;
7033
7704
  const { key, disabled } = getNavigationItemReorderState(navAnchor);
7034
7705
  setReorderHrefKey(key);
@@ -7038,7 +7709,7 @@ function OhhwellsBridge() {
7038
7709
  setToolbarShowEditLink(false);
7039
7710
  setActiveCommands(/* @__PURE__ */ new Set());
7040
7711
  }, []);
7041
- const commitNavigationTextEdit = (0, import_react6.useCallback)((navAnchor) => {
7712
+ const commitNavigationTextEdit = (0, import_react8.useCallback)((navAnchor) => {
7042
7713
  const el = activeElRef.current;
7043
7714
  if (!el) return;
7044
7715
  const key = el.dataset.ohwKey;
@@ -7051,9 +7722,9 @@ function OhhwellsBridge() {
7051
7722
  const html = sanitizeHtml(el.innerHTML);
7052
7723
  const original = originalContentRef.current ?? "";
7053
7724
  if (html !== sanitizeHtml(original)) {
7054
- postToParent({ type: "ow:change", nodes: [{ key, text: html }] });
7725
+ postToParent2({ type: "ow:change", nodes: [{ key, text: html }] });
7055
7726
  const h = document.documentElement.scrollHeight;
7056
- if (h > 50) postToParent({ type: "ow:height", height: h });
7727
+ if (h > 50) postToParent2({ type: "ow:height", height: h });
7057
7728
  }
7058
7729
  }
7059
7730
  el.removeAttribute("contenteditable");
@@ -7061,31 +7732,38 @@ function OhhwellsBridge() {
7061
7732
  setMaxBadge(null);
7062
7733
  setActiveCommands(/* @__PURE__ */ new Set());
7063
7734
  setToolbarShowEditLink(false);
7064
- postToParent({ type: "ow:exit-edit" });
7735
+ postToParent2({ type: "ow:exit-edit" });
7065
7736
  reselectNavigationItem(navAnchor);
7066
- }, [postToParent, reselectNavigationItem]);
7067
- const handleAddNavigationItem = (0, import_react6.useCallback)(() => {
7068
- const selected = selectedElRef.current;
7069
- if (!selected) return;
7070
- const target = getNavigationItemAddHintTarget(selected);
7071
- siblingHintElRef.current = target;
7072
- setSiblingHintRect(target.getBoundingClientRect());
7737
+ }, [postToParent2, reselectNavigationItem]);
7738
+ const handleAddTopLevelNavItem = (0, import_react8.useCallback)(() => {
7739
+ const items = listNavbarItems();
7740
+ addNavAfterAnchorRef.current = items[items.length - 1] ?? null;
7741
+ deselectRef.current();
7742
+ deactivateRef.current();
7743
+ bumpLinkPopoverGrace();
7744
+ setLinkPopover({
7745
+ key: "__add-nav__",
7746
+ mode: "create",
7747
+ intent: "add-nav"
7748
+ });
7073
7749
  }, []);
7074
- const handleItemDragStart = (0, import_react6.useCallback)(() => {
7750
+ const handleItemDragStart = (0, import_react8.useCallback)(() => {
7075
7751
  siblingHintElRef.current = null;
7076
7752
  setSiblingHintRect(null);
7077
7753
  setIsItemDragging(true);
7078
7754
  }, []);
7079
- const handleItemDragEnd = (0, import_react6.useCallback)(() => {
7755
+ const handleItemDragEnd = (0, import_react8.useCallback)(() => {
7080
7756
  setIsItemDragging(false);
7081
7757
  }, []);
7082
7758
  reselectNavigationItemRef.current = reselectNavigationItem;
7083
7759
  commitNavigationTextEditRef.current = commitNavigationTextEdit;
7084
- const select = (0, import_react6.useCallback)((anchor) => {
7760
+ const select = (0, import_react8.useCallback)((anchor) => {
7085
7761
  if (!isNavigationItem(anchor)) return;
7086
7762
  if (activeElRef.current) deactivate();
7087
7763
  selectedElRef.current = anchor;
7088
7764
  clearHrefKeyHover(anchor);
7765
+ hoveredNavContainerRef.current = null;
7766
+ setHoveredNavContainerRect(null);
7089
7767
  setHoveredItemRect(null);
7090
7768
  hoveredItemElRef.current = null;
7091
7769
  siblingHintElRef.current = null;
@@ -7099,11 +7777,13 @@ function OhhwellsBridge() {
7099
7777
  setToolbarShowEditLink(false);
7100
7778
  setActiveCommands(/* @__PURE__ */ new Set());
7101
7779
  }, [deactivate]);
7102
- const selectFrame = (0, import_react6.useCallback)((el) => {
7780
+ const selectFrame = (0, import_react8.useCallback)((el) => {
7103
7781
  if (!isNavigationContainer(el)) return;
7104
7782
  if (activeElRef.current) deactivate();
7105
7783
  selectedElRef.current = el;
7106
7784
  clearHrefKeyHover(el);
7785
+ hoveredNavContainerRef.current = null;
7786
+ setHoveredNavContainerRect(null);
7107
7787
  setHoveredItemRect(null);
7108
7788
  hoveredItemElRef.current = null;
7109
7789
  siblingHintElRef.current = null;
@@ -7116,7 +7796,7 @@ function OhhwellsBridge() {
7116
7796
  setToolbarShowEditLink(false);
7117
7797
  setActiveCommands(/* @__PURE__ */ new Set());
7118
7798
  }, [deactivate]);
7119
- const activate = (0, import_react6.useCallback)((el, options) => {
7799
+ const activate = (0, import_react8.useCallback)((el, options) => {
7120
7800
  if (activeElRef.current === el) return;
7121
7801
  selectedElRef.current = null;
7122
7802
  deactivate();
@@ -7148,15 +7828,15 @@ function OhhwellsBridge() {
7148
7828
  setReorderDragDisabled(reorderDisabled);
7149
7829
  }
7150
7830
  setToolbarRect(getEditMeasureEl(el).getBoundingClientRect());
7151
- postToParent({ type: "ow:enter-edit", key: el.dataset.ohwKey });
7831
+ postToParent2({ type: "ow:enter-edit", key: el.dataset.ohwKey });
7152
7832
  requestAnimationFrame(() => refreshActiveCommandsRef.current());
7153
- }, [deactivate, postToParent]);
7833
+ }, [deactivate, postToParent2]);
7154
7834
  activateRef.current = activate;
7155
7835
  deactivateRef.current = deactivate;
7156
7836
  selectRef.current = select;
7157
7837
  selectFrameRef.current = selectFrame;
7158
7838
  deselectRef.current = deselect;
7159
- (0, import_react6.useLayoutEffect)(() => {
7839
+ (0, import_react8.useLayoutEffect)(() => {
7160
7840
  if (!subdomain || isEditMode) {
7161
7841
  setFetchState("done");
7162
7842
  return;
@@ -7196,6 +7876,7 @@ function OhhwellsBridge() {
7196
7876
  });
7197
7877
  applyLinkByKey(key, val);
7198
7878
  }
7879
+ reconcileNavbarItemsFromContent(content);
7199
7880
  enforceLinkHrefs();
7200
7881
  initSectionsFromContent(content, true);
7201
7882
  sectionsLoadedRef.current = true;
@@ -7224,7 +7905,7 @@ function OhhwellsBridge() {
7224
7905
  cancelled = true;
7225
7906
  };
7226
7907
  }, [subdomain, isEditMode]);
7227
- (0, import_react6.useEffect)(() => {
7908
+ (0, import_react8.useEffect)(() => {
7228
7909
  if (!subdomain || isEditMode) return;
7229
7910
  let debounceTimer = null;
7230
7911
  let observer = null;
@@ -7255,6 +7936,7 @@ function OhhwellsBridge() {
7255
7936
  });
7256
7937
  applyLinkByKey(key, val);
7257
7938
  }
7939
+ reconcileNavbarItemsFromContent(content);
7258
7940
  } finally {
7259
7941
  observer?.observe(document.body, { childList: true, subtree: true });
7260
7942
  }
@@ -7272,26 +7954,44 @@ function OhhwellsBridge() {
7272
7954
  if (debounceTimer) clearTimeout(debounceTimer);
7273
7955
  };
7274
7956
  }, [subdomain, isEditMode, pathname]);
7275
- (0, import_react6.useLayoutEffect)(() => {
7957
+ (0, import_react8.useLayoutEffect)(() => {
7276
7958
  const el = document.getElementById("ohw-loader");
7277
7959
  if (!el) return;
7278
7960
  const visible = Boolean(subdomain) && fetchState !== "done";
7279
7961
  el.style.display = visible ? "flex" : "none";
7280
7962
  }, [subdomain, fetchState]);
7281
- (0, import_react6.useEffect)(() => {
7282
- postToParent({ type: "ow:navigation", path: pathname });
7283
- }, [pathname, postToParent]);
7284
- (0, import_react6.useEffect)(() => {
7963
+ (0, import_react8.useEffect)(() => {
7964
+ postToParent2({ type: "ow:navigation", path: pathname });
7965
+ }, [pathname, postToParent2]);
7966
+ (0, import_react8.useEffect)(() => {
7285
7967
  if (!isEditMode) return;
7968
+ if (linkPopoverSessionRef.current?.intent === "add-nav") return;
7969
+ if (document.querySelector("[data-ohw-section-picker]")) return;
7286
7970
  setLinkPopover(null);
7287
7971
  deselectRef.current();
7288
7972
  deactivateRef.current();
7289
7973
  }, [pathname, isEditMode]);
7290
- (0, import_react6.useEffect)(() => {
7974
+ (0, import_react8.useEffect)(() => {
7975
+ const contentForNav = () => {
7976
+ if (isEditMode) return editContentRef.current;
7977
+ if (!subdomain) return {};
7978
+ return contentCache.get(subdomain) ?? {};
7979
+ };
7980
+ const run = () => reconcileNavbarItemsFromContent(contentForNav());
7981
+ run();
7982
+ const nav = document.querySelector("nav");
7983
+ if (!nav) return;
7984
+ const observer = new MutationObserver(() => {
7985
+ requestAnimationFrame(run);
7986
+ });
7987
+ observer.observe(nav, { childList: true, subtree: true });
7988
+ return () => observer.disconnect();
7989
+ }, [isEditMode, pathname, subdomain, fetchState]);
7990
+ (0, import_react8.useEffect)(() => {
7291
7991
  if (!isEditMode) return;
7292
7992
  const measure = () => {
7293
7993
  const h = document.body.scrollHeight;
7294
- if (h > 50) postToParent({ type: "ow:height", height: h });
7994
+ if (h > 50) postToParent2({ type: "ow:height", height: h });
7295
7995
  };
7296
7996
  const t1 = setTimeout(measure, 50);
7297
7997
  const t2 = setTimeout(measure, 500);
@@ -7310,8 +8010,8 @@ function OhhwellsBridge() {
7310
8010
  if (resizeTimer) clearTimeout(resizeTimer);
7311
8011
  window.removeEventListener("resize", handleResize);
7312
8012
  };
7313
- }, [pathname, isEditMode, postToParent]);
7314
- (0, import_react6.useEffect)(() => {
8013
+ }, [pathname, isEditMode, postToParent2]);
8014
+ (0, import_react8.useEffect)(() => {
7315
8015
  if (!subdomainFromQuery || isEditMode) return;
7316
8016
  const handleClick = (e) => {
7317
8017
  const anchor = e.target.closest("a");
@@ -7327,7 +8027,7 @@ function OhhwellsBridge() {
7327
8027
  document.addEventListener("click", handleClick, true);
7328
8028
  return () => document.removeEventListener("click", handleClick, true);
7329
8029
  }, [subdomainFromQuery, isEditMode, router]);
7330
- (0, import_react6.useEffect)(() => {
8030
+ (0, import_react8.useEffect)(() => {
7331
8031
  if (!isEditMode) {
7332
8032
  editStylesRef.current?.base.remove();
7333
8033
  editStylesRef.current?.forceHover.remove();
@@ -7401,6 +8101,13 @@ function OhhwellsBridge() {
7401
8101
  if (target.closest("[data-ohw-max-badge]")) return;
7402
8102
  if (isInsideLinkEditor(target)) return;
7403
8103
  if (target.closest('[data-ohw-edit-chrome], [data-ohw-item-interaction], [data-ohw-drag-handle-container], [data-slot="drag-handle"]')) {
8104
+ const navFromChrome = resolveNavContainerSelectionTarget(target, e.clientX, e.clientY);
8105
+ if (navFromChrome) {
8106
+ e.preventDefault();
8107
+ e.stopPropagation();
8108
+ selectFrameRef.current(navFromChrome);
8109
+ return;
8110
+ }
7404
8111
  e.preventDefault();
7405
8112
  e.stopPropagation();
7406
8113
  return;
@@ -7415,7 +8122,8 @@ function OhhwellsBridge() {
7415
8122
  bumpLinkPopoverGrace();
7416
8123
  setLinkPopoverRef.current({
7417
8124
  key: editable.dataset.ohwKey ?? "",
7418
- target: getLinkHref(editable)
8125
+ mode: "edit",
8126
+ target: getLinkHref2(editable)
7419
8127
  });
7420
8128
  return;
7421
8129
  }
@@ -7451,6 +8159,13 @@ function OhhwellsBridge() {
7451
8159
  selectRef.current(hrefAnchor);
7452
8160
  return;
7453
8161
  }
8162
+ const navContainerToSelect = resolveNavContainerSelectionTarget(target, e.clientX, e.clientY);
8163
+ if (navContainerToSelect) {
8164
+ e.preventDefault();
8165
+ e.stopPropagation();
8166
+ selectFrameRef.current(navContainerToSelect);
8167
+ return;
8168
+ }
7454
8169
  const selectedContainer = selectedElRef.current;
7455
8170
  if (selectedContainer && isNavigationContainer(selectedContainer)) {
7456
8171
  const navItem = getNavigationItemAnchor(target);
@@ -7509,8 +8224,24 @@ function OhhwellsBridge() {
7509
8224
  };
7510
8225
  const handleMouseOver = (e) => {
7511
8226
  const target = e.target;
8227
+ if (toolbarVariantRef.current !== "select-frame") {
8228
+ const navContainer = target.closest("[data-ohw-nav-container]");
8229
+ if (navContainer && !getNavigationItemAnchor(target)) {
8230
+ hoveredNavContainerRef.current = navContainer;
8231
+ setHoveredNavContainerRect(navContainer.getBoundingClientRect());
8232
+ hoveredItemElRef.current = null;
8233
+ setHoveredItemRect(null);
8234
+ return;
8235
+ }
8236
+ if (!target.closest("[data-ohw-nav-container]")) {
8237
+ hoveredNavContainerRef.current = null;
8238
+ setHoveredNavContainerRect(null);
8239
+ }
8240
+ }
7512
8241
  const navAnchor = getNavigationItemAnchor(target);
7513
8242
  if (navAnchor) {
8243
+ hoveredNavContainerRef.current = null;
8244
+ setHoveredNavContainerRect(null);
7514
8245
  const selected2 = selectedElRef.current;
7515
8246
  if (selected2 === navAnchor || selected2?.contains(navAnchor)) return;
7516
8247
  clearHrefKeyHover(navAnchor);
@@ -7535,6 +8266,14 @@ function OhhwellsBridge() {
7535
8266
  };
7536
8267
  const handleMouseOut = (e) => {
7537
8268
  const target = e.target;
8269
+ if (target.closest("[data-ohw-nav-container]")) {
8270
+ const related2 = e.relatedTarget instanceof Element ? e.relatedTarget : null;
8271
+ if (related2?.closest("[data-ohw-nav-container], [data-ohw-navbar-container-chrome], [data-ohw-navbar-add-button]")) {
8272
+ return;
8273
+ }
8274
+ hoveredNavContainerRef.current = null;
8275
+ setHoveredNavContainerRect(null);
8276
+ }
7538
8277
  const navAnchor = getNavigationItemAnchor(target);
7539
8278
  if (navAnchor) {
7540
8279
  const related2 = e.relatedTarget instanceof Element ? e.relatedTarget : null;
@@ -7656,42 +8395,85 @@ function OhhwellsBridge() {
7656
8395
  }
7657
8396
  return smallest;
7658
8397
  };
8398
+ const dismissImageHover = () => {
8399
+ if (hoveredImageRef.current) {
8400
+ hoveredImageRef.current = null;
8401
+ hoveredImageHasTextOverlapRef.current = false;
8402
+ resumeAnimTracks();
8403
+ postToParentRef.current({ type: "ow:image-unhover" });
8404
+ }
8405
+ clearImageHover();
8406
+ };
8407
+ const probeNavigationHoverAt = (x, y) => {
8408
+ if (toolbarVariantRef.current === "select-frame") {
8409
+ hoveredNavContainerRef.current = null;
8410
+ setHoveredNavContainerRect(null);
8411
+ return;
8412
+ }
8413
+ const navContainer = document.querySelector("[data-ohw-nav-container]");
8414
+ if (!navContainer) {
8415
+ hoveredNavContainerRef.current = null;
8416
+ setHoveredNavContainerRect(null);
8417
+ return;
8418
+ }
8419
+ const containerRect = navContainer.getBoundingClientRect();
8420
+ const overContainer = x >= containerRect.left && x <= containerRect.right && y >= containerRect.top && y <= containerRect.bottom;
8421
+ if (!overContainer) {
8422
+ hoveredNavContainerRef.current = null;
8423
+ setHoveredNavContainerRect(null);
8424
+ return;
8425
+ }
8426
+ const navItemHit = Array.from(
8427
+ navContainer.querySelectorAll("[data-ohw-href-key]")
8428
+ ).find((el) => {
8429
+ if (!isNavigationItem(el) || isNavbarButton2(el)) return false;
8430
+ const itemRect = el.getBoundingClientRect();
8431
+ return x >= itemRect.left && x <= itemRect.right && y >= itemRect.top && y <= itemRect.bottom;
8432
+ });
8433
+ if (navItemHit) {
8434
+ hoveredNavContainerRef.current = null;
8435
+ setHoveredNavContainerRect(null);
8436
+ const selected = selectedElRef.current;
8437
+ if (selected !== navItemHit && !selected?.contains(navItemHit)) {
8438
+ clearHrefKeyHover(navItemHit);
8439
+ hoveredItemElRef.current = navItemHit;
8440
+ setHoveredItemRect(navItemHit.getBoundingClientRect());
8441
+ }
8442
+ return;
8443
+ }
8444
+ hoveredNavContainerRef.current = navContainer;
8445
+ setHoveredNavContainerRect(containerRect);
8446
+ hoveredItemElRef.current = null;
8447
+ setHoveredItemRect(null);
8448
+ };
7659
8449
  const probeImageAt = (clientX, clientY, isDragOver = false, fromParentViewport = false) => {
7660
8450
  if (linkPopoverOpenRef.current) {
7661
- if (hoveredImageRef.current) {
7662
- hoveredImageRef.current = null;
7663
- hoveredImageHasTextOverlapRef.current = false;
7664
- resumeAnimTracks();
7665
- clearImageHover();
7666
- }
8451
+ dismissImageHover();
8452
+ return;
8453
+ }
8454
+ const { x, y } = toProbeCoords(clientX, clientY, fromParentViewport);
8455
+ if (isPointOverNavigation(x, y)) {
8456
+ dismissImageHover();
8457
+ probeNavigationHoverAt(x, y);
7667
8458
  return;
7668
8459
  }
8460
+ hoveredNavContainerRef.current = null;
8461
+ setHoveredNavContainerRect(null);
7669
8462
  const toggleEl = document.querySelector("[data-ohw-state-toggle]");
7670
8463
  if (toggleEl) {
7671
- const { x, y } = toProbeCoords(clientX, clientY, fromParentViewport);
7672
8464
  const tr = toggleEl.getBoundingClientRect();
7673
8465
  if (x >= tr.left && x <= tr.right && y >= tr.top && y <= tr.bottom) {
7674
- if (hoveredImageRef.current) {
7675
- hoveredImageRef.current = null;
7676
- resumeAnimTracks();
7677
- clearImageHover();
7678
- }
8466
+ dismissImageHover();
7679
8467
  return;
7680
8468
  }
7681
8469
  }
7682
8470
  const imgEl = findImageAtPoint(clientX, clientY, fromParentViewport);
7683
8471
  const activeEl = activeElRef.current;
7684
8472
  if (activeEl && (!imgEl || imgEl.contains(activeEl))) {
7685
- if (hoveredImageRef.current) {
7686
- hoveredImageRef.current = null;
7687
- hoveredImageHasTextOverlapRef.current = false;
7688
- resumeAnimTracks();
7689
- clearImageHover();
7690
- }
8473
+ dismissImageHover();
7691
8474
  return;
7692
8475
  }
7693
8476
  if (imgEl) {
7694
- const { x, y } = toProbeCoords(clientX, clientY, fromParentViewport);
7695
8477
  const topEl = document.elementFromPoint(x, y);
7696
8478
  if (topEl?.closest('[data-ohw-toolbar], [data-ohw-edit-chrome], [data-ohw-item-interaction], [data-ohw-drag-handle-container], [data-slot="drag-handle"]')) {
7697
8479
  if (hoveredImageRef.current) {
@@ -7791,12 +8573,12 @@ function OhhwellsBridge() {
7791
8573
  resumeAnimTracks();
7792
8574
  clearImageHover();
7793
8575
  }
7794
- const { x, y } = toProbeCoords(clientX, clientY, fromParentViewport);
8576
+ const { x: x2, y: y2 } = toProbeCoords(clientX, clientY, fromParentViewport);
7795
8577
  const textEl = Array.from(
7796
8578
  document.querySelectorAll(NON_MEDIA_SELECTOR)
7797
8579
  ).find((el) => {
7798
8580
  const er = el.getBoundingClientRect();
7799
- return x >= er.left && x <= er.right && y >= er.top && y <= er.bottom;
8581
+ return x2 >= er.left && x2 <= er.right && y2 >= er.top && y2 <= er.bottom;
7800
8582
  });
7801
8583
  document.querySelectorAll("[data-ohw-hovered]").forEach((el) => el.removeAttribute("data-ohw-hovered"));
7802
8584
  if (textEl && !textEl.hasAttribute("contenteditable")) {
@@ -8093,6 +8875,8 @@ function OhhwellsBridge() {
8093
8875
  sectionsLoadedRef.current = true;
8094
8876
  pendingScheduleConfigRequests.current.splice(0).forEach(processConfigRequest);
8095
8877
  }
8878
+ editContentRef.current = { ...editContentRef.current, ...content };
8879
+ reconcileNavbarItemsFromContent(editContentRef.current);
8096
8880
  enforceLinkHrefs();
8097
8881
  postToParentRef.current({ type: "ow:hydrate-done" });
8098
8882
  };
@@ -8100,6 +8884,7 @@ function OhhwellsBridge() {
8100
8884
  const handleDeactivate = (e) => {
8101
8885
  if (e.data?.type !== "ow:deactivate") return;
8102
8886
  if (Date.now() < linkPopoverGraceUntilRef.current) return;
8887
+ if (document.querySelector("[data-ohw-section-picker]")) return;
8103
8888
  if (linkPopoverOpenRef.current) {
8104
8889
  setLinkPopoverRef.current(null);
8105
8890
  return;
@@ -8109,6 +8894,7 @@ function OhhwellsBridge() {
8109
8894
  };
8110
8895
  window.addEventListener("message", handleDeactivate);
8111
8896
  const handleKeyDown = (e) => {
8897
+ if (e.key === "Escape" && document.querySelector("[data-ohw-section-picker]")) return;
8112
8898
  if ((e.ctrlKey || e.metaKey) && e.key.toLowerCase() === "a" && activeElRef.current) {
8113
8899
  const navAnchor = getNavigationItemAnchor(activeElRef.current);
8114
8900
  if (navAnchor) {
@@ -8123,8 +8909,13 @@ function OhhwellsBridge() {
8123
8909
  return;
8124
8910
  }
8125
8911
  if (e.key === "Escape" && selectedElRef.current && !activeElRef.current) {
8912
+ if (toolbarVariantRef.current === "select-frame") {
8913
+ deselectRef.current();
8914
+ return;
8915
+ }
8126
8916
  const parent = getNavigationSelectionParent(selectedElRef.current);
8127
8917
  if (parent) {
8918
+ e.preventDefault();
8128
8919
  selectFrameRef.current(parent);
8129
8920
  } else {
8130
8921
  deselectRef.current();
@@ -8186,6 +8977,9 @@ function OhhwellsBridge() {
8186
8977
  if (hoveredItemElRef.current) {
8187
8978
  setHoveredItemRect(hoveredItemElRef.current.getBoundingClientRect());
8188
8979
  }
8980
+ if (hoveredNavContainerRef.current) {
8981
+ setHoveredNavContainerRect(hoveredNavContainerRef.current.getBoundingClientRect());
8982
+ }
8189
8983
  if (siblingHintElRef.current) {
8190
8984
  setSiblingHintRect(siblingHintElRef.current.getBoundingClientRect());
8191
8985
  }
@@ -8395,6 +9189,32 @@ function OhhwellsBridge() {
8395
9189
  activateRef.current(textEditable, { caretX: clientX, caretY: clientY });
8396
9190
  return;
8397
9191
  }
9192
+ const navContainer = document.querySelector("[data-ohw-nav-container]");
9193
+ if (navContainer) {
9194
+ const r2 = navContainer.getBoundingClientRect();
9195
+ const pad = 8;
9196
+ const over = clientX >= r2.left - pad && clientX <= r2.right + pad && clientY >= r2.top - pad && clientY <= r2.bottom + pad;
9197
+ if (over && !isPointOverNavItem(navContainer, clientX, clientY)) {
9198
+ selectFrameRef.current(navContainer);
9199
+ return;
9200
+ }
9201
+ }
9202
+ const navRoot = document.querySelector("[data-ohw-nav-root]");
9203
+ if (navRoot && navContainer) {
9204
+ const rr = navRoot.getBoundingClientRect();
9205
+ if (clientX >= rr.left && clientX <= rr.right && clientY >= rr.top && clientY <= rr.bottom && !isPointOverNavItem(navContainer, clientX, clientY)) {
9206
+ const book = navRoot.querySelector('[data-ohw-role="navbar-button"]');
9207
+ if (book) {
9208
+ const br = book.getBoundingClientRect();
9209
+ if (clientX >= br.left && clientX <= br.right && clientY >= br.top && clientY <= br.bottom) {
9210
+ deactivateRef.current();
9211
+ return;
9212
+ }
9213
+ }
9214
+ selectFrameRef.current(navContainer);
9215
+ return;
9216
+ }
9217
+ }
8398
9218
  deactivateRef.current();
8399
9219
  };
8400
9220
  window.addEventListener("message", handleSave);
@@ -8465,7 +9285,7 @@ function OhhwellsBridge() {
8465
9285
  if (imageShowTimerRef.current) clearTimeout(imageShowTimerRef.current);
8466
9286
  };
8467
9287
  }, [isEditMode, refreshStateRules]);
8468
- (0, import_react6.useEffect)(() => {
9288
+ (0, import_react8.useEffect)(() => {
8469
9289
  const handler = (e) => {
8470
9290
  if (e.data?.type !== "ow:request-schedule-config") return;
8471
9291
  const insertAfterVal = e.data.insertAfter;
@@ -8481,7 +9301,7 @@ function OhhwellsBridge() {
8481
9301
  window.addEventListener("message", handler);
8482
9302
  return () => window.removeEventListener("message", handler);
8483
9303
  }, [processConfigRequest]);
8484
- (0, import_react6.useEffect)(() => {
9304
+ (0, import_react8.useEffect)(() => {
8485
9305
  if (!isEditMode) return;
8486
9306
  document.querySelectorAll("[data-ohw-active-state]").forEach((el) => {
8487
9307
  el.removeAttribute("data-ohw-active-state");
@@ -8502,27 +9322,27 @@ function OhhwellsBridge() {
8502
9322
  const next = { ...prev, [pathKey]: sections };
8503
9323
  return shouldUseDevFixtures() ? { ...DEV_SECTIONS_BY_PATH, ...next } : next;
8504
9324
  });
8505
- postToParent({ type: "ow:ready", version: "1", path: pathname, nodes: collectEditableNodes(), sections });
8506
- postToParent({ type: "ow:request-site-pages" });
9325
+ postToParent2({ type: "ow:ready", version: "1", path: pathname, nodes: collectEditableNodes(), sections });
9326
+ postToParent2({ type: "ow:request-site-pages" });
8507
9327
  }, 150);
8508
9328
  return () => {
8509
9329
  cancelAnimationFrame(raf);
8510
9330
  clearTimeout(timer);
8511
9331
  };
8512
- }, [pathname, isEditMode, refreshStateRules, postToParent]);
8513
- (0, import_react6.useEffect)(() => {
9332
+ }, [pathname, isEditMode, refreshStateRules, postToParent2]);
9333
+ (0, import_react8.useEffect)(() => {
8514
9334
  scrollToHashSectionWhenReady();
8515
9335
  const onHashChange = () => scrollToHashSectionWhenReady();
8516
9336
  window.addEventListener("hashchange", onHashChange);
8517
9337
  return () => window.removeEventListener("hashchange", onHashChange);
8518
9338
  }, [pathname]);
8519
- const handleCommand = (0, import_react6.useCallback)((cmd) => {
9339
+ const handleCommand = (0, import_react8.useCallback)((cmd) => {
8520
9340
  document.execCommand(cmd, false);
8521
9341
  activeElRef.current?.focus();
8522
9342
  if (activeElRef.current) setToolbarRect(getEditMeasureEl(activeElRef.current).getBoundingClientRect());
8523
9343
  refreshActiveCommandsRef.current();
8524
9344
  }, []);
8525
- const handleStateChange = (0, import_react6.useCallback)((state) => {
9345
+ const handleStateChange = (0, import_react8.useCallback)((state) => {
8526
9346
  if (!activeStateElRef.current) return;
8527
9347
  const el = activeStateElRef.current;
8528
9348
  if (state === "Default") {
@@ -8535,18 +9355,22 @@ function OhhwellsBridge() {
8535
9355
  }
8536
9356
  setToggleState((prev) => prev ? { ...prev, activeState: state } : null);
8537
9357
  }, [deactivate]);
8538
- const closeLinkPopover = (0, import_react6.useCallback)(() => setLinkPopover(null), []);
8539
- const openLinkPopoverForActive = (0, import_react6.useCallback)(() => {
9358
+ const closeLinkPopover = (0, import_react8.useCallback)(() => {
9359
+ addNavAfterAnchorRef.current = null;
9360
+ setLinkPopover(null);
9361
+ }, []);
9362
+ const openLinkPopoverForActive = (0, import_react8.useCallback)(() => {
8540
9363
  const hrefCtx = getHrefKeyFromElement(activeElRef.current);
8541
9364
  if (!hrefCtx) return;
8542
9365
  bumpLinkPopoverGrace();
8543
9366
  setLinkPopover({
8544
9367
  key: hrefCtx.key,
8545
- target: getLinkHref(hrefCtx.anchor)
9368
+ mode: "edit",
9369
+ target: getLinkHref2(hrefCtx.anchor)
8546
9370
  });
8547
9371
  deactivate();
8548
9372
  }, [deactivate]);
8549
- const openLinkPopoverForSelected = (0, import_react6.useCallback)(() => {
9373
+ const openLinkPopoverForSelected = (0, import_react8.useCallback)(() => {
8550
9374
  const anchor = selectedElRef.current;
8551
9375
  if (!anchor) return;
8552
9376
  const key = anchor.getAttribute("data-ohw-href-key");
@@ -8554,30 +9378,70 @@ function OhhwellsBridge() {
8554
9378
  bumpLinkPopoverGrace();
8555
9379
  setLinkPopover({
8556
9380
  key,
8557
- target: getLinkHref(anchor)
9381
+ mode: "edit",
9382
+ target: getLinkHref2(anchor)
8558
9383
  });
8559
9384
  deselect();
8560
9385
  }, [deselect]);
8561
- const handleLinkPopoverSubmit = (0, import_react6.useCallback)(
9386
+ const handleLinkPopoverSubmit = (0, import_react8.useCallback)(
8562
9387
  (target) => {
8563
- if (!linkPopover) return;
8564
- const { key } = linkPopover;
9388
+ const session = linkPopoverSessionRef.current;
9389
+ if (!session) return;
9390
+ if (session.intent === "add-nav") {
9391
+ const { pageRoute, sectionId } = parseTarget(target);
9392
+ const page = resolvePage(pageRoute, sitePages);
9393
+ const pageSections = getSectionsForPath(sectionsByPath, pageRoute);
9394
+ const section = sectionId ? pageSections.find((s) => s.id === sectionId) : void 0;
9395
+ const label = section?.label ?? page.title;
9396
+ const afterAnchor = addNavAfterAnchorRef.current;
9397
+ const { anchor, hrefKey, labelKey, index, order } = insertNavbarItem(target, label, afterAnchor);
9398
+ applyLinkByKey(hrefKey, target);
9399
+ document.querySelectorAll(`[data-ohw-key="${labelKey}"]`).forEach((el) => {
9400
+ el.textContent = label;
9401
+ });
9402
+ const navCount = String(index + 1);
9403
+ const orderJson = JSON.stringify(order);
9404
+ editContentRef.current = {
9405
+ ...editContentRef.current,
9406
+ [hrefKey]: target,
9407
+ [labelKey]: label,
9408
+ [NAV_COUNT_KEY]: navCount,
9409
+ [NAV_ORDER_KEY]: orderJson
9410
+ };
9411
+ postToParent2({
9412
+ type: "ow:change",
9413
+ nodes: [
9414
+ { key: hrefKey, text: target },
9415
+ { key: labelKey, text: label },
9416
+ { key: NAV_COUNT_KEY, text: navCount },
9417
+ { key: NAV_ORDER_KEY, text: orderJson }
9418
+ ]
9419
+ });
9420
+ addNavAfterAnchorRef.current = null;
9421
+ setLinkPopover(null);
9422
+ enforceLinkHrefs();
9423
+ requestAnimationFrame(() => {
9424
+ selectRef.current(anchor);
9425
+ });
9426
+ return;
9427
+ }
9428
+ const { key } = session;
8565
9429
  applyLinkByKey(key, target);
8566
- postToParent({ type: "ow:change", nodes: [{ key, text: target }] });
9430
+ postToParent2({ type: "ow:change", nodes: [{ key, text: target }] });
8567
9431
  setLinkPopover(null);
8568
9432
  },
8569
- [linkPopover, postToParent]
9433
+ [postToParent2, sitePages, sectionsByPath]
8570
9434
  );
8571
9435
  const showEditLink = toolbarShowEditLink;
8572
9436
  const currentSections = sectionsByPath[pathname] ?? [];
8573
9437
  linkPopoverOpenRef.current = linkPopover !== null;
8574
- const handleMediaReplace = (0, import_react6.useCallback)(
9438
+ const handleMediaReplace = (0, import_react8.useCallback)(
8575
9439
  (key) => {
8576
- postToParent({ type: "ow:image-pick", key, elementType: mediaHover?.elementType ?? "image" });
9440
+ postToParent2({ type: "ow:image-pick", key, elementType: mediaHover?.elementType ?? "image" });
8577
9441
  },
8578
- [postToParent, mediaHover?.elementType]
9442
+ [postToParent2, mediaHover?.elementType]
8579
9443
  );
8580
- const handleVideoSettingsChange = (0, import_react6.useCallback)(
9444
+ const handleVideoSettingsChange = (0, import_react8.useCallback)(
8581
9445
  (key, settings) => {
8582
9446
  document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
8583
9447
  const video = getVideoEl(el);
@@ -8585,7 +9449,7 @@ function OhhwellsBridge() {
8585
9449
  if (typeof settings.autoplay === "boolean") video.autoplay = settings.autoplay;
8586
9450
  if (typeof settings.muted === "boolean") video.muted = settings.muted;
8587
9451
  syncVideoPlayback(video);
8588
- postToParent({
9452
+ postToParent2({
8589
9453
  type: "ow:change",
8590
9454
  nodes: [
8591
9455
  { key: `${key}${VIDEO_AUTOPLAY_SUFFIX}`, text: String(video.autoplay) },
@@ -8597,9 +9461,9 @@ function OhhwellsBridge() {
8597
9461
  );
8598
9462
  });
8599
9463
  },
8600
- [postToParent]
9464
+ [postToParent2]
8601
9465
  );
8602
- const handleMediaFadeOutComplete = (0, import_react6.useCallback)((key) => {
9466
+ const handleMediaFadeOutComplete = (0, import_react8.useCallback)((key) => {
8603
9467
  setUploadingRects((prev) => {
8604
9468
  if (!(key in prev)) return prev;
8605
9469
  const next = { ...prev };
@@ -8607,10 +9471,10 @@ function OhhwellsBridge() {
8607
9471
  return next;
8608
9472
  });
8609
9473
  }, []);
8610
- return bridgeRoot ? (0, import_react_dom2.createPortal)(
8611
- /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(import_jsx_runtime22.Fragment, { children: [
8612
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { ref: attachVisibleViewport, "data-ohw-visible-viewport": "", "aria-hidden": true }),
8613
- Object.entries(uploadingRects).map(([key, { rect, fadingOut }]) => /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
9474
+ return bridgeRoot ? (0, import_react_dom3.createPortal)(
9475
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(import_jsx_runtime23.Fragment, { children: [
9476
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { ref: attachVisibleViewport, "data-ohw-visible-viewport": "", "aria-hidden": true }),
9477
+ Object.entries(uploadingRects).map(([key, { rect, fadingOut }]) => /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
8614
9478
  MediaOverlay,
8615
9479
  {
8616
9480
  hover: { key, rect, elementType: "image", isDragOver: false, hasTextOverlap: false },
@@ -8621,7 +9485,7 @@ function OhhwellsBridge() {
8621
9485
  },
8622
9486
  `uploading-${key}`
8623
9487
  )),
8624
- mediaHover && !(mediaHover.key in uploadingRects) && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
9488
+ mediaHover && !(mediaHover.key in uploadingRects) && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
8625
9489
  MediaOverlay,
8626
9490
  {
8627
9491
  hover: mediaHover,
@@ -8630,9 +9494,17 @@ function OhhwellsBridge() {
8630
9494
  onVideoSettingsChange: handleVideoSettingsChange
8631
9495
  }
8632
9496
  ),
8633
- siblingHintRect && !isItemDragging && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(ItemInteractionLayer, { rect: siblingHintRect, state: "sibling-hint" }),
8634
- hoveredItemRect && !siblingHintRect && !isItemDragging && hoveredItemElRef.current !== selectedElRef.current && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(ItemInteractionLayer, { rect: hoveredItemRect, state: "hover" }),
8635
- toolbarRect && (toolbarVariant === "link-action" || toolbarVariant === "select-frame") && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
9497
+ siblingHintRect && !isItemDragging && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(ItemInteractionLayer, { rect: siblingHintRect, state: "sibling-hint" }),
9498
+ hoveredNavContainerRect && toolbarVariant !== "select-frame" && !linkPopover && !isItemDragging && !siblingHintRect && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(ItemInteractionLayer, { rect: hoveredNavContainerRect, state: "hover" }),
9499
+ toolbarVariant === "select-frame" && toolbarRect && !linkPopover && !isItemDragging && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
9500
+ NavbarContainerChrome,
9501
+ {
9502
+ rect: toolbarRect,
9503
+ onAdd: handleAddTopLevelNavItem
9504
+ }
9505
+ ),
9506
+ hoveredItemRect && !hoveredNavContainerRect && !siblingHintRect && !isItemDragging && hoveredItemElRef.current !== selectedElRef.current && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(ItemInteractionLayer, { rect: hoveredItemRect, state: "hover" }),
9507
+ toolbarRect && (toolbarVariant === "link-action" || toolbarVariant === "select-frame") && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
8636
9508
  ItemInteractionLayer,
8637
9509
  {
8638
9510
  rect: toolbarRect,
@@ -8643,18 +9515,19 @@ function OhhwellsBridge() {
8643
9515
  dragHandleLabel: reorderHrefKey ? `Reorder ${reorderHrefKey}` : "Reorder item",
8644
9516
  onDragHandleDragStart: handleItemDragStart,
8645
9517
  onDragHandleDragEnd: handleItemDragEnd,
8646
- toolbar: toolbarVariant === "link-action" && !isItemDragging ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
9518
+ toolbar: toolbarVariant === "link-action" && !isItemDragging ? /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
8647
9519
  ItemActionToolbar,
8648
9520
  {
8649
9521
  onEditLink: openLinkPopoverForSelected,
8650
- onAddItem: handleAddNavigationItem,
8651
- addItemDisabled: false
9522
+ addItemDisabled: true,
9523
+ editLinkDisabled: false,
9524
+ moreDisabled: true
8652
9525
  }
8653
9526
  ) : void 0
8654
9527
  }
8655
9528
  ),
8656
- toolbarRect && toolbarVariant === "rich-text" && /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(import_jsx_runtime22.Fragment, { children: [
8657
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
9529
+ toolbarRect && toolbarVariant === "rich-text" && /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(import_jsx_runtime23.Fragment, { children: [
9530
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
8658
9531
  EditGlowChrome,
8659
9532
  {
8660
9533
  rect: toolbarRect,
@@ -8663,7 +9536,7 @@ function OhhwellsBridge() {
8663
9536
  dragDisabled: reorderDragDisabled
8664
9537
  }
8665
9538
  ),
8666
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
9539
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
8667
9540
  FloatingToolbar,
8668
9541
  {
8669
9542
  rect: toolbarRect,
@@ -8676,7 +9549,7 @@ function OhhwellsBridge() {
8676
9549
  }
8677
9550
  )
8678
9551
  ] }),
8679
- maxBadge && /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(
9552
+ maxBadge && /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(
8680
9553
  "div",
8681
9554
  {
8682
9555
  "data-ohw-max-badge": "",
@@ -8702,7 +9575,7 @@ function OhhwellsBridge() {
8702
9575
  ]
8703
9576
  }
8704
9577
  ),
8705
- toggleState && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
9578
+ toggleState && !linkPopover && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
8706
9579
  StateToggle,
8707
9580
  {
8708
9581
  rect: toggleState.rect,
@@ -8711,15 +9584,15 @@ function OhhwellsBridge() {
8711
9584
  onStateChange: handleStateChange
8712
9585
  }
8713
9586
  ),
8714
- sectionGap && /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(
9587
+ sectionGap && !linkPopover && /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(
8715
9588
  "div",
8716
9589
  {
8717
9590
  "data-ohw-section-insert-line": "",
8718
9591
  className: "fixed left-0 w-full z-2147483646 flex items-center pointer-events-none",
8719
9592
  style: { top: sectionGap.y, transform: "translateY(-50%)" },
8720
9593
  children: [
8721
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { className: "flex-1 bg-primary", style: { height: 3 } }),
8722
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
9594
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { className: "flex-1 bg-primary", style: { height: 3 } }),
9595
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
8723
9596
  Badge,
8724
9597
  {
8725
9598
  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",
@@ -8732,35 +9605,36 @@ function OhhwellsBridge() {
8732
9605
  children: "Add Section"
8733
9606
  }
8734
9607
  ),
8735
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { className: "flex-1 bg-primary", style: { height: 3 } })
9608
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { className: "flex-1 bg-primary", style: { height: 3 } })
8736
9609
  ]
8737
9610
  }
8738
9611
  ),
8739
- linkPopover && dialogPortalContainer ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
9612
+ linkPopover && dialogPortalContainer ? /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
8740
9613
  LinkPopover,
8741
9614
  {
8742
9615
  panelRef: linkPopoverPanelRef,
8743
9616
  portalContainer: dialogPortalContainer,
8744
9617
  open: true,
8745
- mode: "edit",
9618
+ mode: linkPopover.mode ?? "edit",
8746
9619
  pages: sitePages,
8747
9620
  sections: currentSections,
8748
9621
  sectionsByPath,
8749
9622
  initialTarget: linkPopover.target,
9623
+ existingTargets: linkPopover.intent === "add-nav" ? getNavbarExistingTargets() : [],
8750
9624
  onClose: closeLinkPopover,
8751
9625
  onSubmit: handleLinkPopoverSubmit
8752
9626
  },
8753
- `${linkPopover.key}-${pathname}`
9627
+ linkPopover.key
8754
9628
  ) : null,
8755
- sectionGap && /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(
9629
+ sectionGap && /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(
8756
9630
  "div",
8757
9631
  {
8758
9632
  "data-ohw-section-insert-line": "",
8759
9633
  className: "fixed left-0 w-full z-2147483646 flex items-center pointer-events-none",
8760
9634
  style: { top: sectionGap.y, transform: "translateY(-50%)" },
8761
9635
  children: [
8762
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { className: "flex-1 bg-primary", style: { height: 3 } }),
8763
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
9636
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { className: "flex-1 bg-primary", style: { height: 3 } }),
9637
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
8764
9638
  Badge,
8765
9639
  {
8766
9640
  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",
@@ -8773,7 +9647,7 @@ function OhhwellsBridge() {
8773
9647
  children: "Add Section"
8774
9648
  }
8775
9649
  ),
8776
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { className: "flex-1 bg-primary", style: { height: 3 } })
9650
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { className: "flex-1 bg-primary", style: { height: 3 } })
8777
9651
  ]
8778
9652
  }
8779
9653
  )