@ohhwells/bridge 0.1.71-next.217 → 0.1.71

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
@@ -46,7 +46,6 @@ __export(index_exports, {
46
46
  DropdownMenuItem: () => DropdownMenuItem,
47
47
  DropdownMenuSeparator: () => DropdownMenuSeparator,
48
48
  DropdownMenuTrigger: () => DropdownMenuTrigger,
49
- EmptySection: () => EmptySection,
50
49
  ItemActionToolbar: () => ItemActionToolbar,
51
50
  ItemInteractionLayer: () => ItemInteractionLayer,
52
51
  LinkEditorPanel: () => LinkEditorPanel,
@@ -75,7 +74,7 @@ __export(index_exports, {
75
74
  module.exports = __toCommonJS(index_exports);
76
75
 
77
76
  // src/OhhwellsBridge.tsx
78
- var import_react17 = __toESM(require("react"), 1);
77
+ var import_react16 = __toESM(require("react"), 1);
79
78
  var import_client2 = require("react-dom/client");
80
79
  var import_react_dom3 = require("react-dom");
81
80
 
@@ -157,12 +156,7 @@ function parseAiSectionsState(raw) {
157
156
  media: entry.media && typeof entry.media === "object" ? entry.media : {}
158
157
  }));
159
158
  const removed = Array.isArray(parsed.removed) ? parsed.removed.filter((id) => typeof id === "string" && id.length > 0) : [];
160
- return {
161
- v: 1,
162
- sections,
163
- ...removed.length ? { removed } : {},
164
- ...parsed.hideTemplate === true ? { hideTemplate: true } : {}
165
- };
159
+ return { v: 1, sections, ...removed.length ? { removed } : {} };
166
160
  } catch {
167
161
  return EMPTY_AI_SECTIONS;
168
162
  }
@@ -175,7 +169,6 @@ function applyTreeToState(state, payload) {
175
169
  const entry = {
176
170
  id: payload.id,
177
171
  label: payload.label ?? "Generated section",
178
- ...typeof payload.path === "string" && payload.path ? { path: payload.path } : {},
179
172
  afterSection: payload.mode === "insert" && !insertBefore ? payload.targetSectionId ?? null : null,
180
173
  ...insertBefore && payload.targetSectionId ? { beforeSection: payload.targetSectionId } : {},
181
174
  ...payload.mode === "replace" && payload.targetSectionId ? { replaces: payload.targetSectionId } : {},
@@ -198,317 +191,6 @@ function deleteSectionFromState(state, sectionId) {
198
191
  return { ...state, v: 1, sections: state.sections, removed: [...removed, sectionId] };
199
192
  }
200
193
 
201
- // src/lib/brand-chrome.ts
202
- var BRAND_NAME_KEY = "__ohw_brand_name";
203
- var BRAND_TITLE_KEY = "__ohw_site_title";
204
- var BRAND_FAVICON_LETTER_KEY = "__ohw_favicon_letter";
205
- var BRAND_CHROME_KEYS = /* @__PURE__ */ new Set([
206
- BRAND_NAME_KEY,
207
- BRAND_TITLE_KEY,
208
- BRAND_FAVICON_LETTER_KEY
209
- ]);
210
- function upsertMeta(selector, attr, token, value) {
211
- let el = document.head.querySelector(selector);
212
- if (!el) {
213
- el = document.createElement("meta");
214
- el.setAttribute(attr, token);
215
- document.head.appendChild(el);
216
- }
217
- if (el.getAttribute("content") !== value) el.setAttribute("content", value);
218
- }
219
- function escapeXml(value) {
220
- return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
221
- }
222
- function applyLetterFavicon(letter) {
223
- const svg = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64"><rect width="64" height="64" rx="12" fill="#111827"/><text x="32" y="46" font-family="system-ui,-apple-system,Segoe UI,sans-serif" font-size="40" font-weight="700" text-anchor="middle" fill="#ffffff">${escapeXml(letter)}</text></svg>`;
224
- const href = `data:image/svg+xml,${encodeURIComponent(svg)}`;
225
- let link = document.head.querySelector('link[rel="icon"]');
226
- if (!link) {
227
- link = document.createElement("link");
228
- link.rel = "icon";
229
- document.head.appendChild(link);
230
- }
231
- link.type = "image/svg+xml";
232
- if (link.href !== href) link.href = href;
233
- }
234
- function applyBrandChrome(content) {
235
- const name = content[BRAND_NAME_KEY];
236
- if (typeof name === "string" && name.length > 0) {
237
- document.querySelectorAll("[data-ohw-wordmark]").forEach((el) => {
238
- if (el.textContent !== name) el.textContent = name;
239
- if (el.getAttribute("title") !== name) el.setAttribute("title", name);
240
- });
241
- }
242
- const title = content[BRAND_TITLE_KEY];
243
- if (typeof title === "string" && title.length > 0) {
244
- if (document.title !== title) document.title = title;
245
- upsertMeta('meta[property="og:title"]', "property", "og:title", title);
246
- upsertMeta('meta[name="twitter:title"]', "name", "twitter:title", title);
247
- }
248
- const letter = content[BRAND_FAVICON_LETTER_KEY];
249
- if (typeof letter === "string" && letter.length > 0) {
250
- applyLetterFavicon(letter);
251
- }
252
- }
253
-
254
- // src/lib/brand-kit.ts
255
- var BRAND_KIT_KEY = "__ohw_brand";
256
- var BRAND_VAR_PREFIX = "--ohw-brand-";
257
- var BRAND_VAR_NAMES = ["primary", "accent", "light", "dark", "surface", "border", "muted"].map(
258
- (role) => `${BRAND_VAR_PREFIX}${role}`
259
- );
260
- var FONT_VARS = {
261
- heading: ["--font-heading", "--font-display", "--brand-font-heading"],
262
- body: ["--font-body", "--brand-font-body"]
263
- };
264
- var BRAND_FONT_LINK_ID = "ohw-brand-fonts";
265
- function brandColorVars(kit) {
266
- const { dark, primary, accent, light } = kit.palette;
267
- const mix = (a, aPct, b) => `color-mix(in srgb, ${a} ${aPct}%, ${b})`;
268
- return {
269
- [`${BRAND_VAR_PREFIX}primary`]: primary,
270
- [`${BRAND_VAR_PREFIX}accent`]: accent,
271
- [`${BRAND_VAR_PREFIX}light`]: light,
272
- [`${BRAND_VAR_PREFIX}dark`]: dark,
273
- [`${BRAND_VAR_PREFIX}surface`]: mix(light, 95, dark),
274
- [`${BRAND_VAR_PREFIX}border`]: mix(light, 85, dark),
275
- [`${BRAND_VAR_PREFIX}muted`]: mix(dark, 62, light)
276
- };
277
- }
278
- function parseBrandKit(raw) {
279
- if (!raw) return null;
280
- try {
281
- const parsed = JSON.parse(raw);
282
- const p = parsed?.palette;
283
- const f = parsed?.fonts;
284
- if (!p || !f || typeof p.dark !== "string" || typeof p.primary !== "string" || typeof p.accent !== "string" || typeof p.light !== "string" || typeof f.heading !== "string" || typeof f.body !== "string") {
285
- return null;
286
- }
287
- return {
288
- palette: { dark: p.dark, primary: p.primary, accent: p.accent, light: p.light },
289
- fonts: { heading: f.heading, body: f.body }
290
- };
291
- } catch {
292
- return null;
293
- }
294
- }
295
- function familyOf(stack) {
296
- const first = stack.split(",")[0]?.trim() ?? "";
297
- return first.replace(/^['"]|['"]$/g, "");
298
- }
299
- function loadBrandFonts(families) {
300
- const unique = [...new Set(families.map((f) => f.trim()).filter(Boolean))];
301
- if (unique.length === 0) return;
302
- const spec = unique.map((f) => `${f.replace(/ /g, "+")}:400,500,600,700`).join("|");
303
- const href = `https://fonts.googleapis.com/css?family=${spec}&display=swap`;
304
- let link = document.getElementById(BRAND_FONT_LINK_ID);
305
- if (!link) {
306
- link = document.createElement("link");
307
- link.id = BRAND_FONT_LINK_ID;
308
- link.rel = "stylesheet";
309
- document.head.appendChild(link);
310
- }
311
- if (link.href !== href) link.href = href;
312
- }
313
- function applyBrandToDom(kit) {
314
- const root = document.documentElement;
315
- if (!kit) {
316
- for (const name of BRAND_VAR_NAMES) root.style.removeProperty(name);
317
- for (const name of [...FONT_VARS.heading, ...FONT_VARS.body]) root.style.removeProperty(name);
318
- document.getElementById(BRAND_FONT_LINK_ID)?.remove();
319
- return;
320
- }
321
- for (const [name, value] of Object.entries(brandColorVars(kit))) root.style.setProperty(name, value);
322
- for (const name of FONT_VARS.heading) root.style.setProperty(name, kit.fonts.heading);
323
- for (const name of FONT_VARS.body) root.style.setProperty(name, kit.fonts.body);
324
- loadBrandFonts([familyOf(kit.fonts.heading), familyOf(kit.fonts.body)]);
325
- }
326
-
327
- // src/lib/section-styles.ts
328
- var STYLE_STORE_KEY = "__ohw_styles";
329
- var STYLE_SHEET_ID = "ohw-section-styles";
330
- function parseStyleStore(raw) {
331
- if (!raw) return null;
332
- try {
333
- const parsed = JSON.parse(raw);
334
- if (parsed?.v !== 1) return null;
335
- return {
336
- v: 1,
337
- sections: typeof parsed.sections === "object" && parsed.sections ? parsed.sections : {},
338
- nodes: typeof parsed.nodes === "object" && parsed.nodes ? parsed.nodes : {}
339
- };
340
- } catch {
341
- return null;
342
- }
343
- }
344
- var BG_VALUES = {
345
- surface: "color-mix(in srgb, var(--ohw-brand-light, var(--color-light, #ECECEB)) 94%, var(--ohw-brand-dark, var(--color-dark, #0C0A09)))",
346
- accent: "var(--ohw-brand-primary, var(--color-primary, #0078E5))",
347
- "accent-soft": "color-mix(in srgb, var(--ohw-brand-primary, var(--color-primary, #0078E5)) 12%, var(--ohw-brand-light, var(--color-light, #FFFFFF)))"
348
- };
349
- var HEADLINE_SIZES = { md: 24, lg: 32, xl: 40, display: 56 };
350
- function styleSheetCss() {
351
- const rules = [];
352
- for (const [tone, value] of Object.entries(BG_VALUES)) {
353
- rules.push(`[data-ohw-style-bg="${tone}"] { background: ${value} !important; }`);
354
- }
355
- rules.push(
356
- `[data-ohw-style-bg="accent"] { color: var(--ohw-brand-light, var(--color-light, #FFFFFF)) !important; }`
357
- );
358
- rules.push(
359
- `[data-ohw-style-distribution="space-between"] { display: flex !important; flex-direction: column; justify-content: space-between; }`,
360
- `[data-ohw-style-distribution="center"] { display: flex !important; flex-direction: column; justify-content: center; }`
361
- );
362
- for (const [scale, size] of Object.entries(HEADLINE_SIZES)) {
363
- rules.push(
364
- `[data-ohw-style-headline="${scale}"] :is(h1, h2, h3) { font-size: ${size}px !important; line-height: 1.15 !important; }`
365
- );
366
- }
367
- rules.push(
368
- `[data-ohw-style-aspect="fill-height"] img { height: 100% !important; aspect-ratio: auto !important; object-fit: cover; }`
369
- );
370
- for (const aspect of ["1:1", "4:5", "3:4", "3:2", "16:9", "2:1", "3:1"]) {
371
- rules.push(
372
- `[data-ohw-style-aspect="${aspect.replace(":", "-")}"] img { aspect-ratio: ${aspect.replace(":", " / ")} !important; height: auto !important; object-fit: cover; }`
373
- );
374
- }
375
- const pad = { tight: 40, balanced: 64, airy: 96 };
376
- for (const [spacing, px] of Object.entries(pad)) {
377
- rules.push(
378
- `[data-ohw-style-spacing="${spacing}"] { padding-top: ${px}px !important; padding-bottom: ${px}px !important; }`
379
- );
380
- }
381
- return rules.join("\n");
382
- }
383
- var STYLE_FONT_LINK_ID = "ohw-style-fonts";
384
- function loadStyleFonts(families) {
385
- const unique = [...new Set(families.map((f) => f.trim()).filter(Boolean))];
386
- const existing = document.getElementById(STYLE_FONT_LINK_ID);
387
- if (unique.length === 0) {
388
- existing?.remove();
389
- return;
390
- }
391
- const spec = unique.map((f) => `${f.replace(/ /g, "+")}:400,500,600,700`).join("|");
392
- const href = `https://fonts.googleapis.com/css?family=${spec}&display=swap`;
393
- let link = existing;
394
- if (!link) {
395
- link = document.createElement("link");
396
- link.id = STYLE_FONT_LINK_ID;
397
- link.rel = "stylesheet";
398
- document.head.appendChild(link);
399
- }
400
- if (link.href !== href) link.href = href;
401
- }
402
- var SECTION_ATTRS = {
403
- sectionBackground: "data-ohw-style-bg",
404
- textDistribution: "data-ohw-style-distribution",
405
- headlineScale: "data-ohw-style-headline",
406
- imageAspect: "data-ohw-style-aspect",
407
- spacing: "data-ohw-style-spacing"
408
- };
409
- var NODE_WROTE_ATTR = "data-ohw-style-node";
410
- var NODE_PROPS = ["color", "font-family", "font-size", "background"];
411
- function saveInline(el, prop) {
412
- const attr = `data-ohw-style-prev-${prop}`;
413
- if (!el.hasAttribute(attr)) el.setAttribute(attr, el.style.getPropertyValue(prop));
414
- }
415
- function restoreInline(el, prop) {
416
- const attr = `data-ohw-style-prev-${prop}`;
417
- if (!el.hasAttribute(attr)) return;
418
- const prev = el.getAttribute(attr) ?? "";
419
- if (prev) el.style.setProperty(prop, prev);
420
- else el.style.removeProperty(prop);
421
- el.removeAttribute(attr);
422
- }
423
- function ensureStyleSheet() {
424
- let el = document.getElementById(STYLE_SHEET_ID);
425
- if (!el) {
426
- el = document.createElement("style");
427
- el.id = STYLE_SHEET_ID;
428
- document.head.appendChild(el);
429
- }
430
- const css = styleSheetCss();
431
- if (el.textContent !== css) el.textContent = css;
432
- }
433
- function clearSectionAttrs(root) {
434
- for (const attr of Object.values(SECTION_ATTRS)) {
435
- for (const el of Array.from(root.querySelectorAll(`[${attr}]`))) el.removeAttribute(attr);
436
- }
437
- for (const el of Array.from(root.querySelectorAll("[data-ohw-style-bgcolor]"))) {
438
- restoreInline(el, "background");
439
- el.removeAttribute("data-ohw-style-bgcolor");
440
- }
441
- }
442
- function clearNodeProps(root) {
443
- for (const el of Array.from(root.querySelectorAll(`[${NODE_WROTE_ATTR}]`))) {
444
- const h = el;
445
- for (const prop of NODE_PROPS) restoreInline(h, prop);
446
- h.removeAttribute(NODE_WROTE_ATTR);
447
- }
448
- }
449
- function buttonSurfaceOf(el) {
450
- return el.closest("a, button") ?? el;
451
- }
452
- function applyStylesToDom(store) {
453
- ensureStyleSheet();
454
- clearSectionAttrs(document);
455
- clearNodeProps(document);
456
- loadStyleFonts(
457
- store ? Object.values(store.nodes).flatMap((n) => n.fontFamily ? [n.fontFamily] : []) : []
458
- );
459
- if (!store) return;
460
- for (const [sectionId, override] of Object.entries(store.sections)) {
461
- const sections = document.querySelectorAll(
462
- `[data-ohw-section="${CSS.escape(sectionId)}"]`
463
- );
464
- for (const section of Array.from(sections)) {
465
- for (const [prop, attr] of Object.entries(SECTION_ATTRS)) {
466
- const value = override[prop];
467
- if (value === void 0) continue;
468
- if (prop === "sectionBackground" && override.sectionBackgroundColor !== void 0) continue;
469
- section.setAttribute(attr, String(value).replace(":", "-"));
470
- }
471
- if (override.sectionBackgroundColor !== void 0) {
472
- saveInline(section, "background");
473
- section.style.setProperty("background", override.sectionBackgroundColor, "important");
474
- section.setAttribute("data-ohw-style-bgcolor", "");
475
- }
476
- }
477
- }
478
- for (const [key, override] of Object.entries(store.nodes)) {
479
- const nodes = document.querySelectorAll(`[data-ohw-key="${CSS.escape(key)}"]`);
480
- for (const el of Array.from(nodes)) {
481
- if (override.color !== void 0) {
482
- saveInline(el, "color");
483
- el.style.setProperty("color", override.color, "important");
484
- el.setAttribute(NODE_WROTE_ATTR, "");
485
- }
486
- if (override.fontFamily !== void 0) {
487
- saveInline(el, "font-family");
488
- el.style.setProperty("font-family", `'${override.fontFamily}'`, "important");
489
- el.setAttribute(NODE_WROTE_ATTR, "");
490
- }
491
- if (override.fontSize !== void 0) {
492
- saveInline(el, "font-size");
493
- el.style.setProperty("font-size", `${override.fontSize}px`, "important");
494
- el.setAttribute(NODE_WROTE_ATTR, "");
495
- }
496
- if (override.buttonBackground !== void 0 || override.buttonText !== void 0) {
497
- const surface = buttonSurfaceOf(el);
498
- if (override.buttonBackground !== void 0) {
499
- saveInline(surface, "background");
500
- surface.style.setProperty("background", override.buttonBackground, "important");
501
- }
502
- if (override.buttonText !== void 0) {
503
- saveInline(surface, "color");
504
- surface.style.setProperty("color", override.buttonText, "important");
505
- }
506
- surface.setAttribute(NODE_WROTE_ATTR, "");
507
- }
508
- }
509
- }
510
- }
511
-
512
194
  // src/ui/ai-tree/aiSectionsManager.tsx
513
195
  var import_react_dom = require("react-dom");
514
196
  var import_client = require("react-dom/client");
@@ -523,8 +205,7 @@ function lucideByName(name) {
523
205
  }
524
206
  var typeStyle = (spec, font) => ({
525
207
  fontFamily: font,
526
- // Headings shrink with the viewport (reaching full size around ~900px wide); body copy stays put.
527
- fontSize: spec.size >= 24 ? `clamp(${Math.max(18, Math.round(spec.size * 0.6))}px, ${(spec.size / 9).toFixed(2)}vw, ${spec.size}px)` : spec.size,
208
+ fontSize: spec.size,
528
209
  lineHeight: spec.line,
529
210
  fontWeight: spec.weight
530
211
  });
@@ -539,36 +220,6 @@ var FEATURE_LINE_CSS = [
539
220
  `background-color:currentColor;-webkit-mask:url("${CHECK_MASK}") no-repeat 0 0/24px 24px;`,
540
221
  `mask:url("${CHECK_MASK}") no-repeat 0 0/24px 24px}`
541
222
  ].join("");
542
- function hexLuminance(color) {
543
- const m = /^#([0-9a-f]{6})$/i.exec(color.trim());
544
- if (!m) return null;
545
- const [r2, g, b] = [0, 2, 4].map((i) => {
546
- const c = parseInt(m[1].slice(i, i + 2), 16) / 255;
547
- return c <= 0.03928 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4);
548
- });
549
- return 0.2126 * r2 + 0.7152 * g + 0.0722 * b;
550
- }
551
- function hexContrast(a, b) {
552
- const la = hexLuminance(a);
553
- const lb = hexLuminance(b);
554
- if (la === null || lb === null) return null;
555
- const [hi, lo] = la > lb ? [la, lb] : [lb, la];
556
- return (hi + 0.05) / (lo + 0.05);
557
- }
558
- function accentBandContext(brand) {
559
- const p = brand.palette;
560
- const lightWins = (hexContrast(p.light, p.primary) ?? 99) >= (hexContrast(p.dark, p.primary) ?? 0);
561
- if (lightWins) {
562
- return {
563
- brand: { ...brand, palette: { dark: p.light, primary: p.light, accent: p.light, light: p.primary } },
564
- buttonLabel: p.primary
565
- };
566
- }
567
- return {
568
- brand: { ...brand, palette: { dark: p.dark, primary: p.dark, accent: p.dark, light: p.light } },
569
- buttonLabel: p.light
570
- };
571
- }
572
223
  function textAttrs(ctx, path) {
573
224
  return ctx.keyFor ? { "data-ohw-key": ctx.keyFor(path), "data-ohw-editable": "text" } : {};
574
225
  }
@@ -580,12 +231,7 @@ var AI_RESPONSIVE_CSS = [
580
231
  "@media (max-width: 640px) {",
581
232
  " [data-ai-responsive] [data-ai-grid] { grid-template-columns: 1fr !important; }",
582
233
  " [data-ai-responsive] [data-ai-columns] > * { grid-column: 1 / -1 !important; }",
583
- // Group containers flatten to a column on phones; span placements come along for free.
584
- " [data-ai-responsive] [data-ai-group] { display: flex !important; flex-direction: column !important; }",
585
- " [data-ai-responsive] [data-ai-group] > * { grid-column: auto !important; }",
586
234
  " [data-ai-responsive] [data-ai-section-inner] { padding-left: 20px !important; padding-right: 20px !important; }",
587
- " [data-ai-responsive] { overflow-x: hidden; }",
588
- " [data-ai-responsive] img { max-width: 100%; }",
589
235
  "}"
590
236
  ].join("\n");
591
237
  var TRANSPARENT_PX = "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7";
@@ -663,7 +309,7 @@ function ButtonEl({
663
309
  }) {
664
310
  const secondary = slots.variant === "secondary";
665
311
  const href = str(slots.href);
666
- const linkAttrs = ctx.keyFor && path ? { "data-ohw-href-key": ctx.keyFor(`${path}.href`), "data-ohw-role": "button" } : {};
312
+ const linkAttrs = ctx.keyFor && path ? { "data-ohw-href-key": ctx.keyFor(`${path}.href`) } : {};
667
313
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
668
314
  "a",
669
315
  {
@@ -679,7 +325,7 @@ function ButtonEl({
679
325
  textDecoration: "none",
680
326
  cursor: "pointer",
681
327
  ...typeStyle(AI_TREE_TOKENS.type.button, ctx.brand.fonts.body),
682
- ...secondary ? { border: `1px solid ${ctx.brand.palette.dark}`, color: ctx.brand.palette.dark } : { background: ctx.brand.palette.primary, color: ctx.buttonLabel ?? AI_TREE_TOKENS.textPrimaryForeground }
328
+ ...secondary ? { border: `1px solid ${ctx.brand.palette.dark}`, color: ctx.brand.palette.dark } : { background: ctx.brand.palette.primary, color: AI_TREE_TOKENS.textPrimaryForeground }
683
329
  },
684
330
  children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { ...path ? textAttrs(ctx, `${path}.label`) : {}, children: str(slots.label) })
685
331
  }
@@ -1185,24 +831,7 @@ function CardBlock({ node, ctx, path }) {
1185
831
  minWidth: 0
1186
832
  },
1187
833
  children: [
1188
- media && (horizontal ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1189
- "div",
1190
- {
1191
- style: (
1192
- // An icon hugs its glyph — flex:1 gave a 48px icon half the card and pushed the
1193
- // text to the far side. Photos keep the half-and-half split. The inset has no
1194
- // inner padding (the photo split absorbed that), so the icon carries its own gap.
1195
- /^(lucide|simple):/.test(mediaRef) ? {
1196
- flexShrink: 0,
1197
- display: "flex",
1198
- alignItems: "center",
1199
- padding: mediaInset,
1200
- [mediaPosition === "right" ? "marginLeft" : "marginRight"]: 20
1201
- } : { flex: 1, minWidth: 0, display: "flex", alignItems: "center", padding: mediaInset }
1202
- ),
1203
- children: media
1204
- }
1205
- ) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
834
+ media && (horizontal ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { flex: 1, minWidth: 0, display: "flex", alignItems: "center", padding: mediaInset }, children: media }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1206
835
  "div",
1207
836
  {
1208
837
  style: /^(lucide|simple):/.test(mediaRef) ? { padding: `${AI_TREE_TOKENS.paddingBlock}px ${AI_TREE_TOKENS.paddingBlock}px 0` } : hasBg ? void 0 : { borderRadius: AI_TREE_TOKENS.radiusCard, overflow: "hidden" },
@@ -1293,44 +922,13 @@ function AccordionBlock({ node, ctx, path }) {
1293
922
  ) })
1294
923
  ] }, i)) });
1295
924
  }
1296
- function useIsMobile() {
1297
- const [mobile, setMobile] = import_react.default.useState(
1298
- () => typeof window !== "undefined" && window.matchMedia("(max-width: 768px)").matches
1299
- );
1300
- import_react.default.useEffect(() => {
1301
- const mq = window.matchMedia("(max-width: 768px)");
1302
- const update = () => setMobile(mq.matches);
1303
- update();
1304
- mq.addEventListener("change", update);
1305
- return () => mq.removeEventListener("change", update);
1306
- }, []);
1307
- return mobile;
1308
- }
1309
925
  function Carousel({ items, itemsPerRow, ctx }) {
1310
- const isMobile = useIsMobile();
1311
- const perPage = isMobile ? 1 : itemsPerRow;
1312
- const pages = Math.max(1, Math.ceil(items.length / perPage));
1313
926
  const [page, setPage] = import_react.default.useState(0);
927
+ const pages = Math.max(1, Math.ceil(items.length / itemsPerRow));
1314
928
  const current = Math.min(page, pages - 1);
1315
- if (pages <= 1) {
1316
- const cols = Math.max(1, Math.min(items.length, itemsPerRow));
1317
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1318
- "div",
1319
- {
1320
- "data-ai-grid": String(cols),
1321
- style: {
1322
- display: "grid",
1323
- gridTemplateColumns: `repeat(${cols}, 1fr)`,
1324
- gap: AI_TREE_TOKENS.spacing8,
1325
- alignItems: "start"
1326
- },
1327
- children: items
1328
- }
1329
- );
1330
- }
1331
929
  const pageGroups = Array.from(
1332
930
  { length: pages },
1333
- (_, p) => items.slice(p * perPage, (p + 1) * perPage)
931
+ (_, p) => items.slice(p * itemsPerRow, (p + 1) * itemsPerRow)
1334
932
  );
1335
933
  const chrome = (enabled) => ({
1336
934
  border: `1px solid ${ctx.brand.palette.dark}`,
@@ -1355,69 +953,55 @@ function Carousel({ items, itemsPerRow, ctx }) {
1355
953
  cursor: "pointer",
1356
954
  padding: 0
1357
955
  });
1358
- const viewport = /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { flex: isMobile ? "0 0 auto" : 1, minWidth: 0, width: "100%", overflow: "hidden" }, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1359
- "div",
1360
- {
1361
- style: {
1362
- display: "flex",
1363
- transform: `translateX(-${current * 100}%)`,
1364
- transition: "transform 0.4s ease"
1365
- },
1366
- children: pageGroups.map((group, p) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
956
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { "data-ai-carousel": "", style: { display: "flex", flexDirection: "column", gap: AI_TREE_TOKENS.spacing8, alignItems: "center" }, children: [
957
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: AI_TREE_TOKENS.spacing6, width: "100%" }, children: [
958
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
959
+ "button",
960
+ {
961
+ type: "button",
962
+ "aria-label": "Previous",
963
+ onClick: () => setPage((p) => Math.max(0, p - 1)),
964
+ style: chrome(current > 0),
965
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.ArrowLeft, { size: 24, strokeWidth: 1.5, "aria-hidden": true })
966
+ }
967
+ ),
968
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { flex: 1, minWidth: 0, overflow: "hidden" }, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1367
969
  "div",
1368
970
  {
1369
- "data-ai-grid": String(perPage),
1370
971
  style: {
1371
- flex: "0 0 100%",
1372
- display: "grid",
1373
- gridTemplateColumns: `repeat(${perPage}, 1fr)`,
1374
- gap: AI_TREE_TOKENS.spacing8,
1375
- alignItems: "start"
972
+ display: "flex",
973
+ transform: `translateX(-${current * 100}%)`,
974
+ transition: "transform 0.4s ease"
1376
975
  },
1377
- children: group
1378
- },
1379
- p
1380
- ))
1381
- }
1382
- ) });
1383
- const prevBtn = /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1384
- "button",
1385
- {
1386
- type: "button",
1387
- "aria-label": "Previous",
1388
- onClick: () => setPage((p) => Math.max(0, p - 1)),
1389
- style: chrome(current > 0),
1390
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.ArrowLeft, { size: 24, strokeWidth: 1.5, "aria-hidden": true })
1391
- }
1392
- );
1393
- const nextBtn = /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1394
- "button",
1395
- {
1396
- type: "button",
1397
- "aria-label": "Next",
1398
- onClick: () => setPage((p) => Math.min(pages - 1, p + 1)),
1399
- style: chrome(current < pages - 1),
1400
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.ArrowRight, { size: 24, strokeWidth: 1.5, "aria-hidden": true })
1401
- }
1402
- );
1403
- const dots = /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { display: "flex", gap: 9, justifyContent: "center" }, children: pageGroups.map((_, p) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", "aria-label": `Page ${p + 1}`, onClick: () => setPage(p), style: dot(p === current) }, p)) });
1404
- if (isMobile) {
1405
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { "data-ai-carousel": "", style: { display: "flex", flexDirection: "column", gap: AI_TREE_TOKENS.spacing6, alignItems: "center" }, children: [
1406
- viewport,
1407
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { display: "flex", gap: AI_TREE_TOKENS.spacing6, justifyContent: "center" }, children: [
1408
- prevBtn,
1409
- nextBtn
1410
- ] }),
1411
- dots
1412
- ] });
1413
- }
1414
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { "data-ai-carousel": "", style: { display: "flex", flexDirection: "column", gap: AI_TREE_TOKENS.spacing8, alignItems: "center" }, children: [
1415
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: AI_TREE_TOKENS.spacing6, width: "100%" }, children: [
1416
- prevBtn,
1417
- viewport,
1418
- nextBtn
976
+ children: pageGroups.map((group, p) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
977
+ "div",
978
+ {
979
+ "data-ai-grid": String(itemsPerRow),
980
+ style: {
981
+ flex: "0 0 100%",
982
+ display: "grid",
983
+ gridTemplateColumns: `repeat(${itemsPerRow}, 1fr)`,
984
+ gap: AI_TREE_TOKENS.spacing8,
985
+ alignItems: "start"
986
+ },
987
+ children: group
988
+ },
989
+ p
990
+ ))
991
+ }
992
+ ) }),
993
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
994
+ "button",
995
+ {
996
+ type: "button",
997
+ "aria-label": "Next",
998
+ onClick: () => setPage((p) => Math.min(pages - 1, p + 1)),
999
+ style: chrome(current < pages - 1),
1000
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.ArrowRight, { size: 24, strokeWidth: 1.5, "aria-hidden": true })
1001
+ }
1002
+ )
1419
1003
  ] }),
1420
- dots
1004
+ pages > 1 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { display: "flex", gap: 9, justifyContent: "center" }, children: pageGroups.map((_, p) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", "aria-label": `Page ${p + 1}`, onClick: () => setPage(p), style: dot(p === current) }, p)) })
1421
1005
  ] });
1422
1006
  }
1423
1007
  function CollectionBlock({ node, ctx, path }) {
@@ -1511,49 +1095,6 @@ function renderNode(node, ctx, path) {
1511
1095
  switch (node.type) {
1512
1096
  case "text":
1513
1097
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TextBlock, { slots, ctx, path });
1514
- // Layout container: arranges child blocks, contributes no content of its own. `grid` is a
1515
- // nested 12-column grid the children span (a collage is 3–5 media on it, bottom-aligned so
1516
- // mixed aspects read as a composition); `split` is exactly two children at a ratio; `stack`
1517
- // is a column. Children render through this same dispatcher, so edit markers, media
1518
- // resolution, and copy paths all work unchanged inside a group.
1519
- case "group": {
1520
- const layout = str(slots.layout);
1521
- const gap = slots.spacing === "tight" ? AI_TREE_TOKENS.spacing3 : slots.spacing === "airy" ? AI_TREE_TOKENS.spacing8 : AI_TREE_TOKENS.spacing6;
1522
- const kids = (node.children ?? []).map((child, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1523
- "div",
1524
- {
1525
- style: layout === "grid" ? {
1526
- gridColumn: `span ${typeof child.span === "number" ? Math.min(12, Math.max(1, child.span)) : 12}`,
1527
- minWidth: 0
1528
- } : { minWidth: 0 },
1529
- children: renderNode(child, ctx, `${path}.c${i}`)
1530
- },
1531
- i
1532
- ));
1533
- if (layout === "grid") {
1534
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1535
- "div",
1536
- {
1537
- "data-ai-group": "grid",
1538
- style: { display: "grid", gridTemplateColumns: "repeat(12, 1fr)", gap, alignItems: "end" },
1539
- children: kids
1540
- }
1541
- );
1542
- }
1543
- if (layout === "split") {
1544
- const ratio = str(slots.ratio);
1545
- const cols = ratio === "3:5" ? "3fr 5fr" : ratio === "5:3" ? "5fr 3fr" : "1fr 1fr";
1546
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1547
- "div",
1548
- {
1549
- "data-ai-group": "split",
1550
- style: { display: "grid", gridTemplateColumns: cols, gap, alignItems: "center" },
1551
- children: kids
1552
- }
1553
- );
1554
- }
1555
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { "data-ai-group": "stack", style: { display: "flex", flexDirection: "column", gap }, children: kids });
1556
- }
1557
1098
  case "button":
1558
1099
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ButtonEl, { slots, ctx, path });
1559
1100
  case "button-row":
@@ -1634,81 +1175,33 @@ function renderNode(node, ctx, path) {
1634
1175
  }
1635
1176
  );
1636
1177
  }
1637
- case "form": {
1638
- const formAttrs = ctx.keyFor ? {
1639
- "data-ohw-editable": "form",
1640
- "data-ohw-key": ctx.keyFor(`${path}.form`),
1641
- "data-ohw-success-text": "Thanks \u2014 we'll be in touch shortly."
1642
- } : {};
1643
- const fieldStyle = {
1644
- width: "100%",
1645
- boxSizing: "border-box",
1646
- border: `1px solid ${ctx.brand.palette.accent}`,
1647
- borderRadius: AI_TREE_TOKENS.radiusButton,
1648
- padding: "12px 14px",
1649
- background: "#fff",
1650
- color: ctx.brand.palette.dark,
1651
- outline: "none",
1652
- ...typeStyle(AI_TREE_TOKENS.type.bodyM, ctx.brand.fonts.body)
1653
- };
1654
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("form", { ...formAttrs, style: { display: "flex", flexDirection: "column", gap: AI_TREE_TOKENS.spacing4 }, children: (node.children ?? []).map((child, i) => {
1178
+ case "form":
1179
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { display: "flex", flexDirection: "column", gap: AI_TREE_TOKENS.spacing4 }, children: (node.children ?? []).map((child, i) => {
1655
1180
  if (child.type === "input") {
1656
- const cs2 = child.slots ?? {};
1657
- const kind = str(cs2.kind);
1658
- const label = str(cs2.label);
1659
- const placeholder = str(cs2.placeholder);
1660
- const name = label.toLowerCase().replace(/[^a-z0-9]+/gu, "-").replace(/^-+|-+$/gu, "") || `field-${i}`;
1661
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { display: "flex", flexDirection: "column", gap: 8 }, children: [
1181
+ const cs = child.slots ?? {};
1182
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
1662
1183
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1663
- "label",
1184
+ "div",
1664
1185
  {
1665
1186
  ...textAttrs(ctx, `${path}.c${i}.label`),
1666
- style: {
1667
- ...typeStyle(AI_TREE_TOKENS.type.bodyMBold, ctx.brand.fonts.body),
1668
- color: ctx.brand.palette.dark
1669
- },
1670
- children: label
1187
+ style: { ...typeStyle(AI_TREE_TOKENS.type.bodyMBold, ctx.brand.fonts.body), color: ctx.brand.palette.dark, marginBottom: 6 },
1188
+ children: str(cs.label)
1671
1189
  }
1672
1190
  ),
1673
- kind === "textarea" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1674
- "textarea",
1675
- {
1676
- name,
1677
- placeholder,
1678
- style: { ...fieldStyle, height: 140, resize: "vertical" }
1679
- }
1680
- ) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1681
- "input",
1191
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1192
+ "div",
1682
1193
  {
1683
- name,
1684
- type: kind === "email" ? "email" : "text",
1685
- placeholder,
1686
- style: { ...fieldStyle, height: 48 }
1194
+ style: {
1195
+ border: `1px solid ${ctx.brand.palette.accent}`,
1196
+ borderRadius: AI_TREE_TOKENS.radiusButton,
1197
+ height: cs.kind === "textarea" ? 96 : 42
1198
+ }
1687
1199
  }
1688
1200
  )
1689
1201
  ] }, i);
1690
1202
  }
1691
- const cs = child.slots ?? {};
1692
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1693
- "button",
1694
- {
1695
- type: "submit",
1696
- style: {
1697
- alignSelf: "flex-start",
1698
- border: "none",
1699
- cursor: "pointer",
1700
- padding: `${AI_TREE_TOKENS.spacing3}px ${AI_TREE_TOKENS.spacing6}px`,
1701
- borderRadius: AI_TREE_TOKENS.radiusButton,
1702
- background: ctx.brand.palette.primary,
1703
- color: AI_TREE_TOKENS.textPrimaryForeground,
1704
- ...typeStyle(AI_TREE_TOKENS.type.button, ctx.brand.fonts.body)
1705
- },
1706
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { ...textAttrs(ctx, `${path}.c${i}.label`), children: str(cs.label) })
1707
- },
1708
- i
1709
- );
1203
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ButtonEl, { slots: child.slots ?? {}, ctx, path: `${path}.c${i}` }) }, i);
1710
1204
  }) });
1711
- }
1712
1205
  case "schedule-widget":
1713
1206
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1714
1207
  "div",
@@ -1734,14 +1227,11 @@ function AiTreeRenderer({ tree, brand, resolveMedia, editKeyPrefix }) {
1734
1227
  return null;
1735
1228
  }
1736
1229
  const resolvedBrand = brand ?? AI_DEFAULT_BRAND;
1737
- const band = (tree.settings ?? {}).sectionBackground === "accent" ? accentBandContext(resolvedBrand) : null;
1738
- const blockBrand = band?.brand ?? resolvedBrand;
1739
1230
  const ctx = {
1740
- brand: blockBrand,
1231
+ brand: resolvedBrand,
1741
1232
  resolveMedia: resolveMedia ?? (() => null),
1742
- cardSurface: blockBrand.palette.light.toUpperCase() === AI_DEFAULT_BRAND.palette.light.toUpperCase() ? "#ECECEB" : `color-mix(in srgb, ${blockBrand.palette.light} 90%, ${blockBrand.palette.dark})`,
1743
- keyFor: editKeyPrefix ? (path) => `${editKeyPrefix}.${path}` : null,
1744
- ...band ? { buttonLabel: band.buttonLabel } : {}
1233
+ cardSurface: resolvedBrand.palette.light.toUpperCase() === AI_DEFAULT_BRAND.palette.light.toUpperCase() ? "#ECECEB" : `color-mix(in srgb, ${resolvedBrand.palette.light} 90%, ${resolvedBrand.palette.dark})`,
1234
+ keyFor: editKeyPrefix ? (path) => `${editKeyPrefix}.${path}` : null
1745
1235
  };
1746
1236
  const settings = tree.settings ?? {};
1747
1237
  const pad = AI_TREE_TOKENS.sectionPadding[settings.spacing ?? "balanced"];
@@ -1749,20 +1239,6 @@ function AiTreeRenderer({ tree, brand, resolveMedia, editKeyPrefix }) {
1749
1239
  const overlayAlpha = { low: 0.25, medium: 0.45, high: 0.65 }[settings.overlayIntensity ?? "medium"];
1750
1240
  const backgroundUrl = isOverlay && settings.backgroundMedia ? ctx.resolveMedia(settings.backgroundMedia) : null;
1751
1241
  const bgAttrs = ctx.keyFor && isOverlay && settings.backgroundMedia ? { "data-ohw-key": ctx.keyFor("settings.backgroundMedia"), "data-ohw-editable": "bg-image" } : {};
1752
- const toneBackground = (() => {
1753
- const { dark, primary, light } = resolvedBrand.palette;
1754
- switch (settings.sectionBackground) {
1755
- case "surface":
1756
- return `color-mix(in srgb, ${light} 94%, ${dark})`;
1757
- case "accent":
1758
- return primary;
1759
- case "accent-soft":
1760
- return `color-mix(in srgb, ${primary} 12%, ${light})`;
1761
- default:
1762
- return void 0;
1763
- }
1764
- })();
1765
- const distributed = !isOverlay && settings.textDistribution;
1766
1242
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
1767
1243
  "section",
1768
1244
  {
@@ -1772,11 +1248,10 @@ function AiTreeRenderer({ tree, brand, resolveMedia, editKeyPrefix }) {
1772
1248
  style: {
1773
1249
  position: "relative",
1774
1250
  padding: `${pad}px 0`,
1775
- background: toneBackground ?? (isOverlay && !backgroundUrl ? "#DBEAFE" : void 0),
1251
+ background: isOverlay && !backgroundUrl ? "#DBEAFE" : void 0,
1776
1252
  backgroundImage: backgroundUrl ? `url(${backgroundUrl})` : void 0,
1777
1253
  backgroundSize: "cover",
1778
- backgroundPosition: "center",
1779
- color: settings.sectionBackground === "accent" ? blockBrand.palette.dark : void 0
1254
+ backgroundPosition: "center"
1780
1255
  },
1781
1256
  children: [
1782
1257
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("style", { children: AI_RESPONSIVE_CSS }),
@@ -1800,24 +1275,10 @@ function AiTreeRenderer({ tree, brand, resolveMedia, editKeyPrefix }) {
1800
1275
  display: "grid",
1801
1276
  gridTemplateColumns: "repeat(12, 1fr)",
1802
1277
  gap: AI_TREE_TOKENS.spacing6,
1803
- alignItems: distributed === "space-between" ? "stretch" : (distributed ?? settings.verticalPosition) === "top" ? "start" : "center",
1278
+ alignItems: settings.verticalPosition === "top" ? "start" : "center",
1804
1279
  marginTop: r2 > 0 ? AI_TREE_TOKENS.spacing8 : 0
1805
1280
  },
1806
- children: (row.blocks ?? []).map((block, b) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1807
- "div",
1808
- {
1809
- "data-ai-cell": "",
1810
- style: {
1811
- gridColumn: `span ${Math.min(12, Math.max(1, block.span))}`,
1812
- minWidth: 0,
1813
- // space-between: each column becomes a flex column whose content spreads over
1814
- // the full row height instead of clumping at the top.
1815
- ...distributed === "space-between" ? { display: "flex", flexDirection: "column", justifyContent: "space-between" } : {}
1816
- },
1817
- children: renderNode(block, ctx, `r${r2}.b${b}`)
1818
- },
1819
- b
1820
- ))
1281
+ children: (row.blocks ?? []).map((block, b) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { gridColumn: `span ${Math.min(12, Math.max(1, block.span))}`, minWidth: 0 }, children: renderNode(block, ctx, `r${r2}.b${b}`) }, b))
1821
1282
  },
1822
1283
  r2
1823
1284
  ))
@@ -1833,36 +1294,17 @@ var import_jsx_runtime2 = require("react/jsx-runtime");
1833
1294
  var CONTAINER_ATTR = "data-ohw-ai-generated";
1834
1295
  var REPLACED_ATTR = "data-ohw-ai-replaced-by";
1835
1296
  var REMOVED_ATTR = "data-ohw-ai-removed";
1836
- var TEMPLATE_HIDDEN_ATTR = "data-ohw-ai-template-hidden";
1837
- var CHROME_SECTION_IDS = /* @__PURE__ */ new Set(["navbar", "footer"]);
1838
- function readRootVar(name) {
1839
- if (typeof document === "undefined") return "";
1840
- return getComputedStyle(document.documentElement).getPropertyValue(name).trim();
1841
- }
1842
- function deriveBrandOverride() {
1843
- const dark = readRootVar("--ohw-brand-dark");
1844
- const primary = readRootVar("--ohw-brand-primary");
1845
- const light = readRootVar("--ohw-brand-light");
1846
- if (!dark || !primary || !light) return null;
1847
- const accent = readRootVar("--ohw-brand-accent");
1848
- const heading = readRootVar("--font-heading") || readRootVar("--font-display");
1849
- const body = readRootVar("--font-body");
1850
- return {
1851
- palette: { dark, primary, accent: accent || dark, light },
1852
- fonts: {
1853
- heading: heading || AI_DEFAULT_BRAND.fonts.heading,
1854
- body: body || AI_DEFAULT_BRAND.fonts.body
1855
- }
1856
- };
1857
- }
1858
1297
  function deriveTemplateBrand() {
1859
- const dark = readRootVar("--color-dark");
1860
- const primary = readRootVar("--color-primary");
1861
- const light = readRootVar("--color-light");
1298
+ if (typeof document === "undefined") return null;
1299
+ const cs = getComputedStyle(document.documentElement);
1300
+ const read = (name) => cs.getPropertyValue(name).trim();
1301
+ const dark = read("--color-dark");
1302
+ const primary = read("--color-primary");
1303
+ const light = read("--color-light");
1862
1304
  if (!dark || !primary || !light) return null;
1863
- const accent = readRootVar("--color-accent");
1864
- const heading = readRootVar("--font-heading") || readRootVar("--font-display");
1865
- const body = readRootVar("--font-body");
1305
+ const accent = read("--color-accent");
1306
+ const heading = read("--font-heading") || read("--font-display");
1307
+ const body = read("--font-body");
1866
1308
  return {
1867
1309
  palette: { dark, primary, accent: accent || dark, light },
1868
1310
  fonts: {
@@ -1934,24 +1376,6 @@ function syncRemovedSections(state) {
1934
1376
  }
1935
1377
  }
1936
1378
  }
1937
- function syncTemplateHidden(state, pageHasSections) {
1938
- const hide = state.hideTemplate === true && pageHasSections;
1939
- for (const el of Array.from(document.querySelectorAll(`[${TEMPLATE_HIDDEN_ATTR}]`))) {
1940
- if (!hide) {
1941
- el.style.removeProperty("display");
1942
- el.removeAttribute(TEMPLATE_HIDDEN_ATTR);
1943
- }
1944
- }
1945
- if (!hide) return;
1946
- for (const el of Array.from(document.querySelectorAll("[data-ohw-section]"))) {
1947
- if (el.hasAttribute(CONTAINER_ATTR)) continue;
1948
- if (CHROME_SECTION_IDS.has(el.getAttribute("data-ohw-section") ?? "")) continue;
1949
- if (el.parentElement?.closest("[data-ohw-section]")) continue;
1950
- if (el.hasAttribute(REPLACED_ATTR) || el.hasAttribute(REMOVED_ATTR)) continue;
1951
- el.style.display = "none";
1952
- el.setAttribute(TEMPLATE_HIDDEN_ATTR, "");
1953
- }
1954
- }
1955
1379
  function syncReplacedOriginals(state) {
1956
1380
  for (const el of document.querySelectorAll(`[${REPLACED_ATTR}]`)) {
1957
1381
  const byId = el.getAttribute(REPLACED_ATTR) ?? "";
@@ -1970,40 +1394,10 @@ function syncReplacedOriginals(state) {
1970
1394
  }
1971
1395
  }
1972
1396
  }
1973
- function orderByChain(sections) {
1974
- const ids = new Set(sections.map((entry) => entry.id));
1975
- const after = /* @__PURE__ */ new Map();
1976
- const roots = [];
1977
- for (const entry of sections) {
1978
- const anchor = entry.replaces ?? entry.beforeSection ?? entry.afterSection ?? null;
1979
- if (anchor && ids.has(anchor)) {
1980
- const bucket = after.get(anchor);
1981
- if (bucket) bucket.push(entry);
1982
- else after.set(anchor, [entry]);
1983
- } else {
1984
- roots.push(entry);
1985
- }
1986
- }
1987
- const out = [];
1988
- const seen = /* @__PURE__ */ new Set();
1989
- const visit = (entry) => {
1990
- if (seen.has(entry.id)) return;
1991
- seen.add(entry.id);
1992
- out.push(entry);
1993
- for (const child of after.get(entry.id) ?? []) visit(child);
1994
- };
1995
- for (const root of roots) visit(root);
1996
- return out.length === sections.length ? out : sections;
1997
- }
1998
1397
  function applyAiSectionsToDom(state, options) {
1999
1398
  if (typeof document === "undefined") return;
2000
- const brandOverride = deriveBrandOverride();
2001
1399
  const templateBrand = deriveTemplateBrand();
2002
- const brandKey = brandOverride ? JSON.stringify(brandOverride) : "";
2003
- const pagePath = window.location.pathname;
2004
- const pageSections = state.sections.filter((entry) => (entry.path ?? "/") === pagePath);
2005
- const activeIds = new Set(pageSections.map((entry) => entry.id));
2006
- const ordered = state.hideTemplate === true ? orderByChain(pageSections) : pageSections;
1400
+ const activeIds = new Set(state.sections.map((entry) => entry.id));
2007
1401
  for (const [id, section] of mounted) {
2008
1402
  if (!activeIds.has(id)) {
2009
1403
  section.root.unmount();
@@ -2011,8 +1405,8 @@ function applyAiSectionsToDom(state, options) {
2011
1405
  mounted.delete(id);
2012
1406
  }
2013
1407
  }
2014
- for (const entry of ordered) {
2015
- const serialized = JSON.stringify(entry) + brandKey;
1408
+ for (const entry of state.sections) {
1409
+ const serialized = JSON.stringify(entry);
2016
1410
  const existing = mounted.get(entry.id);
2017
1411
  if (existing && existing.serialized === serialized && existing.container.isConnected) {
2018
1412
  continue;
@@ -2026,7 +1420,6 @@ function applyAiSectionsToDom(state, options) {
2026
1420
  mounted.delete(entry.id);
2027
1421
  }
2028
1422
  container.setAttribute("data-ohw-section", entry.id);
2029
- container.setAttribute("data-ohw-instance", entry.id);
2030
1423
  container.setAttribute("data-ohw-section-label", entry.label);
2031
1424
  placeContainer(container, entry);
2032
1425
  const root = mounted.get(entry.id)?.root ?? (0, import_client.createRoot)(container);
@@ -2037,7 +1430,7 @@ function applyAiSectionsToDom(state, options) {
2037
1430
  AiTreeRenderer,
2038
1431
  {
2039
1432
  tree: entry.tree,
2040
- brand: brandOverride ?? entry.brand ?? options?.brand ?? templateBrand ?? void 0,
1433
+ brand: entry.brand ?? options?.brand ?? templateBrand ?? void 0,
2041
1434
  resolveMedia,
2042
1435
  editKeyPrefix: `ai.${entry.id}`
2043
1436
  }
@@ -2046,20 +1439,8 @@ function applyAiSectionsToDom(state, options) {
2046
1439
  });
2047
1440
  mounted.set(entry.id, { root, container, serialized });
2048
1441
  }
2049
- if (state.hideTemplate === true) {
2050
- let prev = null;
2051
- for (const entry of ordered) {
2052
- const el = mounted.get(entry.id)?.container;
2053
- if (!el) continue;
2054
- if (prev && !(prev.compareDocumentPosition(el) & Node.DOCUMENT_POSITION_FOLLOWING)) {
2055
- prev.insertAdjacentElement("afterend", el);
2056
- }
2057
- prev = el;
2058
- }
2059
- }
2060
1442
  syncReplacedOriginals(state);
2061
1443
  syncRemovedSections(state);
2062
- syncTemplateHidden(state, pageSections.length > 0);
2063
1444
  }
2064
1445
 
2065
1446
  // src/useLinkHrefGuardian.ts
@@ -2666,7 +2047,7 @@ function SchedulingWidget({ notifyOnConnect = false, initialScheduleId, insertAf
2666
2047
  const autoId = (0, import_react5.useId)();
2667
2048
  const insertAfter = insertAfterProp ?? autoId;
2668
2049
  const [schedule, setSchedule] = (0, import_react5.useState)(null);
2669
- const [loading, setLoading] = (0, import_react5.useState)(initialScheduleId !== null);
2050
+ const [loading, setLoading] = (0, import_react5.useState)(true);
2670
2051
  const [inEditor, setInEditor] = (0, import_react5.useState)(false);
2671
2052
  const [isHovered, setIsHovered] = (0, import_react5.useState)(false);
2672
2053
  const [modalState, setModalState] = (0, import_react5.useState)(null);
@@ -2840,10 +2221,8 @@ function SchedulingWidget({ notifyOnConnect = false, initialScheduleId, insertAf
2840
2221
  "*"
2841
2222
  );
2842
2223
  };
2224
+ if (!inEditor && !loading && !schedule) return null;
2843
2225
  const sectionId = `scheduling-${insertAfter}`;
2844
- if (!inEditor && !loading && !schedule) {
2845
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("section", { "data-ohw-section": sectionId, "data-ohw-scheduling-anchor": insertAfter, style: { display: "none" } });
2846
- }
2847
2226
  return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
2848
2227
  "section",
2849
2228
  {
@@ -7760,17 +7139,13 @@ function MediaOverlay({
7760
7139
  hover,
7761
7140
  isUploading,
7762
7141
  fadingOut = false,
7763
- selected = false,
7764
- hovered = false,
7765
7142
  onFadeOutComplete,
7766
7143
  onReplace,
7767
- onSelect,
7768
7144
  onVideoSettingsChange
7769
7145
  }) {
7770
7146
  const { rect } = hover;
7771
7147
  const skeletonRef = React8.useRef(null);
7772
7148
  const isVideo = hover.elementType === "video";
7773
- const showChrome = !selected || hovered;
7774
7149
  const autoplay = hover.videoAutoplay ?? true;
7775
7150
  const muted = hover.videoMuted ?? true;
7776
7151
  const probeRef = React8.useRef(null);
@@ -7784,7 +7159,6 @@ function MediaOverlay({
7784
7159
  (prev) => prev && prev.fullWidth === width && prev.height === height ? prev : { fullWidth: width, height }
7785
7160
  );
7786
7161
  }, [isVideo]);
7787
- const replaceLabel = isVideo ? "Replace video" : hover.elementType === "bg-image" ? "Replace background" : "Replace image";
7788
7162
  const replaceMode = buttonSize ? replaceButtonMode({ width: rect.width, height: rect.height }, buttonSize) : "none";
7789
7163
  const box = {
7790
7164
  position: "fixed",
@@ -7818,7 +7192,7 @@ function MediaOverlay({
7818
7192
  }
7819
7193
  );
7820
7194
  }
7821
- const settingsBar = isVideo && !hover.isDragOver && showChrome ? /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
7195
+ const settingsBar = isVideo && !hover.isDragOver ? /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
7822
7196
  "div",
7823
7197
  {
7824
7198
  "data-ohw-bridge": "",
@@ -7888,12 +7262,10 @@ function MediaOverlay({
7888
7262
  // in-document, pointer-events does it natively. The button below opts back in, so
7889
7263
  // Replace still works.
7890
7264
  pointerEvents: hover.hasTextOverlap ? "none" : "auto",
7891
- // Selected: a firm component ring with no wash, so the image reads as chosen rather
7892
- // than hovered. Hover keeps the existing tinted preview.
7893
- boxShadow: selected ? "inset 0 0 0 2px var(--color-primary)" : "inset 0 0 0 1.5px var(--color-primary)",
7894
- background: selected ? "transparent" : "color-mix(in srgb, var(--color-primary) 20%, transparent)"
7265
+ boxShadow: "inset 0 0 0 1.5px var(--color-primary)",
7266
+ background: "color-mix(in srgb, var(--color-primary) 20%, transparent)"
7895
7267
  },
7896
- onClick: () => onSelect && !selected ? onSelect(hover.key) : onReplace(hover.key),
7268
+ onClick: () => onReplace(hover.key),
7897
7269
  children: [
7898
7270
  /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
7899
7271
  Button,
@@ -7914,17 +7286,17 @@ function MediaOverlay({
7914
7286
  },
7915
7287
  children: [
7916
7288
  isVideo ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_lucide_react5.Film, { size: 14 }) : /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_lucide_react5.ImageIcon, { size: 14 }),
7917
- replaceLabel
7289
+ isVideo ? "Replace video" : "Replace image"
7918
7290
  ]
7919
7291
  }
7920
7292
  ),
7921
- showChrome && replaceMode !== "none" && /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
7293
+ replaceMode === "none" ? null : /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
7922
7294
  Button,
7923
7295
  {
7924
7296
  "data-ohw-media-overlay": "",
7925
7297
  variant: "outline",
7926
7298
  size: "sm",
7927
- "aria-label": replaceLabel,
7299
+ "aria-label": isVideo ? "Replace video" : "Replace image",
7928
7300
  className: "gap-1.5 cursor-pointer hover:bg-background",
7929
7301
  style: {
7930
7302
  ...OVERLAY_BUTTON_STYLE,
@@ -7947,7 +7319,7 @@ function MediaOverlay({
7947
7319
  },
7948
7320
  children: [
7949
7321
  isVideo ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_lucide_react5.Film, { size: 14 }) : /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_lucide_react5.ImageIcon, { size: 14 }),
7950
- replaceMode === "full" ? replaceLabel : null
7322
+ replaceMode === "full" ? isVideo ? "Replace video" : "Replace image" : null
7951
7323
  ]
7952
7324
  }
7953
7325
  )
@@ -8018,9 +7390,6 @@ var import_lucide_react7 = require("lucide-react");
8018
7390
 
8019
7391
  // src/lib/sections.ts
8020
7392
  var LINK_PICKER_EXCLUDED_IDS = /* @__PURE__ */ new Set(["navbar", "footer"]);
8021
- function isChromeSection(el) {
8022
- return el.matches("header, nav, footer, aside");
8023
- }
8024
7393
  function titleCaseSectionId(id) {
8025
7394
  return id.split("-").filter(Boolean).map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
8026
7395
  }
@@ -8031,8 +7400,6 @@ function parseSectionsFromRoot(root) {
8031
7400
  const id = el.getAttribute("data-ohw-section") ?? "";
8032
7401
  if (!id || seen.has(id) || LINK_PICKER_EXCLUDED_IDS.has(id)) continue;
8033
7402
  if (el.parentElement?.closest("[data-ohw-section]")) continue;
8034
- if (el.hasAttribute("data-ohw-ai-template-hidden") || el.hasAttribute("data-ohw-ai-removed") || el.hasAttribute("data-ohw-ai-replaced-by"))
8035
- continue;
8036
7403
  seen.add(id);
8037
7404
  const label = el.getAttribute("data-ohw-section-label") ?? titleCaseSectionId(id);
8038
7405
  sections.push({ id, label });
@@ -8048,169 +7415,21 @@ function parseSectionsFromHtml(html) {
8048
7415
  return parseSectionsFromRoot(doc);
8049
7416
  }
8050
7417
 
8051
- // src/lib/section-instances.ts
8052
- var SECTION_ORDER_KEY = "__ohw_section_order";
8053
- var REMOVED_ATTR2 = "data-ohw-section-removed";
8054
- function isRemovedSection(el) {
8055
- return el.hasAttribute(REMOVED_ATTR2);
8056
- }
8057
- function topLevelSections() {
8058
- return Array.from(document.querySelectorAll("[data-ohw-section]")).filter(
8059
- (el) => !el.parentElement?.closest("[data-ohw-section]") && !isChromeSection(el) && !isRemovedSection(el)
8060
- );
8061
- }
8062
- function instanceIdOf(el) {
8063
- return el.dataset.ohwInstance ?? el.dataset.ohwSection ?? "";
8064
- }
8065
- function planSectionMove(instanceId, targetIndex, currentPath) {
8066
- const sections = topLevelSections();
8067
- const index = sections.findIndex((el) => instanceIdOf(el) === instanceId);
8068
- if (index === -1) return null;
8069
- const dragged = sections[index];
8070
- const others = sections.filter((_, i) => i !== index);
8071
- const clamped = Math.max(0, Math.min(targetIndex, others.length));
8072
- const reordered = [...others.slice(0, clamped), dragged, ...others.slice(clamped)];
8073
- return reordered.map((el, order) => ({
8074
- instanceId: instanceIdOf(el),
8075
- type: el.getAttribute("data-ohw-section") ?? "",
8076
- order,
8077
- pagePath: currentPath
8078
- }));
8079
- }
8080
- function moveSectionInstance(instanceId, direction, currentPath) {
8081
- const sections = topLevelSections();
8082
- const index = sections.findIndex((el) => instanceIdOf(el) === instanceId);
8083
- if (index === -1) return null;
8084
- const siblingIndex = direction === "up" ? index - 1 : index + 1;
8085
- if (siblingIndex < 0 || siblingIndex >= sections.length) return null;
8086
- const entries = planSectionMove(instanceId, siblingIndex, currentPath);
8087
- if (!entries) return null;
8088
- applyPersistedOrder(entries);
8089
- return entries;
8090
- }
8091
- function syncRemovedFlags(entries) {
8092
- const removedIds = new Set(entries.filter((e) => e.removed).map((e) => e.instanceId));
8093
- document.querySelectorAll(`[${REMOVED_ATTR2}]`).forEach((el) => {
8094
- if (!removedIds.has(instanceIdOf(el))) {
8095
- el.style.removeProperty("display");
8096
- el.removeAttribute(REMOVED_ATTR2);
8097
- }
8098
- });
8099
- for (const id of removedIds) {
8100
- const el = document.querySelector(`[data-ohw-instance="${CSS.escape(id)}"]`);
8101
- if (el) {
8102
- el.style.display = "none";
8103
- el.setAttribute(REMOVED_ATTR2, "");
8104
- }
8105
- }
8106
- }
8107
- function applyPersistedOrder(entries) {
8108
- syncRemovedFlags(entries);
8109
- if (entries.length === 0) return;
8110
- const sections = topLevelSections();
8111
- if (sections.length === 0) return;
8112
- const orderIndex = new Map(entries.map((e) => [e.instanceId, e.order]));
8113
- const ordered = [...sections].sort((a, b) => {
8114
- const aOrder = orderIndex.get(instanceIdOf(a));
8115
- const bOrder = orderIndex.get(instanceIdOf(b));
8116
- if (aOrder === void 0 && bOrder === void 0) return 0;
8117
- if (aOrder === void 0) return 1;
8118
- if (bOrder === void 0) return -1;
8119
- return aOrder - bOrder;
8120
- });
8121
- let prev = null;
8122
- for (const el of ordered) {
8123
- if (prev) prev.after(el);
8124
- prev = el;
8125
- }
7418
+ // src/ui/ai-section/AiSectionOverlay.tsx
7419
+ var import_jsx_runtime17 = require("react/jsx-runtime");
7420
+ function readRect(sectionId) {
7421
+ const el = document.querySelector(`[data-ohw-section="${CSS.escape(sectionId)}"]`);
7422
+ if (!el) return null;
7423
+ const r2 = el.getBoundingClientRect();
7424
+ if (r2.width <= 0 || r2.height <= 0) return null;
7425
+ return { top: r2.top, left: r2.left, width: r2.width, height: r2.height };
8126
7426
  }
8127
- function setSectionRemoved(instanceId, currentPath, existingEntries, removed) {
8128
- if (!document.querySelector(`[data-ohw-instance="${CSS.escape(instanceId)}"]`)) return null;
8129
- const byId = new Map(existingEntries.map((e) => [e.instanceId, e]));
8130
- const allSections = Array.from(document.querySelectorAll("[data-ohw-section]")).filter(
8131
- (el) => !el.parentElement?.closest("[data-ohw-section]") && !isChromeSection(el)
8132
- );
8133
- allSections.forEach((el, order) => {
8134
- const id = instanceIdOf(el);
8135
- if (!byId.has(id)) {
8136
- byId.set(id, { instanceId: id, type: el.getAttribute("data-ohw-section") ?? "", order, pagePath: currentPath });
8137
- }
8138
- });
8139
- const target = byId.get(instanceId);
8140
- if (!target) return null;
8141
- byId.set(instanceId, { ...target, removed });
8142
- const entries = Array.from(byId.values());
8143
- applyPersistedOrder(entries);
8144
- return entries;
8145
- }
8146
- function deleteSectionInstance(instanceId, currentPath, existingEntries) {
8147
- return setSectionRemoved(instanceId, currentPath, existingEntries, true);
8148
- }
8149
- function restoreSectionInstance(instanceId, currentPath, existingEntries) {
8150
- return setSectionRemoved(instanceId, currentPath, existingEntries, false);
8151
- }
8152
- function newInstanceId() {
8153
- return typeof crypto !== "undefined" && "randomUUID" in crypto ? crypto.randomUUID() : `instance-${Date.now()}-${Math.random().toString(36).slice(2)}`;
8154
- }
8155
- function getPageSectionOrderEntries(raw, currentPath) {
8156
- if (!raw) return [];
8157
- try {
8158
- const entries = JSON.parse(raw);
8159
- return entries.filter((e) => !e.pagePath || e.pagePath === currentPath);
8160
- } catch {
8161
- return [];
8162
- }
8163
- }
8164
- function rekeySectionSubtree(root, instanceId) {
8165
- const suffix = `::${instanceId}`;
8166
- const rekey = (el, attr) => {
8167
- const current = el.getAttribute(attr);
8168
- if (current) el.setAttribute(attr, `${current}${suffix}`);
8169
- };
8170
- if (root.hasAttribute("data-ohw-key")) rekey(root, "data-ohw-key");
8171
- if (root.hasAttribute("data-ohw-href-key")) rekey(root, "data-ohw-href-key");
8172
- root.querySelectorAll("[data-ohw-key]").forEach((el) => rekey(el, "data-ohw-key"));
8173
- root.querySelectorAll("[data-ohw-href-key]").forEach((el) => rekey(el, "data-ohw-href-key"));
8174
- }
8175
- function initSectionInstancesFromContent(content, currentPath) {
8176
- document.querySelectorAll("[data-ohw-section]:not([data-ohw-instance])").forEach((el) => {
8177
- el.setAttribute("data-ohw-instance", el.getAttribute("data-ohw-section") ?? "");
8178
- });
8179
- const entries = getPageSectionOrderEntries(content[SECTION_ORDER_KEY], currentPath);
8180
- for (const entry of entries) {
8181
- if (entry.instanceId === entry.type) continue;
8182
- if (document.querySelector(`[data-ohw-instance="${CSS.escape(entry.instanceId)}"]`)) continue;
8183
- const original = document.querySelector(
8184
- `[data-ohw-section="${CSS.escape(entry.type)}"][data-ohw-instance="${CSS.escape(entry.type)}"]`
8185
- );
8186
- if (!original) continue;
8187
- const clone = original.cloneNode(true);
8188
- clone.setAttribute("data-ohw-instance", entry.instanceId);
8189
- rekeySectionSubtree(clone, entry.instanceId);
8190
- original.insertAdjacentElement("afterend", clone);
8191
- }
8192
- applyPersistedOrder(entries);
8193
- }
8194
-
8195
- // src/ui/ai-section/AiSectionOverlay.tsx
8196
- var import_jsx_runtime17 = require("react/jsx-runtime");
8197
- function findSectionElement(instanceId) {
8198
- const escaped = CSS.escape(instanceId);
8199
- return document.querySelector(`[data-ohw-instance="${escaped}"]`) ?? document.querySelector(`[data-ohw-section="${escaped}"]:not([data-ohw-instance])`);
8200
- }
8201
- function readRect(instanceId) {
8202
- const el = findSectionElement(instanceId);
8203
- if (!el) return null;
8204
- const r2 = el.getBoundingClientRect();
8205
- if (r2.width <= 0 || r2.height <= 0) return null;
8206
- return { top: r2.top, left: r2.left, width: r2.width, height: r2.height };
8207
- }
8208
- function useLiveSectionRect(sectionId) {
8209
- const [rect, setRect] = (0, import_react8.useState)(null);
8210
- (0, import_react8.useEffect)(() => {
8211
- if (!sectionId) {
8212
- setRect(null);
8213
- return;
7427
+ function useLiveSectionRect(sectionId) {
7428
+ const [rect, setRect] = (0, import_react8.useState)(null);
7429
+ (0, import_react8.useEffect)(() => {
7430
+ if (!sectionId) {
7431
+ setRect(null);
7432
+ return;
8214
7433
  }
8215
7434
  const update = () => {
8216
7435
  const next = readRect(sectionId);
@@ -8222,7 +7441,7 @@ function useLiveSectionRect(sectionId) {
8222
7441
  const opts = { capture: true, passive: true };
8223
7442
  window.addEventListener("scroll", update, opts);
8224
7443
  window.addEventListener("resize", update);
8225
- const el = findSectionElement(sectionId);
7444
+ const el = document.querySelector(`[data-ohw-section="${CSS.escape(sectionId)}"]`);
8226
7445
  const ro = el ? new ResizeObserver(update) : null;
8227
7446
  if (el && ro) ro.observe(el);
8228
7447
  const interval = setInterval(update, 500);
@@ -8235,12 +7454,6 @@ function useLiveSectionRect(sectionId) {
8235
7454
  }, [sectionId]);
8236
7455
  return rect;
8237
7456
  }
8238
- function computeSectionBoundaryFlags(instanceId) {
8239
- const topLevel = topLevelSections();
8240
- const index = topLevel.findIndex((el) => (el.dataset.ohwInstance ?? el.dataset.ohwSection) === instanceId);
8241
- if (index === -1) return { isFirst: true, isLast: true };
8242
- return { isFirst: index === 0, isLast: index === topLevel.length - 1 };
8243
- }
8244
7457
  var PRIMARY2 = "#0885FE";
8245
7458
  function edgeAwareRadius(rect) {
8246
7459
  const container = window.innerWidth <= 480 ? 16 : 24;
@@ -8314,7 +7527,6 @@ function AiSectionOverlay({
8314
7527
  }) {
8315
7528
  const [selectedId, setSelectedId] = (0, import_react8.useState)(null);
8316
7529
  const [reviewId, setReviewId] = (0, import_react8.useState)(null);
8317
- const [reviewButtonsHidden, setReviewButtonsHidden] = (0, import_react8.useState)(false);
8318
7530
  const reviewIdRef = (0, import_react8.useRef)(null);
8319
7531
  reviewIdRef.current = reviewId;
8320
7532
  const selectedIdRef = (0, import_react8.useRef)(null);
@@ -8323,7 +7535,7 @@ function AiSectionOverlay({
8323
7535
  (el) => {
8324
7536
  postToParent2({
8325
7537
  type: "ow:section-selected",
8326
- sectionId: el ? el.dataset.ohwInstance ?? el.dataset.ohwSection ?? null : null,
7538
+ sectionId: el?.dataset.ohwSection ?? null,
8327
7539
  sectionLabel: el ? el.dataset.ohwSectionLabel ?? titleCaseSectionId(el.dataset.ohwSection ?? "") : null
8328
7540
  });
8329
7541
  },
@@ -8332,7 +7544,7 @@ function AiSectionOverlay({
8332
7544
  const selectFromElement = (0, import_react8.useCallback)(
8333
7545
  (el, options) => {
8334
7546
  const sectionEl = el?.closest("[data-ohw-section]") ?? null;
8335
- const id = sectionEl ? sectionEl.dataset.ohwInstance ?? sectionEl.dataset.ohwSection ?? null : null;
7547
+ const id = sectionEl?.dataset.ohwSection ?? null;
8336
7548
  if (id === selectedIdRef.current) return;
8337
7549
  setSelectedId(id);
8338
7550
  if (options?.report !== false) report(sectionEl);
@@ -8353,15 +7565,12 @@ function AiSectionOverlay({
8353
7565
  selectFromElement(sectionEl);
8354
7566
  return sectionEl != null;
8355
7567
  },
8356
- clear: () => {
8357
- setSelectedId(null);
8358
- report(null);
8359
- }
7568
+ clear: () => setSelectedId(null)
8360
7569
  };
8361
7570
  return () => {
8362
7571
  apiRef.current = null;
8363
7572
  };
8364
- }, [apiRef, selectFromElement, report]);
7573
+ }, [apiRef, selectFromElement]);
8365
7574
  (0, import_react8.useEffect)(() => {
8366
7575
  const onMessage = (e) => {
8367
7576
  if (e.data?.type === "ow:ai-select" && e.data.sectionId === null) {
@@ -8376,10 +7585,9 @@ function AiSectionOverlay({
8376
7585
  }
8377
7586
  const found = readRect(sectionId) != null;
8378
7587
  setReviewId(found ? sectionId : null);
8379
- setReviewButtonsHidden(e.data.hideButtons === true);
8380
7588
  postToParent2({ type: "ow:ai-review-started", sectionId, found });
8381
7589
  if (found) {
8382
- document.querySelector(`[data-ohw-instance="${CSS.escape(sectionId)}"]`)?.scrollIntoView({ behavior: "smooth", block: "nearest" });
7590
+ document.querySelector(`[data-ohw-section="${CSS.escape(sectionId)}"]`)?.scrollIntoView({ behavior: "smooth", block: "nearest" });
8383
7591
  }
8384
7592
  }
8385
7593
  };
@@ -8398,7 +7606,7 @@ function AiSectionOverlay({
8398
7606
  return;
8399
7607
  }
8400
7608
  const sec = t.closest("[data-ohw-section]");
8401
- setHoveredId(sec ? sec.dataset.ohwInstance ?? sec.dataset.ohwSection ?? null : null);
7609
+ setHoveredId(sec?.dataset.ohwSection ?? null);
8402
7610
  };
8403
7611
  const onLeave = () => setHoveredId(null);
8404
7612
  document.addEventListener("mousemove", onMove, { passive: true });
@@ -8430,30 +7638,9 @@ function AiSectionOverlay({
8430
7638
  },
8431
7639
  [postToParent2]
8432
7640
  );
8433
- const activeSelectionId = reviewId ? null : selectedId;
8434
- const selectionRect = useLiveSectionRect(activeSelectionId);
7641
+ const selectionRect = useLiveSectionRect(reviewId ? null : selectedId);
8435
7642
  const reviewRect = useLiveSectionRect(reviewId);
8436
7643
  const hoverRect = useLiveSectionRect(reviewId || hoveredId === selectedId ? null : hoveredId);
8437
- (0, import_react8.useEffect)(() => {
8438
- const selectedEl = activeSelectionId ? findSectionElement(activeSelectionId) : null;
8439
- if (!activeSelectionId || !selectionRect || selectedEl && isChromeSection(selectedEl)) {
8440
- postToParent2({ type: "ow:section-rect", instanceId: null, rect: null });
8441
- return;
8442
- }
8443
- const { isFirst, isLast } = computeSectionBoundaryFlags(activeSelectionId);
8444
- postToParent2({
8445
- type: "ow:section-rect",
8446
- instanceId: activeSelectionId,
8447
- rect: {
8448
- top: selectionRect.top + window.scrollY,
8449
- left: selectionRect.left + window.scrollX,
8450
- width: selectionRect.width,
8451
- height: selectionRect.height
8452
- },
8453
- isFirst,
8454
- isLast
8455
- });
8456
- }, [activeSelectionId, selectionRect, postToParent2]);
8457
7644
  return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(import_jsx_runtime17.Fragment, { children: [
8458
7645
  hoverRect && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
8459
7646
  "div",
@@ -8504,16 +7691,13 @@ function AiSectionOverlay({
8504
7691
  border: `2px solid ${PRIMARY2}`,
8505
7692
  borderRadius: edgeAwareRadius(reviewRect),
8506
7693
  zIndex: 2147483200,
8507
- // The veil itself: swallows clicks so the section stays locked until decided. This
8508
- // stopPropagation only guards the bubble phase; the bridge's capture-phase click
8509
- // handlers additionally bail on `[data-ohw-ai-review]`, without which a click on
8510
- // Accept/Discard resolves to the media beneath and opens the file picker.
7694
+ // The veil itself: swallows clicks so the section stays locked until decided.
8511
7695
  background: "rgba(8, 133, 254, 0.04)",
8512
7696
  pointerEvents: "auto",
8513
7697
  cursor: "default"
8514
7698
  },
8515
7699
  onClick: (e) => e.stopPropagation(),
8516
- children: !reviewButtonsHidden && /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
7700
+ children: /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
8517
7701
  "div",
8518
7702
  {
8519
7703
  style: {
@@ -8536,6 +7720,47 @@ function AiSectionOverlay({
8536
7720
  ] });
8537
7721
  }
8538
7722
 
7723
+ // src/lib/section-instances.ts
7724
+ var SECTION_ORDER_KEY = "__ohw_section_order";
7725
+ function getPageSectionOrderEntries(raw, currentPath) {
7726
+ if (!raw) return [];
7727
+ try {
7728
+ const entries = JSON.parse(raw);
7729
+ return entries.filter((e) => !e.pagePath || e.pagePath === currentPath);
7730
+ } catch {
7731
+ return [];
7732
+ }
7733
+ }
7734
+ function rekeySectionSubtree(root, instanceId) {
7735
+ const suffix = `::${instanceId}`;
7736
+ const rekey = (el, attr) => {
7737
+ const current = el.getAttribute(attr);
7738
+ if (current) el.setAttribute(attr, `${current}${suffix}`);
7739
+ };
7740
+ if (root.hasAttribute("data-ohw-key")) rekey(root, "data-ohw-key");
7741
+ if (root.hasAttribute("data-ohw-href-key")) rekey(root, "data-ohw-href-key");
7742
+ root.querySelectorAll("[data-ohw-key]").forEach((el) => rekey(el, "data-ohw-key"));
7743
+ root.querySelectorAll("[data-ohw-href-key]").forEach((el) => rekey(el, "data-ohw-href-key"));
7744
+ }
7745
+ function initSectionInstancesFromContent(content, currentPath) {
7746
+ document.querySelectorAll("[data-ohw-section]:not([data-ohw-instance])").forEach((el) => {
7747
+ el.setAttribute("data-ohw-instance", el.getAttribute("data-ohw-section") ?? "");
7748
+ });
7749
+ const entries = getPageSectionOrderEntries(content[SECTION_ORDER_KEY], currentPath);
7750
+ for (const entry of entries) {
7751
+ if (entry.instanceId === entry.type) continue;
7752
+ if (document.querySelector(`[data-ohw-instance="${CSS.escape(entry.instanceId)}"]`)) continue;
7753
+ const original = document.querySelector(
7754
+ `[data-ohw-section="${CSS.escape(entry.type)}"][data-ohw-instance="${CSS.escape(entry.type)}"]`
7755
+ );
7756
+ if (!original) continue;
7757
+ const clone = original.cloneNode(true);
7758
+ clone.setAttribute("data-ohw-instance", entry.instanceId);
7759
+ rekeySectionSubtree(clone, entry.instanceId);
7760
+ original.insertAdjacentElement("afterend", clone);
7761
+ }
7762
+ }
7763
+
8539
7764
  // src/OhhwellsBridge.tsx
8540
7765
  var import_react_dom4 = require("react-dom");
8541
7766
  var import_navigation3 = require("next/navigation");
@@ -11080,13 +10305,8 @@ function referenceBox(slot) {
11080
10305
  const neighbour = row ? Array.from(row.querySelectorAll('[data-ohw-editable="icon"]')).find(
11081
10306
  (el) => el !== slot && !el.hasAttribute("data-ohw-social-icon-placeholder")
11082
10307
  ) : null;
11083
- if (neighbour) {
11084
- const box2 = neighbour.querySelector(GLYPH_SELECTOR)?.getBoundingClientRect();
11085
- if (box2?.width && box2.height) return box2;
11086
- }
11087
- const own = slot.getBoundingClientRect();
11088
- if (own.width && own.height) return own;
11089
- const box = slot.querySelector(GLYPH_SELECTOR)?.getBoundingClientRect() ?? null;
10308
+ const source = (neighbour ?? slot).querySelector(GLYPH_SELECTOR);
10309
+ const box = source?.getBoundingClientRect() ?? null;
11090
10310
  return box?.width && box.height ? box : null;
11091
10311
  }
11092
10312
  function iconMarkupSizedFor(slot, markup) {
@@ -12887,7 +12107,6 @@ function readLogoSizeState(content, placement) {
12887
12107
  function getLogoElement(el) {
12888
12108
  const marked = el.closest('[data-ohw-role="logo"], [data-ohw-logo]');
12889
12109
  if (marked) return marked;
12890
- if (el.closest('[data-ohw-editable="icon"]')) return null;
12891
12110
  const root = el.closest("nav, [data-ohw-nav-root], footer");
12892
12111
  if (!root) return null;
12893
12112
  const anchor = el.closest("a");
@@ -13761,333 +12980,6 @@ function useNavItemDrag({
13761
12980
  };
13762
12981
  }
13763
12982
 
13764
- // src/useSectionDrag.ts
13765
- var import_react15 = require("react");
13766
-
13767
- // src/lib/section-dnd.ts
13768
- function isFooterSection(el) {
13769
- return el.dataset.ohwSection === "footer";
13770
- }
13771
- function buildSectionDropSlots(draggedInstanceId) {
13772
- const sections = topLevelSections().filter(
13773
- (el) => instanceIdOf(el) !== draggedInstanceId && !isFooterSection(el)
13774
- );
13775
- const slots = [];
13776
- if (sections.length === 0) return slots;
13777
- const left = 0;
13778
- const width = document.documentElement.clientWidth;
13779
- for (let i = 0; i <= sections.length; i++) {
13780
- let y;
13781
- if (i === 0) {
13782
- y = sections[0].getBoundingClientRect().top;
13783
- } else if (i === sections.length) {
13784
- y = sections[sections.length - 1].getBoundingClientRect().bottom;
13785
- } else {
13786
- const prev = sections[i - 1].getBoundingClientRect();
13787
- const next = sections[i].getBoundingClientRect();
13788
- y = (prev.bottom + next.top) / 2;
13789
- }
13790
- slots.push({ insertIndex: i, y, left, width });
13791
- }
13792
- return slots;
13793
- }
13794
- function hitTestSectionDropSlot(y, slots) {
13795
- let best = null;
13796
- for (const slot of slots) {
13797
- const dist = Math.abs(y - slot.y);
13798
- if (!best || dist < best.dist) best = { slot, dist };
13799
- }
13800
- return best?.slot ?? null;
13801
- }
13802
-
13803
- // src/useSectionDrag.ts
13804
- var PRESS_THRESHOLD = 10;
13805
- var EDGE_ZONE = 60;
13806
- var MAX_AUTO_SCROLL_SPEED = 18;
13807
- var SECTION_DRAG_EXCLUDED_SELECTOR = [
13808
- "[data-ohw-toolbar]",
13809
- "[data-ohw-edit-chrome]",
13810
- "[data-ohw-item-interaction]",
13811
- "[data-ohw-drag-handle-container]",
13812
- '[data-slot="drag-handle"]',
13813
- "[data-ohw-item-toolbar-anchor]",
13814
- "[data-ohw-item-drag-surface]",
13815
- "[data-ohw-more-menu]",
13816
- '[data-slot="dropdown-menu-content"]',
13817
- '[data-slot="dropdown-menu-item"]',
13818
- "[data-ohw-state-toggle]",
13819
- "[data-ohw-max-badge]",
13820
- "[data-ohw-floating-panel]",
13821
- "[data-ohw-section-picker]",
13822
- "[data-ohw-link-popover-root]",
13823
- "[data-ohw-link-modal-root]",
13824
- "[data-ohw-link-page-dropdown]",
13825
- '[data-slot="popover-content"]',
13826
- '[data-slot="dialog-content"]',
13827
- '[data-slot="dialog-overlay"]',
13828
- "[data-ohw-ai-review]",
13829
- "[data-ohw-editable]",
13830
- "[data-ohw-editable-state]",
13831
- "[contenteditable]",
13832
- "[data-ohw-href-key]",
13833
- "[data-ohw-footer-col]",
13834
- "[data-ohw-social-label]",
13835
- "a",
13836
- "button",
13837
- '[role="button"]',
13838
- '[data-ohw-role="navbar-button"]',
13839
- '[data-ohw-role="button"]',
13840
- "[data-ohw-carousel]",
13841
- "[data-ohw-carousel-value]",
13842
- "[data-ohw-carousel-slide]",
13843
- "[data-ohw-carousel-overlay]",
13844
- "[data-ohw-media-chrome]",
13845
- "[data-ohw-media-overlay]",
13846
- "[data-ohw-media-skeleton]"
13847
- ].join(", ");
13848
- function visibleClip(ps) {
13849
- if (!ps) return null;
13850
- const top = Math.max(0, ps.headerH - ps.iframeOffsetTop);
13851
- const bottom = Math.min(window.innerHeight, ps.headerH + ps.canvasH - ps.iframeOffsetTop);
13852
- return { top, bottom: Math.max(top, bottom) };
13853
- }
13854
- function useSectionDrag({
13855
- isEditMode,
13856
- editContentRef,
13857
- postToParentRef,
13858
- parentScrollRef,
13859
- navDragRef,
13860
- footerDragRef,
13861
- suppressNextClickRef,
13862
- suppressClickUntilRef
13863
- }) {
13864
- const sectionDragRef = (0, import_react15.useRef)(null);
13865
- const [sectionDropSlots, setSectionDropSlots] = (0, import_react15.useState)([]);
13866
- const [activeSectionDropIndex, setActiveSectionDropIndex] = (0, import_react15.useState)(null);
13867
- const [isSectionDragging, setIsSectionDragging] = (0, import_react15.useState)(false);
13868
- const sectionPointerDragRef = (0, import_react15.useRef)(null);
13869
- const autoScrollRafRef = (0, import_react15.useRef)(null);
13870
- const autoScrollDeltaRef = (0, import_react15.useRef)(0);
13871
- const stopAutoScroll = (0, import_react15.useCallback)(() => {
13872
- if (autoScrollRafRef.current != null) {
13873
- cancelAnimationFrame(autoScrollRafRef.current);
13874
- autoScrollRafRef.current = null;
13875
- }
13876
- autoScrollDeltaRef.current = 0;
13877
- }, []);
13878
- const tickAutoScroll = (0, import_react15.useCallback)(() => {
13879
- if (!sectionDragRef.current) {
13880
- stopAutoScroll();
13881
- return;
13882
- }
13883
- if (autoScrollDeltaRef.current !== 0) {
13884
- postToParentRef.current({ type: "ow:request-scroll", deltaY: autoScrollDeltaRef.current });
13885
- }
13886
- autoScrollRafRef.current = requestAnimationFrame(tickAutoScroll);
13887
- }, [postToParentRef, stopAutoScroll]);
13888
- const updateAutoScroll = (0, import_react15.useCallback)(
13889
- (clientY) => {
13890
- const clip = visibleClip(parentScrollRef.current);
13891
- let delta = 0;
13892
- if (clip) {
13893
- const distTop = clientY - clip.top;
13894
- const distBottom = clip.bottom - clientY;
13895
- if (distTop >= 0 && distTop < EDGE_ZONE) {
13896
- delta = -MAX_AUTO_SCROLL_SPEED * (1 - distTop / EDGE_ZONE);
13897
- } else if (distBottom >= 0 && distBottom < EDGE_ZONE) {
13898
- delta = MAX_AUTO_SCROLL_SPEED * (1 - distBottom / EDGE_ZONE);
13899
- }
13900
- }
13901
- autoScrollDeltaRef.current = delta;
13902
- if (delta !== 0 && autoScrollRafRef.current == null) {
13903
- autoScrollRafRef.current = requestAnimationFrame(tickAutoScroll);
13904
- } else if (delta === 0) {
13905
- stopAutoScroll();
13906
- }
13907
- },
13908
- [parentScrollRef, stopAutoScroll, tickAutoScroll]
13909
- );
13910
- const clearSectionDragVisuals = (0, import_react15.useCallback)(() => {
13911
- sectionDragRef.current?.draggedEl.removeAttribute("data-ohw-section-dragging");
13912
- sectionDragRef.current = null;
13913
- setSectionDropSlots([]);
13914
- setActiveSectionDropIndex(null);
13915
- setIsSectionDragging(false);
13916
- stopAutoScroll();
13917
- document.documentElement.removeAttribute("data-ohw-section-dragging-root");
13918
- unlockItemDragInteraction();
13919
- }, [stopAutoScroll]);
13920
- const refreshSectionDragVisuals = (0, import_react15.useCallback)(
13921
- (session, clientX, clientY) => {
13922
- session.lastClientX = clientX;
13923
- session.lastClientY = clientY;
13924
- const slots = buildSectionDropSlots(session.instanceId);
13925
- const activeSlot = hitTestSectionDropSlot(clientY, slots);
13926
- session.activeSlot = activeSlot;
13927
- setSectionDropSlots(slots);
13928
- const activeIdx = activeSlot ? slots.findIndex((s) => s.insertIndex === activeSlot.insertIndex) : -1;
13929
- setActiveSectionDropIndex(activeIdx >= 0 ? activeIdx : null);
13930
- updateAutoScroll(clientY);
13931
- },
13932
- [updateAutoScroll]
13933
- );
13934
- const beginSectionDrag = (0, import_react15.useCallback)(
13935
- (session) => {
13936
- sectionDragRef.current = session;
13937
- setIsSectionDragging(true);
13938
- lockItemDuringDrag();
13939
- document.documentElement.setAttribute("data-ohw-section-dragging-root", "");
13940
- session.draggedEl.setAttribute("data-ohw-section-dragging", "");
13941
- refreshSectionDragVisuals(session, session.lastClientX, session.lastClientY);
13942
- },
13943
- [refreshSectionDragVisuals]
13944
- );
13945
- const commitSectionDrag = (0, import_react15.useCallback)(() => {
13946
- const session = sectionDragRef.current;
13947
- if (!session) {
13948
- clearSectionDragVisuals();
13949
- return;
13950
- }
13951
- const slot = session.activeSlot ?? hitTestSectionDropSlot(session.lastClientY, buildSectionDropSlots(session.instanceId));
13952
- const entries = slot ? planSectionMove(session.instanceId, slot.insertIndex, window.location.pathname) : null;
13953
- if (!entries) {
13954
- clearSectionDragVisuals();
13955
- return;
13956
- }
13957
- const orderJson = JSON.stringify(entries);
13958
- editContentRef.current = { ...editContentRef.current, [SECTION_ORDER_KEY]: orderJson };
13959
- postToParentRef.current({ type: "ow:change", nodes: [{ key: SECTION_ORDER_KEY, text: orderJson }] });
13960
- applyPersistedOrder(entries);
13961
- clearSectionDragVisuals();
13962
- requestAnimationFrame(() => {
13963
- if (editContentRef.current[SECTION_ORDER_KEY] === orderJson) {
13964
- applyPersistedOrder(entries);
13965
- }
13966
- requestAnimationFrame(() => {
13967
- window.dispatchEvent(new Event("resize"));
13968
- });
13969
- });
13970
- }, [clearSectionDragVisuals, editContentRef, postToParentRef]);
13971
- const startSectionPressDrag = (0, import_react15.useCallback)(
13972
- (el, clientX, clientY, pointerId) => {
13973
- if (navDragRef.current || footerDragRef.current || sectionDragRef.current) return false;
13974
- const instanceId = instanceIdOf(el);
13975
- if (!instanceId) return false;
13976
- sectionPointerDragRef.current = {
13977
- el,
13978
- instanceId,
13979
- startX: clientX,
13980
- startY: clientY,
13981
- pointerId,
13982
- started: false
13983
- };
13984
- return true;
13985
- },
13986
- [footerDragRef, navDragRef]
13987
- );
13988
- (0, import_react15.useEffect)(() => {
13989
- if (!isEditMode) return;
13990
- const onPointerDown = (e) => {
13991
- if (e.button !== 0) return;
13992
- if (navDragRef.current || footerDragRef.current || sectionDragRef.current) return;
13993
- if (sectionPointerDragRef.current) return;
13994
- const target = e.target;
13995
- if (!(target instanceof HTMLElement)) return;
13996
- if (target.closest(SECTION_DRAG_EXCLUDED_SELECTOR)) return;
13997
- const sectionEl = target.closest("[data-ohw-section]");
13998
- if (!sectionEl || isChromeSection(sectionEl) || sectionEl.dataset.ohwSection === "footer") return;
13999
- if (!topLevelSections().includes(sectionEl)) return;
14000
- startSectionPressDrag(sectionEl, e.clientX, e.clientY, e.pointerId);
14001
- };
14002
- const onPointerMove = (e) => {
14003
- const pending = sectionPointerDragRef.current;
14004
- if (!pending) return;
14005
- if (pending.started) {
14006
- e.preventDefault();
14007
- clearTextSelection();
14008
- const session = sectionDragRef.current;
14009
- if (!session) return;
14010
- refreshSectionDragVisuals(session, e.clientX, e.clientY);
14011
- return;
14012
- }
14013
- const dx = e.clientX - pending.startX;
14014
- const dy = e.clientY - pending.startY;
14015
- if (dx * dx + dy * dy < PRESS_THRESHOLD * PRESS_THRESHOLD) return;
14016
- e.preventDefault();
14017
- pending.started = true;
14018
- armItemPressDrag();
14019
- clearTextSelection();
14020
- try {
14021
- document.body.setPointerCapture(pending.pointerId);
14022
- } catch {
14023
- }
14024
- beginSectionDrag({
14025
- instanceId: pending.instanceId,
14026
- draggedEl: pending.el,
14027
- lastClientX: e.clientX,
14028
- lastClientY: e.clientY,
14029
- activeSlot: null
14030
- });
14031
- };
14032
- const endPointerDrag = (e) => {
14033
- const pending = sectionPointerDragRef.current;
14034
- sectionPointerDragRef.current = null;
14035
- try {
14036
- if (document.body.hasPointerCapture(e.pointerId)) {
14037
- document.body.releasePointerCapture(e.pointerId);
14038
- }
14039
- } catch {
14040
- }
14041
- if (!pending) return;
14042
- if (!pending.started) {
14043
- unlockItemDragInteraction();
14044
- return;
14045
- }
14046
- suppressNextClickRef.current = true;
14047
- suppressClickUntilRef.current = Date.now() + 500;
14048
- commitSectionDrag();
14049
- };
14050
- const onKeyDown = (e) => {
14051
- if (e.key !== "Escape") return;
14052
- if (!sectionDragRef.current && !sectionPointerDragRef.current) return;
14053
- sectionPointerDragRef.current = null;
14054
- clearSectionDragVisuals();
14055
- };
14056
- document.addEventListener("pointerdown", onPointerDown, true);
14057
- document.addEventListener("pointermove", onPointerMove, true);
14058
- document.addEventListener("pointerup", endPointerDrag, true);
14059
- document.addEventListener("pointercancel", endPointerDrag, true);
14060
- document.addEventListener("keydown", onKeyDown, true);
14061
- return () => {
14062
- document.removeEventListener("pointerdown", onPointerDown, true);
14063
- document.removeEventListener("pointermove", onPointerMove, true);
14064
- document.removeEventListener("pointerup", endPointerDrag, true);
14065
- document.removeEventListener("pointercancel", endPointerDrag, true);
14066
- document.removeEventListener("keydown", onKeyDown, true);
14067
- unlockItemDragInteraction();
14068
- stopAutoScroll();
14069
- };
14070
- }, [
14071
- beginSectionDrag,
14072
- clearSectionDragVisuals,
14073
- commitSectionDrag,
14074
- footerDragRef,
14075
- isEditMode,
14076
- navDragRef,
14077
- refreshSectionDragVisuals,
14078
- startSectionPressDrag,
14079
- stopAutoScroll,
14080
- suppressClickUntilRef,
14081
- suppressNextClickRef
14082
- ]);
14083
- return {
14084
- sectionDragRef,
14085
- sectionDropSlots,
14086
- activeSectionDropIndex,
14087
- isSectionDragging
14088
- };
14089
- }
14090
-
14091
12983
  // src/ui/footer-container-chrome.tsx
14092
12984
  var import_lucide_react15 = require("lucide-react");
14093
12985
  var import_jsx_runtime29 = require("react/jsx-runtime");
@@ -14140,7 +13032,7 @@ function FooterContainerChrome({
14140
13032
  }
14141
13033
 
14142
13034
  // src/lib/carousel.ts
14143
- var import_react16 = require("react");
13035
+ var import_react15 = require("react");
14144
13036
  var CAROUSEL_ATTR = "data-ohw-carousel";
14145
13037
  var CAROUSEL_VALUE_ATTR = "data-ohw-carousel-value";
14146
13038
  var CAROUSEL_SLIDE_ATTR = "data-ohw-carousel-slide";
@@ -14202,8 +13094,8 @@ function applyCarouselNode(key, val) {
14202
13094
  return true;
14203
13095
  }
14204
13096
  function useOhwCarousel(key, initial) {
14205
- const [images, setImages] = (0, import_react16.useState)(initial);
14206
- (0, import_react16.useEffect)(() => {
13097
+ const [images, setImages] = (0, import_react15.useState)(initial);
13098
+ (0, import_react15.useEffect)(() => {
14207
13099
  const el = document.querySelector(
14208
13100
  `[${CAROUSEL_ATTR}][data-ohw-key="${CSS.escape(key)}"]`
14209
13101
  );
@@ -14285,7 +13177,6 @@ function collectEditableNodes(extraContent, root = document) {
14285
13177
  NAV_ORDER_KEY,
14286
13178
  FOOTER_ORDER_KEY,
14287
13179
  NAV_COUNT_KEY,
14288
- SECTION_ORDER_KEY,
14289
13180
  // A socials row's order and its icons-vs-words setting live under keys no element carries,
14290
13181
  // so collecting the DOM alone left them behind: the draft knew the row was showing icons and
14291
13182
  // had gained an item, and the published page went back to the template's own (OHH-736).
@@ -14333,18 +13224,6 @@ function collectEditableNodes(extraContent, root = document) {
14333
13224
  }
14334
13225
  if (extraContent && !isScoped) {
14335
13226
  applyNavFooterDeleteOverrides(byKey, extraContent);
14336
- for (const key of LOGO_IMAGE_KEYS) {
14337
- if (!(key in extraContent)) continue;
14338
- byKey.set(key, { key, type: "image", text: extraContent[key] ?? "" });
14339
- }
14340
- for (const key of [LOGO_PLACEHOLDER_KEY, LOGO_ALT_KEY]) {
14341
- if (!(key in extraContent)) continue;
14342
- byKey.set(key, { key, type: "meta", text: extraContent[key] ?? "" });
14343
- }
14344
- for (const key of LOGO_SIZE_KEYS) {
14345
- if (!(key in extraContent)) continue;
14346
- byKey.set(key, { key, type: "meta", text: extraContent[key] ?? "" });
14347
- }
14348
13227
  }
14349
13228
  return Array.from(byKey.values());
14350
13229
  }
@@ -14749,7 +13628,6 @@ function fadeInImageElement(img, onReady) {
14749
13628
  function applyEditableImageSrc(img, url) {
14750
13629
  img.removeAttribute("srcset");
14751
13630
  img.removeAttribute("sizes");
14752
- if (img.loading === "lazy") img.loading = "eager";
14753
13631
  img.src = url;
14754
13632
  }
14755
13633
  function fadeInBgImage(el, url, onReady) {
@@ -14814,10 +13692,21 @@ function parseSchedulingInsertAfter(insertAfter) {
14814
13692
  insertBefore: insertAfter.slice(idx + INSERT_BEFORE_MARKER.length)
14815
13693
  };
14816
13694
  }
14817
- function resolveEntryAnchor(entry) {
14818
- if (entry.anchorId !== void 0) return { anchorId: entry.anchorId, beforeId: entry.beforeId ?? null };
14819
- const parsed = parseSchedulingInsertAfter(entry.insertAfter);
14820
- return { anchorId: parsed.anchor, beforeId: parsed.insertBefore };
13695
+ function resolveSchedulingInsert(insertAfter, explicitInsertBefore) {
13696
+ const parsed = parseSchedulingInsertAfter(insertAfter);
13697
+ const insertBefore = explicitInsertBefore ?? parsed.insertBefore;
13698
+ const effectiveInsertAfter = insertBefore && parsed.insertBefore === null ? `${insertAfter}${INSERT_BEFORE_MARKER}${insertBefore}` : insertAfter;
13699
+ return { effectiveInsertAfter, insertBefore };
13700
+ }
13701
+ function getSchedulingMountPoint(insertAfter) {
13702
+ const { anchor } = parseSchedulingInsertAfter(insertAfter);
13703
+ let anchorEl = document.querySelector(`[data-ohw-section="${anchor}"]`);
13704
+ if (!anchorEl && anchor === "scheduling") {
13705
+ const widgets = Array.from(document.querySelectorAll('[data-ohw-section^="scheduling-"]'));
13706
+ anchorEl = widgets.at(-1) ?? null;
13707
+ }
13708
+ if (!anchorEl) return null;
13709
+ return anchorEl.closest('[data-ohw-section-container="scheduling"]') ?? anchorEl;
14821
13710
  }
14822
13711
  function schedulingMountDepth(insertAfter) {
14823
13712
  if (!insertAfter.includes(INSERT_BEFORE_MARKER)) return 0;
@@ -14834,7 +13723,8 @@ function getPageSchedulingEntries(raw) {
14834
13723
  }
14835
13724
  }
14836
13725
  function isSchedulingWidgetMissing(entry) {
14837
- return !document.querySelector(`[data-ohw-section="${schedulingSectionId(entry.insertAfter)}"]`);
13726
+ const { effectiveInsertAfter } = resolveSchedulingInsert(entry.insertAfter);
13727
+ return !document.querySelector(`[data-ohw-section="${schedulingSectionId(effectiveInsertAfter)}"]`);
14838
13728
  }
14839
13729
  function hasMissingSchedulingWidgets(entries) {
14840
13730
  return entries.some(isSchedulingWidgetMissing);
@@ -14864,17 +13754,16 @@ function initSectionsFromContent(content, removeExisting = false) {
14864
13754
  } catch {
14865
13755
  }
14866
13756
  }
14867
- function mountSchedulingWidget(anchorId, notifyOnConnect = false, scheduleId, beforeId, existingWidgetId) {
14868
- const widgetId = existingWidgetId ?? (beforeId ? `${anchorId}${INSERT_BEFORE_MARKER}${beforeId}` : anchorId);
14869
- const sectionId = schedulingSectionId(widgetId);
13757
+ function mountSchedulingWidget(insertAfter, notifyOnConnect = false, scheduleId, explicitInsertBefore) {
13758
+ const { effectiveInsertAfter, insertBefore } = resolveSchedulingInsert(insertAfter, explicitInsertBefore);
13759
+ const sectionId = schedulingSectionId(effectiveInsertAfter);
14870
13760
  if (document.querySelector(`[data-ohw-section="${sectionId}"]`)) return false;
14871
- const anchorEl = document.querySelector(`[data-ohw-section="${anchorId}"]`);
14872
- if (!anchorEl) return false;
14873
- const mountPoint = anchorEl.closest('[data-ohw-section-container="scheduling"]') ?? anchorEl;
13761
+ const mountPoint = getSchedulingMountPoint(effectiveInsertAfter);
13762
+ if (!mountPoint) return false;
14874
13763
  const container = document.createElement("div");
14875
13764
  container.dataset.ohwSectionContainer = "scheduling";
14876
- if (beforeId) {
14877
- const beforeAnchor = document.querySelector(`[data-ohw-section="${beforeId}"]`);
13765
+ if (insertBefore) {
13766
+ const beforeAnchor = document.querySelector(`[data-ohw-section="${insertBefore}"]`);
14878
13767
  const beforePoint = beforeAnchor?.closest('[data-ohw-section-container="scheduling"]') ?? beforeAnchor;
14879
13768
  if (!beforePoint) return false;
14880
13769
  beforePoint.insertAdjacentElement("beforebegin", container);
@@ -14885,38 +13774,30 @@ function mountSchedulingWidget(anchorId, notifyOnConnect = false, scheduleId, be
14885
13774
  }
14886
13775
  tail.insertAdjacentElement("afterend", container);
14887
13776
  }
14888
- try {
14889
- const root = (0, import_client2.createRoot)(container);
14890
- (0, import_react_dom3.flushSync)(() => {
14891
- root.render(
14892
- /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
14893
- SchedulingWidget,
14894
- {
14895
- notifyOnConnect,
14896
- initialScheduleId: scheduleId,
14897
- insertAfter: widgetId
14898
- }
14899
- )
14900
- );
14901
- });
14902
- } catch (err) {
14903
- console.error("[ow:scheduling] render threw", err);
14904
- container.remove();
14905
- return false;
14906
- }
14907
- const tracker = getSectionsTracker();
14908
- let sections = [];
13777
+ const root = (0, import_client2.createRoot)(container);
13778
+ (0, import_react_dom3.flushSync)(() => {
13779
+ root.render(
13780
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
13781
+ SchedulingWidget,
13782
+ {
13783
+ notifyOnConnect,
13784
+ initialScheduleId: scheduleId,
13785
+ insertAfter: effectiveInsertAfter
13786
+ }
13787
+ )
13788
+ );
13789
+ });
13790
+ const tracker = getSectionsTracker();
13791
+ let sections = [];
14909
13792
  try {
14910
13793
  sections = JSON.parse(tracker.textContent || "[]");
14911
13794
  } catch {
14912
13795
  }
14913
13796
  const inEditor = typeof window !== "undefined" && window.self !== window.top;
14914
- if (inEditor && !sections.find((s) => s.type === "scheduling" && s.insertAfter === widgetId)) {
13797
+ if (inEditor && !sections.find((s) => s.type === "scheduling" && s.insertAfter === effectiveInsertAfter)) {
14915
13798
  sections.push({
14916
13799
  type: "scheduling",
14917
- insertAfter: widgetId,
14918
- anchorId,
14919
- beforeId: beforeId ?? null,
13800
+ insertAfter: effectiveInsertAfter,
14920
13801
  pagePath: window.location.pathname,
14921
13802
  ...scheduleId ? { scheduleId } : {}
14922
13803
  });
@@ -14930,8 +13811,7 @@ function mountSchedulingEntries(entries, notifyOnConnect = false) {
14930
13811
  for (let i = pending.length - 1; i >= 0; i--) {
14931
13812
  const entry = pending[i];
14932
13813
  const shouldNotify = typeof notifyOnConnect === "function" ? notifyOnConnect(entry) : notifyOnConnect;
14933
- const { anchorId, beforeId } = resolveEntryAnchor(entry);
14934
- if (mountSchedulingWidget(anchorId, shouldNotify, entry.scheduleId ?? null, beforeId, entry.insertAfter)) {
13814
+ if (mountSchedulingWidget(entry.insertAfter, shouldNotify, entry.scheduleId)) {
14935
13815
  pending.splice(i, 1);
14936
13816
  }
14937
13817
  }
@@ -15077,11 +13957,6 @@ function applyLinkByKey(key, val) {
15077
13957
  hrefAnchors.forEach((el) => applyLinkHref(el, val));
15078
13958
  }
15079
13959
  }
15080
- function isInsideLinkEditor(target) {
15081
- return Boolean(
15082
- 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-ohw-footer-container-chrome]") || target.closest("[data-ohw-footer-add-button]") || target.closest("[data-ohw-more-menu]") || target.closest('[data-slot="dropdown-menu-content"]') || target.closest('[data-slot="popover-content"]') || target.closest('[data-slot="dialog-content"]') || target.closest('[data-slot="dialog-overlay"]')
15083
- );
15084
- }
15085
13960
  function isInsideFloatingPanel(target) {
15086
13961
  return Boolean(target.closest("[data-ohw-floating-panel]"));
15087
13962
  }
@@ -15089,6 +13964,11 @@ function isPointOverFloatingPanel(clientX, clientY) {
15089
13964
  const el = document.elementFromPoint(clientX, clientY);
15090
13965
  return Boolean(el instanceof Element && el.closest("[data-ohw-floating-panel]"));
15091
13966
  }
13967
+ function isInsideLinkEditor(target) {
13968
+ return Boolean(
13969
+ 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-ohw-footer-container-chrome]") || target.closest("[data-ohw-footer-add-button]") || target.closest("[data-ohw-more-menu]") || target.closest('[data-slot="dropdown-menu-content"]') || target.closest('[data-slot="popover-content"]') || target.closest('[data-slot="dialog-content"]') || target.closest('[data-slot="dialog-overlay"]')
13970
+ );
13971
+ }
15092
13972
  function getHrefKeyFromElement(el) {
15093
13973
  if (!el) return null;
15094
13974
  const anchor = el.closest("[data-ohw-href-key]");
@@ -15347,7 +14227,7 @@ function getNavigationSelectionParent(el) {
15347
14227
  if (!isFooterLinksContainer(el) && (el.hasAttribute("data-ohw-footer-col") || el.hasAttribute("data-ohw-footer-column") || isInferredFooterGroup2(el))) {
15348
14228
  return getFooterLinksContainer();
15349
14229
  }
15350
- if (el.hasAttribute("data-ohw-nav-container") || el.hasAttribute("data-ohw-nav-drawer") || el.hasAttribute("data-ohw-footer-col") || el.hasAttribute("data-ohw-footer-column") || isFooterLinksContainer(el) || isInferredFooterGroup2(el)) {
14230
+ if (el.hasAttribute("data-ohw-nav-container") || el.hasAttribute("data-ohw-nav-drawer") || isFooterLinksContainer(el)) {
15351
14231
  return getNavigationRoot(el);
15352
14232
  }
15353
14233
  return null;
@@ -15562,6 +14442,7 @@ var ICONS = {
15562
14442
  insertUnorderedList: '<line x1="8" y1="6" x2="21" y2="6"/><line x1="8" y1="12" x2="21" y2="12"/><line x1="8" y1="18" x2="21" y2="18"/><line x1="3" y1="6" x2="3.01" y2="6"/><line x1="3" y1="12" x2="3.01" y2="12"/><line x1="3" y1="18" x2="3.01" y2="18"/>',
15563
14443
  insertOrderedList: '<line x1="10" y1="6" x2="21" y2="6"/><line x1="10" y1="12" x2="21" y2="12"/><line x1="10" y1="18" x2="21" y2="18"/><path d="M4 6h1v4"/><path d="M4 10h2"/><path d="M6 18H4c0-1 2-2 2-3s-1-1.5-2-1"/>'
15564
14444
  };
14445
+ var HEIGHT_SETTLE_DELAYS = [150, 400, 800];
15565
14446
  var SELECTION_CHROME_GAP2 = 4;
15566
14447
  var TOOLBAR_STROKE_GAP2 = 4;
15567
14448
  var TOOLBAR_OFFSET_FROM_ELEMENT = SELECTION_CHROME_GAP2 + TOOLBAR_STROKE_GAP2;
@@ -15817,9 +14698,9 @@ function FloatingToolbar({
15817
14698
  showEditLink,
15818
14699
  onEditLink
15819
14700
  }) {
15820
- const localRef = import_react17.default.useRef(null);
15821
- const [measuredW, setMeasuredW] = import_react17.default.useState(330);
15822
- const setRefs = import_react17.default.useCallback(
14701
+ const localRef = import_react16.default.useRef(null);
14702
+ const [measuredW, setMeasuredW] = import_react16.default.useState(330);
14703
+ const setRefs = import_react16.default.useCallback(
15823
14704
  (node) => {
15824
14705
  localRef.current = node;
15825
14706
  if (typeof elRef === "function") elRef(node);
@@ -15831,7 +14712,7 @@ function FloatingToolbar({
15831
14712
  },
15832
14713
  [elRef]
15833
14714
  );
15834
- import_react17.default.useLayoutEffect(() => {
14715
+ import_react16.default.useLayoutEffect(() => {
15835
14716
  const node = localRef.current;
15836
14717
  if (!node) return;
15837
14718
  const update = () => {
@@ -15857,7 +14738,7 @@ function FloatingToolbar({
15857
14738
  pointerEvents: "auto"
15858
14739
  },
15859
14740
  children: /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(CustomToolbar, { children: [
15860
- TOOLBAR_GROUPS.map((btns, gi) => /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(import_react17.default.Fragment, { children: [
14741
+ TOOLBAR_GROUPS.map((btns, gi) => /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(import_react16.default.Fragment, { children: [
15861
14742
  gi > 0 && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(CustomToolbarDivider, {}),
15862
14743
  btns.map((btn) => {
15863
14744
  const isActive = activeCommands.has(btn.cmd);
@@ -15941,45 +14822,6 @@ function StateToggle({
15941
14822
  );
15942
14823
  }
15943
14824
  var contentCache = /* @__PURE__ */ new Map();
15944
- var fetchedContentPaths = /* @__PURE__ */ new Set();
15945
- var OHW_LOADER_STYLE = {
15946
- position: "fixed",
15947
- inset: 0,
15948
- background: "#fff",
15949
- zIndex: 2147483646,
15950
- display: "flex",
15951
- alignItems: "center",
15952
- justifyContent: "center"
15953
- };
15954
- function OhwLoaderSpinner() {
15955
- return /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("svg", { width: "28", height: "28", viewBox: "0 0 28 28", fill: "none", "aria-hidden": true, children: [
15956
- /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("circle", { cx: "14", cy: "14", r: "11", stroke: "#E7E5E4", strokeWidth: "3" }),
15957
- /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
15958
- "circle",
15959
- {
15960
- cx: "14",
15961
- cy: "14",
15962
- r: "11",
15963
- stroke: "#1C1917",
15964
- strokeWidth: "3",
15965
- strokeDasharray: "17 52",
15966
- strokeLinecap: "round",
15967
- children: /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
15968
- "animateTransform",
15969
- {
15970
- attributeName: "transform",
15971
- type: "rotate",
15972
- from: "0 14 14",
15973
- to: "360 14 14",
15974
- dur: "0.7s",
15975
- repeatCount: "indefinite"
15976
- }
15977
- )
15978
- }
15979
- )
15980
- ] });
15981
- }
15982
- var OHW_LOADER_PREHYDRATE_SCRIPT = `(function(){try{var p=location.hostname.split(".");var fromHost=p.length>=3&&p[0]!=="www"?p[0]:"";var fromQuery=new URLSearchParams(location.search).get("subdomain")||"";if(!fromHost&&!fromQuery)return;var e=document.getElementById("ohw-loader");if(e)e.style.display="flex"}catch(e){}})();`;
15983
14825
  function resolveSubdomain(subdomainFromQuery) {
15984
14826
  if (subdomainFromQuery) return subdomainFromQuery;
15985
14827
  if (typeof window !== "undefined") {
@@ -16002,8 +14844,8 @@ function OhhwellsBridge() {
16002
14844
  const router = (0, import_navigation3.useRouter)();
16003
14845
  const searchParams = (0, import_navigation3.useSearchParams)();
16004
14846
  const isEditMode = isEditSessionActive();
16005
- const [bridgeRoot, setBridgeRoot] = (0, import_react17.useState)(null);
16006
- (0, import_react17.useEffect)(() => {
14847
+ const [bridgeRoot, setBridgeRoot] = (0, import_react16.useState)(null);
14848
+ (0, import_react16.useEffect)(() => {
16007
14849
  const figtreeFontId = "ohw-figtree-font";
16008
14850
  if (!document.getElementById(figtreeFontId)) {
16009
14851
  const preconnect1 = Object.assign(document.createElement("link"), { rel: "preconnect", href: "https://fonts.googleapis.com" });
@@ -16032,146 +14874,82 @@ function OhhwellsBridge() {
16032
14874
  const subdomain = resolveSubdomain(subdomainFromQuery);
16033
14875
  useLinkHrefGuardian(pathname, subdomain, isEditMode);
16034
14876
  useSavedLinkNavigation(isEditMode);
16035
- const postToParent2 = (0, import_react17.useCallback)((data) => {
14877
+ const postToParent2 = (0, import_react16.useCallback)((data) => {
16036
14878
  if (typeof window !== "undefined" && window.parent !== window) {
16037
14879
  window.parent.postMessage(data, "*");
16038
14880
  }
16039
14881
  }, []);
16040
- const [fetchState, setFetchState] = (0, import_react17.useState)("idle");
16041
- const autoSaveTimers = (0, import_react17.useRef)(/* @__PURE__ */ new Map());
16042
- const activeElRef = (0, import_react17.useRef)(null);
16043
- const pointerHeldRef = (0, import_react17.useRef)(false);
16044
- const selectedElRef = (0, import_react17.useRef)(null);
16045
- const selectedHrefKeyRef = (0, import_react17.useRef)(null);
16046
- const selectedFooterColAttrRef = (0, import_react17.useRef)(null);
16047
- const originalContentRef = (0, import_react17.useRef)(null);
16048
- const activeStateElRef = (0, import_react17.useRef)(null);
16049
- const parentScrollRef = (0, import_react17.useRef)(null);
16050
- const visibleViewportRef = (0, import_react17.useRef)(null);
16051
- const [dialogPortalContainer, setDialogPortalContainer] = (0, import_react17.useState)(null);
16052
- const attachVisibleViewport = (0, import_react17.useCallback)((node) => {
14882
+ const [fetchState, setFetchState] = (0, import_react16.useState)("idle");
14883
+ const autoSaveTimers = (0, import_react16.useRef)(/* @__PURE__ */ new Map());
14884
+ const activeElRef = (0, import_react16.useRef)(null);
14885
+ const pointerHeldRef = (0, import_react16.useRef)(false);
14886
+ const selectedElRef = (0, import_react16.useRef)(null);
14887
+ const selectedHrefKeyRef = (0, import_react16.useRef)(null);
14888
+ const selectedFooterColAttrRef = (0, import_react16.useRef)(null);
14889
+ const originalContentRef = (0, import_react16.useRef)(null);
14890
+ const activeStateElRef = (0, import_react16.useRef)(null);
14891
+ const parentScrollRef = (0, import_react16.useRef)(null);
14892
+ const visibleViewportRef = (0, import_react16.useRef)(null);
14893
+ const [dialogPortalContainer, setDialogPortalContainer] = (0, import_react16.useState)(null);
14894
+ const attachVisibleViewport = (0, import_react16.useCallback)((node) => {
16053
14895
  visibleViewportRef.current = node;
16054
14896
  setDialogPortalContainer(node);
16055
14897
  if (node) applyVisibleViewport(node, parentScrollRef.current);
16056
14898
  }, []);
16057
- const toolbarElRef = (0, import_react17.useRef)(null);
16058
- const glowElRef = (0, import_react17.useRef)(null);
16059
- const hoveredImageRef = (0, import_react17.useRef)(null);
16060
- const hoveredImageHasTextOverlapRef = (0, import_react17.useRef)(false);
16061
- const dragOverElRef = (0, import_react17.useRef)(null);
16062
- const [mediaHover, setMediaHover] = (0, import_react17.useState)(null);
16063
- const [selectedMedia, setSelectedMedia] = (0, import_react17.useState)(null);
16064
- const selectedMediaElRef = (0, import_react17.useRef)(null);
16065
- const clearMediaSelection = (0, import_react17.useCallback)(() => {
16066
- const prev = selectedMediaElRef.current;
16067
- selectedMediaElRef.current = null;
16068
- setSelectedMedia(null);
16069
- const sectionEl = prev?.closest("[data-ohw-section]") ?? null;
16070
- if (sectionEl) {
16071
- postToParentRef.current({
16072
- type: "ow:section-selected",
16073
- sectionId: sectionEl.dataset.ohwSection ?? null,
16074
- sectionLabel: sectionEl.dataset.ohwSectionLabel ?? titleCaseSectionId(sectionEl.dataset.ohwSection ?? ""),
16075
- key: null
16076
- });
16077
- }
16078
- }, []);
16079
- const clearMediaSelectionRef = (0, import_react17.useRef)(clearMediaSelection);
16080
- clearMediaSelectionRef.current = clearMediaSelection;
16081
- const selectMediaElement = (0, import_react17.useCallback)((el) => {
16082
- const r2 = el.getBoundingClientRect();
16083
- const video = el.dataset.ohwEditable === "video" ? el.querySelector("video") : null;
16084
- selectedMediaElRef.current = el;
16085
- setSelectedMedia({
16086
- key: el.dataset.ohwKey ?? "",
16087
- rect: { top: r2.top, left: r2.left, width: r2.width, height: r2.height },
16088
- elementType: el.dataset.ohwEditable ?? "image",
16089
- hasTextOverlap: false,
16090
- isDragOver: false,
16091
- ...video ? { videoAutoplay: video.autoplay, videoMuted: video.muted } : {}
16092
- });
16093
- const sectionEl = el.closest("[data-ohw-section]");
16094
- aiSectionApiRef.current?.selectFromElement(el, { report: false });
16095
- postToParentRef.current({
16096
- type: "ow:section-selected",
16097
- sectionId: sectionEl?.dataset.ohwSection ?? null,
16098
- sectionLabel: sectionEl ? sectionEl.dataset.ohwSectionLabel ?? titleCaseSectionId(sectionEl.dataset.ohwSection ?? "") : null,
16099
- key: el.dataset.ohwKey ?? null,
16100
- // Display name for the pill — the raw key prettifies into fragments ("Img"); the
16101
- // bridge knows what the node IS, so it names it.
16102
- keyLabel: el.dataset.ohwEditable === "video" ? "Video" : el.dataset.ohwEditable === "bg-image" ? "Background" : "Image"
16103
- });
16104
- }, []);
16105
- const selectMediaElementRef = (0, import_react17.useRef)(selectMediaElement);
16106
- selectMediaElementRef.current = selectMediaElement;
16107
- (0, import_react17.useEffect)(() => {
16108
- if (!selectedMedia) return;
16109
- const update = () => {
16110
- const el = selectedMediaElRef.current;
16111
- if (!el || !el.isConnected) {
16112
- clearMediaSelection();
16113
- return;
16114
- }
16115
- const r2 = el.getBoundingClientRect();
16116
- setSelectedMedia(
16117
- (prev) => prev ? { ...prev, rect: { top: r2.top, left: r2.left, width: r2.width, height: r2.height } } : prev
16118
- );
16119
- };
16120
- window.addEventListener("scroll", update, true);
16121
- window.addEventListener("resize", update);
16122
- return () => {
16123
- window.removeEventListener("scroll", update, true);
16124
- window.removeEventListener("resize", update);
16125
- };
16126
- }, [selectedMedia !== null]);
16127
- const [carouselHover, setCarouselHover] = (0, import_react17.useState)(null);
16128
- const [uploadingRects, setUploadingRects] = (0, import_react17.useState)({});
16129
- const hoveredGapRef = (0, import_react17.useRef)(null);
16130
- const imageUnhoverTimerRef = (0, import_react17.useRef)(null);
16131
- const imageShowTimerRef = (0, import_react17.useRef)(null);
16132
- const editStylesRef = (0, import_react17.useRef)(null);
16133
- const activateRef = (0, import_react17.useRef)(() => {
14899
+ const toolbarElRef = (0, import_react16.useRef)(null);
14900
+ const glowElRef = (0, import_react16.useRef)(null);
14901
+ const hoveredImageRef = (0, import_react16.useRef)(null);
14902
+ const hoveredImageHasTextOverlapRef = (0, import_react16.useRef)(false);
14903
+ const dragOverElRef = (0, import_react16.useRef)(null);
14904
+ const [mediaHover, setMediaHover] = (0, import_react16.useState)(null);
14905
+ const [carouselHover, setCarouselHover] = (0, import_react16.useState)(null);
14906
+ const [uploadingRects, setUploadingRects] = (0, import_react16.useState)({});
14907
+ const hoveredGapRef = (0, import_react16.useRef)(null);
14908
+ const imageUnhoverTimerRef = (0, import_react16.useRef)(null);
14909
+ const imageShowTimerRef = (0, import_react16.useRef)(null);
14910
+ const editStylesRef = (0, import_react16.useRef)(null);
14911
+ const activateRef = (0, import_react16.useRef)(() => {
16134
14912
  });
16135
- const deactivateRef = (0, import_react17.useRef)(() => {
14913
+ const deactivateRef = (0, import_react16.useRef)(() => {
16136
14914
  });
16137
- const selectRef = (0, import_react17.useRef)(() => {
14915
+ const selectRef = (0, import_react16.useRef)(() => {
16138
14916
  });
16139
- const selectFrameRef = (0, import_react17.useRef)(() => {
14917
+ const selectFrameRef = (0, import_react16.useRef)(() => {
16140
14918
  });
16141
- const selectLogoRef = (0, import_react17.useRef)(() => {
14919
+ const selectLogoRef = (0, import_react16.useRef)(() => {
16142
14920
  });
16143
- const openLogoSizePanelRef = (0, import_react17.useRef)(() => {
14921
+ const openLogoSizePanelRef = (0, import_react16.useRef)(() => {
16144
14922
  });
16145
- const deselectRef = (0, import_react17.useRef)(() => {
14923
+ const deselectRef = (0, import_react16.useRef)(() => {
16146
14924
  });
16147
- const closeFloatingPanelOnlyRef = (0, import_react17.useRef)(() => {
14925
+ const closeFloatingPanelOnlyRef = (0, import_react16.useRef)(() => {
16148
14926
  });
16149
- const reselectNavigationItemRef = (0, import_react17.useRef)(() => {
14927
+ const reselectNavigationItemRef = (0, import_react16.useRef)(() => {
16150
14928
  });
16151
- const commitNavigationTextEditRef = (0, import_react17.useRef)(() => {
14929
+ const commitNavigationTextEditRef = (0, import_react16.useRef)(() => {
16152
14930
  });
16153
- const handleDeleteSelectedRef = (0, import_react17.useRef)(() => false);
16154
- const runPendingDeleteUndoRef = (0, import_react17.useRef)(() => false);
16155
- const isFooterFrameSelectionRef = (0, import_react17.useRef)(false);
16156
- const refreshActiveCommandsRef = (0, import_react17.useRef)(() => {
14931
+ const handleDeleteSelectedRef = (0, import_react16.useRef)(() => false);
14932
+ const runPendingDeleteUndoRef = (0, import_react16.useRef)(() => false);
14933
+ const isFooterFrameSelectionRef = (0, import_react16.useRef)(false);
14934
+ const refreshActiveCommandsRef = (0, import_react16.useRef)(() => {
16157
14935
  });
16158
- const postToParentRef = (0, import_react17.useRef)(postToParent2);
14936
+ const postToParentRef = (0, import_react16.useRef)(postToParent2);
16159
14937
  postToParentRef.current = postToParent2;
16160
- const aiSectionApiRef = (0, import_react17.useRef)(null);
16161
- const sectionsLoadedRef = (0, import_react17.useRef)(false);
16162
- const pendingScheduleConfigRequests = (0, import_react17.useRef)([]);
16163
- const [toolbarRect, setToolbarRect] = (0, import_react17.useState)(null);
16164
- const [formPickRect, setFormPickRect] = (0, import_react17.useState)(null);
16165
- const formPickElRef = (0, import_react17.useRef)(null);
16166
- const [formViewState, setFormViewStateUi] = (0, import_react17.useState)("default");
16167
- const [formPickCount, setFormPickCount] = (0, import_react17.useState)(null);
16168
- const [formHoverRect, setFormHoverRect] = (0, import_react17.useState)(null);
16169
- const formHoverElRef = (0, import_react17.useRef)(null);
16170
- const [fieldPickRect, setFieldPickRect] = (0, import_react17.useState)(null);
16171
- const fieldPickElRef = (0, import_react17.useRef)(null);
16172
- const [fieldPickState, setFieldPickState] = (0, import_react17.useState)(null);
16173
- const [fieldTypePickerOpen, setFieldTypePickerOpen] = (0, import_react17.useState)(false);
16174
- const clearFormPick = (0, import_react17.useCallback)(() => {
14938
+ const aiSectionApiRef = (0, import_react16.useRef)(null);
14939
+ const sectionsLoadedRef = (0, import_react16.useRef)(false);
14940
+ const pendingScheduleConfigRequests = (0, import_react16.useRef)([]);
14941
+ const [toolbarRect, setToolbarRect] = (0, import_react16.useState)(null);
14942
+ const [formPickRect, setFormPickRect] = (0, import_react16.useState)(null);
14943
+ const formPickElRef = (0, import_react16.useRef)(null);
14944
+ const [formViewState, setFormViewStateUi] = (0, import_react16.useState)("default");
14945
+ const [formPickCount, setFormPickCount] = (0, import_react16.useState)(null);
14946
+ const [formHoverRect, setFormHoverRect] = (0, import_react16.useState)(null);
14947
+ const formHoverElRef = (0, import_react16.useRef)(null);
14948
+ const [fieldPickRect, setFieldPickRect] = (0, import_react16.useState)(null);
14949
+ const fieldPickElRef = (0, import_react16.useRef)(null);
14950
+ const [fieldPickState, setFieldPickState] = (0, import_react16.useState)(null);
14951
+ const [fieldTypePickerOpen, setFieldTypePickerOpen] = (0, import_react16.useState)(false);
14952
+ const clearFormPick = (0, import_react16.useCallback)(() => {
16175
14953
  const form = formPickElRef.current;
16176
14954
  const editing = fieldPickElRef.current;
16177
14955
  if (commitPlaceholderEdit(editing) && editing) {
@@ -16191,7 +14969,7 @@ function OhhwellsBridge() {
16191
14969
  formPickElRef.current = null;
16192
14970
  setFormPickRect(null);
16193
14971
  }, []);
16194
- const clearFieldPick = (0, import_react17.useCallback)(() => {
14972
+ const clearFieldPick = (0, import_react16.useCallback)(() => {
16195
14973
  const wrapper = fieldPickElRef.current;
16196
14974
  if (commitPlaceholderEdit(wrapper) && wrapper) {
16197
14975
  const form = wrapper.closest('[data-ohw-editable="form"]');
@@ -16201,9 +14979,9 @@ function OhhwellsBridge() {
16201
14979
  setFieldPickRect(null);
16202
14980
  setFieldPickState(null);
16203
14981
  }, []);
16204
- const persistFieldsRef = (0, import_react17.useRef)(() => {
14982
+ const persistFieldsRef = (0, import_react16.useRef)(() => {
16205
14983
  });
16206
- const persistFields = (0, import_react17.useCallback)(
14984
+ const persistFields = (0, import_react16.useCallback)(
16207
14985
  (form) => {
16208
14986
  const key = formKeyOf(form);
16209
14987
  if (!key) return;
@@ -16214,7 +14992,7 @@ function OhhwellsBridge() {
16214
14992
  []
16215
14993
  );
16216
14994
  persistFieldsRef.current = persistFields;
16217
- const selectField = (0, import_react17.useCallback)((wrapper) => {
14995
+ const selectField = (0, import_react16.useCallback)((wrapper) => {
16218
14996
  if (fieldPickElRef.current && fieldPickElRef.current !== wrapper) {
16219
14997
  commitPlaceholderEdit(fieldPickElRef.current);
16220
14998
  }
@@ -16227,7 +15005,7 @@ function OhhwellsBridge() {
16227
15005
  setFieldPickState({ type: fieldTypeOf(wrapper), required: isFieldRequired(wrapper) });
16228
15006
  setFieldTypePickerOpen(false);
16229
15007
  }, []);
16230
- const withSelectedField = (0, import_react17.useCallback)(
15008
+ const withSelectedField = (0, import_react16.useCallback)(
16231
15009
  (run) => {
16232
15010
  const wrapper = fieldPickElRef.current;
16233
15011
  const form = formPickElRef.current;
@@ -16240,28 +15018,28 @@ function OhhwellsBridge() {
16240
15018
  },
16241
15019
  [persistFields]
16242
15020
  );
16243
- const handleFieldTypeChange = (0, import_react17.useCallback)(
15021
+ const handleFieldTypeChange = (0, import_react16.useCallback)(
16244
15022
  (type) => withSelectedField((_form, wrapper) => {
16245
15023
  applyFieldType(wrapper, type);
16246
15024
  selectField(wrapper);
16247
15025
  }),
16248
15026
  [selectField, withSelectedField]
16249
15027
  );
16250
- const handleFieldRequiredToggle = (0, import_react17.useCallback)(
15028
+ const handleFieldRequiredToggle = (0, import_react16.useCallback)(
16251
15029
  () => withSelectedField((_form, wrapper) => {
16252
15030
  setFieldRequired(wrapper, !isFieldRequired(wrapper));
16253
15031
  selectField(wrapper);
16254
15032
  }),
16255
15033
  [selectField, withSelectedField]
16256
15034
  );
16257
- const handleFieldDuplicate = (0, import_react17.useCallback)(
15035
+ const handleFieldDuplicate = (0, import_react16.useCallback)(
16258
15036
  () => withSelectedField((form, wrapper) => {
16259
15037
  const copy = duplicateField(form, wrapper);
16260
15038
  selectField(copy);
16261
15039
  }),
16262
15040
  [selectField, withSelectedField]
16263
15041
  );
16264
- const handleFieldDelete = (0, import_react17.useCallback)(
15042
+ const handleFieldDelete = (0, import_react16.useCallback)(
16265
15043
  () => withSelectedField((_form, wrapper) => {
16266
15044
  removeField(wrapper);
16267
15045
  clearFieldPick();
@@ -16269,7 +15047,7 @@ function OhhwellsBridge() {
16269
15047
  }),
16270
15048
  [clearFieldPick, withSelectedField]
16271
15049
  );
16272
- const handleAddField = (0, import_react17.useCallback)(
15050
+ const handleAddField = (0, import_react16.useCallback)(
16273
15051
  (type) => {
16274
15052
  const form = formPickElRef.current;
16275
15053
  if (!form) return;
@@ -16285,8 +15063,8 @@ function OhhwellsBridge() {
16285
15063
  },
16286
15064
  [persistFields, selectField]
16287
15065
  );
16288
- const fieldDragRef = (0, import_react17.useRef)(null);
16289
- const buildFieldDropSlots = (0, import_react17.useCallback)((form, draggedKey) => {
15066
+ const fieldDragRef = (0, import_react16.useRef)(null);
15067
+ const buildFieldDropSlots = (0, import_react16.useCallback)((form, draggedKey) => {
16290
15068
  const others = listFieldWrappers(form).filter((el) => fieldKeyOf(el) !== draggedKey);
16291
15069
  const slots = others.map((el) => {
16292
15070
  const rect = el.getBoundingClientRect();
@@ -16299,7 +15077,7 @@ function OhhwellsBridge() {
16299
15077
  }
16300
15078
  return slots;
16301
15079
  }, []);
16302
- const handleFieldDragStart = (0, import_react17.useCallback)(() => {
15080
+ const handleFieldDragStart = (0, import_react16.useCallback)(() => {
16303
15081
  const wrapper = fieldPickElRef.current;
16304
15082
  const form = formPickElRef.current;
16305
15083
  if (!wrapper || !form) return;
@@ -16308,18 +15086,18 @@ function OhhwellsBridge() {
16308
15086
  setFieldDragging(true);
16309
15087
  setFieldDropSlots(buildFieldDropSlots(form, key));
16310
15088
  }, [buildFieldDropSlots]);
16311
- const handleFieldDragEnd = (0, import_react17.useCallback)(() => {
15089
+ const handleFieldDragEnd = (0, import_react16.useCallback)(() => {
16312
15090
  fieldDragRef.current = null;
16313
15091
  setFieldDropIndex(null);
16314
15092
  setFieldDropSlots([]);
16315
15093
  setFieldDragging(false);
16316
15094
  }, []);
16317
- const [fieldDropIndex, setFieldDropIndex] = (0, import_react17.useState)(null);
16318
- const [fieldDropSlots, setFieldDropSlots] = (0, import_react17.useState)([]);
16319
- const [fieldDragging, setFieldDragging] = (0, import_react17.useState)(false);
16320
- const clearFormPickRef = (0, import_react17.useRef)(clearFormPick);
15095
+ const [fieldDropIndex, setFieldDropIndex] = (0, import_react16.useState)(null);
15096
+ const [fieldDropSlots, setFieldDropSlots] = (0, import_react16.useState)([]);
15097
+ const [fieldDragging, setFieldDragging] = (0, import_react16.useState)(false);
15098
+ const clearFormPickRef = (0, import_react16.useRef)(clearFormPick);
16321
15099
  clearFormPickRef.current = clearFormPick;
16322
- (0, import_react17.useEffect)(() => {
15100
+ (0, import_react16.useEffect)(() => {
16323
15101
  const el = fieldPickElRef.current;
16324
15102
  if (!el || fieldPickRect === null) return;
16325
15103
  const observer = new ResizeObserver(() => {
@@ -16328,7 +15106,7 @@ function OhhwellsBridge() {
16328
15106
  observer.observe(el);
16329
15107
  return () => observer.disconnect();
16330
15108
  }, [fieldPickRect !== null, fieldPickState]);
16331
- (0, import_react17.useEffect)(() => {
15109
+ (0, import_react16.useEffect)(() => {
16332
15110
  const el = formPickElRef.current;
16333
15111
  if (!el || formPickRect === null) return;
16334
15112
  const observer = new ResizeObserver(() => {
@@ -16337,25 +15115,25 @@ function OhhwellsBridge() {
16337
15115
  observer.observe(el);
16338
15116
  return () => observer.disconnect();
16339
15117
  }, [formPickRect !== null, formViewState]);
16340
- const [toolbarVariant, setToolbarVariant] = (0, import_react17.useState)("none");
16341
- const toolbarVariantRef = (0, import_react17.useRef)("none");
15118
+ const [toolbarVariant, setToolbarVariant] = (0, import_react16.useState)("none");
15119
+ const toolbarVariantRef = (0, import_react16.useRef)("none");
16342
15120
  toolbarVariantRef.current = toolbarVariant;
16343
- const [selectedIsCta, setSelectedIsCta] = (0, import_react17.useState)(false);
16344
- const [selectedIsSocial, setSelectedIsSocial] = (0, import_react17.useState)(false);
16345
- const [selectedIsSocialsRow, setSelectedIsSocialsRow] = (0, import_react17.useState)(false);
16346
- const [reorderHrefKey, setReorderHrefKey] = (0, import_react17.useState)(null);
16347
- const [reorderDragDisabled, setReorderDragDisabled] = (0, import_react17.useState)(false);
16348
- const [toggleState, setToggleState] = (0, import_react17.useState)(null);
16349
- const [maxBadge, setMaxBadge] = (0, import_react17.useState)(null);
16350
- const [activeCommands, setActiveCommands] = (0, import_react17.useState)(/* @__PURE__ */ new Set());
16351
- const [sectionGap, setSectionGap] = (0, import_react17.useState)(null);
16352
- const [toolbarShowEditLink, setToolbarShowEditLink] = (0, import_react17.useState)(false);
16353
- const hoveredNavContainerRef = (0, import_react17.useRef)(null);
16354
- const [hoveredNavContainerRect, setHoveredNavContainerRect] = (0, import_react17.useState)(null);
16355
- const hoveredItemElRef = (0, import_react17.useRef)(null);
16356
- const [hoveredItemRect, setHoveredItemRect] = (0, import_react17.useState)(null);
16357
- const [hoveredTextRect, setHoveredTextRect] = (0, import_react17.useState)(null);
16358
- (0, import_react17.useEffect)(() => {
15121
+ const [selectedIsCta, setSelectedIsCta] = (0, import_react16.useState)(false);
15122
+ const [selectedIsSocial, setSelectedIsSocial] = (0, import_react16.useState)(false);
15123
+ const [selectedIsSocialsRow, setSelectedIsSocialsRow] = (0, import_react16.useState)(false);
15124
+ const [reorderHrefKey, setReorderHrefKey] = (0, import_react16.useState)(null);
15125
+ const [reorderDragDisabled, setReorderDragDisabled] = (0, import_react16.useState)(false);
15126
+ const [toggleState, setToggleState] = (0, import_react16.useState)(null);
15127
+ const [maxBadge, setMaxBadge] = (0, import_react16.useState)(null);
15128
+ const [activeCommands, setActiveCommands] = (0, import_react16.useState)(/* @__PURE__ */ new Set());
15129
+ const [sectionGap, setSectionGap] = (0, import_react16.useState)(null);
15130
+ const [toolbarShowEditLink, setToolbarShowEditLink] = (0, import_react16.useState)(false);
15131
+ const hoveredNavContainerRef = (0, import_react16.useRef)(null);
15132
+ const [hoveredNavContainerRect, setHoveredNavContainerRect] = (0, import_react16.useState)(null);
15133
+ const hoveredItemElRef = (0, import_react16.useRef)(null);
15134
+ const [hoveredItemRect, setHoveredItemRect] = (0, import_react16.useState)(null);
15135
+ const [hoveredTextRect, setHoveredTextRect] = (0, import_react16.useState)(null);
15136
+ (0, import_react16.useEffect)(() => {
16359
15137
  const sync = () => {
16360
15138
  const el = document.querySelector(
16361
15139
  '[data-ohw-hovered]:not([contenteditable]):not([data-ohw-href-key]):not([data-ohw-editable="form"] *)'
@@ -16376,56 +15154,43 @@ function OhhwellsBridge() {
16376
15154
  });
16377
15155
  return () => observer.disconnect();
16378
15156
  }, []);
16379
- const siblingHintElRef = (0, import_react17.useRef)(null);
16380
- const [siblingHintRect, setSiblingHintRect] = (0, import_react17.useState)(null);
16381
- const [siblingHintRects, setSiblingHintRects] = (0, import_react17.useState)([]);
16382
- const [isItemDragging, setIsItemDragging] = (0, import_react17.useState)(false);
16383
- const [isFooterFrameSelection, setIsFooterFrameSelection] = (0, import_react17.useState)(false);
15157
+ const siblingHintElRef = (0, import_react16.useRef)(null);
15158
+ const [siblingHintRect, setSiblingHintRect] = (0, import_react16.useState)(null);
15159
+ const [siblingHintRects, setSiblingHintRects] = (0, import_react16.useState)([]);
15160
+ const [isItemDragging, setIsItemDragging] = (0, import_react16.useState)(false);
15161
+ const [isFooterFrameSelection, setIsFooterFrameSelection] = (0, import_react16.useState)(false);
16384
15162
  isFooterFrameSelectionRef.current = isFooterFrameSelection;
16385
- const [navDropdownPreviewOpen, setNavDropdownPreviewOpen] = (0, import_react17.useState)(null);
16386
- const [footerHeadingVisible, setFooterHeadingVisible] = (0, import_react17.useState)(null);
16387
- const footerDragRef = (0, import_react17.useRef)(null);
16388
- const [footerDropSlots, setFooterDropSlots] = (0, import_react17.useState)([]);
16389
- const [activeFooterDropIndex, setActiveFooterDropIndex] = (0, import_react17.useState)(null);
16390
- const [draggedItemRect, setDraggedItemRect] = (0, import_react17.useState)(null);
16391
- const footerPointerDragRef = (0, import_react17.useRef)(null);
16392
- const suppressNextClickRef = (0, import_react17.useRef)(false);
16393
- const suppressClickUntilRef = (0, import_react17.useRef)(0);
16394
- const [linkPopover, setLinkPopover] = (0, import_react17.useState)(null);
16395
- const linkPopoverSessionRef = (0, import_react17.useRef)(null);
16396
- const addNavAfterAnchorRef = (0, import_react17.useRef)(null);
16397
- const editContentRef = (0, import_react17.useRef)({});
16398
- const aiSectionsRef = (0, import_react17.useRef)("");
16399
- const brandKitRef = (0, import_react17.useRef)("");
16400
- const stylesRef = (0, import_react17.useRef)("");
16401
- const pendingDeleteUndoRef = (0, import_react17.useRef)(null);
16402
- const [floatingPanel, setFloatingPanel] = (0, import_react17.useState)(null);
16403
- const floatingPanelOpenRef = (0, import_react17.useRef)(false);
16404
- const setFloatingPanelRef = (0, import_react17.useRef)(setFloatingPanel);
16405
- const [floatingPanelPos, setFloatingPanelPos] = (0, import_react17.useState)(null);
16406
- const [logoSizeDraft, setLogoSizeDraft] = (0, import_react17.useState)(null);
16407
- const [editorViewport, setEditorViewport] = (0, import_react17.useState)("desktop");
16408
- const [parentScrollSnap, setParentScrollSnap] = (0, import_react17.useState)(null);
16409
- const [sitePages, setSitePages] = (0, import_react17.useState)([]);
16410
- const [sectionsByPath, setSectionsByPath] = (0, import_react17.useState)({});
16411
- const sectionsPrefetchGenRef = (0, import_react17.useRef)(0);
16412
- const setLinkPopoverRef = (0, import_react17.useRef)(setLinkPopover);
16413
- const linkPopoverPanelRef = (0, import_react17.useRef)(null);
16414
- const linkPopoverOpenRef = (0, import_react17.useRef)(false);
16415
- const linkPopoverGraceUntilRef = (0, import_react17.useRef)(0);
15163
+ const [floatingPanel, setFloatingPanel] = (0, import_react16.useState)(null);
15164
+ const floatingPanelOpenRef = (0, import_react16.useRef)(false);
15165
+ floatingPanelOpenRef.current = floatingPanel !== null;
15166
+ const [floatingPanelPos, setFloatingPanelPos] = (0, import_react16.useState)(null);
15167
+ const [logoSizeDraft, setLogoSizeDraft] = (0, import_react16.useState)(null);
15168
+ const [editorViewport, setEditorViewport] = (0, import_react16.useState)("desktop");
15169
+ const [parentScrollSnap, setParentScrollSnap] = (0, import_react16.useState)(null);
15170
+ const [navDropdownPreviewOpen, setNavDropdownPreviewOpen] = (0, import_react16.useState)(null);
15171
+ const [footerHeadingVisible, setFooterHeadingVisible] = (0, import_react16.useState)(null);
15172
+ const footerDragRef = (0, import_react16.useRef)(null);
15173
+ const [footerDropSlots, setFooterDropSlots] = (0, import_react16.useState)([]);
15174
+ const [activeFooterDropIndex, setActiveFooterDropIndex] = (0, import_react16.useState)(null);
15175
+ const [draggedItemRect, setDraggedItemRect] = (0, import_react16.useState)(null);
15176
+ const footerPointerDragRef = (0, import_react16.useRef)(null);
15177
+ const suppressNextClickRef = (0, import_react16.useRef)(false);
15178
+ const suppressClickUntilRef = (0, import_react16.useRef)(0);
15179
+ const [linkPopover, setLinkPopover] = (0, import_react16.useState)(null);
15180
+ const linkPopoverSessionRef = (0, import_react16.useRef)(null);
15181
+ const addNavAfterAnchorRef = (0, import_react16.useRef)(null);
15182
+ const editContentRef = (0, import_react16.useRef)({});
15183
+ const aiSectionsRef = (0, import_react16.useRef)("");
15184
+ const pendingDeleteUndoRef = (0, import_react16.useRef)(null);
15185
+ const [sitePages, setSitePages] = (0, import_react16.useState)([]);
15186
+ const [sectionsByPath, setSectionsByPath] = (0, import_react16.useState)({});
15187
+ const sectionsPrefetchGenRef = (0, import_react16.useRef)(0);
15188
+ const setLinkPopoverRef = (0, import_react16.useRef)(setLinkPopover);
15189
+ const linkPopoverPanelRef = (0, import_react16.useRef)(null);
15190
+ const linkPopoverOpenRef = (0, import_react16.useRef)(false);
15191
+ const linkPopoverGraceUntilRef = (0, import_react16.useRef)(0);
16416
15192
  setLinkPopoverRef.current = setLinkPopover;
16417
- setFloatingPanelRef.current = setFloatingPanel;
16418
15193
  linkPopoverSessionRef.current = linkPopover;
16419
- floatingPanelOpenRef.current = Boolean(floatingPanel);
16420
- (0, import_react17.useEffect)(() => {
16421
- const syncViewport = () => {
16422
- const next = window.innerWidth <= 480 ? "mobile" : "desktop";
16423
- setEditorViewport((prev) => prev === next ? prev : next);
16424
- };
16425
- syncViewport();
16426
- window.addEventListener("resize", syncViewport);
16427
- return () => window.removeEventListener("resize", syncViewport);
16428
- }, []);
16429
15194
  const {
16430
15195
  navDragRef,
16431
15196
  navDropSlots,
@@ -16458,20 +15223,10 @@ function OhhwellsBridge() {
16458
15223
  getNavigationItemAnchor,
16459
15224
  isDragHandleDisabled
16460
15225
  });
16461
- const { sectionDropSlots, activeSectionDropIndex, isSectionDragging } = useSectionDrag({
16462
- isEditMode,
16463
- editContentRef,
16464
- postToParentRef,
16465
- parentScrollRef,
16466
- navDragRef,
16467
- footerDragRef,
16468
- suppressNextClickRef,
16469
- suppressClickUntilRef
16470
- });
16471
15226
  const bumpLinkPopoverGrace = () => {
16472
15227
  linkPopoverGraceUntilRef.current = Date.now() + 350;
16473
15228
  };
16474
- const runSectionsPrefetch = (0, import_react17.useCallback)((pages) => {
15229
+ const runSectionsPrefetch = (0, import_react16.useCallback)((pages) => {
16475
15230
  if (!isEditMode || shouldUseDevFixtures() || pages.length === 0) return;
16476
15231
  const gen = ++sectionsPrefetchGenRef.current;
16477
15232
  const paths = pages.map((p) => p.path);
@@ -16490,9 +15245,9 @@ function OhhwellsBridge() {
16490
15245
  );
16491
15246
  });
16492
15247
  }, [isEditMode, pathname]);
16493
- const runSectionsPrefetchRef = (0, import_react17.useRef)(runSectionsPrefetch);
15248
+ const runSectionsPrefetchRef = (0, import_react16.useRef)(runSectionsPrefetch);
16494
15249
  runSectionsPrefetchRef.current = runSectionsPrefetch;
16495
- (0, import_react17.useEffect)(() => {
15250
+ (0, import_react16.useEffect)(() => {
16496
15251
  if (!linkPopover) {
16497
15252
  document.documentElement.removeAttribute("data-ohw-link-popover-open");
16498
15253
  return;
@@ -16520,7 +15275,7 @@ function OhhwellsBridge() {
16520
15275
  document.documentElement.removeAttribute("data-ohw-link-popover-open");
16521
15276
  };
16522
15277
  }, [linkPopover, postToParent2]);
16523
- (0, import_react17.useEffect)(() => {
15278
+ (0, import_react16.useEffect)(() => {
16524
15279
  if (!isEditMode) return;
16525
15280
  const useFixtures = shouldUseDevFixtures();
16526
15281
  if (useFixtures) {
@@ -16544,14 +15299,14 @@ function OhhwellsBridge() {
16544
15299
  if (!useFixtures) postToParent2({ type: "ow:request-site-pages" });
16545
15300
  return () => window.removeEventListener("message", onSitePages);
16546
15301
  }, [isEditMode, postToParent2]);
16547
- (0, import_react17.useEffect)(() => {
15302
+ (0, import_react16.useEffect)(() => {
16548
15303
  if (!isEditMode || shouldUseDevFixtures()) return;
16549
15304
  void loadAllSectionsManifest().then((manifest) => {
16550
15305
  if (Object.keys(manifest).length === 0) return;
16551
15306
  setSectionsByPath((prev) => ({ ...manifest, ...prev }));
16552
15307
  });
16553
15308
  }, [isEditMode]);
16554
- (0, import_react17.useEffect)(() => {
15309
+ (0, import_react16.useEffect)(() => {
16555
15310
  const update = () => {
16556
15311
  const el = activeElRef.current ?? selectedElRef.current;
16557
15312
  if (el) setToolbarRect(getEditMeasureEl(el).getBoundingClientRect());
@@ -16575,10 +15330,10 @@ function OhhwellsBridge() {
16575
15330
  vvp.removeEventListener("resize", update);
16576
15331
  };
16577
15332
  }, []);
16578
- const refreshStateRules = (0, import_react17.useCallback)(() => {
15333
+ const refreshStateRules = (0, import_react16.useCallback)(() => {
16579
15334
  editStylesRef.current?.forceHover && (editStylesRef.current.forceHover.textContent = collectStateRules());
16580
15335
  }, []);
16581
- const processConfigRequest = (0, import_react17.useCallback)((insertAfterVal) => {
15336
+ const processConfigRequest = (0, import_react16.useCallback)((insertAfterVal) => {
16582
15337
  const tracker = getSectionsTracker();
16583
15338
  let entries = [];
16584
15339
  try {
@@ -16601,7 +15356,7 @@ function OhhwellsBridge() {
16601
15356
  }
16602
15357
  window.postMessage({ type: "ow:schedule-config", insertAfter: insertAfterVal, scheduleId: null }, "*");
16603
15358
  }, [isEditMode]);
16604
- const deactivate = (0, import_react17.useCallback)(() => {
15359
+ const deactivate = (0, import_react16.useCallback)(() => {
16605
15360
  const el = activeElRef.current;
16606
15361
  if (!el) return;
16607
15362
  const isFormBlock = el.dataset.ohwEditable === "form";
@@ -16617,7 +15372,7 @@ function OhhwellsBridge() {
16617
15372
  const original = originalContentRef.current ?? "";
16618
15373
  if (html !== sanitizeHtml(original)) {
16619
15374
  postToParentRef.current({ type: "ow:change", nodes: [{ key, text: html }] });
16620
- const h = document.body.scrollHeight;
15375
+ const h = document.documentElement.scrollHeight;
16621
15376
  if (h > 50) postToParentRef.current({ type: "ow:height", height: h });
16622
15377
  }
16623
15378
  }
@@ -16642,12 +15397,12 @@ function OhhwellsBridge() {
16642
15397
  setToolbarShowEditLink(false);
16643
15398
  postToParent2({ type: "ow:exit-edit" });
16644
15399
  }, [postToParent2]);
16645
- const clearSelectedAttr = (0, import_react17.useCallback)(() => {
15400
+ const clearSelectedAttr = (0, import_react16.useCallback)(() => {
16646
15401
  document.querySelectorAll("[data-ohw-selected]").forEach((el) => {
16647
15402
  el.removeAttribute("data-ohw-selected");
16648
15403
  });
16649
15404
  }, []);
16650
- const deselect = (0, import_react17.useCallback)(() => {
15405
+ const deselect = (0, import_react16.useCallback)(() => {
16651
15406
  clearSelectedAttr();
16652
15407
  selectedElRef.current = null;
16653
15408
  selectedHrefKeyRef.current = null;
@@ -16676,12 +15431,12 @@ function OhhwellsBridge() {
16676
15431
  setToolbarVariant("none");
16677
15432
  }
16678
15433
  }, [clearSelectedAttr]);
16679
- const markSelected = (0, import_react17.useCallback)((el) => {
15434
+ const markSelected = (0, import_react16.useCallback)((el) => {
16680
15435
  clearSelectedAttr();
16681
15436
  el.removeAttribute("data-ohw-hovered");
16682
15437
  el.setAttribute("data-ohw-selected", "");
16683
15438
  }, [clearSelectedAttr]);
16684
- const resolveHrefKeyElement = (0, import_react17.useCallback)((hrefKey) => {
15439
+ const resolveHrefKeyElement = (0, import_react16.useCallback)((hrefKey) => {
16685
15440
  if (isFooterHrefKey(hrefKey)) {
16686
15441
  return document.querySelector(
16687
15442
  `footer [data-ohw-href-key="${CSS.escape(hrefKey)}"]`
@@ -16696,7 +15451,7 @@ function OhhwellsBridge() {
16696
15451
  `[data-ohw-href-key="${CSS.escape(hrefKey)}"]`
16697
15452
  );
16698
15453
  }, []);
16699
- const resyncSelectedNavigationItem = (0, import_react17.useCallback)(() => {
15454
+ const resyncSelectedNavigationItem = (0, import_react16.useCallback)(() => {
16700
15455
  const hrefKey = selectedHrefKeyRef.current;
16701
15456
  if (hrefKey) {
16702
15457
  const link = resolveHrefKeyElement(hrefKey);
@@ -16734,7 +15489,7 @@ function OhhwellsBridge() {
16734
15489
  );
16735
15490
  }
16736
15491
  }, [resolveHrefKeyElement]);
16737
- const reselectNavigationItem = (0, import_react17.useCallback)((navAnchor) => {
15492
+ const reselectNavigationItem = (0, import_react16.useCallback)((navAnchor) => {
16738
15493
  selectedElRef.current = navAnchor;
16739
15494
  selectedHrefKeyRef.current = navAnchor.getAttribute("data-ohw-href-key");
16740
15495
  selectedFooterColAttrRef.current = null;
@@ -16765,7 +15520,7 @@ function OhhwellsBridge() {
16765
15520
  setToolbarShowEditLink(false);
16766
15521
  setActiveCommands(/* @__PURE__ */ new Set());
16767
15522
  }, [markSelected]);
16768
- const commitNavigationTextEdit = (0, import_react17.useCallback)((navAnchor) => {
15523
+ const commitNavigationTextEdit = (0, import_react16.useCallback)((navAnchor) => {
16769
15524
  const el = activeElRef.current;
16770
15525
  if (!el) return;
16771
15526
  const key = el.dataset.ohwKey;
@@ -16779,7 +15534,7 @@ function OhhwellsBridge() {
16779
15534
  const original = originalContentRef.current ?? "";
16780
15535
  if (html !== sanitizeHtml(original)) {
16781
15536
  postToParent2({ type: "ow:change", nodes: [{ key, text: html }] });
16782
- const h = document.body.scrollHeight;
15537
+ const h = document.documentElement.scrollHeight;
16783
15538
  if (h > 50) postToParent2({ type: "ow:height", height: h });
16784
15539
  }
16785
15540
  }
@@ -16798,7 +15553,7 @@ function OhhwellsBridge() {
16798
15553
  postToParent2({ type: "ow:exit-edit" });
16799
15554
  reselectNavigationItem(navAnchor);
16800
15555
  }, [postToParent2, reselectNavigationItem]);
16801
- const handleAddTopLevelNavItem = (0, import_react17.useCallback)(() => {
15556
+ const handleAddTopLevelNavItem = (0, import_react16.useCallback)(() => {
16802
15557
  const items = listNavbarRootItems();
16803
15558
  addNavAfterAnchorRef.current = items[items.length - 1] ?? null;
16804
15559
  deselectRef.current();
@@ -16810,7 +15565,7 @@ function OhhwellsBridge() {
16810
15565
  intent: "add-nav"
16811
15566
  });
16812
15567
  }, []);
16813
- const maybeWarnNavLinkDropdownConflict = (0, import_react17.useCallback)(
15568
+ const maybeWarnNavLinkDropdownConflict = (0, import_react16.useCallback)(
16814
15569
  (anchor) => {
16815
15570
  if (!isNavbarHrefKey(anchor.getAttribute("data-ohw-href-key"))) return;
16816
15571
  if (!navDropdownsOpenOnClick()) return;
@@ -16823,7 +15578,7 @@ function OhhwellsBridge() {
16823
15578
  },
16824
15579
  [postToParent2]
16825
15580
  );
16826
- const handleNavDropdownOpenChange = (0, import_react17.useCallback)((open) => {
15581
+ const handleNavDropdownOpenChange = (0, import_react16.useCallback)((open) => {
16827
15582
  const selected = selectedElRef.current;
16828
15583
  if (!selected || !isNavigationItem2(selected)) return;
16829
15584
  setNavGroupForceOpen(selected, open);
@@ -16835,7 +15590,7 @@ function OhhwellsBridge() {
16835
15590
  }
16836
15591
  });
16837
15592
  }, []);
16838
- const handleFooterHeadingVisibleChange = (0, import_react17.useCallback)(
15593
+ const handleFooterHeadingVisibleChange = (0, import_react16.useCallback)(
16839
15594
  (visible) => {
16840
15595
  const selected = selectedElRef.current;
16841
15596
  if (!selected || !isFooterFrameSelectionRef.current) return;
@@ -16859,7 +15614,7 @@ function OhhwellsBridge() {
16859
15614
  },
16860
15615
  [postToParent2]
16861
15616
  );
16862
- const enterEditOnNewItem = (0, import_react17.useCallback)((anchor) => {
15617
+ const enterEditOnNewItem = (0, import_react16.useCallback)((anchor) => {
16863
15618
  const label = anchor.querySelector('[data-ohw-editable="text"]');
16864
15619
  if (!label) {
16865
15620
  selectRef.current(anchor);
@@ -16868,8 +15623,8 @@ function OhhwellsBridge() {
16868
15623
  setNavGroupForceOpen(anchor, true);
16869
15624
  activateRef.current(label);
16870
15625
  }, []);
16871
- const pendingSocialAddRef = (0, import_react17.useRef)(null);
16872
- const handleAddChildItem = (0, import_react17.useCallback)(() => {
15626
+ const pendingSocialAddRef = (0, import_react16.useRef)(null);
15627
+ const handleAddChildItem = (0, import_react16.useCallback)(() => {
16873
15628
  const selected = selectedElRef.current;
16874
15629
  if (!selected) return;
16875
15630
  const socialsRow = isSocialsRow(selected) ? selected : findSocialsRow(selected);
@@ -16978,7 +15733,7 @@ function OhhwellsBridge() {
16978
15733
  enterEditOnNewItem(result.anchor);
16979
15734
  });
16980
15735
  }, [enterEditOnNewItem, isFooterFrameSelection, maybeWarnNavLinkDropdownConflict, postToParent2]);
16981
- const handleAddFooterColumn = (0, import_react17.useCallback)(() => {
15736
+ const handleAddFooterColumn = (0, import_react16.useCallback)(() => {
16982
15737
  if (!canAddFooterColumn()) {
16983
15738
  postToParent2({
16984
15739
  type: "ow:toast",
@@ -16999,7 +15754,7 @@ function OhhwellsBridge() {
16999
15754
  selectRef.current(result.firstLink);
17000
15755
  });
17001
15756
  }, [postToParent2]);
17002
- const clearFooterDragVisuals = (0, import_react17.useCallback)(() => {
15757
+ const clearFooterDragVisuals = (0, import_react16.useCallback)(() => {
17003
15758
  footerDragRef.current = null;
17004
15759
  setSiblingHintRects([]);
17005
15760
  setFooterDropSlots([]);
@@ -17008,7 +15763,7 @@ function OhhwellsBridge() {
17008
15763
  setIsItemDragging(false);
17009
15764
  unlockFooterDragInteraction();
17010
15765
  }, []);
17011
- const refreshFooterDragVisuals = (0, import_react17.useCallback)((session, activeSlot, clientX, clientY) => {
15766
+ const refreshFooterDragVisuals = (0, import_react16.useCallback)((session, activeSlot, clientX, clientY) => {
17012
15767
  const dragged = session.draggedEl;
17013
15768
  setDraggedItemRect(dragged.getBoundingClientRect());
17014
15769
  if (typeof clientX === "number" && typeof clientY === "number") {
@@ -17040,13 +15795,13 @@ function OhhwellsBridge() {
17040
15795
  const activeIdx = activeSlot ? slots.findIndex((s) => s.insertIndex === activeSlot.insertIndex) : -1;
17041
15796
  setActiveFooterDropIndex(activeIdx >= 0 ? activeIdx : null);
17042
15797
  }, []);
17043
- const refreshFooterDragVisualsRef = (0, import_react17.useRef)(refreshFooterDragVisuals);
15798
+ const refreshFooterDragVisualsRef = (0, import_react16.useRef)(refreshFooterDragVisuals);
17044
15799
  refreshFooterDragVisualsRef.current = refreshFooterDragVisuals;
17045
- const commitFooterDragRef = (0, import_react17.useRef)(() => {
15800
+ const commitFooterDragRef = (0, import_react16.useRef)(() => {
17046
15801
  });
17047
- const beginFooterDragRef = (0, import_react17.useRef)(() => {
15802
+ const beginFooterDragRef = (0, import_react16.useRef)(() => {
17048
15803
  });
17049
- const beginFooterDrag = (0, import_react17.useCallback)(
15804
+ const beginFooterDrag = (0, import_react16.useCallback)(
17050
15805
  (session) => {
17051
15806
  const rect = session.draggedEl.getBoundingClientRect();
17052
15807
  session.lastClientX = session.lastClientX || rect.left + rect.width / 2;
@@ -17066,7 +15821,7 @@ function OhhwellsBridge() {
17066
15821
  [refreshFooterDragVisuals]
17067
15822
  );
17068
15823
  beginFooterDragRef.current = beginFooterDrag;
17069
- const commitFooterDrag = (0, import_react17.useCallback)(
15824
+ const commitFooterDrag = (0, import_react16.useCallback)(
17070
15825
  (clientX, clientY) => {
17071
15826
  const session = footerDragRef.current;
17072
15827
  if (!session) {
@@ -17195,7 +15950,7 @@ function OhhwellsBridge() {
17195
15950
  [clearFooterDragVisuals, resolveHrefKeyElement, resyncSelectedNavigationItem]
17196
15951
  );
17197
15952
  commitFooterDragRef.current = commitFooterDrag;
17198
- const startFooterLinkDrag = (0, import_react17.useCallback)(
15953
+ const startFooterLinkDrag = (0, import_react16.useCallback)(
17199
15954
  (anchor, clientX, clientY, wasSelected) => {
17200
15955
  const hrefKey = anchor.getAttribute("data-ohw-href-key");
17201
15956
  if (!hrefKey) return false;
@@ -17231,7 +15986,7 @@ function OhhwellsBridge() {
17231
15986
  },
17232
15987
  [beginFooterDrag]
17233
15988
  );
17234
- const startFooterColumnDrag = (0, import_react17.useCallback)(
15989
+ const startFooterColumnDrag = (0, import_react16.useCallback)(
17235
15990
  (columnEl, clientX, clientY, wasSelected) => {
17236
15991
  const columns = listFooterColumns();
17237
15992
  const idx = columns.indexOf(columnEl);
@@ -17251,7 +16006,7 @@ function OhhwellsBridge() {
17251
16006
  },
17252
16007
  [beginFooterDrag]
17253
16008
  );
17254
- const handleItemDragStart = (0, import_react17.useCallback)(
16009
+ const handleItemDragStart = (0, import_react16.useCallback)(
17255
16010
  (e) => {
17256
16011
  const selected = selectedElRef.current;
17257
16012
  if (!selected) {
@@ -17271,7 +16026,7 @@ function OhhwellsBridge() {
17271
16026
  },
17272
16027
  [startFooterColumnDrag, startFooterLinkDrag, startNavLinkDrag]
17273
16028
  );
17274
- const handleItemDragEnd = (0, import_react17.useCallback)(
16029
+ const handleItemDragEnd = (0, import_react16.useCallback)(
17275
16030
  (e) => {
17276
16031
  if (footerDragRef.current) {
17277
16032
  const x = e?.clientX;
@@ -17297,7 +16052,7 @@ function OhhwellsBridge() {
17297
16052
  },
17298
16053
  [commitFooterDrag, commitNavDrag, navDragRef]
17299
16054
  );
17300
- const handleItemChromePointerDown = (0, import_react17.useCallback)((e) => {
16055
+ const handleItemChromePointerDown = (0, import_react16.useCallback)((e) => {
17301
16056
  if (e.button !== 0) return;
17302
16057
  const selected = selectedElRef.current;
17303
16058
  if (!selected) return;
@@ -17328,7 +16083,7 @@ function OhhwellsBridge() {
17328
16083
  }
17329
16084
  if (armNavPressFromChrome(selected, e.clientX, e.clientY, e.pointerId)) return;
17330
16085
  }, [armNavPressFromChrome]);
17331
- const handleItemChromeClick = (0, import_react17.useCallback)((clientX, clientY) => {
16086
+ const handleItemChromeClick = (0, import_react16.useCallback)((clientX, clientY) => {
17332
16087
  if (suppressNextClickRef.current || Date.now() < suppressClickUntilRef.current) {
17333
16088
  suppressNextClickRef.current = false;
17334
16089
  return;
@@ -17341,7 +16096,7 @@ function OhhwellsBridge() {
17341
16096
  }, []);
17342
16097
  reselectNavigationItemRef.current = reselectNavigationItem;
17343
16098
  commitNavigationTextEditRef.current = commitNavigationTextEdit;
17344
- const select = (0, import_react17.useCallback)((anchor) => {
16099
+ const select = (0, import_react16.useCallback)((anchor) => {
17345
16100
  if (!isNavigationItem2(anchor)) return;
17346
16101
  if (activeElRef.current) deactivate();
17347
16102
  aiSectionApiRef.current?.selectFromElement(anchor);
@@ -17384,7 +16139,7 @@ function OhhwellsBridge() {
17384
16139
  setFloatingPanel(null);
17385
16140
  setLogoSizeDraft(null);
17386
16141
  }, [deactivate, markSelected]);
17387
- const selectFrame = (0, import_react17.useCallback)((el) => {
16142
+ const selectFrame = (0, import_react16.useCallback)((el) => {
17388
16143
  if (!isNavigationContainer(el)) return;
17389
16144
  if (activeElRef.current) deactivate();
17390
16145
  aiSectionApiRef.current?.selectFromElement(el);
@@ -17435,7 +16190,7 @@ function OhhwellsBridge() {
17435
16190
  setFloatingPanel(null);
17436
16191
  setLogoSizeDraft(null);
17437
16192
  }, [deactivate, markSelected, postToParent2]);
17438
- const selectLogo = (0, import_react17.useCallback)(
16193
+ const selectLogo = (0, import_react16.useCallback)(
17439
16194
  (logoEl) => {
17440
16195
  if (activeElRef.current) deactivate();
17441
16196
  selectedElRef.current = logoEl;
@@ -17464,7 +16219,7 @@ function OhhwellsBridge() {
17464
16219
  },
17465
16220
  [deactivate, markSelected]
17466
16221
  );
17467
- const openLogoSizePanel = (0, import_react17.useCallback)((logoEl) => {
16222
+ const openLogoSizePanel = (0, import_react16.useCallback)((logoEl) => {
17468
16223
  const placement = getLogoPlacement(logoEl);
17469
16224
  const draft = readLogoSizeState(editContentRef.current, placement);
17470
16225
  setLogoSizeDraft(draft);
@@ -17477,7 +16232,7 @@ function OhhwellsBridge() {
17477
16232
  placement
17478
16233
  });
17479
16234
  }, []);
17480
- const openSocialsDisplayPanel = (0, import_react17.useCallback)((row) => {
16235
+ const openSocialsDisplayPanel = (0, import_react16.useCallback)((row) => {
17481
16236
  setParentScrollSnap(parentScrollRef.current);
17482
16237
  setFloatingPanel({
17483
16238
  key: "socials-display",
@@ -17487,11 +16242,11 @@ function OhhwellsBridge() {
17487
16242
  row
17488
16243
  });
17489
16244
  }, []);
17490
- const isEditModeRef = (0, import_react17.useRef)(false);
17491
- const requestMissingSocialIconsRef = (0, import_react17.useRef)(() => {
16245
+ const isEditModeRef = (0, import_react16.useRef)(false);
16246
+ const requestMissingSocialIconsRef = (0, import_react16.useRef)(() => {
17492
16247
  });
17493
- const askedSocialIconsRef = (0, import_react17.useRef)(/* @__PURE__ */ new Set());
17494
- const requestMissingSocialIcons = (0, import_react17.useCallback)(() => {
16248
+ const askedSocialIconsRef = (0, import_react16.useRef)(/* @__PURE__ */ new Set());
16249
+ const requestMissingSocialIcons = (0, import_react16.useCallback)(() => {
17495
16250
  const items = Array.from(document.querySelectorAll(`[${SOCIALS_ROW_ATTR}]`)).filter((row) => socialsDisplayFor(row, editContentRef.current).icon).flatMap((row) => {
17496
16251
  const missing = socialsMissingIcons(row);
17497
16252
  listSocialItems(row).forEach((item) => ensureIconSlot(item));
@@ -17503,7 +16258,7 @@ function OhhwellsBridge() {
17503
16258
  }, []);
17504
16259
  requestMissingSocialIconsRef.current = requestMissingSocialIcons;
17505
16260
  isEditModeRef.current = isEditMode;
17506
- const changeSocialsDisplay = (0, import_react17.useCallback)(
16261
+ const changeSocialsDisplay = (0, import_react16.useCallback)(
17507
16262
  (row, next) => {
17508
16263
  if (next.icon) {
17509
16264
  const missing = socialsMissingIcons(row);
@@ -17527,17 +16282,17 @@ function OhhwellsBridge() {
17527
16282
  },
17528
16283
  []
17529
16284
  );
17530
- const closeFloatingPanelOnly = (0, import_react17.useCallback)(() => {
16285
+ const closeFloatingPanelOnly = (0, import_react16.useCallback)(() => {
17531
16286
  setFloatingPanel(null);
17532
16287
  setLogoSizeDraft(null);
17533
16288
  }, []);
17534
16289
  closeFloatingPanelOnlyRef.current = closeFloatingPanelOnly;
17535
- const closeFloatingPanelAndDeselect = (0, import_react17.useCallback)(() => {
16290
+ const closeFloatingPanelAndDeselect = (0, import_react16.useCallback)(() => {
17536
16291
  setFloatingPanel(null);
17537
16292
  setLogoSizeDraft(null);
17538
16293
  deselectRef.current();
17539
16294
  }, []);
17540
- (0, import_react17.useEffect)(() => {
16295
+ (0, import_react16.useEffect)(() => {
17541
16296
  const session = floatingPanel && floatingPanel.kind === "logo-size" ? floatingPanel : null;
17542
16297
  if (!session || !logoSizeDraft) {
17543
16298
  postToParentRef.current({ type: "ow:logo-size-panel", open: false });
@@ -17556,7 +16311,7 @@ function OhhwellsBridge() {
17556
16311
  max: LOGO_SIZE_MAX
17557
16312
  });
17558
16313
  }, [floatingPanel, logoSizeDraft, editorViewport]);
17559
- const persistLogoSizeDraft = (0, import_react17.useCallback)(
16314
+ const persistLogoSizeDraft = (0, import_react16.useCallback)(
17560
16315
  (placement, draft) => {
17561
16316
  const desktopKey = LOGO_SIZE_DESKTOP_KEYS[placement];
17562
16317
  const mobileKey = LOGO_SIZE_MOBILE_KEYS[placement];
@@ -17596,7 +16351,7 @@ function OhhwellsBridge() {
17596
16351
  },
17597
16352
  [postToParent2]
17598
16353
  );
17599
- const activate = (0, import_react17.useCallback)((el, options) => {
16354
+ const activate = (0, import_react16.useCallback)((el, options) => {
17600
16355
  if (activeElRef.current === el) return;
17601
16356
  if (isIconEditable(el)) {
17602
16357
  const social = getSocialItem(el);
@@ -17688,8 +16443,8 @@ function OhhwellsBridge() {
17688
16443
  openLogoSizePanelRef.current = openLogoSizePanel;
17689
16444
  deselectRef.current = deselect;
17690
16445
  closeFloatingPanelOnlyRef.current = closeFloatingPanelOnly;
17691
- const lastSiteWideScopeRef = (0, import_react17.useRef)(null);
17692
- (0, import_react17.useEffect)(() => {
16446
+ const lastSiteWideScopeRef = (0, import_react16.useRef)(null);
16447
+ (0, import_react16.useEffect)(() => {
17693
16448
  if (!isEditMode) {
17694
16449
  if (lastSiteWideScopeRef.current !== false) {
17695
16450
  lastSiteWideScopeRef.current = false;
@@ -17715,34 +16470,22 @@ function OhhwellsBridge() {
17715
16470
  isFooterFrameSelection,
17716
16471
  postToParent2
17717
16472
  ]);
17718
- (0, import_react17.useLayoutEffect)(() => {
16473
+ (0, import_react16.useLayoutEffect)(() => {
17719
16474
  if (!subdomain || isEditMode) {
17720
16475
  setFetchState("done");
17721
16476
  return;
17722
16477
  }
17723
16478
  const applyContent = (content) => {
17724
16479
  const imageLoads = [];
17725
- if (typeof content[BRAND_KIT_KEY] === "string") {
17726
- brandKitRef.current = content[BRAND_KIT_KEY];
17727
- applyBrandToDom(parseBrandKit(content[BRAND_KIT_KEY]));
17728
- }
17729
16480
  if (typeof content[AI_SECTIONS_KEY] === "string") {
17730
16481
  aiSectionsRef.current = content[AI_SECTIONS_KEY];
17731
16482
  applyAiSectionsToDom(parseAiSectionsState(content[AI_SECTIONS_KEY]));
17732
16483
  }
17733
- if (typeof content[STYLE_STORE_KEY] === "string") {
17734
- stylesRef.current = content[STYLE_STORE_KEY];
17735
- applyStylesToDom(parseStyleStore(content[STYLE_STORE_KEY]));
17736
- }
17737
- applyBrandChrome(content);
17738
16484
  for (const [key, val] of Object.entries(content)) {
17739
16485
  if (key === "__ohw_sections") continue;
17740
16486
  if (key === AI_SECTIONS_KEY) continue;
17741
16487
  if (key === LOGO_PLACEHOLDER_KEY) continue;
17742
16488
  if (LOGO_IMAGE_KEYS.includes(key)) continue;
17743
- if (key === BRAND_KIT_KEY) continue;
17744
- if (key === STYLE_STORE_KEY) continue;
17745
- if (BRAND_CHROME_KEYS.has(key)) continue;
17746
16489
  if (applyVideoSettingNode(key, val)) continue;
17747
16490
  if (applyCarouselNode(key, val)) continue;
17748
16491
  document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
@@ -17803,9 +16546,7 @@ function OhhwellsBridge() {
17803
16546
  let cancelled = false;
17804
16547
  setFetchState("loading");
17805
16548
  const apiUrl = process.env.NEXT_PUBLIC_FLOWOPS_API_URL ?? "https://flowops-backend-staging-2yfdh7pwpq-as.a.run.app";
17806
- const initialPath = pathname;
17807
- fetchedContentPaths.add(`${subdomain}::${initialPath}`);
17808
- fetch(`${apiUrl}/api/public/sites/${subdomain}/content?path=${encodeURIComponent(initialPath)}`, { cache: "no-store" }).then((r2) => r2.ok ? r2.json() : null).then((data) => {
16549
+ fetch(`${apiUrl}/api/public/sites/${subdomain}/content`, { cache: "no-store" }).then((r2) => r2.ok ? r2.json() : null).then((data) => {
17809
16550
  if (cancelled) return;
17810
16551
  const content = data?.content ?? {};
17811
16552
  contentCache.set(subdomain, content);
@@ -17818,7 +16559,7 @@ function OhhwellsBridge() {
17818
16559
  cancelled = true;
17819
16560
  };
17820
16561
  }, [subdomain, isEditMode]);
17821
- (0, import_react17.useEffect)(() => {
16562
+ (0, import_react16.useEffect)(() => {
17822
16563
  if (!isEditMode) return;
17823
16564
  const resolveIndex = (form, clientY) => {
17824
16565
  const wrappers = listFieldWrappers(form).filter((el) => fieldKeyOf(el) !== fieldDragRef.current?.key);
@@ -17859,7 +16600,7 @@ function OhhwellsBridge() {
17859
16600
  window.removeEventListener("drop", onDrop, true);
17860
16601
  };
17861
16602
  }, [buildFieldDropSlots, isEditMode, persistFields, selectField]);
17862
- (0, import_react17.useEffect)(() => {
16603
+ (0, import_react16.useEffect)(() => {
17863
16604
  if (!isEditMode) return;
17864
16605
  const mark = () => document.querySelectorAll('[data-ohw-editable="form"]').forEach((form) => {
17865
16606
  markFormFields(form);
@@ -17871,7 +16612,7 @@ function OhhwellsBridge() {
17871
16612
  });
17872
16613
  return () => observer.disconnect();
17873
16614
  }, [isEditMode, fetchState, pathname]);
17874
- (0, import_react17.useEffect)(() => {
16615
+ (0, import_react16.useEffect)(() => {
17875
16616
  if (!isEditMode) return;
17876
16617
  let saveTimer = null;
17877
16618
  const onInput = (e) => {
@@ -17893,14 +16634,14 @@ function OhhwellsBridge() {
17893
16634
  document.addEventListener("input", onInput, true);
17894
16635
  return () => document.removeEventListener("input", onInput, true);
17895
16636
  }, [isEditMode, persistFields]);
17896
- (0, import_react17.useEffect)(() => {
16637
+ (0, import_react16.useEffect)(() => {
17897
16638
  if (isEditMode || fetchState !== "done") return;
17898
16639
  const content = contentCache.get(subdomain) ?? {};
17899
16640
  document.querySelectorAll('[data-ohw-editable="form"]').forEach((form) => {
17900
16641
  reconcileFieldsFromContent(form, content);
17901
16642
  });
17902
16643
  }, [isEditMode, fetchState, subdomain]);
17903
- (0, import_react17.useEffect)(() => {
16644
+ (0, import_react16.useEffect)(() => {
17904
16645
  if (!isEditMode) return;
17905
16646
  const swallow = (e) => {
17906
16647
  const target = e.target;
@@ -17909,12 +16650,12 @@ function OhhwellsBridge() {
17909
16650
  document.addEventListener("submit", swallow, true);
17910
16651
  return () => document.removeEventListener("submit", swallow, true);
17911
16652
  }, [isEditMode]);
17912
- (0, import_react17.useEffect)(() => {
16653
+ (0, import_react16.useEffect)(() => {
17913
16654
  if (isEditMode || fetchState !== "done") return;
17914
16655
  const apiUrl = process.env.NEXT_PUBLIC_FLOWOPS_API_URL ?? "https://flowops-backend-staging-2yfdh7pwpq-as.a.run.app";
17915
16656
  bindPublishedForms(apiUrl, subdomain, contentCache.get(subdomain) ?? {});
17916
16657
  }, [isEditMode, fetchState, subdomain]);
17917
- (0, import_react17.useEffect)(() => {
16658
+ (0, import_react16.useEffect)(() => {
17918
16659
  if (!subdomain || isEditMode) return;
17919
16660
  let debounceTimer = null;
17920
16661
  let observer = null;
@@ -17925,25 +16666,10 @@ function OhhwellsBridge() {
17925
16666
  initSectionInstancesFromContent(content, window.location.pathname);
17926
16667
  observer?.disconnect();
17927
16668
  try {
17928
- applyBrandChrome(content);
17929
- if (typeof content[BRAND_KIT_KEY] === "string") {
17930
- applyBrandToDom(parseBrandKit(content[BRAND_KIT_KEY]));
17931
- }
17932
- if (typeof content[AI_SECTIONS_KEY] === "string") {
17933
- applyAiSectionsToDom(parseAiSectionsState(content[AI_SECTIONS_KEY]));
17934
- }
17935
- if (typeof content[STYLE_STORE_KEY] === "string") {
17936
- applyStylesToDom(parseStyleStore(content[STYLE_STORE_KEY]));
17937
- }
17938
16669
  for (const [key, val] of Object.entries(content)) {
17939
16670
  if (key === "__ohw_sections") continue;
17940
- if (key === AI_SECTIONS_KEY) continue;
17941
16671
  if (key === LOGO_PLACEHOLDER_KEY) continue;
17942
16672
  if (LOGO_IMAGE_KEYS.includes(key)) continue;
17943
- if (key === BRAND_KIT_KEY) continue;
17944
- if (key === STYLE_STORE_KEY) continue;
17945
- if (key === STYLE_STORE_KEY) continue;
17946
- if (BRAND_CHROME_KEYS.has(key)) continue;
17947
16673
  if (applyVideoSettingNode(key, val)) continue;
17948
16674
  if (applyCarouselNode(key, val)) continue;
17949
16675
  document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
@@ -17989,17 +16715,6 @@ function OhhwellsBridge() {
17989
16715
  debounceTimer = setTimeout(applyFromCache, 150);
17990
16716
  };
17991
16717
  applyFromCache();
17992
- const pathCacheKey = `${subdomain}::${pathname}`;
17993
- if (!fetchedContentPaths.has(pathCacheKey)) {
17994
- fetchedContentPaths.add(pathCacheKey);
17995
- const apiUrl = process.env.NEXT_PUBLIC_FLOWOPS_API_URL ?? "https://flowops-backend-staging-2yfdh7pwpq-as.a.run.app";
17996
- fetch(`${apiUrl}/api/public/sites/${subdomain}/content?path=${encodeURIComponent(pathname)}`, { cache: "no-store" }).then((r2) => r2.ok ? r2.json() : null).then((data) => {
17997
- if (!data?.content) return;
17998
- contentCache.set(subdomain, data.content);
17999
- applyFromCache();
18000
- }).catch(() => {
18001
- });
18002
- }
18003
16718
  observer = new MutationObserver(scheduleApply);
18004
16719
  observer.observe(document.body, { childList: true, subtree: true });
18005
16720
  return () => {
@@ -18007,16 +16722,16 @@ function OhhwellsBridge() {
18007
16722
  if (debounceTimer) clearTimeout(debounceTimer);
18008
16723
  };
18009
16724
  }, [subdomain, isEditMode, pathname]);
18010
- (0, import_react17.useLayoutEffect)(() => {
16725
+ (0, import_react16.useLayoutEffect)(() => {
18011
16726
  const el = document.getElementById("ohw-loader");
18012
16727
  if (!el) return;
18013
16728
  const visible = Boolean(subdomain) && fetchState !== "done";
18014
16729
  el.style.display = visible ? "flex" : "none";
18015
16730
  }, [subdomain, fetchState]);
18016
- (0, import_react17.useEffect)(() => {
16731
+ (0, import_react16.useEffect)(() => {
18017
16732
  postToParent2({ type: "ow:navigation", path: pathname });
18018
16733
  }, [pathname, postToParent2]);
18019
- (0, import_react17.useEffect)(() => {
16734
+ (0, import_react16.useEffect)(() => {
18020
16735
  if (!isEditMode) return;
18021
16736
  if (linkPopoverSessionRef.current?.intent === "add-nav") return;
18022
16737
  if (document.querySelector("[data-ohw-section-picker]")) return;
@@ -18024,7 +16739,7 @@ function OhhwellsBridge() {
18024
16739
  deselectRef.current();
18025
16740
  deactivateRef.current();
18026
16741
  }, [pathname, isEditMode]);
18027
- (0, import_react17.useEffect)(() => {
16742
+ (0, import_react16.useEffect)(() => {
18028
16743
  const contentForNav = () => {
18029
16744
  if (isEditMode) return editContentRef.current;
18030
16745
  if (!subdomain) return {};
@@ -18093,36 +16808,35 @@ function OhhwellsBridge() {
18093
16808
  observer?.disconnect();
18094
16809
  };
18095
16810
  }, [isEditMode, pathname, subdomain, fetchState, resyncSelectedNavigationItem]);
18096
- (0, import_react17.useEffect)(() => {
16811
+ (0, import_react16.useEffect)(() => {
18097
16812
  if (!isEditMode) return;
18098
- let lastPosted = 0;
18099
16813
  const measure = () => {
18100
16814
  const h = document.body.scrollHeight;
18101
- if (h > 50 && Math.abs(h - lastPosted) > 1) {
18102
- lastPosted = h;
18103
- postToParent2({ type: "ow:height", height: h });
18104
- }
18105
- };
18106
- let raf = null;
18107
- const schedule = () => {
18108
- if (raf != null) return;
18109
- raf = requestAnimationFrame(() => {
18110
- raf = null;
18111
- measure();
18112
- });
16815
+ if (h > 50) postToParent2({ type: "ow:height", height: h });
18113
16816
  };
18114
16817
  const t1 = setTimeout(measure, 50);
18115
16818
  const t2 = setTimeout(measure, 500);
18116
- const ro = new ResizeObserver(schedule);
18117
- ro.observe(document.body);
16819
+ let lastWidth = window.innerWidth;
16820
+ let resizeTimers = [];
16821
+ const clearResizeTimers = () => {
16822
+ resizeTimers.forEach(clearTimeout);
16823
+ resizeTimers = [];
16824
+ };
16825
+ const handleResize = () => {
16826
+ if (window.innerWidth === lastWidth) return;
16827
+ lastWidth = window.innerWidth;
16828
+ clearResizeTimers();
16829
+ resizeTimers = HEIGHT_SETTLE_DELAYS.map((delay) => setTimeout(measure, delay));
16830
+ };
16831
+ window.addEventListener("resize", handleResize);
18118
16832
  return () => {
18119
16833
  clearTimeout(t1);
18120
16834
  clearTimeout(t2);
18121
- if (raf != null) cancelAnimationFrame(raf);
18122
- ro.disconnect();
16835
+ clearResizeTimers();
16836
+ window.removeEventListener("resize", handleResize);
18123
16837
  };
18124
16838
  }, [pathname, isEditMode, postToParent2]);
18125
- (0, import_react17.useEffect)(() => {
16839
+ (0, import_react16.useEffect)(() => {
18126
16840
  if (!subdomainFromQuery || isEditMode) return;
18127
16841
  const handleClick = (e) => {
18128
16842
  const anchor = e.target.closest("a");
@@ -18138,7 +16852,7 @@ function OhhwellsBridge() {
18138
16852
  document.addEventListener("click", handleClick, true);
18139
16853
  return () => document.removeEventListener("click", handleClick, true);
18140
16854
  }, [subdomainFromQuery, isEditMode, router]);
18141
- (0, import_react17.useEffect)(() => {
16855
+ (0, import_react16.useEffect)(() => {
18142
16856
  if (!isEditMode) {
18143
16857
  editStylesRef.current?.base.remove();
18144
16858
  editStylesRef.current?.forceHover.remove();
@@ -18360,7 +17074,6 @@ function OhhwellsBridge() {
18360
17074
  return;
18361
17075
  }
18362
17076
  const target = e.target;
18363
- if (target.closest("[data-ohw-ai-review]")) return;
18364
17077
  if (target.closest("[data-ohw-toolbar]")) return;
18365
17078
  if (target.closest("[data-ohw-state-toggle]")) return;
18366
17079
  if (target.closest("[data-ohw-max-badge]")) return;
@@ -18372,9 +17085,6 @@ function OhhwellsBridge() {
18372
17085
  )) {
18373
17086
  return;
18374
17087
  }
18375
- if (selectedMediaElRef.current && !selectedMediaElRef.current.contains(target) && !target.closest("[data-ohw-media-overlay], [data-ohw-edit-chrome], [data-ohw-item-interaction]")) {
18376
- clearMediaSelectionRef.current();
18377
- }
18378
17088
  {
18379
17089
  const formEl = getFormElement(target);
18380
17090
  const onSuccessText = target.closest(`[${SUCCESS_TEXT_ATTR}]`);
@@ -18529,11 +17239,8 @@ function OhhwellsBridge() {
18529
17239
  if (isMediaEditable(editable) && !buttonOnMedia) {
18530
17240
  e.preventDefault();
18531
17241
  e.stopPropagation();
18532
- if (selectedMediaElRef.current === editable) {
18533
- postToParentRef.current({ type: "ow:image-pick", key: editable.dataset.ohwKey ?? "", elementType: editable.dataset.ohwEditable ?? "image" });
18534
- } else {
18535
- selectMediaElementRef.current(editable);
18536
- }
17242
+ aiSectionApiRef.current?.selectFromElement(editable);
17243
+ postToParentRef.current({ type: "ow:image-pick", key: editable.dataset.ohwKey ?? "", elementType: editable.dataset.ohwEditable ?? "image" });
18537
17244
  return;
18538
17245
  }
18539
17246
  const socialItem = getSocialItem(editable);
@@ -18669,7 +17376,6 @@ function OhhwellsBridge() {
18669
17376
  };
18670
17377
  const handleDblClick = (e) => {
18671
17378
  const target = e.target;
18672
- if (target.closest("[data-ohw-ai-review]")) return;
18673
17379
  if (target.closest("[data-ohw-toolbar]")) return;
18674
17380
  if (target.closest("[data-ohw-state-toggle]")) return;
18675
17381
  if (target.closest("[data-ohw-max-badge]")) return;
@@ -18721,9 +17427,6 @@ function OhhwellsBridge() {
18721
17427
  setHoveredItemRect(null);
18722
17428
  hoveredNavContainerRef.current = null;
18723
17429
  setHoveredNavContainerRect(null);
18724
- siblingHintElRef.current = null;
18725
- setSiblingHintRect(null);
18726
- setSiblingHintRects([]);
18727
17430
  return;
18728
17431
  }
18729
17432
  {
@@ -18842,6 +17545,7 @@ function OhhwellsBridge() {
18842
17545
  hoveredNavContainerRef.current = null;
18843
17546
  setHoveredNavContainerRect(null);
18844
17547
  hoveredItemElRef.current = editable;
17548
+ setHoveredItemRect(editable.getBoundingClientRect());
18845
17549
  }
18846
17550
  }
18847
17551
  }
@@ -19138,7 +17842,7 @@ function OhhwellsBridge() {
19138
17842
  }
19139
17843
  };
19140
17844
  const probeImageAt = (clientX, clientY, isDragOver = false, fromParentViewport = false) => {
19141
- if (linkPopoverOpenRef.current || floatingPanelOpenRef.current && isPointOverFloatingPanel(clientX, clientY)) {
17845
+ if (linkPopoverOpenRef.current) {
19142
17846
  if (hoveredImageRef.current) {
19143
17847
  hoveredImageRef.current = null;
19144
17848
  hoveredImageHasTextOverlapRef.current = false;
@@ -19472,9 +18176,7 @@ function OhhwellsBridge() {
19472
18176
  return;
19473
18177
  }
19474
18178
  const { y } = toProbeCoords(clientX, clientY, fromParentViewport);
19475
- const sections = Array.from(document.querySelectorAll("[data-ohw-section]")).filter(
19476
- (el) => !el.hasAttribute("data-ohw-ai-template-hidden") && !el.hasAttribute("data-ohw-ai-removed") && !el.hasAttribute("data-ohw-ai-replaced-by") && el.getBoundingClientRect().height > 0
19477
- ).sort((a, b) => a.getBoundingClientRect().top - b.getBoundingClientRect().top);
18179
+ const sections = Array.from(document.querySelectorAll("[data-ohw-section]")).sort((a, b) => a.getBoundingClientRect().top - b.getBoundingClientRect().top);
19478
18180
  const ZONE = 20;
19479
18181
  for (let i = 0; i < sections.length; i++) {
19480
18182
  const a = sections[i];
@@ -19503,7 +18205,8 @@ function OhhwellsBridge() {
19503
18205
  };
19504
18206
  const handleMouseMove = (e) => {
19505
18207
  const { clientX, clientY } = e;
19506
- if (document.documentElement.hasAttribute("data-ohw-panel-dragging") || floatingPanelOpenRef.current && isPointOverFloatingPanel(clientX, clientY) || isOverEditorChrome(clientX, clientY)) {
18208
+ if (pointOwnedByFloatingPanel(clientX, clientY)) return;
18209
+ if (isOverEditorChrome(clientX, clientY)) {
19507
18210
  document.querySelectorAll("[data-ohw-hovered]").forEach((el) => el.removeAttribute("data-ohw-hovered"));
19508
18211
  formHoverElRef.current = null;
19509
18212
  setFormHoverRect(null);
@@ -19511,12 +18214,6 @@ function OhhwellsBridge() {
19511
18214
  setHoveredItemRect(null);
19512
18215
  hoveredNavContainerRef.current = null;
19513
18216
  setHoveredNavContainerRect(null);
19514
- siblingHintElRef.current = null;
19515
- setSiblingHintRect(null);
19516
- setSiblingHintRects([]);
19517
- dismissImageHover();
19518
- clearImageHover();
19519
- setSectionGap(null);
19520
18217
  return;
19521
18218
  }
19522
18219
  if (probeSocialsRowAt(clientX, clientY)) return;
@@ -19528,11 +18225,7 @@ function OhhwellsBridge() {
19528
18225
  if (e.data?.type !== "ow:pointer-sync") return;
19529
18226
  const { clientX, clientY } = e.data;
19530
18227
  if (typeof clientX !== "number" || typeof clientY !== "number") return;
19531
- if (document.documentElement.hasAttribute("data-ohw-panel-dragging") || floatingPanelOpenRef.current && isPointOverFloatingPanel(clientX, clientY)) {
19532
- dismissImageHover();
19533
- clearImageHover();
19534
- return;
19535
- }
18228
+ if (pointOwnedByFloatingPanel(clientX, clientY)) return;
19536
18229
  if (probeSocialsRowAt(clientX, clientY)) return;
19537
18230
  probeSectionGapAt(clientX, clientY);
19538
18231
  probeImageAt(clientX, clientY);
@@ -19807,7 +18500,7 @@ function OhhwellsBridge() {
19807
18500
  timers.set(key, setTimeout(() => {
19808
18501
  timers.delete(key);
19809
18502
  postToParentRef.current({ type: "ow:change", nodes: [{ key, text: html }] });
19810
- const h = document.body.scrollHeight;
18503
+ const h = document.documentElement.scrollHeight;
19811
18504
  if (h > 50) postToParentRef.current({ type: "ow:height", height: h });
19812
18505
  }, 400));
19813
18506
  };
@@ -19815,19 +18508,10 @@ function OhhwellsBridge() {
19815
18508
  if (e.data?.type !== "ow:hydrate") return;
19816
18509
  const content = e.data.content;
19817
18510
  if (!content) return;
19818
- if (typeof content[BRAND_KIT_KEY] === "string") {
19819
- brandKitRef.current = content[BRAND_KIT_KEY];
19820
- applyBrandToDom(parseBrandKit(content[BRAND_KIT_KEY]));
19821
- }
19822
18511
  if (typeof content[AI_SECTIONS_KEY] === "string") {
19823
18512
  aiSectionsRef.current = content[AI_SECTIONS_KEY];
19824
18513
  applyAiSectionsToDom(parseAiSectionsState(content[AI_SECTIONS_KEY]));
19825
18514
  }
19826
- if (typeof content[STYLE_STORE_KEY] === "string") {
19827
- stylesRef.current = content[STYLE_STORE_KEY];
19828
- applyStylesToDom(parseStyleStore(content[STYLE_STORE_KEY]));
19829
- }
19830
- applyBrandChrome(content);
19831
18515
  let sectionsJson = null;
19832
18516
  for (const [key, val] of Object.entries(content)) {
19833
18517
  if (key === "__ohw_sections") {
@@ -19837,9 +18521,6 @@ function OhhwellsBridge() {
19837
18521
  if (key === AI_SECTIONS_KEY) continue;
19838
18522
  if (key === LOGO_PLACEHOLDER_KEY) continue;
19839
18523
  if (LOGO_IMAGE_KEYS.includes(key) && !String(val ?? "").trim()) continue;
19840
- if (key === BRAND_KIT_KEY) continue;
19841
- if (key === STYLE_STORE_KEY) continue;
19842
- if (BRAND_CHROME_KEYS.has(key)) continue;
19843
18524
  if (applyVideoSettingNode(key, val)) continue;
19844
18525
  if (applyCarouselNode(key, val)) continue;
19845
18526
  document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
@@ -19853,8 +18534,6 @@ function OhhwellsBridge() {
19853
18534
  if (video && video.src !== val) applyVideoSrc(video, val);
19854
18535
  } else if (el.dataset.ohwEditable === "link") {
19855
18536
  applyLinkHref(el, val);
19856
- } else if (el.dataset.ohwEditable === "icon") {
19857
- applyIconMarkup(el, val);
19858
18537
  } else if (isIconMarkupValue(val)) {
19859
18538
  } else {
19860
18539
  el.innerHTML = val;
@@ -19875,7 +18554,7 @@ function OhhwellsBridge() {
19875
18554
  reconcileFooterOrderFromContent(editContentRef.current);
19876
18555
  syncNavigationDragCursorAttrs();
19877
18556
  enforceLinkHrefs();
19878
- const hydratedHeight = document.body.scrollHeight;
18557
+ const hydratedHeight = document.documentElement.scrollHeight;
19879
18558
  if (hydratedHeight > 50) postToParentRef.current({ type: "ow:height", height: hydratedHeight });
19880
18559
  postToParentRef.current({ type: "ow:hydrate-done" });
19881
18560
  };
@@ -19939,25 +18618,16 @@ function OhhwellsBridge() {
19939
18618
  nodes: collectEditableNodes(editContentRef.current)
19940
18619
  });
19941
18620
  };
19942
- const clearInteractionChrome = () => {
19943
- deactivateRef.current();
19944
- deselectRef.current();
19945
- clearMediaSelectionRef.current();
19946
- };
19947
18621
  const handleAiApplyTree = (e) => {
19948
18622
  if (e.data?.type !== "ow:ai-apply-tree") return;
19949
18623
  const payload = e.data.payload;
19950
18624
  if (!payload || typeof payload.id !== "string" || !isRenderableTree(payload.tree)) return;
19951
- clearInteractionChrome();
19952
18625
  const previous = aiSectionsRef.current;
19953
- const nextState = applyTreeToState(parseAiSectionsState(previous), {
19954
- ...payload,
19955
- path: payload.path ?? window.location.pathname
19956
- });
18626
+ const nextState = applyTreeToState(parseAiSectionsState(previous), payload);
19957
18627
  const nextValue = serializeAiSectionsState(nextState);
19958
18628
  aiSectionsRef.current = nextValue;
19959
18629
  applyAiSectionsToDom(nextState);
19960
- const newHeight = document.body.scrollHeight;
18630
+ const newHeight = document.documentElement.scrollHeight;
19961
18631
  if (newHeight > 50) postToParentRef.current({ type: "ow:height", height: newHeight });
19962
18632
  postToParentRef.current({ type: "ow:change", nodes: [{ key: AI_SECTIONS_KEY, text: nextValue }] });
19963
18633
  const appliedEl = document.querySelector(`[data-ohw-section="${CSS.escape(payload.id)}"]`);
@@ -19973,13 +18643,12 @@ function OhhwellsBridge() {
19973
18643
  if (!sectionId || sectionId === "navbar" || sectionId === "footer") return;
19974
18644
  const exists = document.querySelector(`[data-ohw-section="${CSS.escape(sectionId)}"]`);
19975
18645
  if (!exists) return;
19976
- clearInteractionChrome();
19977
18646
  const previous = aiSectionsRef.current;
19978
18647
  const nextState = deleteSectionFromState(parseAiSectionsState(previous), sectionId);
19979
18648
  const nextValue = serializeAiSectionsState(nextState);
19980
18649
  aiSectionsRef.current = nextValue;
19981
18650
  applyAiSectionsToDom(nextState);
19982
- const newHeight = document.body.scrollHeight;
18651
+ const newHeight = document.documentElement.scrollHeight;
19983
18652
  if (newHeight > 50) postToParentRef.current({ type: "ow:height", height: newHeight });
19984
18653
  postToParentRef.current({ type: "ow:change", nodes: [{ key: AI_SECTIONS_KEY, text: nextValue }] });
19985
18654
  postToParentRef.current({ type: "ow:ai-section-deleted", sectionId, previous, value: nextValue });
@@ -19989,106 +18658,20 @@ function OhhwellsBridge() {
19989
18658
  const handleAiSetSections = (e) => {
19990
18659
  if (e.data?.type !== "ow:ai-set-sections") return;
19991
18660
  const value = typeof e.data.value === "string" ? e.data.value : "";
19992
- clearInteractionChrome();
19993
18661
  aiSectionsRef.current = value;
19994
18662
  applyAiSectionsToDom(parseAiSectionsState(value));
19995
- applyStylesToDom(parseStyleStore(stylesRef.current));
19996
- const restoredHeight = document.body.scrollHeight;
18663
+ const restoredHeight = document.documentElement.scrollHeight;
19997
18664
  if (restoredHeight > 50) postToParentRef.current({ type: "ow:height", height: restoredHeight });
19998
18665
  postToParentRef.current({ type: "ow:change", nodes: [{ key: AI_SECTIONS_KEY, text: value }] });
19999
18666
  postAiSectionsChanged();
20000
18667
  };
20001
18668
  window.addEventListener("message", handleAiSetSections);
20002
- const handleMoveSection = (e) => {
20003
- if (e.data?.type !== "ow:move-section") return;
20004
- const instanceId = typeof e.data.instanceId === "string" ? e.data.instanceId : "";
20005
- const direction = e.data.direction === "up" || e.data.direction === "down" ? e.data.direction : null;
20006
- if (!instanceId || !direction) return;
20007
- const entries = moveSectionInstance(instanceId, direction, window.location.pathname);
20008
- if (!entries) return;
20009
- const orderJson = JSON.stringify(entries);
20010
- editContentRef.current = { ...editContentRef.current, [SECTION_ORDER_KEY]: orderJson };
20011
- postToParentRef.current({ type: "ow:change", nodes: [{ key: SECTION_ORDER_KEY, text: orderJson }] });
20012
- window.dispatchEvent(new Event("resize"));
20013
- };
20014
- window.addEventListener("message", handleMoveSection);
20015
- const handleAiSetBrand = (e) => {
20016
- if (e.data?.type !== "ow:ai-set-brand") return;
20017
- const value = typeof e.data.value === "string" ? e.data.value : "";
20018
- const previous = brandKitRef.current;
20019
- brandKitRef.current = value;
20020
- applyBrandToDom(parseBrandKit(value));
20021
- if (aiSectionsRef.current) applyAiSectionsToDom(parseAiSectionsState(aiSectionsRef.current));
20022
- applyStylesToDom(parseStyleStore(stylesRef.current));
20023
- postToParentRef.current({ type: "ow:change", nodes: [{ key: BRAND_KIT_KEY, text: value }] });
20024
- postToParentRef.current({ type: "ow:ai-brand-applied", previous, value });
20025
- };
20026
- window.addEventListener("message", handleAiSetBrand);
20027
- const handleAiSetStyles = (e) => {
20028
- if (e.data?.type !== "ow:ai-set-styles") return;
20029
- const value = typeof e.data.value === "string" ? e.data.value : "";
20030
- const previous = stylesRef.current;
20031
- stylesRef.current = value;
20032
- applyStylesToDom(parseStyleStore(value));
20033
- postToParentRef.current({ type: "ow:change", nodes: [{ key: STYLE_STORE_KEY, text: value }] });
20034
- postToParentRef.current({ type: "ow:ai-styles-applied", previous, value });
20035
- };
20036
- window.addEventListener("message", handleAiSetStyles);
20037
- const handleGetBrand = (e) => {
20038
- if (e.data?.type !== "ow:get-brand") return;
20039
- const template = deriveTemplateBrand();
20040
- const value = brandKitRef.current || (template ? JSON.stringify(template) : "");
20041
- postToParentRef.current({ type: "ow:brand-value", value });
20042
- };
20043
- window.addEventListener("message", handleGetBrand);
20044
18669
  const handlePanelDragging = (e) => {
20045
18670
  if (e.data?.type !== "ow:panel-dragging") return;
20046
18671
  if (e.data.dragging) document.documentElement.setAttribute("data-ohw-panel-dragging", "");
20047
18672
  else document.documentElement.removeAttribute("data-ohw-panel-dragging");
20048
18673
  };
20049
18674
  window.addEventListener("message", handlePanelDragging);
20050
- const handleDeleteSection = (e) => {
20051
- if (e.data?.type !== "ow:delete-section") return;
20052
- const instanceId = typeof e.data.instanceId === "string" ? e.data.instanceId : "";
20053
- if (!instanceId) return;
20054
- const currentEntries = getPageSectionOrderEntries(editContentRef.current[SECTION_ORDER_KEY], window.location.pathname);
20055
- const entries = deleteSectionInstance(instanceId, window.location.pathname, currentEntries);
20056
- if (!entries) return;
20057
- const orderJson = JSON.stringify(entries);
20058
- editContentRef.current = { ...editContentRef.current, [SECTION_ORDER_KEY]: orderJson };
20059
- postToParentRef.current({ type: "ow:change", nodes: [{ key: SECTION_ORDER_KEY, text: orderJson }] });
20060
- aiSectionApiRef.current?.clear();
20061
- window.dispatchEvent(new Event("resize"));
20062
- const deleteHeight = document.body.scrollHeight;
20063
- if (deleteHeight > 50) postToParentRef.current({ type: "ow:height", height: deleteHeight });
20064
- const actionId = newInstanceId();
20065
- pendingDeleteUndoRef.current = {
20066
- actionId,
20067
- restore: () => {
20068
- const restoredEntries = getPageSectionOrderEntries(
20069
- editContentRef.current[SECTION_ORDER_KEY],
20070
- window.location.pathname
20071
- );
20072
- const restored = restoreSectionInstance(instanceId, window.location.pathname, restoredEntries);
20073
- if (!restored) return;
20074
- const restoredJson = JSON.stringify(restored);
20075
- editContentRef.current = { ...editContentRef.current, [SECTION_ORDER_KEY]: restoredJson };
20076
- postToParentRef.current({ type: "ow:change", nodes: [{ key: SECTION_ORDER_KEY, text: restoredJson }] });
20077
- window.dispatchEvent(new Event("resize"));
20078
- const restoreHeight = document.body.scrollHeight;
20079
- if (restoreHeight > 50) postToParentRef.current({ type: "ow:height", height: restoreHeight });
20080
- }
20081
- };
20082
- postToParentRef.current({
20083
- type: "ow:toast",
20084
- title: "Section deleted",
20085
- toastType: "success",
20086
- actionLabel: "Undo",
20087
- actionId,
20088
- duration: 6e3
20089
- });
20090
- };
20091
- window.addEventListener("message", handleDeleteSection);
20092
18675
  const handleDeactivate = (e) => {
20093
18676
  if (e.data?.type !== "ow:deactivate") return;
20094
18677
  if (document.documentElement.hasAttribute("data-ohw-panel-dragging")) return;
@@ -20098,15 +18681,8 @@ function OhhwellsBridge() {
20098
18681
  closeLinkPopoverRef.current();
20099
18682
  return;
20100
18683
  }
20101
- if (floatingPanelOpenRef.current) {
20102
- setFloatingPanelRef.current(null);
20103
- deselectRef.current();
20104
- deactivateRef.current();
20105
- return;
20106
- }
20107
18684
  deselectRef.current();
20108
18685
  deactivateRef.current();
20109
- clearMediaSelectionRef.current();
20110
18686
  };
20111
18687
  window.addEventListener("message", handleDeactivate);
20112
18688
  const handleToastAction = (e) => {
@@ -20192,10 +18768,6 @@ function OhhwellsBridge() {
20192
18768
  const handleKeyDown = (e) => {
20193
18769
  if (e.key === "Escape" && document.querySelector("[data-ohw-section-picker]")) return;
20194
18770
  if (e.key === "Escape" && document.querySelector("[data-ohw-more-menu]")) return;
20195
- if (e.key === "Escape" && selectedMediaElRef.current) {
20196
- clearMediaSelectionRef.current();
20197
- return;
20198
- }
20199
18771
  if ((e.ctrlKey || e.metaKey) && e.key.toLowerCase() === "a" && activeElRef.current) {
20200
18772
  e.preventDefault();
20201
18773
  selectAllTextInEditable(activeElRef.current);
@@ -20355,12 +18927,6 @@ function OhhwellsBridge() {
20355
18927
  if (aiSectionsRef.current) {
20356
18928
  nodes.push({ key: AI_SECTIONS_KEY, type: "sections", text: aiSectionsRef.current });
20357
18929
  }
20358
- if (stylesRef.current) {
20359
- nodes.push({ key: STYLE_STORE_KEY, type: "styles", text: stylesRef.current });
20360
- }
20361
- if (brandKitRef.current) {
20362
- nodes.push({ key: BRAND_KIT_KEY, type: "brand", text: brandKitRef.current });
20363
- }
20364
18930
  document.querySelectorAll('[data-ohw-editable="form"]').forEach((form) => {
20365
18931
  const formKey = formKeyOf(form);
20366
18932
  if (!formKey) return;
@@ -20378,12 +18944,8 @@ function OhhwellsBridge() {
20378
18944
  if (inserted) {
20379
18945
  const tracker = getSectionsTracker();
20380
18946
  postToParentRef.current({ type: "ow:change", nodes: [{ key: "__ohw_sections", text: tracker.textContent ?? "[]" }] });
20381
- const reportHeight = () => {
20382
- const h = document.body.scrollHeight;
20383
- if (h > 50) postToParentRef.current({ type: "ow:height", height: h });
20384
- };
20385
- reportHeight();
20386
- setTimeout(reportHeight, 500);
18947
+ const h = document.documentElement.scrollHeight;
18948
+ if (h > 50) postToParentRef.current({ type: "ow:height", height: h });
20387
18949
  }
20388
18950
  };
20389
18951
  const handleSwitchSchedule = (e) => {
@@ -20424,7 +18986,7 @@ function OhhwellsBridge() {
20424
18986
  const updated = sections.filter((s) => !(s.type === "scheduling" && s.pagePath === currentPath));
20425
18987
  tracker.textContent = JSON.stringify(updated);
20426
18988
  postToParentRef.current({ type: "ow:change", nodes: [{ key: "__ohw_sections", text: tracker.textContent }] });
20427
- const h = document.body.scrollHeight;
18989
+ const h = document.documentElement.scrollHeight;
20428
18990
  if (h > 50) postToParentRef.current({ type: "ow:height", height: h });
20429
18991
  };
20430
18992
  const handleCollectSection = (e) => {
@@ -20770,24 +19332,19 @@ function OhhwellsBridge() {
20770
19332
  window.removeEventListener("message", handleAiApplyTree);
20771
19333
  window.removeEventListener("message", handleAiDeleteSection);
20772
19334
  window.removeEventListener("message", handleAiSetSections);
20773
- window.removeEventListener("message", handleMoveSection);
20774
- window.removeEventListener("message", handleAiSetBrand);
20775
- window.removeEventListener("message", handleAiSetStyles);
20776
- window.removeEventListener("message", handleGetBrand);
20777
19335
  window.removeEventListener("message", handlePanelDragging);
20778
- window.removeEventListener("message", handleDeleteSection);
20779
19336
  window.removeEventListener("message", handleDeactivate);
19337
+ document.documentElement.removeAttribute("data-ohw-panel-dragging");
20780
19338
  window.removeEventListener("message", handleToastAction);
20781
19339
  window.removeEventListener("message", handleFormCount);
20782
19340
  window.removeEventListener("message", handleUiEscape);
20783
- document.documentElement.removeAttribute("data-ohw-panel-dragging");
20784
19341
  autoSaveTimers.current.forEach(clearTimeout);
20785
19342
  autoSaveTimers.current.clear();
20786
19343
  if (imageUnhoverTimerRef.current) clearTimeout(imageUnhoverTimerRef.current);
20787
19344
  if (imageShowTimerRef.current) clearTimeout(imageShowTimerRef.current);
20788
19345
  };
20789
19346
  }, [isEditMode, refreshStateRules]);
20790
- (0, import_react17.useEffect)(() => {
19347
+ (0, import_react16.useEffect)(() => {
20791
19348
  if (!isEditMode) return;
20792
19349
  const THRESHOLD = 10;
20793
19350
  const resolveWasSelected = (el) => {
@@ -20943,7 +19500,7 @@ function OhhwellsBridge() {
20943
19500
  unlockFooterDragInteraction();
20944
19501
  };
20945
19502
  }, [isEditMode]);
20946
- (0, import_react17.useEffect)(() => {
19503
+ (0, import_react16.useEffect)(() => {
20947
19504
  const handler = (e) => {
20948
19505
  if (e.data?.type !== "ow:request-schedule-config") return;
20949
19506
  const insertAfterVal = e.data.insertAfter;
@@ -20959,7 +19516,7 @@ function OhhwellsBridge() {
20959
19516
  window.addEventListener("message", handler);
20960
19517
  return () => window.removeEventListener("message", handler);
20961
19518
  }, [processConfigRequest]);
20962
- (0, import_react17.useEffect)(() => {
19519
+ (0, import_react16.useEffect)(() => {
20963
19520
  if (!isEditMode) return;
20964
19521
  document.querySelectorAll("[data-ohw-active-state]").forEach((el) => {
20965
19522
  el.removeAttribute("data-ohw-active-state");
@@ -20983,7 +19540,7 @@ function OhhwellsBridge() {
20983
19540
  postToParent2({
20984
19541
  type: "ow:ready",
20985
19542
  version: "1",
20986
- bridgeVersion: "0.1.71",
19543
+ bridgeVersion: "0.1.70",
20987
19544
  path: pathname,
20988
19545
  nodes: collectEditableNodes(editContentRef.current),
20989
19546
  sections
@@ -20995,13 +19552,13 @@ function OhhwellsBridge() {
20995
19552
  clearTimeout(timer);
20996
19553
  };
20997
19554
  }, [pathname, isEditMode, refreshStateRules, postToParent2]);
20998
- (0, import_react17.useEffect)(() => {
19555
+ (0, import_react16.useEffect)(() => {
20999
19556
  scrollToHashSectionWhenReady();
21000
19557
  const onHashChange = () => scrollToHashSectionWhenReady();
21001
19558
  window.addEventListener("hashchange", onHashChange);
21002
19559
  return () => window.removeEventListener("hashchange", onHashChange);
21003
19560
  }, [pathname]);
21004
- const handleCommand = (0, import_react17.useCallback)((cmd) => {
19561
+ const handleCommand = (0, import_react16.useCallback)((cmd) => {
21005
19562
  const el = activeElRef.current;
21006
19563
  const selBefore = window.getSelection();
21007
19564
  let savedOffsets = null;
@@ -21037,7 +19594,7 @@ function OhhwellsBridge() {
21037
19594
  if (el) setToolbarRect(getEditMeasureEl(el).getBoundingClientRect());
21038
19595
  refreshActiveCommandsRef.current();
21039
19596
  }, []);
21040
- (0, import_react17.useEffect)(() => {
19597
+ (0, import_react16.useEffect)(() => {
21041
19598
  const session = floatingPanel && floatingPanel.kind === "logo-size" ? floatingPanel : null;
21042
19599
  if (!session || !logoSizeDraft) return;
21043
19600
  const onPanelAction = (e) => {
@@ -21075,7 +19632,7 @@ function OhhwellsBridge() {
21075
19632
  window.addEventListener("message", onPanelAction);
21076
19633
  return () => window.removeEventListener("message", onPanelAction);
21077
19634
  }, [floatingPanel, logoSizeDraft, editorViewport, persistLogoSizeDraft, closeFloatingPanelAndDeselect]);
21078
- const handleStateChange = (0, import_react17.useCallback)((state) => {
19635
+ const handleStateChange = (0, import_react16.useCallback)((state) => {
21079
19636
  if (!activeStateElRef.current) return;
21080
19637
  const el = activeStateElRef.current;
21081
19638
  if (state === "Default") {
@@ -21088,7 +19645,7 @@ function OhhwellsBridge() {
21088
19645
  }
21089
19646
  setToggleState((prev) => prev ? { ...prev, activeState: state } : null);
21090
19647
  }, [deactivate]);
21091
- const reselectAfterLinkPopover = (0, import_react17.useCallback)(
19648
+ const reselectAfterLinkPopover = (0, import_react16.useCallback)(
21092
19649
  (hrefKey) => {
21093
19650
  requestAnimationFrame(() => {
21094
19651
  const el = resolveHrefKeyElement(hrefKey);
@@ -21097,7 +19654,7 @@ function OhhwellsBridge() {
21097
19654
  },
21098
19655
  [resolveHrefKeyElement]
21099
19656
  );
21100
- const closeLinkPopover = (0, import_react17.useCallback)(() => {
19657
+ const closeLinkPopover = (0, import_react16.useCallback)(() => {
21101
19658
  const session = linkPopoverSessionRef.current;
21102
19659
  addNavAfterAnchorRef.current = null;
21103
19660
  setLinkPopover(null);
@@ -21105,9 +19662,9 @@ function OhhwellsBridge() {
21105
19662
  reselectAfterLinkPopover(session.key);
21106
19663
  }
21107
19664
  }, [reselectAfterLinkPopover]);
21108
- const closeLinkPopoverRef = (0, import_react17.useRef)(closeLinkPopover);
19665
+ const closeLinkPopoverRef = (0, import_react16.useRef)(closeLinkPopover);
21109
19666
  closeLinkPopoverRef.current = closeLinkPopover;
21110
- const openLinkPopoverForActive = (0, import_react17.useCallback)(() => {
19667
+ const openLinkPopoverForActive = (0, import_react16.useCallback)(() => {
21111
19668
  const hrefCtx = getHrefKeyFromElement(activeElRef.current);
21112
19669
  if (!hrefCtx) return;
21113
19670
  bumpLinkPopoverGrace();
@@ -21118,7 +19675,7 @@ function OhhwellsBridge() {
21118
19675
  });
21119
19676
  deactivate();
21120
19677
  }, [deactivate]);
21121
- const openLinkPopoverForSelected = (0, import_react17.useCallback)(() => {
19678
+ const openLinkPopoverForSelected = (0, import_react16.useCallback)(() => {
21122
19679
  const anchor = selectedElRef.current;
21123
19680
  if (!anchor) return;
21124
19681
  const key = anchor.getAttribute("data-ohw-href-key");
@@ -21135,7 +19692,7 @@ function OhhwellsBridge() {
21135
19692
  });
21136
19693
  deselect();
21137
19694
  }, [deselect]);
21138
- const handleSelectParent = (0, import_react17.useCallback)(() => {
19695
+ const handleSelectParent = (0, import_react16.useCallback)(() => {
21139
19696
  const selected = selectedElRef.current;
21140
19697
  if (!selected) return;
21141
19698
  if (toolbarVariantRef.current === "select-frame") {
@@ -21162,7 +19719,7 @@ function OhhwellsBridge() {
21162
19719
  }
21163
19720
  deselectRef.current();
21164
19721
  }, []);
21165
- const handleDuplicateSelected = (0, import_react17.useCallback)(() => {
19722
+ const handleDuplicateSelected = (0, import_react16.useCallback)(() => {
21166
19723
  const selected = selectedElRef.current;
21167
19724
  if (!selected || !isNavigationItem2(selected)) return;
21168
19725
  const hrefKey = selected.getAttribute("data-ohw-href-key");
@@ -21295,7 +19852,7 @@ function OhhwellsBridge() {
21295
19852
  });
21296
19853
  }
21297
19854
  }, [postToParent2]);
21298
- const runPendingDeleteUndo = (0, import_react17.useCallback)(() => {
19855
+ const runPendingDeleteUndo = (0, import_react16.useCallback)(() => {
21299
19856
  const pending = pendingDeleteUndoRef.current;
21300
19857
  if (!pending) return false;
21301
19858
  pendingDeleteUndoRef.current = null;
@@ -21303,7 +19860,7 @@ function OhhwellsBridge() {
21303
19860
  enforceLinkHrefs();
21304
19861
  return true;
21305
19862
  }, []);
21306
- const handleDeleteSelected = (0, import_react17.useCallback)(() => {
19863
+ const handleDeleteSelected = (0, import_react16.useCallback)(() => {
21307
19864
  const selected = selectedElRef.current;
21308
19865
  if (!selected) return false;
21309
19866
  return deleteSelectedNavFooterItem({
@@ -21324,7 +19881,7 @@ function OhhwellsBridge() {
21324
19881
  }, [postToParent2]);
21325
19882
  handleDeleteSelectedRef.current = handleDeleteSelected;
21326
19883
  runPendingDeleteUndoRef.current = runPendingDeleteUndo;
21327
- const handleLinkPopoverSubmit = (0, import_react17.useCallback)(
19884
+ const handleLinkPopoverSubmit = (0, import_react16.useCallback)(
21328
19885
  (target) => {
21329
19886
  const session = linkPopoverSessionRef.current;
21330
19887
  if (!session) return;
@@ -21390,30 +19947,19 @@ function OhhwellsBridge() {
21390
19947
  const showEditLink = toolbarShowEditLink;
21391
19948
  const currentSections = sectionsByPath[pathname] ?? [];
21392
19949
  linkPopoverOpenRef.current = linkPopover !== null;
21393
- const handleMediaSelect = (0, import_react17.useCallback)((key) => {
21394
- const el = hoveredImageRef.current?.dataset.ohwKey === key ? hoveredImageRef.current : Array.from(document.querySelectorAll(MEDIA_SELECTOR)).find(
21395
- (m) => (m.dataset.ohwKey ?? "") === key
21396
- ) ?? null;
21397
- if (!el) return;
21398
- selectMediaElementRef.current(el);
21399
- }, []);
21400
- const handleMediaReplace = (0, import_react17.useCallback)(
19950
+ const handleMediaReplace = (0, import_react16.useCallback)(
21401
19951
  (key) => {
21402
- postToParent2({
21403
- type: "ow:image-pick",
21404
- key,
21405
- elementType: mediaHover?.elementType ?? selectedMedia?.elementType ?? "image"
21406
- });
19952
+ postToParent2({ type: "ow:image-pick", key, elementType: mediaHover?.elementType ?? "image" });
21407
19953
  },
21408
- [postToParent2, mediaHover?.elementType, selectedMedia?.elementType]
19954
+ [postToParent2, mediaHover?.elementType]
21409
19955
  );
21410
- const handleEditCarousel = (0, import_react17.useCallback)(
19956
+ const handleEditCarousel = (0, import_react16.useCallback)(
21411
19957
  (key) => {
21412
19958
  postToParent2({ type: "ow:carousel-open", key, images: readCarouselValue(key) });
21413
19959
  },
21414
19960
  [postToParent2]
21415
19961
  );
21416
- const handleMediaFadeOutComplete = (0, import_react17.useCallback)((key) => {
19962
+ const handleMediaFadeOutComplete = (0, import_react16.useCallback)((key) => {
21417
19963
  setUploadingRects((prev) => {
21418
19964
  if (!(key in prev)) return prev;
21419
19965
  const next = { ...prev };
@@ -21421,7 +19967,7 @@ function OhhwellsBridge() {
21421
19967
  return next;
21422
19968
  });
21423
19969
  }, []);
21424
- const handleVideoSettingsChange = (0, import_react17.useCallback)(
19970
+ const handleVideoSettingsChange = (0, import_react16.useCallback)(
21425
19971
  (key, settings) => {
21426
19972
  document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
21427
19973
  const video = getVideoEl2(el);
@@ -21443,516 +19989,430 @@ function OhhwellsBridge() {
21443
19989
  },
21444
19990
  [postToParent2]
21445
19991
  );
21446
- return /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(import_jsx_runtime33.Fragment, { children: [
21447
- /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("div", { id: "ohw-loader", suppressHydrationWarning: true, style: { ...OHW_LOADER_STYLE, display: "none" }, children: /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(OhwLoaderSpinner, {}) }),
21448
- /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("script", { suppressHydrationWarning: true, dangerouslySetInnerHTML: { __html: OHW_LOADER_PREHYDRATE_SCRIPT } }),
21449
- bridgeRoot ? (0, import_react_dom4.createPortal)(
21450
- /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(import_jsx_runtime33.Fragment, { children: [
21451
- /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("div", { ref: attachVisibleViewport, "data-ohw-visible-viewport": "", "aria-hidden": true }),
21452
- isEditMode && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(AiSectionOverlay, { postToParent: postToParent2, apiRef: aiSectionApiRef }),
21453
- isSectionDragging && sectionDropSlots.map((slot, i) => /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
21454
- "div",
21455
- {
21456
- className: "pointer-events-none fixed z-2147483646",
21457
- style: { left: slot.left, top: slot.y, width: slot.width, height: 3, transform: "translateY(-50%)" },
21458
- children: /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
21459
- DropIndicator,
21460
- {
21461
- direction: "horizontal",
21462
- state: activeSectionDropIndex === i ? "dragActive" : "dragIdle",
21463
- className: "!h-full !w-full"
21464
- }
21465
- )
21466
- },
21467
- `section-drop-${slot.insertIndex}-${i}`
21468
- )),
21469
- Object.entries(uploadingRects).map(([key, { rect, fadingOut }]) => /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
21470
- MediaOverlay,
21471
- {
21472
- hover: { key, rect, elementType: "image", isDragOver: false, hasTextOverlap: false },
21473
- isUploading: true,
21474
- fadingOut,
21475
- onFadeOutComplete: handleMediaFadeOutComplete,
21476
- onReplace: handleMediaReplace
21477
- },
21478
- `uploading-${key}`
21479
- )),
21480
- mediaHover && !(mediaHover.key in uploadingRects) && mediaHover.key !== selectedMedia?.key && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
21481
- MediaOverlay,
21482
- {
21483
- hover: mediaHover,
21484
- isUploading: false,
21485
- onReplace: handleMediaReplace,
21486
- onSelect: handleMediaSelect,
21487
- onVideoSettingsChange: handleVideoSettingsChange
21488
- }
21489
- ),
21490
- selectedMedia && !(selectedMedia.key in uploadingRects) && !linkPopover && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
21491
- MediaOverlay,
21492
- {
21493
- hover: selectedMedia,
21494
- selected: true,
21495
- hovered: mediaHover?.key === selectedMedia.key,
21496
- isUploading: false,
21497
- onReplace: handleMediaReplace,
21498
- onSelect: handleMediaSelect,
21499
- onVideoSettingsChange: handleVideoSettingsChange
21500
- }
21501
- ),
21502
- carouselHover && !linkPopover && !isItemDragging && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(CarouselOverlay, { hover: carouselHover, onEdit: handleEditCarousel }),
21503
- siblingHintRect && !linkPopover && !isItemDragging && siblingHintRects.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(ItemInteractionLayer, { rect: siblingHintRect, state: "sibling-hint" }),
21504
- siblingHintRects.length > 0 && !linkPopover && !isItemDragging && siblingHintRects.map((rect, i) => /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(ItemInteractionLayer, { rect, state: "sibling-hint" }, `sibling-hint-${i}`)),
21505
- isItemDragging && draggedItemRect && selectedElRef.current !== footerDragRef.current?.draggedEl && selectedElRef.current !== navDragRef.current?.draggedEl && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(ItemInteractionLayer, { rect: draggedItemRect, state: "dragging" }),
21506
- isItemDragging && footerDropSlots.map((slot, i) => /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
21507
- "div",
21508
- {
21509
- className: "pointer-events-none fixed z-2147483646",
21510
- style: {
21511
- left: slot.left,
21512
- top: slot.top,
21513
- width: slot.width,
21514
- height: slot.height
21515
- },
21516
- children: /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
21517
- DropIndicator,
21518
- {
21519
- direction: slot.direction,
21520
- state: activeFooterDropIndex === i ? "dragActive" : "dragIdle",
21521
- className: "!h-full !w-full"
21522
- }
21523
- )
21524
- },
21525
- `footer-drop-${slot.direction}-${slot.columnIndex}-${slot.insertIndex}-${i}`
21526
- )),
21527
- isItemDragging && navDropSlots.map((slot, i) => /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
21528
- "div",
21529
- {
21530
- className: "pointer-events-none fixed z-2147483646",
21531
- style: {
21532
- left: slot.left,
21533
- top: slot.top,
21534
- width: slot.width,
21535
- height: slot.height
21536
- },
21537
- children: /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
21538
- DropIndicator,
21539
- {
21540
- direction: slot.direction,
21541
- state: activeNavDropIndex === i ? "dragActive" : "dragIdle",
21542
- className: "!h-full !w-full"
21543
- }
21544
- )
19992
+ return bridgeRoot ? (0, import_react_dom4.createPortal)(
19993
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(import_jsx_runtime33.Fragment, { children: [
19994
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("div", { ref: attachVisibleViewport, "data-ohw-visible-viewport": "", "aria-hidden": true }),
19995
+ isEditMode && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(AiSectionOverlay, { postToParent: postToParent2, apiRef: aiSectionApiRef }),
19996
+ Object.entries(uploadingRects).map(([key, { rect, fadingOut }]) => /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
19997
+ MediaOverlay,
19998
+ {
19999
+ hover: { key, rect, elementType: "image", isDragOver: false, hasTextOverlap: false },
20000
+ isUploading: true,
20001
+ fadingOut,
20002
+ onFadeOutComplete: handleMediaFadeOutComplete,
20003
+ onReplace: handleMediaReplace
20004
+ },
20005
+ `uploading-${key}`
20006
+ )),
20007
+ mediaHover && !(mediaHover.key in uploadingRects) && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
20008
+ MediaOverlay,
20009
+ {
20010
+ hover: mediaHover,
20011
+ isUploading: false,
20012
+ onReplace: handleMediaReplace,
20013
+ onVideoSettingsChange: handleVideoSettingsChange
20014
+ }
20015
+ ),
20016
+ carouselHover && !linkPopover && !isItemDragging && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(CarouselOverlay, { hover: carouselHover, onEdit: handleEditCarousel }),
20017
+ siblingHintRect && !linkPopover && !isItemDragging && siblingHintRects.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(ItemInteractionLayer, { rect: siblingHintRect, state: "sibling-hint" }),
20018
+ siblingHintRects.length > 0 && !linkPopover && !isItemDragging && siblingHintRects.map((rect, i) => /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(ItemInteractionLayer, { rect, state: "sibling-hint" }, `sibling-hint-${i}`)),
20019
+ isItemDragging && draggedItemRect && selectedElRef.current !== footerDragRef.current?.draggedEl && selectedElRef.current !== navDragRef.current?.draggedEl && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(ItemInteractionLayer, { rect: draggedItemRect, state: "dragging" }),
20020
+ isItemDragging && footerDropSlots.map((slot, i) => /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
20021
+ "div",
20022
+ {
20023
+ className: "pointer-events-none fixed z-2147483646",
20024
+ style: {
20025
+ left: slot.left,
20026
+ top: slot.top,
20027
+ width: slot.width,
20028
+ height: slot.height
21545
20029
  },
21546
- `nav-drop-${slot.direction}-${slot.parentId ?? "root"}-${slot.insertIndex}-${i}`
21547
- )),
21548
- hoveredNavContainerRect && (toolbarVariant !== "select-frame" || isFooterFrameSelection) && !linkPopover && !isItemDragging && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(ItemInteractionLayer, { rect: hoveredNavContainerRect, state: "hover" }),
21549
- hoveredItemRect && !linkPopover && !hoveredNavContainerRect && !isItemDragging && hoveredItemElRef.current !== selectedElRef.current && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(ItemInteractionLayer, { rect: hoveredItemRect, state: "hover" }),
21550
- hoveredTextRect && !hoveredNavContainerRect && !hoveredItemRect && !isItemDragging && toolbarVariant !== "rich-text" && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(ItemInteractionLayer, { rect: hoveredTextRect, state: "hover" }),
21551
- formPickRect && !isItemDragging && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
21552
- ItemInteractionLayer,
21553
- {
21554
- rect: formPickRect,
21555
- state: "active-top",
21556
- itemDragSurface: false,
21557
- toolbarAlign: "left",
21558
- chromeGap: 24,
21559
- toolbar: fieldPickRect ? void 0 : /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(
21560
- "div",
21561
- {
21562
- "data-ohw-form-toolbar": "",
21563
- className: "pointer-events-auto flex items-center gap-0.5 whitespace-nowrap rounded-lg border border-border bg-background p-1 shadow-md",
21564
- children: [
21565
- /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
21566
- "button",
21567
- {
21568
- type: "button",
21569
- "aria-label": "Add field",
21570
- className: "flex h-7 w-7 items-center justify-center rounded-md text-foreground transition-colors hover:bg-muted/80",
21571
- onClick: () => setFieldTypePickerOpen((open) => !open),
21572
- "data-ohw-add-field": "",
21573
- children: /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(import_lucide_react17.Plus, { size: 15, "aria-hidden": true })
21574
- }
21575
- ),
21576
- /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("div", { className: "mx-0.5 h-5 w-px bg-border" }),
21577
- /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(
21578
- "button",
21579
- {
21580
- type: "button",
21581
- className: "flex h-7 items-center gap-1.5 whitespace-nowrap rounded-md px-2 text-[13px] font-semibold text-foreground transition-colors hover:bg-muted/80",
21582
- onClick: () => {
21583
- setFieldTypePickerOpen(false);
21584
- const form = formPickElRef.current;
21585
- if (!form) return;
21586
- postToParent2({
21587
- type: "ow:form-pick",
21588
- formKey: formKeyOf(form),
21589
- hasLongText: formHasLongText(form)
21590
- });
21591
- },
21592
- children: [
21593
- /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(import_lucide_react17.Settings, { size: 14, "aria-hidden": true }),
21594
- "Form settings",
21595
- formPickCount ? (
21596
- // Counter pill, per the design — not a text suffix.
21597
- /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
21598
- "span",
21599
- {
21600
- "data-ohw-form-count": "",
21601
- className: "ml-0.5 inline-flex h-[18px] min-w-[18px] items-center justify-center rounded-full bg-primary px-1.5 text-[11px] font-semibold text-primary-foreground",
21602
- children: formPickCount
21603
- }
21604
- )
21605
- ) : null
21606
- ]
21607
- }
21608
- ),
21609
- /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("div", { className: "mx-0.5 h-5 w-px bg-border" }),
21610
- /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("div", { className: "flex items-center rounded-md bg-muted/70 p-0.5", children: ["default", "success"].map((state) => /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
21611
- "button",
21612
- {
21613
- type: "button",
21614
- "aria-pressed": formViewState === state,
21615
- className: "rounded-md px-2.5 py-1 text-[13px] font-semibold capitalize transition-colors " + (formViewState === state ? "bg-background text-foreground shadow-sm" : "text-muted-foreground hover:text-foreground"),
21616
- onClick: () => {
21617
- setFieldTypePickerOpen(false);
21618
- const form = formPickElRef.current;
21619
- const key = form ? formKeyOf(form) : null;
21620
- if (!form || !key) return;
21621
- const initial = successInitialFor(form, key, editContentRef.current);
21622
- setFormViewState(form, key, state, initial);
21623
- setFormViewStateUi(state);
21624
- setFormPickRect(form.getBoundingClientRect());
21625
- if (state === "success") {
21626
- const successEl = form.querySelector(`[${SUCCESS_TEXT_ATTR}]`);
21627
- if (successEl) requestAnimationFrame(() => activateRef.current(successEl));
21628
- } else {
21629
- deactivateRef.current();
21630
- }
21631
- },
21632
- children: state
21633
- },
21634
- state
21635
- )) })
21636
- ]
21637
- }
21638
- )
21639
- }
21640
- ),
21641
- formHoverRect && !isItemDragging && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
21642
- ItemInteractionLayer,
21643
- {
21644
- rect: formHoverRect,
21645
- state: "hover",
21646
- chromeGap: formHoverElRef.current && getFieldWrapper(formHoverElRef.current) ? 8 : 24
21647
- }
21648
- ),
21649
- fieldPickRect && fieldPickState && !isItemDragging && toolbarVariant !== "rich-text" && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
21650
- ItemInteractionLayer,
21651
- {
21652
- rect: fieldPickRect,
21653
- state: fieldDragging ? "dragging" : "active-top",
21654
- itemDragSurface: false,
21655
- toolbarAlign: "left",
21656
- chromeGap: 10,
21657
- showHandle: true,
21658
- dragHandleLabel: "Reorder field",
21659
- onDragHandleDragStart: handleFieldDragStart,
21660
- onDragHandleDragEnd: handleFieldDragEnd,
21661
- toolbar: /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
21662
- FormFieldToolbar,
21663
- {
21664
- type: fieldPickState.type,
21665
- required: fieldPickState.required,
21666
- onTypeChange: handleFieldTypeChange,
21667
- onRequiredToggle: handleFieldRequiredToggle,
21668
- onDuplicate: handleFieldDuplicate,
21669
- onDelete: handleFieldDelete
21670
- }
21671
- )
21672
- }
21673
- ),
21674
- fieldDragging ? fieldDropSlots.map((slot, i) => /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
21675
- "div",
21676
- {
21677
- className: "pointer-events-none fixed z-[2147483644]",
21678
- style: { top: slot.top, left: slot.left, width: slot.width },
21679
- children: /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
21680
- DropIndicator,
21681
- {
21682
- direction: "horizontal",
21683
- state: fieldDropIndex === i ? "dragActive" : "dragIdle",
21684
- className: "!w-full"
21685
- }
21686
- )
20030
+ children: /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
20031
+ DropIndicator,
20032
+ {
20033
+ direction: slot.direction,
20034
+ state: activeFooterDropIndex === i ? "dragActive" : "dragIdle",
20035
+ className: "!h-full !w-full"
20036
+ }
20037
+ )
20038
+ },
20039
+ `footer-drop-${slot.direction}-${slot.columnIndex}-${slot.insertIndex}-${i}`
20040
+ )),
20041
+ isItemDragging && navDropSlots.map((slot, i) => /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
20042
+ "div",
20043
+ {
20044
+ className: "pointer-events-none fixed z-2147483646",
20045
+ style: {
20046
+ left: slot.left,
20047
+ top: slot.top,
20048
+ width: slot.width,
20049
+ height: slot.height
21687
20050
  },
21688
- `field-drop-${i}`
21689
- )) : null,
21690
- fieldTypePickerOpen && formPickRect ? (() => {
21691
- const toolbar = document.querySelector("[data-ohw-form-toolbar]")?.getBoundingClientRect();
21692
- return /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
21693
- "div",
20051
+ children: /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
20052
+ DropIndicator,
21694
20053
  {
21695
- className: "pointer-events-none fixed z-[2147483645]",
21696
- style: {
21697
- top: toolbar ? toolbar.bottom + 6 : formPickRect.top + 16,
21698
- left: toolbar ? toolbar.left : formPickRect.left + 24
21699
- },
21700
- children: /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(FieldTypePicker, { onPick: handleAddField })
20054
+ direction: slot.direction,
20055
+ state: activeNavDropIndex === i ? "dragActive" : "dragIdle",
20056
+ className: "!h-full !w-full"
21701
20057
  }
21702
- );
21703
- })() : null,
21704
- toolbarVariant === "select-frame" && toolbarRect && !linkPopover && !isItemDragging && !isFooterFrameSelection && selectedElRef.current && isNavbarLinksContainer2(selectedElRef.current) && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(NavbarContainerChrome, { rect: toolbarRect, onAdd: handleAddTopLevelNavItem }),
21705
- toolbarVariant === "select-frame" && toolbarRect && !linkPopover && !isItemDragging && !isFooterFrameSelection && selectedElRef.current && isFooterLinksContainer(selectedElRef.current) && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
21706
- FooterContainerChrome,
21707
- {
21708
- rect: toolbarRect,
21709
- onAdd: handleAddFooterColumn,
21710
- addDisabled: !canAddFooterColumn()
21711
- }
21712
- ),
21713
- toolbarRect && !linkPopover && (toolbarVariant === "link-action" || toolbarVariant === "select-frame" || toolbarVariant === "logo") && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
21714
- ItemInteractionLayer,
21715
- {
21716
- rect: isItemDragging && draggedItemRect && (footerDragRef.current?.wasSelected || navDragRef.current?.wasSelected) ? draggedItemRect : toolbarRect,
21717
- toolbarBelowRect: selectedElRef.current && isNavigationItem2(selectedElRef.current) ? getOpenNavDropdownPanelRect(selectedElRef.current) : null,
21718
- elRef: glowElRef,
21719
- state: isItemDragging ? "dragging" : selectedElRef.current && isNavigationItem2(selectedElRef.current) && getOpenNavDropdownPanelRect(selectedElRef.current) ? "active-bottom" : resolveItemInteractionState(toolbarRect, parentScrollRef.current),
21720
- showHandle: toolbarVariant === "link-action" && Boolean(reorderHrefKey) || toolbarVariant === "select-frame" && isFooterFrameSelection,
21721
- dragDisabled: reorderDragDisabled,
21722
- dragHandleLabel: reorderHrefKey ? `Reorder ${reorderHrefKey}` : "Reorder item",
21723
- onDragHandleDragStart: handleItemDragStart,
21724
- onDragHandleDragEnd: handleItemDragEnd,
21725
- onItemPointerDown: toolbarVariant === "logo" ? void 0 : handleItemChromePointerDown,
21726
- onItemClick: toolbarVariant === "logo" ? void 0 : handleItemChromeClick,
21727
- itemDragSurface: toolbarVariant !== "logo" && !isFooterFrameSelection && !selectedIsSocialsRow,
21728
- toolbar: toolbarVariant === "link-action" && !isItemDragging || toolbarVariant === "select-frame" && (isFooterFrameSelection || selectedIsSocialsRow) && !isItemDragging ? /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
21729
- ItemActionToolbar,
21730
- {
21731
- onEditLink: openLinkPopoverForSelected,
21732
- onStyle: () => {
21733
- const row = selectedElRef.current;
21734
- if (!row) return;
21735
- if (floatingPanel?.kind === "socials-display") closeFloatingPanelOnly();
21736
- else openSocialsDisplayPanel(row);
21737
- },
21738
- showStyle: selectedIsSocialsRow,
21739
- styleActive: floatingPanel?.kind === "socials-display",
21740
- onAddItem: handleAddChildItem,
21741
- onSelectParent: handleSelectParent,
21742
- onDuplicate: handleDuplicateSelected,
21743
- onDelete: handleDeleteSelected,
21744
- addItemDisabled: isFooterFrameSelection && isFooterAddItemDisabled(selectedElRef.current) || selectedElRef.current !== null && (() => {
21745
- const row = isSocialsRow(selectedElRef.current) ? selectedElRef.current : findSocialsRow(selectedElRef.current);
21746
- return row ? !canAddSocialItem(row) : false;
21747
- })(),
21748
- editLinkDisabled: false,
21749
- moreDisabled: false,
21750
- deleteDisabled: selectedElRef.current !== null && (() => {
21751
- const social = getSocialItem(selectedElRef.current);
21752
- return social ? !canRemoveSocialItem(social) : false;
21753
- })(),
21754
- duplicateDisabled: isFooterFrameSelection || selectedIsSocialsRow || selectedElRef.current !== null && (() => {
21755
- const social = getSocialItem(selectedElRef.current);
21756
- const row = social ? findSocialsRow(social) : null;
21757
- return row ? !canAddSocialItem(row) : false;
21758
- })(),
21759
- showEditLink: !isFooterFrameSelection && !selectedIsSocialsRow && navDropdownPreviewOpen === null,
21760
- showAddItem: isFooterFrameSelection || selectedIsSocialsRow || !selectedIsCta && Boolean(
21761
- selectedElRef.current && isNavbarHrefKey(selectedElRef.current.getAttribute("data-ohw-href-key")) && !isNestedNavChild(selectedElRef.current)
20058
+ )
20059
+ },
20060
+ `nav-drop-${slot.direction}-${slot.parentId ?? "root"}-${slot.insertIndex}-${i}`
20061
+ )),
20062
+ hoveredNavContainerRect && (toolbarVariant !== "select-frame" || isFooterFrameSelection) && !linkPopover && !isItemDragging && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(ItemInteractionLayer, { rect: hoveredNavContainerRect, state: "hover" }),
20063
+ hoveredItemRect && !linkPopover && !hoveredNavContainerRect && !isItemDragging && hoveredItemElRef.current !== selectedElRef.current && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(ItemInteractionLayer, { rect: hoveredItemRect, state: "hover" }),
20064
+ hoveredTextRect && !hoveredNavContainerRect && !hoveredItemRect && !isItemDragging && toolbarVariant !== "rich-text" && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(ItemInteractionLayer, { rect: hoveredTextRect, state: "hover" }),
20065
+ formPickRect && !isItemDragging && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
20066
+ ItemInteractionLayer,
20067
+ {
20068
+ rect: formPickRect,
20069
+ state: "active-top",
20070
+ itemDragSurface: false,
20071
+ toolbarAlign: "left",
20072
+ chromeGap: 24,
20073
+ toolbar: fieldPickRect ? void 0 : /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(
20074
+ "div",
20075
+ {
20076
+ "data-ohw-form-toolbar": "",
20077
+ className: "pointer-events-auto flex items-center gap-0.5 whitespace-nowrap rounded-lg border border-border bg-background p-1 shadow-md",
20078
+ children: [
20079
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
20080
+ "button",
20081
+ {
20082
+ type: "button",
20083
+ "aria-label": "Add field",
20084
+ className: "flex h-7 w-7 items-center justify-center rounded-md text-foreground transition-colors hover:bg-muted/80",
20085
+ onClick: () => setFieldTypePickerOpen((open) => !open),
20086
+ "data-ohw-add-field": "",
20087
+ children: /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(import_lucide_react17.Plus, { size: 15, "aria-hidden": true })
20088
+ }
21762
20089
  ),
21763
- showMore: (!selectedIsCta || isFooterFrameSelection) && !selectedIsSocialsRow,
21764
- dropdownOpen: navDropdownPreviewOpen,
21765
- onDropdownOpenChange: handleNavDropdownOpenChange,
21766
- headingVisible: footerHeadingVisible,
21767
- onHeadingVisibleChange: handleFooterHeadingVisibleChange
21768
- }
21769
- ) : void 0
21770
- }
21771
- ),
21772
- toolbarRect && toolbarVariant === "rich-text" && !linkPopover && /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(import_jsx_runtime33.Fragment, { children: [
21773
- /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
21774
- EditGlowChrome,
20090
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("div", { className: "mx-0.5 h-5 w-px bg-border" }),
20091
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(
20092
+ "button",
20093
+ {
20094
+ type: "button",
20095
+ className: "flex h-7 items-center gap-1.5 whitespace-nowrap rounded-md px-2 text-[13px] font-semibold text-foreground transition-colors hover:bg-muted/80",
20096
+ onClick: () => {
20097
+ setFieldTypePickerOpen(false);
20098
+ const form = formPickElRef.current;
20099
+ if (!form) return;
20100
+ postToParent2({
20101
+ type: "ow:form-pick",
20102
+ formKey: formKeyOf(form),
20103
+ hasLongText: formHasLongText(form)
20104
+ });
20105
+ },
20106
+ children: [
20107
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(import_lucide_react17.Settings, { size: 14, "aria-hidden": true }),
20108
+ "Form settings",
20109
+ formPickCount ? (
20110
+ // Counter pill, per the design — not a text suffix.
20111
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
20112
+ "span",
20113
+ {
20114
+ "data-ohw-form-count": "",
20115
+ className: "ml-0.5 inline-flex h-[18px] min-w-[18px] items-center justify-center rounded-full bg-primary px-1.5 text-[11px] font-semibold text-primary-foreground",
20116
+ children: formPickCount
20117
+ }
20118
+ )
20119
+ ) : null
20120
+ ]
20121
+ }
20122
+ ),
20123
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("div", { className: "mx-0.5 h-5 w-px bg-border" }),
20124
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("div", { className: "flex items-center rounded-md bg-muted/70 p-0.5", children: ["default", "success"].map((state) => /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
20125
+ "button",
20126
+ {
20127
+ type: "button",
20128
+ "aria-pressed": formViewState === state,
20129
+ className: "rounded-md px-2.5 py-1 text-[13px] font-semibold capitalize transition-colors " + (formViewState === state ? "bg-background text-foreground shadow-sm" : "text-muted-foreground hover:text-foreground"),
20130
+ onClick: () => {
20131
+ setFieldTypePickerOpen(false);
20132
+ const form = formPickElRef.current;
20133
+ const key = form ? formKeyOf(form) : null;
20134
+ if (!form || !key) return;
20135
+ const initial = successInitialFor(form, key, editContentRef.current);
20136
+ setFormViewState(form, key, state, initial);
20137
+ setFormViewStateUi(state);
20138
+ setFormPickRect(form.getBoundingClientRect());
20139
+ if (state === "success") {
20140
+ const successEl = form.querySelector(`[${SUCCESS_TEXT_ATTR}]`);
20141
+ if (successEl) requestAnimationFrame(() => activateRef.current(successEl));
20142
+ } else {
20143
+ deactivateRef.current();
20144
+ }
20145
+ },
20146
+ children: state
20147
+ },
20148
+ state
20149
+ )) })
20150
+ ]
20151
+ }
20152
+ )
20153
+ }
20154
+ ),
20155
+ formHoverRect && !isItemDragging && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
20156
+ ItemInteractionLayer,
20157
+ {
20158
+ rect: formHoverRect,
20159
+ state: "hover",
20160
+ chromeGap: formHoverElRef.current && getFieldWrapper(formHoverElRef.current) ? 8 : 24
20161
+ }
20162
+ ),
20163
+ fieldPickRect && fieldPickState && !isItemDragging && toolbarVariant !== "rich-text" && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
20164
+ ItemInteractionLayer,
20165
+ {
20166
+ rect: fieldPickRect,
20167
+ state: fieldDragging ? "dragging" : "active-top",
20168
+ itemDragSurface: false,
20169
+ toolbarAlign: "left",
20170
+ chromeGap: 10,
20171
+ showHandle: true,
20172
+ dragHandleLabel: "Reorder field",
20173
+ onDragHandleDragStart: handleFieldDragStart,
20174
+ onDragHandleDragEnd: handleFieldDragEnd,
20175
+ toolbar: /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
20176
+ FormFieldToolbar,
21775
20177
  {
21776
- rect: toolbarRect,
21777
- elRef: glowElRef,
21778
- reorderHrefKey,
21779
- dragDisabled: reorderDragDisabled,
21780
- hideHandle: isItemDragging
20178
+ type: fieldPickState.type,
20179
+ required: fieldPickState.required,
20180
+ onTypeChange: handleFieldTypeChange,
20181
+ onRequiredToggle: handleFieldRequiredToggle,
20182
+ onDuplicate: handleFieldDuplicate,
20183
+ onDelete: handleFieldDelete
21781
20184
  }
21782
- ),
21783
- /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
21784
- FloatingToolbar,
20185
+ )
20186
+ }
20187
+ ),
20188
+ fieldDragging ? fieldDropSlots.map((slot, i) => /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
20189
+ "div",
20190
+ {
20191
+ className: "pointer-events-none fixed z-[2147483644]",
20192
+ style: { top: slot.top, left: slot.left, width: slot.width },
20193
+ children: /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
20194
+ DropIndicator,
21785
20195
  {
21786
- rect: toolbarRect,
21787
- parentScroll: parentScrollRef.current,
21788
- elRef: toolbarElRef,
21789
- onCommand: handleCommand,
21790
- activeCommands,
21791
- showEditLink,
21792
- onEditLink: openLinkPopoverForActive
20196
+ direction: "horizontal",
20197
+ state: fieldDropIndex === i ? "dragActive" : "dragIdle",
20198
+ className: "!w-full"
21793
20199
  }
21794
20200
  )
21795
- ] }),
21796
- maxBadge && /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(
20201
+ },
20202
+ `field-drop-${i}`
20203
+ )) : null,
20204
+ fieldTypePickerOpen && formPickRect ? (() => {
20205
+ const toolbar = document.querySelector("[data-ohw-form-toolbar]")?.getBoundingClientRect();
20206
+ return /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
21797
20207
  "div",
21798
20208
  {
21799
- "data-ohw-max-badge": "",
20209
+ className: "pointer-events-none fixed z-[2147483645]",
21800
20210
  style: {
21801
- position: "fixed",
21802
- top: maxBadge.rect.bottom + 4,
21803
- left: maxBadge.rect.right,
21804
- transform: "translateX(-100%)",
21805
- zIndex: 2147483647,
21806
- background: maxBadge.current > maxBadge.max ? "#FEF2F2" : "#F5F5F4",
21807
- color: maxBadge.current > maxBadge.max ? "#DC2626" : "#78716C",
21808
- border: `1px solid ${maxBadge.current > maxBadge.max ? "#FECACA" : "#E7E5E4"}`,
21809
- borderRadius: 4,
21810
- padding: "2px 6px",
21811
- fontSize: 11,
21812
- fontWeight: 500,
21813
- pointerEvents: "none"
20211
+ top: toolbar ? toolbar.bottom + 6 : formPickRect.top + 16,
20212
+ left: toolbar ? toolbar.left : formPickRect.left + 24
21814
20213
  },
21815
- children: [
21816
- maxBadge.current,
21817
- "/",
21818
- maxBadge.max
21819
- ]
20214
+ children: /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(FieldTypePicker, { onPick: handleAddField })
21820
20215
  }
21821
- ),
21822
- toggleState && !linkPopover && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
21823
- StateToggle,
20216
+ );
20217
+ })() : null,
20218
+ toolbarVariant === "select-frame" && toolbarRect && !linkPopover && !isItemDragging && !isFooterFrameSelection && selectedElRef.current && isNavbarLinksContainer2(selectedElRef.current) && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(NavbarContainerChrome, { rect: toolbarRect, onAdd: handleAddTopLevelNavItem }),
20219
+ toolbarVariant === "select-frame" && toolbarRect && !linkPopover && !isItemDragging && !isFooterFrameSelection && selectedElRef.current && isFooterLinksContainer(selectedElRef.current) && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
20220
+ FooterContainerChrome,
20221
+ {
20222
+ rect: toolbarRect,
20223
+ onAdd: handleAddFooterColumn,
20224
+ addDisabled: !canAddFooterColumn()
20225
+ }
20226
+ ),
20227
+ toolbarRect && !linkPopover && (toolbarVariant === "link-action" || toolbarVariant === "select-frame" || toolbarVariant === "logo") && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
20228
+ ItemInteractionLayer,
20229
+ {
20230
+ rect: isItemDragging && draggedItemRect && (footerDragRef.current?.wasSelected || navDragRef.current?.wasSelected) ? draggedItemRect : toolbarRect,
20231
+ toolbarBelowRect: selectedElRef.current && isNavigationItem2(selectedElRef.current) ? getOpenNavDropdownPanelRect(selectedElRef.current) : null,
20232
+ elRef: glowElRef,
20233
+ state: isItemDragging ? "dragging" : selectedElRef.current && isNavigationItem2(selectedElRef.current) && getOpenNavDropdownPanelRect(selectedElRef.current) ? "active-bottom" : resolveItemInteractionState(toolbarRect, parentScrollRef.current),
20234
+ showHandle: toolbarVariant === "link-action" && Boolean(reorderHrefKey) || toolbarVariant === "select-frame" && isFooterFrameSelection,
20235
+ dragDisabled: reorderDragDisabled,
20236
+ dragHandleLabel: reorderHrefKey ? `Reorder ${reorderHrefKey}` : "Reorder item",
20237
+ onDragHandleDragStart: handleItemDragStart,
20238
+ onDragHandleDragEnd: handleItemDragEnd,
20239
+ onItemPointerDown: toolbarVariant === "logo" ? void 0 : handleItemChromePointerDown,
20240
+ onItemClick: toolbarVariant === "logo" ? void 0 : handleItemChromeClick,
20241
+ itemDragSurface: toolbarVariant !== "logo" && !isFooterFrameSelection && !selectedIsSocialsRow,
20242
+ toolbar: toolbarVariant === "link-action" && !isItemDragging || toolbarVariant === "select-frame" && (isFooterFrameSelection || selectedIsSocialsRow) && !isItemDragging ? /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
20243
+ ItemActionToolbar,
20244
+ {
20245
+ onEditLink: openLinkPopoverForSelected,
20246
+ onStyle: () => {
20247
+ const row = selectedElRef.current;
20248
+ if (!row) return;
20249
+ if (floatingPanel?.kind === "socials-display") closeFloatingPanelOnly();
20250
+ else openSocialsDisplayPanel(row);
20251
+ },
20252
+ showStyle: selectedIsSocialsRow,
20253
+ styleActive: floatingPanel?.kind === "socials-display",
20254
+ onAddItem: handleAddChildItem,
20255
+ onSelectParent: handleSelectParent,
20256
+ onDuplicate: handleDuplicateSelected,
20257
+ onDelete: handleDeleteSelected,
20258
+ addItemDisabled: isFooterFrameSelection && isFooterAddItemDisabled(selectedElRef.current) || selectedElRef.current !== null && (() => {
20259
+ const row = isSocialsRow(selectedElRef.current) ? selectedElRef.current : findSocialsRow(selectedElRef.current);
20260
+ return row ? !canAddSocialItem(row) : false;
20261
+ })(),
20262
+ editLinkDisabled: false,
20263
+ moreDisabled: false,
20264
+ deleteDisabled: selectedElRef.current !== null && (() => {
20265
+ const social = getSocialItem(selectedElRef.current);
20266
+ return social ? !canRemoveSocialItem(social) : false;
20267
+ })(),
20268
+ duplicateDisabled: isFooterFrameSelection || selectedIsSocialsRow || selectedElRef.current !== null && (() => {
20269
+ const social = getSocialItem(selectedElRef.current);
20270
+ const row = social ? findSocialsRow(social) : null;
20271
+ return row ? !canAddSocialItem(row) : false;
20272
+ })(),
20273
+ showEditLink: !isFooterFrameSelection && !selectedIsSocialsRow && navDropdownPreviewOpen === null,
20274
+ showAddItem: isFooterFrameSelection || selectedIsSocialsRow || !selectedIsCta && Boolean(
20275
+ selectedElRef.current && isNavbarHrefKey(selectedElRef.current.getAttribute("data-ohw-href-key")) && !isNestedNavChild(selectedElRef.current)
20276
+ ),
20277
+ showMore: (!selectedIsCta || isFooterFrameSelection) && !selectedIsSocialsRow,
20278
+ dropdownOpen: navDropdownPreviewOpen,
20279
+ onDropdownOpenChange: handleNavDropdownOpenChange,
20280
+ headingVisible: footerHeadingVisible,
20281
+ onHeadingVisibleChange: handleFooterHeadingVisibleChange
20282
+ }
20283
+ ) : void 0
20284
+ }
20285
+ ),
20286
+ toolbarRect && toolbarVariant === "rich-text" && !linkPopover && /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(import_jsx_runtime33.Fragment, { children: [
20287
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
20288
+ EditGlowChrome,
21824
20289
  {
21825
- rect: toggleState.rect,
21826
- activeState: toggleState.activeState,
21827
- states: toggleState.states,
21828
- onStateChange: handleStateChange
20290
+ rect: toolbarRect,
20291
+ elRef: glowElRef,
20292
+ reorderHrefKey,
20293
+ dragDisabled: reorderDragDisabled,
20294
+ hideHandle: isItemDragging
21829
20295
  }
21830
20296
  ),
21831
- sectionGap && !linkPopover && /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(
21832
- "div",
20297
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
20298
+ FloatingToolbar,
21833
20299
  {
21834
- "data-ohw-section-insert-line": "",
21835
- className: "fixed left-0 w-full z-2147483646 flex items-center pointer-events-none",
21836
- style: { top: sectionGap.y, transform: "translateY(-50%)" },
21837
- children: [
21838
- /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("div", { className: "flex-1 bg-primary", style: { height: 3 } }),
21839
- /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
21840
- Badge,
21841
- {
21842
- 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",
21843
- onClick: () => {
21844
- window.parent.postMessage(
21845
- {
21846
- type: "ow:add-section",
21847
- insertAfter: sectionGap.insertAfter,
21848
- insertBefore: sectionGap.insertBefore
21849
- },
21850
- "*"
21851
- );
21852
- },
21853
- children: "Add Section"
21854
- }
21855
- ),
21856
- /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("div", { className: "flex-1 bg-primary", style: { height: 3 } })
21857
- ]
20300
+ rect: toolbarRect,
20301
+ parentScroll: parentScrollRef.current,
20302
+ elRef: toolbarElRef,
20303
+ onCommand: handleCommand,
20304
+ activeCommands,
20305
+ showEditLink,
20306
+ onEditLink: openLinkPopoverForActive
21858
20307
  }
21859
- ),
21860
- linkPopover && dialogPortalContainer ? /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
21861
- LinkPopover,
21862
- {
21863
- panelRef: linkPopoverPanelRef,
21864
- portalContainer: dialogPortalContainer,
21865
- open: true,
21866
- mode: linkPopover.mode ?? "edit",
21867
- pages: sitePages,
21868
- sections: currentSections,
21869
- sectionsByPath,
21870
- initialTarget: linkPopover.target,
21871
- existingTargets: linkPopover.intent === "add-nav" ? getNavbarExistingTargets() : [],
21872
- onClose: closeLinkPopover,
21873
- onSubmit: handleLinkPopoverSubmit
20308
+ )
20309
+ ] }),
20310
+ maxBadge && /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(
20311
+ "div",
20312
+ {
20313
+ "data-ohw-max-badge": "",
20314
+ style: {
20315
+ position: "fixed",
20316
+ top: maxBadge.rect.bottom + 4,
20317
+ left: maxBadge.rect.right,
20318
+ transform: "translateX(-100%)",
20319
+ zIndex: 2147483647,
20320
+ background: maxBadge.current > maxBadge.max ? "#FEF2F2" : "#F5F5F4",
20321
+ color: maxBadge.current > maxBadge.max ? "#DC2626" : "#78716C",
20322
+ border: `1px solid ${maxBadge.current > maxBadge.max ? "#FECACA" : "#E7E5E4"}`,
20323
+ borderRadius: 4,
20324
+ padding: "2px 6px",
20325
+ fontSize: 11,
20326
+ fontWeight: 500,
20327
+ pointerEvents: "none"
21874
20328
  },
21875
- linkPopover.key
21876
- ) : null,
21877
- floatingPanel && floatingPanel.kind === "socials-display" ? /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
21878
- FloatingPanel,
21879
- {
21880
- open: true,
21881
- title: floatingPanel.title,
21882
- context: floatingPanel.context,
21883
- position: floatingPanelPos,
21884
- onPositionChange: setFloatingPanelPos,
21885
- parentScroll: parentScrollSnap ?? parentScrollRef.current,
21886
- onClose: closeFloatingPanelOnly,
21887
- children: /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
21888
- SocialsDisplayPanel,
20329
+ children: [
20330
+ maxBadge.current,
20331
+ "/",
20332
+ maxBadge.max
20333
+ ]
20334
+ }
20335
+ ),
20336
+ toggleState && !linkPopover && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
20337
+ StateToggle,
20338
+ {
20339
+ rect: toggleState.rect,
20340
+ activeState: toggleState.activeState,
20341
+ states: toggleState.states,
20342
+ onStateChange: handleStateChange
20343
+ }
20344
+ ),
20345
+ sectionGap && !linkPopover && /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(
20346
+ "div",
20347
+ {
20348
+ "data-ohw-section-insert-line": "",
20349
+ className: "fixed left-0 w-full z-2147483646 flex items-center pointer-events-none",
20350
+ style: { top: sectionGap.y, transform: "translateY(-50%)" },
20351
+ children: [
20352
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("div", { className: "flex-1 bg-primary", style: { height: 3 } }),
20353
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
20354
+ Badge,
21889
20355
  {
21890
- display: socialsDisplayFor(floatingPanel.row, editContentRef.current),
21891
- onChange: (next) => {
21892
- changeSocialsDisplay(floatingPanel.row, next);
21893
- setFloatingPanel({ ...floatingPanel });
21894
- }
20356
+ 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",
20357
+ onClick: () => {
20358
+ window.parent.postMessage(
20359
+ {
20360
+ type: "ow:add-section",
20361
+ insertAfter: sectionGap.insertAfter,
20362
+ insertBefore: sectionGap.insertBefore
20363
+ },
20364
+ "*"
20365
+ );
20366
+ },
20367
+ children: "Add Section"
21895
20368
  }
21896
- )
21897
- }
21898
- ) : null
21899
- ] }),
21900
- bridgeRoot
21901
- ) : null
21902
- ] });
21903
- }
21904
-
21905
- // src/ui/EmptySection.tsx
21906
- var import_link = __toESM(require("next/link"), 1);
21907
- var import_jsx_runtime34 = require("react/jsx-runtime");
21908
- function EmptySection({ title, homeHref = "/", eyebrowKey, titleKey, subtitleKey }) {
21909
- return /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)(import_jsx_runtime34.Fragment, { children: [
21910
- /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
21911
- "p",
21912
- {
21913
- style: {
21914
- fontFamily: "var(--brand-font-body)",
21915
- fontSize: "0.75rem",
21916
- fontWeight: 500,
21917
- letterSpacing: "0.15em",
21918
- textTransform: "uppercase",
21919
- color: "var(--brand-accent)",
21920
- marginBottom: "1.5rem"
21921
- },
21922
- children: /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(import_link.default, { href: homeHref, style: { color: "inherit" }, children: /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("span", { ...eyebrowKey ? { "data-ohw-editable": "text", "data-ohw-key": eyebrowKey } : {}, children: "Home" }) })
21923
- }
21924
- ),
21925
- /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
21926
- "h1",
21927
- {
21928
- style: {
21929
- fontFamily: "var(--brand-font-heading)",
21930
- fontSize: "clamp(2rem, 3.5vw, 2.75rem)",
21931
- lineHeight: 1.1,
21932
- letterSpacing: "-0.025em",
21933
- color: "var(--brand-text)",
21934
- marginBottom: "1rem"
21935
- },
21936
- ...titleKey ? { "data-ohw-editable": "text", "data-ohw-key": titleKey, "data-ohw-max-length": 80 } : {},
21937
- children: title
21938
- }
21939
- ),
21940
- /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
21941
- "p",
21942
- {
21943
- style: {
21944
- fontFamily: "var(--brand-font-body)",
21945
- fontSize: "1rem",
21946
- lineHeight: 1.7,
21947
- fontWeight: 300,
21948
- color: "var(--brand-text-muted)",
21949
- maxWidth: "340px"
20369
+ ),
20370
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("div", { className: "flex-1 bg-primary", style: { height: 3 } })
20371
+ ]
20372
+ }
20373
+ ),
20374
+ linkPopover && dialogPortalContainer ? /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
20375
+ LinkPopover,
20376
+ {
20377
+ panelRef: linkPopoverPanelRef,
20378
+ portalContainer: dialogPortalContainer,
20379
+ open: true,
20380
+ mode: linkPopover.mode ?? "edit",
20381
+ pages: sitePages,
20382
+ sections: currentSections,
20383
+ sectionsByPath,
20384
+ initialTarget: linkPopover.target,
20385
+ existingTargets: linkPopover.intent === "add-nav" ? getNavbarExistingTargets() : [],
20386
+ onClose: closeLinkPopover,
20387
+ onSubmit: handleLinkPopoverSubmit
21950
20388
  },
21951
- ...subtitleKey ? { "data-ohw-editable": "text", "data-ohw-key": subtitleKey, "data-ohw-max-length": 160 } : {},
21952
- children: "This page doesn't have any content yet."
21953
- }
21954
- )
21955
- ] });
20389
+ linkPopover.key
20390
+ ) : null,
20391
+ floatingPanel && floatingPanel.kind === "socials-display" ? /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
20392
+ FloatingPanel,
20393
+ {
20394
+ open: true,
20395
+ title: floatingPanel.title,
20396
+ context: floatingPanel.context,
20397
+ position: floatingPanelPos,
20398
+ onPositionChange: setFloatingPanelPos,
20399
+ parentScroll: parentScrollSnap ?? parentScrollRef.current,
20400
+ onClose: closeFloatingPanelOnly,
20401
+ children: /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
20402
+ SocialsDisplayPanel,
20403
+ {
20404
+ display: socialsDisplayFor(floatingPanel.row, editContentRef.current),
20405
+ onChange: (next) => {
20406
+ changeSocialsDisplay(floatingPanel.row, next);
20407
+ setFloatingPanel({ ...floatingPanel });
20408
+ }
20409
+ }
20410
+ )
20411
+ }
20412
+ ) : null
20413
+ ] }),
20414
+ bridgeRoot
20415
+ ) : null;
21956
20416
  }
21957
20417
  // Annotate the CommonJS export names for ESM import in node:
21958
20418
  0 && (module.exports = {
@@ -21971,7 +20431,6 @@ function EmptySection({ title, homeHref = "/", eyebrowKey, titleKey, subtitleKey
21971
20431
  DropdownMenuItem,
21972
20432
  DropdownMenuSeparator,
21973
20433
  DropdownMenuTrigger,
21974
- EmptySection,
21975
20434
  ItemActionToolbar,
21976
20435
  ItemInteractionLayer,
21977
20436
  LinkEditorPanel,