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

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;
@@ -7224,7 +7904,7 @@ function OhhwellsBridge() {
7224
7904
  cancelled = true;
7225
7905
  };
7226
7906
  }, [subdomain, isEditMode]);
7227
- (0, import_react6.useEffect)(() => {
7907
+ (0, import_react8.useEffect)(() => {
7228
7908
  if (!subdomain || isEditMode) return;
7229
7909
  let debounceTimer = null;
7230
7910
  let observer = null;
@@ -7272,26 +7952,40 @@ function OhhwellsBridge() {
7272
7952
  if (debounceTimer) clearTimeout(debounceTimer);
7273
7953
  };
7274
7954
  }, [subdomain, isEditMode, pathname]);
7275
- (0, import_react6.useLayoutEffect)(() => {
7955
+ (0, import_react8.useLayoutEffect)(() => {
7276
7956
  const el = document.getElementById("ohw-loader");
7277
7957
  if (!el) return;
7278
7958
  const visible = Boolean(subdomain) && fetchState !== "done";
7279
7959
  el.style.display = visible ? "flex" : "none";
7280
7960
  }, [subdomain, fetchState]);
7281
- (0, import_react6.useEffect)(() => {
7282
- postToParent({ type: "ow:navigation", path: pathname });
7283
- }, [pathname, postToParent]);
7284
- (0, import_react6.useEffect)(() => {
7961
+ (0, import_react8.useEffect)(() => {
7962
+ postToParent2({ type: "ow:navigation", path: pathname });
7963
+ }, [pathname, postToParent2]);
7964
+ (0, import_react8.useEffect)(() => {
7285
7965
  if (!isEditMode) return;
7966
+ if (linkPopoverSessionRef.current?.intent === "add-nav") return;
7967
+ if (document.querySelector("[data-ohw-section-picker]")) return;
7286
7968
  setLinkPopover(null);
7287
7969
  deselectRef.current();
7288
7970
  deactivateRef.current();
7289
7971
  }, [pathname, isEditMode]);
7290
- (0, import_react6.useEffect)(() => {
7972
+ (0, import_react8.useEffect)(() => {
7973
+ if (!isEditMode) return;
7974
+ const run = () => reconcileNavbarItemsFromContent(editContentRef.current);
7975
+ run();
7976
+ const nav = document.querySelector("nav");
7977
+ if (!nav) return;
7978
+ const observer = new MutationObserver(() => {
7979
+ requestAnimationFrame(run);
7980
+ });
7981
+ observer.observe(nav, { childList: true, subtree: true });
7982
+ return () => observer.disconnect();
7983
+ }, [isEditMode, pathname]);
7984
+ (0, import_react8.useEffect)(() => {
7291
7985
  if (!isEditMode) return;
7292
7986
  const measure = () => {
7293
7987
  const h = document.body.scrollHeight;
7294
- if (h > 50) postToParent({ type: "ow:height", height: h });
7988
+ if (h > 50) postToParent2({ type: "ow:height", height: h });
7295
7989
  };
7296
7990
  const t1 = setTimeout(measure, 50);
7297
7991
  const t2 = setTimeout(measure, 500);
@@ -7310,8 +8004,8 @@ function OhhwellsBridge() {
7310
8004
  if (resizeTimer) clearTimeout(resizeTimer);
7311
8005
  window.removeEventListener("resize", handleResize);
7312
8006
  };
7313
- }, [pathname, isEditMode, postToParent]);
7314
- (0, import_react6.useEffect)(() => {
8007
+ }, [pathname, isEditMode, postToParent2]);
8008
+ (0, import_react8.useEffect)(() => {
7315
8009
  if (!subdomainFromQuery || isEditMode) return;
7316
8010
  const handleClick = (e) => {
7317
8011
  const anchor = e.target.closest("a");
@@ -7327,7 +8021,7 @@ function OhhwellsBridge() {
7327
8021
  document.addEventListener("click", handleClick, true);
7328
8022
  return () => document.removeEventListener("click", handleClick, true);
7329
8023
  }, [subdomainFromQuery, isEditMode, router]);
7330
- (0, import_react6.useEffect)(() => {
8024
+ (0, import_react8.useEffect)(() => {
7331
8025
  if (!isEditMode) {
7332
8026
  editStylesRef.current?.base.remove();
7333
8027
  editStylesRef.current?.forceHover.remove();
@@ -7401,6 +8095,13 @@ function OhhwellsBridge() {
7401
8095
  if (target.closest("[data-ohw-max-badge]")) return;
7402
8096
  if (isInsideLinkEditor(target)) return;
7403
8097
  if (target.closest('[data-ohw-edit-chrome], [data-ohw-item-interaction], [data-ohw-drag-handle-container], [data-slot="drag-handle"]')) {
8098
+ const navFromChrome = resolveNavContainerSelectionTarget(target, e.clientX, e.clientY);
8099
+ if (navFromChrome) {
8100
+ e.preventDefault();
8101
+ e.stopPropagation();
8102
+ selectFrameRef.current(navFromChrome);
8103
+ return;
8104
+ }
7404
8105
  e.preventDefault();
7405
8106
  e.stopPropagation();
7406
8107
  return;
@@ -7415,7 +8116,8 @@ function OhhwellsBridge() {
7415
8116
  bumpLinkPopoverGrace();
7416
8117
  setLinkPopoverRef.current({
7417
8118
  key: editable.dataset.ohwKey ?? "",
7418
- target: getLinkHref(editable)
8119
+ mode: "edit",
8120
+ target: getLinkHref2(editable)
7419
8121
  });
7420
8122
  return;
7421
8123
  }
@@ -7451,6 +8153,13 @@ function OhhwellsBridge() {
7451
8153
  selectRef.current(hrefAnchor);
7452
8154
  return;
7453
8155
  }
8156
+ const navContainerToSelect = resolveNavContainerSelectionTarget(target, e.clientX, e.clientY);
8157
+ if (navContainerToSelect) {
8158
+ e.preventDefault();
8159
+ e.stopPropagation();
8160
+ selectFrameRef.current(navContainerToSelect);
8161
+ return;
8162
+ }
7454
8163
  const selectedContainer = selectedElRef.current;
7455
8164
  if (selectedContainer && isNavigationContainer(selectedContainer)) {
7456
8165
  const navItem = getNavigationItemAnchor(target);
@@ -7509,8 +8218,24 @@ function OhhwellsBridge() {
7509
8218
  };
7510
8219
  const handleMouseOver = (e) => {
7511
8220
  const target = e.target;
8221
+ if (toolbarVariantRef.current !== "select-frame") {
8222
+ const navContainer = target.closest("[data-ohw-nav-container]");
8223
+ if (navContainer && !getNavigationItemAnchor(target)) {
8224
+ hoveredNavContainerRef.current = navContainer;
8225
+ setHoveredNavContainerRect(navContainer.getBoundingClientRect());
8226
+ hoveredItemElRef.current = null;
8227
+ setHoveredItemRect(null);
8228
+ return;
8229
+ }
8230
+ if (!target.closest("[data-ohw-nav-container]")) {
8231
+ hoveredNavContainerRef.current = null;
8232
+ setHoveredNavContainerRect(null);
8233
+ }
8234
+ }
7512
8235
  const navAnchor = getNavigationItemAnchor(target);
7513
8236
  if (navAnchor) {
8237
+ hoveredNavContainerRef.current = null;
8238
+ setHoveredNavContainerRect(null);
7514
8239
  const selected2 = selectedElRef.current;
7515
8240
  if (selected2 === navAnchor || selected2?.contains(navAnchor)) return;
7516
8241
  clearHrefKeyHover(navAnchor);
@@ -7535,6 +8260,14 @@ function OhhwellsBridge() {
7535
8260
  };
7536
8261
  const handleMouseOut = (e) => {
7537
8262
  const target = e.target;
8263
+ if (target.closest("[data-ohw-nav-container]")) {
8264
+ const related2 = e.relatedTarget instanceof Element ? e.relatedTarget : null;
8265
+ if (related2?.closest("[data-ohw-nav-container], [data-ohw-navbar-container-chrome], [data-ohw-navbar-add-button]")) {
8266
+ return;
8267
+ }
8268
+ hoveredNavContainerRef.current = null;
8269
+ setHoveredNavContainerRect(null);
8270
+ }
7538
8271
  const navAnchor = getNavigationItemAnchor(target);
7539
8272
  if (navAnchor) {
7540
8273
  const related2 = e.relatedTarget instanceof Element ? e.relatedTarget : null;
@@ -7656,42 +8389,85 @@ function OhhwellsBridge() {
7656
8389
  }
7657
8390
  return smallest;
7658
8391
  };
8392
+ const dismissImageHover = () => {
8393
+ if (hoveredImageRef.current) {
8394
+ hoveredImageRef.current = null;
8395
+ hoveredImageHasTextOverlapRef.current = false;
8396
+ resumeAnimTracks();
8397
+ postToParentRef.current({ type: "ow:image-unhover" });
8398
+ }
8399
+ clearImageHover();
8400
+ };
8401
+ const probeNavigationHoverAt = (x, y) => {
8402
+ if (toolbarVariantRef.current === "select-frame") {
8403
+ hoveredNavContainerRef.current = null;
8404
+ setHoveredNavContainerRect(null);
8405
+ return;
8406
+ }
8407
+ const navContainer = document.querySelector("[data-ohw-nav-container]");
8408
+ if (!navContainer) {
8409
+ hoveredNavContainerRef.current = null;
8410
+ setHoveredNavContainerRect(null);
8411
+ return;
8412
+ }
8413
+ const containerRect = navContainer.getBoundingClientRect();
8414
+ const overContainer = x >= containerRect.left && x <= containerRect.right && y >= containerRect.top && y <= containerRect.bottom;
8415
+ if (!overContainer) {
8416
+ hoveredNavContainerRef.current = null;
8417
+ setHoveredNavContainerRect(null);
8418
+ return;
8419
+ }
8420
+ const navItemHit = Array.from(
8421
+ navContainer.querySelectorAll("[data-ohw-href-key]")
8422
+ ).find((el) => {
8423
+ if (!isNavigationItem(el) || isNavbarButton2(el)) return false;
8424
+ const itemRect = el.getBoundingClientRect();
8425
+ return x >= itemRect.left && x <= itemRect.right && y >= itemRect.top && y <= itemRect.bottom;
8426
+ });
8427
+ if (navItemHit) {
8428
+ hoveredNavContainerRef.current = null;
8429
+ setHoveredNavContainerRect(null);
8430
+ const selected = selectedElRef.current;
8431
+ if (selected !== navItemHit && !selected?.contains(navItemHit)) {
8432
+ clearHrefKeyHover(navItemHit);
8433
+ hoveredItemElRef.current = navItemHit;
8434
+ setHoveredItemRect(navItemHit.getBoundingClientRect());
8435
+ }
8436
+ return;
8437
+ }
8438
+ hoveredNavContainerRef.current = navContainer;
8439
+ setHoveredNavContainerRect(containerRect);
8440
+ hoveredItemElRef.current = null;
8441
+ setHoveredItemRect(null);
8442
+ };
7659
8443
  const probeImageAt = (clientX, clientY, isDragOver = false, fromParentViewport = false) => {
7660
8444
  if (linkPopoverOpenRef.current) {
7661
- if (hoveredImageRef.current) {
7662
- hoveredImageRef.current = null;
7663
- hoveredImageHasTextOverlapRef.current = false;
7664
- resumeAnimTracks();
7665
- clearImageHover();
7666
- }
8445
+ dismissImageHover();
8446
+ return;
8447
+ }
8448
+ const { x, y } = toProbeCoords(clientX, clientY, fromParentViewport);
8449
+ if (isPointOverNavigation(x, y)) {
8450
+ dismissImageHover();
8451
+ probeNavigationHoverAt(x, y);
7667
8452
  return;
7668
8453
  }
8454
+ hoveredNavContainerRef.current = null;
8455
+ setHoveredNavContainerRect(null);
7669
8456
  const toggleEl = document.querySelector("[data-ohw-state-toggle]");
7670
8457
  if (toggleEl) {
7671
- const { x, y } = toProbeCoords(clientX, clientY, fromParentViewport);
7672
8458
  const tr = toggleEl.getBoundingClientRect();
7673
8459
  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
- }
8460
+ dismissImageHover();
7679
8461
  return;
7680
8462
  }
7681
8463
  }
7682
8464
  const imgEl = findImageAtPoint(clientX, clientY, fromParentViewport);
7683
8465
  const activeEl = activeElRef.current;
7684
8466
  if (activeEl && (!imgEl || imgEl.contains(activeEl))) {
7685
- if (hoveredImageRef.current) {
7686
- hoveredImageRef.current = null;
7687
- hoveredImageHasTextOverlapRef.current = false;
7688
- resumeAnimTracks();
7689
- clearImageHover();
7690
- }
8467
+ dismissImageHover();
7691
8468
  return;
7692
8469
  }
7693
8470
  if (imgEl) {
7694
- const { x, y } = toProbeCoords(clientX, clientY, fromParentViewport);
7695
8471
  const topEl = document.elementFromPoint(x, y);
7696
8472
  if (topEl?.closest('[data-ohw-toolbar], [data-ohw-edit-chrome], [data-ohw-item-interaction], [data-ohw-drag-handle-container], [data-slot="drag-handle"]')) {
7697
8473
  if (hoveredImageRef.current) {
@@ -7791,12 +8567,12 @@ function OhhwellsBridge() {
7791
8567
  resumeAnimTracks();
7792
8568
  clearImageHover();
7793
8569
  }
7794
- const { x, y } = toProbeCoords(clientX, clientY, fromParentViewport);
8570
+ const { x: x2, y: y2 } = toProbeCoords(clientX, clientY, fromParentViewport);
7795
8571
  const textEl = Array.from(
7796
8572
  document.querySelectorAll(NON_MEDIA_SELECTOR)
7797
8573
  ).find((el) => {
7798
8574
  const er = el.getBoundingClientRect();
7799
- return x >= er.left && x <= er.right && y >= er.top && y <= er.bottom;
8575
+ return x2 >= er.left && x2 <= er.right && y2 >= er.top && y2 <= er.bottom;
7800
8576
  });
7801
8577
  document.querySelectorAll("[data-ohw-hovered]").forEach((el) => el.removeAttribute("data-ohw-hovered"));
7802
8578
  if (textEl && !textEl.hasAttribute("contenteditable")) {
@@ -8093,6 +8869,8 @@ function OhhwellsBridge() {
8093
8869
  sectionsLoadedRef.current = true;
8094
8870
  pendingScheduleConfigRequests.current.splice(0).forEach(processConfigRequest);
8095
8871
  }
8872
+ editContentRef.current = { ...editContentRef.current, ...content };
8873
+ reconcileNavbarItemsFromContent(editContentRef.current);
8096
8874
  enforceLinkHrefs();
8097
8875
  postToParentRef.current({ type: "ow:hydrate-done" });
8098
8876
  };
@@ -8100,6 +8878,7 @@ function OhhwellsBridge() {
8100
8878
  const handleDeactivate = (e) => {
8101
8879
  if (e.data?.type !== "ow:deactivate") return;
8102
8880
  if (Date.now() < linkPopoverGraceUntilRef.current) return;
8881
+ if (document.querySelector("[data-ohw-section-picker]")) return;
8103
8882
  if (linkPopoverOpenRef.current) {
8104
8883
  setLinkPopoverRef.current(null);
8105
8884
  return;
@@ -8109,6 +8888,7 @@ function OhhwellsBridge() {
8109
8888
  };
8110
8889
  window.addEventListener("message", handleDeactivate);
8111
8890
  const handleKeyDown = (e) => {
8891
+ if (e.key === "Escape" && document.querySelector("[data-ohw-section-picker]")) return;
8112
8892
  if ((e.ctrlKey || e.metaKey) && e.key.toLowerCase() === "a" && activeElRef.current) {
8113
8893
  const navAnchor = getNavigationItemAnchor(activeElRef.current);
8114
8894
  if (navAnchor) {
@@ -8123,8 +8903,13 @@ function OhhwellsBridge() {
8123
8903
  return;
8124
8904
  }
8125
8905
  if (e.key === "Escape" && selectedElRef.current && !activeElRef.current) {
8906
+ if (toolbarVariantRef.current === "select-frame") {
8907
+ deselectRef.current();
8908
+ return;
8909
+ }
8126
8910
  const parent = getNavigationSelectionParent(selectedElRef.current);
8127
8911
  if (parent) {
8912
+ e.preventDefault();
8128
8913
  selectFrameRef.current(parent);
8129
8914
  } else {
8130
8915
  deselectRef.current();
@@ -8186,6 +8971,9 @@ function OhhwellsBridge() {
8186
8971
  if (hoveredItemElRef.current) {
8187
8972
  setHoveredItemRect(hoveredItemElRef.current.getBoundingClientRect());
8188
8973
  }
8974
+ if (hoveredNavContainerRef.current) {
8975
+ setHoveredNavContainerRect(hoveredNavContainerRef.current.getBoundingClientRect());
8976
+ }
8189
8977
  if (siblingHintElRef.current) {
8190
8978
  setSiblingHintRect(siblingHintElRef.current.getBoundingClientRect());
8191
8979
  }
@@ -8395,6 +9183,32 @@ function OhhwellsBridge() {
8395
9183
  activateRef.current(textEditable, { caretX: clientX, caretY: clientY });
8396
9184
  return;
8397
9185
  }
9186
+ const navContainer = document.querySelector("[data-ohw-nav-container]");
9187
+ if (navContainer) {
9188
+ const r2 = navContainer.getBoundingClientRect();
9189
+ const pad = 8;
9190
+ const over = clientX >= r2.left - pad && clientX <= r2.right + pad && clientY >= r2.top - pad && clientY <= r2.bottom + pad;
9191
+ if (over && !isPointOverNavItem(navContainer, clientX, clientY)) {
9192
+ selectFrameRef.current(navContainer);
9193
+ return;
9194
+ }
9195
+ }
9196
+ const navRoot = document.querySelector("[data-ohw-nav-root]");
9197
+ if (navRoot && navContainer) {
9198
+ const rr = navRoot.getBoundingClientRect();
9199
+ if (clientX >= rr.left && clientX <= rr.right && clientY >= rr.top && clientY <= rr.bottom && !isPointOverNavItem(navContainer, clientX, clientY)) {
9200
+ const book = navRoot.querySelector('[data-ohw-role="navbar-button"]');
9201
+ if (book) {
9202
+ const br = book.getBoundingClientRect();
9203
+ if (clientX >= br.left && clientX <= br.right && clientY >= br.top && clientY <= br.bottom) {
9204
+ deactivateRef.current();
9205
+ return;
9206
+ }
9207
+ }
9208
+ selectFrameRef.current(navContainer);
9209
+ return;
9210
+ }
9211
+ }
8398
9212
  deactivateRef.current();
8399
9213
  };
8400
9214
  window.addEventListener("message", handleSave);
@@ -8465,7 +9279,7 @@ function OhhwellsBridge() {
8465
9279
  if (imageShowTimerRef.current) clearTimeout(imageShowTimerRef.current);
8466
9280
  };
8467
9281
  }, [isEditMode, refreshStateRules]);
8468
- (0, import_react6.useEffect)(() => {
9282
+ (0, import_react8.useEffect)(() => {
8469
9283
  const handler = (e) => {
8470
9284
  if (e.data?.type !== "ow:request-schedule-config") return;
8471
9285
  const insertAfterVal = e.data.insertAfter;
@@ -8481,7 +9295,7 @@ function OhhwellsBridge() {
8481
9295
  window.addEventListener("message", handler);
8482
9296
  return () => window.removeEventListener("message", handler);
8483
9297
  }, [processConfigRequest]);
8484
- (0, import_react6.useEffect)(() => {
9298
+ (0, import_react8.useEffect)(() => {
8485
9299
  if (!isEditMode) return;
8486
9300
  document.querySelectorAll("[data-ohw-active-state]").forEach((el) => {
8487
9301
  el.removeAttribute("data-ohw-active-state");
@@ -8502,27 +9316,27 @@ function OhhwellsBridge() {
8502
9316
  const next = { ...prev, [pathKey]: sections };
8503
9317
  return shouldUseDevFixtures() ? { ...DEV_SECTIONS_BY_PATH, ...next } : next;
8504
9318
  });
8505
- postToParent({ type: "ow:ready", version: "1", path: pathname, nodes: collectEditableNodes(), sections });
8506
- postToParent({ type: "ow:request-site-pages" });
9319
+ postToParent2({ type: "ow:ready", version: "1", path: pathname, nodes: collectEditableNodes(), sections });
9320
+ postToParent2({ type: "ow:request-site-pages" });
8507
9321
  }, 150);
8508
9322
  return () => {
8509
9323
  cancelAnimationFrame(raf);
8510
9324
  clearTimeout(timer);
8511
9325
  };
8512
- }, [pathname, isEditMode, refreshStateRules, postToParent]);
8513
- (0, import_react6.useEffect)(() => {
9326
+ }, [pathname, isEditMode, refreshStateRules, postToParent2]);
9327
+ (0, import_react8.useEffect)(() => {
8514
9328
  scrollToHashSectionWhenReady();
8515
9329
  const onHashChange = () => scrollToHashSectionWhenReady();
8516
9330
  window.addEventListener("hashchange", onHashChange);
8517
9331
  return () => window.removeEventListener("hashchange", onHashChange);
8518
9332
  }, [pathname]);
8519
- const handleCommand = (0, import_react6.useCallback)((cmd) => {
9333
+ const handleCommand = (0, import_react8.useCallback)((cmd) => {
8520
9334
  document.execCommand(cmd, false);
8521
9335
  activeElRef.current?.focus();
8522
9336
  if (activeElRef.current) setToolbarRect(getEditMeasureEl(activeElRef.current).getBoundingClientRect());
8523
9337
  refreshActiveCommandsRef.current();
8524
9338
  }, []);
8525
- const handleStateChange = (0, import_react6.useCallback)((state) => {
9339
+ const handleStateChange = (0, import_react8.useCallback)((state) => {
8526
9340
  if (!activeStateElRef.current) return;
8527
9341
  const el = activeStateElRef.current;
8528
9342
  if (state === "Default") {
@@ -8535,18 +9349,22 @@ function OhhwellsBridge() {
8535
9349
  }
8536
9350
  setToggleState((prev) => prev ? { ...prev, activeState: state } : null);
8537
9351
  }, [deactivate]);
8538
- const closeLinkPopover = (0, import_react6.useCallback)(() => setLinkPopover(null), []);
8539
- const openLinkPopoverForActive = (0, import_react6.useCallback)(() => {
9352
+ const closeLinkPopover = (0, import_react8.useCallback)(() => {
9353
+ addNavAfterAnchorRef.current = null;
9354
+ setLinkPopover(null);
9355
+ }, []);
9356
+ const openLinkPopoverForActive = (0, import_react8.useCallback)(() => {
8540
9357
  const hrefCtx = getHrefKeyFromElement(activeElRef.current);
8541
9358
  if (!hrefCtx) return;
8542
9359
  bumpLinkPopoverGrace();
8543
9360
  setLinkPopover({
8544
9361
  key: hrefCtx.key,
8545
- target: getLinkHref(hrefCtx.anchor)
9362
+ mode: "edit",
9363
+ target: getLinkHref2(hrefCtx.anchor)
8546
9364
  });
8547
9365
  deactivate();
8548
9366
  }, [deactivate]);
8549
- const openLinkPopoverForSelected = (0, import_react6.useCallback)(() => {
9367
+ const openLinkPopoverForSelected = (0, import_react8.useCallback)(() => {
8550
9368
  const anchor = selectedElRef.current;
8551
9369
  if (!anchor) return;
8552
9370
  const key = anchor.getAttribute("data-ohw-href-key");
@@ -8554,30 +9372,70 @@ function OhhwellsBridge() {
8554
9372
  bumpLinkPopoverGrace();
8555
9373
  setLinkPopover({
8556
9374
  key,
8557
- target: getLinkHref(anchor)
9375
+ mode: "edit",
9376
+ target: getLinkHref2(anchor)
8558
9377
  });
8559
9378
  deselect();
8560
9379
  }, [deselect]);
8561
- const handleLinkPopoverSubmit = (0, import_react6.useCallback)(
9380
+ const handleLinkPopoverSubmit = (0, import_react8.useCallback)(
8562
9381
  (target) => {
8563
- if (!linkPopover) return;
8564
- const { key } = linkPopover;
9382
+ const session = linkPopoverSessionRef.current;
9383
+ if (!session) return;
9384
+ if (session.intent === "add-nav") {
9385
+ const { pageRoute, sectionId } = parseTarget(target);
9386
+ const page = resolvePage(pageRoute, sitePages);
9387
+ const pageSections = getSectionsForPath(sectionsByPath, pageRoute);
9388
+ const section = sectionId ? pageSections.find((s) => s.id === sectionId) : void 0;
9389
+ const label = section?.label ?? page.title;
9390
+ const afterAnchor = addNavAfterAnchorRef.current;
9391
+ const { anchor, hrefKey, labelKey, index, order } = insertNavbarItem(target, label, afterAnchor);
9392
+ applyLinkByKey(hrefKey, target);
9393
+ document.querySelectorAll(`[data-ohw-key="${labelKey}"]`).forEach((el) => {
9394
+ el.textContent = label;
9395
+ });
9396
+ const navCount = String(index + 1);
9397
+ const orderJson = JSON.stringify(order);
9398
+ editContentRef.current = {
9399
+ ...editContentRef.current,
9400
+ [hrefKey]: target,
9401
+ [labelKey]: label,
9402
+ [NAV_COUNT_KEY]: navCount,
9403
+ [NAV_ORDER_KEY]: orderJson
9404
+ };
9405
+ postToParent2({
9406
+ type: "ow:change",
9407
+ nodes: [
9408
+ { key: hrefKey, text: target },
9409
+ { key: labelKey, text: label },
9410
+ { key: NAV_COUNT_KEY, text: navCount },
9411
+ { key: NAV_ORDER_KEY, text: orderJson }
9412
+ ]
9413
+ });
9414
+ addNavAfterAnchorRef.current = null;
9415
+ setLinkPopover(null);
9416
+ enforceLinkHrefs();
9417
+ requestAnimationFrame(() => {
9418
+ selectRef.current(anchor);
9419
+ });
9420
+ return;
9421
+ }
9422
+ const { key } = session;
8565
9423
  applyLinkByKey(key, target);
8566
- postToParent({ type: "ow:change", nodes: [{ key, text: target }] });
9424
+ postToParent2({ type: "ow:change", nodes: [{ key, text: target }] });
8567
9425
  setLinkPopover(null);
8568
9426
  },
8569
- [linkPopover, postToParent]
9427
+ [postToParent2, sitePages, sectionsByPath]
8570
9428
  );
8571
9429
  const showEditLink = toolbarShowEditLink;
8572
9430
  const currentSections = sectionsByPath[pathname] ?? [];
8573
9431
  linkPopoverOpenRef.current = linkPopover !== null;
8574
- const handleMediaReplace = (0, import_react6.useCallback)(
9432
+ const handleMediaReplace = (0, import_react8.useCallback)(
8575
9433
  (key) => {
8576
- postToParent({ type: "ow:image-pick", key, elementType: mediaHover?.elementType ?? "image" });
9434
+ postToParent2({ type: "ow:image-pick", key, elementType: mediaHover?.elementType ?? "image" });
8577
9435
  },
8578
- [postToParent, mediaHover?.elementType]
9436
+ [postToParent2, mediaHover?.elementType]
8579
9437
  );
8580
- const handleVideoSettingsChange = (0, import_react6.useCallback)(
9438
+ const handleVideoSettingsChange = (0, import_react8.useCallback)(
8581
9439
  (key, settings) => {
8582
9440
  document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
8583
9441
  const video = getVideoEl(el);
@@ -8585,7 +9443,7 @@ function OhhwellsBridge() {
8585
9443
  if (typeof settings.autoplay === "boolean") video.autoplay = settings.autoplay;
8586
9444
  if (typeof settings.muted === "boolean") video.muted = settings.muted;
8587
9445
  syncVideoPlayback(video);
8588
- postToParent({
9446
+ postToParent2({
8589
9447
  type: "ow:change",
8590
9448
  nodes: [
8591
9449
  { key: `${key}${VIDEO_AUTOPLAY_SUFFIX}`, text: String(video.autoplay) },
@@ -8597,9 +9455,9 @@ function OhhwellsBridge() {
8597
9455
  );
8598
9456
  });
8599
9457
  },
8600
- [postToParent]
9458
+ [postToParent2]
8601
9459
  );
8602
- const handleMediaFadeOutComplete = (0, import_react6.useCallback)((key) => {
9460
+ const handleMediaFadeOutComplete = (0, import_react8.useCallback)((key) => {
8603
9461
  setUploadingRects((prev) => {
8604
9462
  if (!(key in prev)) return prev;
8605
9463
  const next = { ...prev };
@@ -8607,10 +9465,10 @@ function OhhwellsBridge() {
8607
9465
  return next;
8608
9466
  });
8609
9467
  }, []);
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)(
9468
+ return bridgeRoot ? (0, import_react_dom3.createPortal)(
9469
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(import_jsx_runtime23.Fragment, { children: [
9470
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { ref: attachVisibleViewport, "data-ohw-visible-viewport": "", "aria-hidden": true }),
9471
+ Object.entries(uploadingRects).map(([key, { rect, fadingOut }]) => /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
8614
9472
  MediaOverlay,
8615
9473
  {
8616
9474
  hover: { key, rect, elementType: "image", isDragOver: false, hasTextOverlap: false },
@@ -8621,7 +9479,7 @@ function OhhwellsBridge() {
8621
9479
  },
8622
9480
  `uploading-${key}`
8623
9481
  )),
8624
- mediaHover && !(mediaHover.key in uploadingRects) && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
9482
+ mediaHover && !(mediaHover.key in uploadingRects) && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
8625
9483
  MediaOverlay,
8626
9484
  {
8627
9485
  hover: mediaHover,
@@ -8630,9 +9488,17 @@ function OhhwellsBridge() {
8630
9488
  onVideoSettingsChange: handleVideoSettingsChange
8631
9489
  }
8632
9490
  ),
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)(
9491
+ siblingHintRect && !isItemDragging && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(ItemInteractionLayer, { rect: siblingHintRect, state: "sibling-hint" }),
9492
+ hoveredNavContainerRect && toolbarVariant !== "select-frame" && !linkPopover && !isItemDragging && !siblingHintRect && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(ItemInteractionLayer, { rect: hoveredNavContainerRect, state: "hover" }),
9493
+ toolbarVariant === "select-frame" && toolbarRect && !linkPopover && !isItemDragging && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
9494
+ NavbarContainerChrome,
9495
+ {
9496
+ rect: toolbarRect,
9497
+ onAdd: handleAddTopLevelNavItem
9498
+ }
9499
+ ),
9500
+ hoveredItemRect && !hoveredNavContainerRect && !siblingHintRect && !isItemDragging && hoveredItemElRef.current !== selectedElRef.current && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(ItemInteractionLayer, { rect: hoveredItemRect, state: "hover" }),
9501
+ toolbarRect && (toolbarVariant === "link-action" || toolbarVariant === "select-frame") && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
8636
9502
  ItemInteractionLayer,
8637
9503
  {
8638
9504
  rect: toolbarRect,
@@ -8643,18 +9509,19 @@ function OhhwellsBridge() {
8643
9509
  dragHandleLabel: reorderHrefKey ? `Reorder ${reorderHrefKey}` : "Reorder item",
8644
9510
  onDragHandleDragStart: handleItemDragStart,
8645
9511
  onDragHandleDragEnd: handleItemDragEnd,
8646
- toolbar: toolbarVariant === "link-action" && !isItemDragging ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
9512
+ toolbar: toolbarVariant === "link-action" && !isItemDragging ? /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
8647
9513
  ItemActionToolbar,
8648
9514
  {
8649
9515
  onEditLink: openLinkPopoverForSelected,
8650
- onAddItem: handleAddNavigationItem,
8651
- addItemDisabled: false
9516
+ addItemDisabled: true,
9517
+ editLinkDisabled: false,
9518
+ moreDisabled: true
8652
9519
  }
8653
9520
  ) : void 0
8654
9521
  }
8655
9522
  ),
8656
- toolbarRect && toolbarVariant === "rich-text" && /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(import_jsx_runtime22.Fragment, { children: [
8657
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
9523
+ toolbarRect && toolbarVariant === "rich-text" && /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(import_jsx_runtime23.Fragment, { children: [
9524
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
8658
9525
  EditGlowChrome,
8659
9526
  {
8660
9527
  rect: toolbarRect,
@@ -8663,7 +9530,7 @@ function OhhwellsBridge() {
8663
9530
  dragDisabled: reorderDragDisabled
8664
9531
  }
8665
9532
  ),
8666
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
9533
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
8667
9534
  FloatingToolbar,
8668
9535
  {
8669
9536
  rect: toolbarRect,
@@ -8676,7 +9543,7 @@ function OhhwellsBridge() {
8676
9543
  }
8677
9544
  )
8678
9545
  ] }),
8679
- maxBadge && /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(
9546
+ maxBadge && /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(
8680
9547
  "div",
8681
9548
  {
8682
9549
  "data-ohw-max-badge": "",
@@ -8702,7 +9569,7 @@ function OhhwellsBridge() {
8702
9569
  ]
8703
9570
  }
8704
9571
  ),
8705
- toggleState && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
9572
+ toggleState && !linkPopover && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
8706
9573
  StateToggle,
8707
9574
  {
8708
9575
  rect: toggleState.rect,
@@ -8711,15 +9578,15 @@ function OhhwellsBridge() {
8711
9578
  onStateChange: handleStateChange
8712
9579
  }
8713
9580
  ),
8714
- sectionGap && /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(
9581
+ sectionGap && !linkPopover && /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(
8715
9582
  "div",
8716
9583
  {
8717
9584
  "data-ohw-section-insert-line": "",
8718
9585
  className: "fixed left-0 w-full z-2147483646 flex items-center pointer-events-none",
8719
9586
  style: { top: sectionGap.y, transform: "translateY(-50%)" },
8720
9587
  children: [
8721
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { className: "flex-1 bg-primary", style: { height: 3 } }),
8722
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
9588
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { className: "flex-1 bg-primary", style: { height: 3 } }),
9589
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
8723
9590
  Badge,
8724
9591
  {
8725
9592
  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 +9599,36 @@ function OhhwellsBridge() {
8732
9599
  children: "Add Section"
8733
9600
  }
8734
9601
  ),
8735
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { className: "flex-1 bg-primary", style: { height: 3 } })
9602
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { className: "flex-1 bg-primary", style: { height: 3 } })
8736
9603
  ]
8737
9604
  }
8738
9605
  ),
8739
- linkPopover && dialogPortalContainer ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
9606
+ linkPopover && dialogPortalContainer ? /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
8740
9607
  LinkPopover,
8741
9608
  {
8742
9609
  panelRef: linkPopoverPanelRef,
8743
9610
  portalContainer: dialogPortalContainer,
8744
9611
  open: true,
8745
- mode: "edit",
9612
+ mode: linkPopover.mode ?? "edit",
8746
9613
  pages: sitePages,
8747
9614
  sections: currentSections,
8748
9615
  sectionsByPath,
8749
9616
  initialTarget: linkPopover.target,
9617
+ existingTargets: linkPopover.intent === "add-nav" ? getNavbarExistingTargets() : [],
8750
9618
  onClose: closeLinkPopover,
8751
9619
  onSubmit: handleLinkPopoverSubmit
8752
9620
  },
8753
- `${linkPopover.key}-${pathname}`
9621
+ linkPopover.key
8754
9622
  ) : null,
8755
- sectionGap && /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(
9623
+ sectionGap && /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(
8756
9624
  "div",
8757
9625
  {
8758
9626
  "data-ohw-section-insert-line": "",
8759
9627
  className: "fixed left-0 w-full z-2147483646 flex items-center pointer-events-none",
8760
9628
  style: { top: sectionGap.y, transform: "translateY(-50%)" },
8761
9629
  children: [
8762
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { className: "flex-1 bg-primary", style: { height: 3 } }),
8763
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
9630
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { className: "flex-1 bg-primary", style: { height: 3 } }),
9631
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
8764
9632
  Badge,
8765
9633
  {
8766
9634
  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 +9641,7 @@ function OhhwellsBridge() {
8773
9641
  children: "Add Section"
8774
9642
  }
8775
9643
  ),
8776
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { className: "flex-1 bg-primary", style: { height: 3 } })
9644
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { className: "flex-1 bg-primary", style: { height: 3 } })
8777
9645
  ]
8778
9646
  }
8779
9647
  )